1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2002 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #ifdef HAVE_GTK /* whole file */
19 #include <xscreensaver-intl.h>
29 #endif /* ENABLE_NLS */
32 # include <pwd.h> /* for getpwuid() */
38 # include <sys/utsname.h> /* for uname() */
39 #endif /* HAVE_UNAME */
49 #ifdef HAVE_SYS_WAIT_H
50 # include <sys/wait.h> /* for waitpid() and associated macros */
54 #include <X11/Xproto.h> /* for CARD32 */
55 #include <X11/Xatom.h> /* for XA_INTEGER */
56 #include <X11/Intrinsic.h>
57 #include <X11/StringDefs.h>
59 /* We don't actually use any widget internals, but these are included
60 so that gdb will have debug info for the widgets... */
61 #include <X11/IntrinsicP.h>
62 #include <X11/ShellP.h>
66 # include <X11/Xmu/Error.h>
68 # include <Xmu/Error.h>
78 # include <capplet-widget.h>
84 #include <glade/glade-xml.h>
85 #endif /* HAVE_GTK2 */
87 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
88 # define GLADE_DIR DEFAULT_ICONDIR
90 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
91 # define DEFAULT_ICONDIR GLADE_DIR
95 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
96 It is unused otherwise, so in that case, stub it out. */
97 static const char *hack_configuration_path = 0;
104 #include "resources.h" /* for parse_time() */
105 #include "visual.h" /* for has_writable_cells() */
106 #include "remote.h" /* for xscreensaver_command() */
109 #include "logo-50.xpm"
110 #include "logo-180.xpm"
112 #include "demo-Gtk-widgets.h"
113 #include "demo-Gtk-support.h"
114 #include "demo-Gtk-conf.h"
126 #endif /* HAVE_GTK2 */
129 extern void exec_command (const char *shell, const char *command, int nice);
132 #define countof(x) (sizeof((x))/sizeof((*x)))
136 char *progclass = "XScreenSaver";
139 /* The order of the items in the mode menu. */
140 static int mode_menu_order[] = {
141 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS };
146 char *short_version; /* version number of this xscreensaver build */
148 GtkWidget *toplevel_widget; /* the main window */
149 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
150 GtkWidget *popup_widget; /* the "Settings" dialog */
151 conf_data *cdata; /* private data for per-hack configuration */
154 GladeXML *glade_ui; /* Glade UI file */
155 #endif /* HAVE_GTK2 */
157 Bool debug_p; /* whether to print diagnostics */
158 Bool initializing_p; /* flag for breaking recursion loops */
159 Bool saving_p; /* flag for breaking recursion loops */
161 char *desired_preview_cmd; /* subprocess we intend to run */
162 char *running_preview_cmd; /* subprocess we are currently running */
163 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
164 Bool running_preview_error_p; /* whether the pid died abnormally */
166 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
167 int subproc_timer_id; /* timer to delay subproc launch */
168 int subproc_check_timer_id; /* timer to check whether it started up */
169 int subproc_check_countdown; /* how many more checks left */
171 int *list_elt_to_hack_number; /* table for sorting the hack list */
172 int *hack_number_to_list_elt; /* the inverse table */
173 Bool *hacks_available_p; /* whether hacks are on $PATH */
174 int list_count; /* how many items are in the list: this may be
175 less than p->screenhacks_count, if some are
178 int _selected_list_element; /* don't use this: call
179 selected_list_element() instead */
181 saver_preferences prefs;
186 /* Total fucking evilness due to the fact that it's rocket science to get
187 a closure object of our own down into the various widget callbacks. */
188 static state *global_state_kludge;
191 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
192 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
193 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
196 static void populate_demo_window (state *, int list_elt);
197 static void populate_prefs_page (state *);
198 static void populate_popup_window (state *);
200 static Bool flush_dialog_changes_and_save (state *);
201 static Bool flush_popup_changes_and_save (state *);
203 static int maybe_reload_init_file (state *);
204 static void await_xscreensaver (state *);
206 static void schedule_preview (state *, const char *cmd);
207 static void kill_preview_subproc (state *);
208 static void schedule_preview_check (state *);
212 /* Some random utility functions
218 time_t now = time ((time_t *) 0);
219 char *ct = (char *) ctime (&now);
220 static char buf[255];
221 int n = strlen(progname);
223 strncpy(buf, progname, n);
226 strncpy(buf+n, ct+11, 8);
227 strcpy(buf+n+9, ": ");
233 name_to_widget (state *s, const char *name)
243 /* First try to load the Glade file from the current directory;
244 if there isn't one there, check the installed directory.
246 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
247 const char * const files[] = { GLADE_FILE_NAME,
248 GLADE_DIR "/" GLADE_FILE_NAME };
250 for (i = 0; i < countof (files); i++)
253 if (!stat (files[i], &st))
255 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
262 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
263 "\tfrom " GLADE_DIR "/ or current directory.\n",
267 # undef GLADE_FILE_NAME
269 glade_xml_signal_autoconnect (s->glade_ui);
272 w = glade_xml_get_widget (s->glade_ui, name);
274 #else /* !HAVE_GTK2 */
276 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
279 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
281 #endif /* HAVE_GTK2 */
284 fprintf (stderr, "%s: no widget \"%s\"\n", blurb(), name);
289 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
290 Takes a scroller, viewport, or list as an argument.
293 ensure_selected_item_visible (GtkWidget *widget)
297 GtkTreeSelection *selection;
301 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
302 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
305 path = gtk_tree_model_get_path (model, &iter);
308 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (widget),
309 path, NULL, FALSE, 0.0, 0.0);
311 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
313 #else /* !HAVE_GTK2 */
315 GtkScrolledWindow *scroller = 0;
317 GtkList *list_widget = 0;
321 GtkWidget *selected = 0;
324 gint parent_h, child_y, child_h, children_h, ignore;
325 double ratio_t, ratio_b;
327 if (GTK_IS_SCROLLED_WINDOW (widget))
329 scroller = GTK_SCROLLED_WINDOW (widget);
330 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
331 list_widget = GTK_LIST (GTK_BIN(vp)->child);
333 else if (GTK_IS_VIEWPORT (widget))
335 vp = GTK_VIEWPORT (widget);
336 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
337 list_widget = GTK_LIST (GTK_BIN(vp)->child);
339 else if (GTK_IS_LIST (widget))
341 list_widget = GTK_LIST (widget);
342 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
343 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
348 slist = list_widget->selection;
349 selected = (slist ? GTK_WIDGET (slist->data) : 0);
353 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
355 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
356 kids; kids = kids->next)
359 adj = gtk_scrolled_window_get_vadjustment (scroller);
361 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
362 &ignore, &ignore, &ignore, &parent_h, &ignore);
363 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
364 &ignore, &child_y, &ignore, &child_h, &ignore);
365 children_h = nkids * child_h;
367 ratio_t = ((double) child_y) / ((double) children_h);
368 ratio_b = ((double) child_y + child_h) / ((double) children_h);
370 if (adj->upper == 0.0) /* no items in list */
373 if (ratio_t < (adj->value / adj->upper) ||
374 ratio_b > ((adj->value + adj->page_size) / adj->upper))
377 int slop = parent_h * 0.75; /* how much to overshoot by */
379 if (ratio_t < (adj->value / adj->upper))
381 double ratio_w = ((double) parent_h) / ((double) children_h);
382 double ratio_l = (ratio_b - ratio_t);
383 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
386 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
388 target = ratio_t * adj->upper;
392 if (target > adj->upper - adj->page_size)
393 target = adj->upper - adj->page_size;
397 gtk_adjustment_set_value (adj, target);
399 #endif /* !HAVE_GTK2 */
403 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
405 GtkWidget *shell = GTK_WIDGET (user_data);
406 while (shell->parent)
407 shell = shell->parent;
408 gtk_widget_destroy (GTK_WIDGET (shell));
412 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
414 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
416 restart_menu_cb (widget, user_data);
417 warning_dialog_dismiss_cb (widget, user_data);
421 warning_dialog (GtkWidget *parent, const char *message,
422 Boolean restart_button_p, int center)
424 char *msg = strdup (message);
427 GtkWidget *dialog = gtk_dialog_new ();
428 GtkWidget *label = 0;
430 GtkWidget *cancel = 0;
433 while (parent && !parent->window)
434 parent = parent->parent;
436 if (!GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
438 fprintf (stderr, "%s: too early for dialog?\n", progname);
446 char *s = strchr (head, '\n');
449 sprintf (name, "label%d", i++);
452 label = gtk_label_new (head);
454 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
455 #endif /* HAVE_GTK2 */
460 GTK_WIDGET (label)->style =
461 gtk_style_copy (GTK_WIDGET (label)->style);
462 GTK_WIDGET (label)->style->font =
463 gdk_font_load (get_string_resource("warning_dialog.headingFont",
465 gtk_widget_set_style (GTK_WIDGET (label),
466 GTK_WIDGET (label)->style);
468 #endif /* !HAVE_GTK2 */
470 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
471 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
472 label, TRUE, TRUE, 0);
473 gtk_widget_show (label);
484 label = gtk_label_new ("");
485 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
486 label, TRUE, TRUE, 0);
487 gtk_widget_show (label);
489 label = gtk_hbutton_box_new ();
490 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
491 label, TRUE, TRUE, 0);
494 if (restart_button_p)
496 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
497 gtk_container_add (GTK_CONTAINER (label), cancel);
500 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
501 gtk_container_add (GTK_CONTAINER (label), ok);
503 #else /* !HAVE_GTK2 */
505 ok = gtk_button_new_with_label ("OK");
506 gtk_container_add (GTK_CONTAINER (label), ok);
508 if (restart_button_p)
510 cancel = gtk_button_new_with_label ("Cancel");
511 gtk_container_add (GTK_CONTAINER (label), cancel);
514 #endif /* !HAVE_GTK2 */
516 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
517 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
518 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
519 GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
520 gtk_widget_show (ok);
521 gtk_widget_grab_focus (ok);
525 GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
526 gtk_widget_show (cancel);
528 gtk_widget_show (label);
529 gtk_widget_show (dialog);
531 if (restart_button_p)
533 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
534 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
536 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
537 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
542 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
543 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
547 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
548 GTK_WIDGET (parent)->window);
551 gtk_window_present (GTK_WINDOW (dialog));
552 #else /* !HAVE_GTK2 */
553 gdk_window_show (GTK_WIDGET (dialog)->window);
554 gdk_window_raise (GTK_WIDGET (dialog)->window);
555 #endif /* !HAVE_GTK2 */
562 run_cmd (state *s, Atom command, int arg)
567 flush_dialog_changes_and_save (s);
568 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
573 sprintf (buf, "Error:\n\n%s", err);
575 strcpy (buf, "Unknown error!");
576 warning_dialog (s->toplevel_widget, buf, False, 100);
583 run_hack (state *s, int list_elt, Bool report_errors_p)
586 if (list_elt < 0) return;
587 hack_number = s->list_elt_to_hack_number[list_elt];
589 flush_dialog_changes_and_save (s);
590 schedule_preview (s, 0);
592 run_cmd (s, XA_DEMO, hack_number + 1);
596 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
608 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
610 state *s = global_state_kludge; /* I hate C so much... */
611 flush_dialog_changes_and_save (s);
612 kill_preview_subproc (s);
617 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
619 state *s = (state *) data;
620 flush_dialog_changes_and_save (s);
627 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
630 char *vers = strdup (screensaver_id + 4);
633 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
635 s = strchr (vers, ',');
639 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
640 non-ASCII characters aren't allowed in localizable string keys."
641 (I don't want to just use (c) instead of © because that doesn't
642 look as good in the plain-old default Latin1 "C" locale.)
645 sprintf(copy, ("Copyright \xC2\xA9 1991-2002 %s"), s);
646 #else /* !HAVE_GTK2 */
647 sprintf(copy, ("Copyright \251 1991-2002 %s"), s);
648 #endif /* !HAVE_GTK2 */
650 sprintf (msg, "%s\n\n%s", copy, desc);
652 /* I can't make gnome_about_new() work here -- it starts dying in
653 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
654 then this might be the thing to do:
658 const gchar *auth[] = { 0 };
659 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
661 gtk_widget_show (about);
663 #else / * GTK but not GNOME * /
667 GdkColormap *colormap;
668 GdkPixmap *gdkpixmap;
671 GtkWidget *dialog = gtk_dialog_new ();
672 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
673 GtkWidget *parent = GTK_WIDGET (menuitem);
674 while (parent->parent)
675 parent = parent->parent;
677 hbox = gtk_hbox_new (FALSE, 20);
678 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
679 hbox, TRUE, TRUE, 0);
681 colormap = gtk_widget_get_colormap (parent);
683 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
684 (gchar **) logo_180_xpm);
685 icon = gtk_pixmap_new (gdkpixmap, mask);
686 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
688 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
690 vbox = gtk_vbox_new (FALSE, 0);
691 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
693 label1 = gtk_label_new (vers);
694 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
695 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
696 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
699 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
700 GTK_WIDGET (label1)->style->font =
701 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
702 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
703 #endif /* HAVE_GTK2 */
705 label2 = gtk_label_new (msg);
706 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
707 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
708 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
711 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
712 GTK_WIDGET (label2)->style->font =
713 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
714 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
715 #endif /* HAVE_GTK2 */
717 hb = gtk_hbutton_box_new ();
719 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
723 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
724 #else /* !HAVE_GTK2 */
725 ok = gtk_button_new_with_label (_("OK"));
726 #endif /* !HAVE_GTK2 */
727 gtk_container_add (GTK_CONTAINER (hb), ok);
729 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
730 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
731 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
733 gtk_widget_show (hbox);
734 gtk_widget_show (icon);
735 gtk_widget_show (vbox);
736 gtk_widget_show (label1);
737 gtk_widget_show (label2);
738 gtk_widget_show (hb);
739 gtk_widget_show (ok);
740 gtk_widget_show (dialog);
742 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
743 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
745 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
746 GTK_WIDGET (parent)->window);
747 gdk_window_show (GTK_WIDGET (dialog)->window);
748 gdk_window_raise (GTK_WIDGET (dialog)->window);
754 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
756 state *s = global_state_kludge; /* I hate C so much... */
757 saver_preferences *p = &s->prefs;
760 if (!p->help_url || !*p->help_url)
762 warning_dialog (s->toplevel_widget,
764 "No Help URL has been specified.\n"), False, 100);
768 help_command = (char *) malloc (strlen (p->load_url_command) +
769 (strlen (p->help_url) * 2) + 20);
770 strcpy (help_command, "( ");
771 sprintf (help_command + strlen(help_command),
772 p->load_url_command, p->help_url, p->help_url);
773 strcat (help_command, " ) &");
774 fprintf(stderr, "## %s\n", help_command);
775 system (help_command);
781 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
783 state *s = global_state_kludge; /* I hate C so much... */
784 run_cmd (s, XA_ACTIVATE, 0);
789 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
791 state *s = global_state_kludge; /* I hate C so much... */
792 run_cmd (s, XA_LOCK, 0);
797 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
799 state *s = global_state_kludge; /* I hate C so much... */
800 run_cmd (s, XA_EXIT, 0);
805 restart_menu_cb (GtkWidget *widget, gpointer user_data)
807 state *s = global_state_kludge; /* I hate C so much... */
808 flush_dialog_changes_and_save (s);
809 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
811 system ("xscreensaver -nosplash &");
813 await_xscreensaver (s);
817 await_xscreensaver (state *s)
821 Display *dpy = GDK_DISPLAY();
822 /* GtkWidget *dialog = 0;*/
825 while (!rversion && (--countdown > 0))
827 /* Check for the version of the running xscreensaver... */
828 server_xscreensaver_version (dpy, &rversion, 0, 0);
830 /* If it's not there yet, wait a second... */
835 /* if (dialog) gtk_widget_destroy (dialog);*/
844 /* Timed out, no screensaver running. */
847 Bool root_p = (geteuid () == 0);
851 "The xscreensaver daemon did not start up properly.\n"
856 _("You are running as root. This usually means that xscreensaver\n"
857 "was unable to contact your X server because access control is\n"
858 "turned on. Try running this command:\n"
860 " xhost +localhost\n"
862 "and then selecting `File / Restart Daemon'.\n"
864 "Note that turning off access control will allow anyone logged\n"
865 "on to this machine to access your screen, which might be\n"
866 "considered a security problem. Please read the xscreensaver\n"
867 "manual and FAQ for more information.\n"
869 "You shouldn't run X as root. Instead, you should log in as a\n"
870 "normal user, and `su' as necessary."));
872 strcat (buf, _("Please check your $PATH and permissions."));
874 warning_dialog (s->toplevel_widget, buf, False, 1);
880 selected_list_element (state *s)
882 return s->_selected_list_element;
887 demo_write_init_file (state *s, saver_preferences *p)
891 /* #### try to figure out why shit keeps getting reordered... */
892 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
896 if (!write_init_file (p, s->short_version, False))
899 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
904 const char *f = init_file_name();
906 warning_dialog (s->toplevel_widget,
907 _("Error:\n\nCouldn't determine init file name!\n"),
911 char *b = (char *) malloc (strlen(f) + 1024);
912 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
913 warning_dialog (s->toplevel_widget, b, False, 100);
922 run_this_cb (GtkButton *button, gpointer user_data)
924 state *s = global_state_kludge; /* I hate C so much... */
925 int list_elt = selected_list_element (s);
926 if (list_elt < 0) return;
927 if (!flush_dialog_changes_and_save (s))
928 run_hack (s, list_elt, True);
933 manual_cb (GtkButton *button, gpointer user_data)
935 state *s = global_state_kludge; /* I hate C so much... */
936 saver_preferences *p = &s->prefs;
937 GtkWidget *list_widget = name_to_widget (s, "list");
938 int list_elt = selected_list_element (s);
940 char *name, *name2, *cmd, *str;
941 if (list_elt < 0) return;
942 hack_number = s->list_elt_to_hack_number[list_elt];
944 flush_dialog_changes_and_save (s);
945 ensure_selected_item_visible (list_widget);
947 name = strdup (p->screenhacks[hack_number]->command);
949 while (isspace (*name2)) name2++;
951 while (*str && !isspace (*str)) str++;
953 str = strrchr (name2, '/');
954 if (str) name = str+1;
956 cmd = get_string_resource ("manualCommand", "ManualCommand");
959 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
961 sprintf (cmd2 + strlen (cmd2),
963 name2, name2, name2, name2);
964 strcat (cmd2, " ) &");
970 warning_dialog (GTK_WIDGET (button),
971 _("Error:\n\nno `manualCommand' resource set."),
980 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
982 GtkWidget *parent = name_to_widget (s, "scroller");
983 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
987 GtkTreeSelection *selection;
988 #endif /* HAVE_GTK2 */
990 if (!was) gtk_widget_set_sensitive (parent, True);
992 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
994 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
995 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
996 gtk_tree_selection_select_iter (selection, &iter);
997 #else /* !HAVE_GTK2 */
998 gtk_list_select_item (GTK_LIST (list), list_elt);
999 #endif /* !HAVE_GTK2 */
1000 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1001 if (!was) gtk_widget_set_sensitive (parent, False);
1006 run_next_cb (GtkButton *button, gpointer user_data)
1008 state *s = global_state_kludge; /* I hate C so much... */
1009 /* saver_preferences *p = &s->prefs; */
1010 Bool ops = s->preview_suppressed_p;
1012 GtkWidget *list_widget = name_to_widget (s, "list");
1013 int list_elt = selected_list_element (s);
1020 if (list_elt >= s->list_count)
1023 s->preview_suppressed_p = True;
1025 flush_dialog_changes_and_save (s);
1026 force_list_select_item (s, list_widget, list_elt, True);
1027 populate_demo_window (s, list_elt);
1028 run_hack (s, list_elt, False);
1030 s->preview_suppressed_p = ops;
1035 run_prev_cb (GtkButton *button, gpointer user_data)
1037 state *s = global_state_kludge; /* I hate C so much... */
1038 /* saver_preferences *p = &s->prefs; */
1039 Bool ops = s->preview_suppressed_p;
1041 GtkWidget *list_widget = name_to_widget (s, "list");
1042 int list_elt = selected_list_element (s);
1045 list_elt = s->list_count - 1;
1050 list_elt = s->list_count - 1;
1052 s->preview_suppressed_p = True;
1054 flush_dialog_changes_and_save (s);
1055 force_list_select_item (s, list_widget, list_elt, True);
1056 populate_demo_window (s, list_elt);
1057 run_hack (s, list_elt, False);
1059 s->preview_suppressed_p = ops;
1063 /* Writes the given settings into prefs.
1064 Returns true if there was a change, False otherwise.
1065 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1068 flush_changes (state *s,
1071 const char *command,
1074 saver_preferences *p = &s->prefs;
1075 Bool changed = False;
1078 if (list_elt < 0 || list_elt >= s->list_count)
1081 hack_number = s->list_elt_to_hack_number[list_elt];
1082 hack = p->screenhacks[hack_number];
1084 if (enabled_p != -1 &&
1085 enabled_p != hack->enabled_p)
1087 hack->enabled_p = enabled_p;
1090 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1091 blurb(), hack->name, enabled_p);
1096 if (!hack->command || !!strcmp (command, hack->command))
1098 if (hack->command) free (hack->command);
1099 hack->command = strdup (command);
1102 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1103 blurb(), hack->name, command);
1109 const char *ov = hack->visual;
1110 if (!ov || !*ov) ov = "any";
1111 if (!*visual) visual = "any";
1112 if (!!strcasecmp (visual, ov))
1114 if (hack->visual) free (hack->visual);
1115 hack->visual = strdup (visual);
1118 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1119 blurb(), hack->name, visual);
1127 /* Helper for the text fields that contain time specifications:
1128 this parses the text, and does error checking.
1131 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1136 if (!sec_p || strchr (line, ':'))
1137 value = parse_time ((char *) line, sec_p, True);
1141 if (sscanf (line, "%u%c", &value, &c) != 1)
1147 value *= 1000; /* Time measures in microseconds */
1153 "Unparsable time format: \"%s\"\n"),
1155 warning_dialog (s->toplevel_widget, b, False, 100);
1164 directory_p (const char *path)
1167 if (!path || !*path)
1169 else if (stat (path, &st))
1171 else if (!S_ISDIR (st.st_mode))
1178 normalize_directory (const char *path)
1182 if (!path || !*path) return 0;
1184 p2 = (char *) malloc (L + 2);
1186 if (p2[L-1] == '/') /* remove trailing slash */
1189 for (s = p2; s && *s; s++)
1192 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1193 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1196 while (s0 > p2 && s0[-1] != '/')
1206 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1207 strcpy (s, s+2), s--;
1208 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1212 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1213 while (s[0] == '/' && s[1] == '/')
1216 /* and strip trailing whitespace for good measure. */
1218 while (isspace(p2[L-1]))
1231 } FlushForeachClosure;
1234 flush_checkbox (GtkTreeModel *model,
1239 FlushForeachClosure *closure = data;
1242 gtk_tree_model_get (model, iter,
1243 COL_ENABLED, &checked,
1246 if (flush_changes (closure->s, closure->i,
1248 *closure->changed = True;
1252 /* don't remove row */
1256 #endif /* HAVE_GTK2 */
1258 /* Flush out any changes made in the main dialog window (where changes
1259 take place immediately: clicking on a checkbox causes the init file
1260 to be written right away.)
1263 flush_dialog_changes_and_save (state *s)
1265 saver_preferences *p = &s->prefs;
1266 saver_preferences P2, *p2 = &P2;
1268 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1269 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1270 FlushForeachClosure closure;
1271 #else /* !HAVE_GTK2 */
1272 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1273 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1275 #endif /* !HAVE_GTK2 */
1277 Bool changed = False;
1280 if (s->saving_p) return False;
1285 /* Flush any checkbox changes in the list down into the prefs struct.
1289 closure.changed = &changed;
1291 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1293 #else /* !HAVE_GTK2 */
1295 for (i = 0; kids; kids = kids->next, i++)
1297 GtkWidget *line = GTK_WIDGET (kids->data);
1298 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1299 GtkWidget *line_check =
1300 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1302 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1304 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1307 #endif /* ~HAVE_GTK2 */
1309 /* Flush the non-hack-specific settings down into the prefs struct.
1312 # define SECONDS(FIELD,NAME) \
1313 w = name_to_widget (s, (NAME)); \
1314 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1316 # define MINUTES(FIELD,NAME) \
1317 w = name_to_widget (s, (NAME)); \
1318 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1320 # define CHECKBOX(FIELD,NAME) \
1321 w = name_to_widget (s, (NAME)); \
1322 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1324 # define PATHNAME(FIELD,NAME) \
1325 w = name_to_widget (s, (NAME)); \
1326 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1328 MINUTES (&p2->timeout, "timeout_spinbutton");
1329 MINUTES (&p2->cycle, "cycle_spinbutton");
1330 CHECKBOX (p2->lock_p, "lock_button");
1331 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1333 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1334 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1335 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1336 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1338 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1339 CHECKBOX (p2->grab_video_p, "grab_video_button");
1340 CHECKBOX (p2->random_image_p, "grab_image_button");
1341 PATHNAME (p2->image_directory, "image_text");
1343 CHECKBOX (p2->verbose_p, "verbose_button");
1344 CHECKBOX (p2->capture_stderr_p, "capture_button");
1345 CHECKBOX (p2->splash_p, "splash_button");
1347 CHECKBOX (p2->install_cmap_p, "install_button");
1348 CHECKBOX (p2->fade_p, "fade_button");
1349 CHECKBOX (p2->unfade_p, "unfade_button");
1350 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1357 /* Warn if the image directory doesn't exist.
1359 if (p2->image_directory &&
1360 *p2->image_directory &&
1361 !directory_p (p2->image_directory))
1364 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1365 p2->image_directory);
1366 warning_dialog (s->toplevel_widget, b, False, 100);
1370 /* Map the mode menu to `saver_mode' enum values. */
1372 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1373 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1374 GtkWidget *selected = gtk_menu_get_active (menu);
1375 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1376 int menu_elt = g_list_index (kids, (gpointer) selected);
1377 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1378 p2->mode = mode_menu_order[menu_elt];
1381 if (p2->mode == ONE_HACK)
1383 int list_elt = selected_list_element (s);
1384 p2->selected_hack = (list_elt >= 0
1385 ? s->list_elt_to_hack_number[list_elt]
1389 # define COPY(field, name) \
1390 if (p->field != p2->field) { \
1393 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1395 p->field = p2->field
1398 COPY(selected_hack, "selected_hack");
1400 COPY(timeout, "timeout");
1401 COPY(cycle, "cycle");
1402 COPY(lock_p, "lock_p");
1403 COPY(lock_timeout, "lock_timeout");
1405 COPY(dpms_enabled_p, "dpms_enabled_p");
1406 COPY(dpms_standby, "dpms_standby");
1407 COPY(dpms_suspend, "dpms_suspend");
1408 COPY(dpms_off, "dpms_off");
1410 COPY(verbose_p, "verbose_p");
1411 COPY(capture_stderr_p, "capture_stderr_p");
1412 COPY(splash_p, "splash_p");
1414 COPY(install_cmap_p, "install_cmap_p");
1415 COPY(fade_p, "fade_p");
1416 COPY(unfade_p, "unfade_p");
1417 COPY(fade_seconds, "fade_seconds");
1419 COPY(grab_desktop_p, "grab_desktop_p");
1420 COPY(grab_video_p, "grab_video_p");
1421 COPY(random_image_p, "random_image_p");
1425 if (!p->image_directory ||
1426 !p2->image_directory ||
1427 strcmp(p->image_directory, p2->image_directory))
1431 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1432 blurb(), p2->image_directory);
1434 if (p->image_directory && p->image_directory != p2->image_directory)
1435 free (p->image_directory);
1436 p->image_directory = p2->image_directory;
1437 p2->image_directory = 0;
1439 populate_prefs_page (s);
1443 Display *dpy = GDK_DISPLAY();
1444 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1445 sync_server_dpms_settings (dpy, enabled_p,
1446 p->dpms_standby / 1000,
1447 p->dpms_suspend / 1000,
1451 changed = demo_write_init_file (s, p);
1454 s->saving_p = False;
1459 /* Flush out any changes made in the popup dialog box (where changes
1460 take place only when the OK button is clicked.)
1463 flush_popup_changes_and_save (state *s)
1465 Bool changed = False;
1466 saver_preferences *p = &s->prefs;
1467 int list_elt = selected_list_element (s);
1469 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1470 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1472 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1473 const char *command = gtk_entry_get_text (cmd);
1478 if (s->saving_p) return False;
1484 if (maybe_reload_init_file (s) != 0)
1490 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1492 if (!strcasecmp (visual, "")) visual = "";
1493 else if (!strcasecmp (visual, "any")) visual = "";
1494 else if (!strcasecmp (visual, "default")) visual = "Default";
1495 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1496 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1497 else if (!strcasecmp (visual, "best")) visual = "Best";
1498 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1499 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1500 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1501 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1502 else if (!strcasecmp (visual, "color")) visual = "Color";
1503 else if (!strcasecmp (visual, "gl")) visual = "GL";
1504 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1505 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1506 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1507 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1508 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1509 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1510 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1511 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
1512 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1515 gdk_beep (); /* unparsable */
1517 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1520 changed = flush_changes (s, list_elt, -1, command, visual);
1523 changed = demo_write_init_file (s, p);
1525 /* Do this to re-launch the hack if (and only if) the command line
1527 populate_demo_window (s, selected_list_element (s));
1531 s->saving_p = False;
1537 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1539 state *s = global_state_kludge; /* I hate C so much... */
1540 if (! s->initializing_p)
1542 s->initializing_p = True;
1543 flush_dialog_changes_and_save (s);
1544 s->initializing_p = False;
1549 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1551 pref_changed_cb (widget, user_data);
1555 /* Callback on menu items in the "mode" options menu.
1558 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1560 state *s = (state *) user_data;
1561 saver_preferences *p = &s->prefs;
1562 GtkWidget *list = name_to_widget (s, "list");
1565 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1567 saver_mode new_mode;
1568 int old_selected = p->selected_hack;
1572 if (menu_items->data == widget)
1575 menu_items = menu_items->next;
1577 if (!menu_items) abort();
1579 new_mode = mode_menu_order[menu_index];
1581 /* Keep the same list element displayed as before; except if we're
1582 switching *to* "one screensaver" mode from any other mode, scroll
1583 to and select "the one".
1586 if (new_mode == ONE_HACK)
1587 list_elt = (p->selected_hack >= 0
1588 ? s->hack_number_to_list_elt[p->selected_hack]
1592 list_elt = selected_list_element (s);
1595 saver_mode old_mode = p->mode;
1597 populate_demo_window (s, list_elt);
1598 force_list_select_item (s, list, list_elt, True);
1599 p->mode = old_mode; /* put it back, so the init file gets written */
1602 pref_changed_cb (widget, user_data);
1604 if (old_selected != p->selected_hack)
1605 abort(); /* dammit, not again... */
1610 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1611 gint page_num, gpointer user_data)
1613 state *s = global_state_kludge; /* I hate C so much... */
1614 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1616 /* If we're switching to page 0, schedule the current hack to be run.
1617 Otherwise, schedule it to stop. */
1619 populate_demo_window (s, selected_list_element (s));
1621 schedule_preview (s, 0);
1626 list_activated_cb (GtkTreeView *list,
1628 GtkTreeViewColumn *column,
1635 g_return_if_fail (!gdk_pointer_is_grabbed ());
1637 str = gtk_tree_path_to_string (path);
1638 list_elt = strtol (str, NULL, 10);
1642 run_hack (s, list_elt, True);
1646 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1648 state *s = (state *)data;
1649 GtkTreeModel *model;
1655 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1658 path = gtk_tree_model_get_path (model, &iter);
1659 str = gtk_tree_path_to_string (path);
1660 list_elt = strtol (str, NULL, 10);
1662 gtk_tree_path_free (path);
1665 populate_demo_window (s, list_elt);
1666 flush_dialog_changes_and_save (s);
1669 #else /* !HAVE_GTK2 */
1671 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1672 list_select_cb that comes in
1673 *after* we've double-clicked.
1677 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1680 state *s = (state *) data;
1681 if (event->type == GDK_2BUTTON_PRESS)
1683 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1684 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1686 last_doubleclick_time = time ((time_t *) 0);
1689 run_hack (s, list_elt, True);
1697 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1699 state *s = (state *) data;
1700 time_t now = time ((time_t *) 0);
1702 if (now >= last_doubleclick_time + 2)
1704 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1705 populate_demo_window (s, list_elt);
1706 flush_dialog_changes_and_save (s);
1711 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1713 state *s = (state *) data;
1714 populate_demo_window (s, -1);
1715 flush_dialog_changes_and_save (s);
1718 #endif /* !HAVE_GTK2 */
1721 /* Called when the checkboxes that are in the left column of the
1722 scrolling list are clicked. This both populates the right pane
1723 (just as clicking on the label (really, listitem) does) and
1724 also syncs this checkbox with the right pane Enabled checkbox.
1729 GtkCellRendererToggle *toggle,
1731 #else /* !HAVE_GTK2 */
1733 #endif /* !HAVE_GTK2 */
1736 state *s = (state *) data;
1739 GtkScrolledWindow *scroller =
1740 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1741 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1742 GtkTreeModel *model = gtk_tree_view_get_model (list);
1743 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1746 #else /* !HAVE_GTK2 */
1747 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1748 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1750 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1751 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1752 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1753 #endif /* !HAVE_GTK2 */
1760 if (!gtk_tree_model_get_iter (model, &iter, path))
1762 g_warning ("bad path: %s", path_string);
1765 gtk_tree_path_free (path);
1767 gtk_tree_model_get (model, &iter,
1768 COL_ENABLED, &active,
1771 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1772 COL_ENABLED, !active,
1775 list_elt = strtol (path_string, NULL, 10);
1776 #else /* !HAVE_GTK2 */
1777 list_elt = gtk_list_child_position (list, line);
1778 #endif /* !HAVE_GTK2 */
1780 /* remember previous scroll position of the top of the list */
1781 adj = gtk_scrolled_window_get_vadjustment (scroller);
1782 scroll_top = adj->value;
1784 flush_dialog_changes_and_save (s);
1785 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1786 populate_demo_window (s, list_elt);
1788 /* restore the previous scroll position of the top of the list.
1789 this is weak, but I don't really know why it's moving... */
1790 gtk_adjustment_set_value (adj, scroll_top);
1796 GtkFileSelection *widget;
1797 } file_selection_data;
1802 store_image_directory (GtkWidget *button, gpointer user_data)
1804 file_selection_data *fsd = (file_selection_data *) user_data;
1805 state *s = fsd->state;
1806 GtkFileSelection *selector = fsd->widget;
1807 GtkWidget *top = s->toplevel_widget;
1808 saver_preferences *p = &s->prefs;
1809 const char *path = gtk_file_selection_get_filename (selector);
1811 if (p->image_directory && !strcmp(p->image_directory, path))
1812 return; /* no change */
1814 if (!directory_p (path))
1817 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1818 warning_dialog (GTK_WIDGET (top), b, False, 100);
1822 if (p->image_directory) free (p->image_directory);
1823 p->image_directory = normalize_directory (path);
1825 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1826 (p->image_directory ? p->image_directory : ""));
1827 demo_write_init_file (s, p);
1832 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1834 file_selection_data *fsd = (file_selection_data *) user_data;
1835 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1839 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1841 browse_image_dir_cancel (button, user_data);
1842 store_image_directory (button, user_data);
1846 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1848 browse_image_dir_cancel (widget, user_data);
1853 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1855 state *s = global_state_kludge; /* I hate C so much... */
1856 saver_preferences *p = &s->prefs;
1857 static file_selection_data *fsd = 0;
1859 GtkFileSelection *selector = GTK_FILE_SELECTION(
1860 gtk_file_selection_new ("Please select the image directory."));
1863 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1865 fsd->widget = selector;
1868 if (p->image_directory && *p->image_directory)
1869 gtk_file_selection_set_filename (selector, p->image_directory);
1871 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1872 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1874 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1875 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1877 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1878 GTK_SIGNAL_FUNC (browse_image_dir_close),
1881 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1883 gtk_window_set_modal (GTK_WINDOW (selector), True);
1884 gtk_widget_show (GTK_WIDGET (selector));
1889 settings_cb (GtkButton *button, gpointer user_data)
1891 state *s = global_state_kludge; /* I hate C so much... */
1892 int list_elt = selected_list_element (s);
1894 populate_demo_window (s, list_elt); /* reset the widget */
1895 populate_popup_window (s); /* create UI on popup window */
1896 gtk_widget_show (s->popup_widget);
1900 settings_sync_cmd_text (state *s)
1903 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1904 char *cmd_line = get_configurator_command_line (s->cdata);
1905 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1906 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1908 # endif /* HAVE_XML */
1912 settings_adv_cb (GtkButton *button, gpointer user_data)
1914 state *s = global_state_kludge; /* I hate C so much... */
1915 GtkNotebook *notebook =
1916 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1918 settings_sync_cmd_text (s);
1919 gtk_notebook_set_page (notebook, 1);
1923 settings_std_cb (GtkButton *button, gpointer user_data)
1925 state *s = global_state_kludge; /* I hate C so much... */
1926 GtkNotebook *notebook =
1927 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1929 /* Re-create UI to reflect the in-progress command-line settings. */
1930 populate_popup_window (s);
1932 gtk_notebook_set_page (notebook, 0);
1936 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1937 gint page_num, gpointer user_data)
1939 state *s = global_state_kludge; /* I hate C so much... */
1940 GtkWidget *adv = name_to_widget (s, "adv_button");
1941 GtkWidget *std = name_to_widget (s, "std_button");
1945 gtk_widget_show (adv);
1946 gtk_widget_hide (std);
1948 else if (page_num == 1)
1950 gtk_widget_hide (adv);
1951 gtk_widget_show (std);
1960 settings_cancel_cb (GtkButton *button, gpointer user_data)
1962 state *s = global_state_kludge; /* I hate C so much... */
1963 gtk_widget_hide (s->popup_widget);
1967 settings_ok_cb (GtkButton *button, gpointer user_data)
1969 state *s = global_state_kludge; /* I hate C so much... */
1970 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1971 int page = gtk_notebook_get_current_page (notebook);
1974 /* Regenerate the command-line from the widget contents before saving.
1975 But don't do this if we're looking at the command-line page already,
1976 or we will blow away what they typed... */
1977 settings_sync_cmd_text (s);
1979 flush_popup_changes_and_save (s);
1980 gtk_widget_hide (s->popup_widget);
1984 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1986 state *s = (state *) data;
1987 settings_cancel_cb (0, (gpointer) s);
1993 /* Populating the various widgets
1997 /* Returns the number of the last hack run by the server.
2000 server_current_hack (void)
2004 unsigned long nitems, bytesafter;
2006 Display *dpy = GDK_DISPLAY();
2007 int hack_number = -1;
2009 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2010 XA_SCREENSAVER_STATUS,
2011 0, 3, False, XA_INTEGER,
2012 &type, &format, &nitems, &bytesafter,
2013 (unsigned char **) &data)
2015 && type == XA_INTEGER
2018 hack_number = (int) data[2] - 1;
2020 if (data) free (data);
2026 /* Finds the number of the last hack to run, and makes that item be
2027 selected by default.
2030 scroll_to_current_hack (state *s)
2032 saver_preferences *p = &s->prefs;
2035 if (p->mode == ONE_HACK)
2036 hack_number = p->selected_hack;
2038 hack_number = server_current_hack ();
2040 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2042 int list_elt = s->hack_number_to_list_elt[hack_number];
2043 GtkWidget *list = name_to_widget (s, "list");
2044 force_list_select_item (s, list, list_elt, True);
2045 populate_demo_window (s, list_elt);
2051 on_path_p (const char *program)
2055 char *cmd = strdup (program);
2056 char *token = strchr (cmd, ' ');
2060 if (token) *token = 0;
2063 if (strchr (cmd, '/'))
2065 result = (0 == stat (cmd, &st));
2069 path = getenv("PATH");
2070 if (!path || !*path)
2074 path = strdup (path);
2075 token = strtok (path, ":");
2079 char *p2 = (char *) malloc (strlen (token) + L + 3);
2083 result = (0 == stat (p2, &st));
2086 token = strtok (0, ":");
2091 if (path) free (path);
2097 populate_hack_list (state *s)
2100 saver_preferences *p = &s->prefs;
2101 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2102 GtkListStore *model;
2103 GtkTreeSelection *selection;
2104 GtkCellRenderer *ren;
2108 g_object_get (G_OBJECT (list),
2113 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2114 g_object_set (G_OBJECT (list), "model", model, NULL);
2115 g_object_unref (model);
2117 ren = gtk_cell_renderer_toggle_new ();
2118 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2120 "active", COL_ENABLED,
2123 g_signal_connect (ren, "toggled",
2124 G_CALLBACK (list_checkbox_cb),
2127 ren = gtk_cell_renderer_text_new ();
2128 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2129 _("Screen Saver"), ren,
2133 g_signal_connect_after (list, "row_activated",
2134 G_CALLBACK (list_activated_cb),
2137 selection = gtk_tree_view_get_selection (list);
2138 g_signal_connect (selection, "changed",
2139 G_CALLBACK (list_select_changed_cb),
2144 for (i = 0; i < s->list_count; i++)
2146 int hack_number = s->list_elt_to_hack_number[i];
2147 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2149 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2151 if (!hack) continue;
2153 /* If we're to suppress uninstalled hacks, check $PATH now. */
2154 if (p->ignore_uninstalled_p && !available_p)
2157 pretty_name = (hack->name
2158 ? strdup (hack->name)
2159 : make_hack_name (hack->command));
2163 /* Make the text foreground be the color of insensitive widgets
2164 (but don't actually make it be insensitive, since we still
2165 want to be able to click on it.)
2167 GtkStyle *style = GTK_WIDGET (list)->style;
2168 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2169 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2170 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2172 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2173 /* " background=\"#%02X%02X%02X\"" */
2175 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2176 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2182 gtk_list_store_append (model, &iter);
2183 gtk_list_store_set (model, &iter,
2184 COL_ENABLED, hack->enabled_p,
2185 COL_NAME, pretty_name,
2190 #else /* !HAVE_GTK2 */
2192 saver_preferences *p = &s->prefs;
2193 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2195 for (i = 0; i < s->list_count; i++)
2197 int hack_number = s->list_elt_to_hack_number[i];
2198 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2200 /* A GtkList must contain only GtkListItems, but those can contain
2201 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2202 and a Label. We handle single and double click events on the
2203 line itself, for clicking on the text, but the interior checkbox
2204 also handles its own events.
2207 GtkWidget *line_hbox;
2208 GtkWidget *line_check;
2209 GtkWidget *line_label;
2211 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2213 if (!hack) continue;
2215 /* If we're to suppress uninstalled hacks, check $PATH now. */
2216 if (p->ignore_uninstalled_p && !available_p)
2219 pretty_name = (hack->name
2220 ? strdup (hack->name)
2221 : make_hack_name (hack->command));
2223 line = gtk_list_item_new ();
2224 line_hbox = gtk_hbox_new (FALSE, 0);
2225 line_check = gtk_check_button_new ();
2226 line_label = gtk_label_new (pretty_name);
2228 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2229 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2230 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2232 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2234 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2236 gtk_widget_show (line_check);
2237 gtk_widget_show (line_label);
2238 gtk_widget_show (line_hbox);
2239 gtk_widget_show (line);
2243 gtk_container_add (GTK_CONTAINER (list), line);
2244 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2245 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2248 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2249 GTK_SIGNAL_FUNC (list_checkbox_cb),
2252 gtk_widget_show (line);
2256 /* Make the widget be colored like insensitive widgets
2257 (but don't actually make it be insensitive, since we
2258 still want to be able to click on it.)
2260 GtkRcStyle *rc_style;
2263 gtk_widget_realize (GTK_WIDGET (line_label));
2265 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2266 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2268 rc_style = gtk_rc_style_new ();
2269 rc_style->fg[GTK_STATE_NORMAL] = fg;
2270 rc_style->bg[GTK_STATE_NORMAL] = bg;
2271 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2273 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2274 gtk_rc_style_unref (rc_style);
2278 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2279 GTK_SIGNAL_FUNC (list_select_cb),
2281 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2282 GTK_SIGNAL_FUNC (list_unselect_cb),
2284 #endif /* !HAVE_GTK2 */
2288 update_list_sensitivity (state *s)
2290 saver_preferences *p = &s->prefs;
2291 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2292 Bool checkable = (p->mode == RANDOM_HACKS);
2293 Bool blankable = (p->mode != DONT_BLANK);
2296 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2297 GtkWidget *use = name_to_widget (s, "use_col_frame");
2298 #endif /* HAVE_GTK2 */
2299 GtkWidget *scroller = name_to_widget (s, "scroller");
2300 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2301 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2304 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2305 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2306 #else /* !HAVE_GTK2 */
2307 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2308 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2310 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2311 #endif /* !HAVE_GTK2 */
2312 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2313 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2315 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2318 gtk_tree_view_column_set_visible (use, checkable);
2319 #else /* !HAVE_GTK2 */
2321 gtk_widget_show (use); /* the "Use" column header */
2323 gtk_widget_hide (use);
2327 GtkBin *line = GTK_BIN (kids->data);
2328 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2329 GtkWidget *line_check =
2330 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2333 gtk_widget_show (line_check);
2335 gtk_widget_hide (line_check);
2339 #endif /* !HAVE_GTK2 */
2344 populate_prefs_page (state *s)
2346 saver_preferences *p = &s->prefs;
2348 /* The file supports timeouts of less than a minute, but the GUI does
2349 not, so throttle the values to be at least one minute (since "0" is
2350 a bad rounding choice...)
2352 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2355 THROTTLE (passwd_timeout);
2358 # define FMT_MINUTES(NAME,N) \
2359 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2361 # define FMT_SECONDS(NAME,N) \
2362 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2364 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2365 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2366 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2367 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2368 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2369 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2370 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2375 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2376 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2379 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2380 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2381 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2382 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2383 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2384 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2385 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2386 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2387 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2388 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2389 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2391 # undef TOGGLE_ACTIVE
2393 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2394 (p->image_directory ? p->image_directory : ""));
2395 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2397 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2400 /* Map the `saver_mode' enum to mode menu to values. */
2402 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2405 for (i = 0; i < countof(mode_menu_order); i++)
2406 if (mode_menu_order[i] == p->mode)
2408 gtk_option_menu_set_history (opt, i);
2409 update_list_sensitivity (s);
2413 Bool found_any_writable_cells = False;
2414 Bool dpms_supported = False;
2416 Display *dpy = GDK_DISPLAY();
2417 int nscreens = ScreenCount(dpy);
2419 for (i = 0; i < nscreens; i++)
2421 Screen *s = ScreenOfDisplay (dpy, i);
2422 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2424 found_any_writable_cells = True;
2429 #ifdef HAVE_XF86VMODE_GAMMA
2430 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2433 #ifdef HAVE_DPMS_EXTENSION
2435 int op = 0, event = 0, error = 0;
2436 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2437 dpms_supported = True;
2439 #endif /* HAVE_DPMS_EXTENSION */
2442 # define SENSITIZE(NAME,SENSITIVEP) \
2443 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2445 /* Blanking and Locking
2447 SENSITIZE ("lock_spinbutton", p->lock_p);
2448 SENSITIZE ("lock_mlabel", p->lock_p);
2452 SENSITIZE ("dpms_frame", dpms_supported);
2453 SENSITIZE ("dpms_button", dpms_supported);
2454 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2455 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2456 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2457 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2458 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2459 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2460 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2461 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2462 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2466 SENSITIZE ("cmap_frame", found_any_writable_cells);
2467 SENSITIZE ("install_button", found_any_writable_cells);
2468 SENSITIZE ("fade_button", found_any_writable_cells);
2469 SENSITIZE ("unfade_button", found_any_writable_cells);
2471 SENSITIZE ("fade_label", (found_any_writable_cells &&
2472 (p->fade_p || p->unfade_p)));
2473 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2474 (p->fade_p || p->unfade_p)));
2482 populate_popup_window (state *s)
2484 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2485 char *doc_string = 0;
2487 /* #### not in Gtk 1.2
2488 gtk_label_set_selectable (doc);
2494 free_conf_data (s->cdata);
2499 saver_preferences *p = &s->prefs;
2500 int list_elt = selected_list_element (s);
2501 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2502 ? s->list_elt_to_hack_number[list_elt]
2504 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2507 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2508 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2509 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2510 s->cdata = load_configurator (cmd_line, s->debug_p);
2511 if (s->cdata && s->cdata->widget)
2512 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2517 doc_string = (s->cdata
2518 ? s->cdata->description
2520 # else /* !HAVE_XML */
2521 doc_string = _("Descriptions not available: no XML support compiled in.");
2522 # endif /* !HAVE_XML */
2524 gtk_label_set_text (doc, (doc_string
2526 : _("No description available.")));
2531 sensitize_demo_widgets (state *s, Bool sensitive_p)
2533 const char *names1[] = { "demo", "settings" };
2534 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2535 "visual", "visual_combo" };
2537 for (i = 0; i < countof(names1); i++)
2539 GtkWidget *w = name_to_widget (s, names1[i]);
2540 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2542 for (i = 0; i < countof(names2); i++)
2544 GtkWidget *w = name_to_widget (s, names2[i]);
2545 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2550 /* Even though we've given these text fields a maximum number of characters,
2551 their default size is still about 30 characters wide -- so measure out
2552 a string in their font, and resize them to just fit that.
2555 fix_text_entry_sizes (state *s)
2559 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2560 const char * const spinbuttons[] = {
2561 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2562 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2563 "dpms_off_spinbutton",
2564 "-fade_spinbutton" };
2568 for (i = 0; i < countof(spinbuttons); i++)
2570 const char *n = spinbuttons[i];
2572 while (*n == '-') n++, cols--;
2573 w = GTK_WIDGET (name_to_widget (s, n));
2574 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2575 gtk_widget_set_usize (w, width, -2);
2578 /* Now fix the width of the combo box.
2580 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2581 w = GTK_COMBO (w)->entry;
2582 width = gdk_string_width (w->style->font, "PseudoColor___");
2583 gtk_widget_set_usize (w, width, -2);
2585 /* Now fix the width of the file entry text.
2587 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2588 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2589 gtk_widget_set_usize (w, width, -2);
2591 /* Now fix the width of the command line text.
2593 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2594 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2595 gtk_widget_set_usize (w, width, -2);
2599 /* Now fix the height of the list widget:
2600 make it default to being around 10 text-lines high instead of 4.
2602 w = GTK_WIDGET (name_to_widget (s, "list"));
2606 int leading = 3; /* approximate is ok... */
2610 PangoFontMetrics *pain =
2611 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2612 w->style->font_desc,
2613 gtk_get_default_language ());
2614 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2615 pango_font_metrics_get_descent (pain));
2616 #else /* !HAVE_GTK2 */
2617 height = w->style->font->ascent + w->style->font->descent;
2618 #endif /* !HAVE_GTK2 */
2622 height += border * 2;
2623 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2624 gtk_widget_set_usize (w, -2, height);
2631 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2634 static char *up_arrow_xpm[] = {
2657 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2658 the end of the array (Gtk 1.2.5.) */
2659 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2660 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2663 static char *down_arrow_xpm[] = {
2686 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2687 the end of the array (Gtk 1.2.5.) */
2688 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2689 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2693 pixmapify_button (state *s, int down_p)
2697 GtkWidget *pixmapwid;
2701 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2702 style = gtk_widget_get_style (w);
2704 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2705 &style->bg[GTK_STATE_NORMAL],
2707 ? (gchar **) down_arrow_xpm
2708 : (gchar **) up_arrow_xpm));
2709 pixmapwid = gtk_pixmap_new (pixmap, mask);
2710 gtk_widget_show (pixmapwid);
2711 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2712 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2716 map_next_button_cb (GtkWidget *w, gpointer user_data)
2718 state *s = (state *) user_data;
2719 pixmapify_button (s, 1);
2723 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2725 state *s = (state *) user_data;
2726 pixmapify_button (s, 0);
2728 #endif /* !HAVE_GTK2 */
2731 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2735 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2736 GtkAllocation *allocation,
2740 GtkWidgetAuxInfo *aux_info;
2742 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2744 aux_info->width = allocation->width;
2745 aux_info->height = -2;
2749 gtk_widget_size_request (label, &req);
2753 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2756 eschew_gtk_lossage (GtkLabel *label)
2758 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2759 aux_info->width = GTK_WIDGET (label)->allocation.width;
2760 aux_info->height = -2;
2764 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2766 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2767 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2770 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2772 gtk_widget_queue_resize (GTK_WIDGET (label));
2777 populate_demo_window (state *s, int list_elt)
2779 saver_preferences *p = &s->prefs;
2782 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2783 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2784 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2785 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2786 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2788 if (p->mode == BLANK_ONLY)
2791 pretty_name = strdup (_("Blank Screen"));
2792 schedule_preview (s, 0);
2794 else if (p->mode == DONT_BLANK)
2797 pretty_name = strdup (_("Screen Saver Disabled"));
2798 schedule_preview (s, 0);
2802 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2803 ? s->list_elt_to_hack_number[list_elt]
2805 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2809 ? strdup (hack->name)
2810 : make_hack_name (hack->command))
2814 schedule_preview (s, hack->command);
2816 schedule_preview (s, 0);
2820 pretty_name = strdup (_("Preview"));
2822 gtk_frame_set_label (frame1, pretty_name);
2823 gtk_frame_set_label (frame2, pretty_name);
2825 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2826 gtk_entry_set_position (cmd, 0);
2830 sprintf (title, "%s: %.100s Settings",
2831 progclass, (pretty_name ? pretty_name : "???"));
2832 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2835 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2837 ? (hack->visual && *hack->visual
2842 sensitize_demo_widgets (s, (hack ? True : False));
2844 if (pretty_name) free (pretty_name);
2846 ensure_selected_item_visible (list);
2848 s->_selected_list_element = list_elt;
2853 widget_deleter (GtkWidget *widget, gpointer data)
2855 /* #### Well, I want to destroy these widgets, but if I do that, they get
2856 referenced again, and eventually I get a SEGV. So instead of
2857 destroying them, I'll just hide them, and leak a bunch of memory
2858 every time the disk file changes. Go go go Gtk!
2860 #### Ok, that's a lie, I get a crash even if I just hide the widget
2861 and don't ever delete it. Fuck!
2864 gtk_widget_destroy (widget);
2866 gtk_widget_hide (widget);
2871 static char **sort_hack_cmp_names_kludge;
2873 sort_hack_cmp (const void *a, const void *b)
2879 int aa = *(int *) a;
2880 int bb = *(int *) b;
2881 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
2882 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
2883 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
2889 initialize_sort_map (state *s)
2891 saver_preferences *p = &s->prefs;
2894 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2895 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2896 if (s->hacks_available_p) free (s->hacks_available_p);
2898 s->list_elt_to_hack_number = (int *)
2899 calloc (sizeof(int), p->screenhacks_count + 1);
2900 s->hack_number_to_list_elt = (int *)
2901 calloc (sizeof(int), p->screenhacks_count + 1);
2902 s->hacks_available_p = (Bool *)
2903 calloc (sizeof(Bool), p->screenhacks_count + 1);
2905 /* Check which hacks actually exist on $PATH
2907 for (i = 0; i < p->screenhacks_count; i++)
2909 screenhack *hack = p->screenhacks[i];
2910 s->hacks_available_p[i] = on_path_p (hack->command);
2913 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
2917 for (i = 0; i < p->screenhacks_count; i++)
2919 if (!p->ignore_uninstalled_p ||
2920 s->hacks_available_p[i])
2921 s->list_elt_to_hack_number[j++] = i;
2925 for (; j < p->screenhacks_count; j++)
2926 s->list_elt_to_hack_number[j] = -1;
2929 /* Generate list of sortable names (once)
2931 sort_hack_cmp_names_kludge = (char **)
2932 calloc (sizeof(char *), p->screenhacks_count);
2933 for (i = 0; i < p->screenhacks_count; i++)
2935 screenhack *hack = p->screenhacks[i];
2936 char *name = (hack->name && *hack->name
2937 ? strdup (hack->name)
2938 : make_hack_name (hack->command));
2940 for (str = name; *str; str++)
2941 *str = tolower(*str);
2942 sort_hack_cmp_names_kludge[i] = name;
2945 /* Sort list->hack map alphabetically
2947 qsort (s->list_elt_to_hack_number,
2948 p->screenhacks_count,
2949 sizeof(*s->list_elt_to_hack_number),
2954 for (i = 0; i < p->screenhacks_count; i++)
2955 free (sort_hack_cmp_names_kludge[i]);
2956 free (sort_hack_cmp_names_kludge);
2957 sort_hack_cmp_names_kludge = 0;
2959 /* Build inverse table */
2960 for (i = 0; i < p->screenhacks_count; i++)
2961 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2966 maybe_reload_init_file (state *s)
2968 saver_preferences *p = &s->prefs;
2971 static Bool reentrant_lock = False;
2972 if (reentrant_lock) return 0;
2973 reentrant_lock = True;
2975 if (init_file_changed_p (p))
2977 const char *f = init_file_name();
2982 if (!f || !*f) return 0;
2983 b = (char *) malloc (strlen(f) + 1024);
2986 "file \"%s\" has changed, reloading.\n"),
2988 warning_dialog (s->toplevel_widget, b, False, 100);
2992 initialize_sort_map (s);
2994 list_elt = selected_list_element (s);
2995 list = name_to_widget (s, "list");
2996 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
2997 populate_hack_list (s);
2998 force_list_select_item (s, list, list_elt, True);
2999 populate_prefs_page (s);
3000 populate_demo_window (s, list_elt);
3001 ensure_selected_item_visible (list);
3006 reentrant_lock = False;
3012 /* Making the preview window have the right X visual (so that GL works.)
3015 static Visual *get_best_gl_visual (state *);
3018 x_visual_to_gdk_visual (Visual *xv)
3020 GList *gvs = gdk_list_visuals();
3021 if (!xv) return gdk_visual_get_system();
3022 for (; gvs; gvs = gvs->next)
3024 GdkVisual *gv = (GdkVisual *) gvs->data;
3025 if (xv == GDK_VISUAL_XVISUAL (gv))
3028 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3029 blurb(), (unsigned long) xv->visualid);
3034 clear_preview_window (state *s)
3039 if (!s->toplevel_widget) return; /* very early */
3040 p = name_to_widget (s, "preview");
3043 if (!window) return;
3045 /* Flush the widget background down into the window, in case a subproc
3047 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3048 gdk_window_clear (window);
3051 int list_elt = selected_list_element (s);
3052 int hack_number = (list_elt >= 0
3053 ? s->list_elt_to_hack_number[list_elt]
3055 Bool available_p = (hack_number >= 0
3056 ? s->hacks_available_p [hack_number]
3059 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3060 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3061 (s->running_preview_error_p
3062 ? (available_p ? 1 : 2)
3064 #else /* !HAVE_GTK2 */
3065 if (s->running_preview_error_p)
3067 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3068 const char * const lines2[] = { N_("Not"), N_("Installed") };
3069 int nlines = countof(lines1);
3070 int lh = p->style->font->ascent + p->style->font->descent;
3074 const char * const *lines = (available_p ? lines1 : lines2);
3076 gdk_window_get_size (window, &w, &h);
3077 y = (h - (lh * nlines)) / 2;
3078 y += p->style->font->ascent;
3079 for (i = 0; i < nlines; i++)
3081 int sw = gdk_string_width (p->style->font, _(lines[i]));
3082 int x = (w - sw) / 2;
3083 gdk_draw_string (window, p->style->font,
3084 p->style->fg_gc[GTK_STATE_NORMAL],
3089 #endif /* !HAVE_GTK2 */
3097 fix_preview_visual (state *s)
3099 GtkWidget *widget = name_to_widget (s, "preview");
3100 Visual *xvisual = get_best_gl_visual (s);
3101 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3102 GdkVisual *dvisual = gdk_visual_get_system();
3103 GdkColormap *cmap = (visual == dvisual
3104 ? gdk_colormap_get_system ()
3105 : gdk_colormap_new (visual, False));
3108 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3109 (visual == dvisual ? "default" : "non-default"),
3110 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3112 if (!GTK_WIDGET_REALIZED (widget) ||
3113 gtk_widget_get_visual (widget) != visual)
3115 gtk_widget_unrealize (widget);
3116 gtk_widget_set_visual (widget, visual);
3117 gtk_widget_set_colormap (widget, cmap);
3118 gtk_widget_realize (widget);
3121 /* Set the Widget colors to be white-on-black. */
3123 GdkWindow *window = widget->window;
3124 GtkStyle *style = gtk_style_copy (widget->style);
3125 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3126 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3127 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3128 GdkGC *fgc = gdk_gc_new(window);
3129 GdkGC *bgc = gdk_gc_new(window);
3130 if (!gdk_color_white (cmap, fg)) abort();
3131 if (!gdk_color_black (cmap, bg)) abort();
3132 gdk_gc_set_foreground (fgc, fg);
3133 gdk_gc_set_background (fgc, bg);
3134 gdk_gc_set_foreground (bgc, bg);
3135 gdk_gc_set_background (bgc, fg);
3136 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3137 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3138 gtk_widget_set_style (widget, style);
3140 /* For debugging purposes, put a title on the window (so that
3141 it can be easily found in the output of "xwininfo -tree".)
3143 gdk_window_set_title (window, "Preview");
3146 gtk_widget_show (widget);
3154 subproc_pretty_name (state *s)
3156 if (s->running_preview_cmd)
3158 char *ps = strdup (s->running_preview_cmd);
3159 char *ss = strchr (ps, ' ');
3161 ss = strrchr (ps, '/');
3172 return strdup ("???");
3177 reap_zombies (state *s)
3179 int wait_status = 0;
3181 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3185 if (pid == s->running_preview_pid)
3187 char *ss = subproc_pretty_name (s);
3188 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3189 (unsigned long) pid, ss);
3193 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3194 (unsigned long) pid);
3200 /* Mostly lifted from driver/subprocs.c */
3202 get_best_gl_visual (state *s)
3204 Display *dpy = GDK_DISPLAY();
3213 av[ac++] = "xscreensaver-gl-helper";
3218 perror ("error creating pipe:");
3225 switch ((int) (forked = fork ()))
3229 sprintf (buf, "%s: couldn't fork", blurb());
3237 close (in); /* don't need this one */
3238 close (ConnectionNumber (dpy)); /* close display fd */
3240 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3242 perror ("could not dup() a new stdout:");
3246 execvp (av[0], av); /* shouldn't return. */
3248 if (errno != ENOENT)
3250 /* Ignore "no such file or directory" errors, unless verbose.
3251 Issue all other exec errors, though. */
3252 sprintf (buf, "%s: running %s", blurb(), av[0]);
3255 exit (1); /* exits fork */
3261 int wait_status = 0;
3263 FILE *f = fdopen (in, "r");
3267 close (out); /* don't need this one */
3270 fgets (buf, sizeof(buf)-1, f);
3273 /* Wait for the child to die. */
3274 waitpid (-1, &wait_status, 0);
3276 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3282 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3288 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3290 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3291 blurb(), av[0], result);
3303 kill_preview_subproc (state *s)
3305 s->running_preview_error_p = False;
3308 clear_preview_window (s);
3310 if (s->subproc_check_timer_id)
3312 gtk_timeout_remove (s->subproc_check_timer_id);
3313 s->subproc_check_timer_id = 0;
3314 s->subproc_check_countdown = 0;
3317 if (s->running_preview_pid)
3319 int status = kill (s->running_preview_pid, SIGTERM);
3320 char *ss = subproc_pretty_name (s);
3327 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3328 blurb(), (unsigned long) s->running_preview_pid, ss);
3333 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3334 blurb(), (unsigned long) s->running_preview_pid, ss);
3338 else if (s->debug_p)
3339 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3340 (unsigned long) s->running_preview_pid, ss);
3343 s->running_preview_pid = 0;
3344 if (s->running_preview_cmd) free (s->running_preview_cmd);
3345 s->running_preview_cmd = 0;
3352 /* Immediately and unconditionally launches the given process,
3353 after appending the -window-id option; sets running_preview_pid.
3356 launch_preview_subproc (state *s)
3358 saver_preferences *p = &s->prefs;
3362 const char *cmd = s->desired_preview_cmd;
3364 GtkWidget *pr = name_to_widget (s, "preview");
3365 GdkWindow *window = pr->window;
3367 s->running_preview_error_p = False;
3369 if (s->preview_suppressed_p)
3371 kill_preview_subproc (s);
3375 new_cmd = malloc (strlen (cmd) + 40);
3377 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3380 /* No window id? No command to run. */
3386 strcpy (new_cmd, cmd);
3387 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3391 kill_preview_subproc (s);
3394 s->running_preview_error_p = True;
3395 clear_preview_window (s);
3399 switch ((int) (forked = fork ()))
3404 sprintf (buf, "%s: couldn't fork", blurb());
3406 s->running_preview_error_p = True;
3412 close (ConnectionNumber (GDK_DISPLAY()));
3414 usleep (250000); /* pause for 1/4th second before launching, to give
3415 the previous program time to die and flush its X
3416 buffer, so we don't get leftover turds on the
3419 exec_command (p->shell, new_cmd, p->nice_inferior);
3420 /* Don't bother printing an error message when we are unable to
3421 exec subprocesses; we handle that by polling the pid later. */
3422 exit (1); /* exits child fork */
3427 if (s->running_preview_cmd) free (s->running_preview_cmd);
3428 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3429 s->running_preview_pid = forked;
3433 char *ss = subproc_pretty_name (s);
3434 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3435 (unsigned long) forked, ss);
3442 schedule_preview_check (s);
3445 if (new_cmd) free (new_cmd);
3450 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3453 hack_environment (state *s)
3455 static const char *def_path =
3456 # ifdef DEFAULT_PATH_PREFIX
3457 DEFAULT_PATH_PREFIX;
3462 Display *dpy = GDK_DISPLAY();
3463 const char *odpy = DisplayString (dpy);
3464 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3465 strcpy (ndpy, "DISPLAY=");
3466 strcat (ndpy, odpy);
3471 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3473 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3474 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3475 So we must leak it (and/or the previous setting). Yay.
3478 if (def_path && *def_path)
3480 const char *opath = getenv("PATH");
3481 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3482 strcpy (npath, "PATH=");
3483 strcat (npath, def_path);
3484 strcat (npath, ":");
3485 strcat (npath, opath);
3489 /* do not free(npath) -- see above */
3492 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3497 /* Called from a timer:
3498 Launches the currently-chosen subprocess, if it's not already running.
3499 If there's a different process running, kills it.
3502 update_subproc_timer (gpointer data)
3504 state *s = (state *) data;
3505 if (! s->desired_preview_cmd)
3506 kill_preview_subproc (s);
3507 else if (!s->running_preview_cmd ||
3508 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3509 launch_preview_subproc (s);
3511 s->subproc_timer_id = 0;
3512 return FALSE; /* do not re-execute timer */
3516 /* Call this when you think you might want a preview process running.
3517 It will set a timer that will actually launch that program a second
3518 from now, if you haven't changed your mind (to avoid double-click
3519 spazzing, etc.) `cmd' may be null meaning "no process".
3522 schedule_preview (state *s, const char *cmd)
3524 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3529 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3531 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3534 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3535 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3537 if (s->subproc_timer_id)
3538 gtk_timeout_remove (s->subproc_timer_id);
3539 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3543 /* Called from a timer:
3544 Checks to see if the subproc that should be running, actually is.
3547 check_subproc_timer (gpointer data)
3549 state *s = (state *) data;
3550 Bool again_p = True;
3552 if (s->running_preview_error_p || /* already dead */
3553 s->running_preview_pid <= 0)
3561 status = kill (s->running_preview_pid, 0);
3562 if (status < 0 && errno == ESRCH)
3563 s->running_preview_error_p = True;
3567 char *ss = subproc_pretty_name (s);
3568 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3569 (unsigned long) s->running_preview_pid, ss,
3570 (s->running_preview_error_p ? "dead" : "alive"));
3574 if (s->running_preview_error_p)
3576 clear_preview_window (s);
3581 /* Otherwise, it's currently alive. We might be checking again, or we
3582 might be satisfied. */
3584 if (--s->subproc_check_countdown <= 0)
3588 return TRUE; /* re-execute timer */
3591 s->subproc_check_timer_id = 0;
3592 s->subproc_check_countdown = 0;
3593 return FALSE; /* do not re-execute timer */
3598 /* Call this just after launching a subprocess.
3599 This sets a timer that will, five times a second for two seconds,
3600 check whether the program is still running. The assumption here
3601 is that if the process didn't stay up for more than a couple of
3602 seconds, then either the program doesn't exist, or it doesn't
3603 take a -window-id argument.
3606 schedule_preview_check (state *s)
3612 fprintf (stderr, "%s: scheduling check\n", blurb());
3614 if (s->subproc_check_timer_id)
3615 gtk_timeout_remove (s->subproc_check_timer_id);
3616 s->subproc_check_timer_id =
3617 gtk_timeout_add (1000 / ticks,
3618 check_subproc_timer, (gpointer) s);
3619 s->subproc_check_countdown = ticks * seconds;
3624 screen_blanked_p (void)
3628 unsigned long nitems, bytesafter;
3630 Display *dpy = GDK_DISPLAY();
3631 Bool blanked_p = False;
3633 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3634 XA_SCREENSAVER_STATUS,
3635 0, 3, False, XA_INTEGER,
3636 &type, &format, &nitems, &bytesafter,
3637 (unsigned char **) &data)
3639 && type == XA_INTEGER
3642 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3644 if (data) free (data);
3649 /* Wake up every now and then and see if the screen is blanked.
3650 If it is, kill off the small-window demo -- no point in wasting
3651 cycles by running two screensavers at once...
3654 check_blanked_timer (gpointer data)
3656 state *s = (state *) data;
3657 Bool blanked_p = screen_blanked_p ();
3658 if (blanked_p && s->running_preview_pid)
3661 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3662 kill_preview_subproc (s);
3665 return True; /* re-execute timer */
3669 /* Setting window manager icon
3673 init_icon (GdkWindow *window)
3675 GdkBitmap *mask = 0;
3678 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3679 (gchar **) logo_50_xpm);
3681 gdk_window_set_icon (window, 0, pixmap, mask);
3685 /* The main demo-mode command loop.
3690 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3691 XrmRepresentation *type, XrmValue *value, XPointer closure)
3694 for (i = 0; quarks[i]; i++)
3696 if (bindings[i] == XrmBindTightly)
3697 fprintf (stderr, (i == 0 ? "" : "."));
3698 else if (bindings[i] == XrmBindLoosely)
3699 fprintf (stderr, "*");
3701 fprintf (stderr, " ??? ");
3702 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3705 fprintf (stderr, ": %s\n", (char *) value->addr);
3713 the_network_is_not_the_computer (state *s)
3715 Display *dpy = GDK_DISPLAY();
3716 char *rversion = 0, *ruser = 0, *rhost = 0;
3717 char *luser, *lhost;
3719 struct passwd *p = getpwuid (getuid ());
3720 const char *d = DisplayString (dpy);
3722 # if defined(HAVE_UNAME)
3724 if (uname (&uts) < 0)
3725 lhost = "<UNKNOWN>";
3727 lhost = uts.nodename;
3729 strcpy (lhost, getenv("SYS$NODE"));
3730 # else /* !HAVE_UNAME && !VMS */
3731 strcat (lhost, "<UNKNOWN>");
3732 # endif /* !HAVE_UNAME && !VMS */
3734 if (p && p->pw_name)
3739 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3741 /* Make a buffer that's big enough for a number of copies of all the
3742 strings, plus some. */
3743 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3744 (ruser ? strlen(ruser) : 0) +
3745 (rhost ? strlen(rhost) : 0) +
3752 if (!rversion || !*rversion)
3756 "The XScreenSaver daemon doesn't seem to be running\n"
3757 "on display \"%s\". Launch it now?"),
3760 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3762 /* Warn that the two processes are running as different users.
3766 "%s is running as user \"%s\" on host \"%s\".\n"
3767 "But the xscreensaver managing display \"%s\"\n"
3768 "is running as user \"%s\" on host \"%s\".\n"
3770 "Since they are different users, they won't be reading/writing\n"
3771 "the same ~/.xscreensaver file, so %s isn't\n"
3772 "going to work right.\n"
3774 "You should either re-run %s as \"%s\", or re-run\n"
3775 "xscreensaver as \"%s\".\n"
3777 "Restart the xscreensaver daemon now?\n"),
3778 progname, luser, lhost,
3780 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3782 progname, (ruser ? ruser : "???"),
3785 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3787 /* Warn that the two processes are running on different hosts.
3791 "%s is running as user \"%s\" on host \"%s\".\n"
3792 "But the xscreensaver managing display \"%s\"\n"
3793 "is running as user \"%s\" on host \"%s\".\n"
3795 "If those two machines don't share a file system (that is,\n"
3796 "if they don't see the same ~%s/.xscreensaver file) then\n"
3797 "%s won't work right.\n"
3799 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3800 progname, luser, lhost,
3802 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3807 else if (!!strcmp (rversion, s->short_version))
3809 /* Warn that the version numbers don't match.
3813 "This is %s version %s.\n"
3814 "But the xscreensaver managing display \"%s\"\n"
3815 "is version %s. This could cause problems.\n"
3817 "Restart the xscreensaver daemon now?\n"),
3818 progname, s->short_version,
3825 warning_dialog (s->toplevel_widget, msg, True, 1);
3827 if (rversion) free (rversion);
3828 if (ruser) free (ruser);
3829 if (rhost) free (rhost);
3834 /* We use this error handler so that X errors are preceeded by the name
3835 of the program that generated them.
3838 demo_ehandler (Display *dpy, XErrorEvent *error)
3840 state *s = global_state_kludge; /* I hate C so much... */
3841 fprintf (stderr, "\nX error in %s:\n", blurb());
3842 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3843 kill_preview_subproc (s);
3849 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3850 of the program that generated them; and also that we can ignore one
3851 particular bogus error message that Gdk madly spews.
3854 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3855 const gchar *message, gpointer user_data)
3857 /* Ignore the message "Got event for unknown window: 0x...".
3858 Apparently some events are coming in for the xscreensaver window
3859 (presumably reply events related to the ClientMessage) and Gdk
3860 feels the need to complain about them. So, just suppress any
3861 messages that look like that one.
3863 if (strstr (message, "unknown window"))
3866 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3867 (log_domain ? log_domain : progclass),
3868 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3869 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3870 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3871 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3872 log_level == G_LOG_LEVEL_INFO ? "info" :
3873 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3875 ((!*message || message[strlen(message)-1] != '\n')
3880 static char *defaults[] = {
3881 #include "XScreenSaver_ad.h"
3886 #ifdef HAVE_CRAPPLET
3887 static struct poptOption crapplet_options[] = {
3888 {NULL, '\0', 0, NULL, 0}
3890 #endif /* HAVE_CRAPPLET */
3893 const char *usage = "[--display dpy] [--prefs]"
3894 # ifdef HAVE_CRAPPLET
3897 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
3900 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3902 state *s = (state *) user_data;
3903 Boolean oi = s->initializing_p;
3904 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3905 s->initializing_p = True;
3906 eschew_gtk_lossage (label);
3907 s->initializing_p = oi;
3913 print_widget_tree (GtkWidget *w, int depth)
3916 for (i = 0; i < depth; i++)
3917 fprintf (stderr, " ");
3918 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3920 if (GTK_IS_LIST (w))
3922 for (i = 0; i < depth+1; i++)
3923 fprintf (stderr, " ");
3924 fprintf (stderr, "...list kids...\n");
3926 else if (GTK_IS_CONTAINER (w))
3928 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3931 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3939 delayed_scroll_kludge (gpointer data)
3941 state *s = (state *) data;
3942 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
3943 ensure_selected_item_visible (w);
3945 /* Oh, this is just fucking lovely, too. */
3946 w = GTK_WIDGET (name_to_widget (s, "preview"));
3947 gtk_widget_hide (w);
3948 gtk_widget_show (w);
3950 return FALSE; /* do not re-execute timer */
3956 create_xscreensaver_demo (void)
3960 nb = name_to_widget (global_state_kludge, "preview_notebook");
3961 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
3963 return name_to_widget (global_state_kludge, "xscreensaver_demo");
3967 create_xscreensaver_settings_dialog (void)
3971 box = name_to_widget (global_state_kludge, "dialog_action_area");
3973 w = name_to_widget (global_state_kludge, "adv_button");
3974 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
3976 w = name_to_widget (global_state_kludge, "std_button");
3977 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
3979 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
3982 #endif /* HAVE_GTK2 */
3985 main (int argc, char **argv)
3989 saver_preferences *p;
3993 Widget toplevel_shell;
3994 char *real_progname = argv[0];
3996 Bool crapplet_p = False;
4000 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4001 textdomain (GETTEXT_PACKAGE);
4004 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4005 # else /* !HAVE_GTK2 */
4006 if (!setlocale (LC_ALL, ""))
4007 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4008 # endif /* !HAVE_GTK2 */
4010 #endif /* ENABLE_NLS */
4012 str = strrchr (real_progname, '/');
4013 if (str) real_progname = str+1;
4016 memset (s, 0, sizeof(*s));
4017 s->initializing_p = True;
4020 global_state_kludge = s; /* I hate C so much... */
4022 progname = real_progname;
4024 s->short_version = (char *) malloc (5);
4025 memcpy (s->short_version, screensaver_id + 17, 4);
4026 s->short_version [4] = 0;
4029 /* Register our error message logger for every ``log domain'' known.
4030 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4031 for all of the domains that seem to be in use.
4034 const char * const domains[] = { 0,
4035 "Gtk", "Gdk", "GLib", "GModule",
4036 "GThread", "Gnome", "GnomeUI" };
4037 for (i = 0; i < countof(domains); i++)
4038 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4041 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4044 const char *dir = DEFAULT_ICONDIR;
4045 if (*dir) add_pixmap_directory (dir);
4047 # endif /* !HAVE_GTK2 */
4048 #endif /* DEFAULT_ICONDIR */
4050 /* This is gross, but Gtk understands --display and not -display...
4052 for (i = 1; i < argc; i++)
4053 if (argv[i][0] && argv[i][1] &&
4054 !strncmp(argv[i], "-display", strlen(argv[i])))
4055 argv[i] = "--display";
4058 /* We need to parse this arg really early... Sigh. */
4059 for (i = 1; i < argc; i++)
4062 (!strcmp(argv[i], "--crapplet") ||
4063 !strcmp(argv[i], "--capplet")))
4065 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4068 for (j = i; j < argc; j++) /* remove it from the list */
4069 argv[j] = argv[j+1];
4071 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4072 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4074 fprintf (stderr, "%s: %s\n", real_progname, usage);
4076 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4079 (!strcmp(argv[i], "--debug") ||
4080 !strcmp(argv[i], "-debug") ||
4081 !strcmp(argv[i], "-d")))
4085 for (j = i; j < argc; j++) /* remove it from the list */
4086 argv[j] = argv[j+1];
4093 (!strcmp(argv[i], "--configdir")))
4097 hack_configuration_path = argv[i+1];
4098 for (j = i; j < argc; j++) /* remove them from the list */
4099 argv[j] = argv[j+2];
4103 if (0 != stat (hack_configuration_path, &st))
4106 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4110 else if (!S_ISDIR (st.st_mode))
4112 fprintf (stderr, "%s: not a directory: %s\n",
4113 blurb(), hack_configuration_path);
4121 fprintf (stderr, "%s: using config directory \"%s\"\n",
4122 progname, hack_configuration_path);
4125 /* Let Gtk open the X connection, then initialize Xt to use that
4126 same connection. Doctor Frankenstein would be proud.
4128 # ifdef HAVE_CRAPPLET
4131 GnomeClient *client;
4132 GnomeClientFlags flags = 0;
4134 int init_results = gnome_capplet_init ("screensaver-properties",
4136 argc, argv, NULL, 0, NULL);
4138 0 upon successful initialization;
4139 1 if --init-session-settings was passed on the cmdline;
4140 2 if --ignore was passed on the cmdline;
4143 So the 1 signifies just to init the settings, and quit, basically.
4144 (Meaning launch the xscreensaver daemon.)
4147 if (init_results < 0)
4150 g_error ("An initialization error occurred while "
4151 "starting xscreensaver-capplet.\n");
4153 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4154 real_progname, init_results);
4159 client = gnome_master_client ();
4162 flags = gnome_client_get_flags (client);
4164 if (flags & GNOME_CLIENT_IS_CONNECTED)
4167 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4168 gnome_client_get_id (client));
4171 char *session_args[20];
4173 session_args[i++] = real_progname;
4174 session_args[i++] = "--capplet";
4175 session_args[i++] = "--init-session-settings";
4176 session_args[i] = 0;
4177 gnome_client_set_priority (client, 20);
4178 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4179 gnome_client_set_restart_command (client, i, session_args);
4183 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4186 gnome_client_flush (client);
4189 if (init_results == 1)
4191 system ("xscreensaver -nosplash &");
4197 # endif /* HAVE_CRAPPLET */
4199 gtk_init (&argc, &argv);
4203 /* We must read exactly the same resources as xscreensaver.
4204 That means we must have both the same progclass *and* progname,
4205 at least as far as the resource database is concerned. So,
4206 put "xscreensaver" in argv[0] while initializing Xt.
4208 argv[0] = "xscreensaver";
4212 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4214 XtToolkitInitialize ();
4215 app = XtCreateApplicationContext ();
4216 dpy = GDK_DISPLAY();
4217 XtAppSetFallbackResources (app, defaults);
4218 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4219 toplevel_shell = XtAppCreateShell (progname, progclass,
4220 applicationShellWidgetClass,
4223 dpy = XtDisplay (toplevel_shell);
4224 db = XtDatabase (dpy);
4225 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4226 XSetErrorHandler (demo_ehandler);
4228 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4229 signal (SIGPIPE, SIG_IGN);
4231 /* After doing Xt-style command-line processing, complain about any
4232 unrecognized command-line arguments.
4234 for (i = 1; i < argc; i++)
4236 char *str = argv[i];
4237 if (str[0] == '-' && str[1] == '-')
4239 if (!strcmp (str, "-prefs"))
4241 else if (crapplet_p)
4242 /* There are lots of random args that we don't care about when we're
4243 started as a crapplet, so just ignore unknown args in that case. */
4247 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname, argv[i]);
4248 fprintf (stderr, "%s: %s\n", real_progname, usage);
4253 /* Load the init file, which may end up consulting the X resource database
4254 and the site-wide app-defaults file. Note that at this point, it's
4255 important that `progname' be "xscreensaver", rather than whatever
4260 hack_environment (s); /* must be before initialize_sort_map() */
4263 initialize_sort_map (s);
4265 /* Now that Xt has been initialized, and the resources have been read,
4266 we can set our `progname' variable to something more in line with
4269 progname = real_progname;
4273 /* Print out all the resources we read. */
4275 XrmName name = { 0 };
4276 XrmClass class = { 0 };
4278 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4284 /* Intern the atoms that xscreensaver_command() needs.
4286 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4287 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4288 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4289 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4290 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4291 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4292 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4293 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4294 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4295 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4296 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4297 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4298 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4301 /* Create the window and all its widgets.
4303 s->base_widget = create_xscreensaver_demo ();
4304 s->popup_widget = create_xscreensaver_settings_dialog ();
4305 s->toplevel_widget = s->base_widget;
4308 /* Set the main window's title. */
4310 char *base_title = _("Screensaver Preferences");
4311 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4312 char *s1, *s2, *s3, *s4;
4313 s1 = (char *) strchr(v, ' '); s1++;
4314 s2 = (char *) strchr(s1, ' ');
4315 s3 = (char *) strchr(v, '('); s3++;
4316 s4 = (char *) strchr(s3, ')');
4320 window_title = (char *) malloc (strlen (base_title) +
4321 strlen (progclass) +
4322 strlen (s1) + strlen (s3) +
4324 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4325 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4326 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4330 /* Adjust the (invisible) notebooks on the popup dialog... */
4332 GtkNotebook *notebook =
4333 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4334 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4338 gtk_widget_hide (std);
4339 # else /* !HAVE_XML */
4340 /* Make the advanced page be the only one available. */
4341 gtk_widget_set_sensitive (std, False);
4342 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4343 gtk_widget_hide (std);
4345 # endif /* !HAVE_XML */
4347 gtk_notebook_set_page (notebook, page);
4348 gtk_notebook_set_show_tabs (notebook, False);
4351 /* Various other widget initializations...
4353 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4354 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4356 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4357 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4360 populate_hack_list (s);
4361 populate_prefs_page (s);
4362 sensitize_demo_widgets (s, False);
4363 fix_text_entry_sizes (s);
4364 scroll_to_current_hack (s);
4366 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4367 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4371 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4372 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4374 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4375 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4377 #endif /* !HAVE_GTK2 */
4379 /* Hook up callbacks to the items on the mode menu. */
4381 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4382 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4383 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4384 for (; kids; kids = kids->next)
4385 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4386 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4391 /* Handle the -prefs command-line argument. */
4394 GtkNotebook *notebook =
4395 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4396 gtk_notebook_set_page (notebook, 1);
4399 # ifdef HAVE_CRAPPLET
4403 GtkWidget *outer_vbox;
4405 gtk_widget_hide (s->toplevel_widget);
4407 capplet = capplet_widget_new ();
4409 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4410 # ifdef HAVE_CRAPPLET_IMMEDIATE
4411 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4412 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4413 /* In crapplet-mode, take off the menubar. */
4414 gtk_widget_hide (name_to_widget (s, "menubar"));
4416 /* Reparent our top-level container to be a child of the capplet
4419 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4420 gtk_widget_ref (outer_vbox);
4421 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4423 GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4424 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4426 /* Find the window above us, and set the title and close handler. */
4428 GtkWidget *window = capplet;
4429 while (window && !GTK_IS_WINDOW (window))
4430 window = window->parent;
4433 gtk_window_set_title (GTK_WINDOW (window), window_title);
4434 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4435 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4440 s->toplevel_widget = capplet;
4442 # endif /* HAVE_CRAPPLET */
4445 /* The Gnome folks hate the menubar. I think it's important to have access
4446 to the commands on the File menu (Restart Daemon, etc.) and to the
4447 About and Documentation commands on the Help menu.
4451 gtk_widget_hide (name_to_widget (s, "menubar"));
4455 free (window_title);
4459 gtk_widget_show (s->toplevel_widget);
4460 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4461 fix_preview_visual (s);
4463 /* Realize page zero, so that we can diddle the scrollbar when the
4464 user tabs back to it -- otherwise, the current hack isn't scrolled
4465 to the first time they tab back there, when started with "-prefs".
4466 (Though it is if they then tab away, and back again.)
4468 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4469 #### understands this crap, explain to me how to make this work.
4471 gtk_widget_realize (name_to_widget (s, "demos_table"));
4474 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4477 /* Issue any warnings about the running xscreensaver daemon. */
4478 the_network_is_not_the_computer (s);
4481 /* Run the Gtk event loop, and not the Xt event loop. This means that
4482 if there were Xt timers or fds registered, they would never get serviced,
4483 and if there were any Xt widgets, they would never have events delivered.
4484 Fortunately, we're using Gtk for all of the UI, and only initialized
4485 Xt so that we could process the command line and use the X resource
4488 s->initializing_p = False;
4490 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4491 after we start up. Otherwise, it always appears scrolled to the top
4492 when in crapplet-mode. */
4493 gtk_timeout_add (500, delayed_scroll_kludge, s);
4497 /* Load every configurator in turn, to scan them for errors all at once. */
4500 for (i = 0; i < p->screenhacks_count; i++)
4502 screenhack *hack = p->screenhacks[i];
4503 conf_data *d = load_configurator (hack->command, False);
4504 if (d) free_conf_data (d);
4510 # ifdef HAVE_CRAPPLET
4512 capplet_gtk_main ();
4514 # endif /* HAVE_CRAPPLET */
4517 kill_preview_subproc (s);
4521 #endif /* HAVE_GTK -- whole file */