Author Topic: Programmable Keyboard Input  (Read 4476 times)

0 Members and 1 Guest are viewing this topic.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Programmable Keyboard Input
« Reply #15 on: December 30, 2019, 01:39:15 pm »
Using the SDL version to rebuild its behavior with "programmable input" and just noticed, that SDL even returns a different char/value for the ALT+SHIFT combo, whereas SHIFT+CTRL returns same as CTRL only and CTRL-SHIFT-ALT returns same as ALT only.

I didn't think folks would need any further flexibility than what I'd started with, but it's quite easy to expand our functionality.  See if this version handles all the extreme input cases you could imagine, where you might need to remap characters for different values:

Code: QB64: [Select]
  1. DECLARE LIBRARY 'function is already used by QB64 so "User32" is not required
  2.     FUNCTION GetKeyState% (BYVAL vkey AS LONG)
  3.  
  4. TYPE KeyboardInfo_Type
  5.     Index AS LONG
  6.     ASCII AS LONG
  7.     Ctrl AS LONG
  8.     Shift AS LONG
  9.     Alt AS LONG
  10.     AltGr AS LONG
  11.     Repeat AS _FLOAT
  12.     LastHit AS _FLOAT
  13.     Down AS LONG
  14.     AltShift AS LONG
  15.     AltCtrl AS LONG
  16.     AltAltGr AS LONG
  17.     CtrlShift AS LONG
  18.     CtrlAlt AS LONG
  19.     CtrlAltGr AS LONG
  20.     ShiftAltGr AS LONG
  21.     CtrlAltShift AS LONG
  22.  
  23. DIM SHARED Keys(254) AS KeyboardInfo_Type
  24. Init_KeyCodes
  25. Remap_KeyCode 65, 65, 1, 97, 160, 142, 0.2: 'A , , a,  , Ž
  26. 'Remap_Extended_KeyCode (Which&, AltShift&, AltCtrl&, AltAltGr&, CtrlShift&, CtrlAltGr&, ShiftAltGr&, CtrlAltShift&)
  27. Remap_Extended_KeyCode 65, 143, 241, 242, 243, 244, 245, 246 ' and more!
  28.  
  29.     k = KeyHit
  30.     IF k <> 0 THEN
  31.         'LOCATE 1, 1
  32.         PRINT k;
  33.         IF ABS(k) < 256 THEN PRINT CHR$(34); CHR$(ABS(k)); CHR$(34) ELSE PRINT
  34.     END IF
  35.     'LOCATE 2, 1: PRINT KeyDown(1) 'Test to see if CHR$(1) is held down (which is generated via a CTRL-A combo)
  36.     _LIMIT 30
  37. LOOP UNTIL k = 27 'escape
  38.  
  39. SUB SetAltGr (Key1 AS INTEGER, Key2 AS INTEGER)
  40.     AltGr(0) = Key1 'any key from our index (0 says no key)
  41.     AltGr(1) = Key2 'PLUS any other key from our index (0 says no additional key)
  42.     'Using this, we can set AltGr to become several things.
  43.     'AltGr(0) = 165, AltGr(1) = 0 -- This would say we're using the RIGHT Alt key (alone) to simulate the AltGr key.  (Windows Onscreen Keyboard does this.)
  44.     'AltGr(0) = 17, AltGr(1) = 18 -- This would use any CTRL-ALT combo to simulate a AltGr keypress.
  45.     'Some useful values are listed for quick reference below
  46.     '0 = NoKey
  47.     '17 = ANY Ctrl
  48.     '18 = ANY Alt
  49.     '162 = Left Control
  50.     '163 = Right Control
  51.     '164 = Left Alt
  52.     '165 = Right Alt
  53.  
  54.     'Default is for AltGr(0) = 165, AltGr(1) = 0, which uses Right-Alt alone as the AltGr key.
  55.     'Feel free to customize the setting to your personal preference/need.
  56.  
  57. FUNCTION KeyHit&
  58.     STATIC ReturnCount AS INTEGER
  59.     STATIC ReturnValues(30) AS LONG
  60.     'IF Keys(1).Index = 0 THEN Init_KeyCodes 'if someone forgets to put the init routine in their code, be certain to initialize the codes before attempting to use them.
  61.  
  62.  
  63.     IF ReturnCount > 0 THEN 'If we generated a cue of values last pass, clear those up first, before getting new values.
  64.         'The only time we really see this is when we hit a shift, ctrl, alt key, usually.
  65.         KeyHit = ReturnValues(1)
  66.         FOR i = 1 TO ReturnCount - 1
  67.             ReturnValues(i) = ReturnValues(i + 1)
  68.         NEXT
  69.         ReturnCount = ReturnCount - 1
  70.         EXIT FUNCTION
  71.     END IF
  72.  
  73.     IF Keys(16).Down THEN Shift = -1 ELSE Shift = 0
  74.     IF Keys(17).Down THEN Ctrl = -1 ELSE Ctrl = 0
  75.     IF Keys(18).Down THEN Alt = -1 ELSE Alt = 0
  76.     IF AltGr(0) <> 0 AND AltGr(1) <> 0 THEN
  77.         IF Keys(AltGr(0)).Down AND Keys(AltGr(1)).Down THEN AltGr = -1 ELSE AltGr = 0
  78.     ELSEIF AltGr(1) <> 0 THEN
  79.         IF Keys(AltGr(1)).Down THEN AltGr = -1 ELSE AltGr = 0
  80.     ELSEIF AltGr(0) <> 0 THEN
  81.         IF Keys(AltGr(0)).Down THEN AltGr = -1 ELSE AltGr = 0
  82.     ELSE
  83.         AltGr = 0
  84.     END IF
  85.  
  86.     'until Ctrl or Alt status, if the key down was used to help generate AltGr as a modifier key
  87.     IF AltGr THEN
  88.         IF (AltGr(0) = 18 OR AltGr(1) = 18) THEN Alt = 0 'if we use both ALT keys to represent part of AltGr, when AltGr is active, Alt isn't.
  89.         IF (AltGr(0) = 164 OR AltGr(1) = 164) AND Keys(165).Down = 0 THEN Alt = 0 'if we use Left ALT keys to represent part of AltGr, when AltGr is active, Left Alt isn't.
  90.         IF (AltGr(0) = 165 OR AltGr(1) = 165) AND Keys(164).Down = 0 THEN Alt = 0 'if we use Right ALT keys to represent part of AltGr, when AltGr is active, Right Alt isn't.
  91.         IF (AltGr(0) = 17 OR AltGr(1) = 17) THEN Ctrl = 0 'if we use both CTRL keys to represent part of AltGr, when AltGr is active, Ctrl isn't.
  92.         IF (AltGr(0) = 162 OR AltGr(1) = 162) AND Keys(163).Down = 0 THEN Ctrl = 0 'if we use Left CTRL keys to represent part of AltGr, when AltGr is active, Left Ctrl isn't.
  93.         IF (AltGr(0) = 163 OR AltGr(1) = 163) AND Keys(162).Down = 0 THEN Ctrl = 0 'if we use Right CTRL keys to represent part of AltGr, when AltGr is active, Right Ctrl isn't.
  94.     END IF
  95.  
  96.     IF Alt AND Shift THEN AltShift = -1 ELSE AltShift = 0
  97.     IF Alt AND Ctrl THEN AltCtrl = -1 ELSE AltCtrl = 0
  98.     IF Alt AND AltAltGR THEN AltAltGR = -1 ELSE AltAltGR = 0
  99.     IF Ctrl AND Shift THEN CtrlShift = -1 ELSE CtrlShift = 0
  100.     IF Shift AND AltGr THEN ShiftAltGr = -1 ELSE ShiftAltGr = 0
  101.     IF Ctrl AND Alt AND Shift THEN CtrlAltShift = -1 ELSE CtrlAltShift = 0
  102.  
  103.         FOR i = 1 TO 254
  104.  
  105.             r = GetKeyState(Keys(i).Index) AND &H8000
  106.  
  107.             IF r THEN 'the key is down
  108.  
  109.  
  110.                 IF Keys(i).LastHit THEN
  111.                     IF ExtendedTimer > Keys(i).LastHit THEN
  112.                         ReturnCount = ReturnCount + 1 'add one to the return buffer
  113.                         ReturnValues(ReturnCount) = Keys(i).Down 'and put the existing value back in the buffer, as a key repeat
  114.                     END IF
  115.                 ELSE
  116.  
  117.                     IF Keys(i).Down = 0 THEN 'the key was up on the last pass.
  118.                         IF CtrlAltShift <> 0 AND Keys(i).CtrlAltShift <> 0 THEN 'return the CtrlAltShift value
  119.                             Keys(i).Down = Keys(i).CtrlAltShift
  120.                         ELSEIF AltAltGR <> 0 AND Keys(i).AltAltGr <> 0 THEN 'return the AltAltGr value
  121.                             Keys(i).Down = Keys(i).AltAltGr
  122.                         ELSEIF CtrlAltGr& <> 0 AND Keys(i).CtrlAltGr& <> 0 THEN 'return the CtrlAltGr& value
  123.                             Keys(i).Down = Keys(i).CtrlAltGr&
  124.                         ELSEIF ShiftAltGr <> 0 AND Keys(i).ShiftAltGr <> 0 THEN 'return the ShiftAltGr value
  125.                             Keys(i).Down = Keys(i).ShiftAltGr
  126.                         ELSEIF CtrlShift <> 0 AND Keys(i).CtrlShift <> 0 THEN 'return the CtrlShift value
  127.                             Keys(i).Down = Keys(i).CtrlShift
  128.                         ELSEIF AltCtrl <> 0 AND Keys(i).AltCtrl <> 0 THEN 'return the AltCtrl value
  129.                             Keys(i).Down = Keys(i).AltCtrl
  130.                         ELSEIF AltShift <> 0 AND Keys(i).AltShift <> 0 THEN 'return the AltShift value
  131.                             Keys(i).Down = Keys(i).AltShift
  132.                         ELSEIF AltGr <> 0 AND Keys(i).AltGr <> 0 THEN 'return the altgr value
  133.                             Keys(i).Down = Keys(i).AltGr
  134.                         ELSEIF Shift <> 0 AND Keys(i).Shift <> 0 THEN 'return the shift value
  135.                             Keys(i).Down = Keys(i).Shift
  136.                             IF _CAPSLOCK = 0 THEN 'caps lock basically reverses the behavior of the shift key with the letters A-Z and a-z
  137.                                 SELECT CASE i
  138.                                     CASE 65 TO 90: Keys(i).Down = Keys(i).ASCII
  139.                                 END SELECT
  140.                             END IF
  141.                         ELSEIF (Ctrl <> 0) AND (Keys(i).Ctrl <> 0) THEN 'return the ctrl value
  142.                             Keys(i).Down = Keys(i).Ctrl
  143.                         ELSEIF Alt <> 0 AND Keys(i).Alt <> 0 THEN 'return the alt value
  144.                             Keys(i).Down = Keys(i).Alt
  145.                         ELSE 'all that's left is to return the ASCII value
  146.                             Keys(i).Down = Keys(i).ASCII
  147.                             IF _CAPSLOCK = 0 THEN 'caps lock basically reverses the behavior of the shift key with the letters A-Z and a-z
  148.                                 SELECT CASE i
  149.                                     CASE 65 TO 90: Keys(i).Down = Keys(i).Shift
  150.                                 END SELECT
  151.                             END IF
  152.                         END IF
  153.                         ReturnCount = ReturnCount + 1 'add one to the return buffer
  154.                         ReturnValues(ReturnCount) = Keys(i).Down 'and store the value in the buffer
  155.  
  156.                         IF Keys(i).Repeat = -1 THEN 'keys that are set to a -1 on repeat simply toggle state as on, or off.
  157.                             Keys(i).LastHit = 1E+1000 'such as SHIFT, CTRL, ALT...
  158.                         ELSE
  159.                             Keys(i).LastHit = ExtendedTimer + Keys(i).Repeat 'and record when we hit it for repeat purposes
  160.                         END IF
  161.                     END IF
  162.                 END IF
  163.             ELSE
  164.                 IF Keys(i).Down THEN 'the key was down on the last pass
  165.                     ReturnCount = ReturnCount + 1
  166.                     ReturnValues(ReturnCount) = -Keys(i).Down 'mark it as being up on this one
  167.                 END IF
  168.                 Keys(i).Down = 0 'and set it back down for future passes
  169.                 Keys(i).LastHit = 0 'once again, set it as being ready to be hit again
  170.             END IF
  171.         NEXT
  172.  
  173.         IF ReturnCount > 0 THEN 'If we generated a cue of values last pass, clear those up first, before getting new values.
  174.             'The only time we really see this is when we hit a shift, ctrl, alt key, usually.
  175.             KeyHit = ReturnValues(1)
  176.             FOR i = 1 TO ReturnCount - 1
  177.                 ReturnValues(i) = ReturnValues(i + 1)
  178.             NEXT
  179.             ReturnCount = ReturnCount - 1
  180.             EXIT FUNCTION
  181.         END IF
  182.  
  183.     END IF 'End of IF _WINDOWHASFOCUS
  184.  
  185.  
  186.  
  187.  
  188. SUB Remap_KeyCode (Which AS LONG, ASCII AS LONG, Ctrl AS LONG, Shift AS LONG, Alt AS LONG, AltGr AS LONG, Repeat AS _FLOAT)
  189.     DIM i AS LONG
  190.     i = Which
  191.     Keys(i).Index = i
  192.     Keys(i).ASCII = ASCII
  193.     Keys(i).Ctrl = Ctrl
  194.     Keys(i).Shift = Shift
  195.     Keys(i).Alt = Alt
  196.     Keys(i).AltGr = AltGr
  197.     Keys(i).Repeat = Repeat
  198.     Keys(i).LastHit = 0
  199.     Keys(i).Down = 0
  200.  
  201. SUB Remap_Extended_KeyCode (Which&, AltShift&, AltCtrl&, AltAltGr&, _
  202.          CtrlShift&, CtrlAltGr&, ShiftAltGr&, CtrlAltShift&)
  203.     Keys(Which&).AltShift = AltShift&
  204.     Keys(Which&).AltCtrl = AltCtrl&
  205.     Keys(Which&).AltAltGr = AltAltGr&
  206.     Keys(Which&).CtrlShift = CtrlShift&
  207.     Keys(Which&).CtrlAltGr = CtrlAltGr&
  208.     Keys(Which&).ShiftAltGr = ShiftAltGr&
  209.     Keys(Which&).CtrlAltShift = CtrlAltShift&
  210.  
  211.  
  212.  
  213. FUNCTION KeyDown& (Code AS LONG)
  214.     FOR i = 1 TO 254
  215.         IF Keys(i).Down = Code THEN KeyDown& = -1: EXIT FUNCTION
  216.     NEXT
  217.     KeyDown& = 0
  218.  
  219. SUB Init_KeyCodes
  220.     RESTORE default_keyboard_data
  221.     FOR i = 1 TO 254
  222.         READ Keys(i).Index, Keys(i).ASCII, Keys(i).Ctrl, Keys(i).Shift, Keys(i).Alt, Keys(i).AltGr, Keys(i).Repeat
  223.         Keys(i).LastHit = 0: Keys(i).Down = 0
  224.     NEXT
  225.  
  226.     default_keyboard_data:
  227.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  228.     DATA 1,900001,0,0,0,0,0.2: 'Left Mouse Button
  229.     DATA 2,900002,0,0,0,0,0.2: 'Right Mouse Button
  230.     DATA 3,900003,0,0,0,0,0.2: 'VK_Cancel
  231.     DATA 4,900004,0,0,0,0,0.2: 'Middle Mouse Button
  232.     DATA 5,900005,0,0,0,0,0.2: 'Mouse Button 4
  233.     DATA 6,900006,0,0,0,0,0.2: 'Mouse Button 5
  234.     DATA 7,900007,0,0,0,0,0.2: 'Undefined
  235.     DATA 8,8,0,0,0,0,0.2: 'Backspace
  236.     DATA 9,9,0,0,0,0,0.2: 'Tab
  237.     DATA 10,900010,0,0,0,0,0.2: 'Reserved
  238.     DATA 11,900011,0,0,0,0,0.2: 'Reserved
  239.     DATA 12,19456,0,0,0,0,0.2: 'Clear
  240.     DATA 13,13,0,0,0,0,0.2: 'Enter
  241.     DATA 14,900014,0,0,0,0,0.2: 'Undefined
  242.     DATA 15,900015,0,0,0,0,0.2: 'Undefined
  243.     DATA 16,100016,0,0,0,0,-1: 'Shift (Notice I set it to simple toddle and report UP/DOWN results for us)
  244.     DATA 17,100017,0,0,0,0,-1: 'Ctrl   (Same)
  245.     DATA 18,100018,0,0,0,0,-1: 'Alt     (Same)
  246.     DATA 19,100019,0,0,0,0,0.2: 'Pause
  247.     DATA 20,100301,0,0,0,0,-1: 'Caps Lock
  248.     DATA 21,900021,0,0,0,0,0.2: 'VK_Hangul
  249.     DATA 22,900022,0,0,0,0,0.2: 'Undefined
  250.     DATA 23,900023,0,0,0,0,0.2: 'VK_Junja
  251.     DATA 24,900024,0,0,0,0,0.2: 'VK_Final
  252.     DATA 25,900025,0,0,0,0,0.2: 'VK_Hanga//VK_Kanji
  253.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  254.     DATA 26,900026,0,0,0,0,0.2: 'Undefined
  255.     DATA 27,27,0,0,0,0,0.2: 'ESC
  256.     DATA 28,900028,0,0,0,0,0.2: 'VK_Convert
  257.     DATA 29,900029,0,0,0,0,0.2: 'VK_NonConvert
  258.     DATA 30,900030,0,0,0,0,0.2: 'VK_Accept
  259.     DATA 31,900031,0,0,0,0,0.2: 'VK_ModeChange
  260.     DATA 32,32,0,0,0,0,0.2: 'VK_Space
  261.     DATA 33,18688,0,0,0,0,0.2: 'Page Up
  262.     DATA 34,20736,0,0,0,0,0.2: 'Page Down
  263.     DATA 35,20224,0,0,0,0,0.2: 'End
  264.     DATA 36,18176,0,0,0,0,0.2: 'Home
  265.     DATA 37,19200,0,0,0,0,0.2: 'Left Arrow
  266.     DATA 38,18432,0,0,0,0,0.2: 'Up Arrow
  267.     DATA 39,19712,0,0,0,0,0.2: 'Right Arrow
  268.     DATA 40,20480,0,0,0,0,0.2: 'Down Arrow
  269.     DATA 41,900041,0,0,0,0,-1: 'VK_SELECT
  270.     DATA 42,900042,0,0,0,0,-1: 'CK_PRINT
  271.     DATA 43,900043,0,0,0,0,-1: 'VK_EXECUTE
  272.     DATA 44,900044,0,0,0,0,-1: 'VK_SNAPSHOT
  273.     DATA 45,20992,0,0,0,0,0.2: 'INS
  274.     DATA 46,21248,0,0,0,0,0.2: 'DEL
  275.     DATA 47,900047,0,0,0,0,0.2: 'VK_HELP
  276.     DATA 48,48,0,41,0,0,0.2: '0
  277.     DATA 49,49,0,33,0,0,0.2: '1
  278.     DATA 50,50,0,64,0,0,0.2: '2
  279.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  280.     DATA 51,51,0,35,0,0,0.2: '3
  281.     DATA 52,52,0,36,0,0,0.2: '4
  282.     DATA 53,53,0,37,0,0,0.2: '5
  283.     DATA 54,54,0,94,0,0,0.2: '6
  284.     DATA 55,55,0,38,0,0,0.2: '7
  285.     DATA 56,56,0,42,0,0,0.2: '8
  286.     DATA 57,57,0,40,0,0,0.2: '9
  287.     DATA 58,900058,0,0,0,0,0.2: 'Undefined
  288.     DATA 59,900059,0,0,0,0,0.2: 'Undefined
  289.     DATA 60,900060,0,0,0,0,0.2: 'Undefined
  290.     DATA 61,900061,0,0,0,0,0.2: 'Undefined
  291.     DATA 62,900062,0,0,0,0,0.2: 'Undefined
  292.     DATA 63,900063,0,0,0,0,0.2: 'Undefined
  293.     DATA 64,900064,0,0,0,0,0.2: 'Undefined
  294.     DATA 65,65,0,97,0,0,0.2: 'a
  295.     DATA 66,66,0,98,0,0,0.2: 'b
  296.     DATA 67,67,0,99,0,0,0.2: 'c
  297.     DATA 68,68,0,100,0,0,0.2: 'd
  298.     DATA 69,69,0,101,0,0,0.2: 'e
  299.     DATA 70,70,0,102,0,0,0.2: 'f
  300.     DATA 71,71,0,103,0,0,0.2: 'g
  301.     DATA 72,72,0,104,0,0,0.2: 'h
  302.     DATA 73,73,0,105,0,0,0.2: 'i
  303.     DATA 74,74,0,106,0,0,0.2: 'j
  304.     DATA 75,75,0,107,0,0,0.2: 'k
  305.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  306.     DATA 76,76,0,108,0,0,0.2: 'l
  307.     DATA 77,77,0,109,0,0,0.2: 'm
  308.     DATA 78,78,0,110,0,0,0.2: 'n
  309.     DATA 79,79,0,111,0,0,0.2: 'o
  310.     DATA 80,80,0,112,0,0,0.2: 'p
  311.     DATA 81,81,0,113,0,0,0.2: 'q
  312.     DATA 82,82,0,114,0,0,0.2: 'r
  313.     DATA 83,83,0,115,0,0,0.2: 's
  314.     DATA 84,84,0,116,0,0,0.2: 't
  315.     DATA 85,85,0,117,0,0,0.2: 'u
  316.     DATA 86,86,0,118,0,0,0.2: 'v
  317.     DATA 87,87,0,119,0,0,0.2: 'w
  318.     DATA 88,88,0,120,0,0,0.2: 'x
  319.     DATA 89,89,0,121,0,0,0.2: 'y
  320.     DATA 90,90,0,122,0,0,0.2: 'z
  321.     DATA 91,100311,0,0,0,0,-1: 'Left WIN
  322.     DATA 92,100312,0,0,0,0,-1: 'Right WIN
  323.     DATA 93,100319,0,0,0,0,-1: 'Applications (Menu)
  324.     DATA 94,900094,0,0,0,0,0.2: 'Reserved
  325.     DATA 95,900095,0,0,0,0,0.2: 'VK_SLEEP
  326.     DATA 96,48,0,0,0,0,0.2: 'Numpad 0
  327.     DATA 97,49,0,0,0,0,0.2: 'Numpad 1
  328.     DATA 98,50,0,0,0,0,0.2: 'Numpad 2
  329.     DATA 99,51,0,0,0,0,0.2: 'Numpad 3
  330.     DATA 100,52,0,0,0,0,0.2: 'Numpad 4
  331.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  332.     DATA 101,53,0,0,0,0,0.2: 'Numpad 5
  333.     DATA 102,54,0,0,0,0,0.2: 'Numpad 6
  334.     DATA 103,55,0,0,0,0,0.2: 'Numpad 7
  335.     DATA 104,56,0,0,0,0,0.2: 'Numpad 8
  336.     DATA 105,57,0,0,0,0,0.2: 'Numpad 9
  337.     DATA 106,42,0,0,0,0,0.2: 'Numpad *
  338.     DATA 107,43,0,0,0,0,0.2: 'Numpad +
  339.     DATA 108,900108,0,0,0,0,0.2: 'VK_SEPARATOR
  340.     DATA 109,51,0,0,0,0,0.2: 'Numpad -
  341.     DATA 110,52,0,0,0,0,0.2: 'Numpad .
  342.     DATA 111,53,0,0,0,0,0.2: 'Numpad /
  343.     DATA 112,15104,0,0,0,0,0.2: 'F1
  344.     DATA 113,15360,0,0,0,0,0.2: 'F2
  345.     DATA 114,15616,0,0,0,0,0.2: 'F3
  346.     DATA 115,15872,0,0,0,0,0.2: 'F4
  347.     DATA 116,16128,0,0,0,0,0.2: 'F5
  348.     DATA 117,16384,0,0,0,0,0.2: 'F6
  349.     DATA 118,16640,0,0,0,0,0.2: 'F7
  350.     DATA 119,16896,0,0,0,0,0.2: 'F8
  351.     DATA 120,17152,0,0,0,0,0.2: 'F9
  352.     DATA 121,17408,0,0,0,0,0.2: 'F10
  353.     DATA 122,34048,0,0,0,0,0.2: 'F11
  354.     DATA 123,34304,0,0,0,0,0.2: 'F12
  355.     DATA 124,900124,0,0,0,0,0.2: 'F13
  356.     DATA 125,900125,0,0,0,0,0.2: 'F14
  357.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  358.     DATA 126,900126,0,0,0,0,0.2: 'F15
  359.     DATA 127,900127,0,0,0,0,0.2: 'F16
  360.     DATA 128,900128,0,0,0,0,0.2: 'F17
  361.     DATA 129,900129,0,0,0,0,0.2: 'F18
  362.     DATA 130,900130,0,0,0,0,0.2: 'F19
  363.     DATA 131,900131,0,0,0,0,0.2: 'F20
  364.     DATA 132,900132,0,0,0,0,0.2: 'F21
  365.     DATA 133,900133,0,0,0,0,0.2: 'F22
  366.     DATA 134,900134,0,0,0,0,0.2: 'F23
  367.     DATA 135,900135,0,0,0,0,0.2: 'F24
  368.     DATA 136,900136,0,0,0,0,0.2: 'Unassigned
  369.     DATA 137,900137,0,0,0,0,0.2: 'Unassigned
  370.     DATA 138,900138,0,0,0,0,0.2: 'Unassigned
  371.     DATA 139,900139,0,0,0,0,0.2: 'Unassigned
  372.     DATA 140,900140,0,0,0,0,0.2: 'Unassigned
  373.     DATA 141,900141,0,0,0,0,0.2: 'Unassigned
  374.     DATA 142,900142,0,0,0,0,0.2: 'Unassigned
  375.     DATA 143,900143,0,0,0,0,0.2: 'Unassigned
  376.     DATA 144,100300,0,0,0,0,-1: 'NUM LOCK
  377.     DATA 145,100302,0,0,0,0,-1: 'SCROLL LOCK
  378.     DATA 146,900146,0,0,0,0,0.2: 'OEM SPECIFIC
  379.     DATA 147,900147,0,0,0,0,0.2: 'OEM SPECIFIC
  380.     DATA 148,900148,0,0,0,0,0.2: 'OEM SPECIFIC
  381.     DATA 149,900149,0,0,0,0,0.2: 'OEM SPECIFIC
  382.     DATA 150,900150,0,0,0,0,0.2: 'OEM SPECIFIC
  383.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  384.     DATA 151,900151,0,0,0,0,0.2: 'Unassigned
  385.     DATA 152,900152,0,0,0,0,0.2: 'Unassigned
  386.     DATA 153,900153,0,0,0,0,0.2: 'Unassigned
  387.     DATA 154,900154,0,0,0,0,0.2: 'Unassigned
  388.     DATA 155,900155,0,0,0,0,0.2: 'Unassigned
  389.     DATA 156,900156,0,0,0,0,0.2: 'Unassigned
  390.     DATA 157,900157,0,0,0,0,0.2: 'Unassigned
  391.     DATA 158,900158,0,0,0,0,0.2: 'Unassigned
  392.     DATA 159,900159,0,0,0,0,0.2: 'Unassigned
  393.     DATA 160,100304,0,0,0,0,-1: 'Left Shift
  394.     DATA 161,100303,0,0,0,0,-1: 'Right Shift
  395.     DATA 162,100306,0,0,0,0,-1: 'Left Control
  396.     DATA 163,100305,0,0,0,0,-1: 'Right Control
  397.     DATA 164,100308,0,0,0,0,-1: 'Left Alt
  398.     DATA 165,100309,0,0,0,0,-1: 'Right Alt
  399.     DATA 166,900166,0,0,0,0,0.2: 'Browser back
  400.     DATA 167,900167,0,0,0,0,0.2: 'Browser forward
  401.     DATA 168,900168,0,0,0,0,0.2: 'Browser refresh
  402.     DATA 169,900169,0,0,0,0,0.2: 'Browser stop
  403.     DATA 170,900170,0,0,0,0,0.2: 'Browser search
  404.     DATA 171,900171,0,0,0,0,0.2: 'Browser favorites
  405.     DATA 172,900172,0,0,0,0,0.2: 'Browser home
  406.     DATA 173,900173,0,0,0,0,0.2: 'Mute
  407.     DATA 174,900174,0,0,0,0,0.2: 'Vol Down
  408.     DATA 175,900175,0,0,0,0,0.2: 'Vol Up
  409.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  410.     DATA 176,900176,0,0,0,0,0.2: 'Media Next
  411.     DATA 177,900177,0,0,0,0,0.2: 'Media prev
  412.     DATA 178,900178,0,0,0,0,0.2: 'Media stop
  413.     DATA 179,900179,0,0,0,0,0.2: 'Media Play/Pause
  414.     DATA 180,900180,0,0,0,0,0.2: 'Launch mail
  415.     DATA 181,900181,0,0,0,0,0.2: 'Launch media select
  416.     DATA 182,900182,0,0,0,0,0.2: 'Launch app1
  417.     DATA 183,900183,0,0,0,0,0.2: 'Launch app2
  418.     DATA 184,900184,0,0,0,0,0.2: 'Reserved
  419.     DATA 185,900185,0,0,0,0,0.2: 'Reserved
  420.     DATA 186,59,0,58,0,0,0.2: ';:
  421.     DATA 187,61,0,43,0,0,0.2: '=+
  422.     DATA 188,44,0,60,0,0,0.2: ',<
  423.     DATA 189,45,0,95,0,0,0.2: '-_
  424.     DATA 190,46,0,62,0,0,0.2: '.>
  425.     DATA 191,47,0,63,0,0,0.2: '/?
  426.     DATA 192,96,0,126,0,0,0.2: '`~
  427.     DATA 193,900193,0,0,0,0,0.2: 'Reserved
  428.     DATA 194,900194,0,0,0,0,0.2: 'Reserved
  429.     DATA 195,900195,0,0,0,0,0.2: 'Reserved
  430.     DATA 196,900196,0,0,0,0,0.2: 'Reserved
  431.     DATA 197,900197,0,0,0,0,0.2: 'Reserved
  432.     DATA 198,900198,0,0,0,0,0.2: 'Reserved
  433.     DATA 199,900199,0,0,0,0,0.2: 'Reserved
  434.     DATA 200,900200,0,0,0,0,0.2: 'Reserved
  435.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  436.     DATA 201,900201,0,0,0,0,0.2: 'Reserved
  437.     DATA 202,900202,0,0,0,0,0.2: 'Reserved
  438.     DATA 203,900203,0,0,0,0,0.2: 'Reserved
  439.     DATA 204,900204,0,0,0,0,0.2: 'Reserved
  440.     DATA 205,900205,0,0,0,0,0.2: 'Reserved
  441.     DATA 206,900206,0,0,0,0,0.2: 'Reserved
  442.     DATA 207,900207,0,0,0,0,0.2: 'Reserved
  443.     DATA 208,900208,0,0,0,0,0.2: 'Reserved
  444.     DATA 209,900209,0,0,0,0,0.2: 'Reserved
  445.     DATA 210,900210,0,0,0,0,0.2: 'Reserved
  446.     DATA 211,900211,0,0,0,0,0.2: 'Reserved
  447.     DATA 212,900212,0,0,0,0,0.2: 'Reserved
  448.     DATA 213,900213,0,0,0,0,0.2: 'Reserved
  449.     DATA 214,900214,0,0,0,0,0.2: 'Reserved
  450.     DATA 215,900215,0,0,0,0,0.2: 'Reserved
  451.     DATA 216,900216,0,0,0,0,0.2: 'Unassigned
  452.     DATA 217,900217,0,0,0,0,0.2: 'Unassigned
  453.     DATA 218,900218,0,0,0,0,0.2: 'Unassigned
  454.     DATA 219,91,0,123,0,0,0.2: '[{
  455.     DATA 220,92,0,124,0,0,0.2: '\|
  456.     DATA 221,93,0,125,0,0,0.2: ']}
  457.     DATA 222,39,0,34,0,0,0.2: ''"
  458.     DATA 223,900223,0,0,0,0,0.2: 'OEM SPECIFIC
  459.     DATA 224,900224,0,0,0,0,0.2: 'Reserved
  460.     DATA 225,900225,0,0,0,0,0.2: 'OEM SPECIFIC d
  461.     DATA 226,900226,0,0,0,0,0.2: 'Either the Angle Bracket key,or Backslash on RT 102-key keyboard
  462.     DATA 227,900227,0,0,0,0,0.2: 'OEM SPECIFIC
  463.     DATA 228,900228,0,0,0,0,0.2: 'OEM SPECIFIC
  464.     DATA 229,900229,0,0,0,0,0.2: 'IME PROCESS key (whatever that is)
  465.     DATA 230,900230,0,0,0,0,0.2: 'OEM SPECIFIC
  466.     DATA 231,900231,0,0,0,0,0.2: 'Used to pass UNICODE characters (however that works)
  467.     DATA 232,900232,0,0,0,0,0.2: 'Unassigned
  468.     DATA 233,900233,0,0,0,0,0.2: 'OEM SPECIFIC
  469.     DATA 234,900234,0,0,0,0,0.2: 'OEM SPECIFIC
  470.     DATA 235,900235,0,0,0,0,0.2: 'OEM SPECIFIC
  471.     DATA 236,900236,0,0,0,0,0.2: 'OEM SPECIFIC
  472.     DATA 237,900237,0,0,0,0,0.2: 'OEM SPECIFIC
  473.     DATA 238,900238,0,0,0,0,0.2: 'OEM SPECIFIC
  474.     DATA 239,900239,0,0,0,0,0.2: 'OEM SPECIFIC
  475.     DATA 240,900240,0,0,0,0,0.2: 'OEM SPECIFIC
  476.     DATA 241,900241,0,0,0,0,0.2: 'OEM SPECIFIC
  477.     DATA 242,900242,0,0,0,0,0.2: 'OEM SPECIFIC
  478.     DATA 243,900243,0,0,0,0,0.2: 'OEM SPECIFIC
  479.     DATA 244,900244,0,0,0,0,0.2: 'OEM SPECIFIC
  480.     DATA 245,900245,0,0,0,0,0.2: 'OEM SPECIFIC
  481.     DATA 246,900246,0,0,0,0,0.2: 'VK_ATTN
  482.     DATA 247,900247,0,0,0,0,0.2: 'VK_ATTN
  483.     DATA 248,900248,0,0,0,0,0.2: 'VK_ATTN
  484.     DATA 249,900249,0,0,0,0,0.2: 'VK_ATTN
  485.     DATA 250,900250,0,0,0,0,0.2: 'VK_ATTN
  486.     DATA 251,900251,0,0,0,0,0.2: 'VK_ATTN
  487.     DATA 252,900252,0,0,0,0,0.2: 'Reserved
  488.     DATA 253,900253,0,0,0,0,0.2: 'VK_PA1
  489.     DATA 254,900253,0,0,0,0,0.2: 'VK_OEM_CLEAR
  490.     DATA 0,0,0,0,0,0,0.2: 'END OF DATA
  491.     AltGr(0) = 165
  492.     AltGr(1) = 0
  493.  
  494.  
  495. FUNCTION ExtendedTimer##
  496.     'modified extendedtimer to store the old day's count, and not have to recalculate it every time the routine is called.
  497.  
  498.     STATIC olds AS _FLOAT, old_day AS _FLOAT
  499.     DIM m AS INTEGER, d AS INTEGER, y AS INTEGER
  500.     DIM s AS _FLOAT, day AS STRING
  501.     IF olds = 0 THEN 'calculate the day the first time the extended timer runs
  502.         day = DATE$
  503.         m = VAL(LEFT$(day, 2))
  504.         d = VAL(MID$(day, 4, 2))
  505.         y = VAL(RIGHT$(day, 4)) - 1970
  506.         SELECT CASE m 'Add the number of days for each previous month passed
  507.             CASE 2: d = d + 31
  508.             CASE 3: d = d + 59
  509.             CASE 4: d = d + 90
  510.             CASE 5: d = d + 120
  511.             CASE 6: d = d + 151
  512.             CASE 7: d = d + 181
  513.             CASE 8: d = d + 212
  514.             CASE 9: d = d + 243
  515.             CASE 10: d = d + 273
  516.             CASE 11: d = d + 304
  517.             CASE 12: d = d + 334
  518.         END SELECT
  519.         IF (y MOD 4) = 2 AND m > 2 THEN d = d + 1 'add a day if this is leap year and we're past february
  520.         d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
  521.         d = d + (y + 2) \ 4 'add in days for leap years passed
  522.         s = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  523.         old_day = s
  524.     END IF
  525.     IF TIMER < oldt THEN 'we went from 23:59:59 (a second before midnight) to 0:0:0 (midnight)
  526.         old_day = s + 83400 'add another worth of seconds to our counter
  527.     END IF
  528.     oldt = TIMER
  529.     olds = old_day + oldt
  530.     ExtendedTimer## = olds
  531.  

We now have the option to remap (or add) extended keycodes to our input handler, if we want to, using:

Code: [Select]
SUB Remap_Extended_KeyCode (Which&, AltShift&, AltCtrl&, AltAltGr&, CtrlShift&, CtrlAltGr&, ShiftAltGr&, CtrlAltShift&)
For testing purposes, I remapped A to give us a whole swarm of different various inputs which it can now relate to:
"A" -- when used alone and in combination with other keys -- returns us values which can be:  65, 1, 97, 160, 142, 143, 241, 242, 243, 244, 245, 246

I'm thinking that someone should be able to map just about any type of character to any sort of combo that they want now, without any problem at all!  Forget plain old key & shift-key combos!!  Now you can do key, shift-key, ctrl-shift-key, alt-shift-key, altgr-key, shift-altgr-key, and several other possible combinations!

If you can detect the keys on your system, you should be able to map them to respond to whatever the heck you want them to for you.  :)

« Last Edit: December 31, 2019, 11:57:35 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline RhoSigma

  • QB64 Developer
  • Forum Resident
  • Posts: 565
    • View Profile
Re: Programmable Keyboard Input
« Reply #16 on: December 30, 2019, 02:04:36 pm »
Well, thank you once again for your efforts Steve,

I'd agree, that this might be overkill for most people. From that point of view it's perfect, that you chose to put the "extended" remapping into a extra SUB, so everybody who doesn't need it can just leave it alone. On the other hand we now have an excellent piece of code suitable for almost any thinkable key combo, and I hope many of our international members will take the time to add at least the regular mappings for their respective keyboards, so this library will evolve well in the future.

For me it will be possible now to exactly rebuilt the INKEY$ behavior as SDL had, and which worked so well for my projects.

Thank You
My Projects:   https://qb64forum.alephc.xyz/index.php?topic=809
GuiTools - A graphic UI framework (can do multiple UI forms/windows in one program)
Libraries - ImageProcess, StringBuffers (virt. files), MD5/SHA2-Hash, LZW etc.
Bonus - Blankers, QB64/Notepad++ setup pack

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Programmable Keyboard Input
« Reply #17 on: December 30, 2019, 02:42:21 pm »
Happy to do it, as this is something which I’ll be using in various projects as well, once we get it all shined up and polished.  ;)

For my latest project, I wanted to be able to allow users to set their own customizable hotkeys for various program actions, and that just isn’t possible with our current input routines.  _KEYHIT doesn’t work right, or play nice, with many of the CTRL keys.  (Heck, it doesn’t even register some of the unmodified keys, like the Menu/App key!)  INKEY$ has limited readability...

I’d always thought, “At least we have _DEVICES to fall back on...”

WRONG!!

It’s completely, utterly, 100% broken.  It doesn’t return the values from the _DEVICES page at all now — it’s more the INP(&H60) character set.  It doesn’t read ALT at all...  It’s just broke.

/sigh

Which, when all taken together, leads me to end up just writing my own programmable input handler, which we see here.  I hope others will find it useful, but in the end, it’s primary purpose is to be something which I can plug in and use for my own personal projects in the future. 

Got to admit though, I will be rather happy once others contribute some international remappings to things.  It’ll be nice to provide that functionality for others in the future, as well.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Programmable Keyboard Input
« Reply #18 on: December 31, 2019, 11:58:43 am »
Edited post above to fix a small glitch with some codes not printing thanks to an incorrect look-up being done with CAPSLOCK modifying things.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Marked as best answer by SMcNeill on December 31, 2019, 07:38:25 am

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Programmable Keyboard Input
« Reply #19 on: December 31, 2019, 12:36:03 pm »
Added a few helper functions to the programmable keyboard routines above. 

Code: QB64: [Select]
  1. DECLARE LIBRARY 'function is already used by QB64 so "User32" is not required
  2.     FUNCTION GetKeyState% (BYVAL vkey AS LONG)
  3.     FUNCTION GetAsyncKeyState% (BYVAL vkey AS LONG)
  4.  
  5. TYPE KeyboardInfo_Type
  6.     Index AS LONG
  7.     ASCII AS LONG
  8.     Ctrl AS LONG
  9.     Shift AS LONG
  10.     Alt AS LONG
  11.     AltGr AS LONG
  12.     Repeat AS _FLOAT
  13.     LastHit AS _FLOAT
  14.     Down AS LONG
  15.     AltShift AS LONG
  16.     AltCtrl AS LONG
  17.     AltAltGr AS LONG
  18.     CtrlShift AS LONG
  19.     CtrlAlt AS LONG
  20.     CtrlAltGr AS LONG
  21.     ShiftAltGr AS LONG
  22.     CtrlAltShift AS LONG
  23.  
  24. DIM SHARED Keys(254) AS KeyboardInfo_Type
  25. Init_KeyCodes
  26. Remap_KeyCode 65, 65, 1, 97, 160, 142, 0.2: 'A , , a,  , Ž
  27. 'Remap_Extended_KeyCode (Which&, AltShift&, AltCtrl&, AltAltGr&, CtrlShift&, CtrlAltGr&, ShiftAltGr&, CtrlAltShift&)
  28. Remap_Extended_KeyCode 65, 143, 241, 242, 243, 244, 245, 246 ' and more!
  29.  
  30.  
  31. PRINT "Test out the input handler.  Try lots of options! => ";
  32. junk$ = ExtendedInput
  33. PRINT junk$
  34. PRINT "Pretty nifty, right?"
  35. KeyClear 'Notice that without this, we'll read the keyup event from where ENTER is lifted up, as the first character value below.
  36.  
  37.     k = KeyHit
  38.     IF k <> 0 THEN
  39.         'LOCATE 1, 1
  40.         PRINT k;
  41.         IF ABS(k) < 256 THEN PRINT CHR$(34); CHR$(ABS(k)); CHR$(34) ELSE PRINT
  42.     END IF
  43.     'LOCATE 2, 1: PRINT KeyDown(1) 'Test to see if CHR$(1) is held down (which is generated via a CTRL-A combo)
  44.     _LIMIT 30
  45. LOOP UNTIL k = 27 'escape
  46.  
  47. SUB SetAltGr (Key1 AS INTEGER, Key2 AS INTEGER)
  48.     AltGr(0) = Key1 'any key from our index (0 says no key)
  49.     AltGr(1) = Key2 'PLUS any other key from our index (0 says no additional key)
  50.     'Using this, we can set AltGr to become several things.
  51.     'AltGr(0) = 165, AltGr(1) = 0 -- This would say we're using the RIGHT Alt key (alone) to simulate the AltGr key.  (Windows Onscreen Keyboard does this.)
  52.     'AltGr(0) = 17, AltGr(1) = 18 -- This would use any CTRL-ALT combo to simulate a AltGr keypress.
  53.     'Some useful values are listed for quick reference below
  54.     '0 = NoKey
  55.     '17 = ANY Ctrl
  56.     '18 = ANY Alt
  57.     '162 = Left Control
  58.     '163 = Right Control
  59.     '164 = Left Alt
  60.     '165 = Right Alt
  61.  
  62.     'Default is for AltGr(0) = 165, AltGr(1) = 0, which uses Right-Alt alone as the AltGr key.
  63.     'Feel free to customize the setting to your personal preference/need.
  64.  
  65. SUB KeyClear
  66.     _DELAY .05 'give time for a keyup event to log itself so we can clear it
  67.     DO: k = KeyHit: LOOP UNTIL k = 0
  68.  
  69. FUNCTION KeyHit&
  70.     STATIC ReturnCount AS INTEGER
  71.     STATIC ReturnValues(30) AS LONG
  72.     SHARED AltGr, Alt, Shift, Ctrl
  73.     IF Keys(1).Index = 0 THEN Init_KeyCodes 'if someone forgets to put the init routine in their code, be certain to initialize the codes before attempting to use them.
  74.  
  75.  
  76.     IF ReturnCount > 0 THEN 'If we generated a cue of values last pass, clear those up first, before getting new values.
  77.         'The only time we really see this is when we hit a shift, ctrl, alt key, usually.
  78.         KeyHit = ReturnValues(1)
  79.         FOR i = 1 TO ReturnCount - 1
  80.             ReturnValues(i) = ReturnValues(i + 1)
  81.         NEXT
  82.         ReturnCount = ReturnCount - 1
  83.         EXIT FUNCTION
  84.     END IF
  85.  
  86.     IF Keys(16).Down THEN Shift = -1 ELSE Shift = 0
  87.     IF Keys(17).Down THEN Ctrl = -1 ELSE Ctrl = 0
  88.     IF Keys(18).Down THEN Alt = -1 ELSE Alt = 0
  89.     IF AltGr(0) <> 0 AND AltGr(1) <> 0 THEN
  90.         IF Keys(AltGr(0)).Down AND Keys(AltGr(1)).Down THEN AltGr = -1 ELSE AltGr = 0
  91.     ELSEIF AltGr(1) <> 0 THEN
  92.         IF Keys(AltGr(1)).Down THEN AltGr = -1 ELSE AltGr = 0
  93.     ELSEIF AltGr(0) <> 0 THEN
  94.         IF Keys(AltGr(0)).Down THEN AltGr = -1 ELSE AltGr = 0
  95.     ELSE
  96.         AltGr = 0
  97.     END IF
  98.  
  99.     'until Ctrl or Alt status, if the key down was used to help generate AltGr as a modifier key
  100.     IF AltGr THEN
  101.         IF (AltGr(0) = 18 OR AltGr(1) = 18) THEN Alt = 0 'if we use both ALT keys to represent part of AltGr, when AltGr is active, Alt isn't.
  102.         IF (AltGr(0) = 164 OR AltGr(1) = 164) AND Keys(165).Down = 0 THEN Alt = 0 'if we use Left ALT keys to represent part of AltGr, when AltGr is active, Left Alt isn't.
  103.         IF (AltGr(0) = 165 OR AltGr(1) = 165) AND Keys(164).Down = 0 THEN Alt = 0 'if we use Right ALT keys to represent part of AltGr, when AltGr is active, Right Alt isn't.
  104.         IF (AltGr(0) = 17 OR AltGr(1) = 17) THEN Ctrl = 0 'if we use both CTRL keys to represent part of AltGr, when AltGr is active, Ctrl isn't.
  105.         IF (AltGr(0) = 162 OR AltGr(1) = 162) AND Keys(163).Down = 0 THEN Ctrl = 0 'if we use Left CTRL keys to represent part of AltGr, when AltGr is active, Left Ctrl isn't.
  106.         IF (AltGr(0) = 163 OR AltGr(1) = 163) AND Keys(162).Down = 0 THEN Ctrl = 0 'if we use Right CTRL keys to represent part of AltGr, when AltGr is active, Right Ctrl isn't.
  107.     END IF
  108.  
  109.     IF Alt AND Shift THEN AltShift = -1 ELSE AltShift = 0
  110.     IF Alt AND Ctrl THEN AltCtrl = -1 ELSE AltCtrl = 0
  111.     IF Alt AND AltAltGR THEN AltAltGR = -1 ELSE AltAltGR = 0
  112.     IF Ctrl AND Shift THEN CtrlShift = -1 ELSE CtrlShift = 0
  113.     IF Shift AND AltGr THEN ShiftAltGr = -1 ELSE ShiftAltGr = 0
  114.     IF Ctrl AND Alt AND Shift THEN CtrlAltShift = -1 ELSE CtrlAltShift = 0
  115.  
  116.         FOR i = 1 TO 254
  117.  
  118.             r = GetKeyState(Keys(i).Index) AND &H8000
  119.  
  120.             IF r THEN 'the key is down
  121.  
  122.  
  123.                 IF Keys(i).LastHit THEN
  124.                     IF ExtendedTimer > Keys(i).LastHit THEN
  125.                         ReturnCount = ReturnCount + 1 'add one to the return buffer
  126.                         ReturnValues(ReturnCount) = Keys(i).Down 'and put the existing value back in the buffer, as a key repeat
  127.                     END IF
  128.                 ELSE
  129.  
  130.                     IF Keys(i).Down = 0 THEN 'the key was up on the last pass.
  131.                         IF CtrlAltShift <> 0 AND Keys(i).CtrlAltShift <> 0 THEN 'return the CtrlAltShift value
  132.                             Keys(i).Down = Keys(i).CtrlAltShift
  133.                         ELSEIF AltAltGR <> 0 AND Keys(i).AltAltGr <> 0 THEN 'return the AltAltGr value
  134.                             Keys(i).Down = Keys(i).AltAltGr
  135.                         ELSEIF CtrlAltGr& <> 0 AND Keys(i).CtrlAltGr& <> 0 THEN 'return the CtrlAltGr& value
  136.                             Keys(i).Down = Keys(i).CtrlAltGr&
  137.                         ELSEIF ShiftAltGr <> 0 AND Keys(i).ShiftAltGr <> 0 THEN 'return the ShiftAltGr value
  138.                             Keys(i).Down = Keys(i).ShiftAltGr
  139.                         ELSEIF CtrlShift <> 0 AND Keys(i).CtrlShift <> 0 THEN 'return the CtrlShift value
  140.                             Keys(i).Down = Keys(i).CtrlShift
  141.                         ELSEIF AltCtrl <> 0 AND Keys(i).AltCtrl <> 0 THEN 'return the AltCtrl value
  142.                             Keys(i).Down = Keys(i).AltCtrl
  143.                         ELSEIF AltShift <> 0 AND Keys(i).AltShift <> 0 THEN 'return the AltShift value
  144.                             Keys(i).Down = Keys(i).AltShift
  145.                         ELSEIF AltGr <> 0 AND Keys(i).AltGr <> 0 THEN 'return the altgr value
  146.                             Keys(i).Down = Keys(i).AltGr
  147.                         ELSEIF Shift <> 0 AND Keys(i).Shift <> 0 THEN 'return the shift value
  148.                             Keys(i).Down = Keys(i).Shift
  149.                             IF _CAPSLOCK = 0 THEN 'caps lock basically reverses the behavior of the shift key with the letters A-Z and a-z
  150.                                 SELECT CASE i
  151.                                     CASE 65 TO 90: Keys(i).Down = Keys(i).ASCII
  152.                                 END SELECT
  153.                             END IF
  154.                         ELSEIF (Ctrl <> 0) AND (Keys(i).Ctrl <> 0) THEN 'return the ctrl value
  155.                             Keys(i).Down = Keys(i).Ctrl
  156.                         ELSEIF Alt <> 0 AND Keys(i).Alt <> 0 THEN 'return the alt value
  157.                             Keys(i).Down = Keys(i).Alt
  158.                         ELSE 'all that's left is to return the ASCII value
  159.                             Keys(i).Down = Keys(i).ASCII
  160.                             IF _CAPSLOCK = 0 THEN 'caps lock basically reverses the behavior of the shift key with the letters A-Z and a-z
  161.                                 SELECT CASE i
  162.                                     CASE 65 TO 90: Keys(i).Down = Keys(i).Shift
  163.                                 END SELECT
  164.                             END IF
  165.                         END IF
  166.                         ReturnCount = ReturnCount + 1 'add one to the return buffer
  167.                         ReturnValues(ReturnCount) = Keys(i).Down 'and store the value in the buffer
  168.  
  169.                         IF Keys(i).Repeat = -1 THEN 'keys that are set to a -1 on repeat simply toggle state as on, or off.
  170.                             Keys(i).LastHit = 1E+1000 'such as SHIFT, CTRL, ALT...
  171.                         ELSE
  172.                             Keys(i).LastHit = ExtendedTimer + Keys(i).Repeat 'and record when we hit it for repeat purposes
  173.                         END IF
  174.                     END IF
  175.                 END IF
  176.             ELSE
  177.                 IF Keys(i).Down THEN 'the key was down on the last pass
  178.                     ReturnCount = ReturnCount + 1
  179.                     ReturnValues(ReturnCount) = -Keys(i).Down 'mark it as being up on this one
  180.                 END IF
  181.                 Keys(i).Down = 0 'and set it back down for future passes
  182.                 Keys(i).LastHit = 0 'once again, set it as being ready to be hit again
  183.             END IF
  184.         NEXT
  185.  
  186.         IF ReturnCount > 0 THEN 'If we generated a cue of values last pass, clear those up first, before getting new values.
  187.             'The only time we really see this is when we hit a shift, ctrl, alt key, usually.
  188.             KeyHit = ReturnValues(1)
  189.             FOR i = 1 TO ReturnCount - 1
  190.                 ReturnValues(i) = ReturnValues(i + 1)
  191.             NEXT
  192.             ReturnCount = ReturnCount - 1
  193.             EXIT FUNCTION
  194.         END IF
  195.  
  196.     END IF 'End of IF _WINDOWHASFOCUS
  197.  
  198.  
  199.  
  200.  
  201. SUB Remap_KeyCode (Which AS LONG, ASCII AS LONG, Ctrl AS LONG, Shift AS LONG, Alt AS LONG, AltGr AS LONG, Repeat AS _FLOAT)
  202.     DIM i AS LONG
  203.     i = Which
  204.     Keys(i).Index = i
  205.     Keys(i).ASCII = ASCII
  206.     Keys(i).Ctrl = Ctrl
  207.     Keys(i).Shift = Shift
  208.     Keys(i).Alt = Alt
  209.     Keys(i).AltGr = AltGr
  210.     Keys(i).Repeat = Repeat
  211.     Keys(i).LastHit = 0
  212.     Keys(i).Down = 0
  213.  
  214. SUB Remap_Extended_KeyCode (Which&, AltShift&, AltCtrl&, AltAltGr&, _
  215.          CtrlShift&, CtrlAltGr&, ShiftAltGr&, CtrlAltShift&)
  216.     Keys(Which&).AltShift = AltShift&
  217.     Keys(Which&).AltCtrl = AltCtrl&
  218.     Keys(Which&).AltAltGr = AltAltGr&
  219.     Keys(Which&).CtrlShift = CtrlShift&
  220.     Keys(Which&).CtrlAltGr = CtrlAltGr&
  221.     Keys(Which&).ShiftAltGr = ShiftAltGr&
  222.     Keys(Which&).CtrlAltShift = CtrlAltShift&
  223.  
  224. FUNCTION KeyDown& (Code AS LONG)
  225.     IF Code <= 0 THEN EXIT FUNCTION
  226.     FOR i = 1 TO 254
  227.         IF GetAsyncKeyState(i) THEN 'first check for actual physical keys down
  228.             IF Keys(i).ASCII = Code THEN KeyDown = -1: EXIT FUNCTION 'then check to see if the code matches anything we've mapped it to.
  229.             IF Keys(i).Shift = Code THEN KeyDown = -1: EXIT FUNCTION
  230.             IF Keys(i).Alt = Code THEN KeyDown = -1: EXIT FUNCTION
  231.             IF Keys(i).AltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  232.             IF Keys(i).AltShift = Code THEN KeyDown = -1: EXIT FUNCTION
  233.             IF Keys(i).AltCtrl = Code THEN KeyDown = -1: EXIT FUNCTION
  234.             IF Keys(i).AltAltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  235.             IF Keys(i).CtrlShift = Code THEN KeyDown = -1: EXIT FUNCTION
  236.             IF Keys(i).CtrlAltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  237.             IF Keys(i).ShiftAltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  238.             IF Keys(i).CtrlAltShift = Code THEN KeyDown = -1: EXIT FUNCTION
  239.         END IF
  240.     NEXT
  241.     KeyDown& = 0
  242.  
  243. SUB Init_KeyCodes
  244.     RESTORE default_keyboard_data
  245.     FOR i = 1 TO 254
  246.         READ Keys(i).Index, Keys(i).ASCII, Keys(i).Ctrl, Keys(i).Shift, Keys(i).Alt, Keys(i).AltGr, Keys(i).Repeat
  247.         Keys(i).LastHit = 0: Keys(i).Down = 0
  248.     NEXT
  249.  
  250.     default_keyboard_data:
  251.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  252.     DATA 1,900001,0,0,0,0,0.2: 'Left Mouse Button
  253.     DATA 2,900002,0,0,0,0,0.2: 'Right Mouse Button
  254.     DATA 3,900003,0,0,0,0,0.2: 'VK_Cancel
  255.     DATA 4,900004,0,0,0,0,0.2: 'Middle Mouse Button
  256.     DATA 5,900005,0,0,0,0,0.2: 'Mouse Button 4
  257.     DATA 6,900006,0,0,0,0,0.2: 'Mouse Button 5
  258.     DATA 7,900007,0,0,0,0,0.2: 'Undefined
  259.     DATA 8,8,0,0,0,0,0.2: 'Backspace
  260.     DATA 9,9,0,0,0,0,0.2: 'Tab
  261.     DATA 10,900010,0,0,0,0,0.2: 'Reserved
  262.     DATA 11,900011,0,0,0,0,0.2: 'Reserved
  263.     DATA 12,19456,0,0,0,0,0.2: 'Clear
  264.     DATA 13,13,0,0,0,0,0.2: 'Enter
  265.     DATA 14,900014,0,0,0,0,0.2: 'Undefined
  266.     DATA 15,900015,0,0,0,0,0.2: 'Undefined
  267.     DATA 16,100016,0,0,0,0,-1: 'Shift (Notice I set it to simple toddle and report UP/DOWN results for us)
  268.     DATA 17,100017,0,0,0,0,-1: 'Ctrl   (Same)
  269.     DATA 18,100018,0,0,0,0,-1: 'Alt     (Same)
  270.     DATA 19,100019,0,0,0,0,0.2: 'Pause
  271.     DATA 20,100301,0,0,0,0,-1: 'Caps Lock
  272.     DATA 21,900021,0,0,0,0,0.2: 'VK_Hangul
  273.     DATA 22,900022,0,0,0,0,0.2: 'Undefined
  274.     DATA 23,900023,0,0,0,0,0.2: 'VK_Junja
  275.     DATA 24,900024,0,0,0,0,0.2: 'VK_Final
  276.     DATA 25,900025,0,0,0,0,0.2: 'VK_Hanga//VK_Kanji
  277.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  278.     DATA 26,900026,0,0,0,0,0.2: 'Undefined
  279.     DATA 27,27,0,0,0,0,0.2: 'ESC
  280.     DATA 28,900028,0,0,0,0,0.2: 'VK_Convert
  281.     DATA 29,900029,0,0,0,0,0.2: 'VK_NonConvert
  282.     DATA 30,900030,0,0,0,0,0.2: 'VK_Accept
  283.     DATA 31,900031,0,0,0,0,0.2: 'VK_ModeChange
  284.     DATA 32,32,0,0,0,0,0.2: 'VK_Space
  285.     DATA 33,18688,0,0,0,0,0.2: 'Page Up
  286.     DATA 34,20736,0,0,0,0,0.2: 'Page Down
  287.     DATA 35,20224,0,0,0,0,0.2: 'End
  288.     DATA 36,18176,0,0,0,0,0.2: 'Home
  289.     DATA 37,19200,0,0,0,0,0.2: 'Left Arrow
  290.     DATA 38,18432,0,0,0,0,0.2: 'Up Arrow
  291.     DATA 39,19712,0,0,0,0,0.2: 'Right Arrow
  292.     DATA 40,20480,0,0,0,0,0.2: 'Down Arrow
  293.     DATA 41,900041,0,0,0,0,-1: 'VK_SELECT
  294.     DATA 42,900042,0,0,0,0,-1: 'CK_PRINT
  295.     DATA 43,900043,0,0,0,0,-1: 'VK_EXECUTE
  296.     DATA 44,900044,0,0,0,0,-1: 'VK_SNAPSHOT
  297.     DATA 45,20992,0,0,0,0,0.2: 'INS
  298.     DATA 46,21248,0,0,0,0,0.2: 'DEL
  299.     DATA 47,900047,0,0,0,0,0.2: 'VK_HELP
  300.     DATA 48,48,0,41,0,0,0.2: '0
  301.     DATA 49,49,0,33,0,0,0.2: '1
  302.     DATA 50,50,0,64,0,0,0.2: '2
  303.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  304.     DATA 51,51,0,35,0,0,0.2: '3
  305.     DATA 52,52,0,36,0,0,0.2: '4
  306.     DATA 53,53,0,37,0,0,0.2: '5
  307.     DATA 54,54,0,94,0,0,0.2: '6
  308.     DATA 55,55,0,38,0,0,0.2: '7
  309.     DATA 56,56,0,42,0,0,0.2: '8
  310.     DATA 57,57,0,40,0,0,0.2: '9
  311.     DATA 58,900058,0,0,0,0,0.2: 'Undefined
  312.     DATA 59,900059,0,0,0,0,0.2: 'Undefined
  313.     DATA 60,900060,0,0,0,0,0.2: 'Undefined
  314.     DATA 61,900061,0,0,0,0,0.2: 'Undefined
  315.     DATA 62,900062,0,0,0,0,0.2: 'Undefined
  316.     DATA 63,900063,0,0,0,0,0.2: 'Undefined
  317.     DATA 64,900064,0,0,0,0,0.2: 'Undefined
  318.     DATA 65,65,0,97,0,0,0.2: 'a
  319.     DATA 66,66,0,98,0,0,0.2: 'b
  320.     DATA 67,67,0,99,0,0,0.2: 'c
  321.     DATA 68,68,0,100,0,0,0.2: 'd
  322.     DATA 69,69,0,101,0,0,0.2: 'e
  323.     DATA 70,70,0,102,0,0,0.2: 'f
  324.     DATA 71,71,0,103,0,0,0.2: 'g
  325.     DATA 72,72,0,104,0,0,0.2: 'h
  326.     DATA 73,73,0,105,0,0,0.2: 'i
  327.     DATA 74,74,0,106,0,0,0.2: 'j
  328.     DATA 75,75,0,107,0,0,0.2: 'k
  329.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  330.     DATA 76,76,0,108,0,0,0.2: 'l
  331.     DATA 77,77,0,109,0,0,0.2: 'm
  332.     DATA 78,78,0,110,0,0,0.2: 'n
  333.     DATA 79,79,0,111,0,0,0.2: 'o
  334.     DATA 80,80,0,112,0,0,0.2: 'p
  335.     DATA 81,81,0,113,0,0,0.2: 'q
  336.     DATA 82,82,0,114,0,0,0.2: 'r
  337.     DATA 83,83,0,115,0,0,0.2: 's
  338.     DATA 84,84,0,116,0,0,0.2: 't
  339.     DATA 85,85,0,117,0,0,0.2: 'u
  340.     DATA 86,86,0,118,0,0,0.2: 'v
  341.     DATA 87,87,0,119,0,0,0.2: 'w
  342.     DATA 88,88,0,120,0,0,0.2: 'x
  343.     DATA 89,89,0,121,0,0,0.2: 'y
  344.     DATA 90,90,0,122,0,0,0.2: 'z
  345.     DATA 91,100311,0,0,0,0,-1: 'Left WIN
  346.     DATA 92,100312,0,0,0,0,-1: 'Right WIN
  347.     DATA 93,100319,0,0,0,0,-1: 'Applications (Menu)
  348.     DATA 94,900094,0,0,0,0,0.2: 'Reserved
  349.     DATA 95,900095,0,0,0,0,0.2: 'VK_SLEEP
  350.     DATA 96,48,0,0,0,0,0.2: 'Numpad 0
  351.     DATA 97,49,0,0,0,0,0.2: 'Numpad 1
  352.     DATA 98,50,0,0,0,0,0.2: 'Numpad 2
  353.     DATA 99,51,0,0,0,0,0.2: 'Numpad 3
  354.     DATA 100,52,0,0,0,0,0.2: 'Numpad 4
  355.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  356.     DATA 101,53,0,0,0,0,0.2: 'Numpad 5
  357.     DATA 102,54,0,0,0,0,0.2: 'Numpad 6
  358.     DATA 103,55,0,0,0,0,0.2: 'Numpad 7
  359.     DATA 104,56,0,0,0,0,0.2: 'Numpad 8
  360.     DATA 105,57,0,0,0,0,0.2: 'Numpad 9
  361.     DATA 106,42,0,0,0,0,0.2: 'Numpad *
  362.     DATA 107,43,0,0,0,0,0.2: 'Numpad +
  363.     DATA 108,900108,0,0,0,0,0.2: 'VK_SEPARATOR
  364.     DATA 109,51,0,0,0,0,0.2: 'Numpad -
  365.     DATA 110,52,0,0,0,0,0.2: 'Numpad .
  366.     DATA 111,53,0,0,0,0,0.2: 'Numpad /
  367.     DATA 112,15104,0,0,0,0,0.2: 'F1
  368.     DATA 113,15360,0,0,0,0,0.2: 'F2
  369.     DATA 114,15616,0,0,0,0,0.2: 'F3
  370.     DATA 115,15872,0,0,0,0,0.2: 'F4
  371.     DATA 116,16128,0,0,0,0,0.2: 'F5
  372.     DATA 117,16384,0,0,0,0,0.2: 'F6
  373.     DATA 118,16640,0,0,0,0,0.2: 'F7
  374.     DATA 119,16896,0,0,0,0,0.2: 'F8
  375.     DATA 120,17152,0,0,0,0,0.2: 'F9
  376.     DATA 121,17408,0,0,0,0,0.2: 'F10
  377.     DATA 122,34048,0,0,0,0,0.2: 'F11
  378.     DATA 123,34304,0,0,0,0,0.2: 'F12
  379.     DATA 124,900124,0,0,0,0,0.2: 'F13
  380.     DATA 125,900125,0,0,0,0,0.2: 'F14
  381.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  382.     DATA 126,900126,0,0,0,0,0.2: 'F15
  383.     DATA 127,900127,0,0,0,0,0.2: 'F16
  384.     DATA 128,900128,0,0,0,0,0.2: 'F17
  385.     DATA 129,900129,0,0,0,0,0.2: 'F18
  386.     DATA 130,900130,0,0,0,0,0.2: 'F19
  387.     DATA 131,900131,0,0,0,0,0.2: 'F20
  388.     DATA 132,900132,0,0,0,0,0.2: 'F21
  389.     DATA 133,900133,0,0,0,0,0.2: 'F22
  390.     DATA 134,900134,0,0,0,0,0.2: 'F23
  391.     DATA 135,900135,0,0,0,0,0.2: 'F24
  392.     DATA 136,900136,0,0,0,0,0.2: 'Unassigned
  393.     DATA 137,900137,0,0,0,0,0.2: 'Unassigned
  394.     DATA 138,900138,0,0,0,0,0.2: 'Unassigned
  395.     DATA 139,900139,0,0,0,0,0.2: 'Unassigned
  396.     DATA 140,900140,0,0,0,0,0.2: 'Unassigned
  397.     DATA 141,900141,0,0,0,0,0.2: 'Unassigned
  398.     DATA 142,900142,0,0,0,0,0.2: 'Unassigned
  399.     DATA 143,900143,0,0,0,0,0.2: 'Unassigned
  400.     DATA 144,100300,0,0,0,0,-1: 'NUM LOCK
  401.     DATA 145,100302,0,0,0,0,-1: 'SCROLL LOCK
  402.     DATA 146,900146,0,0,0,0,0.2: 'OEM SPECIFIC
  403.     DATA 147,900147,0,0,0,0,0.2: 'OEM SPECIFIC
  404.     DATA 148,900148,0,0,0,0,0.2: 'OEM SPECIFIC
  405.     DATA 149,900149,0,0,0,0,0.2: 'OEM SPECIFIC
  406.     DATA 150,900150,0,0,0,0,0.2: 'OEM SPECIFIC
  407.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  408.     DATA 151,900151,0,0,0,0,0.2: 'Unassigned
  409.     DATA 152,900152,0,0,0,0,0.2: 'Unassigned
  410.     DATA 153,900153,0,0,0,0,0.2: 'Unassigned
  411.     DATA 154,900154,0,0,0,0,0.2: 'Unassigned
  412.     DATA 155,900155,0,0,0,0,0.2: 'Unassigned
  413.     DATA 156,900156,0,0,0,0,0.2: 'Unassigned
  414.     DATA 157,900157,0,0,0,0,0.2: 'Unassigned
  415.     DATA 158,900158,0,0,0,0,0.2: 'Unassigned
  416.     DATA 159,900159,0,0,0,0,0.2: 'Unassigned
  417.     DATA 160,100304,0,0,0,0,-1: 'Left Shift
  418.     DATA 161,100303,0,0,0,0,-1: 'Right Shift
  419.     DATA 162,100306,0,0,0,0,-1: 'Left Control
  420.     DATA 163,100305,0,0,0,0,-1: 'Right Control
  421.     DATA 164,100308,0,0,0,0,-1: 'Left Alt
  422.     DATA 165,100309,0,0,0,0,-1: 'Right Alt
  423.     DATA 166,900166,0,0,0,0,0.2: 'Browser back
  424.     DATA 167,900167,0,0,0,0,0.2: 'Browser forward
  425.     DATA 168,900168,0,0,0,0,0.2: 'Browser refresh
  426.     DATA 169,900169,0,0,0,0,0.2: 'Browser stop
  427.     DATA 170,900170,0,0,0,0,0.2: 'Browser search
  428.     DATA 171,900171,0,0,0,0,0.2: 'Browser favorites
  429.     DATA 172,900172,0,0,0,0,0.2: 'Browser home
  430.     DATA 173,900173,0,0,0,0,0.2: 'Mute
  431.     DATA 174,900174,0,0,0,0,0.2: 'Vol Down
  432.     DATA 175,900175,0,0,0,0,0.2: 'Vol Up
  433.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  434.     DATA 176,900176,0,0,0,0,0.2: 'Media Next
  435.     DATA 177,900177,0,0,0,0,0.2: 'Media prev
  436.     DATA 178,900178,0,0,0,0,0.2: 'Media stop
  437.     DATA 179,900179,0,0,0,0,0.2: 'Media Play/Pause
  438.     DATA 180,900180,0,0,0,0,0.2: 'Launch mail
  439.     DATA 181,900181,0,0,0,0,0.2: 'Launch media select
  440.     DATA 182,900182,0,0,0,0,0.2: 'Launch app1
  441.     DATA 183,900183,0,0,0,0,0.2: 'Launch app2
  442.     DATA 184,900184,0,0,0,0,0.2: 'Reserved
  443.     DATA 185,900185,0,0,0,0,0.2: 'Reserved
  444.     DATA 186,59,0,58,0,0,0.2: ';:
  445.     DATA 187,61,0,43,0,0,0.2: '=+
  446.     DATA 188,44,0,60,0,0,0.2: ',<
  447.     DATA 189,45,0,95,0,0,0.2: '-_
  448.     DATA 190,46,0,62,0,0,0.2: '.>
  449.     DATA 191,47,0,63,0,0,0.2: '/?
  450.     DATA 192,96,0,126,0,0,0.2: '`~
  451.     DATA 193,900193,0,0,0,0,0.2: 'Reserved
  452.     DATA 194,900194,0,0,0,0,0.2: 'Reserved
  453.     DATA 195,900195,0,0,0,0,0.2: 'Reserved
  454.     DATA 196,900196,0,0,0,0,0.2: 'Reserved
  455.     DATA 197,900197,0,0,0,0,0.2: 'Reserved
  456.     DATA 198,900198,0,0,0,0,0.2: 'Reserved
  457.     DATA 199,900199,0,0,0,0,0.2: 'Reserved
  458.     DATA 200,900200,0,0,0,0,0.2: 'Reserved
  459.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  460.     DATA 201,900201,0,0,0,0,0.2: 'Reserved
  461.     DATA 202,900202,0,0,0,0,0.2: 'Reserved
  462.     DATA 203,900203,0,0,0,0,0.2: 'Reserved
  463.     DATA 204,900204,0,0,0,0,0.2: 'Reserved
  464.     DATA 205,900205,0,0,0,0,0.2: 'Reserved
  465.     DATA 206,900206,0,0,0,0,0.2: 'Reserved
  466.     DATA 207,900207,0,0,0,0,0.2: 'Reserved
  467.     DATA 208,900208,0,0,0,0,0.2: 'Reserved
  468.     DATA 209,900209,0,0,0,0,0.2: 'Reserved
  469.     DATA 210,900210,0,0,0,0,0.2: 'Reserved
  470.     DATA 211,900211,0,0,0,0,0.2: 'Reserved
  471.     DATA 212,900212,0,0,0,0,0.2: 'Reserved
  472.     DATA 213,900213,0,0,0,0,0.2: 'Reserved
  473.     DATA 214,900214,0,0,0,0,0.2: 'Reserved
  474.     DATA 215,900215,0,0,0,0,0.2: 'Reserved
  475.     DATA 216,900216,0,0,0,0,0.2: 'Unassigned
  476.     DATA 217,900217,0,0,0,0,0.2: 'Unassigned
  477.     DATA 218,900218,0,0,0,0,0.2: 'Unassigned
  478.     DATA 219,91,0,123,0,0,0.2: '[{
  479.     DATA 220,92,0,124,0,0,0.2: '\|
  480.     DATA 221,93,0,125,0,0,0.2: ']}
  481.     DATA 222,39,0,34,0,0,0.2: ''"
  482.     DATA 223,900223,0,0,0,0,0.2: 'OEM SPECIFIC
  483.     DATA 224,900224,0,0,0,0,0.2: 'Reserved
  484.     DATA 225,900225,0,0,0,0,0.2: 'OEM SPECIFIC d
  485.     DATA 226,900226,0,0,0,0,0.2: 'Either the Angle Bracket key,or Backslash on RT 102-key keyboard
  486.     DATA 227,900227,0,0,0,0,0.2: 'OEM SPECIFIC
  487.     DATA 228,900228,0,0,0,0,0.2: 'OEM SPECIFIC
  488.     DATA 229,900229,0,0,0,0,0.2: 'IME PROCESS key (whatever that is)
  489.     DATA 230,900230,0,0,0,0,0.2: 'OEM SPECIFIC
  490.     DATA 231,900231,0,0,0,0,0.2: 'Used to pass UNICODE characters (however that works)
  491.     DATA 232,900232,0,0,0,0,0.2: 'Unassigned
  492.     DATA 233,900233,0,0,0,0,0.2: 'OEM SPECIFIC
  493.     DATA 234,900234,0,0,0,0,0.2: 'OEM SPECIFIC
  494.     DATA 235,900235,0,0,0,0,0.2: 'OEM SPECIFIC
  495.     DATA 236,900236,0,0,0,0,0.2: 'OEM SPECIFIC
  496.     DATA 237,900237,0,0,0,0,0.2: 'OEM SPECIFIC
  497.     DATA 238,900238,0,0,0,0,0.2: 'OEM SPECIFIC
  498.     DATA 239,900239,0,0,0,0,0.2: 'OEM SPECIFIC
  499.     DATA 240,900240,0,0,0,0,0.2: 'OEM SPECIFIC
  500.     DATA 241,900241,0,0,0,0,0.2: 'OEM SPECIFIC
  501.     DATA 242,900242,0,0,0,0,0.2: 'OEM SPECIFIC
  502.     DATA 243,900243,0,0,0,0,0.2: 'OEM SPECIFIC
  503.     DATA 244,900244,0,0,0,0,0.2: 'OEM SPECIFIC
  504.     DATA 245,900245,0,0,0,0,0.2: 'OEM SPECIFIC
  505.     DATA 246,900246,0,0,0,0,0.2: 'VK_ATTN
  506.     DATA 247,900247,0,0,0,0,0.2: 'VK_ATTN
  507.     DATA 248,900248,0,0,0,0,0.2: 'VK_ATTN
  508.     DATA 249,900249,0,0,0,0,0.2: 'VK_ATTN
  509.     DATA 250,900250,0,0,0,0,0.2: 'VK_ATTN
  510.     DATA 251,900251,0,0,0,0,0.2: 'VK_ATTN
  511.     DATA 252,900252,0,0,0,0,0.2: 'Reserved
  512.     DATA 253,900253,0,0,0,0,0.2: 'VK_PA1
  513.     DATA 254,900253,0,0,0,0,0.2: 'VK_OEM_CLEAR
  514.     DATA 0,0,0,0,0,0,0.2: 'END OF DATA
  515.     AltGr(0) = 165
  516.     AltGr(1) = 0
  517.  
  518.  
  519. FUNCTION ExtendedTimer##
  520.     'modified extendedtimer to store the old day's count, and not have to recalculate it every time the routine is called.
  521.  
  522.     STATIC olds AS _FLOAT, old_day AS _FLOAT
  523.     DIM m AS INTEGER, d AS INTEGER, y AS INTEGER
  524.     DIM s AS _FLOAT, day AS STRING
  525.     IF olds = 0 THEN 'calculate the day the first time the extended timer runs
  526.         day = DATE$
  527.         m = VAL(LEFT$(day, 2))
  528.         d = VAL(MID$(day, 4, 2))
  529.         y = VAL(RIGHT$(day, 4)) - 1970
  530.         SELECT CASE m 'Add the number of days for each previous month passed
  531.             CASE 2: d = d + 31
  532.             CASE 3: d = d + 59
  533.             CASE 4: d = d + 90
  534.             CASE 5: d = d + 120
  535.             CASE 6: d = d + 151
  536.             CASE 7: d = d + 181
  537.             CASE 8: d = d + 212
  538.             CASE 9: d = d + 243
  539.             CASE 10: d = d + 273
  540.             CASE 11: d = d + 304
  541.             CASE 12: d = d + 334
  542.         END SELECT
  543.         IF (y MOD 4) = 2 AND m > 2 THEN d = d + 1 'add a day if this is leap year and we're past february
  544.         d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
  545.         d = d + (y + 2) \ 4 'add in days for leap years passed
  546.         s = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  547.         old_day = s
  548.     END IF
  549.     IF TIMER < oldt THEN 'we went from 23:59:59 (a second before midnight) to 0:0:0 (midnight)
  550.         old_day = s + 83400 'add another worth of seconds to our counter
  551.     END IF
  552.     oldt = TIMER
  553.     olds = old_day + oldt
  554.     ExtendedTimer## = olds
  555.  
  556.  
  557.  
  558.  
  559.  
  560.  
  561. FUNCTION ExtendedInput$
  562.     SHARED AltGr, Alt, Shift, Ctrl
  563.     PCOPY 0, 1
  564.     A = _AUTODISPLAY: X = POS(0): Y = CSRLIN
  565.     CP = 0: OldCP = 0 'Cursor Position
  566.     _KEYCLEAR
  567.     DO
  568.         PCOPY 1, 0
  569.  
  570.         k = KeyHit
  571.         IF Alt THEN AltDown = -1 ELSE AltDown = 0
  572.         SELECT CASE k 'without alt, add any keypresses to our input
  573.             CASE 8
  574.                 oldin$ = in$
  575.                 IF CP > 0 THEN OldCP = CP: CP = CP - 1
  576.                 in$ = LEFT$(in$, CP) + MID$(in$, CP + 2) 'backspace to erase input
  577.             CASE 9
  578.                 oldin$ = in$
  579.                 in$ = LEFT$(in$, CP) + SPACE$(4) + MID$(in$, CP + 1) 'four spaces for any TAB entered
  580.                 OldCP = CP
  581.                 CP = CP + 4
  582.             CASE 48 TO 57 '0 to 9
  583.                 IF AltDown THEN
  584.                     AltWasDown = -1: alt$ = alt$ + CHR$(k)
  585.                 ELSE
  586.                     oldin$ = in$
  587.                     in$ = LEFT$(in$, CP) + CHR$(k) + MID$(in$, CP + 1) 'add input to our string
  588.                     OldCP = CP
  589.                     CP = CP + 1
  590.                 END IF
  591.             CASE 1 TO 255 'the rest of the ASCII characters
  592.                 IF Ctrl AND (k = 118 OR k = 86) THEN
  593.                     oldin$ = in$
  594.                     in$ = LEFT$(in$, CP) + _CLIPBOARD$ + MID$(in$, CP + 1) 'ctrl-v paste
  595.                     'CTRL-V leaves cursor in position before the paste, without moving it after.
  596.                     'Feel free to modify that behavior here, if you want it to move to after the paste.
  597.                 ELSEIF Ctrl AND (k = 122 OR k = 90) THEN
  598.                     SWAP in$, oldin$: SWAP OldCP, CP 'ctrl-z undo
  599.                 ELSE
  600.                     oldin$ = in$
  601.                     in$ = LEFT$(in$, CP) + CHR$(k) + MID$(in$, CP + 1) 'add input to our string
  602.                     OldCP = CP
  603.                     CP = CP + 1
  604.                 END IF
  605.             CASE 18176 'Home
  606.                 CP = 0
  607.             CASE 20224 'End
  608.                 CP = LEN(in$)
  609.             CASE 21248 'Delete
  610.                 oldin$ = in$
  611.                 in$ = LEFT$(in$, CP) + MID$(in$, CP + 2)
  612.             CASE 19200 'Left
  613.                 CP = CP - 1
  614.                 IF CP < 0 THEN CP = 0
  615.             CASE 19712 'Right
  616.                 CP = CP + 1
  617.                 IF CP > LEN(in$) THEN CP = LEN(in$)
  618.         END SELECT
  619.  
  620.         alt$ = RIGHT$(alt$, 3)
  621.         IF AltWasDown = -1 AND AltDown = 0 THEN
  622.             v = VAL(alt$)
  623.             IF v >= 0 AND v <= 255 THEN in$ = in$ + CHR$(v): CP = CP + 1
  624.             alt$ = "": AltWasDown = 0
  625.         END IF
  626.         blink = (blink + 1) MOD 30
  627.         LOCATE Y, X
  628.         PRINT LEFT$(in$, CP);
  629.         IF blink \ 15 THEN PRINT " "; ELSE PRINT "_";
  630.         PRINT MID$(in$, CP + 1)
  631.  
  632.         _DISPLAY
  633.         _LIMIT 30
  634.     LOOP UNTIL k = 13
  635.  
  636.     PCOPY 1, 0
  637.     LOCATE Y, X: PRINT in$
  638.     ExtendedInput$ = in$

I've now added in (and slightly modified the code so that it works with the new Keyhit) the ExtendedInput function from here: https://www.qb64.org/forum/index.php?topic=1954.0

Also added a KeyDown routine which can read our new extended/remapped keycodes and tell us if simply if they're up or down.

Also added a KeyClear routine which will wait for key-up presses to finish processing and then clear them so we don't read them with future calls to the Keyhit routine.

« Last Edit: December 31, 2019, 12:38:04 pm by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Programmable Keyboard Input
« Reply #20 on: January 03, 2020, 10:40:31 am »
Playing around with the programmable keyboard, I think I now have it configured so an user can just swap from American to German keyboards with a single simple command.

For those of you with foreign keyboards, and who normally use CP1250, give this a little test run, if you will:

Code: QB64: [Select]
  1. DECLARE LIBRARY 'function is already used by QB64 so "User32" is not required
  2.     FUNCTION GetKeyState% (BYVAL vkey AS LONG)
  3.     FUNCTION GetAsyncKeyState% (BYVAL vkey AS LONG)
  4.  
  5. TYPE KeyboardInfo_Type
  6.     Index AS LONG
  7.     ASCII AS LONG
  8.     Ctrl AS LONG
  9.     Shift AS LONG
  10.     Alt AS LONG
  11.     AltGr AS LONG
  12.     Repeat AS _FLOAT
  13.     LastHit AS _FLOAT
  14.     Down AS LONG
  15.     AltShift AS LONG
  16.     AltCtrl AS LONG
  17.     AltAltGr AS LONG
  18.     CtrlShift AS LONG
  19.     CtrlAlt AS LONG
  20.     CtrlAltGr AS LONG
  21.     ShiftAltGr AS LONG
  22.     CtrlAltShift AS LONG
  23.  
  24. DIM SHARED Keys(254) AS KeyboardInfo_Type
  25. Init_KeyCodes "DE"
  26. 'Remap_KeyCode 65, 65, 1, 97, 160, 142, 0.2: 'A , , a,  , Ž
  27. 'Remap_Extended_KeyCode (Which&, AltShift&, AltCtrl&, AltAltGr&, CtrlShift&, CtrlAltGr&, ShiftAltGr&, CtrlAltShift&)
  28. 'Remap_Extended_KeyCode 65, 143, 241, 242, 243, 244, 245, 246 ' and more!
  29.  
  30.  
  31. 'PRINT "Test out the input handler.  Try lots of options! => ";
  32. 'junk$ = ExtendedInput
  33. 'PRINT junk$
  34. 'PRINT
  35. 'PRINT "Pretty nifty, right?"
  36. 'KeyClear 'Notice that without this, we'll read the keyup event from where ENTER is lifted up, as the first character value below.
  37.  
  38.     k = KeyHit
  39.     IF k <> 0 THEN
  40.         'LOCATE 1, 1
  41.         SELECT CASE k
  42.             CASE 900001 'left mouse
  43.             CASE 1 TO 255
  44.                 PRINT k, CHR$(k)
  45.             CASE IS <= 0
  46.             CASE ELSE
  47.                 PRINT k
  48.         END SELECT
  49.  
  50.     END IF
  51.     'LOCATE 2, 1: PRINT KeyDown(1) 'Test to see if CHR$(1) is held down (which is generated via a CTRL-A combo)
  52.     _LIMIT 30
  53. LOOP UNTIL k = 27 'escape
  54.  
  55. SUB SetAltGr (Key1 AS INTEGER, Key2 AS INTEGER)
  56.     AltGr(0) = Key1 'any key from our index (0 says no key)
  57.     AltGr(1) = Key2 'PLUS any other key from our index (0 says no additional key)
  58.     'Using this, we can set AltGr to become several things.
  59.     'AltGr(0) = 165, AltGr(1) = 0 -- This would say we're using the RIGHT Alt key (alone) to simulate the AltGr key.  (Windows Onscreen Keyboard does this.)
  60.     'AltGr(0) = 17, AltGr(1) = 18 -- This would use any CTRL-ALT combo to simulate a AltGr keypress.
  61.     'Some useful values are listed for quick reference below
  62.     '0 = NoKey
  63.     '17 = ANY Ctrl
  64.     '18 = ANY Alt
  65.     '162 = Left Control
  66.     '163 = Right Control
  67.     '164 = Left Alt
  68.     '165 = Right Alt
  69.  
  70.     'Default is for AltGr(0) = 165, AltGr(1) = 0, which uses Right-Alt alone as the AltGr key.
  71.     'Feel free to customize the setting to your personal preference/need.
  72.  
  73. SUB KeyClear
  74.     _DELAY .05 'give time for a keyup event to log itself so we can clear it
  75.     DO: k = KeyHit: LOOP UNTIL k = 0
  76.  
  77. FUNCTION KeyHit&
  78.     STATIC ReturnCount AS INTEGER
  79.     STATIC ReturnValues(30) AS LONG
  80.     SHARED AltGr, Alt, Shift, Ctrl
  81.     IF Keys(1).Index = 0 THEN Init_KeyCodes "" 'if someone forgets to put the init routine in their code, be certain to initialize the codes before attempting to use them.
  82.  
  83.  
  84.     IF ReturnCount > 0 THEN 'If we generated a cue of values last pass, clear those up first, before getting new values.
  85.         'The only time we really see this is when we hit a shift, ctrl, alt key, usually.
  86.         KeyHit = ReturnValues(1)
  87.         FOR i = 1 TO ReturnCount - 1
  88.             ReturnValues(i) = ReturnValues(i + 1)
  89.         NEXT
  90.         ReturnCount = ReturnCount - 1
  91.         EXIT FUNCTION
  92.     END IF
  93.  
  94.     IF Keys(16).Down THEN Shift = -1 ELSE Shift = 0
  95.     IF Keys(17).Down THEN Ctrl = -1 ELSE Ctrl = 0
  96.     IF Keys(18).Down THEN Alt = -1 ELSE Alt = 0
  97.     IF AltGr(0) <> 0 AND AltGr(1) <> 0 THEN
  98.         IF Keys(AltGr(0)).Down AND Keys(AltGr(1)).Down THEN AltGr = -1 ELSE AltGr = 0
  99.     ELSEIF AltGr(1) <> 0 THEN
  100.         IF Keys(AltGr(1)).Down THEN AltGr = -1 ELSE AltGr = 0
  101.     ELSEIF AltGr(0) <> 0 THEN
  102.         IF Keys(AltGr(0)).Down THEN AltGr = -1 ELSE AltGr = 0
  103.     ELSE
  104.         AltGr = 0
  105.     END IF
  106.  
  107.     'until Ctrl or Alt status, if the key down was used to help generate AltGr as a modifier key
  108.     IF AltGr THEN
  109.         IF (AltGr(0) = 18 OR AltGr(1) = 18) THEN Alt = 0 'if we use both ALT keys to represent part of AltGr, when AltGr is active, Alt isn't.
  110.         IF (AltGr(0) = 164 OR AltGr(1) = 164) AND Keys(165).Down = 0 THEN Alt = 0 'if we use Left ALT keys to represent part of AltGr, when AltGr is active, Left Alt isn't.
  111.         IF (AltGr(0) = 165 OR AltGr(1) = 165) AND Keys(164).Down = 0 THEN Alt = 0 'if we use Right ALT keys to represent part of AltGr, when AltGr is active, Right Alt isn't.
  112.         IF (AltGr(0) = 17 OR AltGr(1) = 17) THEN Ctrl = 0 'if we use both CTRL keys to represent part of AltGr, when AltGr is active, Ctrl isn't.
  113.         IF (AltGr(0) = 162 OR AltGr(1) = 162) AND Keys(163).Down = 0 THEN Ctrl = 0 'if we use Left CTRL keys to represent part of AltGr, when AltGr is active, Left Ctrl isn't.
  114.         IF (AltGr(0) = 163 OR AltGr(1) = 163) AND Keys(162).Down = 0 THEN Ctrl = 0 'if we use Right CTRL keys to represent part of AltGr, when AltGr is active, Right Ctrl isn't.
  115.     END IF
  116.  
  117.     IF Alt AND Shift THEN AltShift = -1 ELSE AltShift = 0
  118.     IF Alt AND Ctrl THEN AltCtrl = -1 ELSE AltCtrl = 0
  119.     IF Alt AND AltAltGR THEN AltAltGR = -1 ELSE AltAltGR = 0
  120.     IF Ctrl AND Shift THEN CtrlShift = -1 ELSE CtrlShift = 0
  121.     IF Shift AND AltGr THEN ShiftAltGr = -1 ELSE ShiftAltGr = 0
  122.     IF Ctrl AND Alt AND Shift THEN CtrlAltShift = -1 ELSE CtrlAltShift = 0
  123.  
  124.         FOR i = 1 TO 254
  125.  
  126.             r = GetKeyState(Keys(i).Index) AND &H8000
  127.  
  128.             IF r THEN 'the key is down
  129.  
  130.  
  131.                 IF Keys(i).LastHit THEN
  132.                     IF ExtendedTimer > Keys(i).LastHit THEN
  133.                         ReturnCount = ReturnCount + 1 'add one to the return buffer
  134.                         ReturnValues(ReturnCount) = Keys(i).Down 'and put the existing value back in the buffer, as a key repeat
  135.                     END IF
  136.                 ELSE
  137.  
  138.                     IF Keys(i).Down = 0 THEN 'the key was up on the last pass.
  139.                         IF CtrlAltShift <> 0 AND Keys(i).CtrlAltShift <> 0 THEN 'return the CtrlAltShift value
  140.                             Keys(i).Down = Keys(i).CtrlAltShift
  141.                         ELSEIF AltAltGR <> 0 AND Keys(i).AltAltGr <> 0 THEN 'return the AltAltGr value
  142.                             Keys(i).Down = Keys(i).AltAltGr
  143.                         ELSEIF CtrlAltGr& <> 0 AND Keys(i).CtrlAltGr& <> 0 THEN 'return the CtrlAltGr& value
  144.                             Keys(i).Down = Keys(i).CtrlAltGr&
  145.                         ELSEIF ShiftAltGr <> 0 AND Keys(i).ShiftAltGr <> 0 THEN 'return the ShiftAltGr value
  146.                             Keys(i).Down = Keys(i).ShiftAltGr
  147.                         ELSEIF CtrlShift <> 0 AND Keys(i).CtrlShift <> 0 THEN 'return the CtrlShift value
  148.                             Keys(i).Down = Keys(i).CtrlShift
  149.                         ELSEIF AltCtrl <> 0 AND Keys(i).AltCtrl <> 0 THEN 'return the AltCtrl value
  150.                             Keys(i).Down = Keys(i).AltCtrl
  151.                         ELSEIF AltShift <> 0 AND Keys(i).AltShift <> 0 THEN 'return the AltShift value
  152.                             Keys(i).Down = Keys(i).AltShift
  153.                         ELSEIF AltGr <> 0 AND Keys(i).AltGr <> 0 THEN 'return the altgr value
  154.                             Keys(i).Down = Keys(i).AltGr
  155.                         ELSEIF Shift <> 0 AND Keys(i).Shift <> 0 THEN 'return the shift value
  156.                             Keys(i).Down = Keys(i).Shift
  157.                             IF _CAPSLOCK = 0 THEN 'caps lock basically reverses the behavior of the shift key with the letters A-Z and a-z
  158.                                 SELECT CASE i
  159.                                     CASE 65 TO 90: Keys(i).Down = Keys(i).ASCII
  160.                                 END SELECT
  161.                             END IF
  162.                         ELSEIF (Ctrl <> 0) AND (Keys(i).Ctrl <> 0) THEN 'return the ctrl value
  163.                             Keys(i).Down = Keys(i).Ctrl
  164.                         ELSEIF Alt <> 0 AND Keys(i).Alt <> 0 THEN 'return the alt value
  165.                             Keys(i).Down = Keys(i).Alt
  166.                         ELSE 'all that's left is to return the ASCII value
  167.                             Keys(i).Down = Keys(i).ASCII
  168.                             IF _CAPSLOCK = 0 THEN 'caps lock basically reverses the behavior of the shift key with the letters A-Z and a-z
  169.                                 SELECT CASE i
  170.                                     CASE 65 TO 90: Keys(i).Down = Keys(i).Shift
  171.                                 END SELECT
  172.                             END IF
  173.                         END IF
  174.                         ReturnCount = ReturnCount + 1 'add one to the return buffer
  175.                         ReturnValues(ReturnCount) = Keys(i).Down 'and store the value in the buffer
  176.  
  177.                         IF Keys(i).Repeat = -1 THEN 'keys that are set to a -1 on repeat simply toggle state as on, or off.
  178.                             Keys(i).LastHit = 1E+1000 'such as SHIFT, CTRL, ALT...
  179.                         ELSE
  180.                             Keys(i).LastHit = ExtendedTimer + Keys(i).Repeat 'and record when we hit it for repeat purposes
  181.                         END IF
  182.                     END IF
  183.                 END IF
  184.             ELSE
  185.                 IF Keys(i).Down THEN 'the key was down on the last pass
  186.                     ReturnCount = ReturnCount + 1
  187.                     ReturnValues(ReturnCount) = -Keys(i).Down 'mark it as being up on this one
  188.                 END IF
  189.                 Keys(i).Down = 0 'and set it back down for future passes
  190.                 Keys(i).LastHit = 0 'once again, set it as being ready to be hit again
  191.             END IF
  192.         NEXT
  193.  
  194.         IF ReturnCount > 0 THEN 'If we generated a cue of values last pass, clear those up first, before getting new values.
  195.             'The only time we really see this is when we hit a shift, ctrl, alt key, usually.
  196.             KeyHit = ReturnValues(1)
  197.             FOR i = 1 TO ReturnCount - 1
  198.                 ReturnValues(i) = ReturnValues(i + 1)
  199.             NEXT
  200.             ReturnCount = ReturnCount - 1
  201.             EXIT FUNCTION
  202.         END IF
  203.  
  204.     END IF 'End of IF _WINDOWHASFOCUS
  205.  
  206.  
  207.  
  208.  
  209. SUB Remap_KeyCode (Which AS LONG, ASCII AS LONG, Ctrl AS LONG, Shift AS LONG, Alt AS LONG, AltGr AS LONG, Repeat AS _FLOAT)
  210.     DIM i AS LONG
  211.     i = Which
  212.     Keys(i).Index = i
  213.     Keys(i).ASCII = ASCII
  214.     Keys(i).Ctrl = Ctrl
  215.     Keys(i).Shift = Shift
  216.     Keys(i).Alt = Alt
  217.     Keys(i).AltGr = AltGr
  218.     Keys(i).Repeat = Repeat
  219.     Keys(i).LastHit = 0
  220.     Keys(i).Down = 0
  221.  
  222. SUB Remap_Extended_KeyCode (Which&, AltShift&, AltCtrl&, AltAltGr&, _
  223.          CtrlShift&, CtrlAltGr&, ShiftAltGr&, CtrlAltShift&)
  224.     Keys(Which&).AltShift = AltShift&
  225.     Keys(Which&).AltCtrl = AltCtrl&
  226.     Keys(Which&).AltAltGr = AltAltGr&
  227.     Keys(Which&).CtrlShift = CtrlShift&
  228.     Keys(Which&).CtrlAltGr = CtrlAltGr&
  229.     Keys(Which&).ShiftAltGr = ShiftAltGr&
  230.     Keys(Which&).CtrlAltShift = CtrlAltShift&
  231.  
  232. FUNCTION KeyDown& (Code AS LONG)
  233.     IF Code <= 0 THEN EXIT FUNCTION
  234.     FOR i = 1 TO 254
  235.         IF GetAsyncKeyState(i) THEN 'first check for actual physical keys down
  236.             IF Keys(i).ASCII = Code THEN KeyDown = -1: EXIT FUNCTION 'then check to see if the code matches anything we've mapped it to.
  237.             IF Keys(i).Shift = Code THEN KeyDown = -1: EXIT FUNCTION
  238.             IF Keys(i).Alt = Code THEN KeyDown = -1: EXIT FUNCTION
  239.             IF Keys(i).AltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  240.             IF Keys(i).AltShift = Code THEN KeyDown = -1: EXIT FUNCTION
  241.             IF Keys(i).AltCtrl = Code THEN KeyDown = -1: EXIT FUNCTION
  242.             IF Keys(i).AltAltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  243.             IF Keys(i).CtrlShift = Code THEN KeyDown = -1: EXIT FUNCTION
  244.             IF Keys(i).CtrlAltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  245.             IF Keys(i).ShiftAltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  246.             IF Keys(i).CtrlAltShift = Code THEN KeyDown = -1: EXIT FUNCTION
  247.         END IF
  248.     NEXT
  249.     KeyDown& = 0
  250.  
  251. SUB Init_KeyCodes (Language AS STRING)
  252.     RESTORE default_keyboard_data
  253.     FOR i = 1 TO 254
  254.         READ Keys(i).Index, Keys(i).ASCII, Keys(i).Ctrl, Keys(i).Shift, Keys(i).Alt, Keys(i).AltGr, Keys(i).Repeat
  255.         Keys(i).LastHit = 0: Keys(i).Down = 0
  256.     NEXT
  257.  
  258.     default_keyboard_data:
  259.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  260.     DATA 1,900001,0,0,0,0,0.2: 'Left Mouse Button
  261.     DATA 2,900002,0,0,0,0,0.2: 'Right Mouse Button
  262.     DATA 3,900003,0,0,0,0,0.2: 'VK_Cancel
  263.     DATA 4,900004,0,0,0,0,0.2: 'Middle Mouse Button
  264.     DATA 5,900005,0,0,0,0,0.2: 'Mouse Button 4
  265.     DATA 6,900006,0,0,0,0,0.2: 'Mouse Button 5
  266.     DATA 7,900007,0,0,0,0,0.2: 'Undefined
  267.     DATA 8,8,0,0,0,0,0.2: 'Backspace
  268.     DATA 9,9,0,0,0,0,0.2: 'Tab
  269.     DATA 10,900010,0,0,0,0,0.2: 'Reserved
  270.     DATA 11,900011,0,0,0,0,0.2: 'Reserved
  271.     DATA 12,19456,0,0,0,0,0.2: 'Clear
  272.     DATA 13,13,0,0,0,0,0.2: 'Enter
  273.     DATA 14,900014,0,0,0,0,0.2: 'Undefined
  274.     DATA 15,900015,0,0,0,0,0.2: 'Undefined
  275.     DATA 16,100016,0,0,0,0,-1: 'Shift (Notice I set it to simple toddle and report UP/DOWN results for us)
  276.     DATA 17,100017,0,0,0,0,-1: 'Ctrl   (Same)
  277.     DATA 18,100018,0,0,0,0,-1: 'Alt     (Same)
  278.     DATA 19,100019,0,0,0,0,0.2: 'Pause
  279.     DATA 20,100301,0,0,0,0,-1: 'Caps Lock
  280.     DATA 21,900021,0,0,0,0,0.2: 'VK_Hangul
  281.     DATA 22,900022,0,0,0,0,0.2: 'Undefined
  282.     DATA 23,900023,0,0,0,0,0.2: 'VK_Junja
  283.     DATA 24,900024,0,0,0,0,0.2: 'VK_Final
  284.     DATA 25,900025,0,0,0,0,0.2: 'VK_Hanga//VK_Kanji
  285.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  286.     DATA 26,900026,0,0,0,0,0.2: 'Undefined
  287.     DATA 27,27,0,0,0,0,0.2: 'ESC
  288.     DATA 28,900028,0,0,0,0,0.2: 'VK_Convert
  289.     DATA 29,900029,0,0,0,0,0.2: 'VK_NonConvert
  290.     DATA 30,900030,0,0,0,0,0.2: 'VK_Accept
  291.     DATA 31,900031,0,0,0,0,0.2: 'VK_ModeChange
  292.     DATA 32,32,0,0,0,0,0.2: 'VK_Space
  293.     DATA 33,18688,0,0,0,0,0.2: 'Page Up
  294.     DATA 34,20736,0,0,0,0,0.2: 'Page Down
  295.     DATA 35,20224,0,0,0,0,0.2: 'End
  296.     DATA 36,18176,0,0,0,0,0.2: 'Home
  297.     DATA 37,19200,0,0,0,0,0.2: 'Left Arrow
  298.     DATA 38,18432,0,0,0,0,0.2: 'Up Arrow
  299.     DATA 39,19712,0,0,0,0,0.2: 'Right Arrow
  300.     DATA 40,20480,0,0,0,0,0.2: 'Down Arrow
  301.     DATA 41,900041,0,0,0,0,-1: 'VK_SELECT
  302.     DATA 42,900042,0,0,0,0,-1: 'CK_PRINT
  303.     DATA 43,900043,0,0,0,0,-1: 'VK_EXECUTE
  304.     DATA 44,900044,0,0,0,0,-1: 'VK_SNAPSHOT
  305.     DATA 45,20992,0,0,0,0,0.2: 'INS
  306.     DATA 46,21248,0,0,0,0,0.2: 'DEL
  307.     DATA 47,900047,0,0,0,0,0.2: 'VK_HELP
  308.     DATA 48,48,0,41,0,0,0.2: '0
  309.     DATA 49,49,0,33,0,0,0.2: '1
  310.     DATA 50,50,0,64,0,0,0.2: '2
  311.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  312.     DATA 51,51,0,35,0,0,0.2: '3
  313.     DATA 52,52,0,36,0,0,0.2: '4
  314.     DATA 53,53,0,37,0,0,0.2: '5
  315.     DATA 54,54,0,94,0,0,0.2: '6
  316.     DATA 55,55,0,38,0,0,0.2: '7
  317.     DATA 56,56,0,42,0,0,0.2: '8
  318.     DATA 57,57,0,40,0,0,0.2: '9
  319.     DATA 58,900058,0,0,0,0,0.2: 'Undefined
  320.     DATA 59,900059,0,0,0,0,0.2: 'Undefined
  321.     DATA 60,900060,0,0,0,0,0.2: 'Undefined
  322.     DATA 61,900061,0,0,0,0,0.2: 'Undefined
  323.     DATA 62,900062,0,0,0,0,0.2: 'Undefined
  324.     DATA 63,900063,0,0,0,0,0.2: 'Undefined
  325.     DATA 64,900064,0,0,0,0,0.2: 'Undefined
  326.     DATA 65,65,0,97,0,0,0.2: 'a
  327.     DATA 66,66,0,98,0,0,0.2: 'b
  328.     DATA 67,67,0,99,0,0,0.2: 'c
  329.     DATA 68,68,0,100,0,0,0.2: 'd
  330.     DATA 69,69,0,101,0,0,0.2: 'e
  331.     DATA 70,70,0,102,0,0,0.2: 'f
  332.     DATA 71,71,0,103,0,0,0.2: 'g
  333.     DATA 72,72,0,104,0,0,0.2: 'h
  334.     DATA 73,73,0,105,0,0,0.2: 'i
  335.     DATA 74,74,0,106,0,0,0.2: 'j
  336.     DATA 75,75,0,107,0,0,0.2: 'k
  337.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  338.     DATA 76,76,0,108,0,0,0.2: 'l
  339.     DATA 77,77,0,109,0,0,0.2: 'm
  340.     DATA 78,78,0,110,0,0,0.2: 'n
  341.     DATA 79,79,0,111,0,0,0.2: 'o
  342.     DATA 80,80,0,112,0,0,0.2: 'p
  343.     DATA 81,81,0,113,0,0,0.2: 'q
  344.     DATA 82,82,0,114,0,0,0.2: 'r
  345.     DATA 83,83,0,115,0,0,0.2: 's
  346.     DATA 84,84,0,116,0,0,0.2: 't
  347.     DATA 85,85,0,117,0,0,0.2: 'u
  348.     DATA 86,86,0,118,0,0,0.2: 'v
  349.     DATA 87,87,0,119,0,0,0.2: 'w
  350.     DATA 88,88,0,120,0,0,0.2: 'x
  351.     DATA 89,89,0,121,0,0,0.2: 'y
  352.     DATA 90,90,0,122,0,0,0.2: 'z
  353.     DATA 91,100311,0,0,0,0,-1: 'Left WIN
  354.     DATA 92,100312,0,0,0,0,-1: 'Right WIN
  355.     DATA 93,100319,0,0,0,0,-1: 'Applications (Menu)
  356.     DATA 94,900094,0,0,0,0,0.2: 'Reserved
  357.     DATA 95,900095,0,0,0,0,0.2: 'VK_SLEEP
  358.     DATA 96,48,0,0,0,0,0.2: 'Numpad 0
  359.     DATA 97,49,0,0,0,0,0.2: 'Numpad 1
  360.     DATA 98,50,0,0,0,0,0.2: 'Numpad 2
  361.     DATA 99,51,0,0,0,0,0.2: 'Numpad 3
  362.     DATA 100,52,0,0,0,0,0.2: 'Numpad 4
  363.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  364.     DATA 101,53,0,0,0,0,0.2: 'Numpad 5
  365.     DATA 102,54,0,0,0,0,0.2: 'Numpad 6
  366.     DATA 103,55,0,0,0,0,0.2: 'Numpad 7
  367.     DATA 104,56,0,0,0,0,0.2: 'Numpad 8
  368.     DATA 105,57,0,0,0,0,0.2: 'Numpad 9
  369.     DATA 106,42,0,0,0,0,0.2: 'Numpad *
  370.     DATA 107,43,0,0,0,0,0.2: 'Numpad +
  371.     DATA 108,900108,0,0,0,0,0.2: 'VK_SEPARATOR
  372.     DATA 109,51,0,0,0,0,0.2: 'Numpad -
  373.     DATA 110,52,0,0,0,0,0.2: 'Numpad .
  374.     DATA 111,53,0,0,0,0,0.2: 'Numpad /
  375.     DATA 112,15104,0,0,0,0,0.2: 'F1
  376.     DATA 113,15360,0,0,0,0,0.2: 'F2
  377.     DATA 114,15616,0,0,0,0,0.2: 'F3
  378.     DATA 115,15872,0,0,0,0,0.2: 'F4
  379.     DATA 116,16128,0,0,0,0,0.2: 'F5
  380.     DATA 117,16384,0,0,0,0,0.2: 'F6
  381.     DATA 118,16640,0,0,0,0,0.2: 'F7
  382.     DATA 119,16896,0,0,0,0,0.2: 'F8
  383.     DATA 120,17152,0,0,0,0,0.2: 'F9
  384.     DATA 121,17408,0,0,0,0,0.2: 'F10
  385.     DATA 122,34048,0,0,0,0,0.2: 'F11
  386.     DATA 123,34304,0,0,0,0,0.2: 'F12
  387.     DATA 124,900124,0,0,0,0,0.2: 'F13
  388.     DATA 125,900125,0,0,0,0,0.2: 'F14
  389.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  390.     DATA 126,900126,0,0,0,0,0.2: 'F15
  391.     DATA 127,900127,0,0,0,0,0.2: 'F16
  392.     DATA 128,900128,0,0,0,0,0.2: 'F17
  393.     DATA 129,900129,0,0,0,0,0.2: 'F18
  394.     DATA 130,900130,0,0,0,0,0.2: 'F19
  395.     DATA 131,900131,0,0,0,0,0.2: 'F20
  396.     DATA 132,900132,0,0,0,0,0.2: 'F21
  397.     DATA 133,900133,0,0,0,0,0.2: 'F22
  398.     DATA 134,900134,0,0,0,0,0.2: 'F23
  399.     DATA 135,900135,0,0,0,0,0.2: 'F24
  400.     DATA 136,900136,0,0,0,0,0.2: 'Unassigned
  401.     DATA 137,900137,0,0,0,0,0.2: 'Unassigned
  402.     DATA 138,900138,0,0,0,0,0.2: 'Unassigned
  403.     DATA 139,900139,0,0,0,0,0.2: 'Unassigned
  404.     DATA 140,900140,0,0,0,0,0.2: 'Unassigned
  405.     DATA 141,900141,0,0,0,0,0.2: 'Unassigned
  406.     DATA 142,900142,0,0,0,0,0.2: 'Unassigned
  407.     DATA 143,900143,0,0,0,0,0.2: 'Unassigned
  408.     DATA 144,100300,0,0,0,0,-1: 'NUM LOCK
  409.     DATA 145,100302,0,0,0,0,-1: 'SCROLL LOCK
  410.     DATA 146,900146,0,0,0,0,0.2: 'OEM SPECIFIC
  411.     DATA 147,900147,0,0,0,0,0.2: 'OEM SPECIFIC
  412.     DATA 148,900148,0,0,0,0,0.2: 'OEM SPECIFIC
  413.     DATA 149,900149,0,0,0,0,0.2: 'OEM SPECIFIC
  414.     DATA 150,900150,0,0,0,0,0.2: 'OEM SPECIFIC
  415.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  416.     DATA 151,900151,0,0,0,0,0.2: 'Unassigned
  417.     DATA 152,900152,0,0,0,0,0.2: 'Unassigned
  418.     DATA 153,900153,0,0,0,0,0.2: 'Unassigned
  419.     DATA 154,900154,0,0,0,0,0.2: 'Unassigned
  420.     DATA 155,900155,0,0,0,0,0.2: 'Unassigned
  421.     DATA 156,900156,0,0,0,0,0.2: 'Unassigned
  422.     DATA 157,900157,0,0,0,0,0.2: 'Unassigned
  423.     DATA 158,900158,0,0,0,0,0.2: 'Unassigned
  424.     DATA 159,900159,0,0,0,0,0.2: 'Unassigned
  425.     DATA 160,100304,0,0,0,0,-1: 'Left Shift
  426.     DATA 161,100303,0,0,0,0,-1: 'Right Shift
  427.     DATA 162,100306,0,0,0,0,-1: 'Left Control
  428.     DATA 163,100305,0,0,0,0,-1: 'Right Control
  429.     DATA 164,100308,0,0,0,0,-1: 'Left Alt
  430.     DATA 165,100309,0,0,0,0,-1: 'Right Alt
  431.     DATA 166,900166,0,0,0,0,0.2: 'Browser back
  432.     DATA 167,900167,0,0,0,0,0.2: 'Browser forward
  433.     DATA 168,900168,0,0,0,0,0.2: 'Browser refresh
  434.     DATA 169,900169,0,0,0,0,0.2: 'Browser stop
  435.     DATA 170,900170,0,0,0,0,0.2: 'Browser search
  436.     DATA 171,900171,0,0,0,0,0.2: 'Browser favorites
  437.     DATA 172,900172,0,0,0,0,0.2: 'Browser home
  438.     DATA 173,900173,0,0,0,0,0.2: 'Mute
  439.     DATA 174,900174,0,0,0,0,0.2: 'Vol Down
  440.     DATA 175,900175,0,0,0,0,0.2: 'Vol Up
  441.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  442.     DATA 176,900176,0,0,0,0,0.2: 'Media Next
  443.     DATA 177,900177,0,0,0,0,0.2: 'Media prev
  444.     DATA 178,900178,0,0,0,0,0.2: 'Media stop
  445.     DATA 179,900179,0,0,0,0,0.2: 'Media Play/Pause
  446.     DATA 180,900180,0,0,0,0,0.2: 'Launch mail
  447.     DATA 181,900181,0,0,0,0,0.2: 'Launch media select
  448.     DATA 182,900182,0,0,0,0,0.2: 'Launch app1
  449.     DATA 183,900183,0,0,0,0,0.2: 'Launch app2
  450.     DATA 184,900184,0,0,0,0,0.2: 'Reserved
  451.     DATA 185,900185,0,0,0,0,0.2: 'Reserved
  452.     DATA 186,59,0,58,0,0,0.2: ';:
  453.     DATA 187,61,0,43,0,0,0.2: '=+
  454.     DATA 188,44,0,60,0,0,0.2: ',<
  455.     DATA 189,45,0,95,0,0,0.2: '-_
  456.     DATA 190,46,0,62,0,0,0.2: '.>
  457.     DATA 191,47,0,63,0,0,0.2: '/?
  458.     DATA 192,96,0,126,0,0,0.2: '`~
  459.     DATA 193,900193,0,0,0,0,0.2: 'Reserved
  460.     DATA 194,900194,0,0,0,0,0.2: 'Reserved
  461.     DATA 195,900195,0,0,0,0,0.2: 'Reserved
  462.     DATA 196,900196,0,0,0,0,0.2: 'Reserved
  463.     DATA 197,900197,0,0,0,0,0.2: 'Reserved
  464.     DATA 198,900198,0,0,0,0,0.2: 'Reserved
  465.     DATA 199,900199,0,0,0,0,0.2: 'Reserved
  466.     DATA 200,900200,0,0,0,0,0.2: 'Reserved
  467.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  468.     DATA 201,900201,0,0,0,0,0.2: 'Reserved
  469.     DATA 202,900202,0,0,0,0,0.2: 'Reserved
  470.     DATA 203,900203,0,0,0,0,0.2: 'Reserved
  471.     DATA 204,900204,0,0,0,0,0.2: 'Reserved
  472.     DATA 205,900205,0,0,0,0,0.2: 'Reserved
  473.     DATA 206,900206,0,0,0,0,0.2: 'Reserved
  474.     DATA 207,900207,0,0,0,0,0.2: 'Reserved
  475.     DATA 208,900208,0,0,0,0,0.2: 'Reserved
  476.     DATA 209,900209,0,0,0,0,0.2: 'Reserved
  477.     DATA 210,900210,0,0,0,0,0.2: 'Reserved
  478.     DATA 211,900211,0,0,0,0,0.2: 'Reserved
  479.     DATA 212,900212,0,0,0,0,0.2: 'Reserved
  480.     DATA 213,900213,0,0,0,0,0.2: 'Reserved
  481.     DATA 214,900214,0,0,0,0,0.2: 'Reserved
  482.     DATA 215,900215,0,0,0,0,0.2: 'Reserved
  483.     DATA 216,900216,0,0,0,0,0.2: 'Unassigned
  484.     DATA 217,900217,0,0,0,0,0.2: 'Unassigned
  485.     DATA 218,900218,0,0,0,0,0.2: 'Unassigned
  486.     DATA 219,91,0,123,0,0,0.2: '[{
  487.     DATA 220,92,0,124,0,0,0.2: '\|
  488.     DATA 221,93,0,125,0,0,0.2: ']}
  489.     DATA 222,39,0,34,0,0,0.2: ''"
  490.     DATA 223,900223,0,0,0,0,0.2: 'OEM SPECIFIC
  491.     DATA 224,900224,0,0,0,0,0.2: 'Reserved
  492.     DATA 225,900225,0,0,0,0,0.2: 'OEM SPECIFIC d
  493.     DATA 226,900226,0,0,0,0,0.2: 'Either the Angle Bracket key,or Backslash on RT 102-key keyboard
  494.     DATA 227,900227,0,0,0,0,0.2: 'OEM SPECIFIC
  495.     DATA 228,900228,0,0,0,0,0.2: 'OEM SPECIFIC
  496.     DATA 229,900229,0,0,0,0,0.2: 'IME PROCESS key (whatever that is)
  497.     DATA 230,900230,0,0,0,0,0.2: 'OEM SPECIFIC
  498.     DATA 231,900231,0,0,0,0,0.2: 'Used to pass UNICODE characters (however that works)
  499.     DATA 232,900232,0,0,0,0,0.2: 'Unassigned
  500.     DATA 233,900233,0,0,0,0,0.2: 'OEM SPECIFIC
  501.     DATA 234,900234,0,0,0,0,0.2: 'OEM SPECIFIC
  502.     DATA 235,900235,0,0,0,0,0.2: 'OEM SPECIFIC
  503.     DATA 236,900236,0,0,0,0,0.2: 'OEM SPECIFIC
  504.     DATA 237,900237,0,0,0,0,0.2: 'OEM SPECIFIC
  505.     DATA 238,900238,0,0,0,0,0.2: 'OEM SPECIFIC
  506.     DATA 239,900239,0,0,0,0,0.2: 'OEM SPECIFIC
  507.     DATA 240,900240,0,0,0,0,0.2: 'OEM SPECIFIC
  508.     DATA 241,900241,0,0,0,0,0.2: 'OEM SPECIFIC
  509.     DATA 242,900242,0,0,0,0,0.2: 'OEM SPECIFIC
  510.     DATA 243,900243,0,0,0,0,0.2: 'OEM SPECIFIC
  511.     DATA 244,900244,0,0,0,0,0.2: 'OEM SPECIFIC
  512.     DATA 245,900245,0,0,0,0,0.2: 'OEM SPECIFIC
  513.     DATA 246,900246,0,0,0,0,0.2: 'VK_ATTN
  514.     DATA 247,900247,0,0,0,0,0.2: 'VK_ATTN
  515.     DATA 248,900248,0,0,0,0,0.2: 'VK_ATTN
  516.     DATA 249,900249,0,0,0,0,0.2: 'VK_ATTN
  517.     DATA 250,900250,0,0,0,0,0.2: 'VK_ATTN
  518.     DATA 251,900251,0,0,0,0,0.2: 'VK_ATTN
  519.     DATA 252,900252,0,0,0,0,0.2: 'Reserved
  520.     DATA 253,900253,0,0,0,0,0.2: 'VK_PA1
  521.     DATA 254,900253,0,0,0,0,0.2: 'VK_OEM_CLEAR
  522.     DATA 0,0,0,0,0,0,0.2: 'END OF DATA
  523.     AltGr(0) = 165
  524.     AltGr(1) = 0
  525.  
  526.     SELECT CASE Language
  527.         CASE "DE"
  528.             RESTORE Microsoft_windows_cp1250:
  529.             FOR i = 128 TO 255
  530.                 READ unicode
  531.                 _MAPUNICODE unicode TO ASCIIcode
  532.             NEXT
  533.             Microsoft_windows_cp1250:
  534.             DATA 8364,0,8218,0,8222,8230,8224,8225,0,8240,352,8249,346,356,381,377
  535.             DATA 0,8216,8217,8220,8221,8226,8211,8212,0,8482,353,8250,347,357,382,378
  536.             DATA 160,711,728,321,164,260,166,167,168,169,350,171,172,173,174,379
  537.             DATA 176,177,731,322,180,181,182,183,184,261,351,187,317,733,318,380
  538.             DATA 340,193,194,258,196,313,262,199,268,201,280,203,282,205,206,270
  539.             DATA 272,323,327,211,212,336,214,215,344,366,218,368,220,221,354,223
  540.             DATA 341,225,226,259,228,314,263,231,269,233,281,235,283,237,238,271
  541.             DATA 273,324,328,243,244,337,246,247,345,367,250,369,252,253,355,729
  542.             'Remap_KeyCode (Which, ASCII, Ctrl , Shift, Alt, AltGr, Repeat AS _FLOAT)
  543.             Remap_KeyCode 226, 60, 0, 62, 124, 92, 0.2 '<>|
  544.             Remap_KeyCode 219, 225, 0, 63, 0, 0, 0.2 '-
  545.             Remap_KeyCode 48, 48, 0, 61, 0, 125, 0.2 '0
  546.             Remap_KeyCode 192, 148, 0, 153, 0, 0, 0.2
  547.             Remap_KeyCode 222, 132, 0, 142, 0, 0, 0.2
  548.             Remap_KeyCode 50, 50, 0, 34, 0, 253, 0.2: '2 .. I don't see a superscript 3 for AltGr codes for the 3 key.
  549.             Remap_KeyCode 51, 51, 0, 35, 0, 0, 0.2: '3 ..I don't see the squiggle for this in the ASCII code.  It needs to be changed, but I dunno with what.
  550.             Remap_KeyCode 54, 54, 0, 38, 0, 0, 0.2: '6
  551.             Remap_KeyCode 55, 55, 0, 47, 0, 123, 0.2: '7
  552.             Remap_KeyCode 56, 56, 0, 40, 0, 91, 0.2: '8
  553.             Remap_KeyCode 57, 57, 0, 41, 0, 93, 0.2: '9
  554.             Remap_KeyCode 186, 129, 0, 154, 0, 0, 0.2: ';:
  555.             Remap_KeyCode 187, 43, 0, 42, 0, 126, 0.2: '=+
  556.             Remap_KeyCode 191, 35, 0, 249, 0, 0, 0.2: '/?
  557.             Remap_KeyCode 81, 81, 0, 113, 0, 64, 0.2: 'q
  558.             Remap_KeyCode 69, 69, 0, 101, 0, 238, 0.2: 'e
  559.             Remap_KeyCode 77, 77, 0, 109, 0, 0, 0.2: 'm -- again, I failed to find the goofy u which AltGr produces in the 256 ASCII set
  560.     END SELECT
  561.  
  562.  
  563.  
  564.  
  565.  
  566. FUNCTION ExtendedTimer##
  567.     'modified extendedtimer to store the old day's count, and not have to recalculate it every time the routine is called.
  568.  
  569.     STATIC olds AS _FLOAT, old_day AS _FLOAT
  570.     DIM m AS INTEGER, d AS INTEGER, y AS INTEGER
  571.     DIM s AS _FLOAT, day AS STRING
  572.     IF olds = 0 THEN 'calculate the day the first time the extended timer runs
  573.         day = DATE$
  574.         m = VAL(LEFT$(day, 2))
  575.         d = VAL(MID$(day, 4, 2))
  576.         y = VAL(RIGHT$(day, 4)) - 1970
  577.         SELECT CASE m 'Add the number of days for each previous month passed
  578.             CASE 2: d = d + 31
  579.             CASE 3: d = d + 59
  580.             CASE 4: d = d + 90
  581.             CASE 5: d = d + 120
  582.             CASE 6: d = d + 151
  583.             CASE 7: d = d + 181
  584.             CASE 8: d = d + 212
  585.             CASE 9: d = d + 243
  586.             CASE 10: d = d + 273
  587.             CASE 11: d = d + 304
  588.             CASE 12: d = d + 334
  589.         END SELECT
  590.         IF (y MOD 4) = 2 AND m > 2 THEN d = d + 1 'add a day if this is leap year and we're past february
  591.         d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
  592.         d = d + (y + 2) \ 4 'add in days for leap years passed
  593.         s = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  594.         old_day = s
  595.     END IF
  596.     IF TIMER < oldt THEN 'we went from 23:59:59 (a second before midnight) to 0:0:0 (midnight)
  597.         old_day = s + 83400 'add another worth of seconds to our counter
  598.     END IF
  599.     oldt = TIMER
  600.     olds = old_day + oldt
  601.     ExtendedTimer## = olds
  602.  
  603.  
  604.  
  605.  
  606.  
  607.  
  608. FUNCTION ExtendedInput$
  609.     SHARED AltGr, Alt, Shift, Ctrl
  610.     PCOPY 0, 1
  611.     A = _AUTODISPLAY: X = POS(0): Y = CSRLIN
  612.     CP = 0: OldCP = 0 'Cursor Position
  613.     _KEYCLEAR
  614.     DO
  615.         PCOPY 1, 0
  616.  
  617.         k = KeyHit
  618.         IF Alt THEN AltDown = -1 ELSE AltDown = 0
  619.         SELECT CASE k 'without alt, add any keypresses to our input
  620.             CASE 8
  621.                 oldin$ = in$
  622.                 IF CP > 0 THEN OldCP = CP: CP = CP - 1
  623.                 in$ = LEFT$(in$, CP) + MID$(in$, CP + 2) 'backspace to erase input
  624.             CASE 9
  625.                 oldin$ = in$
  626.                 in$ = LEFT$(in$, CP) + SPACE$(4) + MID$(in$, CP + 1) 'four spaces for any TAB entered
  627.                 OldCP = CP
  628.                 CP = CP + 4
  629.             CASE 48 TO 57 '0 to 9
  630.                 IF AltDown THEN
  631.                     AltWasDown = -1: alt$ = alt$ + CHR$(k)
  632.                 ELSE
  633.                     oldin$ = in$
  634.                     in$ = LEFT$(in$, CP) + CHR$(k) + MID$(in$, CP + 1) 'add input to our string
  635.                     OldCP = CP
  636.                     CP = CP + 1
  637.                 END IF
  638.             CASE 1 TO 255 'the rest of the ASCII characters
  639.                 IF Ctrl AND (k = 118 OR k = 86) THEN
  640.                     oldin$ = in$
  641.                     in$ = LEFT$(in$, CP) + _CLIPBOARD$ + MID$(in$, CP + 1) 'ctrl-v paste
  642.                     'CTRL-V leaves cursor in position before the paste, without moving it after.
  643.                     'Feel free to modify that behavior here, if you want it to move to after the paste.
  644.                 ELSEIF Ctrl AND (k = 122 OR k = 90) THEN
  645.                     SWAP in$, oldin$: SWAP OldCP, CP 'ctrl-z undo
  646.                 ELSE
  647.                     oldin$ = in$
  648.                     in$ = LEFT$(in$, CP) + CHR$(k) + MID$(in$, CP + 1) 'add input to our string
  649.                     OldCP = CP
  650.                     CP = CP + 1
  651.                 END IF
  652.             CASE 18176 'Home
  653.                 CP = 0
  654.             CASE 20224 'End
  655.                 CP = LEN(in$)
  656.             CASE 21248 'Delete
  657.                 oldin$ = in$
  658.                 in$ = LEFT$(in$, CP) + MID$(in$, CP + 2)
  659.             CASE 19200 'Left
  660.                 CP = CP - 1
  661.                 IF CP < 0 THEN CP = 0
  662.             CASE 19712 'Right
  663.                 CP = CP + 1
  664.                 IF CP > LEN(in$) THEN CP = LEN(in$)
  665.         END SELECT
  666.  
  667.         alt$ = RIGHT$(alt$, 3)
  668.         IF AltWasDown = -1 AND AltDown = 0 THEN
  669.             v = VAL(alt$)
  670.             IF v >= 0 AND v <= 255 THEN in$ = in$ + CHR$(v): CP = CP + 1
  671.             alt$ = "": AltWasDown = 0
  672.         END IF
  673.         blink = (blink + 1) MOD 30
  674.         LOCATE Y, X
  675.         PRINT LEFT$(in$, CP);
  676.         IF blink \ 15 THEN PRINT " "; ELSE PRINT "_";
  677.         PRINT MID$(in$, CP + 1)
  678.  
  679.         _DISPLAY
  680.         _LIMIT 30
  681.     LOOP UNTIL k = 13
  682.  
  683.     PCOPY 1, 0
  684.     LOCATE Y, X: PRINT in$
  685.     ExtendedInput$ = in$
  686.  

I know there's a few symbols missing, but those may simply be my weak eyes overlooking them as I scanned the character page for their codes repeatedly for them.  (Such as the squiggle wiggle SHIFT-3 has with the onscreen keyboard.)

There's also a few onscreen keys which I couldn't seem to read at all... WHY, I dunno. Is it a glitch with the onscreen keyboard?  With the windows library function I used to check key presses?  My code??  I dunno!!  Will it work with an actual keyboard and not the onscreen one that Win 10 provides?  I dunno!

All I can say at this point is, "Feel free to test it out and let me know how it works (if at all)."  Hopefully it'll work for folks with non-standard US keyboards, who use CP1250..  If not -- well, I didn't actually promise it would act.  I just promise that I tried to make it work.. :P
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline RhoSigma

  • QB64 Developer
  • Forum Resident
  • Posts: 565
    • View Profile
Re: Programmable Keyboard Input
« Reply #21 on: January 03, 2020, 12:03:35 pm »
Hi Steve,

tested with the real DE keyboard (not onscreen), it seems to work properly for the regular keys (no modifier) and their respective SHIFT mappings, except for the squiggle wiggle SHIFT-3 (paragraph btw.) However some of the AltGr mappings are wrong or missing.
Man, then I realized, we need a custom font when working with _MAPUNICODE. Done so, but no difference, even worse on some SHIFT+key (interpunctation keys mainly).

Another thing to think of, many european languages can be properly displayed using CP437. I'd prefer not to be forced on a specific CP, because it requires custom fonts to be used, which is not always disireable.

IMHO, you should keep the routines as they were before and let the users in the respective countries contribute the required mappings over time, which you can then put into their own SUBs like "SetKeymapDE()", "SetKeymapFR()" etc.
This might take a bit more time, until people will contribute, but I think it's better than "overautomate" things, which obviously can't be automated very well by its nature.
My Projects:   https://qb64forum.alephc.xyz/index.php?topic=809
GuiTools - A graphic UI framework (can do multiple UI forms/windows in one program)
Libraries - ImageProcess, StringBuffers (virt. files), MD5/SHA2-Hash, LZW etc.
Bonus - Blankers, QB64/Notepad++ setup pack

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Programmable Keyboard Input
« Reply #22 on: January 04, 2020, 07:39:14 pm »
Hi Steve
here my feedback on the last code that you have posted at you last post here.
Well it does not recognize
LeftClick of mouse

more it doesn't distinguish between 3 on keyboard 3 on numlock keyboard, on numlock keyboard, / on numlock and 5

for the remaining part I'm testing for adjust of value and board on the screen.
Programming isn't difficult, only it's  consuming time and coffee

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Programmable Keyboard Input
« Reply #23 on: January 04, 2020, 08:34:38 pm »
Hi Steve
here my feedback on the last code that you have posted at you last post here.
Well it does not recognize
LeftClick of mouse

more it doesn't distinguish between 3 on keyboard 3 on numlock keyboard, on numlock keyboard, / on numlock and 5

for the remaining part I'm testing for adjust of value and board on the screen.

It works with the left mouse just fine; I just have it ignore it on purpose so it doesn't spam the code 900001 every time I clicked on something with the onscreen keyboard.  ;)

Code: [Select]
        SELECT CASE k
            CASE 900001 'left mouse
            CASE 1 TO 255
                PRINT k, CHR$(k)
            CASE IS <= 0
            CASE ELSE
                PRINT k
        END SELECT

Quote
more it doesn't distinguish between 3 on keyboard 3 on numlock keyboard, on numlock keyboard, / on numlock and 5

I mapped these to the Windows Onscreen keyboard, so I'm not certain how accurate they'll work with an actual keyboard.  According to Rho, they don't seem to work worth a hoot for him, so I guess I'll just have to wait for users to assign their own keys and share them with us, before I can build a swappable language/keyboard library.  There's only so much testing/experimenting I can do with the built-in tools Microsoft gives us, and all I can do is try and code the keys to match what they offer for me -- which apparently isn't that similar to the real hardware.   :(

Thanks for testing it out, and reporting back for me.  At the least, perhaps it'll be a roadmap to help show others how they can map their own language/keyboard to work with the system.  I think this, along with the UTF-8 and Unicode convertors I worked up, will be more than capable to handle any input/output needs which someone needs in the future, once they configure the two to work together for them.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Programmable Keyboard Input
« Reply #24 on: January 05, 2020, 11:26:46 am »
Hi Steve
the first feedback:

this is the keyboard that I'm using to test your code.
 
TOSHIBA Satellite pro C850 keyboard.jpg

and these are the difference that I 'm finding with US standard setting
in this moment if I press the key in your application I get the code on the middle with the following characters while if I press the keys on the first column in another application for example Notepad of Windows I get the characters that I read on the keyboard.

Quote
keys used  -> code   char get vs--> IT
shift + . ->62    >   -->:
shift + , -> 60  <  --> ;
ù  -> 35  # -->ù
. ->  249  . -->§
à ->  132     -->à
Shift+à -> 142 ->   -->°
altgr + à-> 132   -->#
ò -> 148 ->  -->ò
shift + ò -> 153 ->   -->ç
altgr +ò ->    -->@
è ->129  -->è
Shift + è -> 154   -->è
altgr + à -> 129  -->[
altgr + + ->126  tilde -->]
' ->225   -->'
93 -> ] --> ì
altgr + shift + + ->125  tilde  --> }
altgr + shift + è -> 154  -->{
shift+ì -> 125  } -->^
shift+3 ->35 # --> £

where you see blank is a character that is not of italian alphabet and not on the keyboard.
Thanks for your work...

now how can I build up  the SUB  IT_Keyboard ? I don't like to mess your wonderful work, so I wait for instructions.
Programming isn't difficult, only it's  consuming time and coffee

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Programmable Keyboard Input
« Reply #25 on: January 05, 2020, 11:39:55 am »
the second feedback:
about
Quote
more it doesn't distinguish between 3 on keyboard 3 on numlock keyboard, on numlock keyboard, / on numlock and 5

I want be clearer than above affirmation:
in your application if I press key 3 with has  also a # on it I get back 51  3, and this same result I get back if I press - on the numeric keypad on the right of the keyboard (over the + and at right of *) so for now we cannot distinguish between these two keys, moreover if I activate NumLock we can add another key that gets back with 51  3 in your application and this is the 3 (PGDN) key on numeric keypad.

the same issue arises with key 5 (%)  and / of numeric keypad that together give back 53  5, moreover if I activate NumLock I get back the same code pressing 5 on numeric keypad. I think that it will be a way to distinguish among these keys.

Thanks to read
Programming isn't difficult, only it's  consuming time and coffee

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Programmable Keyboard Input
« Reply #26 on: January 05, 2020, 12:23:38 pm »
It distinguishes between the numbers and the numpad; it simply has been coded to return the same values for us, making it seem as it it hasn't.

For the number keys above the alphabet:

Code: [Select]
    DATA 48,48,0,41,0,0,0.2: '0
    DATA 49,49,0,33,0,0,0.2: '1
    DATA 50,50,0,64,0,0,0.2: '2
    '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
    DATA 51,51,0,35,0,0,0.2: '3
    DATA 52,52,0,36,0,0,0.2: '4
    DATA 53,53,0,37,0,0,0.2: '5
    DATA 54,54,0,94,0,0,0.2: '6
    DATA 55,55,0,38,0,0,0.2: '7
    DATA 56,56,0,42,0,0,0.2: '8
    DATA 57,57,0,40,0,0,0.2: '9

And for the numpad:
Code: [Select]
    DATA 96,48,0,0,0,0,0.2: 'Numpad 0
    DATA 97,49,0,0,0,0,0.2: 'Numpad 1
    DATA 98,50,0,0,0,0,0.2: 'Numpad 2
    DATA 99,51,0,0,0,0,0.2: 'Numpad 3
    DATA 100,52,0,0,0,0,0.2: 'Numpad 4
    '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
    DATA 101,53,0,0,0,0,0.2: 'Numpad 5
    DATA 102,54,0,0,0,0,0.2: 'Numpad 6
    DATA 103,55,0,0,0,0,0.2: 'Numpad 7
    DATA 104,56,0,0,0,0,0.2: 'Numpad 8
    DATA 105,57,0,0,0,0,0.2: 'Numpad 9
    DATA 106,42,0,0,0,0,0.2: 'Numpad *
    DATA 107,43,0,0,0,0,0.2: 'Numpad +
    DATA 108,900108,0,0,0,0,0.2: 'VK_SEPARATOR
    DATA 109,51,0,0,0,0,0.2: 'Numpad -
    DATA 110,52,0,0,0,0,0.2: 'Numpad .
    DATA 111,53,0,0,0,0,0.2: 'Numpad /

Let's compare the two values 3, for example:
    DATA 51,51,0,35,0,0,0.2: '3
    DATA 99,51,0,0,0,0,0.2: 'Numpad 3

The Red values above represent the internal key which Windows designates for the physical key on our keyboard.
The Blue values are the ASCII values which we return when we press that key.  They're different physical keys, but we've mapped them both to return the same values for us. 

I suppose I should probably write a small helper routine for us though, so one can tell which of the physical keys is being pressed, just in case they wanted to be able to distinguish between the two of them -- though that would actually be just as simple as check with the physical value.

IF keys(51).down then 'It's the number 3, above the letter keys
IF keys(99).down then 'It's the numpad 3
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Programmable Keyboard Input
« Reply #27 on: January 05, 2020, 03:29:03 pm »
now how can I build up  the SUB  IT_Keyboard ? I don't like to mess your wonderful work, so I wait for instructions.

Here's the simplest way:

Code: QB64: [Select]
  1. DECLARE LIBRARY 'function is already used by QB64 so "User32" is not required
  2.     FUNCTION GetKeyState% (BYVAL vkey AS LONG)
  3.     FUNCTION GetAsyncKeyState% (BYVAL vkey AS LONG)
  4.  
  5. TYPE KeyboardInfo_Type
  6.     Index AS LONG
  7.     ASCII AS LONG
  8.     Ctrl AS LONG
  9.     Shift AS LONG
  10.     Alt AS LONG
  11.     AltGr AS LONG
  12.     Repeat AS _FLOAT
  13.     LastHit AS _FLOAT
  14.     Down AS LONG
  15.     AltShift AS LONG
  16.     AltCtrl AS LONG
  17.     AltAltGr AS LONG
  18.     CtrlShift AS LONG
  19.     CtrlAlt AS LONG
  20.     CtrlAltGr AS LONG
  21.     ShiftAltGr AS LONG
  22.     CtrlAltShift AS LONG
  23.  
  24. DIM SHARED Keys(254) AS KeyboardInfo_Type
  25. Init_KeyCodes
  26. Remap_KeyCode 65, 65, 1, 97, 160, 142, 0.2: 'A , , a,  , Ž
  27. 'Remap_Extended_KeyCode (Which&, AltShift&, AltCtrl&, AltAltGr&, CtrlShift&, CtrlAltGr&, ShiftAltGr&, CtrlAltShift&)
  28. Remap_Extended_KeyCode 65, 143, 241, 242, 243, 244, 245, 246 ' and more!
  29.  
  30.  
  31.     k = KeyHit
  32.     IF k > 0 THEN
  33.         CLS
  34.         IF k > 0 THEN PRINT "Physical Key Down: "; Physical_KeyDown(k)
  35.         PRINT "Key Code Down: "; k;
  36.         IF ABS(k) < 256 THEN PRINT CHR$(34); CHR$(ABS(k)); CHR$(34) ELSE PRINT
  37.     END IF
  38.     _LIMIT 30
  39. LOOP UNTIL k = 27 'escape
  40.  
  41. SUB SetAltGr (Key1 AS INTEGER, Key2 AS INTEGER)
  42.     AltGr(0) = Key1 'any key from our index (0 says no key)
  43.     AltGr(1) = Key2 'PLUS any other key from our index (0 says no additional key)
  44.     'Using this, we can set AltGr to become several things.
  45.     'AltGr(0) = 165, AltGr(1) = 0 -- This would say we're using the RIGHT Alt key (alone) to simulate the AltGr key.  (Windows Onscreen Keyboard does this.)
  46.     'AltGr(0) = 17, AltGr(1) = 18 -- This would use any CTRL-ALT combo to simulate a AltGr keypress.
  47.     'Some useful values are listed for quick reference below
  48.     '0 = NoKey
  49.     '17 = ANY Ctrl
  50.     '18 = ANY Alt
  51.     '162 = Left Control
  52.     '163 = Right Control
  53.     '164 = Left Alt
  54.     '165 = Right Alt
  55.  
  56.     'Default is for AltGr(0) = 165, AltGr(1) = 0, which uses Right-Alt alone as the AltGr key.
  57.     'Feel free to customize the setting to your personal preference/need.
  58.  
  59. SUB KeyClear
  60.     _DELAY .05 'give time for a keyup event to log itself so we can clear it
  61.     DO: k = KeyHit: LOOP UNTIL k = 0
  62.  
  63. FUNCTION KeyHit&
  64.     STATIC ReturnCount AS INTEGER
  65.     STATIC ReturnValues(30) AS LONG
  66.     SHARED AltGr, Alt, Shift, Ctrl
  67.     IF Keys(1).Index = 0 THEN Init_KeyCodes 'if someone forgets to put the init routine in their code, be certain to initialize the codes before attempting to use them.
  68.  
  69.  
  70.     IF ReturnCount > 0 THEN 'If we generated a cue of values last pass, clear those up first, before getting new values.
  71.         'The only time we really see this is when we hit a shift, ctrl, alt key, usually.
  72.         KeyHit = ReturnValues(1)
  73.         FOR i = 1 TO ReturnCount - 1
  74.             ReturnValues(i) = ReturnValues(i + 1)
  75.         NEXT
  76.         ReturnCount = ReturnCount - 1
  77.         EXIT FUNCTION
  78.     END IF
  79.  
  80.     IF Keys(16).Down THEN Shift = -1 ELSE Shift = 0
  81.     IF Keys(17).Down THEN Ctrl = -1 ELSE Ctrl = 0
  82.     IF Keys(18).Down THEN Alt = -1 ELSE Alt = 0
  83.     IF AltGr(0) <> 0 AND AltGr(1) <> 0 THEN
  84.         IF Keys(AltGr(0)).Down AND Keys(AltGr(1)).Down THEN AltGr = -1 ELSE AltGr = 0
  85.     ELSEIF AltGr(1) <> 0 THEN
  86.         IF Keys(AltGr(1)).Down THEN AltGr = -1 ELSE AltGr = 0
  87.     ELSEIF AltGr(0) <> 0 THEN
  88.         IF Keys(AltGr(0)).Down THEN AltGr = -1 ELSE AltGr = 0
  89.     ELSE
  90.         AltGr = 0
  91.     END IF
  92.  
  93.     'until Ctrl or Alt status, if the key down was used to help generate AltGr as a modifier key
  94.     IF AltGr THEN
  95.         IF (AltGr(0) = 18 OR AltGr(1) = 18) THEN Alt = 0 'if we use both ALT keys to represent part of AltGr, when AltGr is active, Alt isn't.
  96.         IF (AltGr(0) = 164 OR AltGr(1) = 164) AND Keys(165).Down = 0 THEN Alt = 0 'if we use Left ALT keys to represent part of AltGr, when AltGr is active, Left Alt isn't.
  97.         IF (AltGr(0) = 165 OR AltGr(1) = 165) AND Keys(164).Down = 0 THEN Alt = 0 'if we use Right ALT keys to represent part of AltGr, when AltGr is active, Right Alt isn't.
  98.         IF (AltGr(0) = 17 OR AltGr(1) = 17) THEN Ctrl = 0 'if we use both CTRL keys to represent part of AltGr, when AltGr is active, Ctrl isn't.
  99.         IF (AltGr(0) = 162 OR AltGr(1) = 162) AND Keys(163).Down = 0 THEN Ctrl = 0 'if we use Left CTRL keys to represent part of AltGr, when AltGr is active, Left Ctrl isn't.
  100.         IF (AltGr(0) = 163 OR AltGr(1) = 163) AND Keys(162).Down = 0 THEN Ctrl = 0 'if we use Right CTRL keys to represent part of AltGr, when AltGr is active, Right Ctrl isn't.
  101.     END IF
  102.  
  103.     IF Alt AND Shift THEN AltShift = -1 ELSE AltShift = 0
  104.     IF Alt AND Ctrl THEN AltCtrl = -1 ELSE AltCtrl = 0
  105.     IF Alt AND AltAltGR THEN AltAltGR = -1 ELSE AltAltGR = 0
  106.     IF Ctrl AND Shift THEN CtrlShift = -1 ELSE CtrlShift = 0
  107.     IF Shift AND AltGr THEN ShiftAltGr = -1 ELSE ShiftAltGr = 0
  108.     IF Ctrl AND Alt AND Shift THEN CtrlAltShift = -1 ELSE CtrlAltShift = 0
  109.  
  110.         FOR i = 1 TO 254
  111.  
  112.             r = GetKeyState(Keys(i).Index) AND &H8000
  113.  
  114.             IF r THEN 'the key is down
  115.  
  116.  
  117.                 IF Keys(i).LastHit THEN
  118.                     IF ExtendedTimer > Keys(i).LastHit THEN
  119.                         ReturnCount = ReturnCount + 1 'add one to the return buffer
  120.                         ReturnValues(ReturnCount) = Keys(i).Down 'and put the existing value back in the buffer, as a key repeat
  121.                     END IF
  122.                 ELSE
  123.  
  124.                     IF Keys(i).Down = 0 THEN 'the key was up on the last pass.
  125.                         IF CtrlAltShift <> 0 AND Keys(i).CtrlAltShift <> 0 THEN 'return the CtrlAltShift value
  126.                             Keys(i).Down = Keys(i).CtrlAltShift
  127.                         ELSEIF AltAltGR <> 0 AND Keys(i).AltAltGr <> 0 THEN 'return the AltAltGr value
  128.                             Keys(i).Down = Keys(i).AltAltGr
  129.                         ELSEIF CtrlAltGr& <> 0 AND Keys(i).CtrlAltGr& <> 0 THEN 'return the CtrlAltGr& value
  130.                             Keys(i).Down = Keys(i).CtrlAltGr&
  131.                         ELSEIF ShiftAltGr <> 0 AND Keys(i).ShiftAltGr <> 0 THEN 'return the ShiftAltGr value
  132.                             Keys(i).Down = Keys(i).ShiftAltGr
  133.                         ELSEIF CtrlShift <> 0 AND Keys(i).CtrlShift <> 0 THEN 'return the CtrlShift value
  134.                             Keys(i).Down = Keys(i).CtrlShift
  135.                         ELSEIF AltCtrl <> 0 AND Keys(i).AltCtrl <> 0 THEN 'return the AltCtrl value
  136.                             Keys(i).Down = Keys(i).AltCtrl
  137.                         ELSEIF AltShift <> 0 AND Keys(i).AltShift <> 0 THEN 'return the AltShift value
  138.                             Keys(i).Down = Keys(i).AltShift
  139.                         ELSEIF AltGr <> 0 AND Keys(i).AltGr <> 0 THEN 'return the altgr value
  140.                             Keys(i).Down = Keys(i).AltGr
  141.                         ELSEIF Shift <> 0 AND Keys(i).Shift <> 0 THEN 'return the shift value
  142.                             Keys(i).Down = Keys(i).Shift
  143.                             IF _CAPSLOCK = 0 THEN 'caps lock basically reverses the behavior of the shift key with the letters A-Z and a-z
  144.                                 SELECT CASE i
  145.                                     CASE 65 TO 90: Keys(i).Down = Keys(i).ASCII
  146.                                 END SELECT
  147.                             END IF
  148.                         ELSEIF (Ctrl <> 0) AND (Keys(i).Ctrl <> 0) THEN 'return the ctrl value
  149.                             Keys(i).Down = Keys(i).Ctrl
  150.                         ELSEIF Alt <> 0 AND Keys(i).Alt <> 0 THEN 'return the alt value
  151.                             Keys(i).Down = Keys(i).Alt
  152.                         ELSE 'all that's left is to return the ASCII value
  153.                             Keys(i).Down = Keys(i).ASCII
  154.                             IF _CAPSLOCK = 0 THEN 'caps lock basically reverses the behavior of the shift key with the letters A-Z and a-z
  155.                                 SELECT CASE i
  156.                                     CASE 65 TO 90: Keys(i).Down = Keys(i).Shift
  157.                                 END SELECT
  158.                             END IF
  159.                         END IF
  160.                         ReturnCount = ReturnCount + 1 'add one to the return buffer
  161.                         ReturnValues(ReturnCount) = Keys(i).Down 'and store the value in the buffer
  162.  
  163.                         IF Keys(i).Repeat = -1 THEN 'keys that are set to a -1 on repeat simply toggle state as on, or off.
  164.                             Keys(i).LastHit = 1E+1000 'such as SHIFT, CTRL, ALT...
  165.                         ELSE
  166.                             Keys(i).LastHit = ExtendedTimer + Keys(i).Repeat 'and record when we hit it for repeat purposes
  167.                         END IF
  168.                     END IF
  169.                 END IF
  170.             ELSE
  171.                 IF Keys(i).Down THEN 'the key was down on the last pass
  172.                     ReturnCount = ReturnCount + 1
  173.                     ReturnValues(ReturnCount) = -Keys(i).Down 'mark it as being up on this one
  174.                 END IF
  175.                 Keys(i).Down = 0 'and set it back down for future passes
  176.                 Keys(i).LastHit = 0 'once again, set it as being ready to be hit again
  177.             END IF
  178.         NEXT
  179.  
  180.         IF ReturnCount > 0 THEN 'If we generated a cue of values last pass, clear those up first, before getting new values.
  181.             'The only time we really see this is when we hit a shift, ctrl, alt key, usually.
  182.             KeyHit = ReturnValues(1)
  183.             FOR i = 1 TO ReturnCount - 1
  184.                 ReturnValues(i) = ReturnValues(i + 1)
  185.             NEXT
  186.             ReturnCount = ReturnCount - 1
  187.             EXIT FUNCTION
  188.         END IF
  189.  
  190.     END IF 'End of IF _WINDOWHASFOCUS
  191.  
  192.  
  193.  
  194.  
  195. SUB Remap_KeyCode (Which AS LONG, ASCII AS LONG, Ctrl AS LONG, Shift AS LONG, Alt AS LONG, AltGr AS LONG, Repeat AS _FLOAT)
  196.     DIM i AS LONG
  197.     i = Which
  198.     Keys(i).Index = i
  199.     Keys(i).ASCII = ASCII
  200.     Keys(i).Ctrl = Ctrl
  201.     Keys(i).Shift = Shift
  202.     Keys(i).Alt = Alt
  203.     Keys(i).AltGr = AltGr
  204.     Keys(i).Repeat = Repeat
  205.     Keys(i).LastHit = 0
  206.     Keys(i).Down = 0
  207.  
  208. SUB Remap_Extended_KeyCode (Which&, AltShift&, AltCtrl&, AltAltGr&, _
  209.          CtrlShift&, CtrlAltGr&, ShiftAltGr&, CtrlAltShift&)
  210.     Keys(Which&).AltShift = AltShift&
  211.     Keys(Which&).AltCtrl = AltCtrl&
  212.     Keys(Which&).AltAltGr = AltAltGr&
  213.     Keys(Which&).CtrlShift = CtrlShift&
  214.     Keys(Which&).CtrlAltGr = CtrlAltGr&
  215.     Keys(Which&).ShiftAltGr = ShiftAltGr&
  216.     Keys(Which&).CtrlAltShift = CtrlAltShift&
  217.  
  218. FUNCTION KeyDown& (Code AS LONG)
  219.         IF Code <= 0 THEN EXIT FUNCTION
  220.         FOR i = 1 TO 254
  221.             IF GetAsyncKeyState(i) THEN 'first check for actual physical keys down
  222.                 IF Keys(i).ASCII = Code THEN KeyDown = -1: EXIT FUNCTION 'then check to see if the code matches anything we've mapped it to.
  223.                 IF Keys(i).Shift = Code THEN KeyDown = -1: EXIT FUNCTION
  224.                 IF Keys(i).Alt = Code THEN KeyDown = -1: EXIT FUNCTION
  225.                 IF Keys(i).AltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  226.                 IF Keys(i).AltShift = Code THEN KeyDown = -1: EXIT FUNCTION
  227.                 IF Keys(i).AltCtrl = Code THEN KeyDown = -1: EXIT FUNCTION
  228.                 IF Keys(i).AltAltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  229.                 IF Keys(i).CtrlShift = Code THEN KeyDown = -1: EXIT FUNCTION
  230.                 IF Keys(i).CtrlAltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  231.                 IF Keys(i).ShiftAltGr = Code THEN KeyDown = -1: EXIT FUNCTION
  232.                 IF Keys(i).CtrlAltShift = Code THEN KeyDown = -1: EXIT FUNCTION
  233.             END IF
  234.         NEXT
  235.         KeyDown& = 0
  236.     END IF
  237.  
  238. FUNCTION Physical_KeyDown& (Code AS LONG)
  239.         IF Code <= 0 THEN EXIT FUNCTION
  240.         FOR i = 1 TO 254
  241.             IF GetAsyncKeyState(i) THEN 'first check for actual physical keys down
  242.                 IF Keys(i).ASCII = Code THEN Physical_KeyDown = i: EXIT FUNCTION 'then check to see if the code matches anything we've mapped it to.
  243.                 IF Keys(i).Shift = Code THEN Physical_KeyDown = i: EXIT FUNCTION
  244.                 IF Keys(i).Alt = Code THEN Physical_KeyDown = i: EXIT FUNCTION
  245.                 IF Keys(i).AltGr = Code THEN Physical_KeyDown = i: EXIT FUNCTION
  246.                 IF Keys(i).AltShift = Code THEN Physical_KeyDown = i: EXIT FUNCTION
  247.                 IF Keys(i).AltCtrl = Code THEN Physical_KeyDown = i: EXIT FUNCTION
  248.                 IF Keys(i).AltAltGr = Code THEN Physical_KeyDown = i: EXIT FUNCTION
  249.                 IF Keys(i).CtrlShift = Code THEN Physical_KeyDown = i: EXIT FUNCTION
  250.                 IF Keys(i).CtrlAltGr = Code THEN Physical_KeyDown = i: EXIT FUNCTION
  251.                 IF Keys(i).ShiftAltGr = Code THEN Physical_KeyDown = i: EXIT FUNCTION
  252.                 IF Keys(i).CtrlAltShift = Code THEN Physical_KeyDown = i: EXIT FUNCTION
  253.             END IF
  254.         NEXT
  255.         Physical_KeyDown& = 0
  256.     END IF
  257.  
  258.  
  259. SUB Init_KeyCodes
  260.     RESTORE default_keyboard_data
  261.     FOR i = 1 TO 254
  262.         READ Keys(i).Index, Keys(i).ASCII, Keys(i).Ctrl, Keys(i).Shift, Keys(i).Alt, Keys(i).AltGr, Keys(i).Repeat
  263.         Keys(i).LastHit = 0: Keys(i).Down = 0
  264.     NEXT
  265.  
  266.     default_keyboard_data:
  267.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  268.     DATA 1,900001,0,0,0,0,0.2: 'Left Mouse Button
  269.     DATA 2,900002,0,0,0,0,0.2: 'Right Mouse Button
  270.     DATA 3,900003,0,0,0,0,0.2: 'VK_Cancel
  271.     DATA 4,900004,0,0,0,0,0.2: 'Middle Mouse Button
  272.     DATA 5,900005,0,0,0,0,0.2: 'Mouse Button 4
  273.     DATA 6,900006,0,0,0,0,0.2: 'Mouse Button 5
  274.     DATA 7,900007,0,0,0,0,0.2: 'Undefined
  275.     DATA 8,8,0,0,0,0,0.2: 'Backspace
  276.     DATA 9,9,0,0,0,0,0.2: 'Tab
  277.     DATA 10,900010,0,0,0,0,0.2: 'Reserved
  278.     DATA 11,900011,0,0,0,0,0.2: 'Reserved
  279.     DATA 12,19456,0,0,0,0,0.2: 'Clear
  280.     DATA 13,13,0,0,0,0,0.2: 'Enter
  281.     DATA 14,900014,0,0,0,0,0.2: 'Undefined
  282.     DATA 15,900015,0,0,0,0,0.2: 'Undefined
  283.     DATA 16,100016,0,0,0,0,-1: 'Shift (Notice I set it to simple toddle and report UP/DOWN results for us)
  284.     DATA 17,100017,0,0,0,0,-1: 'Ctrl   (Same)
  285.     DATA 18,100018,0,0,0,0,-1: 'Alt     (Same)
  286.     DATA 19,100019,0,0,0,0,0.2: 'Pause
  287.     DATA 20,100301,0,0,0,0,-1: 'Caps Lock
  288.     DATA 21,900021,0,0,0,0,0.2: 'VK_Hangul
  289.     DATA 22,900022,0,0,0,0,0.2: 'Undefined
  290.     DATA 23,900023,0,0,0,0,0.2: 'VK_Junja
  291.     DATA 24,900024,0,0,0,0,0.2: 'VK_Final
  292.     DATA 25,900025,0,0,0,0,0.2: 'VK_Hanga//VK_Kanji
  293.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  294.     DATA 26,900026,0,0,0,0,0.2: 'Undefined
  295.     DATA 27,27,0,0,0,0,0.2: 'ESC
  296.     DATA 28,900028,0,0,0,0,0.2: 'VK_Convert
  297.     DATA 29,900029,0,0,0,0,0.2: 'VK_NonConvert
  298.     DATA 30,900030,0,0,0,0,0.2: 'VK_Accept
  299.     DATA 31,900031,0,0,0,0,0.2: 'VK_ModeChange
  300.     DATA 32,32,0,0,0,0,0.2: 'VK_Space
  301.     DATA 33,18688,0,0,0,0,0.2: 'Page Up
  302.     DATA 34,20736,0,0,0,0,0.2: 'Page Down
  303.     DATA 35,20224,0,0,0,0,0.2: 'End
  304.     DATA 36,18176,0,0,0,0,0.2: 'Home
  305.     DATA 37,19200,0,0,0,0,0.2: 'Left Arrow
  306.     DATA 38,18432,0,0,0,0,0.2: 'Up Arrow
  307.     DATA 39,19712,0,0,0,0,0.2: 'Right Arrow
  308.     DATA 40,20480,0,0,0,0,0.2: 'Down Arrow
  309.     DATA 41,900041,0,0,0,0,-1: 'VK_SELECT
  310.     DATA 42,900042,0,0,0,0,-1: 'CK_PRINT
  311.     DATA 43,900043,0,0,0,0,-1: 'VK_EXECUTE
  312.     DATA 44,900044,0,0,0,0,-1: 'VK_SNAPSHOT
  313.     DATA 45,20992,0,0,0,0,0.2: 'INS
  314.     DATA 46,21248,0,0,0,0,0.2: 'DEL
  315.     DATA 47,900047,0,0,0,0,0.2: 'VK_HELP
  316.     DATA 48,48,0,41,0,0,0.2: '0
  317.     DATA 49,49,0,33,0,0,0.2: '1
  318.     DATA 50,50,0,64,0,0,0.2: '2
  319.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  320.     DATA 51,51,0,35,0,0,0.2: '3
  321.     DATA 52,52,0,36,0,0,0.2: '4
  322.     DATA 53,53,0,37,0,0,0.2: '5
  323.     DATA 54,54,0,94,0,0,0.2: '6
  324.     DATA 55,55,0,38,0,0,0.2: '7
  325.     DATA 56,56,0,42,0,0,0.2: '8
  326.     DATA 57,57,0,40,0,0,0.2: '9
  327.     DATA 58,900058,0,0,0,0,0.2: 'Undefined
  328.     DATA 59,900059,0,0,0,0,0.2: 'Undefined
  329.     DATA 60,900060,0,0,0,0,0.2: 'Undefined
  330.     DATA 61,900061,0,0,0,0,0.2: 'Undefined
  331.     DATA 62,900062,0,0,0,0,0.2: 'Undefined
  332.     DATA 63,900063,0,0,0,0,0.2: 'Undefined
  333.     DATA 64,900064,0,0,0,0,0.2: 'Undefined
  334.     DATA 65,65,0,97,0,0,0.2: 'a
  335.     DATA 66,66,0,98,0,0,0.2: 'b
  336.     DATA 67,67,0,99,0,0,0.2: 'c
  337.     DATA 68,68,0,100,0,0,0.2: 'd
  338.     DATA 69,69,0,101,0,0,0.2: 'e
  339.     DATA 70,70,0,102,0,0,0.2: 'f
  340.     DATA 71,71,0,103,0,0,0.2: 'g
  341.     DATA 72,72,0,104,0,0,0.2: 'h
  342.     DATA 73,73,0,105,0,0,0.2: 'i
  343.     DATA 74,74,0,106,0,0,0.2: 'j
  344.     DATA 75,75,0,107,0,0,0.2: 'k
  345.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  346.     DATA 76,76,0,108,0,0,0.2: 'l
  347.     DATA 77,77,0,109,0,0,0.2: 'm
  348.     DATA 78,78,0,110,0,0,0.2: 'n
  349.     DATA 79,79,0,111,0,0,0.2: 'o
  350.     DATA 80,80,0,112,0,0,0.2: 'p
  351.     DATA 81,81,0,113,0,0,0.2: 'q
  352.     DATA 82,82,0,114,0,0,0.2: 'r
  353.     DATA 83,83,0,115,0,0,0.2: 's
  354.     DATA 84,84,0,116,0,0,0.2: 't
  355.     DATA 85,85,0,117,0,0,0.2: 'u
  356.     DATA 86,86,0,118,0,0,0.2: 'v
  357.     DATA 87,87,0,119,0,0,0.2: 'w
  358.     DATA 88,88,0,120,0,0,0.2: 'x
  359.     DATA 89,89,0,121,0,0,0.2: 'y
  360.     DATA 90,90,0,122,0,0,0.2: 'z
  361.     DATA 91,100311,0,0,0,0,-1: 'Left WIN
  362.     DATA 92,100312,0,0,0,0,-1: 'Right WIN
  363.     DATA 93,100319,0,0,0,0,-1: 'Applications (Menu)
  364.     DATA 94,900094,0,0,0,0,0.2: 'Reserved
  365.     DATA 95,900095,0,0,0,0,0.2: 'VK_SLEEP
  366.     DATA 96,48,0,0,0,0,0.2: 'Numpad 0
  367.     DATA 97,49,0,0,0,0,0.2: 'Numpad 1
  368.     DATA 98,50,0,0,0,0,0.2: 'Numpad 2
  369.     DATA 99,51,0,0,0,0,0.2: 'Numpad 3
  370.     DATA 100,52,0,0,0,0,0.2: 'Numpad 4
  371.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  372.     DATA 101,53,0,0,0,0,0.2: 'Numpad 5
  373.     DATA 102,54,0,0,0,0,0.2: 'Numpad 6
  374.     DATA 103,55,0,0,0,0,0.2: 'Numpad 7
  375.     DATA 104,56,0,0,0,0,0.2: 'Numpad 8
  376.     DATA 105,57,0,0,0,0,0.2: 'Numpad 9
  377.     DATA 106,42,0,0,0,0,0.2: 'Numpad *
  378.     DATA 107,43,0,0,0,0,0.2: 'Numpad +
  379.     DATA 108,900108,0,0,0,0,0.2: 'VK_SEPARATOR
  380.     DATA 109,51,0,0,0,0,0.2: 'Numpad -
  381.     DATA 110,52,0,0,0,0,0.2: 'Numpad .
  382.     DATA 111,53,0,0,0,0,0.2: 'Numpad /
  383.     DATA 112,15104,0,0,0,0,0.2: 'F1
  384.     DATA 113,15360,0,0,0,0,0.2: 'F2
  385.     DATA 114,15616,0,0,0,0,0.2: 'F3
  386.     DATA 115,15872,0,0,0,0,0.2: 'F4
  387.     DATA 116,16128,0,0,0,0,0.2: 'F5
  388.     DATA 117,16384,0,0,0,0,0.2: 'F6
  389.     DATA 118,16640,0,0,0,0,0.2: 'F7
  390.     DATA 119,16896,0,0,0,0,0.2: 'F8
  391.     DATA 120,17152,0,0,0,0,0.2: 'F9
  392.     DATA 121,17408,0,0,0,0,0.2: 'F10
  393.     DATA 122,34048,0,0,0,0,0.2: 'F11
  394.     DATA 123,34304,0,0,0,0,0.2: 'F12
  395.     DATA 124,900124,0,0,0,0,0.2: 'F13
  396.     DATA 125,900125,0,0,0,0,0.2: 'F14
  397.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  398.     DATA 126,900126,0,0,0,0,0.2: 'F15
  399.     DATA 127,900127,0,0,0,0,0.2: 'F16
  400.     DATA 128,900128,0,0,0,0,0.2: 'F17
  401.     DATA 129,900129,0,0,0,0,0.2: 'F18
  402.     DATA 130,900130,0,0,0,0,0.2: 'F19
  403.     DATA 131,900131,0,0,0,0,0.2: 'F20
  404.     DATA 132,900132,0,0,0,0,0.2: 'F21
  405.     DATA 133,900133,0,0,0,0,0.2: 'F22
  406.     DATA 134,900134,0,0,0,0,0.2: 'F23
  407.     DATA 135,900135,0,0,0,0,0.2: 'F24
  408.     DATA 136,900136,0,0,0,0,0.2: 'Unassigned
  409.     DATA 137,900137,0,0,0,0,0.2: 'Unassigned
  410.     DATA 138,900138,0,0,0,0,0.2: 'Unassigned
  411.     DATA 139,900139,0,0,0,0,0.2: 'Unassigned
  412.     DATA 140,900140,0,0,0,0,0.2: 'Unassigned
  413.     DATA 141,900141,0,0,0,0,0.2: 'Unassigned
  414.     DATA 142,900142,0,0,0,0,0.2: 'Unassigned
  415.     DATA 143,900143,0,0,0,0,0.2: 'Unassigned
  416.     DATA 144,100300,0,0,0,0,-1: 'NUM LOCK
  417.     DATA 145,100302,0,0,0,0,-1: 'SCROLL LOCK
  418.     DATA 146,900146,0,0,0,0,0.2: 'OEM SPECIFIC
  419.     DATA 147,900147,0,0,0,0,0.2: 'OEM SPECIFIC
  420.     DATA 148,900148,0,0,0,0,0.2: 'OEM SPECIFIC
  421.     DATA 149,900149,0,0,0,0,0.2: 'OEM SPECIFIC
  422.     DATA 150,900150,0,0,0,0,0.2: 'OEM SPECIFIC
  423.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  424.     DATA 151,900151,0,0,0,0,0.2: 'Unassigned
  425.     DATA 152,900152,0,0,0,0,0.2: 'Unassigned
  426.     DATA 153,900153,0,0,0,0,0.2: 'Unassigned
  427.     DATA 154,900154,0,0,0,0,0.2: 'Unassigned
  428.     DATA 155,900155,0,0,0,0,0.2: 'Unassigned
  429.     DATA 156,900156,0,0,0,0,0.2: 'Unassigned
  430.     DATA 157,900157,0,0,0,0,0.2: 'Unassigned
  431.     DATA 158,900158,0,0,0,0,0.2: 'Unassigned
  432.     DATA 159,900159,0,0,0,0,0.2: 'Unassigned
  433.     DATA 160,100304,0,0,0,0,-1: 'Left Shift
  434.     DATA 161,100303,0,0,0,0,-1: 'Right Shift
  435.     DATA 162,100306,0,0,0,0,-1: 'Left Control
  436.     DATA 163,100305,0,0,0,0,-1: 'Right Control
  437.     DATA 164,100308,0,0,0,0,-1: 'Left Alt
  438.     DATA 165,100309,0,0,0,0,-1: 'Right Alt
  439.     DATA 166,900166,0,0,0,0,0.2: 'Browser back
  440.     DATA 167,900167,0,0,0,0,0.2: 'Browser forward
  441.     DATA 168,900168,0,0,0,0,0.2: 'Browser refresh
  442.     DATA 169,900169,0,0,0,0,0.2: 'Browser stop
  443.     DATA 170,900170,0,0,0,0,0.2: 'Browser search
  444.     DATA 171,900171,0,0,0,0,0.2: 'Browser favorites
  445.     DATA 172,900172,0,0,0,0,0.2: 'Browser home
  446.     DATA 173,900173,0,0,0,0,0.2: 'Mute
  447.     DATA 174,900174,0,0,0,0,0.2: 'Vol Down
  448.     DATA 175,900175,0,0,0,0,0.2: 'Vol Up
  449.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  450.     DATA 176,900176,0,0,0,0,0.2: 'Media Next
  451.     DATA 177,900177,0,0,0,0,0.2: 'Media prev
  452.     DATA 178,900178,0,0,0,0,0.2: 'Media stop
  453.     DATA 179,900179,0,0,0,0,0.2: 'Media Play/Pause
  454.     DATA 180,900180,0,0,0,0,0.2: 'Launch mail
  455.     DATA 181,900181,0,0,0,0,0.2: 'Launch media select
  456.     DATA 182,900182,0,0,0,0,0.2: 'Launch app1
  457.     DATA 183,900183,0,0,0,0,0.2: 'Launch app2
  458.     DATA 184,900184,0,0,0,0,0.2: 'Reserved
  459.     DATA 185,900185,0,0,0,0,0.2: 'Reserved
  460.     DATA 186,59,0,58,0,0,0.2: ';:
  461.     DATA 187,61,0,43,0,0,0.2: '=+
  462.     DATA 188,44,0,60,0,0,0.2: ',<
  463.     DATA 189,45,0,95,0,0,0.2: '-_
  464.     DATA 190,46,0,62,0,0,0.2: '.>
  465.     DATA 191,47,0,63,0,0,0.2: '/?
  466.     DATA 192,96,0,126,0,0,0.2: '`~
  467.     DATA 193,900193,0,0,0,0,0.2: 'Reserved
  468.     DATA 194,900194,0,0,0,0,0.2: 'Reserved
  469.     DATA 195,900195,0,0,0,0,0.2: 'Reserved
  470.     DATA 196,900196,0,0,0,0,0.2: 'Reserved
  471.     DATA 197,900197,0,0,0,0,0.2: 'Reserved
  472.     DATA 198,900198,0,0,0,0,0.2: 'Reserved
  473.     DATA 199,900199,0,0,0,0,0.2: 'Reserved
  474.     DATA 200,900200,0,0,0,0,0.2: 'Reserved
  475.     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat
  476.     DATA 201,900201,0,0,0,0,0.2: 'Reserved
  477.     DATA 202,900202,0,0,0,0,0.2: 'Reserved
  478.     DATA 203,900203,0,0,0,0,0.2: 'Reserved
  479.     DATA 204,900204,0,0,0,0,0.2: 'Reserved
  480.     DATA 205,900205,0,0,0,0,0.2: 'Reserved
  481.     DATA 206,900206,0,0,0,0,0.2: 'Reserved
  482.     DATA 207,900207,0,0,0,0,0.2: 'Reserved
  483.     DATA 208,900208,0,0,0,0,0.2: 'Reserved
  484.     DATA 209,900209,0,0,0,0,0.2: 'Reserved
  485.     DATA 210,900210,0,0,0,0,0.2: 'Reserved
  486.     DATA 211,900211,0,0,0,0,0.2: 'Reserved
  487.     DATA 212,900212,0,0,0,0,0.2: 'Reserved
  488.     DATA 213,900213,0,0,0,0,0.2: 'Reserved
  489.     DATA 214,900214,0,0,0,0,0.2: 'Reserved
  490.     DATA 215,900215,0,0,0,0,0.2: 'Reserved
  491.     DATA 216,900216,0,0,0,0,0.2: 'Unassigned
  492.     DATA 217,900217,0,0,0,0,0.2: 'Unassigned
  493.     DATA 218,900218,0,0,0,0,0.2: 'Unassigned
  494.     DATA 219,91,0,123,0,0,0.2: '[{
  495.     DATA 220,92,0,124,0,0,0.2: '\|
  496.     DATA 221,93,0,125,0,0,0.2: ']}
  497.     DATA 222,39,0,34,0,0,0.2: ''"
  498.     DATA 223,900223,0,0,0,0,0.2: 'OEM SPECIFIC
  499.     DATA 224,900224,0,0,0,0,0.2: 'Reserved
  500.     DATA 225,900225,0,0,0,0,0.2: 'OEM SPECIFIC d
  501.     DATA 226,900226,0,0,0,0,0.2: 'Either the Angle Bracket key,or Backslash on RT 102-key keyboard
  502.     DATA 227,900227,0,0,0,0,0.2: 'OEM SPECIFIC
  503.     DATA 228,900228,0,0,0,0,0.2: 'OEM SPECIFIC
  504.     DATA 229,900229,0,0,0,0,0.2: 'IME PROCESS key (whatever that is)
  505.     DATA 230,900230,0,0,0,0,0.2: 'OEM SPECIFIC
  506.     DATA 231,900231,0,0,0,0,0.2: 'Used to pass UNICODE characters (however that works)
  507.     DATA 232,900232,0,0,0,0,0.2: 'Unassigned
  508.     DATA 233,900233,0,0,0,0,0.2: 'OEM SPECIFIC
  509.     DATA 234,900234,0,0,0,0,0.2: 'OEM SPECIFIC
  510.     DATA 235,900235,0,0,0,0,0.2: 'OEM SPECIFIC
  511.     DATA 236,900236,0,0,0,0,0.2: 'OEM SPECIFIC
  512.     DATA 237,900237,0,0,0,0,0.2: 'OEM SPECIFIC
  513.     DATA 238,900238,0,0,0,0,0.2: 'OEM SPECIFIC
  514.     DATA 239,900239,0,0,0,0,0.2: 'OEM SPECIFIC
  515.     DATA 240,900240,0,0,0,0,0.2: 'OEM SPECIFIC
  516.     DATA 241,900241,0,0,0,0,0.2: 'OEM SPECIFIC
  517.     DATA 242,900242,0,0,0,0,0.2: 'OEM SPECIFIC
  518.     DATA 243,900243,0,0,0,0,0.2: 'OEM SPECIFIC
  519.     DATA 244,900244,0,0,0,0,0.2: 'OEM SPECIFIC
  520.     DATA 245,900245,0,0,0,0,0.2: 'OEM SPECIFIC
  521.     DATA 246,900246,0,0,0,0,0.2: 'VK_ATTN
  522.     DATA 247,900247,0,0,0,0,0.2: 'VK_ATTN
  523.     DATA 248,900248,0,0,0,0,0.2: 'VK_ATTN
  524.     DATA 249,900249,0,0,0,0,0.2: 'VK_ATTN
  525.     DATA 250,900250,0,0,0,0,0.2: 'VK_ATTN
  526.     DATA 251,900251,0,0,0,0,0.2: 'VK_ATTN
  527.     DATA 252,900252,0,0,0,0,0.2: 'Reserved
  528.     DATA 253,900253,0,0,0,0,0.2: 'VK_PA1
  529.     DATA 254,900253,0,0,0,0,0.2: 'VK_OEM_CLEAR
  530.     DATA 0,0,0,0,0,0,0.2: 'END OF DATA
  531.     AltGr(0) = 165
  532.     AltGr(1) = 0
  533.  
  534.  
  535. FUNCTION ExtendedTimer##
  536.     'modified extendedtimer to store the old day's count, and not have to recalculate it every time the routine is called.
  537.  
  538.     STATIC olds AS _FLOAT, old_day AS _FLOAT
  539.     DIM m AS INTEGER, d AS INTEGER, y AS INTEGER
  540.     DIM s AS _FLOAT, day AS STRING
  541.     IF olds = 0 THEN 'calculate the day the first time the extended timer runs
  542.         day = DATE$
  543.         m = VAL(LEFT$(day, 2))
  544.         d = VAL(MID$(day, 4, 2))
  545.         y = VAL(RIGHT$(day, 4)) - 1970
  546.         SELECT CASE m 'Add the number of days for each previous month passed
  547.             CASE 2: d = d + 31
  548.             CASE 3: d = d + 59
  549.             CASE 4: d = d + 90
  550.             CASE 5: d = d + 120
  551.             CASE 6: d = d + 151
  552.             CASE 7: d = d + 181
  553.             CASE 8: d = d + 212
  554.             CASE 9: d = d + 243
  555.             CASE 10: d = d + 273
  556.             CASE 11: d = d + 304
  557.             CASE 12: d = d + 334
  558.         END SELECT
  559.         IF (y MOD 4) = 2 AND m > 2 THEN d = d + 1 'add a day if this is leap year and we're past february
  560.         d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
  561.         d = d + (y + 2) \ 4 'add in days for leap years passed
  562.         s = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  563.         old_day = s
  564.     END IF
  565.     IF TIMER < oldt THEN 'we went from 23:59:59 (a second before midnight) to 0:0:0 (midnight)
  566.         old_day = s + 83400 'add another worth of seconds to our counter
  567.     END IF
  568.     oldt = TIMER
  569.     olds = old_day + oldt
  570.     ExtendedTimer## = olds
  571.  
  572.  
  573.  
  574.  
  575.  
  576.  
  577. FUNCTION ExtendedInput$
  578.     SHARED AltGr, Alt, Shift, Ctrl
  579.     PCOPY 0, 1
  580.     A = _AUTODISPLAY: X = POS(0): Y = CSRLIN
  581.     CP = 0: OldCP = 0 'Cursor Position
  582.     _KEYCLEAR
  583.     DO
  584.         PCOPY 1, 0
  585.  
  586.         k = KeyHit
  587.         IF Alt THEN AltDown = -1 ELSE AltDown = 0
  588.         SELECT CASE k 'without alt, add any keypresses to our input
  589.             CASE 8
  590.                 oldin$ = in$
  591.                 IF CP > 0 THEN OldCP = CP: CP = CP - 1
  592.                 in$ = LEFT$(in$, CP) + MID$(in$, CP + 2) 'backspace to erase input
  593.             CASE 9
  594.                 oldin$ = in$
  595.                 in$ = LEFT$(in$, CP) + SPACE$(4) + MID$(in$, CP + 1) 'four spaces for any TAB entered
  596.                 OldCP = CP
  597.                 CP = CP + 4
  598.             CASE 48 TO 57 '0 to 9
  599.                 IF AltDown THEN
  600.                     AltWasDown = -1: alt$ = alt$ + CHR$(k)
  601.                 ELSE
  602.                     oldin$ = in$
  603.                     in$ = LEFT$(in$, CP) + CHR$(k) + MID$(in$, CP + 1) 'add input to our string
  604.                     OldCP = CP
  605.                     CP = CP + 1
  606.                 END IF
  607.             CASE 1 TO 255 'the rest of the ASCII characters
  608.                 IF Ctrl AND (k = 118 OR k = 86) THEN
  609.                     oldin$ = in$
  610.                     in$ = LEFT$(in$, CP) + _CLIPBOARD$ + MID$(in$, CP + 1) 'ctrl-v paste
  611.                     'CTRL-V leaves cursor in position before the paste, without moving it after.
  612.                     'Feel free to modify that behavior here, if you want it to move to after the paste.
  613.                 ELSEIF Ctrl AND (k = 122 OR k = 90) THEN
  614.                     SWAP in$, oldin$: SWAP OldCP, CP 'ctrl-z undo
  615.                 ELSE
  616.                     oldin$ = in$
  617.                     in$ = LEFT$(in$, CP) + CHR$(k) + MID$(in$, CP + 1) 'add input to our string
  618.                     OldCP = CP
  619.                     CP = CP + 1
  620.                 END IF
  621.             CASE 18176 'Home
  622.                 CP = 0
  623.             CASE 20224 'End
  624.                 CP = LEN(in$)
  625.             CASE 21248 'Delete
  626.                 oldin$ = in$
  627.                 in$ = LEFT$(in$, CP) + MID$(in$, CP + 2)
  628.             CASE 19200 'Left
  629.                 CP = CP - 1
  630.                 IF CP < 0 THEN CP = 0
  631.             CASE 19712 'Right
  632.                 CP = CP + 1
  633.                 IF CP > LEN(in$) THEN CP = LEN(in$)
  634.         END SELECT
  635.  
  636.         alt$ = RIGHT$(alt$, 3)
  637.         IF AltWasDown = -1 AND AltDown = 0 THEN
  638.             v = VAL(alt$)
  639.             IF v >= 0 AND v <= 255 THEN in$ = in$ + CHR$(v): CP = CP + 1
  640.             alt$ = "": AltWasDown = 0
  641.         END IF
  642.         blink = (blink + 1) MOD 30
  643.         LOCATE Y, X
  644.         PRINT LEFT$(in$, CP);
  645.         IF blink \ 15 THEN PRINT " "; ELSE PRINT "_";
  646.         PRINT MID$(in$, CP + 1)
  647.  
  648.         _DISPLAY
  649.         _LIMIT 30
  650.     LOOP UNTIL k = 13
  651.  
  652.     PCOPY 1, 0
  653.     LOCATE Y, X: PRINT in$
  654.     ExtendedInput$ = in$
  655.  

Step 0: (This may be optional, depending on how you manage things.)

After the DIM SHARED AltGr(1) AS _UNSIGNED _BYTE, and before the Init_KeyCodes, you may want to use the _MAPUNICODE values to set up the proper code page to view and display your characters properly.  (If you need help with this step, let me know, but I figure anyone who uses _MAPUNICODE regularly shouldn't have any issues with using it to set the proper character page.)

Step 1: Run the program and test it out with your keyboard.  Whenever you find a key/combination which doesn't work properly, write down the physical keycode on a piece of paper, along with the symbol which that key should actually represent.  (Make a note of modifier keys, if needed, such as SHIFT or ALTGR, as well.)

Step 2: Once you have a list of keys which you want to remap, copy the whole SUB Init_KeyCodes, and paste a duplicate of it into the IDE.  Rename that duplicate to Init_IT_KeyCodes (for the Italian Keycodes; use a different abbreviation for a different language).

Step 3: Just scroll down the list and change the values that you need to, for the keys which you made a list of above.

For example, let's say Physical Key 186 is the less than "<" sign on your keyboard, rather than the semicolon ";".  The old line in question looks like this:
Code: [Select]
    DATA 186,59,0,58,0,0,0.2: ';:   
Those values represent:     '   Index   Unmodified      Ctrl      Shift       Alt         AltGr     Repeat

Semi-colon is ASCII character 59.  Colon is ASCII character 58.  As you can see, those represent the Unmodified keydown and the Shift-keydown code for that physical key.

Just look at your ASCII chart for your particular code page, and plug in the values that match the character your keyboard should've actually produced.

In this example, less than "<" is normally CHR$(60), so all you'd do is change that line to become:

    DATA 186,60,0,58,0,0,0.2: ';: 

That 59 became a 60.  Now, when you hit that  key, it'll display "<" instead of ";".

Step 4: Repeat with the rest of the physical keys which didn't match anything currently mapped.



And that's all there is to it! 

It's nothing very complicated, but it does require a human to sit down, stare at their personal keyboard, and then look up the proper ASCII values for their character page and plug them into the mapper at least once. 

Once somebody maps their values for us, I'll add them to the program and we'll collect a library that people can just pick and choose from, without having to remap everything personally, every time they want to use the routine.  All we need is someone to map things once to their physical keyboard layout, and then that mapping can be used by other people with that same language/keyboard forever more!!



And here's to hoping that several different people will remap their keyboards, and share them, for us.  :)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Programmable Keyboard Input
« Reply #28 on: January 05, 2020, 08:22:44 pm »
Hi Steve
just another silly question:
Is the default_keyboard_data  the MS CP1252 ?
Programming isn't difficult, only it's  consuming time and coffee

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Programmable Keyboard Input
« Reply #29 on: January 05, 2020, 10:05:57 pm »
Hi Steve
just another silly question:
Is the default_keyboard_data  the MS CP1252 ?

QB64 uses the default character page 437, unless you change yours using MAPUNICODE to map a different page to use/display.  https://www.qb64.org/wiki/Code_Pages#CP_437
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!