DesktopFix
DesktopFix
Fixing Apple's homework 30 years later.

A system extension (INIT) for 68k Macs that fixes desktop pattern corruption at 32bpp (Millions of Colors) on Mac OS 7.6 through 8.1. QuickDraw's FillCRgn and EraseRect traps produce garbled rainbow pixels behind desktop icon labels, masks, and text editing areas. DesktopFix tail-patches both traps, reading the PixPat tile data directly and rendering correct 32bpp pixels to the framebuffer — bypassing the broken code path entirely.

Mac OS 7.6 – 8.1  •  Any 68k Mac with 32-bit Color QuickDraw  •  Confirmed on Quadra 700 + QEMU Q800  •  Public Domain
The Bug

32bpp Pattern Corruption

At Millions of Colors (32bpp), QuickDraw's FillCRgn and EraseRect traps fail to properly tile the desktop PixPat pattern. Instead of the familiar Mac OS watermark background, you get corrupted rainbow garbage pixels behind icon labels, masks, and text editing areas.

The bug was first discovered on a real Quadra 700 running Mac OS 8.1 at Millions of Colors, and later confirmed on QEMU Quadra 800 emulation. Any 68k Mac capable of 32bpp output can hit this bug.

It went unnoticed for decades because 32bpp was uncommon on 68k Macs — most machines like the Quadra 800 only have 1MB of onboard VRAM, not enough for Millions of Colors at usable resolutions. You needed a NuBus video card or expanded VRAM. QEMU provides 4MB, making the bug trivially reproducible.

Corrupted rainbow pixels behind desktop icon labels at 32bpp
Before: corrupted pattern behind icon labels at 32bpp
Clean desktop with DesktopFix installed - correct Mac OS watermark pattern
After: clean desktop with DesktopFix
The Journey

14 Versions to Get It Right

Finding the right trap to patch took extensive diagnostic work — injecting bright colored fills into different QuickDraw traps to see which one actually draws behind desktop icons. The answer wasn't obvious.

v1 – v9
Patched EraseRect (trap $A8A3) — wrong trap entirely. EraseRect handles menus and titlebars, not icon backgrounds. Nine versions chasing the wrong QuickDraw call.
Nuclear red diagnostic: Painting EraseRect bright red proved it handles menus and titlebars, NOT icon backgrounds. Everything was red except the icons.
v10
Found FillCRgn (trap $AA12) with solid color fill. Right trap! But the fix painted a solid purple bar over the icon column — the desktop is a tiled pattern, not a solid color.
Yellow breakthrough: Painting FillCRgn yellow turned the entire desktop yellow, including all icon areas. That was the moment we knew.
v11
PtInRgn region-accurate fill with corruption detection. But rainbow corruption defies simple heuristics — false positives during icon drags, false negatives on actual corruption. Color distance sampling, local variance, uniform-far-from-desktop checks — nothing worked reliably.
"are you forgetting that it's not a 'desktop color'? it's literally a tiled image."
v12
THE BREAKTHROUGH. Read the PixPat tile data directly, render pattern ourselves at 32bpp. Bypass QuickDraw entirely. Don't detect corruption — just always render it correctly.
The fix: Read the pattern tile bitmap, look up CLUT colors, convert to 32bpp, write directly to the framebuffer. If QuickDraw can't do it right, we'll do it ourselves.
v13
Clean build, diagnostics removed. Confirmed working for icon backgrounds. No ghosting, no purple bars, no black fills. The desktop pattern renders correctly.
v14
Added EraseRect patch for text rename corruption. Both bugs fixed. Key discovery: Finder does text editing in its own CGrafPort, not WMgrCPort — so the EraseRect patch reads bkPixPat from whatever color port is current.
Port discovery: The EraseRect patch initially only checked WMgrCPort and did nothing. A magenta diagnostic (no port filter) proved EraseRect IS called for text areas — just through the Finder's own CGrafPort.
The Fix

Two Trap Patches

DesktopFix installs as a standard INIT at boot (shows a wrench icon in the startup parade). It tail-patches two QuickDraw traps — after the originals run and produce corruption, DesktopFix overwrites with correct pixels:

FillCRgn called
Original runs
Garbage at 32bpp
DesktopFix overwrites
Correct pixels!
FillCRgn (Trap $AA12) — Icon Backgrounds
  1. Original FillCRgn runs (produces corruption at 32bpp)
  2. Read PixPat's tile bitmap (patData — raw 8bpp pixel indices)
  3. Look up each pixel index in the color table (pmTable->ctTable[pixVal])
  4. Convert 16-bit-per-channel RGBColor to 32bpp framebuffer format
  5. Write pixels directly to the screen via GDevice PixMap
  6. PtInRgn respects the exact region shape (not just bounding box)
  • Only fires through WMgrCPort (Window Manager port)
  • Region bounding box 1–250px in each dimension
  • Below the menu bar
  • Does not overlap any window's structure region
  • Recursion guard prevents infinite loops
  • Re-validates pixel depth on every call (handles resolution changes)
EraseRect (Trap $A8A3) — Text Rename

Same rendering approach, but for rectangles. When you rename a desktop icon, the Finder erases the text editing area and its surroundings. At 32bpp, this produces the same corruption.

Key difference: the Finder does text editing in its own port, not WMgrCPort. So this patch reads the background PixPat from whatever CGrafPort is current (checking portVersion & $C000 to confirm it's a color port).

Technical Deep Dive

Why the Bug Exists

The Mac OS desktop background is drawn using a PixPat (pixel pattern) — typically the Mac OS watermark pattern stored at 8bpp with a CLUT (Color Lookup Table). When QuickDraw needs to fill a region with this pattern at 32bpp, it must convert 8bpp indexed pixels to 32bpp direct color. This conversion path is broken. It likely saw minimal testing because 32bpp was uncommon on 68k Macs.

PixPat Structure

PixPat (Handle) ├── patType: 1 // color pixel pattern ├── patMap: PixMapHandle │ ├── bounds // tile width x height │ ├── rowBytes // bytes per tile row (mask 0x3FFF) │ ├── pixelSize: 8 // 8 bits per pixel (indexed) │ └── pmTable: CTabHandle │ └── ctTable[0..255]: RGBColor entries └── patData: Handle └── raw 8bpp pixel indices (the tile bitmap)

The Pixel Conversion

For each pixel in the region or rectangle:

tx = x % tileW; // tile-relative X ty = y % tileH; // tile-relative Y pixVal = patData[ty * tileRowBytes + tx]; // 8bpp index cs = &ctab->ctTable[pixVal]; // CLUT lookup pixel32 = (cs->rgb.red >> 8) << 16 | // convert to 32bpp (cs->rgb.green >> 8) << 8 | (cs->rgb.blue >> 8); framebuffer[y][x] = pixel32; // direct write

What Won't Work

GetCPixel: Also broken at 32bpp. Returns black instead of the actual pixel value. Can't use it to sample existing screen pixels.
Corruption detection: Rainbow corruption has no consistent color signature. White checks, black checks, color distance thresholds, local variance analysis — all produce both false positives (painting over clean areas) and false negatives (missing actual corruption).
Solid color fill: The desktop is a tiled pattern (Mac OS watermarks), not a solid color. Filling with any single color — even sampled from adjacent pixels — looks wrong.
EraseRgn trap $A8A9: This is the WRONG trap number. The correct EraseRgn is $A8D4. Using $A8A9 patches something else entirely and causes a DOUBLE MMU FAULT crash.
The Hardware
Quadra 700 - the machine where the bug was first discovered
The Quadra 700 where it all started
v10 diagnostic showing solid purple fill over icon background regions
v10: purple diagnostic reveals FillCRgn regions
v12 showing black fill behind icon labels instead of the desktop pattern
v12: black fill — rendering but not passing through the pattern
QEMU Quadra 800 running Mac OS 8.1 at 32bpp with DesktopFix installed - clean desktop
The fix: clean desktop at Millions of Colors
Build & Install
Building

Requires the Retro68 cross-compiler toolchain for 68k Macs.

# Set up build directory (first time only) mkdir build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=/path/to/Retro68-build/toolchain/m68k-apple-macos/cmake/retro68.toolchain.cmake # Build make

Produces DesktopFix.dsk (HFS disk image) and DesktopFix.bin (MacBinary).

Installing

On an HFS disk image (for QEMU)

Using hfsutils:

# Extract from build disk hmount build/DesktopFix.dsk hcopy -m ":DesktopFix" DesktopFix.bin humount # Install to OS disk image hmount your-os-disk.hda hcopy -m DesktopFix.bin ":System Folder:Extensions:DesktopFix" humount

On a real Mac

Copy DesktopFix to your System Folder:Extensions folder and restart. A pre-built DesktopFix.sit (StuffIt archive) is available in the GitHub repository.

QEMU Setup

Reference QEMU command for Quadra 800 at 32bpp:

qemu-system-m68k -M q800 -m 128 \ -bios /path/to/Q800.ROM \ -display gtk -g 800x600x24 \ -drive file=pram.img,format=raw,if=mtd \ -device scsi-hd,scsi-id=0,drive=hd0 \ -drive file=your-os-disk.hda,media=disk,format=raw,if=none,id=hd0 \ -device nubus-virtio-mmio,romfile=declrom \ -device virtio-tablet-device \ -nic user,model=dp83932

Note: -g 800x600x24 requests 32bpp (the 24 refers to 24 bits of color data, stored in 32-bit pixels). This is what triggers the bug. At 256 Colors or Thousands of Colors, the desktop renders fine.