624cdbba2031bef26f959910179b912b94b861f4
[reactos.git] / reactos / subsystems / mvdm / ntvdm / hardware / mouse.c
1 /*
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>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "ntvdm.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 #include "mouse.h"
17 #include "ps2.h"
18
19 #include "clock.h"
20 #include "video/svga.h"
21
22 /* PRIVATE VARIABLES **********************************************************/
23
24 static const BYTE ScrollMagic[3] = { 200, 100, 80 };
25 static const BYTE ExtraButtonMagic[3] = { 200, 200, 80 };
26
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;
35 static BYTE MouseId;
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;
43
44 static UINT MouseCycles = 10;
45
46 static BYTE PS2Port = 1;
47
48 /* PUBLIC VARIABLES ***********************************************************/
49
50 /* PRIVATE FUNCTIONS **********************************************************/
51
52 static VOID MouseResetConfig(VOID)
53 {
54 /* Reset the configuration to defaults */
55 MouseCycles = 10;
56 Resolution = 4;
57 Scaling = FALSE;
58 Reporting = FALSE;
59 }
60
61 static VOID MouseResetCounters(VOID)
62 {
63 /* Reset all flags and counters */
64 HorzCounter = VertCounter = ScrollCounter = 0;
65 }
66
67 static VOID MouseReset(VOID)
68 {
69 /* Reset everything */
70 MouseResetConfig();
71 MouseResetCounters();
72
73 /* Enter streaming mode and the reset the mouse ID */
74 Mode = MOUSE_STREAMING_MODE;
75 MouseId = 0;
76 ScrollMagicCounter = ExtraButtonMagicCounter = 0;
77 }
78
79 static VOID MouseGetPacket(PMOUSE_PACKET Packet)
80 {
81 /* Clear the packet */
82 RtlZeroMemory(Packet, sizeof(*Packet));
83
84 /* Acquire the mutex */
85 WaitForSingleObject(MouseMutex, INFINITE);
86
87 Packet->Flags |= MOUSE_ALWAYS_SET;
88
89 /* Set the sign flags */
90 if (HorzCounter < 0)
91 {
92 Packet->Flags |= MOUSE_X_SIGN;
93 HorzCounter = -HorzCounter;
94 }
95
96 if (VertCounter < 0)
97 {
98 Packet->Flags |= MOUSE_Y_SIGN;
99 VertCounter = -VertCounter;
100 }
101
102 /* Check for horizontal overflows */
103 if (HorzCounter > MOUSE_MAX)
104 {
105 HorzCounter = MOUSE_MAX;
106 Packet->Flags |= MOUSE_X_OVERFLOW;
107 }
108
109 /* Check for vertical overflows */
110 if (VertCounter > MOUSE_MAX)
111 {
112 VertCounter = MOUSE_MAX;
113 Packet->Flags |= MOUSE_Y_OVERFLOW;
114 }
115
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;
120
121 if (MouseId == 4)
122 {
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;
125 }
126
127 if (MouseId >= 3)
128 {
129 /* Set the scroll counter */
130 Packet->Extra |= ((UCHAR)ScrollCounter & 0x0F);
131 }
132
133 /* Store the counters in the packet */
134 Packet->HorzCounter = LOBYTE(HorzCounter);
135 Packet->VertCounter = LOBYTE(VertCounter);
136
137 /* Reset the counters */
138 MouseResetCounters();
139
140 /* Release the mutex */
141 ReleaseMutex(MouseMutex);
142 }
143
144 static VOID MouseDispatchPacket(PMOUSE_PACKET Packet)
145 {
146 PS2QueuePush(PS2Port, Packet->Flags);
147 PS2QueuePush(PS2Port, Packet->HorzCounter);
148 PS2QueuePush(PS2Port, Packet->VertCounter);
149 if (MouseId >= 3) PS2QueuePush(PS2Port, Packet->Extra);
150 }
151
152 static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
153 {
154 /* Check if we were waiting for a data byte */
155 if (DataByteWait)
156 {
157 PS2QueuePush(PS2Port, MOUSE_ACK);
158
159 switch (DataByteWait)
160 {
161 /* Set Resolution */
162 case 0xE8:
163 {
164 Resolution = Command;
165 break;
166 }
167
168 /* Set Sample Rate */
169 case 0xF3:
170 {
171 /* Check for the scroll wheel enabling sequence */
172 if (MouseId == 0)
173 {
174 if (Command == ScrollMagic[ScrollMagicCounter])
175 {
176 ScrollMagicCounter++;
177 if (ScrollMagicCounter == 3) MouseId = 3;
178 }
179 else
180 {
181 ScrollMagicCounter = 0;
182 }
183 }
184
185 /* Check for the 5-button enabling sequence */
186 if (MouseId == 3)
187 {
188 if (Command == ExtraButtonMagic[ExtraButtonMagicCounter])
189 {
190 ExtraButtonMagicCounter++;
191 if (ExtraButtonMagicCounter == 3) MouseId = 4;
192 }
193 else
194 {
195 ExtraButtonMagicCounter = 0;
196 }
197 }
198
199 MouseCycles = 1000 / (UINT)Command;
200 break;
201 }
202
203 default:
204 {
205 /* Shouldn't happen */
206 ASSERT(FALSE);
207 }
208 }
209
210 DataByteWait = 0;
211 return;
212 }
213
214 /* Check if we're in wrap mode */
215 if (Mode == MOUSE_WRAP_MODE)
216 {
217 /*
218 * In this mode, we just echo whatever byte we get,
219 * except for the 0xEC and 0xFF commands.
220 */
221 if (Command != 0xEC && Command != 0xFF)
222 {
223 PS2QueuePush(PS2Port, Command);
224 return;
225 }
226 }
227
228 switch (Command)
229 {
230 /* Set 1:1 Scaling */
231 case 0xE6:
232 {
233 Scaling = FALSE;
234 PS2QueuePush(PS2Port, MOUSE_ACK);
235 break;
236 }
237
238 /* Set 2:1 Scaling */
239 case 0xE7:
240 {
241 Scaling = TRUE;
242 PS2QueuePush(PS2Port, MOUSE_ACK);
243 break;
244 }
245
246 /* Set Resolution */
247 case 0xE8:
248 /* Set Sample Rate */
249 case 0xF3:
250 {
251 DataByteWait = Command;
252 PS2QueuePush(PS2Port, MOUSE_ACK);
253 break;
254 }
255
256 /* Read Status */
257 case 0xE9:
258 {
259 BYTE Status = ButtonState & 7;
260
261 if (Scaling) Status |= 1 << 4;
262 if (Reporting) Status |= 1 << 5;
263 if (Mode == MOUSE_REMOTE_MODE) Status |= 1 << 6;
264
265 PS2QueuePush(PS2Port, MOUSE_ACK);
266 PS2QueuePush(PS2Port, Status);
267 PS2QueuePush(PS2Port, Resolution);
268 PS2QueuePush(PS2Port, (BYTE)(1000 / MouseCycles));
269 break;
270 }
271
272 /* Enter Streaming Mode */
273 case 0xEA:
274 {
275 MouseResetCounters();
276 Mode = MOUSE_STREAMING_MODE;
277
278 PS2QueuePush(PS2Port, MOUSE_ACK);
279 break;
280 }
281
282 /* Read Packet */
283 case 0xEB:
284 {
285 PS2QueuePush(PS2Port, MOUSE_ACK);
286 MouseGetPacket(&LastPacket);
287 MouseDispatchPacket(&LastPacket);
288 break;
289 }
290
291 /* Return from Wrap Mode */
292 case 0xEC:
293 {
294 if (Mode == MOUSE_WRAP_MODE)
295 {
296 /* Restore the previous mode */
297 MouseResetCounters();
298 Mode = PreviousMode;
299 PS2QueuePush(PS2Port, MOUSE_ACK);
300 }
301 else
302 {
303 PS2QueuePush(PS2Port, MOUSE_ERROR);
304 }
305
306 break;
307 }
308
309 /* Enter Wrap Mode */
310 case 0xEE:
311 {
312 if (Mode != MOUSE_WRAP_MODE)
313 {
314 /* Save the previous mode */
315 PreviousMode = Mode;
316 }
317
318 MouseResetCounters();
319 Mode = MOUSE_WRAP_MODE;
320
321 PS2QueuePush(PS2Port, MOUSE_ACK);
322 break;
323 }
324
325 /* Enter Remote Mode */
326 case 0xF0:
327 {
328 MouseResetCounters();
329 Mode = MOUSE_REMOTE_MODE;
330
331 PS2QueuePush(PS2Port, MOUSE_ACK);
332 break;
333 }
334
335 /* Get Mouse ID */
336 case 0xF2:
337 {
338 PS2QueuePush(PS2Port, MOUSE_ACK);
339 PS2QueuePush(PS2Port, MouseId);
340 break;
341 }
342
343 /* Enable Reporting */
344 case 0xF4:
345 {
346 Reporting = TRUE;
347 MouseResetCounters();
348 PS2QueuePush(PS2Port, MOUSE_ACK);
349 break;
350 }
351
352 /* Disable Reporting */
353 case 0xF5:
354 {
355 Reporting = FALSE;
356 MouseResetCounters();
357 PS2QueuePush(PS2Port, MOUSE_ACK);
358 break;
359 }
360
361 /* Set Defaults */
362 case 0xF6:
363 {
364 /* Reset the configuration and counters */
365 MouseResetConfig();
366 MouseResetCounters();
367 PS2QueuePush(PS2Port, MOUSE_ACK);
368 break;
369 }
370
371 /* Resend */
372 case 0xFE:
373 {
374 PS2QueuePush(PS2Port, MOUSE_ACK);
375 MouseDispatchPacket(&LastPacket);
376 break;
377 }
378
379 /* Reset */
380 case 0xFF:
381 {
382 /* Send ACKnowledge */
383 PS2QueuePush(PS2Port, MOUSE_ACK);
384
385 MouseReset();
386
387 /* Send the Basic Assurance Test success code and the device ID */
388 PS2QueuePush(PS2Port, MOUSE_BAT_SUCCESS);
389 PS2QueuePush(PS2Port, MouseId);
390 break;
391 }
392
393 /* Unknown command */
394 default:
395 {
396 PS2QueuePush(PS2Port, MOUSE_ERROR);
397 }
398 }
399 }
400
401 static VOID FASTCALL MouseStreamingCallback(ULONGLONG ElapsedTime)
402 {
403 UNREFERENCED_PARAMETER(ElapsedTime);
404
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;
407
408 MouseGetPacket(&LastPacket);
409 MouseDispatchPacket(&LastPacket);
410
411 EventsOccurred = FALSE;
412 }
413
414 /* PUBLIC FUNCTIONS ***********************************************************/
415
416 VOID MouseGetDataFast(PCOORD CurrentPosition, PBYTE CurrentButtonState)
417 {
418 WaitForSingleObject(MouseMutex, INFINITE);
419 *CurrentPosition = Position;
420 *CurrentButtonState = LOBYTE(ButtonState);
421 ReleaseMutex(MouseMutex);
422 }
423
424 VOID MouseEventHandler(PMOUSE_EVENT_RECORD MouseEvent)
425 {
426 COORD NewPosition = MouseEvent->dwMousePosition;
427 BOOLEAN DoubleWidth = FALSE, DoubleHeight = FALSE;
428
429 if (!VgaGetDoubleVisionState(&DoubleWidth, &DoubleHeight))
430 {
431 /* Text mode */
432 NewPosition.X *= 8;
433 NewPosition.Y *= 8;
434 }
435
436 /* Adjust for double vision */
437 if (DoubleWidth) NewPosition.X /= 2;
438 if (DoubleHeight) NewPosition.Y /= 2;
439
440 WaitForSingleObject(MouseMutex, INFINITE);
441
442 /* Update the counters */
443 HorzCounter += (NewPosition.X - Position.X) << DoubleWidth;
444 VertCounter += (NewPosition.Y - Position.Y) << DoubleHeight;
445
446 /* Update the position */
447 Position = NewPosition;
448
449 /* Update the button state */
450 ButtonState = MouseEvent->dwButtonState;
451
452 if (MouseEvent->dwEventFlags & MOUSE_WHEELED)
453 {
454 ScrollCounter += (SHORT)HIWORD(MouseEvent->dwButtonState);
455 }
456
457 EventsOccurred = TRUE;
458 ReleaseMutex(MouseMutex);
459 }
460
461 BOOLEAN MouseInit(BYTE PS2Connector)
462 {
463 /* Finish to plug the mouse to the specified PS/2 port */
464 PS2Port = PS2Connector;
465 PS2SetDeviceCmdProc(PS2Port, NULL, MouseCommand);
466
467 MouseMutex = CreateMutex(NULL, FALSE, NULL);
468 if (MouseMutex == NULL) return FALSE;
469
470 StreamTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED,
471 HZ_TO_NS(100),
472 MouseStreamingCallback);
473
474 MouseReset();
475 return TRUE;
476 }