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

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

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


GoStats stats counter