using System;
using System.Runtime.InteropServices;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace DanroJump.Bootstrap
{
///
/// Возвращает фокус окну игры при старте приложения и делает клик по центру окна.
///
[DefaultExecutionOrder(-12000)]
[DisallowMultipleComponent]
[AddComponentMenu("Bootstrap/Startup Window Focus Controller")]
public sealed class StartupWindowFocusController : MonoBehaviour
{
private static StartupWindowFocusController instance;
[SerializeField] private bool clickCenterOnStartup = true;
[SerializeField] [Min(0)] private int startupDelayFrames = 2;
[SerializeField] [Min(0f)] private float postClickSettleSeconds = 0.2f;
private void Awake()
{
if (instance != null && instance != this)
{
Destroy(gameObject);
return;
}
instance = this;
MoveToPersistentScene(gameObject);
}
public async UniTask RunStartupFocusAsync(CancellationToken cancellationToken)
{
if (!clickCenterOnStartup)
{
FocusGameWindow();
return;
}
await ClickCenterOnStartupAsync(cancellationToken);
}
private void OnDestroy()
{
if (instance == this)
{
instance = null;
}
}
private static void MoveToPersistentScene(GameObject gameObject)
{
if (Application.isPlaying)
{
DontDestroyOnLoad(gameObject);
}
}
public static void FocusGameWindow()
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
IntPtr handle = GetActiveWindow();
if (handle != IntPtr.Zero)
{
SetForegroundWindow(handle);
}
#endif
}
private async UniTask ClickCenterOnStartupAsync(CancellationToken cancellationToken)
{
try
{
for (int frame = 0; frame < startupDelayFrames; frame++)
{
await UniTask.Yield(PlayerLoopTiming.Update, cancellationToken);
}
FocusGameWindow();
ClickCenter();
if (postClickSettleSeconds > 0f)
{
await UniTask.Delay(
System.TimeSpan.FromSeconds(postClickSettleSeconds),
DelayType.Realtime,
PlayerLoopTiming.Update,
cancellationToken);
}
}
catch (OperationCanceledException)
{
}
}
private static void ClickCenter()
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
IntPtr handle = GetActiveWindow();
if (!TryGetWindowCenter(handle, out int x, out int y))
{
x = Mathf.RoundToInt(Screen.currentResolution.width * 0.5f);
y = Mathf.RoundToInt(Screen.currentResolution.height * 0.5f);
}
MoveCursorToScreenPoint(x, y);
INPUT[] inputs =
{
new INPUT
{
type = InputMouse,
mi = new MOUSEINPUT { dwFlags = MouseEventfLeftDown }
},
new INPUT
{
type = InputMouse,
mi = new MOUSEINPUT { dwFlags = MouseEventfLeftUp }
}
};
SendInput((uint)inputs.Length, inputs, Marshal.SizeOf());
#endif
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
private const int SystemMetricVirtualScreenX = 76;
private const int SystemMetricVirtualScreenY = 77;
private const int SystemMetricVirtualScreenWidth = 78;
private const int SystemMetricVirtualScreenHeight = 79;
private const uint InputMouse = 0;
private const uint MouseEventfMove = 0x0001;
private const uint MouseEventfAbsolute = 0x8000;
private const uint MouseEventfLeftDown = 0x0002;
private const uint MouseEventfLeftUp = 0x0004;
private static bool TryGetWindowCenter(IntPtr handle, out int x, out int y)
{
x = 0;
y = 0;
if (handle == IntPtr.Zero || !GetWindowRect(handle, out RECT rect))
{
return false;
}
x = rect.Left + Mathf.Max(0, rect.Right - rect.Left) / 2;
y = rect.Top + Mathf.Max(0, rect.Bottom - rect.Top) / 2;
return true;
}
private static void MoveCursorToScreenPoint(int x, int y)
{
int virtualX = GetSystemMetrics(SystemMetricVirtualScreenX);
int virtualY = GetSystemMetrics(SystemMetricVirtualScreenY);
int virtualWidth = Mathf.Max(1, GetSystemMetrics(SystemMetricVirtualScreenWidth));
int virtualHeight = Mathf.Max(1, GetSystemMetrics(SystemMetricVirtualScreenHeight));
int absoluteX = Mathf.RoundToInt((x - virtualX) * 65535f / virtualWidth);
int absoluteY = Mathf.RoundToInt((y - virtualY) * 65535f / virtualHeight);
INPUT[] inputs =
{
new INPUT
{
type = InputMouse,
mi = new MOUSEINPUT
{
dx = absoluteX,
dy = absoluteY,
dwFlags = MouseEventfMove | MouseEventfAbsolute
}
}
};
SendInput((uint)inputs.Length, inputs, Marshal.SizeOf());
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct INPUT
{
public uint type;
public MOUSEINPUT mi;
}
[StructLayout(LayoutKind.Sequential)]
private struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hwnd);
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
private static extern int GetSystemMetrics(int nIndex);
[DllImport("user32.dll")]
private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
#endif
}
}