diff --git a/Framework/App.cs b/Framework/App.cs index 1dd4254..be9a34f 100644 --- a/Framework/App.cs +++ b/Framework/App.cs @@ -303,12 +303,14 @@ public static void Run(string applicationName, int width, int height, bool fulls MainThreadID = Thread.CurrentThread.ManagedThreadId; + // toggle fulscreen flag if (fullscreen) flags |= Platform.FosterFlags.Fullscreen; + // run the application + var name = Platform.ToUTF8(applicationName); title = applicationName; Name = applicationName; - var name = Platform.ToUTF8(applicationName); Platform.FosterStartup(new() { @@ -327,6 +329,9 @@ public static void Run(string applicationName, int width, int height, bool fulls UserPath = Platform.ParseUTF8(Platform.FosterGetUserPath()); Graphics.Initialize(); + // load default input mappings if they exist + Input.AddDefaultSdlGamepadMappings(AppContext.BaseDirectory); + // Clear Time Time.Frame = 0; Time.Duration = new(); diff --git a/Framework/Input/Input.cs b/Framework/Input/Input.cs index 33587cb..4e78f49 100644 --- a/Framework/Input/Input.cs +++ b/Framework/Input/Input.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Numerics; using System.Runtime.InteropServices; +using System.Text; namespace Foster.Framework; @@ -51,30 +52,42 @@ public static class Input /// public static float RepeatInterval = 0.03f; + /// + /// + /// public delegate void TextInputHandler(char value); + + /// + /// Called whenever keyboard text is typed + /// public static event TextInputHandler? OnTextEvent; - internal static readonly List> virtualButtons = new List>(); + /// + /// Holds references to all Virtual Buttons so they can be updated. + /// + internal static readonly List> virtualButtons = []; /// - /// Run at the beginning of a frame to step the input state. - /// After this, the Application will poll the platform for more inputs, which call back - /// to the various Input.On internal methods. + /// Loads 'gamecontrollerdb.txt' from a local file or falls back to the + /// default embedded SDL gamepad mappings /// - internal static void Step() + internal static void AddDefaultSdlGamepadMappings(string relativePath) { - LastState.Copy(State); - State.Copy(nextState); - nextState.Step(); + var path = Path.Combine(relativePath, "gamecontrollerdb.txt"); + if (File.Exists(path)) + AddSdlGamepadMappings(File.ReadAllLines(path)); + } - for (int i = virtualButtons.Count - 1; i >= 0; i--) - { - var button = virtualButtons[i]; - if (button.TryGetTarget(out var target)) - target.Update(); - else - virtualButtons.RemoveAt(i); - } + /// + /// Loads a list of SDL Gamepad Mappings. + /// You can find more information here: https://github.com/mdqinc/SDL_GameControllerDB + /// By default, any 'gamecontrollerdb.txt' found adjacent to the application at runtime + /// will be loaded automatically. + /// + public static void AddSdlGamepadMappings(string[] mappings) + { + foreach (var mapping in mappings) + Platform.SDL_GameControllerAddMapping(mapping); } /// @@ -94,6 +107,27 @@ public static string GetClipboardString() return Platform.ParseUTF8(ptr); } + /// + /// Run at the beginning of a frame to step the input state. + /// After this, the Application will poll the platform for more inputs, which call back + /// to the various Input.On internal methods. + /// + internal static void Step() + { + LastState.Copy(State); + State.Copy(nextState); + nextState.Step(); + + for (int i = virtualButtons.Count - 1; i >= 0; i--) + { + var button = virtualButtons[i]; + if (button.TryGetTarget(out var target)) + target.Update(); + else + virtualButtons.RemoveAt(i); + } + } + private static unsafe void OnText(IntPtr cstr) { byte* ptr = (byte*)cstr; diff --git a/Framework/Platform.cs b/Framework/Platform.cs index d48f6f1..4611432 100644 --- a/Framework/Platform.cs +++ b/Framework/Platform.cs @@ -313,6 +313,11 @@ static unsafe Platform() public static extern void FosterDraw(ref FosterDrawCommand command); [DllImport(DLL)] public static extern void FosterClear(ref FosterClearCommand command); + + // Non-Foster Calls: + + [DllImport(DLL, CharSet = CharSet.Ansi)] + public static extern int SDL_GameControllerAddMapping(string mappingString); // [DllImport(DLL)] // public static extern void emscripten_set_main_loop(IntPtr action, int fps, bool simulateInfiniteLoop); diff --git a/Platform/libs/lib64/libFosterPlatform.so b/Platform/libs/lib64/libFosterPlatform.so index e098793..1c0ca16 100755 Binary files a/Platform/libs/lib64/libFosterPlatform.so and b/Platform/libs/lib64/libFosterPlatform.so differ diff --git a/Platform/src/foster_platform.c b/Platform/src/foster_platform.c index 195aeb7..db2d732 100644 --- a/Platform/src/foster_platform.c +++ b/Platform/src/foster_platform.c @@ -86,7 +86,7 @@ void FosterStartup(FosterDesc desc) SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitorv2"); // use physical button layout, not labels - SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0"); + SDL_SetHintWithPriority(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0", SDL_HINT_OVERRIDE); // by default allow controller presses while unfocused, let game decide if it should handle them SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");