
UNIT  uMCursor;                               { (c) 1994 by NEBULA-Soft. }
      { Mausroutinen fr Textmodus          } { Olaf Bartelt & Oliver Carow }
{ ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ } INTERFACE { ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ }
USES  DOS, video;                             { Einbinden der Units         }

{ The unit VIDEO is also included in the SWAG distribution in the CRT.SWG   }

{ Ä Konstantendeklarationen ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ }
CONST cLinke_taste                 = 1;       { linke Maustaste             }
      cRechte_taste                = 2;       { rechte Maustaste            }
      cMittlere_taste              = 4;       { mittlere Maustaste (bei 3)  }

      cursor_location_changed      = 1;
      left_button_pressed          = 2;
      left_button_released         = 4;
      right_button_pressed         = 8;
      right_button_released        = 16;
      middle_button_pressed        = 32;
      middle_button_released       = 64;

      lastmask                     : WORD    = 0;
      lasthandler                  : POINTER = NIL;

      click_repeat                 = 10;
      mousetextscale               = 8;
      vgatextgraphiccursor         : BOOLEAN = FALSE;


{ Ä Typendeklarationen ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ }
TYPE  mousetype                    = (twobutton, threebutton, another);
      buttonstate                  = (buttondown, buttonup);
      direction                    = (moveright, moveleft, moveup, movedown,
nomove);

{ Ä Variablendeklarationen ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ }
VAR   mouse_present                : BOOLEAN;
      mouse_buttons                : mousetype;
      eventx, eventy, eventbuttons : WORD;
      eventhappened                : BOOLEAN;
      xmotions, ymotions           : WORD;
      mousecursorlevel             : INTEGER;
      fontpoints                   : BYTE;

      maxmousex             : INTEGER;
      maxmousey                    : INTEGER;


{ Ä exportierte Prozeduren und Funktionen ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ }
PROCEDURE set_graphic_mouse_cursor;        { graphischen Mousecursor setzen }
PROCEDURE showmousecursor;

{ ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ } IMPLEMENTATION { ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ }
{$IFDEF VER60}                                { in TP 6.0 gibt es SEGxxxx   }
CONST SEG0040 = $0040;                        { noch nicht! => definieren!  }
      SEGB800 = $B800;
      SEGA000 = $A000;
{$ENDIF}

{ Ä Typendeklarationen ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ }
TYPE  pTextgraphikcursor = ^tTextgraphikcursor;  { Zeiger auf Array         }
      tTextgraphikcursor = ARRAY[0..31] OF LONGINT;

      box                = RECORD
                             left, top, right, bottom : WORD;
                           END;
      pChardefs          = ^tChardefs;
      tChardefs          = ARRAY[0..(32*8)] OF BYTE;

{ Ä Konstantendeklarationen ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ }
CONST pfeil                  : tTextgraphikcursor =
{ Maske:  } ($3FFFFFFF, $1FFFFFFF, $0FFFFFFF, $07FFFFFF, $03FFFFFF, $01FFFFFF,
             $00FFFFFF, $007FFFFF, $003FFFFF, $007FFFFF, $01FFFFFF, $10FFFFFF,
             $B0FFFFFF, $F87FFFFF, $F87FFFFF, $FcFFFFFF,
{ Cursor: }  $00000000, $40000000, $60000000, $70000000, $78000000, $7C000000,
             $7E000000, $7F000000, $7F800000, $7F000000, $7C000000, $46000000,
             $06000000, $03000000, $03000000, $00000000);

      sanduhr : tTextgraphikcursor =        ($0001FFFF,  { 0000000000000001 }
                { Cursorform:      }         $0001FFFF,  { 0000000000000001 }
                                             $8003FFFF,  { 1000000000000011 }
                                             $C7C7FFFF,  { 1100011111000111 }
                                             $E38FFFFF,  { 1110001110001111 }
                                             $F11FFFFF,  { 1111000100011111 }
                                             $F83FFFFF,  { 1111100000111111 }
                                             $FC7FFFFF,  { 1111110001111111 }
                                             $F83FFFFF,  { 1111100000111111 }
                                             $F11FFFFF,  { 1111000100011111 }
                                             $E38FFFFF,  { 1110001110001111 }
                                             $C7C7FFFF,  { 1100011111000111 }
                                             $8003FFFF,  { 1000000000000011 }
                                             $0001FFFF,  { 0000000000000001 }
                                             $0001FFFF,  { 0000000000000001 }
                                             $0000FFFF,  { 0000000000000000 }
                                                { ^^^^ immer! (Textmodus)   }
                { Bildschirmmaske: }         $00000000,  { 0000000000000000 }
                                             $7FFC0000,  { 0111111111111100 }
                                             $20080000,  { 0010000000001000 }
                                             $10100000,  { 0001000000010000 }
                                             $08200000,  { 0000100000100000 }
                                             $04400000,  { 0000010001000000 }
                                             $02800000,  { 0000001010000000 }
                                             $01000000,  { 0000000100000000 }
                                             $02800000,  { 0000001010000000 }
                                             $04400000,  { 0000010001000000 }
                                             $08200000,  { 0000100000100000 }
                                             $10100000,  { 0001000000010000 }
                                             $20080000,  { 0010000000001000 }
                                             $7FFC0000,  { 0111111111111100 }
                                             $00000000,  { 0000000000000000 }
                                             $00000000); { 0000000000000000 }
                                                { ^^^^ immer! (Textmodus)   }

      vgatextgraphicptr      : pTextgraphikcursor = @pfeil;
                                                  { @sanduhr                }
{ Ä Variablendeklarationen ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ }
VAR   hidebox                : box;
      regs                   : REGISTERS;
      vgastoredarray         : ARRAY[1..3, 1..3] OF BYTE;
      lasteventx, lasteventy : WORD;
      hasstoredarray         : BOOLEAN;
      oldexitproc            : POINTER;

CONST chardefs               : pChardefs = NIL;
      charheight             = 16;
      defchar                = $D0;


{ Ä exportierte Prozeduren und Funktionen ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ }
procedure swap(var a,b : word);
var c : word;
begin
 c := a;
 a := b;
 b := c; {swap a and b}
end; {swap}

procedure setMouseCursor(x,y : word);
begin
 with regs do begin
  ax := 4;
  cx := x;
  dx := y; {prepare parameters}
  INTR($33, regs);
 end; {with}
end; {setMouseCursor}

FUNCTION x : WORD;
BEGIN
  regs.AX := 3;
  INTR($33, regs);
  x := regs.CX;
END;

FUNCTION y : WORD;
BEGIN
  regs.AX := 3;
  INTR($33, regs);
  y := regs.DX;
END;

procedure mouseBox(left,top,right,bottom : word);
begin
 if (left > right) then swap(left,right);
 if (top > bottom) then swap(top,bottom); {make sure they are ordered}
 regs.ax := 7;
 regs.cx := left;
 regs.dx := right;
 INTR($33, regs); {set x range}
 regs.ax := 8;
 regs.cx := top;
 regs.dx := bottom;
 INTR($33, regs); {set y range}
end; {mouseBox}


PROCEDURE initmouse;
VAR overridedriver : BOOLEAN;                 { wegen Hercules-Karten       }
    tempvideomode  : BYTE;                    { Zwischenspeicher fr Modus  }
BEGIN
  overridedriver := FALSE;                    { erstmal nicht override!     }

  IF (FALSE AND (MEM[SEG0040:$0049] = 7)) THEN  { doch overriden?           }
  BEGIN
    MEM[SEG0040:$0049] := 6;                  { Ja: Videomodus vortuschen  }
    overridedriver := TRUE;                   {     und override setzen!    }
  END;

  IF vgatextgraphiccursor = TRUE THEN         { Graphikcursor im Textmodus? }
  BEGIN
    tempvideomode := MEM[SEG0040:$0049];      { Videomodus zwischenspeichern}
    MEM[SEG0040:$0049] := 6;                  { anderen Modus vortuschen   }
  END;

  WITH regs DO                                { Maustyp ermitteln           }
  BEGIN                                       { und Anzahl der Tasten auch  }
    AX := 0; BX := 0;                         { Maus initialisieren (00h)   }
    INTR($33, regs);                          { Mausinterrupt aufrufen      }

    mouse_present := (AX <> 0);               { berhaupt Maus vorhanden?   }
    IF (BX AND 2) <> 0 THEN mouse_buttons := twobutton  { Maustasten ermitt.}
                       ELSE IF (BX AND 3) > 0 THEN mouse_buttons := threebutton
                                              ELSE mouse_buttons := another;
  END;

  IF overridedriver = TRUE THEN MEM[SEG0040:$0049] := 7;  { override?       }
  IF vgatextgraphiccursor = TRUE THEN         { Graphikcursor im Textmodus? }
    MEM[SEG0040:$0049] := tempvideomode;      { Ja: Modus restaurieren!     }

  IF (NOT vgatextgraphiccursor) THEN fontpoints := mousetextscale
                                ELSE fontpoints := MEM[SEG0040:$0085];
  maxmousex := maxx * mousetextscale;         { Mausgrenzen ausrechnen      }
  maxmousey := maxy * fontpoints;

  mousebox(0, 0, (visiblex * mousetextscale)-1, (visibley * fontpoints)-1);
  eventbuttons := 0; eventhappened := FALSE;  { noch kein Event gewesen!    }

  xmotions := 8; ymotions := 16; mousecursorlevel := 0;  { Cursor nicht s.  }
  hasstoredarray := FALSE;                    { noch keine Daten im Array   }

  setmousecursor(visiblex * mousetextscale DIV 2, visibley * fontpoints DIV 2);
  eventx := x; eventy := y; lasteventx := eventx; lasteventy := eventy;
END;

PROCEDURE vgascreen2array(newposition, s2a, defaultrange : BOOLEAN);
VAR x, y : WORD;
    w, h : WORD;
    o, l : WORD;
    i, j : BYTE;
BEGIN
  IF (newposition = TRUE) THEN
  BEGIN
    x := eventx DIV mousetextscale;
    y := eventy DIV fontpoints;
  END
  ELSE
  BEGIN
    x := lasteventx DIV mousetextscale;
    y := lasteventy DIV fontpoints;
  END;

  w := visiblex - x; IF (w > 3) THEN w := 3;
  h := visibley - y; IF (h > 3) THEN h := 3;
  o := 2 * x + 2 * visiblex * y;
  l := 2 * visiblex - 2 * w;

  IF (defaultrange = TRUE) THEN
  BEGIN
    FOR i := 0 TO h - 1 DO
    BEGIN
      FOR j := 0 TO w - 1 DO
      BEGIN
        MEM[SEGB800:o] := defchar + i * 3 + j;
        INC(o, 2);
      END;
      INC(o, l);
    END;
  END
  ELSE
    IF (s2a = TRUE) THEN
    BEGIN
      FOR i := 1 TO h DO
      BEGIN
        FOR j := 1 TO w DO
        BEGIN
          vgastoredarray[i, j] := MEM[SEGB800:o];
          INC(o, 2)
        END;
        INC(o, l);
      END;
    END
    ELSE
    BEGIN
      FOR i := 1 TO h DO
      BEGIN
        FOR j := 1 TO w DO
        BEGIN
          MEM[SEGB800:o] := vgastoredarray[i, j];
          INC(o, 2);
        END;
        INC(o, l);
      END;
    END;
END;

PROCEDURE drawvgatextgraphiccursor;
TYPE  lp = ^LONGINT;
CONST sequencerport     = $3C4;
      sequenceraddrmode = $704;
      sequenceraddrnrml = $302;
      vgacontrolerport  = $3CE;
      cpureadmap2       = $204;
      cpuwritemap2      = $402;
      mapstartaddrA000  = $406;
      mapstartaddrB800  = $E06;
      oddevenaddr       = $304;
VAR   o, s              : WORD;
      i, j              : INTEGER;
      s1, s2, s3        : WORD;
      a                 : LONGINT;
      d, mc, ms         : lp;

BEGIN
  ASM
    PUSHF
    CLI
    MOV DX, sequencerport
    MOV AX, sequenceraddrmode
    OUT DX, AX
    MOV DX, vgacontrolerport
    MOV AX, cpureadmap2
    OUT DX, AX
    MOV AX, 5
    OUT DX, AX
    MOV AX, mapstartaddrA000
    OUT DX, AX
    POPF
  END;

   o := 0;
   FOR i := 1 TO 3 DO
   BEGIN
     s1 := vgastoredarray[i, 1] * 32;
     s2 := vgastoredarray[i, 2] * 32;
     s3 := vgastoredarray[i, 3] * 32;

     FOR j := 1 TO fontpoints DO
     BEGIN
       INC(o); chardefs^[o] := MEM[SEGA000:s3];
       INC(o); chardefs^[o] := MEM[SEGA000:s2];
       INC(o); chardefs^[o] := MEM[SEGA000:s1];
       INC(o); INC(s1); INC(s2); INC(s3);
     END;
   END;

   s := eventx MOD mousetextscale;
   a := $FF000000 SHL (mousetextscale - s);

   d  := @chardefs^[(eventy MOD fontpoints) * SIZEOF(LONGINT)];
   ms := @vgatextgraphicptr^;
   mc := @vgatextgraphicptr^[charheight];

   FOR i := 1 TO charheight DO
   BEGIN
     d^ := (d^ and ((ms^ shr s) or a)) or (mc^ shr s);
     INC(WORD(d), SIZEOF(LONGINT));
     INC(WORD(mc), SIZEOF(LONGINT));
     INC(WORD(ms), SIZEOF(LONGINT));
   END;

   ASM
     MOV DX, sequencerport
     MOV AX, cpuwritemap2
     OUT DX, AX
   END;

   o := 0;
   for i := 0 to 2 do begin
      s1 := (defChar + 3 * i    ) * 32;
      s2 := (defChar + 3 * i + 1) * 32;
      s3 := (defChar + 3 * i + 2) * 32;
      for j := 1 to fontPoints do begin
         inc(o); { skip 4th byte }
         mem[segA000:s3] := charDefs^[o];
            { this code is changed to minimize DS variable space ! - RL }
         inc(o);
         mem[segA000:s2] := charDefs^[o];
         inc(o);
         mem[segA000:s1] := charDefs^[o];
         inc(o);
         inc(s1);
         inc(s2);
         inc(s3);
      end; { for j }
   end; { for i }

   (* now we will return the graphic adapter back to normal *)

   asm
      pushf;
      cli; { disable intr .. }
      mov dx, sequencerPort;
      mov ax, sequencerAddrNrml;
      out dx, ax;
      mov ax, oddEvenAddr;
      out dx, ax;

      mov dx, vgaControlerPort;
      mov ax, 4; { map 0 for cpu reads }
      out dx, ax;
      mov ax, $1005;
      out dx, ax;
      mov ax, mapStartAddrB800;
      out dx, ax
      popf;
   end; { asm }

   vgaScreen2Array(true, false, true); { go ahead and paint it .. }

end; {drawVGATextGraphicCursor}

(******************************************************************************
*                               showMouseCursor                               *
******************************************************************************)
procedure showMouseCursor;

begin
 inc(mouseCursorLevel);
   if (not vgaTextGraphicCursor) then begin
    regs.ax:=1; {enable cursor display}
    INTR($33, regs);
   end else if ((mouseCursorLevel = 1) and mouse_present) then begin
      vgaScreen2Array(true, true, false);
      hasStoredArray := true;
      drawVGATextGraphicCursor;
   end;
end; {showMouseCursor}

(******************************************************************************
*                               hideMouseCursor                               *
******************************************************************************)
procedure hideMouseCursor;

begin
 dec(mouseCursorLevel);
   if (not vgaTextGraphicCursor) then begin
    regs.ax:=2; {disable cursor display}
    INTR($33, regs);
   end else if ((mouseCursorLevel = 0) and (hasStoredArray)) then begin
      vgaScreen2Array(false, false, false);
      hasStoredArray := false;
   end;
end; {hideMouseCursor}


(******************************************************************************
*                                  getButton                                  *
******************************************************************************)
function getButton(Button : Byte) : buttonState;

begin
        regs.ax := 3;
        INTR($33, regs);
        if ((regs.bx and Button) <> 0) then
                getButton := buttonDown
                {bit 0 = left, 1 = right, 2 = middle}
        else getButton := buttonUp;
end; {getButton}

(******************************************************************************
*                                buttonPressed                                *
******************************************************************************)
function buttonPressed : boolean;

begin
        regs.ax := 3;
        INTR($33, regs);
        if ((regs.bx and 7) <> 0) then
                buttonPressed := True
        else buttonPressed := False;
end; {buttonPressed}


(******************************************************************************
*                                 lastXPress                                  *
******************************************************************************)
function lastXPress(Button : Byte) : word;

begin
        regs.ax := 5;
        regs.bx := Button;
        INTR($33, regs);
        lastXPress := regs.cx;
end; {lastXpress}

(******************************************************************************
*                                 lastYPress                                  *
******************************************************************************)
function lastYPress(Button : Byte) : word;

begin
        regs.ax := 5;
        regs.bx := Button;
        INTR($33, regs);
        lastYPress := regs.dx;
end; {lastYpress}

(******************************************************************************
*                                buttonPresses                                *
******************************************************************************)
function buttonPresses(Button : Byte) : word; {from last check}

begin
        regs.ax := 5;
        regs.bx := Button;
        INTR($33, regs);
        buttonPresses := regs.bx;
end; {buttonPresses}

(******************************************************************************
*                                lastXRelease                                 *
******************************************************************************)
function lastXRelease(Button : Byte) : word;

begin
        regs.ax := 6;
        regs.bx := Button;
        INTR($33, regs);
        lastXRelease := regs.cx;
end; {lastXRelease}

(******************************************************************************
*                                lastYRelease                                 *
******************************************************************************)
function lastYRelease(Button : Byte) : word;

begin
        regs.ax := 6;
        regs.bx := Button;
        INTR($33, regs);
        lastYRelease := regs.dx;
end; {lastYRelease}

(******************************************************************************
*                               buttonReleases                                *
******************************************************************************)
function buttonReleases(Button : Byte) : word; {from last check}

begin
        regs.ax := 6;
        regs.bx := Button;
        INTR($33, regs);
        buttonReleases := regs.bx;
end; {buttonReleases}

(******************************************************************************
*                             HardwareTextCursor                              *
******************************************************************************)
procedure HardwareTextCursor(fromLine,toLine : byte);

{set text cursor to text, using the scan lines from..to,
        same as intr 10 cursor set in bios :
        color scan lines 0..7, monochrome 0..13 }

begin
        regs.ax := 10;
        regs.bx := 1; {hardware text}
        regs.cx := fromLine;
        regs.dx := toLine;
        INTR($33, regs);
end; {hardwareTextCursor}

(******************************************************************************
*                             softwareTextCursor                              *
******************************************************************************)
procedure softwareTextCursor(screenMask,cursorMask : word);

{ when in this mode the cursor will be achived by ANDing the screen word
        with the screen mask (Attr,Char in high,low order) and
        XORing the cursor mask, ussually used by putting the screen attr
        we want preserved in screen mask (and 0 into screen mask character
        byte), and character + attributes we want to set into cursor mask}

begin
        regs.ax := 10;
        regs.bx := 0;        {software cursor}
        regs.cx := screenMask;
        regs.dx := cursorMask;
        INTR($33, regs);
end; {softwareMouseCursor}

(******************************************************************************
*                               recentXmovement                               *
******************************************************************************)
function recentXmovement : direction;

{from recent call to which direction did we move ?}

var d : integer;

begin
        regs.ax := 11;
        INTR($33, regs);
        d := regs.cx;
        if (d > 0)
                then recentXmovement := moveRight
        else if (d < 0)
                then recentXmovement := moveLeft
        else recentXmovement := noMove;
end; {recentXmovement}

(******************************************************************************
*                               recentYmovement                               *
******************************************************************************)
function recentYmovement : direction;

{from recent call to which direction did we move ?}

var
   d : integer;
begin
        regs.ax := 11;
        INTR($33, regs);
        d := regs.dx;
        if (d > 0)
                then recentYmovement := moveDown
        else if (d < 0)
                then recentYmovement := moveUp
        else recentYmovement := noMove;
end; {recentYmovement}


(******************************************************************************
*                               setEventHandler                               *
******************************************************************************)
procedure setEventHandler(mask : word; handler        : pointer);

{handler must be a far interrupt routine }

begin
        regs.ax := 12; {set event handler function in mouse driver}
        regs.cx := mask;
        regs.es := seg(handler^);
        regs.dx := ofs(handler^);
        INTR($33, regs);
        lastMask := mask;
        lastHandler := handler;
end; {set event Handler}

(******************************************************************************
*                               defaultHandler                                *
******************************************************************************)
{$F+} procedure defaultHandler; assembler; {$F-}
asm
   push ds; { save TP mouse driver }
   mov ax, SEG @data;
   mov ds, ax; { ds = TP:ds, not the driver's ds }
   mov eventX, cx; { where in the x region did it occur }
   mov eventY, dx;
   mov eventButtons, bx;
   mov eventHappened, 1; { eventHapppened := true }
   pop ds; { restore driver's ds }
   ret;
end;

{   this is the default event handler , it simulates :

      begin
               eventX := cx;
               eventY := dx;
               eventButtons := bx;
               eventhappened := True;
      end;

}

(******************************************************************************
*                                doPascalStuff                                *
* this is the pascal stuff that is called when vgaTextGraphicCursor mode has  *
* to update the screen.                                                       *
******************************************************************************)
procedure doPascalStuff; far;
begin
   if (mouseCursorLevel > 0) then begin
      if (hasStoredArray) then begin
         VGAscreen2Array(false, false, false); { move old array to screen -
restore }
         hasStoredArray := false;
      end;
      if (mouseCursorLevel > 0) then begin
         VGAscreen2Array(true, true, false); { move new - from screen to array
}
         hasStoredArray := true; { now we have a stored array }
         drawVGATextGraphicCursor; { do the low level stuff here }
         lastEventX := eventX;
         lastEventY := eventY; { this is the old location }
      end; { go ahead and draw it ... }
   end; { cursorLevel > 0 }
end; {doPascalStuff}

(******************************************************************************
*                            vgaTextGraphicHandler                            *
* this is the same as default handler, only we do the mouse location movement *
* ourself. Notice - if you use another handler, for mouse movement with       *
* VGA text graphic cursor - do the same !!!                                   *
******************************************************************************)
procedure vgaTextGraphicHandler; far; assembler;
label
   noCursorMove;
asm
   push ds; { save TP mouse driver }
   push ax;
   mov ax, SEG @data;
   mov ds, ax; { ds = TP:ds, not the driver's ds }
   pop ax; { ax has the reason .. }
   mov eventX, cx; { where in the x region did it occur }
   mov eventY, dx;
   mov eventButtons, bx;
   mov eventHappened, 1; { eventHapppened := true }
   and ax, CURSOR_LOCATION_CHANGED; { o.k., do we need to handle mouse movement? }
   jz noCursorMove;
   call doPascalStuff;
   mov eventHappened, 0;
   { NOTICE - no movement events are detected in the out world ! - this is a
     wintext consideration - It might be needed to track mouse movements,
     and then it should be changed ! - but this is MY default handler ! }
noCursorMove: { no need for cursor movement handling }
   pop ds; { restore driver's ds }
end; {vgaTextGraphicHandler}

(******************************************************************************
*                                GetLastEvent                                 *
******************************************************************************)
function GetLastEvent(var x,y : word;
        var left_button,right_button,middle_button : buttonState) : boolean;

begin
        getLastEvent := eventhappened; {indicate if any event happened}
        eventhappened := False; {clear to next read/event}
        x := eventX;
        y := eventY;
        if ((eventButtons and cLinke_taste) <> 0) then
                left_button := buttonDown
        else left_button := buttonUp;
        if ((eventButtons and cRechte_taste) <> 0) then
                right_button := buttonDown
        else right_button := buttonUp;
        if ((eventButtons and cMittlere_taste) <> 0) then
                middle_button := buttonDown
        else middle_button := buttonUp;
end; {getLastEvent}

(******************************************************************************
*                              setDefaultHandler                              *
******************************************************************************)
procedure setDefaultHandler(mask : WORD);

{get only event mask, and set event handler to defaultHandler}

begin
   if (vgaTextGraphicCursor) then begin
      mask := mask or CURSOR_LOCATION_CHANGED; { we MUST detect cursor movement
}
           setEventHandler(mask,@vgaTextGraphicHandler);
   end else
           setEventHandler(mask,@defaultHandler);
end; {setDefaultHandler}

(******************************************************************************
*                              defineSensetivity                              *
******************************************************************************)
procedure defineSensetivity(x,y : word);

begin
        regs.ax := 15;
        regs.cx := x; {# of mouse motions to horizontal 8 pixels}
        regs.dx := y; {# of mouse motions to vertical 8 pixels}
        INTR($33, regs);
        XMotions := x;
        YMotions := y; {update global unit variables}
end; {defineSensetivity}

(******************************************************************************
*                              setHideCursorBox                               *
******************************************************************************)
procedure setHideCursorBox(left,top,right,bottom : word);

begin
        regs.ax := 16;
        regs.es := seg(HideBox);
        regs.dx := ofs(HideBox);
        HideBox.left := left;
        HideBox.right := right;
        HideBox.top := top;
        HideBox.bottom := bottom;
        INTR($33, regs);
end; {setHideCursorBox}

(******************************************************************************
*                               waitForRelease                                *
* Wait until button is release, or timeOut 1/100 seconds pass. (might miss a  *
* tenth (1/10) of a second.                                                                                                                     *
******************************************************************************)
procedure waitForRelease(timeout : WORD);
var
    sHour, sMinute, sSecond, sSec100 : word;        { Time at start }
    cHour, cMinute, cSecond, cSec100 : word;        { Current time        }
    stopSec                             : longInt;
    currentSec                          : longInt;
    Delta                             : longInt;
begin
    getTime(sHour, sMinute, sSecond, sSec100);
    stopSec := (sHour*36000 + sMinute*600 + sSecond*10 + sSec100 + timeOut) mod
                    (24*360000);
    repeat
           getTime(cHour, cMinute, cSecond, cSec100);
           currentSec := (cHour*36000 + cMinute*600 + cSecond*10 + cSec100);
           Delta := currentSec - stopSec;
    until (not ButtonPressed) or (Delta >=0) and (Delta < 36000);
end; {waitForRelease}

(******************************************************************************
*                              swapEventHandler                               *
* handler is a far routine.                                                   *
******************************************************************************)
procedure swapEventHandler(mask : WORD; handler : POINTER);
begin
   regs.ax := $14;
   regs.cx := mask;
        regs.es := seg(handler^);
        regs.dx := ofs(handler^);
        INTR($33, regs);
   lastMask := regs.cx;
   lastHandler := ptr(regs.es,regs.dx);
end; {swapEventHandler}

(******************************************************************************
*                            getMouseSaveStateSize                            *
******************************************************************************)
function getMouseSaveStateSize : WORD;
begin
   regs.ax := $15;
   INTR($33, regs);
   getMouseSaveStateSize := regs.bx;
end; {getMouseSaveStateSize}

(******************************************************************************
*                           setVgaTextGraphicCursor                           *
******************************************************************************)
procedure setVgaTextGraphicCursor;
begin
   vgaTextGraphicCursor := false; { assume we can not .. }
   if (queryAdapterType <> vgaColor) then
      exit;
   vgaTextGraphicCursor := true;
end; {setVgaTextGraphicCursor}

(******************************************************************************
*                          resetVgaTextGraphicCursor                          *
******************************************************************************)
PROCEDURE resetvgatextgraphiccursor;
BEGIN
  vgatextgraphiccursor := FALSE;
END;

PROCEDURE myexitproc; FAR;
BEGIN
  EXITPROC := oldexitproc;
  IF (vgatextgraphiccursor AND hasstoredarray) THEN
    vgascreen2array(FALSE, FALSE, FALSE);
  DISPOSE(chardefs);
  resetvgatextgraphiccursor;
  initmouse;
END;

PROCEDURE set_graphic_mouse_cursor;         { graphischen Mauscursor setzen }
BEGIN
  setvgatextgraphiccursor; initmouse; setdefaulthandler(left_button_pressed);
END;

{ Ä Hauptprogramm der Unit ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ }
BEGIN
   eventx := 0; eventy := 0; eventhappened := FALSE;
   NEW(chardefs); initmouse;
   oldexitproc := EXITPROC;
   EXITPROC    := @myexitproc;
END.

