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