using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Unity.XR.CoreUtils.Editor; using UnityEngine; #if INPUT_SYSTEM_1_4_OR_NEWER using UnityEngine.InputSystem; #endif namespace UnityEditor.XR.Management { /// /// Unity Editor class which registers Project Validation rules for XR Plugin Management. /// These are global rules which apply no matter the plug-in provider enabled. /// static class XRPluginManagementProjectValidation { static readonly string k_Category = XRConstants.k_XRPluginManagement; const string k_RunInBackgroundMessage = "Run In Background must be enabled or Input System Background Behavior changed to avoid head-locked rendering of the main camera when focus is lost, such as when the universal menu/system shell is opened."; static readonly BuildTargetGroup[] s_BuildTargetGroups = ((BuildTargetGroup[])Enum.GetValues(typeof(BuildTargetGroup))).Distinct().ToArray(); static readonly List s_BuildValidationRules = new List { #if INPUT_SYSTEM_1_4_OR_NEWER new BuildValidationRule { IsRuleEnabled = () => GetPlayerSettingsProperty("runInBackground") != null && GetInputSettingsProperty("m_BackgroundBehavior") != null, Category = k_Category, Message = k_RunInBackgroundMessage, CheckPredicate = () => PlayerSettings.runInBackground && IsBackgroundBehaviorValid(), FixIt = () => { SetRunInBackground(true); if (!IsBackgroundBehaviorValid()) { // Don't modify the input settings asset if it is the default one that is not editable. // A user must click **Create settings asset** in the Input System Package project settings. // This protects against the Input System package changing the default background behavior setting value. if (IsInputSettingsEditable()) SetBackgroundBehavior(InputSettings.BackgroundBehavior.ResetAndDisableNonBackgroundDevices); else SettingsService.OpenProjectSettings("Project/Input System Package"); } }, FixItAutomatic = IsInputSettingsEditable() || IsBackgroundBehaviorValid(), HelpText = !IsInputSettingsEditable() && !IsBackgroundBehaviorValid() ? "Go to Edit > Project Settings > Input System Package and select Create settings asset to allow automatic fix." : null, FixItMessage = "Go to Edit > Project Settings > Player > Resolution and Presentation and enable Run In Background." + "\nGo to Edit > Project Settings > Input System Package and set Background Behavior to Reset And Disable Non Background Devices.", Error = false, }, #else new BuildValidationRule { IsRuleEnabled = () => GetPlayerSettingsProperty("runInBackground") != null, Category = k_Category, Message = k_RunInBackgroundMessage, CheckPredicate = () => PlayerSettings.runInBackground, FixIt = () => SetRunInBackground(true), FixItMessage = "Go to Edit > Project Settings > Player > Resolution and Presentation and enable Run In Background.", }, #endif }; [InitializeOnLoadMethod] static void RegisterProjectValidationRules() { foreach (var buildTargetGroup in s_BuildTargetGroups) { BuildValidator.AddRules(buildTargetGroup, s_BuildValidationRules); } } static SerializedProperty GetPlayerSettingsProperty(string propertyPath) { var serializedObject = (SerializedObject)typeof(PlayerSettings).GetMethod("GetSerializedObject", BindingFlags.NonPublic | BindingFlags.Static)?.Invoke(null, null); return serializedObject?.FindProperty(propertyPath); } static void SetRunInBackground(bool value) { // Setting PlayerSettings.runInBackground directly does not properly create undo/redo state, // so do so through the SerializedProperty API. var prop = GetPlayerSettingsProperty("runInBackground"); if (prop == null) return; prop.serializedObject.Update(); prop.boolValue = value; prop.serializedObject.ApplyModifiedProperties(); } #if INPUT_SYSTEM_1_4_OR_NEWER static SerializedProperty GetInputSettingsProperty(string propertyPath) { var serializedObject = new SerializedObject(InputSystem.settings); return serializedObject.FindProperty(propertyPath); } static void SetBackgroundBehavior(InputSettings.BackgroundBehavior backgroundBehavior) { // Setting InputSystem.settings.backgroundBehavior directly does not properly create undo/redo state, // so do so through the SerializedProperty API. var prop = GetInputSettingsProperty("m_BackgroundBehavior"); if (prop == null) return; prop.serializedObject.Update(); prop.intValue = (int)backgroundBehavior; prop.serializedObject.ApplyModifiedProperties(); } static bool IsInputSettingsEditable() { return (InputSystem.settings.hideFlags & HideFlags.HideAndDontSave) == 0; } static bool IsBackgroundBehaviorValid() { var backgroundBehavior = InputSystem.settings.backgroundBehavior; return backgroundBehavior == InputSettings.BackgroundBehavior.ResetAndDisableNonBackgroundDevices || backgroundBehavior == InputSettings.BackgroundBehavior.IgnoreFocus; } #endif } }