build system update

-fix win32 folders from being excluded
This commit is contained in:
Joseph Aquino 2025-12-27 11:35:41 -05:00
parent 96732f9346
commit dd0b1d103e
20 changed files with 5969 additions and 1 deletions

1
.gitignore vendored
View File

@ -23,7 +23,6 @@ mono_crash.*
[Rr]eleases/ [Rr]eleases/
x64/ x64/
x86/ x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/ [Aa][Rr][Mm]/
[Aa][Rr][Mm]64/ [Aa][Rr][Mm]64/
bld/ bld/

View File

@ -0,0 +1,57 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Time.hpp>
#include <SFML/System/Win32/SleepImpl.hpp>
#include <SFML/System/Win32/WindowsHeader.hpp>
#include <mmsystem.h>
namespace sf::priv
{
////////////////////////////////////////////////////////////
void sleepImpl(Time time)
{
// Get the minimum supported timer resolution on this system
static const UINT periodMin = []
{
TIMECAPS tc;
timeGetDevCaps(&tc, sizeof(TIMECAPS));
return tc.wPeriodMin;
}();
// Set the timer resolution to the minimum for the Sleep call
timeBeginPeriod(periodMin);
// Wait...
::Sleep(static_cast<DWORD>(time.asMilliseconds()));
// Reset the timer resolution back to the system default
timeEndPeriod(periodMin);
}
} // namespace sf::priv

View File

@ -0,0 +1,48 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
namespace sf
{
class Time;
}
namespace sf::priv
{
////////////////////////////////////////////////////////////
/// \brief Windows implementation of sf::Sleep
///
/// \param time Time to sleep
///
////////////////////////////////////////////////////////////
void sleepImpl(Time time);
} // namespace sf::priv

View File

@ -0,0 +1,55 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
#pragma once
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef _WIN32_WINDOWS
#define _WIN32_WINDOWS 0x0501 // NOLINT(bugprone-reserved-identifier)
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#ifndef WINVER
#define WINVER 0x0501
#endif
#ifndef UNICODE
#define UNICODE 1
#endif
#ifndef _UNICODE
#define _UNICODE 1 // NOLINT(bugprone-reserved-identifier)
#endif
#include <windows.h>

View File

@ -0,0 +1,104 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/Win32/ClipboardImpl.hpp>
#include <SFML/Window/Win32/Utils.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/String.hpp>
#include <SFML/System/Win32/WindowsHeader.hpp>
#include <ostream>
#include <cstring>
namespace sf::priv
{
////////////////////////////////////////////////////////////
String ClipboardImpl::getString()
{
String text;
if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
{
err() << "Failed to get the clipboard data in Unicode format: " << getErrorString(GetLastError()) << std::endl;
return text;
}
if (!OpenClipboard(nullptr))
{
err() << "Failed to open the Win32 clipboard: " << getErrorString(GetLastError()) << std::endl;
return text;
}
HANDLE clipboardHandle = GetClipboardData(CF_UNICODETEXT);
if (!clipboardHandle)
{
err() << "Failed to get Win32 handle for clipboard content: " << getErrorString(GetLastError()) << std::endl;
CloseClipboard();
return text;
}
text = String(static_cast<wchar_t*>(GlobalLock(clipboardHandle)));
GlobalUnlock(clipboardHandle);
CloseClipboard();
return text;
}
////////////////////////////////////////////////////////////
void ClipboardImpl::setString(const String& text)
{
if (!OpenClipboard(nullptr))
{
err() << "Failed to open the Win32 clipboard: " << getErrorString(GetLastError()) << std::endl;
return;
}
if (!EmptyClipboard())
{
err() << "Failed to empty the Win32 clipboard: " << getErrorString(GetLastError()) << std::endl;
CloseClipboard();
return;
}
// Create a Win32-compatible string
const std::size_t stringSize = (text.getSize() + 1) * sizeof(WCHAR);
if (const HANDLE stringHandle = GlobalAlloc(GMEM_MOVEABLE, stringSize))
{
std::memcpy(GlobalLock(stringHandle), text.toWideString().data(), stringSize);
GlobalUnlock(stringHandle);
SetClipboardData(CF_UNICODETEXT, stringHandle);
}
CloseClipboard();
}
} // namespace sf::priv

View File

@ -0,0 +1,68 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
#pragma once
namespace sf
{
class String;
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Give access to the system clipboard
///
////////////////////////////////////////////////////////////
class ClipboardImpl
{
public:
////////////////////////////////////////////////////////////
/// \brief Get the content of the clipboard as string data
///
/// This function returns the content of the clipboard
/// as a string. If the clipboard does not contain string
/// it returns an empty sf::String object.
///
/// \return Current content of the clipboard
///
////////////////////////////////////////////////////////////
static String getString();
////////////////////////////////////////////////////////////
/// \brief Set the content of the clipboard as string data
///
/// This function sets the content of the clipboard as a
/// string.
///
/// \param text sf::String object containing the data to be sent
/// to the clipboard
///
////////////////////////////////////////////////////////////
static void setString(const String& text);
};
} // namespace priv
} // namespace sf

View File

@ -0,0 +1,184 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/Win32/CursorImpl.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/Win32/WindowsHeader.hpp>
#include <ostream>
#include <cstring>
namespace sf::priv
{
////////////////////////////////////////////////////////////
CursorImpl::~CursorImpl()
{
release();
}
////////////////////////////////////////////////////////////
bool CursorImpl::loadFromPixels(const std::uint8_t* pixels, Vector2u size, Vector2u hotspot)
{
release();
// Create the bitmap that will hold our color data
auto bitmapHeader = BITMAPV5HEADER();
bitmapHeader.bV5Size = sizeof(BITMAPV5HEADER);
bitmapHeader.bV5Width = static_cast<LONG>(size.x);
bitmapHeader.bV5Height = -static_cast<LONG>(size.y); // Negative indicates origin is in upper-left corner
bitmapHeader.bV5Planes = 1;
bitmapHeader.bV5BitCount = 32;
bitmapHeader.bV5Compression = BI_BITFIELDS;
bitmapHeader.bV5RedMask = 0x00ff0000;
bitmapHeader.bV5GreenMask = 0x0000ff00;
bitmapHeader.bV5BlueMask = 0x000000ff;
bitmapHeader.bV5AlphaMask = 0xff000000;
std::uint32_t* bitmapData = nullptr;
HDC screenDC = GetDC(nullptr);
HBITMAP color = CreateDIBSection(screenDC,
reinterpret_cast<const BITMAPINFO*>(&bitmapHeader),
DIB_RGB_COLORS,
reinterpret_cast<void**>(&bitmapData),
nullptr,
0);
ReleaseDC(nullptr, screenDC);
if (!color)
{
err() << "Failed to create cursor color bitmap" << std::endl;
return false;
}
// Fill our bitmap with the cursor color data
// We'll have to swap the red and blue channels here
std::uint32_t* bitmapOffset = bitmapData;
for (std::size_t remaining = size.x * size.y; remaining > 0; --remaining, pixels += 4)
{
*bitmapOffset++ = static_cast<std::uint32_t>((pixels[3] << 24) | (pixels[0] << 16) | (pixels[1] << 8) | pixels[2]);
}
// Create a dummy mask bitmap (it won't be used)
HBITMAP mask = CreateBitmap(static_cast<int>(size.x), static_cast<int>(size.y), 1, 1, nullptr);
if (!mask)
{
DeleteObject(color);
err() << "Failed to create cursor mask bitmap" << std::endl;
return false;
}
// Create the structure that describes our cursor
auto cursorInfo = ICONINFO();
cursorInfo.fIcon = FALSE; // This is a cursor and not an icon
cursorInfo.xHotspot = hotspot.x;
cursorInfo.yHotspot = hotspot.y;
cursorInfo.hbmColor = color;
cursorInfo.hbmMask = mask;
// Create the cursor
m_cursor = reinterpret_cast<HCURSOR>(CreateIconIndirect(&cursorInfo));
m_systemCursor = false;
// The data has been copied into the cursor, so get rid of these
DeleteObject(color);
DeleteObject(mask);
if (m_cursor)
{
return true;
}
err() << "Failed to create cursor from bitmaps" << std::endl;
return false;
}
////////////////////////////////////////////////////////////
bool CursorImpl::loadFromSystem(Cursor::Type type)
{
release();
LPCTSTR shape = nullptr;
// clang-format off
switch (type)
{
case Cursor::Type::Arrow: shape = IDC_ARROW; break;
case Cursor::Type::ArrowWait: shape = IDC_APPSTARTING; break;
case Cursor::Type::Wait: shape = IDC_WAIT; break;
case Cursor::Type::Text: shape = IDC_IBEAM; break;
case Cursor::Type::Hand: shape = IDC_HAND; break;
case Cursor::Type::SizeHorizontal: shape = IDC_SIZEWE; break;
case Cursor::Type::SizeVertical: shape = IDC_SIZENS; break;
case Cursor::Type::SizeTopLeftBottomRight: shape = IDC_SIZENWSE; break;
case Cursor::Type::SizeBottomLeftTopRight: shape = IDC_SIZENESW; break;
case Cursor::Type::SizeLeft: shape = IDC_SIZEWE; break;
case Cursor::Type::SizeRight: shape = IDC_SIZEWE; break;
case Cursor::Type::SizeTop: shape = IDC_SIZENS; break;
case Cursor::Type::SizeBottom: shape = IDC_SIZENS; break;
case Cursor::Type::SizeTopLeft: shape = IDC_SIZENWSE; break;
case Cursor::Type::SizeBottomRight: shape = IDC_SIZENWSE; break;
case Cursor::Type::SizeBottomLeft: shape = IDC_SIZENESW; break;
case Cursor::Type::SizeTopRight: shape = IDC_SIZENESW; break;
case Cursor::Type::SizeAll: shape = IDC_SIZEALL; break;
case Cursor::Type::Cross: shape = IDC_CROSS; break;
case Cursor::Type::Help: shape = IDC_HELP; break;
case Cursor::Type::NotAllowed: shape = IDC_NO; break;
}
// clang-format on
// Get the shared system cursor and make sure not to destroy it
m_cursor = LoadCursor(nullptr, shape);
m_systemCursor = true;
if (m_cursor)
{
return true;
}
err() << "Could not create copy of a system cursor" << std::endl;
return false;
}
////////////////////////////////////////////////////////////
void CursorImpl::release()
{
if (m_cursor && !m_systemCursor)
{
DestroyCursor(static_cast<HCURSOR>(m_cursor));
m_cursor = nullptr;
}
}
} // namespace sf::priv

View File

@ -0,0 +1,106 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/Cursor.hpp>
#include <SFML/System/Vector2.hpp>
#include <cstdint>
namespace sf::priv
{
////////////////////////////////////////////////////////////
/// \brief Win32 implementation of Cursor
///
////////////////////////////////////////////////////////////
class CursorImpl
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Refer to sf::Cursor::Cursor().
///
////////////////////////////////////////////////////////////
CursorImpl() = default;
////////////////////////////////////////////////////////////
/// \brief Destructor
///
/// Refer to sf::Cursor::~Cursor().
///
////////////////////////////////////////////////////////////
~CursorImpl();
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
CursorImpl(const CursorImpl&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
CursorImpl& operator=(const CursorImpl&) = delete;
////////////////////////////////////////////////////////////
/// \brief Create a cursor with the provided image
///
/// Refer to sf::Cursor::createFromPixels().
///
////////////////////////////////////////////////////////////
bool loadFromPixels(const std::uint8_t* pixels, Vector2u size, Vector2u hotspot);
////////////////////////////////////////////////////////////
/// \brief Create a native system cursor
///
/// Refer to sf::Cursor::createFromSystem().
///
////////////////////////////////////////////////////////////
bool loadFromSystem(Cursor::Type type);
private:
friend class WindowImplWin32;
////////////////////////////////////////////////////////////
/// \brief Release the cursor, if we have loaded one.
///
////////////////////////////////////////////////////////////
void release();
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
void* m_cursor{}; // Type erasure via `void*` is used here to avoid depending on `windows.h`
bool m_systemCursor{};
};
} // namespace sf::priv

View File

@ -0,0 +1,757 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/InputImpl.hpp>
#include <SFML/Window/Window.hpp>
#include <SFML/System/EnumArray.hpp>
#include <SFML/System/String.hpp>
#include <SFML/System/Win32/WindowsHeader.hpp>
#include <optional>
namespace
{
sf::priv::EnumArray<sf::Keyboard::Key, sf::Keyboard::Scancode, sf::Keyboard::KeyCount> keyToScancodeMapping; ///< Mapping from Key to Scancode
sf::priv::EnumArray<sf::Keyboard::Scancode, sf::Keyboard::Key, sf::Keyboard::ScancodeCount> scancodeToKeyMapping; ///< Mapping from Scancode to Key
sf::Keyboard::Key virtualKeyToSfKey(UINT virtualKey)
{
// clang-format off
switch (virtualKey)
{
case 'A': return sf::Keyboard::Key::A;
case 'B': return sf::Keyboard::Key::B;
case 'C': return sf::Keyboard::Key::C;
case 'D': return sf::Keyboard::Key::D;
case 'E': return sf::Keyboard::Key::E;
case 'F': return sf::Keyboard::Key::F;
case 'G': return sf::Keyboard::Key::G;
case 'H': return sf::Keyboard::Key::H;
case 'I': return sf::Keyboard::Key::I;
case 'J': return sf::Keyboard::Key::J;
case 'K': return sf::Keyboard::Key::K;
case 'L': return sf::Keyboard::Key::L;
case 'M': return sf::Keyboard::Key::M;
case 'N': return sf::Keyboard::Key::N;
case 'O': return sf::Keyboard::Key::O;
case 'P': return sf::Keyboard::Key::P;
case 'Q': return sf::Keyboard::Key::Q;
case 'R': return sf::Keyboard::Key::R;
case 'S': return sf::Keyboard::Key::S;
case 'T': return sf::Keyboard::Key::T;
case 'U': return sf::Keyboard::Key::U;
case 'V': return sf::Keyboard::Key::V;
case 'W': return sf::Keyboard::Key::W;
case 'X': return sf::Keyboard::Key::X;
case 'Y': return sf::Keyboard::Key::Y;
case 'Z': return sf::Keyboard::Key::Z;
case '0': return sf::Keyboard::Key::Num0;
case '1': return sf::Keyboard::Key::Num1;
case '2': return sf::Keyboard::Key::Num2;
case '3': return sf::Keyboard::Key::Num3;
case '4': return sf::Keyboard::Key::Num4;
case '5': return sf::Keyboard::Key::Num5;
case '6': return sf::Keyboard::Key::Num6;
case '7': return sf::Keyboard::Key::Num7;
case '8': return sf::Keyboard::Key::Num8;
case '9': return sf::Keyboard::Key::Num9;
case VK_ESCAPE: return sf::Keyboard::Key::Escape;
case VK_LCONTROL: return sf::Keyboard::Key::LControl;
case VK_LSHIFT: return sf::Keyboard::Key::LShift;
case VK_LMENU: return sf::Keyboard::Key::LAlt;
case VK_LWIN: return sf::Keyboard::Key::LSystem;
case VK_RCONTROL: return sf::Keyboard::Key::RControl;
case VK_RSHIFT: return sf::Keyboard::Key::RShift;
case VK_RMENU: return sf::Keyboard::Key::RAlt;
case VK_RWIN: return sf::Keyboard::Key::RSystem;
case VK_APPS: return sf::Keyboard::Key::Menu;
case VK_OEM_4: return sf::Keyboard::Key::LBracket;
case VK_OEM_6: return sf::Keyboard::Key::RBracket;
case VK_OEM_1: return sf::Keyboard::Key::Semicolon;
case VK_OEM_COMMA: return sf::Keyboard::Key::Comma;
case VK_OEM_PERIOD: return sf::Keyboard::Key::Period;
case VK_OEM_7: return sf::Keyboard::Key::Apostrophe;
case VK_OEM_2: return sf::Keyboard::Key::Slash;
case VK_OEM_5: return sf::Keyboard::Key::Backslash;
case VK_OEM_3: return sf::Keyboard::Key::Grave;
case VK_OEM_PLUS: return sf::Keyboard::Key::Equal;
case VK_OEM_MINUS: return sf::Keyboard::Key::Hyphen;
case VK_SPACE: return sf::Keyboard::Key::Space;
case VK_RETURN: return sf::Keyboard::Key::Enter;
case VK_BACK: return sf::Keyboard::Key::Backspace;
case VK_TAB: return sf::Keyboard::Key::Tab;
case VK_PRIOR: return sf::Keyboard::Key::PageUp;
case VK_NEXT: return sf::Keyboard::Key::PageDown;
case VK_END: return sf::Keyboard::Key::End;
case VK_HOME: return sf::Keyboard::Key::Home;
case VK_INSERT: return sf::Keyboard::Key::Insert;
case VK_DELETE: return sf::Keyboard::Key::Delete;
case VK_ADD: return sf::Keyboard::Key::Add;
case VK_SUBTRACT: return sf::Keyboard::Key::Subtract;
case VK_MULTIPLY: return sf::Keyboard::Key::Multiply;
case VK_DIVIDE: return sf::Keyboard::Key::Divide;
case VK_LEFT: return sf::Keyboard::Key::Left;
case VK_RIGHT: return sf::Keyboard::Key::Right;
case VK_UP: return sf::Keyboard::Key::Up;
case VK_DOWN: return sf::Keyboard::Key::Down;
case VK_NUMPAD0: return sf::Keyboard::Key::Numpad0;
case VK_NUMPAD1: return sf::Keyboard::Key::Numpad1;
case VK_NUMPAD2: return sf::Keyboard::Key::Numpad2;
case VK_NUMPAD3: return sf::Keyboard::Key::Numpad3;
case VK_NUMPAD4: return sf::Keyboard::Key::Numpad4;
case VK_NUMPAD5: return sf::Keyboard::Key::Numpad5;
case VK_NUMPAD6: return sf::Keyboard::Key::Numpad6;
case VK_NUMPAD7: return sf::Keyboard::Key::Numpad7;
case VK_NUMPAD8: return sf::Keyboard::Key::Numpad8;
case VK_NUMPAD9: return sf::Keyboard::Key::Numpad9;
case VK_F1: return sf::Keyboard::Key::F1;
case VK_F2: return sf::Keyboard::Key::F2;
case VK_F3: return sf::Keyboard::Key::F3;
case VK_F4: return sf::Keyboard::Key::F4;
case VK_F5: return sf::Keyboard::Key::F5;
case VK_F6: return sf::Keyboard::Key::F6;
case VK_F7: return sf::Keyboard::Key::F7;
case VK_F8: return sf::Keyboard::Key::F8;
case VK_F9: return sf::Keyboard::Key::F9;
case VK_F10: return sf::Keyboard::Key::F10;
case VK_F11: return sf::Keyboard::Key::F11;
case VK_F12: return sf::Keyboard::Key::F12;
case VK_F13: return sf::Keyboard::Key::F13;
case VK_F14: return sf::Keyboard::Key::F14;
case VK_F15: return sf::Keyboard::Key::F15;
case VK_PAUSE: return sf::Keyboard::Key::Pause;
default: return sf::Keyboard::Key::Unknown;
}
// clang-format on
}
int sfKeyToVirtualKey(sf::Keyboard::Key key)
{
// clang-format off
switch (key)
{
case sf::Keyboard::Key::A: return 'A';
case sf::Keyboard::Key::B: return 'B';
case sf::Keyboard::Key::C: return 'C';
case sf::Keyboard::Key::D: return 'D';
case sf::Keyboard::Key::E: return 'E';
case sf::Keyboard::Key::F: return 'F';
case sf::Keyboard::Key::G: return 'G';
case sf::Keyboard::Key::H: return 'H';
case sf::Keyboard::Key::I: return 'I';
case sf::Keyboard::Key::J: return 'J';
case sf::Keyboard::Key::K: return 'K';
case sf::Keyboard::Key::L: return 'L';
case sf::Keyboard::Key::M: return 'M';
case sf::Keyboard::Key::N: return 'N';
case sf::Keyboard::Key::O: return 'O';
case sf::Keyboard::Key::P: return 'P';
case sf::Keyboard::Key::Q: return 'Q';
case sf::Keyboard::Key::R: return 'R';
case sf::Keyboard::Key::S: return 'S';
case sf::Keyboard::Key::T: return 'T';
case sf::Keyboard::Key::U: return 'U';
case sf::Keyboard::Key::V: return 'V';
case sf::Keyboard::Key::W: return 'W';
case sf::Keyboard::Key::X: return 'X';
case sf::Keyboard::Key::Y: return 'Y';
case sf::Keyboard::Key::Z: return 'Z';
case sf::Keyboard::Key::Num0: return '0';
case sf::Keyboard::Key::Num1: return '1';
case sf::Keyboard::Key::Num2: return '2';
case sf::Keyboard::Key::Num3: return '3';
case sf::Keyboard::Key::Num4: return '4';
case sf::Keyboard::Key::Num5: return '5';
case sf::Keyboard::Key::Num6: return '6';
case sf::Keyboard::Key::Num7: return '7';
case sf::Keyboard::Key::Num8: return '8';
case sf::Keyboard::Key::Num9: return '9';
case sf::Keyboard::Key::Escape: return VK_ESCAPE;
case sf::Keyboard::Key::LControl: return VK_LCONTROL;
case sf::Keyboard::Key::LShift: return VK_LSHIFT;
case sf::Keyboard::Key::LAlt: return VK_LMENU;
case sf::Keyboard::Key::LSystem: return VK_LWIN;
case sf::Keyboard::Key::RControl: return VK_RCONTROL;
case sf::Keyboard::Key::RShift: return VK_RSHIFT;
case sf::Keyboard::Key::RAlt: return VK_RMENU;
case sf::Keyboard::Key::RSystem: return VK_RWIN;
case sf::Keyboard::Key::Menu: return VK_APPS;
case sf::Keyboard::Key::LBracket: return VK_OEM_4;
case sf::Keyboard::Key::RBracket: return VK_OEM_6;
case sf::Keyboard::Key::Semicolon: return VK_OEM_1;
case sf::Keyboard::Key::Comma: return VK_OEM_COMMA;
case sf::Keyboard::Key::Period: return VK_OEM_PERIOD;
case sf::Keyboard::Key::Apostrophe: return VK_OEM_7;
case sf::Keyboard::Key::Slash: return VK_OEM_2;
case sf::Keyboard::Key::Backslash: return VK_OEM_5;
case sf::Keyboard::Key::Grave: return VK_OEM_3;
case sf::Keyboard::Key::Equal: return VK_OEM_PLUS;
case sf::Keyboard::Key::Hyphen: return VK_OEM_MINUS;
case sf::Keyboard::Key::Space: return VK_SPACE;
case sf::Keyboard::Key::Enter: return VK_RETURN;
case sf::Keyboard::Key::Backspace: return VK_BACK;
case sf::Keyboard::Key::Tab: return VK_TAB;
case sf::Keyboard::Key::PageUp: return VK_PRIOR;
case sf::Keyboard::Key::PageDown: return VK_NEXT;
case sf::Keyboard::Key::End: return VK_END;
case sf::Keyboard::Key::Home: return VK_HOME;
case sf::Keyboard::Key::Insert: return VK_INSERT;
case sf::Keyboard::Key::Delete: return VK_DELETE;
case sf::Keyboard::Key::Add: return VK_ADD;
case sf::Keyboard::Key::Subtract: return VK_SUBTRACT;
case sf::Keyboard::Key::Multiply: return VK_MULTIPLY;
case sf::Keyboard::Key::Divide: return VK_DIVIDE;
case sf::Keyboard::Key::Left: return VK_LEFT;
case sf::Keyboard::Key::Right: return VK_RIGHT;
case sf::Keyboard::Key::Up: return VK_UP;
case sf::Keyboard::Key::Down: return VK_DOWN;
case sf::Keyboard::Key::Numpad0: return VK_NUMPAD0;
case sf::Keyboard::Key::Numpad1: return VK_NUMPAD1;
case sf::Keyboard::Key::Numpad2: return VK_NUMPAD2;
case sf::Keyboard::Key::Numpad3: return VK_NUMPAD3;
case sf::Keyboard::Key::Numpad4: return VK_NUMPAD4;
case sf::Keyboard::Key::Numpad5: return VK_NUMPAD5;
case sf::Keyboard::Key::Numpad6: return VK_NUMPAD6;
case sf::Keyboard::Key::Numpad7: return VK_NUMPAD7;
case sf::Keyboard::Key::Numpad8: return VK_NUMPAD8;
case sf::Keyboard::Key::Numpad9: return VK_NUMPAD9;
case sf::Keyboard::Key::F1: return VK_F1;
case sf::Keyboard::Key::F2: return VK_F2;
case sf::Keyboard::Key::F3: return VK_F3;
case sf::Keyboard::Key::F4: return VK_F4;
case sf::Keyboard::Key::F5: return VK_F5;
case sf::Keyboard::Key::F6: return VK_F6;
case sf::Keyboard::Key::F7: return VK_F7;
case sf::Keyboard::Key::F8: return VK_F8;
case sf::Keyboard::Key::F9: return VK_F9;
case sf::Keyboard::Key::F10: return VK_F10;
case sf::Keyboard::Key::F11: return VK_F11;
case sf::Keyboard::Key::F12: return VK_F12;
case sf::Keyboard::Key::F13: return VK_F13;
case sf::Keyboard::Key::F14: return VK_F14;
case sf::Keyboard::Key::F15: return VK_F15;
case sf::Keyboard::Key::Pause: return VK_PAUSE;
default: return 0;
}
// clang-format on
}
WORD sfScanToWinScan(sf::Keyboard::Scancode code)
{
// Convert an SFML scancode to a Windows scancode
// Reference: https://msdn.microsoft.com/en-us/library/aa299374(v=vs.60).aspx
// clang-format off
switch (code)
{
case sf::Keyboard::Scan::A: return 0x1E;
case sf::Keyboard::Scan::B: return 0x30;
case sf::Keyboard::Scan::C: return 0x2E;
case sf::Keyboard::Scan::D: return 0x20;
case sf::Keyboard::Scan::E: return 0x12;
case sf::Keyboard::Scan::F: return 0x21;
case sf::Keyboard::Scan::G: return 0x22;
case sf::Keyboard::Scan::H: return 0x23;
case sf::Keyboard::Scan::I: return 0x17;
case sf::Keyboard::Scan::J: return 0x24;
case sf::Keyboard::Scan::K: return 0x25;
case sf::Keyboard::Scan::L: return 0x26;
case sf::Keyboard::Scan::M: return 0x32;
case sf::Keyboard::Scan::N: return 0x31;
case sf::Keyboard::Scan::O: return 0x18;
case sf::Keyboard::Scan::P: return 0x19;
case sf::Keyboard::Scan::Q: return 0x10;
case sf::Keyboard::Scan::R: return 0x13;
case sf::Keyboard::Scan::S: return 0x1F;
case sf::Keyboard::Scan::T: return 0x14;
case sf::Keyboard::Scan::U: return 0x16;
case sf::Keyboard::Scan::V: return 0x2F;
case sf::Keyboard::Scan::W: return 0x11;
case sf::Keyboard::Scan::X: return 0x2D;
case sf::Keyboard::Scan::Y: return 0x15;
case sf::Keyboard::Scan::Z: return 0x2C;
case sf::Keyboard::Scan::Num1: return 0x02;
case sf::Keyboard::Scan::Num2: return 0x03;
case sf::Keyboard::Scan::Num3: return 0x04;
case sf::Keyboard::Scan::Num4: return 0x05;
case sf::Keyboard::Scan::Num5: return 0x06;
case sf::Keyboard::Scan::Num6: return 0x07;
case sf::Keyboard::Scan::Num7: return 0x08;
case sf::Keyboard::Scan::Num8: return 0x09;
case sf::Keyboard::Scan::Num9: return 0x0A;
case sf::Keyboard::Scan::Num0: return 0x0B;
case sf::Keyboard::Scan::Enter: return 0x1C;
case sf::Keyboard::Scan::Escape: return 0x01;
case sf::Keyboard::Scan::Backspace: return 0x0E;
case sf::Keyboard::Scan::Tab: return 0x0F;
case sf::Keyboard::Scan::Space: return 0x39;
case sf::Keyboard::Scan::Hyphen: return 0x0C;
case sf::Keyboard::Scan::Equal: return 0x0D;
case sf::Keyboard::Scan::LBracket: return 0x1A;
case sf::Keyboard::Scan::RBracket: return 0x1B;
case sf::Keyboard::Scan::Backslash: return 0x2B;
case sf::Keyboard::Scan::Semicolon: return 0x27;
case sf::Keyboard::Scan::Apostrophe: return 0x28;
case sf::Keyboard::Scan::Grave: return 0x29;
case sf::Keyboard::Scan::Comma: return 0x33;
case sf::Keyboard::Scan::Period: return 0x34;
case sf::Keyboard::Scan::Slash: return 0x35;
case sf::Keyboard::Scan::F1: return 0x3B;
case sf::Keyboard::Scan::F2: return 0x3C;
case sf::Keyboard::Scan::F3: return 0x3D;
case sf::Keyboard::Scan::F4: return 0x3E;
case sf::Keyboard::Scan::F5: return 0x3F;
case sf::Keyboard::Scan::F6: return 0x40;
case sf::Keyboard::Scan::F7: return 0x41;
case sf::Keyboard::Scan::F8: return 0x42;
case sf::Keyboard::Scan::F9: return 0x43;
case sf::Keyboard::Scan::F10: return 0x44;
case sf::Keyboard::Scan::F11: return 0x57;
case sf::Keyboard::Scan::F12: return 0x58;
case sf::Keyboard::Scan::F13: return 0x64;
case sf::Keyboard::Scan::F14: return 0x65;
case sf::Keyboard::Scan::F15: return 0x66;
case sf::Keyboard::Scan::F16: return 0x67;
case sf::Keyboard::Scan::F17: return 0x68;
case sf::Keyboard::Scan::F18: return 0x69;
case sf::Keyboard::Scan::F19: return 0x6A;
case sf::Keyboard::Scan::F20: return 0x6B;
case sf::Keyboard::Scan::F21: return 0x6C;
case sf::Keyboard::Scan::F22: return 0x6D;
case sf::Keyboard::Scan::F23: return 0x6E;
case sf::Keyboard::Scan::F24: return 0x76;
case sf::Keyboard::Scan::CapsLock: return 0x3A;
case sf::Keyboard::Scan::PrintScreen: return 0xE037;
case sf::Keyboard::Scan::ScrollLock: return 0x46;
case sf::Keyboard::Scan::Pause: return 0x45;
case sf::Keyboard::Scan::Insert: return 0xE052;
case sf::Keyboard::Scan::Home: return 0xE047;
case sf::Keyboard::Scan::PageUp: return 0xE049;
case sf::Keyboard::Scan::Delete: return 0xE053;
case sf::Keyboard::Scan::End: return 0xE04F;
case sf::Keyboard::Scan::PageDown: return 0xE051;
case sf::Keyboard::Scan::Right: return 0xE04D;
case sf::Keyboard::Scan::Left: return 0xE04B;
case sf::Keyboard::Scan::Down: return 0xE050;
case sf::Keyboard::Scan::Up: return 0xE048;
case sf::Keyboard::Scan::NumLock: return 0xE045;
case sf::Keyboard::Scan::NumpadDivide: return 0xE035;
case sf::Keyboard::Scan::NumpadMultiply: return 0x37;
case sf::Keyboard::Scan::NumpadMinus: return 0x4A;
case sf::Keyboard::Scan::NumpadPlus: return 0x4E;
case sf::Keyboard::Scan::NumpadEqual: return 0x7E;
case sf::Keyboard::Scan::NumpadEnter: return 0xE01C;
case sf::Keyboard::Scan::NumpadDecimal: return 0x53;
case sf::Keyboard::Scan::Numpad1: return 0x4F;
case sf::Keyboard::Scan::Numpad2: return 0x50;
case sf::Keyboard::Scan::Numpad3: return 0x51;
case sf::Keyboard::Scan::Numpad4: return 0x4B;
case sf::Keyboard::Scan::Numpad5: return 0x4C;
case sf::Keyboard::Scan::Numpad6: return 0x4D;
case sf::Keyboard::Scan::Numpad7: return 0x47;
case sf::Keyboard::Scan::Numpad8: return 0x48;
case sf::Keyboard::Scan::Numpad9: return 0x49;
case sf::Keyboard::Scan::Numpad0: return 0x52;
case sf::Keyboard::Scan::NonUsBackslash: return 0x56;
// No known scancode for Keyboard::Scan::Application
// No known scancode for Keyboard::Scan::Execute
// No known scancode for Keyboard::Scan::ModeChange
case sf::Keyboard::Scan::Help: return 0xE061;
case sf::Keyboard::Scan::Menu: return 0xE05D;
case sf::Keyboard::Scan::Select: return 0xE01E;
// No known scancode for Keyboard::Scan::Redo
// No known scancode for Keyboard::Scan::Undo
// No known scancode for Keyboard::Scan::Cut
// No known scancode for Keyboard::Scan::Copy
// No known scancode for Keyboard::Scan::Paste
case sf::Keyboard::Scan::VolumeMute: return 0xE020;
case sf::Keyboard::Scan::VolumeUp: return 0xE02E;
case sf::Keyboard::Scan::VolumeDown: return 0xE02C;
case sf::Keyboard::Scan::MediaPlayPause: return 0xE022;
case sf::Keyboard::Scan::MediaStop: return 0xE024;
case sf::Keyboard::Scan::MediaNextTrack: return 0xE019;
case sf::Keyboard::Scan::MediaPreviousTrack: return 0xE010;
case sf::Keyboard::Scan::LControl: return 0x1D;
case sf::Keyboard::Scan::LShift: return 0x2A;
case sf::Keyboard::Scan::LAlt: return 0x38;
case sf::Keyboard::Scan::LSystem: return 0xE05B;
case sf::Keyboard::Scan::RControl: return 0xE01D;
case sf::Keyboard::Scan::RShift: return 0x36;
case sf::Keyboard::Scan::RAlt: return 0xE038;
case sf::Keyboard::Scan::RSystem: return 0xE05C;
case sf::Keyboard::Scan::Back: return 0xE06A;
case sf::Keyboard::Scan::Forward: return 0xE069;
case sf::Keyboard::Scan::Refresh: return 0xE067;
case sf::Keyboard::Scan::Stop: return 0xE068;
case sf::Keyboard::Scan::Search: return 0xE065;
case sf::Keyboard::Scan::Favorites: return 0xE066;
case sf::Keyboard::Scan::HomePage: return 0xE030;
case sf::Keyboard::Scan::LaunchApplication1: return 0xE06B;
case sf::Keyboard::Scan::LaunchApplication2: return 0xE021;
case sf::Keyboard::Scan::LaunchMail: return 0xE06C;
case sf::Keyboard::Scan::LaunchMediaSelect: return 0xE06D;
// Unable to map to a scancode
default: return 0x0;
}
// clang-format on
}
WORD sfScanToWinScanExtended(sf::Keyboard::Scancode code)
{
// Convert an SFML scancode to a Windows scancode
// Reference: https://msdn.microsoft.com/en-us/library/aa299374(v=vs.60).aspx
// clang-format off
switch (code)
{
case sf::Keyboard::Scan::PrintScreen: return 55 | 0xE100;
case sf::Keyboard::Scan::Insert: return 82 | 0xE100;
case sf::Keyboard::Scan::Home: return 71 | 0xE100;
case sf::Keyboard::Scan::PageUp: return 73 | 0xE100;
case sf::Keyboard::Scan::Delete: return 83 | 0xE100;
case sf::Keyboard::Scan::End: return 79 | 0xE100;
case sf::Keyboard::Scan::PageDown: return 81 | 0xE100;
case sf::Keyboard::Scan::Right: return 77 | 0xE100;
case sf::Keyboard::Scan::Left: return 75 | 0xE100;
case sf::Keyboard::Scan::Down: return 80 | 0xE100;
case sf::Keyboard::Scan::Up: return 72 | 0xE100;
case sf::Keyboard::Scan::NumLock: return 69 | 0xE100;
case sf::Keyboard::Scan::NumpadEnter: return 28 | 0xE100;
case sf::Keyboard::Scan::NumpadDivide: return 53 | 0xE100;
case sf::Keyboard::Scan::Help: return 97 | 0xE100;
case sf::Keyboard::Scan::Menu: return 93 | 0xE100;
case sf::Keyboard::Scan::Select: return 30 | 0xE100;
case sf::Keyboard::Scan::VolumeMute: return 32 | 0xE100;
case sf::Keyboard::Scan::VolumeUp: return 46 | 0xE100;
case sf::Keyboard::Scan::VolumeDown: return 44 | 0xE100;
case sf::Keyboard::Scan::MediaPlayPause: return 34 | 0xE100;
case sf::Keyboard::Scan::MediaStop: return 36 | 0xE100;
case sf::Keyboard::Scan::MediaNextTrack: return 25 | 0xE100;
case sf::Keyboard::Scan::MediaPreviousTrack: return 16 | 0xE100;
case sf::Keyboard::Scan::LSystem: return 91 | 0xE100;
case sf::Keyboard::Scan::RControl: return 29 | 0xE100;
case sf::Keyboard::Scan::RAlt: return 56 | 0xE100;
case sf::Keyboard::Scan::RSystem: return 92 | 0xE100;
case sf::Keyboard::Scan::Back: return 106 | 0xE100;
case sf::Keyboard::Scan::Forward: return 105 | 0xE100;
case sf::Keyboard::Scan::Refresh: return 103 | 0xE100;
case sf::Keyboard::Scan::Stop: return 104 | 0xE100;
case sf::Keyboard::Scan::Search: return 101 | 0xE100;
case sf::Keyboard::Scan::Favorites: return 102 | 0xE100;
case sf::Keyboard::Scan::HomePage: return 48 | 0xE100;
case sf::Keyboard::Scan::LaunchApplication1: return 107 | 0xE100;
case sf::Keyboard::Scan::LaunchApplication2: return 33 | 0xE100;
case sf::Keyboard::Scan::LaunchMail: return 108 | 0xE100;
case sf::Keyboard::Scan::LaunchMediaSelect: return 109 | 0xE100;
// Use non-extended mapping
default: return sfScanToWinScan(code);
}
// clang-format on
}
UINT sfScanToVirtualKey(sf::Keyboard::Scancode code)
{
const WORD winScancode = sfScanToWinScan(code);
// Manually map non-extended key codes
// clang-format off
switch (code)
{
case sf::Keyboard::Scan::Numpad0: return VK_NUMPAD0;
case sf::Keyboard::Scan::Numpad1: return VK_NUMPAD1;
case sf::Keyboard::Scan::Numpad2: return VK_NUMPAD2;
case sf::Keyboard::Scan::Numpad3: return VK_NUMPAD3;
case sf::Keyboard::Scan::Numpad4: return VK_NUMPAD4;
case sf::Keyboard::Scan::Numpad5: return VK_NUMPAD5;
case sf::Keyboard::Scan::Numpad6: return VK_NUMPAD6;
case sf::Keyboard::Scan::Numpad7: return VK_NUMPAD7;
case sf::Keyboard::Scan::Numpad8: return VK_NUMPAD8;
case sf::Keyboard::Scan::Numpad9: return VK_NUMPAD9;
case sf::Keyboard::Scan::NumpadMinus: return VK_SUBTRACT;
case sf::Keyboard::Scan::NumpadDecimal: return VK_DECIMAL;
case sf::Keyboard::Scan::NumpadDivide: return VK_DIVIDE;
case sf::Keyboard::Scan::Pause: return VK_PAUSE;
case sf::Keyboard::Scan::RControl: return VK_RCONTROL;
case sf::Keyboard::Scan::RAlt: return VK_RMENU;
default: return MapVirtualKey(winScancode, MAPVK_VSC_TO_VK_EX);
}
// clang-format on
}
std::optional<sf::String> sfScanToConsumerKeyName(sf::Keyboard::Scancode code)
{
// Convert an SFML scancode to a Windows consumer keyboard key name
// Reference: https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input#keystroke-messages
// clang-format off
switch (code)
{
case sf::Keyboard::Scan::MediaNextTrack: return "Next Track";
case sf::Keyboard::Scan::MediaPreviousTrack: return "Previous Track";
case sf::Keyboard::Scan::MediaStop: return "Stop";
case sf::Keyboard::Scan::MediaPlayPause: return "Play/Pause";
case sf::Keyboard::Scan::VolumeMute: return "Mute";
case sf::Keyboard::Scan::VolumeUp: return "Volume Increment";
case sf::Keyboard::Scan::VolumeDown: return "Volume Decrement";
case sf::Keyboard::Scan::LaunchMediaSelect: return "Consumer Control Configuration";
case sf::Keyboard::Scan::LaunchMail: return "Email Reader";
case sf::Keyboard::Scan::LaunchApplication2: return "Calculator";
case sf::Keyboard::Scan::LaunchApplication1: return "Local Machine Browser";
case sf::Keyboard::Scan::Search: return "Search";
case sf::Keyboard::Scan::HomePage: return "Home";
case sf::Keyboard::Scan::Back: return "Back";
case sf::Keyboard::Scan::Forward: return "Forward";
case sf::Keyboard::Scan::Stop: return "Stop";
case sf::Keyboard::Scan::Refresh: return "Refresh";
case sf::Keyboard::Scan::Favorites: return "Bookmarks";
// Not a consumer key
default: return std::nullopt;
}
// clang-format on
}
/// Ensure the mappings are generated from/to Key and Scancode.
void ensureMappings()
{
static bool isMappingInitialized = false;
if (isMappingInitialized)
return;
// Phase 1: Initialize mappings with default values
keyToScancodeMapping.fill(sf::Keyboard::Scan::Unknown);
scancodeToKeyMapping.fill(sf::Keyboard::Key::Unknown);
// Phase 2: Translate scancode to virtual code to key names
for (unsigned int i = 0; i < sf::Keyboard::ScancodeCount; ++i)
{
const auto scan = static_cast<sf::Keyboard::Scancode>(i);
const UINT virtualKey = sfScanToVirtualKey(scan);
const sf::Keyboard::Key key = virtualKeyToSfKey(virtualKey);
if (key != sf::Keyboard::Key::Unknown && keyToScancodeMapping[key] == sf::Keyboard::Scan::Unknown)
keyToScancodeMapping[key] = scan;
scancodeToKeyMapping[scan] = key;
}
isMappingInitialized = true;
}
bool isValidScancode(sf::Keyboard::Scancode code)
{
return code > sf::Keyboard::Scan::Unknown && static_cast<unsigned int>(code) < sf::Keyboard::ScancodeCount;
}
bool isValidKey(sf::Keyboard::Key key)
{
return key > sf::Keyboard::Key::Unknown && static_cast<unsigned int>(key) < sf::Keyboard::KeyCount;
}
} // namespace
namespace sf::priv::InputImpl
{
////////////////////////////////////////////////////////////
bool isKeyPressed(Keyboard::Key key)
{
const int virtualKey = sfKeyToVirtualKey(key);
return (GetAsyncKeyState(virtualKey) & 0x8000) != 0;
}
////////////////////////////////////////////////////////////
bool isKeyPressed(Keyboard::Scancode code)
{
const UINT virtualKey = sfScanToVirtualKey(code);
return (GetAsyncKeyState(static_cast<int>(virtualKey)) & KF_UP) != 0;
}
////////////////////////////////////////////////////////////
Keyboard::Key localize(Keyboard::Scancode code)
{
if (!isValidScancode(code))
return Keyboard::Key::Unknown;
ensureMappings();
return scancodeToKeyMapping[code];
}
////////////////////////////////////////////////////////////
Keyboard::Scancode delocalize(Keyboard::Key key)
{
if (!isValidKey(key))
return Keyboard::Scan::Unknown;
ensureMappings();
return keyToScancodeMapping[key];
}
////////////////////////////////////////////////////////////
String getDescription(Keyboard::Scancode code)
{
// Try to translate the scan code to a consumer key
if (const auto consumerKeyName = sfScanToConsumerKeyName(code))
return *consumerKeyName;
WORD winCode = sfScanToWinScanExtended(code);
std::array<WCHAR, 1024> name{};
// Remap F13-F23 to values supported by GetKeyNameText
if ((winCode >= 0x64) && (winCode <= 0x6E))
winCode += 0x18;
// Remap F24 to value supported by GetKeyNameText
if (winCode == 0x76)
winCode = 0x87;
if (GetKeyNameText(winCode << 16, name.data(), static_cast<int>(name.size())) > 0)
return name.data();
return "Unknown";
}
////////////////////////////////////////////////////////////
void setVirtualKeyboardVisible(bool /*visible*/)
{
// Not applicable
}
////////////////////////////////////////////////////////////
bool isMouseButtonPressed(Mouse::Button button)
{
int virtualKey = 0;
switch (button)
{
case Mouse::Button::Left:
virtualKey = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON;
break;
case Mouse::Button::Right:
virtualKey = GetSystemMetrics(SM_SWAPBUTTON) ? VK_LBUTTON : VK_RBUTTON;
break;
case Mouse::Button::Middle:
virtualKey = VK_MBUTTON;
break;
case Mouse::Button::Extra1:
virtualKey = VK_XBUTTON1;
break;
case Mouse::Button::Extra2:
virtualKey = VK_XBUTTON2;
break;
default:
virtualKey = 0;
break;
}
return (GetAsyncKeyState(virtualKey) & 0x8000) != 0;
}
////////////////////////////////////////////////////////////
Vector2i getMousePosition()
{
POINT point;
GetCursorPos(&point);
return {point.x, point.y};
}
////////////////////////////////////////////////////////////
Vector2i getMousePosition(const WindowBase& relativeTo)
{
if (const WindowHandle handle = relativeTo.getNativeHandle())
{
POINT point;
GetCursorPos(&point);
ScreenToClient(handle, &point);
return {point.x, point.y};
}
return {};
}
////////////////////////////////////////////////////////////
void setMousePosition(Vector2i position)
{
SetCursorPos(position.x, position.y);
}
////////////////////////////////////////////////////////////
void setMousePosition(Vector2i position, const WindowBase& relativeTo)
{
if (const WindowHandle handle = relativeTo.getNativeHandle())
{
POINT point = {position.x, position.y};
ClientToScreen(handle, &point);
SetCursorPos(point.x, point.y);
}
}
////////////////////////////////////////////////////////////
bool isTouchDown(unsigned int /*finger*/)
{
// Not applicable
return false;
}
////////////////////////////////////////////////////////////
Vector2i getTouchPosition(unsigned int /*finger*/)
{
// Not applicable
return {};
}
////////////////////////////////////////////////////////////
Vector2i getTouchPosition(unsigned int /*finger*/, const WindowBase& /*relativeTo*/)
{
// Not applicable
return {};
}
} // namespace sf::priv::InputImpl

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,227 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/EnumArray.hpp>
#include <SFML/System/Win32/WindowsHeader.hpp>
#include <dinput.h>
#include <mmsystem.h>
namespace sf::priv
{
////////////////////////////////////////////////////////////
/// \brief Windows implementation of joysticks
///
////////////////////////////////////////////////////////////
class JoystickImpl
{
public:
////////////////////////////////////////////////////////////
/// \brief Perform the global initialization of the joystick module
///
////////////////////////////////////////////////////////////
static void initialize();
////////////////////////////////////////////////////////////
/// \brief Perform the global cleanup of the joystick module
///
////////////////////////////////////////////////////////////
static void cleanup();
////////////////////////////////////////////////////////////
/// \brief Check if a joystick is currently connected
///
/// \param index Index of the joystick to check
///
/// \return `true` if the joystick is connected, `false` otherwise
///
////////////////////////////////////////////////////////////
static bool isConnected(unsigned int index);
////////////////////////////////////////////////////////////
/// \brief Enable or disable lazy enumeration updates
///
/// \param status Whether to rely on windows triggering enumeration updates
///
////////////////////////////////////////////////////////////
static void setLazyUpdates(bool status);
////////////////////////////////////////////////////////////
/// \brief Update the connection status of all joysticks
///
////////////////////////////////////////////////////////////
static void updateConnections();
////////////////////////////////////////////////////////////
/// \brief Open the joystick
///
/// \param index Index assigned to the joystick
///
/// \return `true` on success, `false` on failure
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool open(unsigned int index);
////////////////////////////////////////////////////////////
/// \brief Close the joystick
///
////////////////////////////////////////////////////////////
void close();
////////////////////////////////////////////////////////////
/// \brief Get the joystick capabilities
///
/// \return Joystick capabilities
///
////////////////////////////////////////////////////////////
[[nodiscard]] JoystickCaps getCapabilities() const;
////////////////////////////////////////////////////////////
/// \brief Get the joystick identification
///
/// \return Joystick identification
///
////////////////////////////////////////////////////////////
[[nodiscard]] Joystick::Identification getIdentification() const;
////////////////////////////////////////////////////////////
/// \brief Update the joystick and get its new state
///
/// \return Joystick state
///
////////////////////////////////////////////////////////////
[[nodiscard]] JoystickState update();
////////////////////////////////////////////////////////////
/// \brief Perform the global initialization of the joystick module (DInput)
///
////////////////////////////////////////////////////////////
static void initializeDInput();
////////////////////////////////////////////////////////////
/// \brief Perform the global cleanup of the joystick module (DInput)
///
////////////////////////////////////////////////////////////
static void cleanupDInput();
////////////////////////////////////////////////////////////
/// \brief Check if a joystick is currently connected (DInput)
///
/// \param index Index of the joystick to check
///
/// \return `true` if the joystick is connected, `false` otherwise
///
////////////////////////////////////////////////////////////
static bool isConnectedDInput(unsigned int index);
////////////////////////////////////////////////////////////
/// \brief Update the connection status of all joysticks (DInput)
///
////////////////////////////////////////////////////////////
static void updateConnectionsDInput();
////////////////////////////////////////////////////////////
/// \brief Open the joystick (DInput)
///
/// \param index Index assigned to the joystick
///
/// \return `true` on success, `false` on failure
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool openDInput(unsigned int index);
////////////////////////////////////////////////////////////
/// \brief Close the joystick (DInput)
///
////////////////////////////////////////////////////////////
void closeDInput();
////////////////////////////////////////////////////////////
/// \brief Get the joystick capabilities (DInput)
///
/// \return Joystick capabilities
///
////////////////////////////////////////////////////////////
[[nodiscard]] JoystickCaps getCapabilitiesDInput() const;
////////////////////////////////////////////////////////////
/// \brief Update the joystick and get its new state (DInput, Buffered)
///
/// \return Joystick state
///
////////////////////////////////////////////////////////////
[[nodiscard]] JoystickState updateDInputBuffered();
////////////////////////////////////////////////////////////
/// \brief Update the joystick and get its new state (DInput, Polled)
///
/// \return Joystick state
///
////////////////////////////////////////////////////////////
[[nodiscard]] JoystickState updateDInputPolled();
private:
////////////////////////////////////////////////////////////
/// \brief Device enumeration callback function passed to EnumDevices in updateConnections
///
/// \param deviceInstance Device object instance
/// \param userData User data (unused)
///
/// \return DIENUM_CONTINUE to continue enumerating devices or DIENUM_STOP to stop
///
////////////////////////////////////////////////////////////
static BOOL CALLBACK deviceEnumerationCallback(const DIDEVICEINSTANCE* deviceInstance, void* userData);
////////////////////////////////////////////////////////////
/// \brief Device object enumeration callback function passed to EnumObjects in open
///
/// \param deviceObjectInstance Device object instance
/// \param userData User data (pointer to our JoystickImpl object)
///
/// \return DIENUM_CONTINUE to continue enumerating objects or DIENUM_STOP to stop
///
////////////////////////////////////////////////////////////
static BOOL CALLBACK deviceObjectEnumerationCallback(const DIDEVICEOBJECTINSTANCE* deviceObjectInstance, void* userData);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
unsigned int m_index{}; //!< Index of the joystick
JOYCAPS m_caps{}; //!< Joystick capabilities
IDirectInputDevice8W* m_device{}; //!< DirectInput 8.x device
DIDEVCAPS m_deviceCaps{}; //!< DirectInput device capabilities
EnumArray<Joystick::Axis, int, Joystick::AxisCount> m_axes{}; //!< Offsets to the bytes containing the axes states, -1 if not available
std::array<int, Joystick::ButtonCount> m_buttons{}; //!< Offsets to the bytes containing the button states, -1 if not available
Joystick::Identification m_identification; //!< Joystick identification
JoystickState m_state; //!< Buffered joystick state
bool m_buffered{}; //!< `true` if the device uses buffering, `false` if the device uses polling
};
} // namespace sf::priv

View File

@ -0,0 +1,84 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/SensorImpl.hpp>
namespace sf::priv
{
////////////////////////////////////////////////////////////
void SensorImpl::initialize()
{
// TODO: not implemented
}
////////////////////////////////////////////////////////////
void SensorImpl::cleanup()
{
// TODO: not implemented
}
////////////////////////////////////////////////////////////
bool SensorImpl::isAvailable(Sensor::Type /*sensor*/)
{
// TODO: not implemented
return false;
}
////////////////////////////////////////////////////////////
bool SensorImpl::open(Sensor::Type /*sensor*/)
{
// TODO: not implemented
return false;
}
////////////////////////////////////////////////////////////
void SensorImpl::close()
{
// TODO: not implemented
}
////////////////////////////////////////////////////////////
Vector3f SensorImpl::update()
{
// TODO: not implemented
return {};
}
////////////////////////////////////////////////////////////
void SensorImpl::setEnabled(bool /*enabled*/)
{
// TODO: not implemented
}
} // namespace sf::priv

View File

@ -0,0 +1,97 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/Sensor.hpp>
namespace sf::priv
{
////////////////////////////////////////////////////////////
/// \brief Windows implementation of sensors
///
////////////////////////////////////////////////////////////
class SensorImpl
{
public:
////////////////////////////////////////////////////////////
/// \brief Perform the global initialization of the sensor module
///
////////////////////////////////////////////////////////////
static void initialize();
////////////////////////////////////////////////////////////
/// \brief Perform the global cleanup of the sensor module
///
////////////////////////////////////////////////////////////
static void cleanup();
////////////////////////////////////////////////////////////
/// \brief Check if a sensor is available
///
/// \param sensor Sensor to check
///
/// \return `true` if the sensor is available, `false` otherwise
///
////////////////////////////////////////////////////////////
static bool isAvailable(Sensor::Type sensor);
////////////////////////////////////////////////////////////
/// \brief Open the sensor
///
/// \param sensor Type of the sensor
///
/// \return `true` on success, `false` on failure
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool open(Sensor::Type sensor);
////////////////////////////////////////////////////////////
/// \brief Close the sensor
///
////////////////////////////////////////////////////////////
void close();
////////////////////////////////////////////////////////////
/// \brief Update the sensor and get its new value
///
/// \return Sensor value
///
////////////////////////////////////////////////////////////
[[nodiscard]] Vector3f update();
////////////////////////////////////////////////////////////
/// \brief Enable or disable the sensor
///
/// \param enabled `true` to enable, `false` to disable
///
////////////////////////////////////////////////////////////
void setEnabled(bool enabled);
};
} // namespace sf::priv

View File

@ -0,0 +1,53 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/String.hpp>
#include <SFML/System/Win32/WindowsHeader.hpp>
namespace sf::priv
{
inline std::string getErrorString(DWORD error)
{
PTCHAR buffer = nullptr;
if (FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
nullptr,
error,
0,
reinterpret_cast<PTCHAR>(&buffer),
0,
nullptr) == 0)
{
return "Unknown error.";
}
const sf::String message = buffer;
LocalFree(buffer);
return message.toAnsiString();
}
} // namespace sf::priv

View File

@ -0,0 +1,71 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/VideoModeImpl.hpp>
#include <SFML/System/Win32/WindowsHeader.hpp>
#include <algorithm>
namespace sf::priv
{
////////////////////////////////////////////////////////////
std::vector<VideoMode> VideoModeImpl::getFullscreenModes()
{
std::vector<VideoMode> modes;
// Enumerate all available video modes for the primary display adapter
DEVMODE win32Mode;
win32Mode.dmSize = sizeof(win32Mode);
win32Mode.dmDriverExtra = 0;
for (int count = 0; EnumDisplaySettings(nullptr, static_cast<DWORD>(count), &win32Mode); ++count)
{
// Convert to sf::VideoMode
const VideoMode mode({win32Mode.dmPelsWidth, win32Mode.dmPelsHeight}, win32Mode.dmBitsPerPel);
// Add it only if it is not already in the array
if (std::find(modes.begin(), modes.end(), mode) == modes.end())
modes.push_back(mode);
}
return modes;
}
////////////////////////////////////////////////////////////
VideoMode VideoModeImpl::getDesktopMode()
{
DEVMODE win32Mode;
win32Mode.dmSize = sizeof(win32Mode);
win32Mode.dmDriverExtra = 0;
EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &win32Mode);
return VideoMode({win32Mode.dmPelsWidth, win32Mode.dmPelsHeight}, win32Mode.dmBitsPerPel);
}
} // namespace sf::priv

View File

@ -0,0 +1,209 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/VulkanImpl.hpp>
#include <SFML/System/Win32/WindowsHeader.hpp>
#include <string_view>
#include <cstdint>
#define VK_USE_PLATFORM_WIN32_KHR
#define VK_NO_PROTOTYPES
#include <vulkan.h>
namespace
{
struct VulkanLibraryWrapper
{
~VulkanLibraryWrapper()
{
if (library)
FreeLibrary(library);
}
// Try to load the library and all the required entry points
bool loadLibrary()
{
if (library)
return true;
library = LoadLibraryA("vulkan-1.dll");
if (!library)
return false;
if (!loadEntryPoint(vkGetInstanceProcAddr, "vkGetInstanceProcAddr"))
{
FreeLibrary(library);
library = nullptr;
return false;
}
if (!loadEntryPoint(vkEnumerateInstanceLayerProperties, "vkEnumerateInstanceLayerProperties"))
{
FreeLibrary(library);
library = nullptr;
return false;
}
if (!loadEntryPoint(vkEnumerateInstanceExtensionProperties, "vkEnumerateInstanceExtensionProperties"))
{
FreeLibrary(library);
library = nullptr;
return false;
}
return true;
}
template <typename T>
bool loadEntryPoint(T& entryPoint, const char* name)
{
entryPoint = reinterpret_cast<T>(reinterpret_cast<void*>(GetProcAddress(library, name)));
return entryPoint != nullptr;
}
HMODULE library{};
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties{};
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties{};
};
VulkanLibraryWrapper wrapper;
} // namespace
namespace sf::priv
{
////////////////////////////////////////////////////////////
bool VulkanImpl::isAvailable(bool requireGraphics)
{
static bool checked = false;
static bool computeAvailable = false;
static bool graphicsAvailable = false;
if (!checked)
{
checked = true;
// Check if the library is available
computeAvailable = wrapper.loadLibrary();
// To check for instance extensions we don't need to differentiate between graphics and compute
graphicsAvailable = computeAvailable;
if (graphicsAvailable)
{
// Retrieve the available instance extensions
std::vector<VkExtensionProperties> extensionProperties;
std::uint32_t extensionCount = 0;
wrapper.vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
extensionProperties.resize(extensionCount);
wrapper.vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensionProperties.data());
// Check if the necessary extensions are available
bool hasVkKhrSurface = false;
bool hasVkKhrPlatformSurface = false;
for (const VkExtensionProperties& properties : extensionProperties)
{
if (std::string_view(properties.extensionName) == VK_KHR_SURFACE_EXTENSION_NAME)
{
hasVkKhrSurface = true;
}
else if (std::string_view(properties.extensionName) == VK_KHR_WIN32_SURFACE_EXTENSION_NAME)
{
hasVkKhrPlatformSurface = true;
}
}
if (!hasVkKhrSurface || !hasVkKhrPlatformSurface)
graphicsAvailable = false;
}
}
if (requireGraphics)
return graphicsAvailable;
return computeAvailable;
}
////////////////////////////////////////////////////////////
VulkanFunctionPointer VulkanImpl::getFunction(const char* name)
{
if (!isAvailable(false))
return nullptr;
return reinterpret_cast<VulkanFunctionPointer>(GetProcAddress(wrapper.library, name));
}
////////////////////////////////////////////////////////////
const std::vector<const char*>& VulkanImpl::getGraphicsRequiredInstanceExtensions()
{
static const std::vector<const char*> extensions{VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME};
return extensions;
}
////////////////////////////////////////////////////////////
bool VulkanImpl::createVulkanSurface(const VkInstance& instance,
WindowHandle windowHandle,
VkSurfaceKHR& surface,
const VkAllocationCallbacks* allocator)
{
if (!isAvailable())
return false;
// Make a copy of the instance handle since we get it passed as a reference
VkInstance inst = instance;
auto vkCreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(
wrapper.vkGetInstanceProcAddr(inst, "vkCreateWin32SurfaceKHR"));
if (!vkCreateWin32SurfaceKHR)
return false;
VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = VkWin32SurfaceCreateInfoKHR();
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
surfaceCreateInfo.hinstance = GetModuleHandleA(nullptr);
surfaceCreateInfo.hwnd = windowHandle;
return vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, allocator, &surface) == VK_SUCCESS;
}
} // namespace sf::priv

View File

@ -0,0 +1,849 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/ContextSettings.hpp>
#include <SFML/Window/VideoMode.hpp>
#include <SFML/Window/Win32/Utils.hpp>
#include <SFML/Window/Win32/WglContext.hpp>
#include <SFML/Window/WindowImpl.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/String.hpp>
#include <mutex>
#include <sstream>
#include <vector>
#include <cassert>
// We check for this definition in order to avoid multiple definitions of GLAD
// entities during unity builds of SFML.
#ifndef SF_GLAD_WGL_IMPLEMENTATION_INCLUDED
#define SF_GLAD_WGL_IMPLEMENTATION_INCLUDED
#define SF_GLAD_WGL_IMPLEMENTATION
#include <glad/wgl.h>
#endif
namespace
{
namespace WglContextImpl
{
// Some drivers are bugged and don't track the current HDC/HGLRC properly
// In order to deactivate successfully, we need to track it ourselves as well
thread_local sf::priv::WglContext* currentContext(nullptr);
// We use a different loader for wgl functions since we load them directly from OpenGL32.dll
sf::GlFunctionPointer getOpenGl32Function(const char* name)
{
static const HMODULE module = GetModuleHandleA("OpenGL32.dll");
if (module)
return reinterpret_cast<sf::GlFunctionPointer>(GetProcAddress(module, reinterpret_cast<LPCSTR>(name)));
return nullptr;
}
////////////////////////////////////////////////////////////
void ensureInit()
{
static bool initialized = false;
if (!initialized)
{
initialized = true;
gladLoadWGL(nullptr, getOpenGl32Function);
}
}
////////////////////////////////////////////////////////////
void ensureExtensionsInit(HDC deviceContext)
{
static bool initialized = false;
if (!initialized)
{
initialized = true;
// We don't check the return value since the extension
// flags are cleared even if loading fails
gladLoadWGL(deviceContext, sf::priv::WglContext::getFunction);
}
}
} // namespace WglContextImpl
} // namespace
namespace sf::priv
{
////////////////////////////////////////////////////////////
WglContext::WglContext(WglContext* shared) : WglContext(shared, ContextSettings{}, {1u, 1u})
{
}
////////////////////////////////////////////////////////////
WglContext::WglContext(WglContext* shared, const ContextSettings& settings, const WindowImpl& owner, unsigned int bitsPerPixel)
{
WglContextImpl::ensureInit();
// Save the creation settings
m_settings = settings;
// Create the rendering surface from the owner window
createSurface(owner.getNativeHandle(), bitsPerPixel);
// Create the context
createContext(shared);
}
////////////////////////////////////////////////////////////
WglContext::WglContext(WglContext* shared, const ContextSettings& settings, Vector2u size)
{
WglContextImpl::ensureInit();
// Save the creation settings
m_settings = settings;
// Create the rendering surface (window or pbuffer if supported)
createSurface(shared, size, VideoMode::getDesktopMode().bitsPerPixel);
// Create the context
createContext(shared);
}
////////////////////////////////////////////////////////////
WglContext::~WglContext()
{
// Notify unshared OpenGL resources of context destruction
cleanupUnsharedResources();
// Destroy the OpenGL context
if (m_context)
{
if (WglContextImpl::currentContext == this)
{
if (wglMakeCurrent(m_deviceContext, nullptr) == TRUE)
WglContextImpl::currentContext = nullptr;
}
wglDeleteContext(m_context);
}
// Destroy the device context
if (m_deviceContext)
{
if (m_pbuffer)
{
wglReleasePbufferDCARB(m_pbuffer, m_deviceContext);
wglDestroyPbufferARB(m_pbuffer);
}
else
{
ReleaseDC(m_window, m_deviceContext);
}
}
// Destroy the window if we own it
if (m_window && m_ownsWindow)
DestroyWindow(m_window);
}
////////////////////////////////////////////////////////////
GlFunctionPointer WglContext::getFunction(const char* name)
{
if (WglContextImpl::currentContext == nullptr)
return nullptr;
// If we are using the generic GDI implementation, skip to loading directly from OpenGL32.dll since it doesn't support extensions
if (!WglContextImpl::currentContext->m_isGeneric)
{
auto address = reinterpret_cast<GlFunctionPointer>(wglGetProcAddress(reinterpret_cast<LPCSTR>(name)));
if (address)
{
// Test whether the returned value is a valid error code
auto errorCode = reinterpret_cast<std::ptrdiff_t>(address);
if ((errorCode != -1) && (errorCode != 1) && (errorCode != 2) && (errorCode != 3))
return address;
}
}
return WglContextImpl::getOpenGl32Function(name);
}
////////////////////////////////////////////////////////////
bool WglContext::makeCurrent(bool current)
{
if (!m_deviceContext || !m_context)
return false;
if (wglMakeCurrent(m_deviceContext, current ? m_context : nullptr) == FALSE)
{
err() << "Failed to " << (current ? "activate" : "deactivate")
<< " OpenGL context: " << getErrorString(GetLastError()) << std::endl;
return false;
}
WglContextImpl::currentContext = (current ? this : nullptr);
return true;
}
////////////////////////////////////////////////////////////
void WglContext::display()
{
if (m_deviceContext && m_context)
SwapBuffers(m_deviceContext);
}
////////////////////////////////////////////////////////////
void WglContext::setVerticalSyncEnabled(bool enabled)
{
// Make sure that extensions are initialized
WglContextImpl::ensureExtensionsInit(m_deviceContext);
if (SF_GLAD_WGL_EXT_swap_control)
{
if (wglSwapIntervalEXT(enabled) == FALSE)
err() << "Setting vertical sync failed: " << getErrorString(GetLastError()) << std::endl;
}
else
{
static bool warned = false;
if (!warned)
{
// wglSwapIntervalEXT not supported
err() << "Setting vertical sync not supported" << std::endl;
warned = true;
}
}
}
////////////////////////////////////////////////////////////
int WglContext::selectBestPixelFormat(HDC deviceContext, unsigned int bitsPerPixel, const ContextSettings& settings, bool pbuffer)
{
// Selecting a pixel format can be an expensive process on some implementations
// Since the same pixel format should always be selected for a specific combination of inputs
// we can cache the result of the lookup instead of having to perform it multiple times for the same inputs
struct PixelFormatCacheEntry
{
unsigned int bitsPerPixel{};
unsigned int depthBits{};
unsigned int stencilBits{};
unsigned int antiAliasingLevel{};
bool pbuffer{};
int bestFormat{};
};
static std::mutex cacheMutex;
static std::vector<PixelFormatCacheEntry> pixelFormatCache;
// Check if we have already previously found a pixel format for
// the current inputs and return it if one has been previously found
{
const std::lock_guard lock(cacheMutex);
for (const auto& entry : pixelFormatCache)
{
if (bitsPerPixel == entry.bitsPerPixel && settings.depthBits == entry.depthBits &&
settings.stencilBits == entry.stencilBits && settings.antiAliasingLevel == entry.antiAliasingLevel &&
pbuffer == entry.pbuffer)
return entry.bestFormat;
}
}
WglContextImpl::ensureInit();
// Find a suitable pixel format -- first try with wglChoosePixelFormatARB
int bestFormat = 0;
if (SF_GLAD_WGL_ARB_pixel_format)
{
// Define the basic attributes we want for our window
static constexpr std::array intAttributes =
{WGL_DRAW_TO_WINDOW_ARB,
GL_TRUE,
WGL_SUPPORT_OPENGL_ARB,
GL_TRUE,
WGL_DOUBLE_BUFFER_ARB,
GL_TRUE,
WGL_PIXEL_TYPE_ARB,
WGL_TYPE_RGBA_ARB,
0,
0};
// Check how many formats are supporting our requirements
std::array<int, 512> formats{};
UINT nbFormats = 0; // We must initialize to 0 otherwise broken drivers might fill with garbage in the following call
const bool isValid = wglChoosePixelFormatARB(deviceContext,
intAttributes.data(),
nullptr,
static_cast<UINT>(formats.size()),
formats.data(),
&nbFormats) != FALSE;
if (!isValid)
err() << "Failed to enumerate pixel formats: " << getErrorString(GetLastError()) << std::endl;
// Get the best format among the returned ones
if (isValid && (nbFormats > 0))
{
int bestScore = 0x7FFFFFFF;
for (UINT i = 0; i < nbFormats; ++i)
{
// Extract the components of the current format
std::array<int, 7> values{};
static constexpr std::array attributes =
{WGL_RED_BITS_ARB,
WGL_GREEN_BITS_ARB,
WGL_BLUE_BITS_ARB,
WGL_ALPHA_BITS_ARB,
WGL_DEPTH_BITS_ARB,
WGL_STENCIL_BITS_ARB,
WGL_ACCELERATION_ARB};
if (wglGetPixelFormatAttribivARB(deviceContext,
formats[i],
PFD_MAIN_PLANE,
static_cast<UINT>(values.size()),
attributes.data(),
values.data()) == FALSE)
{
err() << "Failed to retrieve pixel format information: " << getErrorString(GetLastError()) << std::endl;
break;
}
std::array sampleValues = {0, 0};
if (SF_GLAD_WGL_ARB_multisample)
{
static constexpr std::array sampleAttributes = {WGL_SAMPLE_BUFFERS_ARB, WGL_SAMPLES_ARB};
if (wglGetPixelFormatAttribivARB(deviceContext,
formats[i],
PFD_MAIN_PLANE,
static_cast<UINT>(sampleAttributes.size()),
sampleAttributes.data(),
sampleValues.data()) == FALSE)
{
err() << "Failed to retrieve pixel format multisampling information: "
<< getErrorString(GetLastError()) << std::endl;
break;
}
}
int sRgbCapableValue = 0;
if (SF_GLAD_WGL_ARB_framebuffer_sRGB || SF_GLAD_WGL_EXT_framebuffer_sRGB)
{
const int sRgbCapableAttribute = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB;
if (wglGetPixelFormatAttribivARB(deviceContext, formats[i], PFD_MAIN_PLANE, 1, &sRgbCapableAttribute, &sRgbCapableValue) ==
FALSE)
{
err() << "Failed to retrieve pixel format sRGB capability information: "
<< getErrorString(GetLastError()) << std::endl;
break;
}
}
if (pbuffer)
{
static constexpr std::array pbufferAttributes = {WGL_DRAW_TO_PBUFFER_ARB};
int pbufferValue = 0;
if (wglGetPixelFormatAttribivARB(deviceContext,
formats[i],
PFD_MAIN_PLANE,
static_cast<UINT>(pbufferAttributes.size()),
pbufferAttributes.data(),
&pbufferValue) == FALSE)
{
err() << "Failed to retrieve pixel format pbuffer information: " << getErrorString(GetLastError())
<< std::endl;
break;
}
if (pbufferValue != GL_TRUE)
continue;
}
// Evaluate the current configuration
const int color = values[0] + values[1] + values[2] + values[3];
const int score = evaluateFormat(bitsPerPixel,
settings,
color,
values[4],
values[5],
sampleValues[0] ? sampleValues[1] : 0,
values[6] == WGL_FULL_ACCELERATION_ARB,
sRgbCapableValue == TRUE);
// Keep it if it's better than the current best
if (score < bestScore)
{
bestScore = score;
bestFormat = formats[i];
}
}
}
}
// Find a pixel format with ChoosePixelFormat, if wglChoosePixelFormatARB is not supported
// ChoosePixelFormat doesn't support pbuffers
if ((bestFormat == 0) && !pbuffer)
{
// Setup a pixel format descriptor from the rendering settings
PIXELFORMATDESCRIPTOR descriptor;
ZeroMemory(&descriptor, sizeof(descriptor));
descriptor.nSize = sizeof(descriptor);
descriptor.nVersion = 1;
descriptor.iLayerType = PFD_MAIN_PLANE;
descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
descriptor.iPixelType = PFD_TYPE_RGBA;
descriptor.cColorBits = static_cast<BYTE>(bitsPerPixel);
descriptor.cDepthBits = static_cast<BYTE>(settings.depthBits);
descriptor.cStencilBits = static_cast<BYTE>(settings.stencilBits);
descriptor.cAlphaBits = bitsPerPixel == 32 ? 8 : 0;
// Get the pixel format that best matches our requirements
bestFormat = ChoosePixelFormat(deviceContext, &descriptor);
}
// If we get this far, the format wasn't found in the cache so add it here
{
const std::lock_guard lock(cacheMutex);
pixelFormatCache.emplace_back(
PixelFormatCacheEntry{bitsPerPixel, settings.depthBits, settings.stencilBits, settings.antiAliasingLevel, pbuffer, bestFormat});
}
return bestFormat;
}
////////////////////////////////////////////////////////////
void WglContext::setDevicePixelFormat(unsigned int bitsPerPixel)
{
const int bestFormat = selectBestPixelFormat(m_deviceContext, bitsPerPixel, m_settings);
if (bestFormat == 0)
{
err() << "Failed to find a suitable pixel format for device context: " << getErrorString(GetLastError()) << '\n'
<< "Cannot create OpenGL context" << std::endl;
return;
}
// Extract the depth and stencil bits from the chosen format
PIXELFORMATDESCRIPTOR actualFormat;
actualFormat.nSize = sizeof(actualFormat);
actualFormat.nVersion = 1;
DescribePixelFormat(m_deviceContext, bestFormat, sizeof(actualFormat), &actualFormat);
// Set the chosen pixel format
if (SetPixelFormat(m_deviceContext, bestFormat, &actualFormat) == FALSE)
{
err() << "Failed to set pixel format for device context: " << getErrorString(GetLastError()) << '\n'
<< "Cannot create OpenGL context" << std::endl;
return;
}
}
////////////////////////////////////////////////////////////
void WglContext::updateSettingsFromPixelFormat()
{
const int format = GetPixelFormat(m_deviceContext);
if (format == 0)
{
err() << "Failed to get selected pixel format: " << getErrorString(GetLastError()) << std::endl;
return;
}
PIXELFORMATDESCRIPTOR actualFormat;
actualFormat.nSize = sizeof(actualFormat);
actualFormat.nVersion = 1;
if (DescribePixelFormat(m_deviceContext, format, sizeof(actualFormat), &actualFormat) == 0)
{
err() << "Failed to retrieve pixel format information: " << getErrorString(GetLastError()) << std::endl;
return;
}
// Detect if we are running using the generic GDI implementation and warn
if (actualFormat.dwFlags & PFD_GENERIC_FORMAT)
{
m_isGeneric = true;
err() << "Warning: Detected \"Microsoft Corporation GDI Generic\" OpenGL implementation" << std::endl;
// Detect if the generic GDI implementation is not accelerated
if (!(actualFormat.dwFlags & PFD_GENERIC_ACCELERATED))
err() << "Warning: The \"Microsoft Corporation GDI Generic\" OpenGL implementation is not "
"hardware-accelerated"
<< std::endl;
}
if (SF_GLAD_WGL_ARB_pixel_format)
{
static constexpr std::array attributes = {WGL_DEPTH_BITS_ARB, WGL_STENCIL_BITS_ARB};
std::array<int, 2> values{};
if (wglGetPixelFormatAttribivARB(m_deviceContext,
format,
PFD_MAIN_PLANE,
static_cast<UINT>(attributes.size()),
attributes.data(),
values.data()) == TRUE)
{
m_settings.depthBits = static_cast<unsigned int>(values[0]);
m_settings.stencilBits = static_cast<unsigned int>(values[1]);
}
else
{
err() << "Failed to retrieve pixel format information: " << getErrorString(GetLastError()) << std::endl;
m_settings.depthBits = actualFormat.cDepthBits;
m_settings.stencilBits = actualFormat.cStencilBits;
}
if (SF_GLAD_WGL_ARB_multisample)
{
static constexpr std::array sampleAttributes = {WGL_SAMPLE_BUFFERS_ARB, WGL_SAMPLES_ARB};
std::array<int, 2> sampleValues{};
if (wglGetPixelFormatAttribivARB(m_deviceContext,
format,
PFD_MAIN_PLANE,
static_cast<UINT>(sampleAttributes.size()),
sampleAttributes.data(),
sampleValues.data()) == TRUE)
{
m_settings.antiAliasingLevel = static_cast<unsigned int>(sampleValues[0] ? sampleValues[1] : 0);
}
else
{
err() << "Failed to retrieve pixel format multisampling information: " << getErrorString(GetLastError())
<< std::endl;
m_settings.antiAliasingLevel = 0;
}
}
else
{
m_settings.antiAliasingLevel = 0;
}
if (SF_GLAD_WGL_ARB_framebuffer_sRGB || SF_GLAD_WGL_EXT_framebuffer_sRGB)
{
static constexpr std::array sRgbCapableAttribute = {WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB};
int sRgbCapableValue = 0;
if (wglGetPixelFormatAttribivARB(m_deviceContext,
format,
PFD_MAIN_PLANE,
static_cast<UINT>(sRgbCapableAttribute.size()),
sRgbCapableAttribute.data(),
&sRgbCapableValue) == TRUE)
{
m_settings.sRgbCapable = (sRgbCapableValue == TRUE);
}
else
{
err() << "Failed to retrieve pixel format sRGB capability information: " << getErrorString(GetLastError())
<< std::endl;
m_settings.sRgbCapable = false;
}
}
else
{
m_settings.sRgbCapable = false;
}
}
else
{
m_settings.depthBits = actualFormat.cDepthBits;
m_settings.stencilBits = actualFormat.cStencilBits;
m_settings.antiAliasingLevel = 0;
}
}
////////////////////////////////////////////////////////////
void WglContext::createSurface(WglContext* shared, Vector2u size, unsigned int bitsPerPixel)
{
// Check if the shared context already exists and pbuffers are supported
if (shared && shared->m_deviceContext && SF_GLAD_WGL_ARB_pbuffer)
{
const int bestFormat = selectBestPixelFormat(shared->m_deviceContext, bitsPerPixel, m_settings, true);
if (bestFormat > 0)
{
static constexpr std::array attributes = {0, 0};
m_pbuffer = wglCreatePbufferARB(shared->m_deviceContext,
bestFormat,
static_cast<int>(size.x),
static_cast<int>(size.y),
attributes.data());
if (m_pbuffer)
{
m_window = shared->m_window;
m_deviceContext = wglGetPbufferDCARB(m_pbuffer);
if (!m_deviceContext)
{
err() << "Failed to retrieve pixel buffer device context: " << getErrorString(GetLastError())
<< std::endl;
wglDestroyPbufferARB(m_pbuffer);
m_pbuffer = nullptr;
}
}
else
{
err() << "Failed to create pixel buffer: " << getErrorString(GetLastError()) << std::endl;
}
}
}
// If pbuffers are not available we use a hidden window as the off-screen surface to draw to
if (!m_deviceContext)
{
// We can't create a memory DC, the resulting context wouldn't be compatible
// with other contexts and thus wglShareLists would always fail
// Create the hidden window
m_window = CreateWindowA("STATIC",
"",
WS_POPUP | WS_DISABLED,
0,
0,
static_cast<int>(size.x),
static_cast<int>(size.y),
nullptr,
nullptr,
GetModuleHandle(nullptr),
nullptr);
ShowWindow(m_window, SW_HIDE);
m_deviceContext = GetDC(m_window);
m_ownsWindow = true;
// Set the pixel format of the device context
setDevicePixelFormat(bitsPerPixel);
}
// Update context settings from the selected pixel format
updateSettingsFromPixelFormat();
}
////////////////////////////////////////////////////////////
void WglContext::createSurface(HWND window, unsigned int bitsPerPixel)
{
m_window = window;
m_deviceContext = GetDC(window);
// Set the pixel format of the device context
setDevicePixelFormat(bitsPerPixel);
// Update context settings from the selected pixel format
updateSettingsFromPixelFormat();
}
////////////////////////////////////////////////////////////
void WglContext::createContext(WglContext* shared)
{
// We can't create an OpenGL context if we don't have a DC
if (!m_deviceContext)
return;
// Get a working copy of the context settings
const ContextSettings settings = m_settings;
// Get the context to share display lists with
HGLRC sharedContext = shared ? shared->m_context : nullptr;
// Create the OpenGL context -- first try using wglCreateContextAttribsARB
while (!m_context && m_settings.majorVersion)
{
if (SF_GLAD_WGL_ARB_create_context)
{
std::vector<int> attributes;
// Check if the user requested a specific context version (anything > 1.1)
if ((m_settings.majorVersion > 1) || ((m_settings.majorVersion == 1) && (m_settings.minorVersion > 1)))
{
attributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB);
attributes.push_back(static_cast<int>(m_settings.majorVersion));
attributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB);
attributes.push_back(static_cast<int>(m_settings.minorVersion));
}
// Check if setting the profile is supported
if (SF_GLAD_WGL_ARB_create_context_profile)
{
const int profile = (m_settings.attributeFlags & ContextSettings::Core)
? WGL_CONTEXT_CORE_PROFILE_BIT_ARB
: WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
const int debug = (m_settings.attributeFlags & ContextSettings::Debug) ? WGL_CONTEXT_DEBUG_BIT_ARB : 0;
attributes.push_back(WGL_CONTEXT_PROFILE_MASK_ARB);
attributes.push_back(profile);
attributes.push_back(WGL_CONTEXT_FLAGS_ARB);
attributes.push_back(debug);
}
else
{
if ((m_settings.attributeFlags & ContextSettings::Core) ||
(m_settings.attributeFlags & ContextSettings::Debug))
err() << "Selecting a profile during context creation is not supported,"
<< "disabling compatibility and debug" << std::endl;
m_settings.attributeFlags = ContextSettings::Default;
}
// Append the terminating 0
attributes.push_back(0);
attributes.push_back(0);
if (sharedContext)
{
static std::recursive_mutex mutex;
const std::lock_guard lock(mutex);
if (WglContextImpl::currentContext == shared)
{
if (wglMakeCurrent(shared->m_deviceContext, nullptr) == FALSE)
{
err() << "Failed to deactivate shared context before sharing: " << getErrorString(GetLastError())
<< std::endl;
return;
}
WglContextImpl::currentContext = nullptr;
}
}
// Create the context
m_context = wglCreateContextAttribsARB(m_deviceContext, sharedContext, attributes.data());
}
else
{
// If wglCreateContextAttribsARB is not supported, there is no need to keep trying
break;
}
// If we couldn't create the context, first try disabling flags,
// then lower the version number and try again -- stop at 0.0
// Invalid version numbers will be generated by this algorithm (like 3.9), but we really don't care
if (!m_context)
{
if (m_settings.attributeFlags != ContextSettings::Default)
{
m_settings.attributeFlags = ContextSettings::Default;
}
else if (m_settings.minorVersion > 0)
{
// If the minor version is not 0, we decrease it and try again
--m_settings.minorVersion;
m_settings.attributeFlags = settings.attributeFlags;
}
else
{
// If the minor version is 0, we decrease the major version
--m_settings.majorVersion;
m_settings.minorVersion = 9;
m_settings.attributeFlags = settings.attributeFlags;
}
}
}
// If wglCreateContextAttribsARB failed, use wglCreateContext
if (!m_context)
{
// set the context version to 1.1 (arbitrary) and disable flags
m_settings.majorVersion = 1;
m_settings.minorVersion = 1;
m_settings.attributeFlags = ContextSettings::Default;
m_context = wglCreateContext(m_deviceContext);
if (!m_context)
{
err() << "Failed to create an OpenGL context for this window: " << getErrorString(GetLastError()) << std::endl;
return;
}
// Share this context with others
if (sharedContext)
{
// wglShareLists doesn't seem to be thread-safe
static std::recursive_mutex mutex;
const std::lock_guard lock(mutex);
if (WglContextImpl::currentContext == shared)
{
if (wglMakeCurrent(shared->m_deviceContext, nullptr) == FALSE)
{
err() << "Failed to deactivate shared context before sharing: " << getErrorString(GetLastError())
<< std::endl;
return;
}
WglContextImpl::currentContext = nullptr;
}
if (wglShareLists(sharedContext, m_context) == FALSE)
err() << "Failed to share the OpenGL context: " << getErrorString(GetLastError()) << std::endl;
}
}
// If we are the shared context, initialize extensions now
// This enables us to re-create the shared context using extensions if we need to
if (!shared && m_context)
{
makeCurrent(true);
WglContextImpl::ensureExtensionsInit(m_deviceContext);
makeCurrent(false);
}
}
} // namespace sf::priv

View File

@ -0,0 +1,197 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/Context.hpp>
#include <SFML/Window/GlContext.hpp>
#include <SFML/System/Vector2.hpp>
#include <glad/wgl.h>
namespace sf
{
struct ContextSettings;
namespace priv
{
class WindowImpl;
////////////////////////////////////////////////////////////
/// \brief Windows (WGL) implementation of OpenGL contexts
///
////////////////////////////////////////////////////////////
class WglContext : public GlContext
{
public:
////////////////////////////////////////////////////////////
/// \brief Create a new default context
///
/// \param shared Context to share the new one with (can be a null pointer)
///
////////////////////////////////////////////////////////////
WglContext(WglContext* shared);
////////////////////////////////////////////////////////////
/// \brief Create a new context attached to a window
///
/// \param shared Context to share the new one with
/// \param settings Creation parameters
/// \param owner Pointer to the owner window
/// \param bitsPerPixel Pixel depth, in bits per pixel
///
////////////////////////////////////////////////////////////
WglContext(WglContext* shared, const ContextSettings& settings, const WindowImpl& owner, unsigned int bitsPerPixel);
////////////////////////////////////////////////////////////
/// \brief Create a new context that embeds its own rendering target
///
/// \param shared Context to share the new one with
/// \param settings Creation parameters
/// \param size Back buffer width and height, in pixels
///
////////////////////////////////////////////////////////////
WglContext(WglContext* shared, const ContextSettings& settings, Vector2u size);
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
~WglContext() override;
////////////////////////////////////////////////////////////
/// \brief Get the address of an OpenGL function
///
/// \param name Name of the function to get the address of
///
/// \return Address of the OpenGL function, 0 on failure
///
////////////////////////////////////////////////////////////
static GlFunctionPointer getFunction(const char* name);
////////////////////////////////////////////////////////////
/// \brief Activate the context as the current target for rendering
///
/// \param current Whether to make the context current or no longer current
///
/// \return `true` on success, `false` if any error happened
///
////////////////////////////////////////////////////////////
bool makeCurrent(bool current) override;
////////////////////////////////////////////////////////////
/// \brief Display what has been rendered to the context so far
///
////////////////////////////////////////////////////////////
void display() override;
////////////////////////////////////////////////////////////
/// \brief Enable or disable vertical synchronization
///
/// Activating vertical synchronization will limit the number
/// of frames displayed to the refresh rate of the monitor.
/// This can avoid some visual artifacts, and limit the framerate
/// to a good value (but not constant across different computers).
///
/// \param enabled: `true` to enable v-sync, `false` to deactivate
///
////////////////////////////////////////////////////////////
void setVerticalSyncEnabled(bool enabled) override;
////////////////////////////////////////////////////////////
/// \brief Select the best pixel format for a given set of settings
///
/// \param deviceContext Device context
/// \param bitsPerPixel Pixel depth, in bits per pixel
/// \param settings Requested context settings
/// \param pbuffer Whether the pixel format should support pbuffers
///
/// \return The best pixel format
///
////////////////////////////////////////////////////////////
static int selectBestPixelFormat(HDC deviceContext,
unsigned int bitsPerPixel,
const ContextSettings& settings,
bool pbuffer = false);
private:
////////////////////////////////////////////////////////////
/// \brief Set the pixel format of the device context
///
/// \param bitsPerPixel Pixel depth, in bits per pixel
///
////////////////////////////////////////////////////////////
void setDevicePixelFormat(unsigned int bitsPerPixel);
////////////////////////////////////////////////////////////
/// \brief Update the context settings from the selected pixel format
///
////////////////////////////////////////////////////////////
void updateSettingsFromPixelFormat();
////////////////////////////////////////////////////////////
/// \brief Create the context's drawing surface
///
/// \param shared Shared context (can be a null pointer)
/// \param size Back buffer width and height, in pixels
/// \param bitsPerPixel Pixel depth, in bits per pixel
///
////////////////////////////////////////////////////////////
void createSurface(WglContext* shared, Vector2u size, unsigned int bitsPerPixel);
////////////////////////////////////////////////////////////
/// \brief Create the context's drawing surface from an existing window
///
/// \param window Window handle of the owning window
/// \param bitsPerPixel Pixel depth, in bits per pixel
///
////////////////////////////////////////////////////////////
void createSurface(HWND window, unsigned int bitsPerPixel);
////////////////////////////////////////////////////////////
/// \brief Create the context
///
/// \param shared Context to share the new one with (can be a null pointer)
///
////////////////////////////////////////////////////////////
void createContext(WglContext* shared);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
HWND m_window{}; //!< Window to which the context is attached
HPBUFFERARB m_pbuffer{}; //!< Handle to a pbuffer if one was created
HDC m_deviceContext{}; //!< Device context associated to the context
HGLRC m_context{}; //!< OpenGL context
bool m_ownsWindow{}; //!< Do we own the target window?
bool m_isGeneric{}; //!< Is this context provided by the generic GDI implementation?
};
} // namespace priv
} // namespace sf

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,315 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/Keyboard.hpp>
#include <SFML/Window/WindowEnums.hpp>
#include <SFML/Window/WindowHandle.hpp>
#include <SFML/Window/WindowImpl.hpp>
#include <SFML/System/Vector2.hpp>
#include <SFML/System/Win32/WindowsHeader.hpp>
#include <cstdint>
namespace sf
{
class String;
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Windows implementation of WindowImpl
///
////////////////////////////////////////////////////////////
class WindowImplWin32 : public WindowImpl
{
public:
////////////////////////////////////////////////////////////
/// \brief Construct the window implementation from an existing control
///
/// \param handle Platform-specific handle of the control
///
////////////////////////////////////////////////////////////
WindowImplWin32(WindowHandle handle);
////////////////////////////////////////////////////////////
/// \brief Create the window implementation
///
/// \param mode Video mode to use
/// \param title Title of the window
/// \param style Window style
/// \param state Window state
/// \param settings Additional settings for the underlying OpenGL context
///
////////////////////////////////////////////////////////////
WindowImplWin32(VideoMode mode, const String& title, std::uint32_t style, State state, const ContextSettings& settings);
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
~WindowImplWin32() override;
////////////////////////////////////////////////////////////
/// \brief Get the OS-specific handle of the window
///
/// \return Handle of the window
///
////////////////////////////////////////////////////////////
[[nodiscard]] WindowHandle getNativeHandle() const override;
////////////////////////////////////////////////////////////
/// \brief Get the position of the window
///
/// \return Position of the window, in pixels
///
////////////////////////////////////////////////////////////
[[nodiscard]] Vector2i getPosition() const override;
////////////////////////////////////////////////////////////
/// \brief Change the position of the window on screen
///
/// \param position New position of the window, in pixels
///
////////////////////////////////////////////////////////////
void setPosition(Vector2i position) override;
////////////////////////////////////////////////////////////
/// \brief Get the client size of the window
///
/// \return Size of the window, in pixels
///
////////////////////////////////////////////////////////////
[[nodiscard]] Vector2u getSize() const override;
////////////////////////////////////////////////////////////
/// \brief Change the size of the rendering region of the window
///
/// \param size New size, in pixels
///
////////////////////////////////////////////////////////////
void setSize(Vector2u size) override;
////////////////////////////////////////////////////////////
/// \brief Change the title of the window
///
/// \param title New title
///
////////////////////////////////////////////////////////////
void setTitle(const String& title) override;
////////////////////////////////////////////////////////////
/// \brief Change the window's icon
///
/// \param size Icon's width and height, in pixels
/// \param pixels Pointer to the pixels in memory, format must be RGBA 32 bits
///
////////////////////////////////////////////////////////////
void setIcon(Vector2u size, const std::uint8_t* pixels) override;
////////////////////////////////////////////////////////////
/// \brief Show or hide the window
///
/// \param visible `true` to show, `false` to hide
///
////////////////////////////////////////////////////////////
void setVisible(bool visible) override;
////////////////////////////////////////////////////////////
/// \brief Show or hide the mouse cursor
///
/// \param visible `true` to show, `false` to hide
///
////////////////////////////////////////////////////////////
void setMouseCursorVisible(bool visible) override;
////////////////////////////////////////////////////////////
/// \brief Grab or release the mouse cursor
///
/// \param grabbed `true` to enable, `false` to disable
///
////////////////////////////////////////////////////////////
void setMouseCursorGrabbed(bool grabbed) override;
////////////////////////////////////////////////////////////
/// \brief Set the displayed cursor to a native system cursor
///
/// \param cursor Native system cursor type to display
///
////////////////////////////////////////////////////////////
void setMouseCursor(const CursorImpl& cursor) override;
////////////////////////////////////////////////////////////
/// \brief Enable or disable automatic key-repeat
///
/// \param enabled `true` to enable, `false` to disable
///
////////////////////////////////////////////////////////////
void setKeyRepeatEnabled(bool enabled) override;
////////////////////////////////////////////////////////////
/// \brief Request the current window to be made the active
/// foreground window
///
////////////////////////////////////////////////////////////
void requestFocus() override;
////////////////////////////////////////////////////////////
/// \brief Check whether the window has the input focus
///
/// \return `true` if window has focus, `false` otherwise
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool hasFocus() const override;
protected:
////////////////////////////////////////////////////////////
/// \brief Process incoming events from the operating system
///
////////////////////////////////////////////////////////////
void processEvents() override;
private:
////////////////////////////////////////////////////////////
/// Register the window class
///
////////////////////////////////////////////////////////////
void registerWindowClass();
////////////////////////////////////////////////////////////
/// \brief Switch to fullscreen mode
///
/// \param mode Video mode to switch to
///
////////////////////////////////////////////////////////////
void switchToFullscreen(const VideoMode& mode);
////////////////////////////////////////////////////////////
/// \brief Free all the graphical resources attached to the window
///
////////////////////////////////////////////////////////////
void cleanup();
////////////////////////////////////////////////////////////
/// \brief Process a Win32 event
///
/// \param message Message to process
/// \param wParam First parameter of the event
/// \param lParam Second parameter of the event
///
////////////////////////////////////////////////////////////
void processEvent(UINT message, WPARAM wParam, LPARAM lParam);
////////////////////////////////////////////////////////////
/// \brief Enables or disables tracking for the mouse cursor leaving the window
///
/// \param track `true` to enable, `false` to disable
///
////////////////////////////////////////////////////////////
void setTracking(bool track);
////////////////////////////////////////////////////////////
/// \brief Grab or release the mouse cursor
///
/// This is not to be confused with setMouseCursorGrabbed.
/// Here m_cursorGrabbed is not modified; it is used,
/// for example, to release the cursor when switching to
/// another application.
///
/// \param grabbed `true` to enable, `false` to disable
///
////////////////////////////////////////////////////////////
void grabCursor(bool grabbed);
////////////////////////////////////////////////////////////
/// \brief Convert content size to window size including window chrome
///
/// \param size Size to convert
///
/// \return Converted size including window chrome
///
////////////////////////////////////////////////////////////
Vector2i contentSizeToWindowSize(Vector2u size);
////////////////////////////////////////////////////////////
/// \brief Convert a Win32 virtual key code to a SFML key code
///
/// \param key Virtual key code to convert
/// \param flags Additional flags
///
/// \return SFML key code corresponding to the key
///
////////////////////////////////////////////////////////////
static Keyboard::Key virtualKeyCodeToSF(WPARAM key, LPARAM flags);
////////////////////////////////////////////////////////////
/// \brief Function called whenever one of our windows receives a message
///
/// \param handle Win32 handle of the window
/// \param message Message received
/// \param wParam First parameter of the message
/// \param lParam Second parameter of the message
///
/// \return `true` to discard the event after it has been processed
///
////////////////////////////////////////////////////////////
static LRESULT CALLBACK globalOnEvent(HWND handle, UINT message, WPARAM wParam, LPARAM lParam);
////////////////////////////////////////////////////////////
/// \brief Convert a Win32 scancode to an sfml scancode
///
/// \param flags input flags
///
/// \return SFML scancode corresponding to the key
///
////////////////////////////////////////////////////////////
static Keyboard::Scancode toScancode(WPARAM wParam, LPARAM lParam);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
HWND m_handle{}; //!< Win32 handle of the window
LONG_PTR m_callback{}; //!< Stores the original event callback function of the control
bool m_cursorVisible{true}; //!< Is the cursor visible or hidden?
HCURSOR m_lastCursor{
LoadCursor(nullptr, IDC_ARROW)}; //!< Last cursor used -- this data is not owned by the window and is required to be always valid
HICON m_icon{}; //!< Custom icon assigned to the window
bool m_keyRepeatEnabled{true}; //!< Automatic key-repeat state for keydown events
Vector2u m_lastSize; //!< The last handled size of the window
bool m_resizing{}; //!< Is the window being resized?
char16_t m_surrogate{}; //!< First half of the surrogate pair, in case we're receiving a Unicode character in two events
bool m_mouseInside{}; //!< Mouse is inside the window?
bool m_fullscreen{}; //!< Is the window fullscreen?
bool m_cursorGrabbed{}; //!< Is the mouse cursor trapped?
};
} // namespace priv
} // namespace sf