e3c0994ab907e0a699cd9799c6b1e6c0b0a8f6ce
[xscreensaver] / hacks / bsod.c
1 /* xscreensaver, Copyright (c) 1998 Jamie Zawinski <jwz@netscape.com>
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  *   TODO:
16  *      -  Should have a "macsbug" mode.
17  *      -  Should simulate a Unix kernel panic and reboot.
18  *      -  Making various boot noises would be fun, too.
19  *      -  Maybe scatter some random bits across the screen,
20  *         to simulate corruption of video ram?
21  *      -  Should randomize the various hex numbers printed.
22  */
23
24 #include "screenhack.h"
25 #include <stdio.h>
26 #include <X11/Xutil.h>
27
28 #ifdef HAVE_XPM
29 # include <X11/xpm.h>
30 # include "images/amiga.xpm"
31 #endif
32
33 #include "images/mac.xbm"
34
35
36 static void
37 draw_string (Display *dpy, Window window, GC gc, XGCValues *gcv,
38              XFontStruct *font, int win_width, int win_height,
39              const char *string, int delay)
40 {
41   int x, y;
42   int width = 0, height = 0, cw = 0;
43   int char_width, line_height;
44
45   const char *s = string;
46   const char *se = string;
47
48   /* This pretty much assumes fixed-width fonts */
49   char_width = (font->per_char
50                 ? font->per_char['n'-font->min_char_or_byte2].width
51                 : font->min_bounds.width);
52   line_height = font->ascent + font->descent + 1;
53
54   while (1)
55     {
56       if (*s == '\n' || !*s)
57         {
58           height++;
59           if (cw > width) width = cw;
60           cw = 0;
61           if (!*s) break;
62         }
63       else
64         cw++;
65       s++;
66     }
67
68   x = (win_width - (width * char_width)) / 2;
69   y = (win_height - (height * line_height)) / 2;
70
71   if (x < char_width) x = char_width;
72   if (y < line_height) y = line_height;
73
74   se = s = string;
75   while (1)
76     {
77       if (*s == '\n' || !*s)
78         {
79           int off = 0;
80           Bool flip = False;
81
82           if (*se == '@' || *se == '_')
83             {
84               if (*se == '@') flip = True;
85               se++;
86               off = (char_width * (width - (s - se))) / 2;
87             }
88
89           if (flip)
90             {
91               XSetForeground(dpy, gc, gcv->background);
92               XSetBackground(dpy, gc, gcv->foreground);
93             }
94
95           if (s != se)
96             XDrawImageString(dpy, window, gc, x+off, y+font->ascent, se, s-se);
97
98           if (flip)
99             {
100               XSetForeground(dpy, gc, gcv->foreground);
101               XSetBackground(dpy, gc, gcv->background);
102             }
103
104           se = s;
105           y += line_height;
106           if (!*s) break;
107           se = s+1;
108
109           if (delay)
110             {
111               XSync(dpy, False);
112               usleep(delay);
113             }
114         }
115       s++;
116     }
117 }
118
119
120 static Pixmap
121 double_pixmap(Display *dpy, GC gc, Visual *visual, int depth, Pixmap pixmap,
122              int pix_w, int pix_h)
123 {
124   int x, y;
125   Pixmap p2 = XCreatePixmap(dpy, pixmap, pix_w*2, pix_h*2, depth);
126   XImage *i1 = XGetImage(dpy, pixmap, 0, 0, pix_w, pix_h, ~0L, ZPixmap);
127   XImage *i2 = XCreateImage(dpy, visual, depth, ZPixmap, 0, 0,
128                             pix_w*2, pix_h*2, 8, 0);
129   i2->data = (unsigned char *) calloc(i2->height, i2->bytes_per_line);
130   for (y = 0; y < pix_h; y++)
131     for (x = 0; x < pix_w; x++)
132       {
133         unsigned long p = XGetPixel(i1, x, y);
134         XPutPixel(i2, x*2,   y*2,   p);
135         XPutPixel(i2, x*2+1, y*2,   p);
136         XPutPixel(i2, x*2,   y*2+1, p);
137         XPutPixel(i2, x*2+1, y*2+1, p);
138       }
139   free(i1->data); i1->data = 0;
140   XDestroyImage(i1);
141   XPutImage(dpy, p2, gc, i2, 0, 0, 0, 0, i2->width, i2->height);
142   free(i2->data); i2->data = 0;
143   XDestroyImage(i2);
144   XFreePixmap(dpy, pixmap);
145   return p2;
146 }
147
148
149 /* Sleep for N seconds and return False.  But if a key or mouse event is
150    seen, discard all pending key or mouse events, and return True.
151  */
152 static Bool
153 bsod_sleep(Display *dpy, int seconds)
154 {
155   XEvent event;
156   int q = seconds * 4;
157   int mask = KeyPressMask|ButtonPressMask;
158   while (q > 0)
159     {
160       XSync(dpy, False);
161       if (XCheckMaskEvent(dpy, mask, &event))
162         {
163           while (XCheckMaskEvent(dpy, mask, &event))
164             ;
165           return True;
166         }
167       q--;
168       usleep(250000);
169     }
170   return False; 
171 }
172
173
174 static void
175 windows (Display *dpy, Window window, int delay, Bool w95p)
176 {
177   XGCValues gcv;
178   XWindowAttributes xgwa;
179   char *fontname;
180   const char *def_font = "fixed";
181   XFontStruct *font;
182   GC gc;
183
184   const char *w95 =
185     ("@Windows\n"
186      "A fatal exception 0E has occured at F0AD:42494C4C\n"
187      "the current application will be terminated.\n"
188      "\n"
189      "* Press any key to terminate the current application.\n"
190      "* Press CTRL+ALT+DELETE again to restart your computer.\n"
191      "  You will lose any unsaved information in all applications.\n"
192      "\n"
193      "\n"
194      "_Press any key to continue");
195
196   const char *wnt =
197     ("*** STOP: 0x0000001E (0x80000003,0x80106fc0,0x8025ea21,0xfd6829e8)\n"
198    "Unhandled Kernel exception c0000047 from fa8418b4 (8025ea21,fd6829e8)\n"
199    "\n"
200    "Dll Base Date Stamp - Name             Dll Base Date Stamp - Name\n"
201    "80100000 2be154c9 - ntoskrnl.exe       80400000 2bc153b0 - hal.dll\n"
202    "80258000 2bd49628 - ncrc710.sys        8025c000 2bd49688 - SCSIPORT.SYS \n"
203    "80267000 2bd49683 - scsidisk.sys       802a6000 2bd496b9 - Fastfat.sys\n"
204    "fa800000 2bd49666 - Floppy.SYS         fa810000 2bd496db - Hpfs_Rec.SYS\n"
205    "fa820000 2bd49676 - Null.SYS           fa830000 2bd4965a - Beep.SYS\n"
206    "fa840000 2bdaab00 - i8042prt.SYS       fa850000 2bd5a020 - SERMOUSE.SYS\n"
207    "fa860000 2bd4966f - kbdclass.SYS       fa870000 2bd49671 - MOUCLASS.SYS\n"
208    "fa880000 2bd9c0be - Videoprt.SYS       fa890000 2bd49638 - NCC1701E.SYS\n"
209    "fa8a0000 2bd4a4ce - Vga.SYS            fa8b0000 2bd496d0 - Msfs.SYS\n"
210    "fa8c0000 2bd496c3 - Npfs.SYS           fa8e0000 2bd496c9 - Ntfs.SYS\n"
211    "fa940000 2bd496df - NDIS.SYS           fa930000 2bd49707 - wdlan.sys\n"
212    "fa970000 2bd49712 - TDI.SYS            fa950000 2bd5a7fb - nbf.sys\n"
213    "fa980000 2bd72406 - streams.sys        fa9b0000 2bd4975f - ubnb.sys\n"
214    "fa9c0000 2bd5bfd7 - usbser.sys         fa9d0000 2bd4971d - netbios.sys\n"
215    "fa9e0000 2bd49678 - Parallel.sys       fa9f0000 2bd4969f - serial.SYS\n"
216    "faa00000 2bd49739 - mup.sys            faa40000 2bd4971f - SMBTRSUP.SYS\n"
217    "faa10000 2bd6f2a2 - srv.sys            faa50000 2bd4971a - afd.sys\n"
218    "faa60000 2bd6fd80 - rdr.sys            faaa0000 2bd49735 - bowser.sys\n"
219    "\n"
220    "Address dword dump Dll Base                                      - Name\n"
221    "801afc20 80106fc0 80106fc0 00000000 00000000 80149905 : "
222      "fa840000 - i8042prt.SYS\n"
223    "801afc24 80149905 80149905 ff8e6b8c 80129c2c ff8e6b94 : "
224      "8025c000 - SCSIPORT.SYS\n"
225    "801afc2c 80129c2c 80129c2c ff8e6b94 00000000 ff8e6b94 : "
226      "80100000 - ntoskrnl.exe\n"
227    "801afc34 801240f2 80124f02 ff8e6df4 ff8e6f60 ff8e6c58 : "
228      "80100000 - ntoskrnl.exe\n"
229    "801afc54 80124f16 80124f16 ff8e6f60 ff8e6c3c 8015ac7e : "
230      "80100000 - ntoskrnl.exe\n"
231    "801afc64 8015ac7e 8015ac7e ff8e6df4 ff8e6f60 ff8e6c58 : "
232      "80100000 - ntoskrnl.exe\n"
233    "801afc70 80129bda 80129bda 00000000 80088000 80106fc0 : "
234      "80100000 - ntoskrnl.exe\n"
235    "\n"
236    "Kernel Debugger Using: COM2 (Port 0x2f8, Baud Rate 19200)\n"
237    "Restart and set the recovery options in the system control panel\n"
238    "or the /CRASHDEBUG system start option. If this message reappears,\n"
239    "contact your system administrator or technical support group."
240      );
241
242   XGetWindowAttributes (dpy, window, &xgwa);
243
244   fontname = get_string_resource ((xgwa.height > 600
245                                    ? (w95p
246                                       ? "windows95.font2"
247                                       : "windowsNT.font2")
248                                    : (w95p
249                                       ? "windows95.font"
250                                       : "windowsNT.font")),
251                                   "Windows.Font");
252   if (!fontname || !*fontname) fontname = (char *)def_font;
253   font = XLoadQueryFont (dpy, fontname);
254   if (!font) font = XLoadQueryFont (dpy, def_font);
255   if (!font) exit(-1);
256   if (fontname && fontname != def_font)
257     free (fontname);
258
259   gcv.font = font->fid;
260   gcv.foreground = get_pixel_resource((w95p
261                                        ? "windows95.foreground"
262                                        : "windowsNT.foreground"),
263                                       "Windows.Foreground",
264                                       dpy, xgwa.colormap);
265   gcv.background = get_pixel_resource((w95p
266                                        ? "windows95.background"
267                                        : "windowsNT.background"),
268                                       "Windows.Background",
269                                       dpy, xgwa.colormap);
270   XSetWindowBackground(dpy, window, gcv.background);
271   XClearWindow(dpy, window);
272
273   gc = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
274
275   if (w95p)
276     draw_string(dpy, window, gc, &gcv, font, xgwa.width, xgwa.height, w95, 0);
277   else
278     draw_string(dpy, window, gc, &gcv, font, 10, 10, wnt, 750);
279
280   XFreeGC(dpy, gc);
281   XSync(dpy, False);
282   bsod_sleep(dpy, delay);
283   XClearWindow(dpy, window);
284   XFreeFont(dpy, font);
285 }
286
287 static void
288 amiga (Display *dpy, Window window, int delay)
289 {
290   XGCValues gcv;
291   XWindowAttributes xgwa;
292   char *fontname;
293   const char *def_font = "fixed";
294   XFontStruct *font;
295   GC gc, gc2;
296   int height;
297   unsigned long fg, bg, bg2;
298   Pixmap pixmap = 0;
299   int pix_w, pix_h;
300
301   const char *string =
302     ("_Software failure.  Press left mouse button to continue.\n"
303      "_Guru Meditation #00000003.00C01570");
304
305   XGetWindowAttributes (dpy, window, &xgwa);
306
307   fontname = get_string_resource ((xgwa.height > 600
308                                    ? "amiga.font2" : "amiga.font"),
309                                   "Amiga.Font");
310   if (!fontname || !*fontname) fontname = (char *)def_font;
311   font = XLoadQueryFont (dpy, fontname);
312   if (!font) font = XLoadQueryFont (dpy, def_font);
313   if (!font) exit(-1);
314   if (fontname && fontname != def_font)
315     free (fontname);
316
317   gcv.font = font->fid;
318   fg = gcv.foreground = get_pixel_resource("amiga.foreground",
319                                            "Amiga.Foreground",
320                                            dpy, xgwa.colormap);
321   bg = gcv.background = get_pixel_resource("amiga.background",
322                                            "Amiga.Background",
323                                            dpy, xgwa.colormap);
324   bg2 = get_pixel_resource("amiga.background2", "Amiga.Background",
325                            dpy, xgwa.colormap);
326   XSetWindowBackground(dpy, window, bg2);
327   XClearWindow(dpy, window);
328
329   gc = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
330   gcv.background = fg; gcv.foreground = bg;
331   gc2 = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
332
333   height = (font->ascent + font->descent) * 6;
334
335 #ifdef HAVE_XPM
336   {
337     XpmAttributes xpmattrs;
338     int result;
339     xpmattrs.valuemask = 0;
340
341 # ifdef XpmCloseness
342     xpmattrs.valuemask |= XpmCloseness;
343     xpmattrs.closeness = 40000;
344 # endif
345 # ifdef XpmVisual
346     xpmattrs.valuemask |= XpmVisual;
347     xpmattrs.visual = xgwa.visual;
348 # endif
349 # ifdef XpmDepth
350     xpmattrs.valuemask |= XpmDepth;
351     xpmattrs.depth = xgwa.depth;
352 # endif
353 # ifdef XpmColormap
354     xpmattrs.valuemask |= XpmColormap;
355     xpmattrs.colormap = xgwa.colormap;
356 # endif
357
358     result = XpmCreatePixmapFromData(dpy, window, amiga_hand,
359                                      &pixmap, 0 /* mask */, &xpmattrs);
360     if (!pixmap || (result != XpmSuccess && result != XpmColorError))
361       pixmap = 0;
362     pix_w = xpmattrs.width;
363     pix_h = xpmattrs.height;
364   }
365 #endif /* HAVE_XPM */
366
367   if (pixmap && xgwa.height > 600)      /* scale up the bitmap */
368     {
369       pixmap = double_pixmap(dpy, gc, xgwa.visual, xgwa.depth,
370                              pixmap, pix_w, pix_h);
371       pix_w *= 2;
372       pix_h *= 2;
373     }
374
375   if (pixmap)
376     {
377       int x = (xgwa.width - pix_w) / 2;
378       int y = ((xgwa.height - pix_h) / 2);
379       XCopyArea(dpy, pixmap, window, gc, 0, 0, pix_w, pix_h, x, y);
380
381       XSync(dpy, False);
382       bsod_sleep(dpy, 2);
383
384       XCopyArea(dpy, pixmap, window, gc, 0, 0, pix_w, pix_h, x, y + height);
385       XClearArea(dpy, window, 0, 0, xgwa.width, y + height, False);
386       XFreePixmap(dpy, pixmap);
387     }
388
389   XFillRectangle(dpy, window, gc2, 0, 0, xgwa.width, height);
390   draw_string(dpy, window, gc, &gcv, font, xgwa.width, height, string, 0);
391
392   {
393     GC gca = gc;
394     while (delay > 0)
395       {
396         XFillRectangle(dpy, window, gca, 0, 0, xgwa.width, font->ascent);
397         XFillRectangle(dpy, window, gca, 0, 0, font->ascent, height);
398         XFillRectangle(dpy, window, gca, xgwa.width-font->ascent, 0,
399                        font->ascent, height);
400         XFillRectangle(dpy, window, gca, 0, height-font->ascent, xgwa.width,
401                        font->ascent);
402         gca = (gca == gc ? gc2 : gc);
403         XSync(dpy, False);
404         if (bsod_sleep(dpy, 1))
405           break;
406         delay--;
407       }
408   }
409
410   XFreeGC(dpy, gc);
411   XFreeGC(dpy, gc2);
412   XSync(dpy, False);
413   XClearWindow(dpy, window);
414   XFreeFont(dpy, font);
415 }
416
417
418 static void
419 mac (Display *dpy, Window window, int delay)
420 {
421   XGCValues gcv;
422   XWindowAttributes xgwa;
423   char *fontname;
424   const char *def_font = "fixed";
425   XFontStruct *font;
426   GC gc;
427   Pixmap pixmap = 0;
428   int pix_w = mac_width;
429   int pix_h = mac_height;
430   int offset = mac_height * 4;
431   int i;
432
433   const char *string = ("0 0 0 0 0 0 0 F\n"
434                         "0 0 0 0 0 0 0 3");
435
436   XGetWindowAttributes (dpy, window, &xgwa);
437
438   fontname = get_string_resource ("mac.font", "Mac.Font");
439   if (!fontname || !*fontname) fontname = (char *)def_font;
440   font = XLoadQueryFont (dpy, fontname);
441   if (!font) font = XLoadQueryFont (dpy, def_font);
442   if (!font) exit(-1);
443   if (fontname && fontname != def_font)
444     free (fontname);
445
446   gcv.font = font->fid;
447   gcv.foreground = get_pixel_resource("mac.foreground", "Mac.Foreground",
448                                       dpy, xgwa.colormap);
449   gcv.background = get_pixel_resource("mac.background", "Mac.Background",
450                                       dpy, xgwa.colormap);
451   XSetWindowBackground(dpy, window, gcv.background);
452   XClearWindow(dpy, window);
453
454   gc = XCreateGC(dpy, window, GCFont|GCForeground|GCBackground, &gcv);
455
456   pixmap = XCreatePixmapFromBitmapData(dpy, window, (char *) mac_bits,
457                                        mac_width, mac_height,
458                                        gcv.foreground,
459                                        gcv.background,
460                                        xgwa.depth);
461
462   draw_string(dpy, window, gc, &gcv, font, xgwa.width, xgwa.height + offset,
463               string, 0);
464
465   for(i = 0; i < 2; i++)
466     {
467       pixmap = double_pixmap(dpy, gc, xgwa.visual, xgwa.depth,
468                              pixmap, pix_w, pix_h);
469       pix_w *= 2; pix_h *= 2;
470     }
471
472   {
473     int x = (xgwa.width - pix_w) / 2;
474     int y = (((xgwa.height + offset) / 2) -
475              pix_h -
476              (font->ascent + font->descent) * 2);
477     if (y < 0) y = 0;
478     XCopyArea(dpy, pixmap, window, gc, 0, 0, pix_w, pix_h, x, y);
479     XFreePixmap(dpy, pixmap);
480   }
481
482   XFreeGC(dpy, gc);
483   XSync(dpy, False);
484   bsod_sleep(dpy, delay);
485   XClearWindow(dpy, window);
486   XFreeFont(dpy, font);
487 }
488
489 \f
490 char *progclass = "BSOD";
491
492 char *defaults [] = {
493   "*delay:                      30",
494
495   "BSOD.Windows.font:           -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
496   "BSOD.Windows.font2:          -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
497   "BSOD.Windows.foreground:     White",
498   "BSOD.Windows.background:     Blue",
499
500   "BSOD.Amiga.font:             -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
501   "BSOD.Amiga.font2:            -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
502   "BSOD.Amiga.foreground:       Red",
503   "BSOD.Amiga.background:       Black",
504   "BSOD.Amiga.background2:      White",
505
506   "BSOD.Mac.font:               -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
507   "BSOD.Mac.foreground:         PaleTurquoise1",
508   "BSOD.Mac.background:         Black",
509   0
510 };
511
512 XrmOptionDescRec options [] = {
513   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
514   { 0, 0, 0, 0 }
515 };
516
517 void
518 screenhack (Display *dpy, Window window)
519 {
520   int i = -1;
521   int j = -1;
522   int delay = get_integer_resource ("delay", "Integer");
523   if (delay < 3) delay = 3;
524
525   if (!get_boolean_resource ("root", "Boolean"))
526     XSelectInput(dpy, window, KeyPressMask|ButtonPressMask);
527
528   while (1)
529     {
530       while (i == j) i = random() % 4;
531       j = i;
532
533       switch (i)
534         {
535         case 0: windows(dpy, window, delay, True); break;
536         case 1: windows(dpy, window, delay, False); break;
537         case 2: amiga(dpy, window, delay); break;
538         case 3: mac(dpy, window, delay); break;
539         default: abort(); break;
540         }
541       XSync (dpy, True);
542     }
543 }