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:
@@ -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, ¤t_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, ¶ms, 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
Reference in New Issue
Block a user