/*===========================================================================
 *
 * File:	DosMouse.CPP
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Sunday, May 20, 2001
 *
 * Implements the CDosMouse class for handling a mouse in a DOS system.
 *
 *=========================================================================*/

	/* Include Files */
#include "dos\dosmouse.h"


/*===========================================================================
 *
 * Begin Local/Global Variable Definitions
 *
 *=========================================================================*/
  DEFINE_FILE();

	/* Static class member */
  boolean CDosMouse::m_HandlerInstalled = FALSE;

	/* Global variables used by the Dos mouse handler */
  short g_DosMouseX = 0;
  short g_DosMouseY = 0;
  short g_DosMouseStatus = 0;
  short g_DosMouseXCount = 0;
  short g_DosMouseYCount = 0;

/*===========================================================================
 *		End of Local/Global Variable Definitions
 *=========================================================================*/



/*===========================================================================
 *
 * Function - void DosMouseHandler (void);
 *
 * The custom mouse handler interrupt function.  Records the various
 * states of the mouse.
 *
 *=========================================================================*/
void interrupt DosMouseHandler (void) {
  //DEFINE_FUNCTION("DosMouseHandler()");

  ASM {
    mov		[g_DosMouseStatus], bx	/* Update button status */

    test	ax, 1                   /* Is this a motion event? */
    jz		HANDLERDONE

    mov		[g_DosMouseX], cx	/* Save mouse position */
    mov		[g_DosMouseY], dx
    mov		[g_DosMouseXCount], si
    mov		[g_DosMouseYCount], di
  };
HANDLERDONE:
  return;
 }
/*===========================================================================
 *		End of Function DosMouseHandler()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Constructor
 *
 *=========================================================================*/
CDosMouse::CDosMouse () {
  //DEFINE_FUNCTION("CDosMouse::CDosMouse()");
  m_Initialized = FALSE;
  m_CursorVisible = FALSE;
  m_HandlerInstalled = FALSE;
 }
/*===========================================================================
 *		End of Class CDosMouse Constructor
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - void Destroy (void);
 *
 * Class pseudo-destructor.
 *
 *=========================================================================*/
void CDosMouse::Destroy (void) {
  //DEFINE_FUNCTION("CDosMouse::Destroy()");

	/* Hide the mouse if it's initialized and visible */
  if (IsInitialized()) {
    Hide();
    RemoveHandler();
   }

  m_Initialized = FALSE;
  m_CursorVisible = FALSE;

 }
/*===========================================================================
 *		End of Class Method CDosMouse::Destroy()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - boolean GetCounters (XCount, YCount);
 *
 * Gets the value of the motion counters since the last call.  Values
 * returned are in 'Mickeys', the smallest increment the mouse can
 * measure.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDosMouse::GetCounters (short& XCount, short& YCount) {
  //DEFINE_FUNCTION("CDosMouse::GetCounters()");
  short XValue;
  short YValue;

	/* Abort if not previously initialized */
  if (!IsInitialized()) return (FALSE);

  ASM {
    mov		ax, 0bh		// Func 0x0B, Get Press Data
    int		33h
    mov		[XValue], cx
    mov		[YValue], dx
   };

  XCount = XValue;
  YCount = YValue;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDosMouse::GetCounters()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - boolean GetPressStatus (ButtonState,  Button);
 *
 * Gets the information on the number of presses for the given button.
 * Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDosMouse::GetPressStatus (mouse_buttonstate_t& ButtonState, 
				   const mouse_button_t Button) {
  //DEFINE_FUNCTION("CDosMouse::GetPressStatus()");
  short Status;
  short Count;
  short XPos;
  short YPos;

  	/* Abort if not previously initialized */
  if (!IsInitialized()) {
    RegisterNotInitError();
    return (FALSE);
   }

  ASM {
    mov		ax, 5h		// Func 5, Get Press Data
    mov		bx, [Button]
    int		33h
    mov		[Status], ax
    mov		[Count], bx
    mov		[XPos], cx
    mov		[YPos], dx
   };

  ButtonState.ButtonStatus.Status = Status;
  ButtonState.EventCount = Count;
  ButtonState.EventXPos = XPos;
  ButtonState.EventYPos = YPos;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDosMouse::GetPressStatus()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - boolean GetReleaseStatus (ButtonState,  Button);
 *
 * Gets the information on the number of releases for the given button.
 * Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDosMouse::GetReleaseStatus (mouse_buttonstate_t& ButtonState, 
				     const mouse_button_t Button) {
  //DEFINE_FUNCTION("CDosMouse::GetReleaseStatus()");
  short Status;
  short Count;
  short XPos;
  short YPos;

  	/* Abort if not previously initialized */
  if (!IsInitialized()) {
    RegisterNotInitError();
    return (FALSE);
   }

  ASM {
    mov		ax, 6h		// Func 6, Get Release Data
    mov		bx, [Button]
    int		33h
    mov		[Status], ax
    mov		[Count], bx
    mov		[XPos], cx
    mov		[YPos], dx
   };

  ButtonState.ButtonStatus.Status = Status;
  ButtonState.EventCount = Count;
  ButtonState.EventXPos = XPos;
  ButtonState.EventYPos = YPos;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDosMouse::GetReleaseStatus()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - boolean GetButtonStatus (ButtonStatus);
 *
 * Attempts to get the status of the mouse buttons. Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDosMouse::GetButtonStatus (mouse_status_t& ButtonStatus) {
  //DEFINE_FUNCTION("CDosMouse::GetButtonStatus()");
  short Value;

	/* Abort if not previously initialized */
  if (!IsInitialized()) {
    RegisterNotInitError();
    return (FALSE);
   }

  ASM {
     mov	ax, 3		// FUNC 3, Get Mouse Position/Status
     int	33h
     mov	[Value], bx
   };

  ButtonStatus.Status = Value;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDosMouse::GetButtonStatus()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - boolean GetPosition (XPos, YPos);
 *
 * Attempts to get the (X,Y) position of the mouse cursor.  Depending
 * on the current video mode, the position might be text or pixel
 * coordinates.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDosMouse::GetPosition (short& XPos, short& YPos) {
  //DEFINE_FUNCTION("CDosMouse::GetPosition()");
  short XValue;
  short YValue;

	/* Abort if not previously initialized */
  if (!IsInitialized()) {
    RegisterNotInitError();
    return (FALSE);
   }

  ASM {
     mov	ax, 3		// FUNC 3, Get Mouse Position/Status
     int	33h
     mov	[XValue], cx
     mov	[YValue], dx
   };

  XPos = XValue;
  YPos = YValue;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDosMouse::GetPosition()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - void Hide (void);
 *
 * Attempt to hide the mouse cursor.
 *
 *=========================================================================*/
void CDosMouse::Hide (void) {
  //DEFINE_FUNCTION("CDosMouse::Hide()");

	/* Ignore if the mouse is already hidden or not initialized */
  if (!IsVisible() || !IsInitialized()) return;


  ASM {
     mov	ax, 2	     /* Func 2, Hide Mouse Interrupt */
     int	33h
   };

  m_CursorVisible = FALSE;
  return;
 }
/*===========================================================================
 *		End of Class Method CDosMouse::Hide()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - boolean Initialize (void);
 *
 * Attempts to initialize the mouse.  Returns FALSE on any error. Does
 * not install the custom interrupt handler.
 *
 *=========================================================================*/
boolean CDosMouse::Initialize (void) {
  //DEFINE_FUNCTION("CDosMouse::Initialize()");
  short MouseButtonCount;
  short Result;

	/* Abort if called in a Win32 console application */
  #if defined(_WIN32) && defined(_CONSOLE)
    ABORT("DOSMouse will not work correctly in a Win32 console application!");
  #endif

	/* Ignore if already initialized */
  if (IsInitialized()) return (TRUE);

  ASM {
     xor	ax, ax			// FUNC 0, Mouse Initialization
     int	33h
     mov        [Result], ax		// Save the result
     or		ax, ax			// Is there a mouse installed ?
     jz		NOMOUSEFOUND
     mov	[MouseButtonCount], bx	// Save the button count
   };
NOMOUSEFOUND:

	/* Check the results */
  if (Result != 0) {
    m_Initialized = TRUE;
    m_CursorVisible = FALSE;
   }
  else
    ErrorHandler.AddError(ERR_BADINPUT, "Failed to find the mouse driver!");

  return (m_Initialized);
 }
/*===========================================================================
 *		End of Class Method CDosMouse::Initialize()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - boolean InstallHandler (void);
 *
 * Class method which attempts to install the custom handler
 * interrupt function.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDosMouse::InstallHandler (void) {
  //DEFINE_FUNCTION("CDosMouse::InstallHandler()");

	/* Ignore if handler is installed or if mouse not initialized */
  if (!IsInitialized()) {
    RegisterNotInitError();
    return (FALSE);
   }

  if (IsHandlerInstalled()) return (TRUE);

#if defined(__TURBOC__)
  short Result;

  ASM {
    mov		dx, offset DosMouseHandler
    mov		bx, seg DosMouseHandler
    mov		es, bx
    mov		ax, 0Ch
    mov		cx, 0ffh
    int		33h
    mov		[Result], ax
   }

  if (Result != 0x0C) return (FALSE);
  m_HandlerInstalled = TRUE;
#else
  ABORT("CDosMouse::InstallHandler() only functions correctly in TurboC under DOS!");
#endif
  
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDosMouse::InstallHandler()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - boolean RemoveHandler (void);
 *
 * Class method which attempts to remove the custom handler
 * interrupt function.  Returns FALSE on any error.
 *
 *=========================================================================*/
void CDosMouse::RemoveHandler (void) {
  //DEFINE_FUNCTION("CDosMouse::RemoveHandler()");

	/* Ignore if handler is not currently installed */
  if (!IsHandlerInstalled()) return;

  ASM {
    mov		ax, 0Ch
    xor		cx, cx
    int		33h
   }

  m_HandlerInstalled = FALSE;
  return;
 }
/*===========================================================================
 *		End of Class Method CDosMouse::RemoveHandler()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - boolean SetPosition (XPos, YPos);
 *
 * Attempts to set the (X,Y) position of the mouse cursor.  Depending
 * on the current video mode, the position might be text or pixel
 * coordinates.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDosMouse::SetPosition (const short XPos, const short YPos) {
  //DEFINE_FUNCTION("CDosMouse::SetPosition()");
  
	/* Abort if not previously initialized */
  if (!IsInitialized()) {
    RegisterNotInitError();
    return (FALSE);
   }

  ASM {
     mov	ax, 4		// FUNC 4, Set Mouse Position
     mov   	cx, XPos
     mov   	dx, YPos
     int	33h
   };

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDosMouse::SetPosition()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - boolean SetWindow (Left, Top, Right, Bottom);
 *
 * Attempt to define the window that the mouse cursor is limited to.
 * Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDosMouse::SetWindow (const short Left,  const short Top, 
			      const short Right, const short Bottom) {
  //DEFINE_FUNCTION("CDosMouse::SetWindow()");

	/* Abort if not previously initialized */
  if (!IsInitialized()) {
    RegisterNotInitError();
    return (FALSE);
   }

  ASM {
     mov	ax, 7		// FUNC 7, Set Horizontal Range
     mov   	cx, Left
     mov   	dx, Right
     int	33h

     mov	ax, 8		// FUNC 8, Set Vertical Range
     mov   	cx, Top
     mov   	dx, Bottom
     int	33h
   };


  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDosMouse::SetWindow()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDosMouse Method - void Show (void);
 *
 * Attempt to show the mouse cursor. 
 *
 *=========================================================================*/
void CDosMouse::Show (void) {
  //DEFINE_FUNCTION("CDosMouse::Show()");

	/* Ignore if the mouse is already visible or not initialized */
  if (IsVisible() || !IsInitialized()) return;

  ASM {
     mov	ax, 1	     /* Func 2, Show Mouse Interrupt */
     int	33h
   };

  m_CursorVisible = TRUE;
  return;
 }
/*===========================================================================
 *		End of Class Method CDosMouse::Show()
 *=========================================================================*/