Skip to content

Commit

Permalink
Add SDL3_FNAPlatform, TextInputEXT.WindowHandle (#494)
Browse files Browse the repository at this point in the history
Co-authored-by: Ethan Lee <flibitijibibo@gmail.com>
  • Loading branch information
thatcosmonaut and flibitijibibo authored Oct 1, 2024
1 parent db16bf9 commit ed3a16e
Show file tree
Hide file tree
Showing 14 changed files with 2,931 additions and 78 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "lib/Theorafile"]
path = lib/Theorafile
url = https://github.com/FNA-XNA/Theorafile
[submodule "lib/SDL3-CS"]
path = lib/SDL3-CS
url = https://github.com/flibitijibibo/SDL3-CS.git
2 changes: 2 additions & 0 deletions FNA.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
<Compile Include="src\FNAPlatform\FNAPlatform.cs" />
<Compile Include="src\FNAPlatform\FNAWindow.cs" />
<Compile Include="src\FNAPlatform\SDL2_FNAPlatform.cs" />
<Compile Include="src\FNAPlatform\SDL3_FNAPlatform.cs" />
<Compile Include="src\FrameworkDispatcher.cs" />
<Compile Include="src\Game.cs" />
<Compile Include="src\GameComponent.cs" />
Expand Down Expand Up @@ -337,6 +338,7 @@
<Compile Include="src\Vector3.cs" />
<Compile Include="src\Vector4.cs" />
<Compile Include="lib\SDL2-CS\src\SDL2.cs" />
<Compile Include="lib\SDL3-CS\SDL3\SDL3.cs" />
<Compile Include="lib\FAudio\csharp\FAudio.cs" />
<Compile Include="lib\Theorafile\csharp\Theorafile.cs" />
</ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions FNA.NetFramework.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
<Compile Include="src\FNAPlatform\FNAPlatform.cs" />
<Compile Include="src\FNAPlatform\FNAWindow.cs" />
<Compile Include="src\FNAPlatform\SDL2_FNAPlatform.cs" />
<Compile Include="src\FNAPlatform\SDL3_FNAPlatform.cs" />
<Compile Include="src\FrameworkDispatcher.cs" />
<Compile Include="src\Game.cs" />
<Compile Include="src\GameComponent.cs" />
Expand Down Expand Up @@ -338,6 +339,7 @@
<Compile Include="src\Vector3.cs" />
<Compile Include="src\Vector4.cs" />
<Compile Include="lib\SDL2-CS\src\SDL2.cs" />
<Compile Include="lib\SDL3-CS\SDL3\SDL3.cs" />
<Compile Include="lib\FAudio\csharp\FAudio.cs" />
<Compile Include="lib\Theorafile\csharp\Theorafile.cs" />
</ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions FNA.NetStandard.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
<Compile Include="src\FNAPlatform\FNAPlatform.cs" />
<Compile Include="src\FNAPlatform\FNAWindow.cs" />
<Compile Include="src\FNAPlatform\SDL2_FNAPlatform.cs" />
<Compile Include="src\FNAPlatform\SDL3_FNAPlatform.cs" />
<Compile Include="src\FrameworkDispatcher.cs" />
<Compile Include="src\Game.cs" />
<Compile Include="src\GameComponent.cs" />
Expand Down Expand Up @@ -337,6 +338,7 @@
<Compile Include="src\Vector3.cs" />
<Compile Include="src\Vector4.cs" />
<Compile Include="lib\SDL2-CS\src\SDL2.cs" />
<Compile Include="lib\SDL3-CS\SDL3\SDL3.cs" />
<Compile Include="lib\FAudio\csharp\FAudio.cs" />
<Compile Include="lib\Theorafile\csharp\Theorafile.cs" />
</ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions FNA.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@
<Compile Include="src\FNAPlatform\FNAPlatform.cs" />
<Compile Include="src\FNAPlatform\FNAWindow.cs" />
<Compile Include="src\FNAPlatform\SDL2_FNAPlatform.cs" />
<Compile Include="src\FNAPlatform\SDL3_FNAPlatform.cs" />
<Compile Include="src\FrameworkDispatcher.cs" />
<Compile Include="src\Game.cs" />
<Compile Include="src\GameComponent.cs" />
Expand Down Expand Up @@ -408,6 +409,7 @@
<Compile Include="src\Vector3.cs" />
<Compile Include="src\Vector4.cs" />
<Compile Include="lib\SDL2-CS\src\SDL2.cs" />
<Compile Include="lib\SDL3-CS\SDL3\SDL3.cs" />
<Compile Include="lib\FAudio\csharp\FAudio.cs" />
<Compile Include="lib\Theorafile\csharp\Theorafile.cs" />
</ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ SRC = \
src/FNAPlatform/FNAPlatform.cs \
src/FNAPlatform/FNAWindow.cs \
src/FNAPlatform/SDL2_FNAPlatform.cs \
src/FNAPlatform/SDL3_FNAPlatform.cs \
src/FrameworkDispatcher.cs \
src/Game.cs \
src/GameComponent.cs \
Expand Down Expand Up @@ -321,6 +322,7 @@ SRC = \
src/Vector3.cs \
src/Vector4.cs \
lib/SDL2-CS/src/SDL2.cs \
lib/SDL3-CS/SDL3/SDL3.cs \
lib/FAudio/csharp/FAudio.cs \
lib/Theorafile/csharp/Theorafile.cs

Expand Down
11 changes: 8 additions & 3 deletions app.config
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<dllmap dll="SDL2" os="windows" target="SDL2.dll"/>
<dllmap dll="SDL2" os="osx" target="libSDL2-2.0.0.dylib"/>
<dllmap dll="SDL2" os="linux,freebsd,netbsd" target="libSDL2-2.0.so.0"/>
<dllmap dll="SDL3" os="windows" target="SDL3.dll"/>
<dllmap dll="SDL3" os="osx" target="libSDL3.0.dylib"/>
<dllmap dll="SDL3" os="linux,freebsd,netbsd" target="libSDL3.so.0"/>

<dllmap dll="FNA3D" os="windows" target="FNA3D.dll"/>
<dllmap dll="FNA3D" os="osx" target="libFNA3D.0.dylib"/>
Expand All @@ -11,4 +11,9 @@
<dllmap dll="FAudio" os="windows" target="FAudio.dll"/>
<dllmap dll="FAudio" os="osx" target="libFAudio.0.dylib"/>
<dllmap dll="FAudio" os="linux,freebsd,netbsd" target="libFAudio.so.0"/>

<!-- Deprecated! -->
<dllmap dll="SDL2" os="windows" target="SDL2.dll"/>
<dllmap dll="SDL2" os="osx" target="libSDL2-2.0.0.dylib"/>
<dllmap dll="SDL2" os="linux,freebsd,netbsd" target="libSDL2-2.0.so.0"/>
</configuration>
1 change: 1 addition & 0 deletions lib/SDL3-CS
Submodule SDL3-CS added at b988c4
197 changes: 135 additions & 62 deletions src/FNAPlatform/FNAPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,16 @@ static FNAPlatform()
* -flibit
*/

// Environment.GetEnvironmentVariable("FNA_PLATFORM_BACKEND");
bool useSDL3 = Environment.GetEnvironmentVariable("FNA_PLATFORM_BACKEND") == "SDL3";

SetEnv = SDL2_FNAPlatform.SetEnv;
if (useSDL3)
{
SetEnv = SDL3_FNAPlatform.SetEnv;
}
else
{
SetEnv = SDL2_FNAPlatform.SetEnv;
}

// Built-in command line arguments
LaunchParameters args = new LaunchParameters();
Expand Down Expand Up @@ -93,63 +100,129 @@ static FNAPlatform()
);
}

Malloc = SDL2_FNAPlatform.Malloc;
Free = SDL2.SDL.SDL_free;
CreateWindow = SDL2_FNAPlatform.CreateWindow;
DisposeWindow = SDL2_FNAPlatform.DisposeWindow;
ApplyWindowChanges = SDL2_FNAPlatform.ApplyWindowChanges;
ScaleForWindow = SDL2_FNAPlatform.ScaleForWindow;
GetWindowBounds = SDL2_FNAPlatform.GetWindowBounds;
GetWindowResizable = SDL2_FNAPlatform.GetWindowResizable;
SetWindowResizable = SDL2_FNAPlatform.SetWindowResizable;
GetWindowBorderless = SDL2_FNAPlatform.GetWindowBorderless;
SetWindowBorderless = SDL2_FNAPlatform.SetWindowBorderless;
SetWindowTitle = SDL2_FNAPlatform.SetWindowTitle;
IsScreenKeyboardShown = SDL2_FNAPlatform.IsScreenKeyboardShown;
RegisterGame = SDL2_FNAPlatform.RegisterGame;
UnregisterGame = SDL2_FNAPlatform.UnregisterGame;
PollEvents = SDL2_FNAPlatform.PollEvents;
GetGraphicsAdapters = SDL2_FNAPlatform.GetGraphicsAdapters;
GetCurrentDisplayMode = SDL2_FNAPlatform.GetCurrentDisplayMode;
GetKeyFromScancode = SDL2_FNAPlatform.GetKeyFromScancode;
IsTextInputActive = SDL2_FNAPlatform.IsTextInputActive;
StartTextInput = SDL2.SDL.SDL_StartTextInput;
StopTextInput = SDL2.SDL.SDL_StopTextInput;
SetTextInputRectangle = SDL2_FNAPlatform.SetTextInputRectangle;
GetMouseState = SDL2_FNAPlatform.GetMouseState;
SetMousePosition = SDL2.SDL.SDL_WarpMouseInWindow;
OnIsMouseVisibleChanged = SDL2_FNAPlatform.OnIsMouseVisibleChanged;
GetRelativeMouseMode = SDL2_FNAPlatform.GetRelativeMouseMode;
SetRelativeMouseMode = SDL2_FNAPlatform.SetRelativeMouseMode;
GetGamePadCapabilities = SDL2_FNAPlatform.GetGamePadCapabilities;
GetGamePadState = SDL2_FNAPlatform.GetGamePadState;
SetGamePadVibration = SDL2_FNAPlatform.SetGamePadVibration;
SetGamePadTriggerVibration = SDL2_FNAPlatform.SetGamePadTriggerVibration;
GetGamePadGUID = SDL2_FNAPlatform.GetGamePadGUID;
SetGamePadLightBar = SDL2_FNAPlatform.SetGamePadLightBar;
GetGamePadGyro = SDL2_FNAPlatform.GetGamePadGyro;
GetGamePadAccelerometer = SDL2_FNAPlatform.GetGamePadAccelerometer;
GetStorageRoot = SDL2_FNAPlatform.GetStorageRoot;
GetDriveInfo = SDL2_FNAPlatform.GetDriveInfo;
ReadFileToPointer = SDL2_FNAPlatform.ReadToPointer;
FreeFilePointer = SDL2_FNAPlatform.FreeFilePointer;
ShowRuntimeError = SDL2_FNAPlatform.ShowRuntimeError;
GetMicrophones = SDL2_FNAPlatform.GetMicrophones;
GetMicrophoneSamples = SDL2_FNAPlatform.GetMicrophoneSamples;
GetMicrophoneQueuedBytes = SDL2_FNAPlatform.GetMicrophoneQueuedBytes;
StartMicrophone = SDL2_FNAPlatform.StartMicrophone;
StopMicrophone = SDL2_FNAPlatform.StopMicrophone;
GetTouchCapabilities = SDL2_FNAPlatform.GetTouchCapabilities;
UpdateTouchPanelState = SDL2_FNAPlatform.UpdateTouchPanelState;
GetNumTouchFingers = SDL2_FNAPlatform.GetNumTouchFingers;
SupportsOrientationChanges = SDL2_FNAPlatform.SupportsOrientationChanges;
NeedsPlatformMainLoop = SDL2_FNAPlatform.NeedsPlatformMainLoop;
RunPlatformMainLoop = SDL2_FNAPlatform.RunPlatformMainLoop;
if (useSDL3)
{
Malloc = SDL3_FNAPlatform.Malloc;
Free = SDL3.SDL.SDL_free;
CreateWindow = SDL3_FNAPlatform.CreateWindow;
DisposeWindow = SDL3_FNAPlatform.DisposeWindow;
ApplyWindowChanges = SDL3_FNAPlatform.ApplyWindowChanges;
ScaleForWindow = SDL3_FNAPlatform.ScaleForWindow;
GetWindowBounds = SDL3_FNAPlatform.GetWindowBounds;
GetWindowResizable = SDL3_FNAPlatform.GetWindowResizable;
SetWindowResizable = SDL3_FNAPlatform.SetWindowResizable;
GetWindowBorderless = SDL3_FNAPlatform.GetWindowBorderless;
SetWindowBorderless = SDL3_FNAPlatform.SetWindowBorderless;
SetWindowTitle = SDL3_FNAPlatform.SetWindowTitle;
IsScreenKeyboardShown = SDL3_FNAPlatform.IsScreenKeyboardShown;
RegisterGame = SDL3_FNAPlatform.RegisterGame;
UnregisterGame = SDL3_FNAPlatform.UnregisterGame;
PollEvents = SDL3_FNAPlatform.PollEvents;
GetGraphicsAdapters = SDL3_FNAPlatform.GetGraphicsAdapters;
GetCurrentDisplayMode = SDL3_FNAPlatform.GetCurrentDisplayMode;
GetKeyFromScancode = SDL3_FNAPlatform.GetKeyFromScancode;
IsTextInputActive = SDL3_FNAPlatform.IsTextInputActive;
StartTextInput = SDL3_FNAPlatform.StartTextInput;
StopTextInput = SDL3_FNAPlatform.StopTextInput;
SetTextInputRectangle = SDL3_FNAPlatform.SetTextInputRectangle;
GetMouseState = SDL3_FNAPlatform.GetMouseState;
SetMousePosition = SDL3_FNAPlatform.WarpMouseInWindow;
OnIsMouseVisibleChanged = SDL3_FNAPlatform.OnIsMouseVisibleChanged;
GetRelativeMouseMode = SDL3_FNAPlatform.GetRelativeMouseMode;
SetRelativeMouseMode = SDL3_FNAPlatform.SetRelativeMouseMode;
GetGamePadCapabilities = SDL3_FNAPlatform.GetGamePadCapabilities;
GetGamePadState = SDL3_FNAPlatform.GetGamePadState;
SetGamePadVibration = SDL3_FNAPlatform.SetGamePadVibration;
SetGamePadTriggerVibration = SDL3_FNAPlatform.SetGamePadTriggerVibration;
GetGamePadGUID = SDL3_FNAPlatform.GetGamePadGUID;
SetGamePadLightBar = SDL3_FNAPlatform.SetGamePadLightBar;
GetGamePadGyro = SDL3_FNAPlatform.GetGamePadGyro;
GetGamePadAccelerometer = SDL3_FNAPlatform.GetGamePadAccelerometer;
GetStorageRoot = SDL3_FNAPlatform.GetStorageRoot;
GetDriveInfo = SDL3_FNAPlatform.GetDriveInfo;
ReadFileToPointer = SDL3_FNAPlatform.ReadToPointer;
FreeFilePointer = SDL3_FNAPlatform.FreeFilePointer;
ShowRuntimeError = SDL3_FNAPlatform.ShowRuntimeError;
GetMicrophones = SDL3_FNAPlatform.GetMicrophones;
GetMicrophoneSamples = SDL3_FNAPlatform.GetMicrophoneSamples;
GetMicrophoneQueuedBytes = SDL3_FNAPlatform.GetMicrophoneQueuedBytes;
StartMicrophone = SDL3_FNAPlatform.StartMicrophone;
StopMicrophone = SDL3_FNAPlatform.StopMicrophone;
GetTouchCapabilities = SDL3_FNAPlatform.GetTouchCapabilities;
UpdateTouchPanelState = SDL3_FNAPlatform.UpdateTouchPanelState;
GetNumTouchFingers = SDL3_FNAPlatform.GetNumTouchFingers;
SupportsOrientationChanges = SDL3_FNAPlatform.SupportsOrientationChanges;
NeedsPlatformMainLoop = SDL3_FNAPlatform.NeedsPlatformMainLoop;
RunPlatformMainLoop = SDL3_FNAPlatform.RunPlatformMainLoop;
}
else
{
Malloc = SDL2_FNAPlatform.Malloc;
Free = SDL2.SDL.SDL_free;
CreateWindow = SDL2_FNAPlatform.CreateWindow;
DisposeWindow = SDL2_FNAPlatform.DisposeWindow;
ApplyWindowChanges = SDL2_FNAPlatform.ApplyWindowChanges;
ScaleForWindow = SDL2_FNAPlatform.ScaleForWindow;
GetWindowBounds = SDL2_FNAPlatform.GetWindowBounds;
GetWindowResizable = SDL2_FNAPlatform.GetWindowResizable;
SetWindowResizable = SDL2_FNAPlatform.SetWindowResizable;
GetWindowBorderless = SDL2_FNAPlatform.GetWindowBorderless;
SetWindowBorderless = SDL2_FNAPlatform.SetWindowBorderless;
SetWindowTitle = SDL2_FNAPlatform.SetWindowTitle;
IsScreenKeyboardShown = SDL2_FNAPlatform.IsScreenKeyboardShown;
RegisterGame = SDL2_FNAPlatform.RegisterGame;
UnregisterGame = SDL2_FNAPlatform.UnregisterGame;
PollEvents = SDL2_FNAPlatform.PollEvents;
GetGraphicsAdapters = SDL2_FNAPlatform.GetGraphicsAdapters;
GetCurrentDisplayMode = SDL2_FNAPlatform.GetCurrentDisplayMode;
GetKeyFromScancode = SDL2_FNAPlatform.GetKeyFromScancode;
IsTextInputActive = SDL2_FNAPlatform.IsTextInputActive;
StartTextInput = SDL2_FNAPlatform.StartTextInput;
StopTextInput = SDL2_FNAPlatform.StopTextInput;
SetTextInputRectangle = SDL2_FNAPlatform.SetTextInputRectangle;
GetMouseState = SDL2_FNAPlatform.GetMouseState;
SetMousePosition = SDL2.SDL.SDL_WarpMouseInWindow;
OnIsMouseVisibleChanged = SDL2_FNAPlatform.OnIsMouseVisibleChanged;
GetRelativeMouseMode = SDL2_FNAPlatform.GetRelativeMouseMode;
SetRelativeMouseMode = SDL2_FNAPlatform.SetRelativeMouseMode;
GetGamePadCapabilities = SDL2_FNAPlatform.GetGamePadCapabilities;
GetGamePadState = SDL2_FNAPlatform.GetGamePadState;
SetGamePadVibration = SDL2_FNAPlatform.SetGamePadVibration;
SetGamePadTriggerVibration = SDL2_FNAPlatform.SetGamePadTriggerVibration;
GetGamePadGUID = SDL2_FNAPlatform.GetGamePadGUID;
SetGamePadLightBar = SDL2_FNAPlatform.SetGamePadLightBar;
GetGamePadGyro = SDL2_FNAPlatform.GetGamePadGyro;
GetGamePadAccelerometer = SDL2_FNAPlatform.GetGamePadAccelerometer;
GetStorageRoot = SDL2_FNAPlatform.GetStorageRoot;
GetDriveInfo = SDL2_FNAPlatform.GetDriveInfo;
ReadFileToPointer = SDL2_FNAPlatform.ReadToPointer;
FreeFilePointer = SDL2_FNAPlatform.FreeFilePointer;
ShowRuntimeError = SDL2_FNAPlatform.ShowRuntimeError;
GetMicrophones = SDL2_FNAPlatform.GetMicrophones;
GetMicrophoneSamples = SDL2_FNAPlatform.GetMicrophoneSamples;
GetMicrophoneQueuedBytes = SDL2_FNAPlatform.GetMicrophoneQueuedBytes;
StartMicrophone = SDL2_FNAPlatform.StartMicrophone;
StopMicrophone = SDL2_FNAPlatform.StopMicrophone;
GetTouchCapabilities = SDL2_FNAPlatform.GetTouchCapabilities;
UpdateTouchPanelState = SDL2_FNAPlatform.UpdateTouchPanelState;
GetNumTouchFingers = SDL2_FNAPlatform.GetNumTouchFingers;
SupportsOrientationChanges = SDL2_FNAPlatform.SupportsOrientationChanges;
NeedsPlatformMainLoop = SDL2_FNAPlatform.NeedsPlatformMainLoop;
RunPlatformMainLoop = SDL2_FNAPlatform.RunPlatformMainLoop;
}

FNALoggerEXT.Initialize();

AppDomain.CurrentDomain.ProcessExit += SDL2_FNAPlatform.ProgramExit;
TitleLocation = SDL2_FNAPlatform.ProgramInit(args);
if (useSDL3)
{
AppDomain.CurrentDomain.ProcessExit += SDL3_FNAPlatform.ProgramExit;
TitleLocation = SDL3_FNAPlatform.ProgramInit(args);
}
else
{
AppDomain.CurrentDomain.ProcessExit += SDL2_FNAPlatform.ProgramExit;
TitleLocation = SDL2_FNAPlatform.ProgramInit(args);
}

/* Do this AFTER ProgramInit so the platform library
* has a chance to load first!
Expand Down Expand Up @@ -264,16 +337,16 @@ ref bool textInputSuppress
public delegate Keys GetKeyFromScancodeFunc(Keys scancode);
public static readonly GetKeyFromScancodeFunc GetKeyFromScancode;

public delegate bool IsTextInputActiveFunc();
public delegate bool IsTextInputActiveFunc(IntPtr window);
public static readonly IsTextInputActiveFunc IsTextInputActive;

public delegate void StartTextInputFunc();
public delegate void StartTextInputFunc(IntPtr window);
public static readonly StartTextInputFunc StartTextInput;

public delegate void StopTextInputFunc();
public delegate void StopTextInputFunc(IntPtr window);
public static readonly StopTextInputFunc StopTextInput;

public delegate void SetTextInputRectangleFunc(Rectangle rectangle);
public delegate void SetTextInputRectangleFunc(IntPtr window, Rectangle rectangle);
public static readonly SetTextInputRectangleFunc SetTextInputRectangle;

public delegate void GetMouseStateFunc(
Expand All @@ -298,10 +371,10 @@ int y
public delegate void OnIsMouseVisibleChangedFunc(bool visible);
public static readonly OnIsMouseVisibleChangedFunc OnIsMouseVisibleChanged;

public delegate bool GetRelativeMouseModeFunc();
public delegate bool GetRelativeMouseModeFunc(IntPtr window);
public static readonly GetRelativeMouseModeFunc GetRelativeMouseMode;

public delegate void SetRelativeMouseModeFunc(bool enable);
public delegate void SetRelativeMouseModeFunc(IntPtr window, bool enable);
public static readonly SetRelativeMouseModeFunc SetRelativeMouseMode;

public delegate GamePadCapabilities GetGamePadCapabilitiesFunc(int index);
Expand Down
4 changes: 2 additions & 2 deletions src/FNAPlatform/README
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ platforms as well as multiple backends for each platform all simultaneously.

That said, if you are adding a new platform, it is extremely likely that you
will NOT be touching anything in FNA itself! The expectation is that 100% of
your work will end up in SDL2 and FNA3D. You _might_ add some OSVersion checks
to SDL2_FNAPlatform.cs, but that should be the only change in the managed code.
your work will end up in SDL3 and FNA3D. You _might_ add some OSVersion checks
to SDL3_FNAPlatform.cs, but that should be the only change in the managed code.

If for some reason you REALLY need a new FNAPlatform, the new platforms will add
code exclusively to this folder. Some interfaces may need to change for new
Expand Down
Loading

0 comments on commit ed3a16e

Please sign in to comment.