diff --git a/SampleGame.iOS/AppDelegate.cs b/SampleGame.iOS/AppDelegate.cs new file mode 100644 index 0000000000..9712408a08 --- /dev/null +++ b/SampleGame.iOS/AppDelegate.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Foundation; +using osu.Framework; +using osu.Framework.iOS; + +namespace SampleGame.iOS +{ + [Register("AppDelegate")] + public class AppDelegate : GameApplicationDelegate + { + protected override Game CreateGame() => new SampleGameGame(); + } +} diff --git a/SampleGame.iOS/Application.cs b/SampleGame.iOS/Application.cs deleted file mode 100644 index c27e589f7a..0000000000 --- a/SampleGame.iOS/Application.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.iOS; - -namespace SampleGame.iOS -{ - public static class Application - { - // This is the main entry point of the application. - public static void Main(string[] args) => GameApplication.Main(new SampleGameGame()); - } -} diff --git a/SampleGame.iOS/Program.cs b/SampleGame.iOS/Program.cs new file mode 100644 index 0000000000..b5dbf419e3 --- /dev/null +++ b/SampleGame.iOS/Program.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using UIKit; + +namespace SampleGame.iOS +{ + public static class Program + { + public static void Main(string[] args) + { + UIApplication.Main(args, null, typeof(AppDelegate)); + } + } +} diff --git a/osu.Framework.Templates/templates/template-empty/TemplateGame.iOS/AppDelegate.cs b/osu.Framework.Templates/templates/template-empty/TemplateGame.iOS/AppDelegate.cs new file mode 100644 index 0000000000..89ed9edbbc --- /dev/null +++ b/osu.Framework.Templates/templates/template-empty/TemplateGame.iOS/AppDelegate.cs @@ -0,0 +1,12 @@ +using osu.Framework.iOS; +using TemplateGame.Game; + +namespace TemplateGame.iOS +{ + /// + public class AppDelegate : GameApplicationDelegate + { + /// + protected override osu.Framework.Game CreateGame() => new TemplateGameGame(); + } +} diff --git a/osu.Framework.Templates/templates/template-empty/TemplateGame.iOS/Application.cs b/osu.Framework.Templates/templates/template-empty/TemplateGame.iOS/Application.cs deleted file mode 100644 index 00995cb86f..0000000000 --- a/osu.Framework.Templates/templates/template-empty/TemplateGame.iOS/Application.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.iOS; -using TemplateGame.Game; - -namespace TemplateGame.iOS -{ - public static class Application - { - public static void Main(string[] args) => GameApplication.Main(new TemplateGameGame()); - } -} diff --git a/osu.Framework.Templates/templates/template-empty/TemplateGame.iOS/Program.cs b/osu.Framework.Templates/templates/template-empty/TemplateGame.iOS/Program.cs new file mode 100644 index 0000000000..66bdcb5cec --- /dev/null +++ b/osu.Framework.Templates/templates/template-empty/TemplateGame.iOS/Program.cs @@ -0,0 +1,16 @@ +using UIKit; + +namespace TemplateGame.iOS +{ + /// + /// + public static class Program + { + /// + /// + public static void Main(string[] args) + { + UIApplication.Main(args, null, typeof(AppDelegate)); + } + } +} diff --git a/osu.Framework.Templates/templates/template-flappy/FlappyDon.iOS/AppDelegate.cs b/osu.Framework.Templates/templates/template-flappy/FlappyDon.iOS/AppDelegate.cs new file mode 100644 index 0000000000..acd6f60330 --- /dev/null +++ b/osu.Framework.Templates/templates/template-flappy/FlappyDon.iOS/AppDelegate.cs @@ -0,0 +1,12 @@ +using FlappyDon.Game; +using osu.Framework.iOS; + +namespace FlappyDon.iOS +{ + /// + public class AppDelegate : GameApplicationDelegate + { + /// + protected override osu.Framework.Game CreateGame() => new FlappyDonGame(); + } +} diff --git a/osu.Framework.Templates/templates/template-flappy/FlappyDon.iOS/Application.cs b/osu.Framework.Templates/templates/template-flappy/FlappyDon.iOS/Application.cs deleted file mode 100644 index 46d6870a8b..0000000000 --- a/osu.Framework.Templates/templates/template-flappy/FlappyDon.iOS/Application.cs +++ /dev/null @@ -1,10 +0,0 @@ -using FlappyDon.Game; -using osu.Framework.iOS; - -namespace FlappyDon.iOS -{ - public static class Application - { - public static void Main(string[] args) => GameApplication.Main(new FlappyDonGame()); - } -} diff --git a/osu.Framework.Templates/templates/template-flappy/FlappyDon.iOS/Program.cs b/osu.Framework.Templates/templates/template-flappy/FlappyDon.iOS/Program.cs new file mode 100644 index 0000000000..b7e7de184f --- /dev/null +++ b/osu.Framework.Templates/templates/template-flappy/FlappyDon.iOS/Program.cs @@ -0,0 +1,16 @@ +using UIKit; + +namespace FlappyDon.iOS +{ + /// + /// + public static class Program + { + /// + /// + public static void Main(string[] args) + { + UIApplication.Main(args, null, typeof(AppDelegate)); + } + } +} diff --git a/osu.Framework.Tests.iOS/AppDelegate.cs b/osu.Framework.Tests.iOS/AppDelegate.cs new file mode 100644 index 0000000000..ada14af271 --- /dev/null +++ b/osu.Framework.Tests.iOS/AppDelegate.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Foundation; +using osu.Framework.iOS; + +namespace osu.Framework.Tests +{ + [Register("AppDelegate")] + public class AppDelegate : GameApplicationDelegate + { + protected override Game CreateGame() => new VisualTestGame(); + } +} diff --git a/osu.Framework.Tests.iOS/Application.cs b/osu.Framework.Tests.iOS/Application.cs deleted file mode 100644 index 7305fe41e6..0000000000 --- a/osu.Framework.Tests.iOS/Application.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.iOS; - -namespace osu.Framework.Tests -{ - public static class Application - { - // This is the main entry point of the application. - public static void Main(string[] args) => GameApplication.Main(new VisualTestGame()); - } -} diff --git a/osu.Framework.Tests.iOS/Program.cs b/osu.Framework.Tests.iOS/Program.cs new file mode 100644 index 0000000000..717b5e161f --- /dev/null +++ b/osu.Framework.Tests.iOS/Program.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using UIKit; + +namespace osu.Framework.Tests +{ + public static class Program + { + public static void Main(string[] args) + { + UIApplication.Main(args, null, typeof(AppDelegate)); + } + } +} diff --git a/osu.Framework.iOS/GameApplication.cs b/osu.Framework.iOS/GameApplicationDelegate.cs similarity index 55% rename from osu.Framework.iOS/GameApplication.cs rename to osu.Framework.iOS/GameApplicationDelegate.cs index fa63f297e1..a223b6327e 100644 --- a/osu.Framework.iOS/GameApplication.cs +++ b/osu.Framework.iOS/GameApplicationDelegate.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using AVFoundation; using Foundation; @@ -10,40 +9,59 @@ using ManagedBass.Fx; using ManagedBass.Mix; using SDL; +using UIKit; +using static SDL.SDL3; namespace osu.Framework.iOS { - public static class GameApplication + /// + /// Base implementation for osu!framework applications. + /// + public abstract class GameApplicationDelegate : UIApplicationDelegate { - private const string output_volume = @"outputVolume"; + internal event Action? DragDrop; - private static IOSGameHost host = null!; - private static Game game = null!; + private const string output_volume = "outputVolume"; private static readonly OutputVolumeObserver output_volume_observer = new OutputVolumeObserver(); - public static unsafe void Main(Game target) - { - NativeLibrary.SetDllImportResolver(typeof(Bass).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/bass.framework/bass", assembly, path)); - NativeLibrary.SetDllImportResolver(typeof(BassFx).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/bass_fx.framework/bass_fx", assembly, path)); - NativeLibrary.SetDllImportResolver(typeof(BassMix).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/bassmix.framework/bassmix", assembly, path)); - NativeLibrary.SetDllImportResolver(typeof(SDL3).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/SDL3.framework/SDL3", assembly, path)); + private IOSGameHost host = null!; - game = target; + public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) + { + mapLibraryNames(); - SDL3.SDL_RunApp(0, null, &main, IntPtr.Zero); - } + SDL_SetMainReady(); + SDL_SetiOSEventPump(true); - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] - private static unsafe int main(int argc, byte** argv) - { var audioSession = AVAudioSession.SharedInstance(); audioSession.AddObserver(output_volume_observer, output_volume, NSKeyValueObservingOptions.New, 0); host = new IOSGameHost(); - host.Run(game); + host.Run(CreateGame()); + return true; + } - return 0; + public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation) + { + // copied verbatim from SDL: https://github.com/libsdl-org/SDL/blob/d252a8fe126b998bd1b0f4e4cf52312cd11de378/src/video/uikit/SDL_uikitappdelegate.m#L508-L535 + // the hope is that the SDL app delegate class does not have such handling exist there, but Apple does not provide a corresponding notification to make that possible. + NSUrl? fileUrl = url.FilePathUrl; + DragDrop?.Invoke(fileUrl != null ? fileUrl.Path! : url.AbsoluteString!); + return true; + } + + /// + /// Creates the class to launch. + /// + protected abstract Game CreateGame(); + + private static void mapLibraryNames() + { + NativeLibrary.SetDllImportResolver(typeof(Bass).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/bass.framework/bass", assembly, path)); + NativeLibrary.SetDllImportResolver(typeof(BassFx).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/bass_fx.framework/bass_fx", assembly, path)); + NativeLibrary.SetDllImportResolver(typeof(BassMix).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/bassmix.framework/bassmix", assembly, path)); + NativeLibrary.SetDllImportResolver(typeof(SDL3).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/SDL3.framework/SDL3", assembly, path)); } private class OutputVolumeObserver : NSObject diff --git a/osu.Framework.iOS/IOSWindow.cs b/osu.Framework.iOS/IOSWindow.cs index 66bc29697f..012092f224 100644 --- a/osu.Framework.iOS/IOSWindow.cs +++ b/osu.Framework.iOS/IOSWindow.cs @@ -47,6 +47,9 @@ public override void Create() uiWindow = Runtime.GetNSObject(WindowHandle); updateSafeArea(); + + var appDelegate = (GameApplicationDelegate)UIApplication.SharedApplication.Delegate; + appDelegate.DragDrop += TriggerDragDrop; } protected override unsafe void RunMainLoop() @@ -59,8 +62,6 @@ protected override unsafe void RunMainLoop() // iOS may be a good forward direction if this ever comes up, as a user may see a potentially higher // frame rate with multi-threaded mode turned on, but it is going to give them worse input latency // and higher power usage. - - SDL_SetiOSEventPump(false); SDL_SetiOSAnimationCallback(SDLWindowHandle, 1, &runFrame, ObjectHandle.Handle); } diff --git a/osu.Framework/Platform/SDL3/SDL3Window.cs b/osu.Framework/Platform/SDL3/SDL3Window.cs index 33e509828f..1932911681 100644 --- a/osu.Framework/Platform/SDL3/SDL3Window.cs +++ b/osu.Framework/Platform/SDL3/SDL3Window.cs @@ -666,6 +666,8 @@ internal virtual void SetIconFromGroup(IconGroup iconGroup) /// public event Action? DragDrop; + protected void TriggerDragDrop(string filename) => DragDrop?.Invoke(filename); + #endregion public void Dispose() diff --git a/osu.Framework/Platform/SDL3/SDL3Window_Input.cs b/osu.Framework/Platform/SDL3/SDL3Window_Input.cs index 7709f531eb..4153f78e2e 100644 --- a/osu.Framework/Platform/SDL3/SDL3Window_Input.cs +++ b/osu.Framework/Platform/SDL3/SDL3Window_Input.cs @@ -217,7 +217,7 @@ private void handleDropEvent(SDL_DropEvent evtDrop) case SDL_EventType.SDL_EVENT_DROP_FILE: string? str = evtDrop.GetData(); if (str != null) - DragDrop?.Invoke(str); + TriggerDragDrop(str); break; }