< Summary

Class:GDX.Developer.InputProxy
Assembly:GDX
File(s):./Packages/com.dotbunny.gdx/GDX/Developer/InputProxy.cs
Covered lines:0
Uncovered lines:119
Coverable lines:119
Total lines:515
Line coverage:0% (0 of 119)
Covered branches:0
Total branches:0
Covered methods:0
Total methods:17
Method coverage:0% (0 of 17)

Coverage History

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
IsExtendedKey(...)0%1821300%
Synthesize(...)0%2100%
Synthesize(...)0%12300%
Synthesize(...)0%2100%
Synthesize(...)0%12300%
Synthesize(...)0%2100%
Synthesize(...)0%12300%
KeyPress(...)0%2100%
MouseClick(...)0%2100%
KeyboardInput(...)0%20400%
InputProxy()0%2100%
HardwareInput(...)0%2100%
MouseInput(...)0%12300%
InputData(...)0%2100%
InputData(...)0%2100%
InputData(...)0%2100%
InputItem(...)0%2100%

File(s)

./Packages/com.dotbunny.gdx/GDX/Developer/InputProxy.cs

#LineLine coverage
 1// Copyright (c) 2020-2023 dotBunny Inc.
 2// dotBunny licenses this file to you under the BSL-1.0 license.
 3// See the LICENSE file in the project root for more information.
 4
 5using System;
 6using System.Collections.Generic;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.InteropServices;
 9using UnityEngine;
 10
 11namespace GDX.Developer
 12{
 13    /// <summary>
 14    ///     A windows-based input simulator.
 15    /// </summary>
 16    /// <remarks>
 17    ///     This is NOT supported in batch mode operation of the editor.
 18    /// </remarks>
 19    public static class InputProxy
 20    {
 21        /// <summary>
 22        ///     A set of flags to describe various aspects of <see cref="KeyboardInput" />, mainly used to define
 23        ///     additional information related to <see cref="KeyboardInput.Key" />.
 24        /// </summary>
 25        [Flags]
 26        public enum KeyboardFlag : uint
 27        {
 28            KeyDown = 0x0000,
 29
 30            /// <summary>
 31            ///     Is the key part of the extended set.
 32            /// </summary>
 33            ExtendedKey = 0x0001,
 34
 35            /// <summary>
 36            ///     A key has been released.
 37            /// </summary>
 38            KeyUp = 0x0002,
 39            Unicode = 0x0004,
 40            ScanCode = 0x0008
 41        }
 42
 43        /// <summary>
 44        ///     Virtual key codes.
 45        /// </summary>
 46        /// <seealso href="https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes" />
 47        public enum KeyCode : ushort
 48        {
 49            Backspace = 0x08,
 50            Tab = 0x09,
 51            Clear = 0x0c,
 52            Return = 0x0d,
 53            Shift = 0x10,
 54            Control = 0x11,
 55            Alt = 0x12,
 56            Pause = 0x13,
 57            CapsLock = 0x14,
 58            Escape = 0x1b,
 59            Space = 0x20,
 60            PageUp = 0x21,
 61            PageDown = 0x22,
 62            End = 0x23,
 63            Home = 0x24,
 64            Left = 0x25,
 65            Up = 0x26,
 66            Right = 0x27,
 67            Down = 0x28,
 68            PrintScreen = 0x2c,
 69            Insert = 0x2d,
 70            Delete = 0x2e,
 71            Number0 = 0x30,
 72            Number1 = 0x31,
 73            Number2 = 0x32,
 74            Number3 = 0x33,
 75            Number4 = 0x34,
 76            Number5 = 0x35,
 77            Number6 = 0x36,
 78            Number7 = 0x37,
 79            Number8 = 0x38,
 80            Number9 = 0x39,
 81            A = 0x41,
 82            B = 0x42,
 83            C = 0x43,
 84            D = 0x44,
 85            E = 0x45,
 86            F = 0x46,
 87            G = 0x47,
 88            H = 0x48,
 89            I = 0x49,
 90            J = 0x4a,
 91            K = 0x4b,
 92            L = 0x4c,
 93            M = 0x4d,
 94            N = 0x4e,
 95            O = 0x4f,
 96            P = 0x50,
 97            Q = 0x51,
 98            R = 0x52,
 99            S = 0x53,
 100            T = 0x54,
 101            U = 0x55,
 102            V = 0x56,
 103            W = 0x57,
 104            X = 0x58,
 105            Y = 0x59,
 106            Z = 0x5a,
 107            LeftWindows = 0x5b,
 108            RightWindows = 0x5c,
 109            Applications = 0x5d,
 110            Sleep = 0x5f,
 111            NumPad0 = 0x60,
 112            NumPad1 = 0x61,
 113            NumPad2 = 0x62,
 114            NumPad3 = 0x63,
 115            NumPad4 = 0x64,
 116            NumPad5 = 0x65,
 117            NumPad6 = 0x66,
 118            NumPad7 = 0x67,
 119            NumPad8 = 0x68,
 120            NumPad9 = 0x69,
 121            NumPadMultiply = 0x6a,
 122            NumPadAdd = 0x6b,
 123            NumPadSubtract = 0x6d,
 124            NumPadDecimal = 0x6e,
 125            NumPadDivide = 0x6f,
 126            F1 = 0x70,
 127            F2 = 0x71,
 128            F3 = 0x72,
 129            F4 = 0x73,
 130            F5 = 0x74,
 131            F6 = 0x75,
 132            F7 = 0x76,
 133            F8 = 0x77,
 134            F9 = 0x78,
 135            F10 = 0x79,
 136            F11 = 0x7a,
 137            F12 = 0x7b,
 138            NumLock = 0x90,
 139            ScrollLock = 0x91,
 140            LeftShift = 0xa0,
 141            RightShift = 0xa1,
 142            LeftControl = 0xa2,
 143            RightControl = 0xa3
 144        }
 145
 146        /// <summary>
 147        /// </summary>
 148        /// <param name="keyCode"></param>
 149        /// <returns></returns>
 150        /// <seealso href="https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input" />
 151        public static bool IsExtendedKey(KeyCode keyCode)
 0152        {
 0153            if (keyCode == KeyCode.Control ||
 154                keyCode == KeyCode.RightControl ||
 155                keyCode == KeyCode.Insert ||
 156                keyCode == KeyCode.Delete ||
 157                keyCode == KeyCode.Home ||
 158                keyCode == KeyCode.End ||
 159                keyCode == KeyCode.Right ||
 160                keyCode == KeyCode.Up ||
 161                keyCode == KeyCode.Left ||
 162                keyCode == KeyCode.Down ||
 163                keyCode == KeyCode.NumLock ||
 164                keyCode == KeyCode.NumPadDivide)
 0165            {
 0166                return true;
 167            }
 168
 0169            return false;
 0170        }
 171
 172        /// <summary>
 173        ///     A set of flags to describe various aspects of <see cref="MouseInput" />, mainly used to define
 174        ///     additional information related to <see cref="MouseInput.Data" />.
 175        /// </summary>
 176        [Flags]
 177        public enum MouseFlag : uint
 178        {
 179            Move = 0x0001,
 180            LeftDown = 0x0002,
 181            LeftUp = 0x0004,
 182            RightDown = 0x0008,
 183            RightUp = 0x0010,
 184            MiddleDown = 0x0020,
 185            MiddleUp = 0x0040,
 186            VerticalWheel = 0x0800,
 187            HorizontalWheel = 0x1000,
 188
 189            /// <summary>
 190            ///     Used in multi-desktop scenarios where you want to treat X/Y against the combined screen space.
 191            ///     You must also use <see cref="MouseFlag.Absolute" />.
 192            /// </summary>
 193            VirtualDesk = 0x4000,
 194
 195            /// <summary>
 196            ///     Indicates provided X/Y are in absolute screenspace instead of deltas of previous position.
 197            /// </summary>
 198            Absolute = 0x8000
 199        }
 200
 201        /// <summary>
 202        ///     A precalculated size of the <see cref="InputItem" />. This is calculated by finding the largest InputDat
 203        ///     struct size and adding either 8 (64bit) or 4 (32bit) bytes to it to account for the difference in pointe
 204        /// </summary>
 205#if UNITY_64
 206        const uint k_InputStructureSize = 40;
 207#else
 208        const uint k_InputStructureSize = 36;
 209#endif // UNITY_64
 210
 211#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
 212        [DllImport("user32.dll", SetLastError = true)]
 213        static extern uint SendInput(uint numberOfInputs, InputItem[] inputs, uint sizeOfInputStructure);
 214
 215        [DllImport("user32.dll")]
 216        static extern uint MapVirtualKey(uint uCode, uint uMapType);
 217#else
 218        static uint SendInput(uint numberOfInputs, InputItem[] inputs, uint sizeOfInputStructure)
 219        {
 220            Debug.LogWarning("InputProxy::SendInput is not supported on this platform.");
 221            return 0;
 222        }
 223        static uint MapVirtualKey(uint uCode, uint uMapType)
 224        {
 225            Debug.LogWarning("InputProxy::MapVirtualKey is not supported on this platform.");
 226            return 0;
 227        }
 228#endif // UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
 229
 230        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 231        public static bool Synthesize(KeyboardInput keyboardInput)
 0232        {
 0233            return Synthesize(new[] { keyboardInput }) == 1;
 0234        }
 235
 236
 237        public static uint Synthesize(KeyboardInput[] keyboardInputs)
 0238        {
 0239            uint count = (uint)keyboardInputs.Length;
 0240            if (count == 0)
 0241            {
 0242                return 0;
 243            }
 244
 0245            InputItem[] items = new InputItem[count];
 0246            for (int i = 0; i < count; i++)
 0247            {
 0248                items[i] = new InputItem(InputType.Keyboard, new InputData(keyboardInputs[i]));
 0249            }
 250
 0251            return SendInput(count, items, k_InputStructureSize);
 0252        }
 253
 254        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 255        public static bool Synthesize(MouseInput mouseInput)
 0256        {
 0257            return Synthesize(new[] { mouseInput }) == 1;
 0258        }
 259
 260        public static uint Synthesize(MouseInput[] mouseInputs)
 0261        {
 0262            uint count = (uint)mouseInputs.Length;
 0263            if (count == 0)
 0264            {
 0265                return 0;
 266            }
 267
 0268            InputItem[] items = new InputItem[count];
 0269            for (int i = 0; i < count; i++)
 0270            {
 0271                items[i] = new InputItem(InputType.Mouse, new InputData(mouseInputs[i]));
 0272            }
 273
 0274            return SendInput(count, items, k_InputStructureSize);
 0275        }
 276
 277        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 278        public static bool Synthesize(HardwareInput hardwareInput)
 0279        {
 0280            return Synthesize(new[] { hardwareInput }) == 1;
 0281        }
 282
 283        public static uint Synthesize(HardwareInput[] hardwareInputs)
 0284        {
 0285            uint count = (uint)hardwareInputs.Length;
 0286            if (count == 0)
 0287            {
 0288                return 0;
 289            }
 290
 0291            InputItem[] items = new InputItem[count];
 0292            for (int i = 0; i < count; i++)
 0293            {
 0294                items[i] = new InputItem(InputType.Hardware, new InputData(hardwareInputs[i]));
 0295            }
 296
 0297            return SendInput(count, items, k_InputStructureSize);
 0298        }
 299
 300        public static bool KeyPress(KeyCode keyCode)
 0301        {
 0302            return Synthesize(new[]
 303            {
 304                new KeyboardInput(keyCode, KeyboardFlag.KeyDown, 0, IntPtr.Zero),
 305                new KeyboardInput(keyCode, KeyboardFlag.KeyUp, 0, IntPtr.Zero)
 306            }) == 2;
 0307        }
 308
 309        public static bool MouseClick(int x, int y, bool virtualMode = false)
 0310        {
 0311            return Synthesize(new[]
 312            {
 313                new MouseInput(x, y, 0, MouseFlag.Move & MouseFlag.Absolute, 0, IntPtr.Zero),
 314                new MouseInput(x, y, 0, MouseFlag.LeftDown, 0, IntPtr.Zero),
 315                new MouseInput(x, y, 0, MouseFlag.LeftUp, 0, IntPtr.Zero)
 316            }) == 3;
 0317        }
 318
 319        /// <summary>
 320        ///     The type of event being synthesized.
 321        /// </summary>
 322        enum InputType : uint
 323        {
 324            Mouse = 0,
 325            Keyboard = 1,
 326            Hardware = 2
 327        }
 328
 329        /// <summary>
 330        ///     A keyboard input event.
 331        /// </summary>
 332        /// <seealso href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput" />
 333        /// <remarks>Order and types matters as it is mapped into native, using 24 bytes.</remarks>
 334        public struct KeyboardInput
 335        {
 336            public readonly ushort Key;
 337            public readonly ushort ScanCode;
 338            public readonly uint Flags;
 339
 340            /// <summary>
 341            ///     The timestamp of the event, if 0, OS will just make its own. This is useful if you want to simulate
 342            ///     a duration of time between input events.
 343            /// </summary>
 344            public uint Timestamp;
 345
 346            public IntPtr ExtraInfo;
 347
 348            public KeyboardInput(KeyCode key, KeyboardFlag flags, uint timestamp, IntPtr extraInfo)
 0349            {
 0350                Key = (ushort)key;
 351
 352
 353                // Safety check for extended key
 0354                if (IsExtendedKey(key) && !flags.HasFlags(KeyboardFlag.ExtendedKey))
 0355                {
 0356                    Flags = (uint)(flags | KeyboardFlag.ExtendedKey);
 0357                }
 358                else
 0359                {
 0360                    Flags = (uint)flags;
 0361                }
 362
 0363                if (k_KnownScanCodes.TryGetValue(Key, out ushort value))
 0364                {
 0365                    ScanCode = value;
 0366                }
 367                else
 0368                {
 0369                    ScanCode = (ushort)(MapVirtualKey((uint)key, 0) & 0xFFU);
 0370                    k_KnownScanCodes.Add(Key, ScanCode);
 0371                }
 372
 0373                Timestamp = timestamp;
 0374                ExtraInfo = extraInfo;
 0375            }
 376        }
 377
 0378        static readonly Dictionary<ushort, ushort> k_KnownScanCodes = new Dictionary<ushort, ushort>();
 379
 380        /// <summary>
 381        ///     A generic hardware input event.
 382        /// </summary>
 383        /// <seealso href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-hardwareinput" />
 384        /// <remarks>
 385        ///     Order and types matters as it is mapped into native, using 8 bytes.
 386        /// </remarks>
 387        public struct HardwareInput
 388        {
 389            /// <summary>
 390            ///     The message generated by the input hardware.
 391            /// </summary>
 392            public uint Message;
 393
 394            /// <summary>
 395            ///     The low-order word of the lParam parameter for.
 396            /// </summary>
 397            public ushort ParamL;
 398
 399            /// <summary>
 400            ///     The high-order word of the lParam parameter for uMsg.
 401            /// </summary>
 402            public ushort ParamH;
 403
 404            public HardwareInput(uint message, ushort paramL, ushort paramH)
 0405            {
 0406                Message = message;
 0407                ParamL = paramL;
 0408                ParamH = paramH;
 0409            }
 410        }
 411
 412        /// <summary>
 413        ///     A mouse input event.
 414        /// </summary>
 415        /// <seealso href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput" />
 416        /// <remarks>
 417        ///     Order and types matters as it is mapped into native, using 32 bytes.
 418        /// </remarks>
 419        public struct MouseInput
 420        {
 421            /// <summary>
 422            ///     The absolute X position or delta depending on the <see cref="MouseFlag" /> used.
 423            /// </summary>
 424            public readonly int X;
 425
 426            /// <summary>
 427            ///     The absolute Y position or delta depending on the <see cref="MouseFlag" /> used.
 428            /// </summary>
 429            public readonly int Y;
 430
 431            public uint Data;
 432            public readonly uint Flags;
 433
 434            /// <summary>
 435            ///     The timestamp of the event, if 0, OS will just make its own. This is useful if you want to simulate
 436            ///     a duration of time between input events.
 437            /// </summary>
 438            public uint Timestamp;
 439
 440            public IntPtr ExtraInfo;
 441
 442            public MouseInput(int x, int y, uint data, MouseFlag flags, uint timestamp, IntPtr extraInfo)
 0443            {
 444
 0445                Data = data;
 446
 447                // Absolute on main monitor?
 0448                if (flags.HasFlags(MouseFlag.Absolute) && !flags.HasFlags(MouseFlag.VirtualDesk))
 0449                {
 450
 0451                    float widthPercent = (float)x / Screen.currentResolution.width;
 0452                    float heightPercent = (float)y / Screen.currentResolution.height;
 0453                    X = (int)(widthPercent * 65535);
 0454                    Y = (int)(heightPercent * 65535);
 0455                }
 456                else
 0457                {
 0458                    X = x;
 0459                    Y = y;
 0460                }
 0461                Flags = (uint)flags;
 0462                Timestamp = timestamp;
 0463                ExtraInfo = extraInfo;
 0464            }
 465        }
 466
 467        /// <summary>
 468        ///     An explicit data structure used to represent the input event being synthesized.
 469        /// </summary>
 470        [StructLayout(LayoutKind.Explicit)]
 471        struct InputData
 472        {
 473            [FieldOffset(0)] readonly MouseInput Mouse;
 474
 475            [FieldOffset(0)] readonly KeyboardInput Keyboard;
 476
 477            [FieldOffset(0)] readonly HardwareInput Hardware;
 478
 479            public InputData(MouseInput mouseInput)
 0480            {
 0481                Keyboard = default;
 0482                Hardware = default;
 0483                Mouse = mouseInput;
 0484            }
 485
 486            public InputData(KeyboardInput keyboardInput)
 0487            {
 0488                Hardware = default;
 0489                Mouse = default;
 0490                Keyboard = keyboardInput;
 0491            }
 492
 493            public InputData(HardwareInput hardwareInput)
 0494            {
 0495                Mouse = default;
 0496                Keyboard = default;
 0497                Hardware = hardwareInput;
 0498            }
 499        }
 500
 501        /// <summary>
 502        ///     An explicit data structure used to represent the input event being synthesized.
 503        /// </summary>
 504        struct InputItem
 505        {
 506            readonly InputType Type;
 507            readonly InputData Data;
 508            public InputItem(InputType type, InputData data)
 0509            {
 0510                Type = type;
 0511                Data = data;
 0512            }
 513        }
 514    }
 515}