Decompiled source of WiresNativeCursorSupport v0.3.2

WiresNativeCursorSupport.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using Valheim.SettingsGui;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("WiresNativeCursorSupport")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("WiresNativeCursorSupport")]
[assembly: AssemblyTitle("WiresNativeCursorSupport")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 WiresNativeCursorSupport
{
	internal static class AccessibilityIntegration
	{
		private static readonly FieldInfo ToggleBlockField = AccessTools.Field(typeof(AccessibilitySettings), "m_toggleBlockToggle");

		private static AccessibilitySettings _owner;

		private static Toggle _toggle;

		private static bool _openedValue;

		internal static void Initialize(AccessibilitySettings settings)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			if ((Object)(object)settings == (Object)null || ToggleBlockField == null)
			{
				return;
			}
			object? value = ToggleBlockField.GetValue(settings);
			Toggle val = (Toggle)((value is Toggle) ? value : null);
			if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).transform.parent == (Object)null)
			{
				return;
			}
			if ((Object)(object)_owner != (Object)(object)settings || (Object)(object)_toggle == (Object)null)
			{
				_owner = settings;
				_toggle = FindOrCreateToggle(val);
			}
			if (!((Object)(object)_toggle == (Object)null))
			{
				_openedValue = Plugin.RuntimeEnabled;
				_toggle.SetIsOnWithoutNotify(_openedValue);
				_toggle.onValueChanged = new ToggleEvent();
				((UnityEvent<bool>)(object)_toggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool runtimeEnabled)
				{
					Plugin.SetRuntimeEnabled(runtimeEnabled);
				});
				SetLabel(_toggle);
			}
		}

		private static Toggle FindOrCreateToggle(Toggle source)
		{
			Transform parent = ((Component)source).transform.parent;
			Transform val = parent.Find("WireNativeCursor");
			if ((Object)(object)val != (Object)null)
			{
				return ((Component)val).GetComponent<Toggle>();
			}
			GameObject obj = Object.Instantiate<GameObject>(((Component)source).gameObject, parent, false);
			((Object)obj).name = "WireNativeCursor";
			obj.transform.SetSiblingIndex(((Component)source).transform.GetSiblingIndex() + 1);
			Toggle component = obj.GetComponent<Toggle>();
			if ((Object)(object)component != (Object)null)
			{
				SetLabel(component);
			}
			return component;
		}

		private static void SetLabel(Toggle toggle)
		{
			if (!((Object)(object)toggle == (Object)null))
			{
				Transform obj = ((Component)toggle).transform.Find("Label");
				TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null);
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)toggle).GetComponentInChildren<TMP_Text>(true);
				}
				if ((Object)(object)val != (Object)null)
				{
					val.text = "Native System Cursor";
				}
			}
		}

		internal static void Save()
		{
			if (!((Object)(object)_toggle == (Object)null))
			{
				bool isOn = _toggle.isOn;
				PlayerPrefs.SetInt("WireNativeCursorSupport", isOn ? 1 : 0);
				PlayerPrefs.Save();
				_openedValue = isOn;
				Plugin.SetRuntimeEnabled(isOn);
			}
		}

		internal static void Cancel()
		{
			if ((Object)(object)_toggle != (Object)null)
			{
				_toggle.SetIsOnWithoutNotify(_openedValue);
			}
			Plugin.SetRuntimeEnabled(_openedValue);
		}

		internal static void ConfigureNavigation(AccessibilitySettings settings, Button okButton)
		{
			if (!((Object)(object)settings == (Object)null) && !((Object)(object)okButton == (Object)null) && !((Object)(object)_toggle == (Object)null) && !(ToggleBlockField == null))
			{
				object? value = ToggleBlockField.GetValue(settings);
				Toggle val = (Toggle)((value is Toggle) ? value : null);
				if (!((Object)(object)val == (Object)null))
				{
					GuiUtils.SetNavigationDown((Selectable)(object)val, (Selectable)(object)_toggle);
					GuiUtils.SetNavigationUp((Selectable)(object)_toggle, (Selectable)(object)val);
					GuiUtils.SetNavigationDown((Selectable)(object)_toggle, (Selectable)(object)okButton);
					GuiUtils.SetNavigationUp((Selectable)(object)okButton, (Selectable)(object)_toggle);
				}
			}
		}
	}
	[HarmonyPatch(typeof(AccessibilitySettings), "Initialize")]
	internal static class AccessibilityInitializePatch
	{
		[HarmonyPostfix]
		private static void Postfix(AccessibilitySettings __instance)
		{
			AccessibilityIntegration.Initialize(__instance);
		}
	}
	[HarmonyPatch(typeof(AccessibilitySettings), "OnOkAsync")]
	internal static class AccessibilityOkPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			AccessibilityIntegration.Save();
		}
	}
	[HarmonyPatch(typeof(AccessibilitySettings), "OnBack")]
	internal static class AccessibilityBackPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			AccessibilityIntegration.Cancel();
		}
	}
	[HarmonyPatch(typeof(AccessibilitySettings), "OnTabOpen")]
	internal static class AccessibilityTabOpenPatch
	{
		[HarmonyPostfix]
		private static void Postfix(AccessibilitySettings __instance, Button okButton)
		{
			AccessibilityIntegration.ConfigureNavigation(__instance, okButton);
		}
	}
	internal static class HoverCursorTracker
	{
		private const float MapDragThresholdPixels = 6f;

		private static readonly MethodInfo ClosestPinToCursorMethod = AccessTools.DeclaredMethod(typeof(Minimap), "GetClosestPinToCursor", (Type[])null, (Type[])null);

		private static EventSystem _eventSystem;

		private static PointerEventData _pointer;

		private static readonly List<RaycastResult> Results = new List<RaycastResult>(32);

		private static int _resolvedFrame = -1;

		private static NativeCursorKind _resolvedKind = NativeCursorKind.Arrow;

		private static bool _mapPointerDown;

		private static bool _mapDragging;

		private static Vector2 _mapPressPosition;

		internal static NativeCursorKind DesiredKind
		{
			get
			{
				if (_resolvedFrame == Time.frameCount)
				{
					return _resolvedKind;
				}
				_resolvedFrame = Time.frameCount;
				_resolvedKind = ResolveCurrentKind();
				return _resolvedKind;
			}
		}

		private static NativeCursorKind ResolveCurrentKind()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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_0141: Unknown result type (might be due to invalid IL or missing references)
			bool flag = IsLargeMapSurface();
			if (UpdateMapDragState(flag))
			{
				return NativeCursorKind.Move;
			}
			EventSystem current = EventSystem.current;
			if ((Object)(object)current == (Object)null)
			{
				return NativeCursorKind.Arrow;
			}
			if (_pointer == null || (Object)(object)_eventSystem != (Object)(object)current)
			{
				_eventSystem = current;
				_pointer = new PointerEventData(current);
			}
			((AbstractEventData)_pointer).Reset();
			_pointer.position = Vector2.op_Implicit(Input.mousePosition);
			Results.Clear();
			current.RaycastAll(_pointer, Results);
			if (Results.Count == 0)
			{
				return NativeCursorKind.Arrow;
			}
			RaycastResult val = Results[0];
			GameObject gameObject = ((RaycastResult)(ref val)).gameObject;
			if ((Object)(object)gameObject == (Object)null || !gameObject.activeInHierarchy)
			{
				return NativeCursorKind.Arrow;
			}
			TMP_InputField componentInParent = gameObject.GetComponentInParent<TMP_InputField>();
			if ((Object)(object)componentInParent != (Object)null && ((UIBehaviour)componentInParent).IsActive() && ((Selectable)componentInParent).IsInteractable())
			{
				return NativeCursorKind.Text;
			}
			InputField componentInParent2 = gameObject.GetComponentInParent<InputField>();
			if ((Object)(object)componentInParent2 != (Object)null && ((UIBehaviour)componentInParent2).IsActive() && ((Selectable)componentInParent2).IsInteractable())
			{
				return NativeCursorKind.Text;
			}
			InventoryElement componentInParent3 = gameObject.GetComponentInParent<InventoryElement>();
			if ((Object)(object)componentInParent3 != (Object)null)
			{
				InventoryGrid componentInParent4 = ((Component)componentInParent3).GetComponentInParent<InventoryGrid>();
				Inventory val2 = (((Object)(object)componentInParent4 != (Object)null) ? componentInParent4.GetInventory() : null);
				if (val2 != null && val2.GetItemAt(componentInParent3.Position.x, componentInParent3.Position.y) != null)
				{
					return NativeCursorKind.Hand;
				}
				return NativeCursorKind.Arrow;
			}
			if (flag && IsMapPinUnderPointer())
			{
				return NativeCursorKind.Hand;
			}
			return NativeCursorKind.Arrow;
		}

		private static bool UpdateMapDragState(bool pointerInsideLargeMap)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (!Input.GetMouseButton(0))
			{
				_mapPointerDown = false;
				_mapDragging = false;
				return false;
			}
			if (!_mapPointerDown)
			{
				if (!Input.GetMouseButtonDown(0) || !pointerInsideLargeMap)
				{
					return false;
				}
				_mapPointerDown = true;
				_mapDragging = false;
				_mapPressPosition = Vector2.op_Implicit(Input.mousePosition);
				return false;
			}
			if (!_mapDragging)
			{
				Vector2 val = Vector2.op_Implicit(Input.mousePosition) - _mapPressPosition;
				if (((Vector2)(ref val)).sqrMagnitude >= 36f)
				{
					_mapDragging = true;
				}
			}
			return _mapDragging;
		}

		private static bool IsLargeMapSurface()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_largeRoot == (Object)null || !instance.m_largeRoot.activeInHierarchy || (Object)(object)instance.m_mapImageLarge == (Object)null)
			{
				return false;
			}
			return RectTransformUtility.RectangleContainsScreenPoint(((Graphic)instance.m_mapImageLarge).rectTransform, Vector2.op_Implicit(Input.mousePosition));
		}

		private static bool IsMapPinUnderPointer()
		{
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || ClosestPinToCursorMethod == null)
			{
				return false;
			}
			try
			{
				return ClosestPinToCursorMethod.Invoke(instance, null) != null;
			}
			catch
			{
				return false;
			}
		}

		internal static void PruneDestroyed()
		{
		}

		internal static void Clear()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			Results.Clear();
			_pointer = null;
			_eventSystem = null;
			_resolvedFrame = -1;
			_resolvedKind = NativeCursorKind.Arrow;
			_mapPointerDown = false;
			_mapDragging = false;
			_mapPressPosition = Vector2.zero;
		}
	}
	internal static class LinuxNativeCursorBackend
	{
		private sealed class CursorFrame
		{
			internal Texture2D Texture;

			internal Vector2 Hotspot;

			internal int DelayMs;
		}

		private sealed class CursorFrames
		{
			internal readonly List<CursorFrame> Frames = new List<CursorFrame>();

			internal int TotalDelayMs;
		}

		private struct XcursorImageNative
		{
			internal uint version;

			internal uint size;

			internal uint width;

			internal uint height;

			internal uint xhot;

			internal uint yhot;

			internal uint delay;

			internal IntPtr pixels;
		}

		private struct XcursorImagesNative
		{
			internal int nimage;

			internal IntPtr images;

			internal IntPtr name;
		}

		private const string XcursorLib = "libXcursor.so.1";

		private const string X11Lib = "libX11.so.6";

		private static readonly Dictionary<NativeCursorKind, CursorFrames> Cursors = new Dictionary<NativeCursorKind, CursorFrames>();

		private static string _theme;

		private static int _size;

		private static bool _initialized;

		private static bool _dirty = true;

		private static NativeCursorKind _appliedKind = NativeCursorKind.Arrow;

		private static int _appliedFrame = -1;

		internal static bool Initialize()
		{
			try
			{
				ResolveThemeAndSize();
				Cursors.Clear();
				LoadKind(NativeCursorKind.Arrow, "left_ptr", "default", "arrow");
				LoadKind(NativeCursorKind.Hand, "pointer", "hand2", "hand1");
				LoadKind(NativeCursorKind.Text, "text", "xterm");
				LoadKind(NativeCursorKind.Move, "grabbing", "closedhand", "fleur", "move");
				LoadKind(NativeCursorKind.ResizeHorizontal, "ew-resize", "sb_h_double_arrow", "size_hor");
				LoadKind(NativeCursorKind.ResizeVertical, "ns-resize", "sb_v_double_arrow", "size_ver");
				LoadKind(NativeCursorKind.Crosshair, "crosshair", "cross");
				LoadKind(NativeCursorKind.Working, "progress", "left_ptr_watch", "half-busy", "watch");
				LoadKind(NativeCursorKind.Wait, "wait", "watch", "progress");
				_initialized = Cursors.ContainsKey(NativeCursorKind.Arrow);
				if (!_initialized)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogError((object)"Linux system cursor theme could not supply an Arrow cursor. Native cursor support will stay inactive.");
					}
					return false;
				}
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("Initialized Linux system cursor-theme backend " + string.Format("(theme='{0}', size={1}px, ", _theme ?? "default", _size) + $"loaded states={Cursors.Count}/9). " + "Cursor images come from the desktop Xcursor theme and are displayed through Unity, so the same backend works under X11, XWayland and native Wayland."));
				}
				return true;
			}
			catch (DllNotFoundException ex)
			{
				ManualLogSource log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogError((object)("Linux libXcursor is unavailable; native cursor support cannot load the desktop cursor theme. " + ex.Message));
				}
				_initialized = false;
				return false;
			}
			catch (Exception ex2)
			{
				ManualLogSource log4 = Plugin.Log;
				if (log4 != null)
				{
					log4.LogError((object)("Could not initialize Linux system cursor backend: " + ex2.Message));
				}
				_initialized = false;
				return false;
			}
		}

		internal static void Apply(NativeCursorKind kind, bool force)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if (!_initialized)
			{
				return;
			}
			CursorFrames frames = GetFrames(kind);
			if (frames != null && frames.Frames.Count != 0)
			{
				int frameIndex = GetFrameIndex(frames);
				if (force || _dirty || kind != _appliedKind || frameIndex != _appliedFrame)
				{
					CursorFrame cursorFrame = frames.Frames[frameIndex];
					Cursor.SetCursor(cursorFrame.Texture, cursorFrame.Hotspot, (CursorMode)0);
					_appliedKind = kind;
					_appliedFrame = frameIndex;
					_dirty = false;
				}
			}
		}

		internal static void MarkDirty()
		{
			_dirty = true;
		}

		internal static void RestoreDefault()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Cursor.SetCursor((Texture2D)null, Vector2.zero, (CursorMode)0);
			_dirty = true;
			_appliedFrame = -1;
			_appliedKind = NativeCursorKind.Arrow;
		}

		internal static void Shutdown()
		{
			foreach (CursorFrames value in Cursors.Values)
			{
				foreach (CursorFrame frame in value.Frames)
				{
					if ((Object)(object)frame.Texture != (Object)null)
					{
						Object.Destroy((Object)(object)frame.Texture);
					}
				}
			}
			Cursors.Clear();
			_initialized = false;
			_dirty = true;
			_appliedFrame = -1;
		}

		private static CursorFrames GetFrames(NativeCursorKind kind)
		{
			if (Cursors.TryGetValue(kind, out var value))
			{
				return value;
			}
			Cursors.TryGetValue(NativeCursorKind.Arrow, out value);
			return value;
		}

		private static int GetFrameIndex(CursorFrames frames)
		{
			if (frames.Frames.Count <= 1 || frames.TotalDelayMs <= 0)
			{
				return 0;
			}
			int num = (int)(Time.unscaledTime * 1000f) % frames.TotalDelayMs;
			int num2 = 0;
			for (int i = 0; i < frames.Frames.Count; i++)
			{
				int num3 = Math.Max(1, frames.Frames[i].DelayMs);
				num2 += num3;
				if (num < num2)
				{
					return i;
				}
			}
			return frames.Frames.Count - 1;
		}

		private static void ResolveThemeAndSize()
		{
			_theme = Environment.GetEnvironmentVariable("XCURSOR_THEME");
			_size = 0;
			string environmentVariable = Environment.GetEnvironmentVariable("XCURSOR_SIZE");
			if (!string.IsNullOrEmpty(environmentVariable))
			{
				int.TryParse(environmentVariable, out _size);
			}
			IntPtr intPtr = IntPtr.Zero;
			try
			{
				intPtr = XOpenDisplay(IntPtr.Zero);
				if (intPtr != IntPtr.Zero)
				{
					IntPtr intPtr2 = XcursorGetTheme(intPtr);
					if (intPtr2 != IntPtr.Zero)
					{
						string text = Marshal.PtrToStringAnsi(intPtr2);
						if (!string.IsNullOrEmpty(text))
						{
							_theme = text;
						}
					}
					int num = XcursorGetDefaultSize(intPtr);
					if (num > 0)
					{
						_size = num;
					}
				}
			}
			catch
			{
			}
			finally
			{
				if (intPtr != IntPtr.Zero)
				{
					try
					{
						XCloseDisplay(intPtr);
					}
					catch
					{
					}
				}
			}
			if (string.IsNullOrEmpty(_theme))
			{
				_theme = ReadDefaultThemeInheritance();
			}
			if (_size <= 0)
			{
				_size = 24;
			}
		}

		private static string ReadDefaultThemeInheritance()
		{
			try
			{
				string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
				string[] array = new string[2]
				{
					Path.Combine(folderPath, ".icons", "default", "index.theme"),
					Path.Combine(folderPath, ".local", "share", "icons", "default", "index.theme")
				};
				foreach (string path in array)
				{
					if (!File.Exists(path))
					{
						continue;
					}
					string[] array2 = File.ReadAllLines(path);
					for (int j = 0; j < array2.Length; j++)
					{
						string text = array2[j].Trim();
						if (text.StartsWith("Inherits=", StringComparison.OrdinalIgnoreCase))
						{
							string text2 = text.Substring("Inherits=".Length).Trim();
							int num = text2.IndexOfAny(new char[3] { ',', ';', ':' });
							if (num >= 0)
							{
								text2 = text2.Substring(0, num).Trim();
							}
							if (!string.IsNullOrEmpty(text2))
							{
								return text2;
							}
						}
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static void LoadKind(NativeCursorKind kind, params string[] names)
		{
			foreach (string text in names)
			{
				CursorFrames cursorFrames = LoadImages(text);
				if (cursorFrames != null && cursorFrames.Frames.Count != 0)
				{
					Cursors[kind] = cursorFrames;
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogDebug((object)($"Linux cursor '{kind}' -> Xcursor '{text}' " + $"({cursorFrames.Frames.Count} frame(s))."));
					}
					return;
				}
			}
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogWarning((object)($"Linux cursor theme has no native image for '{kind}'. " + "Arrow will be used for that state."));
			}
		}

		private static CursorFrames LoadImages(string name)
		{
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			IntPtr intPtr = XcursorLibraryLoadImages(name, _theme, _size);
			if (intPtr == IntPtr.Zero)
			{
				return null;
			}
			try
			{
				XcursorImagesNative xcursorImagesNative = Marshal.PtrToStructure<XcursorImagesNative>(intPtr);
				if (xcursorImagesNative.nimage <= 0 || xcursorImagesNative.images == IntPtr.Zero)
				{
					return null;
				}
				CursorFrames cursorFrames = new CursorFrames();
				for (int i = 0; i < xcursorImagesNative.nimage; i++)
				{
					IntPtr intPtr2 = Marshal.ReadIntPtr(xcursorImagesNative.images, i * IntPtr.Size);
					if (intPtr2 == IntPtr.Zero)
					{
						continue;
					}
					XcursorImageNative image = Marshal.PtrToStructure<XcursorImageNative>(intPtr2);
					if (image.width != 0 && image.height != 0 && !(image.pixels == IntPtr.Zero) && image.width <= 512 && image.height <= 512)
					{
						Texture2D val = CreateTexture(name, image);
						if (!((Object)(object)val == (Object)null))
						{
							CursorFrame cursorFrame = new CursorFrame
							{
								Texture = val,
								Hotspot = new Vector2((float)image.xhot, (float)image.yhot),
								DelayMs = (int)image.delay
							};
							cursorFrames.Frames.Add(cursorFrame);
							cursorFrames.TotalDelayMs += Math.Max(1, cursorFrame.DelayMs);
						}
					}
				}
				if (cursorFrames.Frames.Count == 0)
				{
					return null;
				}
				return cursorFrames;
			}
			finally
			{
				XcursorImagesDestroy(intPtr);
			}
		}

		private static Texture2D CreateTexture(string cursorName, XcursorImageNative image)
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			int num;
			int num2;
			int[] array;
			Color32[] array2;
			checked
			{
				num = (int)image.width;
				num2 = (int)image.height;
				int num3 = num * num2;
				array = new int[num3];
				Marshal.Copy(image.pixels, array, 0, num3);
				array2 = (Color32[])(object)new Color32[num3];
			}
			for (int i = 0; i < num2; i++)
			{
				int num4 = num2 - 1 - i;
				for (int j = 0; j < num; j++)
				{
					int num5 = array[i * num + j];
					byte b = (byte)((uint)num5 >> 24);
					byte b2 = (byte)((uint)num5 >> 16);
					byte b3 = (byte)((uint)num5 >> 8);
					byte b4 = (byte)num5;
					if (b > 0 && b < byte.MaxValue)
					{
						b2 = Unpremultiply(b2, b);
						b3 = Unpremultiply(b3, b);
						b4 = Unpremultiply(b4, b);
					}
					array2[num4 * num + j] = new Color32(b2, b3, b4, b);
				}
			}
			Texture2D val = new Texture2D(num, num2, (TextureFormat)4, false, false)
			{
				name = "WireNativeCursor_Linux_" + cursorName,
				hideFlags = (HideFlags)61
			};
			val.SetPixels32(array2);
			val.Apply(false, false);
			return val;
		}

		private static byte Unpremultiply(byte value, byte alpha)
		{
			int val = (value * 255 + alpha / 2) / alpha;
			return (byte)Math.Min(255, val);
		}

		[DllImport("libXcursor.so.1", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		private static extern IntPtr XcursorLibraryLoadImages(string name, string theme, int size);

		[DllImport("libXcursor.so.1", CallingConvention = CallingConvention.Cdecl)]
		private static extern void XcursorImagesDestroy(IntPtr images);

		[DllImport("libXcursor.so.1", CallingConvention = CallingConvention.Cdecl)]
		private static extern IntPtr XcursorGetTheme(IntPtr display);

		[DllImport("libXcursor.so.1", CallingConvention = CallingConvention.Cdecl)]
		private static extern int XcursorGetDefaultSize(IntPtr display);

		[DllImport("libX11.so.6", CallingConvention = CallingConvention.Cdecl)]
		private static extern IntPtr XOpenDisplay(IntPtr displayName);

		[DllImport("libX11.so.6", CallingConvention = CallingConvention.Cdecl)]
		private static extern int XCloseDisplay(IntPtr display);
	}
	internal static class MacNativeCursorBackend
	{
		private const string ObjCLib = "/usr/lib/libobjc.A.dylib";

		private static readonly Dictionary<NativeCursorKind, IntPtr> Cursors = new Dictionary<NativeCursorKind, IntPtr>();

		private static IntPtr _nsCursorClass;

		private static IntPtr _setSelector;

		private static IntPtr _currentCursorSelector;

		private static IntPtr _respondsSelector;

		private static bool _initialized;

		internal static bool Initialize()
		{
			try
			{
				_nsCursorClass = objc_getClass("NSCursor");
				if (_nsCursorClass == IntPtr.Zero)
				{
					return false;
				}
				_setSelector = sel_registerName("set");
				_currentCursorSelector = sel_registerName("currentCursor");
				_respondsSelector = sel_registerName("respondsToSelector:");
				Cursors.Clear();
				Cursors[NativeCursorKind.Arrow] = GetClassCursor("arrowCursor");
				Cursors[NativeCursorKind.Hand] = GetClassCursor("pointingHandCursor");
				Cursors[NativeCursorKind.Text] = GetClassCursor("IBeamCursor");
				Cursors[NativeCursorKind.Move] = GetClassCursor("closedHandCursor", "openHandCursor");
				Cursors[NativeCursorKind.ResizeHorizontal] = GetClassCursor("columnResizeCursor", "resizeLeftRightCursor");
				Cursors[NativeCursorKind.ResizeVertical] = GetClassCursor("rowResizeCursor", "resizeUpDownCursor");
				Cursors[NativeCursorKind.Crosshair] = GetClassCursor("crosshairCursor");
				IntPtr intPtr = GetClassCursor("busyButClickableCursor");
				if (intPtr == IntPtr.Zero)
				{
					intPtr = Cursors[NativeCursorKind.Arrow];
				}
				Cursors[NativeCursorKind.Working] = intPtr;
				Cursors[NativeCursorKind.Wait] = intPtr;
				_initialized = Cursors[NativeCursorKind.Arrow] != IntPtr.Zero;
				if (_initialized)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogInfo((object)"Initialized macOS AppKit system cursor backend (Arrow, Pointing Hand, I-beam, Closed Hand, Resize, Crosshair, Busy/Progress where available).");
					}
				}
				return _initialized;
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogError((object)("Could not initialize macOS AppKit cursor backend: " + ex.Message));
				}
				_initialized = false;
				return false;
			}
		}

		internal static void Apply(NativeCursorKind kind, bool force)
		{
			if (_initialized)
			{
				IntPtr cursor = GetCursor(kind);
				if (!(cursor == IntPtr.Zero) && (force || !(objc_msgSend(_nsCursorClass, _currentCursorSelector) == cursor)))
				{
					objc_msgSend_void(cursor, _setSelector);
				}
			}
		}

		internal static void RestoreDefault()
		{
			if (_initialized)
			{
				Apply(NativeCursorKind.Arrow, force: true);
			}
		}

		internal static void Shutdown()
		{
			Cursors.Clear();
			_initialized = false;
			_nsCursorClass = IntPtr.Zero;
		}

		private static IntPtr GetCursor(NativeCursorKind kind)
		{
			if (Cursors.TryGetValue(kind, out var value) && value != IntPtr.Zero)
			{
				return value;
			}
			if (Cursors.TryGetValue(NativeCursorKind.Arrow, out value))
			{
				return value;
			}
			return IntPtr.Zero;
		}

		private static IntPtr GetClassCursor(params string[] selectors)
		{
			for (int i = 0; i < selectors.Length; i++)
			{
				IntPtr intPtr = sel_registerName(selectors[i]);
				if (!(intPtr == IntPtr.Zero) && objc_msgSend_bool(_nsCursorClass, _respondsSelector, intPtr))
				{
					IntPtr intPtr2 = objc_msgSend(_nsCursorClass, intPtr);
					if (intPtr2 != IntPtr.Zero)
					{
						return intPtr2;
					}
				}
			}
			return IntPtr.Zero;
		}

		[DllImport("/usr/lib/libobjc.A.dylib", CallingConvention = CallingConvention.Cdecl)]
		private static extern IntPtr objc_getClass(string name);

		[DllImport("/usr/lib/libobjc.A.dylib", CallingConvention = CallingConvention.Cdecl)]
		private static extern IntPtr sel_registerName(string name);

		[DllImport("/usr/lib/libobjc.A.dylib", CallingConvention = CallingConvention.Cdecl)]
		private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector);

		[DllImport("/usr/lib/libobjc.A.dylib", CallingConvention = CallingConvention.Cdecl, EntryPoint = "objc_msgSend")]
		private static extern void objc_msgSend_void(IntPtr receiver, IntPtr selector);

		[DllImport("/usr/lib/libobjc.A.dylib", CallingConvention = CallingConvention.Cdecl, EntryPoint = "objc_msgSend")]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern bool objc_msgSend_bool(IntPtr receiver, IntPtr selector, IntPtr argument);
	}
	internal enum NativeCursorKind
	{
		Arrow,
		Hand,
		Text,
		Move,
		ResizeHorizontal,
		ResizeVertical,
		Crosshair,
		Working,
		Wait
	}
	internal static class NativeCursorController
	{
		private enum HostCursorPlatform
		{
			Unsupported,
			Windows,
			MacOS,
			Linux
		}

		[UnmanagedFunctionPointer(CallingConvention.Winapi)]
		private delegate IntPtr WindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

		private static class NativeMethods
		{
			internal struct POINT
			{
				internal int x;

				internal int y;
			}

			internal struct CURSORINFO
			{
				internal int cbSize;

				internal int flags;

				internal IntPtr hCursor;

				internal POINT ptScreenPos;
			}

			internal struct RECT
			{
				internal int left;

				internal int top;

				internal int right;

				internal int bottom;
			}

			internal delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

			internal const int CURSOR_SHOWING = 1;

			[DllImport("user32.dll", CharSet = CharSet.Auto)]
			internal static extern IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName);

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

			[DllImport("user32.dll")]
			[return: MarshalAs(UnmanagedType.Bool)]
			internal static extern bool GetCursorInfo(ref CURSORINFO pci);

			[DllImport("user32.dll")]
			[return: MarshalAs(UnmanagedType.Bool)]
			internal static extern bool GetCursorPos(out POINT lpPoint);

			[DllImport("user32.dll")]
			[return: MarshalAs(UnmanagedType.Bool)]
			internal static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);

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

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

			[DllImport("user32.dll")]
			[return: MarshalAs(UnmanagedType.Bool)]
			internal static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);

			[DllImport("user32.dll")]
			internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

			[DllImport("user32.dll", CharSet = CharSet.Unicode)]
			internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

			[DllImport("user32.dll", CharSet = CharSet.Unicode)]
			internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

			[DllImport("user32.dll")]
			[return: MarshalAs(UnmanagedType.Bool)]
			internal static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);

			[DllImport("user32.dll", EntryPoint = "SetWindowLongPtrW", SetLastError = true)]
			private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

			[DllImport("user32.dll", EntryPoint = "SetWindowLongW", SetLastError = true)]
			private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, int dwNewLong);

			[DllImport("user32.dll", EntryPoint = "GetWindowLongPtrW", SetLastError = true)]
			private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);

			[DllImport("user32.dll", EntryPoint = "GetWindowLongW", SetLastError = true)]
			private static extern int GetWindowLong32(IntPtr hWnd, int nIndex);

			[DllImport("user32.dll", EntryPoint = "CallWindowProcW")]
			internal static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

			[DllImport("user32.dll", EntryPoint = "DefWindowProcW")]
			internal static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

			internal static IntPtr SetClassLongPtr(IntPtr hWnd, int nIndex, IntPtr value)
			{
				return IntPtr.Zero;
			}

			internal static IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex)
			{
				return IntPtr.Zero;
			}

			internal static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr value)
			{
				if (IntPtr.Size == 8)
				{
					return SetWindowLongPtr64(hWnd, nIndex, value);
				}
				return new IntPtr(SetWindowLong32(hWnd, nIndex, value.ToInt32()));
			}

			internal static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
			{
				if (IntPtr.Size == 8)
				{
					return GetWindowLongPtr64(hWnd, nIndex);
				}
				return new IntPtr(GetWindowLong32(hWnd, nIndex));
			}
		}

		private const int IDC_ARROW = 32512;

		private const int IDC_IBEAM = 32513;

		private const int IDC_WAIT = 32514;

		private const int IDC_CROSS = 32515;

		private const int IDC_SIZEWE = 32644;

		private const int IDC_SIZENS = 32645;

		private const int IDC_SIZEALL = 32646;

		private const int IDC_HAND = 32649;

		private const int IDC_APPSTARTING = 32650;

		private const int GWL_WNDPROC = -4;

		private const int GCLP_HCURSOR = -12;

		private const uint WM_SETCURSOR = 32u;

		private const uint WM_MOUSEACTIVATE = 33u;

		private const uint WM_INPUT = 255u;

		private const uint WM_MOUSEMOVE = 512u;

		private const uint WM_LBUTTONDOWN = 513u;

		private const uint WM_LBUTTONUP = 514u;

		private const uint WM_RBUTTONDOWN = 516u;

		private const uint WM_RBUTTONUP = 517u;

		private const uint WM_MBUTTONDOWN = 519u;

		private const uint WM_MBUTTONUP = 520u;

		private const uint WM_MOUSEWHEEL = 522u;

		private const uint WM_XBUTTONDOWN = 523u;

		private const uint WM_XBUTTONUP = 524u;

		private const uint WM_MOUSEHWHEEL = 526u;

		private const uint WM_CAPTURECHANGED = 533u;

		private const uint WM_MOUSEHOVER = 673u;

		private const uint WM_MOUSELEAVE = 675u;

		private const int HTCLIENT = 1;

		private static readonly WindowProc HookDelegate = WindowProcHook;

		private static readonly IntPtr HookDelegatePtr = Marshal.GetFunctionPointerForDelegate(HookDelegate);

		private static readonly FieldInfo CraftTimerField = AccessTools.Field(typeof(InventoryGui), "m_craftTimer");

		private static readonly FieldInfo DragItemField = AccessTools.Field(typeof(InventoryGui), "m_dragItem");

		private static readonly HashSet<NativeCursorKind> VerifiedKinds = new HashSet<NativeCursorKind>();

		private static bool _supported;

		private static bool _enabled;

		private static HostCursorPlatform _hostPlatform = HostCursorPlatform.Unsupported;

		private static IntPtr _arrow;

		private static IntPtr _hand;

		private static IntPtr _text;

		private static IntPtr _move;

		private static IntPtr _resizeHorizontal;

		private static IntPtr _resizeVertical;

		private static IntPtr _crosshair;

		private static IntPtr _working;

		private static IntPtr _wait;

		private static IntPtr _window;

		private static IntPtr _previousWindowProc;

		private static bool _hookInstalled;

		private static float _nextHookAttempt;

		private static IntPtr _capturedGameCursor;

		private static bool _capturedGameCursorValid;

		private static bool _unitySystemDefaultInstalled;

		private static Texture2D _transparentUnityCursor;

		private static IntPtr _originalClassCursor;

		private static bool _originalClassCursorCaptured;

		private static IntPtr _lastClassCursor;

		private static NativeCursorKind _desiredKind = NativeCursorKind.Arrow;

		private static bool _lastWait;

		private static bool _lastCrafting;

		private static bool _lastShouldControl;

		private static bool _loggedUnityOverwrite;

		private static int _overwriteCorrections;

		internal static void Initialize()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Invalid comparison between Unknown and I4
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Invalid comparison between Unknown and I4
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Invalid comparison between Unknown and I4
			RuntimePlatform platform = Application.platform;
			if ((int)platform == 2 || (int)platform == 7)
			{
				_hostPlatform = HostCursorPlatform.Windows;
				_supported = InitializeWindowsBackend();
			}
			else if ((int)platform == 1 || (int)platform == 0)
			{
				_hostPlatform = HostCursorPlatform.MacOS;
				_supported = MacNativeCursorBackend.Initialize();
				if (_supported)
				{
					CreateTransparentUnityCursor();
				}
			}
			else if ((int)platform == 13 || (int)platform == 16)
			{
				_hostPlatform = HostCursorPlatform.Linux;
				_supported = LinuxNativeCursorBackend.Initialize();
			}
			else
			{
				_hostPlatform = HostCursorPlatform.Unsupported;
				_supported = false;
			}
			if (!_supported)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("Native cursor support is inactive on " + $"platform '{platform}'."));
				}
			}
			else
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("Native cursor backend active: " + _hostPlatform.ToString() + "."));
				}
			}
		}

		private static bool InitializeWindowsBackend()
		{
			_arrow = NativeMethods.LoadCursor(IntPtr.Zero, new IntPtr(32512));
			_hand = NativeMethods.LoadCursor(IntPtr.Zero, new IntPtr(32649));
			_text = NativeMethods.LoadCursor(IntPtr.Zero, new IntPtr(32513));
			_move = NativeMethods.LoadCursor(IntPtr.Zero, new IntPtr(32646));
			_resizeHorizontal = NativeMethods.LoadCursor(IntPtr.Zero, new IntPtr(32644));
			_resizeVertical = NativeMethods.LoadCursor(IntPtr.Zero, new IntPtr(32645));
			_crosshair = NativeMethods.LoadCursor(IntPtr.Zero, new IntPtr(32515));
			_working = NativeMethods.LoadCursor(IntPtr.Zero, new IntPtr(32650));
			_wait = NativeMethods.LoadCursor(IntPtr.Zero, new IntPtr(32514));
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("Loaded Windows system cursors: " + $"arrow=0x{_arrow.ToInt64():X}, " + $"hand=0x{_hand.ToInt64():X}, " + $"text=0x{_text.ToInt64():X}, " + $"move=0x{_move.ToInt64():X}, " + $"resizeH=0x{_resizeHorizontal.ToInt64():X}, " + $"resizeV=0x{_resizeVertical.ToInt64():X}, " + $"crosshair=0x{_crosshair.ToInt64():X}, " + $"working=0x{_working.ToInt64():X}, " + $"wait=0x{_wait.ToInt64():X}."));
			}
			if (_arrow == IntPtr.Zero)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogError((object)"Windows did not return the standard arrow cursor handle.");
				}
				return false;
			}
			CreateTransparentUnityCursor();
			return true;
		}

		private static void CreateTransparentUnityCursor()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			if (!((Object)(object)_transparentUnityCursor != (Object)null))
			{
				_transparentUnityCursor = new Texture2D(32, 32, (TextureFormat)4, false, false);
				((Object)_transparentUnityCursor).name = "WireNativeCursor_TransparentUnityCursor";
				((Object)_transparentUnityCursor).hideFlags = (HideFlags)61;
				_transparentUnityCursor.SetPixels32((Color32[])(object)new Color32[1024]);
				_transparentUnityCursor.Apply(false, false);
			}
		}

		internal static void SetEnabled(bool enabled)
		{
			_enabled = enabled && _supported;
			if (!_enabled)
			{
				RestorePlatformCursor();
			}
			else
			{
				RefreshControlState(forceApply: true);
			}
		}

		internal static void Shutdown()
		{
			_enabled = false;
			RestorePlatformCursor();
			if (_hostPlatform == HostCursorPlatform.MacOS)
			{
				MacNativeCursorBackend.Shutdown();
			}
			else if (_hostPlatform == HostCursorPlatform.Linux)
			{
				LinuxNativeCursorBackend.Shutdown();
			}
			HoverCursorTracker.Clear();
			VerifiedKinds.Clear();
			_capturedGameCursor = IntPtr.Zero;
			_capturedGameCursorValid = false;
			_unitySystemDefaultInstalled = false;
			if ((Object)(object)_transparentUnityCursor != (Object)null)
			{
				Object.Destroy((Object)(object)_transparentUnityCursor);
				_transparentUnityCursor = null;
			}
			_originalClassCursor = IntPtr.Zero;
			_originalClassCursorCaptured = false;
			_lastClassCursor = IntPtr.Zero;
			_loggedUnityOverwrite = false;
			_overwriteCorrections = 0;
			_hostPlatform = HostCursorPlatform.Unsupported;
			_supported = false;
		}

		private static void RestorePlatformCursor()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			switch (_hostPlatform)
			{
			case HostCursorPlatform.Windows:
				RestoreVanillaCursorState();
				RemoveWindowHook();
				break;
			case HostCursorPlatform.MacOS:
				Cursor.SetCursor((Texture2D)null, Vector2.zero, (CursorMode)0);
				MacNativeCursorBackend.RestoreDefault();
				_unitySystemDefaultInstalled = false;
				break;
			case HostCursorPlatform.Linux:
				LinuxNativeCursorBackend.RestoreDefault();
				break;
			}
			_desiredKind = NativeCursorKind.Arrow;
		}

		internal static void LateTick()
		{
			if (_enabled && _supported)
			{
				bool num = (_lastShouldControl = ShouldControlCursor());
				_lastWait = IsWaitState();
				_lastCrafting = IsCrafting();
				if (num)
				{
					ReconcileDesiredCursor("LateUpdate", forceApply: false);
				}
			}
		}

		internal static void OnBeforeRender()
		{
			if (_enabled && _supported && ShouldControlCursor())
			{
				ReconcileDesiredCursor("BeforeRender", forceApply: false);
			}
		}

		internal static void EndOfFrameTick()
		{
			if (_enabled && _supported && ShouldControlCursor())
			{
				ReconcileDesiredCursor("EndOfFrame", forceApply: false);
			}
		}

		internal static void OnUnityCursorVisibilityApplied()
		{
			if (_enabled && _supported && ShouldControlCursor())
			{
				ReconcileDesiredCursor("ZCursor.SetVisible", forceApply: false);
			}
		}

		internal static void OnCursorContextChanged()
		{
			if (_enabled)
			{
				RefreshControlState(forceApply: true);
			}
		}

		internal static void OnApplicationFocusChanged(bool focused)
		{
			if (_enabled && focused)
			{
				if (_hostPlatform == HostCursorPlatform.Windows || _hostPlatform == HostCursorPlatform.MacOS)
				{
					_unitySystemDefaultInstalled = false;
				}
				else if (_hostPlatform == HostCursorPlatform.Linux)
				{
					LinuxNativeCursorBackend.MarkDirty();
				}
				if (_hostPlatform == HostCursorPlatform.Windows)
				{
					_nextHookAttempt = 0f;
				}
				RefreshControlState(forceApply: true);
			}
		}

		internal static void OnHoverStateChanged()
		{
			if (_enabled)
			{
				RefreshControlState(forceApply: true);
			}
		}

		private static void RefreshControlState(bool forceApply)
		{
			if (_enabled && _supported && ShouldControlCursor())
			{
				ReconcileDesiredCursor("ContextChanged", forceApply);
			}
		}

		private static void ReconcileDesiredCursor(string source, bool forceApply)
		{
			if (!_enabled || !_supported || !ShouldControlCursor())
			{
				return;
			}
			NativeCursorKind nativeCursorKind = DetermineCursorKind();
			bool flag = nativeCursorKind != _desiredKind;
			_desiredKind = nativeCursorKind;
			if (_hostPlatform == HostCursorPlatform.Linux)
			{
				LinuxNativeCursorBackend.Apply(nativeCursorKind, flag || forceApply);
			}
			else if (_hostPlatform == HostCursorPlatform.MacOS)
			{
				EnsureUnityUsesSystemDefault();
				MacNativeCursorBackend.Apply(nativeCursorKind, flag || forceApply);
			}
			else if (_hostPlatform == HostCursorPlatform.Windows)
			{
				CaptureGameCursorIfPossible();
				if (!_hookInstalled && Time.unscaledTime >= _nextHookAttempt)
				{
					EnsureWindowHook();
				}
				EnsureUnityUsesSystemDefault();
				if (flag || forceApply)
				{
					ApplyDesiredCursor();
				}
				EnsureDesiredCursorPresent(source);
			}
		}

		private static void EnsureUnityUsesSystemDefault()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_transparentUnityCursor == (Object)null) && !_unitySystemDefaultInstalled)
			{
				Cursor.SetCursor(_transparentUnityCursor, Vector2.zero, (CursorMode)0);
				_unitySystemDefaultInstalled = true;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Masked Unity's own cursor layer with a transparent CPU-readable RGBA32 cursor.");
				}
			}
		}

		private static void CaptureOriginalClassCursor()
		{
			if (!_originalClassCursorCaptured && !(_window == IntPtr.Zero) && NativeMethods.IsWindow(_window))
			{
				_originalClassCursor = NativeMethods.GetClassLongPtr(_window, -12);
				_originalClassCursorCaptured = true;
			}
		}

		private static void UpdateWindowClassCursor()
		{
			if (!(_window == IntPtr.Zero) && NativeMethods.IsWindow(_window))
			{
				CaptureOriginalClassCursor();
				IntPtr intPtr = GetHandle(_desiredKind);
				if (intPtr == IntPtr.Zero)
				{
					intPtr = _arrow;
				}
				if (!(intPtr == IntPtr.Zero) && !(intPtr == _lastClassCursor))
				{
					NativeMethods.SetClassLongPtr(_window, -12, intPtr);
					_lastClassCursor = intPtr;
				}
			}
		}

		private static void RestoreVanillaCursorState()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (_supported)
			{
				Cursor.SetCursor((Texture2D)null, Vector2.zero, (CursorMode)0);
				IntPtr intPtr = (_capturedGameCursorValid ? _capturedGameCursor : _originalClassCursor);
				if (_window != IntPtr.Zero && NativeMethods.IsWindow(_window) && intPtr != IntPtr.Zero)
				{
					NativeMethods.SetClassLongPtr(_window, -12, intPtr);
					_lastClassCursor = intPtr;
				}
				if (intPtr != IntPtr.Zero)
				{
					NativeMethods.SetCursor(intPtr);
				}
				_unitySystemDefaultInstalled = false;
				_desiredKind = NativeCursorKind.Arrow;
			}
		}

		private static NativeCursorKind DetermineCursorKind()
		{
			if (IsWaitState())
			{
				return NativeCursorKind.Wait;
			}
			if (IsCrafting())
			{
				return NativeCursorKind.Working;
			}
			if (IsDraggingInventoryItem())
			{
				return NativeCursorKind.Move;
			}
			return HoverCursorTracker.DesiredKind;
		}

		private static bool IsWaitState()
		{
			FejdStartup instance = FejdStartup.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if (!((Object)(object)instance.m_loading != (Object)null) || !instance.m_loading.activeInHierarchy)
			{
				if ((Object)(object)instance.m_pleaseWait != (Object)null)
				{
					return instance.m_pleaseWait.activeInHierarchy;
				}
				return false;
			}
			return true;
		}

		private static bool IsCrafting()
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || !InventoryGui.IsVisible() || CraftTimerField == null)
			{
				return false;
			}
			if (CraftTimerField.GetValue(instance) is float num)
			{
				return num >= 0f;
			}
			return false;
		}

		private static bool IsDraggingInventoryItem()
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || !InventoryGui.IsVisible() || DragItemField == null)
			{
				return false;
			}
			return DragItemField.GetValue(instance) != null;
		}

		private static bool ShouldControlCursor()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return Application.isFocused && (int)Cursor.lockState == 0 && ZCursor.IsRequested && ZCursor.IsVisible && ZInput.IsMouseActive() && !ZInput.IsTouchActive() && !ZInput.IsGamepadMouseActive() && (_hostPlatform != HostCursorPlatform.Windows || IsCursorInsideClientArea());
			}
			catch
			{
				return false;
			}
		}

		private static bool IsCursorInsideClientArea()
		{
			if (_window == IntPtr.Zero || !NativeMethods.IsWindow(_window))
			{
				return true;
			}
			if (!NativeMethods.GetCursorPos(out var lpPoint))
			{
				return true;
			}
			if (!NativeMethods.ScreenToClient(_window, ref lpPoint))
			{
				return true;
			}
			if (!NativeMethods.GetClientRect(_window, out var lpRect))
			{
				return true;
			}
			if (lpPoint.x >= lpRect.left && lpPoint.y >= lpRect.top && lpPoint.x < lpRect.right)
			{
				return lpPoint.y < lpRect.bottom;
			}
			return false;
		}

		private static void ApplyDesiredCursor()
		{
			if (ShouldControlCursor())
			{
				IntPtr intPtr = GetHandle(_desiredKind);
				if (intPtr == IntPtr.Zero)
				{
					intPtr = _arrow;
				}
				if (!(intPtr == IntPtr.Zero))
				{
					NativeMethods.SetCursor(intPtr);
					VerifyCursor(intPtr, _desiredKind);
				}
			}
		}

		private static void EnsureDesiredCursorPresent(string source)
		{
			if (!ShouldControlCursor())
			{
				return;
			}
			IntPtr intPtr = GetHandle(_desiredKind);
			if (intPtr == IntPtr.Zero)
			{
				intPtr = _arrow;
			}
			if (intPtr == IntPtr.Zero)
			{
				return;
			}
			NativeMethods.CURSORINFO pci = CreateCursorInfo();
			bool flag = NativeMethods.GetCursorInfo(ref pci) && (pci.flags & 1) != 0;
			if (flag && pci.hCursor == intPtr)
			{
				return;
			}
			NativeMethods.SetCursor(intPtr);
			_overwriteCorrections++;
			if (!_loggedUnityOverwrite)
			{
				_loggedUnityOverwrite = true;
				string arg = ((flag && pci.hCursor != IntPtr.Zero) ? $"0x{pci.hCursor.ToInt64():X}" : "hidden/unknown");
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("Detected Unity replacing the native cursor " + $"(actual={arg}, expected=0x{intPtr.ToInt64():X}, " + "source=" + source + "). Persistent cursor reassertion is now active."));
				}
			}
			VerifyCursor(intPtr, _desiredKind);
		}

		private static bool IsMouseCursorMessage(uint msg)
		{
			switch (msg)
			{
			case 33u:
			case 255u:
			case 512u:
			case 513u:
			case 514u:
			case 516u:
			case 517u:
			case 519u:
			case 520u:
			case 522u:
			case 523u:
			case 524u:
			case 526u:
			case 533u:
			case 673u:
			case 675u:
				return true;
			default:
				return false;
			}
		}

		private static IntPtr GetHandle(NativeCursorKind kind)
		{
			switch (kind)
			{
			case NativeCursorKind.Hand:
				return _hand;
			case NativeCursorKind.Text:
				return _text;
			case NativeCursorKind.Move:
				if (!(_move != IntPtr.Zero))
				{
					return _hand;
				}
				return _move;
			case NativeCursorKind.ResizeHorizontal:
				if (!(_resizeHorizontal != IntPtr.Zero))
				{
					return _arrow;
				}
				return _resizeHorizontal;
			case NativeCursorKind.ResizeVertical:
				if (!(_resizeVertical != IntPtr.Zero))
				{
					return _arrow;
				}
				return _resizeVertical;
			case NativeCursorKind.Crosshair:
				if (!(_crosshair != IntPtr.Zero))
				{
					return _arrow;
				}
				return _crosshair;
			case NativeCursorKind.Working:
				return _working;
			case NativeCursorKind.Wait:
				return _wait;
			default:
				return _arrow;
			}
		}

		private static void CaptureGameCursorIfPossible()
		{
			if (_capturedGameCursorValid)
			{
				return;
			}
			NativeMethods.CURSORINFO pci = CreateCursorInfo();
			if (NativeMethods.GetCursorInfo(ref pci) && (pci.flags & 1) != 0 && !(pci.hCursor == IntPtr.Zero))
			{
				_capturedGameCursor = pci.hCursor;
				_capturedGameCursorValid = true;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("Captured Valheim/Unity cursor handle before override: " + $"0x{_capturedGameCursor.ToInt64():X}."));
				}
			}
		}

		private static void RestoreCapturedGameCursor()
		{
			RestoreVanillaCursorState();
		}

		private static void VerifyCursor(IntPtr expected, NativeCursorKind kind)
		{
			if (VerifiedKinds.Contains(kind))
			{
				return;
			}
			NativeMethods.CURSORINFO pci = CreateCursorInfo();
			if (NativeMethods.GetCursorInfo(ref pci) && (pci.flags & 1) != 0 && pci.hCursor == expected)
			{
				VerifiedKinds.Add(kind);
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)($"Runtime verified native cursor state '{kind}' " + $"(Windows reports handle 0x{expected.ToInt64():X})."));
				}
			}
		}

		private static NativeMethods.CURSORINFO CreateCursorInfo()
		{
			return new NativeMethods.CURSORINFO
			{
				cbSize = Marshal.SizeOf(typeof(NativeMethods.CURSORINFO))
			};
		}

		private static void EnsureWindowHook()
		{
			if (_hookInstalled)
			{
				if (_window != IntPtr.Zero && NativeMethods.IsWindow(_window))
				{
					return;
				}
				_hookInstalled = false;
				_window = IntPtr.Zero;
				_previousWindowProc = IntPtr.Zero;
			}
			if (Time.unscaledTime < _nextHookAttempt)
			{
				return;
			}
			_nextHookAttempt = Time.unscaledTime + 1f;
			IntPtr intPtr = FindUnityWindow();
			if (intPtr == IntPtr.Zero)
			{
				return;
			}
			IntPtr intPtr2 = NativeMethods.SetWindowLongPtr(intPtr, -4, HookDelegatePtr);
			if (intPtr2 == IntPtr.Zero)
			{
				int lastWin32Error = Marshal.GetLastWin32Error();
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not subclass the Valheim window for " + $"WM_SETCURSOR (Win32 error {lastWin32Error}). " + "The mod will retry; direct cursor updates remain active."));
				}
				return;
			}
			_window = intPtr;
			_previousWindowProc = intPtr2;
			_hookInstalled = true;
			CaptureOriginalClassCursor();
			UpdateWindowClassCursor();
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)("Installed WM_SETCURSOR hook on " + DescribeWindow(intPtr) + "."));
			}
		}

		private static void RemoveWindowHook()
		{
			if (!_hookInstalled || _window == IntPtr.Zero || _previousWindowProc == IntPtr.Zero)
			{
				_hookInstalled = false;
				_window = IntPtr.Zero;
				_previousWindowProc = IntPtr.Zero;
				return;
			}
			if (NativeMethods.IsWindow(_window) && NativeMethods.GetWindowLongPtr(_window, -4) == HookDelegatePtr)
			{
				NativeMethods.SetWindowLongPtr(_window, -4, _previousWindowProc);
			}
			_hookInstalled = false;
			_window = IntPtr.Zero;
			_previousWindowProc = IntPtr.Zero;
		}

		private static IntPtr FindUnityWindow()
		{
			Process currentProcess = Process.GetCurrentProcess();
			currentProcess.Refresh();
			IntPtr mainWindowHandle = currentProcess.MainWindowHandle;
			if (mainWindowHandle != IntPtr.Zero && NativeMethods.IsWindow(mainWindowHandle))
			{
				return mainWindowHandle;
			}
			uint currentPid = (uint)currentProcess.Id;
			IntPtr best = IntPtr.Zero;
			long bestScore = long.MinValue;
			NativeMethods.EnumWindows(delegate(IntPtr hwnd, IntPtr lParam)
			{
				NativeMethods.GetWindowThreadProcessId(hwnd, out var lpdwProcessId);
				if (lpdwProcessId != currentPid || !NativeMethods.IsWindowVisible(hwnd))
				{
					return true;
				}
				long num = 0L;
				if (NativeMethods.GetClientRect(hwnd, out var lpRect))
				{
					num = (long)Math.Max(0, lpRect.right - lpRect.left) * (long)Math.Max(0, lpRect.bottom - lpRect.top);
				}
				string windowClass = GetWindowClass(hwnd);
				long num2 = num;
				if (windowClass.IndexOf("Unity", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					num2 += 1152921504606846976L;
				}
				if (num2 > bestScore)
				{
					bestScore = num2;
					best = hwnd;
				}
				return true;
			}, IntPtr.Zero);
			return best;
		}

		private static string DescribeWindow(IntPtr hwnd)
		{
			return $"HWND=0x{hwnd.ToInt64():X}, " + "class='" + GetWindowClass(hwnd) + "', title='" + GetWindowTitle(hwnd) + "'";
		}

		private static string GetWindowClass(IntPtr hwnd)
		{
			StringBuilder stringBuilder = new StringBuilder(256);
			NativeMethods.GetClassName(hwnd, stringBuilder, stringBuilder.Capacity);
			return stringBuilder.ToString();
		}

		private static string GetWindowTitle(IntPtr hwnd)
		{
			StringBuilder stringBuilder = new StringBuilder(512);
			NativeMethods.GetWindowText(hwnd, stringBuilder, stringBuilder.Capacity);
			return stringBuilder.ToString();
		}

		private static IntPtr WindowProcHook(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
		{
			if (msg == 32 && _enabled && ShouldControlCursor() && LowWord(lParam) == 1)
			{
				IntPtr intPtr = GetHandle(_desiredKind);
				if (intPtr == IntPtr.Zero)
				{
					intPtr = _arrow;
				}
				if (intPtr != IntPtr.Zero)
				{
					NativeMethods.SetCursor(intPtr);
					VerifyCursor(intPtr, _desiredKind);
					return new IntPtr(1);
				}
			}
			IntPtr result = ((!(_previousWindowProc != IntPtr.Zero)) ? NativeMethods.DefWindowProc(hWnd, msg, wParam, lParam) : NativeMethods.CallWindowProc(_previousWindowProc, hWnd, msg, wParam, lParam));
			if (_enabled && IsMouseCursorMessage(msg) && ShouldControlCursor())
			{
				EnsureDesiredCursorPresent($"WM 0x{msg:X4}");
			}
			return result;
		}

		private static int LowWord(IntPtr value)
		{
			return (ushort)(value.ToInt64() & 0xFFFF);
		}
	}
	[HarmonyPatch(typeof(ZCursor), "SetVisible")]
	internal static class ZCursorSetVisiblePatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			NativeCursorController.OnUnityCursorVisibilityApplied();
		}
	}
	[BepInPlugin("wire.valheim.nativecursorsupport", "Wire's Native Cursor", "0.3.2")]
	[DefaultExecutionOrder(32000)]
	public sealed class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static Action <0>__OnCursorContextChanged;

			public static Action<bool> <1>__OnApplicationFocusChanged;

			public static UnityAction <2>__OnBeforeRender;
		}

		public const string Guid = "wire.valheim.nativecursorsupport";

		public const string Name = "Wire's Native Cursor";

		public const string Version = "0.3.2";

		internal const string PrefKey = "WireNativeCursorSupport";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private Coroutine _endOfFrameLoop;

		internal static bool RuntimeEnabled { get; private set; }

		private void Awake()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			RuntimeEnabled = PlayerPrefs.GetInt("WireNativeCursorSupport", 1) == 1;
			try
			{
				ValidateCurrentGameApi();
				NativeCursorController.Initialize();
				_harmony = new Harmony("wire.valheim.nativecursorsupport");
				InstallCorePatches();
				InstallAccessibilityPatches();
				ZCursor.IsRequestedChanged += NativeCursorController.OnCursorContextChanged;
				ZCursor.IsVisibleChanged += NativeCursorController.OnCursorContextChanged;
				Application.focusChanged += NativeCursorController.OnApplicationFocusChanged;
				object obj = <>O.<2>__OnBeforeRender;
				if (obj == null)
				{
					UnityAction val = NativeCursorController.OnBeforeRender;
					<>O.<2>__OnBeforeRender = val;
					obj = (object)val;
				}
				Application.onBeforeRender += (UnityAction)obj;
				NativeCursorController.SetEnabled(RuntimeEnabled);
				_endOfFrameLoop = ((MonoBehaviour)this).StartCoroutine(EndOfFrameCursorLoop());
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Wire's Native Cursor 0.3.2 loaded. " + $"Enabled={RuntimeEnabled}. " + "Valheim remains the sole owner of cursor visibility and lock state; this mod selects the native/system cursor for the active desktop platform."));
			}
			catch (Exception ex)
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
				_harmony = null;
				NativeCursorController.Shutdown();
				((BaseUnityPlugin)this).Logger.LogError((object)("Wire's Native Cursor 0.3.2 could not initialize safely. No cursor override will be left active. " + ex));
			}
		}

		private void LateUpdate()
		{
			NativeCursorController.LateTick();
		}

		private IEnumerator EndOfFrameCursorLoop()
		{
			WaitForEndOfFrame wait = new WaitForEndOfFrame();
			while (true)
			{
				yield return wait;
				NativeCursorController.EndOfFrameTick();
			}
		}

		private void OnDestroy()
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			if (_endOfFrameLoop != null)
			{
				((MonoBehaviour)this).StopCoroutine(_endOfFrameLoop);
				_endOfFrameLoop = null;
			}
			ZCursor.IsRequestedChanged -= NativeCursorController.OnCursorContextChanged;
			ZCursor.IsVisibleChanged -= NativeCursorController.OnCursorContextChanged;
			Application.focusChanged -= NativeCursorController.OnApplicationFocusChanged;
			object obj = <>O.<2>__OnBeforeRender;
			if (obj == null)
			{
				UnityAction val = NativeCursorController.OnBeforeRender;
				<>O.<2>__OnBeforeRender = val;
				obj = (object)val;
			}
			Application.onBeforeRender -= (UnityAction)obj;
			NativeCursorController.Shutdown();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			Instance = null;
		}

		internal static void SetRuntimeEnabled(bool enabled)
		{
			RuntimeEnabled = enabled;
			NativeCursorController.SetEnabled(enabled);
		}

		private static void ValidateCurrentGameApi()
		{
			RequireMethod(typeof(ZCursor), "SetVisible", typeof(bool));
			RequireMethod(typeof(AccessibilitySettings), "Initialize");
			RequireMethod(typeof(AccessibilitySettings), "OnOkAsync");
			RequireMethod(typeof(AccessibilitySettings), "OnBack");
			RequireMethod(typeof(AccessibilitySettings), "OnTabOpen");
			RequireField(typeof(AccessibilitySettings), "m_toggleBlockToggle");
			RequireField(typeof(InventoryGui), "m_craftTimer");
			RequireField(typeof(FejdStartup), "m_loading");
			RequireField(typeof(FejdStartup), "m_pleaseWait");
		}

		private static void InstallCorePatches()
		{
			try
			{
				Instance._harmony.CreateClassProcessor(typeof(ZCursorSetVisiblePatch)).Patch();
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)"Installed ZCursor visibility-reset compensation.");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Could not patch ZCursor.SetVisible; continuing with the native/window/frame reconciliation paths. " + ex.Message));
				}
			}
		}

		private static void InstallAccessibilityPatches()
		{
			Instance._harmony.CreateClassProcessor(typeof(AccessibilityInitializePatch)).Patch();
			Instance._harmony.CreateClassProcessor(typeof(AccessibilityOkPatch)).Patch();
			Instance._harmony.CreateClassProcessor(typeof(AccessibilityBackPatch)).Patch();
			Instance._harmony.CreateClassProcessor(typeof(AccessibilityTabOpenPatch)).Patch();
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)"Validated and installed Accessibility settings integration.");
			}
		}

		private static void RequireMethod(Type type, string name, params Type[] args)
		{
			if (((args != null && args.Length != 0) ? AccessTools.DeclaredMethod(type, name, args, (Type[])null) : AccessTools.DeclaredMethod(type, name, (Type[])null, (Type[])null)) == null)
			{
				throw new MissingMethodException(type.FullName, name);
			}
		}

		private static void RequireField(Type type, string name)
		{
			if (AccessTools.Field(type, name) == null)
			{
				throw new MissingFieldException(type.FullName, name);
			}
		}
	}
}