WTF Inkey???

General FreeBASIC programming questions.
Post Reply
xbgtc
Posts: 249
Joined: Oct 14, 2007 5:40
Location: Australia

WTF Inkey???

Post by xbgtc »

I wanted to just do a simple thing and can't believe it just doesn't work! - WHY?

I started with:

Code: Select all

do
    if inkey=chr(32) then ?"paused"
    if inkey=chr(13) then ?"Cont.."
    sleep 20,1
loop while inkey<>chr(27)
then when that didn't work tried:

Code: Select all

dim as string in
do
    in=inkey
    if in=chr(32) then ?"paused"
    if in=chr(13) then ?"Cont.."
    sleep 20,1
loop while inkey<>chr(27)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: WTF Inkey???

Post by fxm »

A single Inkey and at the start of the loop:

Code: Select all

dim as string in
do
    in=inkey
    if in=chr(32) then ?"paused"
    if in=chr(13) then ?"Cont.."
    sleep 20,1
loop while in<>chr(27)
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: WTF Inkey???

Post by angros47 »

The issue happens because inkey doesn't evaluate the key currently pressed, but takes the key value from the keyboard buffer.

Likely you might have noticed that, on console, if you type something when a program is running and not accepting input, what you have typed will then be displayed as soon as the console prompt is shown (or an INPUT command is performed). Basically, computer keeps the keys stroked "on hold", and uses them as soon as possible.
The command INKEY takes one key stroke from that buffer: so, if you press a key while the computer is doing some instruction different from inkey, that key will be detected as soon as the first "inkey" instruction is executed. The next INKEY instruction will be empty, since that keystroke has already been processed

So, if you do something like:

Code: Select all

    if inkey=chr(32) then ?"paused"
    if inkey=chr(13) then ?"Cont.."
and you press enter, the first INKEY will return chr(13) (that is not equal to chr(32), so the condition won't be executed), and the second INKEY will return nothing, since the keystroke has already been analyzed from the first one, so the second condition will never be true. It could be true if the enter key is pressed after the first line, but before the second one, but that time interval is too short, less than a millisecond, so it's almost impossible for it to happen.

If you use only one occurrence of INKEY per cycle, as fxm showed you, and store the result in a variable, then you can compare it with anything you want.
xbgtc
Posts: 249
Joined: Oct 14, 2007 5:40
Location: Australia

Re: WTF Inkey???

Post by xbgtc »

OMG i pasted in FXM's code thinking what's the difference from my code hence expecting it not to work but it did! but couldn't understand why as the only difference was

Code: Select all

loop while in<>chr(27)
Ok i now know what was wrong and sorta get you angros47 but the thing is that with my

Code: Select all

loop while inkey<>chr(27)
the program exits flawlessly when escape hit so why does that comparison work?... even the below code exits flawlessly.

Code: Select all

do
    if inkey=chr(32) then ?"paused"
    if inkey=chr(13) then ?"Cont.."
    sleep 20,1
loop while inkey<>chr(27)
This is doing my head in :)
xbgtc
Posts: 249
Joined: Oct 14, 2007 5:40
Location: Australia

Re: WTF Inkey???

Post by xbgtc »

Been playing around with it and now know it's just a timing thing eg. with

Code: Select all

do
    if inkey=chr(32) then ?"paused"
    if inkey=chr(13) then ?"Cont.."
    sleep 20,1
loop while inkey<>chr(27)
ESC will always work as it's after a sleep hence will pick up the key first and if i do

Code: Select all

do
    if inkey=chr(13) then ?"Cont.."
    sleep 20,1
    if inkey=chr(32) then ?"paused"
loop while inkey<>chr(27)
or

Code: Select all

do
    sleep 20,1
    if inkey=chr(32) then ?"paused"
    if inkey=chr(13) then ?"Cont.."
loop while inkey<>chr(27)
then SPACE will always be picked up and ESC no longer works. So apart from it being a bad code practice now know why :)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: WTF Inkey???

Post by fxm »

The whole problem comes from the fact that 'Inkey' reads the first character present in the input buffer by immediately removing it from this buffer.
So if in a sequence we want to test this character several times (against different values), the character must first be stored in a variable by using a single 'Inkey'.

Note: By using 'Select Case Inkey', FreeBASIC takes care at the beginning of memorizing under the hood the value of 'Inkey' for all the following 'Case':

Code: Select all

Do
    Select Case Inkey
    Case Chr(32)
        ?"paused"
    Case Chr(13)
        ?"cont.."
    Case Chr(27)
        Exit Do
    End Select
    Sleep 20, 1
Loop
or:

Code: Select all

Do
    Select Case As Const Asc(Inkey)
    Case 32
        ?"paused"
    Case 13
        ?"cont.."
    Case 27
        Exit Do
    End Select
    Sleep 20, 1
Loop
xbgtc
Posts: 249
Joined: Oct 14, 2007 5:40
Location: Australia

Re: WTF Inkey???

Post by xbgtc »

"'Inkey' reads the first character present in the input buffer by immediately removing it from this buffer"

AND

"A single Inkey and at the start of the loop:"

thanks fxm i think i get it now and wow surprised by that select case code! :)
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: WTF Inkey???

Post by dodicat »

No problems with multikey, use it as much as you like.

Code: Select all



dim as single h,v,stepper,stepval=1,delta=.2

screen 19,32
windowtitle "Up, down or sideways"
do
    screenlock
    cls
    If Multikey(75) Then h-=stepper:stepper+=delta 'left
    If Multikey(77) Then h+=stepper:stepper+=delta 'right
    If Multikey(80) Then v+=stepper:stepper+=delta 'up
    If Multikey(72) Then v-=stepper:stepper+=delta 'down
    if not multikey(75) and not multikey(77) and _ 
       not multikey(80) and not multikey(72) then 
        stepper=stepval
    end if
    if 400+h<50  then h=-350
    if 400+h>750 then h=350
    if 300+v<50  then v=-250
    if 300+v>550 then v=250
    circle(400+h,300+v),50,rgb(0,100,200),,,,f
    screenunlock
    sleep 1
loop until multikey(1)
 
xbgtc
Posts: 249
Joined: Oct 14, 2007 5:40
Location: Australia

Re: WTF Inkey???

Post by xbgtc »

from F1 help:
"MultiKey should always work in graphics mode, as long as the screen is Unlocked" :)

I've used multikey in the past (in one of my programs) and it caused so much problem that i had to ditch it. Actually found the code together with the comment i made at the time (line 42 and i've Asterisk'd the section). Multikey was used just for that block of checks (ESC,ENTER,FUNCTION+ARROW Keys) and pretty sure i was clearing the buffer also, but the problem persisted - oh well :)

Code: Select all

sub betting (brc as ubyte)
    dim as ubyte bt,hb,money,raise,x,y,p=1
    dim as uinteger a,b,c,d,e,n,t
    dim as string text,ik
    hb=val(right(herb,2))
    n=int(rnd(1)*20)+12
    bt=5
    raise=5
    if brc=4 then 'for bet/fold
        put(20,700),bet,(0,200)-(310,225),pset
        locate 47,6:?bt;
    end if
    if brc=1 then 'for bet/check/fold
        put(20,700),bet,(0,0)-(310,25),pset
        locate 47,6:?bt;
    end if
    if brc=2 then 'for call/raise/fold
        put(20,700),bet,(0,75)-(310,100),pset
    end if
    if brc=3 then 'for call/fold
        put(20,700),bet,(0,150)-(310,175),pset
    end if
    do
        t=timer
        ik=inkey
        while ik=""
            if timer-t>2 then put(20,554),black,(0,0)-(320,15),pset 'clear my hand text after 2secs
            sleep 20,1
            if timer-t>n then
                a+=1
                n+=8
                text=str(herclothes)+"_hurryup"+str(a)
                vtp=text
                waitforvid:if flag then exit sub
                if a=3 then a=0
                t=timer
            end if
            ik=inkey    'must have this here
        wend
        put(20,554),black,(0,0)-(320,15),pset 'and straight away if i press a key
        '******************************************************************************************************************************************************************************
        if ik=ESC then quit 0   'have to use inkey as multikey just wouldn't work any good (misses keys repeatedly)
        if ik=ENTER then exit do
        if ik=F1 then savegame
        if ik=F2 then loadgame:if flag then exit sub
        if ik=RA and brc<3 and p<3 then p+=1
        if ik=RA and brc>2 and p=1 then p=2
        if ik=LA and brc<3 and p>1 then p-=1
        if ik=LA and brc>2 and p=2 then p=1
        '******************************************************************************************************************************************************************************
        if brc=1 then
            if p=1 then b=0:c=0:d=310:e=25
            if p=2 then
                locate 47,6
                ?"  ";
                b=0:c=25:d=310:e=50
            end if
            if p=3 then
                locate 47,6
                ?"  ";
                b=0:c=50:d=310:e=75
            end if
        end if
        if brc=2 then
            if p=1 then
                locate 47,22
                ?"  ";
                b=0:c=75:d=310:e=100
            end if
            if p=2 then
                locate 47,22:?raise;" ";
                b=0:c=100:d=310:e=125
            end if
            if p=3 then
                locate 47,22
                ?"  ";
                b=0:c=125:d=310:e=150
            end if
        end if
        if brc=3 then
            if p=1 then b=0:c=150:d=310:e=175
            if p=2 then b=0:c=175:d=310:e=199
        end if
        if brc=4 then   'bet/fold
            if p=1 then b=0:c=200:d=310:e=225   'p must be position
            if p=2 then
                locate 47,6
                ?"  ";
                b=0:c=225:d=310:e=250
            End if
        end if
        if ik=UP and bt<25 and p=1 then
            if brc=1 or brc=4 then bt+=5
        end if
        if ik=UP and raise<25 and brc=2 and p=2 then raise+=5
        if ik=DOWN and bt>5 and p=1 then
            if brc=1 or brc=4 then bt-=5
        end if
        if ik=DOWN and raise>5 and brc=2 and p=2 then raise-=5
        put(20,700),bet,(b,c)-(d,e),pset
        if brc=1 or brc=4 then
            if p=1 and bt<>0 then locate 47,6:?bt;" ";
        end if
        if brc=2 and p=2 and raise<>0 then locate 47,22:?raise;" ";
    loop
    if brc=1 then
        if p=1 then
            money+=bt
            myb="bet"+str(bt)
        end if
        if p=2 then myb="check"
        if p=3 then myb="fold"
    end if
    if brc=2 then
        if p=1 then
            money+=hb
            myb="call"
        end if
        if p=2 then
            money+=raise
            money+=hb
            myb="raise"+str(raise)
        end if
        if p=3 then myb="fold"
    end if
    if brc=3 then
        if p=1 then
            money+=hb
            myb="call"
        end if
        if p=2 then myb="fold"
    end if
    if brc=4 then
        if p=1 then
            money+=bt
            myb="bet"+str(bt)
        end if
        if p=2 then myb="fold"
    end if
    if left(myb,1)="b" or left(myb,1)="r" or myb="call" then
        if accbal>money then
            accbal-=money
            pot+=money
        else
            sellclothes 0,money
            pot+=money
        end if
    end if
    locate 47,1
    ?"                            ";
    put(20,700),black,(0,0)-(399,67),pset
    uppot
    if myb="bet5" then myb="bet05"
    if myb="raise5" then myb="raise05"
    if myb="fold" then
        if clothes=4 and accbal<55 then gameover
    end if
    if clothes=4 and accbal<0 then
        draw string(20,554),"SYSTEM FAILURE... watch bets next time!",&hdddddd
        sleep 2000,1
        put(20,554),black,(0,0)-(320,15),pset
        gameover
        myb="fold"
    end if
end sub
xlucas
Posts: 334
Joined: May 09, 2014 21:19
Location: Argentina

Re: WTF Inkey???

Post by xlucas »

Oh! Your original post brings me memories! I remember when I had that same problem when I started with QuickBasic back then. You'll discover many similar things with time. There's tons of these! Back then, I didn't have a forum where I could ask questions so I kept trying things for a couple of days. I initially solved it, but didn't understand why that worked. With time, it became clear.

About MultiKey, I agree. MultiKey does not get 100% well with InKey. If you use MultiKey in a loop, it works perfectly well as long as no InKeys are there, but then, after the loop ends, you need to make sure to empty the keyboard buffer to be able to use InKey again and even sometimes, it doesn't seem to get empty! On the other hand, if you do use InKey in the same loop, MultiKey will work "mostly OK", but will tend to miss some key presses. The best I have been able to do about that is make sure the MultiKeys in the loop are only used when necessary. For example, instead of this:

Code: Select all

Dim akey As String, mykey As Short
Do
    akey = InKey
    mykey = MultiKey(15)
    
    Select Case akey
        Case "a" : Print "You pressed 'a'"
        Case "b" : Print "You pressed 'b'"
        Case "c"
           Print "You pressed 'c'";
           If mykey Then
               Print " while holding down Tab"
           Else
               Print
           End If
    End Select
Loop Until akey = Chr(27)
Better do this:

Code: Select all

Dim akey As String
Do
    akey = InKey
    
    Select Case akey
        Case "a" : Print "You pressed 'a'"
        Case "b" : Print "You pressed 'b'"
        Case "c"
           Print "You pressed 'c'";
           If MultiKey(15) Then
               Print " while holding down Tab"
           Else
               Print
           End If
    End Select
Loop Until akey = Chr(27)
which will prevent MultiKey from being called all the time and conflicting with InKey. In my example, there doesn't seem to be any reason to use the first code, but sometimes, you will have reasons to do things that way and still, it'll be better to do your best to try to switch to the second one. I do not know why frequent accesses to MultiKey conflict with InKey. All I know is that it can be avoided.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: WTF Inkey???

Post by dodicat »

You cannot use inkey more than once in a loop without problems.
A second inkey after a sleep 1 will have a different outcome if the second inkey is before sleep 1
Here I get one shot at inkey but use it in two different ways.
Multikey with one inkey seems to work fine without flushing the buffer.

Code: Select all

Screen 19,32,2,64
screenset 1,0
#define irange(f,l) Int(Rnd*(((l)+1)-(f))+(f))
#define ub 200+rnd*55
#define onscreen(x,y) (x>0 and x<800 and y>0 and y<600)
Type pt
      As Single x,y,z
      as ulong col
End Type

Function perspective(p As pt,ep As pt) As pt
      Dim As Single   w=1+(p.z/ep.z)
      Return Type((p.x-ep.x)/w+ep.x, (p.y-ep.y)/w+ep.y,(p.z-ep.z)/w+ep.z,p.col)
End Function 

Function readkey(x As Long,y As Long,st As String,message As String,clr As Ulong) As String
    Static As String j,blink
    Var c=Color
    Var i=Inkey()
    If Left(i,1)=Chr(08) Then j=Mid(j,1,Len(j)-1)
    Select Case Left(i,1)
    Case Chr(0) To Chr(254)
        If Left(i,1)<>Chr(08) Then
            j=j+Left(i,1)
        End If
    End Select
    If Frac(Timer)>.5 Then blink=" " Else blink="_"
    If Left(i,1)=Chr(27) Then j=""
    If i<>Chr(13) Then
        Locate x,y,0
        Color clr
        Print  st & j & blink 
        Color c
    Else
        j=Rtrim(j,Chr(13))
        message=j
        j=""
    End If
    Return i
End Function


Dim As pt p(1 To 2000),z
For n As Long=1 To Ubound(p)
      p(n)=Type(irange(-300,1100),irange(-300,900),irange(-500,800),rgb(ub,ub,ub))
Next
Dim  As Single x=400,y=300
dim as string key,message
Do
      Line(0,0)-(800,600),Rgba(0,0,0,20),bf
      For n As Long=1 To Ubound(p)
            p(n).z-=10
            z=perspective(p(n),type(x,y,1000))
           if onscreen(z.x,z.y) then pset(z.x,z.y),z.col
            If p(n).z<-950 Then p(n).z=irange(-500,800)
      Next n
      key=readkey(10,2,"Use arrows or space, enter a string or press END to finish  ",message,rgb(200,200,200))
      locate 12,2,0
      print message
      flip
      If Multikey(&h48) Then y+=1
      If Multikey(&h50) Then y-=1
      If Multikey(&h4B) Then x-=1
      If Multikey(&h4D) Then x+=1
      If Multikey(&h39) Then x=400:y=300
      
      Sleep 1
Loop Until key= Chr(255)+"O" or key=chr(255)+"k"
 
Win 10
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: WTF Inkey???

Post by deltarho[1859] »

I have two monitors and many graphics code here display on both monitors and I have to manually move them. UEZ's Fireworks, on the other hand, centres on my main monitor which, obviously, is what I want. Perhaps graphics coders could give that some thought. Image

Otherwise, nice code dodicat.
speedfixer
Posts: 606
Joined: Nov 28, 2012 1:27
Location: CA, USA moving to WA, USA
Contact:

Re: WTF Inkey???

Post by speedfixer »

re: inkey - biggest error is using more than one inkey per loop, as dodicat mentioned.
Since your program in only sitting and waiting for a key, better to use a little longer sleep delay. Let your computer live a little.
But not longer than about 20 ms. Key bounce and repeats can happen depending on your system.
You should almost always place your sleep at the top or bottom of your loop for predictable results.
Also, flush your buffer ( while inkey <> " : sleep 40 : wend ) before and after your loop to START and EXIT clean. 40 ms is longer than a usual keypress or most keybounces.

re: 2 monitors:

I'm on Linux.
I use xrandr from a small FB program and parse the results.

basically:

Code: Select all

dim as string xdatread
xrcmd = "xrandr --current > " & <xrdatfile>
open pipe(xrcmd) for output as xfnum

[ loop to eof; close ]

open <xrdatfile> for input as xfnum
line input #xfnum, xdatread
[ loop through parsing the file data; close ]

The result is:
size of desktop
size and offset of *each* monitor to each other and to desktop
(number of monitors)

I can then automatically size and place my FB program screens in any new running program to any absolute or relative position on any monitor. And with a little more effort, relative to the other running programs.

I also use wmctrl to move and size my app windows. I use a lot of screens in development cycle.
Wayland will kill all of this.
There are a few hacks, but usually only within one desktop type environ or compositor.

900 lines. I'll share, if anyone wants.

david
Post Reply