2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/hardware/mouse.c
5 * PURPOSE: PS/2 Mouse emulation
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
20 #include "video/svga.h"
22 /* PRIVATE VARIABLES **********************************************************/
24 static const BYTE ScrollMagic
[3] = { 200, 100, 80 };
25 static const BYTE ExtraButtonMagic
[3] = { 200, 200, 80 };
27 static HANDLE MouseMutex
;
28 static PHARDWARE_TIMER StreamTimer
;
29 static MOUSE_PACKET LastPacket
;
30 static MOUSE_MODE Mode
, PreviousMode
;
31 static COORD Position
;
32 static BYTE Resolution
; /* Completely ignored */
33 static BOOLEAN Scaling
; /* Completely ignored */
34 static BOOLEAN Reporting
= FALSE
;
36 static ULONG ButtonState
;
37 static SHORT HorzCounter
;
38 static SHORT VertCounter
;
39 static CHAR ScrollCounter
;
40 static BOOLEAN EventsOccurred
= FALSE
;
41 static BYTE DataByteWait
= 0;
42 static BYTE ScrollMagicCounter
= 0, ExtraButtonMagicCounter
= 0;
44 static UINT MouseCycles
= 10;
46 static BYTE PS2Port
= 1;
48 /* PUBLIC VARIABLES ***********************************************************/
50 /* PRIVATE FUNCTIONS **********************************************************/
52 static VOID
MouseResetConfig(VOID
)
54 /* Reset the configuration to defaults */
61 static VOID
MouseResetCounters(VOID
)
63 /* Reset all flags and counters */
64 HorzCounter
= VertCounter
= ScrollCounter
= 0;
67 static VOID
MouseReset(VOID
)
69 /* Reset everything */
73 /* Enter streaming mode and the reset the mouse ID */
74 Mode
= MOUSE_STREAMING_MODE
;
76 ScrollMagicCounter
= ExtraButtonMagicCounter
= 0;
79 static VOID
MouseGetPacket(PMOUSE_PACKET Packet
)
81 /* Clear the packet */
82 RtlZeroMemory(Packet
, sizeof(*Packet
));
84 /* Acquire the mutex */
85 WaitForSingleObject(MouseMutex
, INFINITE
);
87 Packet
->Flags
|= MOUSE_ALWAYS_SET
;
89 /* Set the sign flags */
92 Packet
->Flags
|= MOUSE_X_SIGN
;
93 HorzCounter
= -HorzCounter
;
98 Packet
->Flags
|= MOUSE_Y_SIGN
;
99 VertCounter
= -VertCounter
;
102 /* Check for horizontal overflows */
103 if (HorzCounter
> MOUSE_MAX
)
105 HorzCounter
= MOUSE_MAX
;
106 Packet
->Flags
|= MOUSE_X_OVERFLOW
;
109 /* Check for vertical overflows */
110 if (VertCounter
> MOUSE_MAX
)
112 VertCounter
= MOUSE_MAX
;
113 Packet
->Flags
|= MOUSE_Y_OVERFLOW
;
116 /* Set the button flags */
117 if (ButtonState
& FROM_LEFT_1ST_BUTTON_PRESSED
) Packet
->Flags
|= MOUSE_LEFT_BUTTON
;
118 if (ButtonState
& FROM_LEFT_2ND_BUTTON_PRESSED
) Packet
->Flags
|= MOUSE_MIDDLE_BUTTON
;
119 if (ButtonState
& RIGHTMOST_BUTTON_PRESSED
) Packet
->Flags
|= MOUSE_RIGHT_BUTTON
;
123 if (ButtonState
& FROM_LEFT_3RD_BUTTON_PRESSED
) Packet
->Extra
|= MOUSE_4TH_BUTTON
;
124 if (ButtonState
& FROM_LEFT_4TH_BUTTON_PRESSED
) Packet
->Extra
|= MOUSE_5TH_BUTTON
;
129 /* Set the scroll counter */
130 Packet
->Extra
|= ((UCHAR
)ScrollCounter
& 0x0F);
133 /* Store the counters in the packet */
134 Packet
->HorzCounter
= LOBYTE(HorzCounter
);
135 Packet
->VertCounter
= LOBYTE(VertCounter
);
137 /* Reset the counters */
138 MouseResetCounters();
140 /* Release the mutex */
141 ReleaseMutex(MouseMutex
);
144 static VOID
MouseDispatchPacket(PMOUSE_PACKET Packet
)
146 PS2QueuePush(PS2Port
, Packet
->Flags
);
147 PS2QueuePush(PS2Port
, Packet
->HorzCounter
);
148 PS2QueuePush(PS2Port
, Packet
->VertCounter
);
149 if (MouseId
>= 3) PS2QueuePush(PS2Port
, Packet
->Extra
);
152 static VOID WINAPI
MouseCommand(LPVOID Param
, BYTE Command
)
154 /* Check if we were waiting for a data byte */
157 PS2QueuePush(PS2Port
, MOUSE_ACK
);
159 switch (DataByteWait
)
164 Resolution
= Command
;
168 /* Set Sample Rate */
171 /* Check for the scroll wheel enabling sequence */
174 if (Command
== ScrollMagic
[ScrollMagicCounter
])
176 ScrollMagicCounter
++;
177 if (ScrollMagicCounter
== 3) MouseId
= 3;
181 ScrollMagicCounter
= 0;
185 /* Check for the 5-button enabling sequence */
188 if (Command
== ExtraButtonMagic
[ExtraButtonMagicCounter
])
190 ExtraButtonMagicCounter
++;
191 if (ExtraButtonMagicCounter
== 3) MouseId
= 4;
195 ExtraButtonMagicCounter
= 0;
199 MouseCycles
= 1000 / (UINT
)Command
;
205 /* Shouldn't happen */
214 /* Check if we're in wrap mode */
215 if (Mode
== MOUSE_WRAP_MODE
)
218 * In this mode, we just echo whatever byte we get,
219 * except for the 0xEC and 0xFF commands.
221 if (Command
!= 0xEC && Command
!= 0xFF)
223 PS2QueuePush(PS2Port
, Command
);
230 /* Set 1:1 Scaling */
234 PS2QueuePush(PS2Port
, MOUSE_ACK
);
238 /* Set 2:1 Scaling */
242 PS2QueuePush(PS2Port
, MOUSE_ACK
);
248 /* Set Sample Rate */
251 DataByteWait
= Command
;
252 PS2QueuePush(PS2Port
, MOUSE_ACK
);
259 BYTE Status
= ButtonState
& 7;
261 if (Scaling
) Status
|= 1 << 4;
262 if (Reporting
) Status
|= 1 << 5;
263 if (Mode
== MOUSE_REMOTE_MODE
) Status
|= 1 << 6;
265 PS2QueuePush(PS2Port
, MOUSE_ACK
);
266 PS2QueuePush(PS2Port
, Status
);
267 PS2QueuePush(PS2Port
, Resolution
);
268 PS2QueuePush(PS2Port
, (BYTE
)(1000 / MouseCycles
));
272 /* Enter Streaming Mode */
275 MouseResetCounters();
276 Mode
= MOUSE_STREAMING_MODE
;
278 PS2QueuePush(PS2Port
, MOUSE_ACK
);
285 PS2QueuePush(PS2Port
, MOUSE_ACK
);
286 MouseGetPacket(&LastPacket
);
287 MouseDispatchPacket(&LastPacket
);
291 /* Return from Wrap Mode */
294 if (Mode
== MOUSE_WRAP_MODE
)
296 /* Restore the previous mode */
297 MouseResetCounters();
299 PS2QueuePush(PS2Port
, MOUSE_ACK
);
303 PS2QueuePush(PS2Port
, MOUSE_ERROR
);
309 /* Enter Wrap Mode */
312 if (Mode
!= MOUSE_WRAP_MODE
)
314 /* Save the previous mode */
318 MouseResetCounters();
319 Mode
= MOUSE_WRAP_MODE
;
321 PS2QueuePush(PS2Port
, MOUSE_ACK
);
325 /* Enter Remote Mode */
328 MouseResetCounters();
329 Mode
= MOUSE_REMOTE_MODE
;
331 PS2QueuePush(PS2Port
, MOUSE_ACK
);
338 PS2QueuePush(PS2Port
, MOUSE_ACK
);
339 PS2QueuePush(PS2Port
, MouseId
);
343 /* Enable Reporting */
347 MouseResetCounters();
348 PS2QueuePush(PS2Port
, MOUSE_ACK
);
352 /* Disable Reporting */
356 MouseResetCounters();
357 PS2QueuePush(PS2Port
, MOUSE_ACK
);
364 /* Reset the configuration and counters */
366 MouseResetCounters();
367 PS2QueuePush(PS2Port
, MOUSE_ACK
);
374 PS2QueuePush(PS2Port
, MOUSE_ACK
);
375 MouseDispatchPacket(&LastPacket
);
382 /* Send ACKnowledge */
383 PS2QueuePush(PS2Port
, MOUSE_ACK
);
387 /* Send the Basic Assurance Test success code and the device ID */
388 PS2QueuePush(PS2Port
, MOUSE_BAT_SUCCESS
);
389 PS2QueuePush(PS2Port
, MouseId
);
393 /* Unknown command */
396 PS2QueuePush(PS2Port
, MOUSE_ERROR
);
401 static VOID FASTCALL
MouseStreamingCallback(ULONGLONG ElapsedTime
)
403 UNREFERENCED_PARAMETER(ElapsedTime
);
405 /* Check if we're not in streaming mode, not reporting, or there's nothing to report */
406 if (Mode
!= MOUSE_STREAMING_MODE
|| !Reporting
|| !EventsOccurred
) return;
408 MouseGetPacket(&LastPacket
);
409 MouseDispatchPacket(&LastPacket
);
411 EventsOccurred
= FALSE
;
414 /* PUBLIC FUNCTIONS ***********************************************************/
416 VOID
MouseGetDataFast(PCOORD CurrentPosition
, PBYTE CurrentButtonState
)
418 WaitForSingleObject(MouseMutex
, INFINITE
);
419 *CurrentPosition
= Position
;
420 *CurrentButtonState
= LOBYTE(ButtonState
);
421 ReleaseMutex(MouseMutex
);
424 VOID
MouseEventHandler(PMOUSE_EVENT_RECORD MouseEvent
)
426 COORD NewPosition
= MouseEvent
->dwMousePosition
;
427 BOOLEAN DoubleWidth
= FALSE
, DoubleHeight
= FALSE
;
429 if (!VgaGetDoubleVisionState(&DoubleWidth
, &DoubleHeight
))
436 /* Adjust for double vision */
437 if (DoubleWidth
) NewPosition
.X
/= 2;
438 if (DoubleHeight
) NewPosition
.Y
/= 2;
440 WaitForSingleObject(MouseMutex
, INFINITE
);
442 /* Update the counters */
443 HorzCounter
+= (NewPosition
.X
- Position
.X
) << DoubleWidth
;
444 VertCounter
+= (NewPosition
.Y
- Position
.Y
) << DoubleHeight
;
446 /* Update the position */
447 Position
= NewPosition
;
449 /* Update the button state */
450 ButtonState
= MouseEvent
->dwButtonState
;
452 if (MouseEvent
->dwEventFlags
& MOUSE_WHEELED
)
454 ScrollCounter
+= (SHORT
)HIWORD(MouseEvent
->dwButtonState
);
457 EventsOccurred
= TRUE
;
458 ReleaseMutex(MouseMutex
);
461 BOOLEAN
MouseInit(BYTE PS2Connector
)
463 /* Finish to plug the mouse to the specified PS/2 port */
464 PS2Port
= PS2Connector
;
465 PS2SetDeviceCmdProc(PS2Port
, NULL
, MouseCommand
);
467 MouseMutex
= CreateMutex(NULL
, FALSE
, NULL
);
468 if (MouseMutex
== NULL
) return FALSE
;
470 StreamTimer
= CreateHardwareTimer(HARDWARE_TIMER_ENABLED
,
472 MouseStreamingCallback
);