#18 Disable unity shortcuts in GameView
#if UNITY_EDITOR
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using UnityEditor;
using UnityEngine;
namespace DreamingSaints.Scripts.Editor
{
/// <summary>
/// Only when in play mode AND Game view is focused: disables Unity editor shortcuts (e.g. Ctrl+Z won't undo editor changes, CTRL+S won't save the scene).
/// When focus is on Inspector/Scene/etc. or when not in play mode, editor shortcuts work normally.
/// When Right Control is held, shortcuts are not disabled so Ctrl+P can start/stop Play.
/// </summary>
[InitializeOnLoad]
public static class DisableEditorShortcutsInGameView
{
#if UNITY_EDITOR_WIN
private const int VK_RCONTROL = 0xA3; // Right Control key
[DllImport("user32.dll")]
private static extern short GetAsyncKeyState(int vKey);// Note: using it instead of InptuSystem.Keyboard, because editor doesn't read it
#endif
private const string AssemblyCoreModule = "UnityEditor.CoreModule";
private const string TypeShortcutIntegration = "UnityEditor.ShortcutManagement.ShortcutIntegration";
private const string PropertyIgnoreWhenPlayModeFocused = "ignoreWhenPlayModeFocused";
private const string TypeGameView = "UnityEditor.GameView";
private const string AssemblyUnityEditorPrefix = "UnityEditor,";
private static MethodInfo s_setIgnoreWhenPlayModeFocused;
private static Type s_gameViewType;
private static bool s_reflectionResolved;
private static bool? s_lastIgnoreValue;
static DisableEditorShortcutsInGameView()
{
EditorApplication.update += OnUpdate;
}
private static void OnUpdate()
{
if (!ResolveReflection())
return;
bool gameViewFocused = s_gameViewType is not null
&& EditorWindow.focusedWindow is not null
&& s_gameViewType.IsInstanceOfType(EditorWindow.focusedWindow);
// When Right Control is held, don't ignore shortcuts so Ctrl+P can start/stop Play.
// Input System doesn't receive key state when polling from EditorApplication.update (Editor quirk), so use Win32 on Windows.
bool rightCtrlHeld = IsRightControlHeld();
bool shouldIgnore = EditorApplication.isPlaying && gameViewFocused && !rightCtrlHeld;
if (shouldIgnore == s_lastIgnoreValue)
return;
s_lastIgnoreValue = shouldIgnore;
SetIgnoreWhenPlayModeFocused(shouldIgnore);
}
private static bool IsRightControlHeld()
{
#if UNITY_EDITOR_WIN
return (GetAsyncKeyState(VK_RCONTROL) & 0x8000) != 0;
#endif
}
private static bool ResolveReflection()
{
if (s_reflectionResolved)
{
if (s_setIgnoreWhenPlayModeFocused is not null)
return true;
Debug.Log("[DisableEditorShortcutsInPlayMode] ResolveReflection: s_setIgnoreWhenPlayModeFocused is null");
return false;
}
s_reflectionResolved = true;
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
if (assembly.FullName.Contains(AssemblyCoreModule))
{
Type shortcutIntegration = assembly.GetType(TypeShortcutIntegration);
if (shortcutIntegration is null) continue;
PropertyInfo property = shortcutIntegration.GetProperty(
PropertyIgnoreWhenPlayModeFocused,
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic
);
if (property is null) break;
MethodInfo setter = property.GetSetMethod(nonPublic: true);
if (setter is null) break;
s_setIgnoreWhenPlayModeFocused = setter;
break;
}
}
foreach (Assembly assembly in assemblies)
{
if (assembly.FullName.StartsWith(AssemblyUnityEditorPrefix))
{
s_gameViewType = assembly.GetType(TypeGameView);
if (s_gameViewType is not null) break;
}
}
if (s_setIgnoreWhenPlayModeFocused is null)
Debug.LogWarning("DisableEditorShortcutsInPlayMode: Unable to find ShortcutIntegration.ignoreWhenPlayModeFocused.");
if (s_gameViewType is null)
Debug.LogWarning("DisableEditorShortcutsInPlayMode: Unable to find UnityEditor.GameView type.");
return s_setIgnoreWhenPlayModeFocused is not null;
}
private static void SetIgnoreWhenPlayModeFocused(bool value)
{
if (s_setIgnoreWhenPlayModeFocused is null)
return;
try
{
s_setIgnoreWhenPlayModeFocused.Invoke(null, new object[] { value });
}
catch (Exception e)
{
Debug.LogWarning("DisableEditorShortcutsInPlayMode: " + e.Message);
}
}
}
}
#endif