{ Rodent unit v1.3 OooO } { << isn't he cute? } { 09/01/93 \/ } { Interrupt-style interface for Microsoft mouse, Turbo Pascal 6.0+} { by Sean L. Palmer } { Released to the Public Domain } { Please credit me if your program uses these routines! } unit Rodent; {$A-,B-,D-,E-,F-,G+,I-,L-,N-,O-,R-,S-,V-,X+} {make sure you alloc enough stack space in main program!} {as written, requires a 286+ and that the mouse exists} interface const x :integer=0; y :integer=0; {current mouse pos} xs:integer=0; ys:integer=0; {mickey counts} left=1; center=2; right=4; {button masks- (btn and left)<>0 if left button down} b:boolean=false; {button status, true if any button down} var btn:byte absolute b; {button status, mask with (btn and mask)<>0 to get a specific button} hidden:boolean; type pMouseHook=^tMouseHook; tMouseHook=procedure; {avoid calling dos, bios, and mouse routines from these if possible} function erasHook(h:tMouseHook):pMouseHook; function moveHook(h:tMouseHook):pMouseHook; function drawHook(h:tMouseHook):pMouseHook; {change out handlers} function clikHook(h:tMouseHook):pMouseHook; function liftHook(h:tMouseHook):pMouseHook; procedure show(f:boolean); {true=show} procedure confine(l,t,r,b:integer); {set min,max bounds} procedure moveTo(h,v:integer); procedure setSpeed(xs,ys,thr:word); {set x,y pix per 16 mickeys, double speed threshold} implementation {This unit should work in any mode, but you need to provide the routines to draw and erase the cursor.} {note: reason coords are scaled *8 throughout is because mouse driver} {stupidly messes with the values differently in different modes.} {This is just a work-around so it won't be limited to every eighth column or row in text modes.} {PS: be very careful using mickey counts in DI & SI in event handler.} var hideCount:byte absolute hidden; {this procedure does nothing, used to disable an event} procedure defaultMouseHook;far;assembler;asm end; {must save previous setting of I-flag} procedure clearInts;inline($9C/$FA); {pushF;cli} procedure restoreInts;inline($9D); {popF} const vDrawHook:tMouseHook=defaultMouseHook; {pre-set all hooks to do nothing} vErasHook:tMouseHook=defaultMouseHook; vMoveHook:tMouseHook=defaultMouseHook; vClikHook:tMouseHook=defaultMouseHook; vLiftHook:tMouseHook=defaultMouseHook; {these all both set a hook to a procedure you provide, and also return the old hook so you can later restore it} {Use something like:} {var savedClikHook:tMouseHook;} {...} {@savedClikHook:=clikHook(myClikHook);} {...} {clikHook(savedClikHook)} function drawHook(h:tMouseHook):pMouseHook;begin drawHook:=@vDrawHook; clearInts; vDrawHook:=h; restoreInts; end; function erasHook(h:tMouseHook):pMouseHook;begin erasHook:=@vErasHook; clearInts; vErasHook:=h; restoreInts; end; function moveHook(h:tMouseHook):pMouseHook;begin moveHook:=@vMoveHook; clearInts; vMoveHook:=h; restoreInts; end; function clikHook(h:tMouseHook):pMouseHook;begin clikHook:=@vclikHook; clearInts; vClikHook:=h; restoreInts; end; function liftHook(h:tMouseHook):pMouseHook;begin liftHook:=@vLiftHook; clearInts; vLiftHook:=h; restoreInts; end; {here is the callback function for the mouse driver} {calling regs:} {ax:triggering event bit mask} {bx:button status bit mask (bit 0=left,1=center,2=right)} {cx:mouse X/bit 7 is sign for di,bit 0 always=0} {dx:mouse Y/bit 7 is sign for si} {di:abs mouse Delta X} {si:abs mouse Delta Y} {bits in event mask:} {0:move} {1:left btn down} {2:left btn up} {3,4:center btn} {5,6:right btn} {This code is real easy to break, be careful!} procedure doMouseHook;far;assembler;asm push ax; mov ax,seg @DATA; mov ds,ax; pop ax; mov xs,si; mov ys,di; {disregard di,si mickey counts} mov btn,bl; and cx,$3FFF; shr cx,3; and dx,$3FFF; shr dx,3; {strip hi bits} push ax; push cx; push dx; {save event status} test hidden,$FF; jnz @NOERAS; call vErasHook; @NOERAS: pop dx; mov y,dx; pop cx; mov x,cx; call vMoveHook; {always assume mouse has moved, disregard bit 0 of ax} test hidden,$FF; jnz @NODRAW; call vDrawHook; @NODRAW: pop ax; {restore event status} @CLIK: test al,00101010b; jz @LIFT; {check any button clik flag} push ax; call vClikHook; pop ax; @LIFT: test al,01010100b; jz @EXIT; {check any button lift flag} call vLiftHook; @EXIT: end; procedure show(f:boolean);begin clearInts; if f then begin if hidden then begin dec(hideCount); if not hidden then vDrawHook; end; end else begin if not hidden then vErasHook; inc(hideCount); end; restoreInts; end; Procedure confine(l,t,r,b:integer);assembler;asm mov ax,7; mov cx,l; shl cx,3; mov dx,r; shl dx,3; int $33; mov ax,8; mov cx,t; shl cx,3; mov dx,b; shl dx,3; int $33; end; procedure moveTo(h,v:integer);begin if not hidden then vErasHook; asm mov cx,h; mov x,cx; shl cx,3; mov dx,v; mov y,dx; shl dx,3; mov ax,4; int $33; end; if not hidden then vDrawHook; end; procedure setSpeed(xs,ys,thr:word);assembler;asm mov ax,$1A; mov bx,xs; shl bx,3; mov cx,ys; shl cx,3; mov dx,thr; int $33; end; var oldMouseHook:pointer; oldEventMask:word; procedure removeMouse;begin if not hidden then show(false); asm les dx,oldMouseHook; mov cx,oldEventMask; mov ax,$C; int $33;end; end; var mouseHook:pointer absolute 0:$33*4; const eventMask=$7F; {all events} function exists:boolean;assembler;asm xor ax,ax; mov es,ax; {get ready to check interrupt vector for nil} mov bx,es:[$33*4]; or bx,es:[$33*4+2]; jz @X; {no} {ax still 0} int $33; @X: {result in al} end; begin if exists then begin setSpeed(32,64,4); {set up a natural-feeling speed for 640x480} moveTo(0,0); confine(0,0,0,0); {trap the little sucker} hideCount:=1; asm push cs; pop es; mov dx,offset doMouseHook; {loc of callback function} mov cx,eventMask; mov ax,$14; int $33; {enable event callbacks} mov oldEventMask,cx; mov word ptr oldMouseHook,dx; mov word ptr oldMouseHook+2,es; end; end else begin writeln('Need mouse.'); halt(1);end; end.