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