vdecampo
redcrab
Paul Doe
freebasic wiki - screenevent
Multikey is the go to keyword to use when you need key input handling in your program that is a little more intelligent than inkey/getkey.
The wiki has good examples of how to use it.
Multikey polls the keyboard and internally stores a snapshot of the state of the keyboard.
This is done very fast and a single press of a key can lead to a long list of repeated key is down events (for lack of a better term)(They are not events).
In the following code pressing <left> or <right> in an ordinary fashion will print text at least 3 times (and it is even more when the sleep in the loop is shortened).
Code: Select all
#include "fbgfx.bi"
#if __FB_LANG__ = "fb"
Using FB '' Scan code constants are stored in the FB namespace in lang FB
#endif
Do
If MultiKey(SC_LEFT ) Then Print "left "
If MultiKey(SC_RIGHT) Then Print "right"
Sleep 15, 1
' Run loop until user presses Escape
Loop Until MultiKey(SC_ESCAPE)
In this tip I built a TMultiKey object that encapsulates the Multikey key input methodology.
It provides a property to determine if the key is released. While a key check with multikey will report -1 (key down)
as long as it is held and for as many times as it is checked in that loop, TMultiKey will report only once (per press) that the key has
been released.
Here is a description of the code and following it, the code itself:
At the start we include fbgfx.bi so that we have access to the keyboard scan codes.
The enum TKeyState defines three states ksUP = 0, ksDOWN = 1, ksRELEASE = 2
ksUP is the idle state of a key ie it has not been pressed since last polled.
The constant RELEASE_TIMEOUT is used to determine how long to retain the released state after the key is pressed.
If the key state is ksRELEASE, the key will revert back to ksUP after approximately 1 second or after its status has been checked.
I use a time check to revert the state of a key back to ksUP. Another option would be to leave the key in the ksRELEASE state
revert it only after it has been checked (maybe for another version).
Without this, the key would retain its ksRELEASED state until the next time it is pressed (not so useful imo).
The type TKey is an object representation of a single key. Its fields and properties are self explanatory. I will note that state_time is used to
store the time at which the key state changed from ksDOWN to ksRELEASE.
The type TMultiKey is the multikey handling encapsulation object.
Keys is an array of TKey used to store the state of each key.
The properties are self explanatory. I will note that the property is_key_release() will set the key state to ksUP.
The sub poll is to be used in the main loop where you would ordinarily check multikey ex. 'if multikey(sc_left) = -1'. Instead use
'[TMultiKeyInsttance].poll()'. This statement sets the state of each keyboard key.
There are 100 predefined keyboard keys on the standard keyboard. To be more accurate, there are just under 100 predefined scan code enums
in fbgfx.
The sub poll() loops through each scan code and sets the state of each key. This could easily be refined to suit your specific needs
and loop through only the keys you are going to use in your program. I did it this way so that the demo could use any key. Its still
fast. Just know that each time poll() is called a 1 to 100 for/next loop will run. This is not designed to be used in a tight loop where
every fraction of a millisecond counts.
The subs _set_keydown_state() and _set_keyup_state() execute the following logic (for each key):
if multikey returns anything other than -1(-1 is key down):
if the key state = ksUP: no action
orelse if the key state = ksDOWN: state_time is set to timer, key state is set to ksRELEASE
orelse if the key state = ksRELEASE:
if this state has existed for longer than RELEASE_TIMEOUT then set the key state to ksUP
if multikey returns -1:
the key state is set to ksDOWN
Here is the code with a simple code test at the end (press <left> or <right> arrow key or <esc> to exit):
Code: Select all
#Include Once "fbgfx.bi"
Using fb
Enum TKeyState
ksUP
ksDOWN
ksRELEASE
End Enum
Const As Double RELEASE_TIMEOUT = 1
'------------------------------------------------------------------------------------------------
Type TKey
As Long scan_code
As TKeyState state
As Double state_time
Declare Property is_down() As Boolean
Declare Property is_up() As Boolean
Declare Property is_release() As Boolean
End Type
Property TKey.is_down() As Boolean
'
Return cbool(this.state = ksDOWN)
End Property
Property TKey.is_up() As Boolean
'
return cbool(this.state = ksUP)
End Property
Property TKey.is_release() As Boolean
'
Return cbool(this.state = ksRELEASE)
End Property
'------------------------------------------------------------------------------------------------
Type TMultiKey
As TKey keys(1 To 100)
Declare Sub poll()
Declare Property is_down(Byval key_code As long) As Boolean
Declare Property is_up(Byval key_code As long) As Boolean
Declare Property is_release(Byval key_code As long) As Boolean
Private:
Declare Sub _set_keydown_state(Byval key_code As Long)
Declare Sub _set_keyup_state(Byval key_code As long)
End Type
sub TMultiKey.poll()
'
With This
For i as Integer = 1 To 100
If Multikey(i) = -1 Then
._set_keydown_state(i)
Else
._set_keyup_state(i)
Endif
Next
end With
End Sub
Property TMultiKey.is_down(Byval key_code As long) As Boolean
'
Return this.keys(key_code).is_down
End Property
Property TMultiKey.is_up(Byval key_code As long) As Boolean
'
Return this.keys(key_code).is_up
End Property
Property TMultiKey.is_release(Byval key_code As long) As Boolean
'
Property = this.keys(key_code).is_release
If this.keys(key_code).is_release = TRUE Then
this.keys(key_code).state = ksUP
EndIf
End Property
Sub TMultiKey._set_keydown_state(Byval key_code As long)
'
With This
Dim As TKey Ptr pKey = @.keys(key_code)
pKey->state = ksDOWN
End With
End Sub
Sub TMultiKey._set_keyup_state(Byval key_code As long)
'
with This
Dim As TKey Ptr pKey = @.keys(key_code)
If pKey->state = ksDOWN Then
pKey->state_time = timer
pKey->state = ksRELEASE
Elseif pKey->state = ksRELEASE Then
If Timer - pKey->state_time >= RELEASE_TIMEOUT Then
pKey->state = ksUP
End If
End If
End With
End Sub
'-------------------------------
'-------------------------------
Dim As TMultiKey mk
Do
mk.poll()
If mk.is_release(sc_left) = TRUE Then Print "<left>"
If mk.is_release(sc_right) = TRUE Then Print "<right>"
Loop While mk.is_down(sc_escape) = FALSE
It is pretty simple stuff and I would be surprised if it hasn't already been a tip (I didn't look, but I will now).
I will probably monkey with this a bit more and post if I add anything that I like.