From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / driver / splash.c
1 /* xscreensaver, Copyright (c) 1991-2017 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   return XTextWidth(font, s, strlen(s));
91 }
92
93
94 static void update_splash_window (saver_info *si);
95 static void draw_splash_window (saver_info *si);
96 static void destroy_splash_window (saver_info *si);
97 static void unsplash_timer (XtPointer closure, XtIntervalId *id);
98
99 static void do_demo (saver_screen_info *ssi);
100 #ifdef PREFS_BUTTON
101 static void do_prefs (saver_screen_info *ssi);
102 #endif /* PREFS_BUTTON */
103 static void do_help (saver_screen_info *ssi);
104
105
106 XFontStruct *
107 splash_load_font (Display *dpy, char *name, char *class)
108 {
109   /* Try RES, RES2, etc. */
110   int i;
111   for (i = 0; i < 4; i++)
112     {
113       char *name2 = (char *) malloc (strlen(name) + 2);
114       char *s;
115       if (i)
116         sprintf (name2, "%s%d", name, i+1);
117       else
118         strcpy (name2, name);
119       s = get_string_resource (dpy, name2, class);
120       free (name2);
121       if (s && *s)
122         {
123           XFontStruct *f = XLoadQueryFont (dpy, s);
124           if (f) return f;
125         }
126     }
127   return XLoadQueryFont (dpy, "fixed");
128 }
129
130
131 struct splash_dialog_data {
132
133   saver_screen_info *prompt_screen;
134   XtIntervalId timer;
135
136   Dimension width;
137   Dimension height;
138
139   char *heading_label;
140   char *body_label;
141   char *body2_label;
142   char *body3_label;
143   char *body4_label;
144   char *demo_label;
145 #ifdef PREFS_BUTTON
146   char *prefs_label;
147 #endif /* PREFS_BUTTON */
148   char *help_label;
149
150   XFontStruct *heading_font;
151   XFontStruct *body_font;
152   XFontStruct *button_font;
153
154   Pixel foreground;
155   Pixel background;
156   Pixel border;
157   Pixel button_foreground;
158   Pixel button_background;
159   Pixel shadow_top;
160   Pixel shadow_bottom;
161
162   Dimension logo_width;
163   Dimension logo_height;
164   Dimension internal_border;
165   Dimension shadow_width;
166
167   Dimension button_width, button_height;
168   Dimension demo_button_x, demo_button_y;
169 #ifdef PREFS_BUTTON
170   Dimension prefs_button_x, prefs_button_y;
171 #endif /* PREFS_BUTTON */
172   Dimension help_button_x, help_button_y;
173
174   Pixmap logo_pixmap;
175   Pixmap logo_clipmask;
176   int logo_npixels;
177   unsigned long *logo_pixels;
178
179   int pressed;
180 };
181
182
183 void
184 make_splash_dialog (saver_info *si)
185 {
186   saver_preferences *p = &si->prefs;
187   int x, y, bw;
188   XSetWindowAttributes attrs;
189   unsigned long attrmask = 0;
190   splash_dialog_data *sp;
191   saver_screen_info *ssi;
192   Colormap cmap;
193
194   Bool whine = decrepit_p ();
195
196   if (whine)
197     {
198       /* If locking is not enabled, make sure they see the message. */
199       if (!p->lock_p)
200         {
201           si->prefs.splash_p = True;
202           if (si->prefs.splash_duration < 5000)
203             si->prefs.splash_duration = 5000;
204         }
205       si->prefs.splash_duration += 3000;
206     }
207
208   if (si->sp_data)
209     return;
210   if (!si->prefs.splash_p ||
211       si->prefs.splash_duration <= 0)
212     return;
213
214   ssi = &si->screens[mouse_screen (si)];
215
216   if (!ssi || !ssi->screen)
217     return;    /* WTF?  Trying to splash while no screens connected? */
218
219   cmap = DefaultColormapOfScreen (ssi->screen);
220
221   sp = (splash_dialog_data *) calloc (1, sizeof(*sp));
222   sp->prompt_screen = ssi;
223
224   sp->heading_label = get_string_resource (si->dpy,
225                                            "splash.heading.label",
226                                            "Dialog.Label.Label");
227   sp->body_label = get_string_resource (si->dpy,
228                                         "splash.body.label",
229                                         "Dialog.Label.Label");
230   sp->body2_label = get_string_resource (si->dpy,
231                                          "splash.body2.label",
232                                          "Dialog.Label.Label");
233   sp->demo_label = get_string_resource (si->dpy,
234                                         "splash.demo.label",
235                                         "Dialog.Button.Label");
236 #ifdef PREFS_BUTTON
237   sp->prefs_label = get_string_resource (si->dpy,
238                                          "splash.prefs.label",
239                                         "Dialog.Button.Label");
240 #endif /* PREFS_BUTTON */
241   sp->help_label = get_string_resource (si->dpy,
242                                         "splash.help.label",
243                                         "Dialog.Button.Label");
244
245
246
247   if (whine)
248     {
249       sp->body3_label = strdup("WARNING: This version is very old!");
250       sp->body4_label = strdup("Please upgrade!");
251     }
252
253   if (!sp->heading_label)
254     sp->heading_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
255   if (!sp->body_label)
256     sp->body_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
257   if (!sp->body2_label)
258     sp->body2_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
259   if (!sp->demo_label) sp->demo_label = strdup("ERROR");
260 #ifdef PREFS_BUTTON
261   if (!sp->prefs_label) sp->prefs_label = strdup("ERROR");
262 #endif /* PREFS_BUTTON */
263   if (!sp->help_label) sp->help_label = strdup("ERROR");
264
265   /* Put the version number in the label. */
266   {
267     char *s = (char *) malloc (strlen(sp->heading_label) + 20);
268     sprintf(s, sp->heading_label, si->version);
269     free (sp->heading_label);
270     sp->heading_label = s;
271   }
272
273   sp->heading_font =
274     splash_load_font (si->dpy, "splash.headingFont", "Dialog.Font");
275   sp->body_font =
276     splash_load_font (si->dpy, "splash.bodyFont", "Dialog.Font");
277   sp->button_font =
278     splash_load_font (si->dpy, "splash.buttonFont", "Dialog.Font");
279
280   sp->foreground = get_pixel_resource (si->dpy, cmap,
281                                        "splash.foreground",
282                                        "Dialog.Foreground");
283   sp->background = get_pixel_resource (si->dpy, cmap, 
284                                        "splash.background",
285                                        "Dialog.Background");
286   sp->border = get_pixel_resource (si->dpy, cmap, 
287                                        "splash.borderColor",
288                                        "Dialog.borderColor");
289
290   if (sp->foreground == sp->background)
291     {
292       /* Make sure the error messages show up. */
293       sp->foreground = BlackPixelOfScreen (ssi->screen);
294       sp->background = WhitePixelOfScreen (ssi->screen);
295     }
296
297   sp->button_foreground = get_pixel_resource (si->dpy, cmap,
298                                               "splash.Button.foreground",
299                                               "Dialog.Button.Foreground");
300   sp->button_background = get_pixel_resource (si->dpy, cmap,
301                                               "splash.Button.background",
302                                               "Dialog.Button.Background");
303   sp->shadow_top = get_pixel_resource (si->dpy, cmap,
304                                        "splash.topShadowColor",
305                                        "Dialog.Foreground");
306   sp->shadow_bottom = get_pixel_resource (si->dpy, cmap,
307                                           "splash.bottomShadowColor",
308                                           "Dialog.Background");
309
310   sp->logo_width = get_integer_resource (si->dpy, 
311                                          "splash.logo.width",
312                                          "Dialog.Logo.Width");
313   sp->logo_height = get_integer_resource (si->dpy, 
314                                           "splash.logo.height",
315                                           "Dialog.Logo.Height");
316   sp->internal_border = get_integer_resource (si->dpy, 
317                                               "splash.internalBorderWidth",
318                                               "Dialog.InternalBorderWidth");
319   sp->shadow_width = get_integer_resource (si->dpy, 
320                                            "splash.shadowThickness",
321                                            "Dialog.ShadowThickness");
322
323   if (sp->logo_width == 0)  sp->logo_width = 150;
324   if (sp->logo_height == 0) sp->logo_height = 150;
325   if (sp->internal_border == 0) sp->internal_border = 15;
326   if (sp->shadow_width == 0) sp->shadow_width = 4;
327
328   {
329     int direction, ascent, descent;
330     XCharStruct overall;
331
332     sp->width = 0;
333     sp->height = 0;
334
335     /* Measure the heading_label. */
336     XTextExtents (sp->heading_font,
337                   sp->heading_label, strlen(sp->heading_label),
338                   &direction, &ascent, &descent, &overall);
339     if (overall.width > sp->width) sp->width = overall.width;
340     sp->height += ascent + descent;
341
342     /* Measure the body_label. */
343     XTextExtents (sp->body_font,
344                   sp->body_label, strlen(sp->body_label),
345                   &direction, &ascent, &descent, &overall);
346     if (overall.width > sp->width) sp->width = overall.width;
347     sp->height += ascent + descent;
348
349     /* Measure the body2_label. */
350     XTextExtents (sp->body_font,
351                   sp->body2_label, strlen(sp->body2_label),
352                   &direction, &ascent, &descent, &overall);
353     if (overall.width > sp->width) sp->width = overall.width;
354     sp->height += ascent + descent;
355
356     /* Measure the optional body3_label. */
357     if (sp->body3_label)
358       {
359         XTextExtents (sp->heading_font,
360                       sp->body3_label, strlen(sp->body3_label),
361                       &direction, &ascent, &descent, &overall);
362         if (overall.width > sp->width) sp->width = overall.width;
363         XTextExtents (sp->heading_font,
364                       sp->body4_label, strlen(sp->body4_label),
365                       &direction, &ascent, &descent, &overall);
366         if (overall.width > sp->width) sp->width = overall.width;
367         sp->height += (ascent + descent) * 5;
368       }
369
370     {
371       Dimension w2 = 0, w3 = 0, w4 = 0;
372       Dimension h2 = 0, h3 = 0, h4 = 0;
373
374       /* Measure the Demo button. */
375       XTextExtents (sp->button_font,
376                     sp->demo_label, strlen(sp->demo_label),
377                     &direction, &ascent, &descent, &overall);
378       w2 = overall.width;
379       h2 = ascent + descent;
380
381 #ifdef PREFS_BUTTON
382       /* Measure the Prefs button. */
383       XTextExtents (sp->button_font,
384                     sp->prefs_label, strlen(sp->prefs_label),
385                     &direction, &ascent, &descent, &overall);
386       w3 = overall.width;
387       h3 = ascent + descent;
388 #else  /* !PREFS_BUTTON */
389       w3 = 0;
390       h3 = 0;
391 #endif /* !PREFS_BUTTON */
392
393       /* Measure the Help button. */
394       XTextExtents (sp->button_font,
395                     sp->help_label, strlen(sp->help_label),
396                     &direction, &ascent, &descent, &overall);
397       w4 = overall.width;
398       h4 = ascent + descent;
399
400       w2 = MAX(w2, w3); w2 = MAX(w2, w4);
401       h2 = MAX(h2, h3); h2 = MAX(h2, h4);
402
403       /* Add some horizontal padding inside the buttons. */
404       w2 += ascent;
405
406       w2 += ((ascent + descent) / 2) + (sp->shadow_width * 2);
407       h2 += ((ascent + descent) / 2) + (sp->shadow_width * 2);
408
409       sp->button_width = w2;
410       sp->button_height = h2;
411
412 #ifdef PREFS_BUTTON
413       w2 *= 3;
414 #else  /* !PREFS_BUTTON */
415       w2 *= 2;
416 #endif /* !PREFS_BUTTON */
417
418       w2 += ((ascent + descent) * 2);  /* for space between buttons */
419
420       if (w2 > sp->width) sp->width = w2;
421       sp->height += h2;
422     }
423
424     sp->width  += (sp->internal_border * 2);
425     sp->height += (sp->internal_border * 3);
426
427     if (sp->logo_height > sp->height)
428       sp->height = sp->logo_height;
429     else if (sp->height > sp->logo_height)
430       sp->logo_height = sp->height;
431
432     sp->logo_width = sp->logo_height;
433
434     sp->width += sp->logo_width;
435   }
436
437   attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
438   attrmask |= CWEventMask;
439   attrs.event_mask = (ExposureMask | ButtonPressMask | ButtonReleaseMask);
440
441   {
442     int sx = 0, sy = 0, w, h;
443
444     x = ssi->x;
445     y = ssi->y;
446     w = ssi->width;
447     h = ssi->height;
448     if (si->prefs.debug_p) w /= 2;
449     x = sx + (((w + sp->width)  / 2) - sp->width);
450     y = sy + (((h + sp->height) / 2) - sp->height);
451     if (x < sx) x = sx;
452     if (y < sy) y = sy;
453   }
454
455   bw = get_integer_resource (si->dpy, 
456                              "splash.borderWidth",
457                              "Dialog.BorderWidth");
458
459   si->splash_dialog =
460     XCreateWindow (si->dpy,
461                    RootWindowOfScreen(ssi->screen),
462                    x, y, sp->width, sp->height, bw,
463                    DefaultDepthOfScreen (ssi->screen), InputOutput,
464                    DefaultVisualOfScreen(ssi->screen),
465                    attrmask, &attrs);
466   XSetWindowBackground (si->dpy, si->splash_dialog, sp->background);
467   XSetWindowBorder (si->dpy, si->splash_dialog, sp->border);
468
469
470   sp->logo_pixmap = xscreensaver_logo (ssi->screen, 
471                                        /* same visual as si->splash_dialog */
472                                        DefaultVisualOfScreen (ssi->screen),
473                                        si->splash_dialog, cmap,
474                                        sp->background, 
475                                        &sp->logo_pixels, &sp->logo_npixels,
476                                        &sp->logo_clipmask, True);
477
478   XMapRaised (si->dpy, si->splash_dialog);
479   XSync (si->dpy, False);
480
481   si->sp_data = sp;
482
483   sp->timer = XtAppAddTimeOut (si->app, si->prefs.splash_duration,
484                                unsplash_timer, (XtPointer) si);
485
486   draw_splash_window (si);
487   XSync (si->dpy, False);
488 }
489
490
491 static void
492 draw_splash_window (saver_info *si)
493 {
494   splash_dialog_data *sp = si->sp_data;
495   XGCValues gcv;
496   GC gc1, gc2;
497   int vspacing, height;
498   int x1, x2, x3, y1, y2;
499   int sw;
500
501 #ifdef PREFS_BUTTON
502   int hspacing;
503   int nbuttons = 3;
504 #endif /* !PREFS_BUTTON */
505
506   height = (sp->heading_font->ascent + sp->heading_font->descent +
507             sp->body_font->ascent + sp->body_font->descent +
508             sp->body_font->ascent + sp->body_font->descent +
509             sp->button_font->ascent + sp->button_font->descent);
510   vspacing = ((sp->height
511                - (4 * sp->shadow_width)
512                - (2 * sp->internal_border)
513                - height) / 5);
514   if (vspacing < 0) vspacing = 0;
515   if (vspacing > (sp->heading_font->ascent * 2))
516     vspacing = (sp->heading_font->ascent * 2);
517             
518   gcv.foreground = sp->foreground;
519   gc1 = XCreateGC (si->dpy, si->splash_dialog, GCForeground, &gcv);
520   gc2 = XCreateGC (si->dpy, si->splash_dialog, GCForeground, &gcv);
521   x1 = sp->logo_width;
522   x3 = sp->width - (sp->shadow_width * 2);
523   y1 = sp->internal_border;
524
525   /* top heading
526    */
527   XSetFont (si->dpy, gc1, sp->heading_font->fid);
528   sw = string_width (sp->heading_font, sp->heading_label);
529   x2 = (x1 + ((x3 - x1 - sw) / 2));
530   y1 += sp->heading_font->ascent;
531   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1,
532                sp->heading_label, strlen(sp->heading_label));
533   y1 += sp->heading_font->descent;
534
535   /* text below top heading
536    */
537   XSetFont (si->dpy, gc1, sp->body_font->fid);
538   y1 += vspacing + sp->body_font->ascent;
539   sw = string_width (sp->body_font, sp->body_label);
540   x2 = (x1 + ((x3 - x1 - sw) / 2));
541   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1,
542                sp->body_label, strlen(sp->body_label));
543   y1 += sp->body_font->descent;
544
545   y1 += sp->body_font->ascent;
546   sw = string_width (sp->body_font, sp->body2_label);
547   x2 = (x1 + ((x3 - x1 - sw) / 2));
548   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1,
549                sp->body2_label, strlen(sp->body2_label));
550   y1 += sp->body_font->descent;
551
552   if (sp->body3_label)
553     {
554       XSetFont (si->dpy, gc1, sp->heading_font->fid);
555       y1 += sp->heading_font->ascent + sp->heading_font->descent;
556       y1 += sp->heading_font->ascent;
557       sw = string_width (sp->heading_font, sp->body3_label);
558       x2 = (x1 + ((x3 - x1 - sw) / 2));
559       XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1,
560                    sp->body3_label, strlen(sp->body3_label));
561       y1 += sp->heading_font->descent + sp->heading_font->ascent;
562       sw = string_width (sp->heading_font, sp->body4_label);
563       x2 = (x1 + ((x3 - x1 - sw) / 2));
564       XDrawString (si->dpy, si->splash_dialog, gc1, x2, y1,
565                    sp->body4_label, strlen(sp->body4_label));
566       y1 += sp->heading_font->descent;
567       XSetFont (si->dpy, gc1, sp->body_font->fid);
568     }
569
570   /* The buttons
571    */
572   XSetForeground (si->dpy, gc1, sp->button_foreground);
573   XSetForeground (si->dpy, gc2, sp->button_background);
574
575 /*  y1 += (vspacing * 2);*/
576   y1 = sp->height - sp->internal_border - sp->button_height;
577
578   x1 += sp->internal_border;
579   y2 = (y1 + ((sp->button_height -
580                (sp->button_font->ascent + sp->button_font->descent))
581               / 2)
582         + sp->button_font->ascent);
583 #ifdef PREFS_BUTTON
584   hspacing = ((sp->width - x1 - (sp->shadow_width * 2) -
585                sp->internal_border - (sp->button_width * nbuttons))
586               / 2);
587 #endif
588
589   x2 = x1 + ((sp->button_width - string_width(sp->button_font, sp->demo_label))
590              / 2);
591   XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1,
592                   sp->button_width, sp->button_height);
593   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2,
594                sp->demo_label, strlen(sp->demo_label));
595   sp->demo_button_x = x1;
596   sp->demo_button_y = y1;
597   
598 #ifdef PREFS_BUTTON
599   x1 += hspacing + sp->button_width;
600   x2 = x1 + ((sp->button_width - string_width(sp->button_font,sp->prefs_label))
601              / 2);
602   XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1,
603                   sp->button_width, sp->button_height);
604   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2,
605                sp->prefs_label, strlen(sp->prefs_label));
606   sp->prefs_button_x = x1;
607   sp->prefs_button_y = y1;
608 #endif /* PREFS_BUTTON */
609
610 #ifdef PREFS_BUTTON
611   x1 += hspacing + sp->button_width;
612 #else  /* !PREFS_BUTTON */
613   x1 = (sp->width - sp->button_width -
614         sp->internal_border - (sp->shadow_width * 2));
615 #endif /* !PREFS_BUTTON */
616
617   x2 = x1 + ((sp->button_width - string_width(sp->button_font,sp->help_label))
618              / 2);
619   XFillRectangle (si->dpy, si->splash_dialog, gc2, x1, y1,
620                   sp->button_width, sp->button_height);
621   XDrawString (si->dpy, si->splash_dialog, gc1, x2, y2,
622                sp->help_label, strlen(sp->help_label));
623   sp->help_button_x = x1;
624   sp->help_button_y = y1;
625
626
627   /* The logo
628    */
629   x1 = sp->shadow_width * 6;
630   y1 = sp->shadow_width * 6;
631   x2 = sp->logo_width - (sp->shadow_width * 12);
632   y2 = sp->logo_height - (sp->shadow_width * 12);
633
634   if (sp->logo_pixmap)
635     {
636       Window root;
637       int x, y;
638       unsigned int w, h, bw, d;
639       XGetGeometry (si->dpy, sp->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
640       XSetForeground (si->dpy, gc1, sp->foreground);
641       XSetBackground (si->dpy, gc1, sp->background);
642       XSetClipMask (si->dpy, gc1, sp->logo_clipmask);
643       XSetClipOrigin (si->dpy, gc1, x1 + ((x2 - (int)w) /2), y1 + ((y2 - (int)h) / 2));
644       if (d == 1)
645         XCopyPlane (si->dpy, sp->logo_pixmap, si->splash_dialog, gc1,
646                     0, 0, w, h,
647                     x1 + ((x2 - (int)w) / 2),
648                     y1 + ((y2 - (int)h) / 2),
649                     1);
650       else
651         XCopyArea (si->dpy, sp->logo_pixmap, si->splash_dialog, gc1,
652                    0, 0, w, h,
653                    x1 + ((x2 - (int)w) / 2),
654                    y1 + ((y2 - (int)h) / 2));
655     }
656
657   /* Solid border inside the logo box. */
658 #if 0
659   XSetForeground (si->dpy, gc1, sp->foreground);
660   XDrawRectangle (si->dpy, si->splash_dialog, gc1, x1, y1, x2-1, y2-1);
661 #endif
662
663   /* The shadow around the logo
664    */
665   draw_shaded_rectangle (si->dpy, si->splash_dialog,
666                          sp->shadow_width * 4,
667                          sp->shadow_width * 4,
668                          sp->logo_width - (sp->shadow_width * 8),
669                          sp->logo_height - (sp->shadow_width * 8),
670                          sp->shadow_width,
671                          sp->shadow_bottom, sp->shadow_top);
672
673   /* The shadow around the whole window
674    */
675   draw_shaded_rectangle (si->dpy, si->splash_dialog,
676                          0, 0, sp->width, sp->height, sp->shadow_width,
677                          sp->shadow_top, sp->shadow_bottom);
678
679   XFreeGC (si->dpy, gc1);
680   XFreeGC (si->dpy, gc2);
681
682   update_splash_window (si);
683 }
684
685
686 static void
687 update_splash_window (saver_info *si)
688 {
689   splash_dialog_data *sp = si->sp_data;
690   int pressed;
691   if (!sp) return;
692   pressed = sp->pressed;
693
694   /* The shadows around the buttons
695    */
696   draw_shaded_rectangle (si->dpy, si->splash_dialog,
697                          sp->demo_button_x, sp->demo_button_y,
698                          sp->button_width, sp->button_height, sp->shadow_width,
699                          (pressed == 1 ? sp->shadow_bottom : sp->shadow_top),
700                          (pressed == 1 ? sp->shadow_top : sp->shadow_bottom));
701 #ifdef PREFS_BUTTON
702   draw_shaded_rectangle (si->dpy, si->splash_dialog,
703                          sp->prefs_button_x, sp->prefs_button_y,
704                          sp->button_width, sp->button_height, sp->shadow_width,
705                          (pressed == 2 ? sp->shadow_bottom : sp->shadow_top),
706                          (pressed == 2 ? sp->shadow_top : sp->shadow_bottom));
707 #endif /* PREFS_BUTTON */
708   draw_shaded_rectangle (si->dpy, si->splash_dialog,
709                          sp->help_button_x, sp->help_button_y,
710                          sp->button_width, sp->button_height, sp->shadow_width,
711                          (pressed == 3 ? sp->shadow_bottom : sp->shadow_top),
712                          (pressed == 3 ? sp->shadow_top : sp->shadow_bottom));
713 }
714
715 static void
716 destroy_splash_window (saver_info *si)
717 {
718   splash_dialog_data *sp = si->sp_data;
719   saver_screen_info *ssi = sp->prompt_screen;
720   Colormap cmap = DefaultColormapOfScreen (ssi->screen);
721   Pixel black = BlackPixelOfScreen (ssi->screen);
722   Pixel white = WhitePixelOfScreen (ssi->screen);
723
724   if (sp->timer)
725     XtRemoveTimeOut (sp->timer);
726
727   if (si->splash_dialog)
728     {
729       XDestroyWindow (si->dpy, si->splash_dialog);
730       si->splash_dialog = 0;
731     }
732   
733   if (sp->heading_label) free (sp->heading_label);
734   if (sp->body_label)    free (sp->body_label);
735   if (sp->body2_label)   free (sp->body2_label);
736   if (sp->body3_label)   free (sp->body3_label);
737   if (sp->body4_label)   free (sp->body4_label);
738   if (sp->demo_label)    free (sp->demo_label);
739 #ifdef PREFS_BUTTON
740   if (sp->prefs_label)   free (sp->prefs_label);
741 #endif /* PREFS_BUTTON */
742   if (sp->help_label)    free (sp->help_label);
743
744   if (sp->heading_font) XFreeFont (si->dpy, sp->heading_font);
745   if (sp->body_font)    XFreeFont (si->dpy, sp->body_font);
746   if (sp->button_font)  XFreeFont (si->dpy, sp->button_font);
747
748   if (sp->foreground != black && sp->foreground != white)
749     XFreeColors (si->dpy, cmap, &sp->foreground, 1, 0L);
750   if (sp->background != black && sp->background != white)
751     XFreeColors (si->dpy, cmap, &sp->background, 1, 0L);
752   if (sp->button_foreground != black && sp->button_foreground != white)
753     XFreeColors (si->dpy, cmap, &sp->button_foreground, 1, 0L);
754   if (sp->button_background != black && sp->button_background != white)
755     XFreeColors (si->dpy, cmap, &sp->button_background, 1, 0L);
756   if (sp->shadow_top != black && sp->shadow_top != white)
757     XFreeColors (si->dpy, cmap, &sp->shadow_top, 1, 0L);
758   if (sp->shadow_bottom != black && sp->shadow_bottom != white)
759     XFreeColors (si->dpy, cmap, &sp->shadow_bottom, 1, 0L);
760
761   if (sp->logo_pixmap)
762     XFreePixmap (si->dpy, sp->logo_pixmap);
763   if (sp->logo_clipmask)
764     XFreePixmap (si->dpy, sp->logo_clipmask);
765   if (sp->logo_pixels)
766     {
767       if (sp->logo_npixels)
768         XFreeColors (si->dpy, cmap, sp->logo_pixels, sp->logo_npixels, 0L);
769       free (sp->logo_pixels);
770       sp->logo_pixels = 0;
771       sp->logo_npixels = 0;
772     }
773
774   memset (sp, 0, sizeof(*sp));
775   free (sp);
776   si->sp_data = 0;
777 }
778
779 void
780 handle_splash_event (saver_info *si, XEvent *event)
781 {
782   splash_dialog_data *sp = si->sp_data;
783   saver_screen_info *ssi;
784   int which = 0;
785   if (!sp) return;
786   ssi = sp->prompt_screen;
787
788   switch (event->xany.type)
789     {
790     case Expose:
791       draw_splash_window (si);
792       break;
793
794     case ButtonPress: case ButtonRelease:
795
796       if (event->xbutton.x >= sp->demo_button_x &&
797           event->xbutton.x < sp->demo_button_x + sp->button_width &&
798           event->xbutton.y >= sp->demo_button_y &&
799           event->xbutton.y < sp->demo_button_y + sp->button_height)
800         which = 1;
801
802 #ifdef PREFS_BUTTON
803       else if (event->xbutton.x >= sp->prefs_button_x &&
804                event->xbutton.x < sp->prefs_button_x + sp->button_width &&
805                event->xbutton.y >= sp->prefs_button_y &&
806                event->xbutton.y < sp->prefs_button_y + sp->button_height)
807         which = 2;
808 #endif /* PREFS_BUTTON */
809
810       else if (event->xbutton.x >= sp->help_button_x &&
811                event->xbutton.x < sp->help_button_x + sp->button_width &&
812                event->xbutton.y >= sp->help_button_y &&
813                event->xbutton.y < sp->help_button_y + sp->button_height)
814         which = 3;
815
816       if (event->xany.type == ButtonPress)
817         {
818           sp->pressed = which;
819           update_splash_window (si);
820           if (which == 0)
821             XBell (si->dpy, False);
822         }
823       else if (event->xany.type == ButtonRelease)
824         {
825           if (which && sp->pressed == which)
826             {
827               destroy_splash_window (si);
828               sp = si->sp_data;
829               switch (which)
830                 {
831                 case 1: do_demo (ssi); break;
832 #ifdef PREFS_BUTTON
833                 case 2: do_prefs (ssi); break;
834 #endif /* PREFS_BUTTON */
835                 case 3: do_help (ssi); break;
836                 default: abort();
837                 }
838             }
839           else if (which == 0 && sp->pressed == 0)
840             {
841               /* click and release on the window but not in a button:
842                  treat that as "dismiss the splash dialog." */
843               destroy_splash_window (si);
844               sp = si->sp_data;
845             }
846           if (sp) sp->pressed = 0;
847           update_splash_window (si);
848         }
849       break;
850
851     default:
852       break;
853     }
854 }
855
856 static void
857 unsplash_timer (XtPointer closure, XtIntervalId *id)
858 {
859   saver_info *si = (saver_info *) closure;
860   if (si && si->sp_data)
861     destroy_splash_window (si);
862 }
863
864 \f
865 /* Button callbacks */
866
867 #ifdef VMS
868 # define pid_t int
869 # define fork  vfork
870 #endif /* VMS */
871
872
873 static void
874 do_demo (saver_screen_info *ssi)
875 {
876   saver_info *si = ssi->global;
877   saver_preferences *p = &si->prefs;
878   const char *cmd = p->demo_command;
879
880   if (cmd && *cmd)
881     fork_and_exec (ssi, cmd);
882   else
883     fprintf (stderr, "%s: no demo-mode command has been specified.\n",
884              blurb());
885 }
886
887 #ifdef PREFS_BUTTON
888 static void
889 do_prefs (saver_screen_info *ssi)
890 {
891   saver_info *si = ssi->global;
892   saver_preferences *p = &si->prefs;
893   const char *cmd = p->prefs_command;
894
895   if (command && *command)
896     fork_and_exec (ssi, cmd);
897   else
898     fprintf (stderr, "%s: no preferences command has been specified.\n",
899              blurb());
900 }
901 #endif /* PREFS_BUTTON */
902
903 static void
904 do_help (saver_screen_info *ssi)
905 {
906   saver_info *si = ssi->global;
907   saver_preferences *p = &si->prefs;
908   char *help_command = 0;
909
910   if (!p->load_url_command || !*p->load_url_command)
911     {
912       fprintf (stderr, "%s: no URL command has been specified.\n", blurb());
913       return;
914     }
915   if (!p->help_url || !*p->help_url)
916     {
917       fprintf (stderr, "%s: no Help URL has been specified.\n", blurb());
918       return;
919     }
920
921   help_command = (char *) malloc (strlen (p->load_url_command) +
922                                   (strlen (p->help_url) * 4) + 10);
923   sprintf (help_command, p->load_url_command,
924            p->help_url, p->help_url, p->help_url, p->help_url);
925
926   fork_and_exec (ssi, help_command);
927   free (help_command);
928 }