diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity
index c8cd993..46495cb 100644
--- a/Assets/Scenes/SampleScene.unity
+++ b/Assets/Scenes/SampleScene.unity
@@ -150,7 +150,7 @@ Transform:
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
- m_RootOrder: 1
+ m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &394699778
MonoBehaviour:
@@ -165,78 +165,5 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
WorkInBackground: 1
+ Async: 0
InterceptMessages: 0
---- !u!1 &1620130889
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 1620130893}
- - component: {fileID: 1620130892}
- m_Layer: 0
- m_Name: MainCamera
- m_TagString: MainCamera
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!20 &1620130892
-Camera:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1620130889}
- m_Enabled: 1
- serializedVersion: 2
- m_ClearFlags: 1
- m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
- m_projectionMatrixMode: 1
- m_GateFitMode: 2
- m_FOVAxisMode: 0
- m_SensorSize: {x: 36, y: 24}
- m_LensShift: {x: 0, y: 0}
- m_FocalLength: 50
- m_NormalizedViewPortRect:
- serializedVersion: 2
- x: 0
- y: 0
- width: 1
- height: 1
- near clip plane: 0.3
- far clip plane: 1000
- field of view: 60
- orthographic: 0
- orthographic size: 5
- m_Depth: -1
- m_CullingMask:
- serializedVersion: 2
- m_Bits: 4294967295
- m_RenderingPath: -1
- m_TargetTexture: {fileID: 0}
- m_TargetDisplay: 0
- m_TargetEye: 3
- m_HDR: 1
- m_AllowMSAA: 1
- m_AllowDynamicResolution: 0
- m_ForceIntoRT: 0
- m_OcclusionCulling: 1
- m_StereoConvergence: 10
- m_StereoSeparation: 0.022
---- !u!4 &1620130893
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1620130889}
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: 0, y: 1, z: -10}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_Children: []
- m_Father: {fileID: 0}
- m_RootOrder: 0
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/Assets/Scripts/LogRawKeyInput.cs b/Assets/Scripts/LogRawKeyInput.cs
index e3fc63d..96099e4 100644
--- a/Assets/Scripts/LogRawKeyInput.cs
+++ b/Assets/Scripts/LogRawKeyInput.cs
@@ -4,11 +4,12 @@
public class LogRawKeyInput : MonoBehaviour
{
public bool WorkInBackground;
+ public bool Async;
public bool InterceptMessages;
private void OnEnable ()
{
- RawKeyInput.Start(WorkInBackground);
+ RawKeyInput.Start(WorkInBackground, Async);
RawKeyInput.OnKeyUp += LogKeyUp;
RawKeyInput.OnKeyDown += LogKeyDown;
}
diff --git a/Assets/UnityRawInput/Runtime/HookArgs.cs b/Assets/UnityRawInput/Runtime/HookArgs.cs
new file mode 100644
index 0000000..90cecc5
--- /dev/null
+++ b/Assets/UnityRawInput/Runtime/HookArgs.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace UnityRawInput
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct KeyboardArgs
+ {
+ public uint Code;
+ public uint ScanCode;
+ public KeyboardFlags Flags;
+ public uint Time;
+ public UIntPtr ExtraInfo;
+
+ public static KeyboardArgs FromPtr (IntPtr ptr)
+ {
+ return (KeyboardArgs)Marshal.PtrToStructure(ptr, typeof(KeyboardArgs));
+ }
+ }
+
+ [Flags]
+ public enum KeyboardFlags : uint
+ {
+ Extended = 0x01,
+ Injected = 0x10,
+ AltDown = 0x20,
+ Up = 0x80,
+ }
+}
diff --git a/Assets/UnityRawInput/Runtime/KbDllHookStruct.cs.meta b/Assets/UnityRawInput/Runtime/HookArgs.cs.meta
similarity index 100%
rename from Assets/UnityRawInput/Runtime/KbDllHookStruct.cs.meta
rename to Assets/UnityRawInput/Runtime/HookArgs.cs.meta
diff --git a/Assets/UnityRawInput/Runtime/KbDllHookStruct.cs b/Assets/UnityRawInput/Runtime/KbDllHookStruct.cs
deleted file mode 100644
index 4dadeca..0000000
--- a/Assets/UnityRawInput/Runtime/KbDllHookStruct.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace UnityRawInput
-{
- [StructLayout(LayoutKind.Sequential)]
- public struct KBDLLHOOKSTRUCT
- {
- public uint vkCode;
- public uint scanCode;
- public KBDLLHOOKSTRUCTFlags flags;
- public uint time;
- public UIntPtr dwExtraInfo;
-
- public static KBDLLHOOKSTRUCT CreateFromPtr (IntPtr ptr)
- {
- return (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(ptr, typeof(KBDLLHOOKSTRUCT));
- }
- }
-
- [Flags]
- public enum KBDLLHOOKSTRUCTFlags : uint
- {
- LLKHF_EXTENDED = 0x01,
- LLKHF_INJECTED = 0x10,
- LLKHF_ALTDOWN = 0x20,
- LLKHF_UP = 0x80,
- }
-}
diff --git a/Assets/UnityRawInput/Runtime/RawKeyInput.cs b/Assets/UnityRawInput/Runtime/RawKeyInput.cs
index cf6f5fa..652e39d 100644
--- a/Assets/UnityRawInput/Runtime/RawKeyInput.cs
+++ b/Assets/UnityRawInput/Runtime/RawKeyInput.cs
@@ -1,5 +1,8 @@
-using System;
+using System;
using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using AOT;
namespace UnityRawInput
{
@@ -17,7 +20,7 @@ public static class RawKeyInput
///
/// Whether the service is running and input messages are being processed.
///
- public static bool IsRunning => hookPtr != IntPtr.Zero;
+ public static bool IsRunning => cts != null && !cts.IsCancellationRequested;
///
/// Whether any key is currently pressed.
///
@@ -32,18 +35,26 @@ public static class RawKeyInput
public static bool InterceptMessages { get; set; }
private static readonly HashSet pressedKeys = new HashSet();
- private static IntPtr hookPtr = IntPtr.Zero;
+ private static readonly List hooks = new List();
+ private static SynchronizationContext unityContext;
+ private static CancellationTokenSource cts;
///
/// Initializes the service and starts processing input messages.
///
/// Whether input messages should be handled when the application is not in focus.
- /// Whether the service started successfully.
- public static bool Start (bool workInBackground)
+ /// Whether to start the service in a background thread.
+ public static void Start (bool workInBackground, bool async)
{
- if (IsRunning) return false;
+ if (!InterceptMessages) return;
+ if (IsRunning) return;
+ cts = new CancellationTokenSource();
+ unityContext = SynchronizationContext.Current;
WorkInBackground = workInBackground;
- return SetHook();
+ if (async)
+ try { Task.Run(() => ListenHooksAsync(cts.Token)); }
+ catch (OperationCanceledException) { }
+ else ListenHooks();
}
///
@@ -51,7 +62,8 @@ public static bool Start (bool workInBackground)
///
public static void Stop ()
{
- RemoveHook();
+ cts?.Cancel();
+ RemoveHooks();
pressedKeys.Clear();
}
@@ -63,32 +75,43 @@ public static bool IsKeyDown (RawKey key)
return pressedKeys.Contains(key);
}
- private static bool SetHook ()
+ private static async Task ListenHooksAsync (CancellationToken token)
{
- if (hookPtr == IntPtr.Zero)
- {
- if (WorkInBackground) hookPtr = Win32API.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, HandleLowLevelHookProc, IntPtr.Zero, 0);
- else hookPtr = Win32API.SetWindowsHookEx(HookType.WH_KEYBOARD, HandleHookProc, IntPtr.Zero, (int)Win32API.GetCurrentThreadId());
- }
+ ListenHooks();
+ while (!token.IsCancellationRequested)
+ await Task.Delay(10, token);
+ }
- if (hookPtr == IntPtr.Zero) return false;
+ private static void ListenHooks ()
+ {
+ hooks.Add(SetKeyboardHook());
+ hooks.Add(SetMouseHook());
+ }
- return true;
+ private static void RemoveHooks ()
+ {
+ foreach (var pointer in hooks)
+ if (pointer != IntPtr.Zero)
+ Win32API.UnhookWindowsHookEx(pointer);
+ hooks.Clear();
}
- private static void RemoveHook ()
+ private static IntPtr SetKeyboardHook ()
{
- if (hookPtr != IntPtr.Zero)
- {
- Win32API.UnhookWindowsHookEx(hookPtr);
- hookPtr = IntPtr.Zero;
- }
+ if (WorkInBackground) return Win32API.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, HandleLowLevelKeyboardProc, IntPtr.Zero, 0);
+ return Win32API.SetWindowsHookEx(HookType.WH_KEYBOARD, HandleKeyboardProc, IntPtr.Zero, (int)Win32API.GetCurrentThreadId());
}
- [AOT.MonoPInvokeCallback(typeof(Win32API.HookProc))]
- private static int HandleHookProc (int code, IntPtr wParam, IntPtr lParam)
+ private static IntPtr SetMouseHook ()
{
- if (code < 0) return Win32API.CallNextHookEx(hookPtr, code, wParam, lParam);
+ if (WorkInBackground) return Win32API.SetWindowsHookEx(HookType.WH_MOUSE_LL, HandleMouseProc, IntPtr.Zero, 0);
+ return Win32API.SetWindowsHookEx(HookType.WH_MOUSE, HandleMouseProc, IntPtr.Zero, (int)Win32API.GetCurrentThreadId());
+ }
+
+ [MonoPInvokeCallback(typeof(Win32API.HookProc))]
+ private static int HandleKeyboardProc (int code, IntPtr wParam, IntPtr lParam)
+ {
+ if (code < 0) return Win32API.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
var isKeyDown = ((int)lParam & (1 << 31)) == 0;
var key = (RawKey)wParam;
@@ -96,34 +119,54 @@ private static int HandleHookProc (int code, IntPtr wParam, IntPtr lParam)
if (isKeyDown) HandleKeyDown(key);
else HandleKeyUp(key);
- return InterceptMessages ? 1 : Win32API.CallNextHookEx(hookPtr, 0, wParam, lParam);
+ return Win32API.CallNextHookEx(IntPtr.Zero, 0, wParam, lParam);
}
- [AOT.MonoPInvokeCallback(typeof(Win32API.HookProc))]
- private static int HandleLowLevelHookProc (int code, IntPtr wParam, IntPtr lParam)
+ [MonoPInvokeCallback(typeof(Win32API.HookProc))]
+ private static int HandleLowLevelKeyboardProc (int code, IntPtr wParam, IntPtr lParam)
{
- if (code < 0) return Win32API.CallNextHookEx(hookPtr, code, wParam, lParam);
+ if (code < 0) return Win32API.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
- var kbd = KBDLLHOOKSTRUCT.CreateFromPtr(lParam);
+ var kbd = KeyboardArgs.FromPtr(lParam);
var keyState = (RawKeyState)wParam;
- var key = (RawKey)kbd.vkCode;
+ var key = (RawKey)kbd.Code;
if (keyState == RawKeyState.KeyDown || keyState == RawKeyState.SysKeyDown) HandleKeyDown(key);
else HandleKeyUp(key);
- return InterceptMessages ? 1 : Win32API.CallNextHookEx(hookPtr, 0, wParam, lParam);
+ return Win32API.CallNextHookEx(IntPtr.Zero, 0, wParam, lParam);
+ }
+
+ [MonoPInvokeCallback(typeof(Win32API.HookProc))]
+ private static int HandleMouseProc (int code, IntPtr wParam, IntPtr lParam)
+ {
+ if (code < 0) return Win32API.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
+ var state = (RawMouseState)wParam;
+ if (state == RawMouseState.LeftButtonDown) HandleKeyDown(RawKey.LeftButton);
+ else if (state == RawMouseState.MiddleButtonDown) HandleKeyDown(RawKey.MiddleButton);
+ else if (state == RawMouseState.RightButtonDown) HandleKeyDown(RawKey.RightButton);
+ else if (state == RawMouseState.LeftButtonUp) HandleKeyUp(RawKey.LeftButton);
+ else if (state == RawMouseState.MiddleButtonUp) HandleKeyUp(RawKey.MiddleButton);
+ else if (state == RawMouseState.RightButtonUp) HandleKeyUp(RawKey.RightButton);
+ else return Win32API.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
+ return Win32API.CallNextHookEx(IntPtr.Zero, 0, wParam, lParam);
}
private static void HandleKeyDown (RawKey key)
{
var added = pressedKeys.Add(key);
- if (added && OnKeyDown != null) OnKeyDown.Invoke(key);
+ if (added) unityContext.Send(InvokeOnUnityThread, key);
+ void InvokeOnUnityThread (object obj) => OnKeyDown?.Invoke((RawKey)obj);
}
private static void HandleKeyUp (RawKey key)
{
+ if (!pressedKeys.Contains(key))
+ HandleKeyDown(key);
+
pressedKeys.Remove(key);
- if (OnKeyUp != null) OnKeyUp.Invoke(key);
+ unityContext.Send(InvokeOnUnityThread, key);
+ void InvokeOnUnityThread (object obj) => OnKeyUp?.Invoke((RawKey)obj);
}
}
}
diff --git a/Assets/UnityRawInput/Runtime/RawMouseState.cs b/Assets/UnityRawInput/Runtime/RawMouseState.cs
new file mode 100644
index 0000000..f687f65
--- /dev/null
+++ b/Assets/UnityRawInput/Runtime/RawMouseState.cs
@@ -0,0 +1,14 @@
+namespace UnityRawInput
+{
+ public enum RawMouseState
+ {
+ MouseMove = 0x0200,
+ LeftButtonDown = 0x0201,
+ LeftButtonUp = 0x0202,
+ MouseWheel = 0x020A,
+ MiddleButtonDown = 0x0207,
+ MiddleButtonUp = 0x0208,
+ RightButtonDown = 0x0204,
+ RightButtonUp = 0x0205
+ }
+}
diff --git a/Assets/UnityRawInput/Runtime/RawMouseState.cs.meta b/Assets/UnityRawInput/Runtime/RawMouseState.cs.meta
new file mode 100644
index 0000000..32553cd
--- /dev/null
+++ b/Assets/UnityRawInput/Runtime/RawMouseState.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: d98a1d9f11b44c84a258162e5ba58630
+timeCreated: 1661385339
\ No newline at end of file