The MASM Forum

General => The Campus => Topic started by: raleep on March 12, 2015, 01:14:58 PM

Title: Detect or Get Keypress
Post by: raleep on March 12, 2015, 01:14:58 PM
I apologise for not doing more research on my own.  I have searched MSDN, but the simple (I think) information I want seems to be buried in masses of what is not immediately useful to me.

I think GetAsyncKeyState would do it if invoked once for each key (as well as once at the start of the routine I want to pause).

I am looking for a function that when called will return when and only when (although a forced return would also be nice) a key is pressed - any key.

Can anyone suggest an answer or a direction for further research?

Thank you - Robert Leeper
Title: Re: Detect or Get Keypress
Post by: raleep on March 12, 2015, 01:38:28 PM
Quote from: raleep on March 12, 2015, 01:14:58 PM
I am looking for a function that when called will return when and only when (although a forced return would also be nice) a key is pressed - any key.
Never mind about the forced return - I see how to do [something like] that.
Title: Re: Detect or Get Keypress
Post by: dedndave on March 12, 2015, 02:07:17 PM
if you're talking about console-mode programs, i suggest crt__getch
it's part of the msvcrt library
but, the console is buggish, and the library often yields best results

for GUI-mode programs, you write a handler in WndProc
it activates when a key is pressed, but allows other processing when no key is in the buffer
Title: Re: Detect or Get Keypress
Post by: rrr314159 on March 12, 2015, 04:21:39 PM
Hello raleep,

Don't really think this is what you want but at least it's a starting point

include \masm32\include\masm32rt.inc

.data
    thechar db 0, 0
    format db 'You pressed %s',10,0
.code

start:
    print "Press any key, ctrl-C to exit", 10, 0
    @@:
        call wait_key
        mov thechar, al
        invoke crt_printf, ADDR format, ADDR thechar
    jmp @B
    ret

end start


Assume you know how to run this with masm32

wait_key is using the function dedndave mentioned, crt__getch, and can be found in \masm32\m32lib\wait_key.asm

Title: Re: Detect or Get Keypress
Post by: MichaelW on March 12, 2015, 05:38:31 PM
Assuming that your goal here is to detect key presses in your app, and not in other apps, crt__getch is easy, and while it cannot detect "any" key it can detect all but the shift and toggle keys.

Edit: modified code to display the return value as a number and a character.

;===============================================================================
include \masm32\include\masm32rt.inc
;===============================================================================
.data
.code
;=============================================================================== 
start: 
;===============================================================================     
  @@:
    invoke crt__getch
    cmp   eax, "X"
    je    @F
    cmp   eax, "x"
    je    @F
    printf("%d\t%c\n",eax,eax)
    jmp   @B
  @@: 
    inkey
    exit
end start
;===============================================================================


Title: Re: Detect or Get Keypress
Post by: dedndave on March 12, 2015, 06:00:25 PM
hi Michael,

as i recall, _getch IS capable of detecting all keys except system keys (ALT-?) and CTRL-C (CTRL-Break)
if you want to detect CTRL-Break, install a handler for it

if it returns 0 or 0E0h on the first call, it must be called again (arrow keys, function keys, etc)
very similar to how the old 16-bit INT 16h worked

here is a function that returns immediately if no key has been pressed (non-blocking)
it's one i have used often
give me a few minutes, and i can adapt it to a blocking function

;*************************************************************************

InKyb   PROC

;Polled Keyboard Input - DednDave 8, 2010
;
;This function returns a keystroke in EAX if there is one in the buffer.
;If the buffer is empty, the function returns immediately.
;
;If the keyboard buffer is empty, AH = 0, AL = 0, ZF = 1.
;If the stroke is a regular key, AH = 0, AL = key char, ZF = 0.
;If the stroke is an extended key, AH = extended key, AL = E0h, ZF = 0.
;If the stroke is a function key, AH = function key, AL = 0, ZF = 0.
;
;ECX, EDX are not preserved.

        call    crt__kbhit
        or      eax,eax
        jz      InKyb1

        call    crt__getch
        and     eax,0FFh
        jz      InKyb0

        cmp     al,0E0h
        jnz     InKyb1

InKyb0: push    eax
        call    crt__getch
        pop     edx
        shl     eax,8
        or      eax,edx

InKyb1: retn

InKyb   ENDP

;*************************************************************************
Title: Re: Detect or Get Keypress
Post by: dedndave on March 12, 2015, 06:08:39 PM
here - give this a try....

Title: Re: Detect or Get Keypress
Post by: MichaelW on March 12, 2015, 06:21:20 PM
Dave, from a 32-bit app running under Windows 7-64 the _getch function does not return until you press a non-toggle or non-shift key. And, one that I forgot to test, it does not return when you press the Pause/Break key. The arrow and function keys work as you describe, returning 0E0h ahead of the arrow keys and 0 ahead of the function keys.

MSDN _getch _getwch (https://msdn.microsoft.com/en-us/library/078sfkak.aspx)

And your app behaves the same way, not responding to the shift, toggle, or Pause/Break keys.
Title: Re: Detect or Get Keypress
Post by: dedndave on March 12, 2015, 06:55:43 PM
that's strange
under XP (32-bit), shift , caps lock, and num lock work as expected
even many of the CTRL keys work

ALT keys are system keys, which msvcrt authors chose not to override - understandably
Title: Re: Detect or Get Keypress
Post by: MichaelW on March 12, 2015, 07:23:11 PM
The key combinations involving the shift and toggle keys, or at least the ones that I tested, work as expected. _getch, like most or all high-level input functions, does not meet the "any key" requirement because it does not detect the shift or toggle keys when they are pressed as single keys. Perhaps I should have interpreted "any key" as "any normal key".

Title: Re: Detect or Get Keypress
Post by: dedndave on March 12, 2015, 07:33:21 PM
you can use one of the console API functions to "get any key"
really, i try not to spend too much time on it, because the console is buggish
no matter what you do, you get buggish behavior   :P

to me, it works fine for simple test programs or tools in "teletype" fashion
i.e., print a line, get a line - no fancy cursor movement stuff
Title: Re: Detect or Get Keypress
Post by: dedndave on March 12, 2015, 07:40:13 PM
here's one that uses ReadConsoleInput...

;***********************************************************************************************

AnyKey  PROC

;Wait for Any Console Key Press - DednDave
;version 1, 12-2011
;version 2, 2-2013
;
;  This function returns when any console key is pressed (bKeyDown = 1).
;A possible drawback is that all input event records are removed from
;the console input queue until a key is pressed. In many cases, this is
;not an issue. The virtual key code, virtual scan code, TCHAR character,
;and control key state values are returned in registers.

;Call With: Nothing
;
;  Returns: EAX = TCHAR character (high word of EAX = 0)
;           ECX = control key state flags
;               Bit   Name                Meaning
;                0    RIGHT_ALT_PRESSED   Right ALT key is pressed
;                1    LEFT_ALT_PRESSED    Left ALT key is pressed
;                2    RIGHT_CTRL_PRESSED  Right CTRL key is pressed
;                3    LEFT_CTRL_PRESSED   Left CTRL key is pressed
;                4    SHIFT_PRESSED       SHIFT key is pressed
;                5    NUMLOCK_ON          NUM LOCK light is on
;                6    SCROLLLOCK_ON       SCROLL LOCK light is on
;                7    CAPSLOCK_ON         CAPS LOCK light is on
;                8    ENHANCED_KEY        Key is enhanced
;           EDX:
;           low word (DX) = virtual key code
;               high word = virtual scan code
;
;           all other registers are preserved

;-------------------------------------------------

    LOCAL   ir          :INPUT_RECORD
    LOCAL   uRecCnt     :UINT
    LOCAL   hStdInp     :HANDLE

;-------------------------------------------------

    INVOKE  GetStdHandle,STD_INPUT_HANDLE
    mov     hStdInp,eax
    .repeat
        INVOKE  ReadConsoleInput,hStdInp,addr ir,1,addr uRecCnt
        movzx   ecx,word ptr ir.EventType
        mov     edx,dword ptr ir.KeyEvent.wVirtualKeyCode
    .until (ecx==KEY_EVENT) && (ecx==ir.KeyEvent.bKeyDown) && (dx!=VK_SHIFT) && (dx!=VK_CONTROL) && (dx!=VK_MENU)
    movzx   eax,word ptr ir.KeyEvent.UnicodeChar
    mov     ecx,ir.KeyEvent.dwControlKeyState
    ret

AnyKey  ENDP

;***********************************************************************************************


it doesn't mention it, but that one is UNICODE aware, i think

notice that you can modify this line to customize behaviour for different key sets

    .until (ecx==KEY_EVENT) && (ecx==ir.KeyEvent.bKeyDown) && (dx!=VK_SHIFT) && (dx!=VK_CONTROL) && (dx!=VK_MENU)
Title: Re: Detect or Get Keypress
Post by: raleep on March 15, 2015, 03:50:21 AM
Quote from: dedndave on March 12, 2015, 02:07:17 PM
if you're talking about console-mode programs, i suggest crt__getch
it's part of the msvcrt library
but, the console is buggish, and the library often yields best results

for GUI-mode programs, you write a handler in WndProc
it activates when a key is pressed, but allows other processing when no key is in the buffer

sbrtst_test:
call beep0
pushad
call wosw
invoke crt__getch
popad
   jmp sbrtst_test


This doesn't work for me; it just goes on beeping until stopped.
Title: Re: Detect or Get Keypress
Post by: jj2007 on March 15, 2015, 04:35:03 AM
Old-fashioned but seems to work:

include \masm32\include\masm32rt.inc

.data?
kb1 db 256 dup(?)
kb2 db 256 dup(?)

.code
start:
  mov esi, offset kb1
  mov edi, offset kb2
  .Repeat
invoke GetKeyboardState, esi
.Repeat
invoke Sleep, 1
invoke GetKeyState, VK_SHIFT
invoke GetKeyboardState, edi
mov ecx, 255
push esi
push edi
repe cmpsb
pop edi
pop esi
.Until sdword ptr ecx>0
neg cl
sub cl, 2
.Break .if ecx==VK_ESCAPE
print str$(ecx), " was pressed", 13, 10
  .Until 0
  print "bye"
  exit
end start

Title: Re: Detect or Get Keypress
Post by: dedndave on March 15, 2015, 06:10:05 AM
sbrtst_test:
call beep0
pushad
call wosw
invoke crt__getch
popad
   jmp sbrtst_test


that code will continually loop and beep whenever a key is pressed

first of all, the PUSHAD/POPAD preserves and restores all the registers, including EAX
crt__getch returns a value in EAX that should be tested for key-presses (destroyed by POPAD)
and - you don't display that value

also, the loop ends with an unconditional branch back to the top
you may way to use a conditional branch, instead
Title: Re: Detect or Get Keypress
Post by: rrr314159 on March 15, 2015, 02:31:42 PM
I know this is a dumb question, but I don't know why it is ... why don't u use the extremely straightforward example I posted?
Title: Re: Detect or Get Keypress
Post by: jj2007 on March 15, 2015, 02:38:31 PM
Quote from: rrr314159 on March 15, 2015, 02:31:42 PM
why don't u use the extremely straightforward example I posted?

OP wants any key, including Shift etc; which, btw, works with the example that I posted
(just to keep up the competitive spirit here  :badgrin: )
Title: Re: Detect or Get Keypress
Post by: rrr314159 on March 15, 2015, 05:13:16 PM
Competition is a fine thing, keeps everyone on their toes, establishes camaraderie and a mutual sense of shared progress. It's great except that, sometimes, when carried too far, it can lead to a very undesirable consequence, which ruins the friendly spirit hutch works so assiduously to cultivate here - and that's no good. That undesirable consequence is: sometimes I lose!

In this case, you're right, OP asked for *all* keys; your routine does that. However - OP is working with dedndave to get a routine working with _getch, even as we speak. Deduction, *all* keys isn't so critical. My example (which I slaved over for, at the very least, a minute or two) also uses _getch; works; is as simple as dirt; so why not start there, and move up to GetKeyboardState after, if really desired?

Moving along, the way you do GetKeyboardState is vey clean compared to hack jobs I've seen. I guess the call to GetKeyState is necessary, or you wouldn't do it, but I don't really know why. OP mentioned GetAsyncKeyState; if that's really wanted your routine (I think) is easily modified to use that instead ... ?

So I reckon you win, still there's a decent chance that *all* keys really isn't necessary.



Title: Re: Detect or Get Keypress
Post by: jj2007 on March 15, 2015, 06:54:29 PM
Quote from: rrr314159 on March 15, 2015, 05:13:16 PMMoving along, the way you do GetKeyboardState is vey clean compared to hack jobs I've seen. I guess the call to GetKeyState is necessary

It seems so. Without it, no success. Apparently GetKeyboardState needs some gentle reminder that it has been asked to do something. My best guess is that the console has a message loop, and therefore needs a message of some kind to be activated.

Now let's wait for Dave's best guess 8)
Title: Re: Detect or Get Keypress
Post by: raleep on March 16, 2015, 06:08:10 AM
Quote from: dedndave on March 15, 2015, 06:10:05 AM
sbrtst_test:
call beep0
pushad
call wosw
invoke crt__getch
popad
   jmp sbrtst_test


that code will continually loop and beep whenever a key is pressed

first of all, the PUSHAD/POPAD preserves and restores all the registers, including EAX
crt__getch returns a value in EAX that should be tested for key-presses (destroyed by POPAD)
and - you don't display that value

also, the loop ends with an unconditional branch back to the top
you may way to use a conditional branch, instead

I expected crt__getch not to return until a key was pressed. :redface:
I'll need to work on this some more.
Thank you.
Title: Re: Detect or Get Keypress
Post by: dedndave on March 16, 2015, 06:15:43 AM
we don't see the code for beep0 or wosw