Daily update on 20250721-223448 by Nitload Youmu. Additional Message : Rename StgSHarp.C to StgSharp.Native. Add SDL to repo.

Signed-off-by: Nitload <YoumuLiu_NSI@whv0t.onmicrosoft.com>
This commit is contained in:
Nitload
2025-07-21 22:34:48 +08:00
parent c624a8ca38
commit 28ea5d1d1e
1215 changed files with 628924 additions and 114 deletions
@@ -0,0 +1,246 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_msctf_h_
#define SDL_msctf_h_
#include <unknwn.h>
#define TF_INVALID_COOKIE (0xffffffff)
#define TF_IPSINK_FLAG_ACTIVE 0x0001
#define TF_TMAE_UIELEMENTENABLEDONLY 0x00000004
/* *INDENT-OFF* */ // clang-format off
typedef struct ITfThreadMgr ITfThreadMgr;
typedef struct ITfDocumentMgr ITfDocumentMgr;
typedef struct ITfClientId ITfClientId;
typedef struct IEnumTfDocumentMgrs IEnumTfDocumentMgrs;
typedef struct IEnumTfFunctionProviders IEnumTfFunctionProviders;
typedef struct ITfFunctionProvider ITfFunctionProvider;
typedef struct ITfCompartmentMgr ITfCompartmentMgr;
typedef struct ITfContext ITfContext;
typedef struct IEnumTfContexts IEnumTfContexts;
typedef struct ITfUIElementSink ITfUIElementSink;
typedef struct ITfUIElement ITfUIElement;
typedef struct ITfUIElementMgr ITfUIElementMgr;
typedef struct IEnumTfUIElements IEnumTfUIElements;
typedef struct ITfThreadMgrEx ITfThreadMgrEx;
typedef struct ITfCandidateListUIElement ITfCandidateListUIElement;
typedef struct ITfReadingInformationUIElement ITfReadingInformationUIElement;
typedef struct ITfInputProcessorProfileActivationSink ITfInputProcessorProfileActivationSink;
typedef struct ITfSource ITfSource;
typedef DWORD TfClientId;
typedef DWORD TfEditCookie;
typedef struct ITfThreadMgrVtbl
{
HRESULT (STDMETHODCALLTYPE *QueryInterface)(ITfThreadMgr *, REFIID, void **);
ULONG (STDMETHODCALLTYPE *AddRef)(ITfThreadMgr *);
ULONG (STDMETHODCALLTYPE *Release)(ITfThreadMgr *);
HRESULT (STDMETHODCALLTYPE *Activate)(ITfThreadMgr *, TfClientId *);
HRESULT (STDMETHODCALLTYPE *Deactivate)(ITfThreadMgr *);
HRESULT (STDMETHODCALLTYPE *CreateDocumentMgr)(ITfThreadMgr *);
HRESULT (STDMETHODCALLTYPE *EnumDocumentMgrs)(ITfThreadMgr *, IEnumTfDocumentMgrs **);
HRESULT (STDMETHODCALLTYPE *GetFocus)(ITfThreadMgr *, ITfDocumentMgr **);
HRESULT (STDMETHODCALLTYPE *SetFocus)(ITfThreadMgr *, ITfDocumentMgr *);
HRESULT (STDMETHODCALLTYPE *AssociateFocus)(ITfThreadMgr *, HWND, ITfDocumentMgr *, ITfDocumentMgr **);
HRESULT (STDMETHODCALLTYPE *IsThreadFocus)(ITfThreadMgr *, BOOL *);
HRESULT (STDMETHODCALLTYPE *GetFunctionProvider)(ITfThreadMgr *, REFCLSID, ITfFunctionProvider **);
HRESULT (STDMETHODCALLTYPE *EnumFunctionProviders)(ITfThreadMgr *, IEnumTfFunctionProviders **);
HRESULT (STDMETHODCALLTYPE *GetGlobalCompartment)(ITfThreadMgr *, ITfCompartmentMgr **);
} ITfThreadMgrVtbl;
struct ITfThreadMgr
{
const struct ITfThreadMgrVtbl *lpVtbl;
};
typedef struct ITfThreadMgrExVtbl
{
HRESULT (STDMETHODCALLTYPE *QueryInterface)(ITfThreadMgrEx *, REFIID, void **);
ULONG (STDMETHODCALLTYPE *AddRef)(ITfThreadMgrEx *);
ULONG (STDMETHODCALLTYPE *Release)(ITfThreadMgrEx *);
HRESULT (STDMETHODCALLTYPE *Activate)(ITfThreadMgrEx *, TfClientId *);
HRESULT (STDMETHODCALLTYPE *Deactivate)(ITfThreadMgrEx *);
HRESULT (STDMETHODCALLTYPE *CreateDocumentMgr)(ITfThreadMgrEx *, ITfDocumentMgr **);
HRESULT (STDMETHODCALLTYPE *EnumDocumentMgrs)(ITfThreadMgrEx *, IEnumTfDocumentMgrs **);
HRESULT (STDMETHODCALLTYPE *GetFocus)(ITfThreadMgrEx *, ITfDocumentMgr **);
HRESULT (STDMETHODCALLTYPE *SetFocus)(ITfThreadMgrEx *, ITfDocumentMgr *);
HRESULT (STDMETHODCALLTYPE *AssociateFocus)(ITfThreadMgrEx *, ITfDocumentMgr *, ITfDocumentMgr **);
HRESULT (STDMETHODCALLTYPE *IsThreadFocus)(ITfThreadMgrEx *, BOOL *);
HRESULT (STDMETHODCALLTYPE *GetFunctionProvider)(ITfThreadMgrEx *, REFCLSID, ITfFunctionProvider **);
HRESULT (STDMETHODCALLTYPE *EnumFunctionProviders)(ITfThreadMgrEx *, IEnumTfFunctionProviders **);
HRESULT (STDMETHODCALLTYPE *GetGlobalCompartment)(ITfThreadMgrEx *, ITfCompartmentMgr **);
HRESULT (STDMETHODCALLTYPE *ActivateEx)(ITfThreadMgrEx *, TfClientId *, DWORD);
HRESULT (STDMETHODCALLTYPE *GetActiveFlags)(ITfThreadMgrEx *, DWORD *);
} ITfThreadMgrExVtbl;
struct ITfThreadMgrEx
{
const struct ITfThreadMgrExVtbl *lpVtbl;
};
typedef struct ITfDocumentMgrVtbl
{
HRESULT (STDMETHODCALLTYPE *QueryInterface)(ITfDocumentMgr *, REFIID, void **);
ULONG (STDMETHODCALLTYPE *AddRef)(ITfDocumentMgr *);
ULONG (STDMETHODCALLTYPE *Release)(ITfDocumentMgr *);
HRESULT (STDMETHODCALLTYPE *CreateContext)(ITfDocumentMgr *, TfClientId, DWORD, IUnknown *, ITfContext **, TfEditCookie *);
HRESULT (STDMETHODCALLTYPE *Push)(ITfDocumentMgr *, ITfContext *);
HRESULT (STDMETHODCALLTYPE *Pop)(ITfDocumentMgr *);
HRESULT (STDMETHODCALLTYPE *GetTop)(ITfDocumentMgr *, ITfContext **);
HRESULT (STDMETHODCALLTYPE *GetBase)(ITfDocumentMgr *, ITfContext **);
HRESULT (STDMETHODCALLTYPE *EnumContexts)(ITfDocumentMgr *, IEnumTfContexts **);
} ITfDocumentMgrVtbl;
struct ITfDocumentMgr
{
const struct ITfDocumentMgrVtbl *lpVtbl;
};
typedef struct ITfUIElementSinkVtbl
{
HRESULT (STDMETHODCALLTYPE *QueryInterface)(ITfUIElementSink *, REFIID, void **);
ULONG (STDMETHODCALLTYPE *AddRef)(ITfUIElementSink *);
ULONG (STDMETHODCALLTYPE *Release)(ITfUIElementSink *);
HRESULT (STDMETHODCALLTYPE *BeginUIElement)(ITfUIElementSink *, DWORD, BOOL *);
HRESULT (STDMETHODCALLTYPE *UpdateUIElement)(ITfUIElementSink *, DWORD);
HRESULT (STDMETHODCALLTYPE *EndUIElement)(ITfUIElementSink *, DWORD);
} ITfUIElementSinkVtbl;
struct ITfUIElementSink
{
const struct ITfUIElementSinkVtbl *lpVtbl;
};
typedef struct ITfUIElementMgrVtbl
{
HRESULT (STDMETHODCALLTYPE *QueryInterface)(ITfUIElementMgr *, REFIID, void **);
ULONG (STDMETHODCALLTYPE *AddRef)(ITfUIElementMgr *);
ULONG (STDMETHODCALLTYPE *Release)(ITfUIElementMgr *);
HRESULT (STDMETHODCALLTYPE *BeginUIElement)(ITfUIElementMgr *, ITfUIElement *, BOOL *, DWORD *);
HRESULT (STDMETHODCALLTYPE *UpdateUIElement)(ITfUIElementMgr *, DWORD);
HRESULT (STDMETHODCALLTYPE *EndUIElement)(ITfUIElementMgr *, DWORD);
HRESULT (STDMETHODCALLTYPE *GetUIElement)(ITfUIElementMgr *, DWORD, ITfUIElement **);
HRESULT (STDMETHODCALLTYPE *EnumUIElements)(ITfUIElementMgr *, IEnumTfUIElements **);
} ITfUIElementMgrVtbl;
struct ITfUIElementMgr
{
const struct ITfUIElementMgrVtbl *lpVtbl;
};
typedef struct ITfCandidateListUIElementVtbl
{
HRESULT (STDMETHODCALLTYPE *QueryInterface)(ITfCandidateListUIElement *, REFIID, void **);
ULONG (STDMETHODCALLTYPE *AddRef)(ITfCandidateListUIElement *);
ULONG (STDMETHODCALLTYPE *Release)(ITfCandidateListUIElement *);
HRESULT (STDMETHODCALLTYPE *GetDescription)(ITfCandidateListUIElement *, BSTR *);
HRESULT (STDMETHODCALLTYPE *GetGUID)(ITfCandidateListUIElement *, GUID *);
HRESULT (STDMETHODCALLTYPE *Show)(ITfCandidateListUIElement *, BOOL);
HRESULT (STDMETHODCALLTYPE *IsShown)(ITfCandidateListUIElement *, BOOL *);
HRESULT (STDMETHODCALLTYPE *GetUpdatedFlags)(ITfCandidateListUIElement *, DWORD *);
HRESULT (STDMETHODCALLTYPE *GetDocumentMgr)(ITfCandidateListUIElement *, ITfDocumentMgr **);
HRESULT (STDMETHODCALLTYPE *GetCount)(ITfCandidateListUIElement *, UINT *);
HRESULT (STDMETHODCALLTYPE *GetSelection)(ITfCandidateListUIElement *, UINT *);
HRESULT (STDMETHODCALLTYPE *GetString)(ITfCandidateListUIElement *, UINT, BSTR *);
HRESULT (STDMETHODCALLTYPE *GetPageIndex)(ITfCandidateListUIElement *, UINT *, UINT, UINT *);
HRESULT (STDMETHODCALLTYPE *SetPageIndex)(ITfCandidateListUIElement *, UINT *, UINT);
HRESULT (STDMETHODCALLTYPE *GetCurrentPage)(ITfCandidateListUIElement *, UINT *);
} ITfCandidateListUIElementVtbl;
struct ITfCandidateListUIElement
{
const struct ITfCandidateListUIElementVtbl *lpVtbl;
};
typedef struct ITfReadingInformationUIElementVtbl
{
HRESULT (STDMETHODCALLTYPE *QueryInterface)(ITfReadingInformationUIElement *, REFIID, void **);
ULONG (STDMETHODCALLTYPE *AddRef)(ITfReadingInformationUIElement *);
ULONG (STDMETHODCALLTYPE *Release)(ITfReadingInformationUIElement *);
HRESULT (STDMETHODCALLTYPE *GetDescription)(ITfReadingInformationUIElement *, BSTR *);
HRESULT (STDMETHODCALLTYPE *GetGUID)(ITfReadingInformationUIElement *, GUID *);
HRESULT (STDMETHODCALLTYPE *Show)(ITfReadingInformationUIElement *, BOOL);
HRESULT (STDMETHODCALLTYPE *IsShown)(ITfReadingInformationUIElement *, BOOL *);
HRESULT (STDMETHODCALLTYPE *GetUpdatedFlags)(ITfReadingInformationUIElement *, DWORD *);
HRESULT (STDMETHODCALLTYPE *GetContext)(ITfReadingInformationUIElement *, ITfContext **);
HRESULT (STDMETHODCALLTYPE *GetString)(ITfReadingInformationUIElement *, BSTR *);
HRESULT (STDMETHODCALLTYPE *GetMaxReadingStringLength)(ITfReadingInformationUIElement *, UINT *);
HRESULT (STDMETHODCALLTYPE *GetErrorIndex)(ITfReadingInformationUIElement *, UINT *);
HRESULT (STDMETHODCALLTYPE *IsVerticalOrderPreferred)(ITfReadingInformationUIElement *, BOOL *);
} ITfReadingInformationUIElementVtbl;
struct ITfReadingInformationUIElement
{
const struct ITfReadingInformationUIElementVtbl *lpVtbl;
};
typedef struct ITfUIElementVtbl
{
HRESULT (STDMETHODCALLTYPE *QueryInterface)(ITfUIElement *, REFIID, void **);
ULONG (STDMETHODCALLTYPE *AddRef)(ITfUIElement *);
ULONG (STDMETHODCALLTYPE *Release)(ITfUIElement *);
HRESULT (STDMETHODCALLTYPE *GetDescription)(ITfUIElement *, BSTR *);
HRESULT (STDMETHODCALLTYPE *GetGUID)(ITfUIElement *, GUID *);
HRESULT (STDMETHODCALLTYPE *Show)(ITfUIElement *, BOOL);
HRESULT (STDMETHODCALLTYPE *IsShown)(ITfUIElement *, BOOL *);
} ITfUIElementVtbl;
struct ITfUIElement
{
const struct ITfUIElementVtbl *lpVtbl;
};
typedef struct ITfInputProcessorProfileActivationSinkVtbl
{
HRESULT (STDMETHODCALLTYPE *QueryInterface)(ITfInputProcessorProfileActivationSink *, REFIID, void **);
ULONG (STDMETHODCALLTYPE *AddRef)(ITfInputProcessorProfileActivationSink *);
ULONG (STDMETHODCALLTYPE *Release)(ITfInputProcessorProfileActivationSink *);
HRESULT (STDMETHODCALLTYPE *OnActivated)(ITfInputProcessorProfileActivationSink *, DWORD, LANGID, REFCLSID, REFGUID, REFGUID, HKL, DWORD);
} ITfInputProcessorProfileActivationSinkVtbl;
struct ITfInputProcessorProfileActivationSink
{
const struct ITfInputProcessorProfileActivationSinkVtbl *lpVtbl;
};
typedef struct ITfSourceVtbl
{
HRESULT (STDMETHODCALLTYPE *QueryInterface)(ITfSource *, REFIID, void **);
ULONG (STDMETHODCALLTYPE *AddRef)(ITfSource *);
ULONG (STDMETHODCALLTYPE *Release)(ITfSource *);
HRESULT (STDMETHODCALLTYPE *AdviseSink)(ITfSource *, REFIID, IUnknown *, DWORD *);
HRESULT (STDMETHODCALLTYPE *UnadviseSink)(ITfSource *, DWORD);
} ITfSourceVtbl;
struct ITfSource
{
const struct ITfSourceVtbl *lpVtbl;
};
/* *INDENT-ON* */ // clang-format on
#endif // SDL_msctf_h_
@@ -0,0 +1,97 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_surface_utils.h"
#include "../SDL_surface_c.h"
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
HICON CreateIconFromSurface(SDL_Surface *surface)
{
SDL_Surface *s = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
if (!s) {
return NULL;
}
/* The dimensions will be needed after s is freed */
const int width = s->w;
const int height = s->h;
BITMAPINFO bmpInfo;
SDL_zero(bmpInfo);
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth = width;
bmpInfo.bmiHeader.biHeight = -height; /* Top-down bitmap */
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 32;
bmpInfo.bmiHeader.biCompression = BI_RGB;
HDC hdc = GetDC(NULL);
void *pBits = NULL;
HBITMAP hBitmap = CreateDIBSection(hdc, &bmpInfo, DIB_RGB_COLORS, &pBits, NULL, 0);
if (!hBitmap) {
ReleaseDC(NULL, hdc);
SDL_DestroySurface(s);
return NULL;
}
SDL_memcpy(pBits, s->pixels, width * height * 4);
SDL_DestroySurface(s);
HBITMAP hMask = CreateBitmap(width, height, 1, 1, NULL);
if (!hMask) {
DeleteObject(hBitmap);
ReleaseDC(NULL, hdc);
return NULL;
}
HDC hdcMem = CreateCompatibleDC(hdc);
HGDIOBJ oldBitmap = SelectObject(hdcMem, hMask);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
BYTE* pixel = (BYTE*)pBits + (y * width + x) * 4;
BYTE alpha = pixel[3];
COLORREF maskColor = (alpha == 0) ? RGB(0, 0, 0) : RGB(255, 255, 255);
SetPixel(hdcMem, x, y, maskColor);
}
}
ICONINFO iconInfo;
iconInfo.fIcon = TRUE;
iconInfo.xHotspot = 0;
iconInfo.yHotspot = 0;
iconInfo.hbmMask = hMask;
iconInfo.hbmColor = hBitmap;
HICON hIcon = CreateIconIndirect(&iconInfo);
SelectObject(hdcMem, oldBitmap);
DeleteDC(hdcMem);
DeleteObject(hBitmap);
DeleteObject(hMask);
ReleaseDC(NULL, hdc);
return hIcon;
}
#endif
@@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_surface_utils_h_
#define SDL_surface_utils_h_
#include "../../core/windows/SDL_windows.h"
#ifdef __cplusplus
extern "C" {
#endif
extern HICON CreateIconFromSurface(SDL_Surface *surface);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,442 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#include "SDL_windowsvideo.h"
#include "SDL_windowswindow.h"
#include "../SDL_clipboard_c.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_clipboardevents_c.h"
#ifdef UNICODE
#define TEXT_FORMAT CF_UNICODETEXT
#else
#define TEXT_FORMAT CF_TEXT
#endif
#define IMAGE_FORMAT CF_DIB
#define IMAGE_MIME_TYPE "image/bmp"
#define BFT_BITMAP 0x4d42 // 'BM'
// Assume we can directly read and write BMP fields without byte swapping
SDL_COMPILE_TIME_ASSERT(verify_byte_order, SDL_BYTEORDER == SDL_LIL_ENDIAN);
static BOOL WIN_OpenClipboard(SDL_VideoDevice *_this)
{
// Retry to open the clipboard in case another application has it open
const int MAX_ATTEMPTS = 3;
int attempt;
HWND hwnd = NULL;
if (_this->windows) {
hwnd = _this->windows->internal->hwnd;
}
for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
if (OpenClipboard(hwnd)) {
return TRUE;
}
SDL_Delay(10);
}
return FALSE;
}
static void WIN_CloseClipboard(void)
{
CloseClipboard();
}
static HANDLE WIN_ConvertBMPtoDIB(const void *bmp, size_t bmp_size)
{
HANDLE hMem = NULL;
if (bmp && bmp_size > sizeof(BITMAPFILEHEADER) && ((BITMAPFILEHEADER *)bmp)->bfType == BFT_BITMAP) {
BITMAPFILEHEADER *pbfh = (BITMAPFILEHEADER *)bmp;
BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)((Uint8 *)bmp + sizeof(BITMAPFILEHEADER));
size_t bih_size = pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD);
size_t pixels_size = pbih->biSizeImage;
if (pbfh->bfOffBits >= (sizeof(BITMAPFILEHEADER) + bih_size) &&
(pbfh->bfOffBits + pixels_size) <= bmp_size) {
const Uint8 *pixels = (const Uint8 *)bmp + pbfh->bfOffBits;
size_t dib_size = bih_size + pixels_size;
hMem = GlobalAlloc(GMEM_MOVEABLE, dib_size);
if (hMem) {
LPVOID dst = GlobalLock(hMem);
if (dst) {
SDL_memcpy(dst, pbih, bih_size);
SDL_memcpy((Uint8 *)dst + bih_size, pixels, pixels_size);
GlobalUnlock(hMem);
} else {
WIN_SetError("GlobalLock()");
GlobalFree(hMem);
hMem = NULL;
}
} else {
SDL_OutOfMemory();
}
} else {
SDL_SetError("Invalid BMP data");
}
} else {
SDL_SetError("Invalid BMP data");
}
return hMem;
}
static void *WIN_ConvertDIBtoBMP(HANDLE hMem, size_t *size)
{
void *bmp = NULL;
size_t mem_size = GlobalSize(hMem);
if (mem_size > sizeof(BITMAPINFOHEADER)) {
LPVOID dib = GlobalLock(hMem);
if (dib) {
BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)dib;
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader#color-tables
size_t color_table_size;
switch (pbih->biCompression) {
case BI_RGB:
if (pbih->biBitCount <= 8) {
color_table_size = sizeof(RGBQUAD) * (pbih->biClrUsed == 0 ? 1 << pbih->biBitCount : pbih->biClrUsed);
} else {
color_table_size = 0;
}
break;
case BI_BITFIELDS:
color_table_size = 3 * sizeof(DWORD);
break;
case 6 /* BI_ALPHABITFIELDS */:
// https://learn.microsoft.com/en-us/previous-versions/windows/embedded/aa452885(v=msdn.10)
color_table_size = 4 * sizeof(DWORD);
break;
default: // FOURCC
color_table_size = sizeof(RGBQUAD) * pbih->biClrUsed;
}
size_t bih_size = pbih->biSize + color_table_size;
size_t dib_size = bih_size + pbih->biSizeImage;
if (dib_size <= mem_size) {
size_t bmp_size = sizeof(BITMAPFILEHEADER) + mem_size;
bmp = SDL_malloc(bmp_size);
if (bmp) {
BITMAPFILEHEADER *pbfh = (BITMAPFILEHEADER *)bmp;
pbfh->bfType = BFT_BITMAP;
pbfh->bfSize = (DWORD)bmp_size;
pbfh->bfReserved1 = 0;
pbfh->bfReserved2 = 0;
pbfh->bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + pbih->biSize + color_table_size);
SDL_memcpy((Uint8 *)bmp + sizeof(BITMAPFILEHEADER), dib, dib_size);
*size = bmp_size;
}
} else {
SDL_SetError("Invalid BMP data");
}
GlobalUnlock(hMem);
} else {
WIN_SetError("GlobalLock()");
}
} else {
SDL_SetError("Invalid BMP data");
}
return bmp;
}
static bool WIN_SetClipboardImage(SDL_VideoDevice *_this)
{
HANDLE hMem;
size_t clipboard_data_size;
const void *clipboard_data;
bool result = true;
clipboard_data = _this->clipboard_callback(_this->clipboard_userdata, IMAGE_MIME_TYPE, &clipboard_data_size);
hMem = WIN_ConvertBMPtoDIB(clipboard_data, clipboard_data_size);
if (hMem) {
// Save the image to the clipboard
if (!SetClipboardData(IMAGE_FORMAT, hMem)) {
result = WIN_SetError("Couldn't set clipboard data");
}
} else {
// WIN_ConvertBMPtoDIB() set the error
result = false;
}
return result;
}
static bool WIN_SetClipboardText(SDL_VideoDevice *_this, const char *mime_type)
{
HANDLE hMem;
size_t clipboard_data_size;
const void *clipboard_data;
bool result = true;
clipboard_data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, &clipboard_data_size);
if (clipboard_data && clipboard_data_size > 0) {
SIZE_T i, size;
LPWSTR tstr = (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (const char *)clipboard_data, clipboard_data_size);
if (!tstr) {
return SDL_SetError("Couldn't convert text from UTF-8");
}
// Find out the size of the data
for (size = 0, i = 0; tstr[i]; ++i, ++size) {
if (tstr[i] == '\n' && (i == 0 || tstr[i - 1] != '\r')) {
// We're going to insert a carriage return
++size;
}
}
size = (size + 1) * sizeof(*tstr);
// Save the data to the clipboard
hMem = GlobalAlloc(GMEM_MOVEABLE, size);
if (hMem) {
LPWSTR dst = (LPWSTR)GlobalLock(hMem);
if (dst) {
// Copy the text over, adding carriage returns as necessary
for (i = 0; tstr[i]; ++i) {
if (tstr[i] == '\n' && (i == 0 || tstr[i - 1] != '\r')) {
*dst++ = '\r';
}
*dst++ = tstr[i];
}
*dst = 0;
GlobalUnlock(hMem);
}
if (!SetClipboardData(TEXT_FORMAT, hMem)) {
result = WIN_SetError("Couldn't set clipboard data");
}
} else {
result = SDL_OutOfMemory();
}
SDL_free(tstr);
}
return result;
}
bool WIN_SetClipboardData(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
size_t i;
bool result = true;
/* I investigated delayed clipboard rendering, and at least with text and image
* formats you have to use an output window, not SDL_HelperWindow, and the system
* requests them being rendered immediately, so there isn't any benefit.
*/
if (WIN_OpenClipboard(_this)) {
EmptyClipboard();
// Set the clipboard text
for (i = 0; i < _this->num_clipboard_mime_types; ++i) {
const char *mime_type = _this->clipboard_mime_types[i];
if (SDL_IsTextMimeType(mime_type)) {
if (!WIN_SetClipboardText(_this, mime_type)) {
result = false;
}
// Only set the first clipboard text
break;
}
}
// Set the clipboard image
for (i = 0; i < _this->num_clipboard_mime_types; ++i) {
const char *mime_type = _this->clipboard_mime_types[i];
if (SDL_strcmp(mime_type, IMAGE_MIME_TYPE) == 0) {
if (!WIN_SetClipboardImage(_this)) {
result = false;
}
break;
}
}
data->clipboard_count = GetClipboardSequenceNumber();
WIN_CloseClipboard();
} else {
result = WIN_SetError("Couldn't open clipboard");
}
return result;
}
void *WIN_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size)
{
void *data = NULL;
if (SDL_IsTextMimeType(mime_type)) {
char *text = NULL;
if (IsClipboardFormatAvailable(TEXT_FORMAT)) {
if (WIN_OpenClipboard(_this)) {
HANDLE hMem;
LPTSTR tstr;
hMem = GetClipboardData(TEXT_FORMAT);
if (hMem) {
tstr = (LPTSTR)GlobalLock(hMem);
if (tstr) {
text = WIN_StringToUTF8(tstr);
GlobalUnlock(hMem);
} else {
WIN_SetError("Couldn't lock clipboard data");
}
} else {
WIN_SetError("Couldn't get clipboard data");
}
WIN_CloseClipboard();
}
}
if (!text) {
text = SDL_strdup("");
}
data = text;
*size = SDL_strlen(text);
} else if (SDL_strcmp(mime_type, IMAGE_MIME_TYPE) == 0) {
if (IsClipboardFormatAvailable(IMAGE_FORMAT)) {
if (WIN_OpenClipboard(_this)) {
HANDLE hMem;
hMem = GetClipboardData(IMAGE_FORMAT);
if (hMem) {
data = WIN_ConvertDIBtoBMP(hMem, size);
} else {
WIN_SetError("Couldn't get clipboard data");
}
WIN_CloseClipboard();
}
}
} else {
data = SDL_GetInternalClipboardData(_this, mime_type, size);
}
return data;
}
bool WIN_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
{
if (SDL_IsTextMimeType(mime_type)) {
if (IsClipboardFormatAvailable(TEXT_FORMAT)) {
return true;
}
} else if (SDL_strcmp(mime_type, IMAGE_MIME_TYPE) == 0) {
if (IsClipboardFormatAvailable(IMAGE_FORMAT)) {
return true;
}
} else {
if (SDL_HasInternalClipboardData(_this, mime_type)) {
return true;
}
}
return false;
}
static int GetClipboardFormatMimeType(UINT format, char *name)
{
static struct
{
UINT format;
const char *mime_type;
} mime_types[] = {
{ TEXT_FORMAT, "text/plain;charset=utf-8" },
{ IMAGE_FORMAT, IMAGE_MIME_TYPE },
};
for (int i = 0; i < SDL_arraysize(mime_types); ++i) {
if (format == mime_types[i].format) {
size_t len = SDL_strlen(mime_types[i].mime_type) + 1;
if (name) {
SDL_memcpy(name, mime_types[i].mime_type, len);
}
return (int)len;
}
}
return 0;
}
static char **GetMimeTypes(int *pnformats)
{
char **new_mime_types = NULL;
*pnformats = 0;
if (WIN_OpenClipboard(SDL_GetVideoDevice())) {
int nformats = 0;
UINT format = 0;
int formatsSz = 0;
for ( ; ; ) {
format = EnumClipboardFormats(format);
if (!format) {
break;
}
int len = GetClipboardFormatMimeType(format, NULL);
if (len > 0) {
++nformats;
formatsSz += len;
}
}
new_mime_types = SDL_AllocateTemporaryMemory((nformats + 1) * sizeof(char *) + formatsSz);
if (new_mime_types) {
format = 0;
char *strPtr = (char *)(new_mime_types + nformats + 1);
int i = 0;
for ( ; ; ) {
format = EnumClipboardFormats(format);
if (!format) {
break;
}
int len = GetClipboardFormatMimeType(format, strPtr);
if (len > 0) {
new_mime_types[i++] = strPtr;
strPtr += len;
}
}
new_mime_types[nformats] = NULL;
*pnformats = nformats;
}
WIN_CloseClipboard();
}
return new_mime_types;
}
void WIN_CheckClipboardUpdate(struct SDL_VideoData *data)
{
DWORD count = GetClipboardSequenceNumber();
if (count != data->clipboard_count) {
if (count) {
int nformats = 0;
char **new_mime_types = GetMimeTypes(&nformats);
if (new_mime_types) {
SDL_SendClipboardUpdate(false, new_mime_types, nformats);
}
}
data->clipboard_count = count;
}
}
#endif // SDL_VIDEO_DRIVER_WINDOWS
@@ -0,0 +1,34 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_windowsclipboard_h_
#define SDL_windowsclipboard_h_
// Forward declaration
struct SDL_VideoData;
extern bool WIN_SetClipboardData(SDL_VideoDevice *_this);
extern void *WIN_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size);
extern bool WIN_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type);
extern void WIN_CheckClipboardUpdate(struct SDL_VideoData *data);
#endif // SDL_windowsclipboard_h_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,39 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_windowsevents_h_
#define SDL_windowsevents_h_
extern LPTSTR SDL_Appname;
extern Uint32 SDL_Appstyle;
extern HINSTANCE SDL_Instance;
extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);
extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam);
extern void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start);
extern void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, bool initial_check);
extern void WIN_PumpEvents(SDL_VideoDevice *_this);
extern void WIN_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);
extern int WIN_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS);
#endif // SDL_windowsevents_h_
@@ -0,0 +1,132 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#include "SDL_windowsvideo.h"
bool WIN_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormat *format, void **pixels, int *pitch)
{
SDL_WindowData *data = window->internal;
bool isstack;
size_t size;
LPBITMAPINFO info;
HBITMAP hbm;
int w, h;
SDL_GetWindowSizeInPixels(window, &w, &h);
// Free the old framebuffer surface
if (data->mdc) {
DeleteDC(data->mdc);
}
if (data->hbm) {
DeleteObject(data->hbm);
}
// Find out the format of the screen
size = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
info = (LPBITMAPINFO)SDL_small_alloc(Uint8, size, &isstack);
if (!info) {
return false;
}
SDL_memset(info, 0, size);
info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// The second call to GetDIBits() fills in the bitfields
hbm = CreateCompatibleBitmap(data->hdc, 1, 1);
GetDIBits(data->hdc, hbm, 0, 0, NULL, info, DIB_RGB_COLORS);
GetDIBits(data->hdc, hbm, 0, 0, NULL, info, DIB_RGB_COLORS);
DeleteObject(hbm);
*format = SDL_PIXELFORMAT_UNKNOWN;
if (info->bmiHeader.biCompression == BI_BITFIELDS) {
int bpp;
Uint32 *masks;
bpp = info->bmiHeader.biPlanes * info->bmiHeader.biBitCount;
masks = (Uint32 *)((Uint8 *)info + info->bmiHeader.biSize);
*format = SDL_GetPixelFormatForMasks(bpp, masks[0], masks[1], masks[2], 0);
}
if (*format == SDL_PIXELFORMAT_UNKNOWN) {
// We'll use RGB format for now
*format = SDL_PIXELFORMAT_XRGB8888;
// Create a new one
SDL_memset(info, 0, size);
info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info->bmiHeader.biPlanes = 1;
info->bmiHeader.biBitCount = 32;
info->bmiHeader.biCompression = BI_RGB;
}
// Fill in the size information
*pitch = (((w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3);
info->bmiHeader.biWidth = w;
info->bmiHeader.biHeight = -h; // negative for topdown bitmap
info->bmiHeader.biSizeImage = (DWORD)h * (*pitch);
data->mdc = CreateCompatibleDC(data->hdc);
data->hbm = CreateDIBSection(data->hdc, info, DIB_RGB_COLORS, pixels, NULL, 0);
SDL_small_free(info, isstack);
if (!data->hbm) {
return WIN_SetError("Unable to create DIB");
}
SelectObject(data->mdc, data->hbm);
return true;
}
bool WIN_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects, int numrects)
{
SDL_WindowData *data = window->internal;
int i;
for (i = 0; i < numrects; ++i) {
BitBlt(data->hdc, rects[i].x, rects[i].y, rects[i].w, rects[i].h,
data->mdc, rects[i].x, rects[i].y, SRCCOPY);
}
return true;
}
void WIN_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->internal;
if (!data) {
// The window wasn't fully initialized
return;
}
if (data->mdc) {
DeleteDC(data->mdc);
data->mdc = NULL;
}
if (data->hbm) {
DeleteObject(data->hbm);
data->hbm = NULL;
}
}
#endif // SDL_VIDEO_DRIVER_WINDOWS
@@ -0,0 +1,25 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
extern bool WIN_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormat *format, void **pixels, int *pitch);
extern bool WIN_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects, int numrects);
extern void WIN_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window);
@@ -0,0 +1,623 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_windowsvideo.h"
#ifdef HAVE_GAMEINPUT_H
#include "../../core/windows/SDL_gameinput.h"
extern "C" {
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/scancodes_windows.h"
}
#define MAX_GAMEINPUT_BUTTONS 7 // GameInputMouseWheelTiltRight is the highest button
static const Uint8 GAMEINPUT_button_map[MAX_GAMEINPUT_BUTTONS] = {
SDL_BUTTON_LEFT,
SDL_BUTTON_RIGHT,
SDL_BUTTON_MIDDLE,
SDL_BUTTON_X1,
SDL_BUTTON_X2,
6,
7
};
typedef struct GAMEINPUT_Device
{
IGameInputDevice *pDevice;
const GameInputDeviceInfo *info;
char *name;
Uint32 instance_id; // generated by SDL
bool registered;
bool delete_requested;
IGameInputReading *last_mouse_reading;
IGameInputReading *last_keyboard_reading;
} GAMEINPUT_Device;
struct WIN_GameInputData
{
IGameInput *pGameInput;
GameInputCallbackToken gameinput_callback_token;
int num_devices;
GAMEINPUT_Device **devices;
GameInputKind enabled_input;
SDL_Mutex *lock;
uint64_t timestamp_offset;
};
static bool GAMEINPUT_InternalAddOrFind(WIN_GameInputData *data, IGameInputDevice *pDevice)
{
GAMEINPUT_Device **devicelist = NULL;
GAMEINPUT_Device *device = NULL;
const GameInputDeviceInfo *info;
bool result = false;
#if GAMEINPUT_API_VERSION >= 1
HRESULT hr = pDevice->GetDeviceInfo(&info);
if (FAILED(hr)) {
return WIN_SetErrorFromHRESULT("IGameInputDevice_GetDeviceInfo", hr);
}
#else
info = pDevice->GetDeviceInfo();
#endif
SDL_LockMutex(data->lock);
{
for (int i = 0; i < data->num_devices; ++i) {
device = data->devices[i];
if (device && device->pDevice == pDevice) {
// we're already added
device->delete_requested = false;
result = true;
goto done;
}
}
device = (GAMEINPUT_Device *)SDL_calloc(1, sizeof(*device));
if (!device) {
goto done;
}
devicelist = (GAMEINPUT_Device **)SDL_realloc(data->devices, (data->num_devices + 1) * sizeof(*devicelist));
if (!devicelist) {
SDL_free(device);
goto done;
}
if (info->displayName) {
// This could give us a product string, but it's NULL for all the devices I've tested
}
pDevice->AddRef();
device->pDevice = pDevice;
device->instance_id = SDL_GetNextObjectID();
device->info = info;
data->devices = devicelist;
data->devices[data->num_devices++] = device;
result = true;
}
done:
SDL_UnlockMutex(data->lock);
return result;
}
static bool GAMEINPUT_InternalRemoveByIndex(WIN_GameInputData *data, int idx)
{
GAMEINPUT_Device **devicelist = NULL;
GAMEINPUT_Device *device;
bool result = false;
SDL_LockMutex(data->lock);
{
if (idx < 0 || idx >= data->num_devices) {
result = SDL_SetError("GAMEINPUT_InternalRemoveByIndex argument idx %d is out of range", idx);
goto done;
}
device = data->devices[idx];
if (device) {
if (device->registered) {
if (device->info->supportedInput & GameInputKindMouse) {
SDL_RemoveMouse(device->instance_id, true);
}
if (device->info->supportedInput & GameInputKindKeyboard) {
SDL_RemoveKeyboard(device->instance_id, true);
}
if (device->last_mouse_reading) {
device->last_mouse_reading->Release();
device->last_mouse_reading = NULL;
}
if (device->last_keyboard_reading) {
device->last_keyboard_reading->Release();
device->last_keyboard_reading = NULL;
}
}
device->pDevice->Release();
SDL_free(device->name);
SDL_free(device);
}
data->devices[idx] = NULL;
if (data->num_devices == 1) {
// last element in the list, free the entire list then
SDL_free(data->devices);
data->devices = NULL;
} else {
if (idx != data->num_devices - 1) {
size_t bytes = sizeof(*devicelist) * (data->num_devices - idx - 1);
SDL_memmove(&data->devices[idx], &data->devices[idx + 1], bytes);
}
}
// decrement the count and return
--data->num_devices;
result = true;
}
done:
SDL_UnlockMutex(data->lock);
return result;
}
static void CALLBACK GAMEINPUT_InternalDeviceCallback(
_In_ GameInputCallbackToken callbackToken,
_In_ void *context,
_In_ IGameInputDevice *pDevice,
_In_ uint64_t timestamp,
_In_ GameInputDeviceStatus currentStatus,
_In_ GameInputDeviceStatus previousStatus)
{
WIN_GameInputData *data = (WIN_GameInputData *)context;
int idx = 0;
GAMEINPUT_Device *device = NULL;
if (!pDevice) {
// This should never happen, but ignore it if it does
return;
}
if (currentStatus & GameInputDeviceConnected) {
GAMEINPUT_InternalAddOrFind(data, pDevice);
} else {
for (idx = 0; idx < data->num_devices; ++idx) {
device = data->devices[idx];
if (device && device->pDevice == pDevice) {
// will be deleted on the next Detect call
device->delete_requested = true;
break;
}
}
}
}
bool WIN_InitGameInput(SDL_VideoDevice *_this)
{
WIN_GameInputData *data;
HRESULT hr;
Uint64 now;
uint64_t timestampUS;
bool result = false;
if (_this->internal->gameinput_context) {
return true;
}
data = (WIN_GameInputData *)SDL_calloc(1, sizeof(*data));
if (!data) {
goto done;
}
_this->internal->gameinput_context = data;
data->lock = SDL_CreateMutex();
if (!data->lock) {
goto done;
}
if (!SDL_InitGameInput(&data->pGameInput)) {
goto done;
}
hr = data->pGameInput->RegisterDeviceCallback(NULL,
(GameInputKindMouse | GameInputKindKeyboard),
GameInputDeviceConnected,
GameInputBlockingEnumeration,
data,
GAMEINPUT_InternalDeviceCallback,
&data->gameinput_callback_token);
if (FAILED(hr)) {
WIN_SetErrorFromHRESULT("IGameInput::RegisterDeviceCallback", hr);
goto done;
}
// Calculate the relative offset between SDL timestamps and GameInput timestamps
now = SDL_GetTicksNS();
timestampUS = data->pGameInput->GetCurrentTimestamp();
data->timestamp_offset = (SDL_NS_TO_US(now) - timestampUS);
result = true;
done:
if (!result) {
WIN_QuitGameInput(_this);
}
return result;
}
static void GAMEINPUT_InitialMouseReading(WIN_GameInputData *data, SDL_Window *window, GAMEINPUT_Device *device, IGameInputReading *reading)
{
GameInputMouseState state;
if (reading->GetMouseState(&state)) {
Uint64 timestamp = SDL_US_TO_NS(reading->GetTimestamp() + data->timestamp_offset);
SDL_MouseID mouseID = device->instance_id;
for (int i = 0; i < MAX_GAMEINPUT_BUTTONS; ++i) {
const GameInputMouseButtons mask = GameInputMouseButtons(1 << i);
bool down = ((state.buttons & mask) != 0);
SDL_SendMouseButton(timestamp, window, mouseID, GAMEINPUT_button_map[i], down);
}
// Invalidate mouse button flags
window->internal->mouse_button_flags = (WPARAM)-1;
}
}
static void GAMEINPUT_HandleMouseDelta(WIN_GameInputData *data, SDL_Window *window, GAMEINPUT_Device *device, IGameInputReading *last_reading, IGameInputReading *reading)
{
GameInputMouseState last;
GameInputMouseState state;
if (last_reading->GetMouseState(&last) && reading->GetMouseState(&state)) {
Uint64 timestamp = SDL_US_TO_NS(reading->GetTimestamp() + data->timestamp_offset);
SDL_MouseID mouseID = device->instance_id;
GameInputMouseState delta;
delta.buttons = (state.buttons ^ last.buttons);
delta.positionX = (state.positionX - last.positionX);
delta.positionY = (state.positionY - last.positionY);
delta.wheelX = (state.wheelX - last.wheelX);
delta.wheelY = (state.wheelY - last.wheelY);
if (delta.positionX || delta.positionY) {
SDL_SendMouseMotion(timestamp, window, mouseID, true, (float)delta.positionX, (float)delta.positionY);
}
if (delta.buttons) {
for (int i = 0; i < MAX_GAMEINPUT_BUTTONS; ++i) {
const GameInputMouseButtons mask = GameInputMouseButtons(1 << i);
if (delta.buttons & mask) {
bool down = ((state.buttons & mask) != 0);
SDL_SendMouseButton(timestamp, window, mouseID, GAMEINPUT_button_map[i], down);
}
}
// Invalidate mouse button flags
window->internal->mouse_button_flags = (WPARAM)-1;
}
if (delta.wheelX || delta.wheelY) {
float fAmountX = (float)delta.wheelX / WHEEL_DELTA;
float fAmountY = (float)delta.wheelY / WHEEL_DELTA;
SDL_SendMouseWheel(timestamp, SDL_GetMouseFocus(), device->instance_id, fAmountX, fAmountY, SDL_MOUSEWHEEL_NORMAL);
}
}
}
static SDL_Scancode GetScancodeFromKeyState(const GameInputKeyState *state)
{
Uint8 index = (Uint8)(state->scanCode & 0xFF);
if ((state->scanCode & 0xFF00) == 0xE000) {
index |= 0x80;
}
return windows_scancode_table[index];
}
static bool KeysHaveScancode(const GameInputKeyState *keys, uint32_t count, SDL_Scancode scancode)
{
for (uint32_t i = 0; i < count; ++i) {
if (GetScancodeFromKeyState(&keys[i]) == scancode) {
return true;
}
}
return false;
}
static void GAMEINPUT_InitialKeyboardReading(WIN_GameInputData *data, SDL_Window *window, GAMEINPUT_Device *device, IGameInputReading *reading)
{
Uint64 timestamp = SDL_US_TO_NS(reading->GetTimestamp() + data->timestamp_offset);
SDL_KeyboardID keyboardID = device->instance_id;
uint32_t max_keys = device->info->keyboardInfo->maxSimultaneousKeys;
GameInputKeyState *keys = SDL_stack_alloc(GameInputKeyState, max_keys);
if (!keys) {
return;
}
uint32_t num_keys = reading->GetKeyState(max_keys, keys);
if (!num_keys) {
// FIXME: We probably need to track key state by keyboardID
SDL_ResetKeyboard();
return;
}
// Go through and send key up events for any key that's not held down
int num_scancodes;
const bool *keyboard_state = SDL_GetKeyboardState(&num_scancodes);
for (int i = 0; i < num_scancodes; ++i) {
if (keyboard_state[i] && !KeysHaveScancode(keys, num_keys, (SDL_Scancode)i)) {
SDL_SendKeyboardKey(timestamp, keyboardID, keys[i].scanCode, (SDL_Scancode)i, false);
}
}
// Go through and send key down events for any key that's held down
for (uint32_t i = 0; i < num_keys; ++i) {
SDL_SendKeyboardKey(timestamp, keyboardID, keys[i].scanCode, GetScancodeFromKeyState(&keys[i]), true);
}
}
#ifdef DEBUG_KEYS
static void DumpKeys(const char *prefix, GameInputKeyState *keys, uint32_t count)
{
SDL_Log("%s", prefix);
for (uint32_t i = 0; i < count; ++i) {
char str[5];
*SDL_UCS4ToUTF8(keys[i].codePoint, str) = '\0';
SDL_Log(" Key 0x%.2x (%s)", keys[i].scanCode, str);
}
}
#endif // DEBUG_KEYS
static void GAMEINPUT_HandleKeyboardDelta(WIN_GameInputData *data, SDL_Window *window, GAMEINPUT_Device *device, IGameInputReading *last_reading, IGameInputReading *reading)
{
Uint64 timestamp = SDL_US_TO_NS(reading->GetTimestamp() + data->timestamp_offset);
SDL_KeyboardID keyboardID = device->instance_id;
uint32_t max_keys = device->info->keyboardInfo->maxSimultaneousKeys;
GameInputKeyState *last = SDL_stack_alloc(GameInputKeyState, max_keys);
GameInputKeyState *keys = SDL_stack_alloc(GameInputKeyState, max_keys);
if (!last || !keys) {
return;
}
uint32_t index_last = 0;
uint32_t index_keys = 0;
uint32_t num_last = last_reading->GetKeyState(max_keys, last);
uint32_t num_keys = reading->GetKeyState(max_keys, keys);
#ifdef DEBUG_KEYS
SDL_Log("Timestamp: %llu", timestamp);
DumpKeys("Last keys:", last, num_last);
DumpKeys("New keys:", keys, num_keys);
#endif
while (index_last < num_last || index_keys < num_keys) {
if (index_last < num_last && index_keys < num_keys) {
if (last[index_last].scanCode == keys[index_keys].scanCode) {
// No change
++index_last;
++index_keys;
} else {
// This key was released
SDL_SendKeyboardKey(timestamp, keyboardID, last[index_last].scanCode, GetScancodeFromKeyState(&last[index_last]), false);
++index_last;
}
} else if (index_last < num_last) {
// This key was released
SDL_SendKeyboardKey(timestamp, keyboardID, last[index_last].scanCode, GetScancodeFromKeyState(&last[index_last]), false);
++index_last;
} else {
// This key was pressed
SDL_SendKeyboardKey(timestamp, keyboardID, keys[index_keys].scanCode, GetScancodeFromKeyState(&keys[index_keys]), true);
++index_keys;
}
}
}
void WIN_UpdateGameInput(SDL_VideoDevice *_this)
{
WIN_GameInputData *data = _this->internal->gameinput_context;
SDL_LockMutex(data->lock);
{
// Key events and relative mouse motion both go to the window with keyboard focus
SDL_Window *window = SDL_GetKeyboardFocus();
for (int i = 0; i < data->num_devices; ++i) {
GAMEINPUT_Device *device = data->devices[i];
IGameInputReading *reading;
if (!device->registered) {
if (device->info->supportedInput & GameInputKindMouse) {
SDL_AddMouse(device->instance_id, device->name, true);
}
if (device->info->supportedInput & GameInputKindKeyboard) {
SDL_AddKeyboard(device->instance_id, device->name, true);
}
device->registered = true;
}
if (device->delete_requested) {
GAMEINPUT_InternalRemoveByIndex(data, i--);
continue;
}
if (!(device->info->supportedInput & data->enabled_input)) {
continue;
}
if (!window) {
continue;
}
if (data->enabled_input & GameInputKindMouse) {
if (device->last_mouse_reading) {
HRESULT hr;
while (SUCCEEDED(hr = data->pGameInput->GetNextReading(device->last_mouse_reading, GameInputKindMouse, device->pDevice, &reading))) {
GAMEINPUT_HandleMouseDelta(data, window, device, device->last_mouse_reading, reading);
device->last_mouse_reading->Release();
device->last_mouse_reading = reading;
}
if (hr != GAMEINPUT_E_READING_NOT_FOUND) {
if (SUCCEEDED(data->pGameInput->GetCurrentReading(GameInputKindMouse, device->pDevice, &reading))) {
GAMEINPUT_HandleMouseDelta(data, window, device, device->last_mouse_reading, reading);
device->last_mouse_reading->Release();
device->last_mouse_reading = reading;
}
}
} else {
if (SUCCEEDED(data->pGameInput->GetCurrentReading(GameInputKindMouse, device->pDevice, &reading))) {
GAMEINPUT_InitialMouseReading(data, window, device, reading);
device->last_mouse_reading = reading;
}
}
}
if (data->enabled_input & GameInputKindKeyboard) {
if (window->text_input_active) {
// Reset raw input while text input is active
if (device->last_keyboard_reading) {
device->last_keyboard_reading->Release();
device->last_keyboard_reading = NULL;
}
} else {
if (device->last_keyboard_reading) {
HRESULT hr;
while (SUCCEEDED(hr = data->pGameInput->GetNextReading(device->last_keyboard_reading, GameInputKindKeyboard, device->pDevice, &reading))) {
GAMEINPUT_HandleKeyboardDelta(data, window, device, device->last_keyboard_reading, reading);
device->last_keyboard_reading->Release();
device->last_keyboard_reading = reading;
}
if (hr != GAMEINPUT_E_READING_NOT_FOUND) {
if (SUCCEEDED(data->pGameInput->GetCurrentReading(GameInputKindKeyboard, device->pDevice, &reading))) {
GAMEINPUT_HandleKeyboardDelta(data, window, device, device->last_keyboard_reading, reading);
device->last_keyboard_reading->Release();
device->last_keyboard_reading = reading;
}
}
} else {
if (SUCCEEDED(data->pGameInput->GetCurrentReading(GameInputKindKeyboard, device->pDevice, &reading))) {
GAMEINPUT_InitialKeyboardReading(data, window, device, reading);
device->last_keyboard_reading = reading;
}
}
}
}
}
}
SDL_UnlockMutex(data->lock);
}
bool WIN_UpdateGameInputEnabled(SDL_VideoDevice *_this)
{
WIN_GameInputData *data = _this->internal->gameinput_context;
bool raw_mouse_enabled = _this->internal->raw_mouse_enabled;
bool raw_keyboard_enabled = _this->internal->raw_keyboard_enabled;
SDL_LockMutex(data->lock);
{
data->enabled_input = (raw_mouse_enabled ? GameInputKindMouse : GameInputKindUnknown) |
(raw_keyboard_enabled ? GameInputKindKeyboard : GameInputKindUnknown);
// Reset input if not enabled
for (int i = 0; i < data->num_devices; ++i) {
GAMEINPUT_Device *device = data->devices[i];
if (device->last_mouse_reading && !raw_mouse_enabled) {
device->last_mouse_reading->Release();
device->last_mouse_reading = NULL;
}
if (device->last_keyboard_reading && !raw_keyboard_enabled) {
device->last_keyboard_reading->Release();
device->last_keyboard_reading = NULL;
}
}
}
SDL_UnlockMutex(data->lock);
return true;
}
void WIN_QuitGameInput(SDL_VideoDevice *_this)
{
WIN_GameInputData *data = _this->internal->gameinput_context;
if (!data) {
return;
}
if (data->pGameInput) {
// free the callback
if (data->gameinput_callback_token) {
#if GAMEINPUT_API_VERSION >= 1
data->pGameInput->UnregisterCallback(data->gameinput_callback_token);
#else
data->pGameInput->UnregisterCallback(data->gameinput_callback_token, 10000);
#endif
data->gameinput_callback_token = 0;
}
// free the list
while (data->num_devices > 0) {
GAMEINPUT_InternalRemoveByIndex(data, 0);
}
data->pGameInput->Release();
data->pGameInput = NULL;
}
if (data->pGameInput) {
SDL_QuitGameInput();
data->pGameInput = NULL;
}
if (data->lock) {
SDL_DestroyMutex(data->lock);
data->lock = NULL;
}
SDL_free(data);
_this->internal->gameinput_context = NULL;
}
#else // !HAVE_GAMEINPUT_H
bool WIN_InitGameInput(SDL_VideoDevice *_this)
{
return SDL_Unsupported();
}
bool WIN_UpdateGameInputEnabled(SDL_VideoDevice *_this)
{
return SDL_Unsupported();
}
void WIN_UpdateGameInput(SDL_VideoDevice *_this)
{
return;
}
void WIN_QuitGameInput(SDL_VideoDevice *_this)
{
return;
}
#endif // HAVE_GAMEINPUT_H
@@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
typedef struct WIN_GameInputData WIN_GameInputData;
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
extern bool WIN_InitGameInput(SDL_VideoDevice *_this);
extern bool WIN_UpdateGameInputEnabled(SDL_VideoDevice *_this);
extern void WIN_UpdateGameInput(SDL_VideoDevice *_this);
extern void WIN_QuitGameInput(SDL_VideoDevice *_this);
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,40 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_windowskeyboard_h_
#define SDL_windowskeyboard_h_
extern void WIN_InitKeyboard(SDL_VideoDevice *_this);
extern void WIN_UpdateKeymap(bool send_event);
extern void WIN_QuitKeyboard(SDL_VideoDevice *_this);
extern void WIN_ResetDeadKeys(void);
extern bool WIN_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
extern bool WIN_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window);
extern bool WIN_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window);
extern bool WIN_ClearComposition(SDL_VideoDevice *_this, SDL_Window *window);
extern bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, struct SDL_VideoData *videodata);
extern void WIN_UpdateIMECandidates(SDL_VideoDevice *_this);
#endif // SDL_windowskeyboard_h_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,27 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_WINDOWS
extern bool WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID);
#endif // SDL_VIDEO_DRIVER_WINDOWS
@@ -0,0 +1,927 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#include "SDL_windowsvideo.h"
#include "../../events/SDL_displayevents_c.h"
#ifdef HAVE_DXGI1_6_H
#define COBJMACROS
#include <dxgi1_6.h>
#endif
// Windows CE compatibility
#ifndef CDS_FULLSCREEN
#define CDS_FULLSCREEN 0
#endif
// #define DEBUG_MODES
// #define HIGHDPI_DEBUG_VERBOSE
static void WIN_UpdateDisplayMode(SDL_VideoDevice *_this, LPCWSTR deviceName, DWORD index, SDL_DisplayMode *mode)
{
SDL_DisplayModeData *data = (SDL_DisplayModeData *)mode->internal;
HDC hdc;
data->DeviceMode.dmFields = (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS);
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition): No simple way to extract the assignment
if (index == ENUM_CURRENT_SETTINGS && (hdc = CreateDCW(deviceName, NULL, NULL, NULL)) != NULL) {
char bmi_data[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)];
LPBITMAPINFO bmi;
HBITMAP hbm;
SDL_zeroa(bmi_data);
bmi = (LPBITMAPINFO)bmi_data;
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
hbm = CreateCompatibleBitmap(hdc, 1, 1);
GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS);
GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS);
DeleteObject(hbm);
DeleteDC(hdc);
if (bmi->bmiHeader.biCompression == BI_BITFIELDS) {
switch (*(Uint32 *)bmi->bmiColors) {
case 0x00FF0000:
mode->format = SDL_PIXELFORMAT_XRGB8888;
break;
case 0x000000FF:
mode->format = SDL_PIXELFORMAT_XBGR8888;
break;
case 0xF800:
mode->format = SDL_PIXELFORMAT_RGB565;
break;
case 0x7C00:
mode->format = SDL_PIXELFORMAT_XRGB1555;
break;
}
} else if (bmi->bmiHeader.biCompression == BI_RGB) {
if (bmi->bmiHeader.biBitCount == 24) {
mode->format = SDL_PIXELFORMAT_RGB24;
} else if (bmi->bmiHeader.biBitCount == 8) {
mode->format = SDL_PIXELFORMAT_INDEX8;
} else if (bmi->bmiHeader.biBitCount == 4) {
mode->format = SDL_PIXELFORMAT_INDEX4LSB;
}
}
} else if (mode->format == SDL_PIXELFORMAT_UNKNOWN) {
// FIXME: Can we tell what this will be?
if ((data->DeviceMode.dmFields & DM_BITSPERPEL) == DM_BITSPERPEL) {
switch (data->DeviceMode.dmBitsPerPel) {
case 32:
mode->format = SDL_PIXELFORMAT_XRGB8888;
break;
case 24:
mode->format = SDL_PIXELFORMAT_RGB24;
break;
case 16:
mode->format = SDL_PIXELFORMAT_RGB565;
break;
case 15:
mode->format = SDL_PIXELFORMAT_XRGB1555;
break;
case 8:
mode->format = SDL_PIXELFORMAT_INDEX8;
break;
case 4:
mode->format = SDL_PIXELFORMAT_INDEX4LSB;
break;
}
}
}
}
static void *WIN_GetDXGIOutput(SDL_VideoDevice *_this, const WCHAR *DeviceName)
{
void *result = NULL;
#ifdef HAVE_DXGI_H
const SDL_VideoData *videodata = (const SDL_VideoData *)_this->internal;
int nAdapter, nOutput;
IDXGIAdapter *pDXGIAdapter;
IDXGIOutput *pDXGIOutput;
if (!videodata->pDXGIFactory) {
return NULL;
}
nAdapter = 0;
while (!result && SUCCEEDED(IDXGIFactory_EnumAdapters(videodata->pDXGIFactory, nAdapter, &pDXGIAdapter))) {
nOutput = 0;
while (!result && SUCCEEDED(IDXGIAdapter_EnumOutputs(pDXGIAdapter, nOutput, &pDXGIOutput))) {
DXGI_OUTPUT_DESC outputDesc;
if (SUCCEEDED(IDXGIOutput_GetDesc(pDXGIOutput, &outputDesc))) {
if (SDL_wcscmp(outputDesc.DeviceName, DeviceName) == 0) {
result = pDXGIOutput;
}
}
if (pDXGIOutput != result) {
IDXGIOutput_Release(pDXGIOutput);
}
nOutput++;
}
IDXGIAdapter_Release(pDXGIAdapter);
nAdapter++;
}
#endif
return result;
}
static void WIN_ReleaseDXGIOutput(void *dxgi_output)
{
#ifdef HAVE_DXGI_H
IDXGIOutput *pDXGIOutput = (IDXGIOutput *)dxgi_output;
if (pDXGIOutput) {
IDXGIOutput_Release(pDXGIOutput);
}
#endif
}
static SDL_DisplayOrientation WIN_GetNaturalOrientation(DEVMODEW *mode)
{
int width = mode->dmPelsWidth;
int height = mode->dmPelsHeight;
// Use unrotated width/height to guess orientation
if (mode->dmDisplayOrientation == DMDO_90 || mode->dmDisplayOrientation == DMDO_270) {
int temp = width;
width = height;
height = temp;
}
if (width >= height) {
return SDL_ORIENTATION_LANDSCAPE;
} else {
return SDL_ORIENTATION_PORTRAIT;
}
}
static SDL_DisplayOrientation WIN_GetDisplayOrientation(DEVMODEW *mode)
{
if (WIN_GetNaturalOrientation(mode) == SDL_ORIENTATION_LANDSCAPE) {
switch (mode->dmDisplayOrientation) {
case DMDO_DEFAULT:
return SDL_ORIENTATION_LANDSCAPE;
case DMDO_90:
return SDL_ORIENTATION_PORTRAIT;
case DMDO_180:
return SDL_ORIENTATION_LANDSCAPE_FLIPPED;
case DMDO_270:
return SDL_ORIENTATION_PORTRAIT_FLIPPED;
default:
return SDL_ORIENTATION_UNKNOWN;
}
} else {
switch (mode->dmDisplayOrientation) {
case DMDO_DEFAULT:
return SDL_ORIENTATION_PORTRAIT;
case DMDO_90:
return SDL_ORIENTATION_LANDSCAPE_FLIPPED;
case DMDO_180:
return SDL_ORIENTATION_PORTRAIT_FLIPPED;
case DMDO_270:
return SDL_ORIENTATION_LANDSCAPE;
default:
return SDL_ORIENTATION_UNKNOWN;
}
}
}
static void WIN_GetRefreshRate(void *dxgi_output, DEVMODEW *mode, int *numerator, int *denominator)
{
// We're not currently using DXGI to query display modes, so fake NTSC timings
switch (mode->dmDisplayFrequency) {
case 119:
case 59:
case 29:
*numerator = (mode->dmDisplayFrequency + 1) * 1000;
*denominator = 1001;
break;
default:
*numerator = mode->dmDisplayFrequency;
*denominator = 1;
break;
}
#ifdef HAVE_DXGI_H
if (dxgi_output) {
IDXGIOutput *pDXGIOutput = (IDXGIOutput *)dxgi_output;
DXGI_MODE_DESC modeToMatch;
DXGI_MODE_DESC closestMatch;
SDL_zero(modeToMatch);
modeToMatch.Width = mode->dmPelsWidth;
modeToMatch.Height = mode->dmPelsHeight;
modeToMatch.RefreshRate.Numerator = *numerator;
modeToMatch.RefreshRate.Denominator = *denominator;
modeToMatch.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
if (SUCCEEDED(IDXGIOutput_FindClosestMatchingMode(pDXGIOutput, &modeToMatch, &closestMatch, NULL))) {
*numerator = closestMatch.RefreshRate.Numerator;
*denominator = closestMatch.RefreshRate.Denominator;
}
}
#endif // HAVE_DXGI_H
}
static float WIN_GetContentScale(SDL_VideoDevice *_this, HMONITOR hMonitor)
{
const SDL_VideoData *videodata = (const SDL_VideoData *)_this->internal;
int dpi = 0;
if (videodata->GetDpiForMonitor) {
UINT hdpi_uint, vdpi_uint;
if (videodata->GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &hdpi_uint, &vdpi_uint) == S_OK) {
dpi = (int)hdpi_uint;
}
}
if (dpi == 0) {
// Window 8.0 and below: same DPI for all monitors
HDC hdc = GetDC(NULL);
if (hdc) {
dpi = GetDeviceCaps(hdc, LOGPIXELSX);
ReleaseDC(NULL, hdc);
}
}
if (dpi == 0) {
// Safe default
dpi = USER_DEFAULT_SCREEN_DPI;
}
return dpi / (float)USER_DEFAULT_SCREEN_DPI;
}
static bool WIN_GetDisplayMode(SDL_VideoDevice *_this, void *dxgi_output, HMONITOR hMonitor, LPCWSTR deviceName, DWORD index, SDL_DisplayMode *mode, SDL_DisplayOrientation *natural_orientation, SDL_DisplayOrientation *current_orientation)
{
SDL_DisplayModeData *data;
DEVMODEW devmode;
devmode.dmSize = sizeof(devmode);
devmode.dmDriverExtra = 0;
if (!EnumDisplaySettingsW(deviceName, index, &devmode)) {
return false;
}
data = (SDL_DisplayModeData *)SDL_malloc(sizeof(*data));
if (!data) {
return false;
}
SDL_zerop(mode);
mode->internal = data;
data->DeviceMode = devmode;
mode->format = SDL_PIXELFORMAT_UNKNOWN;
mode->w = data->DeviceMode.dmPelsWidth;
mode->h = data->DeviceMode.dmPelsHeight;
WIN_GetRefreshRate(dxgi_output, &data->DeviceMode, &mode->refresh_rate_numerator, &mode->refresh_rate_denominator);
// Fill in the mode information
WIN_UpdateDisplayMode(_this, deviceName, index, mode);
if (natural_orientation) {
*natural_orientation = WIN_GetNaturalOrientation(&devmode);
}
if (current_orientation) {
*current_orientation = WIN_GetDisplayOrientation(&devmode);
}
return true;
}
static char *WIN_GetDisplayNameVista(SDL_VideoData *videodata, const WCHAR *deviceName)
{
DISPLAYCONFIG_PATH_INFO *paths = NULL;
DISPLAYCONFIG_MODE_INFO *modes = NULL;
char *result = NULL;
UINT32 pathCount = 0;
UINT32 modeCount = 0;
UINT32 i;
LONG rc;
if (!videodata->GetDisplayConfigBufferSizes || !videodata->QueryDisplayConfig || !videodata->DisplayConfigGetDeviceInfo) {
return NULL;
}
do {
rc = videodata->GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount);
if (rc != ERROR_SUCCESS) {
goto WIN_GetDisplayNameVista_failed;
}
SDL_free(paths);
SDL_free(modes);
paths = (DISPLAYCONFIG_PATH_INFO *)SDL_malloc(sizeof(DISPLAYCONFIG_PATH_INFO) * pathCount);
modes = (DISPLAYCONFIG_MODE_INFO *)SDL_malloc(sizeof(DISPLAYCONFIG_MODE_INFO) * modeCount);
if ((!paths) || (!modes)) {
goto WIN_GetDisplayNameVista_failed;
}
rc = videodata->QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths, &modeCount, modes, 0);
} while (rc == ERROR_INSUFFICIENT_BUFFER);
if (rc == ERROR_SUCCESS) {
for (i = 0; i < pathCount; i++) {
DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName;
DISPLAYCONFIG_TARGET_DEVICE_NAME targetName;
SDL_zero(sourceName);
sourceName.header.adapterId = paths[i].targetInfo.adapterId;
sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
sourceName.header.size = sizeof(sourceName);
sourceName.header.id = paths[i].sourceInfo.id;
rc = videodata->DisplayConfigGetDeviceInfo(&sourceName.header);
if (rc != ERROR_SUCCESS) {
break;
} else if (SDL_wcscmp(deviceName, sourceName.viewGdiDeviceName) != 0) {
continue;
}
SDL_zero(targetName);
targetName.header.adapterId = paths[i].targetInfo.adapterId;
targetName.header.id = paths[i].targetInfo.id;
targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
targetName.header.size = sizeof(targetName);
rc = videodata->DisplayConfigGetDeviceInfo(&targetName.header);
if (rc == ERROR_SUCCESS) {
result = WIN_StringToUTF8W(targetName.monitorFriendlyDeviceName);
/* if we got an empty string, treat it as failure so we'll fallback
to getting the generic name. */
if (result && (*result == '\0')) {
SDL_free(result);
result = NULL;
}
}
break;
}
}
SDL_free(paths);
SDL_free(modes);
return result;
WIN_GetDisplayNameVista_failed:
SDL_free(result);
SDL_free(paths);
SDL_free(modes);
return NULL;
}
#ifdef HAVE_DXGI1_6_H
static bool WIN_GetMonitorDESC1(HMONITOR hMonitor, DXGI_OUTPUT_DESC1 *desc)
{
typedef HRESULT (WINAPI * PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
PFN_CREATE_DXGI_FACTORY CreateDXGIFactoryFunc = NULL;
SDL_SharedObject *hDXGIMod = NULL;
bool found = false;
hDXGIMod = SDL_LoadObject("dxgi.dll");
if (hDXGIMod) {
CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY)SDL_LoadFunction(hDXGIMod, "CreateDXGIFactory1");
}
if (CreateDXGIFactoryFunc) {
static const GUID SDL_IID_IDXGIFactory1 = { 0x770aae78, 0xf26f, 0x4dba, { 0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87 } };
static const GUID SDL_IID_IDXGIOutput6 = { 0x068346e8, 0xaaec, 0x4b84, { 0xad, 0xd7, 0x13, 0x7f, 0x51, 0x3f, 0x77, 0xa1 } };
IDXGIFactory1 *dxgiFactory;
if (SUCCEEDED(CreateDXGIFactoryFunc(&SDL_IID_IDXGIFactory1, (void **)&dxgiFactory))) {
IDXGIAdapter1 *dxgiAdapter;
UINT adapter = 0;
while (!found && SUCCEEDED(IDXGIFactory1_EnumAdapters1(dxgiFactory, adapter, &dxgiAdapter))) {
IDXGIOutput *dxgiOutput;
UINT output = 0;
while (!found && SUCCEEDED(IDXGIAdapter1_EnumOutputs(dxgiAdapter, output, &dxgiOutput))) {
IDXGIOutput6 *dxgiOutput6;
if (SUCCEEDED(IDXGIOutput_QueryInterface(dxgiOutput, &SDL_IID_IDXGIOutput6, (void **)&dxgiOutput6))) {
if (SUCCEEDED(IDXGIOutput6_GetDesc1(dxgiOutput6, desc))) {
if (desc->Monitor == hMonitor) {
found = true;
}
}
IDXGIOutput6_Release(dxgiOutput6);
}
IDXGIOutput_Release(dxgiOutput);
++output;
}
IDXGIAdapter1_Release(dxgiAdapter);
++adapter;
}
IDXGIFactory2_Release(dxgiFactory);
}
}
if (hDXGIMod) {
SDL_UnloadObject(hDXGIMod);
}
return found;
}
static bool WIN_GetMonitorPathInfo(SDL_VideoData *videodata, HMONITOR hMonitor, DISPLAYCONFIG_PATH_INFO *path_info)
{
LONG result;
MONITORINFOEXW view_info;
UINT32 i;
UINT32 num_path_array_elements = 0;
UINT32 num_mode_info_array_elements = 0;
DISPLAYCONFIG_PATH_INFO *path_infos = NULL, *new_path_infos;
DISPLAYCONFIG_MODE_INFO *mode_infos = NULL, *new_mode_infos;
bool found = false;
if (!videodata->GetDisplayConfigBufferSizes || !videodata->QueryDisplayConfig || !videodata->DisplayConfigGetDeviceInfo) {
return false;
}
SDL_zero(view_info);
view_info.cbSize = sizeof(view_info);
if (!GetMonitorInfoW(hMonitor, (MONITORINFO *)&view_info)) {
goto done;
}
do {
if (videodata->GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &num_path_array_elements, &num_mode_info_array_elements) != ERROR_SUCCESS) {
SDL_free(path_infos);
SDL_free(mode_infos);
return false;
}
new_path_infos = (DISPLAYCONFIG_PATH_INFO *)SDL_realloc(path_infos, num_path_array_elements * sizeof(*path_infos));
if (!new_path_infos) {
goto done;
}
path_infos = new_path_infos;
new_mode_infos = (DISPLAYCONFIG_MODE_INFO *)SDL_realloc(mode_infos, num_mode_info_array_elements * sizeof(*mode_infos));
if (!new_mode_infos) {
goto done;
}
mode_infos = new_mode_infos;
result = videodata->QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &num_path_array_elements, path_infos, &num_mode_info_array_elements, mode_infos, NULL);
} while (result == ERROR_INSUFFICIENT_BUFFER);
if (result == ERROR_SUCCESS) {
for (i = 0; i < num_path_array_elements; ++i) {
DISPLAYCONFIG_SOURCE_DEVICE_NAME device_name;
SDL_zero(device_name);
device_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
device_name.header.size = sizeof(device_name);
device_name.header.adapterId = path_infos[i].sourceInfo.adapterId;
device_name.header.id = path_infos[i].sourceInfo.id;
if (videodata->DisplayConfigGetDeviceInfo(&device_name.header) == ERROR_SUCCESS) {
if (SDL_wcscmp(view_info.szDevice, device_name.viewGdiDeviceName) == 0) {
SDL_copyp(path_info, &path_infos[i]);
found = true;
break;
}
}
}
}
done:
SDL_free(path_infos);
SDL_free(mode_infos);
return found;
}
static float WIN_GetSDRWhitePoint(SDL_VideoDevice *_this, HMONITOR hMonitor)
{
DISPLAYCONFIG_PATH_INFO path_info;
SDL_VideoData *videodata = _this->internal;
float SDR_white_level = 1.0f;
if (WIN_GetMonitorPathInfo(videodata, hMonitor, &path_info)) {
/* workarounds for https://github.com/libsdl-org/SDL/issues/11193 */
struct SDL_DISPLAYCONFIG_SDR_WHITE_LEVEL {
DISPLAYCONFIG_DEVICE_INFO_HEADER header;
ULONG SDRWhiteLevel;
} white_level;
#define DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL 11
SDL_zero(white_level);
white_level.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
white_level.header.size = sizeof(white_level);
white_level.header.adapterId = path_info.targetInfo.adapterId;
white_level.header.id = path_info.targetInfo.id;
// WIN_GetMonitorPathInfo() succeeded: DisplayConfigGetDeviceInfo is not NULL
if (videodata->DisplayConfigGetDeviceInfo(&white_level.header) == ERROR_SUCCESS &&
white_level.SDRWhiteLevel > 0) {
SDR_white_level = (white_level.SDRWhiteLevel / 1000.0f);
}
}
return SDR_white_level;
}
static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_HDROutputProperties *HDR)
{
DXGI_OUTPUT_DESC1 desc;
SDL_zerop(HDR);
if (WIN_GetMonitorDESC1(hMonitor, &desc)) {
if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
HDR->SDR_white_level = WIN_GetSDRWhitePoint(_this, hMonitor);
HDR->HDR_headroom = (desc.MaxLuminance / 80.0f) / HDR->SDR_white_level;
}
}
}
#endif // HAVE_DXGI1_6_H
static void WIN_AddDisplay(SDL_VideoDevice *_this, HMONITOR hMonitor, const MONITORINFOEXW *info, int *display_index)
{
int i, index = *display_index;
SDL_VideoDisplay display;
SDL_DisplayData *displaydata;
void *dxgi_output = NULL;
SDL_DisplayMode mode;
SDL_DisplayOrientation natural_orientation;
SDL_DisplayOrientation current_orientation;
float content_scale = WIN_GetContentScale(_this, hMonitor);
#ifdef DEBUG_MODES
SDL_Log("Display: %s", WIN_StringToUTF8W(info->szDevice));
#endif
dxgi_output = WIN_GetDXGIOutput(_this, info->szDevice);
bool found = WIN_GetDisplayMode(_this, dxgi_output, hMonitor, info->szDevice, ENUM_CURRENT_SETTINGS, &mode, &natural_orientation, &current_orientation);
WIN_ReleaseDXGIOutput(dxgi_output);
if (!found) {
return;
}
// Prevent adding duplicate displays. Do this after we know the display is
// ready to be added to allow any displays that we can't fully query to be
// removed
for (i = 0; i < _this->num_displays; ++i) {
SDL_DisplayData *internal = _this->displays[i]->internal;
if (SDL_wcscmp(internal->DeviceName, info->szDevice) == 0) {
bool moved = (index != i);
bool changed_bounds = false;
if (internal->state != DisplayRemoved) {
// We've already enumerated this display, don't move it
return;
}
if (index >= _this->num_displays) {
// This should never happen due to the check above, but just in case...
return;
}
if (moved) {
SDL_VideoDisplay *tmp;
tmp = _this->displays[index];
_this->displays[index] = _this->displays[i];
_this->displays[i] = tmp;
i = index;
}
internal->MonitorHandle = hMonitor;
internal->state = DisplayUnchanged;
if (!_this->setting_display_mode) {
SDL_VideoDisplay *existing_display = _this->displays[i];
SDL_Rect bounds;
SDL_ResetFullscreenDisplayModes(existing_display);
SDL_SetDesktopDisplayMode(existing_display, &mode);
if (WIN_GetDisplayBounds(_this, existing_display, &bounds) &&
SDL_memcmp(&internal->bounds, &bounds, sizeof(bounds)) != 0) {
changed_bounds = true;
SDL_copyp(&internal->bounds, &bounds);
}
if (moved || changed_bounds) {
SDL_SendDisplayEvent(existing_display, SDL_EVENT_DISPLAY_MOVED, 0, 0);
}
SDL_SendDisplayEvent(existing_display, SDL_EVENT_DISPLAY_ORIENTATION, current_orientation, 0);
SDL_SetDisplayContentScale(existing_display, content_scale);
#ifdef HAVE_DXGI1_6_H
SDL_HDROutputProperties HDR;
WIN_GetHDRProperties(_this, hMonitor, &HDR);
SDL_SetDisplayHDRProperties(existing_display, &HDR);
#endif
}
goto done;
}
}
displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata));
if (!displaydata) {
return;
}
SDL_memcpy(displaydata->DeviceName, info->szDevice, sizeof(displaydata->DeviceName));
displaydata->MonitorHandle = hMonitor;
displaydata->state = DisplayAdded;
SDL_zero(display);
display.name = WIN_GetDisplayNameVista(_this->internal, info->szDevice);
if (!display.name) {
DISPLAY_DEVICEW device;
SDL_zero(device);
device.cb = sizeof(device);
if (EnumDisplayDevicesW(info->szDevice, 0, &device, 0)) {
display.name = WIN_StringToUTF8W(device.DeviceString);
}
}
display.desktop_mode = mode;
display.natural_orientation = natural_orientation;
display.current_orientation = current_orientation;
display.content_scale = content_scale;
display.device = _this;
display.internal = displaydata;
WIN_GetDisplayBounds(_this, &display, &displaydata->bounds);
#ifdef HAVE_DXGI1_6_H
WIN_GetHDRProperties(_this, hMonitor, &display.HDR);
#endif
SDL_AddVideoDisplay(&display, false);
SDL_free(display.name);
done:
*display_index += 1;
}
typedef struct _WIN_AddDisplaysData
{
SDL_VideoDevice *video_device;
int display_index;
bool want_primary;
} WIN_AddDisplaysData;
static BOOL CALLBACK WIN_AddDisplaysCallback(HMONITOR hMonitor,
HDC hdcMonitor,
LPRECT lprcMonitor,
LPARAM dwData)
{
WIN_AddDisplaysData *data = (WIN_AddDisplaysData *)dwData;
MONITORINFOEXW info;
SDL_zero(info);
info.cbSize = sizeof(info);
if (GetMonitorInfoW(hMonitor, (LPMONITORINFO)&info) != 0) {
const bool is_primary = ((info.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY);
if (is_primary == data->want_primary) {
WIN_AddDisplay(data->video_device, hMonitor, &info, &data->display_index);
}
}
// continue enumeration
return TRUE;
}
static void WIN_AddDisplays(SDL_VideoDevice *_this)
{
WIN_AddDisplaysData callback_data;
callback_data.video_device = _this;
callback_data.display_index = 0;
callback_data.want_primary = true;
EnumDisplayMonitors(NULL, NULL, WIN_AddDisplaysCallback, (LPARAM)&callback_data);
callback_data.want_primary = false;
EnumDisplayMonitors(NULL, NULL, WIN_AddDisplaysCallback, (LPARAM)&callback_data);
}
bool WIN_InitModes(SDL_VideoDevice *_this)
{
WIN_AddDisplays(_this);
if (_this->num_displays == 0) {
return SDL_SetError("No displays available");
}
return true;
}
bool WIN_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
{
const SDL_DisplayData *data = display->internal;
MONITORINFO minfo;
BOOL rc;
SDL_zero(minfo);
minfo.cbSize = sizeof(MONITORINFO);
rc = GetMonitorInfo(data->MonitorHandle, &minfo);
if (!rc) {
return SDL_SetError("Couldn't find monitor data");
}
rect->x = minfo.rcMonitor.left;
rect->y = minfo.rcMonitor.top;
rect->w = minfo.rcMonitor.right - minfo.rcMonitor.left;
rect->h = minfo.rcMonitor.bottom - minfo.rcMonitor.top;
return true;
}
bool WIN_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
{
const SDL_DisplayData *data = display->internal;
MONITORINFO minfo;
BOOL rc;
SDL_zero(minfo);
minfo.cbSize = sizeof(MONITORINFO);
rc = GetMonitorInfo(data->MonitorHandle, &minfo);
if (!rc) {
return SDL_SetError("Couldn't find monitor data");
}
rect->x = minfo.rcWork.left;
rect->y = minfo.rcWork.top;
rect->w = minfo.rcWork.right - minfo.rcWork.left;
rect->h = minfo.rcWork.bottom - minfo.rcWork.top;
return true;
}
bool WIN_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
{
SDL_DisplayData *data = display->internal;
void *dxgi_output;
DWORD i;
SDL_DisplayMode mode;
dxgi_output = WIN_GetDXGIOutput(_this, data->DeviceName);
for (i = 0;; ++i) {
if (!WIN_GetDisplayMode(_this, dxgi_output, data->MonitorHandle, data->DeviceName, i, &mode, NULL, NULL)) {
break;
}
if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) {
// We don't support palettized modes now
SDL_free(mode.internal);
continue;
}
if (mode.format != SDL_PIXELFORMAT_UNKNOWN) {
if (!SDL_AddFullscreenDisplayMode(display, &mode)) {
SDL_free(mode.internal);
}
} else {
SDL_free(mode.internal);
}
}
WIN_ReleaseDXGIOutput(dxgi_output);
return true;
}
#ifdef DEBUG_MODES
static void WIN_LogMonitor(SDL_VideoDevice *_this, HMONITOR mon)
{
const SDL_VideoData *vid_data = (const SDL_VideoData *)_this->internal;
MONITORINFOEX minfo;
UINT xdpi = 0, ydpi = 0;
char *name_utf8;
if (vid_data->GetDpiForMonitor) {
vid_data->GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
}
SDL_zero(minfo);
minfo.cbSize = sizeof(minfo);
GetMonitorInfo(mon, (LPMONITORINFO)&minfo);
name_utf8 = WIN_StringToUTF8(minfo.szDevice);
SDL_Log("WIN_LogMonitor: monitor \"%s\": dpi: %d windows screen coordinates: %d, %d, %dx%d",
name_utf8,
xdpi,
minfo.rcMonitor.left,
minfo.rcMonitor.top,
minfo.rcMonitor.right - minfo.rcMonitor.left,
minfo.rcMonitor.bottom - minfo.rcMonitor.top);
SDL_free(name_utf8);
}
#endif
bool WIN_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
{
SDL_DisplayData *displaydata = display->internal;
SDL_DisplayModeData *data = (SDL_DisplayModeData *)mode->internal;
LONG status;
#ifdef DEBUG_MODES
SDL_Log("WIN_SetDisplayMode: monitor state before mode change:");
WIN_LogMonitor(_this, displaydata->MonitorHandle);
#endif
/* High-DPI notes:
- ChangeDisplaySettingsEx always takes pixels.
- e.g. if the display is set to 2880x1800 with 200% scaling in Display Settings
- calling ChangeDisplaySettingsEx with a dmPelsWidth/Height other than 2880x1800 will
change the monitor DPI to 96. (100% scaling)
- calling ChangeDisplaySettingsEx with a dmPelsWidth/Height of 2880x1800 (or a NULL DEVMODE*) will
reset the monitor DPI to 192. (200% scaling)
NOTE: these are temporary changes in DPI, not modifications to the Control Panel setting. */
if (mode->internal == display->desktop_mode.internal) {
#ifdef DEBUG_MODES
SDL_Log("WIN_SetDisplayMode: resetting to original resolution");
#endif
status = ChangeDisplaySettingsExW(displaydata->DeviceName, NULL, NULL, CDS_FULLSCREEN, NULL);
} else {
#ifdef DEBUG_MODES
SDL_Log("WIN_SetDisplayMode: changing to %dx%d pixels", data->DeviceMode.dmPelsWidth, data->DeviceMode.dmPelsHeight);
#endif
status = ChangeDisplaySettingsExW(displaydata->DeviceName, &data->DeviceMode, NULL, CDS_FULLSCREEN, NULL);
}
if (status != DISP_CHANGE_SUCCESSFUL) {
const char *reason = "Unknown reason";
switch (status) {
case DISP_CHANGE_BADFLAGS:
reason = "DISP_CHANGE_BADFLAGS";
break;
case DISP_CHANGE_BADMODE:
reason = "DISP_CHANGE_BADMODE";
break;
case DISP_CHANGE_BADPARAM:
reason = "DISP_CHANGE_BADPARAM";
break;
case DISP_CHANGE_FAILED:
reason = "DISP_CHANGE_FAILED";
break;
}
return SDL_SetError("ChangeDisplaySettingsEx() failed: %s", reason);
}
#ifdef DEBUG_MODES
SDL_Log("WIN_SetDisplayMode: monitor state after mode change:");
WIN_LogMonitor(_this, displaydata->MonitorHandle);
#endif
EnumDisplaySettingsW(displaydata->DeviceName, ENUM_CURRENT_SETTINGS, &data->DeviceMode);
WIN_UpdateDisplayMode(_this, displaydata->DeviceName, ENUM_CURRENT_SETTINGS, mode);
return true;
}
void WIN_RefreshDisplays(SDL_VideoDevice *_this)
{
int i;
// Mark all displays as potentially invalid to detect
// entries that have actually been removed
for (i = 0; i < _this->num_displays; ++i) {
SDL_DisplayData *internal = _this->displays[i]->internal;
internal->state = DisplayRemoved;
}
// Enumerate displays to add any new ones and mark still
// connected entries as valid
WIN_AddDisplays(_this);
// Delete any entries still marked as invalid, iterate
// in reverse as each delete takes effect immediately
for (i = _this->num_displays - 1; i >= 0; --i) {
SDL_VideoDisplay *display = _this->displays[i];
SDL_DisplayData *internal = display->internal;
if (internal->state == DisplayRemoved) {
SDL_DelVideoDisplay(display->id, true);
}
}
// Send events for any newly added displays
for (i = 0; i < _this->num_displays; ++i) {
SDL_VideoDisplay *display = _this->displays[i];
SDL_DisplayData *internal = display->internal;
if (internal->state == DisplayAdded) {
SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_ADDED, 0, 0);
}
}
}
void WIN_QuitModes(SDL_VideoDevice *_this)
{
// All fullscreen windows should have restored modes by now
}
#endif // SDL_VIDEO_DRIVER_WINDOWS
@@ -0,0 +1,55 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_windowsmodes_h_
#define SDL_windowsmodes_h_
typedef enum
{
DisplayUnchanged,
DisplayAdded,
DisplayRemoved,
} WIN_DisplayState;
struct SDL_DisplayData
{
WCHAR DeviceName[32];
HMONITOR MonitorHandle;
WIN_DisplayState state;
SDL_Rect bounds;
};
struct SDL_DisplayModeData
{
DEVMODEW DeviceMode;
};
extern bool WIN_InitModes(SDL_VideoDevice *_this);
extern bool WIN_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
extern bool WIN_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
extern bool WIN_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
extern bool WIN_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
extern void WIN_RefreshDisplays(SDL_VideoDevice *_this);
extern void WIN_QuitModes(SDL_VideoDevice *_this);
#endif // SDL_windowsmodes_h_
@@ -0,0 +1,736 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#include "SDL_windowsvideo.h"
#include "SDL_windowsevents.h"
#include "SDL_windowsrawinput.h"
#include "../SDL_video_c.h"
#include "../../events/SDL_mouse_c.h"
#include "../../joystick/usb_ids.h"
#include "../../core/windows/SDL_windows.h" // for checking windows version
typedef struct CachedCursor
{
float scale;
HCURSOR cursor;
struct CachedCursor *next;
} CachedCursor;
struct SDL_CursorData
{
SDL_Surface *surface;
int hot_x;
int hot_y;
CachedCursor *cache;
HCURSOR cursor;
};
typedef struct
{
Uint64 xs[5];
Uint64 ys[5];
Sint64 residual[2];
Uint32 dpiscale;
Uint32 dpidenom;
int last_node;
bool enhanced;
bool dpiaware;
} WIN_MouseData;
DWORD SDL_last_warp_time = 0;
HCURSOR SDL_cursor = NULL;
static SDL_Cursor *SDL_blank_cursor = NULL;
static WIN_MouseData WIN_system_scale_data;
static SDL_Cursor *WIN_CreateCursorAndData(HCURSOR hcursor)
{
if (!hcursor) {
return NULL;
}
SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor));
if (!cursor) {
return NULL;
}
SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data));
if (!data) {
SDL_free(cursor);
return NULL;
}
data->cursor = hcursor;
cursor->internal = data;
return cursor;
}
static bool IsMonochromeSurface(SDL_Surface *surface)
{
int x, y;
Uint8 r, g, b, a;
SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888);
for (y = 0; y < surface->h; y++) {
for (x = 0; x < surface->w; x++) {
SDL_ReadSurfacePixel(surface, x, y, &r, &g, &b, &a);
// Black or white pixel.
if (!((r == 0x00 && g == 0x00 && b == 0x00) || (r == 0xff && g == 0xff && b == 0xff))) {
return false;
}
// Transparent or opaque pixel.
if (!(a == 0x00 || a == 0xff)) {
return false;
}
}
}
return true;
}
static HBITMAP CreateColorBitmap(SDL_Surface *surface)
{
HBITMAP bitmap;
BITMAPINFO bi;
void *pixels;
SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888);
SDL_zero(bi);
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = surface->w;
bi.bmiHeader.biHeight = -surface->h; // Invert height to make the top-down DIB.
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
bitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &pixels, NULL, 0);
if (!bitmap || !pixels) {
WIN_SetError("CreateDIBSection()");
if (bitmap) {
DeleteObject(bitmap);
}
return NULL;
}
SDL_memcpy(pixels, surface->pixels, surface->pitch * surface->h);
return bitmap;
}
/* Generate bitmap with a mask and optional monochrome image data.
*
* For info on the expected mask format see:
* https://devblogs.microsoft.com/oldnewthing/20101018-00/?p=12513
*/
static HBITMAP CreateMaskBitmap(SDL_Surface *surface, bool is_monochrome)
{
HBITMAP bitmap;
bool isstack;
void *pixels;
int x, y;
Uint8 r, g, b, a;
Uint8 *dst;
const int pitch = ((surface->w + 15) & ~15) / 8;
const int size = pitch * surface->h;
static const unsigned char masks[] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 };
SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888);
pixels = SDL_small_alloc(Uint8, size * (is_monochrome ? 2 : 1), &isstack);
if (!pixels) {
SDL_OutOfMemory();
return NULL;
}
dst = (Uint8 *)pixels;
// Make the mask completely transparent.
SDL_memset(dst, 0xff, size);
if (is_monochrome) {
SDL_memset(dst + size, 0x00, size);
}
for (y = 0; y < surface->h; y++, dst += pitch) {
for (x = 0; x < surface->w; x++) {
SDL_ReadSurfacePixel(surface, x, y, &r, &g, &b, &a);
if (a != 0) {
// Reset bit of an opaque pixel.
dst[x >> 3] &= ~masks[x & 7];
}
if (is_monochrome && !(r == 0x00 && g == 0x00 && b == 0x00)) {
// Set bit of white or inverted pixel.
dst[size + (x >> 3)] |= masks[x & 7];
}
}
}
bitmap = CreateBitmap(surface->w, surface->h * (is_monochrome ? 2 : 1), 1, 1, pixels);
SDL_small_free(pixels, isstack);
if (!bitmap) {
WIN_SetError("CreateBitmap()");
return NULL;
}
return bitmap;
}
static HCURSOR WIN_CreateHCursor(SDL_Surface *surface, int hot_x, int hot_y)
{
HCURSOR hcursor = NULL;
bool is_monochrome = IsMonochromeSurface(surface);
ICONINFO ii = {
.fIcon = FALSE,
.xHotspot = (DWORD)hot_x,
.yHotspot = (DWORD)hot_y,
.hbmMask = CreateMaskBitmap(surface, is_monochrome),
.hbmColor = is_monochrome ? NULL : CreateColorBitmap(surface)
};
if (!ii.hbmMask || (!is_monochrome && !ii.hbmColor)) {
SDL_SetError("Couldn't create cursor bitmaps");
goto cleanup;
}
hcursor = CreateIconIndirect(&ii);
if (!hcursor) {
WIN_SetError("CreateIconIndirect failed");
}
cleanup:
if (ii.hbmMask) {
DeleteObject(ii.hbmMask);
}
if (ii.hbmColor) {
DeleteObject(ii.hbmColor);
}
return hcursor;
}
static SDL_Cursor *WIN_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
{
if (!SDL_SurfaceHasAlternateImages(surface)) {
HCURSOR hcursor = WIN_CreateHCursor(surface, hot_x, hot_y);
if (!hcursor) {
return NULL;
}
return WIN_CreateCursorAndData(hcursor);
}
// Dynamically generate cursors at the appropriate DPI
SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor));
if (cursor) {
SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data));
if (!data) {
SDL_free(cursor);
return NULL;
}
data->hot_x = hot_x;
data->hot_y = hot_y;
data->surface = surface;
++surface->refcount;
cursor->internal = data;
}
return cursor;
}
static SDL_Cursor *WIN_CreateBlankCursor(void)
{
SDL_Cursor *cursor = NULL;
SDL_Surface *surface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_ARGB8888);
if (surface) {
cursor = WIN_CreateCursor(surface, 0, 0);
SDL_DestroySurface(surface);
}
return cursor;
}
static SDL_Cursor *WIN_CreateSystemCursor(SDL_SystemCursor id)
{
LPCTSTR name;
switch (id) {
default:
SDL_assert(!"Unknown system cursor ID");
return NULL;
case SDL_SYSTEM_CURSOR_DEFAULT:
name = IDC_ARROW;
break;
case SDL_SYSTEM_CURSOR_TEXT:
name = IDC_IBEAM;
break;
case SDL_SYSTEM_CURSOR_WAIT:
name = IDC_WAIT;
break;
case SDL_SYSTEM_CURSOR_CROSSHAIR:
name = IDC_CROSS;
break;
case SDL_SYSTEM_CURSOR_PROGRESS:
name = IDC_APPSTARTING;
break;
case SDL_SYSTEM_CURSOR_NWSE_RESIZE:
name = IDC_SIZENWSE;
break;
case SDL_SYSTEM_CURSOR_NESW_RESIZE:
name = IDC_SIZENESW;
break;
case SDL_SYSTEM_CURSOR_EW_RESIZE:
name = IDC_SIZEWE;
break;
case SDL_SYSTEM_CURSOR_NS_RESIZE:
name = IDC_SIZENS;
break;
case SDL_SYSTEM_CURSOR_MOVE:
name = IDC_SIZEALL;
break;
case SDL_SYSTEM_CURSOR_NOT_ALLOWED:
name = IDC_NO;
break;
case SDL_SYSTEM_CURSOR_POINTER:
name = IDC_HAND;
break;
case SDL_SYSTEM_CURSOR_NW_RESIZE:
name = IDC_SIZENWSE;
break;
case SDL_SYSTEM_CURSOR_N_RESIZE:
name = IDC_SIZENS;
break;
case SDL_SYSTEM_CURSOR_NE_RESIZE:
name = IDC_SIZENESW;
break;
case SDL_SYSTEM_CURSOR_E_RESIZE:
name = IDC_SIZEWE;
break;
case SDL_SYSTEM_CURSOR_SE_RESIZE:
name = IDC_SIZENWSE;
break;
case SDL_SYSTEM_CURSOR_S_RESIZE:
name = IDC_SIZENS;
break;
case SDL_SYSTEM_CURSOR_SW_RESIZE:
name = IDC_SIZENESW;
break;
case SDL_SYSTEM_CURSOR_W_RESIZE:
name = IDC_SIZEWE;
break;
}
return WIN_CreateCursorAndData(LoadCursor(NULL, name));
}
static SDL_Cursor *WIN_CreateDefaultCursor(void)
{
SDL_SystemCursor id = SDL_GetDefaultSystemCursor();
return WIN_CreateSystemCursor(id);
}
static void WIN_FreeCursor(SDL_Cursor *cursor)
{
SDL_CursorData *data = cursor->internal;
if (data->surface) {
SDL_DestroySurface(data->surface);
}
while (data->cache) {
CachedCursor *entry = data->cache;
data->cache = entry->next;
if (entry->cursor) {
DestroyCursor(entry->cursor);
}
SDL_free(entry);
}
if (data->cursor) {
DestroyCursor(data->cursor);
}
SDL_free(data);
SDL_free(cursor);
}
static HCURSOR GetCachedCursor(SDL_Cursor *cursor)
{
SDL_CursorData *data = cursor->internal;
SDL_Window *focus = SDL_GetMouseFocus();
if (!focus) {
return NULL;
}
float scale = SDL_GetDisplayContentScale(SDL_GetDisplayForWindow(focus));
for (CachedCursor *entry = data->cache; entry; entry = entry->next) {
if (scale == entry->scale) {
return entry->cursor;
}
}
// Need to create a cursor for this content scale
SDL_Surface *surface = NULL;
HCURSOR hcursor = NULL;
CachedCursor *entry = NULL;
surface = SDL_GetSurfaceImage(data->surface, scale);
if (!surface) {
goto error;
}
int hot_x = (int)SDL_round(data->hot_x * scale);
int hot_y = (int)SDL_round(data->hot_y * scale);
hcursor = WIN_CreateHCursor(surface, hot_x, hot_y);
if (!hcursor) {
goto error;
}
entry = (CachedCursor *)SDL_malloc(sizeof(*entry));
if (!entry) {
goto error;
}
entry->cursor = hcursor;
entry->scale = scale;
entry->next = data->cache;
data->cache = entry;
SDL_DestroySurface(surface);
return hcursor;
error:
if (surface) {
SDL_DestroySurface(surface);
}
if (hcursor) {
DestroyCursor(hcursor);
}
SDL_free(entry);
return NULL;
}
static bool WIN_ShowCursor(SDL_Cursor *cursor)
{
if (!cursor) {
cursor = SDL_blank_cursor;
}
if (cursor) {
if (cursor->internal->surface) {
SDL_cursor = GetCachedCursor(cursor);
} else {
SDL_cursor = cursor->internal->cursor;
}
} else {
SDL_cursor = NULL;
}
if (SDL_GetMouseFocus() != NULL) {
SetCursor(SDL_cursor);
}
return true;
}
void WIN_SetCursorPos(int x, int y)
{
// We need to jitter the value because otherwise Windows will occasionally inexplicably ignore the SetCursorPos() or SendInput()
SetCursorPos(x, y);
SetCursorPos(x + 1, y);
SetCursorPos(x, y);
// Flush any mouse motion prior to or associated with this warp
#ifdef _MSC_VER // We explicitly want to use GetTickCount(), not GetTickCount64()
#pragma warning(push)
#pragma warning(disable : 28159)
#endif
SDL_last_warp_time = GetTickCount();
if (!SDL_last_warp_time) {
SDL_last_warp_time = 1;
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
}
static bool WIN_WarpMouse(SDL_Window *window, float x, float y)
{
SDL_WindowData *data = window->internal;
HWND hwnd = data->hwnd;
POINT pt;
// Don't warp the mouse while we're doing a modal interaction
if (data->in_title_click || data->focus_click_pending) {
return true;
}
pt.x = (int)SDL_roundf(x);
pt.y = (int)SDL_roundf(y);
ClientToScreen(hwnd, &pt);
WIN_SetCursorPos(pt.x, pt.y);
// Send the exact mouse motion associated with this warp
SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y);
return true;
}
static bool WIN_WarpMouseGlobal(float x, float y)
{
POINT pt;
pt.x = (int)SDL_roundf(x);
pt.y = (int)SDL_roundf(y);
SetCursorPos(pt.x, pt.y);
return true;
}
static bool WIN_SetRelativeMouseMode(bool enabled)
{
return WIN_SetRawMouseEnabled(SDL_GetVideoDevice(), enabled);
}
static bool WIN_CaptureMouse(SDL_Window *window)
{
if (window) {
SDL_WindowData *data = window->internal;
SetCapture(data->hwnd);
} else {
SDL_Window *focus_window = SDL_GetMouseFocus();
if (focus_window) {
SDL_WindowData *data = focus_window->internal;
if (!data->mouse_tracked) {
SDL_SetMouseFocus(NULL);
}
}
ReleaseCapture();
}
return true;
}
static SDL_MouseButtonFlags WIN_GetGlobalMouseState(float *x, float *y)
{
SDL_MouseButtonFlags result = 0;
POINT pt = { 0, 0 };
bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0;
GetCursorPos(&pt);
*x = (float)pt.x;
*y = (float)pt.y;
result |= GetAsyncKeyState(!swapButtons ? VK_LBUTTON : VK_RBUTTON) & 0x8000 ? SDL_BUTTON_LMASK : 0;
result |= GetAsyncKeyState(!swapButtons ? VK_RBUTTON : VK_LBUTTON) & 0x8000 ? SDL_BUTTON_RMASK : 0;
result |= GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_BUTTON_MMASK : 0;
result |= GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_BUTTON_X1MASK : 0;
result |= GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_BUTTON_X2MASK : 0;
return result;
}
static void WIN_ApplySystemScale(void *internal, Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float *x, float *y)
{
if (!internal) {
return;
}
WIN_MouseData *data = (WIN_MouseData *)internal;
SDL_VideoDisplay *display = window ? SDL_GetVideoDisplayForWindow(window) : SDL_GetVideoDisplay(SDL_GetPrimaryDisplay());
Sint64 ix = (Sint64)*x * 65536;
Sint64 iy = (Sint64)*y * 65536;
Uint32 dpi = display ? (Uint32)(display->content_scale * USER_DEFAULT_SCREEN_DPI) : USER_DEFAULT_SCREEN_DPI;
if (!data->enhanced) { // early return if flat scale
dpi = data->dpiscale * (data->dpiaware ? dpi : USER_DEFAULT_SCREEN_DPI);
ix *= dpi;
iy *= dpi;
ix /= USER_DEFAULT_SCREEN_DPI;
iy /= USER_DEFAULT_SCREEN_DPI;
ix /= 32;
iy /= 32;
// data->residual[0] += ix;
// data->residual[1] += iy;
// ix = 65536 * (data->residual[0] / 65536);
// iy = 65536 * (data->residual[1] / 65536);
// data->residual[0] -= ix;
// data->residual[1] -= iy;
*x = (float)ix / 65536.0f;
*y = (float)iy / 65536.0f;
return;
}
Uint64 *xs = data->xs;
Uint64 *ys = data->ys;
Uint64 absx = SDL_abs(ix);
Uint64 absy = SDL_abs(iy);
Uint64 speed = SDL_min(absx, absy) + (SDL_max(absx, absy) << 1); // super cursed approximation used by Windows
if (speed == 0) {
return;
}
int i, j, k;
for (i = 1; i < 5; i++) {
j = i;
if (speed < xs[j]) {
break;
}
}
i -= 1;
j -= 1;
k = data->last_node;
data->last_node = j;
Uint32 denom = data->dpidenom;
Sint64 scale = 0;
Sint64 xdiff = xs[j+1] - xs[j];
Sint64 ydiff = ys[j+1] - ys[j];
if (xdiff != 0) {
Sint64 slope = ydiff / xdiff;
Sint64 inter = slope * xs[i] - ys[i];
scale += slope - inter / speed;
}
if (j > k) {
denom <<= 1;
xdiff = xs[k+1] - xs[k];
ydiff = ys[k+1] - ys[k];
if (xdiff != 0) {
Sint64 slope = ydiff / xdiff;
Sint64 inter = slope * xs[k] - ys[k];
scale += slope - inter / speed;
}
}
scale *= dpi;
ix *= scale;
iy *= scale;
ix /= denom;
iy /= denom;
// data->residual[0] += ix;
// data->residual[1] += iy;
// ix = 65536 * (data->residual[0] / 65536);
// iy = 65536 * (data->residual[1] / 65536);
// data->residual[0] -= ix;
// data->residual[1] -= iy;
*x = (float)ix / 65536.0f;
*y = (float)iy / 65536.0f;
}
void WIN_InitMouse(SDL_VideoDevice *_this)
{
SDL_Mouse *mouse = SDL_GetMouse();
mouse->CreateCursor = WIN_CreateCursor;
mouse->CreateSystemCursor = WIN_CreateSystemCursor;
mouse->ShowCursor = WIN_ShowCursor;
mouse->FreeCursor = WIN_FreeCursor;
mouse->WarpMouse = WIN_WarpMouse;
mouse->WarpMouseGlobal = WIN_WarpMouseGlobal;
mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode;
mouse->CaptureMouse = WIN_CaptureMouse;
mouse->GetGlobalMouseState = WIN_GetGlobalMouseState;
mouse->ApplySystemScale = WIN_ApplySystemScale;
mouse->system_scale_data = &WIN_system_scale_data;
SDL_SetDefaultCursor(WIN_CreateDefaultCursor());
SDL_blank_cursor = WIN_CreateBlankCursor();
WIN_UpdateMouseSystemScale();
}
void WIN_QuitMouse(SDL_VideoDevice *_this)
{
if (SDL_blank_cursor) {
WIN_FreeCursor(SDL_blank_cursor);
SDL_blank_cursor = NULL;
}
}
static void ReadMouseCurve(int v, Uint64 xs[5], Uint64 ys[5])
{
bool win8 = WIN_IsWindows8OrGreater();
DWORD xbuff[10] = {
0x00000000, 0,
0x00006e15, 0,
0x00014000, 0,
0x0003dc29, 0,
0x00280000, 0
};
DWORD ybuff[10] = {
0x00000000, 0,
win8 ? 0x000111fd : 0x00015eb8, 0,
win8 ? 0x00042400 : 0x00054ccd, 0,
win8 ? 0x0012fc00 : 0x00184ccd, 0,
win8 ? 0x01bbc000 : 0x02380000, 0
};
DWORD xsize = sizeof(xbuff);
DWORD ysize = sizeof(ybuff);
HKEY open_handle;
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\Mouse", 0, KEY_READ, &open_handle) == ERROR_SUCCESS) {
RegQueryValueExW(open_handle, L"SmoothMouseXCurve", NULL, NULL, (BYTE*)xbuff, &xsize);
RegQueryValueExW(open_handle, L"SmoothMouseYCurve", NULL, NULL, (BYTE*)ybuff, &ysize);
RegCloseKey(open_handle);
}
xs[0] = 0; // first node must always be origin
ys[0] = 0; // first node must always be origin
int i;
for (i = 1; i < 5; i++) {
xs[i] = (7 * (Uint64)xbuff[i * 2]);
ys[i] = (v * (Uint64)ybuff[i * 2]) << 17;
}
}
void WIN_UpdateMouseSystemScale(void)
{
SDL_Mouse *mouse = SDL_GetMouse();
if (mouse->ApplySystemScale == WIN_ApplySystemScale) {
mouse->system_scale_data = &WIN_system_scale_data;
}
// always reinitialize to valid defaults, whether fetch was successful or not.
WIN_MouseData *data = &WIN_system_scale_data;
data->residual[0] = 0;
data->residual[1] = 0;
data->dpiscale = 32;
data->dpidenom = (10 * (WIN_IsWindows8OrGreater() ? 120 : 150)) << 16;
data->dpiaware = WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice());
data->enhanced = false;
int v = 10;
if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &v, 0)) {
v = SDL_max(1, SDL_min(v, 20));
data->dpiscale = SDL_max(SDL_max(v, (v - 2) * 4), (v - 6) * 8);
}
int params[3];
if (SystemParametersInfo(SPI_GETMOUSE, 0, &params, 0)) {
data->enhanced = params[2] ? true : false;
if (params[2]) {
ReadMouseCurve(v, data->xs, data->ys);
}
}
}
#endif // SDL_VIDEO_DRIVER_WINDOWS
@@ -0,0 +1,34 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_windowsmouse_h_
#define SDL_windowsmouse_h_
extern DWORD SDL_last_warp_time;
extern HCURSOR SDL_cursor;
extern void WIN_InitMouse(SDL_VideoDevice *_this);
extern void WIN_QuitMouse(SDL_VideoDevice *_this);
extern void WIN_SetCursorPos(int x, int y);
extern void WIN_UpdateMouseSystemScale(void);
#endif // SDL_windowsmouse_h_
@@ -0,0 +1,906 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_WINDOWS
#include "SDL_windowsvideo.h"
#include "SDL_windowsopengles.h"
// WGL implementation of SDL OpenGL support
#ifdef SDL_VIDEO_OPENGL_WGL
#include <SDL3/SDL_opengl.h>
#define DEFAULT_OPENGL "OPENGL32.DLL"
#ifndef WGL_ARB_create_context
#define WGL_ARB_create_context
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
#ifndef WGL_ARB_create_context_profile
#define WGL_ARB_create_context_profile
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#endif
#ifndef WGL_ARB_create_context_robustness
#define WGL_ARB_create_context_robustness
#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261
#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252
#endif
#endif
#ifndef WGL_EXT_create_context_es2_profile
#define WGL_EXT_create_context_es2_profile
#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
#endif
#ifndef WGL_EXT_create_context_es_profile
#define WGL_EXT_create_context_es_profile
#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
#endif
#ifndef WGL_ARB_framebuffer_sRGB
#define WGL_ARB_framebuffer_sRGB
#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9
#endif
#ifndef WGL_ARB_pixel_format_float
#define WGL_ARB_pixel_format_float
#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0
#endif
#ifndef WGL_ARB_context_flush_control
#define WGL_ARB_context_flush_control
#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0x0000
#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
#endif
#ifndef WGL_ARB_create_context_no_error
#define WGL_ARB_create_context_no_error
#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3
#endif
typedef HGLRC(APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC,
HGLRC
hShareContext,
const int
*attribList);
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
#define GetDC(hwnd) (HDC) hwnd
#define ReleaseDC(hwnd, hdc) 1
#define SwapBuffers _this->gl_data->wglSwapBuffers
#define DescribePixelFormat _this->gl_data->wglDescribePixelFormat
#define ChoosePixelFormat _this->gl_data->wglChoosePixelFormat
#define GetPixelFormat _this->gl_data->wglGetPixelFormat
#define SetPixelFormat _this->gl_data->wglSetPixelFormat
#endif
bool WIN_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
void *handle;
if (path == NULL) {
path = SDL_GetHint(SDL_HINT_OPENGL_LIBRARY);
}
if (path == NULL) {
path = DEFAULT_OPENGL;
}
_this->gl_config.dll_handle = SDL_LoadObject(path);
if (!_this->gl_config.dll_handle) {
return false;
}
SDL_strlcpy(_this->gl_config.driver_path, path,
SDL_arraysize(_this->gl_config.driver_path));
// Allocate OpenGL memory
_this->gl_data = (struct SDL_GLDriverData *)SDL_calloc(1, sizeof(struct SDL_GLDriverData));
if (!_this->gl_data) {
return false;
}
// Load function pointers
handle = _this->gl_config.dll_handle;
/* *INDENT-OFF* */ // clang-format off
_this->gl_data->wglGetProcAddress = (PROC (WINAPI *)(const char *))
SDL_LoadFunction(handle, "wglGetProcAddress");
_this->gl_data->wglCreateContext = (HGLRC (WINAPI *)(HDC))
SDL_LoadFunction(handle, "wglCreateContext");
_this->gl_data->wglDeleteContext = (BOOL (WINAPI *)(HGLRC))
SDL_LoadFunction(handle, "wglDeleteContext");
_this->gl_data->wglMakeCurrent = (BOOL (WINAPI *)(HDC, HGLRC))
SDL_LoadFunction(handle, "wglMakeCurrent");
_this->gl_data->wglShareLists = (BOOL (WINAPI *)(HGLRC, HGLRC))
SDL_LoadFunction(handle, "wglShareLists");
/* *INDENT-ON* */ // clang-format on
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
_this->gl_data->wglSwapBuffers = (BOOL(WINAPI *)(HDC))
SDL_LoadFunction(handle, "wglSwapBuffers");
_this->gl_data->wglDescribePixelFormat = (int(WINAPI *)(HDC, int, UINT, LPPIXELFORMATDESCRIPTOR))
SDL_LoadFunction(handle, "wglDescribePixelFormat");
_this->gl_data->wglChoosePixelFormat = (int(WINAPI *)(HDC, const PIXELFORMATDESCRIPTOR *))
SDL_LoadFunction(handle, "wglChoosePixelFormat");
_this->gl_data->wglSetPixelFormat = (BOOL(WINAPI *)(HDC, int, const PIXELFORMATDESCRIPTOR *))
SDL_LoadFunction(handle, "wglSetPixelFormat");
_this->gl_data->wglGetPixelFormat = (int(WINAPI *)(HDC hdc))
SDL_LoadFunction(handle, "wglGetPixelFormat");
#endif
if (!_this->gl_data->wglGetProcAddress ||
!_this->gl_data->wglCreateContext ||
!_this->gl_data->wglDeleteContext ||
!_this->gl_data->wglMakeCurrent
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|| !_this->gl_data->wglSwapBuffers ||
!_this->gl_data->wglDescribePixelFormat ||
!_this->gl_data->wglChoosePixelFormat ||
!_this->gl_data->wglGetPixelFormat ||
!_this->gl_data->wglSetPixelFormat
#endif
) {
return SDL_SetError("Could not retrieve OpenGL functions");
}
/* XXX Too sleazy? WIN_GL_InitExtensions looks for certain OpenGL
extensions via SDL_GL_DeduceMaxSupportedESProfile. This uses
SDL_GL_ExtensionSupported which in turn calls SDL_GL_GetProcAddress.
However SDL_GL_GetProcAddress will fail if the library is not
loaded; it checks for gl_config.driver_loaded > 0. To avoid this
test failing, increment driver_loaded around the call to
WIN_GLInitExtensions.
Successful loading of the library is normally indicated by
SDL_GL_LoadLibrary incrementing driver_loaded immediately after
this function returns 0 to it.
Alternatives to this are:
- moving SDL_GL_DeduceMaxSupportedESProfile to both the WIN and
X11 platforms while adding a function equivalent to
SDL_GL_ExtensionSupported but which directly calls
glGetProcAddress(). Having 3 copies of the
SDL_GL_ExtensionSupported makes this alternative unattractive.
- moving SDL_GL_DeduceMaxSupportedESProfile to a new file shared
by the WIN and X11 platforms while adding a function equivalent
to SDL_GL_ExtensionSupported. This is unattractive due to the
number of project files that will need updating, plus there
will be 2 copies of the SDL_GL_ExtensionSupported code.
- Add a private equivalent of SDL_GL_ExtensionSupported to
SDL_video.c.
- Move the call to WIN_GL_InitExtensions back to WIN_CreateWindow
and add a flag to gl_data to avoid multiple calls to this
expensive function. This is probably the least objectionable
alternative if this increment/decrement trick is unacceptable.
Note that the driver_loaded > 0 check needs to remain in
SDL_GL_ExtensionSupported and SDL_GL_GetProcAddress as they are
public API functions.
*/
++_this->gl_config.driver_loaded;
WIN_GL_InitExtensions(_this);
--_this->gl_config.driver_loaded;
return true;
}
SDL_FunctionPointer WIN_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc)
{
SDL_FunctionPointer func;
// This is to pick up extensions
func = (SDL_FunctionPointer)_this->gl_data->wglGetProcAddress(proc);
if (!func) {
// This is probably a normal GL function
func = (SDL_FunctionPointer)GetProcAddress((HMODULE)_this->gl_config.dll_handle, proc);
}
return func;
}
void WIN_GL_UnloadLibrary(SDL_VideoDevice *_this)
{
SDL_UnloadObject(_this->gl_config.dll_handle);
_this->gl_config.dll_handle = NULL;
// Free OpenGL memory
SDL_free(_this->gl_data);
_this->gl_data = NULL;
}
static void WIN_GL_SetupPixelFormat(SDL_VideoDevice *_this, PIXELFORMATDESCRIPTOR *pfd)
{
SDL_zerop(pfd);
pfd->nSize = sizeof(*pfd);
pfd->nVersion = 1;
pfd->dwFlags = (PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL);
if (_this->gl_config.double_buffer) {
pfd->dwFlags |= PFD_DOUBLEBUFFER;
}
if (_this->gl_config.stereo) {
pfd->dwFlags |= PFD_STEREO;
}
pfd->iLayerType = PFD_MAIN_PLANE;
pfd->iPixelType = PFD_TYPE_RGBA;
pfd->cRedBits = (BYTE)_this->gl_config.red_size;
pfd->cGreenBits = (BYTE)_this->gl_config.green_size;
pfd->cBlueBits = (BYTE)_this->gl_config.blue_size;
pfd->cAlphaBits = (BYTE)_this->gl_config.alpha_size;
if (_this->gl_config.buffer_size) {
pfd->cColorBits = (BYTE)(_this->gl_config.buffer_size - _this->gl_config.alpha_size);
} else {
pfd->cColorBits = (pfd->cRedBits + pfd->cGreenBits + pfd->cBlueBits);
}
pfd->cAccumRedBits = (BYTE)_this->gl_config.accum_red_size;
pfd->cAccumGreenBits = (BYTE)_this->gl_config.accum_green_size;
pfd->cAccumBlueBits = (BYTE)_this->gl_config.accum_blue_size;
pfd->cAccumAlphaBits = (BYTE)_this->gl_config.accum_alpha_size;
pfd->cAccumBits =
(pfd->cAccumRedBits + pfd->cAccumGreenBits + pfd->cAccumBlueBits +
pfd->cAccumAlphaBits);
pfd->cDepthBits = (BYTE)_this->gl_config.depth_size;
pfd->cStencilBits = (BYTE)_this->gl_config.stencil_size;
}
/* Choose the closest pixel format that meets or exceeds the target.
FIXME: Should we weight any particular attribute over any other?
*/
static bool WIN_GL_ChoosePixelFormat(SDL_VideoDevice *_this, HDC hdc, PIXELFORMATDESCRIPTOR *target)
{
PIXELFORMATDESCRIPTOR pfd;
int count, index, best = 0;
unsigned int dist, best_dist = ~0U;
count = DescribePixelFormat(hdc, 1, sizeof(pfd), NULL);
for (index = 1; index <= count; index++) {
if (!DescribePixelFormat(hdc, index, sizeof(pfd), &pfd)) {
continue;
}
if ((pfd.dwFlags & target->dwFlags) != target->dwFlags) {
continue;
}
if (pfd.iLayerType != target->iLayerType) {
continue;
}
if (pfd.iPixelType != target->iPixelType) {
continue;
}
dist = 0;
if (pfd.cColorBits < target->cColorBits) {
continue;
} else {
dist += (pfd.cColorBits - target->cColorBits);
}
if (pfd.cRedBits < target->cRedBits) {
continue;
} else {
dist += (pfd.cRedBits - target->cRedBits);
}
if (pfd.cGreenBits < target->cGreenBits) {
continue;
} else {
dist += (pfd.cGreenBits - target->cGreenBits);
}
if (pfd.cBlueBits < target->cBlueBits) {
continue;
} else {
dist += (pfd.cBlueBits - target->cBlueBits);
}
if (pfd.cAlphaBits < target->cAlphaBits) {
continue;
} else {
dist += (pfd.cAlphaBits - target->cAlphaBits);
}
if (pfd.cAccumBits < target->cAccumBits) {
continue;
} else {
dist += (pfd.cAccumBits - target->cAccumBits);
}
if (pfd.cAccumRedBits < target->cAccumRedBits) {
continue;
} else {
dist += (pfd.cAccumRedBits - target->cAccumRedBits);
}
if (pfd.cAccumGreenBits < target->cAccumGreenBits) {
continue;
} else {
dist += (pfd.cAccumGreenBits - target->cAccumGreenBits);
}
if (pfd.cAccumBlueBits < target->cAccumBlueBits) {
continue;
} else {
dist += (pfd.cAccumBlueBits - target->cAccumBlueBits);
}
if (pfd.cAccumAlphaBits < target->cAccumAlphaBits) {
continue;
} else {
dist += (pfd.cAccumAlphaBits - target->cAccumAlphaBits);
}
if (pfd.cDepthBits < target->cDepthBits) {
continue;
} else {
dist += (pfd.cDepthBits - target->cDepthBits);
}
if (pfd.cStencilBits < target->cStencilBits) {
continue;
} else {
dist += (pfd.cStencilBits - target->cStencilBits);
}
if (dist < best_dist) {
best = index;
best_dist = dist;
}
}
return best;
}
static bool HasExtension(const char *extension, const char *extensions)
{
const char *start;
const char *where, *terminator;
// Extension names should not have spaces.
where = SDL_strchr(extension, ' ');
if (where || *extension == '\0') {
return false;
}
if (!extensions) {
return false;
}
/* It takes a bit of care to be fool-proof about parsing the
* OpenGL extensions string. Don't be fooled by sub-strings,
* etc. */
start = extensions;
for (;;) {
where = SDL_strstr(start, extension);
if (!where) {
break;
}
terminator = where + SDL_strlen(extension);
if (where == start || *(where - 1) == ' ') {
if (*terminator == ' ' || *terminator == '\0') {
return true;
}
}
start = terminator;
}
return false;
}
void WIN_GL_InitExtensions(SDL_VideoDevice *_this)
{
/* *INDENT-OFF* */ // clang-format off
const char *(WINAPI * wglGetExtensionsStringARB)(HDC) = 0;
/* *INDENT-ON* */ // clang-format on
const char *extensions;
HWND hwnd;
HDC hdc;
HGLRC hglrc;
PIXELFORMATDESCRIPTOR pfd;
if (!_this->gl_data) {
return;
}
hwnd =
CreateWindow(SDL_Appname, SDL_Appname, (WS_POPUP | WS_DISABLED), 0, 0,
10, 10, NULL, NULL, SDL_Instance, NULL);
if (!hwnd) {
return;
}
WIN_PumpEvents(_this);
hdc = GetDC(hwnd);
WIN_GL_SetupPixelFormat(_this, &pfd);
SetPixelFormat(hdc, ChoosePixelFormat(hdc, &pfd), &pfd);
hglrc = _this->gl_data->wglCreateContext(hdc);
if (!hglrc) {
return;
}
_this->gl_data->wglMakeCurrent(hdc, hglrc);
/* *INDENT-OFF* */ // clang-format off
wglGetExtensionsStringARB = (const char *(WINAPI *)(HDC))
_this->gl_data->wglGetProcAddress("wglGetExtensionsStringARB");
/* *INDENT-ON* */ // clang-format on
if (wglGetExtensionsStringARB) {
extensions = wglGetExtensionsStringARB(hdc);
} else {
extensions = NULL;
}
// Check for WGL_ARB_pixel_format
_this->gl_data->HAS_WGL_ARB_pixel_format = false;
if (HasExtension("WGL_ARB_pixel_format", extensions)) {
/* *INDENT-OFF* */ // clang-format off
_this->gl_data->wglChoosePixelFormatARB =
(BOOL (WINAPI *)(HDC, const int *, const FLOAT *, UINT, int *, UINT *))
WIN_GL_GetProcAddress(_this, "wglChoosePixelFormatARB");
_this->gl_data->wglGetPixelFormatAttribivARB =
(BOOL (WINAPI *)(HDC, int, int, UINT, const int *, int *))
WIN_GL_GetProcAddress(_this, "wglGetPixelFormatAttribivARB");
/* *INDENT-ON* */ // clang-format on
if ((_this->gl_data->wglChoosePixelFormatARB != NULL) &&
(_this->gl_data->wglGetPixelFormatAttribivARB != NULL)) {
_this->gl_data->HAS_WGL_ARB_pixel_format = true;
}
}
// Check for WGL_EXT_swap_control
_this->gl_data->HAS_WGL_EXT_swap_control_tear = false;
if (HasExtension("WGL_EXT_swap_control", extensions)) {
_this->gl_data->wglSwapIntervalEXT =
(BOOL (WINAPI *)(int))
WIN_GL_GetProcAddress(_this, "wglSwapIntervalEXT");
_this->gl_data->wglGetSwapIntervalEXT =
(int (WINAPI *)(void))
WIN_GL_GetProcAddress(_this, "wglGetSwapIntervalEXT");
if (HasExtension("WGL_EXT_swap_control_tear", extensions)) {
_this->gl_data->HAS_WGL_EXT_swap_control_tear = true;
}
} else {
_this->gl_data->wglSwapIntervalEXT = NULL;
_this->gl_data->wglGetSwapIntervalEXT = NULL;
}
// Check for WGL_EXT_create_context_es2_profile
if (HasExtension("WGL_EXT_create_context_es2_profile", extensions)) {
SDL_GL_DeduceMaxSupportedESProfile(
&_this->gl_data->es_profile_max_supported_version.major,
&_this->gl_data->es_profile_max_supported_version.minor);
}
// Check for WGL_ARB_context_flush_control
if (HasExtension("WGL_ARB_context_flush_control", extensions)) {
_this->gl_data->HAS_WGL_ARB_context_flush_control = true;
}
// Check for WGL_ARB_create_context_robustness
if (HasExtension("WGL_ARB_create_context_robustness", extensions)) {
_this->gl_data->HAS_WGL_ARB_create_context_robustness = true;
}
// Check for WGL_ARB_create_context_no_error
if (HasExtension("WGL_ARB_create_context_no_error", extensions)) {
_this->gl_data->HAS_WGL_ARB_create_context_no_error = true;
}
_this->gl_data->wglMakeCurrent(hdc, NULL);
_this->gl_data->wglDeleteContext(hglrc);
ReleaseDC(hwnd, hdc);
DestroyWindow(hwnd);
WIN_PumpEvents(_this);
}
static int WIN_GL_ChoosePixelFormatARB(SDL_VideoDevice *_this, int *iAttribs, float *fAttribs)
{
HWND hwnd;
HDC hdc;
PIXELFORMATDESCRIPTOR pfd;
HGLRC hglrc;
int pixel_format = 0;
unsigned int matching;
int qAttrib = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB;
int srgb = 0;
hwnd =
CreateWindow(SDL_Appname, SDL_Appname, (WS_POPUP | WS_DISABLED), 0, 0,
10, 10, NULL, NULL, SDL_Instance, NULL);
WIN_PumpEvents(_this);
hdc = GetDC(hwnd);
WIN_GL_SetupPixelFormat(_this, &pfd);
SetPixelFormat(hdc, ChoosePixelFormat(hdc, &pfd), &pfd);
hglrc = _this->gl_data->wglCreateContext(hdc);
if (hglrc) {
_this->gl_data->wglMakeCurrent(hdc, hglrc);
if (_this->gl_data->HAS_WGL_ARB_pixel_format) {
_this->gl_data->wglChoosePixelFormatARB(hdc, iAttribs, fAttribs,
1, &pixel_format,
&matching);
// Check whether we actually got an SRGB capable buffer
_this->gl_data->wglGetPixelFormatAttribivARB(hdc, pixel_format, 0, 1, &qAttrib, &srgb);
_this->gl_config.framebuffer_srgb_capable = srgb;
}
_this->gl_data->wglMakeCurrent(hdc, NULL);
_this->gl_data->wglDeleteContext(hglrc);
}
ReleaseDC(hwnd, hdc);
DestroyWindow(hwnd);
WIN_PumpEvents(_this);
return pixel_format;
}
// actual work of WIN_GL_SetupWindow() happens here.
static bool WIN_GL_SetupWindowInternal(SDL_VideoDevice *_this, SDL_Window *window)
{
HDC hdc = window->internal->hdc;
PIXELFORMATDESCRIPTOR pfd;
int pixel_format = 0;
int iAttribs[64];
int *iAttr;
int *iAccelAttr;
float fAttribs[1] = { 0 };
WIN_GL_SetupPixelFormat(_this, &pfd);
// setup WGL_ARB_pixel_format attribs
iAttr = &iAttribs[0];
*iAttr++ = WGL_DRAW_TO_WINDOW_ARB;
*iAttr++ = GL_TRUE;
*iAttr++ = WGL_RED_BITS_ARB;
*iAttr++ = _this->gl_config.red_size;
*iAttr++ = WGL_GREEN_BITS_ARB;
*iAttr++ = _this->gl_config.green_size;
*iAttr++ = WGL_BLUE_BITS_ARB;
*iAttr++ = _this->gl_config.blue_size;
if (_this->gl_config.alpha_size) {
*iAttr++ = WGL_ALPHA_BITS_ARB;
*iAttr++ = _this->gl_config.alpha_size;
}
*iAttr++ = WGL_DOUBLE_BUFFER_ARB;
*iAttr++ = _this->gl_config.double_buffer;
*iAttr++ = WGL_DEPTH_BITS_ARB;
*iAttr++ = _this->gl_config.depth_size;
if (_this->gl_config.stencil_size) {
*iAttr++ = WGL_STENCIL_BITS_ARB;
*iAttr++ = _this->gl_config.stencil_size;
}
if (_this->gl_config.accum_red_size) {
*iAttr++ = WGL_ACCUM_RED_BITS_ARB;
*iAttr++ = _this->gl_config.accum_red_size;
}
if (_this->gl_config.accum_green_size) {
*iAttr++ = WGL_ACCUM_GREEN_BITS_ARB;
*iAttr++ = _this->gl_config.accum_green_size;
}
if (_this->gl_config.accum_blue_size) {
*iAttr++ = WGL_ACCUM_BLUE_BITS_ARB;
*iAttr++ = _this->gl_config.accum_blue_size;
}
if (_this->gl_config.accum_alpha_size) {
*iAttr++ = WGL_ACCUM_ALPHA_BITS_ARB;
*iAttr++ = _this->gl_config.accum_alpha_size;
}
if (_this->gl_config.stereo) {
*iAttr++ = WGL_STEREO_ARB;
*iAttr++ = GL_TRUE;
}
if (_this->gl_config.multisamplebuffers) {
*iAttr++ = WGL_SAMPLE_BUFFERS_ARB;
*iAttr++ = _this->gl_config.multisamplebuffers;
}
if (_this->gl_config.multisamplesamples) {
*iAttr++ = WGL_SAMPLES_ARB;
*iAttr++ = _this->gl_config.multisamplesamples;
}
if (_this->gl_config.floatbuffers) {
*iAttr++ = WGL_PIXEL_TYPE_ARB;
*iAttr++ = WGL_TYPE_RGBA_FLOAT_ARB;
}
if (_this->gl_config.framebuffer_srgb_capable) {
*iAttr++ = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB;
*iAttr++ = _this->gl_config.framebuffer_srgb_capable;
}
/* We always choose either FULL or NO accel on Windows, because of flaky
drivers. If the app didn't specify, we use FULL, because that's
probably what they wanted (and if you didn't care and got FULL, that's
a perfectly valid result in any case). */
*iAttr++ = WGL_ACCELERATION_ARB;
iAccelAttr = iAttr;
if (_this->gl_config.accelerated) {
*iAttr++ = WGL_FULL_ACCELERATION_ARB;
} else {
*iAttr++ = WGL_NO_ACCELERATION_ARB;
}
*iAttr = 0;
// Choose and set the closest available pixel format
pixel_format = WIN_GL_ChoosePixelFormatARB(_this, iAttribs, fAttribs);
// App said "don't care about accel" and FULL accel failed. Try NO.
if ((!pixel_format) && (_this->gl_config.accelerated < 0)) {
*iAccelAttr = WGL_NO_ACCELERATION_ARB;
pixel_format = WIN_GL_ChoosePixelFormatARB(_this, iAttribs, fAttribs);
*iAccelAttr = WGL_FULL_ACCELERATION_ARB; // if we try again.
}
if (!pixel_format) {
pixel_format = WIN_GL_ChoosePixelFormat(_this, hdc, &pfd);
}
if (!pixel_format) {
return SDL_SetError("No matching GL pixel format available");
}
if (!SetPixelFormat(hdc, pixel_format, &pfd)) {
return WIN_SetError("SetPixelFormat()");
}
return true;
}
bool WIN_GL_SetupWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
// The current context is lost in here; save it and reset it.
SDL_Window *current_win = SDL_GL_GetCurrentWindow();
SDL_GLContext current_ctx = SDL_GL_GetCurrentContext();
const int result = WIN_GL_SetupWindowInternal(_this, window);
WIN_GL_MakeCurrent(_this, current_win, current_ctx);
return result;
}
bool WIN_GL_UseEGL(SDL_VideoDevice *_this)
{
SDL_assert(_this->gl_data != NULL);
SDL_assert(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES);
return SDL_GetHintBoolean(SDL_HINT_OPENGL_ES_DRIVER, false) || _this->gl_config.major_version == 1 || _this->gl_config.major_version > _this->gl_data->es_profile_max_supported_version.major || (_this->gl_config.major_version == _this->gl_data->es_profile_max_supported_version.major && _this->gl_config.minor_version > _this->gl_data->es_profile_max_supported_version.minor); // No WGL extension for OpenGL ES 1.x profiles.
}
SDL_GLContext WIN_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
{
HDC hdc = window->internal->hdc;
HGLRC context, share_context;
if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES && WIN_GL_UseEGL(_this)) {
#ifdef SDL_VIDEO_OPENGL_EGL
// Switch to EGL based functions
WIN_GL_UnloadLibrary(_this);
_this->GL_LoadLibrary = WIN_GLES_LoadLibrary;
_this->GL_GetProcAddress = WIN_GLES_GetProcAddress;
_this->GL_UnloadLibrary = WIN_GLES_UnloadLibrary;
_this->GL_CreateContext = WIN_GLES_CreateContext;
_this->GL_MakeCurrent = WIN_GLES_MakeCurrent;
_this->GL_SetSwapInterval = WIN_GLES_SetSwapInterval;
_this->GL_GetSwapInterval = WIN_GLES_GetSwapInterval;
_this->GL_SwapWindow = WIN_GLES_SwapWindow;
_this->GL_DestroyContext = WIN_GLES_DestroyContext;
_this->GL_GetEGLSurface = WIN_GLES_GetEGLSurface;
if (!WIN_GLES_LoadLibrary(_this, NULL)) {
return NULL;
}
return WIN_GLES_CreateContext(_this, window);
#else
SDL_SetError("SDL not configured with EGL support");
return NULL;
#endif
}
if (_this->gl_config.share_with_current_context) {
share_context = (HGLRC)SDL_GL_GetCurrentContext();
} else {
share_context = 0;
}
if (_this->gl_config.major_version < 3 &&
_this->gl_config.profile_mask == 0 &&
_this->gl_config.flags == 0) {
// Create legacy context
context = _this->gl_data->wglCreateContext(hdc);
if (share_context != 0) {
_this->gl_data->wglShareLists(share_context, context);
}
} else {
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
HGLRC temp_context = _this->gl_data->wglCreateContext(hdc);
if (!temp_context) {
SDL_SetError("Could not create GL context");
return NULL;
}
// Make the context current
if (!WIN_GL_MakeCurrent(_this, window, (SDL_GLContext)temp_context)) {
WIN_GL_DestroyContext(_this, (SDL_GLContext)temp_context);
return NULL;
}
wglCreateContextAttribsARB =
(PFNWGLCREATECONTEXTATTRIBSARBPROC)_this->gl_data->wglGetProcAddress("wglCreateContextAttribsARB");
if (!wglCreateContextAttribsARB) {
SDL_SetError("GL 3.x is not supported");
context = temp_context;
} else {
int attribs[15]; // max 14 attributes plus terminator
int iattr = 0;
attribs[iattr++] = WGL_CONTEXT_MAJOR_VERSION_ARB;
attribs[iattr++] = _this->gl_config.major_version;
attribs[iattr++] = WGL_CONTEXT_MINOR_VERSION_ARB;
attribs[iattr++] = _this->gl_config.minor_version;
// SDL profile bits match WGL profile bits
if (_this->gl_config.profile_mask != 0) {
attribs[iattr++] = WGL_CONTEXT_PROFILE_MASK_ARB;
attribs[iattr++] = _this->gl_config.profile_mask;
}
// SDL flags match WGL flags
if (_this->gl_config.flags != 0) {
attribs[iattr++] = WGL_CONTEXT_FLAGS_ARB;
attribs[iattr++] = _this->gl_config.flags;
}
// only set if wgl extension is available and not the default setting
if ((_this->gl_data->HAS_WGL_ARB_context_flush_control) && (_this->gl_config.release_behavior == 0)) {
attribs[iattr++] = WGL_CONTEXT_RELEASE_BEHAVIOR_ARB;
attribs[iattr++] = _this->gl_config.release_behavior ? WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB : WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB;
}
// only set if wgl extension is available and not the default setting
if ((_this->gl_data->HAS_WGL_ARB_create_context_robustness) && (_this->gl_config.reset_notification != 0)) {
attribs[iattr++] = WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB;
attribs[iattr++] = _this->gl_config.reset_notification ? WGL_LOSE_CONTEXT_ON_RESET_ARB : WGL_NO_RESET_NOTIFICATION_ARB;
}
// only set if wgl extension is available and not the default setting
if ((_this->gl_data->HAS_WGL_ARB_create_context_no_error) && (_this->gl_config.no_error != 0)) {
attribs[iattr++] = WGL_CONTEXT_OPENGL_NO_ERROR_ARB;
attribs[iattr++] = _this->gl_config.no_error;
}
attribs[iattr++] = 0;
// Create the GL 3.x context
context = wglCreateContextAttribsARB(hdc, share_context, attribs);
// Delete the GL 2.x context
_this->gl_data->wglDeleteContext(temp_context);
}
}
if (!context) {
WIN_SetError("Could not create GL context");
return NULL;
}
if (!WIN_GL_MakeCurrent(_this, window, (SDL_GLContext)context)) {
WIN_GL_DestroyContext(_this, (SDL_GLContext)context);
return NULL;
}
return (SDL_GLContext)context;
}
bool WIN_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
{
HDC hdc;
if (!_this->gl_data) {
return SDL_SetError("OpenGL not initialized");
}
// sanity check that higher level handled this.
SDL_assert(window || (window == NULL && !context));
/* Some Windows drivers freak out if hdc is NULL, even when context is
NULL, against spec. Since hdc is _supposed_ to be ignored if context
is NULL, we either use the current GL window, or do nothing if we
already have no current context. */
if (!window) {
window = SDL_GL_GetCurrentWindow();
if (!window) {
SDL_assert(SDL_GL_GetCurrentContext() == NULL);
return true; // already done.
}
}
hdc = window->internal->hdc;
if (!_this->gl_data->wglMakeCurrent(hdc, (HGLRC)context)) {
return WIN_SetError("wglMakeCurrent()");
}
return true;
}
bool WIN_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval)
{
if ((interval < 0) && (!_this->gl_data->HAS_WGL_EXT_swap_control_tear)) {
return SDL_SetError("Negative swap interval unsupported in this GL");
} else if (_this->gl_data->wglSwapIntervalEXT) {
if (!_this->gl_data->wglSwapIntervalEXT(interval)) {
return WIN_SetError("wglSwapIntervalEXT()");
}
} else {
return SDL_Unsupported();
}
return true;
}
bool WIN_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval)
{
if (_this->gl_data->wglGetSwapIntervalEXT) {
*interval = _this->gl_data->wglGetSwapIntervalEXT();
return true;
} else {
return false;
}
}
bool WIN_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
HDC hdc = window->internal->hdc;
if (!SwapBuffers(hdc)) {
return WIN_SetError("SwapBuffers()");
}
return true;
}
bool WIN_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context)
{
if (!_this->gl_data) {
return true;
}
_this->gl_data->wglDeleteContext((HGLRC)context);
return true;
}
#endif // SDL_VIDEO_OPENGL_WGL
#endif // SDL_VIDEO_DRIVER_WINDOWS
@@ -0,0 +1,178 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_windowsopengl_h_
#define SDL_windowsopengl_h_
#ifdef SDL_VIDEO_OPENGL_WGL
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
typedef struct tagPIXELFORMATDESCRIPTOR
{
WORD nSize;
WORD nVersion;
DWORD dwFlags;
BYTE iPixelType;
BYTE cColorBits;
BYTE cRedBits;
BYTE cRedShift;
BYTE cGreenBits;
BYTE cGreenShift;
BYTE cBlueBits;
BYTE cBlueShift;
BYTE cAlphaBits;
BYTE cAlphaShift;
BYTE cAccumBits;
BYTE cAccumRedBits;
BYTE cAccumGreenBits;
BYTE cAccumBlueBits;
BYTE cAccumAlphaBits;
BYTE cDepthBits;
BYTE cStencilBits;
BYTE cAuxBuffers;
BYTE iLayerType;
BYTE bReserved;
DWORD dwLayerMask;
DWORD dwVisibleMask;
DWORD dwDamageMask;
} PIXELFORMATDESCRIPTOR, *PPIXELFORMATDESCRIPTOR, *LPPIXELFORMATDESCRIPTOR;
#endif
struct SDL_GLDriverData
{
bool HAS_WGL_ARB_pixel_format;
bool HAS_WGL_EXT_swap_control_tear;
bool HAS_WGL_ARB_context_flush_control;
bool HAS_WGL_ARB_create_context_robustness;
bool HAS_WGL_ARB_create_context_no_error;
/* Max version of OpenGL ES context that can be created if the
implementation supports WGL_EXT_create_context_es2_profile.
major = minor = 0 when unsupported.
*/
struct
{
int major;
int minor;
} es_profile_max_supported_version;
/* *INDENT-OFF* */ // clang-format off
PROC (WINAPI *wglGetProcAddress)(const char *proc);
HGLRC (WINAPI *wglCreateContext)(HDC hdc);
BOOL (WINAPI *wglDeleteContext)(HGLRC hglrc);
BOOL (WINAPI *wglMakeCurrent)(HDC hdc, HGLRC hglrc);
BOOL (WINAPI *wglShareLists)(HGLRC hglrc1, HGLRC hglrc2);
BOOL (WINAPI *wglChoosePixelFormatARB)(HDC hdc, const int *piAttribIList, const FLOAT * pfAttribFList, UINT nMaxFormats, int *piFormats, UINT * nNumFormats);
BOOL (WINAPI *wglGetPixelFormatAttribivARB)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues);
BOOL (WINAPI *wglSwapIntervalEXT)(int interval);
int (WINAPI *wglGetSwapIntervalEXT)(void);
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
BOOL (WINAPI *wglSwapBuffers)(HDC hdc);
int (WINAPI *wglDescribePixelFormat)(HDC hdc,
int iPixelFormat,
UINT nBytes,
LPPIXELFORMATDESCRIPTOR ppfd);
int (WINAPI *wglChoosePixelFormat)(HDC hdc,
const PIXELFORMATDESCRIPTOR *ppfd);
BOOL (WINAPI *wglSetPixelFormat)(HDC hdc,
int format,
const PIXELFORMATDESCRIPTOR *ppfd);
int (WINAPI *wglGetPixelFormat)(HDC hdc);
#endif
/* *INDENT-ON* */ // clang-format on
};
// OpenGL functions
extern bool WIN_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern SDL_FunctionPointer WIN_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc);
extern void WIN_GL_UnloadLibrary(SDL_VideoDevice *_this);
extern bool WIN_GL_UseEGL(SDL_VideoDevice *_this);
extern bool WIN_GL_SetupWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern SDL_GLContext WIN_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
extern bool WIN_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window,
SDL_GLContext context);
extern bool WIN_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval);
extern bool WIN_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval);
extern bool WIN_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool WIN_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context);
extern void WIN_GL_InitExtensions(SDL_VideoDevice *_this);
#ifndef WGL_ARB_pixel_format
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_DRAW_TO_BITMAP_ARB 0x2002
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_NEED_PALETTE_ARB 0x2004
#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
#define WGL_SWAP_METHOD_ARB 0x2007
#define WGL_NUMBER_OVERLAYS_ARB 0x2008
#define WGL_NUMBER_UNDERLAYS_ARB 0x2009
#define WGL_TRANSPARENT_ARB 0x200A
#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
#define WGL_SHARE_DEPTH_ARB 0x200C
#define WGL_SHARE_STENCIL_ARB 0x200D
#define WGL_SHARE_ACCUM_ARB 0x200E
#define WGL_SUPPORT_GDI_ARB 0x200F
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_STEREO_ARB 0x2012
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_RED_BITS_ARB 0x2015
#define WGL_RED_SHIFT_ARB 0x2016
#define WGL_GREEN_BITS_ARB 0x2017
#define WGL_GREEN_SHIFT_ARB 0x2018
#define WGL_BLUE_BITS_ARB 0x2019
#define WGL_BLUE_SHIFT_ARB 0x201A
#define WGL_ALPHA_BITS_ARB 0x201B
#define WGL_ALPHA_SHIFT_ARB 0x201C
#define WGL_ACCUM_BITS_ARB 0x201D
#define WGL_ACCUM_RED_BITS_ARB 0x201E
#define WGL_ACCUM_GREEN_BITS_ARB 0x201F
#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_AUX_BUFFERS_ARB 0x2024
#define WGL_NO_ACCELERATION_ARB 0x2025
#define WGL_GENERIC_ACCELERATION_ARB 0x2026
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_SWAP_EXCHANGE_ARB 0x2028
#define WGL_SWAP_COPY_ARB 0x2029
#define WGL_SWAP_UNDEFINED_ARB 0x202A
#define WGL_TYPE_RGBA_ARB 0x202B
#define WGL_TYPE_COLORINDEX_ARB 0x202C
#endif
#ifndef WGL_ARB_multisample
#define WGL_SAMPLE_BUFFERS_ARB 0x2041
#define WGL_SAMPLES_ARB 0x2042
#endif
#endif // SDL_VIDEO_OPENGL_WGL
#endif // SDL_windowsopengl_h_
@@ -0,0 +1,142 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_WINDOWS) && defined(SDL_VIDEO_OPENGL_EGL) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#include "SDL_windowsvideo.h"
#include "SDL_windowsopengles.h"
#include "SDL_windowsopengl.h"
#include "SDL_windowswindow.h"
// EGL implementation of SDL OpenGL support
bool WIN_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
// If the profile requested is not GL ES, switch over to WIN_GL functions
if (_this->gl_config.profile_mask != SDL_GL_CONTEXT_PROFILE_ES &&
!SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) {
#ifdef SDL_VIDEO_OPENGL_WGL
WIN_GLES_UnloadLibrary(_this);
_this->GL_LoadLibrary = WIN_GL_LoadLibrary;
_this->GL_GetProcAddress = WIN_GL_GetProcAddress;
_this->GL_UnloadLibrary = WIN_GL_UnloadLibrary;
_this->GL_CreateContext = WIN_GL_CreateContext;
_this->GL_MakeCurrent = WIN_GL_MakeCurrent;
_this->GL_SetSwapInterval = WIN_GL_SetSwapInterval;
_this->GL_GetSwapInterval = WIN_GL_GetSwapInterval;
_this->GL_SwapWindow = WIN_GL_SwapWindow;
_this->GL_DestroyContext = WIN_GL_DestroyContext;
_this->GL_GetEGLSurface = NULL;
return WIN_GL_LoadLibrary(_this, path);
#else
return SDL_SetError("SDL not configured with OpenGL/WGL support");
#endif
}
if (!_this->egl_data) {
return SDL_EGL_LoadLibrary(_this, NULL, EGL_DEFAULT_DISPLAY, _this->gl_config.egl_platform);
}
return true;
}
SDL_GLContext WIN_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_GLContext context;
SDL_WindowData *data = window->internal;
#ifdef SDL_VIDEO_OPENGL_WGL
if (_this->gl_config.profile_mask != SDL_GL_CONTEXT_PROFILE_ES &&
!SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) {
// Switch to WGL based functions
WIN_GLES_UnloadLibrary(_this);
_this->GL_LoadLibrary = WIN_GL_LoadLibrary;
_this->GL_GetProcAddress = WIN_GL_GetProcAddress;
_this->GL_UnloadLibrary = WIN_GL_UnloadLibrary;
_this->GL_CreateContext = WIN_GL_CreateContext;
_this->GL_MakeCurrent = WIN_GL_MakeCurrent;
_this->GL_SetSwapInterval = WIN_GL_SetSwapInterval;
_this->GL_GetSwapInterval = WIN_GL_GetSwapInterval;
_this->GL_SwapWindow = WIN_GL_SwapWindow;
_this->GL_DestroyContext = WIN_GL_DestroyContext;
_this->GL_GetEGLSurface = NULL;
if (!WIN_GL_LoadLibrary(_this, NULL)) {
return NULL;
}
return WIN_GL_CreateContext(_this, window);
}
#endif
context = SDL_EGL_CreateContext(_this, data->egl_surface);
return context;
}
bool WIN_GLES_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context)
{
return SDL_EGL_DestroyContext(_this, context);
}
/* *INDENT-OFF* */ // clang-format off
SDL_EGL_SwapWindow_impl(WIN)
SDL_EGL_MakeCurrent_impl(WIN)
/* *INDENT-ON* */ // clang-format on
bool WIN_GLES_SetupWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
// The current context is lost in here; save it and reset it.
SDL_WindowData *windowdata = window->internal;
SDL_Window *current_win = SDL_GL_GetCurrentWindow();
SDL_GLContext current_ctx = SDL_GL_GetCurrentContext();
if (!_this->egl_data) {
// !!! FIXME: commenting out this assertion is (I think) incorrect; figure out why driver_loaded is wrong for ANGLE instead. --ryan.
#if 0 // When hint SDL_HINT_OPENGL_ES_DRIVER is set to "1" (e.g. for ANGLE support), _this->gl_config.driver_loaded can be 1, while the below lines function.
SDL_assert(!_this->gl_config.driver_loaded);
#endif
if (!SDL_EGL_LoadLibrary(_this, NULL, EGL_DEFAULT_DISPLAY, _this->gl_config.egl_platform)) {
SDL_EGL_UnloadLibrary(_this);
return false;
}
_this->gl_config.driver_loaded = 1;
}
// Create the GLES window surface
windowdata->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)windowdata->hwnd);
if (windowdata->egl_surface == EGL_NO_SURFACE) {
return SDL_SetError("Could not create GLES window surface");
}
return WIN_GLES_MakeCurrent(_this, current_win, current_ctx);
}
EGLSurface WIN_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *windowdata = window->internal;
return windowdata->egl_surface;
}
#endif // SDL_VIDEO_DRIVER_WINDOWS && SDL_VIDEO_OPENGL_EGL
@@ -0,0 +1,48 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_winopengles_h_
#define SDL_winopengles_h_
#ifdef SDL_VIDEO_OPENGL_EGL
#include "../SDL_sysvideo.h"
#include "../SDL_egl_c.h"
// OpenGLES functions
#define WIN_GLES_GetAttribute SDL_EGL_GetAttribute
#define WIN_GLES_GetProcAddress SDL_EGL_GetProcAddressInternal
#define WIN_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
#define WIN_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
#define WIN_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
extern bool WIN_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern SDL_GLContext WIN_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
extern bool WIN_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool WIN_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context);
extern bool WIN_GLES_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context);
extern bool WIN_GLES_SetupWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern SDL_EGLSurface WIN_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window);
#endif // SDL_VIDEO_OPENGL_EGL
#endif // SDL_winopengles_h_
@@ -0,0 +1,264 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_WINDOWS)
#include "SDL_windowsvideo.h"
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#include "SDL_windowsevents.h"
#include "../../joystick/usb_ids.h"
#include "../../events/SDL_events_c.h"
#define ENABLE_RAW_MOUSE_INPUT 0x01
#define ENABLE_RAW_KEYBOARD_INPUT 0x02
typedef struct
{
bool done;
Uint32 flags;
HANDLE ready_event;
HANDLE done_event;
HANDLE thread;
} RawInputThreadData;
static RawInputThreadData thread_data = {
false,
0,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE
};
static DWORD WINAPI WIN_RawInputThread(LPVOID param)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
RawInputThreadData *data = (RawInputThreadData *)param;
RAWINPUTDEVICE devices[2];
HWND window;
UINT count = 0;
window = CreateWindowEx(0, TEXT("Message"), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
if (!window) {
return 0;
}
SDL_zeroa(devices);
if (data->flags & ENABLE_RAW_MOUSE_INPUT) {
devices[count].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP;
devices[count].usUsage = USB_USAGE_GENERIC_MOUSE;
devices[count].dwFlags = 0;
devices[count].hwndTarget = window;
++count;
}
if (data->flags & ENABLE_RAW_KEYBOARD_INPUT) {
devices[count].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP;
devices[count].usUsage = USB_USAGE_GENERIC_KEYBOARD;
devices[count].dwFlags = 0;
devices[count].hwndTarget = window;
++count;
}
if (!RegisterRawInputDevices(devices, count, sizeof(devices[0]))) {
DestroyWindow(window);
return 0;
}
// Make sure we get events as soon as possible
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
// Tell the parent we're ready to go!
SetEvent(data->ready_event);
while (!data->done) {
Uint64 idle_begin = SDL_GetTicksNS();
DWORD result = MsgWaitForMultipleObjects(1, &data->done_event, FALSE, INFINITE, QS_RAWINPUT);
Uint64 idle_end = SDL_GetTicksNS();
if (result != (WAIT_OBJECT_0 + 1)) {
break;
}
// Clear the queue status so MsgWaitForMultipleObjects() will wait again
(void)GetQueueStatus(QS_RAWINPUT);
Uint64 idle_time = idle_end - idle_begin;
Uint64 usb_8khz_interval = SDL_US_TO_NS(125);
Uint64 poll_start = idle_time < usb_8khz_interval ? _this->internal->last_rawinput_poll : idle_end;
WIN_PollRawInput(_this, poll_start);
}
devices[0].dwFlags |= RIDEV_REMOVE;
devices[0].hwndTarget = NULL;
devices[1].dwFlags |= RIDEV_REMOVE;
devices[1].hwndTarget = NULL;
RegisterRawInputDevices(devices, count, sizeof(devices[0]));
DestroyWindow(window);
return 0;
}
static void CleanupRawInputThreadData(RawInputThreadData *data)
{
if (data->thread != INVALID_HANDLE_VALUE) {
data->done = true;
SetEvent(data->done_event);
WaitForSingleObject(data->thread, 3000);
CloseHandle(data->thread);
data->thread = INVALID_HANDLE_VALUE;
}
if (data->ready_event != INVALID_HANDLE_VALUE) {
CloseHandle(data->ready_event);
data->ready_event = INVALID_HANDLE_VALUE;
}
if (data->done_event != INVALID_HANDLE_VALUE) {
CloseHandle(data->done_event);
data->done_event = INVALID_HANDLE_VALUE;
}
}
static bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags)
{
bool result = false;
CleanupRawInputThreadData(&thread_data);
if (flags) {
HANDLE handles[2];
thread_data.flags = flags;
thread_data.ready_event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (thread_data.ready_event == INVALID_HANDLE_VALUE) {
WIN_SetError("CreateEvent");
goto done;
}
thread_data.done = false;
thread_data.done_event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (thread_data.done_event == INVALID_HANDLE_VALUE) {
WIN_SetError("CreateEvent");
goto done;
}
thread_data.thread = CreateThread(NULL, 0, WIN_RawInputThread, &thread_data, 0, NULL);
if (thread_data.thread == INVALID_HANDLE_VALUE) {
WIN_SetError("CreateThread");
goto done;
}
// Wait for the thread to signal ready or exit
handles[0] = thread_data.ready_event;
handles[1] = thread_data.thread;
if (WaitForMultipleObjects(2, handles, FALSE, INFINITE) != WAIT_OBJECT_0) {
SDL_SetError("Couldn't set up raw input handling");
goto done;
}
result = true;
} else {
result = true;
}
done:
if (!result) {
CleanupRawInputThreadData(&thread_data);
}
return result;
}
static bool WIN_UpdateRawInputEnabled(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
Uint32 flags = 0;
if (data->raw_mouse_enabled) {
flags |= ENABLE_RAW_MOUSE_INPUT;
}
if (data->raw_keyboard_enabled) {
flags |= ENABLE_RAW_KEYBOARD_INPUT;
}
if (flags != data->raw_input_enabled) {
if (WIN_SetRawInputEnabled(_this, flags)) {
data->raw_input_enabled = flags;
} else {
return false;
}
}
return true;
}
bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled)
{
SDL_VideoData *data = _this->internal;
data->raw_mouse_enabled = enabled;
if (data->gameinput_context) {
if (!WIN_UpdateGameInputEnabled(_this)) {
data->raw_mouse_enabled = !enabled;
return false;
}
} else {
if (!WIN_UpdateRawInputEnabled(_this)) {
data->raw_mouse_enabled = !enabled;
return false;
}
}
return true;
}
bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled)
{
SDL_VideoData *data = _this->internal;
data->raw_keyboard_enabled = enabled;
if (data->gameinput_context) {
if (!WIN_UpdateGameInputEnabled(_this)) {
data->raw_keyboard_enabled = !enabled;
return false;
}
} else {
if (!WIN_UpdateRawInputEnabled(_this)) {
data->raw_keyboard_enabled = !enabled;
return false;
}
}
return true;
}
#else
bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled)
{
return SDL_Unsupported();
}
bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled)
{
return SDL_Unsupported();
}
#endif // !SDL_PLATFORM_XBOXONE && !SDL_PLATFORM_XBOXSERIES
#endif // SDL_VIDEO_DRIVER_WINDOWS
@@ -0,0 +1,30 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_windowsrawinput_h_
#define SDL_windowsrawinput_h_
extern bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled);
extern bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled);
extern bool WIN_RefreshRawInputEnabled(SDL_VideoDevice *_this);
#endif // SDL_windowsrawinput_h_
@@ -0,0 +1,125 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#include "SDL_windowsvideo.h"
#include "SDL_windowsshape.h"
static void AddRegion(HRGN *mask, int x1, int y1, int x2, int y2)
{
HRGN region = CreateRectRgn(x1, y1, x2, y2);
if (*mask) {
CombineRgn(*mask, *mask, region, RGN_OR);
DeleteObject(region);
} else {
*mask = region;
}
}
static HRGN GenerateSpanListRegion(SDL_Surface *shape, int offset_x, int offset_y)
{
HRGN mask = NULL;
int x, y;
int span_start = -1;
for (y = 0; y < shape->h; ++y) {
const Uint8 *a = (const Uint8 *)shape->pixels + y * shape->pitch;
for (x = 0; x < shape->w; ++x) {
if (*a == SDL_ALPHA_TRANSPARENT) {
if (span_start != -1) {
AddRegion(&mask, offset_x + span_start, offset_y + y, offset_x + x, offset_y + y + 1);
span_start = -1;
}
} else {
if (span_start == -1) {
span_start = x;
}
}
a += 4;
}
if (span_start != -1) {
// Add the final span
AddRegion(&mask, offset_x + span_start, offset_y + y, offset_x + x, offset_y + y + 1);
span_start = -1;
}
}
return mask;
}
bool WIN_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape)
{
SDL_WindowData *data = window->internal;
HRGN mask = NULL;
// Generate a set of spans for the region
if (shape) {
SDL_Surface *stretched = NULL;
RECT rect;
if (shape->w != window->w || shape->h != window->h) {
stretched = SDL_CreateSurface(window->w, window->h, SDL_PIXELFORMAT_ARGB32);
if (!stretched) {
return false;
}
if (!SDL_StretchSurface(shape, NULL, stretched, NULL, SDL_SCALEMODE_LINEAR)) {
SDL_DestroySurface(stretched);
return false;
}
shape = stretched;
}
rect.top = 0;
rect.left = 0;
rect.bottom = 0;
rect.right = 0;
if (!(SDL_GetWindowFlags(data->window) & SDL_WINDOW_BORDERLESS)) {
WIN_AdjustWindowRectForHWND(data->hwnd, &rect, 0);
}
mask = GenerateSpanListRegion(shape, -rect.left, -rect.top);
if (!(SDL_GetWindowFlags(data->window) & SDL_WINDOW_BORDERLESS)) {
// Add the window borders
// top
AddRegion(&mask, 0, 0, -rect.left + shape->w + rect.right + 1, -rect.top + 1);
// left
AddRegion(&mask, 0, -rect.top, -rect.left + 1, -rect.top + shape->h + 1);
// right
AddRegion(&mask, -rect.left + shape->w, -rect.top, -rect.left + shape->w + rect.right + 1, -rect.top + shape->h + 1);
// bottom
AddRegion(&mask, 0, -rect.top + shape->h, -rect.left + shape->w + rect.right + 1, -rect.top + shape->h + rect.bottom + 1);
}
if (stretched) {
SDL_DestroySurface(stretched);
}
}
if (!SetWindowRgn(data->hwnd, mask, TRUE)) {
DeleteObject(mask);
return WIN_SetError("SetWindowRgn failed");
}
return true;
}
#endif // defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
@@ -0,0 +1,28 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_windowsshape_h_
#define SDL_windowsshape_h_
extern bool WIN_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape);
#endif // SDL_windowsshape_h_
@@ -0,0 +1,798 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_WINDOWS
#ifdef SDL_VIDEO_VULKAN
#include "../SDL_vulkan_internal.h"
#endif
#include "../SDL_sysvideo.h"
#include "../SDL_pixels_c.h"
#include "../../SDL_hints_c.h"
#include "../../core/windows/SDL_hid.h"
#include "SDL_windowsvideo.h"
#include "SDL_windowsframebuffer.h"
#include "SDL_windowsmessagebox.h"
#include "SDL_windowsrawinput.h"
#include "SDL_windowsvulkan.h"
#ifdef HAVE_SHOBJIDL_CORE_H
#include <shobjidl_core.h>
#endif
#ifdef SDL_GDK_TEXTINPUT
#include "../gdk/SDL_gdktextinput.h"
#endif
// #define HIGHDPI_DEBUG
// Initialization/Query functions
static bool WIN_VideoInit(SDL_VideoDevice *_this);
static void WIN_VideoQuit(SDL_VideoDevice *_this);
// Hints
bool g_WindowsEnableMessageLoop = true;
bool g_WindowsEnableMenuMnemonics = false;
bool g_WindowFrameUsableWhileCursorHidden = true;
static void SDLCALL UpdateWindowsRawKeyboard(void *userdata, const char *name, const char *oldValue, const char *newValue)
{
SDL_VideoDevice *_this = (SDL_VideoDevice *)userdata;
bool enabled = SDL_GetStringBoolean(newValue, false);
WIN_SetRawKeyboardEnabled(_this, enabled);
}
static void SDLCALL UpdateWindowsEnableMessageLoop(void *userdata, const char *name, const char *oldValue, const char *newValue)
{
g_WindowsEnableMessageLoop = SDL_GetStringBoolean(newValue, true);
}
static void SDLCALL UpdateWindowsEnableMenuMnemonics(void *userdata, const char *name, const char *oldValue, const char *newValue)
{
g_WindowsEnableMenuMnemonics = SDL_GetStringBoolean(newValue, false);
}
static void SDLCALL UpdateWindowFrameUsableWhileCursorHidden(void *userdata, const char *name, const char *oldValue, const char *newValue)
{
g_WindowFrameUsableWhileCursorHidden = SDL_GetStringBoolean(newValue, true);
}
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
static bool WIN_SuspendScreenSaver(SDL_VideoDevice *_this)
{
DWORD result;
if (_this->suspend_screensaver) {
result = SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
} else {
result = SetThreadExecutionState(ES_CONTINUOUS);
}
if (result == 0) {
SDL_SetError("SetThreadExecutionState() failed");
return false;
}
return true;
}
#endif
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
extern void D3D12_XBOX_GetResolution(Uint32 *width, Uint32 *height);
#endif
// Windows driver bootstrap functions
static void WIN_DeleteDevice(SDL_VideoDevice *device)
{
SDL_VideoData *data = device->internal;
SDL_UnregisterApp();
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
if (data->userDLL) {
SDL_UnloadObject(data->userDLL);
}
if (data->shcoreDLL) {
SDL_UnloadObject(data->shcoreDLL);
}
if (data->dwmapiDLL) {
SDL_UnloadObject(data->dwmapiDLL);
}
#endif
#ifdef HAVE_DXGI_H
if (data->pDXGIFactory) {
IDXGIFactory_Release(data->pDXGIFactory);
}
if (data->dxgiDLL) {
SDL_UnloadObject(data->dxgiDLL);
}
#endif
SDL_free(device->internal->rawinput);
SDL_free(device->internal);
SDL_free(device);
}
static SDL_VideoDevice *WIN_CreateDevice(void)
{
SDL_VideoDevice *device;
SDL_VideoData *data;
SDL_RegisterApp(NULL, 0, NULL);
// Initialize all variables that we clean on shutdown
device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
if (device) {
data = (SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData));
} else {
data = NULL;
}
if (!data) {
SDL_UnregisterApp();
SDL_free(device);
return NULL;
}
device->internal = data;
device->system_theme = WIN_GetSystemTheme();
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
data->userDLL = SDL_LoadObject("USER32.DLL");
if (data->userDLL) {
/* *INDENT-OFF* */ // clang-format off
data->CloseTouchInputHandle = (BOOL (WINAPI *)(HTOUCHINPUT))SDL_LoadFunction(data->userDLL, "CloseTouchInputHandle");
data->GetTouchInputInfo = (BOOL (WINAPI *)(HTOUCHINPUT, UINT, PTOUCHINPUT, int)) SDL_LoadFunction(data->userDLL, "GetTouchInputInfo");
data->RegisterTouchWindow = (BOOL (WINAPI *)(HWND, ULONG))SDL_LoadFunction(data->userDLL, "RegisterTouchWindow");
data->SetProcessDPIAware = (BOOL (WINAPI *)(void))SDL_LoadFunction(data->userDLL, "SetProcessDPIAware");
data->SetProcessDpiAwarenessContext = (BOOL (WINAPI *)(DPI_AWARENESS_CONTEXT))SDL_LoadFunction(data->userDLL, "SetProcessDpiAwarenessContext");
data->SetThreadDpiAwarenessContext = (DPI_AWARENESS_CONTEXT (WINAPI *)(DPI_AWARENESS_CONTEXT))SDL_LoadFunction(data->userDLL, "SetThreadDpiAwarenessContext");
data->GetThreadDpiAwarenessContext = (DPI_AWARENESS_CONTEXT (WINAPI *)(void))SDL_LoadFunction(data->userDLL, "GetThreadDpiAwarenessContext");
data->GetAwarenessFromDpiAwarenessContext = (DPI_AWARENESS (WINAPI *)(DPI_AWARENESS_CONTEXT))SDL_LoadFunction(data->userDLL, "GetAwarenessFromDpiAwarenessContext");
data->EnableNonClientDpiScaling = (BOOL (WINAPI *)(HWND))SDL_LoadFunction(data->userDLL, "EnableNonClientDpiScaling");
data->AdjustWindowRectExForDpi = (BOOL (WINAPI *)(LPRECT, DWORD, BOOL, DWORD, UINT))SDL_LoadFunction(data->userDLL, "AdjustWindowRectExForDpi");
data->GetDpiForWindow = (UINT (WINAPI *)(HWND))SDL_LoadFunction(data->userDLL, "GetDpiForWindow");
data->AreDpiAwarenessContextsEqual = (BOOL (WINAPI *)(DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT))SDL_LoadFunction(data->userDLL, "AreDpiAwarenessContextsEqual");
data->IsValidDpiAwarenessContext = (BOOL (WINAPI *)(DPI_AWARENESS_CONTEXT))SDL_LoadFunction(data->userDLL, "IsValidDpiAwarenessContext");
data->GetDisplayConfigBufferSizes = (LONG (WINAPI *)(UINT32,UINT32 *,UINT32 *))SDL_LoadFunction(data->userDLL, "GetDisplayConfigBufferSizes");
data->QueryDisplayConfig = (LONG (WINAPI *)(UINT32,UINT32 *,DISPLAYCONFIG_PATH_INFO*,UINT32 *,DISPLAYCONFIG_MODE_INFO*,DISPLAYCONFIG_TOPOLOGY_ID*))SDL_LoadFunction(data->userDLL, "QueryDisplayConfig");
data->DisplayConfigGetDeviceInfo = (LONG (WINAPI *)(DISPLAYCONFIG_DEVICE_INFO_HEADER*))SDL_LoadFunction(data->userDLL, "DisplayConfigGetDeviceInfo");
data->GetPointerType = (BOOL (WINAPI *)(UINT32, POINTER_INPUT_TYPE *))SDL_LoadFunction(data->userDLL, "GetPointerType");
data->GetPointerPenInfo = (BOOL (WINAPI *)(UINT32, POINTER_PEN_INFO *))SDL_LoadFunction(data->userDLL, "GetPointerPenInfo");
/* *INDENT-ON* */ // clang-format on
} else {
SDL_ClearError();
}
data->shcoreDLL = SDL_LoadObject("SHCORE.DLL");
if (data->shcoreDLL) {
/* *INDENT-OFF* */ // clang-format off
data->GetDpiForMonitor = (HRESULT (WINAPI *)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *))SDL_LoadFunction(data->shcoreDLL, "GetDpiForMonitor");
data->SetProcessDpiAwareness = (HRESULT (WINAPI *)(PROCESS_DPI_AWARENESS))SDL_LoadFunction(data->shcoreDLL, "SetProcessDpiAwareness");
/* *INDENT-ON* */ // clang-format on
} else {
SDL_ClearError();
}
data->dwmapiDLL = SDL_LoadObject("DWMAPI.DLL");
if (data->dwmapiDLL) {
/* *INDENT-OFF* */ // clang-format off
data->DwmFlush = (HRESULT (WINAPI *)(void))SDL_LoadFunction(data->dwmapiDLL, "DwmFlush");
data->DwmEnableBlurBehindWindow = (HRESULT (WINAPI *)(HWND hwnd, const DWM_BLURBEHIND *pBlurBehind))SDL_LoadFunction(data->dwmapiDLL, "DwmEnableBlurBehindWindow");
data->DwmSetWindowAttribute = (HRESULT (WINAPI *)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute))SDL_LoadFunction(data->dwmapiDLL, "DwmSetWindowAttribute");
/* *INDENT-ON* */ // clang-format on
} else {
SDL_ClearError();
}
#endif // #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#ifdef HAVE_DXGI_H
data->dxgiDLL = SDL_LoadObject("DXGI.DLL");
if (data->dxgiDLL) {
/* *INDENT-OFF* */ // clang-format off
typedef HRESULT (WINAPI *CreateDXGI_t)(REFIID riid, void **ppFactory);
/* *INDENT-ON* */ // clang-format on
CreateDXGI_t CreateDXGI;
CreateDXGI = (CreateDXGI_t)SDL_LoadFunction(data->dxgiDLL, "CreateDXGIFactory");
if (CreateDXGI) {
GUID dxgiGUID = { 0x7b7166ec, 0x21c7, 0x44ae, { 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69 } };
CreateDXGI(&dxgiGUID, (void **)&data->pDXGIFactory);
}
}
#endif
// Set the function pointers
device->VideoInit = WIN_VideoInit;
device->VideoQuit = WIN_VideoQuit;
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
device->RefreshDisplays = WIN_RefreshDisplays;
device->GetDisplayBounds = WIN_GetDisplayBounds;
device->GetDisplayUsableBounds = WIN_GetDisplayUsableBounds;
device->GetDisplayModes = WIN_GetDisplayModes;
device->SetDisplayMode = WIN_SetDisplayMode;
#endif
device->PumpEvents = WIN_PumpEvents;
device->WaitEventTimeout = WIN_WaitEventTimeout;
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
device->SendWakeupEvent = WIN_SendWakeupEvent;
device->SuspendScreenSaver = WIN_SuspendScreenSaver;
#endif
device->CreateSDLWindow = WIN_CreateWindow;
device->SetWindowTitle = WIN_SetWindowTitle;
device->SetWindowIcon = WIN_SetWindowIcon;
device->SetWindowPosition = WIN_SetWindowPosition;
device->SetWindowSize = WIN_SetWindowSize;
device->GetWindowBordersSize = WIN_GetWindowBordersSize;
device->GetWindowSizeInPixels = WIN_GetWindowSizeInPixels;
device->SetWindowOpacity = WIN_SetWindowOpacity;
device->ShowWindow = WIN_ShowWindow;
device->HideWindow = WIN_HideWindow;
device->RaiseWindow = WIN_RaiseWindow;
device->MaximizeWindow = WIN_MaximizeWindow;
device->MinimizeWindow = WIN_MinimizeWindow;
device->RestoreWindow = WIN_RestoreWindow;
device->SetWindowBordered = WIN_SetWindowBordered;
device->SetWindowResizable = WIN_SetWindowResizable;
device->SetWindowAlwaysOnTop = WIN_SetWindowAlwaysOnTop;
device->SetWindowFullscreen = WIN_SetWindowFullscreen;
device->SetWindowParent = WIN_SetWindowParent;
device->SetWindowModal = WIN_SetWindowModal;
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
device->GetWindowICCProfile = WIN_GetWindowICCProfile;
device->SetWindowMouseRect = WIN_SetWindowMouseRect;
device->SetWindowMouseGrab = WIN_SetWindowMouseGrab;
device->SetWindowKeyboardGrab = WIN_SetWindowKeyboardGrab;
#endif
device->DestroyWindow = WIN_DestroyWindow;
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
device->CreateWindowFramebuffer = WIN_CreateWindowFramebuffer;
device->UpdateWindowFramebuffer = WIN_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer;
device->OnWindowEnter = WIN_OnWindowEnter;
device->SetWindowHitTest = WIN_SetWindowHitTest;
device->AcceptDragAndDrop = WIN_AcceptDragAndDrop;
device->FlashWindow = WIN_FlashWindow;
device->ApplyWindowProgress = WIN_ApplyWindowProgress;
device->ShowWindowSystemMenu = WIN_ShowWindowSystemMenu;
device->SetWindowFocusable = WIN_SetWindowFocusable;
device->UpdateWindowShape = WIN_UpdateWindowShape;
#endif
#ifdef SDL_VIDEO_OPENGL_WGL
device->GL_LoadLibrary = WIN_GL_LoadLibrary;
device->GL_GetProcAddress = WIN_GL_GetProcAddress;
device->GL_UnloadLibrary = WIN_GL_UnloadLibrary;
device->GL_CreateContext = WIN_GL_CreateContext;
device->GL_MakeCurrent = WIN_GL_MakeCurrent;
device->GL_SetSwapInterval = WIN_GL_SetSwapInterval;
device->GL_GetSwapInterval = WIN_GL_GetSwapInterval;
device->GL_SwapWindow = WIN_GL_SwapWindow;
device->GL_DestroyContext = WIN_GL_DestroyContext;
device->GL_GetEGLSurface = NULL;
#endif
#ifdef SDL_VIDEO_OPENGL_EGL
#ifdef SDL_VIDEO_OPENGL_WGL
if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) {
#endif
// Use EGL based functions
device->GL_LoadLibrary = WIN_GLES_LoadLibrary;
device->GL_GetProcAddress = WIN_GLES_GetProcAddress;
device->GL_UnloadLibrary = WIN_GLES_UnloadLibrary;
device->GL_CreateContext = WIN_GLES_CreateContext;
device->GL_MakeCurrent = WIN_GLES_MakeCurrent;
device->GL_SetSwapInterval = WIN_GLES_SetSwapInterval;
device->GL_GetSwapInterval = WIN_GLES_GetSwapInterval;
device->GL_SwapWindow = WIN_GLES_SwapWindow;
device->GL_DestroyContext = WIN_GLES_DestroyContext;
device->GL_GetEGLSurface = WIN_GLES_GetEGLSurface;
#ifdef SDL_VIDEO_OPENGL_WGL
}
#endif
#endif
#ifdef SDL_VIDEO_VULKAN
device->Vulkan_LoadLibrary = WIN_Vulkan_LoadLibrary;
device->Vulkan_UnloadLibrary = WIN_Vulkan_UnloadLibrary;
device->Vulkan_GetInstanceExtensions = WIN_Vulkan_GetInstanceExtensions;
device->Vulkan_CreateSurface = WIN_Vulkan_CreateSurface;
device->Vulkan_DestroySurface = WIN_Vulkan_DestroySurface;
device->Vulkan_GetPresentationSupport = WIN_Vulkan_GetPresentationSupport;
#endif
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
device->StartTextInput = WIN_StartTextInput;
device->StopTextInput = WIN_StopTextInput;
device->UpdateTextInputArea = WIN_UpdateTextInputArea;
device->ClearComposition = WIN_ClearComposition;
device->SetClipboardData = WIN_SetClipboardData;
device->GetClipboardData = WIN_GetClipboardData;
device->HasClipboardData = WIN_HasClipboardData;
#endif
#ifdef SDL_GDK_TEXTINPUT
GDK_EnsureHints();
device->StartTextInput = GDK_StartTextInput;
device->StopTextInput = GDK_StopTextInput;
device->UpdateTextInputArea = GDK_UpdateTextInputArea;
device->ClearComposition = GDK_ClearComposition;
device->HasScreenKeyboardSupport = GDK_HasScreenKeyboardSupport;
device->ShowScreenKeyboard = GDK_ShowScreenKeyboard;
device->HideScreenKeyboard = GDK_HideScreenKeyboard;
device->IsScreenKeyboardShown = GDK_IsScreenKeyboardShown;
#endif
device->free = WIN_DeleteDevice;
device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT |
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
return device;
}
VideoBootStrap WINDOWS_bootstrap = {
"windows", "SDL Windows video driver", WIN_CreateDevice,
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
WIN_ShowMessageBox,
#else
NULL,
#endif
false
};
static BOOL WIN_DeclareDPIAwareUnaware(SDL_VideoDevice *_this)
{
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
SDL_VideoData *data = _this->internal;
if (data->SetProcessDpiAwarenessContext) {
return data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
} else if (data->SetProcessDpiAwareness) {
// Windows 8.1
return SUCCEEDED(data->SetProcessDpiAwareness(PROCESS_DPI_UNAWARE));
}
#endif
return FALSE;
}
static BOOL WIN_DeclareDPIAwareSystem(SDL_VideoDevice *_this)
{
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
SDL_VideoData *data = _this->internal;
if (data->SetProcessDpiAwarenessContext) {
// Windows 10, version 1607
return data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
} else if (data->SetProcessDpiAwareness) {
// Windows 8.1
return SUCCEEDED(data->SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE));
} else if (data->SetProcessDPIAware) {
// Windows Vista
return data->SetProcessDPIAware();
}
#endif
return FALSE;
}
static BOOL WIN_DeclareDPIAwarePerMonitor(SDL_VideoDevice *_this)
{
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
SDL_VideoData *data = _this->internal;
if (data->SetProcessDpiAwarenessContext) {
// Windows 10, version 1607
return data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
} else if (data->SetProcessDpiAwareness) {
// Windows 8.1
return SUCCEEDED(data->SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE));
} else {
// Older OS: fall back to system DPI aware
return WIN_DeclareDPIAwareSystem(_this);
}
#else
return FALSE;
#endif
}
static BOOL WIN_DeclareDPIAwarePerMonitorV2(SDL_VideoDevice *_this)
{
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
return FALSE;
#else
SDL_VideoData *data = _this->internal;
// Declare DPI aware (may have been done in external code or a manifest, as well)
if (data->SetProcessDpiAwarenessContext) {
// Windows 10, version 1607
/* NOTE: SetThreadDpiAwarenessContext doesn't work here with OpenGL - the OpenGL contents
end up still getting OS scaled. (tested on Windows 10 21H1 19043.1348, NVIDIA 496.49)
NOTE: Enabling DPI awareness through Windows Explorer
(right click .exe -> Properties -> Compatibility -> High DPI Settings ->
check "Override high DPI Scaling behaviour", select Application) gives
a DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE context (at least on Windows 10 21H1), and
setting DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 will fail.
NOTE: Entering exclusive fullscreen in a DPI_AWARENESS_CONTEXT_UNAWARE process
appears to cause Windows to change the .exe manifest to DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE
on future launches. This means attempting to use DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
will fail in the future until you manually clear the "Override high DPI Scaling behaviour"
setting in Windows Explorer (tested on Windows 10 21H2).
*/
if (data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) {
return TRUE;
} else {
return WIN_DeclareDPIAwarePerMonitor(_this);
}
} else {
// Older OS: fall back to per-monitor (or system)
return WIN_DeclareDPIAwarePerMonitor(_this);
}
#endif
}
#ifdef HIGHDPI_DEBUG
static const char *WIN_GetDPIAwareness(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
if (data->GetThreadDpiAwarenessContext && data->AreDpiAwarenessContextsEqual) {
DPI_AWARENESS_CONTEXT context = data->GetThreadDpiAwarenessContext();
if (data->AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE)) {
return "unaware";
} else if (data->AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)) {
return "system";
} else if (data->AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) {
return "permonitor";
} else if (data->AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) {
return "permonitorv2";
} else if (data->AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED)) {
return "unaware_gdiscaled";
}
}
return "";
}
#endif
static void WIN_InitDPIAwareness(SDL_VideoDevice *_this)
{
const char *hint = SDL_GetHint("SDL_WINDOWS_DPI_AWARENESS");
if (!hint || SDL_strcmp(hint, "permonitorv2") == 0) {
WIN_DeclareDPIAwarePerMonitorV2(_this);
} else if (SDL_strcmp(hint, "permonitor") == 0) {
WIN_DeclareDPIAwarePerMonitor(_this);
} else if (SDL_strcmp(hint, "system") == 0) {
WIN_DeclareDPIAwareSystem(_this);
} else if (SDL_strcmp(hint, "unaware") == 0) {
WIN_DeclareDPIAwareUnaware(_this);
}
}
static bool WIN_VideoInit(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
HRESULT hr;
hr = WIN_CoInitialize();
if (SUCCEEDED(hr)) {
data->coinitialized = true;
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
hr = OleInitialize(NULL);
if (SUCCEEDED(hr)) {
data->oleinitialized = true;
} else {
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "OleInitialize() failed: 0x%.8x, using fallback drag-n-drop functionality", (unsigned int)hr);
}
#endif // !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
} else {
SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "CoInitialize() failed: 0x%.8x, using fallback drag-n-drop functionality", (unsigned int)hr);
}
WIN_InitDPIAwareness(_this);
#ifdef HIGHDPI_DEBUG
SDL_Log("DPI awareness: %s", WIN_GetDPIAwareness(_this));
#endif
if (SDL_GetHintBoolean(SDL_HINT_WINDOWS_GAMEINPUT, true)) {
WIN_InitGameInput(_this);
}
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
// For Xbox, we just need to create the single display
{
SDL_DisplayMode mode;
SDL_zero(mode);
D3D12_XBOX_GetResolution(&mode.w, &mode.h);
mode.refresh_rate = 60.0f;
mode.format = SDL_PIXELFORMAT_ARGB8888;
SDL_AddBasicVideoDisplay(&mode);
}
#else // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
if (!WIN_InitModes(_this)) {
return false;
}
WIN_InitKeyboard(_this);
WIN_InitMouse(_this);
WIN_InitDeviceNotification();
if (!_this->internal->gameinput_context) {
WIN_CheckKeyboardAndMouseHotplug(_this, true);
}
#endif
SDL_AddHintCallback(SDL_HINT_WINDOWS_RAW_KEYBOARD, UpdateWindowsRawKeyboard, _this);
SDL_AddHintCallback(SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, UpdateWindowsEnableMessageLoop, NULL);
SDL_AddHintCallback(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, UpdateWindowsEnableMenuMnemonics, NULL);
SDL_AddHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
data->_SDL_WAKEUP = RegisterWindowMessageA("_SDL_WAKEUP");
#endif
#if defined(HAVE_SHOBJIDL_CORE_H)
data->WM_TASKBAR_BUTTON_CREATED = RegisterWindowMessageA("TaskbarButtonCreated");
#endif
return true;
}
void WIN_VideoQuit(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
SDL_RemoveHintCallback(SDL_HINT_WINDOWS_RAW_KEYBOARD, UpdateWindowsRawKeyboard, _this);
SDL_RemoveHintCallback(SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, UpdateWindowsEnableMessageLoop, NULL);
SDL_RemoveHintCallback(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, UpdateWindowsEnableMenuMnemonics, NULL);
SDL_RemoveHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
WIN_SetRawMouseEnabled(_this, false);
WIN_SetRawKeyboardEnabled(_this, false);
WIN_QuitGameInput(_this);
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
WIN_QuitModes(_this);
WIN_QuitDeviceNotification();
WIN_QuitKeyboard(_this);
WIN_QuitMouse(_this);
#if defined(HAVE_SHOBJIDL_CORE_H)
if (data->taskbar_list) {
IUnknown_Release(data->taskbar_list);
data->taskbar_list = NULL;
}
#endif
if (data->oleinitialized) {
OleUninitialize();
data->oleinitialized = false;
}
#endif // !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
if (data->coinitialized) {
WIN_CoUninitialize();
data->coinitialized = false;
}
}
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#define D3D_DEBUG_INFO
#include <d3d9.h>
#ifdef D3D_DEBUG_INFO
#ifndef D3D_SDK_VERSION
#define D3D_SDK_VERSION (32 | 0x80000000)
#endif
#ifndef D3D9b_SDK_VERSION
#define D3D9b_SDK_VERSION (31 | 0x80000000)
#endif
#else //
#ifndef D3D_SDK_VERSION
#define D3D_SDK_VERSION 32
#endif
#ifndef D3D9b_SDK_VERSION
#define D3D9b_SDK_VERSION 31
#endif
#endif
bool D3D_LoadDLL(void **pD3DDLL, IDirect3D9 **pDirect3D9Interface)
{
*pD3DDLL = SDL_LoadObject("D3D9.DLL");
if (*pD3DDLL) {
/* *INDENT-OFF* */ // clang-format off
typedef IDirect3D9 *(WINAPI *Direct3DCreate9_t)(UINT SDKVersion);
typedef HRESULT (WINAPI* Direct3DCreate9Ex_t)(UINT SDKVersion, IDirect3D9Ex **ppD3D);
/* *INDENT-ON* */ // clang-format on
Direct3DCreate9_t Direct3DCreate9Func;
if (SDL_GetHintBoolean(SDL_HINT_WINDOWS_USE_D3D9EX, false)) {
Direct3DCreate9Ex_t Direct3DCreate9ExFunc;
Direct3DCreate9ExFunc = (Direct3DCreate9Ex_t)SDL_LoadFunction(*pD3DDLL, "Direct3DCreate9Ex");
if (Direct3DCreate9ExFunc) {
IDirect3D9Ex *pDirect3D9ExInterface;
HRESULT hr = Direct3DCreate9ExFunc(D3D_SDK_VERSION, &pDirect3D9ExInterface);
if (SUCCEEDED(hr)) {
const GUID IDirect3D9_GUID = { 0x81bdcbca, 0x64d4, 0x426d, { 0xae, 0x8d, 0xad, 0x1, 0x47, 0xf4, 0x27, 0x5c } };
hr = IDirect3D9Ex_QueryInterface(pDirect3D9ExInterface, &IDirect3D9_GUID, (void **)pDirect3D9Interface);
IDirect3D9Ex_Release(pDirect3D9ExInterface);
if (SUCCEEDED(hr)) {
return true;
}
}
}
}
Direct3DCreate9Func = (Direct3DCreate9_t)SDL_LoadFunction(*pD3DDLL, "Direct3DCreate9");
if (Direct3DCreate9Func) {
*pDirect3D9Interface = Direct3DCreate9Func(D3D_SDK_VERSION);
if (*pDirect3D9Interface) {
return true;
}
}
SDL_UnloadObject(*pD3DDLL);
*pD3DDLL = NULL;
}
*pDirect3D9Interface = NULL;
return false;
}
int SDL_GetDirect3D9AdapterIndex(SDL_DisplayID displayID)
{
void *pD3DDLL;
IDirect3D9 *pD3D;
if (!D3D_LoadDLL(&pD3DDLL, &pD3D)) {
SDL_SetError("Unable to create Direct3D interface");
return -1;
} else {
SDL_DisplayData *pData = SDL_GetDisplayDriverData(displayID);
int adapterIndex = D3DADAPTER_DEFAULT;
if (!pData) {
SDL_SetError("Invalid display index");
adapterIndex = -1; // make sure we return something invalid
} else {
char *displayName = WIN_StringToUTF8W(pData->DeviceName);
unsigned int count = IDirect3D9_GetAdapterCount(pD3D);
unsigned int i;
for (i = 0; i < count; i++) {
D3DADAPTER_IDENTIFIER9 id;
IDirect3D9_GetAdapterIdentifier(pD3D, i, 0, &id);
if (SDL_strcmp(id.DeviceName, displayName) == 0) {
adapterIndex = i;
break;
}
}
SDL_free(displayName);
}
// free up the D3D stuff we inited
IDirect3D9_Release(pD3D);
SDL_UnloadObject(pD3DDLL);
return adapterIndex;
}
}
#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
bool SDL_GetDXGIOutputInfo(SDL_DisplayID displayID, int *adapterIndex, int *outputIndex)
{
#ifndef HAVE_DXGI_H
if (adapterIndex) {
*adapterIndex = -1;
}
if (outputIndex) {
*outputIndex = -1;
}
return SDL_SetError("SDL was compiled without DXGI support due to missing dxgi.h header");
#else
const SDL_VideoDevice *videodevice = SDL_GetVideoDevice();
const SDL_VideoData *videodata = videodevice ? videodevice->internal : NULL;
SDL_DisplayData *pData = SDL_GetDisplayDriverData(displayID);
int nAdapter, nOutput;
IDXGIAdapter *pDXGIAdapter;
IDXGIOutput *pDXGIOutput;
if (!adapterIndex) {
return SDL_InvalidParamError("adapterIndex");
}
if (!outputIndex) {
return SDL_InvalidParamError("outputIndex");
}
*adapterIndex = -1;
*outputIndex = -1;
if (!pData) {
return SDL_SetError("Invalid display index");
}
if (!videodata || !videodata->pDXGIFactory) {
return SDL_SetError("Unable to create DXGI interface");
}
nAdapter = 0;
while (*adapterIndex == -1 && SUCCEEDED(IDXGIFactory_EnumAdapters(videodata->pDXGIFactory, nAdapter, &pDXGIAdapter))) {
nOutput = 0;
while (*adapterIndex == -1 && SUCCEEDED(IDXGIAdapter_EnumOutputs(pDXGIAdapter, nOutput, &pDXGIOutput))) {
DXGI_OUTPUT_DESC outputDesc;
if (SUCCEEDED(IDXGIOutput_GetDesc(pDXGIOutput, &outputDesc))) {
if (SDL_wcscmp(outputDesc.DeviceName, pData->DeviceName) == 0) {
*adapterIndex = nAdapter;
*outputIndex = nOutput;
}
}
IDXGIOutput_Release(pDXGIOutput);
nOutput++;
}
IDXGIAdapter_Release(pDXGIAdapter);
nAdapter++;
}
if (*adapterIndex == -1) {
return SDL_SetError("Couldn't find matching adapter");
}
return true;
#endif
}
SDL_SystemTheme WIN_GetSystemTheme(void)
{
SDL_SystemTheme theme = SDL_SYSTEM_THEME_LIGHT;
HKEY hKey;
DWORD dwType = REG_DWORD;
DWORD value = ~0U;
DWORD length = sizeof(value);
// Technically this isn't the system theme, but it's the preference for applications
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
if (RegQueryValueExW(hKey, L"AppsUseLightTheme", 0, &dwType, (LPBYTE)&value, &length) == ERROR_SUCCESS) {
if (value == 0) {
theme = SDL_SYSTEM_THEME_DARK;
}
}
RegCloseKey(hKey);
}
return theme;
}
bool WIN_IsPerMonitorV2DPIAware(SDL_VideoDevice *_this)
{
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
SDL_VideoData *data = _this->internal;
if (data->AreDpiAwarenessContextsEqual && data->GetThreadDpiAwarenessContext) {
// Windows 10, version 1607
return data->AreDpiAwarenessContextsEqual(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, data->GetThreadDpiAwarenessContext());
}
#endif
return false;
}
#endif // SDL_VIDEO_DRIVER_WINDOWS
@@ -0,0 +1,560 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_windowsvideo_h_
#define SDL_windowsvideo_h_
#include "../../core/windows/SDL_windows.h"
#include "../SDL_sysvideo.h"
#ifdef HAVE_DXGI_H
#ifndef __cplusplus
#define CINTERFACE
#define COBJMACROS
#endif
#include <dxgi.h>
#endif
#if defined(_MSC_VER) && (_MSC_VER >= 1500) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#include <msctf.h>
#else
#include "SDL_msctf.h"
#endif
#include <imm.h>
#define MAX_CANDLIST 10
#define MAX_CANDLENGTH 256
#define MAX_CANDSIZE (sizeof(WCHAR) * MAX_CANDLIST * MAX_CANDLENGTH)
#include "SDL_windowsclipboard.h"
#include "SDL_windowsevents.h"
#include "SDL_windowsgameinput.h"
#include "SDL_windowsopengl.h"
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#include "SDL_windowsshape.h"
#include "SDL_windowskeyboard.h"
#include "SDL_windowsmodes.h"
#include "SDL_windowsmouse.h"
#include "SDL_windowsopengles.h"
#endif
#include "SDL_windowswindow.h"
#ifndef USER_DEFAULT_SCREEN_DPI
#define USER_DEFAULT_SCREEN_DPI 96
#endif
#if WINVER < 0x0601
// Touch input definitions
#define TWF_FINETOUCH 1
#define TWF_WANTPALM 2
#define TOUCHEVENTF_MOVE 0x0001
#define TOUCHEVENTF_DOWN 0x0002
#define TOUCHEVENTF_UP 0x0004
DECLARE_HANDLE(HTOUCHINPUT);
typedef struct _TOUCHINPUT
{
LONG x;
LONG y;
HANDLE hSource;
DWORD dwID;
DWORD dwFlags;
DWORD dwMask;
DWORD dwTime;
ULONG_PTR dwExtraInfo;
DWORD cxContact;
DWORD cyContact;
} TOUCHINPUT, *PTOUCHINPUT;
// More-robust display information in Vista...
// This is a huge amount of data to be stuffing into three API calls. :(
typedef struct DISPLAYCONFIG_PATH_SOURCE_INFO
{
LUID adapterId;
UINT32 id;
union
{
UINT32 modeInfoIdx;
struct
{
UINT32 cloneGroupId : 16;
UINT32 sourceModeInfoIdx : 16;
} DUMMYSTRUCTNAME;
} DUMMYUNIONNAME;
UINT32 statusFlags;
} DISPLAYCONFIG_PATH_SOURCE_INFO;
typedef struct DISPLAYCONFIG_RATIONAL
{
UINT32 Numerator;
UINT32 Denominator;
} DISPLAYCONFIG_RATIONAL;
typedef struct DISPLAYCONFIG_PATH_TARGET_INFO
{
LUID adapterId;
UINT32 id;
union
{
UINT32 modeInfoIdx;
struct
{
UINT32 desktopModeInfoIdx : 16;
UINT32 targetModeInfoIdx : 16;
} DUMMYSTRUCTNAME;
} DUMMYUNIONNAME;
UINT32 /*DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY*/ outputTechnology;
UINT32 /*DISPLAYCONFIG_ROTATION*/ rotation;
UINT32 /*DISPLAYCONFIG_SCALING*/ scaling;
DISPLAYCONFIG_RATIONAL refreshRate;
UINT32 /*DISPLAYCONFIG_SCANLINE_ORDERING*/ scanLineOrdering;
BOOL targetAvailable;
UINT32 statusFlags;
} DISPLAYCONFIG_PATH_TARGET_INFO;
typedef struct DISPLAYCONFIG_PATH_INFO
{
DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo;
DISPLAYCONFIG_PATH_TARGET_INFO targetInfo;
UINT32 flags;
} DISPLAYCONFIG_PATH_INFO;
typedef enum
{
DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1,
DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2,
DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE = 3,
DISPLAYCONFIG_MODE_INFO_TYPE_FORCE_UINT32 = 0xFFFFFFFF
} DISPLAYCONFIG_MODE_INFO_TYPE;
typedef struct DISPLAYCONFIG_2DREGION
{
UINT32 cx;
UINT32 cy;
} DISPLAYCONFIG_2DREGION;
typedef struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO
{
UINT64 pixelRate;
DISPLAYCONFIG_RATIONAL hSyncFreq;
DISPLAYCONFIG_RATIONAL vSyncFreq;
DISPLAYCONFIG_2DREGION activeSize;
DISPLAYCONFIG_2DREGION totalSize;
union
{
struct
{
UINT32 videoStandard : 16;
// Vertical refresh frequency divider
UINT32 vSyncFreqDivider : 6;
UINT32 reserved : 10;
} AdditionalSignalInfo;
UINT32 videoStandard;
} DUMMYUNIONNAME;
// Scan line ordering (e.g. progressive, interlaced).
UINT32 /*DISPLAYCONFIG_SCANLINE_ORDERING*/ scanLineOrdering;
} DISPLAYCONFIG_VIDEO_SIGNAL_INFO;
typedef struct DISPLAYCONFIG_SOURCE_MODE
{
UINT32 width;
UINT32 height;
UINT32 /*DISPLAYCONFIG_PIXELFORMAT*/ pixelFormat;
POINTL position;
} DISPLAYCONFIG_SOURCE_MODE;
typedef struct DISPLAYCONFIG_TARGET_MODE
{
DISPLAYCONFIG_VIDEO_SIGNAL_INFO targetVideoSignalInfo;
} DISPLAYCONFIG_TARGET_MODE;
typedef struct DISPLAYCONFIG_DESKTOP_IMAGE_INFO
{
POINTL PathSourceSize;
RECTL DesktopImageRegion;
RECTL DesktopImageClip;
} DISPLAYCONFIG_DESKTOP_IMAGE_INFO;
typedef struct DISPLAYCONFIG_MODE_INFO
{
DISPLAYCONFIG_MODE_INFO_TYPE infoType;
UINT32 id;
LUID adapterId;
union
{
DISPLAYCONFIG_TARGET_MODE targetMode;
DISPLAYCONFIG_SOURCE_MODE sourceMode;
DISPLAYCONFIG_DESKTOP_IMAGE_INFO desktopImageInfo;
} DUMMYUNIONNAME;
} DISPLAYCONFIG_MODE_INFO;
typedef enum DISPLAYCONFIG_TOPOLOGY_ID
{
DISPLAYCONFIG_TOPOLOGY_INTERNAL = 0x00000001,
DISPLAYCONFIG_TOPOLOGY_CLONE = 0x00000002,
DISPLAYCONFIG_TOPOLOGY_EXTEND = 0x00000004,
DISPLAYCONFIG_TOPOLOGY_EXTERNAL = 0x00000008,
DISPLAYCONFIG_TOPOLOGY_FORCE_UINT32 = 0xFFFFFFFF
} DISPLAYCONFIG_TOPOLOGY_ID;
typedef enum
{
DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3,
DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4,
DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6,
DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7,
DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8,
DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9,
DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10,
DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11,
DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF
} DISPLAYCONFIG_DEVICE_INFO_TYPE;
typedef struct DISPLAYCONFIG_DEVICE_INFO_HEADER
{
DISPLAYCONFIG_DEVICE_INFO_TYPE type;
UINT32 size;
LUID adapterId;
UINT32 id;
} DISPLAYCONFIG_DEVICE_INFO_HEADER;
typedef struct DISPLAYCONFIG_SOURCE_DEVICE_NAME
{
DISPLAYCONFIG_DEVICE_INFO_HEADER header;
WCHAR viewGdiDeviceName[CCHDEVICENAME];
} DISPLAYCONFIG_SOURCE_DEVICE_NAME;
typedef struct DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS
{
union
{
struct
{
UINT32 friendlyNameFromEdid : 1;
UINT32 friendlyNameForced : 1;
UINT32 edidIdsValid : 1;
UINT32 reserved : 29;
} DUMMYSTRUCTNAME;
UINT32 value;
} DUMMYUNIONNAME;
} DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS;
typedef struct DISPLAYCONFIG_TARGET_DEVICE_NAME
{
DISPLAYCONFIG_DEVICE_INFO_HEADER header;
DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags;
UINT32 /*DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY*/ outputTechnology;
UINT16 edidManufactureId;
UINT16 edidProductCodeId;
UINT32 connectorInstance;
WCHAR monitorFriendlyDeviceName[64];
WCHAR monitorDevicePath[128];
} DISPLAYCONFIG_TARGET_DEVICE_NAME;
#define QDC_ONLY_ACTIVE_PATHS 0x00000002
#endif // WINVER < 0x0601
#ifndef HAVE_SHELLSCALINGAPI_H
typedef enum MONITOR_DPI_TYPE
{
MDT_EFFECTIVE_DPI = 0,
MDT_ANGULAR_DPI = 1,
MDT_RAW_DPI = 2,
MDT_DEFAULT = MDT_EFFECTIVE_DPI
} MONITOR_DPI_TYPE;
typedef enum PROCESS_DPI_AWARENESS
{
PROCESS_DPI_UNAWARE = 0,
PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2
} PROCESS_DPI_AWARENESS;
#else
#include <shellscalingapi.h>
#endif
typedef struct ITaskbarList3 ITaskbarList3;
#ifndef _DPI_AWARENESS_CONTEXTS_
typedef enum DPI_AWARENESS
{
DPI_AWARENESS_INVALID = -1,
DPI_AWARENESS_UNAWARE = 0,
DPI_AWARENESS_SYSTEM_AWARE = 1,
DPI_AWARENESS_PER_MONITOR_AWARE = 2
} DPI_AWARENESS;
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
#define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT)-1)
#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT)-2)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT)-3)
#endif // _DPI_AWARENESS_CONTEXTS_
// Windows 10 Creators Update
#if NTDDI_VERSION < 0x0A000003
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)
#endif // NTDDI_VERSION < 0x0A000003
// Windows 10 version 1809
#if NTDDI_VERSION < 0x0A000006
#define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((DPI_AWARENESS_CONTEXT)-5)
#endif // NTDDI_VERSION < 0x0A000006
typedef BOOL (*PFNSHFullScreen)(HWND, DWORD);
typedef void (*PFCoordTransform)(SDL_Window *, POINT *);
typedef struct
{
void **lpVtbl;
int refcount;
void *data;
} TSFSink;
#ifndef SDL_DISABLE_WINDOWS_IME
// Definition from Win98DDK version of IMM.H
typedef struct tagINPUTCONTEXT2
{
HWND hWnd;
BOOL fOpen;
POINT ptStatusWndPos;
POINT ptSoftKbdPos;
DWORD fdwConversion;
DWORD fdwSentence;
union
{
LOGFONTA A;
LOGFONTW W;
} lfFont;
COMPOSITIONFORM cfCompForm;
CANDIDATEFORM cfCandForm[4];
HIMCC hCompStr;
HIMCC hCandInfo;
HIMCC hGuideLine;
HIMCC hPrivate;
DWORD dwNumMsgBuf;
HIMCC hMsgBuf;
DWORD fdwInit;
DWORD dwReserve[3];
} INPUTCONTEXT2, *PINPUTCONTEXT2, NEAR *NPINPUTCONTEXT2, FAR *LPINPUTCONTEXT2;
#endif
// Corner rounding support (Win 11+)
#ifndef DWMWA_WINDOW_CORNER_PREFERENCE
#define DWMWA_WINDOW_CORNER_PREFERENCE 33
#endif
typedef enum {
DWMWCP_DEFAULT = 0,
DWMWCP_DONOTROUND = 1,
DWMWCP_ROUND = 2,
DWMWCP_ROUNDSMALL = 3
} DWM_WINDOW_CORNER_PREFERENCE;
// Border Color support (Win 11+)
#ifndef DWMWA_BORDER_COLOR
#define DWMWA_BORDER_COLOR 34
#endif
#ifndef DWMWA_COLOR_DEFAULT
#define DWMWA_COLOR_DEFAULT 0xFFFFFFFF
#endif
#ifndef DWMWA_COLOR_NONE
#define DWMWA_COLOR_NONE 0xFFFFFFFE
#endif
// Transparent window support
#ifndef DWM_BB_ENABLE
#define DWM_BB_ENABLE 0x00000001
#endif
#ifndef DWM_BB_BLURREGION
#define DWM_BB_BLURREGION 0x00000002
#endif
typedef struct
{
DWORD flags;
BOOL enable;
HRGN blur_region;
BOOL transition_on_maxed;
} DWM_BLURBEHIND;
// Private display data
struct SDL_VideoData
{
int render;
bool coinitialized;
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
bool oleinitialized;
#endif // !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
DWORD clipboard_count;
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) // Xbox doesn't support user32/shcore
// Touch input functions
SDL_SharedObject *userDLL;
/* *INDENT-OFF* */ // clang-format off
BOOL (WINAPI *CloseTouchInputHandle)( HTOUCHINPUT );
BOOL (WINAPI *GetTouchInputInfo)( HTOUCHINPUT, UINT, PTOUCHINPUT, int );
BOOL (WINAPI *RegisterTouchWindow)( HWND, ULONG );
BOOL (WINAPI *SetProcessDPIAware)( void );
BOOL (WINAPI *SetProcessDpiAwarenessContext)( DPI_AWARENESS_CONTEXT );
DPI_AWARENESS_CONTEXT (WINAPI *SetThreadDpiAwarenessContext)( DPI_AWARENESS_CONTEXT );
DPI_AWARENESS_CONTEXT (WINAPI *GetThreadDpiAwarenessContext)( void );
DPI_AWARENESS (WINAPI *GetAwarenessFromDpiAwarenessContext)( DPI_AWARENESS_CONTEXT );
BOOL (WINAPI *EnableNonClientDpiScaling)( HWND );
BOOL (WINAPI *AdjustWindowRectExForDpi)( LPRECT, DWORD, BOOL, DWORD, UINT );
UINT (WINAPI *GetDpiForWindow)( HWND );
BOOL (WINAPI *AreDpiAwarenessContextsEqual)(DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT);
BOOL (WINAPI *IsValidDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
// DisplayConfig functions
LONG (WINAPI *GetDisplayConfigBufferSizes)( UINT32, UINT32 *, UINT32 *);
LONG (WINAPI *QueryDisplayConfig)( UINT32, UINT32 *, DISPLAYCONFIG_PATH_INFO*, UINT32 *, DISPLAYCONFIG_MODE_INFO*, DISPLAYCONFIG_TOPOLOGY_ID*);
LONG (WINAPI *DisplayConfigGetDeviceInfo)( DISPLAYCONFIG_DEVICE_INFO_HEADER*);
/* *INDENT-ON* */ // clang-format on
SDL_SharedObject *shcoreDLL;
/* *INDENT-OFF* */ // clang-format off
HRESULT (WINAPI *GetDpiForMonitor)( HMONITOR hmonitor,
MONITOR_DPI_TYPE dpiType,
UINT *dpiX,
UINT *dpiY );
HRESULT (WINAPI *SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS dpiAwareness);
BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType);
BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo);
SDL_SharedObject *dwmapiDLL;
/* *INDENT-OFF* */ // clang-format off
HRESULT (WINAPI *DwmFlush)(void);
HRESULT (WINAPI *DwmEnableBlurBehindWindow)(HWND hwnd, const DWM_BLURBEHIND *pBlurBehind);
HRESULT (WINAPI *DwmSetWindowAttribute)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
/* *INDENT-ON* */ // clang-format on
#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#ifdef HAVE_DXGI_H
SDL_SharedObject *dxgiDLL;
IDXGIFactory *pDXGIFactory;
#endif
bool cleared;
BYTE *rawinput;
UINT rawinput_offset;
UINT rawinput_size;
UINT rawinput_count;
Uint64 last_rawinput_poll;
SDL_Point last_raw_mouse_position;
bool raw_mouse_enabled;
bool raw_keyboard_enabled;
bool pending_E1_key_sequence;
Uint32 raw_input_enabled;
WIN_GameInputData *gameinput_context;
#ifndef SDL_DISABLE_WINDOWS_IME
bool ime_initialized;
bool ime_enabled;
bool ime_available;
bool ime_internal_composition;
bool ime_internal_candidates;
HWND ime_hwnd_main;
HWND ime_hwnd_current;
bool ime_needs_clear_composition;
HIMC ime_himc;
WCHAR *ime_composition;
int ime_composition_length;
WCHAR ime_readingstring[16];
int ime_cursor;
int ime_selected_start;
int ime_selected_length;
bool ime_candidates_open;
bool ime_update_candidates;
char *ime_candidates[MAX_CANDLIST];
int ime_candcount;
DWORD ime_candref;
DWORD ime_candsel;
int ime_candlistindexbase;
bool ime_horizontal_candidates;
#endif
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
COMPOSITIONFORM ime_composition_area;
CANDIDATEFORM ime_candidate_area;
#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#ifndef SDL_DISABLE_WINDOWS_IME
HKL ime_hkl;
SDL_SharedObject *ime_himm32;
/* *INDENT-OFF* */ // clang-format off
UINT (WINAPI *GetReadingString)(HIMC himc, UINT uReadingBufLen, LPWSTR lpwReadingBuf, PINT pnErrorIndex, BOOL *pfIsVertical, PUINT puMaxReadingLen);
BOOL (WINAPI *ShowReadingWindow)(HIMC himc, BOOL bShow);
LPINPUTCONTEXT2 (WINAPI *ImmLockIMC)(HIMC himc);
BOOL (WINAPI *ImmUnlockIMC)(HIMC himc);
LPVOID (WINAPI *ImmLockIMCC)(HIMCC himcc);
BOOL (WINAPI *ImmUnlockIMCC)(HIMCC himcc);
/* *INDENT-ON* */ // clang-format on
#endif // !SDL_DISABLE_WINDOWS_IME
BYTE pre_hook_key_state[256];
UINT _SDL_WAKEUP;
#ifdef HAVE_SHOBJIDL_CORE_H
UINT WM_TASKBAR_BUTTON_CREATED;
ITaskbarList3 *taskbar_list;
#endif
};
extern bool g_WindowsEnableMessageLoop;
extern bool g_WindowsEnableMenuMnemonics;
extern bool g_WindowFrameUsableWhileCursorHidden;
typedef struct IDirect3D9 IDirect3D9;
extern bool D3D_LoadDLL(void **pD3DDLL, IDirect3D9 **pDirect3D9Interface);
extern SDL_SystemTheme WIN_GetSystemTheme(void);
extern bool WIN_IsPerMonitorV2DPIAware(SDL_VideoDevice *_this);
#endif // SDL_windowsvideo_h_
@@ -0,0 +1,194 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/*
* @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
* SDL_x11vulkan.c.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WINDOWS)
#include "../SDL_vulkan_internal.h"
#include "SDL_windowsvideo.h"
#include "SDL_windowswindow.h"
#include "SDL_windowsvulkan.h"
bool WIN_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
VkExtensionProperties *extensions = NULL;
Uint32 extensionCount = 0;
Uint32 i;
bool hasSurfaceExtension = false;
bool hasWin32SurfaceExtension = false;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
if (_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan already loaded");
}
// Load the Vulkan loader library
if (!path) {
path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY);
}
if (!path) {
path = "vulkan-1.dll";
}
_this->vulkan_config.loader_handle = SDL_LoadObject(path);
if (!_this->vulkan_config.loader_handle) {
return false;
}
SDL_strlcpy(_this->vulkan_config.loader_path, path,
SDL_arraysize(_this->vulkan_config.loader_path));
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
_this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
if (!vkGetInstanceProcAddr) {
goto fail;
}
_this->vulkan_config.vkGetInstanceProcAddr = (SDL_FunctionPointer)vkGetInstanceProcAddr;
_this->vulkan_config.vkEnumerateInstanceExtensionProperties =
(SDL_FunctionPointer)vkGetInstanceProcAddr(
VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) {
goto fail;
}
extensions = SDL_Vulkan_CreateInstanceExtensionsList(
(PFN_vkEnumerateInstanceExtensionProperties)
_this->vulkan_config.vkEnumerateInstanceExtensionProperties,
&extensionCount);
if (!extensions) {
goto fail;
}
for (i = 0; i < extensionCount; i++) {
if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasSurfaceExtension = true;
} else if (SDL_strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasWin32SurfaceExtension = true;
}
}
SDL_free(extensions);
if (!hasSurfaceExtension) {
SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension");
goto fail;
} else if (!hasWin32SurfaceExtension) {
SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_WIN32_SURFACE_EXTENSION_NAME "extension");
goto fail;
}
return true;
fail:
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
return false;
}
void WIN_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
{
if (_this->vulkan_config.loader_handle) {
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
}
}
char const * const *WIN_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count)
{
static const char *const extensionsForWin32[] = {
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME
};
if (count) {
*count = SDL_arraysize(extensionsForWin32);
}
return extensionsForWin32;
}
bool WIN_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface)
{
SDL_WindowData *windowData = window->internal;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR =
(PFN_vkCreateWin32SurfaceKHR)vkGetInstanceProcAddr(
instance,
"vkCreateWin32SurfaceKHR");
VkWin32SurfaceCreateInfoKHR createInfo;
VkResult result;
if (!_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan is not loaded");
}
if (!vkCreateWin32SurfaceKHR) {
return SDL_SetError(VK_KHR_WIN32_SURFACE_EXTENSION_NAME
" extension is not enabled in the Vulkan instance.");
}
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.hinstance = windowData->hinstance;
createInfo.hwnd = windowData->hwnd;
result = vkCreateWin32SurfaceKHR(instance, &createInfo, allocator, surface);
if (result != VK_SUCCESS) {
return SDL_SetError("vkCreateWin32SurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
}
return true;
}
void WIN_Vulkan_DestroySurface(SDL_VideoDevice *_this,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator)
{
if (_this->vulkan_config.loader_handle) {
SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator);
}
}
bool WIN_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this,
VkInstance instance,
VkPhysicalDevice physicalDevice,
Uint32 queueFamilyIndex)
{
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR =
(PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)vkGetInstanceProcAddr(
instance,
"vkGetPhysicalDeviceWin32PresentationSupportKHR");
if (!_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan is not loaded");
}
if (!vkGetPhysicalDeviceWin32PresentationSupportKHR) {
return SDL_SetError(VK_KHR_WIN32_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
}
return vkGetPhysicalDeviceWin32PresentationSupportKHR(physicalDevice,
queueFamilyIndex);
}
#endif
@@ -0,0 +1,55 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/*
* @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
* SDL_x11vulkan.h.
*/
#include "SDL_internal.h"
#ifndef SDL_windowsvulkan_h_
#define SDL_windowsvulkan_h_
#include <SDL3/SDL_vulkan.h>
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WINDOWS)
extern bool WIN_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern void WIN_Vulkan_UnloadLibrary(SDL_VideoDevice *_this);
extern char const * const *WIN_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count);
extern bool WIN_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface);
extern void WIN_Vulkan_DestroySurface(SDL_VideoDevice *_this,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator);
bool WIN_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this,
VkInstance instance,
VkPhysicalDevice physicalDevice,
Uint32 queueFamilyIndex);
#endif
#endif // SDL_windowsvulkan_h_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,153 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_windowswindow_h_
#define SDL_windowswindow_h_
#ifdef SDL_VIDEO_OPENGL_EGL
#include "../SDL_egl_c.h"
#else
#include "../SDL_sysvideo.h"
#endif
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
typedef enum SDL_WindowRect
{
SDL_WINDOWRECT_CURRENT,
SDL_WINDOWRECT_WINDOWED,
SDL_WINDOWRECT_FLOATING,
SDL_WINDOWRECT_PENDING
} SDL_WindowRect;
typedef enum SDL_WindowEraseBackgroundMode
{
SDL_ERASEBACKGROUNDMODE_NEVER,
SDL_ERASEBACKGROUNDMODE_INITIAL,
SDL_ERASEBACKGROUNDMODE_ALWAYS,
} SDL_WindowEraseBackgroundMode;
typedef struct
{
void **lpVtbl;
int refcount;
SDL_Window *window;
HWND hwnd;
UINT format_text;
UINT format_file;
} SDLDropTarget;
struct SDL_WindowData
{
SDL_Window *window;
HWND hwnd;
HWND parent;
HDC hdc;
HDC mdc;
HINSTANCE hinstance;
HBITMAP hbm;
WNDPROC wndproc;
HHOOK keyboard_hook;
WPARAM mouse_button_flags;
LPARAM last_pointer_update;
WCHAR high_surrogate;
bool initializing;
bool expected_resize;
bool in_border_change;
bool in_title_click;
Uint8 focus_click_pending;
bool postpone_clipcursor;
bool clipcursor_queued;
bool windowed_mode_was_maximized;
bool in_window_deactivation;
bool force_ws_maximizebox;
bool disable_move_size_events;
bool showing_window;
int in_modal_loop;
RECT initial_size_rect;
RECT cursor_clipped_rect; // last successfully committed clipping rect for this window
RECT cursor_ctrlock_rect; // this is Windows-specific, but probably does not need to be per-window
bool mouse_tracked;
bool destroy_parent_with_window;
SDL_DisplayID last_displayID;
WCHAR *ICMFileName;
SDL_WindowEraseBackgroundMode hint_erase_background_mode;
bool taskbar_button_created;
struct SDL_VideoData *videodata;
#ifdef SDL_VIDEO_OPENGL_EGL
EGLSurface egl_surface;
#endif
// Whether we retain the content of the window when changing state
UINT copybits_flag;
SDLDropTarget *drop_target;
};
extern bool WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern void WIN_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern bool WIN_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
extern bool WIN_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
extern bool WIN_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right);
extern void WIN_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *width, int *height);
extern bool WIN_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity);
extern void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_HideWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered);
extern void WIN_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable);
extern void WIN_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, bool on_top);
extern SDL_FullscreenResult WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen);
extern void WIN_UpdateWindowICCProfile(SDL_Window *window, bool send_event);
extern void *WIN_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
extern bool WIN_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window);
extern bool WIN_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
extern bool WIN_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
extern void WIN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_OnWindowEnter(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_UpdateClipCursor(SDL_Window *window);
extern void WIN_UnclipCursorForWindow(SDL_Window *window);
extern bool WIN_SetWindowHitTest(SDL_Window *window, bool enabled);
extern void WIN_AcceptDragAndDrop(SDL_Window *window, bool accept);
extern bool WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
extern bool WIN_ApplyWindowProgress(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_UpdateDarkModeForHWND(HWND hwnd);
extern bool WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags, SDL_WindowRect rect_type);
extern void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
extern bool WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
extern bool WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_WindowRect rect_type);
extern bool WIN_AdjustWindowRectForHWND(HWND hwnd, LPRECT lpRect, UINT frame_dpi);
extern bool WIN_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent);
extern bool WIN_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal);
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif
#endif // SDL_windowswindow_h_
File diff suppressed because it is too large Load Diff