Decompiled source of BorderlessWindowed v1.1.2

BorderlessWindowed.dll

Decompiled 6 days ago
using System;
using System.Collections;
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 System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using GUIFramework;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using Valheim.SettingsGui;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("BorderlessWindowed")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Riintouge")]
[assembly: AssemblyProduct("BorderlessWindowed")]
[assembly: AssemblyCopyright("Copyright © 2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("0da70214-2c9c-40a9-9b69-43f07bf97314")]
[assembly: AssemblyFileVersion("1.1.2.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.2.0")]
[module: UnverifiableCode]
namespace BorderlessWindowed;

[BepInPlugin("com.riintouge.borderlesswindowed", "Borderless Windowed", "1.1.2")]
[BepInProcess("valheim.exe")]
public class BorderlessWindowed : BaseUnityPlugin
{
	[HarmonyPatch(typeof(GraphicsSettingsManager))]
	private class GraphicsSettingsManagerPatch
	{
		[HarmonyPatch("ApplyTargetResolutionSetting")]
		[HarmonyPostfix]
		private static void ApplyTargetResolutionSettingPostfix()
		{
			((MonoBehaviour)Instance).StartCoroutine(Instance.CoUpdateBorder());
		}
	}

	[HarmonyPatch(typeof(GraphicsSettings))]
	internal class GraphicsSettingsPatch
	{
		private static int LastWindowTypeIndex = -1;

		private static bool SuppressConfigSync = false;

		private static WeakReference<Transform> WeakResolutionsRowTransform = new WeakReference<Transform>(null);

		private static WeakReference<GuiDropdown> WeakWindowTypeDropdown = new WeakReference<GuiDropdown>(null);

		private static List<string> WindowTypeStrings = new List<string>(new string[3] { "Fullscreen", "Borderless Windowed", "Windowed" });

		private static int WindowTypeFullscreen = 0;

		private static int WindowTypeBorderlessWindowed = 1;

		private static int WindowTypeWindowed = 2;

		internal static void SynchronizeWithConfigEntry(GuiDropdown dropdown)
		{
			if (!SuppressConfigSync && ((Object)(object)dropdown != (Object)null || (WeakWindowTypeDropdown.TryGetTarget(out dropdown) && (Object)(object)dropdown != (Object)null)))
			{
				int value = (Screen.fullScreen ? WindowTypeFullscreen : (ShowBorder.Value ? WindowTypeWindowed : WindowTypeBorderlessWindowed));
				((TMP_Dropdown)dropdown).value = value;
			}
		}

		[HarmonyPatch("CreateDynamicGraphicsQualitySettings")]
		[HarmonyPostfix]
		private static void CreateDynamicGraphicsQualitySettingsPostfix(ref GameObject ___m_qualityTogglePrefab, ref List<QualityToggleData> ___m_qualityToggles, ref List<Toggle> ___m_dynamicQualityToggles)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			Transform val = ((Component)___m_qualityToggles.First().m_toggle).transform.parent.Find("Resolution").Find("Anchor");
			Transform val2 = val.Find("Fullscreen");
			GuiToggle fullscreenToggleComp = ((Component)val2).GetComponent<GuiToggle>();
			Transform obj = Object.Instantiate<Transform>(val.Find("Resolutions"));
			((Object)obj).name = "WindowType";
			Object.Destroy((Object)(object)((Component)obj.Find("Label")).gameObject);
			GuiDropdown component = ((Component)obj.Find("Resolution")).GetComponent<GuiDropdown>();
			((TMP_Dropdown)component).ClearOptions();
			((TMP_Dropdown)component).AddOptions(WindowTypeStrings);
			SynchronizeWithConfigEntry(component);
			((UnityEvent<int>)(object)((TMP_Dropdown)component).onValueChanged).AddListener((UnityAction<int>)delegate(int index)
			{
				((Toggle)fullscreenToggleComp).isOn = !((Toggle)fullscreenToggleComp).isOn;
				((Toggle)fullscreenToggleComp).isOn = index == 0;
			});
			LastWindowTypeIndex = ((TMP_Dropdown)component).value;
			WeakResolutionsRowTransform.SetTarget(val);
			WeakWindowTypeDropdown.SetTarget(component);
			obj.SetParent(val, false);
			Transform transform = ((Component)obj).transform;
			transform.localPosition += new Vector3(190f, 0f, 0f);
		}

		[HarmonyPatch("OnResSwitchOK")]
		[HarmonyPostfix]
		private static void OnResSwitchOKPostfix()
		{
			if (WeakWindowTypeDropdown.TryGetTarget(out var target) && (Object)(object)target != (Object)null)
			{
				LastWindowTypeIndex = ((TMP_Dropdown)target).value;
			}
		}

		[HarmonyPatch("OnTestResolution")]
		[HarmonyPostfix]
		private static void OnTestResolutionPostfix()
		{
			if (WeakWindowTypeDropdown.TryGetTarget(out var target) && (Object)(object)target != (Object)null)
			{
				SuppressConfigSync = true;
				ShowBorder.Value = ((TMP_Dropdown)target).value != WindowTypeBorderlessWindowed;
				SuppressConfigSync = false;
			}
		}

		[HarmonyPatch("ResolutionSettingsChanged")]
		[HarmonyPrefix]
		private static bool ResolutionSettingsChangedPrefix(ref bool __result)
		{
			if (WeakWindowTypeDropdown.TryGetTarget(out var target) && (Object)(object)target != (Object)null && ShowBorder.Value == (((TMP_Dropdown)target).value == WindowTypeBorderlessWindowed))
			{
				__result = true;
				return false;
			}
			return true;
		}

		[HarmonyPatch("RevertMode")]
		[HarmonyPostfix]
		private static void RevertModePostfix(ref GraphicsSettings __instance)
		{
			if (WeakWindowTypeDropdown.TryGetTarget(out var target) && (Object)(object)target != (Object)null)
			{
				SuppressConfigSync = true;
				ShowBorder.Value = LastWindowTypeIndex != WindowTypeBorderlessWindowed;
				SuppressConfigSync = false;
				((TMP_Dropdown)target).value = LastWindowTypeIndex;
			}
		}
	}

	public static BorderlessWindowed Instance;

	public static ConfigEntry<bool> LoadOnStart;

	public static ConfigEntry<bool> ShowBorder;

	private readonly Harmony Harmony = new Harmony("com.riintouge.borderlesswindowed");

	private readonly BorderHelper BorderHelper = new BorderHelper();

	private void Awake()
	{
		if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
		{
			LoadOnStart = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Core", "LoadOnStart", true, "Whether this plugin loads on game start.");
			ShowBorder = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "ShowBorder", true, "Whether the border should be shown on the game window.");
			if (LoadOnStart.Value)
			{
				Instance = this;
				Harmony.PatchAll();
				((BaseUnityPlugin)this).Config.SettingChanged += Config_SettingChanged;
			}
		}
	}

	private void Config_SettingChanged(object sender, SettingChangedEventArgs e)
	{
		if (e.ChangedSetting == ShowBorder)
		{
			((MonoBehaviour)Instance).StartCoroutine(Instance.CoUpdateBorder());
			GraphicsSettingsPatch.SynchronizeWithConfigEntry(null);
		}
	}

	internal IEnumerator CoUpdateBorder()
	{
		return BorderHelper.UpdateBorder(ShowBorder.Value);
	}
}
internal class BorderHelper
{
	[CompilerGenerated]
	private sealed class <UpdateBorder>d__5 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public BorderHelper <>4__this;

		public bool enable;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <UpdateBorder>d__5(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			int num = <>1__state;
			if (num == -3 || num == 1)
			{
				try
				{
				}
				finally
				{
					<>m__Finally1();
				}
			}
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			try
			{
				int num = <>1__state;
				BorderHelper borderHelper = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if (Monitor.TryEnter(UpdateBorderLock))
					{
						<>1__state = -3;
						<>2__current = borderHelper.UpdateBorderCore(enable);
						<>1__state = 1;
						return true;
					}
					break;
				case 1:
					<>1__state = -3;
					<>m__Finally1();
					break;
				}
				return false;
			}
			catch
			{
				//try-fault
				((IDisposable)this).Dispose();
				throw;
			}
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		private void <>m__Finally1()
		{
			<>1__state = -1;
			Monitor.Exit(UpdateBorderLock);
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <UpdateBorderCore>d__6 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public bool enable;

		public BorderHelper <>4__this;

		private bool <gameIsStarting>5__2;

		private IntPtr <hWnd>5__3;

		private bool <isMaximized>5__4;

		private bool <isResolutionPreset>5__5;

		private int <sceneWidth>5__6;

		private int <sceneHeight>5__7;

		private NativeMethods.WindowRect <innerInitial>5__8;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <UpdateBorderCore>d__6(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			int num = <>1__state;
			BorderHelper borderHelper = <>4__this;
			switch (num)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<gameIsStarting>5__2 = (Object)(object)Console.instance == (Object)null;
				<>2__current = null;
				<>1__state = 1;
				return true;
			case 1:
			{
				<>1__state = -1;
				if (Screen.fullScreen)
				{
					return false;
				}
				<hWnd>5__3 = FindGameWindowHandle();
				if (<hWnd>5__3 == IntPtr.Zero)
				{
					return false;
				}
				bool flag = GameWindowHasBorder(<hWnd>5__3);
				if (enable == flag)
				{
					return false;
				}
				DebugMessage(">>> BEGIN");
				DebugMessage($"SetGameWindowBorder({enable})");
				<isMaximized>5__4 = NativeMethods.IsZoomed(<hWnd>5__3);
				<isResolutionPreset>5__5 = Screen.resolutions.Any((Resolution x) => Screen.width == ((Resolution)(ref x)).width && Screen.height == ((Resolution)(ref x)).height);
				<sceneWidth>5__6 = Screen.width;
				<sceneHeight>5__7 = Screen.height;
				DebugMessage(string.Format("Scene extents, {0}: {1}x{2} ({3}a preset)", <isMaximized>5__4 ? "maximized" : "windowed", Screen.width, Screen.height, <isResolutionPreset>5__5 ? "" : "not "));
				GetGameWindowRects(<hWnd>5__3, out var outer, out <innerInitial>5__8);
				DebugMessage($"Outer, initial: ({outer.Left},{outer.Top}) to ({outer.Right},{outer.Bottom})");
				DebugMessage($"Inner, initial: ({<innerInitial>5__8.Left},{<innerInitial>5__8.Top}) to ({<innerInitial>5__8.Right},{<innerInitial>5__8.Bottom})");
				if (!borderHelper.outerBorderThickness.HasValue)
				{
					if (!flag)
					{
						return false;
					}
					borderHelper.outerBorderThickness = borderHelper.ComputeBorderThickness(outer, <sceneWidth>5__6, <sceneHeight>5__7);
				}
				if (!<isMaximized>5__4 && !borderHelper.innerWindowedBorderThickness.HasValue)
				{
					if (!flag)
					{
						return false;
					}
					borderHelper.innerWindowedBorderThickness = borderHelper.ComputeBorderThickness(<innerInitial>5__8, <sceneWidth>5__6, <sceneHeight>5__7);
				}
				if (<isMaximized>5__4 && !borderHelper.innerMaximizedBorderThickness.HasValue)
				{
					if (!flag)
					{
						return false;
					}
					borderHelper.innerMaximizedBorderThickness = borderHelper.ComputeBorderThickness(<innerInitial>5__8, <sceneWidth>5__6, <sceneHeight>5__7);
				}
				SetGameWindowBorder(<hWnd>5__3, enable);
				<>2__current = null;
				<>1__state = 2;
				return true;
			}
			case 2:
			{
				<>1__state = -1;
				int left = <innerInitial>5__8.Left;
				int top = <innerInitial>5__8.Top;
				int num2 = <sceneWidth>5__6;
				int num3 = <sceneHeight>5__7;
				if (enable)
				{
					NativeMethods.WindowRect value = borderHelper.outerBorderThickness.Value;
					left -= value.Left;
					top -= value.Top;
					num2 = <sceneWidth>5__6 + value.Left + value.Right;
					if (<isResolutionPreset>5__5)
					{
						num3 += value.Top + value.Bottom;
					}
					else if (!<isMaximized>5__4)
					{
						NativeMethods.WindowRect value2 = borderHelper.innerWindowedBorderThickness.Value;
						num3 += value.Top - value2.Top + (value.Bottom - value2.Bottom);
					}
				}
				else
				{
					NativeMethods.WindowRect windowRect = (<isMaximized>5__4 ? borderHelper.innerMaximizedBorderThickness.Value : borderHelper.innerWindowedBorderThickness.Value);
					left += windowRect.Left;
					top += windowRect.Top;
					if (<gameIsStarting>5__2)
					{
						left--;
					}
					else if (!<isResolutionPreset>5__5)
					{
						num3 += windowRect.Top + windowRect.Bottom;
					}
				}
				uint flags = 52u;
				DebugMessage($"SetWindowPos( ... , {left} , {top} , {num2} , {num3} , ... );");
				if (num3 == <sceneHeight>5__7 && num2 != <sceneWidth>5__6)
				{
					NativeMethods.SetWindowPos(<hWnd>5__3, IntPtr.Zero, left, top, num2, num3 - 1, flags);
				}
				NativeMethods.SetWindowPos(<hWnd>5__3, IntPtr.Zero, left, top, num2, num3, flags);
				<>2__current = null;
				<>1__state = 3;
				return true;
			}
			case 3:
				<>1__state = -1;
				DebugMessage("<<< END\n");
				return false;
			}
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	private const uint GameWindowBorderFlags = 13565952u;

	private static object UpdateBorderLock = new object();

	private NativeMethods.WindowRect? outerBorderThickness;

	private NativeMethods.WindowRect? innerWindowedBorderThickness;

	private NativeMethods.WindowRect? innerMaximizedBorderThickness;

	[IteratorStateMachine(typeof(<UpdateBorder>d__5))]
	public IEnumerator UpdateBorder(bool enable)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <UpdateBorder>d__5(0)
		{
			<>4__this = this,
			enable = enable
		};
	}

	[IteratorStateMachine(typeof(<UpdateBorderCore>d__6))]
	private IEnumerator UpdateBorderCore(bool enable)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <UpdateBorderCore>d__6(0)
		{
			<>4__this = this,
			enable = enable
		};
	}

	private NativeMethods.WindowRect ComputeBorderThickness(NativeMethods.WindowRect outer, int width, int height)
	{
		NativeMethods.WindowRect result = default(NativeMethods.WindowRect);
		result.Left = (outer.Right - outer.Left - width) / 2;
		result.Right = result.Left;
		result.Bottom = outer.Bottom - outer.Top - height;
		DebugMessage($"Computed border thickness: LR {result.Left},{result.Right} to TB {result.Top},{result.Bottom}");
		return result;
	}

	private static void DebugMessage(string message)
	{
	}

	private static IntPtr FindGameWindowHandle()
	{
		IntPtr gameWindowHandle = IntPtr.Zero;
		NativeMethods.EnumDesktopWindows(lpfn: delegate(IntPtr hWnd, int lParam)
		{
			StringBuilder stringBuilder = new StringBuilder(255);
			NativeMethods.GetWindowText(hWnd, stringBuilder, stringBuilder.Capacity + 1);
			string text = stringBuilder.ToString();
			if (NativeMethods.IsWindowVisible(hWnd) && text == "Valheim")
			{
				gameWindowHandle = hWnd;
				return false;
			}
			return true;
		}, hDesktop: IntPtr.Zero, lParam: IntPtr.Zero);
		return gameWindowHandle;
	}

	private static bool GameWindowHasBorder(IntPtr hWnd)
	{
		if (hWnd != IntPtr.Zero)
		{
			return (NativeMethods.GetWindowLong(hWnd, -16) & 0xCF0000) != 0;
		}
		return false;
	}

	private unsafe static void GetGameWindowRects(IntPtr hWnd, out NativeMethods.WindowRect outer, out NativeMethods.WindowRect inner)
	{
		NativeMethods.GetWindowRect(hWnd, out var lpRect);
		outer = lpRect;
		NativeMethods.WindowRect windowRect = default(NativeMethods.WindowRect);
		NativeMethods.DwmGetWindowAttribute(hWnd, 9, &windowRect, sizeof(NativeMethods.WindowRect));
		inner = windowRect;
	}

	private static void SetGameWindowBorder(IntPtr hWnd, bool enable)
	{
		if (!(hWnd == IntPtr.Zero))
		{
			uint windowLong = NativeMethods.GetWindowLong(hWnd, -16);
			bool flag = (windowLong & 0xCF0000) != 0;
			if (enable != flag)
			{
				windowLong = ((!enable) ? (windowLong &= 0xFF30FFFFu) : (windowLong |= 0xCF0000u));
				NativeMethods.SetWindowLong(hWnd, -16, windowLong);
			}
		}
	}
}
public class NativeMethods
{
	public delegate bool EnumDesktopWindowsDelegate(IntPtr hwnd, int lParam);

	public struct WindowRect
	{
		public int Left;

		public int Top;

		public int Right;

		public int Bottom;
	}

	public const int DWMWA_EXTENDED_FRAME_BOUNDS = 9;

	public const int GWL_STYLE = -16;

	public const uint WS_MAXIMIZEBOX = 65536u;

	public const uint WS_MINIMIZEBOX = 131072u;

	public const uint WS_THICKFRAME = 262144u;

	public const uint WS_SYSMENU = 524288u;

	public const uint WS_DLGFRAME = 4194304u;

	public const uint WS_BORDER = 8388608u;

	public const uint SWP_ASYNCWINDOWPOS = 16384u;

	public const uint SWP_DEFERERASE = 8192u;

	public const uint SWP_DRAWFRAME = 32u;

	public const uint SWP_FRAMECHANGED = 32u;

	public const uint SWP_HIDEWINDOW = 128u;

	public const uint SWP_NOACTIVATE = 16u;

	public const uint SWP_NOCOPYBITS = 256u;

	public const uint SWP_NOMOVE = 2u;

	public const uint SWP_NOOWNERZORDER = 512u;

	public const uint SWP_NOREDRAW = 8u;

	public const uint SWP_NOREPOSITION = 512u;

	public const uint SWP_NOSENDCHANGING = 1024u;

	public const uint SWP_NOSIZE = 1u;

	public const uint SWP_NOZORDER = 4u;

	public const uint SWP_SHOWWINDOW = 64u;

	[DllImport("dwmapi.dll")]
	public unsafe static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, void* pvAttribute, int cbAttribute);

	[DllImport("user32.dll", SetLastError = true)]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumDesktopWindowsDelegate lpfn, IntPtr lParam);

	[DllImport("user32.dll", SetLastError = true)]
	public static extern uint GetWindowLong(IntPtr hWnd, int nIndex);

	[DllImport("user32.dll")]
	public static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);

	[DllImport("user32.dll")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool IsWindowVisible(IntPtr hWnd);

	[DllImport("user32.dll")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool IsZoomed(IntPtr hWnd);

	[DllImport("user32.dll", SetLastError = true)]
	public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint flags);

	[DllImport("user32.dll")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool GetWindowRect(IntPtr hWnd, out WindowRect lpRect);

	[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
	public static extern int GetWindowText(IntPtr hWnd, [Out] StringBuilder lpString, int nMaxCount);
}