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