News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

How can I say "You pressed Down Arrow Key" ?

Started by frktons, August 07, 2010, 10:56:21 PM

Previous topic - Next topic

frktons

Quote from: sinsi on August 08, 2010, 12:16:01 AM

include \masm32\include\masm32rt.inc

.data?
stdin   dd ?
howmany dd ?
buffer  INPUT_RECORD <>

.code
start:  invoke GetStdHandle,STD_INPUT_HANDLE
        mov stdin,eax

again:  invoke ReadConsoleInput,stdin,offset buffer,1,offset howmany
        cmp buffer.EventType,KEY_EVENT
        jnz again
        cmp buffer.KeyEvent.wVirtualKeyCode,VK_ESCAPE
        jz finish
        cmp buffer.KeyEvent.wVirtualKeyCode,VK_DOWN
        jnz again
        print "You "
        cmp buffer.KeyEvent.bKeyDown,0
        jnz @1
        print "released"
        jmp @2
@1:     print "pressed"
@2:     print " Down Arrow Key"
        print chr$(13,10)
        jmp again
finish: invoke ExitProcess,0

end start


Thanks sinsi. So we have to resort to API calling, as I suspected.  :P
This examples is really great. It shows how many API to call in order
to build a simple SWITCH/CASE proc based on key pressed by the user.  :U

Quote from: hutch-- on August 08, 2010, 12:22:13 AM
Frank,

From memory you can use combinations of the API GetAsynchKeyState() in a console app loop and this will allow you to trap any set of key combinations. The MSVCRT based algos do most things OK but for a specialised requirement you may have to code it differently.

Thanks Hutch, I didn't know that the C runtime library had this kind of function
inside. Probably if we knew more about it, we could realize that it has already everything
we are talking about.  ::)
Mind is like a parachute. You know what to do in order to use it :-)

GregL

#16
frktons,

You can use the CRT function _getch, if it is an extended key the return will be 0 or 0E0h and you must call _getch again. It then returns the scan code for the extended key.

Here's an example of how you would do this:

INVOKE crt__getch
.IF (eax == 0) || (eax == 0E0h)
    INVOKE crt__getch
.ENDIF


The ret_key procedure in MASM32 uses this method. The getkey macro calls ret_key.

Ghandi

Quote
Thanks sinsi. So we have to resort to API calling, as I suspected.  

You are surprised Frank, when Win32 world has us abstracted from Interrupts and such, instead relyiing on the API to interface between userland and OS for the majority of things. Example, open a file handle without  using API.... Create a window.... Sure, we have control over our ASM code, but when it comes to interfacing with the OS, we're restricted to API. This is not taking in to account the glaringly obvious that of course, you could reverse engineer the operating system and recode the whole thing yourself, but that is not the question at hand.

HR,
Ghandi

dedndave

try this one, Frank...

frktons

Quote from: Greg Lyon on August 08, 2010, 01:11:05 AM
frktons,

You can use the CRT function _getch, if it is an extended key the return will be 0 or 0E0h and you must call _getch again. It then returns the scan code for the extended key.

Here's an example:

.586
.MODEL FLAT, STDCALL
OPTION CASEMAP:NONE

INCLUDE windows.inc
INCLUDE kernel32.inc
INCLUDE msvcrt.inc

.CODE

GetKey PROC

    INVOKE crt__getch
    .IF (eax == 0) || (eax == 0E0h)
        INVOKE crt__getch
    .ENDIF
     
    ret
GetKey ENDP 

END


Thanks Greg, this function albeit not standard is present both in Pelles'C
and in MS C/C++ compiler, it could be a valid wrap on the underlying API.  :U

Quote from: Ghandi on August 08, 2010, 01:12:56 AM

Sure, we have control over our ASM code, but when it comes to interfacing with the OS, we're restricted to API.

HR,
Ghandi

Yes my friend, this is the world we are apparentely living in.  :bg

Quote from: dedndave on August 08, 2010, 01:37:04 AM
try this one, Frank...


Good enough, maybe this version covers all the keystrokes. At least all the ones I
tried have given reliable codes.  :U
I've to spend some time on it trying to understand what the two called functions
do and how can I customize them for my potential needs.  :P
Mind is like a parachute. You know what to do in order to use it :-)

dedndave

well - just as Greg mentioned earlier....
if you call crt__getch and the lower byte of the return value is 0 or 0E0h, it means it is an "extended key"
that means you have to call the crt__getch function again to get the scancode
in my routine, i indicate this by placing it in the high byte of the word, instead of the low byte
also - my routine returns hex values instead of decimal (easier to understand)
so - when you see a value like "4300" or "43E0", it means it is an extended key, like an arrow or function key
when you see a value like "0043", it means it is not an extended key, and typically may be interpreted as normal ASCII


jj2007

Quote from: frktons on August 07, 2010, 11:26:18 PM
Thanks Jochen. It seems working only for letters, not for special
keys, arrows, Fx and so on.  ::)

include \masm32\MasmBasic\MasmBasic.inc
   Init
   Print "Press any key", Cr$
   call ret_key
   .Repeat
      .if ecx
         Print "That was an extended key with code ", Str$(eax), ", try again", Cr$
      .else
         Print "That was ", Chr$(eax), ", try again", Space$(27), Cr$
      .endif
      call ret_key
   .Until eax==VK_ESCAPE
   Exit
end start

dedndave

there are some keys that do not work
but, i guess that is a characteristic of crt__getch
when you hold down the Alt key and enter a decimal with the ten-key pad,
it is supposed to generate that decimal key upon release of the Alt key - don't see that working
maybe i am doing something wrong - i may play with it, later, to see if i can get those keys

dedndave

well - crt__getch must filter out the Alt-ten-key-pad characters
if you want to read those, you'll have to use ReadConsoleInput or one of the other less attractive methods

frktons

Here we have an example of key management similar to the ones I'll use in
my future console based MENUSYS.

It actually works only for arrow keys, F1, ESC, HOME, END keys
that will presumably be the ones I'll use the most in a menu'.

Attached the source code.

Any comment, improvement is welcome.  :bg
Mind is like a parachute. You know what to do in order to use it :-)

dedndave

that is really the hard way to go, Frank
there are a number of issues that must be dealt with if you want to use ReadConsoleInput
first of all, not all event records that come into that function are keyboard presses
and, while ignoring the others may work for simple tests, it may not be what you want for a full program
that is because in practical use, you may have to handle some of the non-keyboard events, as well

second - and this is probably the big one
in order to fully interpret a key code, you must examine the state of the shift, alt, control, and windows keys
you may then need filtering code (maybe something like a look-up table) to determine what the actual keypress means
your code simply examines all key presses and ignores all key releases
that may not be what you want, either
you may want to filter out the shift/alt/control key presses, as well
because the state of these keys when another key is pressed is what counts
(there is a value in the structure for reading the state of these keys: dwControlKeyState)

that is why i suggested using kbhit and getch
much simpler, because the crt lib takes care of all this stuff for you

but, if you want to stick with that method, one function you may find interesting is PeekConsoleInput
it allows you to see if an input event record is in the queue, without calling ReadConsoleInput function
this may be used to allow your program to do other tasks while waiting for a keypress

i was thinking about how i might write a routine, if i wanted to capture the alt-ten-key-pad input
it might be easiest to code by using a hybrid of the 2 methods

frktons

Yes Dave, this is the hard way, but I think it's better to know something about it
before deciding to leave it or to use it and how.
I purposefully ignored the mouse events and key released, because at the moment
I'm only thinking about some keys to use in a Menu. If I had to manage the all
thing [mouse events, CTRL keys, ALT keys and so on] the routine would be bigger
than the program itself.  :bg      Well, a look-up table could help as well.

Maybe in a future time I'll need all these keys to manage an input byte by byte for
getting only numbers, or only letters, or only a fixed number of characters or
whatever. An input routine built this way could be quite hard indeed.  :lol

In the Vertical Menu I'm dreaming about the arrow keys would move from one choice
to the next or to the previous, or to the first or the last, and the ENTER key will
execute the choice, while the ESCAPE key will terminate the menu, or go back
to a previous one. And last but not least F1 will display some help about the choice
that is actually highlighted.

With this in my mind I made some choices in building the routine, but of course
it can, and probably will, change along the process. I'd like to build the MenuV
proc in different ways, learning from their different styles and needs, and deciding
which of them suits better the needs of the particular task I will undertake.  :P

If you feel like, build it your own way, and confronting the results and the limits
of various versions we'll see what happens.  :thumbu

PS: better if I post ASAP an Imagine, I mean a screen that you can display
with the routine we just realized elsewhere in order to make my idea a little bit clearer.

Posting soon...
Mind is like a parachute. You know what to do in order to use it :-)

frktons

Well, an imagine could explain better than 100 words  :P
This imagine is a complete console screen and needs the program
attached in order to be displayed.

This is a sample screen of the menu I have in mind. Time permitting
I'll post the code to manage it before going abroad for holydays.
Mind is like a parachute. You know what to do in order to use it :-)

frktons

I'm preparing the menusys program, and I've reorganized it a little.
Apparently I've done something wrong:



;----------------------------------------------------------------------
; Reads a file containing a console screen format into an array
; of structure CHAR_INFO totalling 8000 bytes, and INVOKE the API
; to display it all at once: WriteConsoleOutput(). Wait a key and
; terminates the program.
;----------------------------------------------------------------------
; Input file: menusys.scn
;----------------------------------------------------------------------
; Author: frktons @ MASM32 forum
; Date: 20/aug/2010.
;----------------------------------------------------------------------


include \masm32\include\masm32rt.inc


Main        PROTO
ConsoleSize PROTO
GetConsole  PROTO
DisplayFmt  PROTO
InitProc    PROTO
AnyKey      PROTO
ShowCursor  PROTO
HideCursor  PROTO



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

BufRows EQU 25                 ;buffer height (rows)
BufCols EQU 80                 ;buffer width (columns)
BufLen  EQU BufRows*BufCols    ;string length (tchars)

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


.data


    ConTitle  db "       **  Win32 Console Menu System  **", 0   
    inpbyte   DWORD  1
    FmtName   db "menusys.scn",0 
         
         

.data?

    menusys CHAR_INFO 2000 dup (<>)
   

    hFile     HANDLE ?
    cci       CONSOLE_CURSOR_INFO <>       


    wHnd      HANDLE ?
    rHnd      HANDLE ?

    windowSize SMALL_RECT <>
    bufferSize COORD      <>
    startPoint COORD      <>

    howmany dd ?
    buffer  INPUT_RECORD <>


.code

start:

Main PROC

    INVOKE InitProc

    INVOKE ConsoleSize

    INVOKE GetConsole

    INVOKE DisplayFmt
       
    INVOKE AnyKey

    CALL   ShowCursor

finish: INVOKE ExitProcess,0

        ret

Main ENDP

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

ConsoleSize PROC

    INVOKE SetConsoleTitle, ADDR ConTitle
 
    CALL   HideCursor

    INVOKE SetConsoleWindowInfo, wHnd, TRUE, ADDR windowSize

    INVOKE SetConsoleScreenBufferSize, wHnd, DWORD PTR bufferSize

    ret

ConsoleSize ENDP

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

GetConsole PROC

    mov hFile, fopen(DWORD PTR FmtName)

    mov ebx, fread(hFile, offset menusys, BufLen)
   
    fclose hFile

    ret
   
GetConsole ENDP

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

ShowCursor PROC

    INVOKE GetConsoleCursorInfo,wHnd,addr cci
    mov cci.bVisible,TRUE
    INVOKE SetConsoleCursorInfo,wHnd,addr cci
    ret
   
ShowCursor ENDP

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


HideCursor PROC

    INVOKE GetConsoleCursorInfo,wHnd,addr cci
    mov cci.bVisible,FALSE
    INVOKE SetConsoleCursorInfo,wHnd,addr cci
    ret
   
HideCursor ENDP

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

InitProc PROC

    INVOKE GetStdHandle, STD_INPUT_HANDLE
    mov rHnd,eax

    INVOKE GetStdHandle, STD_OUTPUT_HANDLE
    mov wHnd,eax 

    mov windowSize.Left, 0
    mov windowSize.Right, 79
    mov windowSize.Top, 0
    mov windowSize.Bottom, 24
   

    mov bufferSize.x, 80
    mov bufferSize.y, 25

    mov startPoint.x, 0
    mov startPoint.y, 0


    ret

InitProc ENDP

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

DispalyFmt PROC

    INVOKE WriteConsoleOutput, wHnd, offset menusys, DWORD PTR bufferSize,
           DWORD PTR startPoint, offset windowSize

    ret

DispalyFmt ENDP

; -------------------------------------------------------------------------
;Returns: key code in buffer.KeyEvent.wVirtualKeyCode WORD size
; -------------------------------------------------------------------------

Anykey PROC

again: 

    INVOKE ReadConsoleInput,rHnd,offset buffer,1,offset howmany
    cmp buffer.EventType,KEY_EVENT
    jnz again

    cmp buffer.KeyEvent.bKeyDown,0
    jz again

    ret

Anykey ENDP

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

end start



When I try to Assemble it I get this error message:


Microsoft (R) Macro Assembler Version 10.00.30319.01
Copyright (C) Microsoft Corporation.  All rights reserved.

Assembling: C:\masm32\examples\viewfmt\menusys.asm
C:\masm32\examples\viewfmt\menusys.asm(123) : error A2111:conflicting parameter
definition
_
Assembly Error


and the line where it gives this error is:


ShowCursor PROC  <----------- here it is

    INVOKE GetConsoleCursorInfo,wHnd,addr cci
    mov cci.bVisible,TRUE
    INVOKE SetConsoleCursorInfo,wHnd,addr cci
    ret
   
ShowCursor ENDP


So I presume it refers to the next line. But I don't think I changed anything.

Any clue?
Mind is like a parachute. You know what to do in order to use it :-)

dedndave

well - things are a little confusing with regard to INVOKE/CALL and the use of PROTO
if you are going to CALL a function, you do not need a PROTO
also, if the function has no parameters, it is simpler to leave the PROTO out and just use CALL
it makes the code easier to understand
however, i don't think it is the cause of the error
let me look at it a little......