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