diff options
Diffstat (limited to 'w16mouse.c')
-rw-r--r-- | w16mouse.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/w16mouse.c b/w16mouse.c new file mode 100644 index 0000000..93aadf5 --- /dev/null +++ b/w16mouse.c @@ -0,0 +1,261 @@ +/* + * VBMouse - win16 mouse driver entry points + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <windows.h> + +#include "dlog.h" +#include "utils.h" +#include "int33.h" +#include "int2fwin.h" +#include "w16mouse.h" + +/** If 1, hook int2f to detect fullscreen DOSBoxes and auto-disable this driver. */ +#define HOOK_INT2F 0 + +#define MOUSE_NUM_BUTTONS 2 + +/** The routine Windows gave us which we should use to report events. */ +static LPFN_MOUSEEVENT eventproc; +/** Current status of the mouse driver (see MOUSEFLAGS_*). */ +static unsigned char mouseflags; +enum { + MOUSEFLAGS_ENABLED = 1 << 0, + MOUSEFLAGS_HAS_VBOX = 1 << 1, + MOUSEFLAGS_VBOX_ENABLED = 1 << 2, + MOUSEFLAGS_HAS_WIN386 = 1 << 3, + MOUSEFLAGS_INT2F_HOOKED = 1 << 4 +}; +#if HOOK_INT2F +/** Existing interrupt2f handler. */ +static LPFN prev_int2f_handler; +#endif + +/* This is how events are delivered to Windows */ + +static void send_event(unsigned short Status, short deltaX, short deltaY, short ButtonCount, short extra1, short extra2); +#pragma aux (MOUSEEVENTPROC) send_event = \ + "call dword ptr [eventproc]" + +#pragma code_seg ( "CALLBACKS" ) + +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 (events & INT33_EVENT_MASK_LEFT_BUTTON_PRESSED) status |= SF_B1_DOWN; + if (events & INT33_EVENT_MASK_LEFT_BUTTON_RELEASED) status |= SF_B1_UP; + if (events & INT33_EVENT_MASK_RIGHT_BUTTON_PRESSED) status |= SF_B2_DOWN; + if (events & INT33_EVENT_MASK_RIGHT_BUTTON_RELEASED) status |= SF_B2_UP; + + if (events & INT33_EVENT_MASK_MOVEMENT) { + status |= SF_MOVEMENT | SF_ABSOLUTE; + } + + (void) buttons; + (void) delta_x; + (void) delta_y; + +#if 0 + dlog_print("w16mouse: event status="); + dlog_printx(status); + dlog_print(" x="); + dlog_printx(x); + dlog_print(" y="); + dlog_printx(y); + dlog_endline(); +#endif + + send_event(status, (uint16_t)(x) * 2, (uint16_t)(y) * 2, MOUSE_NUM_BUTTONS, 0, 0); +} + +#if HOOK_INT2F + +static void display_switch_handler(int function) +#pragma aux display_switch_handler parm caller [ax] modify [ax bx cx dx si di] +{ + if (!(mouseflags & MOUSEFLAGS_ENABLED) || !(mouseflags & MOUSEFLAGS_VBOX_ENABLED)) { + return; + } + + switch (function) { + case INT2F_NOTIFY_BACKGROUND_SWITCH: + dlog_puts("Going background\n"); + break; + case INT2F_NOTIFY_FOREGROUND_SWITCH: + dlog_puts("Going foreground\n"); + break; + } +} + +/** Interrupt 2F handler, which will be called on some Windows 386 mode events. + * This is more complicated than it should be becaused we have to fetch our DS + * without clobbering any registers whatsoever, and chain back to the previous handler. + * @todo OpenWatcom 2.x insists on calling GETDS from a different code segment, so we can't use __interrupt. */ +static void __declspec(naked) __far int2f_handler(void) +{ + _asm { + ; Preserve data segment + push ds + + ; Load our data segment + push ax + mov ax, SEG prev_int2f_handler ; Let's hope that Windows relocates segments with interrupts disabled + mov ds, ax + pop ax + + ; 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 /* HOOK_INT2F */ + +#pragma code_seg () + +/* Driver exported functions. */ + +/** DLL entry point (or driver initialization routine). + * The initialization routine should check whether a mouse exists. + * @return nonzero value indicates a mouse exists. + */ +#pragma off (unreferenced); +BOOL FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment, + WORD wHeapSize, LPSTR lpszCmdLine) +#pragma pop (unreferenced); +{ + // We are not going to bother checking whether a PS2 mouse exists and just assume it does + +#if HOOK_INT2F + // Check now whether we are running under protected mode windows + if (windows_386_enhanced_mode()) { + mouseflags |= MOUSEFLAGS_HAS_WIN386; + } +#endif + + // When running under protected mode Windows, let's tell VMD (the mouse virtualizer) + // what type of mouse we are going to be using + if (mouseflags & MOUSEFLAGS_HAS_WIN386) { + LPFN vmd_entry = win_get_vxd_api_entry(VMD_DEVICE_ID); + if (vmd_entry) { + vmd_set_mouse_type(&vmd_entry, VMD_TYPE_PS2, 0x33, 0); + } + } + + return 1; +} + +/** Called by Windows to retrieve information about the mouse hardware. */ +WORD FAR PASCAL Inquire(LPMOUSEINFO lpMouseInfo) +{ + lpMouseInfo->msExist = 1; + lpMouseInfo->msRelative = 0; + lpMouseInfo->msNumButtons = MOUSE_NUM_BUTTONS; + lpMouseInfo->msRate = 80; + return sizeof(MOUSEINFO); +} + +/** Called by Windows to enable the mouse driver. + * @param lpEventProc Callback function to call when a mouse event happens. */ +VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc) +{ + // Store the windows-given callback + cli(); // Write to far pointer may not be atomic, and we could be interrupted mid-write + eventproc = lpEventProc; + sti(); + + if (!(mouseflags & MOUSEFLAGS_ENABLED)) { + dlog_puts("w16mouse: enable"); + + int33_reset(); + + int33_set_horizontal_window(0, 0x7FFF); + int33_set_vertical_window(0, 0x7FFF); + + int33_set_event_handler(INT33_EVENT_MASK_ALL, int33_mouse_callback); + + dlog_puts("w16mouse: int33 enabled"); + + mouseflags |= MOUSEFLAGS_ENABLED; + +#if HOOK_INT2F + if ((mouseflags & MOUSEFLAGS_HAS_WIN386) && (mouseflags & MOUSEFLAGS_VBOX_ENABLED)) { + cli(); + hook_int2f(&prev_int2f_handler, int2f_handler); + sti(); + dlog_puts("int2F hooked!\n"); + mouseflags |= MOUSEFLAGS_INT2F_HOOKED; + } +#endif + } +} + +/** Called by Windows to disable the mouse driver. */ +VOID FAR PASCAL Disable(VOID) +{ + if (mouseflags & MOUSEFLAGS_ENABLED) { + dlog_puts("w16mouse: disable"); + +#if HOOK_INT2F + if (mouseflags & MOUSEFLAGS_INT2F_HOOKED) { + cli(); + unhook_int2f(prev_int2f_handler); + sti(); + dlog_puts("int2F unhooked!\n"); + mouseflags &= ~MOUSEFLAGS_INT2F_HOOKED; + } +#endif + + int33_reset(); + dlog_puts("w16mouse: int33 reset"); + + mouseflags &= ~MOUSEFLAGS_ENABLED; + } +} + +/** Called by Window to retrieve the interrupt vector number used by this driver, or -1. */ +int FAR PASCAL MouseGetIntVect(VOID) +{ + return 0x33; +} + |