'===========================================================================
' Subject: DETECT KEYDOWN & KEYUP EVENTS      Date: 04-11-96 (18:17)       
'  Author: Eric Benson                        Code: QB, QBasic, PDS        
'  Origin: comp.lang.basic.misc             Packet: KEYBOARD.ABC
'===========================================================================
'This routine I wrote to detect keydown and keyup events works great (using 
'INP(&H60) as someone suggested a while back). To get the ascii of the key that
'was pressed I am using INKEY$.  It sometimes returns zero, I guess the BIOS
'hasn't translated the key when I call INKEY$.  But if you run the program
'below you may notice a fairly substantial (0.5 sec) delay sometimes.  
'Suggestions to get around this?
'I figure I could load an array with ascii codes and index it with the scancode 
'i.e. ascii=scan2ascii(scancode), but I would still need to take care of the 
'shift key.
'Or maybe I should run this in straight DOS and not in a Win95 DOS box.

'Program Keypress.bas by Eric Benson - April 1996
DEFINT A-Z
DECLARE SUB KernelKeyPress (scancode AS INTEGER, ascii AS INTEGER, keystate AS INTEGER)
REM DECLARE SUB BYTECOPY (SEG SOURCE AS ANY, SEG DESTINATION AS ANY, NUMBYTES&)
CONST TRUE = -1, FALSE = 0
CONST keybuffermax = 10, kdown = 1, kpressed = 2, kup = 3
TYPE keyvar
  keyin AS INTEGER
  keyup AS INTEGER
  scancode AS INTEGER
  ascii AS INTEGER
  extended AS INTEGER
END TYPE

CLS
REM GOTO test
DO
  keystate = 0
  KernelKeyPress scancode, ascii, keystate
  IF keystate = kdown THEN
    PRINT "keydown     "; scancode, ascii
  ELSEIF keystate = kup THEN
    PRINT "keyup       "; scancode, ascii
  END IF
LOOP UNTIL scancode = 1
END

test:
'used to look at what the keyboard is outputing
DO
  keyin = INP(&H60)
'  LOCATE 1, 1: PRINT "    "
  LOCATE 1, 1: PRINT keyin
  b$ = INKEY$
LOOP UNTIL keyin = 1
END

SUB KernelKeyPress (scancode AS INTEGER, ascii AS INTEGER, keystate AS INTEGER)
STATIC init AS INTEGER, buffer() AS keyvar
STATIC ibuf AS INTEGER, pressedkey AS INTEGER
IF init THEN
  keyin = INP(&H60)
  b$ = INKEY$
  'Warning inkey$ above does not always trap the keystroke
  IF keyin > 128 AND pressedkey THEN
    FOR i = 1 TO ibuf
      IF keyin = 170 OR buffer(i).keyup = keyin THEN
        scancode = buffer(i).scancode
        ascii = buffer(i).ascii
        keystate = kup
        IF ibuf = 1 THEN
          pressedkey = 0
        ELSEIF i < ibuf THEN
          pressedkey = buffer(i).keyup
          FOR j = i TO ibuf - 1
            SWAP buffer(j), buffer(j + 1)
          NEXT
          REM BYTECOPY buffer(i + 1), buffer(i), (ibuf - i) * 10
        ELSE
          'top key in buffer is unpressed
          pressedkey = buffer(i).keyup
        END IF
        ibuf = ibuf - 1
        EXIT SUB
      END IF
    NEXT
  END IF
  IF pressedkey <> keyin AND keyin < 129 THEN
    IF ibuf THEN
      FOR i = 1 TO ibuf
        IF buffer(i).keyin = keyin THEN EXIT SUB
      NEXT
    END IF
    IF ibuf < keybuffermax THEN
      ibuf = ibuf + 1
      buffer(ibuf).keyin = keyin
      buffer(ibuf).keyup = keyin + 128
     
      'Remove this IF..ENDIF block for normal execution
      'Will hang if shift is pressed
      IF LEN(b$) = 0 THEN
        DO
          b$ = INKEY$
        LOOP UNTIL LEN(b$)
      END IF
     
      IF LEN(b$) = 2 THEN
        buffer(ibuf).extended = ASC(RIGHT$(b$, 1))
      ELSEIF LEN(b$) THEN
        buffer(ibuf).ascii = ASC(b$)
      ELSE
        buffer(ibuf).ascii = 0
      END IF
      buffer(ibuf).scancode = keyin
      pressedkey = keyin
      scancode = keyin
      ascii = buffer(ibuf).ascii
      keystate = kdown
      EXIT SUB
    END IF
  END IF
ELSE
  DIM buffer(1 TO keybuffermax) AS keyvar
  ibuf = 0
  pressedkey = 0
  init = TRUE
END IF
END SUB
