http://ftp.x.org/contrib/applications/xscreensaver-2.17.tar.gz
[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 #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   /* for debugging -- don't ask */
496   if (where >= 69)
497     {
498       where -= 69;
499       sw = (sw * 7) / 12;
500     }
501
502   switch (where)
503     {
504     case 0:     /* center it in the top-right quadrant */
505       x = (sw/2 + w) / 2 + (sw/2) - w;
506       y = (sh/2 + h) / 2 - h;
507       break;
508     case 1:     /* center it in the bottom-right quadrant */
509       x = (sw/2 + w) / 2 + (sw/2) - w;
510       y = (sh/2 + h) / 2 + (sh/2) - h;
511       break;
512     case 2:     /* center it on the screen */
513       x = (sw + w) / 2 - w;
514       y = (sh + h) / 2 - h;
515       break;
516     default:
517       abort ();
518     }
519   if (x + w > sw) x = sw - w;
520   if (y + h > sh) y = sh - h;
521   ac = 0;
522   XtSetArg (av [ac], XtNx, x); ac++;
523   XtSetArg (av [ac], XtNy, y); ac++;
524   XtSetArg (av [ac], XtNoverrideRedirect, True); ac++;
525
526 #ifdef HAVE_MOTIF
527   XtSetArg (av [ac], XmNdefaultPosition, False); ac++;
528 #endif /* HAVE_MOTIF */
529
530   XtSetValues (dialog, av, ac);
531   XtSetValues (form, av, ac);
532
533 #ifdef HAVE_ATHENA
534   XtPopup (dialog, XtGrabNone);
535 #else  /* HAVE_MOTIF */
536   XtManageChild (form);
537 #endif /* HAVE_MOTIF */
538
539   steal_focus_and_colormap (dialog);
540 }
541
542
543 static void
544 make_screenhack_dialog (saver_info *si)
545 {
546   saver_screen_info *ssi = si->default_screen;
547   Widget parent = ssi->toplevel_shell;
548   char **hacks = si->prefs.screenhacks;
549
550   if (ssi->demo_cmap &&
551       ssi->demo_cmap != ssi->cmap &&
552       ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
553     {
554       XFreeColormap (si->dpy, ssi->demo_cmap);
555       ssi->demo_cmap = 0;
556     }
557
558   if (ssi->default_visual == DefaultVisualOfScreen (ssi->screen))
559     ssi->demo_cmap = DefaultColormapOfScreen (ssi->screen);
560   else
561     ssi->demo_cmap = XCreateColormap (si->dpy,
562                                       RootWindowOfScreen (ssi->screen),
563                                       ssi->default_visual, AllocNone);
564
565   create_demo_dialog (parent, ssi->default_visual, ssi->demo_cmap);
566   format_into_label (label1, si->version);
567
568   add_button_callback (next,    next_cb,    (XtPointer) si);
569   add_button_callback (prev,    prev_cb,    (XtPointer) si);
570   add_button_callback (done,    done_cb,    (XtPointer) si);
571   add_button_callback (restart, restart_cb, (XtPointer) si);
572   add_button_callback (edit,    edit_cb,    (XtPointer) si);
573
574 #ifdef HAVE_MOTIF
575   XtAddCallback (demo_list, XmNbrowseSelectionCallback,
576                  select_cb, (XtPointer) si);
577   XtAddCallback (demo_list, XmNdefaultActionCallback,
578                  select_cb, (XtPointer) si);
579   XtAddCallback (text_line, XmNactivateCallback, text_cb, (XtPointer) si);
580
581   for (; *hacks; hacks++)
582     {
583       XmString xmstr = XmStringCreate (*hacks, XmSTRING_DEFAULT_CHARSET);
584       XmListAddItem (demo_list, xmstr, 0);
585       XmStringFree (xmstr);
586     }
587
588   /* Cause the most-recently-run hack to be selected in the list.
589      Do some voodoo to make it be roughly centered in the list (really,
590      just make it not be within +/- 5 of the top/bottom if possible.)
591    */
592   if (ssi->current_hack > 0)
593     {
594       int i = ssi->current_hack+1;
595       int top = i + 5;
596       int bot = i - 5;
597       if (bot < 1) bot = 1;
598       if (top > si->prefs.screenhacks_count)
599         top = si->prefs.screenhacks_count;
600
601       XmListDeselectAllItems(demo_list);        /* LessTif lossage */
602       XmListSelectPos (demo_list, bot, False);
603       ensure_selected_item_visible (demo_list);
604
605       XmListDeselectAllItems(demo_list);        /* LessTif lossage */
606       XmListSelectPos (demo_list, top, False);
607       ensure_selected_item_visible (demo_list);
608
609       XmListDeselectAllItems(demo_list);        /* LessTif lossage */
610       XmListSelectPos (demo_list, i, False);
611       ensure_selected_item_visible (demo_list);
612     }
613
614 #else  /* HAVE_ATHENA */
615
616   XtVaSetValues (demo_list,
617                  XtNlist, hacks,
618                  XtNnumberStrings, si->prefs.screenhacks_count,
619                  0);
620   XtAddCallback (demo_list, XtNcallback, select_cb, si);
621
622   /* ####   still need to do the "select most-recently-run hack"
623      ####   thing for Athena.
624   */
625
626 #endif /* HAVE_ATHENA */
627
628   pop_up_dialog_box(demo_dialog, demo_form,
629                     /* for debugging -- don't ask */
630                     (si->prefs.debug_p ? 69 : 0) +
631                     0);
632 }
633
634 \f
635 /* the Screensaver Parameters dialog */
636
637 static struct resources {
638   int timeout, cycle, secs, ticks, lock_time, passwd_time;
639   int verb, cmap, fade, unfade, lock_p;
640 } res;
641
642
643 static void 
644 hack_time_cb (Display *dpy, char *line, int *store, Bool sec_p)
645 {
646   if (*line)
647     {
648       int value;
649       value = parse_time (line, sec_p, True);
650       if (value < 0)
651         /*XBell (dpy, 0)*/;
652       else
653         *store = value;
654     }
655 }
656
657 static void
658 res_sec_cb (Widget button, XtPointer client_data, XtPointer call_data)
659 {
660   hack_time_cb (XtDisplay (button), get_text_string (button),
661                 (int *) client_data, True);
662 }
663
664 static void
665 res_min_cb (Widget button, XtPointer client_data, XtPointer call_data)
666 {
667   hack_time_cb (XtDisplay (button), get_text_string (button),
668                 (int *) client_data, False);
669 }
670
671 static void
672 res_int_cb (Widget button, XtPointer client_data, XtPointer call_data)
673 {
674   char *line = get_text_string (button);
675   int *store = (int *) client_data;
676   unsigned int value;
677   char c;
678   if (! *line)
679     ;
680   else if (sscanf (line, "%u%c", &value, &c) != 1)
681     XBell (XtDisplay (button), 0);
682   else
683     *store = value;
684 }
685
686 static void
687 res_bool_cb (Widget button, XtPointer client_data, XtPointer call_data)
688 {
689   int *store = (int *) client_data;
690 #ifdef HAVE_MOTIF
691   *store = ((XmToggleButtonCallbackStruct *) call_data)->set;
692 #else /* HAVE_ATHENA */
693   Boolean state = FALSE;
694   XtVaGetValues (button, XtNstate, &state, NULL);
695   *store = state;
696 #endif /* HAVE_ATHENA */
697 }
698
699 static void
700 res_cancel_cb (Widget button, XtPointer client_data, XtPointer call_data)
701 {
702   XtDestroyWidget (resources_dialog);
703   resources_dialog = 0;
704   raise_screenhack_dialog ();
705 }
706
707
708 static void
709 res_done_cb (Widget button, XtPointer client_data, XtPointer call_data)
710 {
711   saver_info *si = (saver_info *) client_data;
712   saver_preferences *p = &si->prefs;
713
714   res_cancel_cb (button, client_data, call_data);
715
716 #ifdef HAVE_ATHENA
717   /* Check all text widgets, since we don't have callbacks for these. */
718   res_min_cb (timeout_text,     (XtPointer) &res.timeout,     NULL);
719   res_min_cb (cycle_text,       (XtPointer) &res.cycle,       NULL);
720   res_sec_cb (fade_text,        (XtPointer) &res.secs,        NULL);
721   res_int_cb (ticks_text,       (XtPointer) &res.ticks,       NULL);
722   res_min_cb (lock_time_text,   (XtPointer) &res.lock_time,   NULL);
723   res_sec_cb (passwd_time_text, (XtPointer) &res.passwd_time, NULL);
724 #endif /* HAVE_ATHENA */
725
726   /* Throttle the timeouts to minimum sane values. */
727   if (res.timeout < 5) res.timeout = 5;
728   if (res.cycle < 2) res.cycle = 2;
729   if (res.passwd_time < 10) res.passwd_time = 10;
730
731   p->timeout = res.timeout * 1000;
732   p->cycle = res.cycle * 1000;
733   p->lock_timeout = res.lock_time * 1000;
734 #ifndef NO_LOCKING
735   p->passwd_timeout = res.passwd_time * 1000;
736 #endif
737   p->fade_seconds = res.secs;
738   p->fade_ticks = res.ticks;
739   p->verbose_p = res.verb;
740   p->install_cmap_p = res.cmap;
741   p->fade_p = res.fade;
742   p->unfade_p = res.unfade;
743   p->lock_p = res.lock_p;
744
745   if (p->debug_p && p->verbose_p)
746     fprintf (stderr, "%s: parameters changed:\n\
747         timeout: %d\n\tcycle:   %d\n\tlock:    %d\n\tpasswd:  %d\n\
748         fade:    %d\n\tfade:    %d\n\tverbose: %d\n\tinstall: %d\n\
749         fade:    %d\n\tunfade:  %d\n\tlock:    %d\n",
750              progname, p->timeout, p->cycle, p->lock_timeout,
751 #ifdef NO_LOCKING
752              0,
753 #else
754              p->passwd_timeout,
755 #endif
756              p->fade_seconds, p->fade_ticks, p->verbose_p, p->install_cmap_p,
757              p->fade_p, p->unfade_p, p->lock_p);
758
759
760 #if defined(HAVE_MIT_SAVER_EXTENSION) || defined(HAVE_SGI_SAVER_EXTENSION)
761   if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
762     {
763       /* Need to set the server timeout to the new one the user has picked.
764        */
765       int server_timeout, server_interval, prefer_blank, allow_exp;
766       XGetScreenSaver (si->dpy, &server_timeout, &server_interval,
767                        &prefer_blank, &allow_exp);
768       if (server_timeout != (p->timeout / 1000))
769         {
770           server_timeout = (p->timeout / 1000);
771           if (p->verbose_p)
772             fprintf (stderr,
773                    "%s: configuring server for saver timeout of %d seconds.\n",
774                      progname, server_timeout);
775           /* Leave all other parameters the same. */
776           XSetScreenSaver (si->dpy, server_timeout, server_interval,
777                            prefer_blank, allow_exp);
778         }
779     }
780 #endif /* HAVE_MIT_SAVER_EXTENSION || HAVE_SGI_SAVER_EXTENSION */
781 }
782
783
784 static void
785 make_resources_dialog (saver_info *si, Widget parent)
786 {
787   saver_screen_info *ssi = si->default_screen;
788
789   if (ssi->demo_cmap &&
790       ssi->demo_cmap != ssi->cmap &&
791       ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
792     {
793       XFreeColormap (si->dpy, ssi->demo_cmap);
794       ssi->demo_cmap = 0;
795     }
796
797   if (ssi->default_visual == DefaultVisualOfScreen (ssi->screen))
798     ssi->demo_cmap = DefaultColormapOfScreen (ssi->screen);
799   else
800     ssi->demo_cmap = XCreateColormap (si->dpy,
801                                      RootWindowOfScreen (ssi->screen),
802                                      ssi->default_visual, AllocNone);
803
804   create_resources_dialog (parent, ssi->default_visual, ssi->demo_cmap);
805
806   add_button_callback (res_done,   res_done_cb,   (XtPointer) si);
807   add_button_callback (res_cancel, res_cancel_cb, (XtPointer) si);
808
809 #define CB(widget,type,slot) \
810         add_text_callback ((widget), (type), (XtPointer) (slot))
811 #define CBT(widget,type,slot) \
812         add_toggle_callback ((widget), (type), (XtPointer) (slot))
813
814 #ifdef HAVE_MOTIF
815   /* When using Athena widgets, we can't set callbacks for these,
816      so we'll check them all if "done" gets pressed.
817    */
818   CB (timeout_text,     res_min_cb,  &res.timeout);
819   CB (cycle_text,       res_min_cb,  &res.cycle);
820   CB (fade_text,        res_sec_cb,  &res.secs);
821   CB (ticks_text,       res_int_cb,  &res.ticks);
822   CB (lock_time_text,   res_min_cb,  &res.lock_time);
823   CB (passwd_time_text, res_sec_cb,  &res.passwd_time);
824 #endif /* HAVE_MOTIF */
825
826   CBT (verbose_toggle,  res_bool_cb, &res.verb);
827   CBT (cmap_toggle,     res_bool_cb, &res.cmap);
828   CBT (fade_toggle,     res_bool_cb, &res.fade);
829   CBT (unfade_toggle,   res_bool_cb, &res.unfade);
830   CBT (lock_toggle,     res_bool_cb, &res.lock_p);
831 #undef CB
832 #undef CBT
833
834   if (si->locking_disabled_p)
835     {
836       disable_widget (passwd_time_text);
837       disable_widget (lock_time_text);
838       disable_widget (lock_toggle);
839     }
840   if (CellsOfScreen (XtScreen (parent)) <= 2)
841     {
842       disable_widget (fade_text);
843       disable_widget (ticks_text);
844       disable_widget (cmap_toggle);
845       disable_widget (fade_toggle);
846       disable_widget (unfade_toggle);
847     }
848 }
849
850
851 static void
852 fmt_time (char *buf, unsigned int s, int min_p)
853 {
854   unsigned int h = 0, m = 0;
855   if (s >= 60)
856     {
857       m += (s / 60);
858       s %= 60;
859     }
860   if (m >= 60)
861     {
862       h += (m / 60);
863       m %= 60;
864     }
865 /*
866   if (min_p && h == 0 && s == 0)
867     sprintf (buf, "%u", m);
868   else if (!min_p && h == 0 && m == 0)
869     sprintf (buf, "%u", s);
870   else
871   if (h == 0)
872     sprintf (buf, "%u:%02u", m, s);
873   else
874 */
875     sprintf (buf, "%u:%02u:%02u", h, m, s);
876 }
877
878 static void
879 pop_resources_dialog (saver_info *si)
880 {
881   saver_preferences *p = &si->prefs;
882   char buf [100];
883
884   res.timeout = p->timeout / 1000;
885   res.cycle = p->cycle / 1000;
886   res.lock_time = p->lock_timeout / 1000;
887 #ifndef NO_LOCKING
888   res.passwd_time = p->passwd_timeout / 1000;
889 #endif
890   res.secs = p->fade_seconds;
891   res.ticks = p->fade_ticks;
892   res.verb = p->verbose_p;
893   res.cmap = p->install_cmap_p;
894   res.fade = p->fade_p;
895   res.unfade = p->unfade_p;
896   res.lock_p = (p->lock_p && !si->locking_disabled_p);
897
898   fmt_time (buf, res.timeout, 1);     set_text_string (timeout_text, buf);
899   fmt_time (buf, res.cycle, 1);       set_text_string (cycle_text, buf);
900   fmt_time (buf, res.lock_time, 1);   set_text_string (lock_time_text, buf);
901   fmt_time (buf, res.passwd_time, 0); set_text_string (passwd_time_text, buf);
902   fmt_time (buf, res.secs, 0);        set_text_string (fade_text, buf);
903   sprintf (buf, "%u", res.ticks);     set_text_string (ticks_text, buf);
904
905   set_toggle_button_state (verbose_toggle, res.verb);
906   set_toggle_button_state (cmap_toggle, res.cmap);
907   set_toggle_button_state (fade_toggle, res.fade);
908   set_toggle_button_state (unfade_toggle, res.unfade);
909   set_toggle_button_state (lock_toggle, res.lock_p);
910
911   pop_up_dialog_box (resources_dialog, resources_form,
912                      /* for debugging -- don't ask */
913                      (si->prefs.debug_p ? 69 : 0) +
914                      1);
915 }
916
917 \f
918 /* The main demo-mode command loop.
919  */
920
921 void
922 demo_mode (saver_info *si)
923 {
924   saver_preferences *p = &si->prefs;
925   si->dbox_up_p = True;
926   initialize_screensaver_window (si);
927   raise_window (si, True, False, False);
928   make_screenhack_dialog (si);
929   while (si->demo_mode_p)
930     {
931       XEvent event;
932       XtAppNextEvent (si->app, &event);
933       switch (event.xany.type)
934         {
935         case 0:         /* synthetic "timeout" event */
936           break;
937
938         case ClientMessage:
939           handle_clientmessage (si, &event, False);
940           break;
941
942         case CreateNotify:
943           if (!p->use_xidle_extension &&
944               !p->use_mit_saver_extension &&
945               !p->use_sgi_saver_extension)
946             {
947               start_notice_events_timer (si, event.xcreatewindow.window);
948 #ifdef DEBUG_TIMERS
949               if (p->verbose_p)
950                 printf ("%s: starting notice_events_timer for 0x%X (%lu)\n",
951                         progname,
952                         (unsigned int) event.xcreatewindow.window,
953                         p->notice_events_timeout);
954 #endif /* DEBUG_TIMERS */
955             }
956           break;
957
958         case ButtonPress:
959         case ButtonRelease:
960           if (!XtWindowToWidget (si->dpy, event.xbutton.window))
961             raise_screenhack_dialog ();
962           /* fall through */
963
964         default:
965 #ifdef HAVE_MIT_SAVER_EXTENSION
966           if (event.type == si->mit_saver_ext_event_number)
967             {
968               /* Get the "real" server window(s) out of the way as soon
969                  as possible. */
970               int i = 0;
971               for (i = 0; i < si->nscreens; i++)
972                 {
973                   saver_screen_info *ssi = &si->screens[i];
974                   if (ssi->server_mit_saver_window &&
975                       window_exists_p (si->dpy, ssi->server_mit_saver_window))
976                     XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
977                 }
978             }
979           else
980 #endif /* HAVE_MIT_SAVER_EXTENSION */
981
982           XtDispatchEvent (&event);
983           break;
984         }
985     }
986   destroy_screenhack_dialogs (si);
987   initialize_screensaver_window (si);
988
989   si->demo_mode_p = True;  /* kludge to inhibit unfade... */
990   unblank_screen (si);
991   si->demo_mode_p = False;
992 }
993
994 static void
995 demo_mode_hack (saver_info *si, char *hack)
996 {
997   if (! si->demo_mode_p) abort ();
998   kill_screenhack (si);
999   if (! si->demo_hack)
1000     blank_screen (si);
1001   si->demo_hack = hack;
1002   spawn_screenhack (si, False);
1003   /* raise_screenhack_dialog(); */
1004 }
1005
1006 static void
1007 demo_mode_done (saver_info *si)
1008 {
1009   kill_screenhack (si);
1010   if (si->demo_hack)
1011     unblank_screen (si);
1012   si->demo_mode_p = False;
1013   si->dbox_up_p = False;
1014   si->demo_hack = 0;
1015 }