using System; using System.Runtime.InteropServices; namespace UnityEngine.XR.OpenXR { /// /// OpenXR Runtime access /// public static class OpenXRRuntime { /// /// Name of the current runtime. /// public static string name => Internal_GetRuntimeName(out var runtimeNamePtr) ? Marshal.PtrToStringAnsi(runtimeNamePtr) : ""; /// /// Version of the current runtime. /// public static string version => Internal_GetRuntimeVersion(out var major, out var minor, out var patch) ? $"{major}.{minor}.{patch}" : ""; /// /// Version of the current runtime API. /// public static string apiVersion => Internal_GetAPIVersion(out var major, out var minor, out var patch) ? $"{major}.{minor}.{patch}" : ""; /// /// Version of the current runtime plug-in. /// public static string pluginVersion => Internal_GetPluginVersion(out var pluginVersionPtr) ? Marshal.PtrToStringAnsi(pluginVersionPtr) : ""; /// /// Check if current runtime API version is greater than 1.1 /// internal static bool isRuntimeAPIVersionGreaterThan1_1() { if (Internal_GetAPIVersion(out var major, out var minor, out var patch)) { if (major >= 1 && minor >= 1) return true; } return false; } /// /// Describes whether the OpenXR Extension with the given name is enabled. /// /// Name of the extension /// True if the extension matching the given name is enabled, false otherwise public static bool IsExtensionEnabled(string extensionName) => Internal_IsExtensionEnabled(extensionName); /// /// Returns the version number of the given extension. /// /// Name of the extension /// Version number of given extension, or zero if the extension was not found public static uint GetExtensionVersion(string extensionName) => Internal_GetExtensionVersion(extensionName); /// /// Returns the list of names of extensions enabled within the OpenXR runtime. /// /// Array of extension names or an empty array if no extensions are enabled. public static string[] GetEnabledExtensions() { var extensions = new string[Internal_GetEnabledExtensionCount()]; for (var i = 0; i < extensions.Length; i++) { Internal_GetEnabledExtensionName((uint)i, out var extensionName); extensions[i] = $"{extensionName}"; } return extensions; } /// /// Returns the list of names of extensions available within the OpenXR runtime. /// /// Array of extension names or an empty array if no extensions are available. public static string[] GetAvailableExtensions() { var extensions = new string[Internal_GetAvailableExtensionCount()]; for (var i = 0; i < extensions.Length; i++) { Internal_GetAvailableExtensionName((uint)i, out var extensionName); extensions[i] = $"{extensionName}"; } return extensions; } /// /// This event is raised when OpenXR wants the application to quit. /// /// Note that this event is raised before Application.wantsToQuit is raised and is generally /// raised because the Runtime has requested the application quit. /// /// Return true and the quit process will continue. Return false and the quit process will cancel. /// public static event Func wantsToQuit; /// /// This event is raised when OpenXR Runtime wants to restart the XR loader by shutting down /// the loader and reinitializing it. /// /// Return true and the restart process will continue. Return false and the XR loader will be /// unloaded and the quit process will begin. /// public static event Func wantsToRestart; /// /// This bool controls whether or not to retry initialization if the runtime reports a /// FORM_FACTOR_UNAVAILABLE error during initialization. /// public static bool retryInitializationOnFormFactorErrors { get { return Internal_GetSoftRestartLoopAtInitialization(); } set { Internal_SetSoftRestartLoopAtInitialization(value); } } /// /// Invokes the given event function and returns true if all invocations return true /// /// Event function /// True if all event invocations return true private static bool InvokeEvent(Func func) { if (func == null) return true; foreach (Func invocation in func.GetInvocationList()) { try { if (!invocation()) return false; } catch (Exception exception) { Debug.LogException(exception); } } return true; } #if UNITY_INCLUDE_TESTS internal static void ClearEvents() { if (wantsToQuit != null) foreach (Func f in wantsToQuit.GetInvocationList()) wantsToQuit -= f; if (wantsToRestart != null) foreach (Func f in wantsToRestart.GetInvocationList()) wantsToRestart -= f; wantsToQuit = null; wantsToRestart = null; } #endif internal static bool ShouldQuit() => InvokeEvent(wantsToQuit); internal static bool ShouldRestart() => InvokeEvent(wantsToRestart); private const string LibraryName = "UnityOpenXR"; [DllImport(LibraryName, EntryPoint = "NativeConfig_GetRuntimeName")] [return: MarshalAs(UnmanagedType.U1)] private static extern bool Internal_GetRuntimeName(out IntPtr runtimeNamePtr); [DllImport(LibraryName, EntryPoint = "NativeConfig_GetRuntimeVersion")] [return: MarshalAs(UnmanagedType.U1)] private static extern bool Internal_GetRuntimeVersion(out ushort major, out ushort minor, out uint patch); [DllImport(LibraryName, EntryPoint = "NativeConfig_GetAPIVersion")] [return: MarshalAs(UnmanagedType.U1)] private static extern bool Internal_GetAPIVersion(out ushort major, out ushort minor, out uint patch); [DllImport(LibraryName, EntryPoint = "NativeConfig_GetPluginVersion")] [return: MarshalAs(UnmanagedType.U1)] private static extern bool Internal_GetPluginVersion(out IntPtr pluginVersionPtr); [DllImport(LibraryName, EntryPoint = "unity_ext_IsExtensionEnabled")] [return: MarshalAs(UnmanagedType.U1)] private static extern bool Internal_IsExtensionEnabled(string extensionName); [DllImport(LibraryName, EntryPoint = "unity_ext_GetExtensionVersion")] private static extern uint Internal_GetExtensionVersion(string extensionName); [DllImport(LibraryName, EntryPoint = "unity_ext_GetEnabledExtensionCount")] private static extern uint Internal_GetEnabledExtensionCount(); [DllImport(LibraryName, EntryPoint = "unity_ext_GetEnabledExtensionName", CharSet = CharSet.Ansi)] [return: MarshalAs(UnmanagedType.U1)] private static extern bool Internal_GetEnabledExtensionNamePtr(uint index, out IntPtr outName); [DllImport(LibraryName, EntryPoint = "session_SetSoftRestartLoopAtInitialization")] private static extern void Internal_SetSoftRestartLoopAtInitialization([MarshalAs(UnmanagedType.I1)] bool value); [DllImport(LibraryName, EntryPoint = "session_GetSoftRestartLoopAtInitialization")] [return: MarshalAs(UnmanagedType.U1)] private static extern bool Internal_GetSoftRestartLoopAtInitialization(); private static bool Internal_GetEnabledExtensionName(uint index, out string extensionName) { if (!Internal_GetEnabledExtensionNamePtr(index, out var extensionNamePtr)) { extensionName = ""; return false; } extensionName = Marshal.PtrToStringAnsi(extensionNamePtr); return true; } [DllImport(LibraryName, EntryPoint = "unity_ext_GetAvailableExtensionCount")] private static extern uint Internal_GetAvailableExtensionCount(); [DllImport(LibraryName, EntryPoint = "unity_ext_GetAvailableExtensionName", CharSet = CharSet.Ansi)] [return: MarshalAs(UnmanagedType.U1)] private static extern bool Internal_GetAvailableExtensionNamePtr(uint index, out IntPtr extensionName); private static bool Internal_GetAvailableExtensionName(uint index, out string extensionName) { if (!Internal_GetAvailableExtensionNamePtr(index, out var extensionNamePtr)) { extensionName = ""; return false; } extensionName = Marshal.PtrToStringAnsi(extensionNamePtr); return true; } [DllImport(LibraryName, EntryPoint = "session_GetLastError", CharSet = CharSet.Ansi)] [return: MarshalAs(UnmanagedType.U1)] private static extern bool Internal_GetLastError(out IntPtr error); /// /// Returns the last error message that was issued in the native code /// /// Last error message /// True if there was an error message, false if not internal static bool GetLastError(out string error) { if (!Internal_GetLastError(out var errorPtr)) { error = ""; return false; } error = Marshal.PtrToStringAnsi(errorPtr); return true; } /// /// Logs the last error message if there is one to the console /// internal static void LogLastError() { if (GetLastError(out var error)) { Debug.LogError(error); } } } }