// ============================================================================ // 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] // -d Enable debug output // // Example: // demo vga.drv // demo -d s3trio.drv // ============================================================================ #include #include #include #include #include #include #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] \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"); } // Demo 5: Text output using ExtTextOut if (info.hasExtTextOut) { logMsg("Demo 5: ExtTextOut text rendering\n"); int32_t ret; // Opaque text: white on blue (built-in font via NULL) const char *msg1 = "Hello from ExtTextOut!"; ret = wdrvExtTextOut(drv, 10, 10, msg1, (int16_t)strlen(msg1), MAKE_RGB(255, 255, 255), MAKE_RGB(0, 0, 170), true, NULL); logMsg(" msg1 ret=%" PRId32 "\n", ret); // Opaque text: yellow on dark red const char *msg2 = "Win3.x DDI Text Output"; ret = wdrvExtTextOut(drv, 10, 30, msg2, (int16_t)strlen(msg2), MAKE_RGB(255, 255, 0), MAKE_RGB(170, 0, 0), true, NULL); logMsg(" msg2 ret=%" PRId32 "\n", ret); // Transparent text: green on existing background const char *msg3 = "Transparent mode"; ret = wdrvExtTextOut(drv, 10, 50, msg3, (int16_t)strlen(msg3), MAKE_RGB(0, 255, 0), MAKE_RGB(0, 0, 0), false, NULL); logMsg(" msg3 ret=%" PRId32 "\n", ret); // Show resolution info char buf[64]; int len = sprintf(buf, "Mode: %dx%d", screenW, screenH); ret = wdrvExtTextOut(drv, 10, 70, buf, (int16_t)len, MAKE_RGB(255, 255, 255), MAKE_RGB(0, 0, 0), true, NULL); logMsg(" msg4 ret=%" PRId32 "\n", ret); logMsg(" Text output done\n"); } // Demo 6: Multiple loaded fonts if (info.hasExtTextOut) { logMsg("Demo 6: Loaded fonts from .FON files\n"); int32_t ret; // Load fonts from .FON files WdrvFontT courFont = wdrvLoadFontFon("COURE.FON", 1); // 9x16 Courier WdrvFontT sansFont = wdrvLoadFontFon("SSERIFE.FON", 0); // MS Sans Serif WdrvFontT sysFont = wdrvLoadFontFon("VGASYS.FON", 0); // System int16_t textY = 100; if (courFont) { const char *msg = "Courier: The quick brown fox jumps over the lazy dog"; ret = wdrvExtTextOut(drv, 10, textY, msg, (int16_t)strlen(msg), MAKE_RGB(255, 255, 255), MAKE_RGB(0, 0, 128), true, courFont); logMsg(" Courier ret=%" PRId32 "\n", ret); textY += 20; } else { logMsg(" COURE.FON not loaded\n"); } if (sansFont) { const char *msg = "Sans Serif: The quick brown fox jumps over the lazy dog"; ret = wdrvExtTextOut(drv, 10, textY, msg, (int16_t)strlen(msg), MAKE_RGB(255, 255, 0), MAKE_RGB(128, 0, 0), true, sansFont); logMsg(" Sans ret=%" PRId32 "\n", ret); textY += 20; } else { logMsg(" SSERIFE.FON not loaded\n"); } if (sysFont) { const char *msg = "System: The quick brown fox jumps over the lazy dog"; ret = wdrvExtTextOut(drv, 10, textY, msg, (int16_t)strlen(msg), MAKE_RGB(0, 255, 255), MAKE_RGB(0, 0, 0), true, sysFont); logMsg(" System ret=%" PRId32 "\n", ret); textY += 20; } else { logMsg(" VGASYS.FON not loaded\n"); } // Built-in font for comparison const char *msg = "Built-in: The quick brown fox jumps over the lazy dog"; ret = wdrvExtTextOut(drv, 10, textY, msg, (int16_t)strlen(msg), MAKE_RGB(255, 255, 255), MAKE_RGB(0, 128, 0), true, NULL); logMsg(" Built-in ret=%" PRId32 "\n", ret); wdrvUnloadFont(courFont); wdrvUnloadFont(sansFont); wdrvUnloadFont(sysFont); logMsg(" Font demo 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; }