[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Sat, 9 Aug 2014 01:39:28 +0000 (01:39 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Sat, 9 Aug 2014 01:39:28 +0000 (01:39 +0000)
Start implementing mouse support.

svn path=/trunk/; revision=63842

reactos/subsystems/ntvdm/CMakeLists.txt
reactos/subsystems/ntvdm/hardware/mouse.c [new file with mode: 0644]
reactos/subsystems/ntvdm/hardware/mouse.h [new file with mode: 0644]
reactos/subsystems/ntvdm/hardware/ps2.c
reactos/subsystems/ntvdm/hardware/ps2.h

index 2ee9f55..7f480dc 100644 (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
diff --git a/reactos/subsystems/ntvdm/hardware/mouse.c b/reactos/subsystems/ntvdm/hardware/mouse.c
new file mode 100644 (file)
index 0000000..63399d9
--- /dev/null
@@ -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;
+}
diff --git a/reactos/subsystems/ntvdm/hardware/mouse.h b/reactos/subsystems/ntvdm/hardware/mouse.h
new file mode 100644 (file)
index 0000000..14e430a
--- /dev/null
@@ -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_
index c98d32e..d2f27bb 100644 (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 */
index 119e9bd..e0eca52 100644 (file)
@@ -24,6 +24,8 @@
 
 /* FUNCTIONS ******************************************************************/
 
+BOOLEAN KeyboardQueuePush(BYTE ScanCode);
+BOOLEAN KeyboardQueuePop(BYTE *ScanCode);
 VOID PS2Dispatch(PINPUT_RECORD InputRecord);
 VOID GenerateKeyboardInterrupts(VOID);