Day 023: INKEY$
#1
INKEY$ is one of several methods to communicate with the keyboard. Others are _KEYHIT, _KEYDOWN, INP(), PEEK and POKE, and for Windows users, the function GetAsyncKeyState%.

So why is INKEY$ so much better than those other choices, why because I code with it, of course!

Well, my comedy bit aside, INKEY$ is comfortable for me because it has easily recognized key associations, by string representation of ASCII values.

Terminate loop with the Esc key example...
Code: (Select All)
DO
_LIMIT 30
LOOP UNTIL INKEY$ = CHR$(27) ' The string character for ASCII code 27.

Since INKEY$ functions as a string, unlike the other alternatives, we need recognize a string is true or false by being either filled or null.

Terminate loop with press almost any key...
Code: (Select All)
DO
_LIMIT 30
LOOP UNTIL LEN(INKEY$) ' Loop exits when INKEY$ is no longer null, (e.g. INKEY$ = "").

I find the best way to set up a keyboard poll routine is by assigning a variable to INKEY$...

Code: (Select All)
DO
    _LIMIT 30
    mykey$ = INKEY$
    IF LEN(mykey$) THEN
        SELECT CASE mykey$
            CASE CHR$(27)
                PRINT " INKEY$ CODE: CHR$(27)"
                EXIT DO ' Escape loop to end program snippet.
            CASE ELSE
                show_Values (mykey$)
        END SELECT
    END IF
LOOP

SUB show_Values (mykey$)
    SELECT CASE LEN(mykey$)
        CASE 1
            b = ASC(mykey$) ' ASC() converts a string character to a numeric value.
            a$ = ""
        CASE 2
            b = ASC(MID$(mykey$, 2, 1))
            a$ = "CHR$(0)" ' This is the nul character INKEY$ reports for 2 byte length key representation like the F1 - F12 keys.
    END SELECT
    b$ = LTRIM$(STR$(b)) ' This is how you convert a numeric variable to a string variable.
        PRINT " INKEY$ CODE: ";
        IF LEN(a$) THEN PRINT a$; " + ";
        PRINT "CHR$("; b$; ") "; "AKA: " + CHR$(34) + CHR$(b) + CHR$(34) + " ";
END SUB

"...press almost any key." WTH is that???

Well, INKEY$ does not register for certain keys like...

Shift
Ctrl
Alt
Fn
Windows key
PrtScr
NumLock
Capslock

These keys need to be referenced in some other either supportive method or via a different key recognition keyword like _KEYDOWN.

An "old school" method I love, and successfully lobbied for 15+ years ago, is the supportive method of using PEEK addresses.

This routine identifies if Alt, Ctrl, or Shift keys are held down while other keys are pressed...
Code: (Select All)
_CONTROLCHR OFF '  Allows us to key input combinations that would otherwise adversely affect screen output.
DO
    _LIMIT 30
    DEF SEG = 0
    IF PEEK(1047) MOD 16 = 1 OR PEEK(1047) MOD 16 = 2 THEN
        KeyCombos = 1 ' Shift
    ELSEIF PEEK(1047) MOD 16 = 3 OR PEEK(1047) MOD 16 = 4 THEN
        KeyCombos = 2 ' Ctrl
    ELSEIF PEEK(1047) MOD 16 = 5 OR PEEK(1047) MOD 16 = 6 THEN
        KeyCombos = 3 ' Ctrl+Shift
    ELSEIF PEEK(1047) MOD 16 = 7 OR PEEK(1047) MOD 16 = 8 THEN
        KeyCombos = 4 ' Alt
    ELSEIF PEEK(1047) MOD 16 = 9 OR PEEK(1047) MOD 16 = 10 THEN
        KeyCombos = 5 ' Shift+Alt
    ELSEIF PEEK(1047) MOD 16 = 12 THEN
        KeyCombos = 6 ' Ctrl+Alt
    ELSEIF PEEK(1047) MOD 16 = 14 THEN
        KeyCombos = 7 ' Shift+Ctrl+Alt
    ELSE
        KeyCombos = 0
    END IF
    DEF SEG

    mykey$ = INKEY$
    IF LEN(mykey$) OR KeyCombos THEN
        SELECT CASE mykey$
            CASE CHR$(27)
                PRINT " INKEY$ CODE: CHR$(27)"
                EXIT DO ' Escape loop to end program snippet.
        END SELECT
        show_Values mykey$, KeyCombos
    END IF
LOOP

SUB show_Values (mykey$, KeyCombos)
    STATIC OldKeyCombos
    IF KeyCombos = OldKeyCombos AND LEN(mykey$) = 0 THEN EXIT SUB ' Neat trick to only print once to screen while PEEK discovered keys are held down.
    DO ' Falx loop to avoid printing INKEY printing if only a non-INKEY$ is held down.
        SELECT CASE LEN(mykey$)
            CASE 0
                EXIT DO
            CASE 1
                b = ASC(mykey$) ' ASC() converts a string character to a numeric value.
                a$ = ""
            CASE 2
                b = ASC(MID$(mykey$, 2, 1))
                a$ = "CHR$(0)" ' This is the nul character INKEY$ reports for 2 byte length key representation like the F1 - F12 keys.
        END SELECT
        b$ = LTRIM$(STR$(b)) ' This is how you convert a numeric variable to a string variable.
        PRINT " INKEY$ CODE: ";
        IF LEN(a$) THEN PRINT a$; " + ";
        PRINT "CHR$("; b$; ") "; "AKA: " + CHR$(34) + CHR$(b) + CHR$(34) + " ";
        EXIT DO
    LOOP
    IF KeyCombos THEN
        SELECT CASE KeyCombos
            CASE 1
                PRINT " Shift Down";
            CASE 2
                PRINT " Crtl Down";
            CASE 3
                PRINT " Shift + Ctrl Down";
            CASE 4
                PRINT " Alt Down";
            CASE 5
                PRINT " Shift + Alt Down";
            CASE 6
                PRINT " Ctrl + Alt Down";
            CASE 7
                PRINT " Shift + Ctrl + Alt Down";
        END SELECT
        OldKeyCombos = KeyCombos
    END IF
    PRINT
END SUB

Cool, right? Well, not so fast there Sparky, there is a drawback. We can only read 3 key inputs at a time. Hold Shift + Ctrl + Alt then press "A" and just like I stated, the 4th input, "A" won't get printed. INP() won't help here either, and interestingly enough, although _KEYHIT and _KEYDOWN can handle most 3 key combinations, I noted some arrow combos and others were not registered when trying to get that technique to work. WinAPI can recognize some but not all 4+ multiple key presses. Fool around with some keys in this example if you have a Windows System. Hold Shift+Ctrl+Alt and try some arrow keys etc to see which ones show up for INKEY$. Some will, some won't, but those are all 4 key press events with this INKEY$ helper.

The only key I set to register in the alphabet is "A" so try Shift+Ctrl+Alt+A to see them all register. Oh, if you fooled with the code to try and get A+B+C+D to register, it would only register A+B+C.

Code: (Select All)
CONST VK_SHIFT = &H10 'SHIFT key
CONST VK_CONTROL = &H11 'CTRL key
CONST VK_MENU = &H12 'ALT key

DECLARE DYNAMIC LIBRARY "User32"
    FUNCTION GetAsyncKeyState% (BYVAL vkey AS LONG)
END DECLARE

DO
    _LIMIT 30
    a% = GetAsyncKeyState%(VK_SHIFT)
    b% = GetAsyncKeyState%(VK_CONTROL)
    c% = GetAsyncKeyState%(VK_MENU)
    d% = GetAsyncKeyState%(&H41)
    SELECT CASE a%
        CASE 0
        CASE ELSE
            PRINT a%
    END SELECT
    PRINT a%, b%, c%, d%, INKEY$
LOOP

So what else can we say about INKEY$? Well, it can be used in combination with other keyboard keywords. Maybe not good practice, but just saying it's workable.

INKEY$ stores key presses in a buffer. You can use _KEYCLEAR to clear that buffer. Some programs need this to stop users who press keys during a program pause from being used in the routine. SLEEP is actually a very simple example...

Code: (Select All)
SLEEP
IF LEN(INKEY$) THEN BEEP ' Will Beep
SLEEP
_KEYCLEAR
IF LEN(INKEY$) THEN BEEP ' Won't beep.

A bit more involved example of the buffer uses a delay to slow the typing output to the screen. With the buffer, Demo 1, the output catches up after you are done fast typing. In demo 2, _KEYCLEAR clears the buffer and the utput stops the moment the user stops fast typing.

Code: (Select All)
PRINT "Demo 1: Buffer will catch up to your typing. Press Es when ready for demo 2..": PRINT
LOCATE , , 1 ' Let's show the cursor for these typing demos...
' That third LOCATE parameter of 1 gives us the underline stype cursor.
' See the LOCATE Keyword of the Day for other cursor appearance options).
DO
    _LIMIT 30
    mykey$ = INKEY$
    IF LEN(mykey$) OR KeyCombos THEN
        SELECT CASE mykey$
            CASE CHR$(27)
                EXIT DO
            CASE ELSE
                IF LEN(mykey$) = 1 THEN
                    PRINT mykey$;
                    _DELAY .25
                END IF
        END SELECT
    END IF
LOOP
PRINT: PRINT: PRINT "Okay, demo two. The printing to the screen stops the moment you stop typing.": PRINT
DO
    _LIMIT 30
    mykey$ = INKEY$
    IF LEN(mykey$) OR KeyCombos THEN
        SELECT CASE mykey$
            CASE CHR$(27)
                EXIT DO
            CASE ELSE
                IF LEN(mykey$) = 1 THEN
                    PRINT mykey$;
                    _KEYCLEAR
                    _DELAY .25
                END IF
        END SELECT
    END IF
LOOP

On a final note, my beloved INKEY$, as stated previously, can't do everything. For instance say you want to use Ctrl + and Ctrl - to resize text, just like browsers. Well, we can't do that with INKEY$, but we can use _KEYHIT or Win32 API. An example of each is provided here: https://staging.qb64phoenix.com/showthread.php?tid=1230

Pete
Reply


Messages In This Thread
Day 023: INKEY$ - by Pete - 12-03-2022, 07:18 PM
RE: Day 023: INKEY$ - by mnrvovrfc - 12-03-2022, 11:58 PM



Users browsing this thread: 1 Guest(s)