http://ftp.x.org/contrib/applications/xscreensaver-2.24.tar.gz
[xscreensaver] / driver / demo.c
index 4d4a7b073eb63357e654f10d9d8131b254ca5d7b..10a177efbf258d2939d9b3dc400e21428f489c0c 100644 (file)
@@ -1,4 +1,5 @@
-/* xscreensaver, Copyright (c) 1993-1996 Jamie Zawinski <jwz@netscape.com>
+/* demo.c --- implements the interactive demo-mode and options dialogs.
+ * xscreensaver, Copyright (c) 1993-1998 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
  * implied warranty.
  */
 
-#include <X11/Intrinsic.h>
-
-#if !__STDC__
-# define _NO_PROTO
+#ifdef HAVE_CONFIG_H
+# include "config.h"
 #endif
 
-#include <Xm/Xm.h>
-#include <Xm/Text.h>
-#include <Xm/List.h>
-#include <Xm/ToggleB.h>
+#include <X11/Intrinsic.h>
+
+/* We don't actually use any widget internals, but these are included
+   so that gdb will have debug info for the widgets... */
+#include <X11/IntrinsicP.h>
+#include <X11/ShellP.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>
+# include <X11/Xaw/Text.h>
+#endif /* HAVE_ATHENA */
 
 #include "xscreensaver.h"
+#include "resources.h"         /* for parse_time() */
 #include <stdio.h>
+#include <string.h>
+#include <ctype.h>
 
-#ifdef HAVE_MIT_SAVER_EXTENSION
-extern int mit_saver_ext_event_number;
-extern Window server_mit_saver_window;
-#endif /* HAVE_MIT_SAVER_EXTENSION */
-
-#ifdef HAVE_SGI_SAVER_EXTENSION
-/* extern int sgi_saver_ext_event_number; */
-#endif /* HAVE_SGI_SAVER_EXTENSION */
-
-extern Bool use_mit_saver_extension;
-extern Bool use_sgi_saver_extension;
-
-extern Time timeout, cycle, lock_timeout;
-#ifndef NO_LOCKING
-extern Time passwd_timeout;
-#endif
-extern int fade_seconds, fade_ticks;
-extern Bool verbose_p, install_cmap_p, fade_p, unfade_p;
-extern Bool lock_p, locking_disabled_p;
-
-static void demo_mode_hack P((char *));
-static void demo_mode_done P((void));
-
-static void focus_fuckus P((Widget dialog));
-static void text_cb P((Widget button, XtPointer, XtPointer));
-
-extern void demo_mode_restart_process ();
+static void demo_mode_hack (saver_info *si, char *);
+static void demo_mode_done (saver_info *si);
 
 extern Widget demo_dialog;
 extern Widget label1;
@@ -66,61 +67,231 @@ extern Widget lock_time_text, passwd_time_text;
 extern Widget verbose_toggle, cmap_toggle, fade_toggle, unfade_toggle,
   lock_toggle;
 
-extern create_demo_dialog ();
-extern create_resources_dialog ();
+
+#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;
+  if (XtIsSubclass(text_widget, textWidgetClass))
+    XtVaGetValues (text_widget, XtNstring, &string, 0);
+  else if (XtIsSubclass(text_widget, dialogWidgetClass))
+    XtVaGetValues (text_widget, XtNvalue, &string, 0);
+  else
+    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
-focus_fuckus (dialog)
-     Widget dialog;
+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)
 {
-  XSetInputFocus (XtDisplay (dialog), XtWindow (dialog),
-                 RevertToParent, CurrentTime);
+  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 ()
+raise_screenhack_dialog (void)
 {
   XMapRaised (XtDisplay (demo_dialog), XtWindow (demo_dialog));
   if (resources_dialog)
     XMapRaised (XtDisplay (resources_dialog), XtWindow (resources_dialog));
-  focus_fuckus (resources_dialog ? resources_dialog : demo_dialog);
+  steal_focus_and_colormap (resources_dialog ? resources_dialog : demo_dialog);
 }
 
 static void
-destroy_screenhack_dialogs ()
+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);
 }
 
+
 static void
-text_cb (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+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);
+}
+
+
+#ifdef HAVE_ATHENA
+/* Bend over backwards to make hitting Return in the text field do the
+   right thing. 
+   */
+extern saver_info *global_si_kludge;
+static void text_enter (Widget w, XEvent *event, String *av, Cardinal *ac)
 {
-  char *line = XmTextGetString (button);
-  demo_mode_hack (line);
+  text_cb (w, global_si_kludge, 0);
 }
 
+static XtActionsRec actions[] = {{"done",      text_enter}
+                               };
+static char translations[] = ("<Key>Return:    done()\n"
+                             "<Key>Linefeed:   done()\n"
+                             "Ctrl<Key>M:      done()\n"
+                             "Ctrl<Key>J:      done()\n");
+#endif /* HAVE_ATHENA */
+
 
 static void
-select_cb (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+select_cb (Widget button, XtPointer client_data, XtPointer call_data)
 {
-  char **hacks = (char **) client_data;
+  saver_info *si = (saver_info *) client_data;
+
+#ifdef HAVE_ATHENA
+  XawListReturnStruct *item = (XawListReturnStruct*)call_data;
+  XtVaSetValues(text_line, XtNstring, item->string, 0);
+
+  demo_mode_hack (si, item->string);
+  if (item->list_index >= 0)
+    si->default_screen->current_hack = item->list_index;
+
+#else  /* HAVE_MOTIF */
   XmListCallbackStruct *lcb = (XmListCallbackStruct *) call_data;
-  XmTextSetString (text_line, hacks [lcb->item_position - 1]);
-  if (lcb->reason == XmCR_DEFAULT_ACTION)
-    text_cb (text_line, 0, 0);
-  focus_fuckus (demo_dialog);
+  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 (lcb->item_position > 0)
+       si->default_screen->current_hack = lcb->item_position - 1;
+    }
+  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 (list)
-     Widget list;
+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)
@@ -144,86 +315,189 @@ ensure_selected_item_visible (list)
     }
   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 (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+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, 0);
+  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);
+  XtVaSetValues(text_line, XtNstring, current->string, 0);
+  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);
+    {
+      XmListDeselectAllItems(demo_list);       /* LessTif lossage */
+      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);
+      int pos = pos_list[0] + 1;
+      if (pos > si->prefs.screenhacks_count)
+       pos = 1;
+      XmListDeselectAllItems(demo_list);       /* LessTif lossage */
+      XmListSelectPos (demo_list, pos, True);
     }
+  XtFree ((char *) pos_list);
   ensure_selected_item_visible (demo_list);
-  text_cb (text_line, 0, 0);
+  demo_mode_hack (si, get_text_string (text_line));
+
+#endif /* HAVE_MOTIF */
 }
 
+
 static void
-prev_cb (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+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, 0);
+  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);
+  XtVaSetValues(text_line, XtNstring, current->string, 0);
+  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);
+    {
+      XmListDeselectAllItems(demo_list);       /* LessTif lossage */
+      XmListSelectPos (demo_list, 0, True);
+    }
   else
     {
+      XmListDeselectAllItems(demo_list);       /* LessTif lossage */
       XmListSelectPos (demo_list, pos_list [0] - 1, True);
       XtFree ((char *) pos_list);
     }
   ensure_selected_item_visible (demo_list);
-  text_cb (text_line, 0, 0);
+  demo_mode_hack (si, get_text_string (text_line));
+
+#endif /* HAVE_MOTIF */
 }
 
 
-static void pop_resources_dialog ();
-static void make_resources_dialog ();
+static void pop_resources_dialog (saver_info *si);
+static void make_resources_dialog (saver_info *si, Widget parent);
 
 static void
-edit_cb (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+edit_cb (Widget button, XtPointer client_data, XtPointer call_data)
 {
-  Widget parent = (Widget) client_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 (parent);
-  pop_resources_dialog ();
+    make_resources_dialog (si, parent);
+  pop_resources_dialog (si);
 }
 
 static void
-done_cb (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+done_cb (Widget button, XtPointer client_data, XtPointer call_data)
 {
-  demo_mode_done ();
+  saver_info *si = (saver_info *) client_data;
+  demo_mode_done (si);
 }
 
 
 static void
-restart_cb (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+restart_cb (Widget button, XtPointer client_data, XtPointer call_data)
 {
-  demo_mode_restart_process ();
+  saver_info *si = (saver_info *) client_data;
+  demo_mode_restart_process (si);
 }
 
+
 void
-pop_up_dialog_box (dialog, form, where)
-     Widget dialog, form;
-     int where;
+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.
@@ -237,13 +511,28 @@ pop_up_dialog_box (dialog, form, where)
   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], XmNwidth, &w); ac++;
-  XtSetArg (av [ac], XmNheight, &h); ac++;
+  XtSetArg (av [ac], XtNwidth, &w); ac++;
+  XtSetArg (av [ac], XtNheight, &h); ac++;
   XtGetValues (form, av, ac);
+
+  /* for debugging -- don't ask */
+  if (where >= 69)
+    {
+      where -= 69;
+      sw = (sw * 7) / 12;
+    }
+
   switch (where)
     {
     case 0:    /* center it in the top-right quadrant */
@@ -264,76 +553,175 @@ pop_up_dialog_box (dialog, form, where)
   if (x + w > sw) x = sw - w;
   if (y + h > sh) y = sh - h;
   ac = 0;
-  XtSetArg (av [ac], XmNx, x); ac++;
-  XtSetArg (av [ac], XmNy, y); ac++;
+  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++;
-  /* I wonder whether this does anything useful? */
-  /*  XtSetArg (av [ac], XmNdialogStyle, XmDIALOG_SYSTEM_MODAL); 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 */
 
-  focus_fuckus (dialog);
+  steal_focus_and_colormap (dialog);
 }
 
 
-static void
-make_screenhack_dialog (parent, hacks)
-     Widget parent;
-     char **hacks;
+void
+make_screenhack_dialog (saver_info *si)
 {
-  char buf [255];
-  Arg av[10];
-  int ac;
-  char *label;
-  XmString xm_label = 0;
-  XmString new_xm_label;
+  saver_screen_info *ssi = si->default_screen;
+  Widget parent = ssi->toplevel_shell;
+  char **hacks = si->prefs.screenhacks;
 
-  create_demo_dialog (parent);
-  ac = 0;
-  XtSetArg (av [ac], XmNlabelString, &xm_label); ac++;
-  XtGetValues (label1, av, ac);
-  XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
-  if (!strcmp (label, XtName (label1)))
-    strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
-  else
-    sprintf (buf, label, screensaver_version);
-  new_xm_label = XmStringCreate (buf, XmSTRING_DEFAULT_CHARSET);
-  ac = 0;
-  XtSetArg (av [ac], XmNlabelString, new_xm_label); ac++;
-  XtSetValues (label1, av, ac);
-  XmStringFree (new_xm_label);
-  XtFree (label);
-
-  XtAddCallback (demo_list, XmNbrowseSelectionCallback, select_cb,
-                (XtPointer) hacks);
-  XtAddCallback (demo_list, XmNdefaultActionCallback, select_cb,
-                (XtPointer) hacks);
-
-  XtAddCallback (text_line, XmNactivateCallback, text_cb, 0);
-  XtAddCallback (next, XmNactivateCallback, next_cb, 0);
-  XtAddCallback (prev, XmNactivateCallback, prev_cb, 0);
-  XtAddCallback (done, XmNactivateCallback, done_cb, 0);
-  XtAddCallback (restart, XmNactivateCallback, restart_cb, 0);
-  XtAddCallback (edit, XmNactivateCallback, edit_cb, (XtPointer) parent);
-
-  for (; *hacks; hacks++)
+  if (ssi->demo_cmap &&
+      ssi->demo_cmap != ssi->cmap &&
+      ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
     {
-      XmString xmstr = XmStringCreate (*hacks, XmSTRING_DEFAULT_CHARSET);
-      XmListAddItem (demo_list, xmstr, 0);
-      /* XmListSelectPos (widget, i, False); */
-      XmStringFree (xmstr);
+      XFreeColormap (si->dpy, ssi->demo_cmap);
+      ssi->demo_cmap = 0;
     }
 
-#if 0
-  /* Dialogs that have scroll-lists don't obey maxWidth!  Fuck!!  Hack it. */
-  ac = 0;
-  XtSetArg (av [ac], XmNmaxWidth, &max_w); ac++;
-  XtGetValues (demo_dialog, av, ac); /* great, this SEGVs */
-#endif
+  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);
+
+  if (hacks)
+    for (; *hacks; hacks++)
+      {
+       XmString xmstr = XmStringCreate (*hacks, XmSTRING_DEFAULT_CHARSET);
+       XmListAddItem (demo_list, xmstr, 0);
+       XmStringFree (xmstr);
+      }
+
+  /* Cause the most-recently-run hack to be selected in the list.
+     Do some voodoo to make it be roughly centered in the list (really,
+     just make it not be within +/- 5 of the top/bottom if possible.)
+   */
+  if (ssi->current_hack > 0)
+    {
+      int i = ssi->current_hack+1;
+      int top = i + 5;
+      int bot = i - 5;
+      if (bot < 1) bot = 1;
+      if (top > si->prefs.screenhacks_count)
+       top = si->prefs.screenhacks_count;
+
+      XmListDeselectAllItems(demo_list);       /* LessTif lossage */
+      XmListSelectPos (demo_list, bot, False);
+      ensure_selected_item_visible (demo_list);
+
+      XmListDeselectAllItems(demo_list);       /* LessTif lossage */
+      XmListSelectPos (demo_list, top, False);
+      ensure_selected_item_visible (demo_list);
+
+      XmListDeselectAllItems(demo_list);       /* LessTif lossage */
+      XmListSelectPos (demo_list, i, False);
+      ensure_selected_item_visible (demo_list);
+    }
 
-  pop_up_dialog_box (demo_dialog, demo_form, 0);
+#else  /* HAVE_ATHENA */
+
+  /* Hook up the text line. */
+
+  XtAppAddActions(XtWidgetToApplicationContext(text_line),
+                 actions, XtNumber(actions));
+  XtOverrideTranslations(text_line, XtParseTranslationTable(translations));
+
+
+  /* Must realize the widget before populating the list, or the dialog
+     will be as wide as the longest string.
+  */
+  XtRealizeWidget (demo_dialog);
+
+  XtVaSetValues (demo_list,
+                XtNlist, hacks,
+                XtNnumberStrings, si->prefs.screenhacks_count,
+                0);
+  XtAddCallback (demo_list, XtNcallback, select_cb, si);
+  if (ssi->current_hack > 0)
+  XawListHighlight(demo_list, ssi->current_hack);
+
+  /* Now that we've populated the list, make sure that the list is as
+     wide as the dialog itself.
+  */
+  {
+    Widget viewport = XtParent(demo_list);
+    Widget subform = XtParent(viewport);
+    Widget box = XtNameToWidget(demo_dialog, "*box");
+    Widget label1 = XtNameToWidget(demo_dialog, "*label1");
+    Widget label2 = XtNameToWidget(demo_dialog, "*label2");
+    Dimension x=0, y=0, w=0, h=0, bw=0, w2=0;
+    XtVaGetValues(subform,
+                 XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
+    XtVaGetValues(box, XtNwidth, &w2, 0);
+    if (w2 != w)
+      XtResizeWidget(subform, w2, h, bw);
+
+    /* Why isn't the viewport getting centered? */
+    XtVaGetValues(viewport,
+                 XtNx, &x, XtNy, &y, XtNheight, &h, XtNborderWidth, &bw, 0);
+    printf("%d %d %d %d\n", x, y, w, h);
+    XtConfigureWidget(viewport, x, y, w2-x-x, h, bw);
+
+    /* And the text line, too. */
+    XtVaGetValues(text_line,
+                 XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
+    XtVaGetValues(viewport, XtNwidth, &w2, 0);
+    if (w2 != w)
+      XtResizeWidget(text_line, w2, h, bw);
+
+    /* And the labels too. */
+    XtVaGetValues(label1,
+                 XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
+    if (w2 != w)
+      XtResizeWidget(label1, w2, h, bw);
+
+    XtVaGetValues(label2,
+                 XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
+    if (w2 != w)
+      XtResizeWidget(label2, w2, h, bw);
+
+  }
+
+#endif /* HAVE_ATHENA */
+
+  monitor_power_on (si);
+  pop_up_dialog_box(demo_dialog, demo_form,
+                   /* for debugging -- don't ask */
+                   (si->prefs.debug_p ? 69 : 0) +
+                   0);
+
+#ifdef HAVE_ATHENA
+  /* For Athena, have to do this after the dialog is managed. */
+  ensure_selected_item_visible (demo_list);
+#endif /* HAVE_ATHENA */
 }
 
 \f
@@ -345,14 +733,8 @@ static struct resources {
 } res;
 
 
-extern int parse_time ();
-
 static void 
-hack_time_cb (dpy, line, store, sec_p)
-     Display *dpy;
-     char *line;
-     int *store;
-     Bool sec_p;
+hack_time_cb (Display *dpy, char *line, int *store, Bool sec_p)
 {
   if (*line)
     {
@@ -366,29 +748,23 @@ hack_time_cb (dpy, line, store, sec_p)
 }
 
 static void
-res_sec_cb (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+res_sec_cb (Widget button, XtPointer client_data, XtPointer call_data)
 {
-  hack_time_cb (XtDisplay (button), XmTextGetString (button),
+  hack_time_cb (XtDisplay (button), get_text_string (button),
                (int *) client_data, True);
 }
 
 static void
-res_min_cb (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+res_min_cb (Widget button, XtPointer client_data, XtPointer call_data)
 {
-  hack_time_cb (XtDisplay (button), XmTextGetString (button),
+  hack_time_cb (XtDisplay (button), get_text_string (button),
                (int *) client_data, False);
 }
 
 static void
-res_int_cb (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+res_int_cb (Widget button, XtPointer client_data, XtPointer call_data)
 {
-  char *line = XmTextGetString (button);
+  char *line = get_text_string (button);
   int *store = (int *) client_data;
   unsigned int value;
   char c;
@@ -401,18 +777,20 @@ res_int_cb (button, client_data, call_data)
 }
 
 static void
-res_bool_cb (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+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 (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+res_cancel_cb (Widget button, XtPointer client_data, XtPointer call_data)
 {
   XtDestroyWidget (resources_dialog);
   resources_dialog = 0;
@@ -421,48 +799,74 @@ res_cancel_cb (button, client_data, call_data)
 
 
 static void
-res_done_cb (button, client_data, call_data)
-     Widget button;
-     XtPointer client_data, call_data;
+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;
 
-  timeout = res.timeout * 1000;
-  cycle = res.cycle * 1000;
-  lock_timeout = res.lock_time * 1000;
+  p->timeout = res.timeout * 1000;
+  p->cycle = res.cycle * 1000;
+  p->lock_timeout = res.lock_time * 1000;
 #ifndef NO_LOCKING
-  passwd_timeout = res.passwd_time * 1000;
+  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;
+
+  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",
+            blurb(), p->timeout, p->cycle, p->lock_timeout,
+#ifdef NO_LOCKING
+            0,
+#else
+            p->passwd_timeout,
 #endif
-  fade_seconds = res.secs;
-  fade_ticks = res.ticks;
-  verbose_p = res.verb;
-  install_cmap_p = res.cmap;
-  fade_p = res.fade;
-  unfade_p = res.unfade;
-  lock_p = res.lock_p;
+            p->fade_seconds, p->fade_ticks, p->verbose_p, p->install_cmap_p,
+            p->fade_p, p->unfade_p, p->lock_p);
+
 
 #if defined(HAVE_MIT_SAVER_EXTENSION) || defined(HAVE_SGI_SAVER_EXTENSION)
-  if (use_mit_saver_extension || use_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 (dpy, &server_timeout, &server_interval,
+      XGetScreenSaver (si->dpy, &server_timeout, &server_interval,
                       &prefer_blank, &allow_exp);
-      if (server_timeout != (timeout / 1000))
+      if (server_timeout != (p->timeout / 1000))
        {
-         server_timeout = (timeout / 1000);
-         if (verbose_p)
+         server_timeout = (p->timeout / 1000);
+         if (p->verbose_p)
            fprintf (stderr,
                   "%s: configuring server for saver timeout of %d seconds.\n",
-                    progname, server_timeout);
+                    blurb(), server_timeout);
          /* Leave all other parameters the same. */
-         XSetScreenSaver (dpy, server_timeout, server_interval,
+         XSetScreenSaver (si->dpy, server_timeout, server_interval,
                           prefer_blank, allow_exp);
        }
     }
@@ -471,57 +875,74 @@ res_done_cb (button, client_data, call_data)
 
 
 static void
-make_resources_dialog (parent)
-     Widget parent;
+make_resources_dialog (saver_info *si, Widget parent)
 {
-  Arg av[10];
-  int ac;
+  saver_screen_info *ssi = si->default_screen;
 
-  create_resources_dialog (parent);
+  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;
+    }
 
-  XtAddCallback (res_done, XmNactivateCallback, res_done_cb, 0);
-  XtAddCallback (res_cancel, XmNactivateCallback, res_cancel_cb, 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) \
-       XtAddCallback ((widget), XmNvalueChangedCallback, (type), \
-                      (XtPointer) (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);
-  CB (verbose_toggle,  res_bool_cb, &res.verb);
-  CB (cmap_toggle,     res_bool_cb, &res.cmap);
-  CB (fade_toggle,     res_bool_cb, &res.fade);
-  CB (unfade_toggle,   res_bool_cb, &res.unfade);
-  CB (lock_toggle,     res_bool_cb, &res.lock_p);
+#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
-  ac = 0;
-  XtSetArg (av[ac], XmNsensitive, False); ac++;
+#undef CBT
 
-  if (locking_disabled_p)
+  if (si->locking_disabled_p)
     {
-      XtSetValues (passwd_time_text, av, ac);
-      XtSetValues (lock_time_text, av, ac);
-      XtSetValues (lock_toggle, av, ac);
+      disable_widget (passwd_time_text);
+      disable_widget (lock_time_text);
+      disable_widget (lock_toggle);
     }
   if (CellsOfScreen (XtScreen (parent)) <= 2)
     {
-      XtSetValues (fade_text, av, ac);
-      XtSetValues (ticks_text, av, ac);
-      XtSetValues (cmap_toggle, av, ac);
-      XtSetValues (fade_toggle, av, ac);
-      XtSetValues (unfade_toggle, av, ac);
+      disable_widget (fade_text);
+      disable_widget (ticks_text);
+      disable_widget (cmap_toggle);
+      disable_widget (fade_toggle);
+      disable_widget (unfade_toggle);
     }
 }
 
 
 static void
-fmt_time (buf, s, min_p)
-     char *buf;
-     unsigned int s;
-     int min_p;
+fmt_time (char *buf, unsigned int s, int min_p)
 {
   unsigned int h = 0, m = 0;
   if (s >= 60)
@@ -548,111 +969,106 @@ fmt_time (buf, s, min_p)
 }
 
 static void
-pop_resources_dialog ()
+pop_resources_dialog (saver_info *si)
 {
+  saver_preferences *p = &si->prefs;
   char buf [100];
 
-  res.timeout = timeout / 1000;
-  res.cycle = cycle / 1000;
-  res.lock_time = lock_timeout / 1000;
+  res.timeout = p->timeout / 1000;
+  res.cycle = p->cycle / 1000;
+  res.lock_time = p->lock_timeout / 1000;
 #ifndef NO_LOCKING
-  res.passwd_time = passwd_timeout / 1000;
+  res.passwd_time = p->passwd_timeout / 1000;
 #endif
-  res.secs = fade_seconds;
-  res.ticks = fade_ticks;
-  res.verb = verbose_p;
-  res.cmap = install_cmap_p;
-  res.fade = fade_p;
-  res.unfade = unfade_p;
-  res.lock_p = (lock_p && !locking_disabled_p);
-
-  fmt_time (buf, res.timeout, 1);     XmTextSetString (timeout_text, buf);
-  fmt_time (buf, res.cycle, 1);       XmTextSetString (cycle_text, buf);
-  fmt_time (buf, res.lock_time, 1);   XmTextSetString (lock_time_text, buf);
-  fmt_time (buf, res.passwd_time, 0); XmTextSetString (passwd_time_text, buf);
-  fmt_time (buf, res.secs, 0);        XmTextSetString (fade_text, buf);
-  sprintf (buf, "%u", res.ticks);     XmTextSetString (ticks_text, buf);
-
-  XmToggleButtonSetState (verbose_toggle, res.verb, True);
-  XmToggleButtonSetState (cmap_toggle, res.cmap, True);
-  XmToggleButtonSetState (fade_toggle, res.fade, True);
-  XmToggleButtonSetState (unfade_toggle, res.unfade, True);
-  XmToggleButtonSetState (lock_toggle, res.lock_p, True);
-
-  pop_up_dialog_box (resources_dialog, resources_form, 1);
+  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);
+
+  monitor_power_on (si);
+  pop_up_dialog_box (resources_dialog, resources_form,
+                    /* for debugging -- don't ask */
+                    (si->prefs.debug_p ? 69 : 0) +
+                    1);
 }
 
 \f
-/* The code on this page isn't actually Motif-specific */
-
-Bool dbox_up_p = False;
-Bool demo_mode_p = False;
-
-extern XtAppContext app;
-extern Widget toplevel_shell;
-extern Bool use_xidle_extension;
-extern Bool use_mit_saver_extension;
-extern Bool use_sgi_saver_extension;
-extern Time notice_events_timeout;
-
-extern char **screenhacks;
-extern char *demo_hack;
-
-extern void notice_events_timer P((XtPointer closure, XtIntervalId *timer));
-extern Bool handle_clientmessage P((/*XEvent *, Bool*/));
+/* The main demo-mode command loop.
+ */
 
 void
-demo_mode ()
+demo_mode (saver_info *si)
 {
-  dbox_up_p = True;
-  initialize_screensaver_window ();
-  raise_window (True, False);
-  make_screenhack_dialog (toplevel_shell, screenhacks);
-  while (demo_mode_p)
+  saver_preferences *p = &si->prefs;
+  si->dbox_up_p = True;
+  monitor_power_on (si);
+  raise_window (si, True, False, False);
+  make_screenhack_dialog (si);
+  while (si->demo_mode_p)
     {
       XEvent event;
-      XtAppNextEvent (app, &event);
+      XtAppNextEvent (si->app, &event);
       switch (event.xany.type)
        {
        case 0:         /* synthetic "timeout" event */
          break;
 
        case ClientMessage:
-         handle_clientmessage (&event, False);
+         handle_clientmessage (si, &event, False);
          break;
 
        case CreateNotify:
-         if (!use_xidle_extension &&
-             !use_mit_saver_extension &&
-             !use_sgi_saver_extension)
+         if (!p->use_xidle_extension &&
+             !p->use_mit_saver_extension &&
+             !p->use_sgi_saver_extension)
            {
-             XtAppAddTimeOut (app, notice_events_timeout, notice_events_timer,
-                              (XtPointer) event.xcreatewindow.window);
+             start_notice_events_timer (si, event.xcreatewindow.window);
 #ifdef DEBUG_TIMERS
-             if (verbose_p)
+             if (p->verbose_p)
                printf ("%s: starting notice_events_timer for 0x%X (%lu)\n",
-                       progname,
+                       blurb(),
                        (unsigned int) event.xcreatewindow.window,
-                       notice_events_timeout);
+                       p->notice_events_timeout);
 #endif /* DEBUG_TIMERS */
            }
          break;
 
        case ButtonPress:
        case ButtonRelease:
-         if (!XtWindowToWidget (dpy, event.xbutton.window))
+         if (!XtWindowToWidget (si->dpy, event.xbutton.window))
            raise_screenhack_dialog ();
          /* fall through */
 
        default:
 #ifdef HAVE_MIT_SAVER_EXTENSION
-         if (event.type == mit_saver_ext_event_number)
+         if (event.type == si->mit_saver_ext_event_number)
            {
-             /* Get the "real" server window out of the way as soon
+             /* Get the "real" server window(s) out of the way as soon
                 as possible. */
-             if (server_mit_saver_window &&
-                 window_exists_p (dpy, server_mit_saver_window))
-               XUnmapWindow (dpy, server_mit_saver_window);
+             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 */
@@ -661,31 +1077,33 @@ demo_mode ()
          break;
        }
     }
-  destroy_screenhack_dialogs ();
-  initialize_screensaver_window ();
-  unblank_screen ();
+  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 (hack)
-     char *hack;
+demo_mode_hack (saver_info *si, char *hack)
 {
-  if (! demo_mode_p) abort ();
-  kill_screenhack ();
-  if (! demo_hack)
-    blank_screen ();
-  demo_hack = hack;
-  spawn_screenhack (False);
+  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 ()
+demo_mode_done (saver_info *si)
 {
-  kill_screenhack ();
-  if (demo_hack)
-    unblank_screen ();
-  demo_mode_p = False;
-  dbox_up_p = False;
-  demo_hack = 0;
+  kill_screenhack (si);
+  if (si->demo_hack)
+    unblank_screen (si);
+  si->demo_mode_p = False;
+  si->dbox_up_p = False;
+  si->demo_hack = 0;
 }