[comp.lang.pascal] mouse routines and a problem

mead@uxh.cso.uiuc.edu (Alan David Mead) (11/03/90)

   Here are some routines from Turbo Technix (May/June 1988).  If they
have been posted previously, I missed them.  They simplify programming
the mouse.  You should get a copy of the article (p52-64?) or a 
discussion of the results of the mouse functions.  Only Microsoft and
(really) 100% compatibles are (garrunteed to be) supported except that
there are 2 extra Logitech functions.

  After MOUSE, there is my modified version of the event handler demo
that was included in the article.  The program is set up for a color
monitor (of any type)--change the offset parameter in the MemW array
in the procedure handler to $B000 for a monochrome.  The code draws
a screen and then continuously displays the coordinates of the cursor,
clicking every time the cursor moves.  Hitting the right button exits
the demo.

I have been having several problems, here are two: 
   1) the mouse cursor is specified by character AND attribute, so 
when you move it from the blue background onto the cyan it doesn't 
look so hot--to compensate, I thought I'd just 
  a) read the color attribute with mem,
  b) clear the low bits, and
  c) redefine the cursor with the new attribute.
But on my IBM PS2 55SX, the blue background returns a 7?  Also, I fear
that I'm over-burdening the event handler (which should be quick, 
right?).  Does anyone know of a better way?

   2) Perhaps related to the above (although I don't think so), the
cursor sometimes forgets to erase itself as it moves.  This results
in green or black (or whatever) blocks messing up the display.  Is this
a timing problem?  It is most acute when the cursor is being moved 
moderately quickly.  

   Any help would be greatly appreciated.  One user recommended a book
by Microsoft (title similar to _Programming a Mouse_)--has anyone used
the book?  Is it any good?  Thanks in advance.

-alan mead : mead@uxh.cso.uiuc

==========[ begin ]===============
unit mouse;

Interface

(*{$U \tp}*)
uses dos;

const
  MDD = $33;

type
  ResetRec = record
    exists   : boolean; { true if mouse installed }
    nButtons : integer; { number buttons on mouse }
  end;

  LocRec = record
    buttonStatus,      { initialized by functions 3 5 & 6    }
    opCount,            { bits 0-2 on if L,R,M button is down }
    column,
    row : integer;
  end;

  moveRec = record     { initialized by function 11 }
    hCount,            { net horizontal movement    }
    vCount : integer;  { net vertical movement      }
  end;

var
  reg : registers;

  { Microsoft mouse functions }
  procedure mReset( var Mouse:ResetRec );             { function 0 }
  procedure mShow;                                  { function 1 }
  procedure mHide;                                  { function 2 }
  procedure mPos( var Mouse:LocRec );               { function 3 }
  procedure mMoveTo( col,row:integer );             { function 4 }
  procedure mPressed( button:integer; var mouse:LocRec ); { function 5 }
  procedure mReleased( button:integer; var mouse:LocRec ); { function 6 }
  procedure mColRange( min,max:integer );           { function 7 }
  procedure mRowRange( min,max:integer );           { function 8 }
  procedure mGraphCursor( hHot,vHot:integer;        { function 9 }
                          MaskSeg,MaskOfs:word );
  procedure mTextCursor( cType,p1,p2:word );        { function 10 }
  procedure mMotion( var moved:moveRec );           { function 11 }
  procedure mInstTask( mask,TaskSeg,TaskOfs:word ); { function 12 }
  procedure mLpenOn;                                { function 13 }
  procedure mLpenOff;                               { function 14 }
  procedure mRatio( horiz,vert:integer );           { function 15 }

implementation

  function Lower( n1,n2:integer ):integer; { local to unit }
  begin
    if n1 < n2 then
      lower := n1
    else
      lower := n2;
  end;

  function Upper( n1,n2:integer ):integer;
  begin
    if n1 > n2 then
      Upper := n1
    else
      Upper := n2;
  end;

  procedure mReset;
  begin
    reg.AX := 0;
    intr( MDD,reg );
    { mouse.exists := ( reg.AX = 0 ); }
    if reg.AX <> 0 then
       mouse.exists := TRUE
    else
      mouse.exists := FALSE;
    mouse.nButtons := reg.BX;
  end;

  procedure mShow;
  begin
    reg.AX := 1;
    intr( MDD,reg );
  end; { mShow }

  procedure mHide;
  begin
    reg.AX := 2;
    intr( MDD,reg );
  end; { mHide }

  procedure mPos;
  begin
    reg.AX := 3;
    intr( MDD,reg );
    mouse.ButtonStatus := reg.BX;
    mouse.Column := reg.CX;
    mouse.Row := reg.DX;
  end; { mPos }

  procedure mMoveTo;
  begin
    reg.AX := 4;
    reg.CX := col;
    reg.DX := row;
    intr( MDD,reg );
  end; { mMoveTo }

  procedure mPressed;
  begin
    reg.AX := 5;
    reg.BX := button;
    intr( MDD,reg );
    mouse.ButtonStatus := reg.AX;
    mouse.OpCount := reg.BX;
    mouse.column := reg.CX;
    mouse.row := reg.DX;
  end; { mPressed }

  procedure mReleased;
  begin
    reg.AX := 6;
    reg.BX := button;
    intr( MDD,reg );
    mouse.ButtonStatus := reg.AX;
    mouse.OpCount := reg.BX;
    mouse.column := reg.CX;
    mouse.row := reg.DX;
  end; {mReleased }

  procedure mColRange;
  begin
    reg.AX := 7;
    reg.CX := lower( min,max );
    reg.DX := upper( min,max );
    intr( MDD,reg );
  end; { mColRange }

  procedure mRowRange;
  begin
    reg.AX := 8;
    reg.CX := lower( min,max );
    reg.DX := upper( min,max );
    intr( MDD,reg );
  end; { mRowRange }

  procedure mGraphCursor;
  begin
    reg.AX := 9;
    reg.BX := hHot;
    reg.CX := vHot;
    reg.DX := maskOfs;
    reg.ES := maskSeg;
    intr( MDD,reg );
  end; { mGraphCursor }

  procedure mTextCursor;
  { NOTES: Type 0 is the software cursor.  When unspecified, p1 and p2 are
      the screen and cursor masks.
    Type 1 is the hardware cursor.  When specified, p1 and p2 are the start
      and stop scan lines, ie the cursor shape.                              }
  begin
    reg.AX := 10;
    reg.BX := ctype;
    reg.CX := p1;
    reg.DX := p2;
    intr( MDD,reg );
  end; { mTextCursor }

  procedure mMotion;            { Net movement since last call  }
  begin                         { Expressed in mickeys (1/100") }
    reg.AX := 11;
    intr( MDD,reg );
    moved.hCount := reg.CX;
    moved.vCount := reg.DX;
  end; { mMotion }

  procedure mInstTask;         { Install user-defined task }
  begin
    reg.AX := 12;
    reg.CX := mask;
    reg.DX := taskOfs;
    reg.ES := taskSeg;
    intr( MDD,reg );
  end; { mInstTask }

  procedure mLpenOn;           { Begin emulation of light pen }
  begin
    reg.AX := 14;
    intr( MDD,reg );
  end; { mLpenOn }

  procedure mLpenOff;          { Stop emulating light pen }
  begin
    reg.AX := 15;
    intr( MDD,reg );
  end; { mLpenOff }

  procedure mRatio;             { set mickey to pixel ratio }
  { NOTES:
      Ratios are R/8.
      Default is 16 for vertical, 8 for horizontal }
  begin
    reg.AX := 15;
    reg.CX := horiz;
    reg.DX := vert;
    intr( MDD,reg );
  end; { mRatio }

end.

==============[ cut here ]================

program mousevnt;

uses dos,crt,mouse;

type
   mEvent = record
      event ,
      btnStatus,
      horiz,
      vert    : word;
   end;

var
  mous : mEvent;
  t : LocRec;
  m    : resetRec;
  x,y : byte;

  procedure handler( Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP : word );
  {
    For speed, I've used inc() to add and SHR | SHL to divide | multiply
      by powers of two.
  }
  interrupt;
  var attribute:word; AtOffset:word;
  begin
    mous.event := AX;
    mous.btnStatus := BX;
    mous.horiz := CX;
    mous.vert := DX;

    AtOffset := (( 5*mous.vert ) SHL 5 )+( mous.horiz * 2 ) ;
    attribute := MemW[ $B800:AtOffset ];
    attribute := ( attribute SHR 8 );   
    attribute := ( attribute SHL 8 );  
    mTextCursor( 0,0,$0040+ attribute );

    mous.horiz := mous.horiz SHR 3; { divide by 8. Mouse goes by (graphic) }
    mous.vert := mous.vert SHR 3;   { pixels instead of rows & columns     }

    inc( mous.horiz );       { So that the upper left is 1,1--not 0,0 }
    inc( mous.vert );

    inline(            { exit processing and far return from the driver }
           $8B/$E5/
           $5D/
           $07/
           $1F/
           $5F/
           $5E/
           $5A/
           $59/
           $5B/
           $58/
           $CB );
  end;

begin
  window( 1,1,80,1 );
  textcolor( white );
  textBackground( Cyan );
  ClrScr;
  GotoXY( 2,1 );
  write( 'MOUSE EVENT-HANDLING DEMO' );

  window( 1,25,80,25 );
  ClrScr;
  GotoXY( 17,1 );
  write( 'Press right button to quit' );

  window( 1,2,80,24 );
  TextColor( White );
  TextBackground( Blue );
  ClrScr;
  window( 1,1,80,25 );
  textbackground( cyan );

  { set up rodent }
  mReset( m );
  if m.exists then
    begin
      mInstTask( 25,seg( handler ),ofs( Handler ));
      mous.event := 0;
      mous.horiz := 0;
      mous.vert := 0;
      mShow;
      mTextCursor( 0,0,$1f40); { white on blue at-symbol }
      x := 0; y := 0;

  { Loop to perform demo }
    repeat
      mPressed( 4,t );
      if ( mous.horiz <> x ) OR ( mous.vert <> y ) then
        begin
          x := mous.horiz;
          y := mous.vert;
          mHide;
          Gotoxy( 50,1 );
          writeln( 'X= ',mous.horiz:5,', Y= ',mous.vert:5 );
          sound( 2000 );
          delay( 5 );
          nosound;
          mShow;
        end;
    until t.ButtonStatus = 2 ;

    GotoXY( 50,1 );
    writeln( 'Good bye !          ' );

  { Clean up and quit }
    mHide;
    mReset( m );
  end;
{  ClrScr;   }
end.
======================[ end ]========================
--
Alan Mead : mead@uxh.cso.uiuc.edu