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