HomePage | Optical Illusions | War Stories | QBasic | Dads Navy Days | Bristol | Bristol, USA | Bristol, Canada | Terre Haute | Miscellany | Web Stuff | About Ray | Site Map | Site Search | Messages | Credits | Links | Web Rings
QBasic | Errors | 40lb Weight | Bits | Chance | Colours | Dates | Delays | File Dialog | Files | Input | Matching | Menus | Mouse | Numbers | SeqNo | SIRDS | Sorts | Text | Timer | DLoads
Press any key to continue ...
How many times have you seen that? The following program, Wait.bas, shows you how it's done.
'Wait.bas Ray Thomas February 2002 DIM Cmmnd AS STRING 'User input CLS PRINT PRINT "Press any key to continue ..." PRINT DO LOOP UNTIL INKEY$ <> "" PRINT PRINT "Press X to continue ..." PRINT DO Cmmnd$ = UCASE$(INKEY$) LOOP UNTIL Cmmnd$ = "X" PRINT PRINT "Press Y or Z to continue ..." PRINT DO Cmmnd$ = UCASE$(INKEY$) LOOP UNTIL Cmmnd$ = "Y" OR Cmmnd$ = "Z" PRINT PRINT "Press A or B to continue ..." PRINT DO Cmmnd$ = UCASE$(INKEY$) LOOP UNTIL INSTR(" AB", Cmmnd$) > 1 PRINT PRINT "Press Alt + C to continue ..." PRINT DO Cmmnd$ = RIGHT$(UCASE$(INKEY$), 1) LOOP UNTIL Cmmnd$ = CHR$(46) PRINT PRINT "Press Alt + X to continue ..." PRINT DO LOOP UNTIL RIGHT$(INKEY$, 1) = CHR$(45) PRINT PRINT "Press ESC key to end the program" DO LOOP UNTIL INKEY$ = CHR$(27)
All the program does is wait for various user inputs before it carries on to the next piece of code. Most of it should be pretty straightforward, but some needs explaining. INKEY$ contains nothing ("") until a key is pressed, it then contains the keyboard scan code of the key pressed while the key is pressed, before returning to "".
In the piece of code :-
DO Cmmnd$ = UCASE$(INKEY$) LOOP UNTIL INSTR(" AB", Cmmnd$) > 1
The space in the INSTR instruction and the > 1 are there as otherwise the loop would exit on the first pass. If the line were :-
CmmnD$ = "" DO Cmmnd$ = UCASE$(INKEY$) LOOP UNTIL INSTR("AB", Cmmnd$) > 0
(or Cmmnd$ = " ") even the code looks logical enough, the loop would exit before a key was pressed.
In the piece of code :-
PRINT PRINT "Press Alt + X to continue ..." PRINT DO LOOP UNTIL RIGHT$(INKEY$, 1) = CHR$(45)
It doesn't matter if the upper or lower "X" is used, the code returned will still be the same, CHR$(45). The RIGHT$ function is there because pressing ALT + X returns a two character code, and it is the second character code that differentiates the key from any other that may be pressed. The program Kbrdcode.bas, later on this page, shows you how to determine the keyboard scan codes returned for various combinations of key presses.
User Input Verification:
Although QBasic can trap lots of errors, see Errors, it will not allow you to design your own error handlers in the case of a user inputting a number larger than 32,767 into an integer. Instead it brings up a "retry from start" message that is certain to wreck any screen design that you might have. QBasic most certainly will not provide an input mask. Suppose you ask a user to input a letter between A and F and they type in "G" or a number, what happens then? Can your program cope with this?
What is needed is some sort of 'behind the scenes' input checker that can verify the user input. INPUT and INPUT$ are both useful but limited for this kind of work because you don't know what the user has typed until they press the Enter key. A much better alternative is INKEY$ which returns the code of the key as soon as it is pressed.
A problem with INKEY$ is that all keyboard input is trapped, including carriage returns, backspaces etc, these "non-printable" characters must be catered for. An example of this is shown in the program below, if the user presses the backspace key to erase a character from their input string then this must be done by the programmer.
Screenshot from userin.bas
'Userin Ray Thomas May 2000 'A program to demonstrate user input routines DECLARE SUB DrawBox (XPosn!, YPosn!) DECLARE SUB GetInput () DECLARE SUB DrawScrn () DECLARE SUB ChkInput (user$) DO CLS PRINT PRINT "Just press Enter to exit this part of the program." PRINT INPUT "Type a letter between A and E or a number between 3 and 7 ", user$ PRINT PRINT IF user$ > "" THEN ChkInput (user$) LOOP UNTIL user$ = "" CLS PRINT PRINT "Although useful, the problem with the last routine is that the" PRINT "program doesn't know what the user input is before enter is pressed." PRINT PRINT "The following routine is much more sophisticated." CALL DrawScrn CALL GetInput END SUB ChkInput (user$) IF LEN(user$) > 1 THEN PRINT "One character only!" UserErr = 1 END IF IF INSTR("ABCDE34567", UCASE$(user$)) = 0 THEN PRINT "Wrong input!" UserErr = 1 END IF IF UserErr = 0 THEN PRINT "Well done, you know what you are doing." UserErr = 0 PRINT PRINT "Press any key to continue ..." DO LOOP UNTIL INKEY$ <> "" END SUB SUB DrawBox (XPosn, YPosn) 'The CHR$ codes are the extended character codes LOCATE YPosn, XPosn PRINT CHR$(201); PRINT STRING$(22, CHR$(205)); PRINT CHR$(187); LOCATE YPosn + 1, XPosn PRINT CHR$(186); LOCATE YPosn + 1, XPosn + 23 PRINT CHR$(186); LOCATE YPosn + 2, XPosn PRINT CHR$(200); PRINT STRING$(22, CHR$(205)); PRINT CHR$(188); END SUB SUB DrawScrn LOCATE 8, 2 PRINT "This box will only accept characters and will automatically upper-case them" CALL DrawBox(24, 9) LOCATE 12, 20 PRINT "This box will only accept figures" CALL DrawBox(24, 13) LOCATE 16, 15 PRINT "This box will change the characters entered" CALL DrawBox(24, 17) LOCATE 21, 15 PRINT "Press ENTER when input for each box is done" LOCATE 22, 24 PRINT "Press ESC when finished" END SUB SUB GetInput DIM UserMsg(4) AS STRING DIM YPosn(3) AS INTEGER DIM UserIn AS STRING YPosn(0) = 10 YPosn(1) = 14 YPosn(2) = 18 '*** Draw the initial colours *** YPosn = 14 FOR Count = 1 TO 2 LOCATE YPosn(Count), 25 COLOR , 1 PRINT STRING$(22, " ") NEXT Count LineNo = 0 LOCATE YPosn(LineNo), 25 COLOR 7, 2 PRINT STRING$(22, " ") DO UserIn$ = INKEY$ IF UserIn$ <> "" THEN '*** If enter is pressed redraw the boxes in a new colour *** IF UserIn$ = CHR$(13) THEN COLOR 7, 1 LOCATE YPosn(LineNo), 25 PRINT STRING$(22, " "); LOCATE YPosn(LineNo), 25 PRINT UserMsg$(LineNo); LineNo = LineNo + 1 IF LineNo = 3 THEN LineNo = 0 LOCATE YPosn(LineNo), 25 COLOR 7, 2 PRINT STRING$(22, " "); COLOR 1 LOCATE YPosn(LineNo), 25 PRINT UserMsg$(LineNo); END IF OldStr$ = UserMsg$(LineNo) '*** check the input for the first box *** IF LineNo = 0 THEN IF INSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZ", UCASE$(UserIn$)) > 0 AND LEN(UserMsg$(LineNo)) < 22 THEN UserMsg$(LineNo) = UserMsg$(LineNo) + UCASE$(UserIn$) END IF END IF '*** check the input for the second box *** IF LineNo = 1 THEN IF INSTR("1234567890", UCASE$(UserIn$)) > 0 AND LEN(UserMsg$(LineNo)) < 22 THEN UserMsg$(LineNo) = UserMsg$(LineNo) + UCASE$(UserIn$) END IF END IF '*** check the imput for the third box *** '*** keep the actual entires and the askerisks in seperate elements *** IF LineNo = 2 THEN IF INSTR("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ", UCASE$(UserIn$)) > 0 AND LEN(UserMsg$(LineNo + 1)) < 22 THEN UserMsg$(LineNo + 1) = UserMsg$(LineNo + 1) + UserIn$ UserMsg$(LineNo) = STRING$(LEN(UserMsg$(LineNo + 1)), "*") END IF END IF '*** if Backspace is pressed remove a character from the chosen string *** IF UserIn$ = CHR$(8) AND LEN(UserMsg$(LineNo)) >= 1 THEN UserMsg$(LineNo) = LEFT$(UserMsg$(LineNo), LEN(UserMsg$(LineNo)) - 1) END IF '*** if the strings have changed then redraw the text *** IF OldStr$ <> UserMsg$(LineNo) OR UserIn$ = CHR$(8) THEN COLOR 10, 2 LOCATE YPosn(LineNo), 25 PRINT STRING$(22, " ") LOCATE YPosn(LineNo), 25 PRINT UserMsg$(LineNo) END IF '*** print the unencoded version of the third box *** IF LineNo = 2 THEN COLOR 7, 0 LOCATE 18, 50 PRINT STRING$(24, " ") LOCATE 18, 50 UserMsg$(3) = LEFT$(UserMsg$(3), LEN(UserMsg$(2))) PRINT "("; UserMsg$(3); ")" END IF END IF LOOP UNTIL UserIn$ = CHR$(27) '*** if ESC is pressed end the loop *** END SUB
User input is not just limited to A - Z or 0 - 9, what happens if you want to do something more elaborate, like opening a help screen when the user presses F1, or you've made a menu and want the user to navigate around them using the arrow keys, think of the possibilities of using the cursor keys to navigate around game screens!
You can also test if the capslock is on, whether the Alt or Ctrl keys are being held down when a key is pressed. Thus, pressing the letter H on the keyboard can produce the ASCII codes 8, 035, 72 or 104 depending what other key, if any, was held down at the same time.
The following program can be used to show the codes returned by the keyboard, even non-printable ones like the function or cursor keys. When you run the program it can be exited by pressing the Esc key.
'KBRDCODE Ray Thomas June 2000 'A program to show the codes returned by the keyboard on a keypress CLS Count = 0 DO IF Count MOD 20 = 0 THEN GOSUB Titles DO UserIn$ = INKEY$ LOOP UNTIL UserIn$ <> "" PRINT LEN(UserIn$); TAB(10); LEFT$(UserIn$, 1); TAB(20); ASC(UserIn$) IF LEN(UserIn$) = 2 THEN UserIn$ = RIGHT$(UserIn$, 1) LOCATE CSRLIN - 1, 30 PRINT UserIn$; TAB(40); ASC(UserIn$) END IF Count = Count + 1 LOOP UNTIL UserIn$ = CHR$(27) END Titles: PRINT PRINT TAB(10); "Characteristics of UserIn$" PRINT PRINT "Length"; TAB(10); "1st Char"; TAB(20); "Asc"; TAB(30); "2nd Char"; TAB(40); "ASC" PRINT RETURN
The next program, XHair.bas, demonstrates how, using these codes, you can move a cross-hair around the screen. This program will set your monitor to SCREEN 12 (640 x 480 VGA) mode. When you run the program it can be exited by pressing the Esc key. The program also shows how to save part of a graphics screen to an array using GET and write it back to the screen using PUT
Screenshots from XHair.bas
'XHair Ray Thomas June 2000 'Program to show the movement of a cross-hair 'against a multi-coloured background DIM XCent AS INTEGER 'XPosn of centre of cross-hair DIM YCent AS INTEGER 'YPosn of centre of cross-hair DIM Grafix%(1 TO 300) 'Array to hold graphics image DIM GXPlosn%(1 TO 1000) 'Array to hold XPlosn graphic YCent = 240 XCent = 320 Move = 30 'Larger number is faster, but jerkier XCol = 1 'Cursor colour SCREEN 12 '640 x 480 pixel VGA screen RANDOMIZE TIMER GOSUB BGround GET (XCent - 15, YCent - 15)-(XCent + 15, YCent + 15), Grafix% GOSUB DrawX '*** move the cursor *** DO DO UserIn$ = INKEY$ LOOP UNTIL UserIn$ <> "" IF LEN(UserIn$) = 2 THEN UserIn$ = RIGHT$(UserIn$, 1) IF XCent < 640 AND YCent < 480 AND XCent > 10 AND YCent > 0 THEN PUT (XCent - 15, YCent - 15), Grafix%, PSET END IF SELECT CASE UserIn$ CASE "1" XCent = XCent - Move YCent = YCent + Move CASE "2" YCent = YCent + Move CASE "3" XCent = XCent + Move YCent = YCent + Move CASE "4" XCent = XCent - Move CASE "6" XCent = XCent + Move CASE "7" XCent = XCent - Move YCent = YCent - Move CASE "8" YCent = YCent - Move CASE "9" XCent = XCent + Move YCent = YCent - Move CASE CHR$(13) GOSUB XPlosn GOSUB Destroy CASE CHR$(32) GOSUB XPlosn GOSUB Destroy CASE CHR$(71) XCent = XCent - Move YCent = YCent - Move CASE CHR$(72) YCent = YCent - Move CASE CHR$(73) XCent = XCent + Move YCent = YCent - Move CASE CHR$(75) XCent = XCent - Move CASE CHR$(77) XCent = XCent + Move CASE CHR$(79) XCent = XCent - Move YCent = YCent + Move CASE CHR$(80) YCent = YCent + Move CASE CHR$(81) XCent = XCent + Move YCent = YCent + Move END SELECT IF XCent < 10 THEN XCent = 640 IF YCent < 0 THEN YCent = 480 IF XCent > 640 THEN XCent = 10 IF YCent > 480 THEN YCent = 0 IF XCent > 10 AND YCent > 0 AND XCent < 640 AND YCent < 480 THEN GET (XCent - 15, YCent - 15)-(XCent + 15, YCent + 15), Grafix% GOSUB DrawX END IF LOOP UNTIL UserIn$ = CHR$(27) END DrawX: CIRCLE (XCent, YCent), 10, XCol 'Draw a circle LINE (XCent, YCent - 15)-(XCent, YCent + 15), XCol 'Vert line LINE (XCent - 15, YCent)-(XCent + 15, YCent), XCol 'Horz line RETURN BGround: FOR Count = 1 TO 5000 PSET (INT(RND * 640), INT(RND * 480)), INT(RND * 15) NEXT Count CIRCLE (200, 240), 100, 2 PAINT (200, 240), 2, 2 RETURN XPlosn: IF XCent > 20 AND XCent < 620 AND YCent > 5 AND YCent < 475 THEN FOR Counter = 1 TO 5 GET (XCent - 25, YCent - 25)-(XCent + 25, YCent + 25), GXPlosn% CIRCLE (XCent, YCent), 25, 4 PAINT (XCent, YCent), 4, 4 CIRCLE (XCent, YCent), 17, 6 PAINT (XCent, YCent), 6, 6 CIRCLE (XCent, YCent), 10, 2 PAINT (XCent, YCent), 2, 2 CIRCLE (XCent, YCent), 5, 7 PAINT (XCent, YCent), 7, 7 FOR Count = 1 TO 3000 NEXT Count CIRCLE (XCent, YCent), 25, 0 PAINT (XCent, YCent), 0, 0 FOR Count = 1 TO 3000 NEXT Count XCalc = ((XCent / 10) - 20) * ((XCent / 10) - 20) YCalc = ((YCent / 10) - 24) * ((YCent / 10) - 24) IF SQR(XCalc + YCalc) > 10 THEN PUT (XCent - 25, YCent - 25), GXPlosn%, PSET END IF NEXT Counter END IF RETURN Destroy: IF SQR(XCalc + YCalc) < 10 THEN GET (XCent - 25, YCent - 25)-(XCent - 25, YCent - 25), GXPlosn% FOR Count = 1 TO 30 PSET (XCent - 22 + INT(RND * 45), YCent - 22 + INT(RND * 45)), INT(RND * 15) NEXT Count END IF RETURN
The larger the movement of the cursor (the value of Move) the quicker the cursor will move, but it becomes very jerky with values of around 40. The GET and PUT functions saves a small square image just big enough to fit the cursor in (in this case 30 x 30 pixels). If the value of Move is less than the size of the cursor then GET will also have to save a small portion of the cursor, and this will get drawn back to the screen with the PUT function.
The SELECT statements tests for nearly all the keypad keys, whether Num Lock is on or off, which lets the cursor move in 1 of 8 drections. The statement also tests to see if the Space Bar or Enter key has been pressed.
The complicated IF statements are there because when writing this I found that the X and Y co-ordinates of the GET and PUT statements had to be limited as the program would produce an error because I was trying to write outside of the screen co-ordinates.
As a further demonstration of what can be achieved there is a small special effect when the Enter key or Space Bar is pressed. This is enhanced when the cursor is over the green circle. The maths used in the effect is the equation of a line. I needed to know if the cursor was within the area of the circle, ie the centre of both circles was less than the radius of the big one. This is given by the equation
distance = Square root of (x2 - x1)^2 + (y2 - y1)^2
The programs on this page, like all the programs written for this site, can be downloaded from the DLoads page.
QBasic | Errors | 40lb Weight | Bits | Chance | Colours | Dates | Delays | File Dialog | Files | Input | Matching | Menus | Mouse | Numbers | SeqNo | SIRDS | Sorts | Text | Timer | DLoads
HomePage | Optical Illusions | War Stories | QBasic | Dads Navy Days | Bristol | Bristol, USA | Bristol, Canada | Terre Haute | Miscellany | Web Stuff | About Ray | Site Map | Site Search | Messages | Credits | Links | Web Rings