[NTVDM]
[reactos.git] / reactos / subsystems / ntvdm / hardware / mouse.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: mouse.c
5 * PURPOSE: Mouse emulation
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "mouse.h"
14 #include "ps2.h"
15 // #include "pic.h"
16
17 // HACK: For the PS/2 bypass and MOUSE.COM driver direct call
18 #include "dos/mouse32.h"
19
20 /* PRIVATE VARIABLES **********************************************************/
21
22 static MOUSE_MODE Mode, PreviousMode;
23 static COORD Position;
24 static ULONG WidthMm, HeightMm, WidthPixels, HeightPixels;
25 static ULONG SampleRate;
26 static ULONG Resolution;
27 static BOOLEAN Scaling;
28 static BOOLEAN Reporting;
29 static BYTE MouseId;
30 static ULONG ButtonState;
31 static SHORT HorzCounter;
32 static SHORT VertCounter;
33 static CHAR ScrollCounter;
34
35 static BYTE PS2Port = 1;
36
37 /* PRIVATE FUNCTIONS **********************************************************/
38
39 static VOID MouseResetConfig(VOID)
40 {
41 /* Reset the configuration to defaults */
42 SampleRate = 100;
43 Resolution = 4;
44 Scaling = FALSE;
45 Reporting = FALSE;
46 }
47
48 static VOID MouseResetCounters(VOID)
49 {
50 /* Reset all flags and counters */
51 ButtonState = HorzCounter = VertCounter = ScrollCounter = 0;
52 }
53
54 static VOID MouseReset(VOID)
55 {
56 /* Reset everything */
57 MouseResetConfig();
58 MouseResetCounters();
59
60 /* Enter streaming mode and the reset the mouse ID */
61 Mode = MOUSE_STREAMING_MODE;
62 MouseId = 0;
63
64 /* Send the Basic Assurance Test success code and the device ID */
65 PS2QueuePush(PS2Port, MOUSE_BAT_SUCCESS);
66 PS2QueuePush(PS2Port, MouseId);
67 }
68
69 #if 0
70 static VOID MouseGetPacket(PMOUSE_PACKET Packet)
71 {
72 /* Clear the packet */
73 RtlZeroMemory(Packet, sizeof(*Packet));
74
75 Packet->Flags |= MOUSE_ALWAYS_SET;
76
77 /* Check for horizontal overflows */
78 if ((HorzCounter < MOUSE_MIN) || (HorzCounter > MOUSE_MAX))
79 {
80 if (HorzCounter > MOUSE_MAX) HorzCounter = MOUSE_MAX;
81 if (HorzCounter < MOUSE_MIN) HorzCounter = MOUSE_MIN;
82
83 Packet->Flags |= MOUSE_X_OVERFLOW;
84 }
85
86 /* Check for vertical overflows */
87 if ((VertCounter < MOUSE_MIN) || (VertCounter > MOUSE_MAX))
88 {
89 if (VertCounter > MOUSE_MIN) VertCounter = MOUSE_MIN;
90 if (VertCounter < MOUSE_MIN) VertCounter = MOUSE_MIN;
91
92 Packet->Flags |= MOUSE_Y_OVERFLOW;
93 }
94
95 /* Set the sign flags */
96 if (HorzCounter & MOUSE_SIGN_BIT) Packet->Flags |= MOUSE_X_SIGN;
97 if (HorzCounter & MOUSE_SIGN_BIT) Packet->Flags |= MOUSE_Y_SIGN;
98
99 /* Set the button flags */
100 if (ButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) Packet->Flags |= MOUSE_LEFT_BUTTON;
101 if (ButtonState & FROM_LEFT_2ND_BUTTON_PRESSED) Packet->Flags |= MOUSE_MIDDLE_BUTTON;
102 if (ButtonState & RIGHTMOST_BUTTON_PRESSED) Packet->Flags |= MOUSE_RIGHT_BUTTON;
103
104 if (MouseId == 4)
105 {
106 if (ButtonState & FROM_LEFT_3RD_BUTTON_PRESSED) Packet->Extra |= MOUSE_4TH_BUTTON;
107 if (ButtonState & FROM_LEFT_4TH_BUTTON_PRESSED) Packet->Extra |= MOUSE_5TH_BUTTON;
108 }
109
110 if (MouseId >= 3)
111 {
112 /* Set the scroll counter */
113 Packet->Extra |= (UCHAR)ScrollCounter & 0x0F;
114 }
115
116 /* Store the counters in the packet */
117 Packet->HorzCounter = LOBYTE(HorzCounter);
118 Packet->VertCounter = LOBYTE(VertCounter);
119
120 /* Reset the counters */
121 MouseResetCounters();
122 }
123 #endif
124
125 /*static*/ VOID MouseUpdatePosition(PCOORD NewPosition)
126 {
127 /* Update the counters */
128 HorzCounter += ((NewPosition->X - Position.X) * WidthMm * Resolution) / WidthPixels;
129 VertCounter += ((NewPosition->Y - Position.Y) * HeightMm * Resolution) / HeightPixels;
130
131 /* Update the position */
132 Position = *NewPosition;
133 }
134
135 /*static*/ VOID MouseUpdateButtons(ULONG NewButtonState)
136 {
137 ButtonState = NewButtonState;
138 }
139
140 /*static*/ VOID MouseScroll(LONG Direction)
141 {
142 ScrollCounter += Direction;
143 }
144
145 /*static*/ COORD MouseGetPosition(VOID)
146 {
147 return Position;
148 }
149
150 static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
151 {
152 switch (Command)
153 {
154 /* Set 1:1 Scaling */
155 case 0xE6:
156 {
157 Scaling = FALSE;
158 PS2QueuePush(PS2Port, MOUSE_ACK);
159 break;
160 }
161
162 /* Set 2:1 Scaling */
163 case 0xE7:
164 {
165 Scaling = TRUE;
166 PS2QueuePush(PS2Port, MOUSE_ACK);
167 break;
168 }
169
170 /* Set Resolution */
171 case 0xE8:
172 {
173 // TODO: NOT IMPLEMENTED
174 UNIMPLEMENTED;
175 break;
176 }
177
178 /* Read Status */
179 case 0xE9:
180 {
181 // TODO: NOT IMPLEMENTED
182 UNIMPLEMENTED;
183 break;
184 }
185
186 /* Enter Streaming Mode */
187 case 0xEA:
188 {
189 MouseResetCounters();
190 Mode = MOUSE_STREAMING_MODE;
191
192 PS2QueuePush(PS2Port, MOUSE_ACK);
193 break;
194 }
195
196 /* Read Packet */
197 case 0xEB:
198 {
199 // TODO: NOT IMPLEMENTED
200 UNIMPLEMENTED;
201 break;
202 }
203
204 /* Return From Wrap Mode */
205 case 0xEC:
206 {
207 if (Mode == MOUSE_WRAP_MODE)
208 {
209 /* Restore the previous mode */
210 MouseResetCounters();
211 Mode = PreviousMode;
212 PS2QueuePush(PS2Port, MOUSE_ACK);
213 }
214 else PS2QueuePush(PS2Port, MOUSE_ERROR);
215
216 break;
217 }
218
219 /* Enter Wrap Mode */
220 case 0xEE:
221 {
222 if (Mode != MOUSE_WRAP_MODE)
223 {
224 /* Save the previous mode */
225 PreviousMode = Mode;
226 }
227
228 MouseResetCounters();
229 Mode = MOUSE_WRAP_MODE;
230
231 PS2QueuePush(PS2Port, MOUSE_ACK);
232 break;
233 }
234
235 /* Enter Remote Mode */
236 case 0xF0:
237 {
238 MouseResetCounters();
239 Mode = MOUSE_REMOTE_MODE;
240
241 PS2QueuePush(PS2Port, MOUSE_ACK);
242 break;
243 }
244
245 /* Get Mouse ID */
246 case 0xF2:
247 {
248 PS2QueuePush(PS2Port, MOUSE_ACK);
249 PS2QueuePush(PS2Port, MouseId);
250 break;
251 }
252
253 /* Set Sample Rate */
254 case 0xF3:
255 {
256 // TODO: NOT IMPLEMENTED
257 UNIMPLEMENTED;
258 break;
259 }
260
261 /* Enable Reporting */
262 case 0xF4:
263 {
264 Reporting = TRUE;
265 PS2QueuePush(PS2Port, MOUSE_ACK);
266 break;
267 }
268
269 /* Disable Reporting */
270 case 0xF5:
271 {
272 Reporting = FALSE;
273 PS2QueuePush(PS2Port, MOUSE_ACK);
274 break;
275 }
276
277 /* Set Defaults */
278 case 0xF6:
279 {
280 /* Reset the configuration and counters */
281 MouseResetConfig();
282 MouseResetCounters();
283 break;
284 }
285
286 /* Resend */
287 case 0xFE:
288 {
289 // TODO: NOT IMPLEMENTED
290 UNIMPLEMENTED;
291 break;
292 }
293
294 /* Reset */
295 case 0xFF:
296 {
297 MouseReset();
298 break;
299 }
300
301 /* Unknown command */
302 default:
303 {
304 PS2QueuePush(PS2Port, MOUSE_ERROR);
305 }
306 }
307 }
308
309 /* PUBLIC FUNCTIONS ***********************************************************/
310
311 VOID MouseEventHandler(PMOUSE_EVENT_RECORD MouseEvent)
312 {
313 // FIXME: Sync our private data
314
315 // HACK: Bypass PS/2 and instead, notify the MOUSE.COM driver directly
316 MouseBiosUpdatePosition(&MouseEvent->dwMousePosition);
317 MouseBiosUpdateButtons(LOWORD(MouseEvent->dwButtonState));
318
319 // PS2QueuePush(PS2Port, Data);
320 }
321
322 BOOLEAN MouseInit(BYTE PS2Connector)
323 {
324 HWND hWnd;
325 HDC hDC;
326
327 /* Get the console window */
328 hWnd = GetConsoleWindow();
329 if (hWnd == NULL) return FALSE;
330
331 /* Get the console window's device context */
332 hDC = GetWindowDC(hWnd);
333 if (hDC == NULL) return FALSE;
334
335 /* Get the parameters */
336 WidthMm = (ULONG)GetDeviceCaps(hDC, HORZSIZE);
337 HeightMm = (ULONG)GetDeviceCaps(hDC, VERTSIZE);
338 WidthPixels = (ULONG)GetDeviceCaps(hDC, HORZRES);
339 HeightPixels = (ULONG)GetDeviceCaps(hDC, VERTRES);
340
341 /* Release the device context */
342 ReleaseDC(hWnd, hDC);
343
344 /* Finish to plug the mouse to the specified PS/2 port */
345 PS2Port = PS2Connector;
346 PS2SetDeviceCmdProc(PS2Port, NULL, MouseCommand);
347
348 MouseReset();
349 return TRUE;
350 }