aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2022-12-31 15:24:54 +0100
committerJavier <dev.git@javispedro.com>2022-12-31 15:26:56 +0100
commitea30df89b6fa62874b82cd75e6cf81a089ee2d25 (patch)
tree50ba771aef093b849411fcb2cee5f90dcc668597
parent831d16b852dc13a9f5ff7a1b56302f94ed193565 (diff)
downloadvbados-ea30df89b6fa62874b82cd75e6cf81a089ee2d25.tar.gz
vbados-ea30df89b6fa62874b82cd75e6cf81a089ee2d25.zip
restore int33 callback+window when windows goes foreground
Even though it should not be necessary, this should help some cases where mouse control is lost when returning from a fullscreen DOS box.
-rw-r--r--int2fwin.h23
-rw-r--r--int33.h22
-rw-r--r--mousew16.c162
3 files changed, 173 insertions, 34 deletions
diff --git a/int2fwin.h b/int2fwin.h
index abd5f2e..d3bd969 100644
--- a/int2fwin.h
+++ b/int2fwin.h
@@ -59,8 +59,8 @@ enum vxd_device_ids
VMD_DEVICE_ID = 0xC
};
-static bool windows_386_enhanced_mode(void);
-#pragma aux windows_386_enhanced_mode = \
+static bool is_windows_386_enhanced_mode(void);
+#pragma aux is_windows_386_enhanced_mode = \
"mov ax, 0x1600" \
"int 0x2F" \
"test al, 0x7F" /* return value is either 0x80 or 0x00 if win386 is not running */ \
@@ -83,8 +83,7 @@ enum vmd_apis {
VMD_SET_MOUSE_TYPE = 0x100,
};
-enum vmd_callouts
-{
+enum vmd_callouts {
/** VMD emits this to know if there is a "win386 aware" DOS mouse driver installed.
* If there is, and the driver responds with CX!=0, VMD will assume
* the driver is taking care of its own instancing
@@ -96,8 +95,7 @@ enum vmd_callouts
VMD_CALLOUT_GET_DOS_MOUSE_API = 0x1
};
-enum vmd_dos_mouse_api_actions
-{
+enum vmd_dos_mouse_api_actions {
VMD_ACTION_MOUSE_EVENT = 1,
VMD_ACTION_HIDE_CURSOR = 2,
VMD_ACTION_SHOW_CURSOR = 3
@@ -129,17 +127,4 @@ static inline bool vmd_set_mouse_type(LPFN *vmd_entry, uint8_t mousetype, int8_t
__value [al] \
__modify [ax]
-/* Miscelaneous helpers. */
-
-static inline void hook_int2f(LPFN *prev, LPFN new)
-{
- *prev = _dos_getvect(0x2F);
- _dos_setvect(0x2F, new);
-}
-
-static inline void unhook_int2f(LPFN prev)
-{
- _dos_setvect(0x2F, prev);
-}
-
#endif
diff --git a/int33.h b/int33.h
index ebe9476..03c61a0 100644
--- a/int33.h
+++ b/int33.h
@@ -227,6 +227,28 @@ static void int33_set_mouse_speed(int16_t x, int16_t y);
__parm [cx] [dx] \
__modify [ax]
+static uint16_t int33_get_mouse_status_size(void);
+#pragma aux int33_get_mouse_status_size = \
+ "mov bx, 0" \
+ "mov ax, 0x15" \
+ "int 0x33" \
+ __value [bx] \
+ __modify [ax]
+
+static void int33_save_mouse_status(uint16_t size, void __far * buffer);
+#pragma aux int33_save_mouse_status = \
+ "mov ax, 0x16" \
+ "int 0x33" \
+ __parm [bx] [es dx] \
+ __modify [ax]
+
+static void int33_load_mouse_status(uint16_t size, void __far * buffer);
+#pragma aux int33_load_mouse_status = \
+ "mov ax, 0x17" \
+ "int 0x33" \
+ __parm [bx] [es dx] \
+ __modify [ax]
+
static void int33_set_sensitivity(uint16_t sens_x, uint16_t sens_y, uint16_t double_speed_threshold);
#pragma aux int33_set_sensitivity = \
"mov ax, 0x1A" \
diff --git a/mousew16.c b/mousew16.c
index cc6d2da..fd25609 100644
--- a/mousew16.c
+++ b/mousew16.c
@@ -29,6 +29,9 @@
/** Whether to enable wheel mouse handling. */
#define USE_WHEEL 1
+/** If win386 is available, hook int2f to detect DOS/Windows screen switches
+ * and reconfigure the int33 driver. */
+#define USE_WIN386 1
/** Verbosely log events as they happen. */
#define TRACE_EVENTS 0
/** Verbosely trace scroll wheel code. */
@@ -39,6 +42,9 @@
/** Windows 3.x only supports 1-2 mouse buttons anyway. */
#define MOUSE_NUM_BUTTONS 2
+/** Prefix for debug messages. */
+#define DPREFIX "mousew16: "
+
/** The routine Windows gave us which we should use to report events. */
static LPFN_MOUSEEVENT eventproc;
/** Current status of the driver. */
@@ -49,6 +55,10 @@ static struct {
/** Whether a mouse wheel was detected. */
bool wheel : 1;
#endif
+#if USE_WIN386
+ /** Whether we are running under windows 386. */
+ bool has_win386 : 1;
+#endif
} flags;
/** Previous deltaX, deltaY from the int33 mouse callback (for relative motion) */
static short prev_delta_x, prev_delta_y;
@@ -69,6 +79,10 @@ static struct {
BOOL WINAPI (*PostMessage)( HWND, UINT, WPARAM, LPARAM );
} userapi;
#endif
+#if USE_WIN386
+/** Previous int2f handler. */
+static LPFN prev_int2f_handler;
+#endif
/* This is how events are delivered to Windows */
@@ -98,11 +112,12 @@ static void print_window_name(HWND hWnd)
{
char buffer[32];
if (userapi.GetWindowText(hWnd, buffer, sizeof(buffer)) > 0) {
- dprintf("w16mouse: hWnd=0x%x name=%s\n", hWnd, buffer);
+ dprintf(DPREFIX "hWnd=0x%x name=%s\n", hWnd, buffer);
}
}
#endif
+/** Helper function to traverse a window hierarchy and find a candidate scrollbar. */
static BOOL CALLBACK __loadds find_scrollbar(HWND hWnd, LPARAM lParam)
{
LPFINDSCROLLBARDATA data = (LPFINDSCROLLBARDATA) lParam;
@@ -135,6 +150,12 @@ static BOOL CALLBACK __loadds find_scrollbar(HWND hWnd, LPARAM lParam)
return ENUM_CHILD_WINDOW_CONTINUE;
}
+/** Send scrolling messages to given window.
+ * @param hWnd window to scroll.
+ * @param vertical true if vertical, false if horizontal.
+ * @param z number of lines to scroll.
+ * @param hScrollBar corresponding scrollbar handle.
+ */
static void post_scroll_msg(HWND hWnd, BOOL vertical, int z, HWND hScrollBar)
{
UINT msg = vertical ? WM_VSCROLL : WM_HSCROLL;
@@ -152,6 +173,7 @@ static void post_scroll_msg(HWND hWnd, BOOL vertical, int z, HWND hScrollBar)
userapi.PostMessage(hWnd, msg, SB_ENDSCROLL, lParam);
}
+/** Send wheel scrolling events to the most likely candidate window. */
static void send_wheel_movement(int8_t z)
{
POINT point;
@@ -165,12 +187,8 @@ static void send_wheel_movement(int8_t z)
// an interrupt handler without causing a re-entrancy issue somewhere.
// Likely it would be better to just move all of this into a hook .DLL
+ // Find current window below the mosue cursor
userapi.GetCursorPos(&point);
-
-#if TRACE_WHEEL
- dprintf("w16mouse: getcursorpos OK\n");
-#endif
-
hWnd = userapi.WindowFromPoint(point);
#if TRACE_WHEEL
@@ -182,18 +200,18 @@ static void send_wheel_movement(int8_t z)
#if TRACE_WHEEL
print_window_name(hWnd);
- dprintf("w16mouse: hWnd=0x%x style=0x%lx\n", hWnd, style);
+ dprintf(DPREFIX "hWnd=0x%x style=0x%lx\n", hWnd, style);
#endif
if (style & WS_VSCROLL) {
#if TRACE_WHEEL
- dprintf("w16mouse: found WS_VSCROLL\n");
+ dprintf(DPREFIX "found WS_VSCROLL\n");
#endif
post_scroll_msg(hWnd, TRUE, z, 0);
break;
} else if (style & WS_HSCROLL) {
#if TRACE_WHEEL
- dprintf("w16mouse: found WS_HSCROLL\n");
+ dprintf(DPREFIX "found WS_HSCROLL\n");
#endif
post_scroll_msg(hWnd, FALSE, z, 0);
break;
@@ -202,7 +220,7 @@ static void send_wheel_movement(int8_t z)
// Let's check if we can find a vertical scroll bar in this window..
#if TRACE_WHEEL
- dprintf("w16mouse: find vertical scrollbar...\n");
+ dprintf(DPREFIX "find vertical scrollbar...\n");
#endif
data.vertical = TRUE;
data.scrollbar = 0;
@@ -214,7 +232,7 @@ static void send_wheel_movement(int8_t z)
// Try a horizontal scrollbar now
#if TRACE_WHEEL
- dprintf("w16mouse: find horizontal scrollbar...\n");
+ dprintf(DPREFIX "find horizontal scrollbar...\n");
#endif
data.vertical = FALSE;
data.scrollbar = 0;
@@ -227,7 +245,7 @@ static void send_wheel_movement(int8_t z)
// Otherwise, try again on the parent window
if (style & WS_CHILD) {
#if TRACE_WHEEL
- dprintf("w16mouse: go into parent...\n");
+ dprintf(DPREFIX "go into parent...\n");
#endif
hWnd = userapi.GetParent(hWnd);
} else {
@@ -238,18 +256,19 @@ static void send_wheel_movement(int8_t z)
}
#if TRACE_WHEEL
- dprintf("w16mouse: wheel end\n");
+ dprintf(DPREFIX "wheel end\n");
#endif
}
#endif /* USE_WHEEL */
+/** Called by the int33 mouse driver. */
static void FAR int33_mouse_callback(uint16_t events, uint16_t buttons, int16_t x, int16_t y, int16_t delta_x, int16_t delta_y)
#pragma aux (INT33_CB) int33_mouse_callback
{
int status = 0;
#if TRACE_EVENTS
- dprintf("w16mouse: events=0x%x buttons=0x%x x=%d y=%d dx=%d dy=%d\n",
+ dprintf(DPREFIX "events=0x%x buttons=0x%x x=%d y=%d dx=%d dy=%d\n",
events, buttons, x, y, delta_x, delta_y);
#endif
@@ -300,7 +319,7 @@ static void FAR int33_mouse_callback(uint16_t events, uint16_t buttons, int16_t
}
#if TRACE_EVENTS
- dprintf("w16mouse: post status=0x%x ", status);
+ dprintf(DPREFIX "post status=0x%x ", status);
if (status & SF_ABSOLUTE) {
dprintf("x=%u y=%u\n", x, y);
} else {
@@ -311,6 +330,93 @@ static void FAR int33_mouse_callback(uint16_t events, uint16_t buttons, int16_t
send_event(status, x, y, MOUSE_NUM_BUTTONS, 0, 0);
}
+#if USE_WIN386
+static void display_switch_handler(int function)
+#pragma aux display_switch_handler parm caller [ax] modify [ax bx cx dx si di]
+{
+ if (!flags.enabled) {
+ return;
+ }
+
+ switch (function) {
+ case INT2F_NOTIFY_BACKGROUND_SWITCH:
+ dputs(DPREFIX "windows going background\n");
+
+ break;
+ case INT2F_NOTIFY_FOREGROUND_SWITCH:
+ dputs(DPREFIX "windows going foreground, reconfiguring int33 driver\n");
+
+ // Normally, we wouldn't need to do anything on a foreground switch
+ // since Windows (VMD) would transparently switch to the int33 driver instance
+ // corresponding to the Windows VM.
+ // i.e. the state of the int33 driver should be transparently restored for us.
+ // However, with some display drivers, the BIOS screen mode may not be completely
+ // restored when Windows goes fullscreen, and this may confuse the int33 driver
+ // into forgetting the window size, even if the int33 driver state was restored.
+ // To avoid this, let's re-set the window size each time Windows goes fullscreen.
+
+ // Additionally, the builtin int33 driver of some emulators cannot be instanced
+ // by VMD (because these drivers effectively exist outside the emulated machine),
+ // in which case the int33 driver is going to be "shared" between DOS and Windows,
+ // resulting in loss of mouse function in Windows whenever a DOS VM is focused.
+ // For these cases, let's just steal control from the DOS program when coming
+ // back into Windows (i.e. let's set the event handler to our own).
+ // The DOS program will likely lose mouse functionality at this point, but
+ // at least it's better than Windows losing control of the mouse permanently.
+
+ // Reset the window size...
+ int33_set_mouse_speed(1, 1);
+ int33_set_horizontal_window(0, max_x);
+ int33_set_vertical_window(0, max_y);
+ // and the event handler, but nothing else.
+ int33_set_event_handler(INT33_EVENT_MASK_ALL, int33_mouse_callback);
+
+ break;
+ }
+}
+
+/** Interrupt 2F handler, which will be called on Windows 386 mode events. */
+static void __declspec(naked) __far int2f_handler(void)
+{
+ _asm {
+ ; Preserve data segment
+ push ds
+
+ ; Load our data segment
+ push SEG prev_int2f_handler ; Let's hope that Windows relocates segments with interrupts disabled
+ pop ds
+
+ ; Check functions we are interested in hooking
+ cmp ax, 0x4001 ; Notify Background Switch
+ je handle_it
+ cmp ax, 0x4002 ; Notify Foreground Switch
+ je handle_it
+
+ ; Otherwise directly jump to next handler
+ jmp next_handler
+
+ handle_it:
+ pushad ; Save and restore 32-bit registers, we may clobber them from C
+ call display_switch_handler
+ popad
+
+ next_handler:
+ ; Store the address of the previous handler
+ push dword ptr [prev_int2f_handler]
+
+ ; Restore original data segment without touching the stack,
+ ; since we want to keep the prev handler address at the top
+ push bp
+ mov bp, sp
+ mov ds, [bp + 6] ; Stack looks like 0: bp, 2: prev_int2f_handler, 6: ds
+ pop bp
+
+ retf 2
+ }
+}
+
+#endif /* USE_WIN386 */
+
#pragma code_seg ()
/* Driver exported functions. */
@@ -333,9 +439,16 @@ BOOL FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment,
dos_print_string("Press any key to terminate.$");
dos_read_character();
// This will cause a "can't load .drv" message from Windows
+ // TODO: This aborts Windows startup, maybe we should still go on
return 0;
}
+#if USE_WIN386
+ if (is_windows_386_enhanced_mode()) {
+ flags.has_win386 = true;
+ }
+#endif
+
return 1;
}
@@ -361,6 +474,16 @@ VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc)
if (!flags.enabled) {
int33_reset();
+#if USE_WIN386
+ // If we are running under win386, hook int2f now.
+ if (flags.has_win386) {
+ _disable();
+ prev_int2f_handler = _dos_getvect(0x2F);
+ _dos_setvect(0x2F, int2f_handler);
+ _enable();
+ }
+#endif
+
#if USE_WHEEL
// Detect whether the int33 driver has wheel support
flags.wheel = int33_get_capabilities() & 1;
@@ -407,7 +530,16 @@ VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc)
VOID FAR PASCAL Disable(VOID)
{
if (flags.enabled) {
+#if USE_WIN386
+ if (flags.has_win386) {
+ // When Windows is shutting down, it's not that important to correctly
+ // preserve the interrupt chain since it will end up being restored.
+ _dos_setvect(0x2F, prev_int2f_handler);
+ }
+#endif
+
int33_reset(); // This removes our handler and all other settings
+
flags.enabled = false;
}
}