aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--doc/absapi.txt22
-rw-r--r--doc/int33.lst732
-rw-r--r--doc/wheelapi.txt109
-rw-r--r--doc/wheelapi2.txt69
-rw-r--r--int15ps2.h83
-rw-r--r--int33.h19
-rw-r--r--mousetsr.c295
-rw-r--r--mousetsr.h44
-rw-r--r--mousew16.c129
-rw-r--r--mousmain.c25
-rw-r--r--utils.h7
-rw-r--r--version.h2
13 files changed, 1356 insertions, 185 deletions
diff --git a/README.md b/README.md
index e51ee47..97001c4 100644
--- a/README.md
+++ b/README.md
@@ -603,8 +603,8 @@ this shouldn't be a problem either.
## Future work
-* The VirtualBox BIOS can crash on warm-boot (e.g. Ctrl+Alt+Del) if the mouse
- was in the middle of sending a packet. A VM reboot fixes it.
+* BIOS can crash on warm-boot (e.g. Ctrl+Alt+Del) if the mouse
+ was in the middle of sending a packet. A hardware reboot fixes it.
We probably need to hook Ctrl+Alt+Del and turn off the mouse.
* DOS has functions to start (FindFirst) and continue (FindNext) a directory
@@ -626,4 +626,3 @@ this shouldn't be a problem either.
* Would it be possible to use a hardware rendered mouse pointer in Windows 3.x,
without having to replace the video driver?
- This would also help other emulators.
diff --git a/doc/absapi.txt b/doc/absapi.txt
new file mode 100644
index 0000000..ffb29f8
--- /dev/null
+++ b/doc/absapi.txt
@@ -0,0 +1,22 @@
+This document describes an extension to the int33 API to allow users to detect
+if mouse interrupts come from a relative-input device (like a standard mouse)
+or an absolute-input device (like a tablet, or an emulator like VirtualBox).
+
+---------------------------------------------------------------------
+Changes in the original INT 33h functions:
+
+INT 33/000C - Define User Interrupt Routine
+INT 33/0014 - Exchange User Interrupt Routines
+Bitfields for mouse call mask:
+Bit(s) Description
+ 0-6 same as Table 03171 (as in int33.lst)
+ 7 (for vertical wheel movement -- seee wheelapi.txt)
+ 8 absolute mouse event
+ 9-15 unused
+Notes: when the user interrupt routine is called, bit 8 of CX indicates that
+ the x, y coordinates passed in CX, DX come from an absolute pointing
+ device (and therefore that the mickey counts in SI, DI may be zero or
+ virtualized).
+ bit 8 will not be set unless the user also sets bits 8 in the event mask
+ passed to int33/000c or int33/0014. However, setting or clearing bit 8
+ in the event mask shall have no other effect.
diff --git a/doc/int33.lst b/doc/int33.lst
new file mode 100644
index 0000000..dcf9ad0
--- /dev/null
+++ b/doc/int33.lst
@@ -0,0 +1,732 @@
+--------M-330000-----------------------------
+INT 33 - MS MOUSE - RESET DRIVER AND READ STATUS
+ AX = 0000h
+Return: AX = status
+ 0000h hardware/driver not installed
+ FFFFh hardware/driver installed
+ BX = number of buttons
+ 0000h other than two
+ 0002h two buttons (many drivers)
+ 0003h Mouse Systems/Logitech three-button mouse
+ FFFFh two buttons
+Notes: since INT 33 might be uninitialized on old machines, the caller
+ should first check that INT 33 is neither 0000h:0000h nor points at
+ an IRET instruction (BYTE CFh) before calling this API
+ to use mouse on a Hercules-compatible monographics card in graphics
+ mode, you must first set 0040h:0049h to 6 for page 0 or 5 for page 1,
+ and then call this function. Logitech drivers v5.01 and v6.00
+ reportedly do not correctly use Hercules graphics in dual-monitor
+ systems, while version 4.10 does.
+ the Logitech mouse driver contains the signature string "LOGITECH"
+ three bytes past the interrupt handler; many of the Logitech mouse
+ utilities check for this signature.
+ Logitech MouseWare v6.30 reportedly does not support CGA video modes
+ if no CGA is present when it is started and the video board is
+ later switched into CGA emulation
+SeeAlso: AX=0011h,AX=0021h,AX=002Fh,INT 62/AX=007Ah,INT 74
+--------M-330001-----------------------------
+INT 33 - MS MOUSE v1.0+ - SHOW MOUSE CURSOR
+ AX = 0001h
+SeeAlso: AX=0002h,INT 16/AX=FFFEh,INT 62/AX=007Bh,INT 6F/AH=06h"F_TRACK_ON"
+--------M-330002-----------------------------
+INT 33 - MS MOUSE v1.0+ - HIDE MOUSE CURSOR
+ AX = 0002h
+Note: multiple calls to hide the cursor will require multiple calls to
+ function 01h to unhide it.
+SeeAlso: AX=0001h,AX=0010h,INT 16/AX=FFFFh,INT 62/AX=007Bh
+SeeAlso: INT 6F/AH=08h"F_TRACK_OFF"
+--------M-330003-----------------------------
+INT 33 - MS MOUSE v1.0+ - RETURN POSITION AND BUTTON STATUS
+ AX = 0003h
+Return: BX = button status (see #03168)
+ CX = column
+ DX = row
+Note: in text modes, all coordinates are specified as multiples of the cell
+ size, typically 8x8 pixels
+SeeAlso: AX=0004h,AX=000Bh,INT 2F/AX=D000h"ZWmous"
+
+Bitfields for mouse button status:
+Bit(s) Description (Table 03168)
+ 0 left button pressed if 1
+ 1 right button pressed if 1
+ 2 middle button pressed if 1 (Mouse Systems/Logitech/Genius)
+--------M-330004-----------------------------
+INT 33 - MS MOUSE v1.0+ - POSITION MOUSE CURSOR
+ AX = 0004h
+ CX = column
+ DX = row
+Note: the row and column are truncated to the next lower multiple of the cell
+ size (typically 8x8 in text modes); however, some versions of the
+ Microsoft documentation incorrectly state that the coordinates are
+ rounded
+SeeAlso: AX=0003h,INT 62/AX=0081h,INT 6F/AH=10h"F_PUT_SPRITE"
+--------M-330005-----------------------------
+INT 33 - MS MOUSE v1.0+ - RETURN BUTTON PRESS DATA
+ AX = 0005h
+ BX = button number (see #03169)
+Return: AX = button states (see #03168)
+ BX = number of times specified button has been pressed since last call
+ CX = column at time specified button was last pressed
+ DX = row at time specified button was last pressed
+Note: at least for the Genius mouse driver, the number of button presses
+ returned is limited to 7FFFh
+SeeAlso: AX=0006h,INT 62/AX=007Ch
+
+(Table 03169)
+Values for mouse button number:
+ 0000h left
+ 0001h right
+ 0002h middle (Mouse Systems/Logitech/Genius mouse)
+--------M-330006-----------------------------
+INT 33 - MS MOUSE v1.0+ - RETURN BUTTON RELEASE DATA
+ AX = 0006h
+ BX = button number (see #03169)
+Return: AX = button states (see #03168)
+ BX = number of times specified button has been released since last call
+ CX = column at time specified button was last released
+ DX = row at time specified button was last released
+Note: at least for the Genius mouse driver, the number of button releases
+ returned is limited to 7FFFh
+SeeAlso: AX=0005h,INT 62/AX=007Ch
+--------M-330007-----------------------------
+INT 33 - MS MOUSE v1.0+ - DEFINE HORIZONTAL CURSOR RANGE
+ AX = 0007h
+ CX = minimum column
+ DX = maximum column
+Note: in text modes, the minimum and maximum columns are truncated to the
+ next lower multiple of the cell size, typically 8x8 pixels
+SeeAlso: AX=0008h,AX=0010h,AX=0031h,INT 62/AX=0080h
+SeeAlso: INT 6F/AH=0Ch"F_SET_LIMITS_X"
+--------M-330008-----------------------------
+INT 33 - MS MOUSE v1.0+ - DEFINE VERTICAL CURSOR RANGE
+ AX = 0008h
+ CX = minimum row
+ DX = maximum row
+Note: in text modes, the minimum and maximum rows are truncated to the
+ next lower multiple of the cell size, typically 8x8 pixels
+SeeAlso: AX=0007h,AX=0010h,AX=0031h,INT 62/AX=0080h
+SeeAlso: INT 6F/AH=0Eh"F_SET_LIMITS_Y"
+--------M-330009-----------------------------
+INT 33 - MS MOUSE v3.0+ - DEFINE GRAPHICS CURSOR
+ AX = 0009h
+ BX = column of cursor hot spot in bitmap (-16 to 16)
+ CX = row of cursor hot spot (-16 to 16)
+ ES:DX -> mask bitmap (see #03170)
+Notes: in graphics modes, the screen contents around the current mouse cursor
+ position are ANDed with the screen mask and then XORed with the
+ cursor mask
+ the Microsoft mouse driver v7.04 and v8.20 uses only BL and CL, so the
+ hot spot row/column should be limited to -128..127
+ Microsoft KnowledgeBase article Q19850 states that the high bit is
+ right-most, but that statement is contradicted by all other available
+ documentation
+SeeAlso: AX=000Ah,AX=0012h,AX=002Ah,INT 62/AX=007Fh,INT 6F/AH=0Ah"F_DEF_MASKS"
+
+Format of mouse mask bitmap:
+Offset Size Description (Table 03170)
+ 00h 16 WORDs screen mask
+ 10h 16 WORDs cursor mask
+Note: each word defines the sixteen pixels of a row, low bit rightmost
+--------M-33000A-----------------------------
+INT 33 - MS MOUSE v3.0+ - DEFINE TEXT CURSOR
+ AX = 000Ah
+ BX = hardware/software text cursor
+ 0000h software
+ CX = screen mask
+ DX = cursor mask
+ 0001h hardware
+ CX = start scan line
+ DX = end scan line
+Note: when the software cursor is selected, the character/attribute data at
+ the current screen position is ANDed with the screen mask and then
+ XORed with the cursor mask
+SeeAlso: AX=0009h,INT 62/AX=007Eh
+--------M-33000B-----------------------------
+INT 33 - MS MOUSE v1.0+ - READ MOTION COUNTERS
+ AX = 000Bh
+Return: CX = number of mickeys mouse moved horizontally since last call
+ DX = number of mickeys mouse moved vertically
+Notes: a mickey is the smallest increment the mouse can sense
+ positive values indicate down/right
+SeeAlso: AX=0003h,AX=001Bh,AX=0027h
+--------M-33000C-----------------------------
+INT 33 - MS MOUSE v1.0+ - DEFINE INTERRUPT SUBROUTINE PARAMETERS
+ AX = 000Ch
+ CX = call mask (see #03171)
+ ES:DX -> FAR routine (see #03172)
+SeeAlso: AX=0018h
+
+Bitfields for mouse call mask:
+Bit(s) Description (Table 03171)
+ 0 call if mouse moves
+ 1 call if left button pressed
+ 2 call if left button released
+ 3 call if right button pressed
+ 4 call if right button released
+ 5 call if middle button pressed (Mouse Systems/Logitech/Genius mouse)
+ 6 call if middle button released (Mouse Systems/Logitech/Genius mouse)
+ 7-15 unused
+Note: some versions of the Microsoft documentation incorrectly state that CX
+ bit 0 means call if mouse cursor moves
+
+(Table 03172)
+Values interrupt routine is called with:
+ AX = condition mask (same bit assignments as call mask)
+ BX = button state
+ CX = cursor column
+ DX = cursor row
+ SI = horizontal mickey count
+ DI = vertical mickey count
+Notes: some versions of the Microsoft documentation erroneously swap the
+ meanings of SI and DI
+ in text modes, the row and column will be reported as a multiple of
+ the character cell size, typically 8x8 pixels
+--------M-33000D-----------------------------
+INT 33 - MS MOUSE v1.0+ - LIGHT PEN EMULATION ON
+ AX = 000Dh
+SeeAlso: AX=000Eh,INT 10/AH=04h
+--------M-33000E-----------------------------
+INT 33 - MS MOUSE v1.0+ - LIGHT PEN EMULATION OFF
+ AX = 000Eh
+SeeAlso: AX=000Dh
+--------M-33000F-----------------------------
+INT 33 - MS MOUSE v1.0+ - DEFINE MICKEY/PIXEL RATIO
+ AX = 000Fh
+ CX = number of mickeys per 8 pixels horizontally (default 8)
+ DX = number of mickeys per 8 pixels vertically (default 16)
+SeeAlso: AX=0013h,AX=001Ah,INT 62/AX=0082h
+--------M-330010-----------------------------
+INT 33 - MS MOUSE v1.0+ - DEFINE SCREEN REGION FOR UPDATING
+ AX = 0010h
+ CX,DX = X,Y coordinates of upper left corner
+ SI,DI = X,Y coordinates of lower right corner
+Note: mouse cursor is hidden in the specified region, and needs to be
+ explicitly turned on again
+SeeAlso: AX=0001h,AX=0002h,AX=0007h,AX=0010h"Genius MOUSE",AX=0031h
+--------M-330012-----------------------------
+INT 33 - MS MOUSE - SET LARGE GRAPHICS CURSOR BLOCK
+ AX = 0012h
+ BH = cursor width in words
+ CH = rows in cursor
+ BL = horizontal hot spot (-16 to 16)
+ CL = vertical hot spot (-16 to 16)
+ ES:DX -> bit map of screen and cursor maps
+Return: AX = FFFFh if successful
+SeeAlso: AX=0009h,AX=002Ah,AX=0035h
+--------M-330013-----------------------------
+INT 33 - MS MOUSE v5.0+ - DEFINE DOUBLE-SPEED THRESHOLD
+ AX = 0013h
+ DX = threshold speed in mickeys/second, 0000h = default of 64/second
+Note: if speed exceeds threshold, the cursor's on-screen motion is doubled
+SeeAlso: AX=000Fh,AX=001Bh,AX=002Ch
+--------M-330014-----------------------------
+INT 33 - MS MOUSE v3.0+ - EXCHANGE INTERRUPT SUBROUTINES
+ AX = 0014h
+ CX = call mask (see #03171)
+ ES:DX -> FAR routine
+Return: CX = call mask of previous interrupt routine
+ ES:DX = FAR address of previous interrupt routine
+SeeAlso: AX=000Ch,AX=0018h
+--------M-330015-----------------------------
+INT 33 - MS MOUSE v6.0+ - RETURN DRIVER STORAGE REQUIREMENTS
+ AX = 0015h
+Return: BX = size of buffer needed to store driver state
+SeeAlso: AX=0016h,AX=0017h,AX=0042h
+--------M-330016-----------------------------
+INT 33 - MS MOUSE v6.0+ - SAVE DRIVER STATE
+ AX = 0016h
+ BX = size of buffer (see AX=0015h)
+ ES:DX -> buffer for driver state
+Note: although not documented (since the Microsoft driver does not use it),
+ many drivers appear to require BX on input
+SeeAlso: AX=0015h,AX=0017h
+--------M-330017-----------------------------
+INT 33 - MS MOUSE v6.0+ - RESTORE DRIVER STATE
+ AX = 0017h
+ BX = size of buffer (see AX=0015h)
+ ES:DX -> buffer containing saved state
+Notes: although not documented (since the Microsoft driver does not use it),
+ many drivers appear to require BX on input
+ some mouse drivers range-check the values in the saved state based on
+ the current video mode; thus, the video mode should be restored
+ before the mouse driver's state is restored
+SeeAlso: AX=0015h,AX=0016h
+--------M-330018-----------------------------
+INT 33 - MS MOUSE v6.0+ - SET ALTERNATE MOUSE USER HANDLER
+ AX = 0018h
+ CX = call mask (see #03174)
+ ES:DX -> FAR routine to be invoked on mouse events (see #03175)
+Return: AX = status
+ 0018h if successful
+ FFFFh on error
+Notes: up to three handlers can be defined by separate calls to this function,
+ each with a different combination of shift states in the call mask;
+ calling this function again with a call mask of 0000h undefines the
+ specified handler (official documentation); specifying the same
+ call mask and an address of 0000h:0000h undefines the handler (real
+ life)
+ some versions of the documentation erroneously reverse the order of
+ the bits in the call mask
+SeeAlso: AX=000Ch,AX=0014h,AX=0019h
+
+Bitfields for mouse call mask:
+Bit(s) Description (Table 03174)
+ 0 call if mouse moves
+ 1 call if left button pressed
+ 2 call if left button released
+ 3 call if right button pressed
+ 4 call if right button released
+ 5 call if shift button pressed during event
+ 6 call if ctrl key pressed during event
+ 7 call if alt key pressed during event
+Note: at least one of 5-7 must be set
+
+(Table 03175)
+Values user handler is called with:
+ AX = condition mask (same bit assignments as call mask)
+ BX = button state
+ CX = cursor column
+ DX = cursor row
+ SI = horizontal mickey count
+ DI = vertical mickey count
+Return: registers preserved
+Note: in text modes, the row and column will be reported as a multiple of
+ the cell size, typically 8x8 pixels
+--------M-330019-----------------------------
+INT 33 - MS MOUSE v6.0+ - RETURN USER ALTERNATE INTERRUPT VECTOR
+ AX = 0019h
+ CX = call mask (see #03174)
+Return: BX:DX = user interrupt vector
+ CX = call mask (0000h if not found)
+Note: attempts to find a user event handler (defined by function 18h)
+ whose call mask matches CX
+SeeAlso: AX=0018h
+--------M-33001A-----------------------------
+INT 33 - MS MOUSE v6.0+ - SET MOUSE SENSITIVITY
+ AX = 001Ah
+ BX = horizontal speed \
+ CX = vertical speed / (see AX=000Fh)
+ DX = double speed threshold (see AX=0013h)
+SeeAlso: AX=0013h,AX=001Bh,INT 62/AX=0082h
+--------M-33001B-----------------------------
+INT 33 - MS MOUSE v6.0+ - RETURN MOUSE SENSITIVITY
+ AX = 001Bh
+Return: BX = horizontal speed
+ CX = vertical speed
+ DX = double speed threshold
+SeeAlso: AX=000Bh,AX=001Ah
+--------M-33001C-----------------------------
+INT 33 - MS MOUSE v6.0+ - SET INTERRUPT RATE
+ AX = 001Ch
+ BX = rate (see #03176)
+Notes: only available on InPort mouse
+ values greater than 4 may cause unpredictable driver behavior
+
+(Table 03176)
+Values for mouse interrupt rate:
+ 00h no interrupts allowed
+ 01h 30 per second
+ 02h 50 per second
+ 03h 100 per second
+ 04h 200 per second
+--------M-33001D-----------------------------
+INT 33 - MS MOUSE v6.0+ - DEFINE DISPLAY PAGE NUMBER
+ AX = 001Dh
+ BX = display page number
+Note: the cursor will be displayed on the specified page
+SeeAlso: AX=001Eh
+--------M-33001E-----------------------------
+INT 33 - MS MOUSE v6.0+ - RETURN DISPLAY PAGE NUMBER
+ AX = 001Eh
+Return: BX = display page number
+SeeAlso: AX=001Dh
+--------M-33001F-----------------------------
+INT 33 - MS MOUSE v6.0+ - DISABLE MOUSE DRIVER
+ AX = 001Fh
+Return: AX = status
+ 001Fh successful
+ ES:BX = INT 33 vector before mouse driver was first installed
+ FFFFh unsuccessful
+Notes: restores vectors for INT 10 and INT 71 (8086) or INT 74 (286/386)
+ if you restore INT 33 to ES:BX, driver will be completely disabled
+ many drivers return AX=001Fh even though the driver has been disabled
+SeeAlso: AX=0020h
+--------M-330020-----------------------------
+INT 33 - MS MOUSE v6.0+ - ENABLE MOUSE DRIVER
+ AX = 0020h
+Return: AX = status
+ 0020h successful
+ FFFFh unsuccessful
+Notes: restores vectors for INT 10h and INT 71h (8086) or INT 74h (286/386)
+ which were removed by function 1Fh
+ Microsoft's documentation states that no value is returned
+SeeAlso: AX=001Fh
+--------M-330021-----------------------------
+INT 33 - MS MOUSE v6.0+ - SOFTWARE RESET
+ AX = 0021h
+Return: AX = status
+ FFFFh if mouse driver installed
+ BX = number of buttons (FFFFh = two buttons)
+ 0021h if mouse driver not installed
+Note: this call is identical to funtion 00h, but does not reset the mouse
+SeeAlso: AX=0000h
+--------M-330022-----------------------------
+INT 33 - MS MOUSE v6.0+ - SET LANGUAGE FOR MESSAGES
+ AX = 0022h
+ BX = language (see #03177)
+Note: only available on international versions of the driver; US versions
+ ignore this call
+SeeAlso: AX=0023h
+
+(Table 03177)
+Values for mouse driver language:
+ 00h English
+ 01h French
+ 02h Dutch
+ 03h German
+ 04h Swedish
+ 05h Finnish
+ 06h Spanish
+ 07h Portugese
+ 08h Italian
+--------M-330023-----------------------------
+INT 33 - MS MOUSE v6.0+ - GET LANGUAGE FOR MESSAGES
+ AX = 0023h
+Return: BX = language (see #03177)
+Note: the US version of the driver always returns zero
+SeeAlso: AX=0022h
+--------M-330024BX0000-----------------------
+INT 33 - MS MOUSE v6.26+ - GET SOFTWARE VERSION, MOUSE TYPE, AND IRQ NUMBER
+ AX = 0024h
+ BX = 0000h to check for function's existence
+Return: AX = FFFFh on error
+ otherwise,
+ BH = major version
+ BL = minor version
+ CH = type (1=bus, 2=serial, 3=InPort, 4=PS/2, 5=HP)
+ CL = interrupt (0=PS/2, 2=IRQ2, 3=IRQ3,...,7=IRQ7,...,0Fh=IRQ15)
+Note: although current Microsoft documentation states that this function was
+ introduced in v6.26, it appears to have been present as early as
+ v6.02 (for earlier versions, use INT 33/AX=006Dh)
+SeeAlso: AX=004Dh,AX=006Dh
+--------M-330025-----------------------------
+INT 33 - MS MOUSE v6.26+ - GET GENERAL DRIVER INFORMATION
+ AX = 0025h
+Return: AX = general information (see #03178)
+ BX = cursor lock flag for OS/2 to prevent reentrancy problems
+ CX = mouse code active flag (for OS/2)
+ DX = mouse driver busy flag (for OS/2)
+
+Bitfields for general mouse driver information:
+Bit(s) Description (Table 03178)
+ 15 driver loaded as device driver rather than TSR
+ 14 driver is newer integrated type
+ 13,12 current cursor type
+ 00 software text cursor
+ 01 hardware text cursor (CRT Controller's cursor)
+ 1X graphics cursor
+ 11-8 interrupt rate (see #03176)
+ 7-0 count of currently-active Mouse Display Drivers (MDD), the newer
+ integrated driver type
+--------M-330026-----------------------------
+INT 33 - MS MOUSE v6.26+ - GET MAXIMUM VIRTUAL COORDINATES
+ AX = 0026h
+Return: BX = mouse-disabled flag (0000h mouse enabled, nonzero disabled)
+ CX = maximum virtual X (for current video mode)
+ DX = maximum virtual Y
+Note: for driver versions before 7.05, this call returns the currently-set
+ maximum coordinates; v7.05+ returns the absolute maximum coordinates
+SeeAlso: AX=0031h
+--------M-330027-----------------------------
+INT 33 - MS MOUSE v7.01+ - GET SCREEN/CURSOR MASKS AND MICKEY COUNTS
+ AX = 0027h
+Return: AX = screen-mask value (or hardware cursor scan-line start for v7.02+)
+ BX = cursor-mask value (or hardware cursor scan-line stop for v7.02+)
+ CX = horizontal mickeys moved since last call
+ DX = vertical mickeys moved since last call
+SeeAlso: AX=000Bh
+--------M-330028-----------------------------
+INT 33 - MS MOUSE v7.0+ - SET VIDEO MODE
+ AX = 0028h
+ CX = new video mode (call is NOP if 0000h)
+ DH = Y font size (00h = default)
+ DL = X font size (00h = default)
+Return: CL = status (00h = successful)
+Notes: DX is ignored unless the selected video mode supports font size control
+ when CX=0000h, an internal flag that had been set by a previous call
+ is cleared; this is required before a mouse reset
+SeeAlso: AX=0029h,INT 10/AH=00h
+--------M-330029-----------------------------
+INT 33 - MS MOUSE v7.0+ - ENUMERATE VIDEO MODES
+ AX = 0029h
+ CX = previous video mode
+ 0000h get first supported video mode
+ other get next supported mode after mode CX
+Return: CX = first/next video mode (0000h = no more video modes)
+ DS:DX -> description of video mode or 0000h:0000h if none
+Notes: the enumerated video modes may be in any order and may repeat
+ the description string (if available) is terminated by '$' followed by
+ a NUL byte
+SeeAlso: AX=0028h
+--------M-33002A-----------------------------
+INT 33 - MS MOUSE v7.02+ - GET CURSOR HOT SPOT
+ AX = 002Ah
+Return: AX = internal counter controlling cursor visibility
+ BX = cursor hot spot column
+ CX = cursor hot spot row
+ DX = mouse type (see #03179)
+Note: the hot spot location is relative to the upper left corner of the
+ cursor block and may range from -128 to +127 both horizontally and
+ vertically
+SeeAlso: AX=0009h,AX=0012h,AX=0035h
+
+(Table 03179)
+Values for mouse type:
+ 00h none
+ 01h bus
+ 02h serial
+ 03h InPort
+ 04h IBM
+ 05h Hewlett-Packard
+--------M-33002B-----------------------------
+INT 33 - MS MOUSE v7.0+ - LOAD ACCELERATION PROFILES
+ AX = 002Bh
+ BX = active acceleration profile
+ 0001h-0004h or FFFFh to restore default curves
+ ES:SI -> buffer containing acceleration profile data (see #03180)
+Return: AX = success flag
+SeeAlso: AX=002Ch,AX=002Dh,AX=0033h
+
+Format of acceleration profile data:
+Offset Size Description (Table 03180)
+ 00h BYTE length of acceleration profile 1
+ 01h BYTE length of acceleration profile 2
+ 02h BYTE length of acceleration profile 3
+ 03h BYTE length of acceleration profile 4
+ 04h 32 BYTEs threshold speeds for acceleration profile 1
+ 24h 32 BYTEs threshold speeds for acceleration profile 2
+ 44h 32 BYTEs threshold speeds for acceleration profile 3
+ 64h 32 BYTEs threshold speeds for acceleration profile 4
+ 84h 32 BYTEs speedup factor for acceleration profile 1
+ (10h = 1.0, 14h = 1.25, 20h = 2.0, etc)
+ A4h 32 BYTEs speedup factor for acceleration profile 2
+ (10h = 1.0, 14h = 1.25, 20h = 2.0, etc)
+ C4h 32 BYTEs speedup factor for acceleration profile 3
+ (10h = 1.0, 14h = 1.25, 20h = 2.0, etc)
+ E4h 32 BYTEs speedup factor for acceleration profile 4
+ (10h = 1.0, 14h = 1.25, 20h = 2.0, etc)
+104h 16 BYTEs name of acceleration profile 1 (blank-padded)
+114h 16 BYTEs name of acceleration profile 2 (blank-padded)
+124h 16 BYTEs name of acceleration profile 3 (blank-padded)
+134h 16 BYTEs name of acceleration profile 4 (blank-padded)
+Note: unused bytes in the threshold speed fields are filled with 7Fh and
+ unused bytes in the speedup factor fields are filled with 10h
+--------M-33002C-----------------------------
+INT 33 - MS MOUSE v7.0+ - GET ACCELERATION PROFILES
+ AX = 002Ch
+Return: AX = status (0000h success)
+ BX = currently-active acceleration profile
+ ES:SI -> acceleration profile data (see #03180)
+SeeAlso: AX=002Bh,AX=002Dh,AX=0033h
+--------M-33002D-----------------------------
+INT 33 - MS MOUSE v7.0+ - SELECT ACCELERATION PROFILE
+ AX = 002Dh
+ BX = acceleration level
+ 0001h-0004h to set profile, or FFFFh to get current profile
+Return: AX = status
+ 0000h successful
+ ES:SI -> 16-byte blank-padded name of acceleration profile
+ FFFEh invalid acceleration curve number
+ ES:SI destroyed
+ BX = active acceleration curve number
+SeeAlso: AX=0013h,AX=002Bh,AX=002Ch,AX=002Eh
+--------M-33002E-----------------------------
+INT 33 - MS MOUSE v8.10+ - SET ACCELERATION PROFILE NAMES
+ AX = 002Eh
+ BL = flag (if nonzero, fill ES:SI buffer with default names on return)
+ ES:SI -> 64-byte buffer containing profile names (16 bytes per name)
+Return: AX = status (0000h success)
+ FFFEh error for ATI Mouse driver
+ ES:SI buffer filled with default names if BL nonzero on entry
+Notes: not supported by Logitech driver v6.10
+ supported by ATI Mouse driver v7.04
+SeeAlso: AX=002Ch,AX=002Dh,AX=012Eh,AX=022Eh
+--------M-33002F-----------------------------
+INT 33 - MS MOUSE v7.02+ - MOUSE HARDWARE RESET
+ AX = 002Fh
+Return: AX = status
+Note: invoked by mouse driver v8.20 on being called with INT 2F/AX=530Bh
+SeeAlso: INT 2F/AH=53h
+--------M-330030-----------------------------
+INT 33 - MS MOUSE v7.04+ - GET/SET BallPoint INFORMATION
+ AX = 0030h
+ CX = command
+ 0000h get status of BallPoint device
+ other set rotation angle and masks
+ BX = rotation angle (-32768 to 32767 degrees)
+ CH = primary button mask
+ CL = secondary button mask
+Return: AX = button status (FFFFh if no BallPoint) (see #03181)
+ BX = rotation angle (0-360 degrees)
+ CH = primary button mask
+ CL = secondary button mask
+Note: not supported by the ATI Mouse driver which calls itself v7.04
+
+Bitfields for BallPoint mouse button status:
+Bit(s) Description (Table 03181)
+ 5 button 1
+ 4 button 2
+ 3 button 3
+ 2 button 4
+ other zero
+--------M-330031-----------------------------
+INT 33 - MS MOUSE v7.05+ - GET CURRENT MINIMUM/MAXIMUM VIRTUAL COORDINATES
+ AX = 0031h
+Return: AX = virtual X minimum
+ BX = virtual Y minimum
+ CX = virtual X maximum
+ DX = virtual Y maximum
+Note: the minimum and maximum values are those set by AX=0007h and AX=0008h;
+ the default is minimum = 0 and maximum = absolute maximum
+ (see AX=0026h)
+SeeAlso: AX=0007h,AX=0008h,AX=0010h,AX=0026h
+--------M-330032-----------------------------
+INT 33 - MS MOUSE v7.05+ - GET ACTIVE ADVANCED FUNCTIONS
+ AX = 0032h
+Return: AX = active function flags (FFFFh for v8.10)
+ bit 15: function 0025h supported
+ bit 14: function 0026h supported
+ ...
+ bit 0: function 0034h supported
+ BX = ??? (0000h) officially unused
+ CX = ??? (E000h) officially unused
+ DX = ??? (0000h) officially unused
+Note: the Italian version of MS MOUSE v8.20 reportedly indicates that
+ functions 0033h and 0034h are not supported even though they are
+--------M-330033-----------------------------
+INT 33 - MS MOUSE v7.05+ - GET SWITCH SETTINGS AND ACCELERATION PROFILE DATA
+ AX = 0033h
+ CX = size of buffer
+ 0000h get required buffer size
+ Return: AX = 0000h
+ CX = required size (0154h for Logitech v6.10, 0159h
+ for MS v8.10-8.20)
+ other
+ ES:DX -> buffer of CX bytes for mouse settings
+ Return: AX = 0000h
+ CX = number of bytes returned
+ ES:DX buffer filled (see #03182)
+SeeAlso: AX=002Bh
+
+Format of mouse settings data buffer:
+Offset Size Description (Table 03182)
+ 00h BYTE mouse type
+ 01h BYTE current language
+ 02h BYTE horizontal sensitivity (00h-64h)
+ 03h BYTE vertical sensitivity (00h-64h)
+ 04h BYTE double-speed threshold (00h-64h)
+ 05h BYTE ballistic curve (01h-04h)
+ 06h BYTE interrupt rate (01h-04h)
+ 07h BYTE cursor override mask
+ 08h BYTE laptop adjustment
+ 09h BYTE memory type (00h-02h)
+ 0Ah BYTE SuperVGA support (00h,01h)
+ 0Bh BYTE rotation angle
+ 0Ch BYTE ???
+ 0Dh BYTE primary button (01h-04h)
+ 0Eh BYTE secondary button (01h-04h)
+ 0Fh BYTE click lock enabled (00h,01h)
+ 10h 324 BYTEs acceleration profile data (see #03180)
+154h 5 BYTEs ??? (Microsoft driver, but not Logitech)
+--------M-330034-----------------------------
+INT 33 - MS MOUSE v8.0+ - GET INITIALIZATION FILE
+ AX = 0034h
+Return: AX = status (0000h successful)
+ ES:DX -> ASCIZ initialization (.INI) file name
+--------M-330035-----------------------------
+INT 33 - MS MOUSE v8.10+ - LCD SCREEN LARGE POINTER SUPPORT
+ AX = 0035h
+ BX = function
+ FFFFh get current settings
+ Return: AX = 0000h
+ BH = style (see #03183)
+ BL = size (see #03184)
+ CH = threshold (00h-64h)
+ CL = active flag (00h disabled, 01h enabled)
+ DX = delay
+ other
+ BH = pointer style (see #03183)
+ BL = size (see #03184)
+ CH = threshold (00h-64h)
+ CL = active flag (00h disable size change, 01h enable)
+ DX = delay (0000h-0064h)
+ Return: AX = 0000h
+Note: not supported by Logitech driver v6.10
+SeeAlso: AX=0012h,AX=002Ah
+
+(Table 03183)
+Values for pointer style:
+ 00h normal
+ 01h reverse
+ 02h transparent
+SeeAlso: #03184
+
+(Table 03184)
+Values for pointer size:
+ 00h small ("1")
+ 01h medium ("1.5")
+ 02h large ("2")
+SeeAlso: #03183
+--------M-33004D-----------------------------
+INT 33 - MS MOUSE - RETURN POINTER TO COPYRIGHT STRING
+ AX = 004Dh
+Return: ES:DI -> copyright message "*** This is Copyright 1983 Microsoft" or
+ "Copyright 19XX...."
+Notes: also supported by Logitech, Kraft, Genius Mouse, and Mouse Systems
+ mouse drivers
+ in the Genius Mouse 9.06 driver, the ASCIZ signature "KYE" immediately
+ follows the above copyright message (KYE Corp. manufactures the
+ driver)
+SeeAlso: AX=0024h,AX=006Dh,AX=0666h
+--------M-33006D-----------------------------
+INT 33 - MS MOUSE - GET VERSION STRING
+ AX = 006Dh 'm'
+Return: ES:DI -> Microsoft version number of resident driver (see #03187)
+Notes: also supported by Logitech, Mouse Systems, Kraft, and Genius mouse
+ drivers
+ the Mouse Systems 7.01 and Genius Mouse 9.06 drivers report their
+ Microsoft version as 7.00 even though they do not support any of the
+ functions from 0025h through 002Dh supported by the MS 7.00 driver
+ (the Genius Mouse driver supports function 0026h, but it differs
+ from the Microsoft function)
+ the TRUEDOX 4.01 driver reports its version as 6.26 through this call,
+ but as 6.24 through AX=0024h
+ There seems to be no reliable method to distinguish MS MOUSE before
+ 3.00 from mouse drivers of other vendors.
+ Some releases of the MS MOUSE 6.00 erroneously return 6.01 instead of
+ their true version number. In this case, a DI value of 01ABh can
+ be used to still detect a 6.00 driver.
+ For returned versions 6.02+, INT 33/AX=0024h should be used to retrieve
+ more accurate version data.
+ True MS MOUSE drivers can also be identified by magic numbers in
+ their copyright message, stored in the driver's segment (ES).
+ These can be found by scanning the first 2 Kb of the mouse
+ driver's segment for a string like: [new since 7.00+]
+ "** This is Copyright 1983[-19xx] Microsoft ***" with the
+ magic number stored one byte after the signature string.
+SeeAlso: AX=0024h,AX=004Dh,AX=006Ah,AX=266Ch
+
+Format of Microsoft version number:
+Offset Size Description (Table 03187)
+ 00h BYTE major version
+ 01h BYTE minor version (BCD)
+
+(Table 04087)
+Values for Microsoft MOUSE copyright string magic numbers:
+ 5564h version 3.00..6.00 (for reported versions up to 5.03, and 6.00)
+ 557Ch version 6.01Z..6.24 (for reported versions 6.01..6.24)
+ E806h version 6.25 (for reported version 6.25)
+ EB02h version 6.26..7.04 (for reported version 6.26..7.04)
+ 0800h Integrated driver 1.0+ (for reported version 9.x+)
+Note: Versions above 7.04 (except for integrated mouse drivers) have a magic
+ number representing their version number, e.g. 0507h for version 7.05
diff --git a/doc/wheelapi.txt b/doc/wheelapi.txt
new file mode 100644
index 0000000..35a1aa6
--- /dev/null
+++ b/doc/wheelapi.txt
@@ -0,0 +1,109 @@
+
+Wheel support in DOS real-mode mouse drivers
+
+List of DOS applications which use the wheel API:
+
+Thanks to Rugxulo and others for collecting this list :-)
+
+- GVFM (graphical file manager)
+- Mpxplay (.MP3, .OGG, etc. audio player)
+- Arachne/GPL (graphical web browser / email suite)
+- Star Commander (file manager)
+- PDCurses 3.x (console library)
+- Necromancer's DOS Navigator (file manager)
+- Fred (text editor using graphics mode)
+- Blocek (graphical Unicode text editor, image viewer)
+- Hammer of Thyrion (DOS port of Hexen II game)
+- 4DOS (COMMAND.COM shell replacement)
+- Deskwork (graphical user interface, StarTrek style)
+
+---------------------------------------------------------------------
+
+API version 1.0
+
+Summary:
+
+This document describes an extension to the commonly used INT 33h Mouse
+API to add wheel (Z axis) support. This draft introduces extra functions
+and additions to the standard INT 33 API. These new and changed functions
+are mentioned in the technote.txt as the WheelAPI.
+
+---------------------------------------------------------------------
+New functions:
+
+INT 33/0011 - Check wheel support and get capabilities flags
+ AX = 0011h
+Return: AX = 574Dh ('WM' in assembly) if Wheel API is supported by driver
+ BX = Capabilities flag (all bits reserved)
+ CX = Capabilities flag
+ Bit(s) Description
+ ------ -----------
+ 0 1=Pointing device supports wheel
+ 1-15 Reserved
+Note: this function should be examined before accessing wheel features
+
+---------------------------------------------------------------------
+Changes in the original INT 33h functions:
+
+INT 33/0000 - Reset driver and read status
+Note: this call clears the wheel movement counter
+
+INT 33/0003 - Get cursor position, buttons status and wheel counter
+ AX = 0003h
+Return: BL = buttons status
+ BH = 8-bit signed counter of wheel movement since last call
+ CX = column
+ DX = row
+Notes: returned wheel counter contains all wheel movements accumulated since
+ the last call to INT 33/AX=0003h, INT 33/AX=0005h/BX=-1 or
+ INT 33/AX=0006h/BX=-1
+ positive value of wheel counter means downward wheel movement
+ this call clears the wheel movement counter
+
+INT 33/0005 - Get button press or wheel movement data
+ AX = 0005h
+ BX = button number or -1 for wheel
+Return: AL = state of buttons
+ AH = 8-bit signed counter of wheel movement
+ ---button info---
+ BX = number of times specified button has been pressed since last call
+ CX = column where specified button was last pressed
+ DX = row where specified button was last pressed
+ ---wheel info---
+ BX = 16-bit signed counter of wheel movement since last call
+ CX = column where wheel was last moved
+ DX = row where wheel was last moved
+Notes: returned wheel counters contain all wheel movements accumulated since
+ the last call to INT 33/AX=0003h, INT 33/AX=0005h/BX=-1 or
+ INT 33/AX=0006h/BX=-1
+ positive value of wheel counter means downward wheel movement
+ this call clears the wheel movement counter for BX=-1
+
+INT 33/0006 - Get button release or wheel movement data
+ AX = 0006h
+ BX = button number or -1 for wheel
+Return: AL = state of buttons
+ AH = 8-bit signed counter of wheel movement
+ ---button info---
+ BX = number of times specified button has been released since last call
+ CX = column where specified button was last released
+ DX = row where specified button was last released
+ ---wheel info---
+ BX = 16-bit signed counter of wheel movement since last call
+ CX = column where wheel was last moved
+ DX = row where wheel was last moved
+Notes: returned wheel counters contain all wheel movements accumulated since
+ the last call to INT 33/AX=0003h, INT 33/AX=0005h/BX=-1 or
+ INT 33/AX=0006h/BX=-1
+ positive value of wheel counter means downward wheel movement
+ this call clears the wheel movement counter for BX=-1
+
+INT 33/000C - Define User Interrupt Routine
+INT 33/0014 - Exchange User Interrupt Routines
+Notes: on entry, bit 7 of CX (call mask) indicates that the user routine
+ will be called on a wheel movement
+ the user routine will be called with BH holding the 8-bit signed
+ counter of wheel movement since the last call to the routine
+
+INT 33/0021 - Software reset
+Note: this call clears the wheel movement counter
diff --git a/doc/wheelapi2.txt b/doc/wheelapi2.txt
new file mode 100644
index 0000000..c2b087f
--- /dev/null
+++ b/doc/wheelapi2.txt
@@ -0,0 +1,69 @@
+This document describes an extension to the CuteMouse wheelapi.txt
+to support two wheels (vertical and horizontal).
+
+NOTE: Draft, subject to change.
+
+---------------------------------------------------------------------
+Changes to the wheelapi v1 functions:
+
+INT 33/0011 - Check wheel support and get capabilities flags
+ AX = 0011h
+Return: AX = 574Dh ('WM' in assembly) if Wheel API is supported by driver
+ CX = Capabilities flag
+ Bit(s) Description
+ ------ -----------
+ 0 1=Pointing device supports wheel
+ 1 1=Pointing device supports 2nd wheel
+ 2-15 Reserved
+Notes: this function should be examined before accessing wheel features.
+ vbmouse currently assumes this is called after each int33/0, otherwise
+ wheel events may be sent as keystrokes instead of wheelapi.
+
+INT 33/0003 - Get cursor position, buttons status and wheel counter
+ AX = 0003h
+Return: BL = buttons status (bits 0-4)
+ CX = column
+ DX = row
+ BH = 8-bit signed counter of wheel movement since last call.
+ positive value means downward wheel movement.
+ AH = 8-bit signed counter of 2nd wheel movement since last call.
+ positive value means rightward wheel movement.
+Note: calling this clears the wheel counter for ALL wheels.
+
+INT 33/0005 - Get button press or wheel movement data
+ AX = 0005h
+ BX = button number, -1 for vertical wheel, -2 for 2nd/horizontal wheel
+ (return values remain the same)
+Note: as in wheelapi v1, AH always contains (1st) wheel movement on return
+ independently of button number requested in BX.
+
+INT 33/0006 - Get button release or wheel movement data
+ AX = 0006h
+ BX = button number, -1 for vertical wheel, -2 for 2nd/horizontal wheel
+ (return values remain the same)
+
+INT 33/000C - Define User Interrupt Routine
+INT 33/0014 - Exchange User Interrupt Routines
+Bitfields for mouse call mask:
+Bit(s) Description
+ 0-6 same as Table 03171 (as in int33.lst)
+ 7 vertical wheel movement (as in wheelapi.txt)
+ 8 (absolute mouse event bit)
+ 9 horizontal wheel movement
+ 10 4th button pressed
+ 11 4th button released
+ 12 5th button pressed
+ 13 5th button released
+ 14-15 unused
+Notes: on entry, bit 9 of CX (call mask) indicates that the user routine
+ will be called on horizontal wheel movement
+ if the user routine is called with bit 9 of AX (condition mask) set,
+ then BH will hold the 8-bit signed counter of HORIZONTAL wheel
+ movement since the last call to the routine. if bit 7 is set, then
+ BH holds VERTICAL wheel movement.
+ it is impossible for the user routine to be called with both
+ vertical (bit 7) and horizontal (bit 9) movement.
+ A program that just sets 0xFFFF event mask and expects to find vertical
+ wheel movement info in BH (wheelapi v1 style) will be confused, as
+ here when bit 9 is set BH will contain horizontal wheel movement
+ instead. DN/2 does this, so horizontal scrolling acts like vertical.
diff --git a/int15ps2.h b/int15ps2.h
index dfc5b3a..db1b878 100644
--- a/int15ps2.h
+++ b/int15ps2.h
@@ -22,6 +22,7 @@
#include <stdbool.h>
#include <stdint.h>
+#include "utils.h"
/** Standard PS/2 mouse IRQ. At least on VirtualBox. */
#define PS2_MOUSE_IRQ 12
@@ -49,19 +50,27 @@ enum ps2m_status {
enum ps2m_packet_size {
PS2M_PACKET_SIZE_STREAMING = 1,
- PS2M_PACKET_SIZE_PLAIN = 3,
+ PS2M_PACKET_SIZE_STD = 3,
PS2M_PACKET_SIZE_EXT = 4,
};
enum ps2m_device_ids {
/** Standard PS/2 mouse, 2 buttons. */
- PS2M_DEVICE_ID_PLAIN = 0,
+ PS2M_DEVICE_ID_STD = 0,
/** IntelliMouse PS/2, with wheel. */
- PS2M_DEVICE_ID_IMPS2 = 3,
+ PS2M_DEVICE_ID_IMPS2 = 3,
/** IntelliMouse Explorer, wheel and 5 buttons. */
- PS2M_DEVICE_ID_IMEX = 4,
+ PS2M_DEVICE_ID_IMEX = 4,
/** IntelliMouse Explorer, wheel, 5 buttons, and horizontal scrolling. */
- PS2M_DEVICE_ID_IMEX_HORZ = 5
+ PS2M_DEVICE_ID_IMEX_HORZ = 5
+};
+
+/** Additional bits set in 4th byte of IMEX protocol. */
+enum ps2m_imex_ext_byte_bits {
+ PS2M_IMEX_BUTTON_4 = 1 << 4,
+ PS2M_IMEX_BUTTON_5 = 1 << 5,
+ PS2M_IMEX_HORIZONTAL_SCROLL = 1 << 6,
+ PS2M_IMEX_VERTICAL_SCROLL = 1 << 7
};
/** Valid PS/2 mouse resolutions in DPI. */
@@ -200,7 +209,7 @@ static ps2m_err ps2m_enable(bool enable);
__value [ah] \
__modify [ax]
-/** Sends the magic sequence to switch the mouse to the IMPS2 protocol. */
+/** Sends the magic sequence to switch the mouse to the IntelliMouse protocol. */
static void ps2m_send_imps2_sequence(void)
{
ps2m_set_sample_rate(PS2M_SAMPLE_RATE_200);
@@ -208,8 +217,28 @@ static void ps2m_send_imps2_sequence(void)
ps2m_set_sample_rate(PS2M_SAMPLE_RATE_80);
}
-/** Detects whether we have a IMPS2 mouse with wheel support. */
-static bool ps2m_detect_wheel(void)
+/** Sends the magic sequence to switch the mouse to the IntelliMouse Explorer protocol. */
+static void ps2m_send_imex_sequence(void)
+{
+ ps2m_set_sample_rate(PS2M_SAMPLE_RATE_200);
+ ps2m_set_sample_rate(PS2M_SAMPLE_RATE_200);
+ ps2m_set_sample_rate(PS2M_SAMPLE_RATE_80);
+}
+
+/** Sends the magic sequence to switch the mouse to the IntelliMouse Explorer
+ * with horz. wheel protocol. Note: unclear if mouse changes device_id after this. */
+static void ps2m_send_imex_horz_sequence(void)
+{
+ ps2m_set_sample_rate(PS2M_SAMPLE_RATE_200);
+ ps2m_set_sample_rate(PS2M_SAMPLE_RATE_80);
+ ps2m_set_sample_rate(PS2M_SAMPLE_RATE_40);
+}
+
+/** Detects whether we have a IMPS2 mouse with wheel support.
+ *
+ * @return true if we have at least imps/2 with 1 wheel support.
+ */
+static bool ps2m_detect_imps2(void)
{
int err;
uint8_t device_id;
@@ -220,14 +249,14 @@ static bool ps2m_detect_wheel(void)
return false;
}
- if (device_id == PS2M_DEVICE_ID_IMPS2) {
+ if (device_id == PS2M_DEVICE_ID_IMPS2
+ || device_id == PS2M_DEVICE_ID_IMEX
+ || device_id == PS2M_DEVICE_ID_IMEX_HORZ) {
// Already wheel
return true;
}
- if (device_id != PS2M_DEVICE_ID_PLAIN) {
- // TODO: Likely we have to accept more device IDs here
- dprintf("Unknown initial mouse device_id=0x%x\n", device_id);
+ if (device_id != PS2M_DEVICE_ID_STD) {
return false;
}
@@ -236,8 +265,36 @@ static bool ps2m_detect_wheel(void)
// Now check if the device id has changed
err = ps2m_get_device_id(&device_id);
-
return err == 0 && device_id == PS2M_DEVICE_ID_IMPS2;
}
+static bool ps2m_detect_imex(void)
+{
+ int err;
+ uint8_t device_id;
+
+ // Get the initial mouse device id
+ err = ps2m_get_device_id(&device_id);
+ if (err) {
+ return false;
+ }
+
+ if (device_id == PS2M_DEVICE_ID_IMEX
+ || device_id == PS2M_DEVICE_ID_IMEX_HORZ) {
+ // Already ImEx
+ return true;
+ }
+
+ if (device_id != PS2M_DEVICE_ID_IMPS2) {
+ return false;
+ }
+
+ // Send the knock sequence to activate the extended packet
+ ps2m_send_imex_sequence();
+
+ // Now check if the device id has changed
+ err = ps2m_get_device_id(&device_id);
+ return err == 0 && device_id == PS2M_DEVICE_ID_IMEX;
+}
+
#endif /* INT15PS2_H */
diff --git a/int33.h b/int33.h
index 03c61a0..f7a469d 100644
--- a/int33.h
+++ b/int33.h
@@ -150,7 +150,8 @@ enum INT33_API {
};
enum INT33_CAPABILITY_BITS {
- INT33_CAPABILITY_MOUSE_API = 1 << 0
+ INT33_CAPABILITY_WHEEL_API = 1 << 0,
+ INT33_CAPABILITY_WHEEL2_API = 1 << 1
};
#define INT33_WHEEL_API_MAGIC 'WM'
@@ -167,11 +168,14 @@ enum INT33_MOUSE_TYPE {
enum INT33_BUTTON_MASK {
INT33_BUTTON_MASK_LEFT = 1 << 0,
INT33_BUTTON_MASK_RIGHT = 1 << 1,
- INT33_BUTTON_MASK_CENTER = 1 << 2
+ INT33_BUTTON_MASK_CENTER = 1 << 2,
+ INT33_BUTTON_MASK_4TH = 1 << 3,
+ INT33_BUTTON_MASK_5TH = 1 << 4,
};
enum INT33_EVENT_MASK {
INT33_EVENT_MASK_MOVEMENT = 1 << 0,
+ INT33_EVENT_MASK_LEFT_BUTTON_PRESSED_INDEX = 1,
INT33_EVENT_MASK_LEFT_BUTTON_PRESSED = 1 << 1,
INT33_EVENT_MASK_LEFT_BUTTON_RELEASED = 1 << 2,
INT33_EVENT_MASK_RIGHT_BUTTON_PRESSED = 1 << 3,
@@ -182,6 +186,17 @@ enum INT33_EVENT_MASK {
// Wheel API Extensions:
/** Wheel mouse movement. */
INT33_EVENT_MASK_WHEEL_MOVEMENT = 1 << 7,
+ /** 2nd/horizontal wheel mouse movement. */
+ INT33_EVENT_MASK_HORIZ_WHEEL_MOVEMENT = 1 << 9,
+
+ INT33_EVENT_MASK_ANY_WHEEL_MOVEMENT
+ = INT33_EVENT_MASK_WHEEL_MOVEMENT | INT33_EVENT_MASK_HORIZ_WHEEL_MOVEMENT,
+
+ INT33_EVENT_MASK_4TH_BUTTON_PRESSED_INDEX = 10,
+ INT33_EVENT_MASK_4TH_BUTTON_PRESSED = 1 << 10,
+ INT33_EVENT_MASK_4TH_BUTTON_RELEASED = 1 << 11,
+ INT33_EVENT_MASK_5TH_BUTTON_PRESSED = 1 << 12,
+ INT33_EVENT_MASK_5TH_BUTTON_RELEASED = 1 << 13,
// Absolute API extensions:
/** The source of the event is an absolute pointing device. */
diff --git a/mousetsr.c b/mousetsr.c
index 8c42057..e571d51 100644
--- a/mousetsr.c
+++ b/mousetsr.c
@@ -537,6 +537,8 @@ static void refresh_video_info(void)
reload_video_info();
+ // This is one of these compatibility-delicate things
+ // TODO: Investigate correct behavior here
if (data.video_mode.type != VIDEO_UNKNOWN) {
// If we know the screen size for this mode, then reset the window to it
data.min.x = 0;
@@ -547,6 +549,21 @@ static void refresh_video_info(void)
}
}
+/** Obtains INT33_EVENT_MASK bitmask corresponding to btn. */
+static uint16_t button_event_mask(int btn, bool released)
+{
+ unsigned int idx;
+ if (btn >= 3) {
+ idx = INT33_EVENT_MASK_4TH_BUTTON_PRESSED_INDEX;
+ btn -= 3;
+ } else {
+ idx = INT33_EVENT_MASK_LEFT_BUTTON_PRESSED_INDEX;
+ }
+ idx += (btn * 2);
+ if (released) idx++;
+ return 1 << idx;
+}
+
/** Calls the application-registered event handler. */
static void call_event_handler(void (__far *handler)(), uint16_t events,
uint16_t buttons, int16_t x, int16_t y,
@@ -574,16 +591,17 @@ static void call_event_handler(void (__far *handler)(), uint16_t events,
* @param absolute whether mouse coordinates are an absolute value
* @param x y if absolute, then absolute coordinates in screen pixels
* if relative, then relative coordinates in mickeys
- * @param z relative wheel mouse movement
+ * @param wheeln wheel number (0 = vertical, 1 = horizontal)
+ * @param z delta movement reported for that wheel (or 0)
*/
-static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, int z)
+static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, char wheeln, int z)
{
uint16_t events = 0;
int i;
#if TRACE_EVENTS
- dprintf("handle mouse event %s buttons=0x%x x=%d y=%d z=%d\n",
- absolute ? "absolute" : "relative", buttons, x, y, z);
+ dprintf("handle mouse event %s buttons=0x%hx x=%d y=%d z%d=%d\n",
+ absolute ? "absolute" : "relative", buttons, x, y, wheeln, z);
#endif
if (absolute) {
@@ -647,9 +665,9 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in
bound_position_to_window();
#if USE_WHEEL
- if (data.haswheel && z) {
- if (!data.usewheelapi && (data.wheel_up_key || data.wheel_down_key)) {
- // Emulate keystrokes on wheel movement
+ if (data.num_wheels && z) {
+ if (wheeln == 0 && !data.usewheelapi && (data.wheel_up_key || data.wheel_down_key)) {
+ // Emulate keystrokes on (vertical) wheel movement
if (z < 0 && data.wheel_up_key) {
for (; z < 0; z++) {
int16_store_keystroke(data.wheel_up_key);
@@ -660,30 +678,31 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in
}
}
} else {
- events |= INT33_EVENT_MASK_WHEEL_MOVEMENT;
+ if (wheeln == 1) events |= INT33_EVENT_MASK_HORIZ_WHEEL_MOVEMENT;
+ else events |= INT33_EVENT_MASK_WHEEL_MOVEMENT;
// Higher byte of buttons contains wheel movement
buttons |= (z & 0xFF) << 8;
// Accumulate delta wheel movement
- data.wheel_delta += z;
- data.wheel_last.x = data.pos.x;
- data.wheel_last.y = data.pos.y;
+ data.wheel[wheeln].delta += z;
+ data.wheel[wheeln].last.x = data.pos.x;
+ data.wheel[wheeln].last.y = data.pos.y;
}
}
#endif
// Update button status
- for (i = 0; i < NUM_BUTTONS; ++i) {
+ for (i = 0; i < data.num_buttons; ++i) {
uint8_t btn = 1 << i;
- uint8_t evt = 0;
+ uint16_t evt = 0;
if ((buttons & btn) && !(data.buttons & btn)) {
// Button pressed
- evt = 1 << (1 + (i * 2)); // Press event mask
+ evt = button_event_mask(i, false);
data.button[i].pressed.count++;
data.button[i].pressed.last.x = data.pos.x;
data.button[i].pressed.last.y = data.pos.y;
} else if (!(buttons & btn) && (data.buttons & btn)) {
// Button released
- evt = 1 << (2 + (i * 2)); // Release event mask
+ evt = button_event_mask(i, true);
data.button[i].released.count++;
data.button[i].released.last.x = data.pos.x;
data.button[i].released.last.y = data.pos.y;
@@ -694,41 +713,78 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in
refresh_cursor();
- events &= data.event_mask;
- if (data.event_handler && events) {
- x = snap_to_grid(data.pos.x, data.screen_granularity.x);
- y = snap_to_grid(data.pos.y, data.screen_granularity.y);
+ if (!data.event_handler) {
+ // No event handler
+ return;
+ }
- call_event_handler(data.event_handler, events,
- buttons, x, y, data.delta.x, data.delta.y);
+ events &= data.event_mask;
+ if (!(events & ~INT33_EVENT_MASK_ABSOLUTE)) {
+ // No event passes the mask
+ return;
}
+
+ x = snap_to_grid(data.pos.x, data.screen_granularity.x);
+ y = snap_to_grid(data.pos.y, data.screen_granularity.y);
+
+ call_event_handler(data.event_handler, events,
+ buttons, x, y, data.delta.x, data.delta.y);
}
static void handle_ps2_packet(void)
{
- unsigned status;
- int x, y, z = 0;
- bool abs = false;
+ unsigned status = data.ps2_packet[0];
- // Decode the PS2 packet...
- status = data.ps2_packet[0];
- x = data.ps2_packet[1];
- y = data.ps2_packet[2];
+ // Decode basic PS/2 packet
+ unsigned buttons = status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3);
+ int x = data.ps2_packet[1], y = data.ps2_packet[2];
-#if USE_WHEEL
- if (data.haswheel) {
- // Sign-extend Z
- z = (int8_t) data.ps2_packet[3];
- }
-#endif
+ // For the extended byte...
+ bool abs = false;
+ int z = 0;
+ char wheeln = 0;
// Sign-extend X, Y as per the status byte
x = status & PS2M_STATUS_X_NEG ? 0xFF00 | x : x;
y = -(status & PS2M_STATUS_Y_NEG ? 0xFF00 | y : y);
+ // Decode extended byte
+#if USE_WHEEL
+ switch (data.device_id) {
+ case PS2M_DEVICE_ID_IMPS2:
+ // ImPS/2. The fourth packet is the (vertical) wheel movement.
+ z = (int8_t) data.ps2_packet[3];
+ break;
+#if USE_IMEX
+ case PS2M_DEVICE_ID_IMEX:
+ case PS2M_DEVICE_ID_IMEX_HORZ:
+ // IntelliMouse Explorer. It can report either:
+ if (data.ps2_packet[3] & PS2M_IMEX_VERTICAL_SCROLL) {
+ // Vertical scrolling, with 6 bits of precision.
+ z = sign_extend(data.ps2_packet[3], 6);
+ } else if (data.ps2_packet[3] & PS2M_IMEX_HORIZONTAL_SCROLL) {
+ // Horizontal scrolling, with 6 bits of precision.
+ z = -sign_extend(data.ps2_packet[3], 6);
+ wheeln = 1;
+ } else {
+ // Or 2 extra buttons (4, 5)
+ if (data.ps2_packet[3] & PS2M_IMEX_BUTTON_4) {
+ buttons |= INT33_BUTTON_MASK_4TH;
+ }
+ if (data.ps2_packet[3] & PS2M_IMEX_BUTTON_5) {
+ buttons |= INT33_BUTTON_MASK_5TH;
+ }
+ // Plus (vertical) scrolling with 4 bits of precision.
+ z = sign_extend(data.ps2_packet[3], 4);
+ }
+ break;
+#endif /* USE_IMEX */
+ }
+#endif /* USE_WHEEL */
+
#if TRACE_PROTO
- dprintf("ps2 packet %x %d %d %d\n", status, x, y, z);
-#endif /* TRACE_EVENTS */
+ dprintf("ps2 decoded packet buttons=%x x=%d y=%d z%d=%d\n", buttons, x, y, wheeln, z);
+#endif /* TRACE_PROTO */
#if USE_VIRTUALBOX
if (data.vbavail) {
@@ -781,6 +837,7 @@ static void handle_ps2_packet(void)
y = scaleu(vmw.y & 0xFFFFU, 0xFFFFU,
MAX(data.max.y, data.screen_max.y));
z = (uint8_t) vmw.z;
+ wheeln = 0; // VMware only supports wheel 0
}
if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_LEFT) {
@@ -798,8 +855,7 @@ static void handle_ps2_packet(void)
}
#endif /* USE_VMWARE */
- handle_mouse_event(status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3),
- abs, x, y, z);
+ handle_mouse_event(buttons, abs, x, y, wheeln, z);
}
/** PS/2 BIOS calls this routine to notify mouse events.
@@ -811,7 +867,7 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui
uint16_t ticks = bda_get_tick_count_lo();
// Are we using the BIOS in 3-packet mode directly?
- if (data.bios_packet_size == PS2M_PACKET_SIZE_PLAIN) {
+ if (data.bios_packet_size == PS2M_PACKET_SIZE_STD) {
// Just forward it to the full packet handler.
data.ps2_packet[0] = word0;
data.ps2_packet[1] = word1;
@@ -825,11 +881,6 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui
// receiving one byte at a time.
// We have to compute synchronization ourselves.
-#if TRACE_PROTO
- dprintf("ps2 callback byte %d/%d = %x\n",
- 1 + data.cur_packet_bytes, data.packet_size, word0 & 0xFF);
-#endif /* TRACE_EVENTS */
-
if (data.cur_packet_bytes &&
ticks >= data.cur_packet_ticks + MAX_PS2_PACKET_DELAY) {
// Assume the start of a new packet
@@ -841,6 +892,11 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui
data.cur_packet_ticks = ticks;
}
+#if TRACE_PROTO
+ dprintf("ps2 callback byte %d/%d = %x\n",
+ 1 + data.cur_packet_bytes, data.packet_size, word0 & 0xFF);
+#endif /* TRACE_PROTO */
+
data.ps2_packet[data.cur_packet_bytes] = word0;
data.cur_packet_bytes++;
@@ -930,20 +986,32 @@ static void reset_mouse_hardware()
ps2m_enable(false);
data.bios_packet_size = PS2M_PACKET_SIZE_STREAMING; // Default to use the BIOS in streaming mode
- data.packet_size = PS2M_PACKET_SIZE_PLAIN;
+ data.packet_size = PS2M_PACKET_SIZE_STD;
data.cur_packet_bytes = 0;
data.cur_packet_ticks = 0;
+ data.num_buttons = MIN(3, MAX_BUTTONS);
+#if USE_WHEEL
+ data.num_wheels = 0;
+ data.usewheelapi = 0;
+#endif
+
+ err = ps2m_get_device_id(&data.device_id);
+ if (err) data.device_id = PS2M_DEVICE_ID_STD;
#if USE_WIN386
if (data.haswin386) {
uint8_t device_id;
- // Normally, win386 does not support anything except PS2M_PACKET_SIZE_PLAIN
- // However, if we detect our special wheelvkd driver is running...
- err = ps2m_get_device_id(&device_id);
- if (err || device_id != PS2M_DEVICE_ID_IMPS2) {
- // Our special driver is not running...
- dputs("Windows running, using plain packet size");
- data.bios_packet_size = PS2M_PACKET_SIZE_PLAIN;
+ // Normally, win386 does not support anything except standard mouse type
+ // and standard packet size. We should not try setting up the BIOS
+ // in streaming mode as some versions of Windows will silently fail to do so.
+
+ // However, our special wheelvkd will allow us to use streaming mode.
+ // wheelvkd signals it is running by using a the imex device IDs even
+ // before the knocking sequence:
+ if (data.device_id == PS2M_DEVICE_ID_STD) {
+ // Our special driver is NOT running...
+ dputs("Windows running, using standard packet size");
+ data.bios_packet_size = PS2M_PACKET_SIZE_STD;
}
}
#endif /* USE_WIN386 */
@@ -951,11 +1019,11 @@ static void reset_mouse_hardware()
// Try to init PS/2 BIOS with desired packet size / streaming mode
err = ps2m_init(data.bios_packet_size);
- if (err && data.bios_packet_size != PS2M_PACKET_SIZE_PLAIN) {
- // However, if there is an error, drop down to plain packet size
+ if (err && data.bios_packet_size != PS2M_PACKET_SIZE_STD) {
+ // However, if there is an error, drop down to std packet size
// Emulators like DOSBox don't support anything but plain packet size
- dputs("BIOS didn't support streaming mode, using plain packet size");
- data.bios_packet_size = PS2M_PACKET_SIZE_PLAIN;
+ dputs("BIOS doesn't support streaming mode, using standard packet size");
+ data.bios_packet_size = PS2M_PACKET_SIZE_STD;
err = ps2m_init(data.bios_packet_size);
}
if (err) {
@@ -965,19 +1033,47 @@ static void reset_mouse_hardware()
#if USE_WHEEL
if (data.usewheel
&& data.bios_packet_size == PS2M_PACKET_SIZE_STREAMING
- && ps2m_detect_wheel()) {
- dputs("PS/2 wheel detected");
- data.haswheel = true;
+ && ps2m_detect_imps2()) {
+ dputs("ImPS/2 detected");
data.packet_size = PS2M_PACKET_SIZE_EXT;
+
+ data.num_wheels = MIN(1, MAX_WHEELS);
+ ps2m_get_device_id(&data.device_id);
+
+#if USE_IMEX
+ // Try to go a bit further
+ if (data.device_id == PS2M_DEVICE_ID_IMPS2) {
+ ps2m_send_imex_sequence();
+
+ ps2m_get_device_id(&data.device_id);
+ if (data.device_id == PS2M_DEVICE_ID_IMEX) {
+ ps2m_send_imex_horz_sequence();
+
+ ps2m_get_device_id(&data.device_id);
+ // According to VirtualBox device ID will not change after this
+ // sequence. We don't need to check for it anyway as our code to
+ // handle both protocols is the same.
+ }
+ }
+
+ if (data.device_id == PS2M_DEVICE_ID_IMEX
+ || data.device_id == PS2M_DEVICE_ID_IMEX_HORZ) {
+ dputs("ImEx detected");
+ data.num_wheels = MIN(2, MAX_WHEELS);
+ data.num_buttons = MIN(5, MAX_BUTTONS);
+ }
+#endif /* USE_IMEX */
+
+ dprintf("found mouse device id = 0x%hx num buttons=%d num wheels=%d\n",
+ data.device_id, data.num_buttons, data.num_wheels);
} else {
if (data.usewheel) dputs("PS/2 wheel NOT detected");
- data.haswheel = false;
}
#if USE_VMWARE
// With the VMware backdoor, we can get the wheel information even if
// we couldn't configure the PS/2 mouse at all.
if (data.vmwavail && data.usewheel) {
- data.haswheel = true;
+ data.num_wheels = 1;
}
#endif /* USE_VMWARE */
#endif /* USE_WHEEL */
@@ -1019,10 +1115,6 @@ static void reset_mouse_settings()
data.cursor_hotspot.y = 0;
memcpy(data.cursor_graphic, default_cursor_graphic, sizeof(data.cursor_graphic));
-#if USE_WHEEL
- data.usewheelapi = false;
-#endif
-
refresh_cursor(); // This will hide the cursor and update data.cursor_visible
}
@@ -1041,7 +1133,7 @@ static void reset_mouse_state()
data.abs_pos.x = -1;
data.abs_pos.y = -1;
data.buttons = 0;
- for (i = 0; i < NUM_BUTTONS; i++) {
+ for (i = 0; i < MAX_BUTTONS; i++) {
data.button[i].pressed.count = 0;
data.button[i].pressed.last.x = 0;
data.button[i].pressed.last.y = 0;
@@ -1049,7 +1141,13 @@ static void reset_mouse_state()
data.button[i].released.last.x = 0;
data.button[i].released.last.y = 0;
}
- data.wheel_delta = 0;
+#if USE_WHEEL
+ for (i = 0; i < MAX_WHEELS; i++) {
+ data.wheel[i].delta = 0;
+ data.wheel[i].last.x = 0;
+ data.wheel[i].last.y = 0;
+ }
+#endif
data.cursor_visible = false;
data.cursor_pos.x = 0;
data.cursor_pos.y = 0;
@@ -1057,14 +1155,16 @@ static void reset_mouse_state()
memset(data.cursor_prev_graphic, 0, sizeof(data.cursor_prev_graphic));
}
+#if USE_WHEEL
/** Return (in the appropiate registers) the wheel movement counter and afterwards reset it. */
-static void return_clear_wheel_counter(union INTPACK __far *r)
+static void return_clear_wheel_counter(union INTPACK __far *r, struct wheelcounter *c)
{
- r->w.cx = snap_to_grid(data.wheel_last.x, data.screen_granularity.x);
- r->w.dx = snap_to_grid(data.wheel_last.y, data.screen_granularity.y);
- r->w.bx = data.wheel_delta;
- data.wheel_delta = 0;
+ r->w.cx = snap_to_grid(c->last.x, data.screen_granularity.x);
+ r->w.dx = snap_to_grid(c->last.y, data.screen_granularity.y);
+ r->w.bx = c->delta;
+ c->delta = 0;
}
+#endif
/** Return (in the appropiate registers) the desired button press counter and afterwards reset it. */
static void return_clear_button_counter(union INTPACK __far *r, struct buttoncounter *c)
@@ -1101,7 +1201,7 @@ static void int33_handler(union INTPACK r)
reset_mouse_hardware();
reset_mouse_state();
r.w.ax = INT33_MOUSE_FOUND;
- r.w.bx = NUM_BUTTONS;
+ r.w.bx = data.num_buttons;
break;
case INT33_SHOW_CURSOR:
if (data.hidden_count > 0) data.hidden_count--;
@@ -1125,9 +1225,13 @@ static void int33_handler(union INTPACK r)
r.w.dx = snap_to_grid(data.pos.y, data.screen_granularity.y);
r.w.bx = data.buttons;
#if USE_WHEEL
- if (data.haswheel) {
- r.h.bh = data.wheel_delta;
- data.wheel_delta = 0;
+ if (data.num_wheels > 0) {
+ r.h.bh = data.wheel[0].delta;
+ data.wheel[0].delta = 0;
+ if (data.num_wheels > 1) {
+ r.h.ah = data.wheel[1].delta;
+ data.wheel[1].delta = 0;
+ }
}
#endif
break;
@@ -1151,18 +1255,19 @@ static void int33_handler(union INTPACK r)
#endif
r.w.ax = data.buttons;
#if USE_WHEEL
- if (data.haswheel) {
- r.h.bh = data.wheel_delta;
- if (r.w.bx == -1) {
+ if (data.num_wheels > 0) {
+ int n = -(int16_t)(r.w.bx);
+ r.h.ah = data.wheel[0].delta;
+ if (n >= 0 && n < MAX_WHEELS) {
// Asked for wheel information
- return_clear_wheel_counter(&r);
+ return_clear_wheel_counter(&r, &data.wheel[n]);
break;
}
}
#endif
// Regular button information
return_clear_button_counter(&r,
- &data.button[MIN(r.w.bx, NUM_BUTTONS - 1)].pressed);
+ &data.button[MIN(r.w.bx, MAX_BUTTONS - 1)].pressed);
break;
case INT33_GET_BUTTON_RELEASED_COUNTER:
#if TRACE_CALLS
@@ -1170,17 +1275,18 @@ static void int33_handler(union INTPACK r)
#endif
r.w.ax = data.buttons;
#if USE_WHEEL
- if (data.haswheel) {
- r.h.bh = data.wheel_delta;
- if (r.w.bx == -1) {
+ if (data.num_wheels > 0) {
+ int n = -(int16_t)(r.w.bx);
+ r.h.ah = data.wheel[0].delta;
+ if (n >= 0 && n < MAX_WHEELS) {
// Asked for wheel information
- return_clear_wheel_counter(&r);
+ return_clear_wheel_counter(&r, &data.wheel[n]);
break;
}
}
#endif
return_clear_button_counter(&r,
- &data.button[MIN(r.w.bx, NUM_BUTTONS - 1)].released);
+ &data.button[MIN(r.w.bx, MAX_BUTTONS - 1)].released);
break;
case INT33_SET_HORIZONTAL_WINDOW:
dprintf("Mouse set horizontal window [%d,%d]\n", r.w.cx, r.w.dx);
@@ -1224,7 +1330,7 @@ static void int33_handler(union INTPACK r)
data.delta.y = 0;
break;
case INT33_SET_EVENT_HANDLER:
- dputs("Mouse set event handler");
+ dprintf("Mouse set event handler mask=0x%x\n", r.w.cx);
data.event_mask = r.w.cx;
data.event_handler = MK_FP(r.w.es, r.w.dx);
break;
@@ -1287,7 +1393,7 @@ static void int33_handler(union INTPACK r)
}
reset_mouse_state();
r.w.ax = INT33_MOUSE_FOUND;
- r.w.bx = NUM_BUTTONS;
+ r.w.bx = data.num_buttons;
break;
case INT33_GET_LANGUAGE:
r.w.bx = 0;
@@ -1316,8 +1422,15 @@ static void int33_handler(union INTPACK r)
dputs("Mouse get capabitilies");
r.w.ax = INT33_WHEEL_API_MAGIC; // Driver supports wheel API
r.w.bx = 0;
- r.w.cx = data.haswheel ? INT33_CAPABILITY_MOUSE_API : 0;
- data.usewheelapi = true; // Someone calling this function likely wants to use wheel API
+ r.w.cx = 0;
+ if (data.num_wheels > 0) {
+ r.w.cx |= INT33_CAPABILITY_WHEEL_API;
+ if (data.num_wheels > 1) {
+ r.w.cx |= INT33_CAPABILITY_WHEEL2_API;
+ }
+ }
+ dprintf(" Returning capabilities=0x%x\n", r.w.cx);
+ data.usewheelapi = true; // Someone calling this function wants to use wheel API
break;
#endif
// Our internal API extensions:
@@ -1366,7 +1479,7 @@ static void windows_mouse_handler(int action, int x, int y, int buttons, int eve
case VMD_ACTION_MOUSE_EVENT:
(void) events;
// Forward event to our internal system
- handle_mouse_event(buttons, true, x, y, 0);
+ handle_mouse_event(buttons, true, x, y, 0, 0);
break;
case VMD_ACTION_HIDE_CURSOR:
dputs("VMD_ACTION_HIDE_CURSOR");
diff --git a/mousetsr.h b/mousetsr.h
index 43fccc1..5625ae1 100644
--- a/mousetsr.h
+++ b/mousetsr.h
@@ -34,8 +34,10 @@
#define USE_VMWARE 1
/** Enable Windows 386/protected mode integration .*/
#define USE_WIN386 1
-/** Enable the wheel. */
+/** Enable the wheel (and ImPS/2 protocol). */
#define USE_WHEEL 1
+/** Enable Intellimouse Explorer protocol (two wheels and 5 buttons). */
+#define USE_IMEX 1
/** Trace mouse events verbosily. */
#define TRACE_EVENTS 0
/** Trace (noisy) API calls. */
@@ -57,8 +59,17 @@
/** Maximum number of 55ms ticks that may pass between two bytes of the same PS/2 packet */
#define MAX_PS2_PACKET_DELAY 2
-/** Number of buttons reported back to user programs. */
-#define NUM_BUTTONS 3
+/** Maximum number of buttons supported by this driver. */
+#define MAX_BUTTONS 5
+
+/** Maximum number of wheels supported. */
+#if USE_IMEX
+#define MAX_WHEELS 2
+#elif USE_WHEEL
+#define MAX_WHEELS 1
+#else
+#define MAX_WHEELS 0
+#endif
/** Size of int33 graphic cursor shape definitions. */
#define GRAPHIC_CURSOR_WIDTH 16
@@ -110,9 +121,13 @@ typedef struct tsrdata {
struct point screen_granularity;
// Detected mouse hardware & status
+ /** Current negotiated PS/2 device_id */
+ uint8_t device_id;
+ /** Number of buttons of current device. */
+ uint8_t num_buttons;
#if USE_WHEEL
- /** Whether the current mouse has a wheel (and support is enabled). */
- bool haswheel;
+ /** Number of wheels of current device. */
+ uint8_t num_wheels;
#endif
/** Packet size that the BIOS is currently using. Either 1 (streaming) or 3 (plain). */
uint8_t bios_packet_size;
@@ -168,18 +183,23 @@ typedef struct tsrdata {
/** Ticks when the above value was last reset. */
uint16_t last_motion_ticks;
/** Current status of buttons (as bitfield). */
- uint16_t buttons;
+ uint8_t buttons;
struct {
struct buttoncounter {
+ /** Last position of cursor where this button was pressed. */
struct point last;
+ /** Number of button presses since last button report. */
uint16_t count;
} pressed, released;
- } button[NUM_BUTTONS];
- /** Total delta movement of the wheel since the last wheel report. */
- int16_t wheel_delta;
- /** Last position where the wheel was moved. */
- struct point wheel_last;
-
+ } button[MAX_BUTTONS];
+#if USE_WHEEL
+ struct wheelcounter {
+ /** Total delta movement of the wheel since the last wheel report. */
+ int16_t delta;
+ /** Last position where the wheel was moved. */
+ struct point last;
+ } wheel[MAX_WHEELS];
+#endif
// Cursor information
/** Whether the cursor is currently displayed or not. */
bool cursor_visible;
diff --git a/mousew16.c b/mousew16.c
index fd25609..78f2315 100644
--- a/mousew16.c
+++ b/mousew16.c
@@ -18,7 +18,6 @@
*/
#include <string.h>
-#include <limits.h>
#include <windows.h>
#include "utils.h"
@@ -36,7 +35,7 @@
#define TRACE_EVENTS 0
/** Verbosely trace scroll wheel code. */
#define TRACE_WHEEL 0
-/** Number of lines to scroll per wheel event. */
+/** Number of lines to scroll per (vertical) wheel event. */
#define WHEEL_SCROLL_LINES 2
/** Windows 3.x only supports 1-2 mouse buttons anyway. */
@@ -118,7 +117,7 @@ static void print_window_name(HWND hWnd)
#endif
/** Helper function to traverse a window hierarchy and find a candidate scrollbar. */
-static BOOL CALLBACK __loadds find_scrollbar(HWND hWnd, LPARAM lParam)
+static BOOL CALLBACK __loadds find_scrollbar_enum_proc(HWND hWnd, LPARAM lParam)
{
LPFINDSCROLLBARDATA data = (LPFINDSCROLLBARDATA) lParam;
char buffer[16];
@@ -150,6 +149,22 @@ static BOOL CALLBACK __loadds find_scrollbar(HWND hWnd, LPARAM lParam)
return ENUM_CHILD_WINDOW_CONTINUE;
}
+/** Finds a scrollbar in the given window. */
+static HWND find_scrollbar(HWND hWnd, BOOL vertical)
+{
+ FINDSCROLLBARDATA data;
+ data.vertical = vertical;
+ data.scrollbar = 0;
+
+#if TRACE_WHEEL
+ dprintf(DPREFIX "find scrollbar on hWnd=0x%x...\n", hWnd);
+#endif
+
+ userapi.EnumChildWindows(hWnd, find_scrollbar_enum_proc, (LONG) (LPVOID) &data);
+
+ return data.scrollbar;
+}
+
/** Send scrolling messages to given window.
* @param hWnd window to scroll.
* @param vertical true if vertical, false if horizontal.
@@ -161,10 +176,12 @@ static void post_scroll_msg(HWND hWnd, BOOL vertical, int z, HWND hScrollBar)
UINT msg = vertical ? WM_VSCROLL : WM_HSCROLL;
WPARAM wParam = z < 0 ? SB_LINEUP : SB_LINEDOWN;
LPARAM lParam = MAKELPARAM(0, hScrollBar);
- UINT i, lines = (z < 0 ? -z : z) * WHEEL_SCROLL_LINES;
+ UINT i, lines = (z < 0 ? -z : z);
+
+ if (!vertical) lines *= WHEEL_SCROLL_LINES;
#if TRACE_WHEEL
- dprintf("w16mouse: sending scroll msg to hWnd=0x%x from=0x%x vert=%d lines=%u\n", hWnd, hScrollBar, vertical, lines);
+ dprintf(DPREFIX "sending scroll msg to hWnd=0x%x from=0x%x vert=%d lines=%u\n", hWnd, hScrollBar, vertical, lines);
#endif
for (i = 0; i < lines; i++) {
@@ -174,13 +191,13 @@ static void post_scroll_msg(HWND hWnd, BOOL vertical, int z, HWND hScrollBar)
}
/** Send wheel scrolling events to the most likely candidate window. */
-static void send_wheel_movement(int8_t z)
+static void send_wheel_movement(int8_t z, BOOL vertical)
{
POINT point;
HWND hWnd;
#if TRACE_WHEEL
- dprintf("w16mouse: wheel=%d\n", z);
+ dprintf(DPREFIX "wheel=%d\n", z);
#endif
// TODO It's highly unlikely that we can call this many functions from
@@ -192,7 +209,7 @@ static void send_wheel_movement(int8_t z)
hWnd = userapi.WindowFromPoint(point);
#if TRACE_WHEEL
- dprintf("w16mouse: initial hWnd=0x%x\n", hWnd);
+ dprintf(DPREFIX "initial hWnd=0x%x\n", hWnd);
#endif
while (hWnd) {
@@ -203,54 +220,55 @@ static void send_wheel_movement(int8_t z)
dprintf(DPREFIX "hWnd=0x%x style=0x%lx\n", hWnd, style);
#endif
- if (style & WS_VSCROLL) {
-#if TRACE_WHEEL
- dprintf(DPREFIX "found WS_VSCROLL\n");
-#endif
- post_scroll_msg(hWnd, TRUE, z, 0);
- break;
- } else if (style & WS_HSCROLL) {
-#if TRACE_WHEEL
- dprintf(DPREFIX "found WS_HSCROLL\n");
-#endif
- post_scroll_msg(hWnd, FALSE, z, 0);
- break;
- } else {
- FINDSCROLLBARDATA data;
-
- // Let's check if we can find a vertical scroll bar in this window..
-#if TRACE_WHEEL
- dprintf(DPREFIX "find vertical scrollbar...\n");
-#endif
- data.vertical = TRUE;
- data.scrollbar = 0;
- userapi.EnumChildWindows(hWnd, find_scrollbar, (LONG) (LPVOID) &data);
- if (data.scrollbar) {
- post_scroll_msg(hWnd, TRUE, z, data.scrollbar);
+ if (vertical) {
+ // Check if this window is scrollable...
+ if (style & WS_VSCROLL) {
+ post_scroll_msg(hWnd, TRUE, z, 0);
break;
- }
-
- // Try a horizontal scrollbar now
-#if TRACE_WHEEL
- dprintf(DPREFIX "find horizontal scrollbar...\n");
-#endif
- data.vertical = FALSE;
- data.scrollbar = 0;
- userapi.EnumChildWindows(hWnd, find_scrollbar, (LONG) (LPVOID) &data);
- if (data.scrollbar) {
- post_scroll_msg(hWnd, FALSE, z, data.scrollbar);
+ } else if (style & WS_HSCROLL) {
+ post_scroll_msg(hWnd, FALSE, z, 0);
break;
- }
-
- // Otherwise, try again on the parent window
- if (style & WS_CHILD) {
-#if TRACE_WHEEL
- dprintf(DPREFIX "go into parent...\n");
-#endif
- hWnd = userapi.GetParent(hWnd);
} else {
- // This was already a topmost window
+ // Otherwise, let's see if we can find a vertical scroll bar in this window..
+ HWND scrollbar = find_scrollbar(hWnd, TRUE);
+ if (scrollbar) {
+ post_scroll_msg(hWnd, TRUE, z, scrollbar);
+ break;
+ }
+
+ // Try a horizontal scrollbar second
+ scrollbar = find_scrollbar(hWnd, FALSE);
+ if (scrollbar) {
+ post_scroll_msg(hWnd, FALSE, z, scrollbar);
+ break;
+ }
+
+ // Otherwise, continue search on the parent window
+ if (style & WS_CHILD) {
+ hWnd = userapi.GetParent(hWnd);
+ } else {
+ // This was already a topmost window
+ break;
+ }
+ }
+ } else {
+ // Similar to above except we try only horizontal scrollbars
+ if (style & WS_HSCROLL) {
+ post_scroll_msg(hWnd, FALSE, z, 0);
break;
+ } else {
+ HWND scrollbar = find_scrollbar(hWnd, FALSE);
+ if (scrollbar) {
+ post_scroll_msg(hWnd, FALSE, z, scrollbar);
+ break;
+ }
+
+ if (style & WS_CHILD) {
+ hWnd = userapi.GetParent(hWnd);
+ } else {
+ // This was already a topmost window
+ break;
+ }
}
}
}
@@ -282,11 +300,12 @@ static void FAR int33_mouse_callback(uint16_t events, uint16_t buttons, int16_t
}
#if USE_WHEEL
- if (flags.wheel && (events & INT33_EVENT_MASK_WHEEL_MOVEMENT)) {
+ if (flags.wheel && (events & INT33_EVENT_MASK_ANY_WHEEL_MOVEMENT)) {
// If wheel API is enabled, higher byte of buttons contains wheel movement
int8_t z = (buttons & 0xFF00) >> 8;
+ BOOL vertical = !(events & INT33_EVENT_MASK_HORIZ_WHEEL_MOVEMENT);
if (z) {
- send_wheel_movement(z);
+ send_wheel_movement(z, vertical);
}
}
#endif
@@ -455,7 +474,7 @@ BOOL FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment,
/** Called by Windows to retrieve information about the mouse hardware. */
WORD FAR PASCAL Inquire(LPMOUSEINFO lpMouseInfo)
{
- lpMouseInfo->msExist = 1;
+ lpMouseInfo->msExist = TRUE;
lpMouseInfo->msRelative = 0;
lpMouseInfo->msNumButtons = MOUSE_NUM_BUTTONS;
lpMouseInfo->msRate = 80;
diff --git a/mousmain.c b/mousmain.c
index f457adf..e33b553 100644
--- a/mousmain.c
+++ b/mousmain.c
@@ -36,12 +36,21 @@
static nl_catd cat;
#if USE_WHEEL
-static void detect_wheel(LPTSRDATA data)
+static bool detect_wheel(LPTSRDATA data)
{
// Do a quick check for a mouse wheel here.
// The TSR will do its own check when it is reset anyway
- if (data->haswheel = ps2m_detect_wheel()) {
+ if (ps2m_detect_imps2()) {
+ if (ps2m_detect_imex()) {
+ data->num_wheels = 2;
+ } else {
+ data->num_wheels = 1;
+ }
printf(_(1, 0, "Wheel mouse found and enabled\n"));
+ return true;
+ } else {
+ data->num_wheels = 0;
+ return false;
}
}
@@ -51,12 +60,12 @@ static int set_wheel(LPTSRDATA data, bool enable)
data->usewheel = enable;
if (data->usewheel) {
- detect_wheel(data);
- if (!data->haswheel) {
+ if (!detect_wheel(data)) {
fprintf(stderr, _(3, 0, "Could not find PS/2 wheel mouse\n"));
}
} else {
- data->haswheel = false;
+ // Force num_wheels to 0 even before the next mouse reset
+ data->num_wheels = 0;
}
return 0;
@@ -64,7 +73,7 @@ static int set_wheel(LPTSRDATA data, bool enable)
static int set_wheel_key(LPTSRDATA data, const char *keyname)
{
- if (!data->usewheel || !data->haswheel) {
+ if (!data->usewheel) {
fprintf(stderr, _(3, 1, "Wheel not detected or support not enabled\n"));
return EXIT_FAILURE;
}
@@ -249,7 +258,7 @@ static int configure_driver(LPTSRDATA data)
dlog_init();
// Check for PS/2 mouse BIOS availability
- if ((err = ps2m_init(PS2M_PACKET_SIZE_PLAIN))) {
+ if ((err = ps2m_init(PS2M_PACKET_SIZE_STD))) {
fprintf(stderr, _(3, 8, "Cannot init PS/2 mouse BIOS, err=%d\n"), err);
// Can't do anything without PS/2
return err;
@@ -260,7 +269,7 @@ static int configure_driver(LPTSRDATA data)
data->usewheel = true;
data->wheel_up_key = 0;
data->wheel_down_key = 0;
- detect_wheel(data);
+ detect_wheel(data); // Prints message if wheel found
#endif
#if USE_INTEGRATION
diff --git a/utils.h b/utils.h
index 715294e..9b9db2a 100644
--- a/utils.h
+++ b/utils.h
@@ -92,4 +92,11 @@ static int scalei_rem(int x, int srcmax, int dstmax, short *rem);
__value [ax] \
__modify [ax cx dx si]
+/** Sign extend x from b bits to 16. */
+static inline int16_t sign_extend(int16_t x, int b)
+{
+ int m = 16 - b;
+ return (x << m) >> m;
+}
+
#endif
diff --git a/version.h b/version.h
index abbbca7..b756585 100644
--- a/version.h
+++ b/version.h
@@ -2,6 +2,6 @@
#define VERSION_H
#define VERSION_MAJOR 0
-#define VERSION_MINOR 0x68
+#define VERSION_MINOR 0x70
#endif // VERSION_H