aboutsummaryrefslogtreecommitdiff
path: root/mousetsr.c
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2024-05-19 21:03:50 +0200
committerJavier <dev.git@javispedro.com>2024-05-19 21:03:50 +0200
commit4a8fe8127c57b0953e2ba2ad4da5e45a20300645 (patch)
tree60eacf9089c2883bc78b3fc8bf22dc1a7c456031 /mousetsr.c
parentdfa929d625028dae01c8317823f210a3b056c385 (diff)
downloadvbados-4a8fe8127c57b0953e2ba2ad4da5e45a20300645.tar.gz
vbados-4a8fe8127c57b0953e2ba2ad4da5e45a20300645.zip
add initial support for 2nd wheel & 4-5 mouse buttons
Diffstat (limited to 'mousetsr.c')
-rw-r--r--mousetsr.c293
1 files changed, 198 insertions, 95 deletions
diff --git a/mousetsr.c b/mousetsr.c
index 8c42057..dad4229 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,18 @@ 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 (data.usewheelapi) {
+ 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[wheeln].delta += z;
+ data.wheel[wheeln].last.x = data.pos.x;
+ data.wheel[wheeln].last.y = data.pos.y;
+ } else if (wheeln == 0 && (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);
@@ -659,31 +686,23 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in
int16_store_keystroke(data.wheel_down_key);
}
}
- } 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;
}
}
#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 = 3;
+#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,43 @@ 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 = 1;
+ 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) {
+ dputs("ImEx detected");
+ data.num_wheels = 2;
+ data.num_buttons = 5;
+
+ 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.
+ }
+ }
+#endif
+
+ dprintf("found mouse device id = 0x%hx\n", data.device_id);
} 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 +1111,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 +1129,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 +1137,11 @@ static void reset_mouse_state()
data.button[i].released.last.x = 0;
data.button[i].released.last.y = 0;
}
- data.wheel_delta = 0;
+ for (i = 0; i < MAX_WHEELS; i++) {
+ data.wheel[i].delta = 0;
+ data.wheel[i].last.x = 0;
+ data.wheel[i].last.y = 0;
+ }
data.cursor_visible = false;
data.cursor_pos.x = 0;
data.cursor_pos.y = 0;
@@ -1058,12 +1150,12 @@ static void reset_mouse_state()
}
/** 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;
}
/** Return (in the appropiate registers) the desired button press counter and afterwards reset it. */
@@ -1101,7 +1193,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 +1217,11 @@ 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.usewheelapi && data.num_wheels > 0) {
+ r.h.bh = data.wheel[0].delta;
+ data.wheel[0].delta = 0;
+ r.x.si = data.wheel[1].delta;
+ data.wheel[1].delta = 0;
}
#endif
break;
@@ -1151,18 +1245,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.usewheelapi && 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 +1265,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.usewheelapi && 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 +1320,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 +1383,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 +1412,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 +1469,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");