SDL3 port WIP.

This commit is contained in:
Martín Lucas Golini
2026-03-21 19:06:19 -03:00
parent c201c8bb50
commit 70cec917da
30 changed files with 2852 additions and 21 deletions

View File

@@ -172,7 +172,7 @@
"eepp-linux": {
"build": [
{
"args": "--disable-static-build --with-mold-linker --with-debug-symbols --address-sanitizer gmake",
"args": "--disable-static-build --with-mold-linker --with-debug-symbols --address-sanitizer --with-backend=SDL3 gmake",
"command": "premake4",
"working_dir": "${project_root}"
},
@@ -464,7 +464,7 @@
"eepp-linux-ninja": {
"build": [
{
"args": "--disable-static-build --with-debug-symbols ninja",
"args": "--disable-static-build --with-debug-symbols --with-backend=SDL3 ninja",
"command": "premake5",
"working_dir": "${project_root}"
},

View File

@@ -166,6 +166,13 @@ class EE_API Engine {
EE::Window::Window* createSDL2Window( const WindowSettings& Settings,
const ContextSettings& Context );
#ifdef EE_BACKEND_SDL3
Backend::WindowBackendLibrary* createSDL3Backend( const WindowSettings& Settings );
EE::Window::Window* createSDL3Window( const WindowSettings& Settings,
const ContextSettings& Context );
#endif
EE::Window::Window* createDefaultWindow( const WindowSettings& Settings,
const ContextSettings& Context );

View File

@@ -38,7 +38,7 @@ enum class WindowFlashOperation {
UntilFocused,
};
enum class WindowBackend : Uint32 { SDL2, Default };
enum class WindowBackend : Uint32 { SDL2, SDL3, Default };
#ifndef EE_SCREEN_KEYBOARD_ENABLED
#define EE_SCREEN_KEYBOARD_ENABLED false
@@ -89,7 +89,8 @@ class WindowSettings {
/** @brief ContextSettings Small class that contains the renderer context information */
class ContextSettings {
public:
static constexpr Int32 FrameRateLimitScreenRefreshRate = (std::numeric_limits<Int32>::max)() - 1;
static constexpr Int32 FrameRateLimitScreenRefreshRate =
( std::numeric_limits<Int32>::max )() - 1;
inline ContextSettings( bool vsync, Int32 frameRateLimit = FrameRateLimitScreenRefreshRate,
Uint32 multisamples = 0, GraphicsLibraryVersion version = GLv_default,

View File

@@ -171,7 +171,8 @@ newoption {
trigger = "with-backend",
description = "Select the backend to use for window and input handling.\n\t\t\tIf no backend is selected or if the selected is not installed the script will search for a backend present in the system, and will use it.",
allowed = {
{ "SDL2", "SDL2" }
{ "SDL2", "SDL2" },
{ "SDL3", "SDL3" },
}
}
newoption {
@@ -802,17 +803,47 @@ function add_sdl2()
end
end
function add_sdl3()
print("Using SDL3 backend");
files { "src/eepp/window/backend/SDL3/*.cpp" }
defines { "EE_BACKEND_SDL_ACTIVE", "EE_SDL_VERSION_3" }
if not can_add_static_backend("SDL3") then
if not os.is_real("emscripten") then
table.insert( link_list, get_backend_link_name( "SDL3" ) )
end
else
insert_static_backend( "SDL3" )
end
end
function set_apple_config()
if is_xcode() or _OPTIONS["use-frameworks"] then
linkoptions { "-F /Library/Frameworks" }
buildoptions { "-F /Library/Frameworks" }
includedirs { "/Library/Frameworks/SDL2.framework/Headers" }
if table.contains(backends, "SDL2") then
includedirs { "/Library/Frameworks/SDL2.framework/Headers" }
end
if table.contains(backends, "SDL3") then
includedirs { "/Library/Frameworks/SDL3.framework/Headers" }
end
end
if os.is("macosx") then
defines { "EE_SDL2_FROM_ROOTPATH" }
if table.contains(backends, "SDL2") then
defines { "EE_SDL2_FROM_ROOTPATH" }
end
if table.contains(backends, "SDL3") then
defines { "EE_SDL3_FROM_ROOTPATH" }
end
if not is_xcode() and not _OPTIONS["use-frameworks"] then
local sdl2flags = popen("sdl2-config --cflags"):gsub("\n", "")
buildoptions { sdl2flags }
if table.contains(backends, "SDL2") then
local sdl2flags = popen("sdl2-config --cflags"):gsub("\n", "")
buildoptions { sdl2flags }
end
if table.contains(backends, "SDL3") then
local sdl3flags = popen("sdl3-config --cflags"):gsub("\n", "")
buildoptions { sdl3flags }
end
end
end
end
@@ -890,9 +921,16 @@ function select_backend()
add_sdl2()
end
if backend_is("SDL3", "SDL3") then
print("Selected SDL3")
add_sdl3()
end
-- If the selected backend is not present, try to find one present
if not backend_selected then
if os_findlib("SDL2", "SDL2") then
if os_findlib("SDL3", "SDL3") then
add_sdl3()
elseif os_findlib("SDL2", "SDL2") then
add_sdl2()
else
print("ERROR: Couldnt find any backend. Forced SDL2.")

View File

@@ -24,6 +24,7 @@ newoption {
description = "Select the backend to use for window and input handling.\n\t\t\tIf no backend is selected or if the selected is not installed the script will search for a backend present in the system, and will use it.",
allowed = {
{ "SDL2", "SDL2" },
{ "SDL3", "SDL3" },
}
}
newoption {
@@ -642,17 +643,45 @@ function add_sdl2()
table.insert( backends, "SDL2" )
end
function add_sdl3()
print("Using SDL3 backend");
if not can_add_static_backend("SDL3") then
table.insert( link_list, get_backend_link_name( "SDL3" ) )
else
print("Using static backend")
insert_static_backend( "SDL3" )
end
table.insert( backends, "SDL3" )
end
function set_apple_config()
if is_xcode() or _OPTIONS["use-frameworks"] then
linkoptions { "-F /Library/Frameworks" }
buildoptions { "-F /Library/Frameworks" }
incdirs { "/Library/Frameworks/SDL2.framework/Headers" }
if table.contains(backends, "SDL2") then
incdirs { "/Library/Frameworks/SDL2.framework/Headers" }
end
if table.contains(backends, "SDL3") then
incdirs { "/Library/Frameworks/SDL3.framework/Headers" }
end
end
if os.istarget("macosx") then
defines { "EE_SDL2_FROM_ROOTPATH" }
if table.contains(backends, "SDL2") then
defines { "EE_SDL2_FROM_ROOTPATH" }
end
if table.contains(backends, "SDL3") then
defines { "EE_SDL3_FROM_ROOTPATH" }
end
if not is_xcode() and not _OPTIONS["use-frameworks"] then
local sdl2flags = popen("sdl2-config --cflags"):gsub("\n", "")
buildoptions { sdl2flags }
if table.contains(backends, "SDL2") then
local sdl2flags = popen("sdl2-config --cflags"):gsub("\n", "")
buildoptions { sdl2flags }
end
if table.contains(backends, "SDL3") then
local sdl3flags = popen("sdl3-config --cflags"):gsub("\n", "")
buildoptions { sdl3flags }
end
end
end
end
@@ -718,9 +747,16 @@ function select_backend()
add_sdl2()
end
if backend_is("SDL3", "SDL3") then
print("Selected SDL3")
add_sdl3()
end
-- If the selected backend is not present, try to find one present
if not backend_selected then
if os_findlib("SDL2", "SDL2") then
if os_findlib("SDL3", "SDL3") then
add_sdl3()
elseif os_findlib("SDL2", "SDL2") then
add_sdl2()
else
print("ERROR: Couldnt find any backend. Forced SDL2.")
@@ -814,6 +850,11 @@ function build_eepp( build_name )
defines { "EE_BACKEND_SDL_ACTIVE", "EE_SDL_VERSION_2" }
end
if table.contains( backends, "SDL3" ) then
files { "src/eepp/window/backend/SDL3/*.cpp" }
defines { "EE_BACKEND_SDL_ACTIVE", "EE_SDL_VERSION_3" }
end
multiple_insert( link_list, os_links )
links { link_list }
@@ -1666,8 +1707,14 @@ workspace "eepp"
links { "eterm-static", "languages-syntax-highlighting-static" }
incdirs { "src/modules/eterm/include/" }
language "C++"
files { "src/tests/unit_tests/*.cpp" }
build_link_configuration( "eepp-unit_tests", true )
files { "src/tests/unit_tests/*.cpp" }
build_link_configuration( "eepp-unit_tests", true )
if table.contains(backends, "SDL2") then
defines { "EE_BACKEND_SDL_ACTIVE", "EE_SDL_VERSION_2" }
end
if table.contains(backends, "SDL3") then
defines { "EE_BACKEND_SDL_ACTIVE", "EE_SDL_VERSION_3" }
end
if os.isfile("external_projects.lua") then
dofile("external_projects.lua")

View File

@@ -0,0 +1,34 @@
#include <eepp/window/backend/SDL3/backendsdl3.hpp>
#ifdef EE_BACKEND_SDL3
#if ( EE_PLATFORM == EE_PLATFORM_ANDROID || EE_PLATFORM == EE_PLATFORM_IOS ) && defined( main )
#undef main
#endif
#if EE_PLATFORM != EE_PLATFORM_ANDROID && EE_PLATFORM != EE_PLATFORM_IOS && \
EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN && !defined( EE_COMPILER_MSVC ) && \
!defined( EE_SDL3_FROM_ROOTPATH )
#include <SDL3/SDL.h>
#else
#include <SDL.h>
#endif
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
WindowBackendSDL3::WindowBackendSDL3() : WindowBackendLibrary() {
SDL_SetHint( SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0" );
// The following hints are not available in SDL3 (or renamed)
// SDL_SetHint( SDL_HINT_IME_SHOW_UI, "1" );
// SDL_SetHint( SDL_HINT_IME_SUPPORT_EXTENDED_TEXT, "1" );
}
WindowBackendSDL3::~WindowBackendSDL3() {
#if EE_PLATFORM != EE_PLATFORM_MACOS
SDL_Quit();
#endif
}
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,25 @@
#ifndef EE_WINDOWCBACKENDSDL3_HPP
#define EE_WINDOWCBACKENDSDL3_HPP
#include <eepp/window/backend.hpp>
#include <eepp/window/backend/SDL3/base.hpp>
#ifdef EE_BACKEND_SDL3
#include <eepp/window/backend/SDL3/displaymanagersdl3.hpp>
#include <eepp/window/backend/SDL3/windowsdl3.hpp>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
class EE_API WindowBackendSDL3 : public WindowBackendLibrary {
public:
WindowBackendSDL3();
~WindowBackendSDL3();
};
}}}} // namespace EE::Window::Backend::SDL3
#endif
#endif

View File

@@ -0,0 +1,32 @@
#ifndef EE_WINDOWBACKEND_BASE_SDL3_HPP
#define EE_WINDOWBACKEND_BASE_SDL3_HPP
#include <eepp/core.hpp>
#ifdef EE_BACKEND_SDL_ACTIVE
#if defined( EE_SDL_VERSION_3 )
#ifndef EE_BACKEND_SDL3
#define EE_BACKEND_SDL3
#endif
#if ( EE_PLATFORM == EE_PLATFORM_ANDROID || EE_PLATFORM == EE_PLATFORM_IOS ) && defined( main )
#undef main
#endif
#if EE_PLATFORM != EE_PLATFORM_ANDROID && EE_PLATFORM != EE_PLATFORM_IOS && \
EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN && !defined( EE_COMPILER_MSVC ) && \
!defined( EE_SDL3_FROM_ROOTPATH )
#include <SDL3/SDL.h>
#else
#include <SDL.h>
#endif
#else
#ifndef EE_BACKEND_SDL_1_2
#define EE_BACKEND_SDL_1_2
#endif
#endif
#endif
#endif

View File

@@ -0,0 +1,45 @@
#include <eepp/system/log.hpp>
#include <eepp/window/backend/SDL3/clipboardsdl3.hpp>
#include <eepp/window/backend/SDL3/windowsdl3.hpp>
#ifdef EE_BACKEND_SDL3
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
ClipboardSDL::ClipboardSDL( EE::Window::Window* window ) : Clipboard( window ) {}
ClipboardSDL::~ClipboardSDL() {}
void ClipboardSDL::init() {}
void ClipboardSDL::setText( const std::string& text ) {
SDL_SetClipboardText( text.c_str() );
}
std::string ClipboardSDL::getText() {
char* text = SDL_GetClipboardText();
std::string str( text ? text : "" );
SDL_free( text );
return str;
}
bool ClipboardSDL::hasPrimarySelection() const {
// SDL3 may not have primary selection support
return false;
}
std::string ClipboardSDL::getPrimarySelectionText() {
return getText();
}
void ClipboardSDL::setPrimarySelectionText( const std::string& text ) {
// No-op
}
String ClipboardSDL::getWideText() {
return String::fromUtf8( getText() );
}
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,41 @@
#ifndef EE_WINDOWCCLIPBOARDSDL3_HPP
#define EE_WINDOWCCLIPBOARDSDL3_HPP
#include <eepp/window/backend.hpp>
#include <eepp/window/backend/SDL3/base.hpp>
#ifdef EE_BACKEND_SDL3
#include <eepp/window/base.hpp>
#include <eepp/window/clipboard.hpp>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
class EE_API ClipboardSDL : public Clipboard {
public:
virtual ~ClipboardSDL();
std::string getText();
String getWideText();
void setText( const std::string& text );
bool hasPrimarySelection() const;
std::string getPrimarySelectionText();
void setPrimarySelectionText( const std::string& text );
protected:
friend class WindowSDL;
ClipboardSDL( EE::Window::Window* window );
void init();
};
}}}} // namespace EE::Window::Backend::SDL3
#endif
#endif

View File

@@ -0,0 +1,85 @@
#include <eepp/window/backend/SDL3/cursormanagersdl3.hpp>
#include <eepp/window/backend/SDL3/cursorsdl3.hpp>
#ifdef EE_BACKEND_SDL3
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
static SDL_Cursor* SDL_SYS_CURSORS[Cursor::SysCursorCount] = { 0 };
static SDL_Cursor* getLoadCursor( const Cursor::SysType& cursor ) {
if ( 0 == SDL_SYS_CURSORS[cursor] ) {
SDL_SYS_CURSORS[cursor] = SDL_CreateSystemCursor( (SDL_SystemCursor)cursor );
}
return SDL_SYS_CURSORS[cursor];
}
CursorManagerSDL::CursorManagerSDL( EE::Window::Window* window ) : CursorManager( window ) {}
Cursor* CursorManagerSDL::create( Texture* tex, const Vector2i& hotspot, const std::string& name ) {
return eeNew( CursorSDL, ( tex, hotspot, name, mWindow ) );
}
Cursor* CursorManagerSDL::create( Image* img, const Vector2i& hotspot, const std::string& name ) {
return eeNew( CursorSDL, ( img, hotspot, name, mWindow ) );
}
Cursor* CursorManagerSDL::create( const std::string& path, const Vector2i& hotspot,
const std::string& name ) {
return eeNew( CursorSDL, ( path, hotspot, name, mWindow ) );
}
void CursorManagerSDL::set( Cursor* cursor ) {
if ( NULL != cursor && cursor != mCurrent ) {
SDL_SetCursor( reinterpret_cast<CursorSDL*>( cursor )->GetCursor() );
mCurrent = cursor;
mCurSysCursor = false;
mSysCursor = Cursor::SysCursorNone;
}
}
void CursorManagerSDL::set( Cursor::SysType syscurid ) {
if ( syscurid != mSysCursor ) {
SDL_SetCursor( getLoadCursor( syscurid ) );
mCurrent = NULL;
mCurSysCursor = true;
mSysCursor = syscurid;
}
}
void CursorManagerSDL::show() {
setVisible( true );
}
void CursorManagerSDL::hide() {
setVisible( false );
}
void CursorManagerSDL::setVisible( bool visible ) {
if ( visible )
SDL_ShowCursor();
else
SDL_HideCursor();
mVisible = visible;
}
void CursorManagerSDL::remove( Cursor* cursor, bool Delete ) {
CursorManager::remove( cursor, Delete );
}
void CursorManagerSDL::reload() {
if ( mVisible ) {
show();
if ( mCurSysCursor ) {
set( mSysCursor );
} else {
set( mCurrent );
}
} else {
hide();
}
}
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,41 @@
#ifndef EE_WINDOWCCURSORMANAGERSDL3_HPP
#define EE_WINDOWCCURSORMANAGERSDL3_HPP
#include <eepp/window/backend/SDL3/base.hpp>
#include <eepp/window/cursormanager.hpp>
#ifdef EE_BACKEND_SDL3
using namespace EE::Window;
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
class EE_API CursorManagerSDL : public CursorManager {
public:
CursorManagerSDL( EE::Window::Window* window );
Cursor* create( Texture* tex, const Vector2i& hotspot, const std::string& name );
Cursor* create( Image* img, const Vector2i& hotspot, const std::string& name );
Cursor* create( const std::string& path, const Vector2i& hotspot, const std::string& name );
void set( Cursor* cursor );
void set( Cursor::SysType syscurid );
void show();
void hide();
void setVisible( bool visible );
void remove( Cursor* cursor, bool Delete = false );
void reload();
};
}}}} // namespace EE::Window::Backend::SDL3
#endif
#endif

View File

@@ -0,0 +1,65 @@
#include <eepp/window/backend/SDL3/cursorsdl3.hpp>
#ifdef EE_BACKEND_SDL3
#include <cstring>
#include <eepp/graphics/image.hpp>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
CursorSDL::CursorSDL( Texture* tex, const Vector2i& hotspot, const std::string& name,
EE::Window::Window* window ) :
Cursor( tex, hotspot, name, window ), mCursor( nullptr ) {
create();
}
CursorSDL::CursorSDL( Graphics::Image* img, const Vector2i& hotspot, const std::string& name,
EE::Window::Window* window ) :
Cursor( img, hotspot, name, window ), mCursor( nullptr ) {
create();
}
CursorSDL::CursorSDL( const std::string& path, const Vector2i& hotspot, const std::string& name,
EE::Window::Window* window ) :
Cursor( path, hotspot, name, window ), mCursor( nullptr ) {
create();
}
CursorSDL::~CursorSDL() {
if ( nullptr != mCursor )
SDL_DestroyCursor( mCursor );
}
void CursorSDL::create() {
if ( nullptr == mImage )
return;
int x = mImage->getWidth();
int y = mImage->getHeight();
int c = mImage->getChannels();
SDL_Surface* surface = SDL_CreateSurface( x, y, SDL_PIXELFORMAT_RGBA32 );
if ( !surface )
return;
Uint8* src = (Uint8*)mImage->getPixels();
Uint8* dst = (Uint8*)surface->pixels;
int srcPitch = x * c;
int dstPitch = surface->pitch;
for ( int row = 0; row < y; ++row ) {
memcpy( dst + row * dstPitch, src + row * srcPitch, srcPitch );
}
mCursor = SDL_CreateColorCursor( surface, mHotSpot.x, mHotSpot.y );
SDL_DestroySurface( surface );
}
SDL_Cursor* CursorSDL::GetCursor() const {
return mCursor;
}
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,37 @@
#ifndef EE_WINDOWCCURSORSDL3_HPP
#define EE_WINDOWCCURSORSDL3_HPP
#include <eepp/window/backend/SDL3/base.hpp>
#include <eepp/window/cursor.hpp>
#ifdef EE_BACKEND_SDL3
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
class EE_API CursorSDL : public Cursor {
public:
SDL_Cursor* GetCursor() const;
protected:
friend class CursorManagerSDL;
SDL_Cursor* mCursor;
CursorSDL( Texture* tex, const Vector2i& hotspot, const std::string& name,
EE::Window::Window* window );
CursorSDL( Graphics::Image* img, const Vector2i& hotspot, const std::string& name,
EE::Window::Window* window );
CursorSDL( const std::string& path, const Vector2i& hotspot, const std::string& name,
EE::Window::Window* window );
virtual ~CursorSDL();
void create();
};
}}}} // namespace EE::Window::Backend::SDL3
#endif
#endif

View File

@@ -0,0 +1,159 @@
#include <eepp/window/backend/SDL3/base.hpp>
#include <eepp/window/backend/SDL3/displaymanagersdl3.hpp>
#ifdef EE_BACKEND_SDL3
#include <cstdlib>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
DisplaySDL3::DisplaySDL3( int index, SDL_DisplayID displayId ) :
Display( index ), mDisplayId( displayId ) {}
std::string DisplaySDL3::getName() const {
return std::string( mDisplayId ? SDL_GetDisplayName( mDisplayId ) : "Unknown" );
}
Rect DisplaySDL3::getBounds() const {
SDL_Rect r{};
if ( mDisplayId && SDL_GetDisplayBounds( mDisplayId, &r ) == 0 )
return Rect( r.x, r.y, r.w, r.h );
return Rect();
}
Rect DisplaySDL3::getUsableBounds() const {
SDL_Rect r{};
if ( mDisplayId && SDL_GetDisplayUsableBounds( mDisplayId, &r ) == 0 )
return Rect( r.x, r.y, r.w, r.h );
return Rect();
}
Float DisplaySDL3::getDPI() {
float scale = 1.0f;
if ( mDisplayId ) {
scale = SDL_GetDisplayContentScale( mDisplayId );
if ( scale <= 0 )
scale = 1.0f;
}
return 96.0f * scale;
}
const int& DisplaySDL3::getIndex() const {
return index;
}
DisplayMode DisplaySDL3::getCurrentMode() const {
const SDL_DisplayMode* mode = mDisplayId ? SDL_GetCurrentDisplayMode( mDisplayId ) : nullptr;
if ( mode )
return DisplayMode( mode->w, mode->h, static_cast<int>( mode->refresh_rate ), index );
return DisplayMode( 0, 0, 0, index );
}
DisplayMode DisplaySDL3::getClosestDisplayMode( DisplayMode wantedMode ) const {
if ( !mDisplayId )
return DisplayMode( 0, 0, 0, index );
SDL_DisplayMode closest;
if ( SDL_GetClosestFullscreenDisplayMode( mDisplayId, wantedMode.Width, wantedMode.Height,
static_cast<float>( wantedMode.RefreshRate ), true,
&closest ) ) {
return DisplayMode( closest.w, closest.h, static_cast<int>( closest.refresh_rate ), index );
}
return DisplayMode( 0, 0, 0, index );
}
const std::vector<DisplayMode>& DisplaySDL3::getModes() const {
if ( displayModes.empty() && mDisplayId ) {
int count = 0;
SDL_DisplayMode** modesArray = SDL_GetFullscreenDisplayModes( mDisplayId, &count );
if ( modesArray ) {
for ( int i = 0; i < count; i++ ) {
SDL_DisplayMode* mode = modesArray[i];
displayModes.push_back( DisplayMode(
mode->w, mode->h, static_cast<int>( mode->refresh_rate ), index ) );
}
SDL_free( modesArray );
}
}
return displayModes;
}
Uint32 DisplaySDL3::getRefreshRate() const {
return getCurrentMode().RefreshRate;
}
Sizeu DisplaySDL3::getSize() const {
DisplayMode mode = getCurrentMode();
return { static_cast<unsigned int>( mode.Width ), static_cast<unsigned int>( mode.Height ) };
}
int DisplayManagerSDL3::getDisplayCount() {
if ( mDisplayIds.empty() ) {
// Initialize SDL video if not already done, similar to SDL2 backend
if ( !SDL_WasInit( SDL_INIT_VIDEO ) && !SDL_Init( SDL_INIT_VIDEO ) ) {
Log::error( "DisplayManagerSDL3: Failed to initialize SDL video: %s", SDL_GetError() );
return 0;
}
int count = 0;
SDL_DisplayID* ids = SDL_GetDisplays( &count );
if ( ids ) {
mDisplayIds.assign( ids, ids + count );
SDL_free( ids );
}
}
return static_cast<int>( mDisplayIds.size() );
}
Display* DisplayManagerSDL3::getDisplayIndex( int index ) {
if ( displays.empty() ) {
int count = getDisplayCount();
if ( count > 0 && !mDisplayIds.empty() ) {
for ( int i = 0; i < count; i++ ) {
displays.push_back( eeNew( DisplaySDL3, ( i, mDisplayIds[i] ) ) );
}
}
}
return ( index >= 0 && index < (int)displays.size() ) ? displays[index] : nullptr;
}
void DisplayManagerSDL3::enableScreenSaver() {
SDL_EnableScreenSaver();
SDL_SetHint( SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1" );
}
void DisplayManagerSDL3::disableScreenSaver() {
SDL_DisableScreenSaver();
SDL_SetHint( SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "0" );
}
void DisplayManagerSDL3::enableMouseFocusClickThrough() {
SDL_SetHint( SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1" );
}
void DisplayManagerSDL3::disableMouseFocusClickThrough() {
SDL_SetHint( SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "0" );
}
void DisplayManagerSDL3::disableBypassCompositor() {
#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
SDL_SetHint( SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0" );
#endif
}
void DisplayManagerSDL3::enableBypassCompositor() {
#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
SDL_SetHint( SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "1" );
#endif
}
int DisplayManagerSDL3::getDisplayIndexFromID( SDL_DisplayID id ) const {
for ( size_t i = 0; i < mDisplayIds.size(); ++i ) {
if ( mDisplayIds[i] == id )
return static_cast<int>( i );
}
return -1;
}
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,64 @@
#ifndef EE_WINDOW_DISPLAYMANAGERSDL3_HPP
#define EE_WINDOW_DISPLAYMANAGERSDL3_HPP
#include <eepp/window/backend/SDL3/base.hpp>
#include <eepp/window/displaymanager.hpp>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
class EE_API DisplaySDL3 : public Display {
public:
DisplaySDL3( int index, SDL_DisplayID displayId );
std::string getName() const;
Rect getBounds() const;
Rect getUsableBounds() const;
Float getDPI();
const int& getIndex() const;
DisplayMode getCurrentMode() const;
DisplayMode getClosestDisplayMode( DisplayMode wantedMode ) const;
const std::vector<DisplayMode>& getModes() const;
Uint32 getRefreshRate() const;
Sizeu getSize() const;
protected:
mutable std::vector<DisplayMode> displayModes;
SDL_DisplayID mDisplayId;
};
class EE_API DisplayManagerSDL3 : public DisplayManager {
public:
int getDisplayCount();
Display* getDisplayIndex( int index );
void enableScreenSaver();
void disableScreenSaver();
void enableMouseFocusClickThrough();
void disableMouseFocusClickThrough();
void disableBypassCompositor();
void enableBypassCompositor();
int getDisplayIndexFromID( SDL_DisplayID id ) const;
protected:
std::vector<SDL_DisplayID> mDisplayIds;
};
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,489 @@
#include <eepp/window/backend/SDL3/inputsdl3.hpp>
#include <eepp/window/backend/SDL3/joystickmanagersdl3.hpp>
#include <eepp/window/backend/SDL3/windowsdl3.hpp>
#include <eepp/window/engine.hpp>
#include <limits>
#ifdef EE_BACKEND_SDL3
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
InputSDL::InputSDL( Window* window ) :
Input( window, eeNew( JoystickManagerSDL, () ) ), mDPIScale( 1.f ) {
#if defined( EE_X11_PLATFORM )
mMouseSpeed = 1.75f;
#endif
}
InputSDL::~InputSDL() {}
void InputSDL::update() {
SDL_Event SDLEvent;
cleanStates();
++mEventsSentId;
if ( mEventsSentId == std::numeric_limits<Uint64>::max() )
mEventsSentId = 0;
if ( !mQueuedEvents.empty() ) {
for ( const auto& prevEvent : mQueuedEvents )
sendEvent( prevEvent );
mQueuedEvents.clear();
}
while ( SDL_PollEvent( &SDLEvent ) )
sendEvent( SDLEvent );
InputEvent endProcessingEvent;
endProcessingEvent.Type = InputEvent::EventsSent;
processEvent( &endProcessingEvent );
}
void InputSDL::waitEvent( const Time& timeout ) {
SDL_Event SDLEvent;
if ( timeout == Time::Zero ) {
if ( SDL_WaitEvent( &SDLEvent ) )
mQueuedEvents.emplace_back( SDLEvent );
} else if ( SDL_WaitEventTimeout( &SDLEvent, (int)timeout.asMilliseconds() ) ) {
if ( SDLEvent.type != SDL_EVENT_FIRST )
mQueuedEvents.emplace_back( SDLEvent );
}
}
bool InputSDL::grabInput() {
SDL_Window* win = static_cast<WindowSDL*>( mWindow )->getSDLWindow();
return SDL_GetWindowMouseGrab( win );
}
void InputSDL::grabInput( const bool& Grab ) {
SDL_Window* win = static_cast<WindowSDL*>( mWindow )->getSDLWindow();
SDL_SetWindowMouseGrab( win, Grab );
}
void InputSDL::injectMousePos( const Uint16& x, const Uint16& y ) {
SDL_Window* win = static_cast<WindowSDL*>( mWindow )->getSDLWindow();
SDL_WarpMouseInWindow( win, static_cast<float>( x ), static_cast<float>( y ) );
}
Vector2i InputSDL::queryMousePos() {
Vector2i mousePos;
float tempMouseX, tempMouseY;
int tempWinPosX, tempWinPosY;
SDL_Window* win = static_cast<WindowSDL*>( mWindow )->getSDLWindow();
SDL_GetGlobalMouseState( &tempMouseX, &tempMouseY );
SDL_GetWindowPosition( win, &tempWinPosX, &tempWinPosY );
mousePos.x = static_cast<Int32>( tempMouseX ) - tempWinPosX;
mousePos.y = static_cast<Int32>( tempMouseY ) - tempWinPosY;
return mousePos;
}
void InputSDL::captureMouse( const bool& capture ) {
SDL_CaptureMouse( capture );
}
bool InputSDL::isMouseCaptured() const {
SDL_Window* win = static_cast<WindowSDL*>( mWindow )->getSDLWindow();
return SDL_GetWindowFlags( win ) & SDL_WINDOW_MOUSE_CAPTURE;
}
std::string InputSDL::getKeyName( const Keycode& keycode ) const {
return std::string( SDL_GetKeyName( static_cast<SDL_Keycode>( keycode ) ) );
}
Keycode InputSDL::getKeyFromName( const std::string& keycode ) const {
return static_cast<Keycode>( SDL_GetKeyFromName( keycode.c_str() ) );
}
std::string InputSDL::getScancodeName( const Scancode& scancode ) const {
return SDL_GetScancodeName( static_cast<SDL_Scancode>( scancode ) );
}
Scancode InputSDL::getScancodeFromName( const std::string& scancode ) const {
return static_cast<Scancode>( SDL_GetScancodeFromName( scancode.c_str() ) );
}
Keycode InputSDL::getKeyFromScancode( const Scancode& scancode ) const {
return static_cast<Keycode>(
SDL_GetKeyFromScancode( static_cast<SDL_Scancode>( scancode ), SDL_KMOD_NONE, 1 ) );
}
Scancode InputSDL::getScancodeFromKey( const Keycode& keycode ) const {
return static_cast<Scancode>(
SDL_GetScancodeFromKey( static_cast<SDL_Keycode>( keycode ), nullptr ) );
}
void InputSDL::init() {
mDPIScale = mWindow->getScale();
mMousePos = queryMousePos();
}
void InputSDL::sendEvent( const SDL_Event& SDLEvent ) {
InputEvent event;
event.Type = InputEvent::NoEvent;
event.WinID = 0;
switch ( SDLEvent.type ) {
case SDL_EVENT_WINDOW_SHOWN: {
event.Type = InputEvent::Window;
event.window.gain = 1;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowShown;
break;
}
case SDL_EVENT_WINDOW_HIDDEN: {
event.Type = InputEvent::Window;
event.window.gain = 0;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowHidden;
break;
}
case SDL_EVENT_WINDOW_EXPOSED: {
event.Type = InputEvent::VideoExpose;
event.WinID = SDLEvent.window.windowID;
event.expose.type = event.Type;
break;
}
case SDL_EVENT_WINDOW_MOVED: {
event.Type = InputEvent::Window;
event.window.gain = 1;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowMoved;
break;
}
case SDL_EVENT_WINDOW_RESIZED: {
event.Type = InputEvent::VideoResize;
event.WinID = SDLEvent.window.windowID;
mDPIScale = mWindow->getScale();
event.resize.w = static_cast<int>( SDLEvent.window.data1 * mDPIScale );
event.resize.h = static_cast<int>( SDLEvent.window.data2 * mDPIScale );
break;
}
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: {
event.Type = InputEvent::Window;
event.window.gain = 1;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowSizeChanged;
break;
}
case SDL_EVENT_WINDOW_MINIMIZED: {
event.Type = InputEvent::Window;
event.window.gain = 0;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowMinimized;
break;
}
case SDL_EVENT_WINDOW_MAXIMIZED: {
event.Type = InputEvent::Window;
event.window.gain = 1;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowMaximized;
break;
}
case SDL_EVENT_WINDOW_RESTORED: {
event.Type = InputEvent::Window;
event.window.gain = 1;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowRestored;
break;
}
case SDL_EVENT_WINDOW_MOUSE_ENTER: {
event.Type = InputEvent::Window;
event.window.gain = 1;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowMouseEnter;
break;
}
case SDL_EVENT_WINDOW_MOUSE_LEAVE: {
event.Type = InputEvent::Window;
event.window.gain = 0;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowMouseLeave;
break;
}
case SDL_EVENT_WINDOW_FOCUS_GAINED: {
event.Type = InputEvent::Window;
event.window.gain = 1;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowKeyboardFocusGain;
break;
}
case SDL_EVENT_WINDOW_FOCUS_LOST: {
event.Type = InputEvent::Window;
event.window.gain = 0;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowKeyboardFocusLost;
break;
}
case SDL_EVENT_WINDOW_CLOSE_REQUESTED: {
event.Type = InputEvent::Window;
event.window.gain = 0;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowClose;
break;
}
case SDL_EVENT_WINDOW_HIT_TEST: {
event.Type = InputEvent::Window;
event.window.gain = 1;
event.WinID = SDLEvent.window.windowID;
event.window.type = InputEvent::WindowHitTest;
break;
}
case SDL_EVENT_TEXT_INPUT: {
String txt = String::fromUtf8( std::string_view{ SDLEvent.text.text } );
event.Type = InputEvent::TextInput;
// Convert from nanoseconds (SDL3) to milliseconds (EE framework)
event.text.timestamp = static_cast<Uint32>( SDLEvent.text.timestamp / 1000000ULL );
event.WinID = SDLEvent.text.windowID;
for ( size_t i = 0; i < txt.size() - 1; i++ ) {
event.text.text = txt[i];
processEvent( &event );
}
event.text.text = txt[txt.size() - 1];
break;
}
case SDL_EVENT_TEXT_EDITING: {
event.Type = InputEvent::TextEditing;
event.textediting.text = SDLEvent.edit.text;
event.textediting.start = SDLEvent.edit.start;
event.textediting.length = SDLEvent.edit.length;
event.WinID = SDLEvent.edit.windowID;
break;
}
case SDL_EVENT_KEY_DOWN: {
event.Type = InputEvent::KeyDown;
event.key.state = SDLEvent.key.down ? 1 : 0;
event.key.which = SDLEvent.key.windowID;
event.key.keysym.sym = static_cast<Keycode>( SDLEvent.key.key );
event.key.keysym.scancode = static_cast<Scancode>( SDLEvent.key.scancode );
event.key.keysym.mod = SDLEvent.key.mod;
event.key.keysym.unicode = 0;
event.WinID = SDLEvent.key.windowID;
break;
}
case SDL_EVENT_KEY_UP: {
event.Type = InputEvent::KeyUp;
event.key.state = SDLEvent.key.down ? 1 : 0;
event.key.which = SDLEvent.key.windowID;
event.key.keysym.sym = static_cast<Keycode>( SDLEvent.key.key );
event.key.keysym.scancode = static_cast<Scancode>( SDLEvent.key.scancode );
event.key.keysym.mod = SDLEvent.key.mod;
event.key.keysym.unicode = 0;
event.WinID = SDLEvent.key.windowID;
break;
}
case SDL_EVENT_MOUSE_MOTION: {
event.Type = InputEvent::MouseMotion;
event.motion.which = SDLEvent.motion.windowID;
event.motion.state = SDLEvent.motion.state;
event.motion.x = static_cast<Int16>( SDLEvent.motion.x * mDPIScale );
event.motion.y = static_cast<Int16>( SDLEvent.motion.y * mDPIScale );
event.motion.xrel = static_cast<Int16>( SDLEvent.motion.xrel * mDPIScale );
event.motion.yrel = static_cast<Int16>( SDLEvent.motion.yrel * mDPIScale );
event.WinID = SDLEvent.motion.windowID;
break;
}
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
event.Type = InputEvent::MouseButtonDown;
event.button.button = SDLEvent.button.button;
event.button.which = SDLEvent.button.windowID;
event.button.state = SDLEvent.button.down ? 1 : 0;
event.button.x = static_cast<Int16>( SDLEvent.button.x * mDPIScale );
event.button.y = static_cast<Int16>( SDLEvent.button.y * mDPIScale );
event.WinID = SDLEvent.button.windowID;
break;
}
case SDL_EVENT_MOUSE_BUTTON_UP: {
event.Type = InputEvent::MouseButtonUp;
event.button.button = SDLEvent.button.button;
event.button.which = SDLEvent.button.windowID;
event.button.state = SDLEvent.button.down ? 1 : 0;
event.button.x = static_cast<Int16>( SDLEvent.button.x * mDPIScale );
event.button.y = static_cast<Int16>( SDLEvent.button.y * mDPIScale );
event.WinID = SDLEvent.button.windowID;
break;
}
case SDL_EVENT_MOUSE_WHEEL: {
Uint8 button;
float x = SDLEvent.wheel.x;
float y = SDLEvent.wheel.y;
if ( y == 0 && x == 0 )
break;
if ( y > 0 ) {
button = EE_BUTTON_WHEELUP;
} else if ( y < 0 ) {
button = EE_BUTTON_WHEELDOWN;
} else if ( x > 0 ) {
button = EE_BUTTON_WHEELRIGHT;
} else if ( x < 0 ) {
button = EE_BUTTON_WHEELLEFT;
} else {
return;
}
// Get mouse position from the event (mouse_x, mouse_y are in window coordinates)
event.button.button = button;
event.button.x = static_cast<Int16>( SDLEvent.wheel.mouse_x * mDPIScale );
event.button.y = static_cast<Int16>( SDLEvent.wheel.mouse_y * mDPIScale );
event.button.which = SDLEvent.wheel.windowID;
event.WinID = SDLEvent.wheel.windowID;
event.Type = InputEvent::MouseButtonDown;
event.button.state = 1;
processEvent( &event );
event.Type = InputEvent::MouseButtonUp;
event.button.state = 0;
processEvent( &event );
event.Type = InputEvent::MouseWheel;
event.wheel.which = SDLEvent.wheel.windowID;
event.wheel.direction = SDLEvent.wheel.direction == SDL_MOUSEWHEEL_NORMAL
? InputEvent::WheelEvent::Normal
: InputEvent::WheelEvent::Flipped;
event.wheel.x = SDLEvent.wheel.x;
event.wheel.y = SDLEvent.wheel.y;
processEvent( &event );
break;
}
case SDL_EVENT_JOYSTICK_AXIS_MOTION: {
event.Type = InputEvent::JoyAxisMotion;
event.jaxis.which = SDLEvent.jaxis.which;
event.jaxis.axis = SDLEvent.jaxis.axis;
event.jaxis.value = SDLEvent.jaxis.value;
break;
}
case SDL_EVENT_JOYSTICK_BALL_MOTION: {
event.Type = InputEvent::JoyBallMotion;
event.jball.which = SDLEvent.jball.which;
event.jball.ball = SDLEvent.jball.ball;
event.jball.xrel = SDLEvent.jball.xrel;
event.jball.yrel = SDLEvent.jball.yrel;
break;
}
case SDL_EVENT_JOYSTICK_HAT_MOTION: {
event.Type = InputEvent::JoyHatMotion;
event.jhat.which = SDLEvent.jhat.which;
event.jhat.value = SDLEvent.jhat.value;
event.jhat.hat = SDLEvent.jhat.hat;
break;
}
case SDL_EVENT_JOYSTICK_BUTTON_DOWN: {
event.Type = InputEvent::JoyButtonDown;
event.jbutton.which = SDLEvent.jbutton.which;
event.jbutton.state = SDLEvent.jbutton.down ? 1 : 0;
event.jbutton.button = SDLEvent.jbutton.button;
break;
}
case SDL_EVENT_JOYSTICK_BUTTON_UP: {
event.Type = InputEvent::JoyButtonUp;
event.jbutton.which = SDLEvent.jbutton.which;
event.jbutton.state = SDLEvent.jbutton.down ? 1 : 0;
event.jbutton.button = SDLEvent.jbutton.button;
break;
}
case SDL_EVENT_JOYSTICK_ADDED: {
// Could be used to dynamically add joysticks, but JoystickManager handles it
break;
}
case SDL_EVENT_JOYSTICK_REMOVED: {
// Could be used to dynamically remove joysticks
break;
}
case SDL_EVENT_QUIT: {
event.Type = InputEvent::Quit;
event.quit.type = event.Type;
break;
}
case SDL_EVENT_DROP_FILE: {
event.Type = InputEvent::FileDropped;
event.file.file = SDLEvent.drop.data;
event.WinID = SDLEvent.drop.windowID;
break;
}
case SDL_EVENT_DROP_TEXT: {
event.Type = InputEvent::TextDropped;
event.textdrop.text = SDLEvent.drop.data;
event.WinID = SDLEvent.drop.windowID;
break;
}
default: {
if ( SDLEvent.type >= SDL_EVENT_USER && SDLEvent.type < SDL_EVENT_LAST ) {
event.Type = InputEvent::EventUser + SDLEvent.type - SDL_EVENT_USER;
event.user.type = event.Type;
event.user.code = SDLEvent.user.code;
event.user.data1 = SDLEvent.user.data1;
event.user.data2 = SDLEvent.user.data2;
event.WinID = SDLEvent.user.windowID;
} else {
event.Type = InputEvent::NoEvent;
}
}
}
// Convert SDL3 joystick ID (which is a handle) to index for framework compatibility
if ( event.Type == InputEvent::JoyAxisMotion || event.Type == InputEvent::JoyBallMotion ||
event.Type == InputEvent::JoyHatMotion || event.Type == InputEvent::JoyButtonDown ||
event.Type == InputEvent::JoyButtonUp ) {
SDL_JoystickID joyId = 0;
switch ( event.Type ) {
case InputEvent::JoyAxisMotion:
joyId = SDLEvent.jaxis.which;
break;
case InputEvent::JoyBallMotion:
joyId = SDLEvent.jball.which;
break;
case InputEvent::JoyHatMotion:
joyId = SDLEvent.jhat.which;
break;
case InputEvent::JoyButtonDown:
case InputEvent::JoyButtonUp:
joyId = SDLEvent.jbutton.which;
break;
default:
joyId = 0;
break;
}
auto* mgr = static_cast<JoystickManagerSDL*>( mJoystickManager );
Uint32 idx = mgr->getIndexFromID( joyId );
if ( UINT32_MAX == idx ) {
// Unknown joystick ID, drop event
return;
}
// Overwrite the 'which' field with the correct index
switch ( event.Type ) {
case InputEvent::JoyAxisMotion:
event.jaxis.which = idx;
break;
case InputEvent::JoyBallMotion:
event.jball.which = idx;
break;
case InputEvent::JoyHatMotion:
event.jhat.which = idx;
break;
case InputEvent::JoyButtonDown:
case InputEvent::JoyButtonUp:
event.jbutton.which = idx;
break;
default:
break;
}
}
EE::Window::Window* win = nullptr;
if ( InputEvent::NoEvent != event.Type ) {
if ( event.WinID == mWindow->getWindowID() || event.WinID == 0 ) {
processEvent( &event );
} else if ( ( win = EE::Window::Engine::instance()->getWindowID( event.WinID ) ) ) {
win->getInput()->processEvent( &event );
} else {
processEvent( &event );
}
}
// In SDL3, drop event data is managed by SDL, do not free
}
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,61 @@
#ifndef EE_WINDOWCINPUTSDL3_HPP
#define EE_WINDOWCINPUTSDL3_HPP
#include <eepp/window/backend.hpp>
#include <eepp/window/backend/SDL3/base.hpp>
#ifdef EE_BACKEND_SDL3
#include <eepp/window/input.hpp>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
class EE_API InputSDL : public Input {
public:
~InputSDL();
void update();
void waitEvent( const Time& timeout = Time::Zero );
bool grabInput();
void grabInput( const bool& Grab );
void injectMousePos( const Uint16& x, const Uint16& y );
Vector2i queryMousePos();
void captureMouse( const bool& capture );
bool isMouseCaptured() const;
std::string getKeyName( const Keycode& keycode ) const;
Keycode getKeyFromName( const std::string& keycode ) const;
std::string getScancodeName( const Scancode& scancode ) const;
Scancode getScancodeFromName( const std::string& scancode ) const;
Keycode getKeyFromScancode( const Scancode& scancode ) const;
Scancode getScancodeFromKey( const Keycode& scancode ) const;
protected:
friend class WindowSDL;
Float mDPIScale;
std::vector<SDL_Event> mQueuedEvents;
InputSDL( EE::Window::Window* window );
void init();
void sendEvent( const SDL_Event& SDLEvent );
};
}}}} // namespace EE::Window::Backend::SDL3
#endif
#endif

View File

@@ -0,0 +1,92 @@
#include <eepp/window/backend/SDL3/joystickmanagersdl3.hpp>
#include <eepp/window/backend/SDL3/joysticksdl3.hpp>
#ifdef EE_BACKEND_SDL3
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
namespace {
void closeSubsystem() {
#if EE_PLATFORM != EE_PLATFORM_MACOS && EE_PLATFORM != EE_PLATFORM_IOS
if ( SDL_WasInit( SDL_INIT_JOYSTICK ) )
SDL_QuitSubSystem( SDL_INIT_JOYSTICK );
#endif
}
} // namespace
JoystickManagerSDL::JoystickManagerSDL() :
JoystickManager(), mAsyncInit( &JoystickManagerSDL::openAsync, this ) {}
JoystickManagerSDL::~JoystickManagerSDL() {
for ( Uint32 i = 0; i < mCount; i++ )
eeSAFE_DELETE( mJoysticks[i] );
closeSubsystem();
mInit = false;
}
void JoystickManagerSDL::update() {
if ( mInit ) {
SDL_UpdateJoysticks();
for ( Uint32 i = 0; i < mCount; i++ )
if ( nullptr != mJoysticks[i] )
mJoysticks[i]->update();
}
}
void JoystickManagerSDL::openAsync() {
Sys::sleep( Milliseconds( 500 ) );
int error = SDL_InitSubSystem( SDL_INIT_JOYSTICK );
if ( !error ) {
int count = 0;
SDL_JoystickID* ids = SDL_GetJoysticks( &count );
if ( ids ) {
mIds.assign( ids, ids + count );
mCount = static_cast<Uint32>( count );
SDL_free( ids );
} else {
mCount = 0;
}
// Build ID -> index mapping
mIdToIndex.clear();
for ( Uint32 i = 0; i < mCount; i++ ) {
mIdToIndex[mIds[i]] = i;
create( i );
}
mInit = true;
if ( mOpenCb )
mOpenCb();
}
}
void JoystickManagerSDL::open( OpenCb openCb ) {
mOpenCb = openCb;
mAsyncInit.launch();
}
void JoystickManagerSDL::close() {
closeSubsystem();
mInit = false;
}
void JoystickManagerSDL::create( const Uint32& index ) {
if ( nullptr != mJoysticks[index] )
mJoysticks[index]->reOpen();
else
mJoysticks[index] = eeNew( JoystickSDL, ( index, mIds[index] ) );
}
Uint32 JoystickManagerSDL::getIndexFromID( SDL_JoystickID id ) const {
auto it = mIdToIndex.find( id );
if ( it != mIdToIndex.end() )
return it->second;
return UINT32_MAX;
}
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,46 @@
#ifndef EE_WINDOWCJOYSTICKMANAGERSDL3_HPP
#define EE_WINDOWCJOYSTICKMANAGERSDL3_HPP
#include <eepp/window/backend.hpp>
#include <eepp/window/backend/SDL3/base.hpp>
#ifdef EE_BACKEND_SDL3
#include <eepp/system/thread.hpp>
#include <eepp/window/joystickmanager.hpp>
#include <unordered_map>
#include <vector>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
class EE_API JoystickManagerSDL : public JoystickManager {
public:
JoystickManagerSDL();
virtual ~JoystickManagerSDL();
void update();
void close();
void open( OpenCb openCb = nullptr );
protected:
void create( const Uint32& index ) override;
void openAsync();
Thread mAsyncInit;
bool mInit{ false };
Uint32 mCount{ 0 };
std::vector<SDL_JoystickID> mIds;
std::unordered_map<SDL_JoystickID, Uint32> mIdToIndex;
public:
Uint32 getIndexFromID( SDL_JoystickID id ) const;
};
}}}} // namespace EE::Window::Backend::SDL3
#endif
#endif

View File

@@ -0,0 +1,70 @@
#include <eepp/window/backend/SDL3/joysticksdl3.hpp>
#ifdef EE_BACKEND_SDL3
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
JoystickSDL::JoystickSDL( const Uint32& index, SDL_JoystickID id ) : Joystick( index ), mJoystick( nullptr ), mId( id ) {
open();
}
JoystickSDL::~JoystickSDL() {
close();
}
void JoystickSDL::open() {
mJoystick = SDL_OpenJoystick( mId );
if ( nullptr != mJoystick ) {
mName = SDL_GetJoystickName( mJoystick );
mHats = SDL_GetNumJoystickHats( mJoystick );
mButtons = eemin( SDL_GetNumJoystickButtons( mJoystick ), 32 );
mAxes = SDL_GetNumJoystickAxes( mJoystick );
mBalls = SDL_GetNumJoystickBalls( mJoystick );
mButtonDown = mButtonDownLast = mButtonUp = 0;
}
}
void JoystickSDL::close() {
if ( nullptr != mJoystick )
SDL_CloseJoystick( mJoystick );
mJoystick = nullptr;
mName = "";
mHats = mButtons = mAxes = mBalls = 0;
}
void JoystickSDL::update() {
if ( nullptr != mJoystick ) {
clearStates();
for ( Int32 i = 0; i < mButtons; i++ ) {
updateButton( i, 0 != SDL_GetJoystickButton( mJoystick, i ) );
}
}
}
Uint8 JoystickSDL::getHat( const Int32& index ) {
if ( index >= 0 && index < mHats )
return SDL_GetJoystickHat( mJoystick, index );
return HAT_CENTERED;
}
Float JoystickSDL::getAxis( const Int32& axis ) {
if ( axis >= 0 && axis < mAxes ) {
return static_cast<Float>( SDL_GetJoystickAxis( mJoystick, axis ) ) / 32768.0f;
}
return 0;
}
Vector2i JoystickSDL::getBallMotion( const Int32& ball ) {
Vector2i v;
if ( ball >= 0 && ball < mBalls )
SDL_GetJoystickBall( mJoystick, ball, &v.x, &v.y );
return v;
}
bool JoystickSDL::isPlugged() const {
return nullptr != mJoystick;
}
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,41 @@
#ifndef EE_WINDOWCJOYSTICKSDL3_HPP
#define EE_WINDOWCJOYSTICKSDL3_HPP
#include <eepp/window/backend.hpp>
#include <eepp/window/backend/SDL3/base.hpp>
#ifdef EE_BACKEND_SDL3
#include <eepp/window/joystick.hpp>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
class EE_API JoystickSDL : public Joystick {
public:
JoystickSDL( const Uint32& index, SDL_JoystickID id );
virtual ~JoystickSDL();
void close();
void open();
void update();
Uint8 getHat( const Int32& index );
Float getAxis( const Int32& axis );
Vector2i getBallMotion( const Int32& ball );
bool isPlugged() const;
protected:
SDL_Joystick* mJoystick;
SDL_JoystickID mId;
};
}}}} // namespace EE::Window::Backend::SDL3
#endif
#endif

View File

@@ -0,0 +1,78 @@
#include <eepp/window/backend/SDL3/base.hpp>
#include <eepp/window/backend/SDL3/platformhelpersdl3.hpp>
#include <eepp/system/log.hpp>
using namespace EE::System;
#ifdef EE_BACKEND_SDL3
#if EE_PLATFORM == EE_PLATFORM_EMSCRIPTEN
#include <emscripten.h>
EM_JS( void, emscripten_open_url, ( const char* msg ),
{ window.open( UTF8ToString( msg ), 'blank' ); } );
#endif
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
PlatformHelperSDL3::PlatformHelperSDL3() {}
bool PlatformHelperSDL3::openURL( const std::string& url ) {
#if EE_PLATFORM == EE_PLATFORM_EMSCRIPTEN
emscripten_open_url( url.c_str() );
return true;
#else
bool res = SDL_OpenURL( url.c_str() );
if ( !res ) {
Log::error( "PlatformHelperSDL3::openURL: Failed with error - %s", SDL_GetError() );
}
return res;
#endif
}
char* PlatformHelperSDL3::iconv( const char* tocode, const char* fromcode, const char* inbuf,
size_t inbytesleft ) {
return SDL_iconv_string( tocode, fromcode, inbuf, inbytesleft );
}
void PlatformHelperSDL3::iconvFree( char* buf ) {
SDL_free( buf );
}
#if EE_PLATFORM == EE_PLATFORM_ANDROID
void* PlatformHelperSDL3::getActivity() {
return SDL_AndroidGetActivity();
}
void* PlatformHelperSDL3::getJNIEnv() {
return SDL_AndroidGetJNIEnv();
}
std::string PlatformHelperSDL3::getExternalStoragePath() {
return std::string( SDL_AndroidGetExternalStoragePath() );
}
std::string PlatformHelperSDL3::getInternalStoragePath() {
return std::string( SDL_AndroidGetInternalStoragePath() );
}
std::string PlatformHelperSDL3::getApkPath() {
static std::string apkPath = "";
if ( "" == apkPath ) {
// Simplified: use SDL_AndroidGetApkPath if available
apkPath = std::string( SDL_AndroidGetApkPath() ? SDL_AndroidGetApkPath() : "" );
}
return apkPath;
}
bool PlatformHelperSDL3::isExternalStorageReadable() {
return 0 != ( SDL_AndroidGetExternalStorageState() & SDL_ANDROID_EXTERNAL_STORAGE_READ );
}
bool PlatformHelperSDL3::isExternalStorageWritable() {
return 0 != ( SDL_AndroidGetExternalStorageState() & SDL_ANDROID_EXTERNAL_STORAGE_WRITE );
}
#endif
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,38 @@
#ifndef EE_PLATFORMHELPERSDL3_HPP
#define EE_PLATFORMHELPERSDL3_HPP
#include <eepp/window/platformhelper.hpp>
#include <string>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
class EE_API PlatformHelperSDL3 : public PlatformHelper {
public:
PlatformHelperSDL3();
bool openURL( const std::string& url );
char* iconv( const char* tocode, const char* fromcode, const char* inbuf, size_t inbytesleft );
void iconvFree( char* buf );
#if EE_PLATFORM == EE_PLATFORM_ANDROID
void* getActivity();
void* getJNIEnv();
std::string getExternalStoragePath();
std::string getInternalStoragePath();
std::string getApkPath();
bool isExternalStorageReadable();
bool isExternalStorageWritable();
#endif
};
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,941 @@
#include <eepp/window/backend/SDL3/base.hpp>
#ifdef EE_BACKEND_SDL3
#include <eepp/graphics/framebuffermanager.hpp>
#include <eepp/graphics/globalbatchrenderer.hpp>
#include <eepp/graphics/renderer/renderer.hpp>
#include <eepp/graphics/shaderprogrammanager.hpp>
#include <eepp/graphics/texturefactory.hpp>
#include <eepp/graphics/vertexbuffermanager.hpp>
#include <eepp/system/filesystem.hpp>
#include <eepp/system/log.hpp>
#include <eepp/window/backend/SDL3/clipboardsdl3.hpp>
#include <eepp/window/backend/SDL3/cursormanagersdl3.hpp>
#include <eepp/window/backend/SDL3/displaymanagersdl3.hpp>
#include <eepp/window/backend/SDL3/inputsdl3.hpp>
#include <eepp/window/backend/SDL3/windowsdl3.hpp>
#include <eepp/window/backend/SDL3/wminfo.hpp>
#include <eepp/window/engine.hpp>
#if EE_PLATFORM == EE_PLATFORM_WIN
#include <eepp/window/backend/SDL3/displaymanagersdl3.hpp>
#endif
#if EE_PLATFORM == EE_PLATFORM_WIN || EE_PLATFORM == EE_PLATFORM_MACOS || \
defined( EE_X11_PLATFORM ) || EE_PLATFORM == EE_PLATFORM_IOS || \
EE_PLATFORM == EE_PLATFORM_ANDROID || EE_PLATFORM == EE_PLATFORM_EMSCRIPTEN
#define SDL3_THREADED_GLCONTEXT
#endif
#if EE_PLATFORM == EE_PLATFORM_WIN
#include <cstdint>
#include <initguid.h>
#include <objbase.h>
#include <shellapi.h>
#include <tlhelp32.h>
bool WindowsIsProcessRunning( const char* processName, bool killProcess = false ) {
bool exists = false;
PROCESSENTRY32 entry = {};
entry.dwSize = sizeof( PROCESSENTRY32 );
HANDLE snapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if ( Process32First( snapshot, &entry ) ) {
while ( Process32Next( snapshot, &entry ) ) {
#ifdef UNICODE
if ( EE::String( entry.szExeFile ).toUtf8() == std::string( processName ) ) {
#else
if ( !stricmp( entry.szExeFile, processName ) ) {
#endif
exists = true;
if ( killProcess ) {
HANDLE aProc = OpenProcess( PROCESS_TERMINATE, 0, entry.th32ProcessID );
if ( aProc ) {
TerminateProcess( aProc, 9 );
CloseHandle( aProc );
}
}
break;
}
}
}
CloseHandle( snapshot );
return exists;
}
#ifndef ERROR_ELEVATION_REQUIRED
#define ERROR_ELEVATION_REQUIRED ( 740 )
#endif
bool WindowsProcessLaunch( std::string command, HWND windowHwnd ) {
#ifdef UNICODE
wchar_t expandedCmd[1024] = {};
#else
char expandedCmd[1024] = {};
#endif
static PROCESS_INFORMATION pi = {};
static STARTUPINFO si = {};
si.cb = sizeof( si );
#if UNICODE
ExpandEnvironmentStrings( EE::String::fromUtf8( command ).toWideString().c_str(), expandedCmd,
1024 );
#else
ExpandEnvironmentStrings( command.c_str(), expandedCmd, 1024 );
#endif
if ( CreateProcess( NULL, expandedCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) ) {
WaitForSingleObject( pi.hProcess, 10000 );
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
return true;
} else {
DWORD error = GetLastError();
if ( error == ERROR_ELEVATION_REQUIRED && 0 != windowHwnd ) {
#ifdef UNICODE
std::intptr_t res = reinterpret_cast<std::intptr_t>(
ShellExecute( windowHwnd, L"open", expandedCmd, L"", NULL, SW_SHOWDEFAULT ) );
#else
std::intptr_t res = reinterpret_cast<std::intptr_t>(
ShellExecute( windowHwnd, "open", expandedCmd, "", NULL, SW_SHOWDEFAULT ) );
#endif
if ( res <= 32 ) {
return false;
}
}
}
return false;
}
DEFINE_GUID( CLSID_UIHostNoLaunch, 0x4CE576FA, 0x83DC, 0x4f88, 0x95, 0x1C, 0x9D, 0x07, 0x82, 0xB4,
0xE3, 0x76 );
DEFINE_GUID( IID_ITipInvocation, 0x37c994e7, 0x432b, 0x4834, 0xa2, 0xf7, 0xdc, 0xe1, 0xf1, 0x3b,
0x83, 0x4b );
struct ITipInvocation : IUnknown {
virtual HRESULT STDMETHODCALLTYPE Toggle( HWND wnd ) = 0;
};
static bool WIN_OSK_VISIBLE = false;
int showOSK( HWND windowHwnd ) {
if ( !WindowsIsProcessRunning( "TabTip.exe" ) ) {
WindowsIsProcessRunning(
"WindowsInternal.ComposableShell.Experiences.TextInput.InputApp.EXE", true );
std::string programFiles( Sys::getOSArchitecture() == "x64" ? "%ProgramW6432%"
: "%ProgramFiles(x86)%" );
WindowsProcessLaunch( programFiles + "\\Common Files\\microsoft shared\\ink\\TabTip.exe",
windowHwnd );
}
CoInitialize( 0 );
ITipInvocation* tip;
CoCreateInstance( CLSID_UIHostNoLaunch, 0, CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER,
IID_ITipInvocation, (void**)&tip );
if ( tip != NULL ) {
tip->Toggle( GetDesktopWindow() );
tip->Release();
WIN_OSK_VISIBLE = true;
}
return 0;
}
int hideOSK() {
WIN_OSK_VISIBLE = false;
return PostMessage( GetDesktopWindow(), WM_SYSCOMMAND, (int)SC_CLOSE, 0 );
}
bool isDarkModeEnabled() {
HKEY hKey;
DWORD value = 1;
DWORD valueSize = sizeof( value );
if ( RegOpenKeyExA( HKEY_CURRENT_USER,
"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0,
KEY_READ, &hKey ) == ERROR_SUCCESS ) {
RegQueryValueExA( hKey, "AppsUseLightTheme", nullptr, nullptr,
reinterpret_cast<LPBYTE>( &value ), &valueSize );
RegCloseKey( hKey );
}
return value == 0;
}
typedef HRESULT( WINAPI* DwmSetWindowAttributeFunc )( HWND, DWORD, LPCVOID, DWORD );
constexpr DWORD DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
void setUserTheme( HWND hwnd ) {
HMODULE hDwmapi = LoadLibraryA( "dwmapi.dll" );
if ( !hDwmapi ) {
return;
}
auto DwmSetWindowAttribute = reinterpret_cast<DwmSetWindowAttributeFunc>(
GetProcAddress( hDwmapi, "DwmSetWindowAttribute" ) );
if ( !DwmSetWindowAttribute ) {
FreeLibrary( hDwmapi );
return;
}
BOOL darkMode = isDarkModeEnabled() ? TRUE : FALSE;
DwmSetWindowAttribute( hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &darkMode, sizeof( darkMode ) );
FreeLibrary( hDwmapi );
}
#elif defined( EE_X11_PLATFORM )
#include <signal.h>
#include <unistd.h>
static pid_t ONBOARD_PID = 0;
void showOSK() {
if ( ONBOARD_PID == 0 ) {
if ( FileSystem::fileExists( "/usr/bin/onboard" ) ) {
pid_t pid = fork();
if ( pid == 0 ) {
execl( "/usr/bin/onboard", "onboard", NULL );
} else if ( pid != -1 ) {
ONBOARD_PID = pid;
}
} else {
EE::System::Log::error(
"\"onboard\" must be installed to be able to use the On Screen Keyboard" );
}
}
}
void hideOSK() {
if ( ONBOARD_PID != 0 ) {
kill( ONBOARD_PID, SIGTERM );
ONBOARD_PID = 0;
}
}
#endif
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
WindowSDL::WindowSDL( WindowSettings Settings, ContextSettings Context ) :
Window( Settings, Context, eeNew( ClipboardSDL, ( this ) ), eeNew( InputSDL, ( this ) ),
eeNew( CursorManagerSDL, ( this ) ) ),
mSDLWindow( NULL ),
mGLContext( NULL ),
mGLContextThread( NULL ),
mWMinfo( NULL ) {
create( Settings, Context );
}
WindowSDL::~WindowSDL() {
if ( NULL != mGLContext ) {
SDL_GL_DestroyContext( mGLContext );
}
if ( NULL != mGLContextThread ) {
SDL_GL_DestroyContext( mGLContextThread );
}
eeSAFE_DELETE( mWMinfo );
if ( NULL != mSDLWindow ) {
SDL_DestroyWindow( mSDLWindow );
}
#if defined( EE_X11_PLATFORM )
hideOSK();
#endif
}
bool WindowSDL::create( WindowSettings Settings, ContextSettings Context ) {
if ( mWindow.Created )
return false;
mWindow.WindowConfig = Settings;
mWindow.ContextConfig = Context;
if ( !SDL_WasInit( SDL_INIT_VIDEO ) && !SDL_Init( SDL_INIT_VIDEO ) ) {
Log::error( "Unable to initialize SDL: %s", SDL_GetError() );
logFailureInit( "WindowSDL", getVersion() );
return false;
}
SDL_DisplayID primaryDisplay = SDL_GetPrimaryDisplay();
const SDL_DisplayMode* dpm = SDL_GetDesktopDisplayMode( primaryDisplay );
if ( dpm ) {
mWindow.DesktopResolution = Sizei( dpm->w, dpm->h );
} else {
Log::error( "Failed to get desktop display mode" );
mWindow.DesktopResolution = Sizei( 800, 600 );
}
#if EE_PLATFORM == EE_PLATFORM_ANDROID
mWindow.WindowConfig.Style = WindowStyle::Fullscreen | WindowStyle::UseDesktopResolution;
#endif
if ( mWindow.WindowConfig.Style & WindowStyle::UseDesktopResolution ) {
mWindow.WindowConfig.Width = mWindow.DesktopResolution.getWidth();
mWindow.WindowConfig.Height = mWindow.DesktopResolution.getHeight();
}
mWindow.Flags = SDL_WINDOW_OPENGL |
( ( !mWindow.WindowConfig.DisableHiDPI ? SDL_WINDOW_HIGH_PIXEL_DENSITY : 0 ) );
if ( mWindow.WindowConfig.Style & WindowStyle::Resize ) {
mWindow.Flags |= SDL_WINDOW_RESIZABLE;
}
if ( mWindow.WindowConfig.Style & WindowStyle::Borderless ) {
mWindow.Flags |= SDL_WINDOW_BORDERLESS;
}
setGLConfig();
Uint32 tmpFlags = mWindow.Flags;
if ( mWindow.WindowConfig.Style & WindowStyle::Fullscreen ) {
tmpFlags |= SDL_WINDOW_FULLSCREEN;
}
if ( mWindow.ContextConfig.Multisamples > 0 ) {
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 );
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, mWindow.ContextConfig.Multisamples );
}
#if EE_PLATFORM != EE_PLATFORM_MACOS && EE_PLATFORM != EE_PLATFORM_IOS && \
EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN
mWindow.WindowConfig.Width *= mWindow.WindowConfig.PixelDensity;
mWindow.WindowConfig.Height *= mWindow.WindowConfig.PixelDensity;
#endif
mSDLWindow = SDL_CreateWindow( mWindow.WindowConfig.Title.c_str(), mWindow.WindowConfig.Width,
mWindow.WindowConfig.Height, tmpFlags );
if ( NULL == mSDLWindow ) {
Log::error( "Unable to create window: %s", SDL_GetError() );
logFailureInit( "WindowSDL", getVersion() );
return false;
}
#if EE_PLATFORM == EE_PLATFORM_ANDROID || EE_PLATFORM == EE_PLATFORM_IOS
Log::notice( "Choosing GL Version from: %d", Context.Version );
if ( GLv_default != Context.Version ) {
if ( GLv_ES1 == Context.Version || GLv_2 == Context.Version ) {
if ( GLv_2 == Context.Version )
mWindow.ContextConfig.Version = GLv_ES1;
Log::notice( "Starting GLES1" );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 1 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 );
} else {
Log::notice( "Starting GLES2" );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 2 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 0 );
}
} else {
#if defined( EE_GLES2 )
Log::notice( "Starting GLES2 default" );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 2 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 0 );
#else
Log::notice( "Starting GLES1 default" );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 1 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 );
#endif
}
#else
if ( GLv_3CP == Context.Version ) {
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 2 );
}
#endif
#ifdef SDL3_THREADED_GLCONTEXT
if ( mWindow.ContextConfig.SharedGLContext ) {
SDL_GL_SetAttribute( SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1 );
mGLContextThread = SDL_GL_CreateContext( mSDLWindow );
mGLContext = SDL_GL_CreateContext( mSDLWindow );
} else {
mGLContext = SDL_GL_CreateContext( mSDLWindow );
}
#else
mGLContext = SDL_GL_CreateContext( mSDLWindow );
mWindow.ContextConfig.SharedGLContext = false;
#endif
if ( NULL == mGLContext
#ifdef SDL3_THREADED_GLCONTEXT
|| ( mWindow.ContextConfig.SharedGLContext && NULL == mGLContextThread )
#endif
) {
Log::error( "Unable to create context: %s", SDL_GetError() );
logFailureInit( "WindowSDL", getVersion() );
return false;
}
int w, h;
SDL_GetWindowSizeInPixels( mSDLWindow, &w, &h );
if ( w > 0 && h > 0 ) {
mWindow.WindowConfig.Width = w;
mWindow.WindowConfig.Height = h;
mWindow.WindowSize = Sizei( mWindow.WindowConfig.Width, mWindow.WindowConfig.Height );
mLastWindowedSize = mWindow.WindowSize;
} else {
Log::error( "Window failed to create!" );
if ( NULL != SDL_GetError() && SDL_GetError()[0] != '\0' ) {
Log::error( "SDL Error: %s", SDL_GetError() );
}
return false;
}
SDL_GL_SetSwapInterval( ( mWindow.ContextConfig.VSync ? 1 : 0 ) );
SDL_GL_MakeCurrent( mSDLWindow, mGLContext );
mID = SDL_GetWindowID( mSDLWindow );
mWMinfo = eeNew( WMInfo, ( mSDLWindow ) );
if ( NULL == Renderer::existsSingleton() ) {
Renderer::createSingleton( mWindow.ContextConfig.Version );
Renderer::instance()->init();
if ( mWindow.ContextConfig.Multisamples > 0 )
Renderer::instance()->multisample( true );
}
getMainContext();
setTitle( mWindow.WindowConfig.Title );
createView();
setup2D( false );
mWindow.Created = true;
if ( "" != mWindow.WindowConfig.Icon ) {
setIcon( mWindow.WindowConfig.Icon );
}
static_cast<ClipboardSDL*>( mClipboard )->init();
static_cast<InputSDL*>( mInput )->init();
mCursorManager->set( Cursor::SysArrow );
logSuccessfulInit( getVersion() );
return true;
}
Uint32 WindowSDL::getWindowID() const {
return mID;
}
void WindowSDL::makeCurrent() {
SDL_GL_MakeCurrent( mSDLWindow, mGLContext );
}
void WindowSDL::close() {
SDL_DestroyWindow( mSDLWindow );
mSDLWindow = NULL;
mGLContext = NULL;
mGLContextThread = NULL;
Window::close();
}
void WindowSDL::setCurrent() {
makeCurrent();
}
bool WindowSDL::isThreadedGLContext() const {
#ifdef SDL3_THREADED_GLCONTEXT
return mWindow.ContextConfig.SharedGLContext;
#else
return false;
#endif
}
void WindowSDL::setGLContextThread() {
mGLContextMutex.lock();
SDL_GL_MakeCurrent( mSDLWindow, mGLContextThread );
}
void WindowSDL::unsetGLContextThread() {
SDL_GL_MakeCurrent( mSDLWindow, NULL );
mGLContextMutex.unlock();
}
int WindowSDL::getCurrentDisplayIndex() const {
if ( !mSDLWindow )
return 0;
SDL_DisplayID displayID = SDL_GetDisplayForWindow( mSDLWindow );
// Map display ID to index using the display manager
auto* dispMgr = static_cast<DisplayManagerSDL3*>( Engine::instance()->getDisplayManager() );
int idx = dispMgr->getDisplayIndexFromID( displayID );
return idx >= 0 ? idx : 0;
}
std::string WindowSDL::getVersion() {
int major = SDL_MAJOR_VERSION;
int minor = SDL_MINOR_VERSION;
int patch = SDL_MICRO_VERSION;
return String::format( "SDL %d.%d.%d", major, minor, patch );
}
void WindowSDL::setGLConfig() {
if ( mWindow.ContextConfig.DepthBufferSize )
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, mWindow.ContextConfig.DepthBufferSize );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, ( mWindow.ContextConfig.DoubleBuffering ? 1 : 0 ) );
if ( mWindow.ContextConfig.StencilBufferSize )
SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, mWindow.ContextConfig.StencilBufferSize );
if ( mWindow.WindowConfig.BitsPerPixel == 16 ) {
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 4 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 4 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 4 );
SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 4 );
} else {
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
}
}
void WindowSDL::toggleFullscreen() {
Log::info( "toggleFullscreen called: %s", isWindowed() ? "is windowed" : "is fullscreen" );
if ( isWindowed() ) {
mWinPos = getPosition();
mWindow.Maximized = isMaximized();
}
SDL_SetWindowFullscreen( mSDLWindow, !isWindowed() ? false : true );
BitOp::setBitFlagValue( &mWindow.WindowConfig.Style, WindowStyle::Fullscreen,
isWindowed() ? 1 : 0 );
getCursorManager()->reload();
if ( isWindowed() ) {
setPosition( mWinPos.x, mWinPos.y );
if ( mWindow.Maximized )
maximize();
}
}
void WindowSDL::setTitle( const std::string& title ) {
if ( mWindow.WindowConfig.Title != title ) {
mWindow.WindowConfig.Title = title;
SDL_SetWindowTitle( mSDLWindow, title.c_str() );
}
}
bool WindowSDL::isActive() const {
Uint64 flags = SDL_GetWindowFlags( mSDLWindow );
return 0 != ( ( flags & SDL_WINDOW_INPUT_FOCUS ) && ( flags & SDL_WINDOW_MOUSE_FOCUS ) );
}
bool WindowSDL::isVisible() const {
Uint64 flags = SDL_GetWindowFlags( mSDLWindow );
return 0 != ( !( flags & SDL_WINDOW_HIDDEN ) && !( flags & SDL_WINDOW_MINIMIZED ) );
}
bool WindowSDL::hasFocus() const {
Uint64 flags = SDL_GetWindowFlags( mSDLWindow );
return 0 != ( flags & ( SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS ) );
}
bool WindowSDL::hasInputFocus() const {
Uint64 flags = SDL_GetWindowFlags( mSDLWindow );
return 0 != ( flags & SDL_WINDOW_INPUT_FOCUS );
}
bool WindowSDL::hasMouseFocus() const {
Uint64 flags = SDL_GetWindowFlags( mSDLWindow );
return 0 != ( flags & SDL_WINDOW_MOUSE_FOCUS );
}
void WindowSDL::onWindowResize( Uint32 width, Uint32 height ) {
if ( width == mWindow.WindowConfig.Width && height == mWindow.WindowConfig.Height )
return;
Log::debug( "onWindowResize: Width %d Height %d.", width, height );
mWindow.WindowConfig.Width = width;
mWindow.WindowConfig.Height = height;
mWindow.WindowSize = Sizei( width, height );
if ( isWindowed() )
mLastWindowedSize = Sizei( width, height );
mDefaultView.reset( Rectf( 0, 0, mWindow.WindowConfig.Width, mWindow.WindowConfig.Height ) );
setup2D( false );
SDL_PumpEvents();
SDL_FlushEvent( SDL_EVENT_WINDOW_RESIZED );
mCursorManager->reload();
sendVideoResizeCb();
}
void WindowSDL::setSize( Uint32 width, Uint32 height, bool windowed ) {
if ( ( !width || !height ) ) {
width = mWindow.DesktopResolution.getWidth();
height = mWindow.DesktopResolution.getHeight();
}
if ( this->isWindowed() == windowed && width == mWindow.WindowConfig.Width &&
height == mWindow.WindowConfig.Height )
return;
Log::debug( "Switching from %s to %s. Width: %d Height %d.",
this->isWindowed() ? "windowed" : "fullscreen",
windowed ? "windowed" : "fullscreen", width, height );
Uint32 oldWidth = mWindow.WindowConfig.Width;
Uint32 oldHeight = mWindow.WindowConfig.Height;
mWindow.WindowConfig.Width = width;
mWindow.WindowConfig.Height = height;
if ( windowed ) {
mWindow.WindowSize = Sizei( width, height );
} else {
mWindow.WindowSize = Sizei( oldWidth, oldHeight );
}
if ( isWindowed() && !windowed ) {
mWinPos = getPosition();
} else {
SDL_SetWindowFullscreen( mSDLWindow, !windowed );
}
if ( windowed )
mLastWindowedSize = Sizei( width, height );
SDL_SetWindowSize( mSDLWindow, width, height );
if ( isWindowed() && !windowed ) {
mWinPos = getPosition();
setGLConfig();
SDL_SetWindowFullscreen( mSDLWindow, !windowed );
}
if ( isWindowed() && windowed ) {
setPosition( mWinPos.x, mWinPos.y );
}
BitOp::setBitFlagValue( &mWindow.WindowConfig.Style, WindowStyle::Fullscreen, !windowed );
mDefaultView.reset( Rectf( 0, 0, width, height ) );
setup2D( false );
SDL_PumpEvents();
SDL_FlushEvent( SDL_EVENT_WINDOW_RESIZED );
mCursorManager->reload();
sendVideoResizeCb();
}
void WindowSDL::swapBuffers() {
SDL_GL_SwapWindow( mSDLWindow );
}
std::vector<DisplayMode> WindowSDL::getDisplayModes() const {
std::vector<DisplayMode> result;
int displayCount = 0;
SDL_DisplayID* displays = SDL_GetDisplays( &displayCount );
if ( displays ) {
for ( int x = 0; x < displayCount; x++ ) {
int modeCount = 0;
SDL_DisplayMode** modesArray = SDL_GetFullscreenDisplayModes( displays[x], &modeCount );
if ( modesArray ) {
for ( int i = 0; i < modeCount; i++ ) {
SDL_DisplayMode* mode = modesArray[i];
result.push_back( DisplayMode( mode->w, mode->h, (int)mode->refresh_rate, x ) );
}
SDL_free( modesArray );
}
}
SDL_free( displays );
}
return result;
}
void WindowSDL::setGamma( Float Red, Float Green, Float Blue ) {
// SDL3 removed gamma ramp functionality
}
eeWindowHandle WindowSDL::getWindowHandler() const {
if ( NULL != mWMinfo ) {
return mWMinfo->getWindowHandler();
}
return 0;
}
bool WindowSDL::setIcon( const std::string& Path ) {
int x, y, c;
if ( !mWindow.Created ) {
if ( Image::getInfo( Path.c_str(), &x, &y, &c ) ) {
mWindow.WindowConfig.Icon = Path;
return true;
}
return false;
}
Image Img( Path );
if ( NULL != Img.getPixelsPtr() ) {
#if EE_PLATFORM == EE_PLATFORM_WIN
if ( Img.getWidth() > 64 || Img.getHeight() > 64 ) {
Img.resize( 64, 64 );
}
#endif
const Uint8* Ptr = Img.getPixelsPtr();
x = Img.getWidth();
y = Img.getHeight();
c = Img.getChannels();
if ( ( x % 8 ) == 0 && ( y % 8 ) == 0 ) {
SDL_Surface* iconSurface = SDL_CreateSurface( x, y, SDL_PIXELFORMAT_RGBA32 );
if ( iconSurface ) {
Uint32 ssize = iconSurface->w * iconSurface->h * c;
for ( Uint32 i = 0; i < ssize; i++ ) {
( static_cast<Uint8*>( iconSurface->pixels ) )[i + 0] = ( Ptr )[i];
}
SDL_SetWindowIcon( mSDLWindow, iconSurface );
SDL_DestroySurface( iconSurface );
return true;
}
}
}
return false;
}
void WindowSDL::minimize() {
SDL_MinimizeWindow( mSDLWindow );
}
void WindowSDL::maximize() {
SDL_MaximizeWindow( mSDLWindow );
}
bool WindowSDL::isMaximized() const {
return SDL_GetWindowFlags( mSDLWindow ) & SDL_WINDOW_MAXIMIZED;
}
bool WindowSDL::isMinimized() const {
return SDL_GetWindowFlags( mSDLWindow ) & SDL_WINDOW_MINIMIZED;
}
void WindowSDL::hide() {
SDL_HideWindow( mSDLWindow );
}
void WindowSDL::raise() {
SDL_RaiseWindow( mSDLWindow );
}
void WindowSDL::restore() {
SDL_RestoreWindow( mSDLWindow );
}
void WindowSDL::flash( WindowFlashOperation op ) {
#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN
SDL_FlashOperation sdlOp = SDL_FLASH_BRIEFLY;
if ( op == WindowFlashOperation::Cancel )
sdlOp = SDL_FLASH_CANCEL;
else if ( op == WindowFlashOperation::UntilFocused )
sdlOp = SDL_FLASH_UNTIL_FOCUSED;
SDL_FlashWindow( mSDLWindow, sdlOp );
#endif
}
void WindowSDL::show() {
SDL_ShowWindow( mSDLWindow );
}
void WindowSDL::setPosition( int Left, int Top ) {
SDL_SetWindowPosition( mSDLWindow, Left, Top );
}
Vector2i WindowSDL::getPosition() const {
Vector2i p;
SDL_GetWindowPosition( mSDLWindow, &p.x, &p.y );
return p;
}
void WindowSDL::updateDesktopResolution() const {
SDL_DisplayID displayID = SDL_GetDisplayForWindow( mSDLWindow );
const SDL_DisplayMode* dpm = SDL_GetDesktopDisplayMode( displayID );
if ( dpm ) {
mWindow.DesktopResolution = Sizei( dpm->w, dpm->h );
} else {
Log::warning( "Failed to get desktop display mode for display %u", displayID );
}
}
const Sizei& WindowSDL::getDesktopResolution() const {
updateDesktopResolution();
return Window::getDesktopResolution();
}
Rect WindowSDL::getBorderSize() const {
Rect bordersSize;
SDL_GetWindowBordersSize( mSDLWindow, &bordersSize.Top, &bordersSize.Left, &bordersSize.Bottom,
&bordersSize.Right );
return bordersSize;
}
Float WindowSDL::getScale() const {
int realX, realY;
int scaledX, scaledY;
SDL_GetWindowSizeInPixels( mSDLWindow, &realX, &realY );
SDL_GetWindowSize( mSDLWindow, &scaledX, &scaledY );
return (Float)realX / (Float)scaledX;
}
bool WindowSDL::hasNativeMessageBox() const {
return true;
}
Uint32 toSDLMsgBoxType( const Window::MessageBoxType& type ) {
switch ( type ) {
case Window::MessageBoxType::Error:
return SDL_MESSAGEBOX_ERROR;
case Window::MessageBoxType::Warning:
return SDL_MESSAGEBOX_WARNING;
case Window::MessageBoxType::Information:
return SDL_MESSAGEBOX_INFORMATION;
}
return SDL_MESSAGEBOX_INFORMATION;
}
bool WindowSDL::showMessageBox( const MessageBoxType& type, const std::string& title,
const std::string& message ) {
return SDL_ShowSimpleMessageBox( toSDLMsgBoxType( type ), title.c_str(), message.c_str(),
mSDLWindow );
}
SDL_Window* WindowSDL::getSDLWindow() const {
return mSDLWindow;
}
void WindowSDL::startOnScreenKeyboard() {
#if EE_PLATFORM == EE_PLATFORM_WIN
showOSK( getWindowHandler() );
#elif defined( EE_X11_PLATFORM )
showOSK();
#endif
}
void WindowSDL::stopOnScreenKeyboard() {
#if EE_PLATFORM == EE_PLATFORM_WIN
hideOSK();
#elif defined( EE_X11_PLATFORM )
hideOSK();
#endif
}
bool WindowSDL::isOnScreenKeyboardActive() const {
#if EE_PLATFORM == EE_PLATFORM_WIN
return WIN_OSK_VISIBLE;
#elif defined( EE_X11_PLATFORM )
return ONBOARD_PID != 0;
#else
return false;
#endif
}
void WindowSDL::startTextInput() {
if ( mWindow.WindowConfig.UseScreenKeyboard ) {
startOnScreenKeyboard();
} else {
SDL_StartTextInput( mSDLWindow );
}
}
bool WindowSDL::isTextInputActive() const {
if ( mWindow.WindowConfig.UseScreenKeyboard )
return isOnScreenKeyboardActive();
return SDL_TextInputActive( mSDLWindow );
}
void WindowSDL::stopTextInput() {
if ( mWindow.WindowConfig.UseScreenKeyboard ) {
stopOnScreenKeyboard();
} else {
SDL_StopTextInput( mSDLWindow );
}
}
void WindowSDL::setTextInputRect( const Rect& rect ) {
SDL_Rect r;
r.x = rect.Left;
r.y = rect.Top;
r.w = rect.getSize().getWidth();
r.h = rect.getSize().getHeight();
SDL_SetTextInputArea( mSDLWindow, &r, 0 );
}
void WindowSDL::clearComposition() {
SDL_ClearComposition( mSDLWindow );
}
bool WindowSDL::hasScreenKeyboardSupport() const {
return SDL_HasScreenKeyboardSupport();
}
bool WindowSDL::isScreenKeyboardShown() const {
return SDL_ScreenKeyboardShown( mSDLWindow );
}
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,145 @@
#ifndef EE_WINDOWCWINDOWSDL3_HPP
#define EE_WINDOWCWINDOWSDL3_HPP
#include <eepp/window/backend.hpp>
#include <eepp/window/backend/SDL3/base.hpp>
#ifdef EE_BACKEND_SDL3
#include <eepp/window/backend/SDL3/wminfo.hpp>
#include <eepp/window/window.hpp>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
class EE_API WindowSDL : public Window {
public:
WindowSDL( WindowSettings Settings, ContextSettings Context );
virtual ~WindowSDL();
bool create( WindowSettings Settings, ContextSettings Context );
Uint32 getWindowID() const;
void makeCurrent();
void close();
void setCurrent();
void toggleFullscreen();
void setTitle( const std::string& title );
bool setIcon( const std::string& Path );
bool isActive() const;
bool isVisible() const;
bool hasFocus() const;
bool hasInputFocus() const;
bool hasMouseFocus() const;
void setSize( Uint32 width, Uint32 height, bool windowed );
std::vector<DisplayMode> getDisplayModes() const;
void setGamma( Float Red, Float Green, Float Blue );
eeWindowHandle getWindowHandler() const;
virtual void minimize();
virtual void maximize();
virtual bool isMaximized() const;
virtual bool isMinimized() const;
virtual void hide();
virtual void raise();
virtual void restore();
virtual void flash( WindowFlashOperation op );
virtual void show();
virtual void setPosition( int Left, int Top );
virtual Vector2i getPosition() const;
const Sizei& getDesktopResolution() const;
virtual Rect getBorderSize() const;
virtual Float getScale() const;
virtual bool hasNativeMessageBox() const;
virtual bool showMessageBox( const MessageBoxType& type, const std::string& title,
const std::string& message );
SDL_Window* getSDLWindow() const;
void startOnScreenKeyboard();
void stopOnScreenKeyboard();
bool isOnScreenKeyboardActive() const;
void startTextInput();
bool isTextInputActive() const;
void stopTextInput();
void setTextInputRect( const Rect& rect );
void clearComposition();
bool hasScreenKeyboardSupport() const;
bool isScreenKeyboardShown() const;
bool isThreadedGLContext() const;
void setGLContextThread();
void unsetGLContextThread();
int getCurrentDisplayIndex() const;
protected:
friend class ClipboardSDL;
SDL_Window* mSDLWindow{ nullptr };
SDL_GLContext mGLContext;
SDL_GLContext mGLContextThread;
Mutex mGLContextMutex;
Uint32 mID{ 0 };
WMInfo* mWMinfo;
Vector2i mWinPos;
void swapBuffers();
void setGLConfig();
std::string getVersion();
void updateDesktopResolution() const;
void onWindowResize( Uint32 width, Uint32 height );
};
}}}} // namespace EE::Window::Backend::SDL3
#endif
#endif

View File

@@ -0,0 +1,34 @@
#include <eepp/window/backend/SDL3/wminfo.hpp>
#ifdef EE_BACKEND_SDL3
#include <SDL3/SDL.h>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
WMInfo::WMInfo( SDL_Window* win ) : mProps( SDL_GetWindowProperties( win ) ) {}
WMInfo::~WMInfo() {}
#if defined( EE_X11_PLATFORM )
X11Window WMInfo::getWindow() const {
return 0;
}
#endif
eeWindowHandle WMInfo::getWindowHandler() const {
#if EE_PLATFORM == EE_PLATFORM_WIN
return (eeWindowHandle)SDL_GetPointerProperty( mProps, SDL_PROP_WINDOW_WIN32_HWND_POINTER, 0 );
#elif defined( EE_X11_PLATFORM )
return (eeWindowHandle)SDL_GetNumberProperty( mProps, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0 );
#elif EE_PLATFORM == EE_PLATFORM_MACOS
return (eeWindowHandle)SDL_GetPointerProperty( mProps, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER,
0 );
#else
return 0;
#endif
}
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -0,0 +1,27 @@
#ifndef EE_BACKEND_SDL3_WMINFO_HPP
#define EE_BACKEND_SDL3_WMINFO_HPP
#include <eepp/window/backend/SDL3/base.hpp>
#include <eepp/window/windowhandle.hpp>
namespace EE { namespace Window { namespace Backend { namespace SDL3 {
class EE_API WMInfo {
public:
WMInfo( SDL_Window* win );
~WMInfo();
#if defined( EE_X11_PLATFORM )
X11Window getWindow() const;
#endif
eeWindowHandle getWindowHandler() const;
protected:
SDL_PropertiesID mProps;
};
}}}} // namespace EE::Window::Backend::SDL3
#endif

View File

@@ -24,6 +24,10 @@
#include <eepp/window/backend.hpp>
#include <eepp/window/backend/SDL2/backendsdl2.hpp>
#include <eepp/window/backend/SDL2/platformhelpersdl2.hpp>
#if defined(EE_SDL_VERSION_3)
#include <eepp/window/backend/SDL3/backendsdl3.hpp>
#include <eepp/window/backend/SDL3/platformhelpersdl3.hpp>
#endif
#include <eepp/window/engine.hpp>
#if EE_PLATFORM == EE_PLATFORM_ANDROID
@@ -31,10 +35,13 @@
#endif
#define BACKEND_SDL2 1
#define BACKEND_SDL3 2
#ifndef DEFAULT_BACKEND
#if defined( EE_BACKEND_SDL2 )
#if defined( EE_BACKEND_SDL3 )
#define DEFAULT_BACKEND BACKEND_SDL3
#elif defined( EE_BACKEND_SDL2 )
#define DEFAULT_BACKEND BACKEND_SDL2
#endif
@@ -150,10 +157,35 @@ EE::Window::Window* Engine::createSDL2Window( const WindowSettings& Settings,
#endif
}
#ifdef EE_BACKEND_SDL3
Backend::WindowBackendLibrary* Engine::createSDL3Backend( const WindowSettings& ) {
#if defined( EE_SDL_VERSION_3 )
return eeNew( Backend::SDL3::WindowBackendSDL3, () );
#else
return NULL;
#endif
}
EE::Window::Window* Engine::createSDL3Window( const WindowSettings& Settings,
const ContextSettings& Context ) {
#if defined( EE_SDL_VERSION_3 )
if ( NULL == mBackend ) {
mBackend = createSDL3Backend( Settings );
}
return eeNew( Backend::SDL3::WindowSDL, ( Settings, Context ) );
#else
return NULL;
#endif
}
#endif
EE::Window::Window* Engine::createDefaultWindow( const WindowSettings& Settings,
const ContextSettings& Context ) {
#if DEFAULT_BACKEND == BACKEND_SDL2
return createSDL2Window( Settings, Context );
#elif DEFAULT_BACKEND == BACKEND_SDL3
return createSDL3Window( Settings, Context );
#else
return NULL;
#endif
@@ -251,6 +283,8 @@ bool Engine::isRunning() const {
WindowBackend Engine::getDefaultBackend() const {
#if DEFAULT_BACKEND == BACKEND_SDL2
return WindowBackend::SDL2;
#elif DEFAULT_BACKEND == BACKEND_SDL3
return WindowBackend::SDL3;
#else
return WindowBackend::Default;
#endif
@@ -293,6 +327,8 @@ WindowSettings Engine::createWindowSettings( IniFile* ini, std::string iniKeyNam
if ( "sdl2" == backend )
winBackend = WindowBackend::SDL2;
else if ( "sdl3" == backend )
winBackend = WindowBackend::SDL3;
Uint32 Style = WindowStyle::Titlebar;
@@ -382,6 +418,8 @@ PlatformHelper* Engine::getPlatformHelper() {
if ( NULL == mPlatformHelper ) {
#if DEFAULT_BACKEND == BACKEND_SDL2
mPlatformHelper = eeNew( Backend::SDL2::PlatformHelperSDL2, () );
#elif DEFAULT_BACKEND == BACKEND_SDL3
mPlatformHelper = eeNew( Backend::SDL3::PlatformHelperSDL3, () );
#endif
}
@@ -392,6 +430,8 @@ DisplayManager* Engine::getDisplayManager() {
if ( NULL == mDisplayManager ) {
#if DEFAULT_BACKEND == BACKEND_SDL2
mDisplayManager = eeNew( Backend::SDL2::DisplayManagerSDL2, () );
#elif DEFAULT_BACKEND == BACKEND_SDL3
mDisplayManager = eeNew( Backend::SDL3::DisplayManagerSDL3, () );
#endif
}

View File

@@ -348,9 +348,17 @@ void Window::close() {
void Window::setFrameRateLimit( Uint32 FrameRateLimit ) {
if ( FrameRateLimit == ContextSettings::FrameRateLimitScreenRefreshRate ) {
Display* currentDisplay = Engine::instance()->getDisplayManager()->getDisplayIndex(
isOpen() ? getCurrentDisplayIndex() : 0 );
FrameRateLimit = currentDisplay->getRefreshRate();
Display* currentDisplay = nullptr;
if ( Engine::existsSingleton() && Engine::instance()->getDisplayManager() ) {
currentDisplay = Engine::instance()->getDisplayManager()->getDisplayIndex(
getCurrentDisplayIndex() );
}
if ( currentDisplay ) {
FrameRateLimit = currentDisplay->getRefreshRate();
} else {
// Fallback to 60 FPS if display info not available
FrameRateLimit = 60;
}
}
mFrameData.FPS.Limit = (Float)FrameRateLimit;
}