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 #include "../console/video.h"
25 /* PRIVATE VARIABLES **********************************************************/
27 static const BYTE ScrollMagic
[3] = { 200, 100, 80 };
28 static const BYTE ExtraButtonMagic
[3] = { 200, 200, 80 };
30 static HANDLE MouseMutex
;
31 static PHARDWARE_TIMER StreamTimer
;
32 static MOUSE_PACKET LastPacket
;
33 static MOUSE_MODE Mode
, PreviousMode
;
34 static COORD Position
;
35 static BYTE Resolution
; /* Completely ignored */
36 static BOOLEAN Scaling
; /* Completely ignored */
37 static BOOLEAN Reporting
= FALSE
;
39 static ULONG ButtonState
;
40 static SHORT HorzCounter
;
41 static SHORT VertCounter
;
42 static CHAR ScrollCounter
;
43 static BOOLEAN EventsOccurred
= FALSE
;
44 static BYTE DataByteWait
= 0;
45 static BYTE ScrollMagicCounter
= 0, ExtraButtonMagicCounter
= 0;
47 static UINT MouseCycles
= 10;
49 static BYTE PS2Port
= 1;
51 /* PUBLIC VARIABLES ***********************************************************/
53 /* PRIVATE FUNCTIONS **********************************************************/
55 static VOID
MouseResetConfig(VOID
)
57 /* Reset the configuration to defaults */
64 static VOID
MouseResetCounters(VOID
)
66 /* Reset all flags and counters */
67 HorzCounter
= VertCounter
= ScrollCounter
= 0;
70 static VOID
MouseReset(VOID
)
72 /* Reset everything */
76 /* Enter streaming mode and the reset the mouse ID */
77 Mode
= MOUSE_STREAMING_MODE
;
79 ScrollMagicCounter
= ExtraButtonMagicCounter
= 0;
82 static VOID
MouseGetPacket(PMOUSE_PACKET Packet
)
84 /* Clear the packet */
85 RtlZeroMemory(Packet
, sizeof(*Packet
));
87 /* Acquire the mutex */
88 WaitForSingleObject(MouseMutex
, INFINITE
);
90 Packet
->Flags
|= MOUSE_ALWAYS_SET
;
92 /* Set the sign flags */
95 Packet
->Flags
|= MOUSE_X_SIGN
;
96 HorzCounter
= -HorzCounter
;
101 Packet
->Flags
|= MOUSE_Y_SIGN
;
102 VertCounter
= -VertCounter
;
105 /* Check for horizontal overflows */
106 if (HorzCounter
> MOUSE_MAX
)
108 HorzCounter
= MOUSE_MAX
;
109 Packet
->Flags
|= MOUSE_X_OVERFLOW
;
112 /* Check for vertical overflows */
113 if (VertCounter
> MOUSE_MAX
)
115 VertCounter
= MOUSE_MAX
;
116 Packet
->Flags
|= MOUSE_Y_OVERFLOW
;
119 /* Set the button flags */
120 if (ButtonState
& FROM_LEFT_1ST_BUTTON_PRESSED
) Packet
->Flags
|= MOUSE_LEFT_BUTTON
;
121 if (ButtonState
& FROM_LEFT_2ND_BUTTON_PRESSED
) Packet
->Flags
|= MOUSE_MIDDLE_BUTTON
;
122 if (ButtonState
& RIGHTMOST_BUTTON_PRESSED
) Packet
->Flags
|= MOUSE_RIGHT_BUTTON
;
126 if (ButtonState
& FROM_LEFT_3RD_BUTTON_PRESSED
) Packet
->Extra
|= MOUSE_4TH_BUTTON
;
127 if (ButtonState
& FROM_LEFT_4TH_BUTTON_PRESSED
) Packet
->Extra
|= MOUSE_5TH_BUTTON
;
132 /* Set the scroll counter */
133 Packet
->Extra
|= ((UCHAR
)ScrollCounter
& 0x0F);
136 /* Store the counters in the packet */
137 Packet
->HorzCounter
= LOBYTE(HorzCounter
);
138 Packet
->VertCounter
= LOBYTE(VertCounter
);
140 /* Reset the counters */
141 MouseResetCounters();
143 /* Release the mutex */
144 ReleaseMutex(MouseMutex
);
147 static VOID
MouseDispatchPacket(PMOUSE_PACKET Packet
)
149 PS2QueuePush(PS2Port
, Packet
->Flags
);
150 PS2QueuePush(PS2Port
, Packet
->HorzCounter
);
151 PS2QueuePush(PS2Port
, Packet
->VertCounter
);
152 if (MouseId
>= 3) PS2QueuePush(PS2Port
, Packet
->Extra
);
155 static VOID WINAPI
MouseCommand(LPVOID Param
, BYTE Command
)
157 /* Check if we were waiting for a data byte */
160 PS2QueuePush(PS2Port
, MOUSE_ACK
);
162 switch (DataByteWait
)
167 Resolution
= Command
;
171 /* Set Sample Rate */
174 /* Check for the scroll wheel enabling sequence */
177 if (Command
== ScrollMagic
[ScrollMagicCounter
])
179 ScrollMagicCounter
++;
180 if (ScrollMagicCounter
== 3) MouseId
= 3;
184 ScrollMagicCounter
= 0;
188 /* Check for the 5-button enabling sequence */
191 if (Command
== ExtraButtonMagic
[ExtraButtonMagicCounter
])
193 ExtraButtonMagicCounter
++;
194 if (ExtraButtonMagicCounter
== 3) MouseId
= 4;
198 ExtraButtonMagicCounter
= 0;
202 MouseCycles
= 1000 / (UINT
)Command
;
208 /* Shouldn't happen */
217 /* Check if we're in wrap mode */
218 if (Mode
== MOUSE_WRAP_MODE
)
221 * In this mode, we just echo whatever byte we get,
222 * except for the 0xEC and 0xFF commands.
224 if (Command
!= 0xEC && Command
!= 0xFF)
226 PS2QueuePush(PS2Port
, Command
);
233 /* Set 1:1 Scaling */
237 PS2QueuePush(PS2Port
, MOUSE_ACK
);
241 /* Set 2:1 Scaling */
245 PS2QueuePush(PS2Port
, MOUSE_ACK
);
251 /* Set Sample Rate */
254 DataByteWait
= Command
;
255 PS2QueuePush(PS2Port
, MOUSE_ACK
);
262 BYTE Status
= ButtonState
& 7;
264 if (Scaling
) Status
|= 1 << 4;
265 if (Reporting
) Status
|= 1 << 5;
266 if (Mode
== MOUSE_REMOTE_MODE
) Status
|= 1 << 6;
268 PS2QueuePush(PS2Port
, MOUSE_ACK
);
269 PS2QueuePush(PS2Port
, Status
);
270 PS2QueuePush(PS2Port
, Resolution
);
271 PS2QueuePush(PS2Port
, (BYTE
)(1000 / MouseCycles
));
275 /* Enter Streaming Mode */
278 MouseResetCounters();
279 Mode
= MOUSE_STREAMING_MODE
;
281 PS2QueuePush(PS2Port
, MOUSE_ACK
);
288 PS2QueuePush(PS2Port
, MOUSE_ACK
);
289 MouseGetPacket(&LastPacket
);
290 MouseDispatchPacket(&LastPacket
);
294 /* Return from Wrap Mode */
297 if (Mode
== MOUSE_WRAP_MODE
)
299 /* Restore the previous mode */
300 MouseResetCounters();
302 PS2QueuePush(PS2Port
, MOUSE_ACK
);
306 PS2QueuePush(PS2Port
, MOUSE_ERROR
);
312 /* Enter Wrap Mode */
315 if (Mode
!= MOUSE_WRAP_MODE
)
317 /* Save the previous mode */
321 MouseResetCounters();
322 Mode
= MOUSE_WRAP_MODE
;
324 PS2QueuePush(PS2Port
, MOUSE_ACK
);
328 /* Enter Remote Mode */
331 MouseResetCounters();
332 Mode
= MOUSE_REMOTE_MODE
;
334 PS2QueuePush(PS2Port
, MOUSE_ACK
);
341 PS2QueuePush(PS2Port
, MOUSE_ACK
);
342 PS2QueuePush(PS2Port
, MouseId
);
346 /* Enable Reporting */
350 MouseResetCounters();
351 PS2QueuePush(PS2Port
, MOUSE_ACK
);
355 /* Disable Reporting */
359 MouseResetCounters();
360 PS2QueuePush(PS2Port
, MOUSE_ACK
);
367 /* Reset the configuration and counters */
369 MouseResetCounters();
370 PS2QueuePush(PS2Port
, MOUSE_ACK
);
377 PS2QueuePush(PS2Port
, MOUSE_ACK
);
378 MouseDispatchPacket(&LastPacket
);
385 /* Send ACKnowledge */
386 PS2QueuePush(PS2Port
, MOUSE_ACK
);
390 /* Send the Basic Assurance Test success code and the device ID */
391 PS2QueuePush(PS2Port
, MOUSE_BAT_SUCCESS
);
392 PS2QueuePush(PS2Port
, MouseId
);
396 /* Unknown command */
399 PS2QueuePush(PS2Port
, MOUSE_ERROR
);
404 static VOID FASTCALL
MouseStreamingCallback(ULONGLONG ElapsedTime
)
406 UNREFERENCED_PARAMETER(ElapsedTime
);
408 /* Check if we're not in streaming mode, not reporting, or there's nothing to report */
409 if (Mode
!= MOUSE_STREAMING_MODE
|| !Reporting
|| !EventsOccurred
) return;
411 MouseGetPacket(&LastPacket
);
412 MouseDispatchPacket(&LastPacket
);
414 EventsOccurred
= FALSE
;
417 /* PUBLIC FUNCTIONS ***********************************************************/
419 VOID
MouseGetDataFast(PCOORD CurrentPosition
, PBYTE CurrentButtonState
)
421 WaitForSingleObject(MouseMutex
, INFINITE
);
422 *CurrentPosition
= Position
;
423 *CurrentButtonState
= LOBYTE(ButtonState
);
424 ReleaseMutex(MouseMutex
);
427 VOID
MouseEventHandler(PMOUSE_EVENT_RECORD MouseEvent
)
429 COORD NewPosition
= MouseEvent
->dwMousePosition
;
430 BOOLEAN DoubleWidth
= FALSE
, DoubleHeight
= FALSE
;
432 if (!VgaGetDoubleVisionState(&DoubleWidth
, &DoubleHeight
))
439 /* Adjust for double vision */
440 if (DoubleWidth
) NewPosition
.X
/= 2;
441 if (DoubleHeight
) NewPosition
.Y
/= 2;
443 WaitForSingleObject(MouseMutex
, INFINITE
);
445 /* Update the counters */
446 HorzCounter
+= (NewPosition
.X
- Position
.X
) << DoubleWidth
;
447 VertCounter
+= (NewPosition
.Y
- Position
.Y
) << DoubleHeight
;
449 /* Update the position */
450 Position
= NewPosition
;
452 /* Update the button state */
453 ButtonState
= MouseEvent
->dwButtonState
;
455 if (MouseEvent
->dwEventFlags
& MOUSE_WHEELED
)
457 ScrollCounter
+= (SHORT
)HIWORD(MouseEvent
->dwButtonState
);
460 EventsOccurred
= TRUE
;
461 ReleaseMutex(MouseMutex
);
464 BOOLEAN
MouseInit(BYTE PS2Connector
)
466 /* Finish to plug the mouse to the specified PS/2 port */
467 PS2Port
= PS2Connector
;
468 PS2SetDeviceCmdProc(PS2Port
, NULL
, MouseCommand
);
470 MouseMutex
= CreateMutex(NULL
, FALSE
, NULL
);
471 if (MouseMutex
== NULL
) return FALSE
;
473 StreamTimer
= CreateHardwareTimer(HARDWARE_TIMER_ENABLED
,
475 MouseStreamingCallback
);