2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: VDM 32-bit compatible MOUSE.COM driver
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
18 #include "bios/bios.h"
21 #include "dos32krnl/dos.h"
23 /* PRIVATE VARIABLES **********************************************************/
25 static BOOLEAN DriverEnabled
= TRUE
;
26 static MOUSE_DRIVER_STATE DriverState
;
28 /* PRIVATE FUNCTIONS **********************************************************/
30 static VOID
PaintMouseCursor(VOID
)
32 if (Bda
->VideoMode
<= 3)
35 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Bda
->VideoPage
* Bda
->VideoPageSize
);
37 EmulatorReadMemory(&EmulatorContext
,
39 + (DriverState
.Position
.Y
* Bda
->ScreenColumns
40 + DriverState
.Position
.X
) * sizeof(WORD
),
44 DriverState
.Character
= Character
;
45 Character
&= DriverState
.TextCursor
.ScreenMask
;
46 Character
^= DriverState
.TextCursor
.CursorMask
;
48 EmulatorWriteMemory(&EmulatorContext
,
50 + (DriverState
.Position
.Y
* Bda
->ScreenColumns
51 + DriverState
.Position
.X
) * sizeof(WORD
),
57 // TODO: NOT IMPLEMENTED
62 static VOID
EraseMouseCursor(VOID
)
64 if (Bda
->VideoMode
<= 3)
66 DWORD VideoAddress
= TO_LINEAR(TEXT_VIDEO_SEG
, Bda
->VideoPage
* Bda
->VideoPageSize
);
68 EmulatorWriteMemory(&EmulatorContext
,
70 + (DriverState
.Position
.Y
* Bda
->ScreenColumns
71 + DriverState
.Position
.X
) * sizeof(WORD
),
72 (LPVOID
)&DriverState
.Character
,
77 // TODO: NOT IMPLEMENTED
82 static VOID
CallMouseUserHandlers(USHORT CallMask
)
86 USHORT AX
, BX
, CX
, DX
, SI
, DI
;
89 if ((DriverState
.Handler0
.CallMask
& CallMask
) != 0 &&
90 DriverState
.Handler0
.Callback
!= (ULONG
)NULL
)
93 * Set the parameters for the callback.
94 * NOTE: In text modes, the row and column will be reported
95 * as a multiple of the cell size, typically 8x8 pixels.
106 setBX(DriverState
.ButtonState
);
107 setCX(DriverState
.Position
.X
);
108 setDX(DriverState
.Position
.Y
);
109 setSI(DriverState
.MickeysPerCellHoriz
);
110 setDI(DriverState
.MickeysPerCellVert
);
112 DPRINT1("Calling Handler0 %04X:%04X with CallMask 0x%04X\n",
113 HIWORD(DriverState
.Handler0
.Callback
),
114 LOWORD(DriverState
.Handler0
.Callback
),
117 /* Call the callback */
118 RunCallback16(&DosContext
, DriverState
.Handler0
.Callback
);
128 for (i
= 0; i
< sizeof(DriverState
.Handlers
)/sizeof(DriverState
.Handlers
[0]); ++i
)
130 /* Call the suitable handlers */
131 if ((DriverState
.Handlers
[i
].CallMask
& CallMask
) != 0 &&
132 DriverState
.Handlers
[i
].Callback
!= (ULONG
)NULL
)
135 * Set the parameters for the callback.
136 * NOTE: In text modes, the row and column will be reported
137 * as a multiple of the cell size, typically 8x8 pixels.
148 setBX(DriverState
.ButtonState
);
149 setCX(DriverState
.Position
.X
);
150 setDX(DriverState
.Position
.Y
);
151 setSI(DriverState
.MickeysPerCellHoriz
);
152 setDI(DriverState
.MickeysPerCellVert
);
154 DPRINT1("Calling Handler[%d] %04X:%04X with CallMask 0x%04X\n",
156 HIWORD(DriverState
.Handlers
[i
].Callback
),
157 LOWORD(DriverState
.Handlers
[i
].Callback
),
160 /* Call the callback */
161 RunCallback16(&DosContext
, DriverState
.Handlers
[i
].Callback
);
174 static VOID WINAPI
BiosMouseService(LPWORD Stack
)
183 DriverEnabled
= TRUE
;
184 DriverState
.ShowCount
= 0;
185 DriverState
.ButtonState
= 0;
187 /* Set the default text cursor */
188 DriverState
.TextCursor
.ScreenMask
= 0xFFFF; /* Display everything */
189 DriverState
.TextCursor
.CursorMask
= 0xFF00; /* ... but with inverted attributes */
191 /* Set the default graphics cursor */
192 DriverState
.GraphicsCursor
.HotSpot
.X
= 3;
193 DriverState
.GraphicsCursor
.HotSpot
.Y
= 1;
195 DriverState
.GraphicsCursor
.ScreenMask
[0] = 0xC3FF; // 1100001111111111
196 DriverState
.GraphicsCursor
.ScreenMask
[1] = 0xC0FF; // 1100000011111111
197 DriverState
.GraphicsCursor
.ScreenMask
[2] = 0xC07F; // 1100000001111111
198 DriverState
.GraphicsCursor
.ScreenMask
[3] = 0xC01F; // 1100000000011111
199 DriverState
.GraphicsCursor
.ScreenMask
[4] = 0xC00F; // 1100000000001111
200 DriverState
.GraphicsCursor
.ScreenMask
[5] = 0xC007; // 1100000000000111
201 DriverState
.GraphicsCursor
.ScreenMask
[6] = 0xC003; // 1100000000000011
202 DriverState
.GraphicsCursor
.ScreenMask
[7] = 0xC007; // 1100000000000111
203 DriverState
.GraphicsCursor
.ScreenMask
[8] = 0xC01F; // 1100000000011111
204 DriverState
.GraphicsCursor
.ScreenMask
[9] = 0xC01F; // 1100000000011111
205 DriverState
.GraphicsCursor
.ScreenMask
[10] = 0xC00F; // 1100000000001111
206 DriverState
.GraphicsCursor
.ScreenMask
[11] = 0xC60F; // 1100011000001111
207 DriverState
.GraphicsCursor
.ScreenMask
[12] = 0xFF07; // 1111111100000111
208 DriverState
.GraphicsCursor
.ScreenMask
[13] = 0xFF07; // 1111111100000111
209 DriverState
.GraphicsCursor
.ScreenMask
[14] = 0xFF87; // 1111111110000111
210 DriverState
.GraphicsCursor
.ScreenMask
[15] = 0xFFCF; // 1111111111001111
212 DriverState
.GraphicsCursor
.CursorMask
[0] = 0x0000; // 0000000000000000
213 DriverState
.GraphicsCursor
.CursorMask
[1] = 0x1C00; // 0001110000000000
214 DriverState
.GraphicsCursor
.CursorMask
[2] = 0x1F00; // 0001111100000000
215 DriverState
.GraphicsCursor
.CursorMask
[3] = 0x1F80; // 0001111110000000
216 DriverState
.GraphicsCursor
.CursorMask
[4] = 0x1FE0; // 0001111111100000
217 DriverState
.GraphicsCursor
.CursorMask
[5] = 0x1FF0; // 0001111111110000
218 DriverState
.GraphicsCursor
.CursorMask
[6] = 0x1FF8; // 0001111111111000
219 DriverState
.GraphicsCursor
.CursorMask
[7] = 0x1FE0; // 0001111111100000
220 DriverState
.GraphicsCursor
.CursorMask
[8] = 0x1FC0; // 0001111111000000
221 DriverState
.GraphicsCursor
.CursorMask
[9] = 0x1FC0; // 0001111111000000
222 DriverState
.GraphicsCursor
.CursorMask
[10] = 0x19E0; // 0001100111100000
223 DriverState
.GraphicsCursor
.CursorMask
[11] = 0x00E0; // 0000000011100000
224 DriverState
.GraphicsCursor
.CursorMask
[12] = 0x0070; // 0000000001110000
225 DriverState
.GraphicsCursor
.CursorMask
[13] = 0x0070; // 0000000001110000
226 DriverState
.GraphicsCursor
.CursorMask
[14] = 0x0030; // 0000000000110000
227 DriverState
.GraphicsCursor
.CursorMask
[15] = 0x0000; // 0000000000000000
229 /* Initialize the counters */
230 DriverState
.HorizCount
= DriverState
.VertCount
= 0;
232 for (i
= 0; i
< NUM_MOUSE_BUTTONS
; i
++)
234 DriverState
.PressCount
[i
] = DriverState
.ReleaseCount
[i
] = 0;
237 /* Initialize the resolution */
238 DriverState
.MickeysPerCellHoriz
= 8;
239 DriverState
.MickeysPerCellVert
= 16;
241 /* Return mouse information */
242 setAX(0xFFFF); // Hardware & driver installed
243 setBX(NUM_MOUSE_BUTTONS
);
248 /* Show Mouse Cursor */
251 DriverState
.ShowCount
++;
252 if (DriverState
.ShowCount
> 0) PaintMouseCursor();
257 /* Hide Mouse Cursor */
260 DriverState
.ShowCount
--;
261 if (DriverState
.ShowCount
<= 0) EraseMouseCursor();
266 /* Return Position And Button Status */
269 setBX(DriverState
.ButtonState
);
270 setCX(DriverState
.Position
.X
);
271 setDX(DriverState
.Position
.Y
);
275 /* Position Mouse Cursor */
283 ClientToScreen(GetConsoleWindow(), &Point
);
284 SetCursorPos(Point
.x
, Point
.y
);
289 /* Return Button Press Data */
292 WORD Button
= getBX();
294 setAX(DriverState
.ButtonState
);
295 setBX(DriverState
.PressCount
[Button
]);
296 setCX(DriverState
.LastPress
[Button
].X
);
297 setDX(DriverState
.LastPress
[Button
].Y
);
299 /* Reset the counter */
300 DriverState
.PressCount
[Button
] = 0;
305 /* Return Button Release Data */
308 WORD Button
= getBX();
310 setAX(DriverState
.ButtonState
);
311 setBX(DriverState
.ReleaseCount
[Button
]);
312 setCX(DriverState
.LastRelease
[Button
].X
);
313 setDX(DriverState
.LastRelease
[Button
].Y
);
315 /* Reset the counter */
316 DriverState
.ReleaseCount
[Button
] = 0;
322 /* Define Graphics Cursor */
325 PWORD MaskBitmap
= (PWORD
)SEG_OFF_TO_PTR(getES(), getDX());
327 DriverState
.GraphicsCursor
.HotSpot
.X
= getBX();
328 DriverState
.GraphicsCursor
.HotSpot
.Y
= getCX();
330 RtlCopyMemory(DriverState
.GraphicsCursor
.ScreenMask
,
332 sizeof(DriverState
.GraphicsCursor
.ScreenMask
));
334 RtlCopyMemory(DriverState
.GraphicsCursor
.CursorMask
,
336 sizeof(DriverState
.GraphicsCursor
.CursorMask
));
341 /* Define Text Cursor */
348 /* Define software cursor */
349 DriverState
.TextCursor
.ScreenMask
= getCX();
350 DriverState
.TextCursor
.CursorMask
= getDX();
352 else if (BX
== 0x0001)
354 /* Define hardware cursor */
355 DPRINT1("Defining hardware cursor is unimplemented\n");
357 // CX == start scan line
358 // DX == end scan line
362 DPRINT1("Invalid BX value 0x%04X\n", BX
);
368 /* Read Motion Counters */
371 setCX(DriverState
.HorizCount
);
372 setDX(DriverState
.VertCount
);
374 /* Reset the counters */
375 DriverState
.HorizCount
= DriverState
.VertCount
= 0;
380 /* Define Interrupt Subroutine Parameters, compatible MS MOUSE v1.0+ */
383 DriverState
.Handler0
.CallMask
= getCX();
384 DriverState
.Handler0
.Callback
= MAKELONG(getDX(), getES()); // Far pointer to the callback
385 DPRINT1("Define callback 0x%04X, %04X:%04X\n",
386 DriverState
.Handler0
.CallMask
,
387 HIWORD(DriverState
.Handler0
.Callback
),
388 LOWORD(DriverState
.Handler0
.Callback
));
392 /* Define Mickey/Pixel Ratio */
395 DriverState
.MickeysPerCellHoriz
= getCX();
396 DriverState
.MickeysPerCellVert
= getDX();
400 /* Exchange Interrupt Subroutines, compatible MS MOUSE v3.0+ (see function 0x0C) */
403 USHORT OldCallMask
= DriverState
.Handler0
.CallMask
;
404 ULONG OldCallback
= DriverState
.Handler0
.Callback
;
406 DriverState
.Handler0
.CallMask
= getCX();
407 DriverState
.Handler0
.Callback
= MAKELONG(getDX(), getES()); // Far pointer to the callback
409 /* Return old callmask in CX and callback vector in ES:DX */
411 setES(HIWORD(OldCallback
));
412 setDX(LOWORD(OldCallback
));
417 /* Return Driver Storage Requirements */
420 setBX(sizeof(MOUSE_DRIVER_STATE
));
424 /* Save Driver State */
427 *((PMOUSE_DRIVER_STATE
)SEG_OFF_TO_PTR(getES(), getDX())) = DriverState
;
431 /* Restore Driver State */
434 DriverState
= *((PMOUSE_DRIVER_STATE
)SEG_OFF_TO_PTR(getES(), getDX()));
438 /* Set Alternate Mouse User Handler, compatible MS MOUSE v6.0+ */
442 * Up to three handlers can be defined by separate calls to this
443 * function, each with a different combination of shift states in
444 * the call mask; calling this function again with a call mask of
445 * 0000h undefines the specified handler (official documentation);
446 * specifying the same call mask and an address of 0000h:0000h
447 * undefines the handler (real life).
448 * See Ralf Brown: http://www.ctyme.com/intr/rb-5981.htm
449 * for more information.
453 USHORT CallMask
= getCX();
454 ULONG Callback
= MAKELONG(getDX(), getES()); // Far pointer to the callback
455 BOOLEAN Success
= FALSE
;
457 DPRINT1("Define v6.0+ callback 0x%04X, %04X:%04X\n",
458 CallMask
, HIWORD(Callback
), LOWORD(Callback
));
460 if (CallMask
== 0x0000)
463 * Find the handler entry corresponding to the given
464 * callback and undefine it.
466 for (i
= 0; i
< sizeof(DriverState
.Handlers
)/sizeof(DriverState
.Handlers
[0]); ++i
)
468 if (DriverState
.Handlers
[i
].Callback
== Callback
)
470 /* Found it, undefine the handler */
471 DriverState
.Handlers
[i
].CallMask
= 0x0000;
472 DriverState
.Handlers
[i
].Callback
= (ULONG
)NULL
;
478 else if (Callback
== (ULONG
)NULL
)
481 * Find the handler entry corresponding to the given
482 * callmask and undefine it.
484 for (i
= 0; i
< sizeof(DriverState
.Handlers
)/sizeof(DriverState
.Handlers
[0]); ++i
)
486 if (DriverState
.Handlers
[i
].CallMask
== CallMask
)
488 /* Found it, undefine the handler */
489 DriverState
.Handlers
[i
].CallMask
= 0x0000;
490 DriverState
.Handlers
[i
].Callback
= (ULONG
)NULL
;
499 * Try to find a handler entry corresponding to the given
500 * callmask to redefine it, otherwise find an empty handler
501 * entry and set the new handler in there.
504 USHORT EmptyHandler
= 0xFFFF; // Invalid handler
506 for (i
= 0; i
< sizeof(DriverState
.Handlers
)/sizeof(DriverState
.Handlers
[0]); ++i
)
508 /* Find the first empty handler */
509 if (EmptyHandler
== 0xFFFF &&
510 DriverState
.Handlers
[i
].CallMask
== 0x0000 &&
511 DriverState
.Handlers
[i
].Callback
== (ULONG
)NULL
)
516 if (DriverState
.Handlers
[i
].CallMask
== CallMask
)
518 /* Found it, redefine the handler */
519 DriverState
.Handlers
[i
].CallMask
= CallMask
;
520 DriverState
.Handlers
[i
].Callback
= Callback
;
527 * If we haven't found anything and we found
528 * an empty handler, set it.
530 if (!Success
&& EmptyHandler
!= 0xFFFF
531 /* && EmptyHandler < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]) */)
533 DriverState
.Handlers
[EmptyHandler
].CallMask
= CallMask
;
534 DriverState
.Handlers
[EmptyHandler
].Callback
= Callback
;
539 /* If we failed, set error code */
540 if (!Success
) setAX(0xFFFF);
545 /* Return User Alternate Interrupt Vector, compatible MS MOUSE v6.0+ */
549 USHORT CallMask
= getCX();
551 BOOLEAN Success
= FALSE
;
554 * Find the handler entry corresponding to the given callmask.
556 for (i
= 0; i
< sizeof(DriverState
.Handlers
)/sizeof(DriverState
.Handlers
[0]); ++i
)
558 if (DriverState
.Handlers
[i
].CallMask
== CallMask
)
561 Callback
= DriverState
.Handlers
[i
].Callback
;
569 /* Return the callback vector in BX:DX */
570 setBX(HIWORD(Callback
));
571 setDX(LOWORD(Callback
));
575 /* We failed, set error code */
582 /* Disable Mouse Driver */
588 DriverEnabled
= FALSE
;
592 /* Enable Mouse Driver */
595 DriverEnabled
= TRUE
;
601 DPRINT1("BIOS Function INT 33h, AX = 0x%04X NOT IMPLEMENTED\n", getAX());
606 /* PUBLIC FUNCTIONS ***********************************************************/
608 VOID
MouseBiosUpdatePosition(PCOORD NewPosition
)
610 SHORT DeltaX
= NewPosition
->X
- DriverState
.Position
.X
;
611 SHORT DeltaY
= NewPosition
->Y
- DriverState
.Position
.Y
;
613 if (!DriverEnabled
) return;
615 DriverState
.HorizCount
+= (DeltaX
* (SHORT
)DriverState
.MickeysPerCellHoriz
) / 8;
616 DriverState
.VertCount
+= (DeltaY
* (SHORT
)DriverState
.MickeysPerCellVert
) / 8;
618 if (DriverState
.ShowCount
> 0)
621 DriverState
.Position
= *NewPosition
;
625 /* Call the mouse handlers */
626 CallMouseUserHandlers(0x0001); // We use MS MOUSE v1.0+ format
629 VOID
MouseBiosUpdateButtons(WORD ButtonState
)
632 USHORT CallMask
= 0x0000; // We use MS MOUSE v1.0+ format
634 if (!DriverEnabled
) return;
636 for (i
= 0; i
< NUM_MOUSE_BUTTONS
; i
++)
638 BOOLEAN OldState
= (DriverState
.ButtonState
>> i
) & 1;
639 BOOLEAN NewState
= (ButtonState
>> i
) & 1;
641 if (NewState
> OldState
)
644 DriverState
.PressCount
[i
]++;
645 DriverState
.LastPress
[i
] = DriverState
.Position
;
647 CallMask
|= (1 << (2 * i
+ 1));
649 else if (NewState
< OldState
)
652 DriverState
.ReleaseCount
[i
]++;
653 DriverState
.LastRelease
[i
] = DriverState
.Position
;
655 CallMask
|= (1 << (2 * i
+ 2));
659 DriverState
.ButtonState
= ButtonState
;
661 /* Call the mouse handlers */
662 CallMouseUserHandlers(CallMask
);
665 BOOLEAN
DosMouseInitialize(VOID
)
667 /* Clear the state */
668 RtlZeroMemory(&DriverState
, sizeof(DriverState
));
670 /* Initialize the interrupt handler */
671 RegisterDosInt32(BIOS_MOUSE_INTERRUPT
, BiosMouseService
);
676 VOID
DosMouseCleanup(VOID
)
678 if (DriverState
.ShowCount
> 0) EraseMouseCursor();