http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.34.tar.gz
[xscreensaver] / driver / demo-Gtk.c
index 11c64d8985de896a76f8c47856559f4da3c80aae..c11224d4b0ecb43fb4ae4e50c825007d83bc5492 100644 (file)
@@ -1,5 +1,5 @@
 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
- * xscreensaver, Copyright (c) 1993-1999 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 1993-2001 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
@@ -33,6 +33,7 @@
 #endif /* HAVE_UNAME */
 
 #include <stdio.h>
+#include <sys/stat.h>
 
 #include <X11/Xproto.h>                /* for CARD32 */
 #include <X11/Xatom.h>         /* for XA_INTEGER */
@@ -61,7 +62,7 @@
 # include <capplet-widget.h>
 #endif
 
-extern Display *gdk_display;
+#include <gdk/gdkx.h>
 
 #include "version.h"
 #include "prefs.h"
@@ -70,6 +71,9 @@ extern Display *gdk_display;
 #include "remote.h"            /* for xscreensaver_command() */
 #include "usleep.h"
 
+#include "logo-50.xpm"
+#include "logo-180.xpm"
+
 #include "demo-Gtk-widgets.h"
 
 #include <stdio.h>
@@ -109,6 +113,7 @@ static void populate_demo_window (GtkWidget *toplevel,
 static void populate_prefs_page (GtkWidget *top, prefs_pair *pair);
 static int apply_changes_and_save (GtkWidget *widget);
 static int maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair);
+static void await_xscreensaver (GtkWidget *widget);
 
 \f
 /* Some random utility functions
@@ -171,7 +176,7 @@ ensure_selected_item_visible (GtkWidget *widget)
        kids; kids = kids->next)
     nkids++;
 
-  adj = gtk_scrolled_window_get_vadjustment (scroller);                        
+  adj = gtk_scrolled_window_get_vadjustment (scroller);
 
   gdk_window_get_geometry (GTK_WIDGET(vp)->window,
                            &ignore, &ignore, &ignore, &parent_h, &ignore);
@@ -247,6 +252,9 @@ warning_dialog (GtkWidget *parent, const char *message,
   while (parent->parent)
     parent = parent->parent;
 
+  if (!GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
+    return;
+
   head = msg;
   while (head)
     {
@@ -257,12 +265,19 @@ warning_dialog (GtkWidget *parent, const char *message,
       sprintf (name, "label%d", i++);
 
       {
-        char buf[255];
         label = gtk_label_new (head);
-        sprintf (buf, "warning_dialog.%s.font", name);
-        GTK_WIDGET (label)->style = gtk_style_copy (GTK_WIDGET (label)->style);
-        GTK_WIDGET (label)->style->font =
-          gdk_font_load (get_string_resource (buf, "Dialog.Label.Font"));
+
+        if (i == 1)
+          {
+            GTK_WIDGET (label)->style =
+              gtk_style_copy (GTK_WIDGET (label)->style);
+            GTK_WIDGET (label)->style->font =
+              gdk_font_load (get_string_resource("warning_dialog.headingFont",
+                                                 "Dialog.Font"));
+            gtk_widget_set_style (GTK_WIDGET (label),
+                                  GTK_WIDGET (label)->style);
+          }
+
         if (center <= 0)
           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
@@ -321,6 +336,7 @@ warning_dialog (GtkWidget *parent, const char *message,
                                  GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
                                  (gpointer) dialog);
     }
+
   gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
                                 GTK_WIDGET (parent)->window);
 
@@ -338,7 +354,7 @@ run_cmd (GtkWidget *widget, Atom command, int arg)
   int status;
 
   apply_changes_and_save (widget);
-  status = xscreensaver_command (gdk_display, command, arg, False, &err);
+  status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
   if (status < 0)
     {
       char buf [255];
@@ -362,7 +378,7 @@ run_hack (GtkWidget *widget, int which, Bool report_errors_p)
   else
     {
       char *s = 0;
-      xscreensaver_command (gdk_display, XA_DEMO, which + 1, False, &s);
+      xscreensaver_command (GDK_DISPLAY(), XA_DEMO, which + 1, False, &s);
       if (s) free (s);
     }
 }
@@ -420,20 +436,110 @@ paste_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
 void
 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
 {
-  char buf [2048];
-  char *s = strdup (screensaver_id + 4);
-  char *s2;
+  char msg [2048];
+  char *vers = strdup (screensaver_id + 4);
+  char *s;
+  char copy[1024];
+  char *desc = "For updates, check http://www.jwz.org/xscreensaver/";
 
-  s2 = strchr (s, ',');
-  *s2 = 0;
-  s2 += 2;
+  s = strchr (vers, ',');
+  *s = 0;
+  s += 2;
+
+  sprintf(copy, "Copyright \251 1991-2001 %s", s);
 
-  sprintf (buf, "%s\n%s\n\n"
-           "For updates, check http://www.jwz.org/xscreensaver/",
-           s, s2);
-  free (s);
+  sprintf (msg, "%s\n\n%s", copy, desc);
 
-  warning_dialog (GTK_WIDGET (menuitem), buf, False, 100);
+  /* I can't make gnome_about_new() work here -- it starts dying in
+     gdk_imlib_get_visual() under gnome_about_new().  If this worked,
+     then this might be the thing to do:
+
+     #ifdef HAVE_CRAPPLET
+     {
+       const gchar *auth[] = { 0 };
+       GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
+                                           "xscreensaver.xpm");
+       gtk_widget_show (about);
+     }
+     #else / * GTK but not GNOME * /
+      ...
+   */
+  {
+    GdkColormap *colormap;
+    GdkPixmap *gdkpixmap;
+    GdkBitmap *mask;
+
+    GtkWidget *dialog = gtk_dialog_new ();
+    GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
+    GtkWidget *parent = GTK_WIDGET (menuitem);
+    while (parent->parent)
+      parent = parent->parent;
+
+    hbox = gtk_hbox_new (FALSE, 20);
+    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
+                        hbox, TRUE, TRUE, 0);
+
+    colormap = gtk_widget_get_colormap (parent);
+    gdkpixmap =
+      gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
+                                             (gchar **) logo_180_xpm);
+    icon = gtk_pixmap_new (gdkpixmap, mask);
+    gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
+
+    gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
+
+    vbox = gtk_vbox_new (FALSE, 0);
+    gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+
+    label1 = gtk_label_new (vers);
+    gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
+    gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
+    gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
+
+    GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
+    GTK_WIDGET (label1)->style->font =
+      gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
+    gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
+
+    label2 = gtk_label_new (msg);
+    gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
+    gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
+    gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
+
+    GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
+    GTK_WIDGET (label2)->style->font =
+      gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
+    gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
+
+    hb = gtk_hbutton_box_new ();
+
+    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
+                        hb, TRUE, TRUE, 0);
+
+    ok = gtk_button_new_with_label ("OK");
+    gtk_container_add (GTK_CONTAINER (hb), ok);
+
+    gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
+    gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
+    gtk_window_set_title (GTK_WINDOW (dialog), progclass);
+
+    gtk_widget_show (hbox);
+    gtk_widget_show (icon);
+    gtk_widget_show (vbox);
+    gtk_widget_show (label1);
+    gtk_widget_show (label2);
+    gtk_widget_show (hb);
+    gtk_widget_show (ok);
+    gtk_widget_show (dialog);
+
+    gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
+                               GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
+                               (gpointer) dialog);
+    gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
+                                  GTK_WIDGET (parent)->window);
+    gdk_window_show (GTK_WIDGET (dialog)->window);
+    gdk_window_raise (GTK_WIDGET (dialog)->window);
+  }
 }
 
 
@@ -493,10 +599,73 @@ restart_menu_cb (GtkWidget *widget, gpointer user_data)
   run_cmd (GTK_WIDGET (widget), XA_RESTART, 0);
 #else
   apply_changes_and_save (GTK_WIDGET (widget));
-  xscreensaver_command (gdk_display, XA_EXIT, 0, False, NULL);
+  xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
   sleep (1);
   system ("xscreensaver -nosplash &");
 #endif
+
+  await_xscreensaver (GTK_WIDGET (widget));
+}
+
+static void
+await_xscreensaver (GtkWidget *widget)
+{
+  int countdown = 5;
+
+  Display *dpy = GDK_DISPLAY();
+  /*  GtkWidget *dialog = 0;*/
+  char *rversion = 0;
+
+  while (!rversion && (--countdown > 0))
+    {
+      /* Check for the version of the running xscreensaver... */
+      server_xscreensaver_version (dpy, &rversion, 0, 0);
+
+      /* If it's not there yet, wait a second... */
+      sleep (1);
+    }
+
+/*  if (dialog) gtk_widget_destroy (dialog);*/
+
+  if (rversion)
+    {
+      /* Got it. */
+      free (rversion);
+    }
+  else
+    {
+      /* Timed out, no screensaver running. */
+
+      char buf [1024];
+      Bool root_p = (geteuid () == 0);
+      
+      strcpy (buf, 
+              "Error:\n\n"
+              "The xscreensaver daemon did not start up properly.\n"
+              "\n");
+
+      if (root_p)
+        strcat (buf,
+            "You are running as root.  This usually means that xscreensaver\n"
+            "was unable to contact your X server because access control is\n"
+            "turned on.  Try running this command:\n"
+            "\n"
+            "                        xhost +localhost\n"
+            "\n"
+            "and then selecting `File / Restart Daemon'.\n"
+            "\n"
+            "Note that turning off access control will allow anyone logged\n"
+            "on to this machine to access your screen, which might be\n"
+            "considered a security problem.  Please read the xscreensaver\n"
+            "manual and FAQ for more information.\n"
+            "\n"
+            "You shouldn't run X as root. Instead, you should log in as a\n"
+            "normal user, and `su' as necessary.");
+      else
+        strcat (buf, "Please check your $PATH and permissions.");
+
+      warning_dialog (widget, buf, False, 1);
+    }
 }
 
 
@@ -774,6 +943,63 @@ hack_time_text (GtkWidget *widget, const char *line, Time *store, Bool sec_p)
 }
 
 
+static Bool
+directory_p (const char *path)
+{
+  struct stat st;
+  if (!path || !*path)
+    return False;
+  else if (stat (path, &st))
+    return False;
+  else if (!S_ISDIR (st.st_mode))
+    return False;
+  else
+    return True;
+}
+
+static char *
+normalize_directory (const char *path)
+{
+  int L;
+  char *p2, *s;
+  if (!path) return 0;
+  L = strlen (path);;
+  p2 = (char *) malloc (L + 2);
+  strcpy (p2, path);
+  if (p2[L-1] == '/')  /* remove trailing slash */
+    p2[--L] = 0;
+
+  for (s = p2; s && *s; s++)
+    {
+      if (*s == '/' &&
+          (!strncmp (s, "/../", 4) ||                  /* delete "XYZ/../" */
+           !strncmp (s, "/..\000", 4)))                        /* delete "XYZ/..$" */
+        {
+          char *s0 = s;
+          while (s0 > p2 && s0[-1] != '/')
+            s0--;
+          if (s0 > p2)
+            {
+              s0--;
+              s += 3;
+              strcpy (s0, s);
+              s = s0-1;
+            }
+        }
+      else if (*s == '/' && !strncmp (s, "/./", 3))    /* delete "/./" */
+        strcpy (s, s+2), s--;
+      else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
+        *s = 0, s--;
+    }
+
+  for (s = p2; s && *s; s++)           /* normalize consecutive slashes */
+    while (s[0] == '/' && s[1] == '/')
+      strcpy (s, s+1);
+
+  return p2;
+}
+
+
 void
 prefs_ok_cb (GtkButton *button, gpointer user_data)
 {
@@ -813,25 +1039,57 @@ prefs_ok_cb (GtkButton *button, gpointer user_data)
      *(field) = value; \
   } while(0)
 
+# define PATHNAME(field, name) do { \
+    char *line = gtk_entry_get_text (\
+                    GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))); \
+    if (! *line) \
+      ; \
+    else if (!directory_p (line)) \
+      { \
+       char b[255]; \
+       sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n", line); \
+       warning_dialog (GTK_WIDGET (button), b, False, 100); \
+        if ((field)) free ((field)); \
+        (field) = strdup(line); \
+      } \
+   else { \
+     if ((field)) free ((field)); \
+     (field) = strdup(line); \
+    } \
+  } while(0)
+
 # define CHECKBOX(field, name) \
   field = gtk_toggle_button_get_active (\
              GTK_TOGGLE_BUTTON (name_to_widget (GTK_WIDGET(button), (name))))
 
-  MINUTES (&p2->timeout,        "timeout_text");
-  MINUTES (&p2->cycle,          "cycle_text");
-  SECONDS (&p2->fade_seconds,   "fade_text");
-  INTEGER (&p2->fade_ticks,     "ticks_text");
-  MINUTES (&p2->lock_timeout,   "lock_text");
-  SECONDS (&p2->passwd_timeout, "pass_text");
-  CHECKBOX (p2->verbose_p,      "verbose_button");
-  CHECKBOX (p2->install_cmap_p, "install_button");
-  CHECKBOX (p2->fade_p,         "fade_button");
-  CHECKBOX (p2->unfade_p,       "unfade_button");
-  CHECKBOX (p2->lock_p,         "lock_button");
+  MINUTES (&p2->timeout,          "timeout_text");
+  MINUTES (&p2->cycle,            "cycle_text");
+  CHECKBOX (p2->lock_p,           "lock_button");
+  MINUTES (&p2->lock_timeout,     "lock_text");
+
+  CHECKBOX (p2->dpms_enabled_p,   "dpms_button");
+  MINUTES (&p2->dpms_standby,     "dpms_standby_text");
+  MINUTES (&p2->dpms_suspend,     "dpms_suspend_text");
+  MINUTES (&p2->dpms_off,         "dpms_off_text");
+
+  CHECKBOX (p2->grab_desktop_p,   "grab_desk_button");
+  CHECKBOX (p2->grab_video_p,     "grab_video_button");
+  CHECKBOX (p2->random_image_p,   "grab_image_button");
+  PATHNAME (p2->image_directory,  "image_text");
+
+  CHECKBOX (p2->verbose_p,        "verbose_button");
+  CHECKBOX (p2->capture_stderr_p, "capture_button");
+  CHECKBOX (p2->splash_p,         "splash_button");
+
+  CHECKBOX (p2->install_cmap_p,   "install_button");
+  CHECKBOX (p2->fade_p,           "fade_button");
+  CHECKBOX (p2->unfade_p,         "unfade_button");
+  SECONDS (&p2->fade_seconds,     "fade_text");
 
 # undef SECONDS
 # undef MINUTES
 # undef INTEGER
+# undef PATHNAME
 # undef CHECKBOX
 
 # define COPY(field) \
@@ -840,21 +1098,51 @@ prefs_ok_cb (GtkButton *button, gpointer user_data)
 
   COPY(timeout);
   COPY(cycle);
+  COPY(lock_p);
   COPY(lock_timeout);
-  COPY(passwd_timeout);
-  COPY(fade_seconds);
-  COPY(fade_ticks);
+
+  COPY(dpms_enabled_p);
+  COPY(dpms_standby);
+  COPY(dpms_suspend);
+  COPY(dpms_off);
+
+  COPY (grab_desktop_p);
+  COPY (grab_video_p);
+  COPY (random_image_p);
+
+  if (!p->image_directory ||
+      !p2->image_directory ||
+      strcmp(p->image_directory, p2->image_directory))
+    changed = True;
+  if (p->image_directory && p->image_directory != p2->image_directory)
+    free (p->image_directory);
+  p->image_directory = normalize_directory (p2->image_directory);
+  if (p2->image_directory) free (p2->image_directory);
+  p2->image_directory = 0;
+
   COPY(verbose_p);
+  COPY(capture_stderr_p);
+  COPY(splash_p);
+
   COPY(install_cmap_p);
   COPY(fade_p);
   COPY(unfade_p);
-  COPY(lock_p);
+  COPY(fade_seconds);
 # undef COPY
 
   populate_prefs_page (GTK_WIDGET (button), pair);
 
   if (changed)
-    demo_write_init_file (GTK_WIDGET (button), p);
+    {
+      Display *dpy = GDK_DISPLAY();
+      sync_server_dpms_settings (dpy, p->dpms_enabled_p,
+                                 p->dpms_standby / 1000,
+                                 p->dpms_suspend / 1000,
+                                 p->dpms_off / 1000,
+                                 False);
+
+      demo_write_init_file (GTK_WIDGET (button), p);
+    }
 }
 
 
@@ -915,6 +1203,177 @@ list_unselect_cb (GtkList *list, GtkWidget *child)
   populate_demo_window (GTK_WIDGET (list), -1, pair);
 }
 
+
+static int updating_enabled_cb = 0;  /* kludge to make sure that enabled_cb
+                                        is only run by user action, not by
+                                        program action. */
+
+/* Called when the checkboxes that are in the left column of the
+   scrolling list are clicked.  This both populates the right pane
+   (just as clicking on the label (really, listitem) does) and
+   also syncs this checkbox with  the right pane Enabled checkbox.
+ */
+static void
+list_checkbox_cb (GtkWidget *cb, gpointer client_data)
+{
+  prefs_pair *pair = (prefs_pair *) client_data;
+
+  GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
+  GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
+
+  GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
+  GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
+  GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
+  GtkAdjustment *adj;
+  double scroll_top;
+
+  GtkToggleButton *enabled =
+    GTK_TOGGLE_BUTTON (name_to_widget (cb, "enabled"));
+
+  int which = gtk_list_child_position (list, line);
+
+  /* remember previous scroll position of the top of the list */
+  adj = gtk_scrolled_window_get_vadjustment (scroller);
+  scroll_top = adj->value;
+
+  apply_changes_and_save (GTK_WIDGET (list));
+  gtk_list_select_item (list, which);
+  /* ensure_selected_item_visible (GTK_WIDGET (list)); */
+  populate_demo_window (GTK_WIDGET (list), which, pair);
+  
+  updating_enabled_cb++;
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (enabled),
+                                GTK_TOGGLE_BUTTON (cb)->active);
+  updating_enabled_cb--;
+
+  /* restore the previous scroll position of the top of the list.
+     this is weak, but I don't really know why it's moving... */
+  gtk_adjustment_set_value (adj, scroll_top);
+}
+
+
+/* Called when the right pane Enabled checkbox is clicked.  This syncs
+   the corresponding checkbox inside the scrolling list to the state
+   of this checkbox.
+ */
+void
+enabled_cb (GtkWidget *cb, gpointer client_data)
+{
+  int which = selected_hack_number (cb);
+  
+  if (updating_enabled_cb) return;
+
+  if (which != -1)
+    {
+      GtkList *list = GTK_LIST (name_to_widget (cb, "list"));
+      GList *kids = GTK_LIST (list)->children;
+      GtkWidget *line = GTK_WIDGET (g_list_nth_data (kids, which));
+      GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
+      GtkWidget *line_check =
+        GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
+
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
+                                    GTK_TOGGLE_BUTTON (cb)->active);
+    }
+}
+
+
+
+typedef struct {
+  prefs_pair *pair;
+  GtkFileSelection *widget;
+} file_selection_data;
+
+
+
+static void
+store_image_directory (GtkWidget *button, gpointer user_data)
+{
+  file_selection_data *fsd = (file_selection_data *) user_data;
+  prefs_pair *pair = fsd->pair;
+  GtkFileSelection *selector = fsd->widget;
+  GtkWidget *top = toplevel_widget;
+  saver_preferences *p = pair->a;
+  char *path = gtk_file_selection_get_filename (selector);
+
+  if (p->image_directory && !strcmp(p->image_directory, path))
+    return;  /* no change */
+
+  if (!directory_p (path))
+    {
+      char b[255];
+      sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n", path);
+      warning_dialog (GTK_WIDGET (top), b, False, 100);
+      return;
+    }
+
+  if (p->image_directory) free (p->image_directory);
+  p->image_directory = normalize_directory (path);
+
+  gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "image_text")),
+                      (p->image_directory ? p->image_directory : ""));
+  demo_write_init_file (GTK_WIDGET (top), p);
+}
+
+
+static void
+browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
+{
+  file_selection_data *fsd = (file_selection_data *) user_data;
+  gtk_widget_hide (GTK_WIDGET (fsd->widget));
+}
+
+static void
+browse_image_dir_ok (GtkWidget *button, gpointer user_data)
+{
+  browse_image_dir_cancel (button, user_data);
+  store_image_directory (button, user_data);
+}
+
+static void
+browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+  browse_image_dir_cancel (widget, user_data);
+}
+
+
+void
+browse_image_dir_cb (GtkButton *button, gpointer user_data)
+{
+  /* prefs_pair *pair = (prefs_pair *) client_data; */
+  prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
+  saver_preferences *p = pair->a;
+  static file_selection_data *fsd = 0;
+
+  GtkFileSelection *selector = GTK_FILE_SELECTION(
+    gtk_file_selection_new ("Please select the image directory."));
+
+  if (!fsd)
+    fsd = (file_selection_data *) malloc (sizeof (*fsd));  
+
+  fsd->widget = selector;
+  fsd->pair = pair;
+
+  if (p->image_directory && *p->image_directory)
+    gtk_file_selection_set_filename (selector, p->image_directory);
+
+  gtk_signal_connect (GTK_OBJECT (selector->ok_button),
+                      "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
+                      (gpointer *) fsd);
+  gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
+                      "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
+                      (gpointer *) fsd);
+  gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
+                      GTK_SIGNAL_FUNC (browse_image_dir_close),
+                      (gpointer *) fsd);
+
+  gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
+
+  gtk_window_set_modal (GTK_WINDOW (selector), True);
+  gtk_widget_show (GTK_WIDGET (selector));
+}
+
+
 \f
 /* Populating the various widgets
  */
@@ -941,48 +1400,6 @@ format_time (char *buf, Time time)
 }
 
 
-static char *
-make_pretty_name (const char *shell_command)
-{
-  char *s = strdup (shell_command);
-  char *s2;
-  char res_name[255];
-
-  for (s2 = s; *s2; s2++)      /* truncate at first whitespace */
-    if (isspace (*s2))
-      {
-        *s2 = 0;
-        break;
-      }
-
-  s2 = strrchr (s, '/');       /* if pathname, take last component */
-  if (s2)
-    {
-      s2 = strdup (s2+1);
-      free (s);
-      s = s2;
-    }
-
-  if (strlen (s) > 50)         /* 51 is hereby defined as "unreasonable" */
-    s[50] = 0;
-
-  sprintf (res_name, "hacks.%s.name", s);              /* resource? */
-  s2 = get_string_resource (res_name, res_name);
-  if (s2)
-    return s2;
-
-  for (s2 = s; *s2; s2++)      /* if it has any capitals, return it */
-    if (*s2 >= 'A' && *s2 <= 'Z')
-      return s;
-
-  if (s[0] >= 'a' && s[0] <= 'z')                      /* else cap it */
-    s[0] -= 'a'-'A';
-  if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z')       /* (magic leading X) */
-    s[1] -= 'a'-'A';
-  return s;
-}
-
-
 /* Finds the number of the last hack to run, and makes that item be
    selected by default.
  */
@@ -994,7 +1411,7 @@ scroll_to_current_hack (GtkWidget *toplevel, prefs_pair *pair)
   int format;
   unsigned long nitems, bytesafter;
   CARD32 *data = 0;
-  Display *dpy = gdk_display;
+  Display *dpy = GDK_DISPLAY();
   int which = 0;
   GtkList *list;
 
@@ -1036,18 +1453,50 @@ populate_hack_list (GtkWidget *toplevel, prefs_pair *pair)
 
   for (h = hacks; h && *h; h++)
     {
+      /* A GtkList must contain only GtkListItems, but those can contain
+         an arbitrary widget.  We add an Hbox, and inside that, a Checkbox
+         and a Label.  We handle single and double click events on the
+         line itself, for clicking on the text, but the interior checkbox
+         also handles its own events.
+       */
       GtkWidget *line;
+      GtkWidget *line_hbox;
+      GtkWidget *line_check;
+      GtkWidget *line_label;
+
       char *pretty_name = (h[0]->name
                            ? strdup (h[0]->name)
-                           : make_pretty_name (h[0]->command));
+                           : make_hack_name (h[0]->command));
+
+      line = gtk_list_item_new ();
+      line_hbox = gtk_hbox_new (FALSE, 0);
+      line_check = gtk_check_button_new ();
+      line_label = gtk_label_new (pretty_name);
+
+      gtk_container_add (GTK_CONTAINER (line), line_hbox);
+      gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
+      gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
+
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
+                                    h[0]->enabled_p);
+      gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
+
+      gtk_widget_show (line_check);
+      gtk_widget_show (line_label);
+      gtk_widget_show (line_hbox);
+      gtk_widget_show (line);
 
-      line = gtk_list_item_new_with_label (pretty_name);
       free (pretty_name);
 
       gtk_container_add (GTK_CONTAINER (list), line);
       gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
                           GTK_SIGNAL_FUNC (list_doubleclick_cb),
                           (gpointer) pair);
+
+      gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
+                          GTK_SIGNAL_FUNC (list_checkbox_cb),
+                          (gpointer) pair);
+
 #if 0 /* #### */
       GTK_WIDGET (GTK_BIN(line)->child)->style =
         gtk_style_copy (GTK_WIDGET (text_line)->style);
@@ -1076,16 +1525,51 @@ populate_prefs_page (GtkWidget *top, prefs_pair *pair)
   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "cycle_text")), s);
   format_time (s, p->lock_timeout);
   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "lock_text")), s);
-  format_time (s, p->passwd_timeout);
-  gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "pass_text")), s);
+
+  format_time (s, p->dpms_standby);
+  gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_standby_text")),s);
+  format_time (s, p->dpms_suspend);
+  gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_suspend_text")),s);
+  format_time (s, p->dpms_off);
+  gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_off_text")), s);
+
   format_time (s, p->fade_seconds);
   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "fade_text")), s);
-  sprintf (s, "%u", p->fade_ticks);
-  gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "ticks_text")), s);
 
+  gtk_toggle_button_set_active (
+                   GTK_TOGGLE_BUTTON (name_to_widget (top, "lock_button")),
+                   p->lock_p);
   gtk_toggle_button_set_active (
                    GTK_TOGGLE_BUTTON (name_to_widget (top, "verbose_button")),
                    p->verbose_p);
+  gtk_toggle_button_set_active (
+                   GTK_TOGGLE_BUTTON (name_to_widget (top, "capture_button")),
+                   p->capture_stderr_p);
+  gtk_toggle_button_set_active (
+                   GTK_TOGGLE_BUTTON (name_to_widget (top, "splash_button")),
+                   p->splash_p);
+
+  gtk_toggle_button_set_active (
+                   GTK_TOGGLE_BUTTON (name_to_widget (top, "dpms_button")),
+                   p->dpms_enabled_p);
+
+  gtk_toggle_button_set_active (
+                   GTK_TOGGLE_BUTTON (name_to_widget (top,"grab_desk_button")),
+                   p->grab_desktop_p);
+  gtk_toggle_button_set_active (
+                   GTK_TOGGLE_BUTTON (name_to_widget(top,"grab_video_button")),
+                   p->grab_video_p);
+  gtk_toggle_button_set_active (
+                   GTK_TOGGLE_BUTTON (name_to_widget(top,"grab_image_button")),
+                   p->random_image_p);
+  gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "image_text")),
+                      (p->image_directory ? p->image_directory : ""));
+  gtk_widget_set_sensitive (GTK_WIDGET (name_to_widget (top, "image_text")),
+                            p->random_image_p);
+  gtk_widget_set_sensitive (
+                       GTK_WIDGET (name_to_widget (top,"image_browse_button")),
+                            p->random_image_p);
+
   gtk_toggle_button_set_active (
                    GTK_TOGGLE_BUTTON (name_to_widget (top, "install_button")),
                    p->install_cmap_p);
@@ -1095,14 +1579,13 @@ populate_prefs_page (GtkWidget *top, prefs_pair *pair)
   gtk_toggle_button_set_active (
                    GTK_TOGGLE_BUTTON (name_to_widget (top, "unfade_button")),
                    p->unfade_p);
-  gtk_toggle_button_set_active (
-                   GTK_TOGGLE_BUTTON (name_to_widget (top, "lock_button")),
-                   p->lock_p);
 
 
   {
     Bool found_any_writable_cells = False;
-    Display *dpy = gdk_display;
+    Bool dpms_supported = False;
+
+    Display *dpy = GDK_DISPLAY();
     int nscreens = ScreenCount(dpy);
     int i;
     for (i = 0; i < nscreens; i++)
@@ -1115,17 +1598,59 @@ populate_prefs_page (GtkWidget *top, prefs_pair *pair)
          }
       }
 
+#ifdef HAVE_XF86VMODE_GAMMA
+    found_any_writable_cells = True;  /* if we can gamma fade, go for it */
+#endif
+
+#ifdef HAVE_DPMS_EXTENSION
+    {
+      int op = 0, event = 0, error = 0;
+      if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
+        dpms_supported = True;
+    }
+#endif /* HAVE_DPMS_EXTENSION */
+
+
+    /* Blanking and Locking
+     */
     gtk_widget_set_sensitive (
-                           GTK_WIDGET (name_to_widget (top, "fade_label")),
-                           found_any_writable_cells);
+                           GTK_WIDGET (name_to_widget (top, "lock_label")),
+                           p->lock_p);
     gtk_widget_set_sensitive (
-                           GTK_WIDGET (name_to_widget (top, "ticks_label")),
-                           found_any_writable_cells);
+                           GTK_WIDGET (name_to_widget (top, "lock_text")),
+                           p->lock_p);
+
+    /* DPMS
+     */
     gtk_widget_set_sensitive (
-                           GTK_WIDGET (name_to_widget (top, "fade_text")),
-                           found_any_writable_cells);
+                      GTK_WIDGET (name_to_widget (top, "dpms_frame")),
+                      dpms_supported);
+    gtk_widget_set_sensitive (
+                      GTK_WIDGET (name_to_widget (top, "dpms_button")),
+                      dpms_supported);
+    gtk_widget_set_sensitive (
+                       GTK_WIDGET (name_to_widget (top, "dpms_standby_label")),
+                       dpms_supported && p->dpms_enabled_p);
+    gtk_widget_set_sensitive (
+                       GTK_WIDGET (name_to_widget (top, "dpms_standby_text")),
+                       dpms_supported && p->dpms_enabled_p);
+    gtk_widget_set_sensitive (
+                       GTK_WIDGET (name_to_widget (top, "dpms_suspend_label")),
+                       dpms_supported && p->dpms_enabled_p);
+    gtk_widget_set_sensitive (
+                       GTK_WIDGET (name_to_widget (top, "dpms_suspend_text")),
+                       dpms_supported && p->dpms_enabled_p);
+    gtk_widget_set_sensitive (
+                       GTK_WIDGET (name_to_widget (top, "dpms_off_label")),
+                       dpms_supported && p->dpms_enabled_p);
+    gtk_widget_set_sensitive (
+                       GTK_WIDGET (name_to_widget (top, "dpms_off_text")),
+                       dpms_supported && p->dpms_enabled_p);
+
+    /* Colormaps
+     */
     gtk_widget_set_sensitive (
-                           GTK_WIDGET (name_to_widget (top, "ticks_text")),
+                           GTK_WIDGET (name_to_widget (top, "cmap_frame")),
                            found_any_writable_cells);
     gtk_widget_set_sensitive (
                            GTK_WIDGET (name_to_widget (top, "install_button")),
@@ -1136,6 +1661,15 @@ populate_prefs_page (GtkWidget *top, prefs_pair *pair)
     gtk_widget_set_sensitive (
                            GTK_WIDGET (name_to_widget (top, "unfade_button")),
                            found_any_writable_cells);
+
+    gtk_widget_set_sensitive (
+                           GTK_WIDGET (name_to_widget (top, "fade_label")),
+                           (found_any_writable_cells &&
+                            (p->fade_p || p->unfade_p)));
+    gtk_widget_set_sensitive (
+                           GTK_WIDGET (name_to_widget (top, "fade_text")),
+                           (found_any_writable_cells &&
+                            (p->fade_p || p->unfade_p)));
   }
 
 }
@@ -1173,8 +1707,9 @@ sensitize_demo_widgets (GtkWidget *toplevel, Bool sensitive_p)
 static void
 fix_text_entry_sizes (GtkWidget *toplevel)
 {
-  const char *names[] = { "timeout_text", "cycle_text", "fade_text",
-                          "ticks_text", "lock_text", "pass_text" };
+  const char *names[] = { "timeout_text", "cycle_text", "lock_text",
+                          "dpms_standby_text", "dpms_suspend_text",
+                          "dpms_off_text", "fade_text" };
   int i;
   int width = 0;
   GtkWidget *w;
@@ -1194,6 +1729,12 @@ fix_text_entry_sizes (GtkWidget *toplevel)
   width = gdk_text_width (w->style->font, "PseudoColor___", 14);
   gtk_widget_set_usize (w, width, -2);
 
+  /* Now fix the size of the file entry text.
+   */
+  w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "image_text"));
+  width = gdk_text_width (w->style->font, "MMMMMMMMMMMMMM", 14);
+  gtk_widget_set_usize (w, width, -2);
+
 #if 0
   /* Now fix the size of the list.
    */
@@ -1268,7 +1809,7 @@ static char *down_arrow_xpm[] = {
 };
 
 static void
-pixmapify_buttons (GtkWidget *toplevel)
+pixmapify_button (GtkWidget *toplevel, int down_p)
 {
   GdkPixmap *pixmap;
   GdkBitmap *mask;
@@ -1276,27 +1817,31 @@ pixmapify_buttons (GtkWidget *toplevel)
   GtkStyle *style;
   GtkWidget *w;
 
-  w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "next"));
+  w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel),
+                                  (down_p ? "next" : "prev")));
   style = gtk_widget_get_style (w);
   mask = 0;
   pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
                                          &style->bg[GTK_STATE_NORMAL],
-                                         (gchar **) down_arrow_xpm);
+                                         (down_p
+                                          ? (gchar **) down_arrow_xpm
+                                          : (gchar **) up_arrow_xpm));
   pixmapwid = gtk_pixmap_new (pixmap, mask);
   gtk_widget_show (pixmapwid);
   gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
   gtk_container_add (GTK_CONTAINER (w), pixmapwid);
+}
 
-  w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "prev"));
-  style = gtk_widget_get_style (w);
-  mask = 0;
-  pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
-                                         &style->bg[GTK_STATE_NORMAL],
-                                         (gchar **) up_arrow_xpm);
-  pixmapwid = gtk_pixmap_new (pixmap, mask);
-  gtk_widget_show (pixmapwid);
-  gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
-  gtk_container_add (GTK_CONTAINER (w), pixmapwid);
+static void
+map_next_button_cb (GtkWidget *w, gpointer user_data)
+{
+  pixmapify_button (w, 1);
+}
+
+static void
+map_prev_button_cb (GtkWidget *w, gpointer user_data)
+{
+  pixmapify_button (w, 0);
 }
 
 
@@ -1353,7 +1898,7 @@ get_hack_blurb (screenhack *hack)
   char *prog_name = strdup (hack->command);
   char *pretty_name = (hack->name
                        ? strdup (hack->name)
-                       : make_pretty_name (hack->command));
+                       : make_hack_name (hack->command));
   char doc_name[255], doc_class[255];
   char *s, *s2;
 
@@ -1458,7 +2003,7 @@ populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair)
   char *pretty_name = (hack
                        ? (hack->name
                           ? strdup (hack->name)
-                          : make_pretty_name (hack->command))
+                          : make_hack_name (hack->command))
                        : 0);
   char *doc_string = hack ? get_hack_blurb (hack) : 0;
 
@@ -1466,7 +2011,11 @@ populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair)
   gtk_label_set_text (doc, (doc_string ? doc_string : ""));
   gtk_entry_set_text (cmd, (hack ? hack->command : ""));
   gtk_entry_set_position (cmd, 0);
+
+  updating_enabled_cb++;
   gtk_toggle_button_set_active (enabled, (hack ? hack->enabled_p : False));
+  updating_enabled_cb--;
+
   gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
                       (hack
                        ? (hack->visual && *hack->visual
@@ -1549,6 +2098,22 @@ maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair)
 }
 
 
+\f
+/* Setting window manager icon
+ */
+
+static void
+init_icon (GdkWindow *window)
+{
+  GdkBitmap *mask = 0;
+  GdkColor transp;
+  GdkPixmap *pixmap =
+    gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
+                                  (gchar **) logo_50_xpm);
+  if (pixmap)
+    gdk_window_set_icon (window, 0, pixmap, mask);
+}
+
 \f
 /* The main demo-mode command loop.
  */
@@ -1580,7 +2145,7 @@ mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
 static void
 the_network_is_not_the_computer (GtkWidget *parent)
 {
-  Display *dpy = gdk_display;
+  Display *dpy = GDK_DISPLAY();
   char *rversion, *ruser, *rhost;
   char *luser, *lhost;
   char *msg = 0;
@@ -1764,7 +2329,6 @@ map_window_cb (GtkWidget *w, gpointer user_data)
 {
   Boolean oi = initializing_p;
   initializing_p = True;
-  pixmapify_buttons (w);
   eschew_gtk_lossage (w);
   ensure_selected_item_visible (GTK_WIDGET(name_to_widget(w, "list")));
   initializing_p = oi;
@@ -1775,11 +2339,6 @@ int
 main (int argc, char **argv)
 {
   XtAppContext app;
-# ifdef HAVE_CRAPPLET
-  GnomeClient *client;
-  GnomeClientFlags flags;
-  int init_results;
-# endif /* HAVE_CRAPPLET */
   prefs_pair Pair, *pair;
   saver_preferences P, P2, *p, *p2;
   Bool prefs = False;
@@ -1846,7 +2405,7 @@ main (int argc, char **argv)
 
 # else  /* !HAVE_CRAPPLET */
         fprintf (stderr, "%s: not compiled with --crapplet support\n",
-                 real_progname[i]);
+                 real_progname);
         USAGE ();
         exit (1);
 # endif /* !HAVE_CRAPPLET */
@@ -1858,9 +2417,21 @@ main (int argc, char **argv)
 # ifdef HAVE_CRAPPLET
   if (crapplet_p)
     {
-      init_results = gnome_capplet_init ("screensaver-properties",
-                                         short_version,
-                                         argc, argv, NULL, 0, NULL);
+      GnomeClient *client;
+      GnomeClientFlags flags = 0;
+
+      int init_results = gnome_capplet_init ("screensaver-properties",
+                                             short_version,
+                                             argc, argv, NULL, 0, NULL);
+      /* init_results is:
+         0 upon successful initialization;
+         1 if --init-session-settings was passed on the cmdline;
+         2 if --ignore was passed on the cmdline;
+        -1 on error.
+
+         So the 1 signifies just to init the settings, and quit, basically.
+         (Meaning launch the xscreensaver daemon.)
+       */
 
       if (init_results < 0)
         {
@@ -1878,14 +2449,38 @@ main (int argc, char **argv)
 
       if (client)
         flags = gnome_client_get_flags (client);
-      else
-        flags = 0;
 
       if (flags & GNOME_CLIENT_IS_CONNECTED)
         {
-          gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
+          int token =
+            gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
+                                         gnome_client_get_id (client));
+          if (token)
+            {
+              char *session_args[20];
+              int i = 0;
+              session_args[i++] = real_progname;
+              session_args[i++] = "--capplet";
+              session_args[i++] = "--init-session-settings";
+              session_args[i] = 0;
+              gnome_client_set_priority (client, 20);
+              gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
+              gnome_client_set_restart_command (client, i, session_args);
+            }
+          else
+            {
+              gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
+            }
+
           gnome_client_flush (client);
         }
+
+      if (init_results == 1)
+       {
+         system ("xscreensaver -nosplash &");
+         return 0;
+       }
+
     }
   else
 # endif /* HAVE_CRAPPLET */
@@ -1907,7 +2502,7 @@ main (int argc, char **argv)
    */
   XtToolkitInitialize ();
   app = XtCreateApplicationContext ();
-  dpy = gdk_display;
+  dpy = GDK_DISPLAY();
   XtAppSetFallbackResources (app, defaults);
   XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
   toplevel_shell = XtAppCreateShell (progname, progclass,
@@ -2022,6 +2617,12 @@ main (int argc, char **argv)
   gtk_signal_connect (
               GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "list")),
               "map", GTK_SIGNAL_FUNC(map_window_cb), 0);
+  gtk_signal_connect (
+              GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "prev")),
+              "map", GTK_SIGNAL_FUNC(map_prev_button_cb), 0);
+  gtk_signal_connect (
+              GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "next")),
+              "map", GTK_SIGNAL_FUNC(map_next_button_cb), 0);
 
 
   /* Handle the -prefs command-line argument. */
@@ -2046,10 +2647,6 @@ main (int argc, char **argv)
       gtk_container_remove (GTK_CONTAINER (gtk_window), top_vbox);
       GTK_OBJECT_SET_FLAGS (top_vbox, GTK_FLOATING);
 
-      /* This is a crock, but otherwise, the Control Center expands to
-         be as tall as the screen. */
-      gtk_window_set_default_size (GTK_WINDOW (top_vbox), 600, 400);
-
       /* In crapplet-mode, take off the menubar. */
       gtk_widget_hide (name_to_widget (gtk_window, "menubar"));
 
@@ -2068,6 +2665,7 @@ main (int argc, char **argv)
 # endif /* HAVE_CRAPPLET */
     {
       gtk_widget_show (gtk_window);
+      init_icon (GTK_WIDGET(gtk_window)->window);
 
       /* Issue any warnings about the running xscreensaver daemon. */
       the_network_is_not_the_computer (gtk_window);