http://ftp.x.org/contrib/applications/xscreensaver-2.34.tar.gz
[xscreensaver] / driver / demo.c
1 /* demo.c --- implements the interactive demo-mode and options dialogs.
2  * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <X11/Intrinsic.h>
18 #include <X11/StringDefs.h>
19
20 /* We don't actually use any widget internals, but these are included
21    so that gdb will have debug info for the widgets... */
22 #include <X11/IntrinsicP.h>
23 #include <X11/ShellP.h>
24
25 #ifdef HAVE_MOTIF
26 # include <Xm/Xm.h>
27 # include <Xm/Text.h>
28 # include <Xm/List.h>
29 # include <Xm/ToggleB.h>
30
31 #else  /* HAVE_ATHENA */
32   /* Athena demo code contributed by Jon A. Christopher <jac8782@tamu.edu> */
33   /* Copyright 1997, with the same permissions as above. */
34 # include <X11/Shell.h>
35 # include <X11/Xaw/Form.h>
36 # include <X11/Xaw/Box.h>
37 # include <X11/Xaw/List.h>
38 # include <X11/Xaw/Command.h>
39 # include <X11/Xaw/Toggle.h>
40 # include <X11/Xaw/Viewport.h>
41 # include <X11/Xaw/Dialog.h>
42 # include <X11/Xaw/Scrollbar.h>
43 # include <X11/Xaw/Text.h>
44 #endif /* HAVE_ATHENA */
45
46 #include "xscreensaver.h"
47 #include "resources.h"          /* for parse_time() */
48 #include <stdio.h>
49 #include <string.h>
50 #include <ctype.h>
51
52 #ifdef _VROOT_H_
53 ERROR!  You must not include vroot.h in this file.
54 #endif
55
56 static void demo_mode_hack (saver_info *si, char *);
57 static void demo_mode_done (saver_info *si);
58
59 extern Widget demo_dialog;
60 extern Widget label1;
61 extern Widget text_line;
62 extern Widget demo_form;
63 extern Widget demo_list;
64 extern Widget next, prev, done, restart, edit;
65
66 extern Widget resources_dialog;
67 extern Widget resources_form;
68 extern Widget res_done, res_cancel;
69 extern Widget timeout_text, cycle_text, fade_text, ticks_text;
70 extern Widget lock_time_text, passwd_time_text;
71 extern Widget verbose_toggle, cmap_toggle, fade_toggle, unfade_toggle,
72   lock_toggle;
73
74
75 #ifdef HAVE_MOTIF
76
77 # define set_toggle_button_state(toggle,state) \
78   XmToggleButtonSetState ((toggle), (state), True)
79 # define set_text_string(text_widget,string) \
80   XmTextSetString ((text_widget), (string))
81 # define add_button_callback(button,cb,arg) \
82   XtAddCallback ((button), XmNactivateCallback, (cb), (arg))
83 # define add_toggle_callback(button,cb,arg) \
84   XtAddCallback ((button), XmNvalueChangedCallback, (cb), (arg))
85 # define add_text_callback add_toggle_callback
86
87 #else  /* HAVE_ATHENA */
88
89 # define set_toggle_button_state(toggle,state) \
90   XtVaSetValues((toggle), XtNstate, (state),  0)
91 # define set_text_string(text_widget,string) \
92   XtVaSetValues ((text_widget), XtNvalue, (string), 0)
93 # define add_button_callback(button,cb,arg) \
94   XtAddCallback ((button), XtNcallback, (cb), (arg))
95 # define add_toggle_callback add_button_callback
96 # define add_text_callback(b,c,a) ERROR!
97
98 #endif /* HAVE_ATHENA */
99
100
101 #define disable_widget(widget) \
102   XtVaSetValues((widget), XtNsensitive, False, 0)
103
104
105 static char *
106 get_text_string (Widget text_widget)
107 {
108 #ifdef HAVE_MOTIF
109   return XmTextGetString (text_widget);
110 #else  /* HAVE_ATHENA */
111   char *string = 0;
112   if (XtIsSubclass(text_widget, textWidgetClass))
113     XtVaGetValues (text_widget, XtNstring, &string, 0);
114   else if (XtIsSubclass(text_widget, dialogWidgetClass))
115     XtVaGetValues (text_widget, XtNvalue, &string, 0);
116   else
117     string = 0;
118
119   return string;
120 #endif /* HAVE_ATHENA */
121 }
122
123 static char *
124 get_label_string (Widget label_widget)
125 {
126 #ifdef HAVE_MOTIF
127   char *label = 0;
128   XmString xm_label = 0;
129   XtVaGetValues (label_widget, XmNlabelString, &xm_label, 0);
130   if (!xm_label)
131     return 0;
132   XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
133   return label;
134 #else  /* HAVE_ATHENA */
135   char *label = 0;
136   XtVaGetValues (label_widget, XtNlabel, &label, 0);
137   return (label ? strdup(label) : 0);
138 #endif /* HAVE_ATHENA */
139 }
140
141
142 static void
143 set_label_string (Widget label_widget, char *string)
144 {
145 #ifdef HAVE_MOTIF
146   XmString xm_string = XmStringCreate (string, XmSTRING_DEFAULT_CHARSET);
147   XtVaSetValues (label_widget, XmNlabelString, xm_string, 0);
148   XmStringFree (xm_string);
149 #else  /* HAVE_ATHENA */
150   XtVaSetValues (label_widget, XtNlabel, string, 0);
151 #endif /* HAVE_ATHENA */
152 }
153
154
155 void
156 format_into_label (Widget label, const char *arg)
157 {
158   char *text = get_label_string (label);
159   char *buf = (char *) malloc ((text ? strlen(text) : 100) + strlen(arg) + 10);
160
161   if (!text || !strcmp (text, XtName (label)))
162       strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
163     else
164       sprintf (buf, text, arg);
165
166     set_label_string (label, buf);
167     free (buf);
168     XtFree (text);
169 }
170
171
172 void
173 steal_focus_and_colormap (Widget dialog)
174 {
175   Display *dpy = XtDisplay (dialog);
176   Window window = XtWindow (dialog);
177   Colormap cmap = 0;
178   XSetInputFocus (dpy, window, RevertToParent, CurrentTime);
179
180   XtVaGetValues (dialog, XtNcolormap, &cmap, 0);
181   if (cmap)
182     XInstallColormap (dpy, cmap);
183 }
184
185 static void
186 raise_screenhack_dialog (void)
187 {
188   XMapRaised (XtDisplay (demo_dialog), XtWindow (demo_dialog));
189   if (resources_dialog)
190     XMapRaised (XtDisplay (resources_dialog), XtWindow (resources_dialog));
191   steal_focus_and_colormap (resources_dialog ? resources_dialog : demo_dialog);
192 }
193
194 static void
195 destroy_screenhack_dialogs (saver_info *si)
196 {
197   saver_screen_info *ssi = si->default_screen;
198
199   if (demo_dialog) XtDestroyWidget (demo_dialog);
200   if (resources_dialog) XtDestroyWidget (resources_dialog);
201   demo_dialog = resources_dialog = 0;
202
203   if (ssi->demo_cmap &&
204       ssi->demo_cmap != ssi->cmap &&
205       ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
206     {
207       XFreeColormap (si->dpy, ssi->demo_cmap);
208       ssi->demo_cmap = 0;
209     }
210
211   /* Since we installed our colormap to display the dialogs properly, put
212      the old one back, so that the screensaver_window is now displayed
213      properly. */
214   if (ssi->cmap)
215     XInstallColormap (si->dpy, ssi->cmap);
216 }
217
218
219 static void
220 text_cb (Widget text_widget, XtPointer client_data, XtPointer call_data)
221 {
222   saver_info *si = (saver_info *) client_data;
223   saver_preferences *p = &si->prefs;
224   char *line;
225   line = get_text_string (text_widget);
226
227   if (p->verbose_p)
228     fprintf (stderr, "%s: processing text \"%s\".\n", blurb(), line);
229
230   demo_mode_hack (si, line);
231 }
232
233
234 #ifdef HAVE_ATHENA
235 /* Bend over backwards to make hitting Return in the text field do the
236    right thing. 
237    */
238 extern saver_info *global_si_kludge;
239 static void text_enter (Widget w, XEvent *event, String *av, Cardinal *ac)
240 {
241   text_cb (w, global_si_kludge, 0);
242 }
243
244 static XtActionsRec actions[] = {{"done",      text_enter}
245                                 };
246 static char translations[] = ("<Key>Return:     done()\n"
247                               "<Key>Linefeed:   done()\n"
248                               "Ctrl<Key>M:      done()\n"
249                               "Ctrl<Key>J:      done()\n");
250 #endif /* HAVE_ATHENA */
251
252
253 static void
254 select_cb (Widget button, XtPointer client_data, XtPointer call_data)
255 {
256   saver_info *si = (saver_info *) client_data;
257
258 #ifdef HAVE_ATHENA
259   XawListReturnStruct *item = (XawListReturnStruct*)call_data;
260   XtVaSetValues(text_line, XtNstring, item->string, 0);
261
262   demo_mode_hack (si, item->string);
263   if (item->list_index >= 0)
264     si->default_screen->current_hack = item->list_index;
265
266 #else  /* HAVE_MOTIF */
267   XmListCallbackStruct *lcb = (XmListCallbackStruct *) call_data;
268   char *string = 0;
269   if (lcb->item)
270     XmStringGetLtoR (lcb->item, XmSTRING_DEFAULT_CHARSET, &string);
271   set_text_string (text_line, (string ? string : ""));
272   if (lcb->reason == XmCR_DEFAULT_ACTION && string)
273     {
274       demo_mode_hack (si, string);
275       if (lcb->item_position > 0)
276         si->default_screen->current_hack = lcb->item_position - 1;
277     }
278   if (string)
279     XtFree (string);
280 #endif /* HAVE_MOTIF */
281   steal_focus_and_colormap (demo_dialog);
282 }
283
284
285 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
286  */
287 static void
288 ensure_selected_item_visible (Widget list)
289 {
290 #ifdef HAVE_MOTIF
291   int *pos_list = 0;
292   int pos_count = 0;
293   if (XmListGetSelectedPos (list, &pos_list, &pos_count) && pos_count > 0)
294     {
295       int top = -2;
296       int visible = 0;
297       XtVaGetValues (list,
298                      XmNtopItemPosition, &top,
299                      XmNvisibleItemCount, &visible,
300                      0);
301       if (pos_list[0] >= top + visible)
302         {
303           int pos = pos_list[0] - visible + 1;
304           if (pos < 0) pos = 0;
305           XmListSetPos (list, pos);
306         }
307       else if (pos_list[0] < top)
308         {
309           XmListSetPos (list, pos_list[0]);
310         }
311     }
312   if (pos_list)
313     XtFree ((char *) pos_list);
314
315 #else  /* HAVE_ATHENA */
316 # ifdef HAVE_XawViewportSetCoordinates
317
318   int margin = 16;      /* should be line height or something. */
319   int count = 0;
320   int pos;
321   Dimension list_h = 0, vp_h = 0;
322   Dimension top_margin = 4;  /* I don't know where this value comes from */
323   Position vp_x = 0, vp_y = 0, current_y;
324   double cratio;
325   Widget viewport = XtParent(demo_list);
326   Widget sb = (viewport ? XtNameToWidget(viewport, "*vertical") : 0);
327   float sb_top = 0, sb_size = 0;
328   XawListReturnStruct *current = XawListShowCurrent(demo_list);
329   if (!current || !sb) return;
330
331   XtVaGetValues(demo_list,
332                 XtNnumberStrings, &count,
333                 XtNheight, &list_h,
334                 0);
335   if (count < 2 || list_h < 10) return;
336
337   XtVaGetValues(viewport, XtNheight, &vp_h, XtNx, &vp_x, XtNy, &vp_y, 0);
338   if (vp_h < 10) return;
339
340   XtVaGetValues(sb, XtNtopOfThumb, &sb_top, XtNshown, &sb_size, 0);
341   if (sb_size <= 0) return;
342
343   pos = current->list_index;
344   cratio = ((double) pos)  / ((double) count);
345   current_y = (cratio * list_h);
346
347   if (cratio < sb_top ||
348       cratio > sb_top + sb_size)
349     {
350       if (cratio < sb_top)
351         current_y -= (vp_h - margin - margin);
352       else
353         current_y -= margin;
354
355       if ((long)current_y >= (long) list_h)
356         current_y = (Position) ((long)list_h - (long)vp_h);
357
358       if ((long)current_y < (long)top_margin)
359         current_y = (Position)top_margin;
360
361       XawViewportSetCoordinates (viewport, vp_x, current_y);
362     }
363 # endif /* HAVE_XawViewportSetCoordinates */
364 #endif /* HAVE_ATHENA */
365 }
366
367
368 static void
369 next_cb (Widget button, XtPointer client_data, XtPointer call_data)
370 {
371   saver_info *si = (saver_info *) client_data;
372   saver_preferences *p = &si->prefs;
373
374   if (p->verbose_p)
375     fprintf (stderr, "%s: Run Next\n", blurb());
376
377   {
378 #ifdef HAVE_ATHENA
379   int cnt;
380   XawListReturnStruct *current = XawListShowCurrent(demo_list);
381   if (current->list_index == XAW_LIST_NONE)
382     XawListHighlight(demo_list, 0);
383   else
384     {
385       XtVaGetValues(demo_list,
386                     XtNnumberStrings, &cnt,
387                     NULL);
388       if (current->list_index + 1 < cnt)
389         {
390           current->list_index++;
391           XawListHighlight(demo_list, current->list_index);
392         }
393     }
394
395   ensure_selected_item_visible (demo_list);
396   current = XawListShowCurrent(demo_list);
397   XtVaSetValues(text_line, XtNstring, current->string, 0);
398   demo_mode_hack (si, current->string);
399
400 #else  /* HAVE_MOTIF */
401
402   int *pos_list;
403   int pos_count;
404   if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
405     {
406       XmListDeselectAllItems(demo_list);        /* LessTif lossage */
407       XmListSelectPos (demo_list, 1, True);
408     }
409   else
410     {
411       int pos = pos_list[0] + 1;
412       if (pos > si->prefs.screenhacks_count)
413         pos = 1;
414       XmListDeselectAllItems(demo_list);        /* LessTif lossage */
415       XmListSelectPos (demo_list, pos, True);
416     }
417   XtFree ((char *) pos_list);
418   ensure_selected_item_visible (demo_list);
419   demo_mode_hack (si, get_text_string (text_line));
420
421 #endif /* HAVE_MOTIF */
422   }
423 }
424
425
426 static void
427 prev_cb (Widget button, XtPointer client_data, XtPointer call_data)
428 {
429   saver_info *si = (saver_info *) client_data;
430   saver_preferences *p = &si->prefs;
431
432   if (p->verbose_p)
433     fprintf (stderr, "%s: Run Previous\n", blurb());
434
435   {
436 #ifdef HAVE_ATHENA
437   XawListReturnStruct *current=XawListShowCurrent(demo_list);
438   if (current->list_index == XAW_LIST_NONE)
439     XawListHighlight(demo_list, 0);
440   else
441     {
442       if (current->list_index >= 1)
443         {
444           current->list_index--;
445           XawListHighlight(demo_list, current->list_index);
446         }
447     }
448
449   ensure_selected_item_visible (demo_list);
450   current = XawListShowCurrent(demo_list);
451   XtVaSetValues(text_line, XtNstring, current->string, 0);
452   demo_mode_hack (si, current->string);
453
454 #else  /* HAVE_MOTIF */
455
456   int *pos_list;
457   int pos_count;
458   if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
459     {
460       XmListDeselectAllItems(demo_list);        /* LessTif lossage */
461       XmListSelectPos (demo_list, 0, True);
462     }
463   else
464     {
465       XmListDeselectAllItems(demo_list);        /* LessTif lossage */
466       XmListSelectPos (demo_list, pos_list [0] - 1, True);
467       XtFree ((char *) pos_list);
468     }
469   ensure_selected_item_visible (demo_list);
470   demo_mode_hack (si, get_text_string (text_line));
471
472 #endif /* HAVE_MOTIF */
473   }
474 }
475
476
477 static void pop_resources_dialog (saver_info *si);
478 static void make_resources_dialog (saver_info *si, Widget parent);
479
480 static void
481 edit_cb (Widget button, XtPointer client_data, XtPointer call_data)
482 {
483   saver_info *si = (saver_info *) client_data;
484   saver_screen_info *ssi = si->default_screen;
485   saver_preferences *p = &si->prefs;
486   Widget parent = ssi->toplevel_shell;
487
488   if (p->verbose_p)
489     fprintf (stderr, "%s: Preferences\n", blurb());
490
491   if (! resources_dialog)
492     make_resources_dialog (si, parent);
493   pop_resources_dialog (si);
494 }
495
496 static void
497 done_cb (Widget button, XtPointer client_data, XtPointer call_data)
498 {
499   saver_info *si = (saver_info *) client_data;
500   demo_mode_done (si);
501 }
502
503
504 static void
505 restart_cb (Widget button, XtPointer client_data, XtPointer call_data)
506 {
507   saver_info *si = (saver_info *) client_data;
508   saver_preferences *p = &si->prefs;
509   if (p->verbose_p)
510     fprintf (stderr, "%s: Restart\n", blurb());
511   demo_mode_restart_process (si);
512 }
513
514
515 void
516 pop_up_dialog_box (Widget dialog, Widget form, int where)
517 {
518   /* I'm sure this is the wrong way to pop up a dialog box, but I can't
519      figure out how else to do it.
520
521      It's important that the screensaver dialogs not get decorated or
522      otherwise reparented by the window manager, because they need to be
523      children of the *real* root window, not the WM's virtual root, in
524      order for us to guarentee that they are visible above the screensaver
525      window itself.
526    */
527   Arg av [100];
528   int ac = 0;
529   Dimension sw, sh, x, y, w, h;
530
531 #ifdef HAVE_ATHENA
532   XtRealizeWidget (dialog);
533 #else  /* HAVE_MOTIF */
534   /* Motif likes us to realize the *child* of the shell... */
535   XtRealizeWidget (form);
536 #endif /* HAVE_MOTIF */
537
538   sw = WidthOfScreen (XtScreen (dialog));
539   sh = HeightOfScreen (XtScreen (dialog));
540   ac = 0;
541   XtSetArg (av [ac], XtNwidth, &w); ac++;
542   XtSetArg (av [ac], XtNheight, &h); ac++;
543   XtGetValues (form, av, ac);
544
545   /* for debugging -- don't ask */
546   if (where >= 69)
547     {
548       where -= 69;
549       sw = (sw * 7) / 12;
550     }
551
552   switch (where)
553     {
554     case 0:     /* center it in the top-right quadrant */
555       x = (sw/2 + w) / 2 + (sw/2) - w;
556       y = (sh/2 + h) / 2 - h;
557       break;
558     case 1:     /* center it in the bottom-right quadrant */
559       x = (sw/2 + w) / 2 + (sw/2) - w;
560       y = (sh/2 + h) / 2 + (sh/2) - h;
561       break;
562     case 2:     /* center it on the screen */
563       x = (sw + w) / 2 - w;
564       y = (sh + h) / 2 - h;
565       break;
566     case 3:     /* center it in the top 2/3rds of the screen */
567       x = (sw + w) / 2 - w;
568       y = (sh*2/3 + h) / 2 - h;
569       break;
570     default:
571       abort ();
572     }
573   if (x + w > sw) x = sw - w;
574   if (y + h > sh) y = sh - h;
575   ac = 0;
576   XtSetArg (av [ac], XtNx, x); ac++;
577   XtSetArg (av [ac], XtNy, y); ac++;
578   XtSetArg (av [ac], XtNoverrideRedirect, True); ac++;
579
580 #ifdef HAVE_MOTIF
581   XtSetArg (av [ac], XmNdefaultPosition, False); ac++;
582 #endif /* HAVE_MOTIF */
583
584   XtSetValues (dialog, av, ac);
585   XtSetValues (form, av, ac);
586
587 #ifdef HAVE_ATHENA
588   XtPopup (dialog, XtGrabNone);
589 #else  /* HAVE_MOTIF */
590   XtManageChild (form);
591 #endif /* HAVE_MOTIF */
592
593   steal_focus_and_colormap (dialog);
594 }
595
596
597 void
598 make_screenhack_dialog (saver_info *si)
599 {
600   saver_screen_info *ssi = si->default_screen;
601   Widget parent = ssi->toplevel_shell;
602   char **hacks = si->prefs.screenhacks;
603
604   if (ssi->demo_cmap &&
605       ssi->demo_cmap != ssi->cmap &&
606       ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
607     {
608       XFreeColormap (si->dpy, ssi->demo_cmap);
609       ssi->demo_cmap = 0;
610     }
611
612   if (ssi->default_visual == DefaultVisualOfScreen (ssi->screen))
613     ssi->demo_cmap = DefaultColormapOfScreen (ssi->screen);
614   else
615     ssi->demo_cmap = XCreateColormap (si->dpy,
616                                       RootWindowOfScreen (ssi->screen),
617                                       ssi->default_visual, AllocNone);
618
619   create_demo_dialog (parent, ssi->default_visual, ssi->demo_cmap);
620   format_into_label (label1, si->version);
621
622   add_button_callback (next,    next_cb,    (XtPointer) si);
623   add_button_callback (prev,    prev_cb,    (XtPointer) si);
624   add_button_callback (done,    done_cb,    (XtPointer) si);
625   add_button_callback (restart, restart_cb, (XtPointer) si);
626   add_button_callback (edit,    edit_cb,    (XtPointer) si);
627
628 #ifdef HAVE_MOTIF
629   XtAddCallback (demo_list, XmNbrowseSelectionCallback,
630                  select_cb, (XtPointer) si);
631   XtAddCallback (demo_list, XmNdefaultActionCallback,
632                  select_cb, (XtPointer) si);
633   XtAddCallback (text_line, XmNactivateCallback, text_cb, (XtPointer) si);
634
635   if (hacks)
636     for (; *hacks; hacks++)
637       {
638         XmString xmstr = XmStringCreate (*hacks, XmSTRING_DEFAULT_CHARSET);
639         XmListAddItem (demo_list, xmstr, 0);
640         XmStringFree (xmstr);
641       }
642
643   /* Cause the most-recently-run hack to be selected in the list.
644      Do some voodoo to make it be roughly centered in the list (really,
645      just make it not be within +/- 5 of the top/bottom if possible.)
646    */
647   if (ssi->current_hack > 0)
648     {
649       int i = ssi->current_hack+1;
650       int top = i + 5;
651       int bot = i - 5;
652       if (bot < 1) bot = 1;
653       if (top > si->prefs.screenhacks_count)
654         top = si->prefs.screenhacks_count;
655
656       XmListDeselectAllItems(demo_list);        /* LessTif lossage */
657       XmListSelectPos (demo_list, bot, False);
658       ensure_selected_item_visible (demo_list);
659
660       XmListDeselectAllItems(demo_list);        /* LessTif lossage */
661       XmListSelectPos (demo_list, top, False);
662       ensure_selected_item_visible (demo_list);
663
664       XmListDeselectAllItems(demo_list);        /* LessTif lossage */
665       XmListSelectPos (demo_list, i, False);
666       ensure_selected_item_visible (demo_list);
667     }
668
669 #else  /* HAVE_ATHENA */
670
671   /* Hook up the text line. */
672
673   XtAppAddActions(XtWidgetToApplicationContext(text_line),
674                   actions, XtNumber(actions));
675   XtOverrideTranslations(text_line, XtParseTranslationTable(translations));
676
677
678   /* Must realize the widget before populating the list, or the dialog
679      will be as wide as the longest string.
680   */
681   XtRealizeWidget (demo_dialog);
682
683   XtVaSetValues (demo_list,
684                  XtNlist, hacks,
685                  XtNnumberStrings, si->prefs.screenhacks_count,
686                  0);
687   XtAddCallback (demo_list, XtNcallback, select_cb, si);
688   if (ssi->current_hack > 0)
689   XawListHighlight(demo_list, ssi->current_hack);
690
691   /* Now that we've populated the list, make sure that the list is as
692      wide as the dialog itself.
693   */
694   {
695     Widget viewport = XtParent(demo_list);
696     Widget subform = XtParent(viewport);
697     Widget box = XtNameToWidget(demo_dialog, "*box");
698     Widget label1 = XtNameToWidget(demo_dialog, "*label1");
699     Widget label2 = XtNameToWidget(demo_dialog, "*label2");
700     Dimension x=0, y=0, w=0, h=0, bw=0, w2=0;
701     XtVaGetValues(subform,
702                   XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
703     XtVaGetValues(box, XtNwidth, &w2, 0);
704     if (w2 != w)
705       XtResizeWidget(subform, w2, h, bw);
706
707     /* Why isn't the viewport getting centered? */
708     XtVaGetValues(viewport,
709                   XtNx, &x, XtNy, &y, XtNheight, &h, XtNborderWidth, &bw, 0);
710 /*    printf("%d %d %d %d\n", x, y, w, h); */
711     XtConfigureWidget(viewport, x, y, w2-x-x, h, bw);
712
713     /* And the text line, too. */
714     XtVaGetValues(text_line,
715                   XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
716     XtVaGetValues(viewport, XtNwidth, &w2, 0);
717     if (w2 != w)
718       XtResizeWidget(text_line, w2, h, bw);
719
720     /* And the labels too. */
721     XtVaGetValues(label1,
722                   XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
723     if (w2 != w)
724       XtResizeWidget(label1, w2, h, bw);
725
726     XtVaGetValues(label2,
727                   XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
728     if (w2 != w)
729       XtResizeWidget(label2, w2, h, bw);
730
731   }
732
733 #endif /* HAVE_ATHENA */
734
735   monitor_power_on (si);
736   pop_up_dialog_box(demo_dialog, demo_form,
737                     /* for debugging -- don't ask */
738                     (si->prefs.debug_p ? 69 : 0) +
739                     0);
740
741 #ifdef HAVE_ATHENA
742   /* For Athena, have to do this after the dialog is managed. */
743   ensure_selected_item_visible (demo_list);
744 #endif /* HAVE_ATHENA */
745 }
746
747 \f
748 /* the Screensaver Parameters dialog */
749
750 static struct resources {
751   int timeout, cycle, secs, ticks, lock_time, passwd_time;
752   int verb, cmap, fade, unfade, lock_p;
753 } res;
754
755
756 static void 
757 hack_time_cb (Display *dpy, char *line, int *store, Bool sec_p)
758 {
759   if (*line)
760     {
761       int value;
762       value = parse_time (line, sec_p, True);
763       if (value < 0)
764         /*XBell (dpy, 0)*/;
765       else
766         *store = value;
767     }
768 }
769
770 static void
771 res_sec_cb (Widget button, XtPointer client_data, XtPointer call_data)
772 {
773   hack_time_cb (XtDisplay (button), get_text_string (button),
774                 (int *) client_data, True);
775 }
776
777 static void
778 res_min_cb (Widget button, XtPointer client_data, XtPointer call_data)
779 {
780   hack_time_cb (XtDisplay (button), get_text_string (button),
781                 (int *) client_data, False);
782 }
783
784 static void
785 res_int_cb (Widget button, XtPointer client_data, XtPointer call_data)
786 {
787   char *line = get_text_string (button);
788   int *store = (int *) client_data;
789   unsigned int value;
790   char c;
791   if (! *line)
792     ;
793   else if (sscanf (line, "%u%c", &value, &c) != 1)
794     XBell (XtDisplay (button), 0);
795   else
796     *store = value;
797 }
798
799 static void
800 res_bool_cb (Widget button, XtPointer client_data, XtPointer call_data)
801 {
802   int *store = (int *) client_data;
803 #ifdef HAVE_MOTIF
804   *store = ((XmToggleButtonCallbackStruct *) call_data)->set;
805 #else /* HAVE_ATHENA */
806   Boolean state = FALSE;
807   XtVaGetValues (button, XtNstate, &state, NULL);
808   *store = state;
809 #endif /* HAVE_ATHENA */
810 }
811
812 static void
813 res_cancel_cb (Widget button, XtPointer client_data, XtPointer call_data)
814 {
815   saver_info *si = (saver_info *) client_data;
816   saver_preferences *p = &si->prefs;
817
818   XtDestroyWidget (resources_dialog);
819   resources_dialog = 0;
820   raise_screenhack_dialog ();
821
822   if (p->verbose_p)
823     fprintf (stderr, "%s: lowering preferences dialog.\n", blurb());
824 }
825
826
827 static void
828 res_done_cb (Widget button, XtPointer client_data, XtPointer call_data)
829 {
830   saver_info *si = (saver_info *) client_data;
831   saver_preferences *p = &si->prefs;
832
833   res_cancel_cb (button, client_data, call_data);
834
835 #ifdef HAVE_ATHENA
836   /* Check all text widgets, since we don't have callbacks for these. */
837   res_min_cb (timeout_text,     (XtPointer) &res.timeout,     NULL);
838   res_min_cb (cycle_text,       (XtPointer) &res.cycle,       NULL);
839   res_sec_cb (fade_text,        (XtPointer) &res.secs,        NULL);
840   res_int_cb (ticks_text,       (XtPointer) &res.ticks,       NULL);
841   res_min_cb (lock_time_text,   (XtPointer) &res.lock_time,   NULL);
842   res_sec_cb (passwd_time_text, (XtPointer) &res.passwd_time, NULL);
843 #endif /* HAVE_ATHENA */
844
845   /* Throttle the timeouts to minimum sane values. */
846   if (res.timeout < 5) res.timeout = 5;
847   if (res.cycle < 2) res.cycle = 2;
848   if (res.passwd_time < 10) res.passwd_time = 10;
849
850   p->timeout = res.timeout * 1000;
851   p->cycle = res.cycle * 1000;
852   p->lock_timeout = res.lock_time * 1000;
853 #ifndef NO_LOCKING
854   p->passwd_timeout = res.passwd_time * 1000;
855 #endif
856   p->fade_seconds = res.secs;
857   p->fade_ticks = res.ticks;
858   p->verbose_p = res.verb;
859   p->install_cmap_p = res.cmap;
860   p->fade_p = res.fade;
861   p->unfade_p = res.unfade;
862   p->lock_p = res.lock_p;
863
864   if (p->debug_p && p->verbose_p)
865     fprintf (stderr, "%s: parameters changed:\n\
866         timeout: %d\n\tcycle:   %d\n\tlock:    %d\n\tpasswd:  %d\n\
867         fade:    %d\n\tfade:    %d\n\tverbose: %d\n\tinstall: %d\n\
868         fade:    %d\n\tunfade:  %d\n\tlock:    %d\n",
869              blurb(), p->timeout, p->cycle, p->lock_timeout,
870 #ifdef NO_LOCKING
871              0,
872 #else
873              p->passwd_timeout,
874 #endif
875              p->fade_seconds, p->fade_ticks, p->verbose_p, p->install_cmap_p,
876              p->fade_p, p->unfade_p, p->lock_p);
877
878
879 #if defined(HAVE_MIT_SAVER_EXTENSION) || defined(HAVE_SGI_SAVER_EXTENSION)
880   if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
881     {
882       /* Need to set the server timeout to the new one the user has picked.
883        */
884       int server_timeout, server_interval, prefer_blank, allow_exp;
885       XGetScreenSaver (si->dpy, &server_timeout, &server_interval,
886                        &prefer_blank, &allow_exp);
887       if (server_timeout != (p->timeout / 1000))
888         {
889           server_timeout = (p->timeout / 1000);
890           if (p->verbose_p)
891             fprintf (stderr,
892                    "%s: configuring server for saver timeout of %d seconds.\n",
893                      blurb(), server_timeout);
894           /* Leave all other parameters the same. */
895           XSetScreenSaver (si->dpy, server_timeout, server_interval,
896                            prefer_blank, allow_exp);
897         }
898     }
899 #endif /* HAVE_MIT_SAVER_EXTENSION || HAVE_SGI_SAVER_EXTENSION */
900
901   write_init_file (si);
902 }
903
904
905 static void
906 make_resources_dialog (saver_info *si, Widget parent)
907 {
908   saver_screen_info *ssi = si->default_screen;
909
910   if (ssi->demo_cmap &&
911       ssi->demo_cmap != ssi->cmap &&
912       ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
913     {
914       XFreeColormap (si->dpy, ssi->demo_cmap);
915       ssi->demo_cmap = 0;
916     }
917
918   if (ssi->default_visual == DefaultVisualOfScreen (ssi->screen))
919     ssi->demo_cmap = DefaultColormapOfScreen (ssi->screen);
920   else
921     ssi->demo_cmap = XCreateColormap (si->dpy,
922                                      RootWindowOfScreen (ssi->screen),
923                                      ssi->default_visual, AllocNone);
924
925   create_resources_dialog (parent, ssi->default_visual, ssi->demo_cmap);
926
927   add_button_callback (res_done,   res_done_cb,   (XtPointer) si);
928   add_button_callback (res_cancel, res_cancel_cb, (XtPointer) si);
929
930 #define CB(widget,type,slot) \
931         add_text_callback ((widget), (type), (XtPointer) (slot))
932 #define CBT(widget,type,slot) \
933         add_toggle_callback ((widget), (type), (XtPointer) (slot))
934
935 #ifdef HAVE_MOTIF
936   /* When using Athena widgets, we can't set callbacks for these,
937      so we'll check them all if "done" gets pressed.
938    */
939   CB (timeout_text,     res_min_cb,  &res.timeout);
940   CB (cycle_text,       res_min_cb,  &res.cycle);
941   CB (fade_text,        res_sec_cb,  &res.secs);
942   CB (ticks_text,       res_int_cb,  &res.ticks);
943   CB (lock_time_text,   res_min_cb,  &res.lock_time);
944   CB (passwd_time_text, res_sec_cb,  &res.passwd_time);
945 #endif /* HAVE_MOTIF */
946
947   CBT (verbose_toggle,  res_bool_cb, &res.verb);
948   CBT (cmap_toggle,     res_bool_cb, &res.cmap);
949   CBT (fade_toggle,     res_bool_cb, &res.fade);
950   CBT (unfade_toggle,   res_bool_cb, &res.unfade);
951   CBT (lock_toggle,     res_bool_cb, &res.lock_p);
952 #undef CB
953 #undef CBT
954
955   if (si->locking_disabled_p)
956     {
957       disable_widget (passwd_time_text);
958       disable_widget (lock_time_text);
959       disable_widget (lock_toggle);
960     }
961   if (!si->fading_possible_p)
962     {
963       disable_widget (fade_text);
964       disable_widget (ticks_text);
965       disable_widget (cmap_toggle);
966       disable_widget (fade_toggle);
967       disable_widget (unfade_toggle);
968     }
969 }
970
971
972 static void
973 fmt_time (char *buf, unsigned int s, int min_p)
974 {
975   unsigned int h = 0, m = 0;
976   if (s >= 60)
977     {
978       m += (s / 60);
979       s %= 60;
980     }
981   if (m >= 60)
982     {
983       h += (m / 60);
984       m %= 60;
985     }
986 /*
987   if (min_p && h == 0 && s == 0)
988     sprintf (buf, "%u", m);
989   else if (!min_p && h == 0 && m == 0)
990     sprintf (buf, "%u", s);
991   else
992   if (h == 0)
993     sprintf (buf, "%u:%02u", m, s);
994   else
995 */
996     sprintf (buf, "%u:%02u:%02u", h, m, s);
997 }
998
999 static void
1000 pop_resources_dialog (saver_info *si)
1001 {
1002   saver_preferences *p = &si->prefs;
1003   char buf [100];
1004
1005   if (p->verbose_p)
1006     fprintf (stderr, "%s: raising preferences dialog.\n", blurb());
1007
1008   res.timeout = p->timeout / 1000;
1009   res.cycle = p->cycle / 1000;
1010   res.lock_time = p->lock_timeout / 1000;
1011 #ifndef NO_LOCKING
1012   res.passwd_time = p->passwd_timeout / 1000;
1013 #endif
1014   res.secs = p->fade_seconds;
1015   res.ticks = p->fade_ticks;
1016   res.verb = p->verbose_p;
1017   res.cmap = p->install_cmap_p;
1018   res.fade = p->fade_p;
1019   res.unfade = p->unfade_p;
1020   res.lock_p = (p->lock_p && !si->locking_disabled_p);
1021
1022   fmt_time (buf, res.timeout, 1);     set_text_string (timeout_text, buf);
1023   fmt_time (buf, res.cycle, 1);       set_text_string (cycle_text, buf);
1024   fmt_time (buf, res.lock_time, 1);   set_text_string (lock_time_text, buf);
1025   fmt_time (buf, res.passwd_time, 0); set_text_string (passwd_time_text, buf);
1026   fmt_time (buf, res.secs, 0);        set_text_string (fade_text, buf);
1027   sprintf (buf, "%u", res.ticks);     set_text_string (ticks_text, buf);
1028
1029   set_toggle_button_state (verbose_toggle, res.verb);
1030   set_toggle_button_state (cmap_toggle, res.cmap);
1031   set_toggle_button_state (fade_toggle, res.fade);
1032   set_toggle_button_state (unfade_toggle, res.unfade);
1033   set_toggle_button_state (lock_toggle, res.lock_p);
1034
1035   monitor_power_on (si);
1036   pop_up_dialog_box (resources_dialog, resources_form,
1037                      /* for debugging -- don't ask */
1038                      (si->prefs.debug_p ? 69 : 0) +
1039                      1);
1040 }
1041
1042 \f
1043 /* The main demo-mode command loop.
1044  */
1045
1046 void
1047 demo_mode (saver_info *si)
1048 {
1049   saver_preferences *p = &si->prefs;
1050   Bool prefs_p = (si->demo_mode_p == (Bool) 2);  /* kludge! */
1051
1052   if (p->verbose_p)
1053     fprintf (stderr, "%s: Demo Mode.\n", blurb());
1054
1055   si->selection_mode = 0;
1056   si->dbox_up_p = True;
1057   monitor_power_on (si);
1058   raise_window (si, True, False, False);
1059   make_screenhack_dialog (si);
1060
1061   if (prefs_p)
1062     edit_cb (0, si, 0);         /* pop up preferences panel */
1063
1064   while (si->demo_mode_p)
1065     {
1066       XEvent event;
1067       XtAppNextEvent (si->app, &event);
1068       switch (event.xany.type)
1069         {
1070         case 0:         /* synthetic "timeout" event */
1071           break;
1072
1073         case ClientMessage:
1074           handle_clientmessage (si, &event, False);
1075           break;
1076
1077         case CreateNotify:
1078           if (!p->use_xidle_extension &&
1079               !p->use_mit_saver_extension &&
1080               !p->use_sgi_saver_extension)
1081             {
1082               start_notice_events_timer (si, event.xcreatewindow.window);
1083 #ifdef DEBUG_TIMERS
1084               if (p->verbose_p)
1085                 fprintf (stderr,
1086                          "%s: starting notice_events_timer for 0x%X (%lu)\n",
1087                          blurb(),
1088                          (unsigned int) event.xcreatewindow.window,
1089                          p->notice_events_timeout);
1090 #endif /* DEBUG_TIMERS */
1091             }
1092           break;
1093
1094         case ButtonPress:
1095         case ButtonRelease:
1096           if (!XtWindowToWidget (si->dpy, event.xbutton.window))
1097             raise_screenhack_dialog ();
1098           /* fall through */
1099
1100         default:
1101 #ifdef HAVE_MIT_SAVER_EXTENSION
1102           if (event.type == si->mit_saver_ext_event_number)
1103             {
1104               /* Get the "real" server window(s) out of the way as soon
1105                  as possible. */
1106               int i = 0;
1107               for (i = 0; i < si->nscreens; i++)
1108                 {
1109                   saver_screen_info *ssi = &si->screens[i];
1110                   if (ssi->server_mit_saver_window &&
1111                       window_exists_p (si->dpy, ssi->server_mit_saver_window))
1112                     XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1113                 }
1114             }
1115           else
1116 #endif /* HAVE_MIT_SAVER_EXTENSION */
1117
1118           XtDispatchEvent (&event);
1119           break;
1120         }
1121     }
1122
1123   if (p->verbose_p)
1124     fprintf (stderr, "%s: Demo Mode done.\n", blurb());
1125
1126   kill_screenhack (si);
1127
1128   destroy_screenhack_dialogs (si);
1129   initialize_screensaver_window (si);
1130
1131   si->dbox_up_p = False;
1132   si->demo_hack = 0;
1133
1134   si->demo_mode_p = True;  /* kludge to inhibit unfade... */
1135   unblank_screen (si);
1136   si->demo_mode_p = False;
1137 }
1138
1139 static void
1140 demo_mode_hack (saver_info *si, char *hack)
1141 {
1142   if (! si->demo_mode_p) abort ();
1143   kill_screenhack (si);
1144   if (! si->demo_hack)
1145     blank_screen (si);
1146   si->demo_hack = hack;
1147   spawn_screenhack (si, False);
1148   /* raise_screenhack_dialog(); */
1149 }
1150
1151 static void
1152 demo_mode_done (saver_info *si)
1153 {
1154   si->demo_mode_p = False;
1155 }