#if ENABLE_INPUT_SYSTEM
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.InputSystem;
using UnityEngine.PlayerLoop;
namespace RosettaUI
{
///
/// UIの状態に応じて入力デバイスをブロックするクラス
/// PreUpdate.InputForUIUpdateフェーズのあとでcurrentデバイスをダミーに差し替えてアプリケーション側に入力が届かないようにする
/// * FixedUpdateはPreUpdateの前なので入力は有効
///
public static class InputDeviceBlocker
{
public enum Device
{
Keyboard,
Pointer,
Mouse,
}
private static readonly Dictionary>> DeviceShouldBlockFuncListTable = new();
private static Keyboard _dummyKeyboard;
private static Mouse _dummyMouse;
private static Pointer _dummyPointer;
private static Keyboard _originalKeyboard;
private static Mouse _originalMouse;
private static Pointer _originalPointer;
public static bool Enabled { get; private set; }
static InputDeviceBlocker()
{
StaticResourceUtility.AddResetStaticResourceCallback(() =>
{
SetEnable(false);
_dummyMouse = null;
_dummyPointer = null;
});
}
public static void RegisterShouldBlockFuncIfNotYet(Device device, Func shouldBlockFunc)
{
if (shouldBlockFunc == null) return;
if (!DeviceShouldBlockFuncListTable.TryGetValue(device, out var list))
{
list = new List>();
DeviceShouldBlockFuncListTable[device] = list;
SetEnable(true);
}
if (list.Contains(shouldBlockFunc)) return;
list.Add(shouldBlockFunc);
}
public static void UnregisterShouldBlockFunc(Device device, Func shouldBlockFunc)
{
if (shouldBlockFunc == null) return;
if (!DeviceShouldBlockFuncListTable.TryGetValue(device, out var list)) return;
list.Remove(shouldBlockFunc);
}
private static void SetEnable(bool enable)
{
if (Enabled == enable) return;
// PreUpdate.InputForUIUpdateの前後のほうがより厳密だがUnity2022にはないのでPreUpdate全体を指定
var enableDeviceTiming = typeof(PreUpdate);
var disableDeviceTiming = typeof(PreUpdate);
PlayerLoopInjector.RemoveActionBefore(enableDeviceTiming, EnableDevice);
PlayerLoopInjector.RemoveActionAfter(disableDeviceTiming, CheckAndDisableDevice);
if (enable)
{
PlayerLoopInjector.AddActionBefore(enableDeviceTiming, EnableDevice);
PlayerLoopInjector.AddActionAfter(disableDeviceTiming, CheckAndDisableDevice);
}
else
{
EnableDevice();
}
Enabled = enable;
}
private static void EnableDevice()
{
_originalKeyboard?.MakeCurrent();
_originalKeyboard = null;
_originalMouse?.MakeCurrent();
_originalMouse = null;
_originalPointer?.MakeCurrent();
_originalPointer = null;
}
private static void CheckAndDisableDevice()
{
foreach (var (device, funcList) in DeviceShouldBlockFuncListTable)
{
var block = funcList.Any(f => f());
if (!block) continue;
switch (device)
{
case Device.Keyboard:
_originalKeyboard = Keyboard.current;
_dummyKeyboard ??= InputSystem.AddDevice();
_dummyKeyboard.MakeCurrent();
break;
case Device.Pointer:
_originalPointer = Pointer.current;
_dummyPointer ??= InputSystem.AddDevice();
_dummyPointer.MakeCurrent();
break;
case Device.Mouse:
_originalMouse = Mouse.current;
_dummyMouse ??= InputSystem.AddDevice();
_dummyMouse.MakeCurrent();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
}
#endif