note: I posted this in the WIP but it got lost amongst the great projects there. It belongs here anyway, and eventually in the codebase.
Version 1.1
Changes from 1.0
1. Added failsafe to keyinfo.txt not existing, program will read default keys via data statements
2. Each key is now set up with a timer.
remstart
=============================================================================
=============================================================================
Application Title: Keyboard Input Handler
Version: 1.1
Dev Language: DBPro
Platform: WinXP / DirectX9.0c(Dec. 2005) +
Developed by: Zenassem
E-mail: Zenassem@hotmail.com
Starting Date: 4/15/07
Target Completion Date: 4/22/07
Latest Update: 4/20/07
Current Status: W.I.P., Code Snippet
Projected Status: CodeBase Entry, DLL
License: Open Source - "Freeware"
-----
Thank You List:
Milkman - Helping with UDT limitations and possible solution
(Preprocessor OOP)
Ian M - For helping with Functions/parameters
(And for letting me know I'm the only Moron. =P)
Essence - For beta-testing, and suggestions
... More to be added
-----
Let me know of any suggestions you may have
==============================================================================
==============================================================================
remend
`%%% CONSTANTS
`@ KB_Key Abbreviations and Scancodes
#constant KB_ESC 1 `Escape
#constant KB_1 2 `1
#constant KB_2 3 `2
#constant KB_3 4 `3
#constant KB_4 5 `4
#constant KB_5 6 `5
#constant KB_6 7 `6
#constant KB_7 8 `7
#constant KB_8 9 `8
#constant KB_9 10 `10
#constant KB_0 11 `0
#constant KB_HYP 12 `-
#constant KB_EQU 13 `=
#constant KB_BKS 14 `Back Space
#constant KB_TAB 15 `TAB
#constant KB_Q 16 `Q
#constant KB_W 17 `W
#constant KB_E 18 `E
#constant KB_R 19 `R
#constant KB_T 20 `T
#constant KB_Y 21 `Y
#constant KB_U 22 `U
#constant KB_I 23 `I
#constant KB_O 24 `O
#constant KB_P 25 `P
#constant KB_LBR 26 `[
#constant KB_RBR 27 `]
#constant KB_RTN 28 `Return
#constant KB_LCTRL 29 `Left Control
#constant KB_A 30 `A
#constant KB_S 31 `S
#constant KB_D 32 `D
#constant KB_F 33 `F
#constant KB_G 34 `G
#constant KB_H 35 `H
#constant KB_J 36 `J
#constant KB_K 37 `K
#constant KB_L 38 `L
#constant KB_SCOL 39 `;
#constant KB_APOS 40 `'
#constant KB_TIL 41 `~
#constant KB_LSHFT 42 `Left Shift
#constant KB_BSLSH 43 `\
#constant KB_Z 44 `Z
#constant KB_X 45 `X
#constant KB_C 46 `C
#constant KB_V 47 `V
#constant KB_B 48 `B
#constant KB_N 49 `N
#constant KB_M 50 `M
#constant KB_COM 51 `,
#constant KB_PER 52 `.
#constant KB_FSLSH 53 `/
#constant KB_RSHFT 54 `Right Shift
#constant KB_NP_MUL 55 `*
#constant KB_LALT 56 `Left Alt
#constant KB_SPC 57 `Space Bar
#constant KB_CAPSL 58 `Caps Lock
#constant KB_F1 59 `F1
#constant KB_F2 60 `F2
#constant KB_F3 61 `F3
#constant KB_F4 62 `F4
#constant KB_F5 63 `F5
#constant KB_F6 64 `F6
#constant KB_F7 65 `F7
#constant KB_F8 66 `F8
#constant KB_F9 67 `F9
#constant KB_F10 68 `F10
#constant KB_F11 69 `F11
#constant KB_F12 70 `F12
#constant KB_NP_7 71 `Num Pad 7
#constant KB_NP_8 72 `Num Pad 8
#constant KB_NP_9 73 `Num Pad 9
#constant KB_NP_SUB 74 `Num Pad -
#constant KB_NP_4 75 `Num Pad 4
#constant KB_NP_5 76 `Num Pad 5
#constant KB_NP_6 77 `Num Pad 6
#constant KB_NP_ADD 78 `Num Pad +
#constant KB_NP_1 79 `Num Pad 1
#constant KB_NP_2 80 `Num Pad 2
#constant KB_NP_3 81 `Num Pad 3
#constant KB_NP_0 82 `Num Pad 0
#constant KB_NP_DEC 83 `Num Pad .
#constant KB_NP_ENT 156 `Num Pad Enter
#constant KB_RCTRL 157 `Right CTL
#constant KB_RALT 184 `Right Alt
#constant KB_HOM 199 `Home
#constant KB_UP 200 `Up Arrow
#constant KB_PGUP 201 `PgUp
#constant KB_LEFT 203 `Left Arrow
#constant KB_RIGHT 205 `Right Arrow
#constant KB_END 207 `END
#constant KB_DOWN 208 `Down Arrow
#constant KB_PGDN 209 `PgDn
#constant KB_INS 210 `Insert
#constant KB_DEL 211 `Delete
#constant KB_LWIN 219 `Left Windows
#constant KB_RWIN 220 `Right Windows
#constant KB_SPEC 221 `Special
`@Special keys may be diff't on other KB's
#constant KB_BRK 197 `Pause/Break
#constant KB_MEN 221 `Menu
#constant MAX_NUM_KEYS 255
#constant MAX_SC_BUFFER_SIZE 60
#constant MAX_COMBO_TIME 1000 `Combo Time currently set to 1 second
#constant MAX_NUM_COMBOS 3
#constant TRUE 1
#constant FALSE 0
#constant NSET 2
`%%% User Defined Types (UDT) Declerations
`@ KeyType = Storage for Individual key states
`(*)-Reminder- Change types to Boolean
kstate as boolean
scval as integer
kname as string
`@ KeyTpe = Storage for individual Keys
type KeyType
ScnCode as integer
KeyName as string
IsPressed as boolean
IsReleased as integer
IsHeld as boolean
KeyTimeInit as integer
KeyTimeHeld as integer
RecKeyTimeHeld as integer
endtype
`@ AllKeyStatType = Storage for Entire KeyBoard States
type AllKeyStatType
NumKeysPressed as integer
SimKeysPressed as string
NumKeysHeld as integer
ScBuffer as string
ScBufferLen as integer
CharPlHolder as integer
combo as string
InitTimer as integer
TimePassed as integer
NoEntry as boolean
endtype
`%%% Initialize UDTS
`--- Define & Init kb_KeyInfo Array
dim kb_KeyInfo(MAX_NUM_KEYS) as KeyType
for index=0 to MAX_NUM_KEYS
kb_KeyInfo(index).ScnCode=index
kb_KeyInfo(index).KeyName="NULL"
kb_KeyInfo(index).IsPressed=FALSE
kb_KeyInfo(index).IsReleased=NSET
kb_KeyInfo(index).IsHeld=FALSE
kB_KeyInfo(index).KeyTimeInit=0
kb_KeyInfo(index).KeyTimeHeld=0
next index
`--- Read data from keyinfo.txt - Fill kb_KeyInfo Array fields
if file exist ("keyinfo.txt")
open to read 1, "keyinfo.txt"
while file end(1)=FALSE
read string 1, scncode$ `(*)-Reminder- Find out about read byte, word, long
scncode=val(scncode$) `@ Convert string to integer
read string 1, keyname$
kb_KeyInfo(scncode).ScnCode=scncode
kb_KeyInfo(scncode).KeyName=keyname$
endwhile
close file 1
else
restore keyinfodat
for index=1 to 100
read scval: read kname
kb_KeyInfo(scval).ScnCode=scval
kb_KeyInfo(scval).KeyName=kname
next index
endif
`--- Define & Init kb_Stats
kb_Stats as AllKeyStatType
kb_Stats.NumKeysPressed=0
kb_Stats.SimKeysPressed=""
kb_Stats.NumKeysHeld=0
kb_Stats.ScBuffer=""
kb_Stats.ScBufferLen=0
kb_Stats.CharPlHolder=0
kb_Stats.Combo=""
kb_Stats.InitTimer=timer()
kb_Stats.TimePassed=0
kb_Stats.NoEntry=TRUE
`==============================================================================
`@ This STARTS the END-USER portion of Code to test the KBIH
`==============================================================================
`@ Minimal Screen and Sync setup
sync on
sync rate 0
backdrop off
`(!)-Note- END-USER's would provide list of combos to test KBIH Combo-Recoginition
dim ComboList$(MAX_NUM_COMBOS)
ComboList$(0)="205205205" `@ RIGHT , RIGHT , RIGHT
ComboList$(1)="200200200" `@ UP , UP , UP
ComboList$(2)="203203203" `@ LEFT , LEFT , LEFT
`@ Test Program Variables
FL_GameDone as boolean
FL_GameDone=FALSE
bval as boolean
strval as string
ival as integer
kbht as integer
`%%% BEGIN Main-Loop
while FL_GameDone=FALSE
`--------------------------------------------------------------------------
`(!)-Note- End-User would place this line at beginning of their Main-Loop Source
f_CompTimePassed()
`--------------------------------------------------------------------------
cls
gosub ProcAllKeyStates `@ Process Key State Changes
`Test Individual Key Functions
if f_IsKeyPressed(KB_A)
Print "Test f_IsKeyPressed(KB_A)"
strval=f_GetKeyName(KB_A)
print "Test f_GetKeyName "+strval
ival = f_GetScanCode("KB_A")
print "Test f_GetScanCode " +str$(ival)
sync
endif
if f_IsKeyReleased(KB_A)=1
print "Test f_IsKeyReleased(KB_A)"
kbht=f_GetKeyTimeHeld(KB_A)
print "KeyTimeHeld "+str$(kbht)
sync
sleep 1000
endif
if f_IsKeyHeld(KB_A)
print "Test f_IsKeyHeld(KB_A)"
sync
endif
`Test AllKeyState Functions
ival=f_GetNumKeysPressed()
print "Test f_GetNumKeysPressed " +str$(ival)
sync
if (f_IsNoEntry()) AND (f_GetTimePassed()>=MAX_COMBO_TIME) OR (f_GetBufferLen()>=MAX_SC_BUFFER_SIZE)
`@ Parse multiple Key History buffer
gosub ParseSCBuffer
endif
sync
`--------------------------------------------------------------------------
`(!)-Note- END USER would place these lines at the END of their Main-Loop Source
`@ These lines RESET the kb_timers if necessary
if f_GetTimePassed()>=MAX_COMBO_TIME
f_InitKbTimer()
endif
`--------------------------------------------------------------------------
endwhile
`%%% END Main-Loop
`==============================================================================
`@ This ENDS the END-USER Portion of Code to test the KBIH
`==============================================================================
`%%% Subroutines & Functions
`--- KeyType Functions
function f_IsKeyPressed(keycode as integer)
retbool as boolean
retbool=kb_KeyInfo(keycode).IsPressed
endfunction retbool
function f_IsKeyReleased(keycode as integer)
retval=kb_KeyInfo(keycode).IsReleased
endfunction retval
function f_IsKeyHeld(keycode as integer)
retbool as boolean
retbool=kb_KeyInfo(keycode).IsHeld
endfunction retbool
function f_GetKeyName(keycode as integer)
retstr as string
retstr=kb_KeyInfo(keycode).KeyName
endfunction retstr
function f_GetKeyTime(keycode as integer)
retval=kb_KeyInfo(keycode).KeyTimeheld
endfunction retval
function f_InitKeyTime(keycode as integer)
kb_KeyInfo(keycode).KeyTimeInit=timer()
endfunction
function f_CompKeyTimeHeld(keycode as integer)
kb_KeyInfo(keycode).KeyTimeHeld=(timer()-kb_KeyInfo(keycode).KeyTimeInit)
kb_KeyInfo(keycode).RecKeyTimeHeld=kb_KeyInfo(keycode).KeyTimeHeld
endfunction
function f_GetKeyTimeHeld(keycode as integer)
retval as integer
retval=(kb_KeyInfo(keycode).RecKeyTimeHeld)
endfunction retval
function f_ResetKeyTimeHeld(keycode as integer)
kb_KeyInfo(keycode).KeyTimeHeld=0
endfunction
function f_GetScanCode(keyname as string)
found as boolean
found=FALSE
index=0
repeat
if keyname=kb_KeyInfo(index).KeyName
found=TRUE
retval=kb_KeyInfo(index).ScnCode
endif
inc index
until (found)
if Not found
retval=0
endif
endfunction retval
`--- AllKeyStatType Functions
function f_GetNumKeysPressed()
retval=kb_Stats.NumKeysPressed
endfunction retval
function f_GetNumKeysHeld()
retval=kb_Stats.NumKeysHeld
endfunction retval
function f_AddToScBuffer(keycode as integer)
kb_Stats.ScBuffer=kb_Stats.ScBuffer+"/"+str$(keycode)
endfunction
function f_ClearScBuffer()
kb_Stats.ScBuffer=""
endfunction
function f_GetBufferLen()
retval=Len(kb_Stats.ScBuffer)
endfunction retval
function f_ClearCombo()
kb_Stats.Combo=""
endfunction
function f_InitKbTimer()
kb_Stats.InitTimer=timer()
kb_Stats.TimePassed=0
endfunction
function f_CompTimePassed()
kb_Stats.TimePassed=(timer()-kb_Stats.InitTimer)
endfunction
function f_GetTimePassed()
retval=kb_Stats.TimePassed
endfunction retval
function f_GetSimKeysPressed()
retstr as string
retstr=kb_Stats.SimKeysPressed
endfunction retstr
function f_IsNoEntry()
retbool as boolean
retbool=kb_Stats.NoEntry
endfunction retbool
ProcAllKeyStates:
`@ If no keys are pressed -> kb_No_Entry is true
if scancode()=0
kb_Stats.NoEntry=TRUE
kb_Stats.NumKeysPressed=0
kb_Stats.SimKeysPressed=""
kb_Stats.NumKeysHeld=0
else
kb_Stats.NoEntry=FALSE
endif
`(*)-Reminder- This needs to be optimized. No use looking for codes
` that don't exist. Based on programmer choice an new array or
` multiple arrays will be created for zones to shorten how many
` scancodes are actually searched.
`@ Search through all *possible scancodes
for index=0 to MAX_NUM_KEYS
kstate=keystate(index)
`@ If the key was released -> SET it to some arbitrary number other than 0 or 1
if (kb_KeyInfo(index).IsReleased=TRUE)
kb_KeyInfo(index).IsReleased=NSET
endif
`@ If the key was in a pressed state & now is not -> SET IsReleased to
` 1 & IsPressed to 0
if (kstate=0) AND (kb_KeyInfo(index).IsPressed=TRUE)
kb_KeyInfo(index).IsPressed=FALSE
kb_keyInfo(index).IsHeld=FALSE
kb_KeyInfo(index).IsReleased=TRUE
f_CompKeyTimeHeld(index)
f_ResetKeyTimeHeld(index)
dec kb_Stats.NumKeysPressed
endif
`@ If the key is pressed & was prev being pressed last check -> SET
` IsHeld to 1
if (kstate=1) AND (kb_KeyInfo(index).IsPressed=TRUE)
kb_KeyInfo(index).IsHeld=TRUE
endif
`@ If the key is pressed & NOT being held -> SET IsPressed to 1 & ADD the scancode
` to the buffer$
if (kstate=1) AND (kb_KeyInfo(index).IsHeld=FALSE)
kb_KeyInfo(index).IsPressed=TRUE
kb_KeyInfo(index).IsReleased=FALSE
f_InitKeyTime(index)
inc kb_Stats.NumKeysPressed
kb_Stats.ScBuffer=kb_Stats.ScBuffer+"/"+str$(kb_KeyInfo(index).ScnCode)
`(*)-Reminder- I am adding "/'s" to the the buffer only to parse them out later.
` Reason: In the future I need to prevent the condition that scancodes could
` converge to form a different scancode for combos. Implement that in the parser
` later. For example: scancode 20 and scancode 3 "should not" be confused with
` scancode 203.
endif
next index
return
ParseSCBuffer:
`(*)-Reminder- Implement accidental scancode convergence (noted above)
` ,also look for for shorter combos in a longer string.
`(*)-Reminder- All PRINT Statements will be removed from this SubRoutine
` !!!PRINT =TESTING PURPOSES ONLY!!!
`
` END-USER will Decide what they wish to do with All info Available
` these types of statements or Actions Based on Info would be the decision
` of the END-USER
`@ If the buffer is empty -> do nothing
if kb_Stats.ScBuffer<>""
kb_Stats.CharPlHolder=0
kb_Stats.ScBufferLen=len(kb_Stats.ScBuffer)
for index=0 to f_GetBufferLen()
if mid$(kb_Stats.ScBuffer,index)="/" OR index=kb_Stats.ScBufferLen
`@ If we reached the last scancode -> "DO NOT" lose the last character
if index=kb_Stats.ScBufferLen
inc index
endif
for index2=kb_Stats.CharPlHolder to index-1
kb_Stats.Combo=kb_Stats.Combo+mid$(kb_Stats.ScBuffer, index2)
next index2
kb_Stats.CharPlHolder=index+1
endif
next index
for index=0 to MAX_NUM_COMBOS
if kb_Stats.Combo=ComboList$(index)
endif
next index
`@ Reset Kb_Combo$
f_ClearCombo()
`@ Reset kb_Stats.ScBuffer=
f_ClearScBuffer()
endif
return
keyinfodat:
data 1,"KB_ESC",2,"KB_1",3,"KB_2",4,"KB_3",5,"KB_4"
data 6,"KB_5",7,"KB_6",8,"KB_7",9,"KB_8",10,"KB_9"
data 11,"KB_0",12,"KB_HYP",13,"KB_EQU",14,"KB_BKS",15,"KB_TAB"
data 16,"KB_Q",17,"KB_W",18,"KB_E",19,"KB_R",20,"KB_T"
data 21,"KB_Y",22,"KB_U",23,"KB_I",24,"KB_O",25,"KB_P"
data 26,"KB_LBR",27,"KB_RBR",28,"KB_RTN",29,"KB_LCTRL",30,"KB_A"
data 31,"KB_S",32,"KB_D",33,"KB_F",34,"KB_G",35,"KB_H"
data 36,"KB_J",37,"KB_K",38,"KB_L",39,"KB_SCOL",40,"KB_APOS"
data 41,"B_TIL",42,"KB_LSHFT",43,"KB_BSLSH",44,"KB_Z",45,"KB_X"
data 46,"KB_C",47,"KB_V",48,"KB_B",49,"KB_N",50,"KB_M"
data 51,"KB_COM",52,"KB_PER",53,"KB_FSLSH",54,"KB_RSHFT",55,"KB_NP_MUL"
data 56,"KB_LALT",57,"KB_SPC",58,"KB_CAPSL",59,"KB_F1",60,"KB_F2"
data 61,"KB_F3",62,"KB_F4",63,"KB_F5",64,"KB_F6",65,"KB_F7"
data 66,"KB_F8",67,"KB_F9",68,"KB_F10",69,"KB_F11",70,"KB_F12"
data 71,"KB_NP_7",72,"KB_NP_8",73,"KB_NP_9",74,"KB_NP_SUB",75,"KB_NP_4"
data 76,"KB_NP_5",77,"KB_NP_6",78,"KB_NP_ADD",79,"KB_NP_1",80,"KB_NP_2"
data 81,"KB_NP_3",82,"KB_NP_0",83,"KB_NP_DEC",156,"KB_NP_ENT",157,"KB_RCTRL"
data 184,"KB_RALT",197,"KB_BRK",199,"KB_HOM",200,"KB_UP",201,"KB_PGUP"
data 203,"KB_LEFT",205,"KB_RIGHT",207,"KB_END",208,"KB_DOWN",209,"KB_PGDN"
data 210,"KB_INS",211,"KB_DEL",219,"KB_LWIN",220,"KB_RWIN",221,"KB_SPEC"
I hope this code is useful to someone. When coding games with more complexity, it can be difficult (especially for newcomers) to keep track of what is happening at the Keyboard. DBpro native commands alone, don't cover a lot of scenarios by themselves. Including detecting multiple key presses and the like. The Entry$ buffer doesn't serve every purpose either.
So I decided to spend some time and code a Keyboard Input Handler. Basically it's group of functions (like a library), that allow coders a more stream-lined, simplified, and accurate way to read input from the keyboard and perform logic based on the results.
Please feel free to give comments and suggestions.
It's almost complete. Things I am currently working on
-The individual key timers
-A second array that lets users decide which keys to enable from a file. That will also allow them to customize the key names to jump, fire, or whatever. This will increase efficiency, as right now the code goes from0 to 255 to process scancodes. A lot of which don't even exist.
-Finding Combos within the string (for fighting games and such). Right now it looks for an exact match. It doesn't find codes within a longer string.
-Some other tweaks, and code cleanup.
The code contains a main loop that tests out some of the functions.
Attached is a .txt file. This file, will eventually be used by the programmer, to define which keys need to be monitored as well as custom names.