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