| | 1 | | // Copyright (c) 2020-2024 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 | |
|
| | 5 | | using System; |
| | 6 | | using System.Collections.Generic; |
| | 7 | | using System.Runtime.CompilerServices; |
| | 8 | | using System.Runtime.InteropServices; |
| | 9 | | using UnityEngine; |
| | 10 | |
|
| | 11 | | namespace 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 | | public enum SynthesizeMethod |
| | 22 | | { |
| | 23 | | Native, |
| | 24 | | InputSystem // TODO:Add path to push the unity input system manually |
| | 25 | | } |
| | 26 | |
|
| | 27 | | /// <summary> |
| | 28 | | /// A set of flags to describe various aspects of <see cref="KeyboardInput" />, mainly used to define |
| | 29 | | /// additional information related to <see cref="KeyboardInput.Key" />. |
| | 30 | | /// </summary> |
| | 31 | | [Flags] |
| | 32 | | public enum KeyboardFlag : uint |
| | 33 | | { |
| | 34 | | KeyDown = 0x0000, |
| | 35 | |
|
| | 36 | | /// <summary> |
| | 37 | | /// Is the key part of the extended set. |
| | 38 | | /// </summary> |
| | 39 | | ExtendedKey = 0x0001, |
| | 40 | |
|
| | 41 | | /// <summary> |
| | 42 | | /// A key has been released. |
| | 43 | | /// </summary> |
| | 44 | | KeyUp = 0x0002, |
| | 45 | | Unicode = 0x0004, |
| | 46 | | ScanCode = 0x0008 |
| | 47 | | } |
| | 48 | |
|
| | 49 | | /// <summary> |
| | 50 | | /// Virtual key codes. |
| | 51 | | /// </summary> |
| | 52 | | /// <seealso href="https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes" /> |
| | 53 | | public enum KeyCode : ushort |
| | 54 | | { |
| | 55 | | Invalid = 0x00, |
| | 56 | | Backspace = 0x08, |
| | 57 | | Tab = 0x09, |
| | 58 | | Clear = 0x0c, |
| | 59 | | Return = 0x0d, |
| | 60 | | Shift = 0x10, |
| | 61 | | Control = 0x11, |
| | 62 | | Alt = 0x12, |
| | 63 | | Pause = 0x13, |
| | 64 | | CapsLock = 0x14, |
| | 65 | | Escape = 0x1b, |
| | 66 | | Space = 0x20, |
| | 67 | | PageUp = 0x21, |
| | 68 | | PageDown = 0x22, |
| | 69 | | End = 0x23, |
| | 70 | | Home = 0x24, |
| | 71 | | Left = 0x25, |
| | 72 | | Up = 0x26, |
| | 73 | | Right = 0x27, |
| | 74 | | Down = 0x28, |
| | 75 | | PrintScreen = 0x2c, |
| | 76 | | Insert = 0x2d, |
| | 77 | | Delete = 0x2e, |
| | 78 | | Number0 = 0x30, |
| | 79 | | Number1 = 0x31, |
| | 80 | | Number2 = 0x32, |
| | 81 | | Number3 = 0x33, |
| | 82 | | Number4 = 0x34, |
| | 83 | | Number5 = 0x35, |
| | 84 | | Number6 = 0x36, |
| | 85 | | Number7 = 0x37, |
| | 86 | | Number8 = 0x38, |
| | 87 | | Number9 = 0x39, |
| | 88 | | A = 0x41, |
| | 89 | | B = 0x42, |
| | 90 | | C = 0x43, |
| | 91 | | D = 0x44, |
| | 92 | | E = 0x45, |
| | 93 | | F = 0x46, |
| | 94 | | G = 0x47, |
| | 95 | | H = 0x48, |
| | 96 | | I = 0x49, |
| | 97 | | J = 0x4a, |
| | 98 | | K = 0x4b, |
| | 99 | | L = 0x4c, |
| | 100 | | M = 0x4d, |
| | 101 | | N = 0x4e, |
| | 102 | | O = 0x4f, |
| | 103 | | P = 0x50, |
| | 104 | | Q = 0x51, |
| | 105 | | R = 0x52, |
| | 106 | | S = 0x53, |
| | 107 | | T = 0x54, |
| | 108 | | U = 0x55, |
| | 109 | | V = 0x56, |
| | 110 | | W = 0x57, |
| | 111 | | X = 0x58, |
| | 112 | | Y = 0x59, |
| | 113 | | Z = 0x5a, |
| | 114 | | LeftWindows = 0x5b, |
| | 115 | | RightWindows = 0x5c, |
| | 116 | | Applications = 0x5d, |
| | 117 | | Sleep = 0x5f, |
| | 118 | | NumPad0 = 0x60, |
| | 119 | | NumPad1 = 0x61, |
| | 120 | | NumPad2 = 0x62, |
| | 121 | | NumPad3 = 0x63, |
| | 122 | | NumPad4 = 0x64, |
| | 123 | | NumPad5 = 0x65, |
| | 124 | | NumPad6 = 0x66, |
| | 125 | | NumPad7 = 0x67, |
| | 126 | | NumPad8 = 0x68, |
| | 127 | | NumPad9 = 0x69, |
| | 128 | | NumPadMultiply = 0x6a, |
| | 129 | | NumPadAdd = 0x6b, |
| | 130 | | NumPadSubtract = 0x6d, |
| | 131 | | NumPadDecimal = 0x6e, |
| | 132 | | NumPadDivide = 0x6f, |
| | 133 | | F1 = 0x70, |
| | 134 | | F2 = 0x71, |
| | 135 | | F3 = 0x72, |
| | 136 | | F4 = 0x73, |
| | 137 | | F5 = 0x74, |
| | 138 | | F6 = 0x75, |
| | 139 | | F7 = 0x76, |
| | 140 | | F8 = 0x77, |
| | 141 | | F9 = 0x78, |
| | 142 | | F10 = 0x79, |
| | 143 | | F11 = 0x7a, |
| | 144 | | F12 = 0x7b, |
| | 145 | | NumLock = 0x90, |
| | 146 | | ScrollLock = 0x91, |
| | 147 | | LeftShift = 0xa0, |
| | 148 | | RightShift = 0xa1, |
| | 149 | | LeftControl = 0xa2, |
| | 150 | | RightControl = 0xa3 |
| | 151 | | } |
| | 152 | |
|
| | 153 | | public static KeyCode GetKeyCode(string keyCode) |
| 0 | 154 | | { |
| 0 | 155 | | int hash = keyCode.GetStableLowerCaseHashCode(); |
| 0 | 156 | | switch (hash) |
| | 157 | | { |
| | 158 | | case -68092673: |
| 0 | 159 | | return KeyCode.Backspace; |
| | 160 | | case 696029671: |
| 0 | 161 | | return KeyCode.Tab; |
| | 162 | | case 613662425: |
| 0 | 163 | | return KeyCode.Clear; |
| | 164 | | case -607330920: |
| 0 | 165 | | return KeyCode.Return; |
| | 166 | | case -387753726: |
| 0 | 167 | | return KeyCode.Shift; |
| | 168 | | case -1166912341: |
| 0 | 169 | | return KeyCode.Control; |
| | 170 | | case -63485571: |
| 0 | 171 | | return KeyCode.Alt; |
| | 172 | | case -1685075688: |
| 0 | 173 | | return KeyCode.Pause; |
| | 174 | | case -1943382536: |
| 0 | 175 | | return KeyCode.CapsLock; |
| | 176 | | case 1110997421: |
| 0 | 177 | | return KeyCode.Escape; |
| | 178 | | case 1144830560: |
| 0 | 179 | | return KeyCode.Space; |
| | 180 | | case 234281596: |
| 0 | 181 | | return KeyCode.PageUp; |
| | 182 | | case -1752670719: |
| 0 | 183 | | return KeyCode.PageDown; |
| | 184 | | case -1226285125: |
| 0 | 185 | | return KeyCode.End; |
| | 186 | | case 1414245067: |
| 0 | 187 | | return KeyCode.Home; |
| | 188 | | case -871206253: |
| 0 | 189 | | return KeyCode.Left; |
| | 190 | | case 1543969241: |
| 0 | 191 | | return KeyCode.Up; |
| | 192 | | case 109637592: |
| 0 | 193 | | return KeyCode.Right; |
| | 194 | | case 1367190538: |
| 0 | 195 | | return KeyCode.Down; |
| | 196 | | case -98072495: |
| 0 | 197 | | return KeyCode.PrintScreen; |
| | 198 | | case 695394959: |
| 0 | 199 | | return KeyCode.Insert; |
| | 200 | | case 1381955065: |
| 0 | 201 | | return KeyCode.Delete; |
| | 202 | | case -252055437: |
| 0 | 203 | | return KeyCode.Number0; |
| | 204 | | case -252055438: |
| 0 | 205 | | return KeyCode.Number1; |
| | 206 | | case -252055439: |
| 0 | 207 | | return KeyCode.Number2; |
| | 208 | | case -252055440: |
| 0 | 209 | | return KeyCode.Number3; |
| | 210 | | case -252055433: |
| 0 | 211 | | return KeyCode.Number4; |
| | 212 | | case -252055434: |
| 0 | 213 | | return KeyCode.Number5; |
| | 214 | | case -252055435: |
| 0 | 215 | | return KeyCode.Number6; |
| | 216 | | case -252055436: |
| 0 | 217 | | return KeyCode.Number7; |
| | 218 | | case -252055429: |
| 0 | 219 | | return KeyCode.Number8; |
| | 220 | | case -252055430: |
| 0 | 221 | | return KeyCode.Number9; |
| | 222 | | case 372029373: |
| 0 | 223 | | return KeyCode.A; |
| | 224 | | case 372029376: |
| 0 | 225 | | return KeyCode.B; |
| | 226 | | case 372029375: |
| 0 | 227 | | return KeyCode.C; |
| | 228 | | case 372029370: |
| 0 | 229 | | return KeyCode.D; |
| | 230 | | case 372029369: |
| 0 | 231 | | return KeyCode.E; |
| | 232 | | case 372029372: |
| 0 | 233 | | return KeyCode.F; |
| | 234 | | case 372029371: |
| 0 | 235 | | return KeyCode.G; |
| | 236 | | case 372029382: |
| 0 | 237 | | return KeyCode.H; |
| | 238 | | case 372029381: |
| 0 | 239 | | return KeyCode.I; |
| | 240 | | case 372029384: |
| 0 | 241 | | return KeyCode.J; |
| | 242 | | case 372029383: |
| 0 | 243 | | return KeyCode.K; |
| | 244 | | case 372029378: |
| 0 | 245 | | return KeyCode.L; |
| | 246 | | case 372029377: |
| 0 | 247 | | return KeyCode.M; |
| | 248 | | case 372029380: |
| 0 | 249 | | return KeyCode.N; |
| | 250 | | case 372029379: |
| 0 | 251 | | return KeyCode.O; |
| | 252 | | case 372029390: |
| 0 | 253 | | return KeyCode.P; |
| | 254 | | case 372029389: |
| 0 | 255 | | return KeyCode.Q; |
| | 256 | | case 372029392: |
| 0 | 257 | | return KeyCode.R; |
| | 258 | | case 372029391: |
| 0 | 259 | | return KeyCode.S; |
| | 260 | | case 372029386: |
| 0 | 261 | | return KeyCode.T; |
| | 262 | | case 372029385: |
| 0 | 263 | | return KeyCode.U; |
| | 264 | | case 372029388: |
| 0 | 265 | | return KeyCode.V; |
| | 266 | | case 372029387: |
| 0 | 267 | | return KeyCode.W; |
| | 268 | | case 372029398: |
| 0 | 269 | | return KeyCode.X; |
| | 270 | | case 372029397: |
| 0 | 271 | | return KeyCode.Y; |
| | 272 | | case 372029400: |
| 0 | 273 | | return KeyCode.Z; |
| | 274 | | case -1953951152: |
| 0 | 275 | | return KeyCode.LeftWindows; |
| | 276 | | case -1623418013: |
| 0 | 277 | | return KeyCode.RightWindows; |
| | 278 | | case 1523316373: |
| 0 | 279 | | return KeyCode.Applications; |
| | 280 | | case -1711919009: |
| 0 | 281 | | return KeyCode.Sleep; |
| | 282 | | case 191376843: |
| 0 | 283 | | return KeyCode.NumPad0; |
| | 284 | | case 191376842: |
| 0 | 285 | | return KeyCode.NumPad1; |
| | 286 | | case 191376841: |
| 0 | 287 | | return KeyCode.NumPad2; |
| | 288 | | case 191376840: |
| 0 | 289 | | return KeyCode.NumPad3; |
| | 290 | | case 191376839: |
| 0 | 291 | | return KeyCode.NumPad4; |
| | 292 | | case 191376838: |
| 0 | 293 | | return KeyCode.NumPad5; |
| | 294 | | case 191376837: |
| 0 | 295 | | return KeyCode.NumPad6; |
| | 296 | | case 191376836: |
| 0 | 297 | | return KeyCode.NumPad7; |
| | 298 | | case 191376851: |
| 0 | 299 | | return KeyCode.NumPad8; |
| | 300 | | case 191376850: |
| 0 | 301 | | return KeyCode.NumPad9; |
| | 302 | | case -1585322177: |
| 0 | 303 | | return KeyCode.NumPadMultiply; |
| | 304 | | case 1476018722: |
| 0 | 305 | | return KeyCode.NumPadAdd; |
| | 306 | | case 283539863: |
| 0 | 307 | | return KeyCode.NumPadSubtract; |
| | 308 | | case -1957785586: |
| 0 | 309 | | return KeyCode.NumPadDecimal; |
| | 310 | | case 1060101014: |
| 0 | 311 | | return KeyCode.NumPadDivide; |
| | 312 | | case -1467239129: |
| 0 | 313 | | return KeyCode.F1; |
| | 314 | | case -1063954602: |
| 0 | 315 | | return KeyCode.F2; |
| | 316 | | case 1664928753: |
| 0 | 317 | | return KeyCode.F3; |
| | 318 | | case -1870523656: |
| 0 | 319 | | return KeyCode.F4; |
| | 320 | | case 858359699: |
| 0 | 321 | | return KeyCode.F5; |
| | 322 | | case 1261644226: |
| 0 | 323 | | return KeyCode.F6; |
| | 324 | | case -304439715: |
| 0 | 325 | | return KeyCode.F7; |
| | 326 | | case -257385548: |
| 0 | 327 | | return KeyCode.F8; |
| | 328 | | case -1823469489: |
| 0 | 329 | | return KeyCode.F9; |
| | 330 | | case -1461555849: |
| 0 | 331 | | return KeyCode.F10; |
| | 332 | | case -1461555850: |
| 0 | 333 | | return KeyCode.F11; |
| | 334 | | case -1461555851: |
| 0 | 335 | | return KeyCode.F12; |
| | 336 | | case -1717124259: |
| 0 | 337 | | return KeyCode.NumLock; |
| | 338 | | case -1411354708: |
| 0 | 339 | | return KeyCode.ScrollLock; |
| | 340 | | case -1825696445: |
| 0 | 341 | | return KeyCode.LeftShift; |
| | 342 | | case 965494012: |
| 0 | 343 | | return KeyCode.RightShift; |
| | 344 | | case -595951364: |
| 0 | 345 | | return KeyCode.LeftControl; |
| | 346 | | case -873360029: |
| 0 | 347 | | return KeyCode.RightControl; |
| | 348 | | } |
| | 349 | |
|
| 0 | 350 | | return KeyCode.Invalid; |
| 0 | 351 | | } |
| | 352 | |
|
| | 353 | | /// <summary> |
| | 354 | | /// </summary> |
| | 355 | | /// <param name="keyCode"></param> |
| | 356 | | /// <returns></returns> |
| | 357 | | /// <seealso href="https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input" /> |
| | 358 | | public static bool IsExtendedKey(KeyCode keyCode) |
| 0 | 359 | | { |
| 0 | 360 | | if (keyCode == KeyCode.Control || |
| | 361 | | keyCode == KeyCode.RightControl || |
| | 362 | | keyCode == KeyCode.Insert || |
| | 363 | | keyCode == KeyCode.Delete || |
| | 364 | | keyCode == KeyCode.Home || |
| | 365 | | keyCode == KeyCode.End || |
| | 366 | | keyCode == KeyCode.Right || |
| | 367 | | keyCode == KeyCode.Up || |
| | 368 | | keyCode == KeyCode.Left || |
| | 369 | | keyCode == KeyCode.Down || |
| | 370 | | keyCode == KeyCode.NumLock || |
| | 371 | | keyCode == KeyCode.NumPadDivide) |
| 0 | 372 | | { |
| 0 | 373 | | return true; |
| | 374 | | } |
| | 375 | |
|
| 0 | 376 | | return false; |
| 0 | 377 | | } |
| | 378 | |
|
| | 379 | | /// <summary> |
| | 380 | | /// A set of flags to describe various aspects of <see cref="MouseInput" />, mainly used to define |
| | 381 | | /// additional information related to <see cref="MouseInput.Data" />. |
| | 382 | | /// </summary> |
| | 383 | | [Flags] |
| | 384 | | public enum MouseFlag : uint |
| | 385 | | { |
| | 386 | | Move = 0x0001, |
| | 387 | | LeftDown = 0x0002, |
| | 388 | | LeftUp = 0x0004, |
| | 389 | | RightDown = 0x0008, |
| | 390 | | RightUp = 0x0010, |
| | 391 | | MiddleDown = 0x0020, |
| | 392 | | MiddleUp = 0x0040, |
| | 393 | | VerticalWheel = 0x0800, |
| | 394 | | HorizontalWheel = 0x1000, |
| | 395 | |
|
| | 396 | | /// <summary> |
| | 397 | | /// Used in multi-desktop scenarios where you want to treat X/Y against the combined screen space. |
| | 398 | | /// You must also use <see cref="MouseFlag.Absolute" />. |
| | 399 | | /// </summary> |
| | 400 | | VirtualDesk = 0x4000, |
| | 401 | |
|
| | 402 | | /// <summary> |
| | 403 | | /// Indicates provided X/Y are in absolute screenspace instead of deltas of previous position. |
| | 404 | | /// </summary> |
| | 405 | | Absolute = 0x8000 |
| | 406 | | } |
| | 407 | |
|
| | 408 | | /// <summary> |
| | 409 | | /// A precalculated size of the <see cref="InputItem" />. This is calculated by finding the largest InputDat |
| | 410 | | /// struct size and adding either 8 (64bit) or 4 (32bit) bytes to it to account for the difference in pointe |
| | 411 | | /// </summary> |
| | 412 | | #if UNITY_64 |
| | 413 | | const uint k_InputStructureSize = 40; |
| | 414 | | #else |
| | 415 | | const uint k_InputStructureSize = 36; |
| | 416 | | #endif // UNITY_64 |
| | 417 | |
|
| | 418 | | #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN |
| | 419 | | [DllImport("user32.dll", SetLastError = true)] |
| | 420 | | static extern uint SendInput(uint numberOfInputs, InputItem[] inputs, uint sizeOfInputStructure); |
| | 421 | |
|
| | 422 | | [DllImport("user32.dll")] |
| | 423 | | static extern uint MapVirtualKey(uint uCode, uint uMapType); |
| | 424 | | #else |
| | 425 | | static uint SendInput(uint numberOfInputs, InputItem[] inputs, uint sizeOfInputStructure) |
| | 426 | | { |
| | 427 | | Debug.LogWarning("InputProxy::SendInput is not supported on this platform."); |
| | 428 | | return 0; |
| | 429 | | } |
| | 430 | | static uint MapVirtualKey(uint uCode, uint uMapType) |
| | 431 | | { |
| | 432 | | Debug.LogWarning("InputProxy::MapVirtualKey is not supported on this platform."); |
| | 433 | | return 0; |
| | 434 | | } |
| | 435 | | #endif // UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN |
| | 436 | |
|
| | 437 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 438 | | public static bool Synthesize(KeyboardInput keyboardInput, SynthesizeMethod method = SynthesizeMethod.Native) |
| 0 | 439 | | { |
| 0 | 440 | | return Synthesize(new[] { keyboardInput }, method) == 1; |
| 0 | 441 | | } |
| | 442 | |
|
| | 443 | | public static uint Synthesize(KeyboardInput[] keyboardInputs, SynthesizeMethod method = SynthesizeMethod.Native) |
| 0 | 444 | | { |
| 0 | 445 | | uint count = (uint)keyboardInputs.Length; |
| 0 | 446 | | if (count == 0) |
| 0 | 447 | | { |
| 0 | 448 | | return 0; |
| | 449 | | } |
| | 450 | |
|
| 0 | 451 | | InputItem[] items = new InputItem[count]; |
| 0 | 452 | | for (int i = 0; i < count; i++) |
| 0 | 453 | | { |
| 0 | 454 | | items[i] = new InputItem(InputType.Keyboard, new InputData(keyboardInputs[i])); |
| 0 | 455 | | } |
| | 456 | |
|
| 0 | 457 | | return SendInput(count, items, k_InputStructureSize); |
| 0 | 458 | | } |
| | 459 | |
|
| | 460 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 461 | | public static bool Synthesize(MouseInput mouseInput, SynthesizeMethod method = SynthesizeMethod.Native) |
| 0 | 462 | | { |
| 0 | 463 | | return Synthesize(new[] { mouseInput }, method) == 1; |
| 0 | 464 | | } |
| | 465 | |
|
| | 466 | | public static uint Synthesize(MouseInput[] mouseInputs, SynthesizeMethod method = SynthesizeMethod.Native) |
| 0 | 467 | | { |
| 0 | 468 | | uint count = (uint)mouseInputs.Length; |
| 0 | 469 | | if (count == 0) |
| 0 | 470 | | { |
| 0 | 471 | | return 0; |
| | 472 | | } |
| | 473 | |
|
| 0 | 474 | | InputItem[] items = new InputItem[count]; |
| 0 | 475 | | for (int i = 0; i < count; i++) |
| 0 | 476 | | { |
| 0 | 477 | | items[i] = new InputItem(InputType.Mouse, new InputData(mouseInputs[i])); |
| 0 | 478 | | } |
| | 479 | |
|
| 0 | 480 | | return SendInput(count, items, k_InputStructureSize); |
| 0 | 481 | | } |
| | 482 | |
|
| | 483 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 484 | | public static bool Synthesize(HardwareInput hardwareInput, SynthesizeMethod method = SynthesizeMethod.Native) |
| 0 | 485 | | { |
| 0 | 486 | | return Synthesize(new[] { hardwareInput }, method) == 1; |
| 0 | 487 | | } |
| | 488 | |
|
| | 489 | | public static uint Synthesize(HardwareInput[] hardwareInputs, SynthesizeMethod method = SynthesizeMethod.Native) |
| 0 | 490 | | { |
| 0 | 491 | | uint count = (uint)hardwareInputs.Length; |
| 0 | 492 | | if (count == 0) |
| 0 | 493 | | { |
| 0 | 494 | | return 0; |
| | 495 | | } |
| | 496 | |
|
| 0 | 497 | | InputItem[] items = new InputItem[count]; |
| 0 | 498 | | for (int i = 0; i < count; i++) |
| 0 | 499 | | { |
| 0 | 500 | | items[i] = new InputItem(InputType.Hardware, new InputData(hardwareInputs[i])); |
| 0 | 501 | | } |
| | 502 | |
|
| 0 | 503 | | return SendInput(count, items, k_InputStructureSize); |
| 0 | 504 | | } |
| | 505 | |
|
| | 506 | | public static bool KeyPress(KeyCode keyCode, SynthesizeMethod method = SynthesizeMethod.Native) |
| 0 | 507 | | { |
| 0 | 508 | | return Synthesize( |
| | 509 | | new[] |
| | 510 | | { |
| | 511 | | new KeyboardInput(keyCode, KeyboardFlag.KeyDown, 0, IntPtr.Zero), |
| | 512 | | new KeyboardInput(keyCode, KeyboardFlag.KeyUp, 0, IntPtr.Zero) |
| | 513 | | }, method) == 2; |
| 0 | 514 | | } |
| | 515 | |
|
| | 516 | | public static bool MouseClick(int x, int y, bool virtualMode = false, |
| | 517 | | SynthesizeMethod method = SynthesizeMethod.Native) |
| 0 | 518 | | { |
| 0 | 519 | | return Synthesize( |
| | 520 | | new[] |
| | 521 | | { |
| | 522 | | new MouseInput(x, y, 0, MouseFlag.Move & MouseFlag.Absolute, 0, IntPtr.Zero), |
| | 523 | | new MouseInput(x, y, 0, MouseFlag.LeftDown, 0, IntPtr.Zero), |
| | 524 | | new MouseInput(x, y, 0, MouseFlag.LeftUp, 0, IntPtr.Zero) |
| | 525 | | }, method) == 3; |
| 0 | 526 | | } |
| | 527 | |
|
| | 528 | | /// <summary> |
| | 529 | | /// The type of event being synthesized. |
| | 530 | | /// </summary> |
| | 531 | | enum InputType : uint |
| | 532 | | { |
| | 533 | | Mouse = 0, |
| | 534 | | Keyboard = 1, |
| | 535 | | Hardware = 2 |
| | 536 | | } |
| | 537 | |
|
| | 538 | | /// <summary> |
| | 539 | | /// A keyboard input event. |
| | 540 | | /// </summary> |
| | 541 | | /// <seealso href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput" /> |
| | 542 | | /// <remarks>Order and types matters as it is mapped into native, using 24 bytes.</remarks> |
| | 543 | | public struct KeyboardInput |
| | 544 | | { |
| | 545 | | public readonly ushort Key; |
| | 546 | | public readonly ushort ScanCode; |
| | 547 | | public readonly uint Flags; |
| | 548 | |
|
| | 549 | | /// <summary> |
| | 550 | | /// The timestamp of the event, if 0, OS will just make its own. This is useful if you want to simulate |
| | 551 | | /// a duration of time between input events. |
| | 552 | | /// </summary> |
| | 553 | | public uint Timestamp; |
| | 554 | |
|
| | 555 | | public IntPtr ExtraInfo; |
| | 556 | |
|
| | 557 | | public KeyboardInput(KeyCode key, KeyboardFlag flags, uint timestamp, IntPtr extraInfo) |
| 0 | 558 | | { |
| 0 | 559 | | Key = (ushort)key; |
| | 560 | |
|
| | 561 | | // Safety check for extended key |
| 0 | 562 | | if (IsExtendedKey(key) && !flags.HasFlags(KeyboardFlag.ExtendedKey)) |
| 0 | 563 | | { |
| 0 | 564 | | Flags = (uint)(flags | KeyboardFlag.ExtendedKey); |
| 0 | 565 | | } |
| | 566 | | else |
| 0 | 567 | | { |
| 0 | 568 | | Flags = (uint)flags; |
| 0 | 569 | | } |
| | 570 | |
|
| 0 | 571 | | if (k_KnownScanCodes.TryGetValue(Key, out ushort value)) |
| 0 | 572 | | { |
| 0 | 573 | | ScanCode = value; |
| 0 | 574 | | } |
| | 575 | | else |
| 0 | 576 | | { |
| 0 | 577 | | ScanCode = (ushort)(MapVirtualKey((uint)key, 0) & 0xFFU); |
| 0 | 578 | | k_KnownScanCodes.Add(Key, ScanCode); |
| 0 | 579 | | } |
| | 580 | |
|
| 0 | 581 | | Timestamp = timestamp; |
| 0 | 582 | | ExtraInfo = extraInfo; |
| 0 | 583 | | } |
| | 584 | | } |
| | 585 | |
|
| 0 | 586 | | static readonly Dictionary<ushort, ushort> k_KnownScanCodes = new Dictionary<ushort, ushort>(); |
| | 587 | |
|
| | 588 | | /// <summary> |
| | 589 | | /// A generic hardware input event. |
| | 590 | | /// </summary> |
| | 591 | | /// <seealso href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-hardwareinput" /> |
| | 592 | | /// <remarks> |
| | 593 | | /// Order and types matters as it is mapped into native, using 8 bytes. |
| | 594 | | /// </remarks> |
| | 595 | | public struct HardwareInput |
| | 596 | | { |
| | 597 | | /// <summary> |
| | 598 | | /// The message generated by the input hardware. |
| | 599 | | /// </summary> |
| | 600 | | public uint Message; |
| | 601 | |
|
| | 602 | | /// <summary> |
| | 603 | | /// The low-order word of the lParam parameter for. |
| | 604 | | /// </summary> |
| | 605 | | public ushort ParamL; |
| | 606 | |
|
| | 607 | | /// <summary> |
| | 608 | | /// The high-order word of the lParam parameter for uMsg. |
| | 609 | | /// </summary> |
| | 610 | | public ushort ParamH; |
| | 611 | |
|
| | 612 | | public HardwareInput(uint message, ushort paramL, ushort paramH) |
| 0 | 613 | | { |
| 0 | 614 | | Message = message; |
| 0 | 615 | | ParamL = paramL; |
| 0 | 616 | | ParamH = paramH; |
| 0 | 617 | | } |
| | 618 | | } |
| | 619 | |
|
| | 620 | | /// <summary> |
| | 621 | | /// A mouse input event. |
| | 622 | | /// </summary> |
| | 623 | | /// <seealso href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput" /> |
| | 624 | | /// <remarks> |
| | 625 | | /// Order and types matters as it is mapped into native, using 32 bytes. |
| | 626 | | /// </remarks> |
| | 627 | | public struct MouseInput |
| | 628 | | { |
| | 629 | | /// <summary> |
| | 630 | | /// The absolute X position or delta depending on the <see cref="MouseFlag" /> used. |
| | 631 | | /// </summary> |
| | 632 | | public readonly int X; |
| | 633 | |
|
| | 634 | | /// <summary> |
| | 635 | | /// The absolute Y position or delta depending on the <see cref="MouseFlag" /> used. |
| | 636 | | /// </summary> |
| | 637 | | public readonly int Y; |
| | 638 | |
|
| | 639 | | public uint Data; |
| | 640 | | public readonly uint Flags; |
| | 641 | |
|
| | 642 | | /// <summary> |
| | 643 | | /// The timestamp of the event, if 0, OS will just make its own. This is useful if you want to simulate |
| | 644 | | /// a duration of time between input events. |
| | 645 | | /// </summary> |
| | 646 | | public uint Timestamp; |
| | 647 | |
|
| | 648 | | public IntPtr ExtraInfo; |
| | 649 | |
|
| | 650 | | public MouseInput(int x, int y, uint data, MouseFlag flags, uint timestamp, IntPtr extraInfo) |
| 0 | 651 | | { |
| 0 | 652 | | Data = data; |
| | 653 | |
|
| | 654 | | // Absolute on main monitor? |
| 0 | 655 | | if (flags.HasFlags(MouseFlag.Absolute) && !flags.HasFlags(MouseFlag.VirtualDesk)) |
| 0 | 656 | | { |
| 0 | 657 | | float widthPercent = (float)x / Screen.currentResolution.width; |
| 0 | 658 | | float heightPercent = (float)y / Screen.currentResolution.height; |
| 0 | 659 | | X = (int)(widthPercent * 65535); |
| 0 | 660 | | Y = (int)(heightPercent * 65535); |
| 0 | 661 | | } |
| | 662 | | else |
| 0 | 663 | | { |
| 0 | 664 | | X = x; |
| 0 | 665 | | Y = y; |
| 0 | 666 | | } |
| | 667 | |
|
| 0 | 668 | | Flags = (uint)flags; |
| 0 | 669 | | Timestamp = timestamp; |
| 0 | 670 | | ExtraInfo = extraInfo; |
| 0 | 671 | | } |
| | 672 | | } |
| | 673 | |
|
| | 674 | | /// <summary> |
| | 675 | | /// An explicit data structure used to represent the input event being synthesized. |
| | 676 | | /// </summary> |
| | 677 | | [StructLayout(LayoutKind.Explicit)] |
| | 678 | | struct InputData |
| | 679 | | { |
| | 680 | | [FieldOffset(0)] readonly MouseInput Mouse; |
| | 681 | |
|
| | 682 | | [FieldOffset(0)] readonly KeyboardInput Keyboard; |
| | 683 | |
|
| | 684 | | [FieldOffset(0)] readonly HardwareInput Hardware; |
| | 685 | |
|
| | 686 | | public InputData(MouseInput mouseInput) |
| 0 | 687 | | { |
| 0 | 688 | | Keyboard = default; |
| 0 | 689 | | Hardware = default; |
| 0 | 690 | | Mouse = mouseInput; |
| 0 | 691 | | } |
| | 692 | |
|
| | 693 | | public InputData(KeyboardInput keyboardInput) |
| 0 | 694 | | { |
| 0 | 695 | | Hardware = default; |
| 0 | 696 | | Mouse = default; |
| 0 | 697 | | Keyboard = keyboardInput; |
| 0 | 698 | | } |
| | 699 | |
|
| | 700 | | public InputData(HardwareInput hardwareInput) |
| 0 | 701 | | { |
| 0 | 702 | | Mouse = default; |
| 0 | 703 | | Keyboard = default; |
| 0 | 704 | | Hardware = hardwareInput; |
| 0 | 705 | | } |
| | 706 | | } |
| | 707 | |
|
| | 708 | | /// <summary> |
| | 709 | | /// An explicit data structure used to represent the input event being synthesized. |
| | 710 | | /// </summary> |
| | 711 | | struct InputItem |
| | 712 | | { |
| | 713 | | readonly InputType Type; |
| | 714 | | readonly InputData Data; |
| | 715 | |
|
| | 716 | | public InputItem(InputType type, InputData data) |
| 0 | 717 | | { |
| 0 | 718 | | Type = type; |
| 0 | 719 | | Data = data; |
| 0 | 720 | | } |
| | 721 | | } |
| | 722 | | } |
| | 723 | | } |