Start implementing mouse support.


svn path=/trunk/; revision=63842
This commit is contained in:
Aleksandar Andrejevic 2014-08-09 01:39:28 +00:00
parent 8a230af1a7
commit 9065679ba7
5 changed files with 477 additions and 66 deletions

View file

@ -19,6 +19,7 @@ list(APPEND SOURCE
hardware/speaker.c
hardware/timer.c
hardware/vga.c
hardware/mouse.c
dos/dos32krnl/bios.c
dos/dos32krnl/dos.c
dos/dos32krnl/dosfiles.c

View file

@ -0,0 +1,329 @@
/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: mouse.c
* PURPOSE: Mouse emulation
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
*/
/* INCLUDES *******************************************************************/
#define NDEBUG
#include "mouse.h"
#include "ps2.h"
/* PRIVATE VARIABLES **********************************************************/
static MOUSE_MODE Mode, PreviousMode;
static COORD Position;
static ULONG WidthMm, HeightMm, WidthPixels, HeightPixels;
static ULONG SampleRate;
static ULONG Resolution;
static BOOLEAN Scaling;
static BOOLEAN Reporting;
static BYTE MouseId;
static ULONG ButtonState;
static SHORT HorzCounter;
static SHORT VertCounter;
static CHAR ScrollCounter;
/* PRIVATE FUNCTIONS **********************************************************/
static VOID MouseResetConfig(VOID)
{
/* Reset the configuration to defaults */
SampleRate = 100;
Resolution = 4;
Scaling = FALSE;
Reporting = FALSE;
}
static VOID MouseResetCounters(VOID)
{
/* Reset all flags and counters */
ButtonState = HorzCounter = VertCounter = ScrollCounter = 0;
}
static VOID MouseReset(VOID)
{
/* Reset everything */
MouseResetConfig();
MouseResetCounters();
/* Enter streaming mode and the reset the mouse ID */
Mode = MOUSE_STREAMING_MODE;
MouseId = 0;
/* Send the Basic Assurance Test success code and the device ID */
KeyboardQueuePush(MOUSE_BAT_SUCCESS);
KeyboardQueuePush(MouseId);
}
#if 0
static VOID MouseGetPacket(PMOUSE_PACKET Packet)
{
/* Clear the packet */
ZeroMemory(Packet, sizeof(MOUSE_PACKET));
Packet->Flags |= MOUSE_ALWAYS_SET;
/* Check for horizontal overflows */
if ((HorzCounter < MOUSE_MIN) || (HorzCounter > MOUSE_MAX))
{
if (HorzCounter > MOUSE_MAX) HorzCounter = MOUSE_MAX;
if (HorzCounter < MOUSE_MIN) HorzCounter = MOUSE_MIN;
Packet->Flags |= MOUSE_X_OVERFLOW;
}
/* Check for vertical overflows */
if ((VertCounter < MOUSE_MIN) || (VertCounter > MOUSE_MAX))
{
if (VertCounter > MOUSE_MIN) VertCounter = MOUSE_MIN;
if (VertCounter < MOUSE_MIN) VertCounter = MOUSE_MIN;
Packet->Flags |= MOUSE_Y_OVERFLOW;
}
/* Set the sign flags */
if (HorzCounter & MOUSE_SIGN_BIT) Packet->Flags |= MOUSE_X_SIGN;
if (HorzCounter & MOUSE_SIGN_BIT) Packet->Flags |= MOUSE_Y_SIGN;
/* Set the button flags */
if (ButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) Packet->Flags |= MOUSE_LEFT_BUTTON;
if (ButtonState & FROM_LEFT_2ND_BUTTON_PRESSED) Packet->Flags |= MOUSE_MIDDLE_BUTTON;
if (ButtonState & RIGHTMOST_BUTTON_PRESSED) Packet->Flags |= MOUSE_RIGHT_BUTTON;
if (MouseId == 4)
{
if (ButtonState & FROM_LEFT_3RD_BUTTON_PRESSED) Packet->Extra |= MOUSE_4TH_BUTTON;
if (ButtonState & FROM_LEFT_4TH_BUTTON_PRESSED) Packet->Extra |= MOUSE_5TH_BUTTON;
}
if (MouseId >= 3)
{
/* Set the scroll counter */
Packet->Extra |= (UCHAR)ScrollCounter & 0x0F;
}
/* Store the counters in the packet */
Packet->HorzCounter = LOBYTE(HorzCounter);
Packet->VertCounter = LOBYTE(VertCounter);
/* Reset the counters */
MouseResetCounters();
}
#endif
/* PUBLIC FUNCTIONS ***********************************************************/
VOID MouseUpdatePosition(PCOORD NewPosition)
{
/* Update the counters */
HorzCounter += ((NewPosition->X - Position.X) * WidthMm * Resolution) / WidthPixels;
VertCounter += ((NewPosition->Y - Position.Y) * HeightMm * Resolution) / HeightPixels;
/* Update the position */
Position = *NewPosition;
}
VOID MouseUpdateButtons(ULONG NewButtonState)
{
ButtonState = NewButtonState;
}
VOID MouseScroll(LONG Direction)
{
ScrollCounter += Direction;
}
COORD MouseGetPosition(VOID)
{
return Position;
}
VOID MouseCommand(BYTE Command)
{
switch (Command)
{
/* Set 1:1 Scaling */
case 0xE6:
{
Scaling = FALSE;
KeyboardQueuePush(MOUSE_ACK);
break;
}
/* Set 2:1 Scaling */
case 0xE7:
{
Scaling = TRUE;
KeyboardQueuePush(MOUSE_ACK);
break;
}
/* Set Resolution */
case 0xE8:
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
break;
}
/* Read Status */
case 0xE9:
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
break;
}
/* Enter Streaming Mode */
case 0xEA:
{
MouseResetCounters();
Mode = MOUSE_STREAMING_MODE;
KeyboardQueuePush(MOUSE_ACK);
break;
}
/* Read Packet */
case 0xEB:
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
break;
}
/* Return From Wrap Mode */
case 0xEC:
{
if (Mode == MOUSE_WRAP_MODE)
{
/* Restore the previous mode */
MouseResetCounters();
Mode = PreviousMode;
KeyboardQueuePush(MOUSE_ACK);
}
else KeyboardQueuePush(MOUSE_ERROR);
break;
}
/* Enter Wrap Mode */
case 0xEE:
{
if (Mode != MOUSE_WRAP_MODE)
{
/* Save the previous mode */
PreviousMode = Mode;
}
MouseResetCounters();
Mode = MOUSE_WRAP_MODE;
KeyboardQueuePush(MOUSE_ACK);
break;
}
/* Enter Remote Mode */
case 0xF0:
{
MouseResetCounters();
Mode = MOUSE_REMOTE_MODE;
KeyboardQueuePush(MOUSE_ACK);
break;
}
/* Get Mouse ID */
case 0xF2:
{
KeyboardQueuePush(MOUSE_ACK);
KeyboardQueuePush(MouseId);
break;
}
/* Set Sample Rate */
case 0xF3:
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
break;
}
/* Enable Reporting */
case 0xF4:
{
Reporting = TRUE;
KeyboardQueuePush(MOUSE_ACK);
break;
}
/* Disable Reporting */
case 0xF5:
{
Reporting = FALSE;
KeyboardQueuePush(MOUSE_ACK);
break;
}
/* Set Defaults */
case 0xF6:
{
/* Reset the configuration and counters */
MouseResetConfig();
MouseResetCounters();
break;
}
/* Resend */
case 0xFE:
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
break;
}
/* Reset */
case 0xFF:
{
MouseReset();
break;
}
/* Unknown command */
default:
{
KeyboardQueuePush(MOUSE_ERROR);
}
}
}
BOOLEAN MouseInit(VOID)
{
HWND hWnd;
HDC hDC;
/* Get the console window */
hWnd = GetConsoleWindow();
if (hWnd == NULL) return FALSE;
/* Get the console window's device context */
hDC = GetWindowDC(hWnd);
if (hDC == NULL) return FALSE;
/* Get the parameters */
WidthMm = (ULONG)GetDeviceCaps(hDC, HORZSIZE);
HeightMm = (ULONG)GetDeviceCaps(hDC, VERTSIZE);
WidthPixels = (ULONG)GetDeviceCaps(hDC, HORZRES);
HeightPixels = (ULONG)GetDeviceCaps(hDC, VERTRES);
/* Release the device context */
ReleaseDC(hWnd, hDC);
MouseReset();
return TRUE;
}

View file

@ -0,0 +1,78 @@
/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: mouse.h
* PURPOSE: Mouse emulation
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
*/
#ifndef _MOUSE_H_
#define _MOUSE_H_
/* INCLUDES *******************************************************************/
#include "ntvdm.h"
/* DEFINES ********************************************************************/
/* Mouse packet constants */
#define MOUSE_MIN -256
#define MOUSE_MAX 255
#define MOUSE_SIGN_BIT (1 << 8)
/* Mouse packet flags */
#define MOUSE_LEFT_BUTTON (1 << 0)
#define MOUSE_RIGHT_BUTTON (1 << 1)
#define MOUSE_MIDDLE_BUTTON (1 << 2)
#define MOUSE_ALWAYS_SET (1 << 3)
#define MOUSE_X_SIGN (1 << 4)
#define MOUSE_Y_SIGN (1 << 5)
#define MOUSE_X_OVERFLOW (1 << 6)
#define MOUSE_Y_OVERFLOW (1 << 7)
/* Mouse packet extra flags */
#define MOUSE_4TH_BUTTON (1 << 4)
#define MOUSE_5TH_BUTTON (1 << 5)
/* Command responses */
#define MOUSE_BAT_SUCCESS 0xAA
#define MOUSE_ACK 0xFA
#define MOUSE_ERROR 0xFC
/*
* Scrolling directions
*
* It may seem odd that the directions are implemented this way, but
* this is how it's done on real hardware. It works because the two
* scroll wheels can't be used at the same time.
*/
#define MOUSE_SCROLL_UP 1
#define MOUSE_SCROLL_DOWN -1
#define MOUSE_SCROLL_RIGHT 2
#define MOUSE_SCROLL_LET -2
typedef enum _MOUSE_MODE
{
MOUSE_STREAMING_MODE,
MOUSE_REMOTE_MODE,
MOUSE_WRAP_MODE,
} MOUSE_MODE, *PMOUSE_MODE;
typedef struct _MOUSE_PACKET
{
BYTE Flags;
BYTE HorzCounter;
BYTE VertCounter;
BYTE Extra;
} MOUSE_PACKET, *PMOUSE_PACKET;
/* FUNCTIONS ******************************************************************/
VOID MouseUpdatePosition(PCOORD NewPosition);
VOID MouseUpdateButtons(ULONG NewButtonState);
VOID MouseScroll(LONG Direction);
COORD MouseGetPosition(VOID);
VOID MouseCommand(BYTE Command);
BOOLEAN MouseInit(VOID);
#endif // _MOUSE_H_

View file

@ -14,6 +14,7 @@
#include "io.h"
#include "ps2.h"
#include "pic.h"
#include "mouse.h"
/* PRIVATE VARIABLES **********************************************************/
@ -28,69 +29,6 @@ static HANDLE QueueMutex = NULL;
/* PRIVATE FUNCTIONS **********************************************************/
static BOOLEAN KeyboardQueuePush(BYTE ScanCode)
{
BOOLEAN Result = TRUE;
WaitForSingleObject(QueueMutex, INFINITE);
/* Check if the keyboard queue is full */
if (!KeyboardQueueEmpty && (KeyboardQueueStart == KeyboardQueueEnd))
{
Result = FALSE;
goto Done;
}
/* Insert the value in the queue */
KeyboardQueue[KeyboardQueueEnd] = ScanCode;
KeyboardQueueEnd++;
KeyboardQueueEnd %= KEYBOARD_BUFFER_SIZE;
/* Since we inserted a value, it's not empty anymore */
KeyboardQueueEmpty = FALSE;
Done:
ReleaseMutex(QueueMutex);
return Result;
}
static BOOLEAN KeyboardQueuePop(BYTE *ScanCode)
{
BOOLEAN Result = TRUE;
/* Make sure the keyboard queue is not empty (fast check) */
if (KeyboardQueueEmpty) return FALSE;
WaitForSingleObject(QueueMutex, INFINITE);
/*
* Recheck whether keyboard queue is not empty (it
* may have changed after having grabbed the mutex).
*/
if (KeyboardQueueEmpty)
{
Result = FALSE;
goto Done;
}
/* Get the scan code */
*ScanCode = KeyboardQueue[KeyboardQueueStart];
/* Remove the value from the queue */
KeyboardQueueStart++;
KeyboardQueueStart %= KEYBOARD_BUFFER_SIZE;
/* Check if the queue is now empty */
if (KeyboardQueueStart == KeyboardQueueEnd)
{
KeyboardQueueEmpty = TRUE;
}
Done:
ReleaseMutex(QueueMutex);
return Result;
}
static BYTE WINAPI PS2ReadPort(ULONG Port)
{
if (Port == PS2_CONTROL_PORT)
@ -156,14 +94,14 @@ static VOID WINAPI PS2WritePort(ULONG Port, BYTE Data)
/* Disable mouse */
case 0xA7:
{
// TODO: Mouse support
// TODO: Not implemented
break;
}
/* Enable mouse */
case 0xA8:
{
// TODO: Mouse support
// TODO: Not implemented
break;
}
@ -268,7 +206,7 @@ static VOID WINAPI PS2WritePort(ULONG Port, BYTE Data)
case 0xD4:
{
// TODO: Mouse support
MouseCommand(Data);
break;
}
}
@ -282,6 +220,69 @@ static VOID WINAPI PS2WritePort(ULONG Port, BYTE Data)
/* PUBLIC FUNCTIONS ***********************************************************/
BOOLEAN KeyboardQueuePush(BYTE ScanCode)
{
BOOLEAN Result = TRUE;
WaitForSingleObject(QueueMutex, INFINITE);
/* Check if the keyboard queue is full */
if (!KeyboardQueueEmpty && (KeyboardQueueStart == KeyboardQueueEnd))
{
Result = FALSE;
goto Done;
}
/* Insert the value in the queue */
KeyboardQueue[KeyboardQueueEnd] = ScanCode;
KeyboardQueueEnd++;
KeyboardQueueEnd %= KEYBOARD_BUFFER_SIZE;
/* Since we inserted a value, it's not empty anymore */
KeyboardQueueEmpty = FALSE;
Done:
ReleaseMutex(QueueMutex);
return Result;
}
BOOLEAN KeyboardQueuePop(BYTE *ScanCode)
{
BOOLEAN Result = TRUE;
/* Make sure the keyboard queue is not empty (fast check) */
if (KeyboardQueueEmpty) return FALSE;
WaitForSingleObject(QueueMutex, INFINITE);
/*
* Recheck whether keyboard queue is not empty (it
* may have changed after having grabbed the mutex).
*/
if (KeyboardQueueEmpty)
{
Result = FALSE;
goto Done;
}
/* Get the scan code */
*ScanCode = KeyboardQueue[KeyboardQueueStart];
/* Remove the value from the queue */
KeyboardQueueStart++;
KeyboardQueueStart %= KEYBOARD_BUFFER_SIZE;
/* Check if the queue is now empty */
if (KeyboardQueueStart == KeyboardQueueEnd)
{
KeyboardQueueEmpty = TRUE;
}
Done:
ReleaseMutex(QueueMutex);
return Result;
}
VOID PS2Dispatch(PINPUT_RECORD InputRecord)
{
/* Check the event type */

View file

@ -24,6 +24,8 @@
/* FUNCTIONS ******************************************************************/
BOOLEAN KeyboardQueuePush(BYTE ScanCode);
BOOLEAN KeyboardQueuePop(BYTE *ScanCode);
VOID PS2Dispatch(PINPUT_RECORD InputRecord);
VOID GenerateKeyboardInterrupts(VOID);