mirror of
https://github.com/reactos/reactos.git
synced 2024-07-31 00:28:56 +00:00
[NTVDM]
Start implementing mouse support. svn path=/trunk/; revision=63842
This commit is contained in:
parent
8a230af1a7
commit
9065679ba7
|
@ -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
|
||||
|
|
329
reactos/subsystems/ntvdm/hardware/mouse.c
Normal file
329
reactos/subsystems/ntvdm/hardware/mouse.c
Normal 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;
|
||||
}
|
78
reactos/subsystems/ntvdm/hardware/mouse.h
Normal file
78
reactos/subsystems/ntvdm/hardware/mouse.h
Normal 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_
|
|
@ -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 */
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
BOOLEAN KeyboardQueuePush(BYTE ScanCode);
|
||||
BOOLEAN KeyboardQueuePop(BYTE *ScanCode);
|
||||
VOID PS2Dispatch(PINPUT_RECORD InputRecord);
|
||||
VOID GenerateKeyboardInterrupts(VOID);
|
||||
|
||||
|
|
Loading…
Reference in a new issue