http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.29.tar.gz
[xscreensaver] / driver / splash.c
1 /* xscreensaver, Copyright (c) 1991-2001 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
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #include <X11/Intrinsic.h>
17
18 #include "xscreensaver.h"
19 #include "resources.h"
20
21 #undef MAX
22 #define MAX(a,b) ((a)>(b)?(a):(b))
23
24 void
25 draw_shaded_rectangle (Display *dpy, Window window,
26                        int x, int y,
27                        int width, int height,
28                        int thickness,
29                        unsigned long top_color,
30                        unsigned long bottom_color)
31 {
32   XPoint points[4];
33   XGCValues gcv;
34   GC gc1, gc2;
35   if (thickness == 0) return;
36
37   gcv.foreground = top_color;
38   gc1 = XCreateGC (dpy, window, GCForeground, &gcv);
39   gcv.foreground = bottom_color;
40   gc2 = XCreateGC (dpy, window, GCForeground, &gcv);
41
42   points [0].x = x;
43   points [0].y = y;
44   points [1].x = x + width;
45   points [1].y = y;
46   points [2].x = x + width - thickness;
47   points [2].y = y + thickness;
48   points [3].x = x;
49   points [3].y = y + thickness;
50   XFillPolygon (dpy, window, gc1, points, 4, Convex, CoordModeOrigin);
51
52   points [0].x = x;
53   points [0].y = y + thickness;
54   points [1].x = x;
55   points [1].y = y + height;
56   points [2].x = x + thickness;
57   points [2].y = y + height - thickness;
58   points [3].x = x + thickness;
59   points [3].y = y + thickness;
60   XFillPolygon (dpy, window, gc1, points, 4, Convex, CoordModeOrigin);
61
62   points [0].x = x + width;
63   points [0].y = y;
64   points [1].x = x + width - thickness;
65   points [1].y = y + thickness;
66   points [2].x = x + width - thickness;
67   points [2].y = y + height - thickness;
68   points [3].x = x + width;
69   points [3].y = y + height - thickness;
70   XFillPolygon (dpy, window, gc2, points, 4, Convex, CoordModeOrigin);
71
72   points [0].x = x;
73   points [0].y = y + height;
74   points [1].x = x + width;
75   points [1].y = y + height;
76   points [2].x = x + width;
77   points [2].y = y + height - thickness;
78   points [3].x = x + thickness;
79   points [3].y = y + height - thickness;
80   XFillPolygon (dpy, window, gc2, points, 4, Convex, CoordModeOrigin);
81
82   XFreeGC (dpy, gc1);
83   XFreeGC (dpy, gc2);
84 }
85
86
87 int
88 string_width (XFontStruct *font, char *s)
89 {
90   int direction, ascent, descent;
91   XCharStruct overall;
92   XTextExtents (font, s, strlen(s), &direction, &ascent, &descent, &overall);
93   return overall.width;
94 }
95
96
97 static void update_splash_window (saver_info *si);
98 static void draw_splash_window (saver_info *si);
99 static void destroy_splash_window (saver_info *si);
100 static void unsplash_timer (XtPointer closure, XtIntervalId *id);
101
102 static void do_demo (saver_info *si);
103 static void do_prefs (saver_info *si);
104 static void do_help (saver_info *si);
105
106
107 struct splash_dialog_data {
108   XtIntervalId timer;
109
110   Dimension width;
111   Dimension height;
112
113   char *heading_label;
114   char *body_label;
115   char *body2_label;
116   char *demo_label;
117   char *prefs_label;
118   char *help_label;
119
120   XFontStruct *heading_font;
121   XFontStruct *body_font;
122   XFontStruct *button_font;
123
124   Pixel foreground;
125   Pixel background;
126   Pixel button_foreground;
127   Pixel button_background;
128   Pixel shadow_top;
129   Pixel shadow_bottom;
130
131   Dimension logo_width;
132   Dimension logo_height;
133   Dimension internal_border;
134   Dimension shadow_width;
135
136   Dimension button_width, button_height;
137   Dimension demo_button_x, demo_button_y;
138   Dimension prefs_button_x, prefs_button_y;
139   Dimension help_button_x, help_button_y;
140
141   Pixmap logo_pixmap;
142   int logo_npixels;
143   unsigned long *logo_pixels;
144
145   int pressed;
146 };
147
148
149 void
150 make_splash_dialog (saver_info *si)
151 {
152   int x, y, bw;
153   XSetWindowAttributes attrs;
154   unsigned long attrmask = 0;
155   splash_dialog_data *sp;
156   Screen *screen = si->default_screen->screen;
157   Colormap cmap = DefaultColormapOfScreen (screen);
158   char *f;
159
160   if (si->sp_data)
161     return;
162   if (!si->prefs.splash_p ||
163       si->prefs.splash_duration <= 0)
164     return;
165
166   sp = (splash_dialog_data *) calloc (1, sizeof(*sp));
167
168   sp->heading_label = get_string_resource ("splash.heading.label",
169                                            "Dialog.Label.Label");
170   sp->body_label = get_string_resource ("splash.body.label",
171                                         "Dialog.Label.Label");
172   sp->body2_label = get_string_resource ("splash.body2.label",
173                                          "Dialog.Label.Label");
174   sp->demo_label = get_string_resource ("splash.demo.label",
175                                         "Dialog.Button.Label");
176   sp->prefs_label = get_string_resource ("splash.prefs.label",
177                                         "Dialog.Button.Label");
178   sp->help_label = get_string_resource ("splash.help.label",
179                                         "Dialog.Button.Label");
180
181   if (!sp->heading_label)
182     sp->heading_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
183   if (!sp->body_label)
184     sp->body_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
185   if (!sp->body2_label)
186     sp->body2_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
187   if (!sp->demo_label) sp->demo_label = strdup("ERROR");
188   if (!sp->prefs_label) sp->prefs_label = strdup("ERROR");
189   if (!sp->help_label) sp->help_label = strdup("ERROR");
190
191   /* Put the version number in the label. */
192   {
193     char *s = (char *) malloc (strlen(sp->heading_label) + 20);
194     sprintf(s, sp->heading_label, si->version);
195     free (sp->heading_label);
196     sp->heading_label = s;
197   }
198
199   f = get_string_resource ("splash.headingFont", "Dialog.Font");
200   sp->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
201   if (!sp->heading_font) sp->heading_font = XLoadQueryFont (si->dpy, "fixed");
202   if (f) free (f);
203
204   f = get_string_resource("splash.bodyFont", "Dialog.Font");
205   sp->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
206   if (!sp->body_font) sp->body_font = XLoadQueryFont (si->dpy, "fixed");
207   if (f) free (f);
208
209   f = get_string_resource("splash.buttonFont", "Dialog.Font");
210   sp->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
211   if (!sp->button_font) sp->button_font = XLoadQueryFont (si->dpy, "fixed");
212   if (f) free (f);
213
214   sp->foreground = get_pixel_resource ("splash.foreground",
215                                        "Dialog.Foreground",
216                                        si->dpy, cmap);
217   sp->background = get_pixel_resource ("splash.background",
218                                        "Dialog.Background",
219                                        si->dpy, cmap);
220
221   if (sp->foreground == sp->background)
222     {
223       /* Make sure the error messages show up. */
224       sp->foreground = BlackPixelOfScreen (screen);
225       sp->background = WhitePixelOfScreen (screen);
226     }
227
228   sp->button_foreground = get_pixel_resource ("splash.Button.foreground",
229                                               "Dialog.Button.Foreground",
230                                               si->dpy, cmap);
231   sp->button_background = get_pixel_resource ("splash.Button.background",
232                                               "Dialog.Button.Background",
233                                               si->dpy, cmap);
234   sp->shadow_top = get_pixel_resource ("splash.topShadowColor",
235                                        "Dialog.Foreground",
236                                        si->dpy, cmap);
237   sp->shadow_bottom = get_pixel_resource ("splash.bottomShadowColor",
238                                           "Dialog.Background",
239                                           si->dpy, cmap);
240
241   sp->logo_width = get_integer_resource ("splash.logo.width",
242                                          "Dialog.Logo.Width");
243   sp->logo_height = get_integer_resource ("splash.logo.height",
244                                           "Dialog.Logo.Height");
245   sp->internal_border = get_integer_resource ("splash.internalBorderWidth",
246                                               "Dialog.InternalBorderWidth");
247   sp->shadow_width = get_integer_resource ("splash.shadowThickness",
248                                            "Dialog.ShadowThickness");
249
250   if (sp->logo_width == 0)  sp->logo_width = 150;
251   if (sp->logo_height == 0) sp->logo_height = 150;
252   if (sp->internal_border == 0) sp->internal_border = 15;
253   if (sp->shadow_width == 0) sp->shadow_width = 4;
254
255   {
256     int direction, ascent, descent;
257     XCharStruct overall;
258
259     sp->width = 0;
260     sp->height = 0;
261
262     /* Measure the heading_label. */
263     XTextExtents (sp->heading_font,
264                   sp->heading_label, strlen(sp->heading_label),
265                   &direction, &ascent, &descent, &overall);
266     if (overall.width > sp->width) sp->width = overall.width;
267     sp->height += ascent + descent;
268
269     /* Measure the body_label. */
270     XTextExtents (sp->body_font,
271                   sp->body_label, strlen(sp->body_label),
272                   &direction, &ascent, &descent, &overall);
273     if (overall.width > sp->width) sp->width = overall.width;
274     sp->height += ascent + descent;
275
276     /* Measure the body2_label. */
277     XTextExtents (sp->body_font,
278                   sp->body2_label, strlen(sp->body2_label),
279                   &direction, &ascent, &descent, &overall);
280     if (overall.width > sp->width) sp->width = overall.width;
281     sp->height += ascent + descent;
282
283     {
284       Dimension w2 = 0, w3 = 0, w4 = 0;
285       Dimension h2 = 0, h3 = 0, h4 = 0;
286
287       /* Measure the Demo button. */
288       XTextExtents (sp->button_font,
289                     sp->demo_label, strlen(sp->demo_label),
290                     &direction, &ascent, &descent, &overall);
291       w2 = overall.width;
292       h2 = ascent + descent;
293
294       /* Measure the Prefs button. */
295       XTextExtents (sp->button_font,
296                     sp->prefs_label, strlen(sp->prefs_label),
297                     &direction, &ascent, &descent, &overall);
298       w3 = overall.width;
299       h3 = ascent + descent;
300
301       /* Measure the Help button. */
302       XTextExtents (sp->button_font,
303                     sp->help_label, strlen(sp->help_label),
304                     &direction, &ascent, &descent, &overall);
305       w4 = overall.width;
306       h4 = ascent + descent;
307
308       w2 = MAX(w2, w3); w2 = MAX(w2, w4);
309       h2 = MAX(h2, h3); h2 = MAX(h2, h4);
310
311       w2 += ((ascent + descent) / 2) + (sp->shadow_width * 2);
312       h2 += ((ascent + descent) / 2) + (sp->shadow_width * 2);
313
314       sp->button_width = w2;
315       sp->button_height = h2;
316
317       w2 *= 3;
318
319       w2 += ((ascent + descent) * 2);  /* for space between buttons */
320
321       if (w2 > sp->width) sp->width = w2;
322       sp->height += h2;
323     }
324
325     sp->width  += (sp->internal_border * 2);
326     sp->height += (sp->internal_border * 3);
327
328     if (sp->logo_height > sp->height)
329       sp->height = sp->logo_height;
330     else if (sp->height > sp->logo_height)
331       sp->logo_height = sp->height;
332
333     sp->logo_width = sp->logo_height;
334
335     sp->width += sp->logo_width;
336   }
337
338   attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
339   attrmask |= CWEventMask;
340   attrs.event_mask = (ExposureMask | ButtonPressMask | ButtonReleaseMask);
341
342   {
343     int sx, sy, w, h;
344     get_screen_viewport (si->default_screen, &sx, &sy, &w, &h, False);
345     if (si->prefs.debug_p) w /= 2;
346     x = sx + (((w + sp->width)  / 2) - sp->width);
347     y = sy + (((h + sp->height) / 2) - sp->height);
348     if (x < sx) x = sx;
349     if (y < sy) y = sy;
350   }
351
352   bw = get_integer_resource ("splash.borderWidth", "Dialog.BorderWidth");
353
354   si->splash_dialog =
355     XCreateWindow (si->dpy,
356                    RootWindowOfScreen(screen),
357                    x, y, sp->width, sp->height, bw,
358                    DefaultDepthOfScreen (screen), InputOutput,
359                    DefaultVisualOfScreen(screen),
360                    attrmask, &attrs);
361   XSetWindowBackground (si->dpy, si->splash_dialog, sp->background);
362
363   sp->logo_pixmap = xscreensaver_logo (si->dpy, si->splash_dialog, cmap,
364                                        sp->background, 
365                                        &sp->logo_pixels, &sp->logo_npixels,
366                                        True);
367
368   XMapRaised (si->dpy, si->splash_dialog);
369   XSync (si->dpy, False);
370
371   si->sp_data = sp;
372
373   sp->timer = XtAppAddTimeOut (si->app, si->prefs.splash_duration,
374                                unsplash_timer, (XtPointer) si);
375
376   draw_splash_window (si);
377   XSync (si->dpy, False);
378 }
379
380
381 static void
382 draw_splash_window (saver_info *si)
383 {
384   splash_dialog_data *sp = si->sp_data;
385   XGCValues gcv;
386   GC gc1, gc2;
387   int hspacing, vspacing, height;
388   int x1, x2, x3, y1, y2;
389   int sw;
390
391   height = (sp->heading_font->ascent + sp->heading_font->descent +
392             sp->body_font->ascent + sp->body_font->descent +
393             sp->body_font->ascent + sp->body_font->descent +
394             sp->button_font->ascent + sp->button_font->descent);
395   vspacing = ((sp->height
396                - (4 * sp->shadow_width)
397                - (2 * sp->internal_border)
398                - height) / 5);
399   if (vspacing < 0) vspacing = 0;
400   if (vspacing > (sp->heading_font->ascent * 2))
401     vspacing = (sp->heading_font->ascent * 2);
402             
403   gcv.foreground = sp->foreground;
404   gc1 = XCreateGC (si->dpy, si->splash_dialog, GCForeground, &gcv);
405   gc2 = XCreateGC (si->dpy, si->splash_dialog, GCForeground, &gcv);
406   x1 = sp->logo_width;
407   x3 = sp->width - (sp->shadow_width * 2);
408   y1 = sp->internal_border;
409
410   /* top heading
411    */
412   XSetFont (si->dpy, gc1, sp->heading_font->fid);
413   sw = string_width (sp->heading_font, sp->heading_label);
414   x2 = (x1 + ((x3 - x1 - sw) / 2));
415   y1 += sp->heading_font->ascent;
416   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1,
417                sp->heading_label, strlen(sp->heading_label));
418   y1 += sp->heading_font->descent;
419
420   /* text below top heading
421    */
422   XSetFont (si->dpy, gc1, sp->body_font->fid);
423   y1 += vspacing + sp->body_font->ascent;
424   sw = string_width (sp->body_font, sp->body_label);
425   x2 = (x1 + ((x3 - x1 - sw) / 2));
426   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1,
427                sp->body_label, strlen(sp->body_label));
428   y1 += sp->body_font->descent;
429
430   y1 += sp->body_font->ascent;
431   sw = string_width (sp->body_font, sp->body2_label);
432   x2 = (x1 + ((x3 - x1 - sw) / 2));
433   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1,
434                sp->body2_label, strlen(sp->body2_label));
435   y1 += sp->body_font->descent;
436
437   /* The buttons
438    */
439   XSetForeground (si->dpy, gc1, sp->button_foreground);
440   XSetForeground (si->dpy, gc2, sp->button_background);
441
442 /*  y1 += (vspacing * 2);*/
443   y1 = sp->height - sp->internal_border - sp->button_height;
444
445   x1 += sp->internal_border;
446   y2 = (y1 + ((sp->button_height -
447                (sp->button_font->ascent + sp->button_font->descent))
448               / 2)
449         + sp->button_font->ascent);
450   hspacing = ((sp->width - x1 - (sp->shadow_width * 2) -
451                sp->internal_border - (sp->button_width * 3))
452               / 2);
453
454   x2 = x1 + ((sp->button_width - string_width(sp->button_font, sp->demo_label))
455              / 2);
456   XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1,
457                   sp->button_width, sp->button_height);
458   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2,
459                sp->demo_label, strlen(sp->demo_label));
460   sp->demo_button_x = x1;
461   sp->demo_button_y = y1;
462   
463   x1 += hspacing + sp->button_width;
464   x2 = x1 + ((sp->button_width - string_width(sp->button_font,sp->prefs_label))
465              / 2);
466   XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1,
467                   sp->button_width, sp->button_height);
468   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2,
469                sp->prefs_label, strlen(sp->prefs_label));
470   sp->prefs_button_x = x1;
471   sp->prefs_button_y = y1;
472
473   x1 += hspacing + sp->button_width;
474   x2 = x1 + ((sp->button_width - string_width(sp->button_font,sp->help_label))
475              / 2);
476   XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1,
477                   sp->button_width, sp->button_height);
478   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2,
479                sp->help_label, strlen(sp->help_label));
480   sp->help_button_x = x1;
481   sp->help_button_y = y1;
482
483
484   /* The logo
485    */
486   x1 = sp->shadow_width * 3;
487   y1 = sp->shadow_width * 3;
488   x2 = sp->logo_width - (sp->shadow_width * 6);
489   y2 = sp->logo_height - (sp->shadow_width * 6);
490
491   if (sp->logo_pixmap)
492     {
493       Window root;
494       int x, y;
495       unsigned int w, h, bw, d;
496       XGetGeometry (si->dpy, sp->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
497       XSetForeground (si->dpy, gc1, sp->foreground);
498       XSetBackground (si->dpy, gc1, sp->background);
499       if (d == 1)
500         XCopyPlane (si->dpy, sp->logo_pixmap, si->splash_dialog, gc1,
501                     0, 0, w, h,
502                     x1 + ((x2 - (int)w) / 2),
503                     y1 + ((y2 - (int)h) / 2),
504                     1);
505       else
506         XCopyArea (si->dpy, sp->logo_pixmap, si->splash_dialog, gc1,
507                    0, 0, w, h,
508                    x1 + ((x2 - (int)w) / 2),
509                    y1 + ((y2 - (int)h) / 2));
510     }
511
512   /* Solid border inside the logo box. */
513   XSetForeground (si->dpy, gc1, sp->foreground);
514   XDrawRectangle (si->dpy, si->splash_dialog, gc1, x1, y1, x2-1, y2-1);
515
516   /* The shadow around the logo
517    */
518   draw_shaded_rectangle (si->dpy, si->splash_dialog,
519                          sp->shadow_width * 2,
520                          sp->shadow_width * 2,
521                          sp->logo_width - (sp->shadow_width * 4),
522                          sp->logo_height - (sp->shadow_width * 4),
523                          sp->shadow_width,
524                          sp->shadow_bottom, sp->shadow_top);
525
526   /* The shadow around the whole window
527    */
528   draw_shaded_rectangle (si->dpy, si->splash_dialog,
529                          0, 0, sp->width, sp->height, sp->shadow_width,
530                          sp->shadow_top, sp->shadow_bottom);
531
532   XFreeGC (si->dpy, gc1);
533   XFreeGC (si->dpy, gc2);
534
535   update_splash_window (si);
536 }
537
538
539 static void
540 update_splash_window (saver_info *si)
541 {
542   splash_dialog_data *sp = si->sp_data;
543   int pressed;
544   if (!sp) return;
545   pressed = sp->pressed;
546
547   /* The shadows around the buttons
548    */
549   draw_shaded_rectangle (si->dpy, si->splash_dialog,
550                          sp->demo_button_x, sp->demo_button_y,
551                          sp->button_width, sp->button_height, sp->shadow_width,
552                          (pressed == 1 ? sp->shadow_bottom : sp->shadow_top),
553                          (pressed == 1 ? sp->shadow_top : sp->shadow_bottom));
554   draw_shaded_rectangle (si->dpy, si->splash_dialog,
555                          sp->prefs_button_x, sp->prefs_button_y,
556                          sp->button_width, sp->button_height, sp->shadow_width,
557                          (pressed == 2 ? sp->shadow_bottom : sp->shadow_top),
558                          (pressed == 2 ? sp->shadow_top : sp->shadow_bottom));
559   draw_shaded_rectangle (si->dpy, si->splash_dialog,
560                          sp->help_button_x, sp->help_button_y,
561                          sp->button_width, sp->button_height, sp->shadow_width,
562                          (pressed == 3 ? sp->shadow_bottom : sp->shadow_top),
563                          (pressed == 3 ? sp->shadow_top : sp->shadow_bottom));
564 }
565
566 static void
567 destroy_splash_window (saver_info *si)
568 {
569   splash_dialog_data *sp = si->sp_data;
570   Screen *screen = si->default_screen->screen;
571   Colormap cmap = DefaultColormapOfScreen (screen);
572   Pixel black = BlackPixelOfScreen (screen);
573   Pixel white = WhitePixelOfScreen (screen);
574
575   if (sp->timer)
576     XtRemoveTimeOut (sp->timer);
577
578   if (si->splash_dialog)
579     {
580       XDestroyWindow (si->dpy, si->splash_dialog);
581       si->splash_dialog = 0;
582     }
583   
584   if (sp->heading_label) free (sp->heading_label);
585   if (sp->body_label)    free (sp->body_label);
586   if (sp->demo_label)    free (sp->demo_label);
587   if (sp->prefs_label)   free (sp->prefs_label);
588   if (sp->help_label)    free (sp->help_label);
589
590   if (sp->heading_font) XFreeFont (si->dpy, sp->heading_font);
591   if (sp->body_font)    XFreeFont (si->dpy, sp->body_font);
592   if (sp->button_font)  XFreeFont (si->dpy, sp->button_font);
593
594   if (sp->foreground != black && sp->foreground != white)
595     XFreeColors (si->dpy, cmap, &sp->foreground, 1, 0L);
596   if (sp->background != black && sp->background != white)
597     XFreeColors (si->dpy, cmap, &sp->background, 1, 0L);
598   if (sp->button_foreground != black && sp->button_foreground != white)
599     XFreeColors (si->dpy, cmap, &sp->button_foreground, 1, 0L);
600   if (sp->button_background != black && sp->button_background != white)
601     XFreeColors (si->dpy, cmap, &sp->button_background, 1, 0L);
602   if (sp->shadow_top != black && sp->shadow_top != white)
603     XFreeColors (si->dpy, cmap, &sp->shadow_top, 1, 0L);
604   if (sp->shadow_bottom != black && sp->shadow_bottom != white)
605     XFreeColors (si->dpy, cmap, &sp->shadow_bottom, 1, 0L);
606
607   if (sp->logo_pixmap)
608     XFreePixmap (si->dpy, sp->logo_pixmap);
609   if (sp->logo_npixels && sp->logo_pixels)
610     XFreeColors (si->dpy, cmap, sp->logo_pixels, sp->logo_npixels, 0L);
611   if (sp->logo_pixels)
612     free (sp->logo_pixels);
613
614   memset (sp, 0, sizeof(*sp));
615   free (sp);
616
617   si->sp_data = 0;
618 }
619
620 void
621 handle_splash_event (saver_info *si, XEvent *event)
622 {
623   splash_dialog_data *sp = si->sp_data;
624   int which = 0;
625   if (!sp) return;
626
627   switch (event->xany.type)
628     {
629     case Expose:
630       draw_splash_window (si);
631       break;
632
633     case ButtonPress: case ButtonRelease:
634
635       if (event->xbutton.x >= sp->demo_button_x &&
636           event->xbutton.x < sp->demo_button_x + sp->button_width &&
637           event->xbutton.y >= sp->demo_button_y &&
638           event->xbutton.y < sp->demo_button_y + sp->button_height)
639         which = 1;
640
641       else if (event->xbutton.x >= sp->prefs_button_x &&
642                event->xbutton.x < sp->prefs_button_x + sp->button_width &&
643                event->xbutton.y >= sp->prefs_button_y &&
644                event->xbutton.y < sp->prefs_button_y + sp->button_height)
645         which = 2;
646
647       else if (event->xbutton.x >= sp->help_button_x &&
648                event->xbutton.x < sp->help_button_x + sp->button_width &&
649                event->xbutton.y >= sp->help_button_y &&
650                event->xbutton.y < sp->help_button_y + sp->button_height)
651         which = 3;
652
653       if (event->xany.type == ButtonPress)
654         {
655           sp->pressed = which;
656           update_splash_window (si);
657           if (which == 0)
658             XBell (si->dpy, False);
659         }
660       else if (event->xany.type == ButtonRelease)
661         {
662           if (which && sp->pressed == which)
663             {
664               destroy_splash_window (si);
665               switch (which)
666                 {
667                 case 1: do_demo (si); break;
668                 case 2: do_prefs (si); break;
669                 case 3: do_help (si); break;
670                 default: abort();
671                 }
672             }
673           sp->pressed = 0;
674           update_splash_window (si);
675         }
676       break;
677
678     default:
679       break;
680     }
681 }
682
683 static void
684 unsplash_timer (XtPointer closure, XtIntervalId *id)
685 {
686   saver_info *si = (saver_info *) closure;
687   if (si && si->sp_data)
688     destroy_splash_window (si);
689 }
690
691 \f
692 /* Button callbacks */
693
694 #ifdef VMS
695 # define pid_t int
696 # define fork  vfork
697 #endif /* VMS */
698
699 static void
700 fork_and_exec (saver_info *si, const char *command, const char *desc)
701 {
702   saver_preferences *p = &si->prefs;
703   pid_t forked;
704   char buf [512];
705   char *av[5];
706   int ac;
707
708   if (!command || !*command)
709     {
710       fprintf (stderr, "%s: no %s command has been specified.\n",
711                blurb(), desc);
712       return;
713     }
714
715   switch ((int) (forked = fork ()))
716     {
717     case -1:
718       sprintf (buf, "%s: couldn't fork", blurb());
719       perror (buf);
720       break;
721
722     case 0:
723       close (ConnectionNumber (si->dpy));               /* close display fd */
724       hack_subproc_environment (si->default_screen);    /* set $DISPLAY */
725       ac = 0;
726       av [ac++] = (char *) p->shell;
727       av [ac++] = (char *) "-c";
728       av [ac++] = (char *) command;
729       av [ac]   = 0;
730       execvp (av[0], av);                               /* shouldn't return. */
731
732       sprintf (buf, "%s: execvp(\"%s\", \"%s\", \"%s\") failed",
733                blurb(), av[0], av[1], av[2]);
734       perror (buf);
735       fflush (stderr);
736       fflush (stdout);
737       exit (1);                  /* Note that this only exits a child fork.  */
738       break;
739
740     default:
741       /* parent fork. */
742       break;
743     }
744 }
745
746
747 static void
748 do_demo (saver_info *si)
749 {
750   saver_preferences *p = &si->prefs;
751   fork_and_exec (si, p->demo_command, "demo-mode");
752 }
753
754 static void
755 do_prefs (saver_info *si)
756 {
757   saver_preferences *p = &si->prefs;
758   fork_and_exec (si, p->prefs_command, "preferences");
759 }
760
761 static void
762 do_help (saver_info *si)
763 {
764   saver_preferences *p = &si->prefs;
765   char *help_command;
766
767   if (!p->help_url || !*p->help_url)
768     {
769       fprintf (stderr, "%s: no Help URL has been specified.\n", blurb());
770       return;
771     }
772
773   help_command = (char *) malloc (strlen (p->load_url_command) +
774                                   (strlen (p->help_url) * 2) + 10);
775   sprintf (help_command, p->load_url_command, p->help_url, p->help_url);
776   fork_and_exec (si, help_command, "URL-loading");
777   free (help_command);
778 }