From 157d79f2d6bf459c3918bf92f811741e10c3ec6c Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Thu, 19 Mar 2026 14:19:41 -0500 Subject: [PATCH] Fixed mouse bounding on real hardware. INI is now written with DOS line endings, not UNIX. --- dvx/platform/dvxPlatformDos.c | 95 ++++++++++++++++++++++------------- dvxshell/Makefile | 2 +- 2 files changed, 61 insertions(+), 36 deletions(-) diff --git a/dvx/platform/dvxPlatformDos.c b/dvx/platform/dvxPlatformDos.c index 58ec7d4..3d291c8 100644 --- a/dvx/platform/dvxPlatformDos.c +++ b/dvx/platform/dvxPlatformDos.c @@ -68,6 +68,17 @@ static void sysInfoAppend(const char *fmt, ...); static bool sHasMouseWheel = false; static int32_t sLastWheelDelta = 0; +// Software cursor tracking. Many real-hardware mouse drivers fail to +// honour INT 33h functions 07h/08h (set coordinate range) in VESA modes +// because they don't recognise non-standard video modes. We bypass the +// driver's position entirely: platformMousePoll reads raw mickey deltas +// via function 0Bh, accumulates them into sCurX/sCurY, and clamps to +// the screen bounds. Function 03h is still used for button state. +static int32_t sMouseRangeW = 0; +static int32_t sMouseRangeH = 0; +static int32_t sCurX = 0; +static int32_t sCurY = 0; + // Alt+key scan code to ASCII lookup table (indexed by BIOS scan code). // INT 16h returns these scan codes with ascii=0 for Alt+key combos. // Using a 256-byte lookup table instead of a switch or if-chain because @@ -1122,30 +1133,20 @@ bool platformKeyboardRead(PlatformKeyEventT *evt) { void platformMouseInit(int32_t screenW, int32_t screenH) { __dpmi_regs r; + sMouseRangeW = screenW; + sMouseRangeH = screenH; + sCurX = screenW / 2; + sCurY = screenH / 2; + // Function 00h: reset driver, detect mouse hardware memset(&r, 0, sizeof(r)); r.x.ax = 0x0000; __dpmi_int(0x33, &r); - // Function 07h: set horizontal min/max range + // Flush any stale mickey counters so the first poll starts clean. + // Function 0Bh returns and resets the accumulated motion counters. memset(&r, 0, sizeof(r)); - r.x.ax = 0x0007; - r.x.cx = 0; - r.x.dx = screenW - 1; - __dpmi_int(0x33, &r); - - // Function 08h: set vertical min/max range - memset(&r, 0, sizeof(r)); - r.x.ax = 0x0008; - r.x.cx = 0; - r.x.dx = screenH - 1; - __dpmi_int(0x33, &r); - - // Function 04h: warp cursor to center of screen - memset(&r, 0, sizeof(r)); - r.x.ax = 0x0004; - r.x.cx = screenW / 2; - r.x.dx = screenH / 2; + r.x.ax = 0x000B; __dpmi_int(0x33, &r); } @@ -1173,32 +1174,55 @@ void platformMouseSetAccel(int32_t threshold) { // platformMousePoll // ============================================================ // -// Reads current mouse state via INT 33h function 03h. -// Returns: CX=X position, DX=Y position, BX=button state -// (bit 0 = left, bit 1 = right, bit 2 = middle). -// -// Polling is used instead of a callback/event model because the -// DVX event loop already runs at frame rate. Installing a real-mode -// callback for mouse events would add DPMI mode-switch overhead -// on every mickeyed movement, which is wasteful when we only sample -// once per frame anyway. +// Reads button state via function 03h and raw mickey deltas via +// function 0Bh. Position is tracked in software (sCurX/sCurY) +// rather than using the driver's coordinates, because many real- +// hardware mouse drivers cannot handle VESA mode coordinate ranges. +// Function 0Bh returns the accumulated mickey motion since the last +// call and is reliable across all drivers. void platformMousePoll(int32_t *mx, int32_t *my, int32_t *buttons) { __dpmi_regs r; + // Function 03h: read button state only memset(&r, 0, sizeof(r)); r.x.ax = 0x0003; __dpmi_int(0x33, &r); - *mx = r.x.cx; - *my = r.x.dx; - *buttons = r.x.bx & 0x07; // BL only: bits 0-2 = left/right/middle + *buttons = r.x.bx & 0x07; // BH = signed 8-bit wheel counter (cleared on read by the driver). - // Only meaningful if the wheel API was activated via platformMouseWheelInit. if (sHasMouseWheel) { sLastWheelDelta = (int32_t)(int8_t)(r.x.bx >> 8); } + + // Function 0Bh: read mickey motion counters (signed 16-bit deltas, + // cleared on read). Accumulate into software cursor position. + memset(&r, 0, sizeof(r)); + r.x.ax = 0x000B; + __dpmi_int(0x33, &r); + + sCurX += (int16_t)r.x.cx; + sCurY += (int16_t)r.x.dx; + + if (sCurX < 0) { + sCurX = 0; + } + + if (sCurX >= sMouseRangeW) { + sCurX = sMouseRangeW - 1; + } + + if (sCurY < 0) { + sCurY = 0; + } + + if (sCurY >= sMouseRangeH) { + sCurY = sMouseRangeH - 1; + } + + *mx = sCurX; + *my = sCurY; } @@ -1247,12 +1271,13 @@ int32_t platformMouseWheelPoll(void) { // so the pointer visually sticks to the border. void platformMouseWarp(int32_t x, int32_t y) { - __dpmi_regs r; + sCurX = x; + sCurY = y; + // Flush any pending mickeys so the next poll doesn't undo the warp + __dpmi_regs r; memset(&r, 0, sizeof(r)); - r.x.ax = 0x0004; - r.x.cx = x; - r.x.dx = y; + r.x.ax = 0x000B; __dpmi_int(0x33, &r); } diff --git a/dvxshell/Makefile b/dvxshell/Makefile index e0e2c05..d055ce1 100644 --- a/dvxshell/Makefile +++ b/dvxshell/Makefile @@ -32,7 +32,7 @@ $(TARGET): $(OBJS) $(LIBDIR)/libdvx.a $(LIBDIR)/libtasks.a | $(BINDIR) rm -f $(BINDIR)/dvx $(CONFIGDIR)/dvx.ini: ../dvx.ini | $(CONFIGDIR) - cp $< $@ + sed 's/$$/\r/' $< > $@ $(OBJDIR)/%.o: %.c | $(OBJDIR) $(CC) $(CFLAGS) -c -o $@ $<