1 module PixelPerfectEngine.system.inputHandler; 2 /* 3 * Copyright (C) 2015-2019, by Laszlo Szeremi under the Boost license. 4 * 5 * Pixel Perfect Engine, input module 6 */ 7 8 import std.stdio; 9 import std.conv; 10 import std.algorithm.searching; 11 import bindbc.sdl; 12 /** 13 * Basic Force Feedback implementation. 14 */ 15 public class ForceFeedbackHandler{ 16 public SDL_Haptic*[] haptic; 17 private InputHandler src; 18 19 public this(InputHandler src){ 20 SDL_InitSubSystem(SDL_INIT_HAPTIC); 21 this.src = src; 22 foreach(SDL_Joystick* p; src.joysticks){ 23 24 if(p !is null){ 25 haptic ~= SDL_HapticOpenFromJoystick(p); 26 //; 27 28 if(SDL_HapticRumbleSupported(haptic[haptic.length-1]) == SDL_TRUE){ 29 SDL_HapticRumbleInit(haptic[haptic.length-1]); 30 } 31 } 32 } 33 } 34 public void reinitalize(){ 35 foreach(SDL_Joystick* p; src.joysticks){ 36 haptic.length = 0; 37 if(p !is null){ 38 haptic ~= SDL_HapticOpenFromJoystick(p); 39 //; 40 41 if(SDL_HapticRumbleSupported(haptic[haptic.length-1]) == SDL_TRUE){ 42 SDL_HapticRumbleInit(haptic[haptic.length-1]); 43 } 44 } 45 } 46 } 47 48 public void addEffect(int deviceID, SDL_HapticEffect* type){ 49 SDL_HapticNewEffect(haptic[deviceID], type); 50 } 51 public void runEffect(int deviceID, int num, uint type){ 52 SDL_HapticRunEffect(haptic[deviceID], num, type); 53 } 54 public void updateEffect(int deviceID, int num, SDL_HapticEffect* type){ 55 SDL_HapticUpdateEffect(haptic[deviceID], num, type); 56 } 57 public void stopEffect(int deviceID, int num){ 58 SDL_HapticStopEffect(haptic[deviceID], num); 59 } 60 61 public void runRumbleEffect(int deviceID, float strenght, uint duration){ 62 SDL_HapticRumblePlay(haptic[deviceID], strenght, duration); 63 } 64 public void stopRumbleEffect(int deviceID){ 65 SDL_HapticRumbleStop(haptic[deviceID]); 66 } 67 } 68 /** 69 * Handles the events for the various input devices. 70 */ 71 public class InputHandler : TextInputHandler{ 72 public AxisListener[] al; 73 public InputListener[] il; 74 public MouseListener[] ml; 75 public TextInputListener[] tl; 76 public SystemEventListener[] sel; 77 private TextInputListener tiSelect; 78 private bool tiEnable, enableBindingCapture, delOldOnEvent, delConflKeys, exitOnSysKeys; 79 //private string name; 80 public KeyBinding[] kb; 81 private string hatpos, proposedID; 82 private int mouseX, mouseY; 83 public SDL_Joystick*[] joysticks; 84 public string[] joyNames; 85 public int[] joyButtons, joyAxes, joyHats; 86 ///Upon construction, it detects the connected joysticks. 87 public this(){ 88 SDL_InitSubSystem(SDL_INIT_JOYSTICK); 89 int j = SDL_NumJoysticks(); 90 for(int i ; i < j ; i++){ 91 joysticks ~= SDL_JoystickOpen(i); 92 if(joysticks[i] !is null){ 93 joyNames ~= to!string(SDL_JoystickName(joysticks[i])); 94 joyButtons ~= SDL_JoystickNumButtons(joysticks[i]); 95 joyAxes ~= SDL_JoystickNumAxes(joysticks[i]); 96 joyHats ~= SDL_JoystickNumHats(joysticks[i]); 97 /*writeln("Buttons: ", SDL_JoystickNumButtons(joysticks[i])); 98 writeln("Axes: ", SDL_JoystickNumAxes(joysticks[i])); 99 writeln("Hats: ", SDL_JoystickNumHats(joysticks[i]));*/ 100 } 101 } 102 } 103 104 ~this(){ 105 foreach(SDL_Joystick* p; joysticks) 106 SDL_JoystickClose(p); 107 } 108 /** 109 * Captures a key for binding 110 */ 111 public void captureEvent(string proposedID, bool delOldOnEvent, bool delConflKeys, bool exitOnSysKeys){ 112 this.proposedID = proposedID; 113 enableBindingCapture = true; 114 this.delOldOnEvent = delOldOnEvent; 115 this.delConflKeys = delConflKeys; 116 this.exitOnSysKeys = exitOnSysKeys; 117 } 118 /** 119 * Polls for events. If there's any, calls the eventlisteners. 120 */ 121 public void test(){ 122 SDL_Event event; 123 while(SDL_PollEvent(&event)){ 124 //writeln(event.type); 125 if(enableBindingCapture){ 126 127 KeyBinding kb0; 128 if(event.type == SDL_KEYDOWN){ 129 kb0 = KeyBinding(event.key.keysym.mod, event.key.keysym.scancode, 0, proposedID, Devicetype.KEYBOARD); 130 enableBindingCapture = false; 131 }else if(event.type == SDL_JOYBUTTONDOWN){ 132 133 kb0 = KeyBinding(0, event.jbutton.button, event.jbutton.which, proposedID, Devicetype.JOYSTICK); 134 enableBindingCapture = false; 135 }else if(event.type == SDL_JOYHATMOTION){ 136 kb0 = KeyBinding(4, event.jhat.hat, event.jhat.which, proposedID, Devicetype.JOYSTICK); 137 enableBindingCapture = false; 138 }else if(event.type == SDL_JOYAXISMOTION){ 139 140 if((event.jaxis.value > 4096 || event.jaxis.value <-4096)){ 141 kb0 = KeyBinding(8, event.jaxis.axis, event.jaxis.which, proposedID, Devicetype.JOYSTICK); 142 enableBindingCapture = false; 143 } 144 } 145 if(!enableBindingCapture){ 146 if(exitOnSysKeys){ 147 foreach(KeyBinding kb1; kb){ 148 if(kb1.conflKey(kb0) && kb1.ID[0..3] == "sys"){ 149 return; 150 151 } 152 } 153 154 } 155 int[] removelist; 156 if(delOldOnEvent){ 157 for(int i; i < kb.length; i++){ 158 if(kb[i].ID == kb0.ID){ 159 removelist ~= i; 160 } 161 } 162 } 163 if(delConflKeys){ 164 for(int i; i < kb.length; i++){ 165 if(kb[i].conflKey(kb0)){ 166 removelist ~= i; 167 } 168 } 169 } 170 171 if(delOldOnEvent && delConflKeys){ 172 KeyBinding[] kb1; 173 for(int i; i < kb.length; i++){ 174 if(count(removelist, i) == 0){ 175 kb1 ~= kb[i]; 176 } 177 } 178 kb = kb1; 179 } 180 kb ~= kb0; 181 } 182 } 183 switch(event.type){ 184 case SDL_KEYDOWN: 185 186 if(!tiEnable){ 187 foreach(k; kb){ 188 if(event.key.keysym.scancode == k.scancode && ((event.key.keysym.mod | k.keymodIgnore) == (k.keymod | k.keymodIgnore)) && k.devicetype == Devicetype.KEYBOARD){ 189 invokeKeyPressed(k.ID, event.key.timestamp, 0, Devicetype.KEYBOARD); 190 } 191 } 192 }else{ 193 switch(event.key.keysym.scancode){ 194 case SDL_SCANCODE_RETURN, SDL_SCANCODE_RETURN2, SDL_SCANCODE_KP_ENTER: 195 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.ENTER); 196 break; 197 case SDL_SCANCODE_ESCAPE: 198 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.ESCAPE); 199 break; 200 case SDL_SCANCODE_BACKSPACE: 201 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.BACKSPACE); 202 break; 203 case SDL_SCANCODE_UP: 204 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.CURSORUP); 205 break; 206 case SDL_SCANCODE_DOWN: 207 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.CURSORDOWN); 208 break; 209 case SDL_SCANCODE_LEFT: 210 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.CURSORLEFT); 211 break; 212 case SDL_SCANCODE_RIGHT: 213 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.CURSORRIGHT); 214 break; 215 case SDL_SCANCODE_INSERT: 216 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.INSERT); 217 break; 218 case SDL_SCANCODE_DELETE: 219 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.DELETE); 220 break; 221 case SDL_SCANCODE_HOME: 222 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.HOME); 223 break; 224 case SDL_SCANCODE_END: 225 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.END); 226 break; 227 case SDL_SCANCODE_PAGEUP: 228 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.PAGEUP); 229 break; 230 case SDL_SCANCODE_PAGEDOWN: 231 tiSelect.textInputKeyEvent(event.key.timestamp, event.key.windowID, TextInputKey.PAGEDOWN); 232 break; 233 default: break; 234 } 235 } 236 break; 237 case SDL_KEYUP: 238 if(!tiEnable){ 239 foreach(k; kb ){ 240 if(event.key.keysym.scancode == k.scancode && ((event.key.keysym.mod | k.keymodIgnore) == (k.keymod | k.keymodIgnore)) && k.devicetype == Devicetype.KEYBOARD){ 241 invokeKeyReleased(k.ID, event.key.timestamp, 0, Devicetype.KEYBOARD); 242 } 243 } 244 } 245 break; 246 case SDL_TEXTINPUT: 247 if(tiEnable){ 248 import std.utf : toUTF32; 249 tiSelect.textInputEvent(event.text.timestamp, event.text.windowID, toUTF32(event.text.text.idup)); 250 } 251 break; 252 case SDL_JOYBUTTONDOWN: 253 foreach(k; kb){ 254 if(event.jbutton.button == k.scancode && 0 == k.keymod && k.devicetype == Devicetype.JOYSTICK && k.devicenumber == event.jbutton.which){ 255 invokeKeyPressed(k.ID, event.jbutton.timestamp, event.jbutton.which, Devicetype.JOYSTICK); 256 } 257 } 258 break; 259 case SDL_JOYBUTTONUP: 260 foreach(k; kb){ 261 if(event.jbutton.button == k.scancode && 0 == k.keymod && k.devicetype == Devicetype.JOYSTICK && k.devicenumber == event.jbutton.which){ 262 invokeKeyReleased(k.ID, event.jbutton.timestamp, event.jbutton.which, Devicetype.JOYSTICK); 263 } 264 } 265 break; 266 case SDL_JOYHATMOTION: 267 foreach(k; kb){ 268 if(event.jhat.alignof == k.scancode && 4 == k.keymod && k.devicetype == Devicetype.JOYSTICK && k.devicenumber == event.jhat.which){ 269 invokeKeyReleased(hatpos, event.jhat.timestamp, event.jhat.which, Devicetype.JOYSTICK); 270 invokeKeyPressed(k.ID, event.jhat.timestamp, event.jhat.which, Devicetype.JOYSTICK); 271 hatpos = k.ID; 272 } 273 } 274 break; 275 case SDL_JOYAXISMOTION: 276 foreach(k; kb){ 277 if(event.jaxis.axis == k.scancode && 8 == k.keymod && k.devicetype == Devicetype.JOYSTICK && k.devicenumber == event.jaxis.which){ 278 invokeAxisEvent(k.ID, event.jaxis.timestamp, event.jaxis.value, event.jaxis.which, Devicetype.JOYSTICK); 279 } 280 } 281 break; 282 case SDL_MOUSEBUTTONDOWN: 283 invokeMouseEvent(event.button.which, event.button.timestamp, event.button.windowID, event.button.button, event.button.state, event.button.clicks, event.button.x, event.button.y); 284 break; 285 case SDL_MOUSEBUTTONUP: 286 invokeMouseEvent(event.button.which, event.button.timestamp, event.button.windowID, event.button.button, event.button.state, event.button.clicks, event.button.x, event.button.y); 287 break; 288 case SDL_MOUSEMOTION: 289 invokeMouseMotionEvent(event.motion.timestamp, event.motion.windowID, event.motion.which, event.motion.state, event.motion.x, event.motion.y, event.motion.xrel, event.motion.yrel); 290 mouseX = event.motion.x; 291 mouseY = event.motion.y; 292 break; 293 case SDL_MOUSEWHEEL: 294 invokeMouseWheelEvent(event.wheel.type, event.wheel.timestamp, event.wheel.windowID, event.wheel.which, event.wheel.x, event.wheel.y); 295 break; 296 case SDL_QUIT: 297 invokeQuitEvent(); 298 break; 299 case SDL_JOYDEVICEADDED: 300 int i = event.jdevice.which; 301 joysticks ~= SDL_JoystickOpen(i); 302 if(joysticks[i] !is null){ 303 joyNames ~= to!string(SDL_JoystickName(joysticks[i])); 304 joyButtons ~= SDL_JoystickNumButtons(joysticks[i]); 305 joyAxes ~= SDL_JoystickNumAxes(joysticks[i]); 306 joyHats ~= SDL_JoystickNumHats(joysticks[i]); 307 /*writeln("Buttons: ", SDL_JoystickNumButtons(joysticks[i])); 308 writeln("Axes: ", SDL_JoystickNumAxes(joysticks[i])); 309 writeln("Hats: ", SDL_JoystickNumHats(joysticks[i]));*/ 310 } 311 invokeControllerAddedEvent(i); 312 break; 313 case SDL_JOYDEVICEREMOVED: 314 SDL_JoystickClose(joysticks[event.jdevice.which]); 315 invokeControllerRemovedEvent(event.jdevice.which); 316 break; 317 default: break; 318 } 319 320 } 321 322 } 323 324 private void invokeKeyPressed(string ID, uint timestamp, uint devicenumber, uint devicetype){ 325 foreach(i; il){ 326 if(i) 327 i.keyPressed(ID, timestamp, devicenumber, devicetype); 328 } 329 } 330 private void invokeKeyReleased(string ID, uint timestamp, uint devicenumber, uint devicetype){ 331 foreach(i; il){ 332 if(i) 333 i.keyReleased(ID, timestamp, devicenumber, devicetype); 334 } 335 } 336 private void invokeMouseEvent(uint which, uint timestamp, uint windowID, ubyte button, ubyte state, ubyte clicks, int x, int y){ 337 foreach(MouseListener m; ml){ 338 if(m) 339 m.mouseButtonEvent(which, timestamp, windowID, button, state, clicks, x, y); 340 } 341 } 342 private void invokeMouseWheelEvent(uint type, uint timestamp, uint windowID, uint which, int x, int y){ 343 foreach(MouseListener m; ml){ 344 if(m) 345 m.mouseWheelEvent(type, timestamp, windowID, which, x, y, mouseX, mouseY); 346 } 347 } 348 private void invokeMouseMotionEvent(uint timestamp, uint windowID, uint which, uint state, int x, int y, int relX, int relY){ 349 foreach(MouseListener m; ml){ 350 if(m) 351 m.mouseMotionEvent(timestamp, windowID, which, state, x, y, relX, relY); 352 } 353 } 354 private void invokeAxisEvent(string ID, uint timestamp, short val, uint devicenumber, uint devicetype){ 355 foreach(a; al){ 356 if(a) 357 a.axisEvent(ID, timestamp, val, devicenumber, devicetype); 358 } 359 } 360 private void invokeQuitEvent(){ 361 foreach(SystemEventListener q; sel){ 362 q.onQuit(); 363 } 364 } 365 private void invokeControllerRemovedEvent(uint ID){ 366 foreach(SystemEventListener q; sel){ 367 q.controllerRemoved(ID); 368 } 369 } 370 private void invokeControllerAddedEvent(uint ID){ 371 foreach(SystemEventListener q; sel){ 372 q.controllerAdded(ID); 373 } 374 } 375 ///Sets wether to use the newer XInput over the older DirectInput. 376 public static setXInput(bool state){ 377 if(state){ 378 SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "1"); 379 }else{ 380 SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "0"); 381 } 382 } 383 ///Starts the TextInputEvent and disables the polling of normal input events. 384 public void startTextInput(TextInputListener tl){ 385 if(tiSelect !is null){ 386 tiSelect.dropTextInput(); 387 } 388 SDL_StartTextInput(); 389 tiEnable = true; 390 391 tiSelect = tl; 392 } 393 ///Stops the TextInputEvent and enables the polling of normal input events. 394 public void stopTextInput(TextInputListener tl){ 395 SDL_StopTextInput(); 396 tl.dropTextInput(); 397 tiEnable = false; 398 tiSelect = null; 399 } 400 /*public void addTextInputListener(TextInputListener til){ 401 //tl[] ~= til; 402 } 403 public void removeTextInputListener(TextInputListener tl){ 404 //tl.remove(ID); 405 406 }*/ 407 /* 408 * Converts between SDL and PPE key modificators. 409 * PPE native key modificator layout: 410 * bit 0: Left Shift 411 * bit 1: Right Shift 412 * bit 2: Any Shift 413 * bit 3: Left Control 414 * bit 4: Right Control 415 * bit 5: Any Control 416 * bit 6: Left Alt 417 * bit 7: Right Alt 418 * bit 8: Any Alt 419 * bit 9: Left OSKey 420 * bit 10: Right OSKey 421 * bit 11: Any OSKey 422 * bit 12: NumLock 423 * bit 13: CapsLock 424 * bit 14: 425 */ 426 } 427 428 429 /** 430 * Defines a keybinding. 431 */ 432 public struct KeyBinding{ 433 public uint scancode; ///The code of the phisical key relative to the US English keyboard. 434 public uint devicenumber; ///The identificator of the device. 435 public uint devicetype; ///The type of the device. 436 public ushort keymod, keymodIgnore; ///Keymod sets the modifierkeys required to activate the event, keymodIgnore sets the keys that are ignored during event polling. 437 public string ID; ///ID of the event 438 this(ushort keymod, uint scancode, uint devicenumber, string ID, uint devicetype, ushort keymodIgnore = KeyModifier.ANY){ 439 this.keymod = keymod; 440 this.scancode = scancode; 441 this.devicenumber = devicenumber; 442 this.ID = ID; 443 this.devicetype = devicetype; 444 this.keymodIgnore = keymodIgnore; 445 } 446 ///Returns if there's a conflicting key. 447 public bool conflKey(KeyBinding a){ 448 if(a.scancode == this.scancode && a.keymod == this.keymod && a.devicenumber == this.devicenumber && a.devicetype == this.devicetype){ 449 return true; 450 } 451 return false; 452 } 453 ///Returns true if the two KeyBindings have the same ID 454 public bool conflID(KeyBinding a){ 455 if(a.ID == this.ID){ 456 return true; 457 } 458 return false; 459 } 460 ///Returns true it the two KeyBindings are equal. 461 public bool opEquals(const KeyBinding s){ 462 if(s.scancode == this.scancode && s.keymod == this.keymod && s.devicenumber == this.devicenumber && s.devicetype == this.devicetype && s.ID == this.ID){ 463 return true; 464 } 465 return false; 466 } 467 ///Returns a standard string representation of the keybinding. 468 public string toString(){ 469 string s; 470 s ~= "ID: "; 471 s ~= ID; 472 s ~= " Keycode: "; 473 s ~= to!string(scancode); 474 s ~= " Keymod: "; 475 s ~= to!string(keymod); 476 s ~= " Devicetype: "; 477 s ~= to!string(devicetype); 478 s ~= " Devicenumber: "; 479 s ~= to!string(devicenumber); 480 return s; 481 } 482 } 483 484 public interface InputListener{ 485 public void keyPressed(string ID, uint timestamp, uint devicenumber, uint devicetype); 486 public void keyReleased(string ID, uint timestamp, uint devicenumber, uint devicetype); 487 } 488 489 public interface AxisListener{ 490 public void axisEvent(string ID, uint timestamp, short val, uint devicenumber, uint devicetype); 491 } 492 493 public interface MovementListener{ 494 public void movementEvent(string ID, short x, short y, short relX, short relY, uint devicenumber, uint devicetype); 495 } 496 497 public interface MouseListener{ 498 public void mouseButtonEvent(uint which, uint timestamp, uint windowID, ubyte button, ubyte state, ubyte clicks, int x, int y); 499 public void mouseWheelEvent(uint type, uint timestamp, uint windowID, uint which, int x, int y, int wX, int wY); 500 public void mouseMotionEvent(uint timestamp, uint windowID, uint which, uint state, int x, int y, int relX, int relY); 501 } 502 503 public interface TextInputListener{ 504 public void textInputEvent(uint timestamp, uint windowID, dstring text); 505 public void textInputKeyEvent(uint timestamp, uint windowID, TextInputKey key, ushort modifier = 0); 506 //public void textSelectionEvent(uint timestamp, uint window, int action); 507 //public void textClipboardEvent(uint timestamp, uint window, wstring text); 508 public void dropTextInput(); 509 } 510 511 public interface TextInputHandler{ 512 public void startTextInput(TextInputListener tl); 513 public void stopTextInput(TextInputListener tl); 514 //public void addTextInputListener(TextInputListener tl); DEPRECATED! 515 //public void removeTextInputListener(TextInputListener tl); DEPRECATED! 516 } 517 518 public interface SystemEventListener{ 519 public void onQuit(); 520 public void controllerRemoved(uint ID); 521 public void controllerAdded(uint ID); 522 } 523 524 public enum Devicetype{ 525 KEYBOARD = 0, 526 JOYSTICK = 1, 527 MOUSE = 2, 528 TOUCHSCREEN = 3 529 } 530 531 public enum TextInputKey{ 532 ENTER = 1, 533 ESCAPE = 2, 534 BACKSPACE = 3, 535 CURSORUP = 4, 536 CURSORDOWN = 5, 537 CURSORLEFT = 6, 538 CURSORRIGHT = 7, 539 INSERT = 8, 540 DELETE = 9, 541 HOME = 10, 542 END = 11, 543 PAGEUP = 12, 544 PAGEDOWN = 13 545 } 546 547 public enum TextInputType : uint{ 548 NULL = 0, 549 TEXT = 1, 550 DECIMAL = 2, 551 DISABLE = 65536, ///For use in listboxes 552 } 553 554 /// Standard key modifiers to avoid public SDL imports and to allow alternative library backends. 555 public enum KeyModifier : ushort{ 556 NONE = 0x0000, 557 LSHIFT = 0x0001, 558 RSHIFT = 0x0002, 559 SHIFT = 0x0003, 560 LCTRL = 0x0040, 561 RCTRL = 0x0080, 562 CTRL = 0x00C0, 563 LALT = 0x0100, 564 RALT = 0x0200, 565 ALT = 0x0300, 566 LGUI = 0x0400, 567 RGUI = 0x0800, 568 GUI = 0x0C00, 569 NUM = 0x1000, 570 CAPS = 0x2000, 571 LOCKKEYIGNORE = NUM + CAPS, ///Use this if only Caps lock and Num lock needs to be ignored 572 MODE = 0x4000, 573 RESERVED = 0x8000, 574 ANY = 0xFFFF 575 } 576 public enum JoyModifier : ushort{ 577 BUTTONS = 0x0000, 578 DPAD = 0x0004, 579 AXIS = 0x0008 580 } 581 public enum MouseButton : ubyte{ 582 LEFT = 1, 583 MID = 2, 584 RIGHT = 3, 585 NEXT = 4, 586 PREVIOUS = 5 587 } 588 public enum ButtonState : ubyte{ 589 RELEASED = 0, 590 PRESSED = 1 591 } 592 public enum ScanCode : uint{ 593 A = 4, 594 B = 5, 595 C = 6, 596 D = 7, 597 E = 8, 598 F = 9, 599 G = 10, 600 H = 11, 601 I = 12, 602 J = 13, 603 K = 14, 604 L = 15, 605 M = 16, 606 N = 17, 607 O = 18, 608 P = 19, 609 Q = 20, 610 R = 21, 611 S = 22, 612 T = 23, 613 U = 24, 614 V = 25, 615 W = 26, 616 X = 27, 617 Y = 28, 618 Z = 29, 619 620 n1 = 30, 621 n2 = 31, 622 n3 = 32, 623 n4 = 33, 624 n5 = 34, 625 n6 = 35, 626 n7 = 36, 627 n8 = 37, 628 n9 = 38, 629 n0 = 39, 630 631 ENTER = 40, 632 ESCAPE = 41, 633 BACKSPACE = 42, 634 TAB = 43, 635 SPACE = 44, 636 637 MINUS = 45, 638 EQUALS = 46, 639 LEFTBRACKET = 47, 640 RIGHTBRACKET = 48, 641 BACKSLASH = 49, 642 NONUSLASH = 50, 643 SEMICOLON = 51, 644 APOSTROPHE = 52, 645 GRAVE = 53, 646 COMMA = 54, 647 PERIOD = 55, 648 SLASH = 56, 649 CAPSLOCK = 57, 650 651 F1 = 58, 652 F2 = 59, 653 F3 = 60, 654 F4 = 61, 655 F5 = 62, 656 F6 = 63, 657 F7 = 64, 658 F8 = 65, 659 F9 = 66, 660 F10 = 67, 661 F11 = 68, 662 F12 = 69, 663 664 PRINTSCREEN = 70, 665 SCROLLLOCK = 71, 666 PAUSE = 72, 667 INSERT = 73, 668 HOME = 74, 669 PAGEUP = 75, 670 DELETE = 76, 671 END = 77, 672 PAGEDOWN = 78, 673 RIGHT = 79, 674 LEFT = 80, 675 DOWN = 81, 676 UP = 82, 677 678 NUMLOCK = 83, 679 NP_DIVIDE = 84, 680 NP_MULTIPLY = 85, 681 NP_MINUS = 86, 682 NP_PLUS = 87, 683 NP_ENTER = 88, 684 685 np1 = 89, 686 np2 = 90, 687 np3 = 91, 688 np4 = 92, 689 np5 = 93, 690 np6 = 94, 691 np7 = 95, 692 np8 = 96, 693 np9 = 97, 694 np0 = 98, 695 696 NP_PERIOD = 99, 697 698 NONUSBACKSLASH = 100, 699 APPLICATION = 101, 700 701 NP_EQUALS = 102, 702 703 F13 = 104, 704 F14 = 105, 705 F15 = 106, 706 F16 = 107, 707 F17 = 108, 708 F18 = 109, 709 F19 = 110, 710 F20 = 111, 711 F21 = 112, 712 F22 = 113, 713 F23 = 114, 714 F24 = 115, 715 716 EXECUTE = 116, 717 HELP = 117, 718 MENU = 118, 719 SELECT = 119, 720 STOP = 120, 721 REDO = 121, 722 UNDO = 122, 723 CUT = 123, 724 COPY = 124, 725 PASTE = 125, 726 FIND = 126, 727 MUTE = 127, 728 VOLUME_UP = 128, 729 VOLUME_DOWN = 129, 730 731 NP_COMMA = 133, 732 NP_EQUALSAS400 = 134, 733 734 INTERNATIONAL1 = 135, 735 INTERNATIONAL2 = 136, 736 INTERNATIONAL3 = 137, 737 INTERNATIONAL4 = 138, 738 INTERNATIONAL5 = 139, 739 INTERNATIONAL6 = 140, 740 INTERNATIONAL7 = 141, 741 INTERNATIONAL8 = 142, 742 INTERNATIONAL9 = 143, 743 744 LANGUAGE1 = 144, 745 LANGUAGE2 = 145, 746 LANGUAGE3 = 146, 747 LANGUAGE4 = 147, 748 LANGUAGE5 = 148, 749 LANGUAGE6 = 149, 750 LANGUAGE7 = 150, 751 LANGUAGE8 = 151, 752 LANGUAGE9 = 152, 753 754 ALTERASE = 153, 755 SYSREQ = 154, 756 CANCEL = 155, 757 PRIOR = 157, 758 ENTER2 = 158, 759 SEPARATOR = 159, 760 OUT = 160, 761 OPERATE = 161, 762 CLEARAGAIN = 162, 763 CRSEL = 163, 764 EXSEL = 164, 765 766 NP00 = 176, 767 NP000 = 177, 768 THROUSANDSEPAR = 178, 769 HUNDREDSSEPAR = 179, 770 CURRENCYUNIT = 180, 771 CURRENCYSUBUNIT = 181, 772 NP_LEFTPAREN = 182, 773 NP_RIGHTPAREN = 183, 774 NP_LEFTBRACE = 184, 775 NP_RIGHTBRACE = 185, 776 NP_TAB = 186, 777 NP_BACKSPACE = 187, 778 NP_A = 188, 779 NP_B = 189, 780 NP_C = 190, 781 NP_D = 191, 782 NP_E = 192, 783 NP_F = 193, 784 NP_XOR = 194, 785 NP_POWER = 195, 786 NP_PERCENT = 196, 787 NP_LESS = 197, 788 NP_GREATER = 198, 789 NP_AMPERSAND = 199, 790 NP_DBAMPERSAND = 200, 791 NP_VERTICALBAR = 201, 792 NP_DBVERTICALBAR= 202, 793 NP_COLON = 203, 794 NP_HASH = 204, 795 NP_SPACE = 205, 796 NP_AT = 206, 797 NP_EXCLAM = 207, 798 NP_MEMSTORE = 208, 799 NP_MEMRECALL = 209, 800 NP_MEMCLEAR = 210, 801 NP_MEMADD = 211, 802 NP_MEMSUBSTRACT = 212, 803 NP_MEMMULTIPLY = 213, 804 NP_MEMDIVIDE = 214, 805 NP_PLUSMINUS = 215, 806 NP_CLEAR = 216, 807 NP_CLEARENTRY = 217, 808 NP_BINARY = 218, 809 NP_OCTAL = 219, 810 NP_DECIMAL = 220, 811 NP_HEXADECIMAL = 221, 812 813 LCTRL = 224, 814 LSHIFT = 225, 815 LALT = 226, 816 LGUI = 227, 817 RCTRL = 228, 818 RSHIFT = 229, 819 RALT = 230, 820 RGUI = 231, 821 822 AUDIONEXT = 258, 823 AUDIOPREV = 259, 824 AUDIOSTOP = 260, 825 AUDIOPLAY = 261, 826 AUDIOMUTE = 262, 827 MEDIASELECT = 263, 828 WWW = 264, 829 MAIL = 265, 830 CALCULATOR = 266, 831 COMPUTER = 267, 832 }