Fixed mouse bounding on real hardware. INI is now written with DOS line endings, not UNIX.

This commit is contained in:
Scott Duensing 2026-03-19 14:19:41 -05:00
parent a4727754e3
commit 157d79f2d6
2 changed files with 61 additions and 36 deletions

View file

@ -68,6 +68,17 @@ static void sysInfoAppend(const char *fmt, ...);
static bool sHasMouseWheel = false; static bool sHasMouseWheel = false;
static int32_t sLastWheelDelta = 0; 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). // 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. // 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 // 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) { void platformMouseInit(int32_t screenW, int32_t screenH) {
__dpmi_regs r; __dpmi_regs r;
sMouseRangeW = screenW;
sMouseRangeH = screenH;
sCurX = screenW / 2;
sCurY = screenH / 2;
// Function 00h: reset driver, detect mouse hardware // Function 00h: reset driver, detect mouse hardware
memset(&r, 0, sizeof(r)); memset(&r, 0, sizeof(r));
r.x.ax = 0x0000; r.x.ax = 0x0000;
__dpmi_int(0x33, &r); __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)); memset(&r, 0, sizeof(r));
r.x.ax = 0x0007; r.x.ax = 0x000B;
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;
__dpmi_int(0x33, &r); __dpmi_int(0x33, &r);
} }
@ -1173,32 +1174,55 @@ void platformMouseSetAccel(int32_t threshold) {
// platformMousePoll // platformMousePoll
// ============================================================ // ============================================================
// //
// Reads current mouse state via INT 33h function 03h. // Reads button state via function 03h and raw mickey deltas via
// Returns: CX=X position, DX=Y position, BX=button state // function 0Bh. Position is tracked in software (sCurX/sCurY)
// (bit 0 = left, bit 1 = right, bit 2 = middle). // rather than using the driver's coordinates, because many real-
// // hardware mouse drivers cannot handle VESA mode coordinate ranges.
// Polling is used instead of a callback/event model because the // Function 0Bh returns the accumulated mickey motion since the last
// DVX event loop already runs at frame rate. Installing a real-mode // call and is reliable across all drivers.
// 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.
void platformMousePoll(int32_t *mx, int32_t *my, int32_t *buttons) { void platformMousePoll(int32_t *mx, int32_t *my, int32_t *buttons) {
__dpmi_regs r; __dpmi_regs r;
// Function 03h: read button state only
memset(&r, 0, sizeof(r)); memset(&r, 0, sizeof(r));
r.x.ax = 0x0003; r.x.ax = 0x0003;
__dpmi_int(0x33, &r); __dpmi_int(0x33, &r);
*mx = r.x.cx; *buttons = r.x.bx & 0x07;
*my = r.x.dx;
*buttons = r.x.bx & 0x07; // BL only: bits 0-2 = left/right/middle
// BH = signed 8-bit wheel counter (cleared on read by the driver). // BH = signed 8-bit wheel counter (cleared on read by the driver).
// Only meaningful if the wheel API was activated via platformMouseWheelInit.
if (sHasMouseWheel) { if (sHasMouseWheel) {
sLastWheelDelta = (int32_t)(int8_t)(r.x.bx >> 8); 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. // so the pointer visually sticks to the border.
void platformMouseWarp(int32_t x, int32_t y) { 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)); memset(&r, 0, sizeof(r));
r.x.ax = 0x0004; r.x.ax = 0x000B;
r.x.cx = x;
r.x.dx = y;
__dpmi_int(0x33, &r); __dpmi_int(0x33, &r);
} }

View file

@ -32,7 +32,7 @@ $(TARGET): $(OBJS) $(LIBDIR)/libdvx.a $(LIBDIR)/libtasks.a | $(BINDIR)
rm -f $(BINDIR)/dvx rm -f $(BINDIR)/dvx
$(CONFIGDIR)/dvx.ini: ../dvx.ini | $(CONFIGDIR) $(CONFIGDIR)/dvx.ini: ../dvx.ini | $(CONFIGDIR)
cp $< $@ sed 's/$$/\r/' $< > $@
$(OBJDIR)/%.o: %.c | $(OBJDIR) $(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $< $(CC) $(CFLAGS) -c -o $@ $<