WinDriver/demo.c
2026-02-21 18:01:54 -06:00

323 lines
9.9 KiB
C

// ============================================================================
// demo.c - Demonstration of using Windows 3.x display drivers from DOS
//
// This program loads a Windows 3.x accelerated display driver (.DRV file)
// and uses its hardware-accelerated functions to draw on screen.
//
// Usage: demo [-d] <driver.drv>
// -d Enable debug output
//
// Example:
// demo vga.drv
// demo -d s3trio.drv
// ============================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <pc.h>
#include <dpmi.h>
#include "win31drv/windrv.h"
#include "win31drv/wintypes.h"
#include "win31drv/log.h"
__attribute__((noinline))
static void demoDrawing(WdrvHandleT drv);
static void printDriverInfo(WdrvHandleT drv);
static void printUsage(const char *progName);
static void setupPalette(WdrvHandleT drv);
int main(int argc, char *argv[])
{
const char *driverPath = NULL;
bool debug = false;
// Parse arguments
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-d") == 0) {
debug = true;
} else if (argv[i][0] != '-') {
driverPath = argv[i];
} else {
printUsage(argv[0]);
return 1;
}
}
if (!driverPath) {
printUsage(argv[0]);
return 1;
}
logInit("OUTPUT.LOG");
// Initialize the library
logMsg("Initializing windrv library...\n");
int32_t err = wdrvInit();
if (err != WDRV_OK) {
logErr("Failed to initialize: %s\n", wdrvGetLastErrorString());
return 1;
}
if (debug) {
wdrvSetDebug(true);
}
// Load the driver
logMsg("Loading driver: %s\n", driverPath);
WdrvHandleT drv = wdrvLoadDriver(driverPath);
if (!drv) {
logErr("Failed to load driver: %s\n", wdrvGetLastErrorString());
wdrvShutdown();
return 1;
}
// Print driver info
printDriverInfo(drv);
// Redirect stdout to the log file before entering SVGA mode.
// In graphics mode, console output through INT 21h can crash
// because the console handler tries to access VGA text memory
// that's now part of the SVGA framebuffer.
freopen("OUTPUT.LOG", "a", stdout);
// Enable the driver (set video mode)
logMsg("Enabling driver...\n");
err = wdrvEnable(drv, 0, 0, 0); // Use driver defaults
if (err != WDRV_OK) {
logErr("Failed to enable driver: %s\n", wdrvGetLastErrorString());
wdrvUnloadDriver(drv);
wdrvShutdown();
return 1;
}
// Set up a 256-color palette via the driver's SetPalette DDI.
// This updates both the driver's internal color table (used by
// RealizeObject for color matching) and the VGA DAC hardware.
setupPalette(drv);
// Run drawing demos
demoDrawing(drv);
logMsg("Drawing complete. Press any key...\n");
// Wait for a keypress so we can see the output
while (!kbhit()) {
// Busy-wait; kbhit() does keyboard I/O which keeps DOSBox-X responsive
}
(void)getkey(); // consume the key
// Disable the driver (restore text mode)
logMsg("Calling wdrvDisable...\n");
err = wdrvDisable(drv);
if (err != WDRV_OK) {
logMsg("wdrvDisable failed (err=%d), forcing text mode via INT 10h\n", (int)err);
__dpmi_regs dr;
memset(&dr, 0, sizeof(dr));
dr.x.ax = 0x0003; // INT 10h AH=00 AL=03: set 80x25 text mode
__dpmi_int(0x10, &dr);
}
// Clean up
logMsg("Unloading driver...\n");
wdrvUnloadDriver(drv);
wdrvShutdown();
logMsg("Done.\n");
logShutdown();
return 0;
}
static void printUsage(const char *progName)
{
logErr("Usage: %s [-d] <driver.drv>\n", progName);
logErr(" -d Enable debug output\n");
logErr("\nLoads a Windows 3.x display driver and demonstrates\n");
logErr("its accelerated drawing functions from DOS.\n");
}
static void printDriverInfo(WdrvHandleT drv)
{
WdrvInfoT info;
wdrvGetInfo(drv, &info);
logMsg("\n=== Driver Information ===\n");
logMsg(" Name: %s\n", info.driverName);
logMsg(" Version: %u.%u\n", info.driverVersion >> 8,
info.driverVersion & 0xFF);
logMsg(" Resolution: %" PRId32 "x%" PRId32 "\n", info.maxWidth, info.maxHeight);
logMsg(" Color depth: %" PRId32 " bpp\n", info.maxBpp);
logMsg(" Colors: %" PRId32 "\n", info.numColors);
logMsg(" Raster caps: 0x%04" PRIX32 "\n", info.rasterCaps);
logMsg(" Capabilities:\n");
logMsg(" BitBlt: %s\n", info.hasBitBlt ? "yes" : "no");
logMsg(" Output: %s\n", info.hasOutput ? "yes" : "no");
logMsg(" Pixel: %s\n", info.hasPixel ? "yes" : "no");
logMsg(" StretchBlt: %s\n", info.hasStretchBlt ? "yes" : "no");
logMsg(" ExtTextOut: %s\n", info.hasExtTextOut ? "yes" : "no");
logMsg(" SetPalette: %s\n", info.hasSetPalette ? "yes" : "no");
logMsg(" SetCursor: %s\n", info.hasSetCursor ? "yes" : "no");
}
// noinline: when inlined into main with -O2, the optimizer mishandles
// callee-saved registers across the thunk calls in the Demo 2 → Demo 3
// transition, causing the handle pointer to be corrupted.
__attribute__((noinline))
static void demoDrawing(WdrvHandleT drv)
{
WdrvInfoT info;
wdrvGetInfo(drv, &info);
int16_t screenW = (int16_t)info.maxWidth;
int16_t screenH = (int16_t)info.maxHeight;
if (screenW == 0) {
screenW = 640;
}
if (screenH == 0) {
screenH = 480;
}
logMsg("demoDrawing: screenW=%d screenH=%d hasBitBlt=%d hasOutput=%d hasPixel=%d\n",
screenW, screenH, info.hasBitBlt, info.hasOutput, info.hasPixel);
// Demo 1: Fill rectangles
if (info.hasBitBlt) {
logMsg("Demo 1: Fill rectangles\n");
// Clear screen to white
wdrvFillRect(drv, 0, 0, screenW, screenH, MAKE_RGB(255, 255, 255));
static const uint32_t vgaColors[] = {
MAKE_RGB( 0, 0, 0), // black
MAKE_RGB( 0, 0, 255), // blue
MAKE_RGB( 0, 255, 0), // green
MAKE_RGB( 0, 255, 255), // cyan
MAKE_RGB(255, 0, 0), // red
MAKE_RGB(255, 0, 255), // magenta
MAKE_RGB(255, 255, 0), // yellow
MAKE_RGB(170, 170, 170), // light gray
MAKE_RGB( 85, 85, 85), // dark gray
MAKE_RGB( 85, 85, 255), // light blue
MAKE_RGB( 85, 255, 85), // light green
MAKE_RGB( 85, 255, 255), // light cyan
MAKE_RGB(255, 85, 85), // light red
MAKE_RGB(255, 85, 255), // light magenta
MAKE_RGB(255, 255, 85), // yellow-ish
MAKE_RGB(128, 128, 128), // mid gray
};
int16_t boxW = screenW / 4;
int16_t boxH = screenH / 4;
for (int16_t row = 0; row < 4; row++) {
for (int16_t col = 0; col < 4; col++) {
int idx = row * 4 + col;
wdrvFillRect(drv, col * boxW + 4, row * boxH + 4,
boxW - 8, boxH - 8, vgaColors[idx]);
}
}
logMsg(" Drew %d colored rectangles\n", 16);
}
// Demo 2: Draw pixel patterns
if (info.hasPixel) {
logMsg("Demo 2: Pixel patterns (8x8)\n");
int pixCount = 0;
for (int16_t y = 0; y < 8 && y < screenH; y++) {
for (int16_t x = 0; x < 8 && x < screenW; x++) {
uint8_t r = (uint8_t)(x * 32);
uint8_t g = (uint8_t)(y * 32);
uint8_t b = (uint8_t)((x + y) * 16);
wdrvSetPixel(drv, x + screenW - 18, y + 10, MAKE_RGB(r, g, b));
pixCount++;
}
}
logMsg(" Drew %d pixels\n", pixCount);
}
// Demo 3: Draw lines using Output (polyline with realized pen)
if (info.hasOutput) {
logMsg("Demo 3: Lines (starburst)\n");
int16_t cx = screenW / 2;
int16_t cy = screenH / 2;
int16_t radius = (screenH < screenW ? screenH : screenW) / 3;
int lineCount = 0;
for (int angle = 0; angle < 360; angle += 15) {
int32_t dx = 0;
int32_t dy = 0;
int a = angle % 360;
int qa = a % 90;
int32_t s = (int32_t)qa * radius / 90;
int32_t c = (int32_t)(90 - qa) * radius / 90;
if (a < 90) {
dx = s;
dy = -c;
} else if (a < 180) {
dx = c;
dy = s;
} else if (a < 270) {
dx = -s;
dy = c;
} else {
dx = -c;
dy = -s;
}
Point16T pts[2];
pts[0].x = cx;
pts[0].y = cy;
pts[1].x = (int16_t)(cx + dx);
pts[1].y = (int16_t)(cy + dy);
uint32_t lineColor = MAKE_RGB(
(uint8_t)(angle * 255 / 360),
(uint8_t)(255 - angle * 255 / 360),
128);
wdrvPolyline(drv, pts, 2, lineColor);
lineCount++;
}
logMsg(" Drew %d lines\n", lineCount);
}
// Demo 4: Screen-to-screen blit test
if (info.hasBitBlt) {
logMsg("Demo 4: Screen-to-screen blit\n");
WdrvBitBltParamsT bp;
memset(&bp, 0, sizeof(bp));
bp.srcX = 0;
bp.srcY = 0;
bp.dstX = screenW / 2;
bp.dstY = screenH / 2;
bp.width = screenW / 4;
bp.height = screenH / 4;
bp.rop3 = SRCCOPY;
wdrvBitBlt(drv, &bp);
logMsg(" Screen blit done\n");
}
}
static void setupPalette(WdrvHandleT drv)
{
// The driver sets up its own palette during Enable (via VBE 4F09)
// and stores an internal color table that RealizeObject uses for
// color matching. We leave the palette as-is so the DAC and the
// internal table stay in sync. RealizeObject will find the best
// matching index, and the DAC will display the correct color.
(void)drv;
}