Decompiled source of ChangeDefaultDisplayMonitor v1.0.0

plugins/MonitorSwitcher/MonitorSwitcher.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using On.RoR2;
using On.RoR2.UI;
using R2API;
using RoR2;
using RoR2.ConVar;
using RoR2.UI;
using TMPro;
using UnityEngine;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MonitorSwitcher")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+73a3aebcfd7d8d588f675aa4448a9ae6d4d5b8c6")]
[assembly: AssemblyProduct("MonitorSwitcher")]
[assembly: AssemblyTitle("MonitorSwitcher")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MonitorSwitcher
{
	internal static class DisplayMonitorConVar
	{
		private sealed class MonitorConVarImpl : BaseConVar
		{
			public MonitorConVarImpl()
				: base("display_monitor", (ConVarFlags)4, (string)null, "Which display the game window is shown on. Valid values are DISPLAY1, DISPLAY2, ... as listed by the Display Monitor option in the settings menu.")
			{
			}

			public override string GetString()
			{
				return MonitorManager.GetCurrentDisplayDeviceName() ?? string.Empty;
			}

			public override void SetString(string newValue)
			{
				//IL_006e: Unknown result type (might be due to invalid IL or missing references)
				Log.Info($"MonitorSwitcher: SetString('{newValue}') loadFinished={RoR2Application.loadFinished}");
				string error2;
				if (string.IsNullOrEmpty(newValue))
				{
					MonitorManager.TryMoveToMonitor(null, out var _);
				}
				else if (!RoR2Application.loadFinished)
				{
					MonitorSwitcherPlugin.PendingStartupMonitor = newValue;
				}
				else if (!MonitorManager.TryMoveToMonitor(newValue, out error2))
				{
					throw new ConCommandException(error2 ?? ("Unknown display '" + newValue + "'."));
				}
			}
		}

		public const string Name = "display_monitor";

		public static readonly BaseConVar Instance = (BaseConVar)(object)new MonitorConVarImpl();

		private static bool _registered;

		public static void Register(Console console)
		{
			if (_registered)
			{
				return;
			}
			if (console.FindConVar("display_monitor") != null)
			{
				_registered = true;
				return;
			}
			_registered = true;
			MethodInfo method = typeof(Console).GetMethod("RegisterConVarInternal", BindingFlags.Instance | BindingFlags.NonPublic);
			if (method == null)
			{
				Log.Error("MonitorSwitcher: could not find Console.RegisterConVarInternal");
				return;
			}
			method.Invoke(console, new object[1] { Instance });
			Log.Info("MonitorSwitcher: registered convar 'display_monitor'");
		}
	}
	internal static class Localization
	{
		public const string SettingNameToken = "MONITOR_SWITCHER_SETTING_NAME";

		public const string ChoiceTokenPrefix = "MONITOR_SWITCHER_DISPLAY_";

		public static void Init()
		{
			LanguageAPI.Add("MONITOR_SWITCHER_SETTING_NAME", "Display Monitor");
		}

		public static string ChoiceToken(int index)
		{
			return "MONITOR_SWITCHER_DISPLAY_" + index;
		}
	}
	internal static class Log
	{
		private static ManualLogSource _logSource;

		internal static void Init(ManualLogSource logSource)
		{
			_logSource = logSource;
		}

		internal static void Debug(object data)
		{
			_logSource.LogDebug(data);
		}

		internal static void Error(object data)
		{
			_logSource.LogError(data);
		}

		internal static void Fatal(object data)
		{
			_logSource.LogFatal(data);
		}

		internal static void Info(object data)
		{
			_logSource.LogInfo(data);
		}

		internal static void Message(object data)
		{
			_logSource.LogMessage(data);
		}

		internal static void Warning(object data)
		{
			_logSource.LogWarning(data);
		}
	}
	internal class MonitorDropdownControl : BaseSettingsControl
	{
		internal static string PendingSettingName;

		public MPDropdown dropdown;

		private DisplayInfo[] _displays = Array.Empty<DisplayInfo>();

		private bool _syncing;

		protected void Awake()
		{
			if (string.IsNullOrEmpty(base.settingName))
			{
				base.settingName = PendingSettingName;
			}
			((BaseSettingsControl)this).Awake();
		}

		protected override void OnUpdateControls()
		{
			RefreshOptions();
		}

		internal void Init(MPDropdown targetDropdown)
		{
			dropdown = targetDropdown;
			if (Object.op_Implicit((Object)(object)dropdown))
			{
				((UnityEventBase)((TMP_Dropdown)dropdown).onValueChanged).RemoveAllListeners();
				((UnityEvent<int>)(object)((TMP_Dropdown)dropdown).onValueChanged).AddListener((UnityAction<int>)OnDropdownValueChanged);
			}
			RefreshOptions();
		}

		private void RefreshOptions()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)dropdown))
			{
				return;
			}
			_displays = MonitorManager.Enumerate().ToArray();
			string currentValue = ((BaseSettingsControl)this).GetCurrentValue();
			int valueWithoutNotify = 0;
			List<OptionData> list = new List<OptionData>(_displays.Length);
			for (int i = 0; i < _displays.Length; i++)
			{
				list.Add(new OptionData(_displays[i].Label));
				if (string.Equals(_displays[i].DeviceName, currentValue, StringComparison.OrdinalIgnoreCase))
				{
					valueWithoutNotify = i;
				}
			}
			_syncing = true;
			((TMP_Dropdown)dropdown).ClearOptions();
			((TMP_Dropdown)dropdown).AddOptions(list);
			((TMP_Dropdown)dropdown).SetValueWithoutNotify(valueWithoutNotify);
			_syncing = false;
		}

		private void OnDropdownValueChanged(int value)
		{
			if (!_syncing && value >= 0 && value < _displays.Length)
			{
				((BaseSettingsControl)this).SubmitSetting(_displays[value].DeviceName);
			}
		}
	}
	internal struct NativeRect
	{
		public int Left;

		public int Top;

		public int Right;

		public int Bottom;

		public int Width => Right - Left;

		public int Height => Bottom - Top;
	}
	[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
	internal struct MonitorInfoEx
	{
		public int CbSize;

		public NativeRect RcMonitor;

		public NativeRect RcWork;

		public uint DwFlags;

		[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
		public string SzDevice;
	}
	[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
	internal struct DisplayDevice
	{
		public int Cb;

		[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
		public string DeviceName;

		[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
		public string DeviceString;

		public uint StateFlags;

		[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
		public string DeviceID;

		[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
		public string DeviceKey;
	}
	[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
	internal struct DevMode
	{
		[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
		public string DeviceName;

		public ushort SpecVersion;

		public ushort DriverVersion;

		public ushort Size;

		public ushort DriverExtra;

		public uint Fields;

		public int PositionX;

		public int PositionY;

		public uint DisplayOrientation;

		public uint DisplayFixedOutput;

		public short Color;

		public short Duplex;

		public short YResolution;

		public short TTOption;

		public short Collate;

		[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
		public string FormName;

		public ushort LogPixels;

		public uint BitsPerPel;

		public uint PelsWidth;

		public uint PelsHeight;

		public uint DisplayFlags;

		public uint DisplayFrequency;

		public uint ICMethod;

		public uint ICMIntent;

		public uint MediaType;

		public uint DitherType;

		public uint Reserved1;

		public uint Reserved2;

		public uint PanningWidth;

		public uint PanningHeight;
	}
	internal struct DisplayModeInfo
	{
		public int Width;

		public int Height;

		public int RefreshRate;
	}
	internal sealed class DisplayInfo
	{
		private const string DeviceNamePrefix = "DISPLAY";

		public string DeviceName;

		public string FullName;

		public string FriendlyName;

		public NativeRect Bounds;

		public NativeRect WorkArea;

		public bool IsPrimary;

		public string Label => $"Display {DisplayNumber} ({Bounds.Width} x {Bounds.Height})";

		private string DisplayNumber
		{
			get
			{
				if (DeviceName != null && DeviceName.StartsWith("DISPLAY", StringComparison.OrdinalIgnoreCase) && int.TryParse(DeviceName.Substring("DISPLAY".Length), out var result))
				{
					return result.ToString();
				}
				return "?";
			}
		}
	}
	internal static class MonitorManager
	{
		private delegate bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref NativeRect lprcMonitor, IntPtr dwData);

		private static class Native
		{
			[DllImport("user32.dll")]
			internal static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumProc lpfnEnum, IntPtr dwData);

			[DllImport("user32.dll", CharSet = CharSet.Unicode)]
			internal static extern bool GetMonitorInfo(IntPtr hMonitor, ref MonitorInfoEx lpmi);

			[DllImport("user32.dll", CharSet = CharSet.Unicode)]
			internal static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DisplayDevice lpDisplayDevice, uint dwFlags);

			[DllImport("user32.dll", CharSet = CharSet.Unicode)]
			internal static extern bool EnumDisplaySettings(string lpszDeviceName, uint iModeNum, ref DevMode lpDevMode);

			[DllImport("user32.dll")]
			internal static extern IntPtr GetActiveWindow();

			[DllImport("user32.dll")]
			internal static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);

			[DllImport("user32.dll")]
			internal static extern bool GetWindowRect(IntPtr hWnd, out NativeRect lpRect);

			[DllImport("user32.dll")]
			internal static extern bool MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool bRepaint);

			[DllImport("user32.dll")]
			internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
		}

		private const uint MONITORINFOF_PRIMARY = 1u;

		private const uint MONITOR_DEFAULTTONEAREST = 2u;

		private const uint SWP_NOACTIVATE = 16u;

		private const uint SWP_SHOWWINDOW = 64u;

		private static readonly Dictionary<string, string> FriendlyNameCache = new Dictionary<string, string>();

		public static event Action DisplayChanged;

		public static List<DisplayInfo> Enumerate()
		{
			List<DisplayInfo> list = new List<DisplayInfo>();
			MonitorEnumProc lpfnEnum = delegate(IntPtr hMonitor, IntPtr hdc, ref NativeRect rc, IntPtr data)
			{
				MonitorInfoEx lpmi = new MonitorInfoEx
				{
					CbSize = Marshal.SizeOf(typeof(MonitorInfoEx))
				};
				if (Native.GetMonitorInfo(hMonitor, ref lpmi))
				{
					list.Add(new DisplayInfo
					{
						DeviceName = NormalizeDeviceName(lpmi.SzDevice),
						FullName = lpmi.SzDevice,
						Bounds = lpmi.RcMonitor,
						WorkArea = lpmi.RcWork,
						IsPrimary = ((lpmi.DwFlags & 1) != 0),
						FriendlyName = GetFriendlyName(lpmi.SzDevice)
					});
				}
				return true;
			};
			Native.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, lpfnEnum, IntPtr.Zero);
			return list;
		}

		public static string GetCurrentDisplayDeviceName()
		{
			IntPtr gameWindowHandle = GetGameWindowHandle();
			if (gameWindowHandle != IntPtr.Zero)
			{
				IntPtr intPtr = Native.MonitorFromWindow(gameWindowHandle, 2u);
				if (intPtr != IntPtr.Zero)
				{
					MonitorInfoEx lpmi = new MonitorInfoEx
					{
						CbSize = Marshal.SizeOf(typeof(MonitorInfoEx))
					};
					if (Native.GetMonitorInfo(intPtr, ref lpmi))
					{
						return NormalizeDeviceName(lpmi.SzDevice);
					}
				}
			}
			return GetPrimary()?.DeviceName;
		}

		public static string GetCurrentDisplayFullName()
		{
			IntPtr gameWindowHandle = GetGameWindowHandle();
			if (gameWindowHandle != IntPtr.Zero)
			{
				IntPtr intPtr = Native.MonitorFromWindow(gameWindowHandle, 2u);
				if (intPtr != IntPtr.Zero)
				{
					MonitorInfoEx lpmi = new MonitorInfoEx
					{
						CbSize = Marshal.SizeOf(typeof(MonitorInfoEx))
					};
					if (Native.GetMonitorInfo(intPtr, ref lpmi))
					{
						return lpmi.SzDevice;
					}
				}
			}
			return GetPrimary()?.FullName ?? string.Empty;
		}

		public static List<DisplayModeInfo> GetModesForMonitor(string fullDeviceName)
		{
			List<DisplayModeInfo> list = new List<DisplayModeInfo>();
			DevMode lpDevMode = NewDevMode();
			for (uint num = 0u; Native.EnumDisplaySettings(fullDeviceName, num, ref lpDevMode); num++)
			{
				if (lpDevMode.PelsWidth != 0 && lpDevMode.PelsHeight != 0 && lpDevMode.DisplayFrequency != 0)
				{
					list.Add(new DisplayModeInfo
					{
						Width = (int)lpDevMode.PelsWidth,
						Height = (int)lpDevMode.PelsHeight,
						RefreshRate = (int)lpDevMode.DisplayFrequency
					});
				}
				lpDevMode = NewDevMode();
			}
			return list;
		}

		public static DisplayModeInfo? GetCurrentMode(string fullDeviceName)
		{
			DevMode lpDevMode = NewDevMode();
			if (Native.EnumDisplaySettings(fullDeviceName, uint.MaxValue, ref lpDevMode) && lpDevMode.PelsWidth != 0 && lpDevMode.PelsHeight != 0)
			{
				return new DisplayModeInfo
				{
					Width = (int)lpDevMode.PelsWidth,
					Height = (int)lpDevMode.PelsHeight,
					RefreshRate = (int)lpDevMode.DisplayFrequency
				};
			}
			return null;
		}

		private static DevMode NewDevMode()
		{
			return new DevMode
			{
				Size = (ushort)Marshal.SizeOf(typeof(DevMode))
			};
		}

		public static bool TryMoveToMonitor(string deviceName, out string error)
		{
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Invalid comparison between Unknown and I4
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Invalid comparison between Unknown and I4
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			error = null;
			string normalized = NormalizeDeviceName(deviceName);
			List<DisplayInfo> list = Enumerate();
			DisplayInfo displayInfo = ((!string.IsNullOrEmpty(normalized)) ? list.Find((DisplayInfo d) => string.Equals(d.DeviceName, normalized, StringComparison.OrdinalIgnoreCase)) : null);
			displayInfo = displayInfo ?? list.Find((DisplayInfo d) => d.IsPrimary);
			if (displayInfo == null)
			{
				error = "No displays are currently connected.";
				return false;
			}
			IntPtr gameWindowHandle = GetGameWindowHandle();
			Log.Info($"MonitorSwitcher: move to '{normalized}' hwnd={gameWindowHandle} target={displayInfo.DeviceName} bounds={displayInfo.Bounds.Left},{displayInfo.Bounds.Top} {displayInfo.Bounds.Width}x{displayInfo.Bounds.Height} mode={Screen.fullScreenMode}");
			if (gameWindowHandle == IntPtr.Zero)
			{
				error = "Could not find the game window.";
				return false;
			}
			FullScreenMode fullScreenMode = Screen.fullScreenMode;
			if ((int)fullScreenMode == 0)
			{
				Screen.fullScreenMode = (FullScreenMode)3;
				PlaceWindow(gameWindowHandle, displayInfo.Bounds);
				Screen.fullScreenMode = fullScreenMode;
				MonitorManager.DisplayChanged?.Invoke();
				return true;
			}
			if ((int)fullScreenMode == 1)
			{
				PlaceWindow(gameWindowHandle, displayInfo.Bounds);
				Log.Info("MonitorSwitcher: post-move current display = '" + GetCurrentDisplayFullName() + "'");
				MonitorManager.DisplayChanged?.Invoke();
				return true;
			}
			if (Native.GetWindowRect(gameWindowHandle, out var lpRect))
			{
				int num = Mathf.Clamp(lpRect.Width, 0, displayInfo.WorkArea.Width);
				int num2 = Mathf.Clamp(lpRect.Height, 0, displayInfo.WorkArea.Height);
				int x = displayInfo.WorkArea.Left + (displayInfo.WorkArea.Width - num) / 2;
				int y = displayInfo.WorkArea.Top + (displayInfo.WorkArea.Height - num2) / 2;
				Native.MoveWindow(gameWindowHandle, x, y, num, num2, bRepaint: true);
			}
			else
			{
				Native.MoveWindow(gameWindowHandle, displayInfo.WorkArea.Left, displayInfo.WorkArea.Top, displayInfo.WorkArea.Width, displayInfo.WorkArea.Height, bRepaint: true);
			}
			MonitorManager.DisplayChanged?.Invoke();
			return true;
		}

		private static void PlaceWindow(IntPtr hwnd, NativeRect bounds)
		{
			int x = bounds.Left;
			int y = bounds.Top;
			int width = bounds.Width;
			int height = bounds.Height;
			bool flag = Native.SetWindowPos(hwnd, IntPtr.Zero, x, y, width, height, 80u);
			Log.Info($"MonitorSwitcher: SetWindowPos({x},{y},{width}x{height}) result={flag}");
			RoR2Application.onNextUpdate += delegate
			{
				if (hwnd != IntPtr.Zero)
				{
					Native.SetWindowPos(hwnd, IntPtr.Zero, x, y, width, height, 80u);
				}
			};
		}

		private static DisplayInfo GetPrimary()
		{
			return Enumerate().Find((DisplayInfo d) => d.IsPrimary);
		}

		private static string NormalizeDeviceName(string deviceName)
		{
			if (deviceName != null && deviceName.StartsWith("\\\\.\\", StringComparison.Ordinal))
			{
				return deviceName.Substring(4);
			}
			return deviceName;
		}

		private static IntPtr GetGameWindowHandle()
		{
			try
			{
				IntPtr mainWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
				if (mainWindowHandle != IntPtr.Zero)
				{
					return mainWindowHandle;
				}
			}
			catch
			{
			}
			return Native.GetActiveWindow();
		}

		private static string GetFriendlyName(string deviceName)
		{
			if (FriendlyNameCache.TryGetValue(deviceName, out var value))
			{
				return value;
			}
			DisplayDevice lpDisplayDevice = new DisplayDevice
			{
				Cb = Marshal.SizeOf(typeof(DisplayDevice))
			};
			if (Native.EnumDisplayDevices(deviceName, 1u, ref lpDisplayDevice, 0u) && !string.IsNullOrEmpty(lpDisplayDevice.DeviceString))
			{
				FriendlyNameCache[deviceName] = lpDisplayDevice.DeviceString;
				return lpDisplayDevice.DeviceString;
			}
			uint num = 0u;
			while (true)
			{
				DisplayDevice lpDisplayDevice2 = new DisplayDevice
				{
					Cb = Marshal.SizeOf(typeof(DisplayDevice))
				};
				if (!Native.EnumDisplayDevices(null, num, ref lpDisplayDevice2, 0u))
				{
					break;
				}
				if (string.Equals(lpDisplayDevice2.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(lpDisplayDevice2.DeviceString))
				{
					FriendlyNameCache[deviceName] = lpDisplayDevice2.DeviceString;
					return lpDisplayDevice2.DeviceString;
				}
				num++;
			}
			FriendlyNameCache[deviceName] = deviceName;
			return deviceName;
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("Ovalsquare.MonitorSwitcher", "MonitorSwitcher", "1.0.0")]
	public class MonitorSwitcherPlugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static hook_Start <0>__OnSettingsPanelStart;

			public static hook_OnEnable <1>__OnSettingsPanelEnable;

			public static Action <2>__OnDisplayChanged;
		}

		public const string PluginAuthor = "Ovalsquare";

		public const string PluginName = "MonitorSwitcher";

		public const string PluginGUID = "Ovalsquare.MonitorSwitcher";

		public const string PluginVersion = "1.0.0";

		internal static string PendingStartupMonitor;

		private void Awake()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			Log.Init(((BaseUnityPlugin)this).Logger);
			Localization.Init();
			Console.CacheConVars += new hook_CacheConVars(OnCacheConVars);
			Console.LoadStartupConfigs += new hook_LoadStartupConfigs(OnLoadStartupConfigs);
			object obj = <>O.<0>__OnSettingsPanelStart;
			if (obj == null)
			{
				hook_Start val = SettingsRowInjector.OnSettingsPanelStart;
				<>O.<0>__OnSettingsPanelStart = val;
				obj = (object)val;
			}
			SettingsPanelController.Start += (hook_Start)obj;
			object obj2 = <>O.<1>__OnSettingsPanelEnable;
			if (obj2 == null)
			{
				hook_OnEnable val2 = SettingsRowInjector.OnSettingsPanelEnable;
				<>O.<1>__OnSettingsPanelEnable = val2;
				obj2 = (object)val2;
			}
			SettingsPanelController.OnEnable += (hook_OnEnable)obj2;
			RoR2Application.OnMainMenuControllerInitialized += new hook_OnMainMenuControllerInitialized(OnMainMenuControllerInitialized);
			MonitorManager.DisplayChanged += OnDisplayChanged;
			ResolutionListFix.Hook();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Ovalsquare.MonitorSwitcher AWAKE_COMPLETE");
		}

		private static void OnDisplayChanged()
		{
			ResolutionListFix.RefreshActiveControls();
		}

		private void OnCacheConVars(orig_CacheConVars orig, Console self)
		{
			orig.Invoke(self);
			DisplayMonitorConVar.Register(self);
		}

		private void OnLoadStartupConfigs(orig_LoadStartupConfigs orig, Console self)
		{
			DisplayMonitorConVar.Register(self);
			orig.Invoke(self);
		}

		private void OnMainMenuControllerInitialized(orig_OnMainMenuControllerInitialized orig, RoR2Application self)
		{
			orig.Invoke(self);
			ApplyPendingStartupMonitor();
		}

		private static void ApplyPendingStartupMonitor()
		{
			string pendingStartupMonitor = PendingStartupMonitor;
			PendingStartupMonitor = null;
			if (!string.IsNullOrEmpty(pendingStartupMonitor))
			{
				if (MonitorManager.TryMoveToMonitor(pendingStartupMonitor, out var error))
				{
					Log.Info("MonitorSwitcher: applied saved monitor " + pendingStartupMonitor + ".");
				}
				else
				{
					Log.Warning("MonitorSwitcher: could not apply saved monitor " + pendingStartupMonitor + ": " + error);
				}
			}
		}

		private void OnDestroy()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			Console.CacheConVars -= new hook_CacheConVars(OnCacheConVars);
			Console.LoadStartupConfigs -= new hook_LoadStartupConfigs(OnLoadStartupConfigs);
			object obj = <>O.<0>__OnSettingsPanelStart;
			if (obj == null)
			{
				hook_Start val = SettingsRowInjector.OnSettingsPanelStart;
				<>O.<0>__OnSettingsPanelStart = val;
				obj = (object)val;
			}
			SettingsPanelController.Start -= (hook_Start)obj;
			object obj2 = <>O.<1>__OnSettingsPanelEnable;
			if (obj2 == null)
			{
				hook_OnEnable val2 = SettingsRowInjector.OnSettingsPanelEnable;
				<>O.<1>__OnSettingsPanelEnable = val2;
				obj2 = (object)val2;
			}
			SettingsPanelController.OnEnable -= (hook_OnEnable)obj2;
			RoR2Application.OnMainMenuControllerInitialized -= new hook_OnMainMenuControllerInitialized(OnMainMenuControllerInitialized);
			MonitorManager.DisplayChanged -= OnDisplayChanged;
			ResolutionListFix.Unhook();
			Log.Info("Ovalsquare.MonitorSwitcher unloaded");
		}
	}
	internal static class ResolutionListFix
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static hook_GenerateResolutionOptions <0>__OnGenerateResolutionOptions;
		}

		private static readonly FieldInfo ResolutionOptionsField = typeof(ResolutionControl).GetField("resolutionOptions", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly Type ResolutionOptionType = typeof(ResolutionControl).GetNestedType("ResolutionOption", BindingFlags.NonPublic);

		private static readonly ConstructorInfo ResolutionOptionCtor = ResolutionOptionType?.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

		private static readonly FieldInfo SizeField = ResolutionOptionType?.GetField("size", BindingFlags.Instance | BindingFlags.Public);

		private static readonly FieldInfo RefreshRatesField = ResolutionOptionType?.GetField("supportedRefreshRates", BindingFlags.Instance | BindingFlags.Public);

		public static void Hook()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			object obj = <>O.<0>__OnGenerateResolutionOptions;
			if (obj == null)
			{
				hook_GenerateResolutionOptions val = OnGenerateResolutionOptions;
				<>O.<0>__OnGenerateResolutionOptions = val;
				obj = (object)val;
			}
			ResolutionControl.GenerateResolutionOptions += (hook_GenerateResolutionOptions)obj;
		}

		public static void Unhook()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			object obj = <>O.<0>__OnGenerateResolutionOptions;
			if (obj == null)
			{
				hook_GenerateResolutionOptions val = OnGenerateResolutionOptions;
				<>O.<0>__OnGenerateResolutionOptions = val;
				obj = (object)val;
			}
			ResolutionControl.GenerateResolutionOptions -= (hook_GenerateResolutionOptions)obj;
		}

		public static void RefreshActiveControls()
		{
			ResolutionControl[] array = Object.FindObjectsOfType<ResolutionControl>();
			foreach (ResolutionControl self in array)
			{
				try
				{
					RebuildFromCurrentMonitor(self);
				}
				catch (Exception data)
				{
					Log.Error(data);
				}
			}
		}

		private static void OnGenerateResolutionOptions(orig_GenerateResolutionOptions orig, ResolutionControl self)
		{
			orig.Invoke(self);
			try
			{
				RebuildFromCurrentMonitor(self);
			}
			catch (Exception data)
			{
				Log.Error(data);
			}
		}

		private static void RebuildFromCurrentMonitor(ResolutionControl self)
		{
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Expected O, but got Unknown
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			if (ResolutionOptionsField == null || ResolutionOptionType == null || ResolutionOptionCtor == null || SizeField == null || RefreshRatesField == null || !Object.op_Implicit((Object)(object)self.resolutionDropdown))
			{
				return;
			}
			string currentDisplayFullName = MonitorManager.GetCurrentDisplayFullName();
			if (string.IsNullOrEmpty(currentDisplayFullName))
			{
				return;
			}
			List<DisplayModeInfo> modesForMonitor = MonitorManager.GetModesForMonitor(currentDisplayFullName);
			if (modesForMonitor.Count == 0)
			{
				return;
			}
			List<DisplayModeInfo> list = (from m in modesForMonitor
				orderby (long)m.Width * (long)m.Height descending, m.RefreshRate descending
				select m).ToList();
			List<Vector2Int> list2 = new List<Vector2Int>();
			Dictionary<Vector2Int, List<int>> dictionary = new Dictionary<Vector2Int, List<int>>();
			Vector2Int val = default(Vector2Int);
			foreach (DisplayModeInfo item in list)
			{
				((Vector2Int)(ref val))..ctor(item.Width, item.Height);
				if (!dictionary.TryGetValue(val, out var value))
				{
					value = (dictionary[val] = new List<int>());
					list2.Add(val);
				}
				if (!value.Contains(item.RefreshRate))
				{
					value.Add(item.RefreshRate);
				}
			}
			Array array = Array.CreateInstance(ResolutionOptionType, list2.Count);
			OptionData[] array2 = (OptionData[])(object)new OptionData[list2.Count];
			for (int num = 0; num < list2.Count; num++)
			{
				object obj = ResolutionOptionCtor.Invoke(null);
				SizeField.SetValue(obj, list2[num]);
				List<int> list4 = (List<int>)RefreshRatesField.GetValue(obj);
				list4.AddRange(dictionary[list2[num]]);
				array.SetValue(obj, num);
				int num2 = num;
				Vector2Int val2 = list2[num];
				object arg = ((Vector2Int)(ref val2)).x;
				val2 = list2[num];
				array2[num2] = new OptionData($"{arg}x{((Vector2Int)(ref val2)).y}");
			}
			ResolutionOptionsField.SetValue(self, array);
			((TMP_Dropdown)self.resolutionDropdown).ClearOptions();
			((TMP_Dropdown)self.resolutionDropdown).AddOptions(array2.ToList());
			Resolution currentResolution = Screen.currentResolution;
			int width = ((Resolution)(ref currentResolution)).width;
			currentResolution = Screen.currentResolution;
			Vector2Int current2 = new Vector2Int(width, ((Resolution)(ref currentResolution)).height);
			int num3 = list2.FindIndex((Vector2Int s) => s == current2);
			if (num3 < 0)
			{
				DisplayModeInfo? currentMode = MonitorManager.GetCurrentMode(currentDisplayFullName);
				if (currentMode.HasValue)
				{
					num3 = list2.FindIndex((Vector2Int s) => s == new Vector2Int(currentMode.Value.Width, currentMode.Value.Height));
				}
			}
			if (num3 < 0)
			{
				num3 = 0;
			}
			((TMP_Dropdown)self.resolutionDropdown).value = num3;
			DisplayModeInfo displayModeInfo = list[0];
			Log.Info($"MonitorSwitcher: resolution list rebuilt for {currentDisplayFullName} ({modesForMonitor.Count} modes, largest {displayModeInfo.Width}x{displayModeInfo.Height}@{displayModeInfo.RefreshRate})");
		}
	}
	internal static class SettingsRowInjector
	{
		private static readonly FieldInfo SettingsControllersField = typeof(SettingsPanelController).GetField("settingsControllers", BindingFlags.Instance | BindingFlags.NonPublic);

		public static void OnSettingsPanelStart(orig_Start orig, SettingsPanelController self)
		{
			InjectRow(self);
			orig.Invoke(self);
			RefreshSettingsControllersSnapshot(self);
		}

		public static void OnSettingsPanelEnable(orig_OnEnable orig, SettingsPanelController self)
		{
			orig.Invoke(self);
			Log.Info("MonitorSwitcher: settings panel enabled: " + ((Object)((Component)self).gameObject).name);
		}

		private static void InjectRow(SettingsPanelController panel)
		{
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ResolutionControl componentInChildren = ((Component)panel).GetComponentInChildren<ResolutionControl>(true);
				if (!Object.op_Implicit((Object)(object)componentInChildren))
				{
					return;
				}
				Transform parent = ((Component)componentInChildren).transform.parent;
				Transform val = (((Object)(object)parent != (Object)null) ? parent.parent : null);
				if ((Object)(object)val == (Object)null)
				{
					Log.Warning("MonitorSwitcher: could not find the Video settings row list; skipping row injection.");
					return;
				}
				GameObject val2 = Object.Instantiate<GameObject>(((Component)parent).gameObject, val);
				((Object)val2).name = "Option, Display Monitor";
				val2.transform.SetAsLastSibling();
				ResolutionControl componentInChildren2 = val2.GetComponentInChildren<ResolutionControl>(true);
				if (Object.op_Implicit((Object)(object)componentInChildren2))
				{
					Object.Destroy((Object)(object)componentInChildren2);
				}
				LanguageTextMeshController componentInChildren3 = val2.GetComponentInChildren<LanguageTextMeshController>(true);
				if (Object.op_Implicit((Object)(object)componentInChildren3))
				{
					componentInChildren3.token = "MONITOR_SWITCHER_SETTING_NAME";
				}
				MPDropdown val3 = null;
				MPDropdown[] componentsInChildren = val2.GetComponentsInChildren<MPDropdown>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					if (i == 0)
					{
						val3 = componentsInChildren[i];
						continue;
					}
					((Component)componentsInChildren[i]).gameObject.SetActive(false);
					Object.Destroy((Object)(object)componentsInChildren[i]);
				}
				if ((Object)(object)val3 == (Object)null)
				{
					Log.Warning("MonitorSwitcher: no MPDropdown found in cloned row; skipping row injection.");
					Object.Destroy((Object)(object)val2);
					return;
				}
				MonitorDropdownControl.PendingSettingName = "display_monitor";
				MonitorDropdownControl monitorDropdownControl = val2.AddComponent<MonitorDropdownControl>();
				((BaseSettingsControl)monitorDropdownControl).nameToken = "MONITOR_SWITCHER_SETTING_NAME";
				((BaseSettingsControl)monitorDropdownControl).nameLabel = componentInChildren3;
				((BaseSettingsControl)monitorDropdownControl).settingSource = (SettingSource)0;
				((BaseSettingsControl)monitorDropdownControl).useConfirmationDialog = true;
				monitorDropdownControl.Init(val3);
				Log.Info($"MonitorSwitcher: injected Display Monitor dropdown as last row of '{((Object)val).name}' with {MonitorManager.Enumerate().Count} display(s).");
			}
			catch (Exception data)
			{
				Log.Error(data);
			}
		}

		private static void RefreshSettingsControllersSnapshot(SettingsPanelController panel)
		{
			try
			{
				if (SettingsControllersField != null)
				{
					BaseSettingsControl[] componentsInChildren = ((Component)panel).GetComponentsInChildren<BaseSettingsControl>(true);
					SettingsControllersField.SetValue(panel, componentsInChildren);
				}
			}
			catch (Exception data)
			{
				Log.Error(data);
			}
		}
	}
}