ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-2.07.tar.gz
[xscreensaver] / driver / demo.c
diff --git a/driver/demo.c b/driver/demo.c
new file mode 100644 (file)
index 0000000..a0ae2da
--- /dev/null
@@ -0,0 +1,978 @@
+/* demo.c --- implements the interactive demo-mode and options dialogs.
+ * xscreensaver, Copyright (c) 1993-1997 Jamie Zawinski <jwz@netscape.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <X11/Intrinsic.h>
+
+#ifdef HAVE_MOTIF
+# include <Xm/Xm.h>
+# include <Xm/Text.h>
+# include <Xm/List.h>
+# include <Xm/ToggleB.h>
+
+#else  /* HAVE_ATHENA */
+  /* Athena demo code contributed by Jon A. Christopher <jac8782@tamu.edu> */
+  /* Copyright 1997, with the same permissions as above. */
+# include <X11/StringDefs.h>
+# include <X11/Shell.h>
+# include <X11/Xaw/Form.h>
+# include <X11/Xaw/Box.h>
+# include <X11/Xaw/List.h>
+# include <X11/Xaw/Command.h>
+# include <X11/Xaw/Toggle.h>
+# include <X11/Xaw/Viewport.h>
+# include <X11/Xaw/Dialog.h>
+# include <X11/Xaw/Scrollbar.h>
+#endif /* HAVE_ATHENA */
+
+#include "xscreensaver.h"
+#include "resources.h"         /* for parse_time() */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+static void demo_mode_hack (saver_info *si, char *);
+static void demo_mode_done (saver_info *si);
+
+extern Widget demo_dialog;
+extern Widget label1;
+extern Widget text_line;
+extern Widget demo_form;
+extern Widget demo_list;
+extern Widget next, prev, done, restart, edit;
+
+extern Widget resources_dialog;
+extern Widget resources_form;
+extern Widget res_done, res_cancel;
+extern Widget timeout_text, cycle_text, fade_text, ticks_text;
+extern Widget lock_time_text, passwd_time_text;
+extern Widget verbose_toggle, cmap_toggle, fade_toggle, unfade_toggle,
+  lock_toggle;
+
+
+#ifdef HAVE_MOTIF
+
+# define set_toggle_button_state(toggle,state) \
+  XmToggleButtonSetState ((toggle), (state), True)
+# define set_text_string(text_widget,string) \
+  XmTextSetString ((text_widget), (string))
+# define add_button_callback(button,cb,arg) \
+  XtAddCallback ((button), XmNactivateCallback, (cb), (arg))
+# define add_toggle_callback(button,cb,arg) \
+  XtAddCallback ((button), XmNvalueChangedCallback, (cb), (arg))
+# define add_text_callback add_toggle_callback
+
+#else  /* HAVE_ATHENA */
+
+# define set_toggle_button_state(toggle,state) \
+  XtVaSetValues((toggle), XtNstate, (state),  0)
+# define set_text_string(text_widget,string) \
+  XtVaSetValues ((text_widget), XtNvalue, (string), 0)
+# define add_button_callback(button,cb,arg) \
+  XtAddCallback ((button), XtNcallback, (cb), (arg))
+# define add_toggle_callback add_button_callback
+# define add_text_callback(b,c,a) ERROR!
+
+#endif /* HAVE_ATHENA */
+
+
+#define disable_widget(widget) \
+  XtVaSetValues((widget), XtNsensitive, False, 0)
+
+
+static char *
+get_text_string (Widget text_widget)
+{
+#ifdef HAVE_MOTIF
+  return XmTextGetString (text_widget);
+#else  /* HAVE_ATHENA */
+  char *string = 0;
+  XtVaGetValues (text_widget, XtNvalue, &string, 0);
+  return string;
+#endif /* HAVE_ATHENA */
+}
+
+static char *
+get_label_string (Widget label_widget)
+{
+#ifdef HAVE_MOTIF
+  char *label = 0;
+  XmString xm_label = 0;
+  XtVaGetValues (label_widget, XmNlabelString, &xm_label, 0);
+  if (!xm_label)
+    return 0;
+  XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
+  return label;
+#else  /* HAVE_ATHENA */
+  char *label = 0;
+  XtVaGetValues (label_widget, XtNlabel, &label, 0);
+  return (label ? strdup(label) : 0);
+#endif /* HAVE_ATHENA */
+}
+
+
+static void
+set_label_string (Widget label_widget, char *string)
+{
+#ifdef HAVE_MOTIF
+  XmString xm_string = XmStringCreate (string, XmSTRING_DEFAULT_CHARSET);
+  XtVaSetValues (label_widget, XmNlabelString, xm_string, 0);
+  XmStringFree (xm_string);
+#else  /* HAVE_ATHENA */
+  XtVaSetValues (label_widget, XtNlabel, string, 0);
+#endif /* HAVE_ATHENA */
+}
+
+
+void
+format_into_label (Widget label, const char *arg)
+{
+  char *text = get_label_string (label);
+  char *buf = (char *) malloc ((text ? strlen(text) : 100) + strlen(arg) + 10);
+
+  if (!text || !strcmp (text, XtName (label)))
+      strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
+    else
+      sprintf (buf, text, arg);
+
+    set_label_string (label, buf);
+    free (buf);
+    XtFree (text);
+}
+
+
+void
+steal_focus_and_colormap (Widget dialog)
+{
+  Display *dpy = XtDisplay (dialog);
+  Window window = XtWindow (dialog);
+  Colormap cmap = 0;
+  XSetInputFocus (dpy, window, RevertToParent, CurrentTime);
+
+  XtVaGetValues (dialog, XtNcolormap, &cmap, 0);
+  if (cmap)
+    XInstallColormap (dpy, cmap);
+}
+
+static void
+raise_screenhack_dialog (void)
+{
+  XMapRaised (XtDisplay (demo_dialog), XtWindow (demo_dialog));
+  if (resources_dialog)
+    XMapRaised (XtDisplay (resources_dialog), XtWindow (resources_dialog));
+  steal_focus_and_colormap (resources_dialog ? resources_dialog : demo_dialog);
+}
+
+static void
+destroy_screenhack_dialogs (saver_info *si)
+{
+  saver_screen_info *ssi = si->default_screen;
+
+  if (demo_dialog) XtDestroyWidget (demo_dialog);
+  if (resources_dialog) XtDestroyWidget (resources_dialog);
+  demo_dialog = resources_dialog = 0;
+
+  if (ssi->demo_cmap &&
+      ssi->demo_cmap != ssi->cmap &&
+      ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
+    {
+      XFreeColormap (si->dpy, ssi->demo_cmap);
+      ssi->demo_cmap = 0;
+    }
+
+  /* Since we installed our colormap to display the dialogs properly, put
+     the old one back, so that the screensaver_window is now displayed
+     properly. */
+  if (ssi->cmap)
+    XInstallColormap (si->dpy, ssi->cmap);
+}
+
+#ifdef HAVE_MOTIF
+
+static void
+text_cb (Widget text_widget, XtPointer client_data, XtPointer call_data)
+{
+  saver_info *si = (saver_info *) client_data;
+  char *line;
+  line = get_text_string (text_widget);
+  demo_mode_hack (si, line);
+}
+
+#endif /* HAVE_MOTIF */
+
+
+static void
+select_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  saver_info *si = (saver_info *) client_data;
+
+#ifdef HAVE_ATHENA
+  XawListReturnStruct *item = (XawListReturnStruct*)call_data;
+  demo_mode_hack (si, item->string);
+#else  /* HAVE_MOTIF */
+  XmListCallbackStruct *lcb = (XmListCallbackStruct *) call_data;
+  char *string = 0;
+  if (lcb->item)
+    XmStringGetLtoR (lcb->item, XmSTRING_DEFAULT_CHARSET, &string);
+  set_text_string (text_line, (string ? string : ""));
+  if (lcb->reason == XmCR_DEFAULT_ACTION && string)
+    demo_mode_hack (si, string);
+  if (string)
+    XtFree (string);
+#endif /* HAVE_MOTIF */
+  steal_focus_and_colormap (demo_dialog);
+}
+
+
+#if 0  /* configure does this now */
+#ifdef HAVE_ATHENA
+# if !defined(_Viewport_h)
+   /* The R4 Athena libs don't have this function.  I don't know the right
+      way to tell, but I note that the R5 version of Viewport.h defines
+      _XawViewport_h, while the R4 version defines _Viewport_h.  So we'll
+     try and key off of that... */
+#  define HAVE_XawViewportSetCoordinates
+# endif
+#endif /* HAVE_ATHENA */
+#endif /* 0 */
+
+
+/* Why this behavior isn't automatic in *either* toolkit, I'll never know.
+ */
+static void
+ensure_selected_item_visible (Widget list)
+{
+#ifdef HAVE_MOTIF
+  int *pos_list = 0;
+  int pos_count = 0;
+  if (XmListGetSelectedPos (list, &pos_list, &pos_count) && pos_count > 0)
+    {
+      int top = -2;
+      int visible = 0;
+      XtVaGetValues (list,
+                    XmNtopItemPosition, &top,
+                    XmNvisibleItemCount, &visible,
+                    0);
+      if (pos_list[0] >= top + visible)
+       {
+         int pos = pos_list[0] - visible + 1;
+         if (pos < 0) pos = 0;
+         XmListSetPos (list, pos);
+       }
+      else if (pos_list[0] < top)
+       {
+         XmListSetPos (list, pos_list[0]);
+       }
+    }
+  if (pos_list)
+    XtFree ((char *) pos_list);
+
+#else  /* HAVE_ATHENA */
+# ifdef HAVE_XawViewportSetCoordinates
+
+  int margin = 16;     /* should be line height or something. */
+  int count = 0;
+  int pos;
+  Dimension list_h = 0, vp_h = 0;
+  Dimension top_margin = 4;  /* I don't know where this value comes from */
+  Position vp_x = 0, vp_y = 0, current_y;
+  double cratio;
+  Widget viewport = XtParent(demo_list);
+  Widget sb = (viewport ? XtNameToWidget(viewport, "*vertical") : 0);
+  float sb_top = 0, sb_size = 0;
+  XawListReturnStruct *current = XawListShowCurrent(demo_list);
+  if (!current || !sb) return;
+
+  XtVaGetValues(demo_list,
+               XtNnumberStrings, &count,
+               XtNheight, &list_h,
+               0);
+  if (count < 2 || list_h < 10) return;
+
+  XtVaGetValues(viewport, XtNheight, &vp_h, XtNx, &vp_x, XtNy, &vp_y, 0);
+  if (vp_h < 10) return;
+
+  XtVaGetValues(sb, XtNtopOfThumb, &sb_top, XtNshown, &sb_size, 0);
+  if (sb_size <= 0) return;
+
+  pos = current->list_index;
+  cratio = ((double) pos)  / ((double) count);
+  current_y = (cratio * list_h);
+
+  if (cratio < sb_top ||
+      cratio > sb_top + sb_size)
+    {
+      if (cratio < sb_top)
+       current_y -= (vp_h - margin - margin);
+      else
+       current_y -= margin;
+
+      if ((long)current_y >= (long) list_h)
+       current_y = (Position) ((long)list_h - (long)vp_h);
+
+      if ((long)current_y < (long)top_margin)
+       current_y = (Position)top_margin;
+
+      XawViewportSetCoordinates (viewport, vp_x, current_y);
+    }
+# endif /* HAVE_XawViewportSetCoordinates */
+#endif /* HAVE_ATHENA */
+}
+
+
+static void
+next_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  saver_info *si = (saver_info *) client_data;
+
+#ifdef HAVE_ATHENA
+  int cnt;
+  XawListReturnStruct *current = XawListShowCurrent(demo_list);
+  if (current->list_index == XAW_LIST_NONE)
+    XawListHighlight(demo_list,1);
+  else
+    {
+      XtVaGetValues(demo_list,
+                   XtNnumberStrings, &cnt,
+                   NULL);
+      if (current->list_index + 1 < cnt)
+       {
+         current->list_index++;
+         XawListHighlight(demo_list, current->list_index);
+       }
+    }
+
+  ensure_selected_item_visible (demo_list);
+  current = XawListShowCurrent(demo_list);
+  demo_mode_hack (si, current->string);
+
+#else  /* HAVE_MOTIF */
+
+  int *pos_list;
+  int pos_count;
+  if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
+    XmListSelectPos (demo_list, 1, True);
+  else
+    {
+      int pos = pos_list [0];
+      XmListSelectPos (demo_list, pos + 1, True);
+      XtFree ((char *) pos_list);
+      if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
+       abort ();
+      if (pos_list [0] == pos)
+       XmListSelectPos (demo_list, 1, True);
+      XtFree ((char *) pos_list);
+    }
+  ensure_selected_item_visible (demo_list);
+  demo_mode_hack (si, get_text_string (text_line));
+
+#endif /* HAVE_MOTIF */
+}
+
+
+static void
+prev_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  saver_info *si = (saver_info *) client_data;
+
+#ifdef HAVE_ATHENA
+  XawListReturnStruct *current=XawListShowCurrent(demo_list);
+  if (current->list_index == XAW_LIST_NONE)
+    XawListHighlight(demo_list,1);
+  else
+    {
+      if (current->list_index>=1)
+       {
+         current->list_index--;
+         XawListHighlight(demo_list, current->list_index);
+       }
+    }
+
+  ensure_selected_item_visible (demo_list);
+  current = XawListShowCurrent(demo_list);
+  demo_mode_hack (si, current->string);
+
+#else  /* HAVE_MOTIF */
+
+  int *pos_list;
+  int pos_count;
+  if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
+    XmListSelectPos (demo_list, 0, True);
+  else
+    {
+      XmListSelectPos (demo_list, pos_list [0] - 1, True);
+      XtFree ((char *) pos_list);
+    }
+  ensure_selected_item_visible (demo_list);
+  demo_mode_hack (si, get_text_string (text_line));
+
+#endif /* HAVE_MOTIF */
+}
+
+
+static void pop_resources_dialog (saver_info *si);
+static void make_resources_dialog (saver_info *si, Widget parent);
+
+static void
+edit_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  saver_info *si = (saver_info *) client_data;
+  saver_screen_info *ssi = si->default_screen;
+  Widget parent = ssi->toplevel_shell;
+  if (! resources_dialog)
+    make_resources_dialog (si, parent);
+  pop_resources_dialog (si);
+}
+
+static void
+done_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  saver_info *si = (saver_info *) client_data;
+  demo_mode_done (si);
+}
+
+
+static void
+restart_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  saver_info *si = (saver_info *) client_data;
+  demo_mode_restart_process (si);
+}
+
+void
+pop_up_dialog_box (Widget dialog, Widget form, int where)
+{
+  /* I'm sure this is the wrong way to pop up a dialog box, but I can't
+     figure out how else to do it.
+
+     It's important that the screensaver dialogs not get decorated or
+     otherwise reparented by the window manager, because they need to be
+     children of the *real* root window, not the WM's virtual root, in
+     order for us to guarentee that they are visible above the screensaver
+     window itself.
+   */
+  Arg av [100];
+  int ac = 0;
+  Dimension sw, sh, x, y, w, h;
+
+#ifdef HAVE_ATHENA
+  XtRealizeWidget (dialog);
+#else  /* HAVE_MOTIF */
+  /* Motif likes us to realize the *child* of the shell... */
+  XtRealizeWidget (form);
+#endif /* HAVE_MOTIF */
+
+  sw = WidthOfScreen (XtScreen (dialog));
+  sh = HeightOfScreen (XtScreen (dialog));
+  ac = 0;
+  XtSetArg (av [ac], XtNwidth, &w); ac++;
+  XtSetArg (av [ac], XtNheight, &h); ac++;
+  XtGetValues (form, av, ac);
+
+#ifdef DEBUG
+  if (where >= 69)
+    {
+      where -= 69;
+      sw = (sw * 7) / 12;
+    }
+#endif
+
+  switch (where)
+    {
+    case 0:    /* center it in the top-right quadrant */
+      x = (sw/2 + w) / 2 + (sw/2) - w;
+      y = (sh/2 + h) / 2 - h;
+      break;
+    case 1:    /* center it in the bottom-right quadrant */
+      x = (sw/2 + w) / 2 + (sw/2) - w;
+      y = (sh/2 + h) / 2 + (sh/2) - h;
+      break;
+    case 2:    /* center it on the screen */
+      x = (sw + w) / 2 - w;
+      y = (sh + h) / 2 - h;
+      break;
+    default:
+      abort ();
+    }
+  if (x + w > sw) x = sw - w;
+  if (y + h > sh) y = sh - h;
+  ac = 0;
+  XtSetArg (av [ac], XtNx, x); ac++;
+  XtSetArg (av [ac], XtNy, y); ac++;
+  XtSetArg (av [ac], XtNoverrideRedirect, True); ac++;
+
+#ifdef HAVE_MOTIF
+  XtSetArg (av [ac], XmNdefaultPosition, False); ac++;
+#endif /* HAVE_MOTIF */
+
+  XtSetValues (dialog, av, ac);
+  XtSetValues (form, av, ac);
+
+#ifdef HAVE_ATHENA
+  XtPopup (dialog, XtGrabNone);
+#else  /* HAVE_MOTIF */
+  XtManageChild (form);
+#endif /* HAVE_MOTIF */
+
+  steal_focus_and_colormap (dialog);
+}
+
+
+static void
+make_screenhack_dialog (saver_info *si)
+{
+  saver_screen_info *ssi = si->default_screen;
+  Widget parent = ssi->toplevel_shell;
+  char **hacks = si->prefs.screenhacks;
+
+  if (ssi->demo_cmap &&
+      ssi->demo_cmap != ssi->cmap &&
+      ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
+    {
+      XFreeColormap (si->dpy, ssi->demo_cmap);
+      ssi->demo_cmap = 0;
+    }
+
+  if (ssi->default_visual == DefaultVisualOfScreen (ssi->screen))
+    ssi->demo_cmap = DefaultColormapOfScreen (ssi->screen);
+  else
+    ssi->demo_cmap = XCreateColormap (si->dpy,
+                                     RootWindowOfScreen (ssi->screen),
+                                     ssi->default_visual, AllocNone);
+
+  create_demo_dialog (parent, ssi->default_visual, ssi->demo_cmap);
+  format_into_label (label1, si->version);
+
+  add_button_callback (next,    next_cb,    (XtPointer) si);
+  add_button_callback (prev,    prev_cb,    (XtPointer) si);
+  add_button_callback (done,    done_cb,    (XtPointer) si);
+  add_button_callback (restart, restart_cb, (XtPointer) si);
+  add_button_callback (edit,    edit_cb,    (XtPointer) si);
+
+#ifdef HAVE_MOTIF
+  XtAddCallback (demo_list, XmNbrowseSelectionCallback,
+                select_cb, (XtPointer) si);
+  XtAddCallback (demo_list, XmNdefaultActionCallback,
+                select_cb, (XtPointer) si);
+  XtAddCallback (text_line, XmNactivateCallback, text_cb, (XtPointer) si);
+
+  for (; *hacks; hacks++)
+    {
+      XmString xmstr = XmStringCreate (*hacks, XmSTRING_DEFAULT_CHARSET);
+      XmListAddItem (demo_list, xmstr, 0);
+      XmStringFree (xmstr);
+    }
+
+#else  /* HAVE_ATHENA */
+
+  XtVaSetValues (demo_list,
+                XtNlist, hacks,
+                XtNnumberStrings, si->prefs.screenhacks_count,
+                0);
+  XtAddCallback (demo_list, XtNcallback, select_cb, si);
+
+#endif /* HAVE_ATHENA */
+
+  pop_up_dialog_box(demo_dialog, demo_form,
+#ifdef DEBUG
+                   (si->prefs.debug_p ? 69 : 0) +
+#endif
+                   0);
+}
+
+\f
+/* the Screensaver Parameters dialog */
+
+static struct resources {
+  int timeout, cycle, secs, ticks, lock_time, passwd_time;
+  int verb, cmap, fade, unfade, lock_p;
+} res;
+
+
+static void 
+hack_time_cb (Display *dpy, char *line, int *store, Bool sec_p)
+{
+  if (*line)
+    {
+      int value;
+      value = parse_time (line, sec_p, True);
+      if (value < 0)
+       /*XBell (dpy, 0)*/;
+      else
+       *store = value;
+    }
+}
+
+static void
+res_sec_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  hack_time_cb (XtDisplay (button), get_text_string (button),
+               (int *) client_data, True);
+}
+
+static void
+res_min_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  hack_time_cb (XtDisplay (button), get_text_string (button),
+               (int *) client_data, False);
+}
+
+static void
+res_int_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  char *line = get_text_string (button);
+  int *store = (int *) client_data;
+  unsigned int value;
+  char c;
+  if (! *line)
+    ;
+  else if (sscanf (line, "%u%c", &value, &c) != 1)
+    XBell (XtDisplay (button), 0);
+  else
+    *store = value;
+}
+
+static void
+res_bool_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  int *store = (int *) client_data;
+#ifdef HAVE_MOTIF
+  *store = ((XmToggleButtonCallbackStruct *) call_data)->set;
+#else /* HAVE_ATHENA */
+  Boolean state = FALSE;
+  XtVaGetValues (button, XtNstate, &state, NULL);
+  *store = state;
+#endif /* HAVE_ATHENA */
+}
+
+static void
+res_cancel_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  XtDestroyWidget (resources_dialog);
+  resources_dialog = 0;
+  raise_screenhack_dialog ();
+}
+
+
+static void
+res_done_cb (Widget button, XtPointer client_data, XtPointer call_data)
+{
+  saver_info *si = (saver_info *) client_data;
+  saver_preferences *p = &si->prefs;
+
+  res_cancel_cb (button, client_data, call_data);
+
+#ifdef HAVE_ATHENA
+  /* Check all text widgets, since we don't have callbacks for these. */
+  res_min_cb (timeout_text,     (XtPointer) &res.timeout,     NULL);
+  res_min_cb (cycle_text,       (XtPointer) &res.cycle,       NULL);
+  res_sec_cb (fade_text,        (XtPointer) &res.secs,        NULL);
+  res_int_cb (ticks_text,       (XtPointer) &res.ticks,       NULL);
+  res_min_cb (lock_time_text,   (XtPointer) &res.lock_time,   NULL);
+  res_sec_cb (passwd_time_text, (XtPointer) &res.passwd_time, NULL);
+#endif /* HAVE_ATHENA */
+
+  /* Throttle the timeouts to minimum sane values. */
+  if (res.timeout < 5) res.timeout = 5;
+  if (res.cycle < 2) res.cycle = 2;
+  if (res.passwd_time < 10) res.passwd_time = 10;
+
+  p->timeout = res.timeout * 1000;
+  p->cycle = res.cycle * 1000;
+  p->lock_timeout = res.lock_time * 1000;
+#ifndef NO_LOCKING
+  p->passwd_timeout = res.passwd_time * 1000;
+#endif
+  p->fade_seconds = res.secs;
+  p->fade_ticks = res.ticks;
+  p->verbose_p = res.verb;
+  p->install_cmap_p = res.cmap;
+  p->fade_p = res.fade;
+  p->unfade_p = res.unfade;
+  p->lock_p = res.lock_p;
+
+#ifdef DEBUG
+  if (p->debug_p && p->verbose_p)
+    fprintf (stderr, "%s: parameters changed:\n\
+       timeout: %d\n\tcycle:   %d\n\tlock:    %d\n\tpasswd:  %d\n\
+       fade:    %d\n\tfade:    %d\n\tverbose: %d\n\tinstall: %d\n\
+       fade:    %d\n\tunfade:  %d\n\tlock:    %d\n",
+            progname, p->timeout, p->cycle, p->lock_timeout,
+# ifdef NO_LOCKING
+            0,
+# else
+            p->passwd_timeout,
+# endif
+            p->fade_seconds, p->fade_ticks, p->verbose_p, p->install_cmap_p,
+            p->fade_p, p->unfade_p, p->lock_p);
+#endif /* DEBUG */
+
+#if defined(HAVE_MIT_SAVER_EXTENSION) || defined(HAVE_SGI_SAVER_EXTENSION)
+  if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
+    {
+      /* Need to set the server timeout to the new one the user has picked.
+       */
+      int server_timeout, server_interval, prefer_blank, allow_exp;
+      XGetScreenSaver (si->dpy, &server_timeout, &server_interval,
+                      &prefer_blank, &allow_exp);
+      if (server_timeout != (p->timeout / 1000))
+       {
+         server_timeout = (p->timeout / 1000);
+         if (p->verbose_p)
+           fprintf (stderr,
+                  "%s: configuring server for saver timeout of %d seconds.\n",
+                    progname, server_timeout);
+         /* Leave all other parameters the same. */
+         XSetScreenSaver (si->dpy, server_timeout, server_interval,
+                          prefer_blank, allow_exp);
+       }
+    }
+#endif /* HAVE_MIT_SAVER_EXTENSION || HAVE_SGI_SAVER_EXTENSION */
+}
+
+
+static void
+make_resources_dialog (saver_info *si, Widget parent)
+{
+  saver_screen_info *ssi = si->default_screen;
+
+  if (ssi->demo_cmap &&
+      ssi->demo_cmap != ssi->cmap &&
+      ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
+    {
+      XFreeColormap (si->dpy, ssi->demo_cmap);
+      ssi->demo_cmap = 0;
+    }
+
+  if (ssi->default_visual == DefaultVisualOfScreen (ssi->screen))
+    ssi->demo_cmap = DefaultColormapOfScreen (ssi->screen);
+  else
+    ssi->demo_cmap = XCreateColormap (si->dpy,
+                                    RootWindowOfScreen (ssi->screen),
+                                    ssi->default_visual, AllocNone);
+
+  create_resources_dialog (parent, ssi->default_visual, ssi->demo_cmap);
+
+  add_button_callback (res_done,   res_done_cb,   (XtPointer) si);
+  add_button_callback (res_cancel, res_cancel_cb, (XtPointer) si);
+
+#define CB(widget,type,slot) \
+       add_text_callback ((widget), (type), (XtPointer) (slot))
+#define CBT(widget,type,slot) \
+       add_toggle_callback ((widget), (type), (XtPointer) (slot))
+
+#ifdef HAVE_MOTIF
+  /* When using Athena widgets, we can't set callbacks for these,
+     so we'll check them all if "done" gets pressed.
+   */
+  CB (timeout_text,    res_min_cb,  &res.timeout);
+  CB (cycle_text,      res_min_cb,  &res.cycle);
+  CB (fade_text,       res_sec_cb,  &res.secs);
+  CB (ticks_text,      res_int_cb,  &res.ticks);
+  CB (lock_time_text,  res_min_cb,  &res.lock_time);
+  CB (passwd_time_text,        res_sec_cb,  &res.passwd_time);
+#endif /* HAVE_MOTIF */
+
+  CBT (verbose_toggle, res_bool_cb, &res.verb);
+  CBT (cmap_toggle,    res_bool_cb, &res.cmap);
+  CBT (fade_toggle,    res_bool_cb, &res.fade);
+  CBT (unfade_toggle,  res_bool_cb, &res.unfade);
+  CBT (lock_toggle,    res_bool_cb, &res.lock_p);
+#undef CB
+#undef CBT
+
+  if (si->locking_disabled_p)
+    {
+      disable_widget (passwd_time_text);
+      disable_widget (lock_time_text);
+      disable_widget (lock_toggle);
+    }
+  if (CellsOfScreen (XtScreen (parent)) <= 2)
+    {
+      disable_widget (fade_text);
+      disable_widget (ticks_text);
+      disable_widget (cmap_toggle);
+      disable_widget (fade_toggle);
+      disable_widget (unfade_toggle);
+    }
+}
+
+
+static void
+fmt_time (char *buf, unsigned int s, int min_p)
+{
+  unsigned int h = 0, m = 0;
+  if (s >= 60)
+    {
+      m += (s / 60);
+      s %= 60;
+    }
+  if (m >= 60)
+    {
+      h += (m / 60);
+      m %= 60;
+    }
+/*
+  if (min_p && h == 0 && s == 0)
+    sprintf (buf, "%u", m);
+  else if (!min_p && h == 0 && m == 0)
+    sprintf (buf, "%u", s);
+  else
+  if (h == 0)
+    sprintf (buf, "%u:%02u", m, s);
+  else
+*/
+    sprintf (buf, "%u:%02u:%02u", h, m, s);
+}
+
+static void
+pop_resources_dialog (saver_info *si)
+{
+  saver_preferences *p = &si->prefs;
+  char buf [100];
+
+  res.timeout = p->timeout / 1000;
+  res.cycle = p->cycle / 1000;
+  res.lock_time = p->lock_timeout / 1000;
+#ifndef NO_LOCKING
+  res.passwd_time = p->passwd_timeout / 1000;
+#endif
+  res.secs = p->fade_seconds;
+  res.ticks = p->fade_ticks;
+  res.verb = p->verbose_p;
+  res.cmap = p->install_cmap_p;
+  res.fade = p->fade_p;
+  res.unfade = p->unfade_p;
+  res.lock_p = (p->lock_p && !si->locking_disabled_p);
+
+  fmt_time (buf, res.timeout, 1);     set_text_string (timeout_text, buf);
+  fmt_time (buf, res.cycle, 1);       set_text_string (cycle_text, buf);
+  fmt_time (buf, res.lock_time, 1);   set_text_string (lock_time_text, buf);
+  fmt_time (buf, res.passwd_time, 0); set_text_string (passwd_time_text, buf);
+  fmt_time (buf, res.secs, 0);        set_text_string (fade_text, buf);
+  sprintf (buf, "%u", res.ticks);     set_text_string (ticks_text, buf);
+
+  set_toggle_button_state (verbose_toggle, res.verb);
+  set_toggle_button_state (cmap_toggle, res.cmap);
+  set_toggle_button_state (fade_toggle, res.fade);
+  set_toggle_button_state (unfade_toggle, res.unfade);
+  set_toggle_button_state (lock_toggle, res.lock_p);
+
+  pop_up_dialog_box (resources_dialog, resources_form,
+#ifdef DEBUG
+                    (si->prefs.debug_p ? 69 : 0) +
+#endif
+                    1);
+}
+
+\f
+/* The main demo-mode command loop.
+ */
+
+void
+demo_mode (saver_info *si)
+{
+  saver_preferences *p = &si->prefs;
+  si->dbox_up_p = True;
+  initialize_screensaver_window (si);
+  raise_window (si, True, False, False);
+  make_screenhack_dialog (si);
+  while (si->demo_mode_p)
+    {
+      XEvent event;
+      XtAppNextEvent (si->app, &event);
+      switch (event.xany.type)
+       {
+       case 0:         /* synthetic "timeout" event */
+         break;
+
+       case ClientMessage:
+         handle_clientmessage (si, &event, False);
+         break;
+
+       case CreateNotify:
+         if (!p->use_xidle_extension &&
+             !p->use_mit_saver_extension &&
+             !p->use_sgi_saver_extension)
+           {
+             start_notice_events_timer (si, event.xcreatewindow.window);
+#ifdef DEBUG_TIMERS
+             if (p->verbose_p)
+               printf ("%s: starting notice_events_timer for 0x%X (%lu)\n",
+                       progname,
+                       (unsigned int) event.xcreatewindow.window,
+                       p->notice_events_timeout);
+#endif /* DEBUG_TIMERS */
+           }
+         break;
+
+       case ButtonPress:
+       case ButtonRelease:
+         if (!XtWindowToWidget (si->dpy, event.xbutton.window))
+           raise_screenhack_dialog ();
+         /* fall through */
+
+       default:
+#ifdef HAVE_MIT_SAVER_EXTENSION
+         if (event.type == si->mit_saver_ext_event_number)
+           {
+             /* Get the "real" server window(s) out of the way as soon
+                as possible. */
+             int i = 0;
+             for (i = 0; i < si->nscreens; i++)
+               {
+                 saver_screen_info *ssi = &si->screens[i];
+                 if (ssi->server_mit_saver_window &&
+                     window_exists_p (si->dpy, ssi->server_mit_saver_window))
+                   XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
+               }
+           }
+         else
+#endif /* HAVE_MIT_SAVER_EXTENSION */
+
+         XtDispatchEvent (&event);
+         break;
+       }
+    }
+  destroy_screenhack_dialogs (si);
+  initialize_screensaver_window (si);
+
+  si->demo_mode_p = True;  /* kludge to inhibit unfade... */
+  unblank_screen (si);
+  si->demo_mode_p = False;
+}
+
+static void
+demo_mode_hack (saver_info *si, char *hack)
+{
+  if (! si->demo_mode_p) abort ();
+  kill_screenhack (si);
+  if (! si->demo_hack)
+    blank_screen (si);
+  si->demo_hack = hack;
+  spawn_screenhack (si, False);
+  /* raise_screenhack_dialog(); */
+}
+
+static void
+demo_mode_done (saver_info *si)
+{
+  kill_screenhack (si);
+  if (si->demo_hack)
+    unblank_screen (si);
+  si->demo_mode_p = False;
+  si->dbox_up_p = False;
+  si->demo_hack = 0;
+}