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