http://www.archive.org/download/tucows_10294_XScreenSaver/xscreensaver-4.10.tar.gz
[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   GC gc;
2104   XGCValues gcv;
2105   XWindowAttributes xgwa;
2106   scrolling_window *ts;
2107   const char *sysname;
2108   char buf[2048];
2109
2110   const char *msg1 =
2111    "Console Login:\n"
2112    "\n"
2113    "     ******* Unexpected HPMC/TOC. Processor HPA FFFFFFFF'"
2114    "FFFA0000 *******\n"
2115    "                              GENERAL REGISTERS:\n"
2116    "r00/03 00000000'00000000 00000000'00000000 00000000'00000000 00000000'"
2117    "006C76C0\n"
2118    "r04/07 00000000'00000001 00000000'0126E328 00000000'00000000 00000000'"
2119    "0122B640\n"
2120    "r08/11 00000000'00000000 00000000'0198CFC0 00000000'000476FE 00000000'"
2121    "00000001\n"
2122    "r12/15 00000000'40013EE8 00000000'08000080 00000000'4002530C 00000000'"
2123    "4002530C\n"
2124    "r16/19 00000000'7F7F2A00 00000000'00000001 00000000'00000000 00000000'"
2125    "00000000\n"
2126    "r20/23 00000000'006C8048 00000000'00000001 00000000'00000000 00000000'"
2127    "00000000\n"
2128    "r24/27 00000000'00000000 00000000'00000000 00000000'00000000 00000000'"
2129    "00744378\n"
2130    "r28/31 00000000'00000000 00000000'007DD628 00000000'0199F2B0 00000000'"
2131    "00000000\n"
2132    "                              CONTROL REGISTERS:\n"
2133    "sr0/3  00000000'0F3B4000 00000000'0C2A2000 00000000'016FF800 00000000'"
2134    "00000000\n"
2135    "sr4/7  00000000'00000000 00000000'016FF800 00000000'0DBF1400 00000000'"
2136    "00000000\n"
2137    "pcq =  00000000'00000000.00000000'00104950 00000000'00000000.00000000'"
2138    "00104A14\n"
2139    "isr =  00000000'10240006 ior = 00000000'67D9E220 iir = 08000240 rctr = "
2140    "7FF10BB6\n"
2141    "\n"
2142    "pid reg cr8/cr9    00007700'0000B3A9 00000000'0000C5D8\n"
2143    "pid reg cr12/cr13  00000000'00000000 00000000'00000000\n"
2144    "ipsw = 000000FF'080CFF1F iva = 00000000'0002C000 sar = 3A ccr = C0\n"
2145    "tr0/3  00000000'006C76C0 00000000'00000001 00000000'00000000 00000000'"
2146    "7F7CE000\n"
2147    "tr4/7  00000000'03790000 0000000C'4FB68340 00000000'C07EE13F 00000000'"
2148    "0199F2B0\n"
2149    "eiem = FFFFFFF0'FFFFFFFF eirr = 80000000'00000000 itmr = 0000000C'"
2150    "4FD8EDE1\n"
2151    "cr1/4  00000000'00000000 00000000'00000000 00000000'00000000 00000000'"
2152    "00000000\n"
2153    "cr5/7  00000000'00000000 00000000'00000000 00000000'"
2154    "00000000\n"
2155    "                           MACHINE CHECK PARAMETERS:\n"
2156    "Check Type = 00000000 CPU STATE = 9E000001 Cache Check = 00000000\n"
2157    "TLB Check = 00000000 Bus Check = 00000000 PIM State = ? SIU "
2158    "Status = ????????\n"
2159    "Assists = 00000000 Processor = 00000000\n"
2160    "Slave Addr = 00000000'00000000 Master Addr = 00000000'00000000\n"
2161    "\n"
2162    "\n"
2163    "TOC,    pcsq.pcoq = 0'0.0'104950   , isr.ior = 0'10240006.0'67d9e220\n"
2164    "@(#)B2352B/9245XB HP-UX (B.11.00) #1: Wed Nov  5 22:38:19 PST 1997\n"
2165    "Transfer of control: (display==0xd904, flags==0x0)\n"
2166    "\n"
2167    "\n"
2168    "\n"
2169    "*** A system crash has occurred.  (See the above messages for details.)\n"
2170    "*** The system is now preparing to dump physical memory to disk, for use\n"
2171    "*** in debugging the crash.\n"
2172    "\n"
2173    "*** The dump will be a SELECTIVE dump:  40 of 256 megabytes.\n"
2174    "*** To change this dump type, press any key within 10 seconds.\n"
2175    "*** Proceeding with selective dump.\n"
2176    "\n"
2177    "*** The dump may be aborted at any time by pressing ESC.\n";
2178   const char *msg2 =
2179    "\n*** System rebooting.\n";
2180
2181   XGetWindowAttributes (dpy, window, &xgwa);
2182   ts = make_scrolling_window (dpy, window, "HPUX", False);
2183   ts->columns = 10000;  /* never wrap */
2184   ts->sub_x = 0;
2185   ts->sub_y = 0;
2186   ts->sub_width = xgwa.width;
2187   ts->sub_height = xgwa.height;
2188
2189   sysname = "HPUX";
2190 # ifdef HAVE_UNAME
2191   {
2192     struct utsname uts;
2193     char *s;
2194     if (uname (&uts) >= 0)
2195       sysname = uts.nodename;
2196     s = strchr (sysname, '.');
2197     if (s) *s = 0;
2198   }
2199 # endif /* !HAVE_UNAME */
2200
2201   gcv.foreground = get_pixel_resource ("HPUX.foreground", "HPUX.Foreground",
2202                                        dpy, xgwa.colormap);
2203   gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
2204   XFillRectangle (dpy, window, gc, 0, 0, xgwa.width, xgwa.height);
2205   if (bsod_sleep (dpy, 1))
2206     goto DONE;
2207   
2208   scrolling_puts (ts,
2209                   "                                                       "
2210                   "                                                       "
2211                   "                                                       \n",
2212                   0);
2213   sprintf (buf, "%.100s [HP Release B.11.00] (see /etc/issue)\n", sysname);
2214   scrolling_puts (ts, buf, 0);
2215   if (bsod_sleep (dpy, 1))
2216     goto DONE;
2217   scrolling_puts (ts, msg1, 0);
2218   {
2219     int i;
2220     int steps = 11;
2221     int size = 40;
2222     for (i = 0; i <= steps; i++)
2223       {
2224         if (i > steps) i = steps;
2225         sprintf (buf, 
2226                "*** Dumping: %3d%% complete (%d of 40 MB) (device 64:0x2)\r",
2227                  i * 100 / steps,
2228                  i * size / steps);
2229         scrolling_puts (ts, buf, 0);
2230         XSync (dpy, False);
2231         usleep (1500000);
2232         if (bsod_sleep (dpy, 0))
2233           goto DONE;
2234       }
2235   }
2236
2237   scrolling_puts (ts, msg2, 0);
2238
2239   XSync(dpy, False);
2240   bsod_sleep(dpy, delay);
2241
2242  DONE:
2243   free_scrolling_window (ts);
2244 }
2245
2246 \f
2247
2248 /* IBM OS/390 aka MVS aka z/OS.
2249    Text from Dan Espen <dane@mk.telcordia.com>.
2250    Apparently this isn't actually a crash, just a random session...
2251    But who can tell.
2252  */
2253
2254 static void
2255 os390 (Display* dpy, Window window, int delay)
2256 {
2257   GC gc;
2258   XGCValues gcv;
2259   XWindowAttributes xgwa;
2260   scrolling_window *ts;
2261   int i;
2262
2263   const char *msg[] = {
2264    "* ISPF Subtask abend *\n",
2265    "SPF      ENDED DUE TO ERROR+\n",
2266    "READY\n",
2267    "\n",
2268    "IEA995I SYMPTOM DUMP OUTPUT\n",
2269    "  USER COMPLETION CODE=0222\n",
2270    " TIME=23.00.51  SEQ=03210  CPU=0000  ASID=00AE\n",
2271    " PSW AT TIME OF ERROR  078D1000   859DAF18  ILC 2  INTC 0D\n",
2272    "   NO ACTIVE MODULE FOUND\n",
2273    "   NAME=UNKNOWN\n",
2274    "   DATA AT PSW  059DAF12 - 00181610  0A0D9180  70644710\n",
2275    "   AR/GR 0: 00000000/80000000   1: 00000000/800000DE\n",
2276    "         2: 00000000/196504DC   3: 00000000/00037A78\n",
2277    "         4: 00000000/00037B78   5: 00000000/0003351C\n",
2278    "         6: 00000000/0000F0AD   7: 00000000/00012000\n",
2279    "         8: 00000000/059DAF10   9: 00000000/0002D098\n",
2280    "         A: 00000000/059D9F10   B: 00000000/059D8F10\n",
2281    "         C: 00000000/859D7F10   D: 00000000/00032D60\n",
2282    "         E: 00000000/00033005   F: 01000002/00000041\n",
2283    " END OF SYMPTOM DUMP\n",
2284    "ISPS014 - ** Logical screen request failed - abend 0000DE **\n",
2285    "ISPS015 - ** Contact your system programmer or dialog developer.**\n",
2286    "*** ISPF Main task abend ***\n",
2287    "IEA995I SYMPTOM DUMP OUTPUT\n",
2288    "  USER COMPLETION CODE=0222\n",
2289    " TIME=23.00.52  SEQ=03211  CPU=0000  ASID=00AE\n",
2290    " PSW AT TIME OF ERROR  078D1000   8585713C  ILC 2  INTC 0D\n",
2291    "   ACTIVE LOAD MODULE           ADDRESS=05855000  OFFSET=0000213C\n",
2292    "   NAME=ISPMAIN\n",
2293    "   DATA AT PSW  05857136 - 00181610  0A0D9180  D3304770\n",
2294    "   GR 0: 80000000   1: 800000DE\n",
2295    "      2: 00015260   3: 00000038\n",
2296    "      4: 00012508   5: 00000000\n",
2297    "      6: 000173AC   7: FFFFFFF8\n",
2298    "      8: 05858000   9: 00012CA0\n",
2299    "      A: 05857000   B: 05856000\n",
2300    "      C: 85855000   D: 00017020\n",
2301    "      E: 85857104   F: 00000000\n",
2302    " END OF SYMPTOM DUMP\n",
2303    "READY\n",
2304    "***_\n"
2305   };
2306
2307   XGetWindowAttributes (dpy, window, &xgwa);
2308   ts = make_scrolling_window (dpy, window, "OS390", False);
2309   ts->columns = 10000;  /* never wrap */
2310   ts->sub_x = 0;
2311   ts->sub_y = 0;
2312   ts->sub_width = xgwa.width;
2313   ts->sub_height = xgwa.height;
2314
2315   gcv.foreground = get_pixel_resource ("390.background", "390.Background",
2316                                        dpy, xgwa.colormap);
2317   gc = XCreateGC (dpy, window, GCForeground, &gcv);
2318   XFillRectangle (dpy, window, gc, 0, 0, xgwa.width, xgwa.height);
2319   XFreeGC (dpy, gc);
2320
2321   for (i = 0; i < countof (msg); i++)
2322     {
2323       scrolling_puts (ts, msg[i], 0);
2324       usleep (100000);
2325       if (bsod_sleep(dpy, 0)) goto DONE;
2326     }
2327
2328   XSync(dpy, False);
2329   bsod_sleep(dpy, delay);
2330 DONE:
2331   free_scrolling_window (ts);
2332 }
2333
2334
2335 \f
2336 /*
2337  * Simulate various Apple II crashes. The memory map encouraged many
2338  * programs to use the primary hi-res video page for various storage,
2339  * and the secondary hi-res page for active display. When it crashed
2340  * into Applesoft or the monitor, it would revert to the primary page
2341  * and you'd see memory garbage on the screen. Also, it was common for
2342  * copy-protected games to use the primary text page for important
2343  * code, because that made it really hard to reverse-engineer them. The
2344  * result often looked like what this generates.
2345  *
2346  * Sometimes an imaginary user types some of the standard commands to
2347  * recover from crashes.  You can turn off BSOD*apple2SimulateUser to
2348  * prevent this.
2349  *
2350  * It simulates the following characteristics of standard television
2351  * monitors:
2352  *
2353  * - Realistic rendering of a composite video signal
2354  * - Compression & brightening on the right, as the scan gets truncated
2355  *   because of saturation in the flyback transformer
2356  * - Blooming of the picture dependent on brightness
2357  * - Overscan, cutting off a few pixels on the left side.
2358  * - Colored text in mixed graphics/text modes
2359  *
2360  * It's amazing how much it makes your high-end monitor look like at
2361  * large late-70s TV.  All you need is to put a big "Solid State" logo
2362  * in curly script on it and you'd be set.
2363  *
2364  * Trevor Blackwell <tlb@tlb.org> 
2365  */
2366
2367 /*
2368  * Implementation notes:
2369  *
2370  * There are roughly 3 parts to this hack:
2371  *
2372  * - emulation of A2 Basic and Monitor. Not much more than printing random
2373  *   plausible messages. Here we work in the A2 memory space.
2374  *
2375  * - emulation of the A2's video output section, which shifted bits out of main
2376  *   memory at a 14 MHz dot clock rate, sort of. You could only read one byte
2377  *   per MHz, so there were various schemes for turning 8 bits into 14 screen
2378  *   pixels.
2379  *
2380  * - simulation of an NTSC television, which turned the bits into colored
2381  *   graphics and text.
2382  * 
2383  * The A2 had 3 display modes: text, lores, and hires. Text was 40x24, and it
2384  * disabled color in the TV. Lores gave you 40x48 graphics blocks, using the
2385  * same memory as the text screen. Each could be one of 16 colors. Hires gave
2386  * you 280x192 pixels. Odd pixels were blue or purple, and even pixels were
2387  * orange or green depending on the setting of the high bit in each byte.
2388  *
2389  * The graphics modes could also have 4 lines of text at the bottom. This was
2390  * fairly unreadable if you had a color monitor.
2391  *
2392  * Each mode had 2 different screens using different memory space. In hires
2393  * mode this was sometimes used for double buffering, but more often the lower
2394  * screen was full of code/data and the upper screen was used for display, so
2395  * you got random garbage on the screen.
2396  * 
2397  * In DirectColor or TrueColor modes, it generates pixel values directly from
2398  * RGB values it calculates across each scan line. In PseudoColor mode, it
2399  * consider each possible pattern of 5 preceding bit values in each possible
2400  * position modulo 4 and allocates a color for each. A few things, like the
2401  * brightening on the right side as the horizontal trace slows down, aren't
2402  * done in PseudoColor.
2403  *
2404  * The text font is based on X's standard 6x10 font, with a few tweaks like
2405  * putting a slash across the zero.
2406  *
2407  * I'd like to add a bit of visible retrace, but it conflicts with being able
2408  * to bitcopy the image when fast scrolling. After another couple of CPU
2409  * generations, we could probably regenerate the whole image from scratch every
2410  * time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
2411  * looks too slow.
2412  */
2413
2414 static char * apple2_basic_errors[]={
2415   "BREAK",
2416   "NEXT WITHOUT FOR",
2417   "SYNTAX ERROR",
2418   "RETURN WITHOUT GOSUB",
2419   "ILLEGAL QUANTITY",
2420   "OVERFLOW",
2421   "OUT OF MEMORY",
2422   "BAD SUBSCRIPT ERROR",
2423   "DIVISION BY ZERO",
2424   "STRING TOO LONG",
2425   "FORMULA TOO COMPLEX",
2426   "UNDEF'D FUNCTION",
2427   "OUT OF DATA"
2428 };
2429 static char * apple2_dos_errors[]={
2430   "VOLUME MISMATCH",
2431   "I/O ERROR",
2432   "DISK FULL",
2433   "NO BUFFERS AVAILABLE",
2434   "PROGRAM TOO LARGE",
2435 };
2436
2437 struct apple2_state {
2438   char hireslines[192][40];
2439   char textlines[24][40];
2440   int gr_text;
2441   enum {
2442     A2_GR_FULL=1,
2443     A2_GR_LORES=2,
2444     A2_GR_HIRES=4
2445   } gr_mode;
2446   int cursx;
2447   int cursy;
2448   int blink;
2449   int rowimage[24];
2450 };
2451
2452 enum {
2453   A2_SP_ROWMASK=1023,
2454   A2_SP_PUT=1024,
2455   A2_SP_COPY=2048,
2456 };
2457
2458 static void
2459 a2_scroll(struct apple2_state *st)
2460 {
2461   int i;
2462   int top=(st->gr_mode&(A2_GR_LORES|A2_GR_HIRES)) ? 20 : 0;
2463   if ((st->gr_mode&A2_GR_FULL) && (st->gr_mode&A2_GR_HIRES)) return;
2464   if (st->gr_mode&A2_GR_FULL) top=0;
2465   for (i=top; i<23; i++) {
2466     if (memcmp(st->textlines[i],st->textlines[i+1],40)) {
2467       memcpy(st->textlines[i],st->textlines[i+1],40);
2468       st->rowimage[i]=st->rowimage[i+1];
2469     }
2470   }
2471   memset(st->textlines[23],0xe0,40);
2472   st->rowimage[23]=-1;
2473 }
2474
2475 static void
2476 a2_printc(struct apple2_state *st, char c)
2477 {
2478   st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
2479   if (c=='\n') {
2480     if (st->cursy==23) {
2481       a2_scroll(st);
2482     } else {
2483       st->rowimage[st->cursy]=-1;
2484       st->cursy++;
2485       st->rowimage[st->cursy]=-1;
2486     }
2487     st->cursx=0;
2488   } else {
2489     st->textlines[st->cursy][st->cursx]=c ^ 0xc0;
2490     st->rowimage[st->cursy]=-1;
2491     st->cursx++;
2492     if (st->cursx==40) {
2493       if (st->cursy==23) {
2494         a2_scroll(st);
2495       } else {
2496         st->rowimage[st->cursy]=-1;
2497         st->cursy++;
2498         st->rowimage[st->cursy]=-1;
2499       }
2500       st->cursx=0;
2501     }
2502   }
2503   st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
2504 }
2505
2506 static void
2507 a2_goto(struct apple2_state *st, int r, int c)
2508 {
2509   st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
2510   st->cursy=r;
2511   st->cursx=c;
2512   st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
2513 }
2514
2515 static void
2516 a2_cls(struct apple2_state *st) 
2517 {
2518   int i;
2519   for (i=0; i<24; i++) {
2520     memset(st->textlines[i],0xe0,40);
2521     st->rowimage[i]=-1;
2522   }
2523 }
2524
2525 static void
2526 a2_invalidate(struct apple2_state *st) 
2527 {
2528   int i;
2529   for (i=0; i<24; i++) {
2530     st->rowimage[i]=-1;
2531   }
2532 }
2533
2534 static void
2535 a2_poke(struct apple2_state *st, int addr, int val)
2536 {
2537   
2538   if (addr>=0x400 && addr<0x800) {
2539     /* text memory */
2540     int row=((addr&0x380)/0x80) + ((addr&0x7f)/0x28)*8;
2541     int col=(addr&0x7f)%0x28;
2542     if (row<24 && col<40) {
2543       st->textlines[row][col]=val;
2544       if (!(st->gr_mode&(A2_GR_HIRES)) ||
2545           (!(st->gr_mode&(A2_GR_FULL)) && row>=20)) {
2546         st->rowimage[row]=-1;
2547       }
2548     }
2549   }
2550   else if (addr>=0x2000 && addr<0x4000) {
2551     int row=(((addr&0x1c00) / 0x400) * 1 +
2552              ((addr&0x0380) / 0x80) * 8 +
2553              ((addr&0x0078) / 0x28) * 64);
2554     int col=((addr&0x07f)%0x28);
2555     if (row<192 && col<40) {
2556       st->hireslines[row][col]=val;
2557       if (st->gr_mode&A2_GR_HIRES) {
2558         st->rowimage[row/8]=-1;
2559       }
2560     }
2561   }
2562 }
2563
2564 /* This table lists fixes for characters that differ from the standard 6x10
2565    font. Each encodes a pixel, as (charindex*7 + x) + (y<<10) + (value<<15)
2566    where value is 0 for white and 1 for black. */
2567 static unsigned short a2_fixfont[] = {
2568   /* Fix $ */  0x8421, 0x941d,
2569   /* Fix % */  0x8024, 0x0028, 0x8425, 0x0426, 0x0825, 0x1027, 0x1426, 0x9427,
2570                0x1824, 0x9828,
2571   /* Fix * */  0x8049, 0x8449, 0x8849, 0x0c47, 0x0c48, 0x0c4a, 0x0c4b, 0x9049,
2572                0x9449, 0x9849,
2573   /* Fix , */  0x9057, 0x1458, 0x9856, 0x1857, 0x1c56,
2574   /* Fix . */  0x1465, 0x1864, 0x1866, 0x1c65,
2575   /* Fix / */  0x006e, 0x186a,
2576   /* Fix 0 */  0x8874, 0x8c73, 0x9072,
2577   /* Fix 1 */  0x0878, 0x1878, 0x187c,
2578   /* Fix 5 */  0x8895, 0x0c94, 0x0c95,
2579   /* Fix 6 */  0x809f, 0x8c9c, 0x109c,
2580   /* Fix 7 */  0x8ca4, 0x0ca5, 0x90a3, 0x10a4,
2581   /* Fix 9 */  0x08b3, 0x8cb3, 0x98b0,
2582   /* Fix : */  0x04b9, 0x08b8, 0x08ba, 0x0cb9, 0x90b9, 0x14b9, 0x18b8, 0x18b9,
2583                0x18ba, 0x1cb9,
2584   /* Fix ; */  0x04c0, 0x08bf, 0x08c1, 0x0cc0, 0x90c0, 0x14c1, 0x98bf, 0x18c0,
2585                0x1cbf,
2586   /* Fix < */  0x80c8, 0x00c9, 0x84c7, 0x04c8, 0x88c6, 0x08c7, 0x8cc5, 0x0cc6,
2587                0x90c6, 0x10c7, 
2588                0x94c7, 0x14c8, 0x98c8, 0x18c9,
2589   /* Fix > */  0x80d3, 0x00d4, 0x84d4, 0x04d5, 0x88d5, 0x08d6, 0x8cd6, 0x0cd7,
2590                0x90d5, 0x10d6, 
2591                0x94d4, 0x14d5, 0x98d3, 0x18d4,
2592   /* Fix @ */  0x88e3, 0x08e4, 0x8ce4, 0x98e5,
2593   /* Fix B */  0x84ef, 0x04f0, 0x88ef, 0x08f0, 0x8cef, 0x90ef, 0x10f0, 0x94ef,
2594                0x14f0,
2595   /* Fix D */  0x84fd, 0x04fe, 0x88fd, 0x08fe, 0x8cfd, 0x0cfe, 0x90fd, 0x10fe,
2596                0x94fd, 0x14fe,
2597   /* Fix G */  0x8116, 0x0516, 0x9916,
2598   /* Fix J */  0x0129, 0x012a, 0x052a, 0x852b, 0x092a, 0x892b, 0x0d2a, 0x8d2b,
2599                0x112a, 0x912b, 
2600                0x152a, 0x952b, 0x992a,
2601   /* Fix M */  0x853d, 0x853f, 0x093d, 0x893e, 0x093f,
2602   /* Fix Q */  0x915a, 0x155a, 0x955b, 0x155c, 0x195b, 0x995c, 0x1d5c,
2603   /* Fix V */  0x8d7b, 0x0d7c, 0x0d7e, 0x8d7f, 0x917b, 0x117c, 0x117e, 0x917f,
2604   /* Fix [ */  0x819e, 0x81a2, 0x859e, 0x899e, 0x8d9e, 0x919e, 0x959e, 0x999e,
2605                0x99a2,
2606   /* Fix \ */  0x01a5, 0x19a9,
2607   /* Fix ] */  0x81ac, 0x81b0, 0x85b0, 0x89b0, 0x8db0, 0x91b0, 0x95b0, 0x99ac,
2608                0x99b0,
2609   /* Fix ^ */  0x01b5, 0x05b4, 0x05b6, 0x09b3, 0x89b5, 0x09b7, 0x8db4, 0x8db6,
2610                0x91b3, 0x91b7,
2611   /* Fix _ */  0x9db9, 0x9dbf,
2612   0,
2613 };
2614
2615 struct ntsc_dec {
2616   char pattern[600];
2617   int ntscy[600];
2618   int ntsci[600];
2619   int ntscq[600];
2620   int multi[600];
2621   int multq[600];
2622   int brightness_control;
2623 };
2624
2625 /*
2626   First generate the I and Q reference signals, which we'll multiply by the
2627   input signal to accomplish the demodulation. Normally they are shifted 33
2628   degrees from the colorburst. I think this was convenient for
2629   inductor-capacitor-vacuum tube implementation.
2630                
2631   The tint control, FWIW, just adds a phase shift to the chroma signal, and 
2632   the color control controls the amplitude.
2633                
2634   In text modes (colormode==0) the system disabled the color burst, and no
2635   color was detected by the monitor.
2636
2637   freq_error gives a mismatch between the built-in oscillator and the TV's
2638   colorbust. Older II Plus machines seemed to occasionally get instability
2639   problems -- the crystal oscillator was a single transistor if I remember
2640   correctly -- and the frequency would vary enough that the tint would change
2641   across the width of the screen.  The left side would be in correct tint
2642   because it had just gotten resynchronized with the color burst.
2643 */
2644 static void
2645 ntsc_set_demod(struct ntsc_dec *it, double tint_control, 
2646                double color_control, double brightness_control,
2647                double freq_error, 
2648                int colormode)
2649 {
2650   int i;
2651
2652   it->brightness_control=(int)(1024.0*brightness_control);
2653
2654   for (i=0; i<600; i++) {
2655     double phase=90.0-90.0*i + freq_error*i/600.0 + tint_control;
2656     it->multi[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 65536.0 * 
2657                        color_control * colormode * 4);
2658     it->multq[i]=(int)(cos(3.1415926/180.0*(phase-33)) * 65536.0 * 
2659                        color_control * colormode * 4);
2660   }
2661 }
2662
2663 /* Here we model the analog circuitry of an NTSC television. Basically, it
2664    splits the signal into 3 signals: Y, I and Q. Y corresponds to luminance,
2665    and you get it by low-pass filtering the input signal to below 3.57 MHz.
2666
2667    I and Q are the in-phase and quadrature components of the 3.57 MHz
2668    subcarrier. We get them by multiplying by cos(3.57 MHz*t) and sin(3.57
2669    MHz*t), and low-pass filtering. Because the eye has less resolution in some
2670    colors than others, the I component gets low-pass filtered at 1.5 MHz and
2671    the Q at 0.5 MHz. The I component is approximately orange-blue, and Q is
2672    roughly purple-green. See http://www.ntsc-tv.com for details.
2673  */
2674 static void
2675 ntsc_to_yiq(struct ntsc_dec *it) 
2676 {
2677   int i;
2678   int fyx[10],fyy[10];
2679   int fix[10],fiy[10];
2680   int fqx[10],fqy[10];
2681   int pixghost;
2682   int iny,ini,inq,pix,blank;
2683   
2684   for (i=0; i<10; i++) fyx[i]=fyy[i]=fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
2685   pixghost=0;
2686   for (i=0; i<600; i++) {
2687     /* Get the video out signal, and add a teeny bit of ghosting, typical of RF
2688        monitor cables. This corresponds to a pretty long cable, but looks right
2689        to me. */
2690     pix=it->pattern[i]*1024;
2691     if (i>=20) pixghost += it->pattern[i-20]*15;
2692     if (i>=30) pixghost -= it->pattern[i-30]*15;
2693     pix += pixghost;
2694
2695     /* Get Y, I, Q before filtering */
2696     iny=pix;
2697     ini=(pix*it->multi[i])>>16;
2698     inq=(pix*it->multq[i])>>16;
2699             
2700     blank = (i>=7 && i<596 ? it->brightness_control : -200);
2701
2702     /* Now filter them. These are infinite impulse response filters calculated
2703        by the script at http://www-users.cs.york.ac.uk/~fisher/mkfilter. This
2704        is fixed-point integer DSP, son. No place for wimps. We do it in integer
2705        because you can count on integer being faster on most CPUs. We care
2706        about speed because we need to recalculate every time we blink text, and
2707        when we spew random bytes into screen memory. This is roughly 16.16
2708        fixed point arithmetic, but we scale some filter values up by a few bits
2709        to avoid some nasty precision errors. */
2710             
2711     /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz 
2712        with an extra zero at 3.5 MHz, from
2713        mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
2714        Delay about 2 */
2715
2716     fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3]; 
2717     fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6]; 
2718     fyx[6] = (iny * 1897) >> 13;
2719     fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3]; 
2720     fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6]; 
2721     fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
2722       + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
2723     if (i>=2) it->ntscy[i-2] = blank + (fyy[6]>>3);
2724
2725     /* Filter I and Q at 1.5 MHz. 3 pole Butterworth from
2726        mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0
2727        Delay about 3.
2728
2729        The NTSC spec says the Q value should be filtered at 0.5 MHz at the
2730        transmit end, But the Apple's video circuitry doesn't any such thing.
2731        AFAIK, oldish televisions (before comb filters) simply applied a 1.5 MHz
2732        filter to both after the demodulator.
2733     */
2734
2735     fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
2736     fix[3] = (ini * 1413) >> 14;
2737     fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
2738     fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
2739       + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
2740     if (i>=3) it->ntsci[i-3] = fiy[3]>>2;
2741
2742     fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
2743     fqx[3] = (inq * 1413) >> 14;
2744     fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
2745     fqy[3] = (fqx[0]+fqx[3]) + 3*(fqx[1]+fqx[2])
2746       + ((16559*fqy[0] - 72008*fqy[1] + 109682*fqy[2]) >> 16);
2747     if (i>=3) it->ntscq[i-3] = fqy[3]>>2;
2748
2749   }
2750   for (; i<610; i++) {
2751     if (i-2<600) it->ntscy[i-2]=0;
2752     if (i-3<600) it->ntsci[i-3]=0;
2753     if (i-9<600) it->ntscq[i-9]=0;
2754   }
2755 }
2756
2757 enum {
2758   A2_CMAP_HISTBITS=5,
2759   A2_CMAP_LEVELS=2,
2760   A2_CMAP_OFFSETS=4,
2761 };
2762
2763 #define A2_CMAP_INDEX(COLORMODE, LEVEL, HIST, OFFSET) \
2764 ((((COLORMODE)*A2_CMAP_LEVELS+(LEVEL))<<A2_CMAP_HISTBITS)+(HIST))* \
2765 A2_CMAP_OFFSETS+(OFFSET)
2766
2767 static void
2768 apple2(Display *dpy, Window window, int delay)
2769 {
2770   int w,h,i,j,x,y,textrow,row,col,stepno,colormode,imgrow;
2771   char c,*s;
2772   struct timeval basetime_tv;
2773   double next_actiontime;
2774   XWindowAttributes xgwa;
2775   int visclass;
2776   int screen_xo,screen_yo;
2777   XImage *image=NULL;
2778   XGCValues gcv;
2779   GC gc=NULL;
2780   XImage *text_im=NULL;
2781   unsigned long colors[A2_CMAP_INDEX(1, A2_CMAP_LEVELS-1,
2782                                      (1<<A2_CMAP_HISTBITS)-1,
2783                                      A2_CMAP_OFFSETS-3)+1];
2784   int n_colors=0;
2785   int screen_plan[24];
2786   struct ntsc_dec *dec=NULL;
2787   short *raw_rgb=NULL, *rrp;
2788   struct apple2_state *st=NULL;
2789   char *typing=NULL,*printing=NULL;
2790   char printbuf[1024];
2791   char prompt=']';
2792   int simulate_user;
2793   double tint_control,color_control,brightness_control,contrast_control;
2794   double freq_error=0.0,freq_error_inc=0.0;
2795   double horiz_desync=5.0;
2796   int flutter_horiz_desync=0;
2797   int flutter_tint=0;
2798   double crtload[192];
2799   int red_invprec,red_shift,green_invprec,green_shift,blue_invprec,blue_shift;
2800   int fillptr, fillbyte;
2801   int use_shm,use_cmap,use_color;
2802   /* localbyteorder is 1 if MSB first, 0 otherwise */
2803   unsigned int localbyteorder_loc = MSBFirst<<24;
2804   int localbyteorder=*(char *)&localbyteorder_loc;
2805 #ifdef HAVE_XSHM_EXTENSION
2806   XShmSegmentInfo shm_info;
2807 #endif
2808   
2809 #ifdef HAVE_XSHM_EXTENSION
2810   use_shm=get_boolean_resource ("useSHM", "Boolean");
2811 #else
2812   use_shm=0;
2813 #endif
2814
2815   /* Model the video controls on a standard television */
2816   tint_control = get_float_resource("apple2TVTint","Apple2TVTint");
2817   color_control = get_float_resource("apple2TVColor","Apple2TVColor")/100.0;
2818   brightness_control = get_float_resource("apple2TVBrightness",
2819                                           "Apple2TVBrightness") / 100.0;
2820   contrast_control = get_float_resource("apple2TVContrast",
2821                                         "Apple2TVContrast") / 100.0;
2822   simulate_user = get_boolean_resource("apple2SimulateUser",
2823                                        "Apple2SimulateUser");
2824
2825   XGetWindowAttributes (dpy, window, &xgwa);
2826   visclass=xgwa.visual->class;
2827   red_shift=red_invprec=green_shift=green_invprec=blue_shift=blue_invprec=-1;
2828   if (visclass == TrueColor || xgwa.visual->class == DirectColor) {
2829     use_cmap=0;
2830     use_color=!mono_p;
2831   }
2832   else if (visclass == PseudoColor || visclass == StaticColor) {
2833     use_cmap=1;
2834     use_color=!mono_p;
2835   }
2836   else {
2837     use_cmap=1;
2838     use_color=0;
2839   }
2840
2841   /* The Apple II screen was 280x192, sort of. We expand the width to 300
2842      pixels to allow for overscan. We then pick a size within the window
2843      that's an integer multiple of 300x192. The small case happens when
2844      we're displaying in a subwindow. Then it ends up showing the center
2845      of the screen, which is OK. */
2846   w=xgwa.width;
2847   h = (xgwa.height/192)*192;
2848   if (w<300) w=300;
2849   if (h==0) h=192;
2850
2851   dec=(struct ntsc_dec *)malloc(sizeof(struct ntsc_dec));
2852   
2853   if (use_cmap) {
2854     int hist,offset,level;
2855     int colorprec=8;
2856
2857   cmap_again:
2858     n_colors=0;
2859     /* Typically allocates 214 distinct colors, but will scale back its
2860        ambitions pretty far if it can't get them */
2861     for (colormode=0; colormode<=use_color; colormode++) {
2862       ntsc_set_demod(dec, tint_control, color_control, brightness_control,
2863                      0.0, colormode);
2864       for (level=0; level<2; level++) {
2865         for (hist=0; hist<(1<<A2_CMAP_HISTBITS); hist++) {
2866           for (offset=0; offset<4; offset++) {
2867             int interpy,interpi,interpq,r,g,b;
2868             int levelmult=level ? 64 : 32;
2869             int prec=colormode ? colorprec : (colorprec*2+2)/3;
2870             int precmask=(0xffff<<(16-prec))&0xffff;
2871             XColor col;
2872
2873             if (A2_CMAP_INDEX(colormode,level,hist,offset) != n_colors) {
2874               fprintf(stderr, "apple2: internal colormap allocation error\n");
2875               goto bailout;
2876             }
2877
2878             for (i=0; i<600; i++) dec->pattern[i]=0;
2879             for (i=0; i<A2_CMAP_HISTBITS; i++) {
2880               dec->pattern[64+offset-i]=(hist>>i)&1;
2881             }
2882         
2883             ntsc_to_yiq(dec);
2884             interpy=dec->ntscy[63+offset];
2885             interpi=dec->ntsci[63+offset];
2886             interpq=dec->ntscq[63+offset];
2887
2888             r=(interpy + ((+68128*interpi+40894*interpq)>>16))*levelmult;
2889             g=(interpy + ((-18087*interpi-41877*interpq)>>16))*levelmult;
2890             b=(interpy + ((-72417*interpi+113312*interpq)>>16))*levelmult;
2891             if (r<0) r=0;
2892             if (r>65535) r=65535;
2893             if (g<0) g=0;
2894             if (g>65535) g=65535;
2895             if (b<0) b=0;
2896             if (b>65535) b=65535;
2897           
2898             col.red=r & precmask;
2899             col.green=g & precmask;
2900             col.blue=b & precmask;
2901             col.pixel=0;
2902             if (!XAllocColor(dpy, xgwa.colormap, &col)) {
2903               XFreeColors(dpy, xgwa.colormap, colors, n_colors, 0L);
2904               n_colors=0;
2905               colorprec--;
2906               if (colorprec<3) {
2907                 goto bailout;
2908               }
2909               goto cmap_again;
2910             }
2911             colors[n_colors++]=col.pixel;
2912           }
2913         }
2914       }
2915     }
2916   } else {
2917     /* Is there a standard way to do this? Does this handle all cases? */
2918     int shift, prec;
2919     for (shift=0; shift<32; shift++) {
2920       for (prec=1; prec<16 && prec<32-shift; prec++) {
2921         unsigned long mask=(0xffffUL>>(16-prec)) << shift;
2922         if (red_shift<0 && mask==xgwa.visual->red_mask)
2923           red_shift=shift, red_invprec=16-prec;
2924         if (green_shift<0 && mask==xgwa.visual->green_mask)
2925           green_shift=shift, green_invprec=16-prec;
2926         if (blue_shift<0 && mask==xgwa.visual->blue_mask)
2927           blue_shift=shift, blue_invprec=16-prec;
2928       }
2929     }
2930     if (red_shift<0 || green_shift<0 || blue_shift<0) {
2931       if (0) fprintf(stderr,"Can't figure out color space\n");
2932       goto bailout;
2933     }
2934     raw_rgb=(short *)calloc(w*3, sizeof(short));
2935   }
2936
2937   gcv.background=0;
2938   gc = XCreateGC(dpy, window, GCBackground, &gcv);
2939   XSetWindowBackground(dpy, window, gcv.background);
2940   XClearWindow(dpy,window);
2941
2942   screen_xo=(xgwa.width-w)/2;
2943   screen_yo=(xgwa.height-h)/2;
2944
2945   if (use_shm) {
2946 #ifdef HAVE_XSHM_EXTENSION
2947     image = create_xshm_image (dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, 
2948                                &shm_info, w, h);
2949 #endif
2950     if (!image) {
2951       fprintf(stderr, "create_xshm_image failed\n");
2952       use_shm=0;
2953     }
2954   }
2955   if (!image) {
2956     image = XCreateImage(dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, 0,
2957                          w, h, 8, 0);
2958     image->data = (char *)calloc(image->height, image->bytes_per_line);
2959   }
2960
2961   st=(struct apple2_state *)calloc(1,sizeof(struct apple2_state));
2962
2963   /*
2964     Generate the font. It used a 5x7 font which looks a lot like the standard X
2965     6x10 font, with a few differences. So we render up all the uppercase
2966     letters of 6x10, and make a few tweaks (like putting a slash across the
2967     zero) according to fixfont.
2968    */
2969   {
2970     const char *def_font="6x10";
2971     XFontStruct *font;
2972     Pixmap text_pm;
2973     GC gc;
2974     
2975     font = XLoadQueryFont (dpy, def_font);
2976     if (!font) {
2977       fprintf(stderr,"Can't load font %s\n",def_font);
2978       goto bailout;
2979     }
2980     
2981     text_pm=XCreatePixmap(dpy, window, 64*7, 8, xgwa.depth);
2982     
2983     gcv.foreground=1;
2984     gcv.background=0;
2985     gcv.font=font->fid;
2986     gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2987     
2988     XSetForeground(dpy, gc, 0);
2989     XFillRectangle(dpy, text_pm, gc, 0, 0, 64*7, 8);
2990     XSetForeground(dpy, gc, 1);
2991     for (i=0; i<64; i++) {
2992       char c=32+i;
2993       int x=7*i+1;
2994       int y=7;
2995       if (c=='0') {
2996         c='O';
2997         XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2998       } else {
2999         XDrawString(dpy, text_pm, gc, x, y, &c, 1);
3000       }
3001     }
3002     text_im = XGetImage(dpy, text_pm, 0, 0, 64*7, 8, ~0L, ZPixmap);
3003     XFreeGC(dpy, gc);
3004     XFreePixmap(dpy, text_pm);
3005
3006     for (i=0; a2_fixfont[i]; i++) {
3007       XPutPixel(text_im, a2_fixfont[i]&0x3ff,
3008                 (a2_fixfont[i]>>10)&0xf,
3009                 (a2_fixfont[i]>>15)&1);
3010     }
3011   }
3012
3013   /*
3014     Simulate plausible initial memory contents.
3015    */
3016   {
3017     int addr=0;
3018     while (addr<0x4000) {
3019       int n;
3020
3021       switch (random()%4) {
3022       case 0:
3023       case 1:
3024         n=random()%500;
3025         for (i=0; i<n && addr<0x4000; i++) {
3026           u_char rb=((random()%6==0 ? 0 : random()%16) |
3027                      ((random()%5==0 ? 0 : random()%16)<<4));
3028           a2_poke(st, addr++, rb);
3029         }
3030         break;
3031       
3032       case 2:
3033         /* Simulate shapes stored in memory. We use the font since we have it.
3034            Unreadable, since rows of each character are stored in consecutive
3035            bytes. It was typical to store each of the 7 possible shifts of
3036            bitmaps, for fastest blitting to the screen. */
3037         x=random()%(text_im->width);
3038         for (i=0; i<100; i++) {
3039           for (y=0; y<8; y++) {
3040             c=0;
3041             for (j=0; j<8; j++) {
3042               c |= XGetPixel(text_im, (x+j)%text_im->width, y)<<j;
3043             }
3044             a2_poke(st, addr++, c);
3045           }
3046           x=(x+1)%(text_im->width);
3047         }
3048         break;
3049
3050       case 3:
3051         if (addr>0x2000) {
3052           n=random()%200;
3053           for (i=0; i<n && addr<0x4000; i++) {
3054             a2_poke(st, addr++, 0);
3055           }
3056         }
3057         break;
3058
3059       }
3060     }
3061   }
3062   
3063   if (random()%4==0 &&
3064       !use_cmap && use_color &&
3065       xgwa.visual->bits_per_rgb>=8) {
3066     flutter_tint=1;
3067   }
3068   else if (random()%3==0) {
3069     flutter_horiz_desync=1;
3070   }
3071
3072   crtload[0]=0.0;
3073   stepno=0;
3074   a2_goto(st,23,0);
3075   gettimeofday(&basetime_tv, NULL);
3076   if (random()%2==0) basetime_tv.tv_sec -= 1; /* random blink phase */
3077   next_actiontime=0.0;
3078   fillptr=fillbyte=0;
3079   while (1) {
3080     double curtime,blinkphase;
3081     int startdisplayrow=0;
3082     int cheapdisplay=0;
3083     int nodelay=0;
3084     {
3085       struct timeval curtime_tv;
3086       gettimeofday(&curtime_tv, NULL);
3087       curtime=(curtime_tv.tv_sec - basetime_tv.tv_sec) + 
3088         0.000001*(curtime_tv.tv_usec - basetime_tv.tv_usec);
3089     }
3090     if (curtime>delay) goto finished;
3091
3092     if (bsod_sleep(dpy,0)) goto finished;
3093
3094     if (flutter_tint && st->gr_mode && !printing) {
3095       /* Oscillator instability. Look for freq_error below. We should only do
3096          this with color depth>=8, since otherwise you see pixels changing. */
3097       freq_error_inc += (-0.10*freq_error_inc
3098                          + ((int)(random()&0xff)-0x80) * 0.01);
3099       freq_error += freq_error_inc;
3100       a2_invalidate(st);
3101       nodelay=1;
3102     }
3103     else if (flutter_horiz_desync) {
3104       /* Horizontal sync during vertical sync instability. */
3105       horiz_desync += (-0.10*(horiz_desync-3.0) +
3106                        ((int)(random()&0xff)-0x80) * 
3107                        ((int)(random()&0xff)-0x80) *
3108                        ((int)(random()&0xff)-0x80) * 0.0000003);
3109       for (i=0; i<3; i++) st->rowimage[i]=-1;
3110       nodelay=1;
3111     } 
3112
3113     /* It's super-important to get the cursor/text flash out at exactly the
3114        right time, or it looks wrong. So if we're almost due for a blink, wait
3115        for it so we don't miss it in the middle of a screen update. */
3116     blinkphase=curtime/0.8;
3117     if (blinkphase-floor(blinkphase)>0.7 && !printing && !nodelay) {
3118       /* We're about to blink */
3119       int delay = ((1.0-(blinkphase-floor(blinkphase)))*0.8) * 1000000;
3120       if (delay<1000) delay=1000;
3121       usleep(delay);
3122       continue;
3123     }
3124
3125     /* The blinking rate was controlled by 555 timer with a resistor/capacitor
3126        time constant. Because the capacitor was electrolytic, the flash rate
3127        varied somewhat between machines. I'm guessing 1.6 seconds/cycle was
3128        reasonable. (I soldered a resistor in mine to make it blink faster.) */
3129     i=st->blink;
3130     st->blink=((int)blinkphase)&1;
3131     if (st->blink!=i && !(st->gr_mode&A2_GR_FULL)) {
3132       int downcounter=0;
3133       /* For every row with blinking text, set the changed flag. This basically
3134          works great except with random screen garbage in text mode, when we
3135          end up redrawing the whole screen every second */
3136       for (row=(st->gr_mode ? 20 : 0); row<24; row++) {
3137         for (col=0; col<40; col++) {
3138           c=st->textlines[row][col];
3139           if ((c & 0xc0) == 0x40) {
3140             downcounter=4;
3141             break;
3142           }
3143         }
3144         if (downcounter>0) {
3145           st->rowimage[row]=-1;
3146           downcounter--;
3147         }
3148       }
3149       st->rowimage[st->cursy]=-1;
3150       startdisplayrow=random()%24;
3151     } 
3152     else if (next_actiontime > curtime && !printing && !nodelay) {
3153       int delay = (next_actiontime-curtime)*1000000;
3154
3155       if (delay>100000) delay=100000;
3156       if (delay<1000) delay=1000;
3157       usleep(delay);
3158       continue;
3159     }
3160
3161     if (printing) {
3162       cheapdisplay=1;
3163       while (*printing) {
3164         if (*printing=='\001') { /* pause */
3165           printing++;
3166           for (i=20; i<24; i++) st->rowimage[i]=-1;
3167           break;
3168         } 
3169         else if (*printing=='\n') {
3170           a2_printc(st,*printing);
3171           printing++;
3172           break;
3173         }
3174         else {
3175           a2_printc(st,*printing);
3176           printing++;
3177         }
3178       }
3179       if (!*printing) printing=NULL;
3180     }
3181     else if (curtime >= next_actiontime) {
3182       if (typing) {
3183         /* If we're in the midst of typing a string, emit a character with
3184            random timing. */
3185         a2_printc(st, *typing);
3186         if (*typing=='\n') {
3187           next_actiontime = curtime;
3188         } else {
3189           next_actiontime = curtime + (random()%1000)*0.0003 + 0.3;
3190         }
3191         typing++;
3192
3193         if (!*typing) typing=NULL;
3194
3195       } 
3196       else {
3197         next_actiontime=curtime;
3198
3199         switch(stepno) {
3200         case 0:
3201           a2_invalidate(st);
3202           if (0) {
3203             /*
3204               For testing color rendering. The spec is:
3205                            red grn blu
3206               0  black       0   0   0
3207               1  red       227  30  96
3208               2  dk blue    96  78 189
3209               3  purple    255  68 253
3210               4  dk green    0 163  96
3211               5  gray      156 156 156
3212               6  med blue   20 207 253
3213               7  lt blue   208 195 255
3214               8  brown      96 114   3
3215               9  orange    255 106  60
3216               10 grey      156 156 156
3217               11 pink      255 160 208
3218               12 lt green   20 245  60
3219               13 yellow    208 221 141
3220               14 aqua      114 255 208
3221               15 white     255 255 255
3222             */
3223             st->gr_mode=A2_GR_LORES;
3224             for (row=0; row<24; row++) {
3225               for (col=0; col<40; col++) {
3226                 st->textlines[row][col]=(row&15)*17;
3227               }
3228             }
3229             next_actiontime+=0.4;
3230             stepno=88;
3231           }
3232           else if (random()%3==0) {
3233             st->gr_mode=0;
3234             next_actiontime+=0.4;
3235             stepno=88;
3236           }
3237           else if (random()%4==0) {
3238             st->gr_mode=A2_GR_LORES;
3239             if (random()%3==0) st->gr_mode |= A2_GR_FULL;
3240             next_actiontime+=0.4;
3241             stepno=88;
3242           } 
3243           else if (random()%2==0) {
3244             st->gr_mode=A2_GR_HIRES;
3245             stepno=73;
3246           }
3247           else {
3248             st->gr_mode=A2_GR_HIRES;
3249             next_actiontime+=0.4;
3250             stepno=88;
3251           }
3252           break;
3253
3254         case 88:
3255           /* An illegal instruction or a reset caused it to drop into the
3256              assembly language monitor, where you could disassemble code & view
3257              data in hex. */
3258           if (random()%3==0) {
3259             char ibytes[128];
3260             char itext[128];
3261             int addr=0xd000+random()%0x3000;
3262             sprintf(ibytes,
3263                     "%02X",random()%0xff);
3264             sprintf(itext,
3265                     "???");
3266             sprintf(printbuf,
3267                     "\n\n"
3268                     "%04X: %-15s %s\n"
3269                     " A=%02X X=%02X Y=%02X S=%02X F=%02X\n"
3270                     "*",
3271                     addr,ibytes,itext,
3272                     random()%0xff, random()%0xff,
3273                     random()%0xff, random()%0xff,
3274                     random()%0xff);
3275             printing=printbuf;
3276             a2_goto(st,23,1);
3277             if (st->gr_mode) {
3278               stepno=11;
3279             } else {
3280               stepno=13;
3281             }
3282             prompt='*';
3283             next_actiontime += 2.0 + (random()%1000)*0.0002;
3284           }
3285           else {
3286             /* Lots of programs had at least their main functionality in
3287                Applesoft Basic, which had a lot of limits (memory, string
3288                length, etc) and would sometimes crash unexpectedly. */
3289             sprintf(printbuf,
3290                     "\n"
3291                     "\n"
3292                     "\n"
3293                     "?%s IN %d\n"
3294                     "\001]",
3295                     apple2_basic_errors[random() %
3296                                         (sizeof(apple2_basic_errors)
3297                                          /sizeof(char *))],
3298                     (1000*(random()%(random()%59+1)) +
3299                      100*(random()%(random()%9+1)) +
3300                      5*(random()%(random()%199+1)) +
3301                      1*(random()%(random()%(random()%2+1)+1))));
3302             printing=printbuf;
3303             a2_goto(st,23,1);
3304             stepno=1;
3305             prompt=']';
3306             next_actiontime += 2.0 + (random()%1000)*0.0002;
3307           }
3308           break;
3309       
3310         case 1:
3311           if (simulate_user && random()%3==0) {
3312             /* This was how you reset the Basic interpreter. The sort of
3313                incantation you'd have on a little piece of paper taped to the
3314                side of your machine */
3315             typing="CALL -1370";
3316             stepno=2;
3317           } 
3318           else if (simulate_user && random()%2==0) {
3319             typing="CATALOG\n";
3320             stepno=22;
3321           }
3322           else {
3323             next_actiontime+=1.0;
3324             stepno=6;
3325           }
3326           break;
3327
3328         case 2:
3329           stepno=3;
3330           next_actiontime += 0.5;
3331           break;
3332       
3333         case 3:
3334           st->gr_mode=0;
3335           a2_cls(st);
3336           a2_goto(st,0,16);
3337           for (s="APPLE ]["; *s; s++) a2_printc(st,*s);
3338           a2_goto(st,23,0);
3339           a2_printc(st,']');
3340           next_actiontime+=1.0;
3341           stepno=6;
3342           break;
3343
3344         case 6:
3345           if (simulate_user && random()%50==0 && 0) { /* disabled, too goofy */
3346             typing="10 PRINT \"TRS-80S SUCK!!!\"\n"
3347               "]20 GOTO 10\n"
3348               "]RUN";
3349             stepno=7;
3350           }
3351           else {
3352             stepno=8;
3353             next_actiontime += delay;
3354           }
3355           break;
3356
3357         case 7:
3358           for (i=0; i<30; i++) {
3359             for (s="\nTRS-80S SUCK"; *s; s++) a2_printc(st,*s);
3360           }
3361           stepno=8;
3362           next_actiontime+=delay;
3363
3364         case 8:
3365           break;
3366
3367         case 22:
3368           if (random()%50==0) {
3369             sprintf(printbuf,"\nDISK VOLUME 254\n\n"
3370                     " A 002 HELLO\n"
3371                     "\n"
3372                     "]");
3373             printing=printbuf;
3374           }
3375           else {
3376             sprintf(printbuf,"\n?%s\n]",
3377                     apple2_dos_errors[random()%
3378                                       (sizeof(apple2_dos_errors) /
3379                                        sizeof(char *))]);
3380             printing=printbuf;
3381           }
3382           stepno=6;
3383           next_actiontime+=1.0;
3384           break;
3385
3386         case 11:
3387           if (simulate_user && random()%2==0) {
3388             /* This was how you went back to text mode in the monitor */
3389             typing="FB4BG";
3390             stepno=12;
3391           } else {
3392             next_actiontime+=1.0;
3393             stepno=6;
3394           }
3395           break;
3396
3397         case 12:
3398           st->gr_mode=0;
3399           a2_invalidate(st);
3400           a2_printc(st,'\n');
3401           a2_printc(st,'*');
3402           stepno=13;
3403           next_actiontime+=2.0;
3404           break;
3405
3406         case 13:
3407           /* This reset things into Basic */
3408           if (simulate_user && random()%2==0) {
3409             typing="FAA6G";
3410             stepno=2;
3411           }
3412           else {
3413             stepno=8;
3414             next_actiontime+=delay;
3415           }
3416           break;
3417
3418         case 73:
3419           for (i=0; i<1500; i++) {
3420             a2_poke(st, fillptr, fillbyte);
3421             fillptr++;
3422             fillbyte = (fillbyte+1)&0xff;
3423           }
3424           next_actiontime += 0.08;
3425           /* When you hit c000, it changed video settings */
3426           if (fillptr>=0xc000) {
3427             a2_invalidate(st);
3428             st->gr_mode=0;
3429           }
3430           /* And it seemed to reset around here, I dunno why */
3431           if (fillptr>=0xcf00) stepno=3;
3432           break;
3433         }
3434       }
3435     }
3436
3437     /* Now, we turn the data in the Apple II video into a screen display. This
3438        is interesting because of the interaction with the NTSC color decoding
3439        in a color television. */
3440
3441     colormode=use_color && st->gr_mode!=0;
3442     if (!use_cmap) {
3443       ntsc_set_demod(dec, tint_control, color_control, brightness_control,
3444                      freq_error, colormode);
3445     }
3446     imgrow=0;
3447     for (textrow=0; textrow<24; textrow++) {
3448       if (st->rowimage[textrow] == textrow) {
3449         screen_plan[textrow]=0;
3450       }
3451       else if (cheapdisplay && st->rowimage[textrow]>=0 &&
3452                textrow<21 && st->rowimage[textrow]<21 && 
3453                st->rowimage[textrow]>=2 && textrow>=2 &&
3454                (st->rowimage[textrow]+1)*h/24 + screen_xo <= xgwa.height) {
3455         screen_plan[textrow]= A2_SP_COPY | st->rowimage[textrow];
3456         for (i=0; i<8; i++) {
3457           crtload[textrow*8+i]=crtload[st->rowimage[textrow]*8+i];
3458         }
3459         startdisplayrow=0;
3460       }
3461       else {
3462         st->rowimage[textrow]=imgrow;
3463         screen_plan[textrow]=imgrow | A2_SP_PUT;
3464         
3465         for (row=textrow*8; row<textrow*8+8; row++) {
3466           char *pp;
3467           int pixmultinc,pixbright;
3468           int scanstart_i, scanend_i;
3469           int squishright_i, squishdiv;
3470           int pixrate;
3471           double bloomthisrow,shiftthisrow;
3472           int ytop=(imgrow*h/24) + ((row-textrow*8) * h/192);
3473           int ybot=ytop+h/192;
3474
3475           /* First we generate the pattern that the video circuitry shifts out
3476              of memory. It has a 14.something MHz dot clock, equal to 4 times
3477              the color burst frequency. So each group of 4 bits defines a
3478              color.  Each character position, or byte in hires, defines 14
3479              dots, so odd and even bytes have different color spaces. So,
3480              pattern[0..600] gets the dots for one scan line. */
3481
3482           memset(dec->pattern,0,sizeof(dec->pattern));
3483           pp=dec->pattern+20;
3484         
3485           if ((st->gr_mode&A2_GR_HIRES) && (row<160 ||
3486                                             (st->gr_mode&A2_GR_FULL))) {
3487
3488             /* Emulate the mysterious pink line, due to a bit getting
3489                stuck in a shift register between the end of the last
3490                row and the beginning of this one. */
3491             if ((st->hireslines[row][0] & 0x80) &&
3492                 (st->hireslines[row][39]&0x40)) {
3493               pp[-1]=1;
3494             }
3495
3496             for (col=0; col<40; col++) {
3497               u_char b=st->hireslines[row][col];
3498               int shift=(b&0x80)?0:1;
3499
3500               /* Each of the low 7 bits in hires mode corresponded to 2 dot
3501                  clocks, shifted by one if the high bit was set. */
3502               for (i=0; i<7; i++) {
3503                 pp[shift+1] = pp[shift] =(b>>i)&1;
3504                 pp+=2;
3505               }
3506             }
3507           } 
3508           else if ((st->gr_mode&A2_GR_LORES) && (row<160 ||
3509                                                  (st->gr_mode&A2_GR_FULL))) {
3510             for (col=0; col<40; col++) {
3511               u_char nib=(st->textlines[textrow][col] >> (((row/4)&1)*4))&0xf;
3512               /* The low or high nybble was shifted out one bit at a time. */
3513               for (i=0; i<14; i++) {
3514                 *pp = (nib>>((col*14+i)&3))&1;
3515                 pp++;
3516               }
3517             }
3518           }
3519           else {
3520             for (col=0; col<40; col++) {
3521               int rev;
3522               c=st->textlines[textrow][col];
3523               /* hi bits control inverse/blink as follows:
3524                   0x00: inverse
3525                   0x40: blink
3526                   0x80: normal
3527                   0xc0: normal */
3528               rev=!(c&0x80) && (!(c&0x40) || st->blink);
3529
3530               for (i=0; i<7; i++) {
3531                 for (i=0; i<7; i++) {
3532                   unsigned long pix=XGetPixel(text_im,
3533                                               ((c&0x3f)^0x20)*7+i, row%8);
3534                   pp[1] = pp[2] = pix^rev;
3535                   pp+=2;
3536                 }
3537               }
3538             }
3539           }
3540
3541           /*
3542             Interpolate the 600-dotclock line into however many horizontal
3543             screen pixels we're using, and convert to RGB. 
3544
3545             We add some 'bloom', variations in the horizontal scan width with
3546             the amount of brightness, extremely common on period TV sets. They
3547             had a single oscillator which generated both the horizontal scan
3548             and (during the horizontal retrace interval) the high voltage for
3549             the electron beam. More brightness meant more load on the
3550             oscillator, which caused an decrease in horizontal deflection. Look
3551             for (bloomthisrow).
3552
3553             Also, the A2 did a bad job of generating horizontal sync pulses
3554             during the vertical blanking interval. This, and the fact that the
3555             horizontal frequency was a bit off meant that TVs usually went a
3556             bit out of sync during the vertical retrace, and the top of the
3557             screen would be bent a bit to the left or right. Look for
3558             (shiftthisrow).
3559
3560             We also add a teeny bit of left overscan, just enough to be
3561             annoying, but you can still read the left column of text.
3562             
3563             We also simulate compression & brightening on the right side of the
3564             screen. Most TVs do this, but you don't notice because they
3565             overscan so it's off the right edge of the CRT. But the A2 video
3566             system used so much of the horizontal scan line that you had to
3567             crank the horizontal width down in order to not lose the right few
3568             characters, and you'd see the compression on the right
3569             edge. Associated with compression is brightening; since the
3570             electron beam was scanning slower, the same drive signal hit the
3571             phosphor harder. Look for (squishright_i) and (squishdiv).
3572           */
3573
3574           for (i=j=0; i<600; i++) {
3575             j += dec->pattern[i];
3576           }
3577           crtload[row] = (crtload[row>1 ? row-1 : 0]) * 0.98 + 0.02*(j/600.0) +
3578             (row>180 ? (row-180)*(row-180)*0.0005 : 0.0);
3579           bloomthisrow = -10.0 * crtload[row];
3580           shiftthisrow=((row<18) ? ((18-row)*(18-row)* 0.002 + (18-row)*0.05)
3581                         * horiz_desync : 0.0);
3582
3583           scanstart_i=(int)((bloomthisrow+shiftthisrow+18.0)*65536.0);
3584           if (scanstart_i<0) scanstart_i=0;
3585           if (scanstart_i>30*65536) scanstart_i=30*65536;
3586           scanend_i=599*65536;
3587           squishright_i=scanstart_i + 530*65536;
3588           squishdiv=w/15;
3589           pixrate=(int)((560.0-2.0*bloomthisrow)*65536.0/w);
3590           
3591           if (use_cmap) {
3592             for (y=ytop; y<ybot; y++) {
3593               int level=(!(y==ytop && ybot-ytop>=3) &&
3594                          !(y==ybot-1 && ybot-ytop>=5));
3595               int hist=0;
3596               int histi=0;
3597
3598               pixmultinc=pixrate;
3599               for (x=0, i=scanstart_i;
3600                    x<w && i<scanend_i;
3601                    x++, i+=pixmultinc) {
3602                 int pati=(i>>16);
3603                 int offset=pati&3;
3604                 while (pati>=histi) {
3605                   hist=(((hist<<1) & ((1<<A2_CMAP_HISTBITS)-1)) |
3606                         dec->pattern[histi]);
3607                   histi++;
3608                 }
3609                 XPutPixel(image, x, y, 
3610                           colors[A2_CMAP_INDEX(colormode,level,hist,offset)]);
3611                 if (i >= squishright_i) {
3612                   pixmultinc += pixmultinc/squishdiv;
3613                 }
3614               }
3615               for ( ; x<w; x++) {
3616                 XPutPixel(image, x, y, colors[0]);
3617               }
3618             }
3619           } else {
3620
3621             ntsc_to_yiq(dec);
3622
3623             pixbright=(int)(contrast_control*65536.0);
3624             pixmultinc=pixrate;
3625             for (x=0, i=scanstart_i, rrp=raw_rgb;
3626                  x<w && i<scanend_i;
3627                  x++, i+=pixmultinc, rrp+=3) {
3628               int pixfrac=i&0xffff;
3629               int invpixfrac=65536-pixfrac;
3630               int pati=i>>16;
3631               int r,g,b;
3632
3633               int interpy=((dec->ntscy[pati]*invpixfrac + 
3634                             dec->ntscy[pati+1]*pixfrac)>>16);
3635               int interpi=((dec->ntsci[pati]*invpixfrac + 
3636                             dec->ntsci[pati+1]*pixfrac)>>16);
3637               int interpq=((dec->ntscq[pati]*invpixfrac + 
3638                             dec->ntscq[pati+1]*pixfrac)>>16);
3639
3640               /*
3641                 According to the NTSC spec, Y,I,Q are generated as:
3642                 
3643                 y=0.30 r + 0.59 g + 0.11 b
3644                 i=0.60 r - 0.28 g - 0.32 b
3645                 q=0.21 r - 0.52 g + 0.31 b
3646                 
3647                 So if you invert the implied 3x3 matrix you get what standard
3648                 televisions implement with a bunch of resistors (or directly in
3649                 the CRT -- don't ask):
3650                 
3651                 r = y + 0.948 i + 0.624 q
3652                 g = y - 0.276 i - 0.639 q
3653                 b = y - 1.105 i + 1.729 q
3654                 
3655                 These coefficients are below in 16.16 format.
3656               */
3657
3658               r=((interpy + ((+68128*interpi+40894*interpq)>>16))*pixbright)
3659                                                            >>16;
3660               g=((interpy + ((-18087*interpi-41877*interpq)>>16))*pixbright)
3661                                                            >>16;
3662               b=((interpy + ((-72417*interpi+113312*interpq)>>16))*pixbright)
3663                                                             >>16;
3664               if (r<0) r=0;
3665               if (g<0) g=0;
3666               if (b<0) b=0;
3667               rrp[0]=r;
3668               rrp[1]=g;
3669               rrp[2]=b;
3670
3671               if (i>=squishright_i) {
3672                 pixmultinc += pixmultinc/squishdiv;
3673                 pixbright += pixbright/squishdiv;
3674               }
3675             }
3676             for ( ; x<w; x++, rrp+=3) {
3677               rrp[0]=rrp[1]=rrp[2]=0;
3678             }
3679
3680             for (y=ytop; y<ybot; y++) {
3681               /* levelmult represents the vertical size of scan lines. Each
3682                  line is brightest in the middle, and there's a dark band
3683                  between them. */
3684               int levelmult;
3685               double levelmult_fp=(y + 0.5 - (ytop+ybot)*0.5) / (ybot-ytop);
3686               levelmult_fp = 1.0-(levelmult_fp*levelmult_fp*levelmult_fp
3687                                   *levelmult_fp)*16.0;
3688               if (levelmult_fp<0.0) levelmult_fp=0.0;
3689               levelmult = (int)(64.9*levelmult_fp);
3690
3691               /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes
3692                   to show why standard graphics sw has to be fast, or else
3693                   people will have to work around it and risk incompatibility.
3694                   The quickdraw folks understood this. The other answer would
3695                   be for X11 to have fewer formats for bitm.. oh, never
3696                   mind. If neither of these cases work (they probably cover 99%
3697                   of setups) it falls back on the Xlib routines. */
3698               if (image->format==ZPixmap && image->bits_per_pixel==32 && 
3699                   sizeof(unsigned long)==4 &&
3700                   image->byte_order==localbyteorder) {
3701                 unsigned long *pixelptr =
3702                   (unsigned long *) (image->data + y * image->bytes_per_line);
3703                 for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
3704                   unsigned long ntscri, ntscgi, ntscbi;
3705                   ntscri=((unsigned long)rrp[0])*levelmult;
3706                   ntscgi=((unsigned long)rrp[1])*levelmult;
3707                   ntscbi=((unsigned long)rrp[2])*levelmult;
3708                   if (ntscri>65535) ntscri=65535;
3709                   if (ntscgi>65535) ntscgi=65535;
3710                   if (ntscbi>65535) ntscbi=65535;
3711                   *pixelptr++ = ((ntscri>>red_invprec)<<red_shift) |
3712                     ((ntscgi>>green_invprec)<<green_shift) |
3713                     ((ntscbi>>blue_invprec)<<blue_shift);
3714                 }
3715               }
3716               else if (image->format==ZPixmap && image->bits_per_pixel==16 && 
3717                        sizeof(unsigned short)==2 &&
3718                        image->byte_order==localbyteorder) {
3719                 unsigned short *pixelptr =
3720                 (unsigned short *)(image->data + y*image->bytes_per_line);
3721                 for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
3722                   unsigned long ntscri, ntscgi, ntscbi;
3723                   ntscri=((unsigned long)rrp[0])*levelmult;
3724                   ntscgi=((unsigned long)rrp[1])*levelmult;
3725                   ntscbi=((unsigned long)rrp[2])*levelmult;
3726                   if (ntscri>65535) ntscri=65535;
3727                   if (ntscgi>65535) ntscgi=65535;
3728                   if (ntscbi>65535) ntscbi=65535;
3729                   *pixelptr++ = ((ntscri>>red_invprec)<<red_shift) |
3730                     ((ntscgi>>green_invprec)<<green_shift) |
3731                     ((ntscbi>>blue_invprec)<<blue_shift);
3732                 }
3733                 
3734               }
3735               else {
3736                 for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
3737                   unsigned long pixel, ntscri, ntscgi, ntscbi;
3738                   /* Convert to 16-bit color values, with saturation. The ntscr
3739                      values are 22.10 fixed point, and levelmult is 24.6, so we
3740                      get 16 bits out*/
3741                   ntscri=((unsigned long)rrp[0])*levelmult;
3742                   ntscgi=((unsigned long)rrp[1])*levelmult;
3743                   ntscbi=((unsigned long)rrp[2])*levelmult;
3744                   if (ntscri>65535) ntscri=65535;
3745                   if (ntscgi>65535) ntscgi=65535;
3746                   if (ntscbi>65535) ntscbi=65535;
3747                   pixel = ((ntscri>>red_invprec)<<red_shift) |
3748                     ((ntscgi>>green_invprec)<<green_shift) |
3749                     ((ntscbi>>blue_invprec)<<blue_shift);
3750                   XPutPixel(image, x, y, pixel);
3751                 }
3752               }
3753             }
3754           }
3755         }
3756         imgrow++;
3757       }
3758     }
3759
3760     /* For just the the rows which changed, blit the image to the screen. */
3761     for (textrow=0; textrow<24; ) {
3762       int top,bot,srcrow,srctop,nrows;
3763       
3764       nrows=1;
3765       while (textrow+nrows < 24 &&
3766              screen_plan[textrow+nrows] == screen_plan[textrow]+nrows)
3767         nrows++;
3768
3769       top=h*textrow/24;
3770       bot=h*(textrow+nrows)/24;
3771       srcrow=screen_plan[textrow]&A2_SP_ROWMASK;
3772       srctop=srcrow*h/24;
3773
3774       if (screen_plan[textrow] & A2_SP_COPY) {
3775         if (0) printf("Copy %d screenrows %d to %d\n", nrows, srcrow, textrow);
3776         XCopyArea(dpy, window, window, gc,
3777                   screen_xo, screen_yo + srctop,
3778                   w, bot-top,
3779                   screen_xo, screen_yo + top);
3780       }
3781       else if (screen_plan[textrow] & A2_SP_PUT) {
3782         if (0) printf("Draw %d imgrows %d to %d\n", nrows, srcrow, textrow);
3783         if (use_shm) {
3784 #ifdef HAVE_XSHM_EXTENSION
3785           XShmPutImage(dpy, window, gc, image, 
3786                        0, srctop, screen_xo, screen_yo + top,
3787                        w, bot-top, False);
3788 #endif
3789         } else {
3790           XPutImage(dpy, window, gc, image, 
3791                     0, srctop,
3792                     screen_xo, screen_yo + top,
3793                     w, bot-top);
3794         }
3795       }
3796       textrow += nrows;
3797     }
3798     XSync(dpy,0);
3799
3800     for (textrow=0; textrow<24; textrow++) {
3801       st->rowimage[textrow]=textrow;
3802     }
3803   }
3804
3805  finished:
3806   XSync(dpy,False);
3807   XClearWindow(dpy, window);
3808   goto cleanup;
3809
3810  bailout:
3811   ;
3812
3813  cleanup:
3814   if (image) {
3815     if (use_shm) {
3816 #ifdef HAVE_XSHM_EXTENSION
3817       destroy_xshm_image(dpy, image, &shm_info);
3818 #endif
3819     } else {
3820       XDestroyImage(image);
3821     }
3822     image=NULL;
3823   }
3824   if (text_im) XDestroyImage(text_im);
3825   if (gc) XFreeGC(dpy, gc);
3826   if (st) free(st);
3827   if (raw_rgb) free(raw_rgb);
3828   if (dec) free(dec);
3829   if (n_colors) XFreeColors(dpy, xgwa.colormap, colors, n_colors, 0L);
3830 }
3831
3832
3833 \f
3834 char *progclass = "BSOD";
3835
3836 char *defaults [] = {
3837   "*delay:                 30",
3838
3839   "*doOnly:                ",
3840   "*doWindows:             True",
3841   "*doNT:                  True",
3842   "*doWin2K:               True",
3843   "*doAmiga:               True",
3844   "*doMac:                 True",
3845   "*doMacsBug:             True",
3846   "*doMac1:                True",
3847   "*doMacX:                True",
3848   "*doSCO:                 True",
3849   "*doAtari:               False",      /* boring */
3850   "*doBSD:                 False",      /* boring */
3851   "*doLinux:               True",
3852   "*doSparcLinux:          False",      /* boring */
3853   "*doBlitDamage:          True",
3854   "*doSolaris:             True",
3855   "*doHPUX:                True",
3856   "*doApple2:              True",
3857
3858   ".Windows.font:          -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3859   ".Windows.font2:         -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
3860   ".Windows.foreground:    White",
3861   ".Windows.background:    #0000AA",    /* EGA color 0x01. */
3862
3863   ".Amiga.font:            -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3864   ".Amiga.font2:           -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
3865   ".Amiga.foreground:      Red",
3866   ".Amiga.background:      Black",
3867   ".Amiga.background2:     White",
3868
3869   ".Mac.font:              -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3870   ".Mac.foreground:        PaleTurquoise1",
3871   ".Mac.background:        Black",
3872
3873   ".Atari.foreground:      Black",
3874   ".Atari.background:      White",
3875
3876   ".MacsBug.font:          -*-courier-medium-r-*-*-*-100-*-*-m-*-*-*",
3877   ".MacsBug.font2:         -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3878   ".MacsBug.font3:         -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3879   ".MacsBug.foreground:    Black",
3880   ".MacsBug.background:    White",
3881   ".MacsBug.borderColor:   #AAAAAA",
3882
3883   ".mac1.foreground:       Black",
3884   ".mac1.background:       White",
3885
3886   ".macX.textForeground:   White",
3887   ".macX.textBackground:   Black",
3888   ".macX.background:       #888888",
3889   ".macX.font:             -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3890   ".macX.font2:            -*-courier-bold-r-*-*-*-240-*-*-m-*-*-*",
3891
3892   ".SCO.font:              -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3893   ".SCO.font2:             -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3894   ".SCO.foreground:        White",
3895   ".SCO.background:        Black",
3896
3897   ".Linux.font:            9x15bold",
3898   ".Linux.font2:           -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3899   ".Linux.foreground:      White",
3900   ".Linux.background:      Black",
3901
3902   ".SparcLinux.font:       -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3903   ".SparcLinux.font2:      -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3904   ".SparcLinux.foreground: White",
3905   ".SparcLinux.background: Black",
3906
3907   ".BSD.font:              vga",
3908   ".BSD.font:              -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
3909   ".BSD.font2:             -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3910 /* ".BSD.font2:            -sun-console-medium-r-*-*-22-*-*-*-m-*-*-*", */
3911   ".BSD.foreground:        #c0c0c0",
3912   ".BSD.background:        Black",
3913
3914   ".Solaris.font:          -sun-gallant-*-*-*-*-19-*-*-*-*-120-*-*",
3915   ".Solaris.font2:         -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3916   ".Solaris.foreground:    Black",
3917   ".Solaris.background:    White",
3918   "*dontClearRoot:         True",
3919
3920   ".HPUX.font:             9x15bold",
3921   ".HPUX.font2:            -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3922   ".HPUX.foreground:       Black",
3923   ".HPUX.background:       White",
3924
3925   ".OS390.font:            9x15bold",
3926   ".OS390.font2:           -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
3927   ".OS390.background:      Black",
3928   ".OS390.foreground:      Red",
3929
3930   "*apple2TVColor:         50",
3931   "*apple2TVTint:          5",
3932   "*apple2TVBrightness:    10",
3933   "*apple2TVContrast:      90",
3934   "*apple2SimulateUser:    True",
3935
3936 #ifdef HAVE_XSHM_EXTENSION
3937   "*useSHM:                True",
3938 #endif
3939   0
3940 };
3941
3942 XrmOptionDescRec options [] = {
3943   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
3944   { "-only",            ".doOnly",              XrmoptionSepArg, 0 },
3945   { "-windows",         ".doWindows",           XrmoptionNoArg,  "True"  },
3946   { "-no-windows",      ".doWindows",           XrmoptionNoArg,  "False" },
3947   { "-nt",              ".doNT",                XrmoptionNoArg,  "True"  },
3948   { "-no-nt",           ".doNT",                XrmoptionNoArg,  "False" },
3949   { "-2k",              ".doWin2K",             XrmoptionNoArg,  "True"  },
3950   { "-no-2k",           ".doWin2K",             XrmoptionNoArg,  "False" },
3951   { "-amiga",           ".doAmiga",             XrmoptionNoArg,  "True"  },
3952   { "-no-amiga",        ".doAmiga",             XrmoptionNoArg,  "False" },
3953   { "-mac",             ".doMac",               XrmoptionNoArg,  "True"  },
3954   { "-no-mac",          ".doMac",               XrmoptionNoArg,  "False" },
3955   { "-mac1",            ".doMac1",              XrmoptionNoArg,  "True"  },
3956   { "-no-mac1",         ".doMac1",              XrmoptionNoArg,  "False" },
3957   { "-macx",            ".doMacX",              XrmoptionNoArg,  "True"  },
3958   { "-no-macx",         ".doMacX",              XrmoptionNoArg,  "False" },
3959   { "-atari",           ".doAtari",             XrmoptionNoArg,  "True"  },
3960   { "-no-atari",        ".doAtari",             XrmoptionNoArg,  "False" },
3961   { "-macsbug",         ".doMacsBug",           XrmoptionNoArg,  "True"  },
3962   { "-no-macsbug",      ".doMacsBug",           XrmoptionNoArg,  "False" },
3963   { "-apple2",          ".doApple2",            XrmoptionNoArg,  "True"  },
3964   { "-no-apple2",       ".doApple2",            XrmoptionNoArg,  "False" },
3965   { "-sco",             ".doSCO",               XrmoptionNoArg,  "True"  },
3966   { "-no-sco",          ".doSCO",               XrmoptionNoArg,  "False" },
3967   { "-bsd",             ".doBSD",               XrmoptionNoArg,  "True"  },
3968   { "-no-bsd",          ".doBSD",               XrmoptionNoArg,  "False" },
3969   { "-linux",           ".doLinux",             XrmoptionNoArg,  "True"  },
3970   { "-no-linux",        ".doLinux",             XrmoptionNoArg,  "False" },
3971   { "-sparclinux",      ".doSparcLinux",        XrmoptionNoArg,  "True"  },
3972   { "-no-sparclinux",   ".doSparcLinux",        XrmoptionNoArg,  "False" },
3973   { "-blitdamage",      ".doBlitDamage",        XrmoptionNoArg,  "True"  },
3974   { "-no-blitdamage",   ".doBlitDamage",        XrmoptionNoArg,  "False" },
3975   { "-solaris",         ".doSolaris",           XrmoptionNoArg,  "True"  },
3976   { "-no-solaris",      ".doSolaris",           XrmoptionNoArg,  "False" },
3977   { "-hpux",            ".doHPUX",              XrmoptionNoArg,  "True"  },
3978   { "-no-hpux",         ".doHPUX",              XrmoptionNoArg,  "False" },
3979   { "-os390",           ".doOS390",             XrmoptionNoArg,  "True"  },
3980   { "-no-os390",        ".doOS390",             XrmoptionNoArg,  "False" },
3981   { 0, 0, 0, 0 }
3982 };
3983
3984
3985 static struct {
3986   const char *name;
3987   void (*fn) (Display *, Window, int delay);
3988 } all_modes[] = {
3989   { "Windows",          windows_31 },
3990   { "Nt",               windows_nt },
3991   { "2k",               windows_2k },
3992   { "Amiga",            amiga },
3993   { "Mac",              mac },
3994   { "MacsBug",          macsbug },
3995   { "Mac1",             mac1 },
3996   { "MacX",             macx },
3997   { "SCO",              sco },
3998   { "SparcLinux",       sparc_linux },
3999   { "BSD",              bsd },
4000   { "Atari",            atari },
4001   { "BlitDamage",       blitdamage },
4002   { "Solaris",          sparc_solaris },
4003   { "Linux",            linux_fsck },
4004   { "HPUX",             hpux },
4005   { "OS390",            os390 },
4006   { "Apple2",           apple2 },
4007 };
4008
4009
4010 void
4011 screenhack (Display *dpy, Window window)
4012 {
4013   int loop = 0;
4014   int i = -1;
4015   int j = -1;
4016   int only = -1;
4017   int delay = get_integer_resource ("delay", "Integer");
4018   if (delay < 3) delay = 3;
4019
4020   {
4021     char *s = get_string_resource("doOnly", "DoOnly");
4022     if (s && *s)
4023       {
4024         int count = countof(all_modes);
4025         for (only = 0; only < count; only++)
4026           if (!strcasecmp (s, all_modes[only].name))
4027             break;
4028         if (only >= count)
4029           {
4030             fprintf (stderr, "%s: unknown -only mode: \"%s\"\n", progname, s);
4031             only = -1;
4032           }
4033       }
4034     if (s) free (s);
4035   }
4036
4037   if (!get_boolean_resource ("root", "Boolean"))
4038     {
4039       XWindowAttributes xgwa;
4040       XGetWindowAttributes (dpy, window, &xgwa);
4041       XSelectInput (dpy, window,
4042                     xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
4043     }
4044
4045   while (1)
4046     {
4047       Bool did;
4048       int count = countof(all_modes);
4049       char name[100], class[100];
4050
4051       if (only > 0)
4052         i = only;
4053       else
4054         do {  i = (random() & 0xFF) % count; } while (i == j);
4055
4056       sprintf (name,  "do%s", all_modes[i].name);
4057       sprintf (class, "Do%s", all_modes[i].name);
4058
4059       did = False;
4060       if (only > 0 || get_boolean_resource(name, class))
4061         {
4062           all_modes[i].fn (dpy, window, delay);
4063           did = True;
4064         }
4065
4066       loop++;
4067       if (loop > 100) j = -1;
4068       if (loop > 200)
4069         {
4070           fprintf (stderr, "%s: no display modes enabled?\n", progname);
4071           exit(-1);
4072         }
4073       if (!did) continue;
4074       XSync (dpy, False);
4075       j = i;
4076       loop = 0;
4077     }
4078 }