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 *******************************************************************/
16 #include "video/vga.h"
18 /* PRIVATE VARIABLES **********************************************************/
20 static const BYTE ScrollMagic
[3] = { 200, 100, 80 };
21 static const BYTE ExtraButtonMagic
[3] = { 200, 200, 80 };
23 static HANDLE MouseMutex
;
24 static PHARDWARE_TIMER StreamTimer
;
25 static MOUSE_PACKET LastPacket
;
26 static MOUSE_MODE Mode
, PreviousMode
;
27 static COORD Position
;
28 static BYTE Resolution
; /* Completely ignored */
29 static BOOLEAN Scaling
; /* Completely ignored */
30 static BOOLEAN Reporting
;
32 static ULONG ButtonState
;
33 static SHORT HorzCounter
;
34 static SHORT VertCounter
;
35 static CHAR ScrollCounter
;
36 static BOOLEAN EventsOccurred
= FALSE
;
37 static BYTE DataByteWait
= 0;
38 static BYTE ScrollMagicCounter
= 0, ExtraButtonMagicCounter
= 0;
40 static BYTE PS2Port
= 1;
42 /* PUBLIC VARIABLES ***********************************************************/
44 UINT MouseCycles
= 10;
46 /* PRIVATE FUNCTIONS **********************************************************/
48 static VOID
MouseResetConfig(VOID
)
50 /* Reset the configuration to defaults */
57 static VOID
MouseResetCounters(VOID
)
59 /* Reset all flags and counters */
60 HorzCounter
= VertCounter
= ScrollCounter
= 0;
63 static VOID
MouseReset(VOID
)
65 /* Reset everything */
69 /* Enter streaming mode and the reset the mouse ID */
70 Mode
= MOUSE_STREAMING_MODE
;
72 ScrollMagicCounter
= ExtraButtonMagicCounter
= 0;
74 /* Send the Basic Assurance Test success code and the device ID */
75 PS2QueuePush(PS2Port
, MOUSE_BAT_SUCCESS
);
76 PS2QueuePush(PS2Port
, MouseId
);
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 PS2QueuePush(PS2Port
, MOUSE_ACK
);
252 DataByteWait
= Command
;
259 BYTE Status
= ButtonState
& 7;
260 PS2QueuePush(PS2Port
, MOUSE_ACK
);
262 if (Scaling
) Status
|= 1 << 4;
263 if (Reporting
) Status
|= 1 << 5;
264 if (Mode
== MOUSE_REMOTE_MODE
) Status
|= 1 << 6;
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
);
301 else PS2QueuePush(PS2Port
, MOUSE_ERROR
);
306 /* Enter Wrap Mode */
309 if (Mode
!= MOUSE_WRAP_MODE
)
311 /* Save the previous mode */
315 MouseResetCounters();
316 Mode
= MOUSE_WRAP_MODE
;
318 PS2QueuePush(PS2Port
, MOUSE_ACK
);
322 /* Enter Remote Mode */
325 MouseResetCounters();
326 Mode
= MOUSE_REMOTE_MODE
;
328 PS2QueuePush(PS2Port
, MOUSE_ACK
);
335 PS2QueuePush(PS2Port
, MOUSE_ACK
);
336 PS2QueuePush(PS2Port
, MouseId
);
340 /* Enable Reporting */
344 PS2QueuePush(PS2Port
, MOUSE_ACK
);
348 /* Disable Reporting */
352 PS2QueuePush(PS2Port
, MOUSE_ACK
);
359 /* Reset the configuration and counters */
361 MouseResetCounters();
368 PS2QueuePush(PS2Port
, MOUSE_ACK
);
369 MouseDispatchPacket(&LastPacket
);
380 /* Unknown command */
383 PS2QueuePush(PS2Port
, MOUSE_ERROR
);
388 static VOID FASTCALL
MouseStreamingCallback(ULONGLONG ElapsedTime
)
390 UNREFERENCED_PARAMETER(ElapsedTime
);
392 /* Check if we're not in streaming mode, not reporting, or there's nothing to report */
393 if (Mode
!= MOUSE_STREAMING_MODE
|| !Reporting
|| !EventsOccurred
) return;
395 MouseGetPacket(&LastPacket
);
396 MouseDispatchPacket(&LastPacket
);
398 EventsOccurred
= FALSE
;
401 /* PUBLIC FUNCTIONS ***********************************************************/
403 VOID
MouseGetDataFast(PCOORD CurrentPosition
, PBYTE CurrentButtonState
)
405 WaitForSingleObject(MouseMutex
, INFINITE
);
406 *CurrentPosition
= Position
;
407 *CurrentButtonState
= LOBYTE(ButtonState
);
408 ReleaseMutex(MouseMutex
);
411 VOID
MouseEventHandler(PMOUSE_EVENT_RECORD MouseEvent
)
413 COORD NewPosition
= MouseEvent
->dwMousePosition
;
414 BOOLEAN DoubleWidth
= FALSE
, DoubleHeight
= FALSE
;
416 if (!VgaGetDoubleVisionState(&DoubleWidth
, &DoubleHeight
))
423 /* Adjust for double vision */
424 if (DoubleWidth
) NewPosition
.X
/= 2;
425 if (DoubleHeight
) NewPosition
.Y
/= 2;
427 WaitForSingleObject(MouseMutex
, INFINITE
);
429 /* Update the counters */
430 HorzCounter
+= NewPosition
.X
- Position
.X
;
431 VertCounter
+= NewPosition
.Y
- Position
.Y
;
433 /* Update the position */
434 Position
= NewPosition
;
436 /* Update the button state */
437 ButtonState
= MouseEvent
->dwButtonState
;
439 if (MouseEvent
->dwEventFlags
& MOUSE_WHEELED
)
441 ScrollCounter
+= (SHORT
)HIWORD(MouseEvent
->dwButtonState
);
444 EventsOccurred
= TRUE
;
445 ReleaseMutex(MouseMutex
);
448 BOOLEAN
MouseInit(BYTE PS2Connector
)
450 /* Finish to plug the mouse to the specified PS/2 port */
451 PS2Port
= PS2Connector
;
452 PS2SetDeviceCmdProc(PS2Port
, NULL
, MouseCommand
);
454 MouseMutex
= CreateMutex(NULL
, FALSE
, NULL
);
455 if (MouseMutex
== NULL
) return FALSE
;
457 StreamTimer
= CreateHardwareTimer(HARDWARE_TIMER_ENABLED
, 100, MouseStreamingCallback
);