How markrussinovich/doompaint works
Purpose and entry points
MS Paint Doom runs ViZDoom headlessly, then displays real Doom frames by pasting them into Microsoft Paint as clipboard images. run.bat is the primary Windows launcher; it creates the environment, installs dependencies, and updates ViZDoom’s OpenAL DLL. Python orchestration starts in mspaintdoom/main.py; run_debug.bat enables live input diagnostics.
Game orchestration and timing
mspaintdoom/main.py owns startup, CLI options, lifecycle, pause/focus behavior, and the self-clocking render/paste loop. The engine advances independently at Doom’s 35 Hz; paste publication waits for both a new engine frame and evidence that Paint consumed the previous clipboard data. This keeps simulation real-time while limiting visible output to Paint’s throughput.
• mspaintdoom/main.py
• mspaintdoom/engine_vzd.py
ViZDoom engine boundary
engine_vzd.py configures and drives ViZDoom/ ZDoom: WAD selection, map, skill, resolution, action inputs, frame extraction, and engine sound reset behavior. It uses the bundled shareware IWAD by default; ViZDoom-provided Freedoom supports unavailable episodes/maps. The WAD is game content, not application logic.
• mspaintdoom/engine_vzd.py
• wad/doom1.wad
• requirements.txt
Paint output and window control
paint_out.py launches/manages Paint, prepares its canvas, chooses zoom, and executes the validated paste route. Preferred output is synthetic Ctrl+V; UI Automation’s Edit > Paste is the fallback after repeated failed consumptions. Frames remain floating selections; each subsequent paste implicitly commits the prior frame, preserving Paint undo history as frame history.
• mspaintdoom/paint_out.py
• mspaintdoom/sendinput.py
• mspaintdoom/capture.py
OLE clipboard serving
Instead of replacing clipboard contents per frame, clipserve.py exposes a persistent Windows OLE IDataObject whose image bytes are updated in place. Paint retrieves DIB data through GetData; this avoids freeing data during Paint’s asynchronous paste read. Clipboard consumption is also the pacing acknowledgment. Clipboard-loss detection permits another application’s copy operation while paused and reclaims ownership only when publishing resumes.
• mspaintdoom/clipserve.py
• exp_ole_clip.py
Input isolation and control translation
A low-level Windows keyboard hook captures gameplay keys only while Paint is foreground, preventing arrows, shortcuts, and selection movement from affecting Paint. Input is sampled at engine-tic cadence with tap latching. Ctrl+Z is specially reinjected so Paint undo remains available; failure to install the hook falls back to asynchronous key polling.
• mspaintdoom/keys.py
• mspaintdoom/sendinput.py
• mspaintdoom/main.py
Audio and platform dependencies
Engine effects use ViZDoom/OpenAL; startup and runtime recovery reset ZDoom sound when metering indicates silent output. Separate music handling reads WAD music lumps, converts MUS where required, and plays MIDI through Windows MCI, synchronized with focus pause state. This is Windows-specific software relying on Win32 input, clipboard/OLE, UI Automation, Paint, MCI, ViZDoom, and OpenAL. The launcher replaces ViZDoom’s older OpenAL with the included version for default-device tracking.
• mspaintdoom/music.py
• third_party/OpenAL-COPYING.txt
• run.bat
Main entry points: main.py Runtime orchestrator · engine_vzd.py ViZDoom engine adapter · clipserve.py OLE clipboard server · run.bat Windows launcher
Components 16
Windows launch
- Windows launcher · Creates the environment, installs dependencies, and updates OpenAL.
run.bat - Debug launcher · Enables live input diagnostics.
run_debug.bat - Python dependencies
requirements.txt
Game runtime
- Runtime orchestrator · Owns lifecycle, focus/pause behavior, and the self-clocking publication loop.
mspaintdoom/main.py - ViZDoom engine adapter · Configures ZDoom and advances gameplay at 35 Hz.
mspaintdoom/engine_vzd.py - ViZDoom / ZDoom
- Doom IWAD · Default bundled shareware game data.
wad/doom1.wad
Paint integration
- Paint controller · Prepares Paint and chooses synthetic or UI Automation paste.
mspaintdoom/paint_out.py - OLE clipboard server · Updates image bytes in a persistent clipboard object and detects consumption.
mspaintdoom/clipserve.py - Microsoft Paint · Displays floating pasted frame selections and retains undo history.
- Synthetic input
mspaintdoom/sendinput.py - Paint capture checks · Validates that Paint consumed pasted clipboard data.
mspaintdoom/capture.py - OLE prototype · Reference implementation for OLE clipboard behavior.
exp_ole_clip.py
Input and audio
- Gameplay key isolation · Captures keys only while Paint is foreground and falls back to polling.
mspaintdoom/keys.py - WAD music playback · Extracts music lumps and synchronizes playback with focus pause state.
mspaintdoom/music.py - Bundled OpenAL · Replaces ViZDoom's older library for device tracking.
third_party/OpenAL-COPYING.txt
Connections 21
- Windows launcher → Python dependencies: installs
- Debug launcher → Windows launcher: debug variant
- Windows launcher → Runtime orchestrator: starts
- Windows launcher → Bundled OpenAL: updates ViZDoom with
- Runtime orchestrator → ViZDoom engine adapter: drives tics and frames
- ViZDoom engine adapter → ViZDoom / ZDoom: configures and controls
- ViZDoom engine adapter → Doom IWAD: loads
- Runtime orchestrator → OLE clipboard server: publishes new frames
- OLE clipboard server → Paint controller: consumption acknowledgment
- Paint controller → Synthetic input: requests Ctrl+V
- Synthetic input → Microsoft Paint: pastes frames
- Paint controller → Paint capture checks: validates paste route
- Paint capture checks → OLE clipboard server: confirms consumption
- OLE clipboard server → Microsoft Paint: serves DIB via GetData
- Runtime orchestrator → Gameplay key isolation: samples at tic cadence
- Gameplay key isolation → ViZDoom engine adapter: translates gameplay actions
- Gameplay key isolation → Synthetic input: reinjects Ctrl+Z
- Runtime orchestrator → WAD music playback: controls focus pause
- WAD music playback → Doom IWAD: reads music lumps
- ViZDoom engine adapter → Bundled OpenAL: uses effects audio
- OLE prototype → OLE clipboard server: informs implementation