| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- //
- // Support for bubbling up to C# the virtual methods calls for Setup, Start and Stop in Application
- //
- // This is done by using an ApplicationProxy in C++ that bubbles up
- //
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Reflection;
- using System.Runtime.InteropServices;
- using System.Threading.Tasks;
- using Urho.IO;
- using Urho.Audio;
- using Urho.Resources;
- using Urho.Actions;
- using Urho.Gui;
- namespace Urho {
-
- public partial class Application {
- // references needed to prevent GC from collecting callbacks passed to native code
- static ActionIntPtr setupCallback;
- static ActionIntPtr startCallback;
- static ActionIntPtr stopCallback;
- static TaskCompletionSource<bool> exitTask;
- static int renderThreadId = -1;
- static readonly List<Action> actionsToDipatch = new List<Action>();
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- public delegate void ActionIntPtr (IntPtr value);
- [DllImport (Consts.NativeImport, CallingConvention=CallingConvention.Cdecl)]
- static extern IntPtr ApplicationProxy_ApplicationProxy (IntPtr contextHandle, ActionIntPtr setup, ActionIntPtr start, ActionIntPtr stop, string args, IntPtr externalWindow);
- static Application current;
- public static Application Current
- {
- get
- {
- if (current == null)
- throw new InvalidOperationException("The application is not configured yet");
- return current;
- }
- private set { current = value; }
- }
- public static bool HasCurrent => current != null;
- static Context currentContext;
- public static Context CurrentContext
- {
- get
- {
- if (currentContext == null)
- throw new InvalidOperationException("The application is not configured yet");
- return currentContext;
- }
- private set { currentContext = value; }
- }
- public Application() : this(new Context(), null) {}
- public Application(ApplicationOptions options) : this(new Context(), options) {}
- /// <summary>
- /// Supports the simple style with callbacks
- /// </summary>
- Application (Context context, ApplicationOptions options = null) : base (UrhoObjectFlag.Empty)
- {
- if (context == null)
- throw new ArgumentNullException (nameof(context));
- if (context.Refs() < 1)
- context.AddRef();
- //keep references to callbacks (supposed to be passed to native code) as long as the App is alive
- setupCallback = ProxySetup;
- startCallback = ProxyStart;
- stopCallback = ProxyStop;
- Options = options ?? new ApplicationOptions(assetsFolder: null);
- handle = ApplicationProxy_ApplicationProxy (context.Handle, setupCallback, startCallback, stopCallback, Options.ToString(), Options.ExternalWindow);
- Runtime.RegisterObject (this);
- }
- public bool IsClosed { get; private set; }
- public IntPtr Handle => handle;
- public IUrhoSurface UrhoSurface { get; internal set; }
- /// <summary>
- /// Application options
- /// </summary>
- public ApplicationOptions Options { get; private set; }
- /// <summary>
- /// Frame update event
- /// </summary>
- public event Action<UpdateEventArgs> Update;
-
- /// <summary>
- /// Invoke actions in the Main Thread (the next Update call)
- /// </summary>
- public static void InvokeOnMain(Action action)
- {
- lock (actionsToDipatch)
- {
- actionsToDipatch.Add(action);
- }
- }
- /// <summary>
- /// Invoke actions in the Main Thread (the next Update call)
- /// </summary>
- public static Task InvokeOnMainAsync(Action action)
- {
- var tcs = new TaskCompletionSource<bool>();
- InvokeOnMain(() =>
- {
- action();
- tcs.TrySetResult(true);
- });
- return tcs.Task;
- }
- static Application GetApp(IntPtr h) => Runtime.LookupObject<Application>(h);
- void HandleUpdate(UpdateEventArgs args)
- {
- var timeStep = args.TimeStep;
- Update?.Invoke(args);
- ActionManager.Update(timeStep);
- OnUpdate(timeStep);
- if (actionsToDipatch.Count > 0)
- {
- lock (actionsToDipatch)
- {
- foreach (var action in actionsToDipatch)
- action();
- actionsToDipatch.Clear();
- }
- }
- }
- [MonoPInvokeCallback(typeof(ActionIntPtr))]
- static void ProxySetup (IntPtr h)
- {
- Runtime.Setup();
- Current = GetApp(h);
- CurrentContext = Current.Context;
- Current.Setup ();
- }
- [MonoPInvokeCallback(typeof(ActionIntPtr))]
- static void ProxyStart (IntPtr h)
- {
- Runtime.Start();
- Current = GetApp(h);
- Current.SubscribeToAppEvents();
- Current.Start();
- Started?.Invoke();
- #if ANDROID
- renderThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
- #endif
- }
- [MonoPInvokeCallback(typeof(ActionIntPtr))]
- static void ProxyStop (IntPtr h)
- {
- LogSharp.Debug("ProxyStop");
- UrhoPlatformInitializer.Initialized = false;
- var context = Current.Context;
- var app = GetApp (h);
- app.IsClosed = true;
- app.UnsubscribeFromAppEvents();
- app.Stop ();
- LogSharp.Debug("ProxyStop: Runtime.Cleanup");
- Runtime.Cleanup();
- LogSharp.Debug("ProxyStop: Releasing context");
- #if ANDROID
- if (context.Refs() > 0)
- context.ReleaseRef();
- #endif
- LogSharp.Debug("ProxyStop: Disposing context");
- context.Dispose();
- Current = null;
- Stoped?.Invoke();
- LogSharp.Debug("ProxyStop: end");
- exitTask?.TrySetResult(true);
- }
- Subscription updateSubscription = null;
- void SubscribeToAppEvents()
- {
- updateSubscription = Engine.SubscribeToUpdate(HandleUpdate);
- }
- void UnsubscribeFromAppEvents()
- {
- updateSubscription?.Unsubscribe();
- }
- internal static async Task StopCurrent()
- {
- if (current == null)
- return;
- #if WINDOWS_UWP
- UWP.UrhoSurface.StopRendering().Wait();
- #endif
- #if ANDROID
- if (Current.UrhoSurface?.IsAlive == false)
- {
- Current.Engine.Exit();
- }
- else if (System.Threading.Thread.CurrentThread.ManagedThreadId != renderThreadId)
- {
- exitTask = new TaskCompletionSource<bool>();
- InvokeOnMainAsync(() => Current.Engine.Exit()).Wait();
- Current.UrhoSurface?.Remove();
- Current.UrhoSurface = null;
- await exitTask.Task;//.Wait();
- }
- else
- {
- Current.Engine.Exit();
- new Android.OS.Handler(Android.OS.Looper.MainLooper).PostAtFrontOfQueue(() => {
- Current.UrhoSurface?.Remove();
- Current.UrhoSurface = null;
- });
- }
- #else
- Current.Engine.Exit ();
- #endif
- #if IOS || WINDOWS_UWP
- ProxyStop(Current.Handle);
- #endif
- }
- public bool IsExiting => Runtime.IsClosing || Engine.Exiting;
- public Task Exit()
- {
- if (IsClosed)
- return Task.FromResult<object>(null);
- IsClosed = true;
- return StopCurrent();
- }
- protected override bool AllowNativeDelete => false;
- protected virtual void Setup () {}
- public static event Action Started;
- protected virtual void Start () {}
- public static event Action Stoped;
- protected virtual void Stop () {}
- protected virtual void OnUpdate(float timeStep) { }
- internal ActionManager ActionManager { get; } = new ActionManager();
- [DllImport(Consts.NativeImport, EntryPoint = "Urho_GetPlatform", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr GetPlatform();
- static Platforms platform;
- public static Platforms Platform {
- get
- {
- Runtime.Validate(typeof(Application));
- if (platform == Platforms.Unknown)
- platform = PlatformsMap.FromString(Marshal.PtrToStringAnsi(GetPlatform()));
- return platform;
- }
- }
- //
- // GetSubsystem helpers
- //
- ResourceCache resourceCache;
- public ResourceCache ResourceCache {
- get
- {
- Runtime.Validate(typeof(Application));
- if (resourceCache == null)
- resourceCache = new ResourceCache (UrhoObject_GetSubsystem (handle, ResourceCache.TypeStatic.Code));
- return resourceCache;
- }
- }
- UrhoConsole console;
- public UrhoConsole Console {
- get
- {
- Runtime.Validate(typeof(Application));
- if (console == null)
- console = new UrhoConsole (UrhoObject_GetSubsystem (handle, UrhoConsole.TypeStatic.Code));
- return console;
- }
- }
-
- Urho.Network.Network network;
- public Urho.Network.Network Network {
- get
- {
- Runtime.Validate(typeof(Application));
- if (network == null)
- network = new Urho.Network.Network (UrhoObject_GetSubsystem (handle, Urho.Network.Network.TypeStatic.Code));
- return network;
- }
- }
-
- Time time;
- public Time Time {
- get
- {
- Runtime.Validate(typeof(Application));
- if (time == null)
- time = new Time (UrhoObject_GetSubsystem (handle, Time.TypeStatic.Code));
- return time;
- }
- }
-
- WorkQueue workQueue;
- public WorkQueue WorkQueue {
- get
- {
- Runtime.Validate(typeof(Application));
- if (workQueue == null)
- workQueue = new WorkQueue (UrhoObject_GetSubsystem (handle, WorkQueue.TypeStatic.Code));
- return workQueue;
- }
- }
-
- Profiler profiler;
- public Profiler Profiler {
- get
- {
- Runtime.Validate(typeof(Application));
- if (profiler == null)
- profiler = new Profiler (UrhoObject_GetSubsystem (handle, Profiler.TypeStatic.Code));
- return profiler;
- }
- }
-
- FileSystem fileSystem;
- public FileSystem FileSystem {
- get
- {
- Runtime.Validate(typeof(Application));
- if (fileSystem == null)
- fileSystem = new FileSystem (UrhoObject_GetSubsystem (handle, FileSystem.TypeStatic.Code));
- return fileSystem;
- }
- }
-
- Log log;
- public Log Log {
- get
- {
- Runtime.Validate(typeof(Application));
- if (log == null)
- log = new Log (UrhoObject_GetSubsystem (handle, Log.TypeStatic.Code));
- return log;
- }
- }
-
- Input input;
- public Input Input {
- get
- {
- Runtime.Validate(typeof(Application));
- if (input == null)
- input = new Input (UrhoObject_GetSubsystem (handle, Input.TypeStatic.Code));
- return input;
- }
- }
-
- Urho.Audio.Audio audio;
- public Urho.Audio.Audio Audio {
- get
- {
- Runtime.Validate(typeof(Application));
- if (audio == null)
- audio = new Audio.Audio (UrhoObject_GetSubsystem (handle, Urho.Audio.Audio.TypeStatic.Code));
- return audio;
- }
- }
-
- UI uI;
- public UI UI {
- get
- {
- Runtime.Validate(typeof(Application));
- if (uI == null)
- uI = new UI (UrhoObject_GetSubsystem (handle, UI.TypeStatic.Code));
- return uI;
- }
- }
-
- Graphics graphics;
- public Graphics Graphics {
- get
- {
- Runtime.Validate(typeof(Application));
- if (graphics == null)
- graphics = new Graphics (UrhoObject_GetSubsystem (handle, Graphics.TypeStatic.Code));
- return graphics;
- }
- }
-
- Renderer renderer;
- public Renderer Renderer {
- get
- {
- Runtime.Validate(typeof(Application));
- if (renderer == null)
- renderer = new Renderer (UrhoObject_GetSubsystem (handle, Renderer.TypeStatic.Code));
- return renderer;
- }
- }
- [DllImport (Consts.NativeImport, CallingConvention=CallingConvention.Cdecl)]
- extern static IntPtr Application_GetEngine (IntPtr handle);
- Engine engine;
- public Engine Engine {
- get
- {
- if (engine == null)
- engine = new Engine (Application_GetEngine (handle));
- return engine;
- }
- }
- public static T CreateInstance<T>(ApplicationOptions options = null) where T : Application
- {
- return (T)CreateInstance(typeof (T), options);
- }
- public static Application CreateInstance(Type applicationType, ApplicationOptions options = null)
- {
- var ctors = applicationType.GetTypeInfo().DeclaredConstructors.ToArray();
- var ctorWithOptions = ctors.FirstOrDefault(c => c.GetParameters().Length == 1 && c.GetParameters()[0].ParameterType == typeof (ApplicationOptions));
- if (ctorWithOptions != null)
- {
- return (Application) Activator.CreateInstance(applicationType, options);
- }
- var ctorDefault = ctors.FirstOrDefault(c => c.GetParameters().Length == 0);
- if (ctorDefault != null)
- {
- return (Application) Activator.CreateInstance(applicationType);
- }
- throw new InvalidOperationException($"{applicationType} doesn't have parameterless constructor.");
- }
- }
- public interface IUrhoSurface
- {
- void Remove();
- bool IsAlive { get; }
- }
- }
|