23a42974c6ae88d7015d173d4b6940835efa04ae
[xscreensaver] / driver / demo.c
1 /* demo.c --- implements the interactive demo-mode and options dialogs.
2  * xscreensaver, Copyright (c) 1993-1998 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   monitor_power_on (si);
634   pop_up_dialog_box(demo_dialog, demo_form,
635                     /* for debugging -- don't ask */
636                     (si->prefs.debug_p ? 69 : 0) +
637                     0);
638
639 #ifdef HAVE_ATHENA
640   /* For Athena, have to do this after the dialog is managed. */
641   ensure_selected_item_visible (demo_list);
642 #endif /* HAVE_ATHENA */
643 }
644
645 \f
646 /* the Screensaver Parameters dialog */
647
648 static struct resources {
649   int timeout, cycle, secs, ticks, lock_time, passwd_time;
650   int verb, cmap, fade, unfade, lock_p;
651 } res;
652
653
654 static void 
655 hack_time_cb (Display *dpy, char *line, int *store, Bool sec_p)
656 {
657   if (*line)
658     {
659       int value;
660       value = parse_time (line, sec_p, True);
661       if (value < 0)
662         /*XBell (dpy, 0)*/;
663       else
664         *store = value;
665     }
666 }
667
668 static void
669 res_sec_cb (Widget button, XtPointer client_data, XtPointer call_data)
670 {
671   hack_time_cb (XtDisplay (button), get_text_string (button),
672                 (int *) client_data, True);
673 }
674
675 static void
676 res_min_cb (Widget button, XtPointer client_data, XtPointer call_data)
677 {
678   hack_time_cb (XtDisplay (button), get_text_string (button),
679                 (int *) client_data, False);
680 }
681
682 static void
683 res_int_cb (Widget button, XtPointer client_data, XtPointer call_data)
684 {
685   char *line = get_text_string (button);
686   int *store = (int *) client_data;
687   unsigned int value;
688   char c;
689   if (! *line)
690     ;
691   else if (sscanf (line, "%u%c", &value, &c) != 1)
692     XBell (XtDisplay (button), 0);
693   else
694     *store = value;
695 }
696
697 static void
698 res_bool_cb (Widget button, XtPointer client_data, XtPointer call_data)
699 {
700   int *store = (int *) client_data;
701 #ifdef HAVE_MOTIF
702   *store = ((XmToggleButtonCallbackStruct *) call_data)->set;
703 #else /* HAVE_ATHENA */
704   Boolean state = FALSE;
705   XtVaGetValues (button, XtNstate, &state, NULL);
706   *store = state;
707 #endif /* HAVE_ATHENA */
708 }
709
710 static void
711 res_cancel_cb (Widget button, XtPointer client_data, XtPointer call_data)
712 {
713   XtDestroyWidget (resources_dialog);
714   resources_dialog = 0;
715   raise_screenhack_dialog ();
716 }
717
718
719 static void
720 res_done_cb (Widget button, XtPointer client_data, XtPointer call_data)
721 {
722   saver_info *si = (saver_info *) client_data;
723   saver_preferences *p = &si->prefs;
724
725   res_cancel_cb (button, client_data, call_data);
726
727 #ifdef HAVE_ATHENA
728   /* Check all text widgets, since we don't have callbacks for these. */
729   res_min_cb (timeout_text,     (XtPointer) &res.timeout,     NULL);
730   res_min_cb (cycle_text,       (XtPointer) &res.cycle,       NULL);
731   res_sec_cb (fade_text,        (XtPointer) &res.secs,        NULL);
732   res_int_cb (ticks_text,       (XtPointer) &res.ticks,       NULL);
733   res_min_cb (lock_time_text,   (XtPointer) &res.lock_time,   NULL);
734   res_sec_cb (passwd_time_text, (XtPointer) &res.passwd_time, NULL);
735 #endif /* HAVE_ATHENA */
736
737   /* Throttle the timeouts to minimum sane values. */
738   if (res.timeout < 5) res.timeout = 5;
739   if (res.cycle < 2) res.cycle = 2;
740   if (res.passwd_time < 10) res.passwd_time = 10;
741
742   p->timeout = res.timeout * 1000;
743   p->cycle = res.cycle * 1000;
744   p->lock_timeout = res.lock_time * 1000;
745 #ifndef NO_LOCKING
746   p->passwd_timeout = res.passwd_time * 1000;
747 #endif
748   p->fade_seconds = res.secs;
749   p->fade_ticks = res.ticks;
750   p->verbose_p = res.verb;
751   p->install_cmap_p = res.cmap;
752   p->fade_p = res.fade;
753   p->unfade_p = res.unfade;
754   p->lock_p = res.lock_p;
755
756   if (p->debug_p && p->verbose_p)
757     fprintf (stderr, "%s: parameters changed:\n\
758         timeout: %d\n\tcycle:   %d\n\tlock:    %d\n\tpasswd:  %d\n\
759         fade:    %d\n\tfade:    %d\n\tverbose: %d\n\tinstall: %d\n\
760         fade:    %d\n\tunfade:  %d\n\tlock:    %d\n",
761              progname, p->timeout, p->cycle, p->lock_timeout,
762 #ifdef NO_LOCKING
763              0,
764 #else
765              p->passwd_timeout,
766 #endif
767              p->fade_seconds, p->fade_ticks, p->verbose_p, p->install_cmap_p,
768              p->fade_p, p->unfade_p, p->lock_p);
769
770
771 #if defined(HAVE_MIT_SAVER_EXTENSION) || defined(HAVE_SGI_SAVER_EXTENSION)
772   if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
773     {
774       /* Need to set the server timeout to the new one the user has picked.
775        */
776       int server_timeout, server_interval, prefer_blank, allow_exp;
777       XGetScreenSaver (si->dpy, &server_timeout, &server_interval,
778                        &prefer_blank, &allow_exp);
779       if (server_timeout != (p->timeout / 1000))
780         {
781           server_timeout = (p->timeout / 1000);
782           if (p->verbose_p)
783             fprintf (stderr,
784                    "%s: configuring server for saver timeout of %d seconds.\n",
785                      progname, server_timeout);
786           /* Leave all other parameters the same. */
787           XSetScreenSaver (si->dpy, server_timeout, server_interval,
788                            prefer_blank, allow_exp);
789         }
790     }
791 #endif /* HAVE_MIT_SAVER_EXTENSION || HAVE_SGI_SAVER_EXTENSION */
792 }
793
794
795 static void
796 make_resources_dialog (saver_info *si, Widget parent)
797 {
798   saver_screen_info *ssi = si->default_screen;
799
800   if (ssi->demo_cmap &&
801       ssi->demo_cmap != ssi->cmap &&
802       ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
803     {
804       XFreeColormap (si->dpy, ssi->demo_cmap);
805       ssi->demo_cmap = 0;
806     }
807
808   if (ssi->default_visual == DefaultVisualOfScreen (ssi->screen))
809     ssi->demo_cmap = DefaultColormapOfScreen (ssi->screen);
810   else
811     ssi->demo_cmap = XCreateColormap (si->dpy,
812                                      RootWindowOfScreen (ssi->screen),
813                                      ssi->default_visual, AllocNone);
814
815   create_resources_dialog (parent, ssi->default_visual, ssi->demo_cmap);
816
817   add_button_callback (res_done,   res_done_cb,   (XtPointer) si);
818   add_button_callback (res_cancel, res_cancel_cb, (XtPointer) si);
819
820 #define CB(widget,type,slot) \
821         add_text_callback ((widget), (type), (XtPointer) (slot))
822 #define CBT(widget,type,slot) \
823         add_toggle_callback ((widget), (type), (XtPointer) (slot))
824
825 #ifdef HAVE_MOTIF
826   /* When using Athena widgets, we can't set callbacks for these,
827      so we'll check them all if "done" gets pressed.
828    */
829   CB (timeout_text,     res_min_cb,  &res.timeout);
830   CB (cycle_text,       res_min_cb,  &res.cycle);
831   CB (fade_text,        res_sec_cb,  &res.secs);
832   CB (ticks_text,       res_int_cb,  &res.ticks);
833   CB (lock_time_text,   res_min_cb,  &res.lock_time);
834   CB (passwd_time_text, res_sec_cb,  &res.passwd_time);
835 #endif /* HAVE_MOTIF */
836
837   CBT (verbose_toggle,  res_bool_cb, &res.verb);
838   CBT (cmap_toggle,     res_bool_cb, &res.cmap);
839   CBT (fade_toggle,     res_bool_cb, &res.fade);
840   CBT (unfade_toggle,   res_bool_cb, &res.unfade);
841   CBT (lock_toggle,     res_bool_cb, &res.lock_p);
842 #undef CB
843 #undef CBT
844
845   if (si->locking_disabled_p)
846     {
847       disable_widget (passwd_time_text);
848       disable_widget (lock_time_text);
849       disable_widget (lock_toggle);
850     }
851   if (CellsOfScreen (XtScreen (parent)) <= 2)
852     {
853       disable_widget (fade_text);
854       disable_widget (ticks_text);
855       disable_widget (cmap_toggle);
856       disable_widget (fade_toggle);
857       disable_widget (unfade_toggle);
858     }
859 }
860
861
862 static void
863 fmt_time (char *buf, unsigned int s, int min_p)
864 {
865   unsigned int h = 0, m = 0;
866   if (s >= 60)
867     {
868       m += (s / 60);
869       s %= 60;
870     }
871   if (m >= 60)
872     {
873       h += (m / 60);
874       m %= 60;
875     }
876 /*
877   if (min_p && h == 0 && s == 0)
878     sprintf (buf, "%u", m);
879   else if (!min_p && h == 0 && m == 0)
880     sprintf (buf, "%u", s);
881   else
882   if (h == 0)
883     sprintf (buf, "%u:%02u", m, s);
884   else
885 */
886     sprintf (buf, "%u:%02u:%02u", h, m, s);
887 }
888
889 static void
890 pop_resources_dialog (saver_info *si)
891 {
892   saver_preferences *p = &si->prefs;
893   char buf [100];
894
895   res.timeout = p->timeout / 1000;
896   res.cycle = p->cycle / 1000;
897   res.lock_time = p->lock_timeout / 1000;
898 #ifndef NO_LOCKING
899   res.passwd_time = p->passwd_timeout / 1000;
900 #endif
901   res.secs = p->fade_seconds;
902   res.ticks = p->fade_ticks;
903   res.verb = p->verbose_p;
904   res.cmap = p->install_cmap_p;
905   res.fade = p->fade_p;
906   res.unfade = p->unfade_p;
907   res.lock_p = (p->lock_p && !si->locking_disabled_p);
908
909   fmt_time (buf, res.timeout, 1);     set_text_string (timeout_text, buf);
910   fmt_time (buf, res.cycle, 1);       set_text_string (cycle_text, buf);
911   fmt_time (buf, res.lock_time, 1);   set_text_string (lock_time_text, buf);
912   fmt_time (buf, res.passwd_time, 0); set_text_string (passwd_time_text, buf);
913   fmt_time (buf, res.secs, 0);        set_text_string (fade_text, buf);
914   sprintf (buf, "%u", res.ticks);     set_text_string (ticks_text, buf);
915
916   set_toggle_button_state (verbose_toggle, res.verb);
917   set_toggle_button_state (cmap_toggle, res.cmap);
918   set_toggle_button_state (fade_toggle, res.fade);
919   set_toggle_button_state (unfade_toggle, res.unfade);
920   set_toggle_button_state (lock_toggle, res.lock_p);
921
922   monitor_power_on (si);
923   pop_up_dialog_box (resources_dialog, resources_form,
924                      /* for debugging -- don't ask */
925                      (si->prefs.debug_p ? 69 : 0) +
926                      1);
927 }
928
929 \f
930 /* The main demo-mode command loop.
931  */
932
933 void
934 demo_mode (saver_info *si)
935 {
936   saver_preferences *p = &si->prefs;
937   si->dbox_up_p = True;
938   monitor_power_on (si);
939   raise_window (si, True, False, False);
940   make_screenhack_dialog (si);
941   while (si->demo_mode_p)
942     {
943       XEvent event;
944       XtAppNextEvent (si->app, &event);
945       switch (event.xany.type)
946         {
947         case 0:         /* synthetic "timeout" event */
948           break;
949
950         case ClientMessage:
951           handle_clientmessage (si, &event, False);
952           break;
953
954         case CreateNotify:
955           if (!p->use_xidle_extension &&
956               !p->use_mit_saver_extension &&
957               !p->use_sgi_saver_extension)
958             {
959               start_notice_events_timer (si, event.xcreatewindow.window);
960 #ifdef DEBUG_TIMERS
961               if (p->verbose_p)
962                 printf ("%s: starting notice_events_timer for 0x%X (%lu)\n",
963                         progname,
964                         (unsigned int) event.xcreatewindow.window,
965                         p->notice_events_timeout);
966 #endif /* DEBUG_TIMERS */
967             }
968           break;
969
970         case ButtonPress:
971         case ButtonRelease:
972           if (!XtWindowToWidget (si->dpy, event.xbutton.window))
973             raise_screenhack_dialog ();
974           /* fall through */
975
976         default:
977 #ifdef HAVE_MIT_SAVER_EXTENSION
978           if (event.type == si->mit_saver_ext_event_number)
979             {
980               /* Get the "real" server window(s) out of the way as soon
981                  as possible. */
982               int i = 0;
983               for (i = 0; i < si->nscreens; i++)
984                 {
985                   saver_screen_info *ssi = &si->screens[i];
986                   if (ssi->server_mit_saver_window &&
987                       window_exists_p (si->dpy, ssi->server_mit_saver_window))
988                     XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
989                 }
990             }
991           else
992 #endif /* HAVE_MIT_SAVER_EXTENSION */
993
994           XtDispatchEvent (&event);
995           break;
996         }
997     }
998   destroy_screenhack_dialogs (si);
999   initialize_screensaver_window (si);
1000
1001   si->demo_mode_p = True;  /* kludge to inhibit unfade... */
1002   unblank_screen (si);
1003   si->demo_mode_p = False;
1004 }
1005
1006 static void
1007 demo_mode_hack (saver_info *si, char *hack)
1008 {
1009   if (! si->demo_mode_p) abort ();
1010   kill_screenhack (si);
1011   if (! si->demo_hack)
1012     blank_screen (si);
1013   si->demo_hack = hack;
1014   spawn_screenhack (si, False);
1015   /* raise_screenhack_dialog(); */
1016 }
1017
1018 static void
1019 demo_mode_done (saver_info *si)
1020 {
1021   kill_screenhack (si);
1022   if (si->demo_hack)
1023     unblank_screen (si);
1024   si->demo_mode_p = False;
1025   si->dbox_up_p = False;
1026   si->demo_hack = 0;
1027 }