From 9065679ba7707aebf4018db9b8d30a9904c01b66 Mon Sep 17 00:00:00 2001 From: Aleksandar Andrejevic Date: Sat, 9 Aug 2014 01:39:28 +0000 Subject: [PATCH] [NTVDM] Start implementing mouse support. svn path=/trunk/; revision=63842 --- reactos/subsystems/ntvdm/CMakeLists.txt | 1 + reactos/subsystems/ntvdm/hardware/mouse.c | 329 ++++++++++++++++++++++ reactos/subsystems/ntvdm/hardware/mouse.h | 78 +++++ reactos/subsystems/ntvdm/hardware/ps2.c | 133 ++++----- reactos/subsystems/ntvdm/hardware/ps2.h | 2 + 5 files changed, 477 insertions(+), 66 deletions(-) create mode 100644 reactos/subsystems/ntvdm/hardware/mouse.c create mode 100644 reactos/subsystems/ntvdm/hardware/mouse.h diff --git a/reactos/subsystems/ntvdm/CMakeLists.txt b/reactos/subsystems/ntvdm/CMakeLists.txt index 2ee9f55eaad..7f480dc8f82 100644 --- a/reactos/subsystems/ntvdm/CMakeLists.txt +++ b/reactos/subsystems/ntvdm/CMakeLists.txt @@ -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 diff --git a/reactos/subsystems/ntvdm/hardware/mouse.c b/reactos/subsystems/ntvdm/hardware/mouse.c new file mode 100644 index 00000000000..63399d90cce --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/mouse.c @@ -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 + */ + +/* 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; +} diff --git a/reactos/subsystems/ntvdm/hardware/mouse.h b/reactos/subsystems/ntvdm/hardware/mouse.h new file mode 100644 index 00000000000..14e430a53b0 --- /dev/null +++ b/reactos/subsystems/ntvdm/hardware/mouse.h @@ -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 + */ + +#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_ diff --git a/reactos/subsystems/ntvdm/hardware/ps2.c b/reactos/subsystems/ntvdm/hardware/ps2.c index c98d32e76df..d2f27bba649 100644 --- a/reactos/subsystems/ntvdm/hardware/ps2.c +++ b/reactos/subsystems/ntvdm/hardware/ps2.c @@ -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 */ diff --git a/reactos/subsystems/ntvdm/hardware/ps2.h b/reactos/subsystems/ntvdm/hardware/ps2.h index 119e9bdc471..e0eca521057 100644 --- a/reactos/subsystems/ntvdm/hardware/ps2.h +++ b/reactos/subsystems/ntvdm/hardware/ps2.h @@ -24,6 +24,8 @@ /* FUNCTIONS ******************************************************************/ +BOOLEAN KeyboardQueuePush(BYTE ScanCode); +BOOLEAN KeyboardQueuePop(BYTE *ScanCode); VOID PS2Dispatch(PINPUT_RECORD InputRecord); VOID GenerateKeyboardInterrupts(VOID);