
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{ This is the MOUSESUB.PAS include file for the MOUSE.PAS unit. }
{ It contains various special mouse routines used by the Mouse unit. }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{ Written by Michael Day as of 03/20/93 }
{ This code is released to the public domain. }

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{ The following procedures use the mouse functions to provide }
{ a higher level of control over the mouse }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{---------------------------------------------------------------------------}
{ Normalizes a mouse X position to standard position info }

function GetMx(X:integer):integer;
var Temp : integer;
begin
   case CrtMode^ of
     0,1 : begin
             if MaxCrtX < 64 then
                Temp := (X shr 1) div MouseTextWidth      {320x200 text}
             else
                Temp := X div MouseTextWidth;             {???x??? text}
           end;
     2,3 : Temp := X div MouseTextWidth;                  {640x200 text}
     4,5 : begin
             if HercGraphMouse then
               Temp := X                         {720x348 herc graphics}
             else
               Temp := X shr 1;                       {320x200 graphics}
           end;
     6   : Temp := X;                                 {640x200 graphics}
     7   : Temp := X div MouseTextWidth;                  {640x??? text}
     $D,$13 : Temp := X shr 1;                        {320x200 graphics}
   else
     Temp := X;                                       {640x??? graphics}
   end;
   if ZeroMouse then
     GetMx := Temp                        {zero based mouse positioning}
   else
     GetMx := succ(Temp);            {mouse positioning starts with one}
end;

{---------------------------------------------------------------------------}
{ Normalizes a mouse Y position to standard position info }

function GetMy(Y:integer):integer;
var Temp : integer;
begin
   if M.TextMouse then
     Temp := Y div MouseTextHeight     {convert position for text modes}
   else
     Temp := Y;                      {no conversion needed for graphics}
   if ZeroMouse then
     GetMy := Temp                        {zero based mouse positioning}
   else
     GetMy := succ(Temp);            {mouse positioning starts with one}
end;

{---------------------------------------------------------------------------}
{ converts a standard X position to a mouse X position }

function PutMx(X:integer):integer;
var Temp : integer;
begin
   if ZeroMouse then
     Temp := X                            {zero based mouse positioning}
   else
     Temp := pred(X);                {mouse positioning starts with one}
   if Temp < 0 then Temp := 0;                      {clip value to zero}
   case CrtMode^ of
     0,1 : begin
             if MaxCrtX < 64 then
               PutMx := (Temp * MouseTextWidth) shl 1     {320x200 text}
             else
               PutMx := Temp * MouseTextWidth;            {???x??? text}
           end;
     2,3 : PutMx := Temp * MouseTextWidth;                {640x200 text}
     4,5 : begin
             if HercGraphMouse then
               PutMx := Temp                     {720x348 herc graphics}
             else
               PutMx := Temp shl 1;                   {320x200 graphics}
           end;
     6   : PutMx := Temp;                             {640x200 graphics}
     7   : PutMx := Temp * MouseTextWidth;                {640x??? text}
     $D,$13 : PutMx := Temp shl 1;                    {320x200 graphics}
   else
     PutMx := Temp;                                   {640x??? graphics}
   end;
end;

{---------------------------------------------------------------------------}
{ converts a standard Y position to a mouse Y position }

function PutMy(Y:integer):integer;
var Temp : integer;
begin
   if ZeroMouse then
     Temp := Y                            {zero based mouse positioning}
   else
     Temp := pred(Y);                {mouse positioning starts with one}
   if Temp < 0 then Temp := 0;                      {clip value to zero}
   if M.TextMouse then
     PutMy := Temp * MouseTextHeight   {convert position for text modes}
   else
     PutMy := Temp;                  {no conversion needed for graphics}
end;

{---------------------------------------------------------------------------}
{ This procedure is not a standard mouse function. It is however needed to }
{ work with the Hercules graphics display. When you use the Hercules }
{ graphics display you must call this with the proper display page after }
{ you call InitGraph, but before you call InitMouse. InitGraph needs CrtMode}
{ to be at 7 to detect the Herc display, but the Mouse needs it at 5 or 6 }
{ to detect when the Herc card is in graphs mode. (The Herc card has no }
{ provision for telling the system that it is graphics mode.) }
{ Note: Be sure to call this procedure with a Pg of -1 if you turn graphics }
{ off or anytime before you call InitGraph or DetectGraph. The Mouse unit }
{ contains an Exit procedure that calls SetHercMouse with a value of -1 if }
{ a Hercules graph mode was selected so that the CrtMode byte will be }
{ properly restored on exit from the program. }

procedure SetHercMouse(Pg:integer);
begin
  Case Pg of
    0 : begin
          CrtMode^ := 6;     { put mouse on Hercules graphics display Pg 0 }
          HercGraphMouse := true;
        end;
    1 : begin
          CrtMode^ := 5;     { put mouse on Hercules graphics display Pg 1 }
          HercGraphMouse := true;
        end;
  else
    begin
      CrtMode^ := 7;      { indicate that Hercules display is in text mode }
      HercGraphMouse := false;
    end;
  end; {case Pg}
end;

{---------------------------------------------------------------------------}
{ Check if a mouse point is currently in the specified area}
{ returns true if it is, false if not}
{  Recommended calling method: }
{  If MousePointIn(GetMx(Mx),GetMy(My),x1,y1,x2,y2) then DoSomething;}

function MousePointIn(Mx,My, x1,y1,x2,y2:integer):boolean;
begin
   if (Mx >= x1) and
      (Mx <= x2) and                               {check if in the box area}
      (My >= y1) and
      (My <= y2) then
     MousePointIn := true                          {<-- return true if it is}
   else
     MousePointIn := false;                   {<-- return false if it is not}
end;


{---------------------------------------------------------------------------}
function MouseClick:boolean;           {has the mouse been clicked recently?}
begin
   MouseClick := MouseClicked;               {get a copy of the click status}
   MouseClicked := false;                             {then clear the status}
end;


{---------------------------------------------------------------------------}
{This causes the mouse to spin if the mouse clock cursor is selected }
{do not call this yourself, it is called by ReadMouse}

procedure SpinMouse;
const i : word = 0;
      OldClk : word = 0;
begin
  if SysClk^ and $fffc = OldClk then Exit;
  OldClk := SysClk^ and $fffc;
  if M.TextMouse then
  begin
    if (M.MouseTShape = MouseClockCursor) then
    begin
      inc(i);
      if i > 3 then i := 0;
      with MouseTClockArray[i] do
        SetMouseTextCursor(Select,Start,Stop);
      M.MouseSpinerOn := true;
      M.MouseTShape := MouseClockCursor;
    end;
  end
  else
  begin
    if (M.MouseGShape = MouseClockCursor) then
    begin
      inc(i);
      if i > 7 then i := 0;
      SetMouseGraphicCursor(MouseGClockArray[i]);
      M.MouseSpinerOn := true;
      M.MouseGShape := MouseClockCursor;
    end;
  end;
end;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{ the following are misc subroutines used by the Mouse unit }

procedure ReleaseSimMouse;
begin
   if MouseSize <> 0 then
   begin
     if MouseBack <> nil then
       FreeMem(MouseBack,MouseSize);
     if MouseMask <> nil then                    { release all cursor memory}
       FreeMem(MouseMask,MouseSize);
     if MouseFore <> nil then
       FreeMem(MouseFore,MouseSize);
   end;
   MouseBack := nil;
   MouseMask := nil;
   MouseFore := nil;
   MouseSize := 0;                                {<-- mark that it is empty}
end;

{---------------------------------------------------------------------------}
{ This is called by InitMouse to initialize the mouse mode flags}
{ Note: if you are using a Hercules display card in graphics mode,}
{ you must call the SetHercMouse() procedure before calling InitMouse}

procedure InitMouseMode;
begin
   {init common mouse stuff}
   M.MouseGShape := 0;                  {<-- switch to std graph mouse shape}
   M.MouseTShape := 0;                   {<-- switch to std text mouse shape}
   MouseVisible := false;                 {<-- mouse starts off as invisible}
   MouseUpdate := true;
   MouseReDraw := false;
   MouseCondo := false;
   MouseClicked := false;
   MouseButtons := 0;                                 {<-- reset the buttons}
   MouseClickButton := 0;
   ClickMouseX := 0;                           {<-- clear mouse XY locations}
   ClickMouseY := 0;
   MousePressX := 0;
   MousePressY := 0;
   MouseReleaseX := 0;
   MouseReleaseY := 0;

   MouseX := 0;
   MouseY := 0;
   M.MouseHideX1 := -1;
   M.MouseHideX2 := -1;                {Save HideMouseArea - Mouse hide area}
   M.MouseHideY1 := -1;
   M.MouseHideY2 := -1;

   {init simulated mouse stuff}
   M.MouseAreaX1 := 0;                        {initialize mouse bounded area}
   M.MouseAreaY1 := 0;                             {assume defaults to start}
   M.MouseAreaX2 := 639;
   M.MouseAreaY2 := 199;
   MouseTextWidth := 8;                   {BIOS characters are always 8 wide}
   MouseTextHeight := 8;                     {default mouse text height to 8}

   if CrtCols^ = 0 then                       {if BIOS text column width = 0}
     MaxCrtX := 80                              {then force text width to 80}
   else
     MaxCrtX := CrtCols^;                      {else use the indicated width}

   if CrtRows^ = 0 then                         {if BIOS text row height = 0}
     MaxCrtY := 25                             {then force text height to 25}
   else
     MaxCrtY := succ(CrtRows^);               {else use the indicated height}

   if HercGraphMouse then                  {if Herc graphics, handle special}
   begin
     ZeroMouse := true;                 {assume zero mouse for herc graphics}
     M.TextMouse := false;                          {this is a graphics mode}
     M.MouseAreaX2 := 719;
     M.MouseAreaY2 := 347;
   end
   else if (CrtMode^ < 4) or (CrtMode^ = 7) then   {modes 1-3 and 7 are text}
   begin
     M.TextMouse := true;                                 {mark as text mode}
     ZeroMouse := false;                  {CRT based text modes start at one}
     M.MouseAreaX2 := MaxCrtX * MouseTextWidth;  {adjust mouse area based on}
     M.MouseAreaY2 := MaxCrtY * MouseTextHeight; {rows and columns BIOS data}
   end
   else                                               {the rest are graphics}
   begin
     ZeroMouse := true;                           {graph modes start at zero}
     M.TextMouse := false;                                 {mark as graphics}
     case CrtMode^ of
       $F,$10  : M.MouseAreaY2 := 349;                     {640x350 graphics}
       $11,$12 : M.MouseAreaY2 := 479;                     {640x480 graphics}
     end;
   end;

 {$IFDEF GMOUSE}                                  { if we are using graphics}
   ReleaseSimMouse;
 {$ENDIF}

end;

{---------------------------------------------------------------------------}
{This is called by InitMouse and ResetMouse to initialize the mouse position}

procedure InitMousePos;
begin
   if MouseInstalled then
   begin
     asm
       mov AX,3
       int $33                               {Get the current mouse location}
       mov [MouseX],CX                            {save mouse X and Y values}
       mov [MouseY],DX
     end;
   end
   else
   begin
     MouseX := M.MouseAreaX2 shr 1;           {start with calculated default}
     MouseY := M.MouseAreaY2 shr 1;               {screen center if no mouse}
   end;
   SimMouseX := 0;
   SimMouseY := 0;
end;

{---------------------------------------------------------------------------}
{$IFDEF GMOUSE}                 { if we are using graphics enable this stuff}
procedure MakeSimMouse;     { Create a BGI based mouse cursor on the screen }
var i,ii,Mx1,My1,Mx2,My2:integer;                { called from ShowBGIMouse }
    PixelColor:word;
begin
   {if mouse image is inside BGI safe area, then turn off redraw}
   if (MouseImageX > 0) and (EndImageX < GetMaxX) and
      (MouseImageY > 0) and (EndImageY < GetMaxY) then
     MouseReDraw := false;                {image is safe, so turn off redraw}

   with CurMouseMask do
   begin
     if MouseSize = 0 then     {no previously allocated, so grab some memory}
     begin
       MouseSize := ImageSize(0,0,15,15);               {compute cursor size}
       GetMem(MouseBack,MouseSize);                    {then grab the memory}
       GetMem(MouseMask,MouseSize);
       GetMem(MouseFore,MouseSize);
     end;

     Mx1 := IntLimit(MouseImageX,0,GetMaxX);        {compute real image size}
     My1 := IntLimit(MouseImageY,0,GetMaxY);         {bounded by screen area}
     Mx2 := IntLimit(EndImageX,0,GetMaxX);
     My2 := IntLimit(EndImageY,0,GetMaxY);
     GetImage(Mx1,My1,Mx2,My2,MouseBack^);          {save area behind cursor}

     {create the cursor mask with BGI}
     for ii := abs(My1-MouseImageY) to 15-abs(My2-EndImageY) do
     begin
       for i := abs(Mx1-MouseImageX) to 15-abs(Mx2-EndImageX) do
       begin
         if (Def[0][ii] shl i) and $8000 = 0 then
           PixelColor := 0                         {use background for black}
         else
           PixelColor := GetMaxColor;               {use max color for white}
         PutPixel(MouseImageX+i,MouseImageY+ii,PixelColor);
       end;
     end;                                               {save the mask image}
     GetImage(Mx1,My1,Mx2,My2,MouseMask^);

     {create the cursor overlay with BGI}
     for ii := abs(My1-MouseImageY) to 15-abs(My2-EndImageY) do
     begin
       for i := abs(Mx1-MouseImageX) to 15-abs(Mx2-EndImageX) do
       begin
         if (Def[1][ii] shl i) and $8000 = 0 then
           PixelColor := 0                         {use background for black}
         else
           PixelColor := MouseColor;         {use specified color foreground}
         PutPixel(MouseImageX+i,MouseImageY+ii,PixelColor);
       end;
     end;                                     {Save the cursor overlay image}
     GetImage(Mx1,My1,Mx2,My2,MouseFore^);
   end;

   MouseImageX := Mx1;
   MouseImageY := My1;
   PutImage(MouseImageX,MouseImageY,MouseBack^,NormalPut);   { restore image}
   PutImage(MouseImageX,MouseImageY,MouseMask^,AndPut);       { then display}
   PutImage(MouseImageX,MouseImageY,MouseFore^,OrPut);          { new cursor}
end;
{$ENDIF}

{---------------------------------------------------------------------------}
{display a simulated BGI mouse cursor on the screen }

{$IFDEF GMOUSE}
procedure ShowSimMouse;                             { called from ShowMouse }
begin                        {if mouse currently on, restore old image first}
  if MouseVisible and (MouseBack <> nil) then
     PutImage(OldImageX,OldImageY,MouseBack^,NormalPut);

  {compute the mouse image position}
  MouseImageX := GetMx(MouseX)-CurMouseMask.HotX;
  MouseImageY := GetMy(MouseY)-CurMouseMask.HotY;
  EndImageX := MouseImageX+15;
  EndImageY := MouseImageY+15;

  {if cursor never made, or image is partially off screen, remake it}
  if (MouseImageX < 0) or (EndImageX > GetMaxX) or
     (MouseImageY < 0) or (EndImageY > GetMaxY) then
    MouseReDraw := true;

  If (MouseSize = 0) or MouseReDraw then
  begin
    MakeSimMouse;                           { do we need to create a cursor?}
  end
  else
  begin                                   { if cursor already exists, use it}
    GetImage(MouseImageX,MouseImageY,               { save image under mouse}
             EndImageX,EndImageY,MouseBack^);
    PutImage(MouseImageX,MouseImageY,MouseMask^,AndPut);      { then display}
    PutImage(MouseImageX,MouseImageY,MouseFore^,OrPut);         { new cursor}
  end;
  OldImageX := MouseImageX;                          { and save where we are}
  OldImageY := MouseImageY;
  SimMouseX := MouseX;                             { save sim mouse position}
  SimMouseY := MouseY;
  MouseVisible := true;
end;
{$ENDIF}

{---------------------------------------------------------------------------}
{hide a simulated BGI mouse cursor on the screen }

{$IFDEF GMOUSE}
procedure HideSimMouse;
begin
   if MouseState < 0 then Exit;            { nothing to do if already hidden}
   if MouseBack = nil then Exit;        { or if we don't have anything saved}
   PutImage(OldImageX,OldImageY,MouseBack^,NormalPut);
end;
{$ENDIF}

{---------------------------------------------------------------------------}
{ End Of Include File MOUSESUB.PAS }
