2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: Mouse emulation
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
18 #include "video/vga.h"
20 /* PRIVATE VARIABLES **********************************************************/
22 static const BYTE ScrollMagic
[3] = { 200, 100, 80 };
23 static const BYTE ExtraButtonMagic
[3] = { 200, 200, 80 };
25 static HANDLE MouseMutex
;
26 static PHARDWARE_TIMER StreamTimer
;
27 static MOUSE_PACKET LastPacket
;
28 static MOUSE_MODE Mode
, PreviousMode
;
29 static COORD Position
;
30 static BYTE Resolution
; /* Completely ignored */
31 static BOOLEAN Scaling
; /* Completely ignored */
32 static BOOLEAN Reporting
;
34 static ULONG ButtonState
;
35 static SHORT HorzCounter
;
36 static SHORT VertCounter
;
37 static CHAR ScrollCounter
;
38 static BOOLEAN EventsOccurred
= FALSE
;
39 static BYTE DataByteWait
= 0;
40 static BYTE ScrollMagicCounter
= 0, ExtraButtonMagicCounter
= 0;
42 static BYTE PS2Port
= 1;
44 /* PUBLIC VARIABLES ***********************************************************/
46 UINT MouseCycles
= 10;
48 /* PRIVATE FUNCTIONS **********************************************************/
50 static VOID
MouseResetConfig(VOID
)
52 /* Reset the configuration to defaults */
59 static VOID
MouseResetCounters(VOID
)
61 /* Reset all flags and counters */
62 HorzCounter
= VertCounter
= ScrollCounter
= 0;
65 static VOID
MouseReset(VOID
)
67 /* Reset everything */
71 /* Enter streaming mode and the reset the mouse ID */
72 Mode
= MOUSE_STREAMING_MODE
;
74 ScrollMagicCounter
= ExtraButtonMagicCounter
= 0;
76 /* Send the Basic Assurance Test success code and the device ID */
77 PS2QueuePush(PS2Port
, MOUSE_BAT_SUCCESS
);
78 PS2QueuePush(PS2Port
, MouseId
);
81 static VOID
MouseGetPacket(PMOUSE_PACKET Packet
)
83 /* Clear the packet */
84 RtlZeroMemory(Packet
, sizeof(*Packet
));
86 /* Acquire the mutex */
87 WaitForSingleObject(MouseMutex
, INFINITE
);
89 Packet
->Flags
|= MOUSE_ALWAYS_SET
;
91 /* Set the sign flags */
94 Packet
->Flags
|= MOUSE_X_SIGN
;
95 HorzCounter
= -HorzCounter
;
100 Packet
->Flags
|= MOUSE_Y_SIGN
;
101 VertCounter
= -VertCounter
;
104 /* Check for horizontal overflows */
105 if (HorzCounter
> MOUSE_MAX
)
107 HorzCounter
= MOUSE_MAX
;
108 Packet
->Flags
|= MOUSE_X_OVERFLOW
;
111 /* Check for vertical overflows */
112 if (VertCounter
> MOUSE_MAX
)
114 VertCounter
= MOUSE_MAX
;
115 Packet
->Flags
|= MOUSE_Y_OVERFLOW
;
118 /* Set the button flags */
119 if (ButtonState
& FROM_LEFT_1ST_BUTTON_PRESSED
) Packet
->Flags
|= MOUSE_LEFT_BUTTON
;
120 if (ButtonState
& FROM_LEFT_2ND_BUTTON_PRESSED
) Packet
->Flags
|= MOUSE_MIDDLE_BUTTON
;
121 if (ButtonState
& RIGHTMOST_BUTTON_PRESSED
) Packet
->Flags
|= MOUSE_RIGHT_BUTTON
;
125 if (ButtonState
& FROM_LEFT_3RD_BUTTON_PRESSED
) Packet
->Extra
|= MOUSE_4TH_BUTTON
;
126 if (ButtonState
& FROM_LEFT_4TH_BUTTON_PRESSED
) Packet
->Extra
|= MOUSE_5TH_BUTTON
;
131 /* Set the scroll counter */
132 Packet
->Extra
|= (UCHAR
)ScrollCounter
& 0x0F;
135 /* Store the counters in the packet */
136 Packet
->HorzCounter
= LOBYTE(HorzCounter
);
137 Packet
->VertCounter
= LOBYTE(VertCounter
);
139 /* Reset the counters */
140 MouseResetCounters();
142 /* Release the mutex */
143 ReleaseMutex(MouseMutex
);
146 static VOID
MouseDispatchPacket(PMOUSE_PACKET Packet
)
148 PS2QueuePush(PS2Port
, Packet
->Flags
);
149 PS2QueuePush(PS2Port
, Packet
->HorzCounter
);
150 PS2QueuePush(PS2Port
, Packet
->VertCounter
);
151 if (MouseId
>= 3) PS2QueuePush(PS2Port
, Packet
->Extra
);
154 static VOID WINAPI
MouseCommand(LPVOID Param
, BYTE Command
)
156 /* Check if we were waiting for a data byte */
159 PS2QueuePush(PS2Port
, MOUSE_ACK
);
161 switch (DataByteWait
)
166 Resolution
= Command
;
170 /* Set Sample Rate */
173 /* Check for the scroll wheel enabling sequence */
176 if (Command
== ScrollMagic
[ScrollMagicCounter
])
178 ScrollMagicCounter
++;
179 if (ScrollMagicCounter
== 3) MouseId
= 3;
183 ScrollMagicCounter
= 0;
187 /* Check for the 5-button enabling sequence */
190 if (Command
== ExtraButtonMagic
[ExtraButtonMagicCounter
])
192 ExtraButtonMagicCounter
++;
193 if (ExtraButtonMagicCounter
== 3) MouseId
= 4;
197 ExtraButtonMagicCounter
= 0;
201 MouseCycles
= 1000 / (UINT
)Command
;
207 /* Shouldn't happen */
216 /* Check if we're in wrap mode */
217 if (Mode
== MOUSE_WRAP_MODE
)
220 * In this mode, we just echo whatever byte we get,
221 * except for the 0xEC and 0xFF commands.
223 if (Command
!= 0xEC && Command
!= 0xFF)
225 PS2QueuePush(PS2Port
, Command
);
232 /* Set 1:1 Scaling */
236 PS2QueuePush(PS2Port
, MOUSE_ACK
);
240 /* Set 2:1 Scaling */
244 PS2QueuePush(PS2Port
, MOUSE_ACK
);
250 /* Set Sample Rate */
253 PS2QueuePush(PS2Port
, MOUSE_ACK
);
254 DataByteWait
= Command
;
261 BYTE Status
= ButtonState
& 7;
262 PS2QueuePush(PS2Port
, MOUSE_ACK
);
264 if (Scaling
) Status
|= 1 << 4;
265 if (Reporting
) Status
|= 1 << 5;
266 if (Mode
== MOUSE_REMOTE_MODE
) Status
|= 1 << 6;
268 PS2QueuePush(PS2Port
, Status
);
269 PS2QueuePush(PS2Port
, Resolution
);
270 PS2QueuePush(PS2Port
, (BYTE
)(1000 / MouseCycles
));
274 /* Enter Streaming Mode */
277 MouseResetCounters();
278 Mode
= MOUSE_STREAMING_MODE
;
280 PS2QueuePush(PS2Port
, MOUSE_ACK
);
287 PS2QueuePush(PS2Port
, MOUSE_ACK
);
288 MouseGetPacket(&LastPacket
);
289 MouseDispatchPacket(&LastPacket
);
293 /* Return From Wrap Mode */
296 if (Mode
== MOUSE_WRAP_MODE
)
298 /* Restore the previous mode */
299 MouseResetCounters();
301 PS2QueuePush(PS2Port
, MOUSE_ACK
);
303 else PS2QueuePush(PS2Port
, MOUSE_ERROR
);
308 /* Enter Wrap Mode */
311 if (Mode
!= MOUSE_WRAP_MODE
)
313 /* Save the previous mode */
317 MouseResetCounters();
318 Mode
= MOUSE_WRAP_MODE
;
320 PS2QueuePush(PS2Port
, MOUSE_ACK
);
324 /* Enter Remote Mode */
327 MouseResetCounters();
328 Mode
= MOUSE_REMOTE_MODE
;
330 PS2QueuePush(PS2Port
, MOUSE_ACK
);
337 PS2QueuePush(PS2Port
, MOUSE_ACK
);
338 PS2QueuePush(PS2Port
, MouseId
);
342 /* Enable Reporting */
346 PS2QueuePush(PS2Port
, MOUSE_ACK
);
350 /* Disable Reporting */
354 PS2QueuePush(PS2Port
, MOUSE_ACK
);
361 /* Reset the configuration and counters */
363 MouseResetCounters();
370 PS2QueuePush(PS2Port
, MOUSE_ACK
);
371 MouseDispatchPacket(&LastPacket
);
382 /* Unknown command */
385 PS2QueuePush(PS2Port
, MOUSE_ERROR
);
390 static VOID FASTCALL
MouseStreamingCallback(ULONGLONG ElapsedTime
)
392 UNREFERENCED_PARAMETER(ElapsedTime
);
394 /* Check if we're not in streaming mode, not reporting, or there's nothing to report */
395 if (Mode
!= MOUSE_STREAMING_MODE
|| !Reporting
|| !EventsOccurred
) return;
397 MouseGetPacket(&LastPacket
);
398 MouseDispatchPacket(&LastPacket
);
400 EventsOccurred
= FALSE
;
403 /* PUBLIC FUNCTIONS ***********************************************************/
405 VOID
MouseGetDataFast(PCOORD CurrentPosition
, PBYTE CurrentButtonState
)
407 WaitForSingleObject(MouseMutex
, INFINITE
);
408 *CurrentPosition
= Position
;
409 *CurrentButtonState
= LOBYTE(ButtonState
);
410 ReleaseMutex(MouseMutex
);
413 VOID
MouseEventHandler(PMOUSE_EVENT_RECORD MouseEvent
)
415 COORD NewPosition
= MouseEvent
->dwMousePosition
;
416 BOOLEAN DoubleWidth
= FALSE
, DoubleHeight
= FALSE
;
418 if (!VgaGetDoubleVisionState(&DoubleWidth
, &DoubleHeight
))
425 /* Adjust for double vision */
426 if (DoubleWidth
) NewPosition
.X
/= 2;
427 if (DoubleHeight
) NewPosition
.Y
/= 2;
429 WaitForSingleObject(MouseMutex
, INFINITE
);
431 /* Update the counters */
432 HorzCounter
+= (NewPosition
.X
- Position
.X
) << DoubleWidth
;
433 VertCounter
+= (NewPosition
.Y
- Position
.Y
) << DoubleHeight
;
435 /* Update the position */
436 Position
= NewPosition
;
438 /* Update the button state */
439 ButtonState
= MouseEvent
->dwButtonState
;
441 if (MouseEvent
->dwEventFlags
& MOUSE_WHEELED
)
443 ScrollCounter
+= (SHORT
)HIWORD(MouseEvent
->dwButtonState
);
446 EventsOccurred
= TRUE
;
447 ReleaseMutex(MouseMutex
);
450 BOOLEAN
MouseInit(BYTE PS2Connector
)
452 /* Finish to plug the mouse to the specified PS/2 port */
453 PS2Port
= PS2Connector
;
454 PS2SetDeviceCmdProc(PS2Port
, NULL
, MouseCommand
);
456 MouseMutex
= CreateMutex(NULL
, FALSE
, NULL
);
457 if (MouseMutex
== NULL
) return FALSE
;
459 StreamTimer
= CreateHardwareTimer(HARDWARE_TIMER_ENABLED
,
461 MouseStreamingCallback
);