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