
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