fbe2a7706d0f1baf4595d74ece97c8df870a2d9e
[xscreensaver] / hacks / bsod.c
1 /* xscreensaver, Copyright (c) 1998-2003 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * Blue Screen of Death: the finest in personal computer emulation.
12  * Concept cribbed from Stephen Martin <smartin@mks.com>;
13  * this version written by jwz, 4-Jun-98.
14  */
15
16 #include <math.h>
17 #include "screenhack.h"
18 #include "xpm-pixmap.h"
19 #include <stdio.h>
20 #include <time.h>
21 #include <sys/time.h>
22 #include <X11/Xutil.h>
23
24 #ifdef HAVE_XSHM_EXTENSION
25 #include "xshm.h"
26 #endif
27
28 #ifdef HAVE_UNAME
29 # include <sys/utsname.h>
30 #endif /* HAVE_UNAME */
31
32 #include "images/amiga.xpm"
33 #include "images/atari.xbm"
34 #include "images/mac.xbm"
35 #include "images/macbomb.xbm"
36 #include "images/hmac.xpm"
37
38 #undef countof
39 #define countof(x) (sizeof((x))/sizeof((*x)))
40
41 static int
42 draw_string (Display *dpy, Window window, GC gc, XGCValues *gcv,
43              XFontStruct *font,
44              int xoff, int yoff,
45              int win_width, int win_height,
46              const char *string, int delay)
47 {
48   int x, y;
49   int width = 0, height = 0, cw = 0;
50   int char_width, line_height;
51
52   const char *s = string;
53   const char *se = string;
54
55   /* This pretty much assumes fixed-width fonts */
56   char_width = (font->per_char
57                 ? font->per_char['n'-font->min_char_or_byte2].width
58                 : font->min_bounds.width);
59   line_height = font->ascent + font->descent + 1;
60
61   while (1)
62     {
63       if (*s == '\n' || !*s)
64         {
65           height++;
66           if (cw > width) width = cw;
67           cw = 0;
68           if (!*s) break;
69         }
70       else
71         cw++;
72       s++;
73     }
74
75   x = (win_width - (width * char_width)) / 2;
76   y = (win_height - (height * line_height)) / 2;
77
78   if (x < 0) x = 2;
79   if (y < 0) y = 2;
80
81   x += xoff;
82   y += yoff;
83
84   se = s = string;
85   while (1)
86     {
87       if (*s == '\n' || !*s)
88         {
89           int off = 0;
90           Bool flip = False;
91
92           if (*se == '@' || *se == '_')
93             {
94               if (*se == '@') flip = True;
95               se++;
96               off = (char_width * (width - (s - se))) / 2;
97             }
98
99           if (flip)
100             {
101               XSetForeground(dpy, gc, gcv->background);
102               XSetBackground(dpy, gc, gcv->foreground);
103             }
104
105           if (s != se)
106             XDrawImageString(dpy, window, gc, x+off, y+font->ascent, se, s-se);
107
108           if (flip)
109             {
110               XSetForeground(dpy, gc, gcv->foreground);
111               XSetBackground(dpy, gc, gcv->background);
112             }
113
114           se = s;
115           y += line_height;
116           if (!*s) break;
117           se = s+1;
118
119           if (delay)
120             {
121               XSync(dpy, False);
122               usleep(delay);
123             }
124         }
125       s++;
126     }
127
128   return width * char_width;
129 }
130
131
132 static Pixmap
133 double_pixmap(Display *dpy, GC gc, Visual *visual, int depth, Pixmap pixmap,
134              int pix_w, int pix_h)
135 {
136   int x, y;
137   Pixmap p2 = XCreatePixmap(dpy, pixmap, pix_w*2, pix_h*2, depth);
138   XImage *i1 = XGetImage(dpy, pixmap, 0, 0, pix_w, pix_h, ~0L, ZPixmap);
139   XImage *i2 = XCreateImage(dpy, visual, depth, ZPixmap, 0, 0,
140                             pix_w*2, pix_h*2, 8, 0);
141   i2->data = (char *) calloc(i2->height, i2->bytes_per_line);
142   for (y = 0; y < pix_h; y++)
143     for (x = 0; x < pix_w; x++)
144       {
145         unsigned long p = XGetPixel(i1, x, y);
146         XPutPixel(i2, x*2,   y*2,   p);
147         XPutPixel(i2, x*2+1, y*2,   p);
148         XPutPixel(i2, x*2,   y*2+1, p);
149         XPutPixel(i2, x*2+1, y*2+1, p);
150       }
151   free(i1->data); i1->data = 0;
152   XDestroyImage(i1);
153   XPutImage(dpy, p2, gc, i2, 0, 0, 0, 0, i2->width, i2->height);
154   free(i2->data); i2->data = 0;
155   XDestroyImage(i2);
156   XFreePixmap(dpy, pixmap);
157   return p2;
158 }
159
160
161 /* Sleep for N seconds and return False.  But if a key or mouse event is
162    seen, discard all pending key or mouse events, and return True.
163  */
164 static Bool
165 bsod_sleep(Display *dpy, int seconds)
166 {
167   int q = seconds * 4;
168   int quantum = 250000;
169
170   if (seconds == -1)
171     q = 1, quantum = 100000;
172
173   do
174     {
175       XSync(dpy, False);
176       while (XPending (dpy))
177         {
178           XEvent event;
179           XNextEvent (dpy, &event);
180           if (event.xany.type == ButtonPress)
181             return True;
182           if (event.xany.type == KeyPress)
183             {
184               KeySym keysym;
185               char c = 0;
186               XLookupString (&event.xkey, &c, 1, &keysym, 0);
187               if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
188                 return True;
189             }
190           screenhack_handle_event (dpy, &event);
191         }
192
193       if (q > 0)
194         {
195           q--;
196           usleep(quantum);
197         }
198     }
199   while (q > 0);
200
201   return False; 
202 }
203
204
205 static void
206 windows (Display *dpy, Window window, int delay, int which)
207 {
208   XGCValues gcv;
209   XWindowAttributes xgwa;
210   char *fontname;
211   const char *def_font = "fixed";
212   XFontStruct *font;
213   GC gc;
214
215   const char *w95 =
216     ("@Windows\n"
217      "A fatal exception 0E has occured at F0AD:42494C4C\n"
218      "the current application will be terminated.\n"
219      "\n"
220      "* Press any key to terminate the current application.\n"
221      "* Press CTRL+ALT+DELETE again to restart your computer.\n"
222      "  You will lose any unsaved information in all applications.\n"
223      "\n"
224      "\n"
225      "_Press any key to continue");
226
227   const char *wnt = /* from Jim Niemira <urmane@urmane.org> */
228     ("*** STOP: 0x0000001E (0x80000003,0x80106fc0,0x8025ea21,0xfd6829e8)\n"
229    "Unhandled Kernel exception c0000047 from fa8418b4 (8025ea21,fd6829e8)\n"
230    "\n"
231    "Dll Base Date Stamp - Name             Dll Base Date Stamp - Name\n"
232    "80100000 2be154c9 - ntoskrnl.exe       80400000 2bc153b0 - hal.dll\n"
233    "80258000 2bd49628 - ncrc710.sys        8025c000 2bd49688 - SCSIPORT.SYS \n"
234    "80267000 2bd49683 - scsidisk.sys       802a6000 2bd496b9 - Fastfat.sys\n"
235    "fa800000 2bd49666 - Floppy.SYS         fa810000 2bd496db - Hpfs_Rec.SYS\n"
236    "fa820000 2bd49676 - Null.SYS           fa830000 2bd4965a - Beep.SYS\n"
237    "fa840000 2bdaab00 - i8042prt.SYS       fa850000 2bd5a020 - SERMOUSE.SYS\n"
238    "fa860000 2bd4966f - kbdclass.SYS       fa870000 2bd49671 - MOUCLASS.SYS\n"
239    "fa880000 2bd9c0be - Videoprt.SYS       fa890000 2bd49638 - NCC1701E.SYS\n"
240    "fa8a0000 2bd4a4ce - Vga.SYS            fa8b0000 2bd496d0 - Msfs.SYS\n"
241    "fa8c0000 2bd496c3 - Npfs.SYS           fa8e0000 2bd496c9 - Ntfs.SYS\n"
242    "fa940000 2bd496df - NDIS.SYS           fa930000 2bd49707 - wdlan.sys\n"
243    "fa970000 2bd49712 - TDI.SYS            fa950000 2bd5a7fb - nbf.sys\n"
244    "fa980000 2bd72406 - streams.sys        fa9b0000 2bd4975f - ubnb.sys\n"
245    "fa9c0000 2bd5bfd7 - usbser.sys         fa9d0000 2bd4971d - netbios.sys\n"
246    "fa9e0000 2bd49678 - Parallel.sys       fa9f0000 2bd4969f - serial.SYS\n"
247    "faa00000 2bd49739 - mup.sys            faa40000 2bd4971f - SMBTRSUP.SYS\n"
248    "faa10000 2bd6f2a2 - srv.sys            faa50000 2bd4971a - afd.sys\n"
249    "faa60000 2bd6fd80 - rdr.sys            faaa0000 2bd49735 - bowser.sys\n"
250    "\n"
251    "Address dword dump Dll Base                                      - Name\n"
252    "801afc20 80106fc0 80106fc0 00000000 00000000 80149905 : "
253      "fa840000 - i8042prt.SYS\n"
254    "801afc24 80149905 80149905 ff8e6b8c 80129c2c ff8e6b94 : "
255      "8025c000 - SCSIPORT.SYS\n"
256    "801afc2c 80129c2c 80129c2c ff8e6b94 00000000 ff8e6b94 : "
257      "80100000 - ntoskrnl.exe\n"
258    "801afc34 801240f2 80124f02 ff8e6df4 ff8e6f60 ff8e6c58 : "
259      "80100000 - ntoskrnl.exe\n"
260    "801afc54 80124f16 80124f16 ff8e6f60 ff8e6c3c 8015ac7e : "
261      "80100000 - ntoskrnl.exe\n"
262    "801afc64 8015ac7e 8015ac7e ff8e6df4 ff8e6f60 ff8e6c58 : "
263      "80100000 - ntoskrnl.exe\n"
264    "801afc70 80129bda 80129bda 00000000 80088000 80106fc0 : "
265      "80100000 - ntoskrnl.exe\n"
266    "\n"
267    "Kernel Debugger Using: COM2 (Port 0x2f8, Baud Rate 19200)\n"
268    "Restart and set the recovery options in the system control panel\n"
269    "or the /CRASHDEBUG system start option. If this message reappears,\n"
270    "contact your system administrator or technical support group."
271      );
272
273   const char *w2ka =
274     ("*** STOP: 0x000000D1 (0xE1D38000,0x0000001C,0x00000000,0xF09D42DA)\n"
275      "DRIVER_IRQL_NOT_LESS_OR_EQUAL \n"
276      "\n"
277     "*** Address F09D42DA base at F09D4000, DateStamp 39f459ff - CRASHDD.SYS\n"
278      "\n"
279      "Beginning dump of physical memory\n");
280   const char *w2kb =
281     ("Physical memory dump complete. Contact your system administrator or\n"
282      "technical support group.\n");
283
284   const char *wmea =
285     ("    Windows protection error.  You need to restart your computer.");
286   const char *wmeb =
287     ("    System halted.");
288
289   if (which < 0 || which > 2) abort();
290
291   /* kludge to lump Win2K and WinME together; seems silly to add another
292      preference/command line option just for this little one. */
293   if (which == 2 && (random() % 2))
294     which = 3;
295
296   XGetWindowAttributes (dpy, window, &xgwa);
297
298   fontname = get_string_resource ((xgwa.height > 600
299                                    ? (which == 0 ? "windows95.font2" :
300                                       which == 1 ? "windowsNT.font2" :
301                                       which == 2 ? "windows2K.font2" :
302                                                    "windowsME.font2")
303                                    : (which == 0 ? "windows95.font" :
304                                       which == 1 ? "windowsNT.font" :
305                                       which == 2 ? "windows2K.font" :
306                                                    "windowsME.font")),
307                                   "Windows.Font");
308   if (!fontname || !*fontname) fontname = (char *)def_font;
309   font = XLoadQueryFont (dpy, fontname);
310   if (!font) font = XLoadQueryFont (dpy, def_font);
311   if (!font) exit(-1);
312   if (fontname && fontname != def_font)
313     free (fontname);
314
315   gcv.font = font->fid;
316   gcv.foreground = get_pixel_resource((which == 0 ? "windows95.foreground" :
317                                        which == 1 ? "windowsNT.foreground" :
318                                        which == 2 ? "windows2K.foreground" :
319                                                     "windowsME.foreground"),
320                                       "Windows.Foreground",
321                                       dpy, xgwa.colormap);
322   gcv.background = get_pixel_resource((which == 0 ? "windows95.background" :
323                                        which == 1 ? "windowsNT.background" :
324                                        which == 2 ? "windows2K.background" :
325                                                     "windowsME.background"),
326                                       "Windows.Background",
327                                       dpy, xgwa.colormap);
328   XSetWindowBackground(dpy, window, gcv.background);
329   XClearWindow(dpy, window);
330
331   gc = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
332
333   if (which == 0)
334     draw_string(dpy, window, gc, &gcv, font,
335                 0, 0, xgwa.width, xgwa.height, w95, 0);
336   else if (which == 1)
337     draw_string(dpy, window, gc, &gcv, font, 0, 0, 10, 10, wnt, 750);
338   else if (which == 2)
339     {
340       int line_height = font->ascent + font->descent + 1;
341       int x = 20;
342       int y = (xgwa.height / 4);
343
344       draw_string(dpy, window, gc, &gcv, font, x, y, 10, 10, w2ka, 750);
345       y += line_height * 6;
346       bsod_sleep(dpy, 4);
347       draw_string(dpy, window, gc, &gcv, font, x, y, 10, 10, w2kb, 750);
348     }
349   else if (which == 3)
350     {
351       int line_height = font->ascent + font->descent;
352       int x = 0;
353       int y = (xgwa.height - line_height * 3) / 2;
354       draw_string (dpy, window, gc, &gcv, font, x, y, 10, 10, wmea, 0);
355       y += line_height * 2;
356       x = draw_string (dpy, window, gc, &gcv, font, x, y, 10, 10, wmeb, 0);
357       y += line_height;
358       while (delay > 0)
359         {
360           XDrawImageString (dpy, window, gc, x, y, "_", 1);
361           XSync(dpy, False);
362           usleep(120000L);
363           XDrawImageString (dpy, window, gc, x, y, " ", 1);
364           XSync(dpy, False);
365           usleep(120000L);
366           if (bsod_sleep(dpy, 0))
367             delay = 0;
368           else
369             delay--;
370         }
371     }
372   else
373     abort();
374
375   XFreeGC(dpy, gc);
376   XSync(dpy, False);
377   bsod_sleep(dpy, delay);
378   XClearWindow(dpy, window);
379   XFreeFont(dpy, font);
380 }
381
382 static void
383 windows_31 (Display *dpy, Window window, int delay)
384 {
385   windows (dpy, window, delay, 0);
386 }
387
388 static void
389 windows_nt (Display *dpy, Window window, int delay)
390 {
391   windows (dpy, window, delay, 1);
392 }
393
394 static void
395 windows_2k (Display *dpy, Window window, int delay)
396 {
397   windows (dpy, window, delay, 2);
398 }
399
400
401 /* SCO OpenServer 5 panic, by Tom Kelly <tom@ancilla.toronto.on.ca>
402  */
403 static void
404 sco (Display *dpy, Window window, int delay)
405 {
406   XGCValues gcv;
407   XWindowAttributes xgwa;
408   char *fontname;
409   const char *def_font = "fixed";
410   XFontStruct *font;
411   GC gc;
412   int lines_1 = 0, lines_2 = 0, lines_3 = 0, lines_4 = 0;
413   const char *s;
414
415   const char *sco_panic_1 =
416     ("Unexpected trap in kernel mode:\n"
417      "\n"
418      "cr0 0x80010013     cr2  0x00000014     cr3 0x00000000  tlb  0x00000000\n"
419      "ss  0x00071054    uesp  0x00012055     efl 0x00080888  ipl  0x00000005\n"
420      "cs  0x00092585     eip  0x00544a4b     err 0x004d4a47  trap 0x0000000E\n"
421      "eax 0x0045474b     ecx  0x0042544b     edx 0x57687920  ebx  0x61726520\n"
422      "esp 0x796f7520     ebp  0x72656164     esi 0x696e6720  edi  0x74686973\n"
423      "ds  0x3f000000     es   0x43494c48     fs  0x43525343  gs   0x4f4d4b53\n"
424      "\n"
425      "PANIC: k_trap - kernel mode trap type 0x0000000E\n"
426      "Trying to dump 5023 pages to dumpdev hd (1/41), 63 pages per '.'\n"
427     );
428   const char *sco_panic_2 =
429    ("................................................................."
430     "..............\n"
431     );
432   const char *sco_panic_3 =
433     ("5023 pages dumped\n"
434      "\n"
435      "\n"
436      );
437   const char *sco_panic_4 =
438     ("**   Safe to Power Off   **\n"
439      "           - or -\n"
440      "** Press Any Key to Reboot **\n"
441     );
442
443   for (s = sco_panic_1; *s; s++) if (*s == '\n') lines_1++;
444   for (s = sco_panic_2; *s; s++) if (*s == '\n') lines_2++;
445   for (s = sco_panic_3; *s; s++) if (*s == '\n') lines_3++;
446   for (s = sco_panic_4; *s; s++) if (*s == '\n') lines_4++;
447
448   XGetWindowAttributes (dpy, window, &xgwa);
449
450   fontname = get_string_resource ((xgwa.height > 600
451                                    ? "sco.font2"
452                                    : "sco.font"),
453                                   "SCO.Font");
454   if (!fontname || !*fontname) fontname = (char *)def_font;
455   font = XLoadQueryFont (dpy, fontname);
456   if (!font) font = XLoadQueryFont (dpy, def_font);
457   if (!font) exit(-1);
458   if (fontname && fontname != def_font)
459     free (fontname);
460
461   gcv.font = font->fid;
462   gcv.foreground = get_pixel_resource(("sco.foreground"),
463                                       "SCO.Foreground",
464                                       dpy, xgwa.colormap);
465   gcv.background = get_pixel_resource(("sco.background"),
466                                       "SCO.Background",
467                                       dpy, xgwa.colormap);
468   XSetWindowBackground(dpy, window, gcv.background);
469   XClearWindow(dpy, window);
470
471   gc = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
472
473   draw_string(dpy, window, gc, &gcv, font,
474               10, xgwa.height - ((lines_1 + lines_2 + lines_3 + lines_4 + 1) *
475                                  (font->ascent + font->descent + 1)),
476               10, 10,
477               sco_panic_1, 0);
478   XSync(dpy, False);
479   for (s = sco_panic_2; *s; s++)
480     {
481       char *ss = strdup(sco_panic_2);
482       ss[s - sco_panic_2] = 0;
483       draw_string(dpy, window, gc, &gcv, font,
484                   10, xgwa.height - ((lines_2 + lines_3 + lines_4 + 1) *
485                                      (font->ascent + font->descent + 1)),
486                   10, 10,
487                   ss, 0);
488       XSync(dpy, False);
489       free(ss);
490       if (bsod_sleep (dpy, -1))
491         goto DONE;
492     }
493
494   draw_string(dpy, window, gc, &gcv, font,
495               10, xgwa.height - ((lines_3 + lines_4 + 1) *
496                                  (font->ascent + font->descent + 1)),
497               10, 10,
498               sco_panic_3, 0);
499   XSync(dpy, False);
500   if (bsod_sleep(dpy, 1))
501     goto DONE;
502   draw_string(dpy, window, gc, &gcv, font,
503               10, xgwa.height - ((lines_4 + 1) *
504                                  (font->ascent + font->descent + 1)),
505               10, 10,
506               sco_panic_4, 0);
507   XSync(dpy, False);
508
509   bsod_sleep(dpy, delay);
510  DONE:
511   XClearWindow(dpy, window);
512   XFreeGC(dpy, gc);
513   XFreeFont(dpy, font);
514 }
515
516
517 /* Linux (sparc) panic, by Tom Kelly <tom@ancilla.toronto.on.ca>
518  */
519 static void
520 sparc_linux (Display *dpy, Window window, int delay)
521 {
522   XGCValues gcv;
523   XWindowAttributes xgwa;
524   char *fontname;
525   const char *def_font = "fixed";
526   XFontStruct *font;
527   GC gc;
528   int lines = 1;
529   const char *s;
530
531   const char *linux_panic =
532     ("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
533         "Unable to handle kernel paging request at virtual address f0d4a000\n"
534         "tsk->mm->context = 00000014\n"
535         "tsk->mm->pgd = f26b0000\n"
536         "              \\|/ ____ \\|/\n"
537         "              \"@'/ ,. \\`@\"\n"
538         "              /_| \\__/ |_\\\n"
539         "                 \\__U_/\n"
540         "gawk(22827): Oops\n"
541         "PSR: 044010c1 PC: f001c2cc NPC: f001c2d0 Y: 00000000\n"
542         "g0: 00001000 g1: fffffff7 g2: 04401086 g3: 0001eaa0\n"
543         "g4: 000207dc g5: f0130400 g6: f0d4a018 g7: 00000001\n"
544         "o0: 00000000 o1: f0d4a298 o2: 00000040 o3: f1380718\n"
545         "o4: f1380718 o5: 00000200 sp: f1b13f08 ret_pc: f001c2a0\n"
546         "l0: efffd880 l1: 00000001 l2: f0d4a230 l3: 00000014\n"
547         "l4: 0000ffff l5: f0131550 l6: f012c000 l7: f0130400\n"
548         "i0: f1b13fb0 i1: 00000001 i2: 00000002 i3: 0007c000\n"
549         "i4: f01457c0 i5: 00000004 i6: f1b13f70 i7: f0015360\n"
550         "Instruction DUMP:\n"
551     );
552
553   for (s = linux_panic; *s; s++) if (*s == '\n') lines++;
554
555   XGetWindowAttributes (dpy, window, &xgwa);
556
557   fontname = get_string_resource ((xgwa.height > 600
558                                    ? "sparclinux.font2"
559                                    : "sparclinux.font"),
560                                   "SparcLinux.Font");
561   if (!fontname || !*fontname) fontname = (char *)def_font;
562   font = XLoadQueryFont (dpy, fontname);
563   if (!font) font = XLoadQueryFont (dpy, def_font);
564   if (!font) exit(-1);
565   if (fontname && fontname != def_font)
566     free (fontname);
567
568   gcv.font = font->fid;
569   gcv.foreground = get_pixel_resource(("sparclinux.foreground"),
570                                       "SparcLinux.Foreground",
571                                       dpy, xgwa.colormap);
572   gcv.background = get_pixel_resource(("sparclinux.background"),
573                                       "SparcLinux.Background",
574                                       dpy, xgwa.colormap);
575   XSetWindowBackground(dpy, window, gcv.background);
576   XClearWindow(dpy, window);
577
578   gc = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
579
580   draw_string(dpy, window, gc, &gcv, font,
581               10, xgwa.height - (lines * (font->ascent + font->descent + 1)),
582               10, 10,
583               linux_panic, 0);
584   XFreeGC(dpy, gc);
585   XSync(dpy, False);
586   bsod_sleep(dpy, delay);
587   XClearWindow(dpy, window);
588   XFreeFont(dpy, font);
589 }
590
591 /* BSD Panic by greywolf@starwolf.com - modeled after the Linux panic above.
592    By Grey Wolf <greywolf@siteROCK.com>
593  */
594 static void
595 bsd (Display *dpy, Window window, int delay)
596 {
597   XGCValues gcv;
598   XWindowAttributes xgwa;
599   char *fontname;
600   const char *def_font = "fixed";
601   XFontStruct *font;
602   GC gc;
603   int lines = 1;
604   int i, n, b;
605   const char *rbstr, *panicking;
606   char syncing[80], bbuf[5], *bp;
607
608   const char *panicstr[] =
609    {"panic: ifree: freeing free inode",
610     "panic: blkfree: freeing free block",
611     "panic: improbability coefficient below zero",
612     "panic: cgsixmmap",
613     "panic: crazy interrupts",
614     "panic: nmi",
615     "panic: attempted windows install",
616     "panic: don't",
617     "panic: free inode isn't",
618     "panic: cpu_fork: curproc",
619     "panic: malloc: out of space in kmem_map",
620     "panic: vogon starship detected",
621     "panic: teleport chamber: out of order",
622     "panic: Brain fried - core dumped"};
623      
624   for (i = 0; i < sizeof(syncing); i++)
625     syncing[i] = 0;
626
627   i = (random() & 0xffff) % (sizeof(panicstr) / sizeof(*panicstr));
628
629   panicking = panicstr[i];
630   strcpy(syncing, "Syncing disks: ");
631
632   b = (random() & 0xff) % 40;
633   for (n = 0; (n < 20) && (b > 0); n++)
634     {
635       if (i)
636         {
637           i = (random() & 0x7);
638           b -= (random() & 0xff) % 20;
639           if (b < 0)
640             b = 0;
641         }
642       sprintf (bbuf, "%d ", b);
643       strcat (syncing, bbuf);
644     }
645
646   if (b)
647     rbstr = "damn!";
648   else
649     rbstr = "sunk!";
650
651   lines = 5;
652
653   XGetWindowAttributes (dpy, window, &xgwa);
654
655   fontname = get_string_resource ((xgwa.height > 600
656                                    ? "bsd.font2"
657                                    : "bsd.font"),
658                                   "BSD.Font");
659   if (!fontname || !*fontname) fontname = (char *)def_font;
660   font = XLoadQueryFont (dpy, fontname);
661   if (!font) font = XLoadQueryFont (dpy, def_font);
662   if (!font) exit(-1);
663   if (fontname && fontname != def_font)
664     free (fontname);
665
666   gcv.font = font->fid;
667   gcv.foreground = get_pixel_resource(("bsd.foreground"),
668                                       "BSD.Foreground",
669                                       dpy, xgwa.colormap);
670   gcv.background = get_pixel_resource(("bsd.background"),
671                                       "BSD.Background",
672                                       dpy, xgwa.colormap);
673   XSetWindowBackground(dpy, window, gcv.background);
674   XClearWindow(dpy, window);
675
676   gc = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
677
678   draw_string(dpy, window, gc, &gcv, font,
679               10, xgwa.height - (lines * (font->ascent + font->descent + 1)),
680               10, 10,
681               panicking, 0);
682   XSync(dpy, False);
683   lines--;
684
685   for (bp = syncing; *bp;)
686     {
687       char *bsd_bufs, oc = 0;
688       for (;*bp && (*bp != ' '); bp++)
689         ;
690       if (*bp == ' ')
691         {
692           oc = *bp;
693           *bp = 0;
694         }
695       bsd_bufs = strdup(syncing);
696       draw_string(dpy, window, gc, &gcv, font,
697                   10,
698                   xgwa.height - (lines * (font->ascent + font->descent + 1)),
699                   10, 10,
700                   bsd_bufs, 0);
701       XSync(dpy, False);
702       free(bsd_bufs);
703       if (oc)
704         *bp = oc;
705       if (bsod_sleep(dpy, -1))
706         goto DONE;
707       bp++;
708     }
709
710   lines--;
711   
712   draw_string(dpy, window, gc, &gcv, font,
713               10, xgwa.height - (lines * (font->ascent + font->descent + 1)),
714               10, 10,
715               rbstr, 0);
716   lines--;
717   draw_string(dpy, window, gc, &gcv, font,
718               10, xgwa.height - (lines * (font->ascent + font->descent + 1)),
719               10, 10,
720               "Rebooting", 0);
721
722   XFreeGC(dpy, gc);
723   XSync(dpy, False);
724   bsod_sleep(dpy, delay);
725
726 DONE:
727   XClearWindow(dpy, window);
728   XFreeFont(dpy, font);
729 }
730
731 static void
732 amiga (Display *dpy, Window window, int delay)
733 {
734   XGCValues gcv;
735   XWindowAttributes xgwa;
736   char *fontname;
737   const char *def_font = "fixed";
738   XFontStruct *font;
739   GC gc, gc2;
740   int height;
741   unsigned long fg, bg, bg2;
742   Pixmap pixmap = 0;
743   int pix_w = 0, pix_h = 0;
744   int string_width;
745   int margin;
746
747   const char *string =
748     ("_Software failure.  Press left mouse button to continue.\n"
749      "_Guru Meditation #00000003.00C01570");
750
751   XGetWindowAttributes (dpy, window, &xgwa);
752
753   fontname = get_string_resource ((xgwa.height > 600
754                                    ? "amiga.font2" : "amiga.font"),
755                                   "Amiga.Font");
756   if (!fontname || !*fontname) fontname = (char *)def_font;
757   font = XLoadQueryFont (dpy, fontname);
758   if (!font) font = XLoadQueryFont (dpy, def_font);
759   if (!font) exit(-1);
760   if (fontname && fontname != def_font)
761     free (fontname);
762
763   gcv.font = font->fid;
764   fg = gcv.foreground = get_pixel_resource("amiga.foreground",
765                                            "Amiga.Foreground",
766                                            dpy, xgwa.colormap);
767   bg = gcv.background = get_pixel_resource("amiga.background",
768                                            "Amiga.Background",
769                                            dpy, xgwa.colormap);
770   bg2 = get_pixel_resource("amiga.background2", "Amiga.Background",
771                            dpy, xgwa.colormap);
772   XSetWindowBackground(dpy, window, bg2);
773   XClearWindow(dpy, window);
774
775   gc = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
776   gcv.background = fg; gcv.foreground = bg;
777   gc2 = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
778
779   height = (font->ascent + font->descent) * 6;
780
781 #if defined(HAVE_GDK_PIXBUF) || defined (HAVE_XPM)
782   pixmap = xpm_data_to_pixmap (dpy, window, (char **) amiga_hand,
783                                &pix_w, &pix_h, 0);
784 #endif /* HAVE_GDK_PIXBUF || HAVE_XPM */
785
786   if (pixmap && xgwa.height > 600)      /* scale up the bitmap */
787     {
788       pixmap = double_pixmap(dpy, gc, xgwa.visual, xgwa.depth,
789                              pixmap, pix_w, pix_h);
790       pix_w *= 2;
791       pix_h *= 2;
792     }
793
794   if (pixmap)
795     {
796       int x = (xgwa.width - pix_w) / 2;
797       int y = ((xgwa.height - pix_h) / 2);
798       XCopyArea(dpy, pixmap, window, gc, 0, 0, pix_w, pix_h, x, y);
799
800       XSync(dpy, False);
801       bsod_sleep(dpy, 2);
802
803       XCopyArea(dpy, pixmap, window, gc, 0, 0, pix_w, pix_h, x, y + height);
804       XClearArea(dpy, window, 0, 0, xgwa.width, y + height, False);
805       XFreePixmap(dpy, pixmap);
806     }
807
808   XFillRectangle(dpy, window, gc2, 0, 0, xgwa.width, height);
809   margin = font->ascent;
810   string_width = draw_string(dpy, window, gc, &gcv, font,
811                              margin, 0,
812                              xgwa.width - (margin * 2), height,
813                              string, 0);
814   {
815     GC gca = gc;
816     while (delay > 0)
817       {
818         int x2;
819         XFillRectangle(dpy, window, gca, 0, 0, xgwa.width, margin);
820         XFillRectangle(dpy, window, gca, 0, 0, margin, height);
821         XFillRectangle(dpy, window, gca,
822                        0, height - margin, xgwa.width, margin);
823         x2 = margin + string_width;
824         if (x2 < xgwa.width - margin) x2 = xgwa.width - margin;
825         XFillRectangle(dpy, window, gca, x2, 0, margin, height);
826
827         gca = (gca == gc ? gc2 : gc);
828         XSync(dpy, False);
829         if (bsod_sleep(dpy, 1))
830           break;
831         delay--;
832       }
833   }
834
835   XFreeGC(dpy, gc);
836   XFreeGC(dpy, gc2);
837   XSync(dpy, False);
838   XClearWindow(dpy, window);
839   XFreeFont(dpy, font);
840 }
841
842
843 /* Atari ST, by Marcus Herbert <rhoenie@nobiscum.de>
844    Marcus had this to say:
845
846         Though I still have my Atari somewhere, I hardly remember
847         the meaning of the bombs. I think 9 bombs was "bus error" or
848         something like that.  And you often had a few bombs displayed
849         quickly and then the next few ones coming up step by step.
850         Perhaps somebody else can tell you more about it..  its just
851         a quick hack :-}
852  */
853 static void
854 atari (Display *dpy, Window window, int delay)
855 {
856         
857   XGCValues gcv;
858   XWindowAttributes xgwa;
859   const char *def_font = "fixed";
860   XFontStruct *font;
861   GC gc;
862   Pixmap pixmap = 0;
863   int pix_w = atari_width;
864   int pix_h = atari_height;
865   int offset;
866   int i, x, y;
867
868   XGetWindowAttributes (dpy, window, &xgwa);
869
870   font = XLoadQueryFont (dpy, def_font);
871   if (!font) exit(-1);
872                 
873   gcv.font = font->fid;
874   gcv.foreground = get_pixel_resource("atari.foreground", "Atari.Foreground",
875                                       dpy, xgwa.colormap);
876   gcv.background = get_pixel_resource("atari.background", "Atari.Background",
877                                       dpy, xgwa.colormap);
878
879   XSetWindowBackground(dpy, window, gcv.background);
880   XClearWindow(dpy, window);
881
882   gc = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
883
884   pixmap = XCreatePixmapFromBitmapData(dpy, window, (char *) atari_bits,
885                                        pix_w, pix_h,
886                                        gcv.foreground, gcv.background,
887                                        xgwa.depth);
888   pixmap = double_pixmap(dpy, gc, xgwa.visual, xgwa.depth,
889                          pixmap, pix_w, pix_h);
890   pix_w *= 2;
891   pix_h *= 2;
892
893   offset = pix_w + 2;
894   x = 5;
895   y = (xgwa.height - (xgwa.height / 5));
896   if (y < 0) y = 0;
897
898   for (i=0 ; i<7 ; i++) {
899     XCopyArea(dpy, pixmap, window, gc, 0, 0, pix_w, pix_h,
900               (x + (i*offset)), y);
901   }  
902   
903   for (i=7 ; i<10 ; i++) {
904     if (bsod_sleep(dpy, 1))
905       goto DONE;
906     XCopyArea(dpy, pixmap, window, gc, 0, 0, pix_w, pix_h,
907               (x + (i*offset)), y);
908   }
909
910   bsod_sleep(dpy, delay);
911  DONE:
912   XFreePixmap(dpy, pixmap);
913   XFreeGC(dpy, gc);
914   XSync(dpy, False);
915   XClearWindow(dpy, window);
916   XFreeFont(dpy, font);
917 }
918
919
920 static void
921 mac (Display *dpy, Window window, int delay)
922 {
923   XGCValues gcv;
924   XWindowAttributes xgwa;
925   char *fontname;
926   const char *def_font = "fixed";
927   XFontStruct *font;
928   GC gc;
929   Pixmap pixmap = 0;
930   int pix_w = mac_width;
931   int pix_h = mac_height;
932   int offset = mac_height * 4;
933   int i;
934
935   const char *string = ("0 0 0 0 0 0 0 F\n"
936                         "0 0 0 0 0 0 0 3");
937
938   XGetWindowAttributes (dpy, window, &xgwa);
939
940   fontname = get_string_resource ("mac.font", "Mac.Font");
941   if (!fontname || !*fontname) fontname = (char *)def_font;
942   font = XLoadQueryFont (dpy, fontname);
943   if (!font) font = XLoadQueryFont (dpy, def_font);
944   if (!font) exit(-1);
945   if (fontname && fontname != def_font)
946     free (fontname);
947
948   gcv.font = font->fid;
949   gcv.foreground = get_pixel_resource("mac.foreground", "Mac.Foreground",
950                                       dpy, xgwa.colormap);
951   gcv.background = get_pixel_resource("mac.background", "Mac.Background",
952                                       dpy, xgwa.colormap);
953   XSetWindowBackground(dpy, window, gcv.background);
954   XClearWindow(dpy, window);
955
956   gc = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
957
958   pixmap = XCreatePixmapFromBitmapData(dpy, window, (char *) mac_bits,
959                                        mac_width, mac_height,
960                                        gcv.foreground,
961                                        gcv.background,
962                                        xgwa.depth);
963
964   for(i = 0; i < 2; i++)
965     {
966       pixmap = double_pixmap(dpy, gc, xgwa.visual, xgwa.depth,
967                              pixmap, pix_w, pix_h);
968       pix_w *= 2; pix_h *= 2;
969     }
970
971   {
972     int x = (xgwa.width - pix_w) / 2;
973     int y = (((xgwa.height + offset) / 2) -
974              pix_h -
975              (font->ascent + font->descent) * 2);
976     if (y < 0) y = 0;
977     XCopyArea(dpy, pixmap, window, gc, 0, 0, pix_w, pix_h, x, y);
978     XFreePixmap(dpy, pixmap);
979   }
980
981   draw_string(dpy, window, gc, &gcv, font, 0, 0,
982               xgwa.width, xgwa.height + offset, string, 0);
983
984   XFreeGC(dpy, gc);
985   XSync(dpy, False);
986   bsod_sleep(dpy, delay);
987   XClearWindow(dpy, window);
988   XFreeFont(dpy, font);
989 }
990
991 static void
992 macsbug (Display *dpy, Window window, int delay)
993 {
994   XGCValues gcv;
995   XWindowAttributes xgwa;
996   char *fontname;
997   const char *def_font = "fixed";
998   XFontStruct *font;
999   GC gc, gc2;
1000
1001   int char_width, line_height;
1002   int col_right, row_top, row_bottom, page_right, page_bottom, body_top;
1003   int xoff, yoff;
1004
1005   const char *left = ("    SP     \n"
1006                       " 04EB0A58  \n"
1007                       "58 00010000\n"
1008                       "5C 00010000\n"
1009                       "   ........\n"
1010                       "60 00000000\n"
1011                       "64 000004EB\n"
1012                       "   ........\n"
1013                       "68 0000027F\n"
1014                       "6C 2D980035\n"
1015                       "   ....-..5\n"
1016                       "70 00000054\n"
1017                       "74 0173003E\n"
1018                       "   ...T.s.>\n"
1019                       "78 04EBDA76\n"
1020                       "7C 04EBDA8E\n"
1021                       "   .S.L.a.U\n"
1022                       "80 00000000\n"
1023                       "84 000004EB\n"
1024                       "   ........\n"
1025                       "88 00010000\n"
1026                       "8C 00010000\n"
1027                       "   ...{3..S\n"
1028                       "\n"
1029                       "\n"
1030                       " CurApName \n"
1031                       "  Finder   \n"
1032                       "\n"
1033                       " 32-bit VM \n"
1034                       "SR Smxnzvc0\n"
1035                       "D0 04EC0062\n"
1036                       "D1 00000053\n"
1037                       "D2 FFFF0100\n"
1038                       "D3 00010000\n"
1039                       "D4 00010000\n"
1040                       "D5 04EBDA76\n"
1041                       "D6 04EBDA8E\n"
1042                       "D7 00000001\n"
1043                       "\n"
1044                       "A0 04EBDA76\n"
1045                       "A1 04EBDA8E\n"
1046                       "A2 A0A00060\n"
1047                       "A3 027F2D98\n"
1048                       "A4 027F2E58\n"
1049                       "A5 04EC04F0\n"
1050                       "A6 04EB0A86\n"
1051                       "A7 04EB0A58");
1052   const char *bottom = ("  _A09D\n"
1053                         "     +00884    40843714     #$0700,SR         "
1054                         "                  ; A973        | A973\n"
1055                         "     +00886    40843765     *+$0400           "
1056                         "                                | 4A1F\n"
1057                         "     +00888    40843718     $0004(A7),([0,A7[)"
1058                         "                  ; 04E8D0AE    | 66B8");
1059
1060 #if 0
1061   const char *body = ("Bus Error at 4BF6D6CC\n"
1062                       "while reading word from 4BF6D6CC in User data space\n"
1063                       " Unable to access that address\n"
1064                       "  PC: 2A0DE3E6\n"
1065                       "  Frame Type: B008");
1066 #else
1067   const char * body = ("PowerPC unmapped memory exception at 003AFDAC "
1068                                                 "BowelsOfTheMemoryMgr+04F9C\n"
1069                       " Calling chain using A6/R1 links\n"
1070                       "  Back chain  ISA  Caller\n"
1071                       "  00000000    PPC  28C5353C  __start+00054\n"
1072                       "  24DB03C0    PPC  28B9258C  main+0039C\n"
1073                       "  24DB0350    PPC  28B9210C  MainEvent+00494\n"
1074                       "  24DB02B0    PPC  28B91B40  HandleEvent+00278\n"
1075                       "  24DB0250    PPC  28B83DAC  DoAppleEvent+00020\n"
1076                       "  24DB0210    PPC  FFD3E5D0  "
1077                                                 "AEProcessAppleEvent+00020\n"
1078                       "  24DB0132    68K  00589468\n"
1079                       "  24DAFF8C    68K  00589582\n"
1080                       "  24DAFF26    68K  00588F70\n"
1081                       "  24DAFEB3    PPC  00307098  "
1082                                                 "EmToNatEndMoveParams+00014\n"
1083                       "  24DAFE40    PPC  28B9D0B0  DoScript+001C4\n"
1084                       "  24DAFDD0    PPC  28B9C35C  RunScript+00390\n"
1085                       "  24DAFC60    PPC  28BA36D4  run_perl+000E0\n"
1086                       "  24DAFC10    PPC  28BC2904  perl_run+002CC\n"
1087                       "  24DAFA80    PPC  28C18490  Perl_runops+00068\n"
1088                       "  24DAFA30    PPC  28BE6CC0  Perl_pp_backtick+000FC\n"
1089                       "  24DAF9D0    PPC  28BA48B8  Perl_my_popen+00158\n"
1090                       "  24DAF980    PPC  28C5395C  sfclose+00378\n"
1091                       "  24DAF930    PPC  28BA568C  free+0000C\n"
1092                       "  24DAF8F0    PPC  28BA6254  pool_free+001D0\n"
1093                       "  24DAF8A0    PPC  FFD48F14  DisposePtr+00028\n"
1094                       "  24DAF7C9    PPC  00307098  "
1095                                                 "EmToNatEndMoveParams+00014\n"
1096                       "  24DAF780    PPC  003AA180  __DisposePtr+00010");
1097 #endif
1098
1099   const char *s;
1100   int body_lines = 1;
1101
1102   for (s = body; *s; s++) if (*s == '\n') body_lines++;
1103
1104   XGetWindowAttributes (dpy, window, &xgwa);
1105
1106   fontname = get_string_resource ((xgwa.height > 850
1107                                    ? "macsbug.font3"
1108                                    : (xgwa.height > 700
1109                                       ? "macsbug.font2"
1110                                       : "macsbug.font")),
1111                                   "MacsBug.Font");
1112   if (!fontname || !*fontname) fontname = (char *)def_font;
1113   font = XLoadQueryFont (dpy, fontname);
1114   if (!font) font = XLoadQueryFont (dpy, def_font);
1115   if (!font) exit(-1);
1116   if (fontname && fontname != def_font)
1117     free (fontname);
1118
1119   gcv.font = font->fid;
1120   gcv.foreground = get_pixel_resource("macsbug.foreground",
1121                                       "MacsBug.Foreground",
1122                                       dpy, xgwa.colormap);
1123   gcv.background = get_pixel_resource("macsbug.background",
1124                                       "MacsBug.Background",
1125                                       dpy, xgwa.colormap);
1126
1127   gc = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
1128
1129   gcv.foreground = gcv.background;
1130   gc2 = XCreateGC(dpy, window, GCForeground, &gcv);
1131
1132   XSetWindowBackground(dpy, window,
1133                        get_pixel_resource("macsbug.borderColor",
1134                                           "MacsBug.BorderColor",
1135                                           dpy, xgwa.colormap));
1136   XClearWindow(dpy, window);
1137
1138   char_width = (font->per_char
1139                 ? font->per_char['n'-font->min_char_or_byte2].width
1140                 : font->min_bounds.width);
1141   line_height = font->ascent + font->descent + 1;
1142
1143   col_right = char_width * 12;
1144   page_bottom = line_height * 47;
1145
1146   if (page_bottom > xgwa.height) page_bottom = xgwa.height;
1147
1148   row_bottom = page_bottom - line_height;
1149   row_top = row_bottom - (line_height * 4);
1150   page_right = col_right + (char_width * 88);
1151   body_top = row_top - (line_height * body_lines);
1152
1153   page_bottom += 2;
1154   row_bottom += 2;
1155   body_top -= 4;
1156
1157   xoff = (xgwa.width - page_right) / 2;
1158   yoff = (xgwa.height - page_bottom) / 2;
1159   if (xoff < 0) xoff = 0;
1160   if (yoff < 0) yoff = 0;
1161
1162   XFillRectangle(dpy, window, gc2, xoff, yoff, page_right, page_bottom);
1163
1164   draw_string(dpy, window, gc, &gcv, font, xoff, yoff, 10, 10, left, 0);
1165   draw_string(dpy, window, gc, &gcv, font, xoff+col_right, yoff+row_top,
1166               10, 10, bottom, 0);
1167
1168   XFillRectangle(dpy, window, gc, xoff + col_right, yoff, 2, page_bottom);
1169   XDrawLine(dpy, window, gc,
1170             xoff+col_right, yoff+row_top, xoff+page_right, yoff+row_top);
1171   XDrawLine(dpy, window, gc,
1172             xoff+col_right, yoff+row_bottom, xoff+page_right, yoff+row_bottom);
1173   XDrawRectangle(dpy, window, gc,  xoff, yoff, page_right, page_bottom);
1174
1175   if (body_top > 4)
1176     body_top = 4;
1177
1178   draw_string(dpy, window, gc, &gcv, font,
1179               xoff + col_right + char_width, yoff + body_top, 10, 10, body,
1180               500);
1181
1182   while (delay > 0)
1183     {
1184       XDrawLine(dpy, window, gc,
1185                 xoff+col_right+(char_width/2)+2, yoff+row_bottom+3,
1186                 xoff+col_right+(char_width/2)+2, yoff+page_bottom-3);
1187       XSync(dpy, False);
1188       usleep(666666L);
1189       XDrawLine(dpy, window, gc2,
1190                 xoff+col_right+(char_width/2)+2, yoff+row_bottom+3,
1191                 xoff+col_right+(char_width/2)+2, yoff+page_bottom-3);
1192       XSync(dpy, False);
1193       usleep(333333L);
1194       if (bsod_sleep(dpy, 0))
1195         break;
1196       delay--;
1197     }
1198
1199   XFreeGC(dpy, gc);
1200   XFreeGC(dpy, gc2);
1201   XClearWindow(dpy, window);
1202   XFreeFont(dpy, font);
1203 }
1204
1205 static void
1206 mac1 (Display *dpy, Window window, int delay)
1207 {
1208   XGCValues gcv;
1209   XWindowAttributes xgwa;
1210   GC gc;
1211   Pixmap pixmap = 0;
1212   int pix_w = macbomb_width;
1213   int pix_h = macbomb_height;
1214
1215   XGetWindowAttributes (dpy, window, &xgwa);
1216
1217   gcv.foreground = get_pixel_resource("mac1.foreground", "Mac.Foreground",
1218                                       dpy, xgwa.colormap);
1219   gcv.background = get_pixel_resource("mac1.background", "Mac.Background",
1220                                       dpy, xgwa.colormap);
1221   XSetWindowBackground(dpy, window, gcv.background);
1222   XClearWindow(dpy, window);
1223
1224   gc = XCreateGC(dpy, window, GCForeground|GCBackground, &gcv);
1225
1226   pixmap = XCreatePixmapFromBitmapData(dpy, window, (char *) macbomb_bits,
1227                                        macbomb_width, macbomb_height,
1228                                        gcv.foreground,
1229                                        gcv.background,
1230                                        xgwa.depth);
1231
1232   {
1233     int x = (xgwa.width - pix_w) / 2;
1234     int y = (xgwa.height - pix_h) / 2;
1235     if (y < 0) y = 0;
1236     XFillRectangle (dpy, window, gc, 0, 0, xgwa.width, xgwa.height);
1237     XSync(dpy, False);
1238     if (bsod_sleep(dpy, 1))
1239       goto DONE;
1240     XCopyArea(dpy, pixmap, window, gc, 0, 0, pix_w, pix_h, x, y);
1241   }
1242
1243  DONE:
1244   XFreeGC(dpy, gc);
1245   XFreePixmap(dpy, pixmap);
1246   XSync(dpy, False);
1247   bsod_sleep(dpy, delay);
1248   XClearWindow(dpy, window);
1249 }
1250
1251
1252 static void
1253 macx (Display *dpy, Window window, int delay)
1254 {
1255   XGCValues gcv;
1256   XWindowAttributes xgwa;
1257   char *fontname = 0;
1258   const char *def_font = "fixed";
1259   XFontStruct *font;
1260   GC gc;
1261
1262   const char *macx_panic =
1263    ("panic(cpu 0): Unable to find driver for this platform: "
1264     "\"PowerMac 3,5\".\n"
1265     "\n"
1266     "backtrace: 0x0008c2f4 0x0002a7a0 0x001f0204 0x001d4e4c 0x001d4c5c "
1267     "0x001a56cc 0x01d5dbc 0x001c621c 0x00037430 0x00037364\n"
1268     "\n"
1269     "\n"
1270     "\n"
1271     "No debugger configured - dumping debug information\n"
1272     "\n"
1273     "version string : Darwin Kernel Version 1.3:\n"
1274     "Thu Mar  1 06:56:40 PST 2001; root:xnu/xnu-123.5.obj~1/RELEASE_PPC\n"
1275     "\n"
1276     "\n"
1277     "\n"
1278     "\n"
1279     "DBAT0: 00000000 00000000\n"
1280     "DBAT1: 00000000 00000000\n"
1281     "DBAT2: 80001FFE 8000003A\n"
1282     "DBAT3: 90001FFE 9000003A\n"
1283     "MSR=00001030\n"
1284     "backtrace: 0x0008c2f4 0x0002a7a0 0x001f0204 0x001d4e4c 0x001d4c5c "
1285     "0x001a56cc 0x01d5dbc 0x001c621c 0x00037430 0x00037364\n"
1286     "\n"
1287     "panic: We are hanging here...\n");
1288
1289   XGetWindowAttributes (dpy, window, &xgwa);
1290
1291   gcv.background = get_pixel_resource("macX.background",
1292                                       "MacX.Background",
1293                                       dpy, xgwa.colormap);
1294   XSetWindowBackground(dpy, window, gcv.background);
1295   XClearWindow(dpy, window);
1296
1297   fontname = get_string_resource ((xgwa.height > 900
1298                                    ? "macX.font2"
1299                                    : "macX.font"),
1300                                   "MacX.Font");
1301   if (!fontname || !*fontname) fontname = (char *)def_font;
1302   font = XLoadQueryFont (dpy, fontname);
1303   if (!font) font = XLoadQueryFont (dpy, def_font);
1304   if (!font) exit(-1);
1305   if (fontname && fontname != def_font)
1306     free (fontname);
1307
1308   gcv.font = font->fid;
1309   gcv.foreground = get_pixel_resource("macsbug.foreground",
1310                                       "MacsBug.Foreground",
1311                                       dpy, xgwa.colormap);
1312   gcv.background = get_pixel_resource("macsbug.background",
1313                                       "MacsBug.Background",
1314                                       dpy, xgwa.colormap);
1315
1316
1317   gcv.foreground = get_pixel_resource("macX.textForeground",
1318                                       "MacX.TextForeground",
1319                                       dpy, xgwa.colormap);
1320   gcv.background = get_pixel_resource("macX.textBackground",
1321                                       "MacX.TextBackground",
1322                                       dpy, xgwa.colormap);
1323   gc = XCreateGC(dpy, window, GCForeground|GCBackground|GCFont, &gcv);
1324
1325 #if defined(HAVE_GDK_PIXBUF) || defined (HAVE_XPM)
1326   {
1327     Pixmap pixmap = 0;
1328     Pixmap mask = 0;
1329     int x, y, pix_w, pix_h;
1330     pixmap = xpm_data_to_pixmap (dpy, window, (char **) happy_mac,
1331                                  &pix_w, &pix_h, &mask);
1332
1333     x = (xgwa.width - pix_w) / 2;
1334     y = (xgwa.height - pix_h) / 2;
1335     if (y < 0) y = 0;
1336     XSync(dpy, False);
1337     bsod_sleep(dpy, 2);
1338     XSetClipMask (dpy, gc, mask);
1339     XSetClipOrigin (dpy, gc, x, y);
1340     XCopyArea (dpy, pixmap, window, gc, 0, 0, pix_w, pix_h, x, y);
1341     XSetClipMask (dpy, gc, None);
1342     XFreePixmap (dpy, pixmap);
1343   }
1344 #endif /* HAVE_GDK_PIXBUF || HAVE_XPM */
1345
1346   bsod_sleep(dpy, 3);
1347
1348   {
1349     const char *s;
1350     int x = 0, y = 0;
1351     int char_width, line_height;
1352     char_width = (font->per_char
1353                   ? font->per_char['n'-font->min_char_or_byte2].width
1354                   : font->min_bounds.width);
1355     line_height = font->ascent + font->descent;
1356
1357     s = macx_panic;
1358     y = font->ascent;
1359     while (*s)
1360       {
1361         int ox = x;
1362         int oy = y;
1363         if (*s == '\n' || x + char_width >= xgwa.width)
1364           {
1365             x = 0;
1366             y += line_height;
1367           }
1368
1369         if (*s == '\n')
1370           {
1371             /* Note that to get this goofy effect, they must be actually
1372                emitting LF CR at the end of each line instead of CR LF!
1373              */
1374             XDrawImageString (dpy, window, gc, ox, oy, " ", 1);
1375             XDrawImageString (dpy, window, gc, ox, y, " ", 1);
1376           }
1377         else
1378           {
1379             XDrawImageString (dpy, window, gc, x, y, s, 1);
1380             x += char_width;
1381           }
1382         s++;
1383       }
1384   }
1385
1386   XFreeGC(dpy, gc);
1387   XSync(dpy, False);
1388   bsod_sleep(dpy, delay);
1389   XClearWindow(dpy, window);
1390 }
1391
1392 \f
1393 /* blit damage
1394  *
1395  * by Martin Pool <mbp@samba.org>, Feb 2000.
1396  *
1397  * This is meant to look like the preferred failure mode of NCD
1398  * Xterms.  The parameters for choosing what to copy where might not
1399  * be quite right, but it looks about ugly enough.
1400  */
1401 static void
1402 blitdamage (Display *dpy, Window window, int delay)
1403 {
1404   XGCValues gcv;
1405   XWindowAttributes xwa;
1406   GC gc0;
1407   int i;
1408   int delta_x = 0, delta_y = 0;
1409   int w, h;
1410   int chunk_h, chunk_w;
1411   int steps;
1412   long gc_mask = 0;
1413   int src_x, src_y;
1414   int x, y;
1415   
1416   XGetWindowAttributes(dpy, window, &xwa);
1417
1418   load_random_image (xwa.screen, window, window);
1419
1420   w = xwa.width;
1421   h = xwa.height;
1422
1423   gc_mask = GCForeground;
1424   
1425   gcv.plane_mask = random();
1426   gc_mask |= GCPlaneMask;
1427   
1428   gc0 = XCreateGC(dpy, window, gc_mask, &gcv);
1429
1430   steps = 50;
1431   chunk_w = w / (random() % 1 + 1);
1432   chunk_h = h / (random() % 1 + 1);
1433   if (random() & 0x1000) 
1434     delta_y = random() % 600;
1435   if (!delta_y || (random() & 0x2000))
1436     delta_x = random() % 600;
1437   src_x = 0; 
1438   src_y = 0; 
1439   x = 0;
1440   y = 0;
1441   
1442   for (i = 0; i < steps; i++) {
1443     if (x + chunk_w > w) 
1444       x -= w;
1445     else
1446       x += delta_x;
1447     
1448     if (y + chunk_h > h)
1449       y -= h;
1450     else
1451       y += delta_y;
1452     
1453     XCopyArea(dpy, window, window, gc0,
1454               src_x, src_y, 
1455               chunk_w, chunk_h,
1456               x, y);
1457
1458     if (bsod_sleep(dpy, 0))
1459       goto DONE;
1460   }
1461
1462   bsod_sleep(dpy, delay);
1463
1464  DONE:
1465   XFreeGC(dpy, gc0);
1466 }
1467
1468 \f
1469 /*
1470  * SPARC Solaris panic. Should look pretty authentic on Solaris boxes.
1471  * Anton Solovyev <solovam@earthlink.net>
1472  */ 
1473
1474 typedef struct
1475 {
1476   Display *dpy;
1477   Window window;
1478   GC gc, erase_gc;
1479   XFontStruct *xfs;
1480   int sub_width;                /* Text subwindow width in pixels */
1481   int sub_height;               /* Text subwindow height in pixels */
1482   int sub_x;                    /* upper left corner of the text subwindow */
1483   int sub_y;                    /* upper left corner of the text subwindow */
1484   int char_width;               /* Char width in pixels */
1485   int line_height;              /* Line height in pixels */
1486   int columns;                  /* Number of columns in the text screen */
1487   int x;                        /* horizontal position of the cursor */
1488 } scrolling_window;
1489
1490
1491 static scrolling_window *
1492 make_scrolling_window (Display *dpy, Window window,
1493                        const char *name,
1494                        Bool grab_screen_p)
1495 {
1496   const char *def_font = "fixed";
1497   scrolling_window* ts;
1498   XWindowAttributes xgwa;
1499   XGCValues gcv;
1500   char* fn;
1501   char buf1[100], buf2[100];
1502
1503   ts = malloc (sizeof (*ts));
1504   ts->window = window;
1505   ts->dpy = dpy;
1506
1507   ts->x = 0;
1508
1509   XGetWindowAttributes (dpy, window, &xgwa);
1510
1511   if (grab_screen_p)
1512     {
1513       ts->sub_width = xgwa.width * 0.8;
1514       ts->sub_height = xgwa.height * 0.8;
1515     }
1516   else
1517     {
1518       ts->sub_width  = xgwa.width - 20;
1519       ts->sub_height = xgwa.height - 20;
1520       if (ts->sub_width  < 20) ts->sub_width  = 20;
1521       if (ts->sub_height < 20) ts->sub_height = 20;
1522     }
1523
1524   sprintf (buf1, "%.50s.font", name);
1525   sprintf (buf2, "%.50s.Font", name);
1526   fn = get_string_resource (buf1, buf2);
1527   ts->xfs = XLoadQueryFont (dpy, fn);
1528   if (!ts->xfs)
1529     {
1530       sprintf (buf1, "%.50s.font2", name);
1531       fn = get_string_resource(buf1, buf2);
1532       ts->xfs = XLoadQueryFont(dpy, fn);
1533     }
1534   if (!ts->xfs)
1535     ts->xfs = XLoadQueryFont(dpy, def_font);
1536   if (!ts->xfs)
1537     exit (1);
1538   gcv.font = ts->xfs->fid;
1539   ts->char_width = (ts->xfs->per_char
1540                     ? ts->xfs->per_char['n'-ts->xfs->min_char_or_byte2].width
1541                     : ts->xfs->min_bounds.width);
1542   ts->line_height = ts->xfs->ascent + ts->xfs->descent + 1;
1543
1544   ts->columns = ts->sub_width / ts->char_width;
1545
1546   ts->sub_x = (xgwa.width - ts->sub_width) / 2;
1547   ts->sub_y = (xgwa.height - ts->sub_height) / 2;
1548
1549   if (!grab_screen_p) ts->sub_height += ts->sub_y, ts->sub_y = 0;
1550
1551   if (grab_screen_p)
1552     load_random_image (xgwa.screen, window, window);
1553
1554   sprintf (buf1, "%.50s.background", name);
1555   sprintf (buf2, "%.50s.Background", name);
1556   gcv.background = get_pixel_resource (buf1, buf2, dpy, xgwa.colormap);
1557
1558   sprintf (buf1, "%.50s.foreground", name);
1559   sprintf (buf2, "%.50s.Foreground", name);
1560   gcv.foreground = get_pixel_resource (buf1, buf2, dpy, xgwa.colormap);
1561
1562   ts->gc = XCreateGC (dpy, window,
1563                       GCForeground | GCBackground | GCFont,
1564                       &gcv);
1565   gcv.foreground = gcv.background;
1566   ts->erase_gc = XCreateGC (dpy, window,
1567                             GCForeground | GCBackground,
1568                             &gcv);
1569   XSetWindowBackground (dpy, window, gcv.background);
1570   return(ts);
1571 }
1572
1573 static void
1574 free_scrolling_window (scrolling_window* ts)
1575 {
1576   XFreeGC (ts->dpy, ts->gc);
1577   XFreeGC (ts->dpy, ts->erase_gc);
1578   XFreeFont (ts->dpy, ts->xfs);
1579   free (ts);
1580 }
1581
1582 static void
1583 scrolling_putc (scrolling_window* ts, const char aChar)
1584 {
1585   switch (aChar)
1586     {
1587     case '\n':
1588       ts->x = 0;
1589       XCopyArea (ts->dpy, ts->window, ts->window, ts->gc,
1590                  ts->sub_x, ts->sub_y + ts->line_height,
1591                  ts->sub_width, ts->sub_height,
1592                  ts->sub_x, ts->sub_y);
1593       XFillRectangle (ts->dpy, ts->window, ts->erase_gc,
1594                       ts->sub_x, ts->sub_y + ts->sub_height - ts->line_height,
1595                       ts->sub_width, ts->line_height);
1596       break;
1597     case '\r':
1598       ts->x = 0;
1599     case '\b':
1600       if(ts->x > 0)
1601         ts->x--;
1602       break;
1603     default:
1604       if (ts->x >= ts->columns)
1605         scrolling_putc (ts, '\n');
1606       XDrawImageString (ts->dpy, ts->window, ts->gc,
1607                         (ts->sub_x +
1608                          (ts->x * ts->char_width)
1609                          - ts->xfs->min_bounds.lbearing),
1610                         (ts->sub_y + ts->sub_height - ts->xfs->descent),
1611                         &aChar, 1);
1612       ts->x++;
1613       break;
1614     }
1615 }
1616
1617 static Bool
1618 scrolling_puts (scrolling_window *ts, const char* aString, int delay)
1619 {
1620   const char *c;
1621   for (c = aString; *c; ++c)
1622     {
1623       scrolling_putc (ts, *c);
1624       if (delay)
1625         {
1626           XSync(ts->dpy, 0);
1627           usleep(delay);
1628           if (bsod_sleep (ts->dpy, 0))
1629             return True;
1630         }
1631     }
1632   XSync (ts->dpy, 0);
1633   return False;
1634 }
1635
1636 static void
1637 sparc_solaris (Display* dpy, Window window, int delay)
1638 {
1639   const char *msg1 =
1640     "BAD TRAP: cpu=0 type=0x31 rp=0x2a10043b5e0 addr=0xf3880 mmu_fsr=0x0\n"
1641     "BAD TRAP occured in module \"unix\" due to an illegal access to a"
1642     " user address.\n"
1643     "adb: trap type = 0x31\n"
1644     "addr=0xf3880\n"
1645     "pid=307, pc=0x100306e4, sp=0x2a10043ae81, tstate=0x4480001602,"
1646     " context=0x87f\n"
1647     "g1-g7: 1045b000, 32f, 10079440, 180, 300000ebde8, 0, 30000953a20\n"
1648     "Begin traceback... sp = 2a10043ae81\n"
1649     "Called from 100bd060, fp=2a10043af31, args=f3700 300008cc988 f3880 0"
1650     " 1 300000ebde0.\n"
1651     "Called from 101fe1bc, fp=2a10043b011, args=3000045a240 104465a0"
1652     " 300008e47d0 300008e48fa 300008ae350 300008ae410\n"
1653     "Called from 1007c520, fp=2a10043b0c1, args=300008e4878 300003596e8 0"
1654     " 3000045a320 0 3000045a220\n"
1655     "Called from 1007c498, fp=2a10043b171, args=1045a000 300007847f0 20"
1656     " 3000045a240 1 0\n"
1657     "Called from 1007972c, fp=2a10043b221, args=1 300009517c0 30000951e58 1"
1658     " 300007847f0 0\n"
1659     "Called from 10031e10, fp=2a10043b2d1, args=3000095b0c8 0 300009396a8"
1660     " 30000953a20 0 1\n"
1661     "Called from 10000bdd8, fp=ffffffff7ffff1c1, args=0 57 100131480"
1662     " 100131480 10012a6e0 0\n"
1663     "End traceback...\n"
1664     "panic[cpu0]/thread=30000953a20: trap\n"
1665     "syncing file systems...";
1666   const char *msg2 =
1667     " 1 done\n"
1668     "dumping to /dev/dsk/c0t0d0s3, offset 26935296\n";
1669   const char *msg3 =
1670     ": 2803 pages dumped, compression ratio 2.88, dump succeeded\n";
1671   const char *msg4 =
1672     "rebooting...\n"
1673     "Resetting ...";
1674
1675   scrolling_window *ts;
1676   int i;
1677   char buf[256];
1678
1679   ts = make_scrolling_window (dpy, window, "Solaris", True);
1680
1681   scrolling_puts (ts, msg1, 0);
1682   if (bsod_sleep (dpy, 3))
1683     goto DONE;
1684
1685   scrolling_puts (ts, msg2, 0);
1686   if (bsod_sleep (dpy, 2))
1687     goto DONE;
1688
1689   for (i = 1; i <= 100; ++i)
1690     {
1691       sprintf(buf, "\b\b\b\b\b\b\b\b\b\b\b%3d%% done", i);
1692       scrolling_puts (ts, buf, 0);
1693       if (bsod_sleep (dpy, -1))
1694         goto DONE;
1695     }
1696
1697   scrolling_puts (ts, msg3, 0);
1698   if (bsod_sleep (dpy, 2))
1699     goto DONE;
1700
1701   scrolling_puts (ts, msg4, 0);
1702   if (bsod_sleep(dpy, 3))
1703     goto DONE;
1704
1705   bsod_sleep (dpy, 3);
1706
1707  DONE:
1708   free_scrolling_window (ts);
1709 }
1710
1711 /* Linux panic and fsck, by jwz
1712  */
1713 static void
1714 linux_fsck (Display *dpy, Window window, int delay)
1715 {
1716   XWindowAttributes xgwa;
1717   scrolling_window *ts;
1718   int i;
1719   const char *sysname;
1720   char buf[1024];
1721
1722   const char *linux_panic[] = {
1723    " kernel: Unable to handle kernel paging request at virtual "
1724      "address 0000f0ad\n",
1725    " kernel:  printing eip:\n",
1726    " kernel: c01becd7\n",
1727    " kernel: *pde = 00000000\n",
1728    " kernel: Oops: 0000\n",
1729    " kernel: CPU:    0\n",
1730    " kernel: EIP:    0010:[<c01becd7>]    Tainted: P \n",
1731    " kernel: EFLAGS: 00010286\n",
1732    " kernel: eax: 0000ff00   ebx: ca6b7e00   ecx: ce1d7a60   edx: ce1d7a60\n",
1733    " kernel: esi: ca6b7ebc   edi: 00030000   ebp: d3655ca0   esp: ca6b7e5c\n",
1734    " kernel: ds: 0018   es: 0018   ss: 0018\n",
1735    " kernel: Process crond (pid: 1189, stackpage=ca6b7000)\n",
1736    " kernel: Stack: d3655ca0 ca6b7ebc 00030054 ca6b7e7c c01c1e5b "
1737        "00000287 00000020 c01c1fbf \n",
1738    "",
1739    " kernel:        00005a36 000000dc 000001f4 00000000 00000000 "
1740        "ce046d40 00000001 00000000 \n",
1741    "", "", "",
1742    " kernel:        ffffffff d3655ca0 d3655b80 00030054 c01bef93 "
1743        "d3655ca0 ca6b7ebc 00030054 \n",
1744    "", "", "",
1745    " kernel: Call Trace:    [<c01c1e5b>] [<c01c1fbf>] [<c01bef93>] "
1746        "[<c01bf02b>] [<c0134c4f>]\n",
1747    "", "", "",
1748    " kernel:   [<c0142562>] [<c0114f8c>] [<c0134de3>] [<c010891b>]\n",
1749    " kernel: \n",
1750    " kernel: Code: 2a 00 75 08 8b 44 24 2c 85 c0 74 0c 8b 44 24 58 83 48 18 "
1751       "08 \n",
1752    0
1753   };
1754
1755   XGetWindowAttributes (dpy, window, &xgwa);
1756   XSetWindowBackground (dpy, window, 
1757                         get_pixel_resource("Linux.background",
1758                                            "Linux.Background",
1759                                            dpy, xgwa.colormap));
1760   XClearWindow(dpy, window);
1761
1762   sysname = "linux";
1763 # ifdef HAVE_UNAME
1764   {
1765     struct utsname uts;
1766     char *s;
1767     if (uname (&uts) >= 0)
1768       sysname = uts.nodename;
1769     s = strchr (sysname, '.');
1770     if (s) *s = 0;
1771   }
1772 # endif /* !HAVE_UNAME */
1773
1774
1775   ts = make_scrolling_window (dpy, window, "Linux", False);
1776
1777   scrolling_puts (ts, "waiting for X server to shut down ", 0);
1778   usleep (100000);
1779   if (bsod_sleep (dpy, 0))
1780     goto PANIC;
1781   scrolling_puts (ts,
1782                   "XIO:  fatal IO error 2 (broken pipe) on X server \":0.0\"\n"
1783                   "        after 339471 requests (339471 known processed) "
1784                   "with 0 events remaining\n",
1785                   0);
1786   if (scrolling_puts (ts, ".........\n", 300000))
1787     goto PANIC;
1788   if (bsod_sleep (dpy, 0))
1789     goto PANIC;
1790   scrolling_puts (ts,
1791                   "xinit:  X server slow to shut down, sending KILL signal.\n",
1792                   0);
1793   scrolling_puts (ts, "waiting for server to die ", 0);
1794   if (scrolling_puts (ts, "...\n", 300000))
1795     goto PANIC;
1796   if (bsod_sleep (dpy, 0))
1797     goto PANIC;
1798   scrolling_puts (ts, "xinit:  Can't kill server\n", 0);
1799
1800   if (bsod_sleep (dpy, 2))
1801     goto PANIC;
1802
1803   sprintf (buf, "\n%s Login: ", sysname);
1804   scrolling_puts (ts, buf, 0);
1805   if (bsod_sleep (dpy, 1))
1806     goto PANIC;
1807   scrolling_puts (ts,
1808     "\n\n"
1809     "Parallelizing fsck version 1.22 (22-Jun-2001)\n"
1810     "e2fsck 1.22, 22-Jun-2001 for EXT2 FS 0.5b, 95/08/09\n"
1811     "Warning!  /dev/hda1 is mounted.\n"
1812     "/dev/hda1 contains a file system with errors, check forced.\n",
1813                 0);
1814   if (bsod_sleep (dpy, 1))
1815     goto PANIC;
1816
1817   if (0 == random() % 2)
1818   scrolling_puts (ts,
1819      "Couldn't find ext2 superblock, trying backup blocks...\n"
1820      "The filesystem size (according to the superblock) is 3644739 blocks\n"
1821      "The physical size of the device is 3636706 blocks\n"
1822      "Either the superblock or the partition table is likely to be corrupt!\n"
1823      "Abort<y>? no\n",
1824                 0);
1825   if (bsod_sleep (dpy, 1))
1826     goto PANIC;
1827
1828  AGAIN:
1829
1830   scrolling_puts (ts, "Pass 1: Checking inodes, blocks, and sizes\n", 0);
1831   if (bsod_sleep (dpy, 2))
1832     goto PANIC;
1833
1834   i = (random() % 60) - 20;
1835   while (--i > 0)
1836     {
1837       int b = random() % 0xFFFF;
1838       sprintf (buf, "Deleted inode %d has zero dtime.  Fix<y>? yes\n\n", b);
1839       scrolling_puts (ts, buf, 0);
1840     }
1841
1842   i = (random() % 40) - 10;
1843   if (i > 0)
1844     {
1845       int g = random() % 0xFFFF;
1846       int b = random() % 0xFFFFFFF;
1847
1848       if (bsod_sleep (dpy, 1))
1849         goto PANIC;
1850
1851       sprintf (buf, "Warning: Group %d's copy of the group descriptors "
1852                "has a bad block (%d).\n", g, b);
1853       scrolling_puts (ts, buf, 0);
1854
1855       b = random() % 0x3FFFFF;
1856       while (--i > 0)
1857         {
1858           b += random() % 0xFFFF;
1859           sprintf (buf,
1860                    "Error reading block %d (Attempt to read block "
1861                    "from filesystem resulted in short read) while doing "
1862                    "inode scan.  Ignore error<y>?",
1863                    b);
1864           scrolling_puts (ts, buf, 0);
1865           usleep (10000);
1866           scrolling_puts (ts, " yes\n\n", 0);
1867         }
1868     }
1869
1870   if (0 == (random() % 10))
1871     {
1872
1873       if (bsod_sleep (dpy, 1))
1874         goto PANIC;
1875
1876       i = 3 + (random() % 10);
1877       while (--i > 0)
1878         scrolling_puts (ts, "Could not allocate 256 block(s) for inode table: "
1879                         "No space left on device\n", 0);
1880       scrolling_puts (ts, "Restarting e2fsck from the beginning...\n", 0);
1881
1882       if (bsod_sleep (dpy, 2))
1883         goto PANIC;
1884
1885       goto AGAIN;
1886     }
1887
1888   i = (random() % 20) - 5;
1889
1890   if (i > 0)
1891     if (bsod_sleep (dpy, 1))
1892       goto PANIC;
1893
1894   while (--i > 0)
1895     {
1896       int j = 5 + (random() % 10);
1897       int w = random() % 4;
1898
1899       while (--j > 0)
1900         {
1901           int b = random() % 0xFFFFF;
1902           int g = random() % 0xFFF;
1903
1904           if (0 == (random() % 10))
1905             b = 0;
1906           else if (0 == (random() % 10))
1907             b = -1;
1908
1909           if (w == 0)
1910             sprintf (buf,
1911                      "Inode table for group %d not in group.  (block %d)\n"
1912                      "WARNING: SEVERE DATA LOSS POSSIBLE.\n"
1913                      "Relocate<y>?",
1914                      g, b);
1915           else if (w == 1)
1916             sprintf (buf,
1917                      "Block bitmap for group %d not in group.  (block %d)\n"
1918                      "Relocate<y>?",
1919                      g, b);
1920           else if (w == 2)
1921             sprintf (buf,
1922                      "Inode bitmap %d for group %d not in group.\n"
1923                      "Continue<y>?",
1924                      b, g);
1925           else /* if (w == 3) */
1926             sprintf (buf,
1927                      "Bad block %d in group %d's inode table.\n"
1928                      "WARNING: SEVERE DATA LOSS POSSIBLE.\n"
1929                      "Relocate<y>?",
1930                      b, g);
1931
1932           scrolling_puts (ts, buf, 0);
1933           scrolling_puts (ts, " yes\n\n", 0);
1934         }
1935       if (bsod_sleep (dpy, 0))
1936         goto PANIC;
1937       usleep (1000);
1938     }
1939
1940
1941   if (0 == random() % 10) goto PANIC;
1942   scrolling_puts (ts, "Pass 2: Checking directory structure\n", 0);
1943   if (bsod_sleep (dpy, 2))
1944     goto PANIC;
1945
1946   i = (random() % 20) - 5;
1947   while (--i > 0)
1948     {
1949       int n = random() % 0xFFFFF;
1950       int o = random() % 0xFFF;
1951       sprintf (buf, "Directory inode %d, block 0, offset %d: "
1952                "directory corrupted\n"
1953                "Salvage<y>? ",
1954                n, o);
1955       scrolling_puts (ts, buf, 0);
1956       usleep (1000);
1957       scrolling_puts (ts, " yes\n\n", 0);
1958
1959       if (0 == (random() % 100))
1960         {
1961           sprintf (buf, "Missing '.' in directory inode %d.\nFix<y>?", n);
1962           scrolling_puts (ts, buf, 0);
1963           usleep (1000);
1964           scrolling_puts (ts, " yes\n\n", 0);
1965         }
1966
1967       if (bsod_sleep (dpy, 0))
1968         goto PANIC;
1969     }
1970
1971   if (0 == random() % 10) goto PANIC;
1972   scrolling_puts (ts,
1973                   "Pass 3: Checking directory connectivity\n"
1974                   "/lost+found not found.  Create? yes\n",
1975                 0);
1976   if (bsod_sleep (dpy, 2))
1977     goto PANIC;
1978
1979   /* Unconnected directory inode 4949 (/var/spool/squid/06/???)
1980      Connect to /lost+found<y>? yes
1981
1982      '..' in /var/spool/squid/06/08 (20351) is <The NULL inode> (0), should be 
1983      /var/spool/squid/06 (20350).
1984      Fix<y>? yes
1985
1986      Unconnected directory inode 128337 (/var/spool/squid/06/???)
1987      Connect to /lost+found<y>? yes
1988    */
1989
1990
1991   if (0 == random() % 10) goto PANIC;
1992   scrolling_puts (ts, "Pass 4: Checking reference counts\n", 0);
1993   if (bsod_sleep (dpy, 2))
1994     goto PANIC;
1995
1996   /* Inode 2 ref count is 19, should be 20.  Fix<y>? yes
1997
1998      Inode 4949 ref count is 3, should be 2.  Fix<y>? yes
1999
2000         ...
2001
2002      Inode 128336 ref count is 3, should be 2.  Fix<y>? yes
2003
2004      Inode 128337 ref count is 3, should be 2.  Fix<y>? yes
2005
2006    */
2007
2008
2009   if (0 == random() % 10) goto PANIC;
2010   scrolling_puts (ts, "Pass 5: Checking group summary information\n", 0);
2011   if (bsod_sleep (dpy, 2))
2012     goto PANIC;
2013
2014   i = (random() % 200) - 50;
2015   if (i > 0)
2016     {
2017       scrolling_puts (ts, "Block bitmap differences: ", 0);
2018       while (--i > 0)
2019         {
2020           sprintf (buf, " %d", -(random() % 0xFFF));
2021           scrolling_puts (ts, buf, 0);
2022           usleep (1000);
2023         }
2024       scrolling_puts (ts, "\nFix? yes\n\n", 0);
2025     }
2026
2027
2028   i = (random() % 100) - 50;
2029   if (i > 0)
2030     {
2031       scrolling_puts (ts, "Inode bitmap differences: ", 0);
2032       while (--i > 0)
2033         {
2034           sprintf (buf, " %d", -(random() % 0xFFF));
2035           scrolling_puts (ts, buf, 0);
2036           usleep (1000);
2037         }
2038       scrolling_puts (ts, "\nFix? yes\n\n", 0);
2039     }
2040
2041   i = (random() % 20) - 5;
2042   while (--i > 0)
2043     {
2044       int g = random() % 0xFFFF;
2045       int c = random() % 0xFFFF;
2046       sprintf (buf,
2047                "Free blocks count wrong for group #0 (%d, counted=%d).\nFix? ",
2048                g, c);
2049       scrolling_puts (ts, buf, 0);
2050       usleep (1000);
2051       scrolling_puts (ts, " yes\n\n", 0);
2052       if (bsod_sleep (dpy, 0))
2053         goto PANIC;
2054     }
2055
2056  PANIC:
2057
2058   i = 0;
2059   scrolling_puts (ts, "\n\n", 0);
2060   while (linux_panic[i])
2061     {
2062       time_t t = time ((time_t *) 0);
2063       struct tm *tm = localtime (&t);
2064       char prefix[100];
2065
2066       if (*linux_panic[i])
2067         {
2068           strftime (prefix, sizeof(prefix)-1, "%b %d %k:%M:%S ", tm);
2069           scrolling_puts (ts, prefix, 0);
2070           scrolling_puts (ts, sysname, 0);
2071           scrolling_puts (ts, linux_panic[i], 0);
2072           XSync(dpy, False);
2073           usleep(1000);
2074         }
2075       else
2076         usleep (300000);
2077
2078       if (bsod_sleep (dpy, 0))
2079         goto DONE;
2080       i++;
2081     }
2082
2083   if (bsod_sleep (dpy, 4))
2084     goto DONE;
2085
2086
2087   XSync(dpy, False);
2088   bsod_sleep(dpy, delay);
2089
2090  DONE:
2091   free_scrolling_window (ts);
2092   XClearWindow(dpy, window);
2093 }
2094
2095 \f
2096
2097 /* HPUX panic, by Tobias Klausmann <klausman@schwarzvogel.de>
2098  */
2099
2100 static void
2101 hpux (Display* dpy, Window window, int delay)
2102 {
2103   XWindowAttributes xgwa;
2104   scrolling_window *ts;
2105   const char *sysname;
2106   char buf[2048];
2107
2108   const char *msg1 =
2109    "Console Login:\n"
2110    "\n"
2111    "     ******* Unexpected HPMC/TOC. Processor HPA FFFFFFFF'"
2112    "FFFA0000 *******\n"
2113    "                              GENERAL REGISTERS:\n"
2114    "r00/03 00000000'00000000 00000000'00000000 00000000'00000000 00000000'"
2115    "006C76C0\n"
2116    "r04/07 00000000'00000001 00000000'0126E328 00000000'00000000 00000000'"
2117    "0122B640\n"
2118    "r08/11 00000000'00000000 00000000'0198CFC0 00000000'000476FE 00000000'"
2119    "00000001\n"
2120    "r12/15 00000000'40013EE8 00000000'08000080 00000000'4002530C 00000000'"
2121    "4002530C\n"
2122    "r16/19 00000000'7F7F2A00 00000000'00000001 00000000'00000000 00000000'"
2123    "00000000\n"
2124    "r20/23 00000000'006C8048 00000000'00000001 00000000'00000000 00000000'"
2125    "00000000\n"
2126    "r24/27 00000000'00000000 00000000'00000000 00000000'00000000 00000000'"
2127    "00744378\n"
2128    "r28/31 00000000'00000000 00000000'007DD628 00000000'0199F2B0 00000000'"
2129    "00000000\n"
2130    "                              CONTROL REGISTERS:\n"
2131    "sr0/3  00000000'0F3B4000 00000000'0C2A2000 00000000'016FF800 00000000'"
2132    "00000000\n"
2133    "sr4/7  00000000'00000000 00000000'016FF800 00000000'0DBF1400 00000000'"
2134    "00000000\n"
2135    "pcq =  00000000'00000000.00000000'00104950 00000000'00000000.00000000'"
2136    "00104A14\n"
2137    "isr =  00000000'10240006 ior = 00000000'67D9E220 iir = 08000240 rctr = "
2138    "7FF10BB6\n"
2139    "\n"
2140    "pid reg cr8/cr9    00007700'0000B3A9 00000000'0000C5D8\n"
2141    "pid reg cr12/cr13  00000000'00000000 00000000'00000000\n"
2142    "ipsw = 000000FF'080CFF1F iva = 00000000'0002C000 sar = 3A ccr = C0\n"
2143    "tr0/3  00000000'006C76C0 00000000'00000001 00000000'00000000 00000000'"
2144    "7F7CE000\n"
2145    "tr4/7  00000000'03790000 0000000C'4FB68340 00000000'C07EE13F 00000000'"
2146    "0199F2B0\n"
2147    "eiem = FFFFFFF0'FFFFFFFF eirr = 80000000'00000000 itmr = 0000000C'"
2148    "4FD8EDE1\n"
2149    "cr1/4  00000000'00000000 00000000'00000000 00000000'00000000 00000000'"
2150    "00000000\n"
2151    "cr5/7  00000000'00000000 00000000'00000000 00000000'"
2152    "00000000\n"
2153    "                           MACHINE CHECK PARAMETERS:\n"
2154    "Check Type = 00000000 CPU STATE = 9E000001 Cache Check = 00000000\n"
2155    "TLB Check = 00000000 Bus Check = 00000000 PIM State = ? SIU "
2156    "Status = ????????\n"
2157    "Assists = 00000000 Processor = 00000000\n"
2158    "Slave Addr = 00000000'00000000 Master Addr = 00000000'00000000\n"
2159    "\n"
2160    "\n"
2161    "TOC,    pcsq.pcoq = 0'0.0'104950   , isr.ior = 0'10240006.0'67d9e220\n"
2162    "@(#)B2352B/9245XB HP-UX (B.11.00) #1: Wed Nov  5 22:38:19 PST 1997\n"
2163    "Transfer of control: (display==0xd904, flags==0x0)\n"
2164    "\n"
2165    "\n"
2166    "\n"
2167    "*** A system crash has occurred.  (See the above messages for details.)\n"
2168    "*** The system is now preparing to dump physical memory to disk, for use\n"
2169    "*** in debugging the crash.\n"
2170    "\n"
2171    "*** The dump will be a SELECTIVE dump:  40 of 256 megabytes.\n"
2172    "*** To change this dump type, press any key within 10 seconds.\n"
2173    "*** Proceeding with selective dump.\n"
2174    "\n"
2175    "*** The dump may be aborted at any time by pressing ESC.\n";
2176   const char *msg2 =
2177    "\n*** System rebooting.\n";
2178
2179   XGetWindowAttributes (dpy, window, &xgwa);
2180   ts = make_scrolling_window (dpy, window, "HPUX", False);
2181   XClearWindow(dpy,window);
2182   ts->columns = 10000;  /* never wrap */
2183   ts->sub_x = 0;
2184   ts->sub_y = 0;
2185   ts->sub_width = xgwa.width;
2186   ts->sub_height = xgwa.height;
2187
2188   sysname = "HPUX";
2189 # ifdef HAVE_UNAME
2190   {
2191     struct utsname uts;
2192     char *s;
2193     if (uname (&uts) >= 0)
2194       sysname = uts.nodename;
2195     s = strchr (sysname, '.');
2196     if (s) *s = 0;
2197   }
2198 # endif /* !HAVE_UNAME */
2199
2200   if (bsod_sleep (dpy, 1))
2201     goto DONE;
2202   
2203   scrolling_puts (ts,
2204                   "                                                       "
2205                   "                                                       "
2206                   "                                                       \n",
2207                   0);
2208   sprintf (buf, "%.100s [HP Release B.11.00] (see /etc/issue)\n", sysname);
2209   scrolling_puts (ts, buf, 0);
2210   if (bsod_sleep (dpy, 1))
2211     goto DONE;
2212   scrolling_puts (ts, msg1, 0);
2213   {
2214     int i;
2215     int steps = 11;
2216     int size = 40;
2217     for (i = 0; i <= steps; i++)
2218       {
2219         if (i > steps) i = steps;
2220         sprintf (buf, 
2221                "*** Dumping: %3d%% complete (%d of 40 MB) (device 64:0x2)\r",
2222                  i * 100 / steps,
2223                  i * size / steps);
2224         scrolling_puts (ts, buf, 0);
2225         XSync (dpy, False);
2226         usleep (1500000);
2227         if (bsod_sleep (dpy, 0))
2228           goto DONE;
2229       }
2230   }
2231
2232   scrolling_puts (ts, msg2, 0);
2233
2234   XSync(dpy, False);
2235   bsod_sleep(dpy, delay);
2236
2237  DONE:
2238   free_scrolling_window (ts);
2239 }
2240
2241 \f
2242
2243 /* IBM OS/390 aka MVS aka z/OS.
2244    Text from Dan Espen <dane@mk.telcordia.com>.
2245    Apparently this isn't actually a crash, just a random session...
2246    But who can tell.
2247  */
2248
2249 static void
2250 os390 (Display* dpy, Window window, int delay)
2251 {
2252   GC gc;
2253   XGCValues gcv;
2254   XWindowAttributes xgwa;
2255   scrolling_window *ts;
2256   int i;
2257
2258   const char *msg[] = {
2259    "* ISPF Subtask abend *\n",
2260    "SPF      ENDED DUE TO ERROR+\n",
2261    "READY\n",
2262    "\n",
2263    "IEA995I SYMPTOM DUMP OUTPUT\n",
2264    "  USER COMPLETION CODE=0222\n",
2265    " TIME=23.00.51  SEQ=03210  CPU=0000  ASID=00AE\n",
2266    " PSW AT TIME OF ERROR  078D1000   859DAF18  ILC 2  INTC 0D\n",
2267    "   NO ACTIVE MODULE FOUND\n",
2268    "   NAME=UNKNOWN\n",
2269    "   DATA AT PSW  059DAF12 - 00181610  0A0D9180  70644710\n",
2270    "   AR/GR 0: 00000000/80000000   1: 00000000/800000DE\n",
2271    "         2: 00000000/196504DC   3: 00000000/00037A78\n",
2272    "         4: 00000000/00037B78   5: 00000000/0003351C\n",
2273    "         6: 00000000/0000F0AD   7: 00000000/00012000\n",
2274    "         8: 00000000/059DAF10   9: 00000000/0002D098\n",
2275    "         A: 00000000/059D9F10   B: 00000000/059D8F10\n",
2276    "         C: 00000000/859D7F10   D: 00000000/00032D60\n",
2277    "         E: 00000000/00033005   F: 01000002/00000041\n",
2278    " END OF SYMPTOM DUMP\n",
2279    "ISPS014 - ** Logical screen request failed - abend 0000DE **\n",
2280    "ISPS015 - ** Contact your system programmer or dialog developer.**\n",
2281    "*** ISPF Main task abend ***\n",
2282    "IEA995I SYMPTOM DUMP OUTPUT\n",
2283    "  USER COMPLETION CODE=0222\n",
2284    " TIME=23.00.52  SEQ=03211  CPU=0000  ASID=00AE\n",
2285    " PSW AT TIME OF ERROR  078D1000   8585713C  ILC 2  INTC 0D\n",
2286    "   ACTIVE LOAD MODULE           ADDRESS=05855000  OFFSET=0000213C\n",
2287    "   NAME=ISPMAIN\n",
2288    "   DATA AT PSW  05857136 - 00181610  0A0D9180  D3304770\n",
2289    "   GR 0: 80000000   1: 800000DE\n",
2290    "      2: 00015260   3: 00000038\n",
2291    "      4: 00012508   5: 00000000\n",
2292    "      6: 000173AC   7: FFFFFFF8\n",
2293    "      8: 05858000   9: 00012CA0\n",
2294    "      A: 05857000   B: 05856000\n",
2295    "      C: 85855000   D: 00017020\n",
2296    "      E: 85857104   F: 00000000\n",
2297    " END OF SYMPTOM DUMP\n",
2298    "READY\n",
2299    "***_\n"
2300   };
2301
2302   XGetWindowAttributes (dpy, window, &xgwa);
2303   ts = make_scrolling_window (dpy, window, "OS390", False);
2304   ts->columns = 10000;  /* never wrap */
2305   ts->sub_x = 0;
2306   ts->sub_y = 0;
2307   ts->sub_width = xgwa.width;
2308   ts->sub_height = xgwa.height;
2309
2310   gcv.foreground = get_pixel_resource ("390.background", "390.Background",
2311                                        dpy, xgwa.colormap);
2312   gc = XCreateGC (dpy, window, GCForeground, &gcv);
2313   XFillRectangle (dpy, window, gc, 0, 0, xgwa.width, xgwa.height);
2314   XFreeGC (dpy, gc);
2315
2316   for (i = 0; i < countof (msg); i++)
2317     {
2318       scrolling_puts (ts, msg[i], 0);
2319       usleep (100000);
2320       if (bsod_sleep(dpy, 0)) goto DONE;
2321     }
2322
2323   XSync(dpy, False);
2324   bsod_sleep(dpy, delay);
2325 DONE:
2326   free_scrolling_window (ts);
2327 }
2328
2329
2330 \f
2331 /*
2332  * Simulate various Apple II crashes. The memory map encouraged many
2333  * programs to use the primary hi-res video page for various storage,
2334  * and the secondary hi-res page for active display. When it crashed
2335  * into Applesoft or the monitor, it would revert to the primary page
2336  * and you'd see memory garbage on the screen. Also, it was common for
2337  * copy-protected games to use the primary text page for important
2338  * code, because that made it really hard to reverse-engineer them. The
2339  * result often looked like what this generates.
2340  *
2341  * Sometimes an imaginary user types some of the standard commands to
2342  * recover from crashes.  You can turn off BSOD*apple2SimulateUser to
2343  * prevent this.
2344  *
2345  * It simulates the following characteristics of standard television
2346  * monitors:
2347  *
2348  * - Realistic rendering of a composite video signal
2349  * - Compression & brightening on the right, as the scan gets truncated
2350  *   because of saturation in the flyback transformer
2351  * - Blooming of the picture dependent on brightness
2352  * - Overscan, cutting off a few pixels on the left side.
2353  * - Colored text in mixed graphics/text modes
2354  *
2355  * It's amazing how much it makes your high-end monitor look like at
2356  * large late-70s TV.  All you need is to put a big "Solid State" logo
2357  * in curly script on it and you'd be set.
2358  *
2359  * Trevor Blackwell <tlb@tlb.org> 
2360  */
2361
2362 /*
2363  * Implementation notes:
2364  *
2365  * There are roughly 3 parts to this hack:
2366  *
2367  * - emulation of A2 Basic and Monitor. Not much more than printing random
2368  *   plausible messages. Here we work in the A2 memory space.
2369  *
2370  * - emulation of the A2's video output section, which shifted bits out of main
2371  *   memory at a 14 MHz dot clock rate, sort of. You could only read one byte
2372  *   per MHz, so there were various schemes for turning 8 bits into 14 screen
2373  *   pixels.
2374  *
2375  * - simulation of an NTSC television, which turned the bits into colored
2376  *   graphics and text.
2377  * 
2378  * The A2 had 3 display modes: text, lores, and hires. Text was 40x24, and it
2379  * disabled color in the TV. Lores gave you 40x48 graphics blocks, using the
2380  * same memory as the text screen. Each could be one of 16 colors. Hires gave
2381  * you 280x192 pixels. Odd pixels were blue or purple, and even pixels were
2382  * orange or green depending on the setting of the high bit in each byte.
2383  *
2384  * The graphics modes could also have 4 lines of text at the bottom. This was
2385  * fairly unreadable if you had a color monitor.
2386  *
2387  * Each mode had 2 different screens using different memory space. In hires
2388  * mode this was sometimes used for double buffering, but more often the lower
2389  * screen was full of code/data and the upper screen was used for display, so
2390  * you got random garbage on the screen.
2391  * 
2392  * In DirectColor or TrueColor modes, it generates pixel values directly from
2393  * RGB values it calculates across each scan line. In PseudoColor mode, it
2394  * consider each possible pattern of 5 preceding bit values in each possible
2395  * position modulo 4 and allocates a color for each. A few things, like the
2396  * brightening on the right side as the horizontal trace slows down, aren't
2397  * done in PseudoColor.
2398  *
2399  * The text font is based on X's standard 6x10 font, with a few tweaks like
2400  * putting a slash across the zero.
2401  *
2402  * I'd like to add a bit of visible retrace, but it conflicts with being able
2403  * to bitcopy the image when fast scrolling. After another couple of CPU
2404  * generations, we could probably regenerate the whole image from scratch every
2405  * time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
2406  * looks too slow.
2407  */
2408
2409 static char * apple2_basic_errors[]={
2410   "BREAK",
2411   "NEXT WITHOUT FOR",
2412   "SYNTAX ERROR",
2413   "RETURN WITHOUT GOSUB",
2414   "ILLEGAL QUANTITY",
2415   "OVERFLOW",
2416   "OUT OF MEMORY",
2417   "BAD SUBSCRIPT ERROR",
2418   "DIVISION BY ZERO",
2419   "STRING TOO LONG",
2420   "FORMULA TOO COMPLEX",
2421   "UNDEF'D FUNCTION",
2422   "OUT OF DATA"
2423 };
2424 static char * apple2_dos_errors[]={
2425   "VOLUME MISMATCH",
2426   "I/O ERROR",
2427   "DISK FULL",
2428   "NO BUFFERS AVAILABLE",
2429   "PROGRAM TOO LARGE",
2430 };
2431
2432 struct apple2_state {
2433   char hireslines[192][40];
2434   char textlines[24][40];
2435   int gr_text;
2436   enum {
2437     A2_GR_FULL=1,
2438     A2_GR_LORES=2,
2439     A2_GR_HIRES=4
2440   } gr_mode;
2441   int cursx;
2442   int cursy;
2443   int blink;
2444   int rowimage[24];
2445 };
2446
2447 enum {
2448   A2_SP_ROWMASK=1023,
2449   A2_SP_PUT=1024,
2450   A2_SP_COPY=2048,
2451 };
2452
2453 static void
2454 a2_scroll(struct apple2_state *st)
2455 {
2456   int i;
2457   int top=(st->gr_mode&(A2_GR_LORES|A2_GR_HIRES)) ? 20 : 0;
2458   if ((st->gr_mode&A2_GR_FULL) && (st->gr_mode&A2_GR_HIRES)) return;
2459   if (st->gr_mode&A2_GR_FULL) top=0;
2460   for (i=top; i<23; i++) {
2461     if (memcmp(st->textlines[i],st->textlines[i+1],40)) {
2462       memcpy(st->textlines[i],st->textlines[i+1],40);
2463       st->rowimage[i]=st->rowimage[i+1];
2464     }
2465   }
2466   memset(st->textlines[23],0xe0,40);
2467   st->rowimage[23]=-1;
2468 }
2469
2470 static void
2471 a2_printc(struct apple2_state *st, char c)
2472 {
2473   st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
2474   if (c=='\n') {
2475     if (st->cursy==23) {
2476       a2_scroll(st);
2477     } else {
2478       st->rowimage[st->cursy]=-1;
2479       st->cursy++;
2480       st->rowimage[st->cursy]=-1;
2481     }
2482     st->cursx=0;
2483   } else {
2484     st->textlines[st->cursy][st->cursx]=c ^ 0xc0;
2485     st->rowimage[st->cursy]=-1;
2486     st->cursx++;
2487     if (st->cursx==40) {
2488       if (st->cursy==23) {
2489         a2_scroll(st);
2490       } else {
2491         st->rowimage[st->cursy]=-1;
2492         st->cursy++;
2493         st->rowimage[st->cursy]=-1;
2494       }
2495       st->cursx=0;
2496     }
2497   }
2498   st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
2499 }
2500
2501 static void
2502 a2_goto(struct apple2_state *st, int r, int c)
2503 {
2504   st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
2505   st->cursy=r;
2506   st->cursx=c;
2507   st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
2508 }
2509
2510 static void
2511 a2_cls(struct apple2_state *st) 
2512 {
2513   int i;
2514   for (i=0; i<24; i++) {
2515     memset(st->textlines[i],0xe0,40);
2516     st->rowimage[i]=-1;
2517   }
2518 }
2519
2520 static void
2521 a2_invalidate(struct apple2_state *st) 
2522 {
2523   int i;
2524   for (i=0; i<24; i++) {
2525     st->rowimage[i]=-1;
2526   }
2527 }
2528
2529 static void
2530 a2_poke(struct apple2_state *st, int addr, int val)
2531 {
2532   
2533   if (addr>=0x400 && addr<0x800) {
2534     /* text memory */
2535     int row=((addr&0x380)/0x80) + ((addr&0x7f)/0x28)*8;
2536     int col=(addr&0x7f)%0x28;
2537     if (row<24 && col<40) {
2538       st->textlines[row][col]=val;
2539       if (!(st->gr_mode&(A2_GR_HIRES)) ||
2540           (!(st->gr_mode&(A2_GR_FULL)) && row>=20)) {
2541         st->rowimage[row]=-1;
2542       }
2543     }
2544   }
2545   else if (addr>=0x2000 && addr<0x4000) {
2546     int row=(((addr&0x1c00) / 0x400) * 1 +
2547              ((addr&0x0380) / 0x80) * 8 +
2548              ((addr&0x0078) / 0x28) * 64);
2549     int col=((addr&0x07f)%0x28);
2550     if (row<192 && col<40) {
2551       st->hireslines[row][col]=val;
2552       if (st->gr_mode&A2_GR_HIRES) {
2553         st->rowimage[row/8]=-1;
2554       }
2555     }
2556   }
2557 }
2558
2559 /* This table lists fixes for characters that differ from the standard 6x10
2560    font. Each encodes a pixel, as (charindex*7 + x) + (y<<10) + (value<<15)
2561    where value is 0 for white and 1 for black. */
2562 static unsigned short a2_fixfont[] = {
2563   /* Fix $ */  0x8421, 0x941d,
2564   /* Fix % */  0x8024, 0x0028, 0x8425, 0x0426, 0x0825, 0x1027, 0x1426, 0x9427,
2565                0x1824, 0x9828,
2566   /* Fix * */  0x8049, 0x8449, 0x8849, 0x0c47, 0x0c48, 0x0c4a, 0x0c4b, 0x9049,
2567                0x9449, 0x9849,
2568   /* Fix , */  0x9057, 0x1458, 0x9856, 0x1857, 0x1c56,
2569   /* Fix . */  0x1465, 0x1864, 0x1866, 0x1c65,
2570   /* Fix / */  0x006e, 0x186a,
2571   /* Fix 0 */  0x8874, 0x8c73, 0x9072,
2572   /* Fix 1 */  0x0878, 0x1878, 0x187c,
2573   /* Fix 5 */  0x8895, 0x0c94, 0x0c95,
2574   /* Fix 6 */  0x809f, 0x8c9c, 0x109c,
2575   /* Fix 7 */  0x8ca4, 0x0ca5, 0x90a3, 0x10a4,
2576   /* Fix 9 */  0x08b3, 0x8cb3, 0x98b0,
2577   /* Fix : */  0x04b9, 0x08b8, 0x08ba, 0x0cb9, 0x90b9, 0x14b9, 0x18b8, 0x18b9,
2578                0x18ba, 0x1cb9,
2579   /* Fix ; */  0x04c0, 0x08bf, 0x08c1, 0x0cc0, 0x90c0, 0x14c1, 0x98bf, 0x18c0,
2580                0x1cbf,
2581   /* Fix < */  0x80c8, 0x00c9, 0x84c7, 0x04c8, 0x88c6, 0x08c7, 0x8cc5, 0x0cc6,
2582                0x90c6, 0x10c7, 
2583                0x94c7, 0x14c8, 0x98c8, 0x18c9,
2584   /* Fix > */  0x80d3, 0x00d4, 0x84d4, 0x04d5, 0x88d5, 0x08d6, 0x8cd6, 0x0cd7,
2585                0x90d5, 0x10d6, 
2586                0x94d4, 0x14d5, 0x98d3, 0x18d4,
2587   /* Fix @ */  0x88e3, 0x08e4, 0x8ce4, 0x98e5,
2588   /* Fix B */  0x84ef, 0x04f0, 0x88ef, 0x08f0, 0x8cef, 0x90ef, 0x10f0, 0x94ef,
2589                0x14f0,
2590   /* Fix D */  0x84fd, 0x04fe, 0x88fd, 0x08fe, 0x8cfd, 0x0cfe, 0x90fd, 0x10fe,
2591                0x94fd, 0x14fe,
2592   /* Fix G */  0x8116, 0x0516, 0x9916,
2593   /* Fix J */  0x0129, 0x012a, 0x052a, 0x852b, 0x092a, 0x892b, 0x0d2a, 0x8d2b,
2594                0x112a, 0x912b, 
2595                0x152a, 0x952b, 0x992a,
2596   /* Fix M */  0x853d, 0x853f, 0x093d, 0x893e, 0x093f,
2597   /* Fix Q */  0x915a, 0x155a, 0x955b, 0x155c, 0x195b, 0x995c, 0x1d5c,
2598   /* Fix V */  0x8d7b, 0x0d7c, 0x0d7e, 0x8d7f, 0x917b, 0x117c, 0x117e, 0x917f,
2599   /* Fix [ */  0x819e, 0x81a2, 0x859e, 0x899e, 0x8d9e, 0x919e, 0x959e, 0x999e,
2600                0x99a2,
2601   /* Fix \ */  0x01a5, 0x19a9,
2602   /* Fix ] */  0x81ac, 0x81b0, 0x85b0, 0x89b0, 0x8db0, 0x91b0, 0x95b0, 0x99ac,
2603                0x99b0,
2604   /* Fix ^ */  0x01b5, 0x05b4, 0x05b6, 0x09b3, 0x89b5, 0x09b7, 0x8db4, 0x8db6,
2605                0x91b3, 0x91b7,
2606   /* Fix _ */  0x9db9, 0x9dbf,
2607   0,
2608 };
2609
2610 struct ntsc_dec {
2611   char pattern[600];
2612   int ntscy[600];
2613   int ntsci[600];
2614   int ntscq[600];
2615   int multi[600];
2616   int multq[600];
2617   int brightness_control;
2618 };
2619
2620 /*
2621   First generate the I and Q reference signals, which we'll multiply by the
2622   input signal to accomplish the demodulation. Normally they are shifted 33
2623   degrees from the colorburst. I think this was convenient for
2624   inductor-capacitor-vacuum tube implementation.
2625                
2626   The tint control, FWIW, just adds a phase shift to the chroma signal, and 
2627   the color control controls the amplitude.
2628                
2629   In text modes (colormode==0) the system disabled the color burst, and no
2630   color was detected by the monitor.
2631
2632   freq_error gives a mismatch between the built-in oscillator and the TV's
2633   colorbust. Older II Plus machines seemed to occasionally get instability
2634   problems -- the crystal oscillator was a single transistor if I remember
2635   correctly -- and the frequency would vary enough that the tint would change
2636   across the width of the screen.  The left side would be in correct tint
2637   because it had just gotten resynchronized with the color burst.
2638 */
2639 static void
2640 ntsc_set_demod(struct ntsc_dec *it, double tint_control, 
2641                double color_control, double brightness_control,
2642                double freq_error, 
2643                int colormode)
2644 {
2645   int i;
2646
2647   it->brightness_control=(int)(1024.0*brightness_control);
2648
2649   for (i=0; i<600; i++) {
2650     double phase=90.0-90.0*i + freq_error*i/600.0 + tint_control;
2651     it->multi[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 65536.0 * 
2652                        color_control * colormode * 4);
2653     it->multq[i]=(int)(cos(3.1415926/180.0*(phase-33)) * 65536.0 * 
2654                        color_control * colormode * 4);
2655   }
2656 }
2657
2658 /* Here we model the analog circuitry of an NTSC television. Basically, it
2659    splits the signal into 3 signals: Y, I and Q. Y corresponds to luminance,
2660    and you get it by low-pass filtering the input signal to below 3.57 MHz.
2661
2662    I and Q are the in-phase and quadrature components of the 3.57 MHz
2663    subcarrier. We get them by multiplying by cos(3.57 MHz*t) and sin(3.57
2664    MHz*t), and low-pass filtering. Because the eye has less resolution in some
2665    colors than others, the I component gets low-pass filtered at 1.5 MHz and
2666    the Q at 0.5 MHz. The I component is approximately orange-blue, and Q is
2667    roughly purple-green. See http://www.ntsc-tv.com for details.
2668  */
2669 static void
2670 ntsc_to_yiq(struct ntsc_dec *it) 
2671 {
2672   int i;
2673   int fyx[10],fyy[10];
2674   int fix[10],fiy[10];
2675   int fqx[10],fqy[10];
2676   int pixghost;
2677   int iny,ini,inq,pix,blank;
2678   
2679   for (i=0; i<10; i++) fyx[i]=fyy[i]=fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
2680   pixghost=0;
2681   for (i=0; i<600; i++) {
2682     /* Get the video out signal, and add a teeny bit of ghosting, typical of RF
2683        monitor cables. This corresponds to a pretty long cable, but looks right
2684        to me. */
2685     pix=it->pattern[i]*1024;
2686     if (i>=20) pixghost += it->pattern[i-20]*15;
2687     if (i>=30) pixghost -= it->pattern[i-30]*15;
2688     pix += pixghost;
2689
2690     /* Get Y, I, Q before filtering */
2691     iny=pix;
2692     ini=(pix*it->multi[i])>>16;
2693     inq=(pix*it->multq[i])>>16;
2694             
2695     blank = (i>=7 && i<596 ? it->brightness_control : -200);
2696
2697     /* Now filter them. These are infinite impulse response filters calculated
2698        by the script at http://www-users.cs.york.ac.uk/~fisher/mkfilter. This
2699        is fixed-point integer DSP, son. No place for wimps. We do it in integer
2700        because you can count on integer being faster on most CPUs. We care
2701        about speed because we need to recalculate every time we blink text, and
2702        when we spew random bytes into screen memory. This is roughly 16.16
2703        fixed point arithmetic, but we scale some filter values up by a few bits
2704        to avoid some nasty precision errors. */
2705             
2706     /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz 
2707        with an extra zero at 3.5 MHz, from
2708        mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
2709        Delay about 2 */
2710
2711     fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3]; 
2712     fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6]; 
2713     fyx[6] = (iny * 1897) >> 13;
2714     fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3]; 
2715     fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6]; 
2716     fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
2717       + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
2718     if (i>=2) it->ntscy[i-2] = blank + (fyy[6]>>3);
2719
2720     /* Filter I and Q at 1.5 MHz. 3 pole Butterworth from
2721        mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0
2722        Delay about 3.
2723
2724        The NTSC spec says the Q value should be filtered at 0.5 MHz at the
2725        transmit end, But the Apple's video circuitry doesn't any such thing.
2726        AFAIK, oldish televisions (before comb filters) simply applied a 1.5 MHz
2727        filter to both after the demodulator.
2728     */
2729
2730     fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
2731     fix[3] = (ini * 1413) >> 14;
2732     fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
2733     fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
2734       + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
2735     if (i>=3) it->ntsci[i-3] = fiy[3]>>2;
2736
2737     fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
2738     fqx[3] = (inq * 1413) >> 14;
2739     fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
2740     fqy[3] = (fqx[0]+fqx[3]) + 3*(fqx[1]+fqx[2])
2741       + ((16559*fqy[0] - 72008*fqy[1] + 109682*fqy[2]) >> 16);
2742     if (i>=3) it->ntscq[i-3] = fqy[3]>>2;
2743
2744   }
2745   for (; i<610; i++) {
2746     if (i-2<600) it->ntscy[i-2]=0;
2747     if (i-3<600) it->ntsci[i-3]=0;
2748     if (i-9<600) it->ntscq[i-9]=0;
2749   }
2750 }
2751
2752 enum {
2753   A2_CMAP_HISTBITS=5,
2754   A2_CMAP_LEVELS=2,
2755   A2_CMAP_OFFSETS=4,
2756 };
2757
2758 #define A2_CMAP_INDEX(COLORMODE, LEVEL, HIST, OFFSET) \
2759 ((((COLORMODE)*A2_CMAP_LEVELS+(LEVEL))<<A2_CMAP_HISTBITS)+(HIST))* \
2760 A2_CMAP_OFFSETS+(OFFSET)
2761
2762 static void
2763 apple2(Display *dpy, Window window, int delay)
2764 {
2765   int w,h,i,j,x,y,textrow,row,col,stepno,colormode,imgrow;
2766   char c,*s;
2767   struct timeval basetime_tv;
2768   double next_actiontime;
2769   XWindowAttributes xgwa;
2770   int visclass;
2771   int screen_xo,screen_yo;
2772   XImage *image=NULL;
2773   XGCValues gcv;
2774   GC gc=NULL;
2775   XImage *text_im=NULL;
2776   unsigned long colors[A2_CMAP_INDEX(1, A2_CMAP_LEVELS-1,
2777                                      (1<<A2_CMAP_HISTBITS)-1,
2778                                      A2_CMAP_OFFSETS-3)+1];
2779   int n_colors=0;
2780   int screen_plan[24];
2781   struct ntsc_dec *dec=NULL;
2782   short *raw_rgb=NULL, *rrp;
2783   struct apple2_state *st=NULL;
2784   char *typing=NULL,*printing=NULL;
2785   char printbuf[1024];
2786   char prompt=']';
2787   int simulate_user;
2788   double tint_control,color_control,brightness_control,contrast_control;
2789   double freq_error=0.0,freq_error_inc=0.0;
2790   double horiz_desync=5.0;
2791   int flutter_horiz_desync=0;
2792   int flutter_tint=0;
2793   double crtload[192];
2794   int red_invprec,red_shift,green_invprec,green_shift,blue_invprec,blue_shift;
2795   int fillptr, fillbyte;
2796   int use_shm,use_cmap,use_color;
2797   /* localbyteorder is 1 if MSB first, 0 otherwise */
2798   unsigned int localbyteorder_loc = MSBFirst<<24;
2799   int localbyteorder=*(char *)&localbyteorder_loc;
2800 #ifdef HAVE_XSHM_EXTENSION
2801   XShmSegmentInfo shm_info;
2802 #endif
2803   
2804 #ifdef HAVE_XSHM_EXTENSION
2805   use_shm=get_boolean_resource ("useSHM", "Boolean");
2806 #else
2807   use_shm=0;
2808 #endif
2809
2810   /* Model the video controls on a standard television */
2811   tint_control = get_float_resource("apple2TVTint","Apple2TVTint");
2812   color_control = get_float_resource("apple2TVColor","Apple2TVColor")/100.0;
2813   brightness_control = get_float_resource("apple2TVBrightness",
2814                                           "Apple2TVBrightness") / 100.0;
2815   contrast_control = get_float_resource("apple2TVContrast",
2816                                         "Apple2TVContrast") / 100.0;
2817   simulate_user = get_boolean_resource("apple2SimulateUser",
2818                                        "Apple2SimulateUser");
2819
2820   XGetWindowAttributes (dpy, window, &xgwa);
2821   visclass=xgwa.visual->class;
2822   red_shift=red_invprec=green_shift=green_invprec=blue_shift=blue_invprec=-1;
2823   if (visclass == TrueColor || xgwa.visual->class == DirectColor) {
2824     use_cmap=0;
2825     use_color=!mono_p;
2826   }
2827   else if (visclass == PseudoColor || visclass == StaticColor) {
2828     use_cmap=1;
2829     use_color=!mono_p;
2830   }
2831   else {
2832     use_cmap=1;
2833     use_color=0;
2834   }
2835
2836   /* The Apple II screen was 280x192, sort of. We expand the width to 300
2837      pixels to allow for overscan. We then pick a size within the window
2838      that's an integer multiple of 300x192. The small case happens when
2839      we're displaying in a subwindow. Then it ends up showing the center
2840      of the screen, which is OK. */
2841   w=xgwa.width;
2842   h = (xgwa.height/192)*192;
2843   if (w<300) w=300;
2844   if (h==0) h=192;
2845
2846   dec=(struct ntsc_dec *)malloc(sizeof(struct ntsc_dec));
2847   
2848   if (use_cmap) {
2849     int hist,offset,level;
2850     int colorprec=8;
2851
2852   cmap_again:
2853     n_colors=0;
2854     /* Typically allocates 214 distinct colors, but will scale back its
2855        ambitions pretty far if it can't get them */
2856     for (colormode=0; colormode<=use_color; colormode++) {
2857       ntsc_set_demod(dec, tint_control, color_control, brightness_control,
2858                      0.0, colormode);
2859       for (level=0; level<2; level++) {
2860         for (hist=0; hist<(1<<A2_CMAP_HISTBITS); hist++) {
2861           for (offset=0; offset<4; offset++) {
2862             int interpy,interpi,interpq,r,g,b;
2863             int levelmult=level ? 64 : 32;
2864             int prec=colormode ? colorprec : (colorprec*2+2)/3;
2865             int precmask=(0xffff<<(16-prec))&0xffff;
2866             XColor col;
2867
2868             if (A2_CMAP_INDEX(colormode,level,hist,offset) != n_colors) {
2869               fprintf(stderr, "apple2: internal colormap allocation error\n");
2870               goto bailout;
2871             }
2872
2873             for (i=0; i<600; i++) dec->pattern[i]=0;
2874             for (i=0; i<A2_CMAP_HISTBITS; i++) {
2875               dec->pattern[64+offset-i]=(hist>>i)&1;
2876             }
2877         
2878             ntsc_to_yiq(dec);
2879             interpy=dec->ntscy[63+offset];
2880             interpi=dec->ntsci[63+offset];
2881             interpq=dec->ntscq[63+offset];
2882
2883             r=(interpy + ((+68128*interpi+40894*interpq)>>16))*levelmult;
2884             g=(interpy + ((-18087*interpi-41877*interpq)>>16))*levelmult;
2885             b=(interpy + ((-72417*interpi+113312*interpq)>>16))*levelmult;
2886             if (r<0) r=0;
2887             if (r>65535) r=65535;
2888             if (g<0) g=0;
2889             if (g>65535) g=65535;
2890             if (b<0) b=0;
2891             if (b>65535) b=65535;
2892           
2893             col.red=r & precmask;
2894             col.green=g & precmask;
2895             col.blue=b & precmask;
2896             col.pixel=0;
2897             if (!XAllocColor(dpy, xgwa.colormap, &col)) {
2898               XFreeColors(dpy, xgwa.colormap, colors, n_colors, 0L);
2899               n_colors=0;
2900               colorprec--;
2901               if (colorprec<3) {
2902                 goto bailout;
2903               }
2904               goto cmap_again;
2905             }
2906             colors[n_colors++]=col.pixel;
2907           }
2908         }
2909       }
2910     }
2911   } else {
2912     /* Is there a standard way to do this? Does this handle all cases? */
2913     int shift, prec;
2914     for (shift=0; shift<32; shift++) {
2915       for (prec=1; prec<16 && prec<32-shift; prec++) {
2916         unsigned long mask=(0xffffUL>>(16-prec)) << shift;
2917         if (red_shift<0 && mask==xgwa.visual->red_mask)
2918           red_shift=shift, red_invprec=16-prec;
2919         if (green_shift<0 && mask==xgwa.visual->green_mask)
2920           green_shift=shift, green_invprec=16-prec;
2921         if (blue_shift<0 && mask==xgwa.visual->blue_mask)
2922           blue_shift=shift, blue_invprec=16-prec;
2923       }
2924     }
2925     if (red_shift<0 || green_shift<0 || blue_shift<0) {
2926       if (0) fprintf(stderr,"Can't figure out color space\n");
2927       goto bailout;
2928     }
2929     raw_rgb=(short *)calloc(w*3, sizeof(short));
2930   }
2931
2932   gcv.background=0;
2933   gc = XCreateGC(dpy, window, GCBackground, &gcv);
2934   XSetWindowBackground(dpy, window, gcv.background);
2935   XClearWindow(dpy,window);
2936
2937   screen_xo=(xgwa.width-w)/2;
2938   screen_yo=(xgwa.height-h)/2;
2939
2940   if (use_shm) {
2941 #ifdef HAVE_XSHM_EXTENSION
2942     image = create_xshm_image (dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, 
2943                                &shm_info, w, h);
2944 #endif
2945     if (!image) {
2946       fprintf(stderr, "create_xshm_image failed\n");
2947       use_shm=0;
2948     }
2949   }
2950   if (!image) {
2951     image = XCreateImage(dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, 0,
2952                          w, h, 8, 0);
2953     image->data = (char *)calloc(image->height, image->bytes_per_line);
2954   }
2955
2956   st=(struct apple2_state *)calloc(1,sizeof(struct apple2_state));
2957
2958   /*
2959     Generate the font. It used a 5x7 font which looks a lot like the standard X
2960     6x10 font, with a few differences. So we render up all the uppercase
2961     letters of 6x10, and make a few tweaks (like putting a slash across the
2962     zero) according to fixfont.
2963    */
2964   {
2965     const char *def_font="6x10";
2966     XFontStruct *font;
2967     Pixmap text_pm;
2968     GC gc;
2969     
2970     font = XLoadQueryFont (dpy, def_font);
2971     if (!font) {
2972       fprintf(stderr,"Can't load font %s\n",def_font);
2973       goto bailout;
2974     }
2975     
2976     text_pm=XCreatePixmap(dpy, window, 64*7, 8, xgwa.depth);
2977     
2978     gcv.foreground=1;
2979     gcv.background=0;
2980     gcv.font=font->fid;
2981     gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2982     
2983     XSetForeground(dpy, gc, 0);
2984     XFillRectangle(dpy, text_pm, gc, 0, 0, 64*7, 8);
2985     XSetForeground(dpy, gc, 1);
2986     for (i=0; i<64; i++) {
2987       char c=32+i;
2988       int x=7*i+1;
2989       int y=7;
2990       if (c=='0') {
2991         c='O';
2992         XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2993       } else {
2994         XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2995       }
2996     }
2997     text_im = XGetImage(dpy, text_pm, 0, 0, 64*7, 8, ~0L, ZPixmap);
2998     XFreeGC(dpy, gc);
2999     XFreePixmap(dpy, text_pm);
3000
3001     for (i=0; a2_fixfont[i]; i++) {
3002       XPutPixel(text_im, a2_fixfont[i]&0x3ff,
3003                 (a2_fixfont[i]>>10)&0xf,
3004                 (a2_fixfont[i]>>15)&1);
3005     }
3006   }
3007
3008   /*
3009     Simulate plausible initial memory contents.
3010    */
3011   {
3012     int addr=0;
3013     while (addr<0x4000) {
3014       int n;
3015
3016       switch (random()%4) {
3017       case 0:
3018       case 1:
3019         n=random()%500;
3020         for (i=0; i<n && addr<0x4000; i++) {
3021           u_char rb=((random()%6==0 ? 0 : random()%16) |
3022                      ((random()%5==0 ? 0 : random()%16)<<4));
3023           a2_poke(st, addr++, rb);
3024         }
3025         break;
3026       
3027       case 2:
3028         /* Simulate shapes stored in memory. We use the font since we have it.
3029            Unreadable, since rows of each character are stored in consecutive
3030            bytes. It was typical to store each of the 7 possible shifts of
3031            bitmaps, for fastest blitting to the screen. */
3032         x=random()%(text_im->width);
3033         for (i=0; i<100; i++) {
3034           for (y=0; y<8; y++) {
3035             c=0;
3036             for (j=0; j<8; j++) {
3037               c |= XGetPixel(text_im, (x+j)%text_im->width, y)<<j;
3038             }
3039             a2_poke(st, addr++, c);
3040           }
3041           x=(x+1)%(text_im->width);
3042         }
3043         break;
3044
3045       case 3:
3046         if (addr>0x2000) {
3047           n=random()%200;
3048           for (i=0; i<n && addr<0x4000; i++) {
3049             a2_poke(st, addr++, 0);
3050           }
3051         }
3052         break;
3053
3054       }
3055     }
3056   }
3057   
3058   if (random()%4==0 &&
3059       !use_cmap && use_color &&
3060       xgwa.visual->bits_per_rgb>=8) {
3061     flutter_tint=1;
3062   }
3063   else if (random()%3==0) {
3064     flutter_horiz_desync=1;
3065   }
3066
3067   crtload[0]=0.0;
3068   stepno=0;
3069   a2_goto(st,23,0);
3070   gettimeofday(&basetime_tv, NULL);
3071   if (random()%2==0) basetime_tv.tv_sec -= 1; /* random blink phase */
3072   next_actiontime=0.0;
3073   fillptr=fillbyte=0;
3074   while (1) {
3075     double curtime,blinkphase;
3076     int startdisplayrow=0;
3077     int cheapdisplay=0;
3078     int nodelay=0;
3079     {
3080       struct timeval curtime_tv;
3081       gettimeofday(&curtime_tv, NULL);
3082       curtime=(curtime_tv.tv_sec - basetime_tv.tv_sec) + 
3083         0.000001*(curtime_tv.tv_usec - basetime_tv.tv_usec);
3084     }
3085     if (curtime>delay) goto finished;
3086
3087     if (bsod_sleep(dpy,0)) goto finished;
3088
3089     if (flutter_tint && st->gr_mode && !printing) {
3090       /* Oscillator instability. Look for freq_error below. We should only do
3091          this with color depth>=8, since otherwise you see pixels changing. */
3092       freq_error_inc += (-0.10*freq_error_inc
3093                          + ((int)(random()&0xff)-0x80) * 0.01);
3094       freq_error += freq_error_inc;
3095       a2_invalidate(st);
3096       nodelay=1;
3097     }
3098     else if (flutter_horiz_desync) {
3099       /* Horizontal sync during vertical sync instability. */
3100       horiz_desync += (-0.10*(horiz_desync-3.0) +
3101                        ((int)(random()&0xff)-0x80) * 
3102                        ((int)(random()&0xff)-0x80) *
3103                        ((int)(random()&0xff)-0x80) * 0.0000003);
3104       for (i=0; i<3; i++) st->rowimage[i]=-1;
3105       nodelay=1;
3106     } 
3107
3108     /* It's super-important to get the cursor/text flash out at exactly the
3109        right time, or it looks wrong. So if we're almost due for a blink, wait
3110        for it so we don't miss it in the middle of a screen update. */
3111     blinkphase=curtime/0.8;
3112     if (blinkphase-floor(blinkphase)>0.7 && !printing && !nodelay) {
3113       /* We're about to blink */
3114       int delay = ((1.0-(blinkphase-floor(blinkphase)))*0.8) * 1000000;
3115       if (delay<1000) delay=1000;
3116       usleep(delay);
3117       continue;
3118     }
3119
3120     /* The blinking rate was controlled by 555 timer with a resistor/capacitor
3121        time constant. Because the capacitor was electrolytic, the flash rate
3122        varied somewhat between machines. I'm guessing 1.6 seconds/cycle was
3123        reasonable. (I soldered a resistor in mine to make it blink faster.) */
3124     i=st->blink;
3125     st->blink=((int)blinkphase)&1;
3126     if (st->blink!=i && !(st->gr_mode&A2_GR_FULL)) {
3127       int downcounter=0;
3128       /* For every row with blinking text, set the changed flag. This basically
3129          works great except with random screen garbage in text mode, when we
3130          end up redrawing the whole screen every second */
3131       for (row=(st->gr_mode ? 20 : 0); row<24; row++) {
3132         for (col=0; col<40; col++) {
3133           c=st->textlines[row][col];
3134           if ((c & 0xc0) == 0x40) {
3135             downcounter=4;
3136             break;
3137           }
3138         }
3139         if (downcounter>0) {
3140           st->rowimage[row]=-1;
3141           downcounter--;
3142         }
3143       }
3144       st->rowimage[st->cursy]=-1;
3145       startdisplayrow=random()%24;
3146     } 
3147     else if (next_actiontime > curtime && !printing && !nodelay) {
3148       int delay = (next_actiontime-curtime)*1000000;
3149
3150       if (delay>100000) delay=100000;
3151       if (delay<1000) delay=1000;
3152       usleep(delay);
3153       continue;
3154     }
3155
3156     if (printing) {
3157       cheapdisplay=1;
3158       while (*printing) {
3159         if (*printing=='\001') { /* pause */
3160           printing++;
3161           for (i=20; i<24; i++) st->rowimage[i]=-1;
3162           break;
3163         } 
3164         else if (*printing=='\n') {
3165           a2_printc(st,*printing);
3166           printing++;
3167           break;
3168         }
3169         else {
3170           a2_printc(st,*printing);
3171           printing++;
3172         }
3173       }
3174       if (!*printing) printing=NULL;
3175     }
3176     else if (curtime >= next_actiontime) {
3177       if (typing) {
3178         /* If we're in the midst of typing a string, emit a character with
3179            random timing. */
3180         a2_printc(st, *typing);
3181         if (*typing=='\n') {
3182           next_actiontime = curtime;
3183         } else {
3184           next_actiontime = curtime + (random()%1000)*0.0003 + 0.3;
3185         }
3186         typing++;
3187
3188         if (!*typing) typing=NULL;
3189
3190       } 
3191       else {
3192         next_actiontime=curtime;
3193
3194         switch(stepno) {
3195         case 0:
3196           a2_invalidate(st);
3197           if (0) {
3198             /*
3199               For testing color rendering. The spec is:
3200                            red grn blu
3201               0  black       0   0   0
3202               1  red       227  30  96
3203               2  dk blue    96  78 189
3204               3  purple    255  68 253
3205               4  dk green    0 163  96
3206               5  gray      156 156 156
3207               6  med blue   20 207 253
3208               7  lt blue   208 195 255
3209               8  brown      96 114   3
3210               9  orange    255 106  60
3211               10 grey      156 156 156
3212               11 pink      255 160 208
3213               12 lt green   20 245  60
3214               13 yellow    208 221 141
3215               14 aqua      114 255 208
3216               15 white     255 255 255
3217             */
3218             st->gr_mode=A2_GR_LORES;
3219             for (row=0; row<24; row++) {
3220               for (col=0; col<40; col++) {
3221                 st->textlines[row][col]=(row&15)*17;
3222               }
3223             }
3224             next_actiontime+=0.4;
3225             stepno=88;
3226           }
3227           else if (random()%3==0) {
3228             st->gr_mode=0;
3229             next_actiontime+=0.4;
3230             stepno=88;
3231           }
3232           else if (random()%4==0) {
3233             st->gr_mode=A2_GR_LORES;
3234             if (random()%3==0) st->gr_mode |= A2_GR_FULL;
3235             next_actiontime+=0.4;
3236             stepno=88;
3237           } 
3238           else if (random()%2==0) {
3239             st->gr_mode=A2_GR_HIRES;
3240             stepno=73;
3241           }
3242           else {
3243             st->gr_mode=A2_GR_HIRES;
3244             next_actiontime+=0.4;
3245             stepno=88;
3246           }
3247           break;
3248
3249         case 88:
3250           /* An illegal instruction or a reset caused it to drop into the
3251              assembly language monitor, where you could disassemble code & view
3252              data in hex. */
3253           if (random()%3==0) {
3254             char ibytes[128];
3255             char itext[128];
3256             int addr=0xd000+random()%0x3000;
3257             sprintf(ibytes,
3258                     "%02X",random()%0xff);
3259             sprintf(itext,
3260                     "???");
3261             sprintf(printbuf,
3262                     "\n\n"
3263                     "%04X: %-15s %s\n"
3264                     " A=%02X X=%02X Y=%02X S=%02X F=%02X\n"
3265                     "*",
3266                     addr,ibytes,itext,
3267                     random()%0xff, random()%0xff,
3268                     random()%0xff, random()%0xff,
3269                     random()%0xff);
3270             printing=printbuf;
3271             a2_goto(st,23,1);
3272             if (st->gr_mode) {
3273               stepno=11;
3274             } else {
3275               stepno=13;
3276             }
3277             prompt='*';
3278             next_actiontime += 2.0 + (random()%1000)*0.0002;
3279           }
3280           else {
3281             /* Lots of programs had at least their main functionality in
3282                Applesoft Basic, which had a lot of limits (memory, string
3283                length, etc) and would sometimes crash unexpectedly. */
3284             sprintf(printbuf,
3285                     "\n"
3286                     "\n"
3287                     "\n"
3288                     "?%s IN %d\n"
3289                     "\001]",
3290                     apple2_basic_errors[random() %
3291                                         (sizeof(apple2_basic_errors)
3292                                          /sizeof(char *))],
3293                     (1000*(random()%(random()%59+1)) +
3294                      100*(random()%(random()%9+1)) +
3295                      5*(random()%(random()%199+1)) +
3296                      1*(random()%(random()%(random()%2+1)+1))));
3297             printing=printbuf;
3298             a2_goto(st,23,1);
3299             stepno=1;
3300             prompt=']';
3301             next_actiontime += 2.0 + (random()%1000)*0.0002;
3302           }
3303           break;
3304       
3305         case 1:
3306           if (simulate_user && random()%3==0) {
3307             /* This was how you reset the Basic interpreter. The sort of
3308                incantation you'd have on a little piece of paper taped to the
3309                side of your machine */
3310             typing="CALL -1370";
3311             stepno=2;
3312           } 
3313           else if (simulate_user && random()%2==0) {
3314             typing="CATALOG\n";
3315             stepno=22;
3316           }
3317           else {
3318             next_actiontime+=1.0;
3319             stepno=6;
3320           }
3321           break;
3322
3323         case 2:
3324           stepno=3;
3325           next_actiontime += 0.5;
3326           break;
3327       
3328         case 3:
3329           st->gr_mode=0;
3330           a2_cls(st);
3331           a2_goto(st,0,16);
3332           for (s="APPLE ]["; *s; s++) a2_printc(st,*s);
3333           a2_goto(st,23,0);
3334           a2_printc(st,']');
3335           next_actiontime+=1.0;
3336           stepno=6;
3337           break;
3338
3339         case 6:
3340           if (simulate_user && random()%50==0 && 0) { /* disabled, too goofy */
3341             typing="10 PRINT \"TRS-80S SUCK!!!\"\n"
3342               "]20 GOTO 10\n"
3343               "]RUN";
3344             stepno=7;
3345           }
3346           else {
3347             stepno=8;
3348             next_actiontime += delay;
3349           }
3350           break;
3351
3352         case 7:
3353           for (i=0; i<30; i++) {
3354             for (s="\nTRS-80S SUCK"; *s; s++) a2_printc(st,*s);
3355           }
3356           stepno=8;
3357           next_actiontime+=delay;
3358
3359         case 8:
3360           break;
3361
3362         case 22:
3363           if (random()%50==0) {
3364             sprintf(printbuf,"\nDISK VOLUME 254\n\n"
3365                     " A 002 HELLO\n"
3366                     "\n"
3367                     "]");
3368             printing=printbuf;
3369           }
3370           else {
3371             sprintf(printbuf,"\n?%s\n]",
3372                     apple2_dos_errors[random()%
3373                                       (sizeof(apple2_dos_errors) /
3374                                        sizeof(char *))]);
3375             printing=printbuf;
3376           }
3377           stepno=6;
3378           next_actiontime+=1.0;
3379           break;
3380
3381         case 11:
3382           if (simulate_user && random()%2==0) {
3383             /* This was how you went back to text mode in the monitor */
3384             typing="FB4BG";
3385             stepno=12;
3386           } else {
3387             next_actiontime+=1.0;
3388             stepno=6;
3389           }
3390           break;
3391
3392         case 12:
3393           st->gr_mode=0;
3394           a2_invalidate(st);
3395           a2_printc(st,'\n');
3396           a2_printc(st,'*');
3397           stepno=13;
3398           next_actiontime+=2.0;
3399           break;
3400
3401         case 13:
3402           /* This reset things into Basic */
3403           if (simulate_user && random()%2==0) {
3404             typing="FAA6G";
3405             stepno=2;
3406           }
3407           else {
3408             stepno=8;
3409             next_actiontime+=delay;
3410           }
3411           break;
3412
3413         case 73:
3414           for (i=0; i<1500; i++) {
3415             a2_poke(st, fillptr, fillbyte);
3416             fillptr++;
3417             fillbyte = (fillbyte+1)&0xff;
3418           }
3419           next_actiontime += 0.08;
3420           /* When you hit c000, it changed video settings */
3421           if (fillptr>=0xc000) {
3422             a2_invalidate(st);
3423             st->gr_mode=0;
3424           }
3425           /* And it seemed to reset around here, I dunno why */
3426           if (fillptr>=0xcf00) stepno=3;
3427           break;
3428         }
3429       }
3430     }
3431
3432     /* Now, we turn the data in the Apple II video into a screen display. This
3433        is interesting because of the interaction with the NTSC color decoding
3434        in a color television. */
3435
3436     colormode=use_color && st->gr_mode!=0;
3437     if (!use_cmap) {
3438       ntsc_set_demod(dec, tint_control, color_control, brightness_control,
3439                      freq_error, colormode);
3440     }
3441     imgrow=0;
3442     for (textrow=0; textrow<24; textrow++) {
3443       if (st->rowimage[textrow] == textrow) {
3444         screen_plan[textrow]=0;
3445       }
3446       else if (cheapdisplay && st->rowimage[textrow]>=0 &&
3447                textrow<21 && st->rowimage[textrow]<21 && 
3448                st->rowimage[textrow]>=2 && textrow>=2 &&
3449                (st->rowimage[textrow]+1)*h/24 + screen_xo <= xgwa.height) {
3450         screen_plan[textrow]= A2_SP_COPY | st->rowimage[textrow];
3451         for (i=0; i<8; i++) {
3452           crtload[textrow*8+i]=crtload[st->rowimage[textrow]*8+i];
3453         }
3454         startdisplayrow=0;
3455       }
3456       else {
3457         st->rowimage[textrow]=imgrow;
3458         screen_plan[textrow]=imgrow | A2_SP_PUT;
3459         
3460         for (row=textrow*8; row<textrow*8+8; row++) {
3461           char *pp;
3462           int pixmultinc,pixbright;
3463           int scanstart_i, scanend_i;
3464           int squishright_i, squishdiv;
3465           int pixrate;
3466           double bloomthisrow,shiftthisrow;
3467           int ytop=(imgrow*h/24) + ((row-textrow*8) * h/192);
3468           int ybot=ytop+h/192;
3469
3470           /* First we generate the pattern that the video circuitry shifts out
3471              of memory. It has a 14.something MHz dot clock, equal to 4 times
3472              the color burst frequency. So each group of 4 bits defines a
3473              color.  Each character position, or byte in hires, defines 14
3474              dots, so odd and even bytes have different color spaces. So,
3475              pattern[0..600] gets the dots for one scan line. */
3476
3477           memset(dec->pattern,0,sizeof(dec->pattern));
3478           pp=dec->pattern+20;
3479         
3480           if ((st->gr_mode&A2_GR_HIRES) && (row<160 ||
3481                                             (st->gr_mode&A2_GR_FULL))) {
3482
3483             /* Emulate the mysterious pink line, due to a bit getting
3484                stuck in a shift register between the end of the last
3485                row and the beginning of this one. */
3486             if ((st->hireslines[row][0] & 0x80) &&
3487                 (st->hireslines[row][39]&0x40)) {
3488               pp[-1]=1;
3489             }
3490
3491             for (col=0; col<40; col++) {
3492               u_char b=st->hireslines[row][col];
3493               int shift=(b&0x80)?0:1;
3494
3495               /* Each of the low 7 bits in hires mode corresponded to 2 dot
3496                  clocks, shifted by one if the high bit was set. */
3497               for (i=0; i<7; i++) {
3498                 pp[shift+1] = pp[shift] =(b>>i)&1;
3499                 pp+=2;
3500               }
3501             }
3502           } 
3503           else if ((st->gr_mode&A2_GR_LORES) && (row<160 ||
3504                                                  (st->gr_mode&A2_GR_FULL))) {
3505             for (col=0; col<40; col++) {
3506               u_char nib=(st->textlines[textrow][col] >> (((row/4)&1)*4))&0xf;
3507               /* The low or high nybble was shifted out one bit at a time. */
3508               for (i=0; i<14; i++) {
3509                 *pp = (nib>>((col*14+i)&3))&1;
3510                 pp++;
3511               }
3512             }
3513           }
3514           else {
3515             for (col=0; col<40; col++) {
3516               int rev;
3517               c=st->textlines[textrow][col];
3518               /* hi bits control inverse/blink as follows:
3519                   0x00: inverse
3520                   0x40: blink
3521                   0x80: normal
3522                   0xc0: normal */
3523               rev=!(c&0x80) && (!(c&0x40) || st->blink);
3524
3525               for (i=0; i<7; i++) {
3526                 for (i=0; i<7; i++) {
3527                   unsigned long pix=XGetPixel(text_im,
3528                                               ((c&0x3f)^0x20)*7+i, row%8);
3529                   pp[1] = pp[2] = pix^rev;
3530                   pp+=2;
3531                 }
3532               }
3533             }
3534           }
3535
3536           /*
3537             Interpolate the 600-dotclock line into however many horizontal
3538             screen pixels we're using, and convert to RGB. 
3539
3540             We add some 'bloom', variations in the horizontal scan width with
3541             the amount of brightness, extremely common on period TV sets. They
3542             had a single oscillator which generated both the horizontal scan
3543             and (during the horizontal retrace interval) the high voltage for
3544             the electron beam. More brightness meant more load on the
3545             oscillator, which caused an decrease in horizontal deflection. Look
3546             for (bloomthisrow).
3547
3548             Also, the A2 did a bad job of generating horizontal sync pulses
3549             during the vertical blanking interval. This, and the fact that the
3550             horizontal frequency was a bit off meant that TVs usually went a
3551             bit out of sync during the vertical retrace, and the top of the
3552             screen would be bent a bit to the left or right. Look for
3553             (shiftthisrow).
3554
3555             We also add a teeny bit of left overscan, just enough to be
3556             annoying, but you can still read the left column of text.
3557             
3558             We also simulate compression & brightening on the right side of the
3559             screen. Most TVs do this, but you don't notice because they
3560             overscan so it's off the right edge of the CRT. But the A2 video
3561             system used so much of the horizontal scan line that you had to
3562             crank the horizontal width down in order to not lose the right few
3563             characters, and you'd see the compression on the right
3564             edge. Associated with compression is brightening; since the
3565             electron beam was scanning slower, the same drive signal hit the
3566             phosphor harder. Look for (squishright_i) and (squishdiv).
3567           */
3568
3569           for (i=j=0; i<600; i++) {
3570             j += dec->pattern[i];
3571           }
3572           crtload[row] = (crtload[row>1 ? row-1 : 0]) * 0.98 + 0.02*(j/600.0) +
3573             (row>180 ? (row-180)*(row-180)*0.0005 : 0.0);
3574           bloomthisrow = -10.0 * crtload[row];
3575           shiftthisrow=((row<18) ? ((18-row)*(18-row)* 0.002 + (18-row)*0.05)
3576                         * horiz_desync : 0.0);
3577
3578           scanstart_i=(int)((bloomthisrow+shiftthisrow+18.0)*65536.0);
3579           if (scanstart_i<0) scanstart_i=0;
3580           if (scanstart_i>30*65536) scanstart_i=30*65536;
3581           scanend_i=599*65536;
3582           squishright_i=scanstart_i + 530*65536;
3583           squishdiv=w/15;
3584           pixrate=(int)((560.0-2.0*bloomthisrow)*65536.0/w);
3585           
3586           if (use_cmap) {
3587             for (y=ytop; y<ybot; y++) {
3588               int level=(!(y==ytop && ybot-ytop>=3) &&
3589                          !(y==ybot-1 && ybot-ytop>=5));
3590               int hist=0;
3591               int histi=0;
3592
3593               pixmultinc=pixrate;
3594               for (x=0, i=scanstart_i;
3595                    x<w && i<scanend_i;
3596                    x++, i+=pixmultinc) {
3597                 int pati=(i>>16);
3598                 int offset=pati&3;
3599                 while (pati>=histi) {
3600                   hist=(((hist<<1) & ((1<<A2_CMAP_HISTBITS)-1)) |
3601                         dec->pattern[histi]);
3602                   histi++;
3603                 }
3604                 XPutPixel(image, x, y, 
3605                           colors[A2_CMAP_INDEX(colormode,level,hist,offset)]);
3606                 if (i >= squishright_i) {
3607                   pixmultinc += pixmultinc/squishdiv;
3608                 }
3609               }
3610               for ( ; x<w; x++) {
3611                 XPutPixel(image, x, y, colors[0]);
3612               }
3613             }
3614           } else {
3615
3616             ntsc_to_yiq(dec);
3617
3618             pixbright=(int)(contrast_control*65536.0);
3619             pixmultinc=pixrate;
3620             for (x=0, i=scanstart_i, rrp=raw_rgb;
3621                  x<w && i<scanend_i;
3622                  x++, i+=pixmultinc, rrp+=3) {
3623               int pixfrac=i&0xffff;
3624               int invpixfrac=65536-pixfrac;
3625               int pati=i>>16;
3626               int r,g,b;
3627
3628               int interpy=((dec->ntscy[pati]*invpixfrac + 
3629                             dec->ntscy[pati+1]*pixfrac)>>16);
3630               int interpi=((dec->ntsci[pati]*invpixfrac + 
3631                             dec->ntsci[pati+1]*pixfrac)>>16);
3632               int interpq=((dec->ntscq[pati]*invpixfrac + 
3633                             dec->ntscq[pati+1]*pixfrac)>>16);
3634
3635               /*
3636                 According to the NTSC spec, Y,I,Q are generated as:
3637                 
3638                 y=0.30 r + 0.59 g + 0.11 b
3639                 i=0.60 r - 0.28 g - 0.32 b
3640                 q=0.21 r - 0.52 g + 0.31 b
3641                 
3642                 So if you invert the implied 3x3 matrix you get what standard
3643                 televisions implement with a bunch of resistors (or directly in
3644                 the CRT -- don't ask):
3645                 
3646                 r = y + 0.948 i + 0.624 q
3647                 g = y - 0.276 i - 0.639 q
3648                 b = y - 1.105 i + 1.729 q
3649                 
3650                 These coefficients are below in 16.16 format.
3651               */
3652
3653               r=((interpy + ((+68128*interpi+40894*interpq)>>16))*pixbright)
3654                                                            >>16;
3655               g=((interpy + ((-18087*interpi-41877*interpq)>>16))*pixbright)
3656                                                            >>16;
3657               b=((interpy + ((-72417*interpi+113312*interpq)>>16))*pixbright)
3658                                                             >>16;
3659               if (r<0) r=0;
3660               if (g<0) g=0;
3661               if (b<0) b=0;
3662               rrp[0]=r;
3663               rrp[1]=g;
3664               rrp[2]=b;
3665
3666               if (i>=squishright_i) {
3667                 pixmultinc += pixmultinc/squishdiv;
3668                 pixbright += pixbright/squishdiv;
3669               }
3670             }
3671             for ( ; x<w; x++, rrp+=3) {
3672               rrp[0]=rrp[1]=rrp[2]=0;
3673             }
3674
3675             for (y=ytop; y<ybot; y++) {
3676               /* levelmult represents the vertical size of scan lines. Each
3677                  line is brightest in the middle, and there's a dark band
3678                  between them. */
3679               int levelmult;
3680               double levelmult_fp=(y + 0.5 - (ytop+ybot)*0.5) / (ybot-ytop);
3681               levelmult_fp = 1.0-(levelmult_fp*levelmult_fp*levelmult_fp
3682                                   *levelmult_fp)*16.0;
3683               if (levelmult_fp<0.0) levelmult_fp=0.0;
3684               levelmult = (int)(64.9*levelmult_fp);
3685
3686               /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes
3687                   to show why standard graphics sw has to be fast, or else
3688                   people will have to work around it and risk incompatibility.
3689                   The quickdraw folks understood this. The other answer would
3690                   be for X11 to have fewer formats for bitm.. oh, never
3691                   mind. If neither of these cases work (they probably cover 99%
3692                   of setups) it falls back on the Xlib routines. */
3693               if (image->format==ZPixmap && image->bits_per_pixel==32 && 
3694                   sizeof(unsigned long)==4 &&
3695                   image->byte_order==localbyteorder) {
3696                 unsigned long *pixelptr =
3697                   (unsigned long *) (image->data + y * image->bytes_per_line);
3698                 for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
3699                   unsigned long ntscri, ntscgi, ntscbi;
3700                   ntscri=((unsigned long)rrp[0])*levelmult;
3701                   ntscgi=((unsigned long)rrp[1])*levelmult;
3702                   ntscbi=((unsigned long)rrp[2])*levelmult;
3703                   if (ntscri>65535) ntscri=65535;
3704                   if (ntscgi>65535) ntscgi=65535;
3705                   if (ntscbi>65535) ntscbi=65535;
3706                   *pixelptr++ = ((ntscri>>red_invprec)<<red_shift) |
3707                     ((ntscgi>>green_invprec)<<green_shift) |
3708                     ((ntscbi>>blue_invprec)<<blue_shift);
3709                 }
3710               }
3711               else if (image->format==ZPixmap && image->bits_per_pixel==16 && 
3712                        sizeof(unsigned short)==2 &&
3713                        image->byte_order==localbyteorder) {
3714                 unsigned short *pixelptr =
3715                 (unsigned short *)(image->data + y*image->bytes_per_line);
3716                 for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
3717                   unsigned long ntscri, ntscgi, ntscbi;
3718                   ntscri=((unsigned long)rrp[0])*levelmult;
3719                   ntscgi=((unsigned long)rrp[1])*levelmult;
3720                   ntscbi=((unsigned long)rrp[2])*levelmult;
3721                   if (ntscri>65535) ntscri=65535;
3722                   if (ntscgi>65535) ntscgi=65535;
3723                   if (ntscbi>65535) ntscbi=65535;
3724                   *pixelptr++ = ((ntscri>>red_invprec)<<red_shift) |
3725                     ((ntscgi>>green_invprec)<<green_shift) |
3726                     ((ntscbi>>blue_invprec)<<blue_shift);
3727                 }
3728                 
3729               }
3730               else {
3731                 for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
3732                   unsigned long pixel, ntscri, ntscgi, ntscbi;
3733                   /* Convert to 16-bit color values, with saturation. The ntscr
3734                      values are 22.10 fixed point, and levelmult is 24.6, so we
3735                      get 16 bits out*/
3736                   ntscri=((unsigned long)rrp[0])*levelmult;
3737                   ntscgi=((unsigned long)rrp[1])*levelmult;
3738                   ntscbi=((unsigned long)rrp[2])*levelmult;
3739                   if (ntscri>65535) ntscri=65535;
3740                   if (ntscgi>65535) ntscgi=65535;
3741                   if (ntscbi>65535) ntscbi=65535;
3742                   pixel = ((ntscri>>red_invprec)<<red_shift) |
3743                     ((ntscgi>>green_invprec)<<green_shift) |
3744                     ((ntscbi>>blue_invprec)<<blue_shift);
3745                   XPutPixel(image, x, y, pixel);
3746                 }
3747               }
3748             }
3749           }
3750         }
3751         imgrow++;
3752       }
3753     }
3754
3755     /* For just the the rows which changed, blit the image to the screen. */
3756     for (textrow=0; textrow<24; ) {
3757       int top,bot,srcrow,srctop,nrows;
3758       
3759       nrows=1;
3760       while (textrow+nrows < 24 &&
3761              screen_plan[textrow+nrows] == screen_plan[textrow]+nrows)
3762         nrows++;
3763
3764       top=h*textrow/24;
3765       bot=h*(textrow+nrows)/24;
3766       srcrow=screen_plan[textrow]&A2_SP_ROWMASK;
3767       srctop=srcrow*h/24;
3768
3769       if (screen_plan[textrow] & A2_SP_COPY) {
3770         if (0) printf("Copy %d screenrows %d to %d\n", nrows, srcrow, textrow);
3771         XCopyArea(dpy, window, window, gc,
3772                   screen_xo, screen_yo + srctop,
3773                   w, bot-top,
3774                   screen_xo, screen_yo + top);
3775       }
3776       else if (screen_plan[textrow] & A2_SP_PUT) {
3777         if (0) printf("Draw %d imgrows %d to %d\n", nrows, srcrow, textrow);
3778         if (use_shm) {
3779 #ifdef HAVE_XSHM_EXTENSION
3780           XShmPutImage(dpy, window, gc, image, 
3781                        0, srctop, screen_xo, screen_yo + top,
3782                        w, bot-top, False);
3783 #endif
3784         } else {
3785           XPutImage(dpy, window, gc, image, 
3786                     0, srctop,
3787                     screen_xo, screen_yo + top,
3788                     w, bot-top);
3789         }
3790       }
3791       textrow += nrows;
3792     }
3793     XSync(dpy,0);
3794
3795     for (textrow=0; textrow<24; textrow++) {
3796       st->rowimage[textrow]=textrow;
3797     }
3798   }
3799
3800  finished:
3801   XSync(dpy,False);
3802   XClearWindow(dpy, window);
3803   goto cleanup;
3804
3805  bailout:
3806   ;
3807
3808  cleanup:
3809   if (image) {
3810     if (use_shm) {
3811 #ifdef HAVE_XSHM_EXTENSION
3812       destroy_xshm_image(dpy, image, &shm_info);
3813 #endif
3814     } else {
3815       XDestroyImage(image);
3816     }
3817     image=NULL;
3818   }
3819   if (text_im) XDestroyImage(text_im);
3820   if (gc) XFreeGC(dpy, gc);
3821   if (st) free(st);
3822   if (raw_rgb) free(raw_rgb);
3823   if (dec) free(dec);
3824   if (n_colors) XFreeColors(dpy, xgwa.colormap, colors, n_colors, 0L);
3825 }
3826
3827
3828 \f
3829 char *progclass = "BSOD";
3830
3831 char *defaults [] = {
3832   "*delay:                 30",
3833
3834   "*doOnly:                ",
3835   "*doWindows:             True",
3836   "*doNT:                  True",
3837   "*doWin2K:               True",
3838   "*doAmiga:               True",
3839   "*doMac:                 True",
3840   "*doMacsBug:             True",
3841   "*doMac1:                True",
3842   "*doMacX:                True",
3843   "*doSCO:                 True",
3844   "*doAtari:               False",      /* boring */
3845   "*doBSD:                 False",      /* boring */
3846   "*doLinux:               True",
3847   "*doSparcLinux:          False",      /* boring */
3848   "*doBlitDamage:          True",
3849   "*doSolaris:             True",
3850   "*doHPUX:                True",
3851   "*doApple2:              True",
3852   "*doOS390:               True",
3853
3854   ".Windows.font:          -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3855   ".Windows.font2:         -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
3856   ".Windows.foreground:    White",
3857   ".Windows.background:    #0000AA",    /* EGA color 0x01. */
3858
3859   ".Amiga.font:            -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3860   ".Amiga.font2:           -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
3861   ".Amiga.foreground:      Red",
3862   ".Amiga.background:      Black",
3863   ".Amiga.background2:     White",
3864
3865   ".Mac.font:              -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3866   ".Mac.foreground:        PaleTurquoise1",
3867   ".Mac.background:        Black",
3868
3869   ".Atari.foreground:      Black",
3870   ".Atari.background:      White",
3871
3872   ".MacsBug.font:          -*-courier-medium-r-*-*-*-100-*-*-m-*-*-*",
3873   ".MacsBug.font2:         -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3874   ".MacsBug.font3:         -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3875   ".MacsBug.foreground:    Black",
3876   ".MacsBug.background:    White",
3877   ".MacsBug.borderColor:   #AAAAAA",
3878
3879   ".mac1.foreground:       Black",
3880   ".mac1.background:       White",
3881
3882   ".macX.textForeground:   White",
3883   ".macX.textBackground:   Black",
3884   ".macX.background:       #888888",
3885   ".macX.font:             -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3886   ".macX.font2:            -*-courier-bold-r-*-*-*-240-*-*-m-*-*-*",
3887
3888   ".SCO.font:              -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3889   ".SCO.font2:             -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3890   ".SCO.foreground:        White",
3891   ".SCO.background:        Black",
3892
3893   ".Linux.font:            9x15bold",
3894   ".Linux.font2:           -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3895   ".Linux.foreground:      White",
3896   ".Linux.background:      Black",
3897
3898   ".SparcLinux.font:       -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3899   ".SparcLinux.font2:      -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3900   ".SparcLinux.foreground: White",
3901   ".SparcLinux.background: Black",
3902
3903   ".BSD.font:              vga",
3904   ".BSD.font:              -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3905   ".BSD.font2:             -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3906 /* ".BSD.font2:            -sun-console-medium-r-*-*-22-*-*-*-m-*-*-*", */
3907   ".BSD.foreground:        #c0c0c0",
3908   ".BSD.background:        Black",
3909
3910   ".Solaris.font:          -sun-gallant-*-*-*-*-19-*-*-*-*-120-*-*",
3911   ".Solaris.font2:         -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3912   ".Solaris.foreground:    Black",
3913   ".Solaris.background:    White",
3914   "*dontClearRoot:         True",
3915
3916   ".HPUX.font:             9x15bold",
3917   ".HPUX.font2:            -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3918   ".HPUX.foreground:       White",
3919   ".HPUX.background:       Black",
3920
3921   ".OS390.font:            9x15bold",
3922   ".OS390.font2:           -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3923   ".OS390.background:      Black",
3924   ".OS390.foreground:      Red",
3925
3926   "*apple2TVColor:         50",
3927   "*apple2TVTint:          5",
3928   "*apple2TVBrightness:    10",
3929   "*apple2TVContrast:      90",
3930   "*apple2SimulateUser:    True",
3931
3932 #ifdef HAVE_XSHM_EXTENSION
3933   "*useSHM:                True",
3934 #endif
3935   0
3936 };
3937
3938 XrmOptionDescRec options [] = {
3939   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
3940   { "-only",            ".doOnly",              XrmoptionSepArg, 0 },
3941   { "-windows",         ".doWindows",           XrmoptionNoArg,  "True"  },
3942   { "-no-windows",      ".doWindows",           XrmoptionNoArg,  "False" },
3943   { "-nt",              ".doNT",                XrmoptionNoArg,  "True"  },
3944   { "-no-nt",           ".doNT",                XrmoptionNoArg,  "False" },
3945   { "-2k",              ".doWin2K",             XrmoptionNoArg,  "True"  },
3946   { "-no-2k",           ".doWin2K",             XrmoptionNoArg,  "False" },
3947   { "-amiga",           ".doAmiga",             XrmoptionNoArg,  "True"  },
3948   { "-no-amiga",        ".doAmiga",             XrmoptionNoArg,  "False" },
3949   { "-mac",             ".doMac",               XrmoptionNoArg,  "True"  },
3950   { "-no-mac",          ".doMac",               XrmoptionNoArg,  "False" },
3951   { "-mac1",            ".doMac1",              XrmoptionNoArg,  "True"  },
3952   { "-no-mac1",         ".doMac1",              XrmoptionNoArg,  "False" },
3953   { "-macx",            ".doMacX",              XrmoptionNoArg,  "True"  },
3954   { "-no-macx",         ".doMacX",              XrmoptionNoArg,  "False" },
3955   { "-atari",           ".doAtari",             XrmoptionNoArg,  "True"  },
3956   { "-no-atari",        ".doAtari",             XrmoptionNoArg,  "False" },
3957   { "-macsbug",         ".doMacsBug",           XrmoptionNoArg,  "True"  },
3958   { "-no-macsbug",      ".doMacsBug",           XrmoptionNoArg,  "False" },
3959   { "-apple2",          ".doApple2",            XrmoptionNoArg,  "True"  },
3960   { "-no-apple2",       ".doApple2",            XrmoptionNoArg,  "False" },
3961   { "-sco",             ".doSCO",               XrmoptionNoArg,  "True"  },
3962   { "-no-sco",          ".doSCO",               XrmoptionNoArg,  "False" },
3963   { "-bsd",             ".doBSD",               XrmoptionNoArg,  "True"  },
3964   { "-no-bsd",          ".doBSD",               XrmoptionNoArg,  "False" },
3965   { "-linux",           ".doLinux",             XrmoptionNoArg,  "True"  },
3966   { "-no-linux",        ".doLinux",             XrmoptionNoArg,  "False" },
3967   { "-sparclinux",      ".doSparcLinux",        XrmoptionNoArg,  "True"  },
3968   { "-no-sparclinux",   ".doSparcLinux",        XrmoptionNoArg,  "False" },
3969   { "-blitdamage",      ".doBlitDamage",        XrmoptionNoArg,  "True"  },
3970   { "-no-blitdamage",   ".doBlitDamage",        XrmoptionNoArg,  "False" },
3971   { "-solaris",         ".doSolaris",           XrmoptionNoArg,  "True"  },
3972   { "-no-solaris",      ".doSolaris",           XrmoptionNoArg,  "False" },
3973   { "-hpux",            ".doHPUX",              XrmoptionNoArg,  "True"  },
3974   { "-no-hpux",         ".doHPUX",              XrmoptionNoArg,  "False" },
3975   { "-os390",           ".doOS390",             XrmoptionNoArg,  "True"  },
3976   { "-no-os390",        ".doOS390",             XrmoptionNoArg,  "False" },
3977   { 0, 0, 0, 0 }
3978 };
3979
3980
3981 static struct {
3982   const char *name;
3983   void (*fn) (Display *, Window, int delay);
3984 } all_modes[] = {
3985   { "Windows",          windows_31 },
3986   { "Nt",               windows_nt },
3987   { "2k",               windows_2k },
3988   { "Amiga",            amiga },
3989   { "Mac",              mac },
3990   { "MacsBug",          macsbug },
3991   { "Mac1",             mac1 },
3992   { "MacX",             macx },
3993   { "SCO",              sco },
3994   { "SparcLinux",       sparc_linux },
3995   { "BSD",              bsd },
3996   { "Atari",            atari },
3997   { "BlitDamage",       blitdamage },
3998   { "Solaris",          sparc_solaris },
3999   { "Linux",            linux_fsck },
4000   { "HPUX",             hpux },
4001   { "OS390",            os390 },
4002   { "Apple2",           apple2 },
4003 };
4004
4005
4006 void
4007 screenhack (Display *dpy, Window window)
4008 {
4009   int loop = 0;
4010   int i = -1;
4011   int j = -1;
4012   int only = -1;
4013   int delay = get_integer_resource ("delay", "Integer");
4014   if (delay < 3) delay = 3;
4015
4016   {
4017     char *s = get_string_resource("doOnly", "DoOnly");
4018     if (s && *s)
4019       {
4020         int count = countof(all_modes);
4021         for (only = 0; only < count; only++)
4022           if (!strcasecmp (s, all_modes[only].name))
4023             break;
4024         if (only >= count)
4025           {
4026             fprintf (stderr, "%s: unknown -only mode: \"%s\"\n", progname, s);
4027             only = -1;
4028           }
4029       }
4030     if (s) free (s);
4031   }
4032
4033   if (!get_boolean_resource ("root", "Boolean"))
4034     {
4035       XWindowAttributes xgwa;
4036       XGetWindowAttributes (dpy, window, &xgwa);
4037       XSelectInput (dpy, window,
4038                     xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
4039     }
4040
4041   while (1)
4042     {
4043       Bool did;
4044       int count = countof(all_modes);
4045       char name[100], class[100];
4046
4047       if (only > 0)
4048         i = only;
4049       else
4050         do {  i = (random() & 0xFF) % count; } while (i == j);
4051
4052       sprintf (name,  "do%s", all_modes[i].name);
4053       sprintf (class, "Do%s", all_modes[i].name);
4054
4055       did = False;
4056       if (only > 0 || get_boolean_resource(name, class))
4057         {
4058           all_modes[i].fn (dpy, window, delay);
4059           did = True;
4060         }
4061
4062       loop++;
4063       if (loop > 100) j = -1;
4064       if (loop > 200)
4065         {
4066           fprintf (stderr, "%s: no display modes enabled?\n", progname);
4067           exit(-1);
4068         }
4069       if (!did) continue;
4070       XSync (dpy, False);
4071       j = i;
4072       loop = 0;
4073     }
4074 }