'===========================================================================
' Subject: MOUSE DEMO                         Date: 01-25-96 (00:00)       
'  Author: Hardin Brothers                    Code: QB, QBasic, PDS        
'  Origin: kwmelvin@nr.infi.net             Packet: MOUSE.ABC
'===========================================================================
'----------------------------------------------------------------------------
'    FileName:  MOUSEDEMO.BAS for QBasic 1.x
'      Author:  Hardin Brothers
' Publication:  _DOS World_ issue #25 January 1996 pp.51-57
' DW Internet:  75300.2361@compuserve.com
'      DW BBS:  603-924-3181 [8-N-1]
'  DW Address:  DOS World, 86 Elm St., Peterborough, NH 03458  USA
'    Comments:  To find out what the mouse is doing, your program must "talk"
'            :  to the mouse driver. A mouse and driver must be installed
'            :  for this program to work. Then your program must make requests
'            :  of the mouse driver by calling Interrupt 33 hexadecimal. (An
'            :  Interrupt is similar to a CALL or GOSUB in QBasic, but instead
'            :  of working within a program, it lets one program call another,
'            :  without worrying about where the second program is in memory.
'            :
' Essential Mouse Services
' Procedure  :  Purpose
'------------:---------------------------------------------------------------
'   MouseCall:  Calls mouse and passes it a command via CPU registers.
'            :
'    MouseInt:  Initializes mouse and reports number of buttons. Returns 0
'            :  if mouse driver unavailable or mouse not present.
'            :
'  MouseSetup:  Sets up mouse machine-language routine. You must call this
'            :  subprocedure before calling any other service!
' -----------:---------------------------------------------------------------
' Optional Mouse Services   
' Procedure  :  Purpose
' -----------:---------------------------------------------------------------
'   MouseShow:  Displays mouse cursor.
'            :
'   MouseHide:  Hides mouse cursor. To avoid appearance of "ghost" cursors,
'            :  use this service before printing or updating screen.
'            :
' MouseGetPr_:  Gets information about last mouse click. Shows which button
'essInfo     :  is down, number of clicks since last call, vertical and
'            :  horizontal position of cursor at last mouse click.
'            :
' MouseGetRe_:  Gets information about last release of mouse buttons. Use
'leaseInfo   :  MouseGetPressInfo and MouseGetReleaseInfo together to let
'            :  user drag objects around screen.
'            :
' MouseGetSt_:  Gets information on current status of mouse.
'atus        :
' MouseReadC_:  Returns horizontal and vertical position of mouse cursor,
'ounters     :  relative to its location last time function called.
'            :
' MouseSetHo_:  Restricts mouse movement by setting minimum and maximum
'rzRange     :  horizontal range of mouse cursor.
'            :
' MouseSetVe_:  Restricts mouse movement by setting minimum and maximum
'rtRange     :  vertical range of mouse cursor.
'            :
'MouseSetPosn:  Sets mouse cursor to correspond to values of HPosn% and VPosn%
'            :
'            :
'----------------------------------------------------------------------------
DECLARE SUB Demo3 ()
DECLARE SUB Demo2 ()
DECLARE SUB Demo1 ()
DECLARE FUNCTION MouseInit% ()
DECLARE SUB MouseCall ()
DECLARE SUB MouseGetPressInfo (LBtn%, RBtn%, MBtn%, Count%, HPosn%, VPosn%)
DECLARE SUB MouseGetReleaseInfo (LBtn%, RBtn%, MBtn%, Count%, HPosn%, VPosn%)
DECLARE SUB MouseGetStatus (LBtn%, RBtn%, MBtn%, HPosn%, VPosn%)
DECLARE SUB MouseHide ()
DECLARE SUB MouseReadCounters (HCount%, VCount%)
DECLARE SUB MouseSetHorizRange (HMin%, HMax%)
DECLARE SUB MouseSetPosn (HPosn%, VPosn%)
DECLARE SUB MouseSetup ()
DECLARE SUB MouseSetVertRange (VMin%, VMax%)
DECLARE SUB MouseShow ()

MouseInt:
' Machine language routine 40 bytes long:
DATA 55          : 'PUSH BP
DATA 89, E5      : 'MOV  BP, SP
DATA 56          : 'PUSH SI
DATA 8B, 76, 06  : 'MOV  SI,[BP+6]
DATA 8B, 04      : 'MOV  AX,[SI]
DATA 8B, 5C, 02  : 'MOV  BX,[SI+2]
DATA 8B, 4C, 04  : 'MOV  CX,[SI+4]
DATA 8B, 54, 06  : 'MOV  DX,[SI+6]
DATA 8E, 44, 08  : 'MOV  ES,[SI+8]
DATA CD, 33      : 'INT  33h
DATA 8C, 44, 08  : 'MOV  [SI+8],ES
DATA 89, 54, 06  : 'MOV  [SI+6],DX
DATA 89, 4C, 04  : 'MOV  [SI+4],CX
DATA 89, 5C, 02  : 'MOV  [SI+2],BX
DATA 89, 04      : 'MOV  [SI],AX
DATA 5E          : 'POP     SI
DATA 5D          : 'POP  BP
DATA CB          : 'RETF

TYPE MouseRegs
  AX AS INTEGER
  BX AS INTEGER
  CX AS INTEGER
  DX AS INTEGER
  ES AS INTEGER
END TYPE

DIM SHARED MReg AS MouseRegs
REDIM SHARED MouseRoutine%(0 TO 19)
DIM SHARED MouseReady%
MouseReady% = 0
MouseSetup
MouseReady% = MouseInit

DEFINT A-Z

CLS
TYPE TextMask
  Bg AS INTEGER
  Fg AS INTEGER
  Char AS INTEGER
  Mask AS INTEGER
END TYPE

IF MouseReady% THEN
  Demo1
  Demo2
  Demo3
ELSE
  CLS
  PRINT "No Mouse available for demonstration"
END IF
END

SUB Demo1
'*
'* display the mouse
'* and its current status
'*
  CLS
  PRINT , , "Press Esc to end"
  PRINT "Mouse has"; MouseReady%; "buttons";
  MouseShow
  DO
    CALL MouseGetStatus(L%, R%, M%, Horiz%, Vert%)
    LOCATE 3, 1
    IF L% THEN PRINT "Left Dn";  ELSE PRINT "Left Up";
    IF MouseReady > 2 THEN
      IF M% THEN PRINT " -- Mid Dn";  ELSE PRINT " -- Mid Up";
    END IF
    IF R% THEN PRINT " -- Right Dn";  ELSE PRINT " -- Right Up";
    LOCATE 4, 1
    PRINT USING "Mouse at column ### and row ###"; Horiz%; Vert%
    Esc$ = INKEY$
  LOOP UNTIL Esc$ = CHR$(27)
  MouseHide
END SUB

SUB Demo2
  RANDOMIZE TIMER
  MouseShow
  DO
    CLS
    PRINT "Click left button for new window - right button to end. "
    Top = INT(RND * 15) + 2
    Vert = INT(RND * (22 - Top)) + 3
    Left = INT(RND * 60) + 1
    Horiz = INT(RND * (77 - Left)) + 3
    LOCATE Top, Left
    PRINT CHR$(201); STRING$(Horiz, 205); CHR$(187);
    FOR I = 1 TO Vert
      LOCATE Top + I, Left
      PRINT CHR$(186); STRING$(Horiz, 32); CHR$(186);
    NEXT I
    LOCATE Top + Vert, Left
    PRINT CHR$(200); STRING$(Horiz, 205); CHR$(188);
    CALL MouseSetHorizRange(Left * 8, (Left + Horiz - 1) * 8)
    CALL MouseSetVertRange(Top * 8, (Top + Vert - 2) * 8)
Demo2LP:
  L = 0: R = 1: M = 0
  CALL MouseGetPressInfo(L, R, M, CNT, H, V)
  IF R THEN
    MouseHide
    EXIT SUB
  END IF
  L = 1: R = 0: M = 0
  CALL MouseGetPressInfo(L, R, M, CNT, H, V)
  IF L = 0 THEN GOTO Demo2LP
  DO
    CALL MouseGetStatus(L, R, M, H, V)
  LOOP UNTIL (L OR R OR M) = 0
LOOP
END SUB

SUB Demo3
  CLS
  PRINT "Press Esc to end"
  PRINT "Use mouse and left button or arrow keys and Enter to select"
  DIM CH$(1 TO 4)
  FOR I = 1 TO 4
    CH$(I) = CHR$(64 + I)
    LOCATE 10 + I, 35
    PRINT "Select "; CH$(I);
  NEXT I
  COLOR 0, 7
  LOCATE 11, 35
  PRINT "Select "; CH$(1);
  OldSel = 1
  Hit = 0
  CALL MouseSetHorizRange(34 * 8, 34 * 8)
  CALL MouseSetVertRange(10 * 8, 13 * 8)
  CALL MouseSetPosn(34 * 8, 10 * 8)
  DO
    CALL MouseGetStatus(L, R, M, H, V)
    Sel = V \ 8 - 9
    Hit = L OR R OR M
    K$ = INKEY$
    IF LEN(K$) THEN
      SELECT CASE K$
        CASE CHR$(27)             'Esc
          EXIT SUB
        CASE CHR$(0) + CHR$(72)   'Up arrow
          IF Sel > 1 THEN Sel = Sel - 1
        CASE CHR$(0) + CHR$(80)   'Dn arrow
          IF Sel < 4 THEN Sel = Sel + 1
        CASE CHR$(13)
          Hit = -1
      END SELECT
    END IF
    IF Sel <> OldSel THEN
      COLOR 7, 0
      LOCATE 10 + OldSel, 35
      PRINT "Select "; CH$(OldSel);
      COLOR 0, 7
      LOCATE 10 + Sel, 35
      PRINT "Select "; CH$(Sel);
      OldSel = Sel
      IF Sel <> V \ 8 - 9 THEN
        CALL MouseSetPosn(34 * 8, (Sel + 9) * 8)
      END IF
    END IF
    IF Hit THEN
    COLOR 7, 0
    LOCATE 25, 1
    PRINT USING "Selected &"; CH$(Sel);
    Hit = 0
  END IF
LOOP
END SUB

SUB MouseCall
'*
'* Call mouse using current values in MReg registers
'*
  DEF SEG = VARSEG(MouseRoutine%(0))
  Addr% = VARPTR(MouseRoutine%(0))
  CALL Absolute(MReg, Addr%)
  DEF SEG
END SUB

SUB MouseGetPressInfo (LBtn%, RBtn%, MBtn%, Count%, HPosn%, VPosn%)
'*
'* Gets info about last mouse click
'* Set LBtn%, RBtn%, or MBtn% <> 0 for button to query
'* LBtn%, RBtn%, or MBtn% return 1 to show which
'* button is currently down or 0 to show which button
'* is currently up
'* Count% has count of clicks since last call
'* HPosn% and VPosn% are horizontal and vertical position
'* of cursor at last click
'*
  IF MouseReady% THEN
    IF (LBtn% OR RBtn% OR MBtn%) <> 0 THEN
      MReg.AX = 5
      IF LBtn% THEN MReg.BX = 0
      IF RBtn% THEN MReg.BX = 1
      IF MBtn% THEN MReg.BX = 2
      MouseCall
      LBtn% = MReg.AX AND 1
      RBtn% = (MReg.AX AND 2) \ 2
      MBtn% = (MReg.AX AND 4) \ 4
      Count% = MReg.BX
      HPosn% = MReg.CX
      VPosn% = MReg.DX
    ELSE
      CALL MouseGetStatus(LBtn%, RBtn%, MBtn%, HPosn%, VPosn%)
      Count% = 0
    END IF
  ELSE
    LBtn% = 0: RBtn% = 0: MBtn% = 0
    Count% = 0
    HPosn% = -1: VPosn% = -1
  END IF
END SUB

SUB MouseGetReleaseInfo (LBtn%, RBtn%, MBtn%, Count%, HPosn%, VPosn%)
'*
'* Gets info about last mouse release
'* Set LBtn%, RBtn%, or MBtn% <> 0 for button query
'* LBtn%, RBtn%, or MBtn% return 1 to show which
'* button is currently down or 0 to show which
'* button is currently up
'* Count% has count of releases since last call
'* HPosn% and VPosn% are horizontal and vertical position
'* of cursor at last release
'*
  IF MouseReady% THEN
    IF (LBtn% OR RBtn% OR MBtn%) <> 0 THEN
      MReg.AX = 6
      IF LBtn% THEN MReg.BX = 0
      IF RBtn% THEN MReg.BX = 1
      IF MBtn% THEN MReg.BX = 2
      MouseCall
      LBtn% = MReg.AX AND 1
      RBtn% = (MReg.AX AND 2) \ 2
      MBtn% = (MReg.AX AND 4) \ 4
      Count% = MReg.BX
      HPosn% = MReg.CX
      VPosn% = MReg.DX
    ELSE
      CALL MouseGetStatus(LBtn%, RBtn%, MBtn%, HPosn%, VPosn%)
      Count% = 0
    END IF
  ELSE
    LBtn% = 0: RBtn% = 0: MBtn% = 0
    Count% = 0
    HPosn% = -1: VPosn% = -1
  END IF
END SUB

SUB MouseGetStatus (LBtn%, RBtn%, MBtn%, HPosn%, VPosn%)
'*
'* Gets current mouse status
'* LBtn%, RBtn%, and MBtn% return
'* 1 if button is down or 0 if button is up
'* HPosn% returns horizontal position
'* VPosn% returns vertical position
'*
  IF MouseReady% THEN
    MReg.AX = 3
    MouseCall
    LBtn% = MReg.BX AND 1
    RBtn% = (MReg.BX AND 2) \ 2
    MBtn% = (MReg.BX AND 4) \ 4
    HPosn% = MReg.CX
    VPosn% = MReg.DX
  ELSE
    LBtn% = 0: RBtn% = 0: MBtn% = 0
    HPosn% = -1: VPosn% = -1
  END IF
END SUB

SUB MouseHide
'*
'* Hide the mouse cursor
'* Decrement the mouse cursor flag
'* Cursor hidden if flag <> 0
'* MouseInit sets flag to -1
'*
  IF MouseReady% THEN
    MReg.AX = 2
    MouseCall
  END IF
END SUB

FUNCTION MouseInit%
'*
'* Initialize mouse and return
'* number of buttons
'* Return 0 if mouse driver
'* unavailable or mouse not
'* present
'*
  DEF SEG = 0
  Sum% = 0
  FOR I% = &H33 * 4 TO &H33 * 4 + 3
    Sum% = Sum% + PEEK(I%)
  NEXT I%
  IF Sum% = 0 THEN
    MouseInit% = 0
    EXIT FUNCTION
  END IF
  MReg.AX = 0
  MouseCall
  IF MReg.AX = 0 THEN
    MouseInit% = 0
  ELSE
    MouseInit% = MReg.BX
  END IF
END FUNCTION

SUB MouseReadCounters (HCount%, VCount%)
'*
'* Returns mouse movement relative to
'* the last time this function was called
'* Measurements are in "mickeys". By default,
'* 1 mickey = 1 pixel horizontally and
'* 2 mickeys = 1 pixel vertically
'*
  IF MouseReady% THEN
    MReg.AX = 11
    MouseCall
    HCount% = MReg.CX
    VCount% = MReg.DX
  END IF
END SUB

SUB MouseSetHorizRange (HMin%, HMax%)
'*
'* Sets the minimum and maximum horizontal
'* range of the mouse cursor.
'* Moves the cursor inside the range if it's
'* outside.
'*
  IF MouseReady% THEN
    MReg.AX = 7
    MReg.CX = HMin%
    MReg.DX = HMax%
    MouseCall
  END IF
END SUB

SUB MouseSetPosn (HPosn%, VPosn%)
'*
'* Sets mouse cursor to HPosn and VPosn
'*
  IF MouseReady% THEN
    MReg.AX = 4
    MReg.CX = HPosn%
    MReg.DX = VPosn%
    MouseCall
  END IF
END SUB

SUB MouseSetup
'*
'* Setup to use mouse
'* MUST be called before
'* MouseInit or any other
'* mouse calls
'*
  RESTORE MouseInt
  DEF SEG = VARSEG(MouseRoutine%(0))
  Addr% = VARPTR(MouseRoutine%(0))
  FOR I = 0 TO 39
    READ A$
    POKE Addr% + I, VAL("&H" + A$)
  NEXT I
  IF A$ <> "CB" THEN ERROR 255
  DEF SEG
END SUB

SUB MouseSetVertRange (VMin%, VMax%)
'*
'* Sets the minimum and maximum vertical
'* range of the mouse cursor
'* Moves the cursor inside the range if it's
'* outside
  IF MouseReady% THEN
    MReg.AX = 8
    MReg.CX = VMin%
    MReg.DX = VMax%
    MouseCall
  END IF
END SUB

SUB MouseShow
'*
'* Display the mouse cursor
'* Increments mouse cursor flag
'* Cursor is displayed if flag is 0
'* MouseInit sets flag to -1
'*
  IF MouseReady% THEN
    MReg.AX = 1
    MouseCall
  END IF
END SUB

