aboutsummaryrefslogtreecommitdiff
path: root/mousetsr.c
diff options
context:
space:
mode:
Diffstat (limited to 'mousetsr.c')
-rw-r--r--mousetsr.c625
1 files changed, 491 insertions, 134 deletions
diff --git a/mousetsr.c b/mousetsr.c
index 8c42057..0965b6b 100644
--- a/mousetsr.c
+++ b/mousetsr.c
@@ -27,6 +27,9 @@
#include "int16kbd.h"
#include "int2fwin.h"
#include "int33.h"
+#include "pic.h"
+#include "serial.h"
+#include "sermouse.h"
#include "vbox.h"
#include "vmware.h"
#include "mousetsr.h"
@@ -35,6 +38,8 @@
TSRDATA data;
+static const char version_string[] = "VBADOS";
+
static const uint16_t default_cursor_graphic[] = {
0x3FFF, 0x1FFF, 0x0FFF, 0x07FF,
0x03FF, 0x01FF, 0x00FF, 0x007F,
@@ -537,6 +542,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,43 +554,52 @@ static void refresh_video_info(void)
}
}
-/** 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,
- int16_t delta_x, int16_t delta_y)
+/** Obtains INT33_EVENT_MASK bitmask corresponding to btn. */
+static uint16_t button_event_mask(int btn, bool released)
{
-#if TRACE_EVENTS
- dprintf("calling event handler events=0x%x buttons=0x%x x=%d y=%d dx=%d dy=%d\n",
- events, buttons, x, y, delta_x, delta_y);
-#endif
-
- __asm {
- mov ax, [events]
- mov bx, [buttons]
- mov cx, [x]
- mov dx, [y]
- mov si, [delta_x]
- mov di, [delta_y]
-
- call dword ptr [handler]
+ 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(uint16_t events, uint16_t buttons,
+ int16_t x, int16_t y,
+ int16_t delta_x, int16_t delta_y,
+ void (__far *handler)());
+#pragma aux call_event_handler = \
+ "push bp" \
+ "mov bp, sp" \
+ "push ds" \
+ "call dword ptr 0x2[bp]" \
+ "pop ds" \
+ "pop bp" \
+ __parm __caller [ax] [bx] [cx] [dx] [si] [di] [] \
+ __modify [es fs gs]
+
/** Process a mouse event internally.
* @param buttons currently pressed buttons as a bitfield
* @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,43 +663,44 @@ 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)) {
+ if (data.num_wheels && z) {
+ if (!data.usewheelapi && (data.wheel_key[wheeln][WHEEL_DIR_UP] || data.wheel_key[wheeln][WHEEL_DIR_DOWN])) {
// Emulate keystrokes on wheel movement
- if (z < 0 && data.wheel_up_key) {
+ if (z < 0 && data.wheel_key[wheeln][WHEEL_DIR_UP]) {
for (; z < 0; z++) {
- int16_store_keystroke(data.wheel_up_key);
+ int16_store_keystroke(data.wheel_key[wheeln][WHEEL_DIR_UP], 0);
}
- } else if (z > 0 && data.wheel_down_key) {
+ } else if (z > 0 && data.wheel_key[wheeln][WHEEL_DIR_DOWN]) {
for (; z > 0; z--) {
- int16_store_keystroke(data.wheel_down_key);
+ int16_store_keystroke(data.wheel_key[wheeln][WHEEL_DIR_DOWN], 0);
}
}
} 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 +711,90 @@ 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);
+
+#if TRACE_EVENTS
+ dprintf("calling user event handler events=0x%x buttons=0x%x x=%d y=%d dx=%d dy=%d\n",
+ events, buttons, x, y, data.delta.x, data.delta.y);
+#endif
+
+ call_event_handler(events, buttons, x, y, data.delta.x, data.delta.y,
+ data.event_handler);
+
+#if TRACE_EVENTS
+ dputs("return from user event handler");
+#endif
}
static void handle_ps2_packet(void)
{
- unsigned status;
- int x, y, z = 0;
- bool abs = false;
+ unsigned status = data.cur_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.cur_packet[1], y = data.cur_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.cur_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.cur_packet[3] & PS2M_IMEX_VERTICAL_SCROLL) {
+ // Vertical scrolling, with 6 bits of precision.
+ z = sign_extend(data.cur_packet[3], 6);
+ // Assume 4th/5th buttons are still pressed if they were
+ buttons |= data.buttons & (INT33_BUTTON_MASK_4TH|INT33_BUTTON_MASK_5TH);
+ } else if (data.cur_packet[3] & PS2M_IMEX_HORIZONTAL_SCROLL) {
+ // Horizontal scrolling, with 6 bits of precision.
+ z = sign_extend(data.cur_packet[3], 6);
+ wheeln = 1;
+ buttons |= data.buttons & (INT33_BUTTON_MASK_4TH|INT33_BUTTON_MASK_5TH);
+ } else {
+ // Or 2 extra buttons (4, 5)
+ if (data.cur_packet[3] & PS2M_IMEX_BUTTON_4) {
+ buttons |= INT33_BUTTON_MASK_4TH;
+ }
+ if (data.cur_packet[3] & PS2M_IMEX_BUTTON_5) {
+ buttons |= INT33_BUTTON_MASK_5TH;
+ }
+ // Plus (vertical) scrolling with 4 bits of precision.
+ z = sign_extend(data.cur_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,16 +847,17 @@ 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) {
- status |= PS2M_STATUS_BUTTON_1;
+ buttons |= INT33_BUTTON_MASK_LEFT;
}
if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_RIGHT) {
- status |= PS2M_STATUS_BUTTON_2;
+ buttons |= INT33_BUTTON_MASK_RIGHT;
}
if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_MIDDLE) {
- status |= PS2M_STATUS_BUTTON_3;
+ buttons |= INT33_BUTTON_MASK_CENTER;
}
} else {
return; // Ignore the PS/2 packet otherwise, it is likely garbage
@@ -798,8 +865,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,11 +877,11 @@ 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;
- data.ps2_packet[2] = word2;
+ data.cur_packet[0] = word0;
+ data.cur_packet[1] = word1;
+ data.cur_packet[2] = word2;
(void) word3;
handle_ps2_packet();
return;
@@ -825,23 +891,23 @@ 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) {
+ ticks >= data.cur_packet_ticks + MAX_PACKET_DELAY) {
// Assume the start of a new packet
- dprintf("dropping packet! prev_ticks=%u new_ticks=%u\n",
- data.cur_packet_ticks, ticks);
+ dprintf("dropping packet! cur_bytes=%u prev_ticks=%u new_ticks=%u\n",
+ data.cur_packet_bytes, data.cur_packet_ticks, ticks);
data.cur_packet_bytes = 0;
}
if (data.cur_packet_bytes == 0) {
data.cur_packet_ticks = ticks;
}
- data.ps2_packet[data.cur_packet_bytes] = word0;
+#if TRACE_PROTO
+ dprintf("ps2 callback byte %d/%d = %hx\n",
+ 1 + data.cur_packet_bytes, data.packet_size, word0 & 0xFF);
+#endif /* TRACE_PROTO */
+
+ data.cur_packet[data.cur_packet_bytes] = word0;
data.cur_packet_bytes++;
if (data.cur_packet_bytes >= data.packet_size) {
@@ -922,28 +988,41 @@ static void set_absolute(bool enable)
}
#endif /* USE_INTEGRATION */
-static void reset_mouse_hardware()
+static bool reset_ps2_mouse()
{
int err;
// Stop receiving bytes...
ps2m_enable(false);
+ data.port = 0;
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,33 +1030,61 @@ 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) {
- dputs("error on ps2m_init during reset, ignoring");
+ dputs("error on ps2m_init during reset");
+ return false;
}
#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) {
+ 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 */
@@ -996,6 +1103,56 @@ static void reset_mouse_hardware()
#endif
ps2m_enable(true);
+ return true;
+}
+
+#if USE_SERIAL
+static bool reset_serial_mouse()
+{
+ // TODO: Currently this is not power-cycling nor redetecting the mouse
+ unsigned iobase = data.port_io;
+
+ data.cur_packet_bytes = 0;
+ data.cur_packet_ticks = 0;
+
+ while (serial_data_ready(iobase)) {
+ uint8_t byte = serial_read_data(iobase);
+ dprintf("got byte from serial %hx '%c'\n", byte, byte);
+ }
+
+ // First, reconfigure the serial port to our liking
+ serial_configure_line(iobase, SERIAL_DIVISOR_1200, SERIAL_LCR_WL_7|SERIAL_LCR_PAR_NONE);
+ // Keep the mouse powered on
+ serial_set_modem_control(iobase, SERIAL_MCR_DTR | SERIAL_MCR_RTS);
+ // Reset and enable the FIFO
+ serial_set_fifo_control(iobase, SERIAL_FCR_FIFO_ENABLE|SERIAL_FCR_FIFO_RESET);
+ // And finally enable interrupts
+ serial_configure_interrupts(iobase, SERIAL_IER_ERBFI);
+ pic_unmask_irq(data.port_irq);
+
+ dputs("Serial mouse reset done");
+
+ return true;
+}
+#endif
+
+static bool reset_mouse_hardware()
+{
+ dputs("Reset mouse hardware");
+
+ // I don't want to search for mouse again.
+ // So this won't switch from serial to PS/2.
+ // For that, reload the mouse driver.
+
+ if (data.port == 0) {
+ return reset_ps2_mouse();
+ } else {
+#if USE_SERIAL
+ return reset_serial_mouse();
+#else
+ return false;
+#endif
+ }
}
/** Reset "software" mouse settings, i.e. those configurable by the client program. */
@@ -1019,10 +1176,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
}
@@ -1030,8 +1183,8 @@ static void reset_mouse_settings()
static void reset_mouse_state()
{
int i;
- data.pos.x = data.min.x;
- data.pos.y = data.min.y;
+ data.pos.x = data.min.x + (data.max.x - data.min.x) / 2;
+ data.pos.y = data.min.y + (data.max.y - data.min.y) / 2;
data.pos_frac.x = 0;
data.pos_frac.y = 0;
data.delta.x = 0;
@@ -1041,7 +1194,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 +1202,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 +1216,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)
@@ -1098,10 +1259,14 @@ static void int33_handler(union INTPACK r)
dputs("Mouse reset");
reload_video_info();
reset_mouse_settings();
- reset_mouse_hardware();
reset_mouse_state();
- r.w.ax = INT33_MOUSE_FOUND;
- r.w.bx = NUM_BUTTONS;
+ if (reset_mouse_hardware()) {
+ r.w.ax = INT33_MOUSE_FOUND;
+ r.w.bx = data.num_buttons;
+ } else {
+ r.w.ax = 0;
+ r.w.bx = 0;
+ }
break;
case INT33_SHOW_CURSOR:
if (data.hidden_count > 0) data.hidden_count--;
@@ -1125,9 +1290,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 +1320,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 +1340,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 +1395,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;
@@ -1281,13 +1452,16 @@ static void int33_handler(union INTPACK r)
dputs("Mouse reset settings");
reload_video_info();
reset_mouse_settings();
+ reset_mouse_state();
if (!data.bios_packet_size || !data.packet_size) {
// Someone is calling this without calling reset first
- reset_mouse_hardware();
+ if (!reset_mouse_hardware()) {
+ r.w.ax = 0;
+ break;
+ }
}
- 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;
@@ -1296,8 +1470,10 @@ static void int33_handler(union INTPACK r)
dputs("Mouse get driver info");
r.h.bh = REPORTED_VERSION_MAJOR;
r.h.bl = REPORTED_VERSION_MINOR;
- r.h.ch = INT33_MOUSE_TYPE_PS2;
- r.h.cl = 0;
+ // TODO Seems that windows may use this, albeit only when the following has MSB set?
+ r.h.ch = data.port == 0 ? INT33_MOUSE_TYPE_PS2 : INT33_MOUSE_TYPE_SERIAL;
+ r.h.cl = data.port_irq;
+ r.h.al = data.port;
break;
case INT33_GET_MAX_COORDINATES:
r.w.bx = 0;
@@ -1310,14 +1486,26 @@ static void int33_handler(union INTPACK r)
r.w.cx = data.max.x;
r.w.dx = data.max.y;
break;
+ case INT33_GET_VERSION_STRING:
+ dputs("Mouse get version string");
+ r.x.es = FP_SEG(&version_string);
+ r.x.di = FP_OFF(&version_string);
+ break;
#if USE_WHEEL
// Wheel API extensions:
case INT33_GET_CAPABILITIES:
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:
@@ -1357,6 +1545,169 @@ void __declspec(naked) __far int33_isr(void)
}
}
+#if USE_SERIAL
+static void handle_serial_m_packet()
+{
+ int x = (int8_t) ((data.cur_packet[1] & SERMOUSE_MS_X_LO_MASK)
+ | ((data.cur_packet[0] & SERMOUSE_MS_X_HI_MASK) << SERMOUSE_MS_X_HI_SHIFT));
+ int y = (int8_t) ((data.cur_packet[2] & SERMOUSE_MS_Y_LO_MASK)
+ | ((data.cur_packet[0] & SERMOUSE_MS_Y_HI_MASK) << SERMOUSE_MS_Y_HI_SHIFT));
+ unsigned buttons = 0;
+ bool abs = false;
+ char wheeln = 0;
+ int z = 0;
+
+ if (data.cur_packet[0] & SERMOUSE_MS_BUTTON_LEFT)
+ buttons |= INT33_BUTTON_MASK_LEFT;
+ if (data.cur_packet[0] & SERMOUSE_MS_BUTTON_RIGHT)
+ buttons |= INT33_BUTTON_MASK_RIGHT;
+ if (data.num_buttons >= 3 && (data.buttons & INT33_BUTTON_MASK_CENTER))
+ buttons |= INT33_BUTTON_MASK_CENTER; // preserve status of mid button for now
+
+#if TRACE_PROTO
+ dprintf("serial decoded M packet buttons=%x x=%d y=%d\n", buttons, x, y);
+#endif /* TRACE_PROTO */
+
+ handle_mouse_event(buttons, abs, x, y, wheeln, z);
+}
+
+static void handle_serial_m3_packet()
+{
+ // No need to send mouse movement as it will have been processed
+ // back when we received the 3rd byte.
+ // Instead focus on wheel and mid mouse button which come in the 4rd byte.
+ int x = 0;
+ int y = 0;
+ // Preserve old button state for most part
+ unsigned buttons = data.buttons & (INT33_BUTTON_MASK_LEFT|INT33_BUTTON_MASK_RIGHT);
+ bool abs = false;
+ char wheeln = 0; // only one wheel supported for now
+ int z = sign_extend(data.cur_packet[3] & SERMOUSE_MS_Z_LO_MASK, 4);
+
+ if (data.cur_packet[3] & SERMOUSE_MS_BUTTON_CENTER) {
+ buttons |= INT33_BUTTON_MASK_CENTER;
+ }
+
+#if TRACE_PROTO
+ dprintf("serial decoded M3 packet buttons=%x z=%d\n", buttons, z);
+#endif /* TRACE_PROTO */
+
+ // Only send 2nd event if really necessary
+ if (buttons != data.buttons || z) {
+ handle_mouse_event(buttons, abs, x, y, wheeln, z);
+ }
+}
+
+static void handle_serial_data(uint8_t byte)
+{
+ // TODO: not checking cur_packet_ticks for now, don't think it necessary for serial
+ // due to different synchronization
+ // Depending on the serial protocol now...
+ if (data.device_id == SERMOUSE_DEVICE_ID_MS) {
+ // All these protocols use bit #6 (MSB since we use 7 data bits) as Start-of-Frame indicator
+ if (byte & (1 << 6)) {
+ if (data.buttons & INT33_BUTTON_MASK_CENTER && data.cur_packet_bytes == 3) {
+ // Was the previous packet a 3byte one and the middle button is down?
+ // Assume the mouse just skipped the 4th byte and release the middle button
+ // TODO I am not sure if this hack is required
+#if TRACE_PROTO
+ dputs("serial releasing mid button");
+#endif /* TRACE_PROTO */
+ handle_mouse_event(data.buttons & ~INT33_BUTTON_MASK_CENTER, false, 0, 0, 0, 0);
+ }
+
+ // Prepare for new packet
+ data.cur_packet_bytes = 0;
+ }
+
+#if TRACE_PROTO
+ dprintf("serial byte %d/%d = %hx\n",
+ 1 + data.cur_packet_bytes, data.packet_size, byte);
+#endif /* TRACE_PROTO */
+
+ data.cur_packet[data.cur_packet_bytes] = byte;
+ data.cur_packet_bytes++;
+
+ // However there is no End-of-Frame indicator.
+ // We do not know if this is going to be a 3 byte or 4 byte packet
+ // (apparently 4-byte packet mouses may send up a 3-byte packet
+ // if no wheel or extra buttons are modified)
+ // So we will send a "main" mouse event when we receive the 3rd byte
+ // and then follow up with a second mouse event if we receive the 4rd.
+
+ if (data.cur_packet_bytes == 3) {
+ // send main mouse event now
+ handle_serial_m_packet();
+ } else if (data.cur_packet_bytes == 4) {
+ // handle the 4th byte
+ handle_serial_m3_packet();
+ }
+
+ if (data.cur_packet_bytes >= data.packet_size) {
+ data.cur_packet_bytes = 0;
+ }
+ }
+}
+
+static void irq3_4_handler()
+#pragma aux irq3_4_handler "*" parm caller [] modify [ax bx cx dx es]
+{
+ uint16_t iobase = data.port_io;
+ uint8_t intid, byte;
+
+ pic_eoi_irq(data.port_irq);
+
+ while ((intid = serial_read_pending_interrupt(iobase)) != SERIAL_IID_NONE) {
+ switch (intid) {
+ case SERIAL_IID_ERROR:
+ byte = serial_get_line_status(iobase);
+ // If there is an error, we should likely clear our packet buffer
+ if (byte & SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE) {
+ dputs("serial rx error");
+ data.cur_packet_bytes = 0;
+ }
+ break;
+ case SERIAL_IID_DATA:
+ case SERIAL_IID_STALE:
+ while (serial_data_ready(iobase)) {
+ byte = serial_read_data(iobase);
+ handle_serial_data(byte);
+ }
+ break;
+ case SERIAL_IID_MODEM:
+ byte = serial_get_modem_status(iobase); // Read but ignore
+ break;
+ }
+ }
+}
+
+void __declspec(naked) __far irq3_4_isr(void)
+{
+ __asm {
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ mov bp, sp
+ push cs
+ pop ds
+
+ call irq3_4_handler
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+
+ ; We don't chain to the previous handler
+ iret
+ }
+}
+#endif
+
#if USE_WIN386
/** Windows will call this function to notify events when we are inside a DOS box. */
static void windows_mouse_handler(int action, int x, int y, int buttons, int events)
@@ -1366,7 +1717,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");
@@ -1472,7 +1823,7 @@ void __declspec(naked) __far int2f_isr(void)
popa
; Jump to the next handler in the chain
- jmp dword ptr cs:[data + 4] ; wasm doesn't support structs, this is data.prev_int2f_handler
+ jmp dword ptr cs:[data + (4*2)] ; wasm doesn't support structs, this is data.prev_int2f_handler
}
}
#endif
@@ -1490,7 +1841,13 @@ static LPTSRDATA int33_get_tsr_data(void);
LPTSRDATA __far get_tsr_data(bool installed)
{
if (installed) {
- return int33_get_tsr_data();
+ // Check if int33 ISR actually points to something before calling it.
+ if (_dos_getvect(0x33)) {
+ // Call our magic function, which also returns our data segment
+ return int33_get_tsr_data();
+ } else {
+ return 0;
+ }
} else {
// Get the TSR data of this instance, not the one currently installed
// This is as simple as getting the data from this segment