1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2003 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))
303 path = gtk_tree_path_new_first ();
305 path = gtk_tree_model_get_path (model, &iter);
307 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
309 gtk_tree_path_free (path);
311 #else /* !HAVE_GTK2 */
313 GtkScrolledWindow *scroller = 0;
315 GtkList *list_widget = 0;
319 GtkWidget *selected = 0;
322 gint parent_h, child_y, child_h, children_h, ignore;
323 double ratio_t, ratio_b;
325 if (GTK_IS_SCROLLED_WINDOW (widget))
327 scroller = GTK_SCROLLED_WINDOW (widget);
328 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
329 list_widget = GTK_LIST (GTK_BIN(vp)->child);
331 else if (GTK_IS_VIEWPORT (widget))
333 vp = GTK_VIEWPORT (widget);
334 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
335 list_widget = GTK_LIST (GTK_BIN(vp)->child);
337 else if (GTK_IS_LIST (widget))
339 list_widget = GTK_LIST (widget);
340 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
341 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
346 slist = list_widget->selection;
347 selected = (slist ? GTK_WIDGET (slist->data) : 0);
351 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
353 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
354 kids; kids = kids->next)
357 adj = gtk_scrolled_window_get_vadjustment (scroller);
359 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
360 &ignore, &ignore, &ignore, &parent_h, &ignore);
361 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
362 &ignore, &child_y, &ignore, &child_h, &ignore);
363 children_h = nkids * child_h;
365 ratio_t = ((double) child_y) / ((double) children_h);
366 ratio_b = ((double) child_y + child_h) / ((double) children_h);
368 if (adj->upper == 0.0) /* no items in list */
371 if (ratio_t < (adj->value / adj->upper) ||
372 ratio_b > ((adj->value + adj->page_size) / adj->upper))
375 int slop = parent_h * 0.75; /* how much to overshoot by */
377 if (ratio_t < (adj->value / adj->upper))
379 double ratio_w = ((double) parent_h) / ((double) children_h);
380 double ratio_l = (ratio_b - ratio_t);
381 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
384 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
386 target = ratio_t * adj->upper;
390 if (target > adj->upper - adj->page_size)
391 target = adj->upper - adj->page_size;
395 gtk_adjustment_set_value (adj, target);
397 #endif /* !HAVE_GTK2 */
401 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
403 GtkWidget *shell = GTK_WIDGET (user_data);
404 while (shell->parent)
405 shell = shell->parent;
406 gtk_widget_destroy (GTK_WIDGET (shell));
410 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
412 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
414 restart_menu_cb (widget, user_data);
415 warning_dialog_dismiss_cb (widget, user_data);
419 warning_dialog (GtkWidget *parent, const char *message,
420 Boolean restart_button_p, int center)
422 char *msg = strdup (message);
425 GtkWidget *dialog = gtk_dialog_new ();
426 GtkWidget *label = 0;
428 GtkWidget *cancel = 0;
431 while (parent && !parent->window)
432 parent = parent->parent;
435 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
437 fprintf (stderr, "%s: too early for dialog?\n", progname);
445 char *s = strchr (head, '\n');
448 sprintf (name, "label%d", i++);
451 label = gtk_label_new (head);
453 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
454 #endif /* HAVE_GTK2 */
459 GTK_WIDGET (label)->style =
460 gtk_style_copy (GTK_WIDGET (label)->style);
461 GTK_WIDGET (label)->style->font =
462 gdk_font_load (get_string_resource("warning_dialog.headingFont",
464 gtk_widget_set_style (GTK_WIDGET (label),
465 GTK_WIDGET (label)->style);
467 #endif /* !HAVE_GTK2 */
469 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
470 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
471 label, TRUE, TRUE, 0);
472 gtk_widget_show (label);
483 label = gtk_label_new ("");
484 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
485 label, TRUE, TRUE, 0);
486 gtk_widget_show (label);
488 label = gtk_hbutton_box_new ();
489 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
490 label, TRUE, TRUE, 0);
493 if (restart_button_p)
495 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
496 gtk_container_add (GTK_CONTAINER (label), cancel);
499 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
500 gtk_container_add (GTK_CONTAINER (label), ok);
502 #else /* !HAVE_GTK2 */
504 ok = gtk_button_new_with_label ("OK");
505 gtk_container_add (GTK_CONTAINER (label), ok);
507 if (restart_button_p)
509 cancel = gtk_button_new_with_label ("Cancel");
510 gtk_container_add (GTK_CONTAINER (label), cancel);
513 #endif /* !HAVE_GTK2 */
515 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
516 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
517 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
518 GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
519 gtk_widget_show (ok);
520 gtk_widget_grab_focus (ok);
524 GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
525 gtk_widget_show (cancel);
527 gtk_widget_show (label);
528 gtk_widget_show (dialog);
530 if (restart_button_p)
532 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
533 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
535 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
536 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
541 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
542 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
546 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
547 GTK_WIDGET (parent)->window);
550 gtk_window_present (GTK_WINDOW (dialog));
551 #else /* !HAVE_GTK2 */
552 gdk_window_show (GTK_WIDGET (dialog)->window);
553 gdk_window_raise (GTK_WIDGET (dialog)->window);
554 #endif /* !HAVE_GTK2 */
561 run_cmd (state *s, Atom command, int arg)
566 flush_dialog_changes_and_save (s);
567 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
572 sprintf (buf, "Error:\n\n%s", err);
574 strcpy (buf, "Unknown error!");
575 warning_dialog (s->toplevel_widget, buf, False, 100);
582 run_hack (state *s, int list_elt, Bool report_errors_p)
585 if (list_elt < 0) return;
586 hack_number = s->list_elt_to_hack_number[list_elt];
588 flush_dialog_changes_and_save (s);
589 schedule_preview (s, 0);
591 run_cmd (s, XA_DEMO, hack_number + 1);
595 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
607 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
609 state *s = global_state_kludge; /* I hate C so much... */
610 flush_dialog_changes_and_save (s);
611 kill_preview_subproc (s);
616 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
618 state *s = (state *) data;
619 flush_dialog_changes_and_save (s);
626 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
629 char *vers = strdup (screensaver_id + 4);
632 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
634 s = strchr (vers, ',');
638 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
639 non-ASCII characters aren't allowed in localizable string keys."
640 (I don't want to just use (c) instead of © because that doesn't
641 look as good in the plain-old default Latin1 "C" locale.)
644 sprintf(copy, ("Copyright \xC2\xA9 1991-2003 %s"), s);
645 #else /* !HAVE_GTK2 */
646 sprintf(copy, ("Copyright \251 1991-2003 %s"), s);
647 #endif /* !HAVE_GTK2 */
649 sprintf (msg, "%s\n\n%s", copy, desc);
651 /* I can't make gnome_about_new() work here -- it starts dying in
652 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
653 then this might be the thing to do:
657 const gchar *auth[] = { 0 };
658 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
660 gtk_widget_show (about);
662 #else / * GTK but not GNOME * /
666 GdkColormap *colormap;
667 GdkPixmap *gdkpixmap;
670 GtkWidget *dialog = gtk_dialog_new ();
671 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
672 GtkWidget *parent = GTK_WIDGET (menuitem);
673 while (parent->parent)
674 parent = parent->parent;
676 hbox = gtk_hbox_new (FALSE, 20);
677 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
678 hbox, TRUE, TRUE, 0);
680 colormap = gtk_widget_get_colormap (parent);
682 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
683 (gchar **) logo_180_xpm);
684 icon = gtk_pixmap_new (gdkpixmap, mask);
685 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
687 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
689 vbox = gtk_vbox_new (FALSE, 0);
690 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
692 label1 = gtk_label_new (vers);
693 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
694 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
695 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
698 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
699 GTK_WIDGET (label1)->style->font =
700 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
701 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
702 #endif /* HAVE_GTK2 */
704 label2 = gtk_label_new (msg);
705 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
706 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
707 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
710 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
711 GTK_WIDGET (label2)->style->font =
712 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
713 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
714 #endif /* HAVE_GTK2 */
716 hb = gtk_hbutton_box_new ();
718 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
722 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
723 #else /* !HAVE_GTK2 */
724 ok = gtk_button_new_with_label (_("OK"));
725 #endif /* !HAVE_GTK2 */
726 gtk_container_add (GTK_CONTAINER (hb), ok);
728 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
729 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
730 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
732 gtk_widget_show (hbox);
733 gtk_widget_show (icon);
734 gtk_widget_show (vbox);
735 gtk_widget_show (label1);
736 gtk_widget_show (label2);
737 gtk_widget_show (hb);
738 gtk_widget_show (ok);
739 gtk_widget_show (dialog);
741 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
742 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
744 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
745 GTK_WIDGET (parent)->window);
746 gdk_window_show (GTK_WIDGET (dialog)->window);
747 gdk_window_raise (GTK_WIDGET (dialog)->window);
753 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
755 state *s = global_state_kludge; /* I hate C so much... */
756 saver_preferences *p = &s->prefs;
759 if (!p->help_url || !*p->help_url)
761 warning_dialog (s->toplevel_widget,
763 "No Help URL has been specified.\n"), False, 100);
767 help_command = (char *) malloc (strlen (p->load_url_command) +
768 (strlen (p->help_url) * 2) + 20);
769 strcpy (help_command, "( ");
770 sprintf (help_command + strlen(help_command),
771 p->load_url_command, p->help_url, p->help_url);
772 strcat (help_command, " ) &");
773 system (help_command);
779 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
781 state *s = global_state_kludge; /* I hate C so much... */
782 run_cmd (s, XA_ACTIVATE, 0);
787 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
789 state *s = global_state_kludge; /* I hate C so much... */
790 run_cmd (s, XA_LOCK, 0);
795 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
797 state *s = global_state_kludge; /* I hate C so much... */
798 run_cmd (s, XA_EXIT, 0);
803 restart_menu_cb (GtkWidget *widget, gpointer user_data)
805 state *s = global_state_kludge; /* I hate C so much... */
806 flush_dialog_changes_and_save (s);
807 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
809 system ("xscreensaver -nosplash &");
811 await_xscreensaver (s);
815 await_xscreensaver (state *s)
819 Display *dpy = GDK_DISPLAY();
820 /* GtkWidget *dialog = 0;*/
823 while (!rversion && (--countdown > 0))
825 /* Check for the version of the running xscreensaver... */
826 server_xscreensaver_version (dpy, &rversion, 0, 0);
828 /* If it's not there yet, wait a second... */
833 /* if (dialog) gtk_widget_destroy (dialog);*/
842 /* Timed out, no screensaver running. */
845 Bool root_p = (geteuid () == 0);
849 "The xscreensaver daemon did not start up properly.\n"
854 _("You are running as root. This usually means that xscreensaver\n"
855 "was unable to contact your X server because access control is\n"
856 "turned on. Try running this command:\n"
858 " xhost +localhost\n"
860 "and then selecting `File / Restart Daemon'.\n"
862 "Note that turning off access control will allow anyone logged\n"
863 "on to this machine to access your screen, which might be\n"
864 "considered a security problem. Please read the xscreensaver\n"
865 "manual and FAQ for more information.\n"
867 "You shouldn't run X as root. Instead, you should log in as a\n"
868 "normal user, and `su' as necessary."));
870 strcat (buf, _("Please check your $PATH and permissions."));
872 warning_dialog (s->toplevel_widget, buf, False, 1);
878 selected_list_element (state *s)
880 return s->_selected_list_element;
885 demo_write_init_file (state *s, saver_preferences *p)
889 /* #### try to figure out why shit keeps getting reordered... */
890 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
894 if (!write_init_file (p, s->short_version, False))
897 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
902 const char *f = init_file_name();
904 warning_dialog (s->toplevel_widget,
905 _("Error:\n\nCouldn't determine init file name!\n"),
909 char *b = (char *) malloc (strlen(f) + 1024);
910 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
911 warning_dialog (s->toplevel_widget, b, False, 100);
920 run_this_cb (GtkButton *button, gpointer user_data)
922 state *s = global_state_kludge; /* I hate C so much... */
923 int list_elt = selected_list_element (s);
924 if (list_elt < 0) return;
925 if (!flush_dialog_changes_and_save (s))
926 run_hack (s, list_elt, True);
931 manual_cb (GtkButton *button, gpointer user_data)
933 state *s = global_state_kludge; /* I hate C so much... */
934 saver_preferences *p = &s->prefs;
935 GtkWidget *list_widget = name_to_widget (s, "list");
936 int list_elt = selected_list_element (s);
938 char *name, *name2, *cmd, *str;
939 if (list_elt < 0) return;
940 hack_number = s->list_elt_to_hack_number[list_elt];
942 flush_dialog_changes_and_save (s);
943 ensure_selected_item_visible (list_widget);
945 name = strdup (p->screenhacks[hack_number]->command);
947 while (isspace (*name2)) name2++;
949 while (*str && !isspace (*str)) str++;
951 str = strrchr (name2, '/');
952 if (str) name = str+1;
954 cmd = get_string_resource ("manualCommand", "ManualCommand");
957 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
959 sprintf (cmd2 + strlen (cmd2),
961 name2, name2, name2, name2);
962 strcat (cmd2, " ) &");
968 warning_dialog (GTK_WIDGET (button),
969 _("Error:\n\nno `manualCommand' resource set."),
978 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
980 GtkWidget *parent = name_to_widget (s, "scroller");
981 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
985 GtkTreeSelection *selection;
986 #endif /* HAVE_GTK2 */
988 if (!was) gtk_widget_set_sensitive (parent, True);
990 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
992 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
993 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
994 gtk_tree_selection_select_iter (selection, &iter);
995 #else /* !HAVE_GTK2 */
996 gtk_list_select_item (GTK_LIST (list), list_elt);
997 #endif /* !HAVE_GTK2 */
998 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
999 if (!was) gtk_widget_set_sensitive (parent, False);
1004 run_next_cb (GtkButton *button, gpointer user_data)
1006 state *s = global_state_kludge; /* I hate C so much... */
1007 /* saver_preferences *p = &s->prefs; */
1008 Bool ops = s->preview_suppressed_p;
1010 GtkWidget *list_widget = name_to_widget (s, "list");
1011 int list_elt = selected_list_element (s);
1018 if (list_elt >= s->list_count)
1021 s->preview_suppressed_p = True;
1023 flush_dialog_changes_and_save (s);
1024 force_list_select_item (s, list_widget, list_elt, True);
1025 populate_demo_window (s, list_elt);
1026 run_hack (s, list_elt, False);
1028 s->preview_suppressed_p = ops;
1033 run_prev_cb (GtkButton *button, gpointer user_data)
1035 state *s = global_state_kludge; /* I hate C so much... */
1036 /* saver_preferences *p = &s->prefs; */
1037 Bool ops = s->preview_suppressed_p;
1039 GtkWidget *list_widget = name_to_widget (s, "list");
1040 int list_elt = selected_list_element (s);
1043 list_elt = s->list_count - 1;
1048 list_elt = s->list_count - 1;
1050 s->preview_suppressed_p = True;
1052 flush_dialog_changes_and_save (s);
1053 force_list_select_item (s, list_widget, list_elt, True);
1054 populate_demo_window (s, list_elt);
1055 run_hack (s, list_elt, False);
1057 s->preview_suppressed_p = ops;
1061 /* Writes the given settings into prefs.
1062 Returns true if there was a change, False otherwise.
1063 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1066 flush_changes (state *s,
1069 const char *command,
1072 saver_preferences *p = &s->prefs;
1073 Bool changed = False;
1076 if (list_elt < 0 || list_elt >= s->list_count)
1079 hack_number = s->list_elt_to_hack_number[list_elt];
1080 hack = p->screenhacks[hack_number];
1082 if (enabled_p != -1 &&
1083 enabled_p != hack->enabled_p)
1085 hack->enabled_p = enabled_p;
1088 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1089 blurb(), hack->name, enabled_p);
1094 if (!hack->command || !!strcmp (command, hack->command))
1096 if (hack->command) free (hack->command);
1097 hack->command = strdup (command);
1100 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1101 blurb(), hack->name, command);
1107 const char *ov = hack->visual;
1108 if (!ov || !*ov) ov = "any";
1109 if (!*visual) visual = "any";
1110 if (!!strcasecmp (visual, ov))
1112 if (hack->visual) free (hack->visual);
1113 hack->visual = strdup (visual);
1116 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1117 blurb(), hack->name, visual);
1125 /* Helper for the text fields that contain time specifications:
1126 this parses the text, and does error checking.
1129 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1134 if (!sec_p || strchr (line, ':'))
1135 value = parse_time ((char *) line, sec_p, True);
1139 if (sscanf (line, "%u%c", &value, &c) != 1)
1145 value *= 1000; /* Time measures in microseconds */
1151 "Unparsable time format: \"%s\"\n"),
1153 warning_dialog (s->toplevel_widget, b, False, 100);
1162 directory_p (const char *path)
1165 if (!path || !*path)
1167 else if (stat (path, &st))
1169 else if (!S_ISDIR (st.st_mode))
1176 normalize_directory (const char *path)
1180 if (!path || !*path) return 0;
1182 p2 = (char *) malloc (L + 2);
1184 if (p2[L-1] == '/') /* remove trailing slash */
1187 for (s = p2; s && *s; s++)
1190 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1191 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1194 while (s0 > p2 && s0[-1] != '/')
1204 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1205 strcpy (s, s+2), s--;
1206 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1210 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1211 while (s[0] == '/' && s[1] == '/')
1214 /* and strip trailing whitespace for good measure. */
1216 while (isspace(p2[L-1]))
1229 } FlushForeachClosure;
1232 flush_checkbox (GtkTreeModel *model,
1237 FlushForeachClosure *closure = data;
1240 gtk_tree_model_get (model, iter,
1241 COL_ENABLED, &checked,
1244 if (flush_changes (closure->s, closure->i,
1246 *closure->changed = True;
1250 /* don't remove row */
1254 #endif /* HAVE_GTK2 */
1256 /* Flush out any changes made in the main dialog window (where changes
1257 take place immediately: clicking on a checkbox causes the init file
1258 to be written right away.)
1261 flush_dialog_changes_and_save (state *s)
1263 saver_preferences *p = &s->prefs;
1264 saver_preferences P2, *p2 = &P2;
1266 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1267 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1268 FlushForeachClosure closure;
1269 #else /* !HAVE_GTK2 */
1270 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1271 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1273 #endif /* !HAVE_GTK2 */
1275 Bool changed = False;
1278 if (s->saving_p) return False;
1283 /* Flush any checkbox changes in the list down into the prefs struct.
1287 closure.changed = &changed;
1289 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1291 #else /* !HAVE_GTK2 */
1293 for (i = 0; kids; kids = kids->next, i++)
1295 GtkWidget *line = GTK_WIDGET (kids->data);
1296 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1297 GtkWidget *line_check =
1298 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1300 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1302 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1305 #endif /* ~HAVE_GTK2 */
1307 /* Flush the non-hack-specific settings down into the prefs struct.
1310 # define SECONDS(FIELD,NAME) \
1311 w = name_to_widget (s, (NAME)); \
1312 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1314 # define MINUTES(FIELD,NAME) \
1315 w = name_to_widget (s, (NAME)); \
1316 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1318 # define CHECKBOX(FIELD,NAME) \
1319 w = name_to_widget (s, (NAME)); \
1320 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1322 # define PATHNAME(FIELD,NAME) \
1323 w = name_to_widget (s, (NAME)); \
1324 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1326 MINUTES (&p2->timeout, "timeout_spinbutton");
1327 MINUTES (&p2->cycle, "cycle_spinbutton");
1328 CHECKBOX (p2->lock_p, "lock_button");
1329 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1331 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1332 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1333 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1334 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1336 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1337 CHECKBOX (p2->grab_video_p, "grab_video_button");
1338 CHECKBOX (p2->random_image_p, "grab_image_button");
1339 PATHNAME (p2->image_directory, "image_text");
1341 CHECKBOX (p2->verbose_p, "verbose_button");
1342 CHECKBOX (p2->capture_stderr_p, "capture_button");
1343 CHECKBOX (p2->splash_p, "splash_button");
1345 CHECKBOX (p2->install_cmap_p, "install_button");
1346 CHECKBOX (p2->fade_p, "fade_button");
1347 CHECKBOX (p2->unfade_p, "unfade_button");
1348 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1355 /* Warn if the image directory doesn't exist.
1357 if (p2->image_directory &&
1358 *p2->image_directory &&
1359 !directory_p (p2->image_directory))
1362 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1363 p2->image_directory);
1364 warning_dialog (s->toplevel_widget, b, False, 100);
1368 /* Map the mode menu to `saver_mode' enum values. */
1370 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1371 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1372 GtkWidget *selected = gtk_menu_get_active (menu);
1373 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1374 int menu_elt = g_list_index (kids, (gpointer) selected);
1375 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1376 p2->mode = mode_menu_order[menu_elt];
1379 if (p2->mode == ONE_HACK)
1381 int list_elt = selected_list_element (s);
1382 p2->selected_hack = (list_elt >= 0
1383 ? s->list_elt_to_hack_number[list_elt]
1387 # define COPY(field, name) \
1388 if (p->field != p2->field) { \
1391 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1393 p->field = p2->field
1396 COPY(selected_hack, "selected_hack");
1398 COPY(timeout, "timeout");
1399 COPY(cycle, "cycle");
1400 COPY(lock_p, "lock_p");
1401 COPY(lock_timeout, "lock_timeout");
1403 COPY(dpms_enabled_p, "dpms_enabled_p");
1404 COPY(dpms_standby, "dpms_standby");
1405 COPY(dpms_suspend, "dpms_suspend");
1406 COPY(dpms_off, "dpms_off");
1408 COPY(verbose_p, "verbose_p");
1409 COPY(capture_stderr_p, "capture_stderr_p");
1410 COPY(splash_p, "splash_p");
1412 COPY(install_cmap_p, "install_cmap_p");
1413 COPY(fade_p, "fade_p");
1414 COPY(unfade_p, "unfade_p");
1415 COPY(fade_seconds, "fade_seconds");
1417 COPY(grab_desktop_p, "grab_desktop_p");
1418 COPY(grab_video_p, "grab_video_p");
1419 COPY(random_image_p, "random_image_p");
1423 if (!p->image_directory ||
1424 !p2->image_directory ||
1425 strcmp(p->image_directory, p2->image_directory))
1429 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1430 blurb(), p2->image_directory);
1432 if (p->image_directory && p->image_directory != p2->image_directory)
1433 free (p->image_directory);
1434 p->image_directory = p2->image_directory;
1435 p2->image_directory = 0;
1437 populate_prefs_page (s);
1441 Display *dpy = GDK_DISPLAY();
1442 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1443 sync_server_dpms_settings (dpy, enabled_p,
1444 p->dpms_standby / 1000,
1445 p->dpms_suspend / 1000,
1449 changed = demo_write_init_file (s, p);
1452 s->saving_p = False;
1457 /* Flush out any changes made in the popup dialog box (where changes
1458 take place only when the OK button is clicked.)
1461 flush_popup_changes_and_save (state *s)
1463 Bool changed = False;
1464 saver_preferences *p = &s->prefs;
1465 int list_elt = selected_list_element (s);
1467 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1468 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1470 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1471 const char *command = gtk_entry_get_text (cmd);
1476 if (s->saving_p) return False;
1482 if (maybe_reload_init_file (s) != 0)
1488 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1490 if (!strcasecmp (visual, "")) visual = "";
1491 else if (!strcasecmp (visual, "any")) visual = "";
1492 else if (!strcasecmp (visual, "default")) visual = "Default";
1493 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1494 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1495 else if (!strcasecmp (visual, "best")) visual = "Best";
1496 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1497 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1498 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1499 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1500 else if (!strcasecmp (visual, "color")) visual = "Color";
1501 else if (!strcasecmp (visual, "gl")) visual = "GL";
1502 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1503 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1504 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1505 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1506 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1507 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1508 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1509 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
1510 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1513 gdk_beep (); /* unparsable */
1515 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1518 changed = flush_changes (s, list_elt, -1, command, visual);
1521 changed = demo_write_init_file (s, p);
1523 /* Do this to re-launch the hack if (and only if) the command line
1525 populate_demo_window (s, selected_list_element (s));
1529 s->saving_p = False;
1535 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1537 state *s = global_state_kludge; /* I hate C so much... */
1538 if (! s->initializing_p)
1540 s->initializing_p = True;
1541 flush_dialog_changes_and_save (s);
1542 s->initializing_p = False;
1547 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1549 pref_changed_cb (widget, user_data);
1553 /* Callback on menu items in the "mode" options menu.
1556 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1558 state *s = (state *) user_data;
1559 saver_preferences *p = &s->prefs;
1560 GtkWidget *list = name_to_widget (s, "list");
1563 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1565 saver_mode new_mode;
1566 int old_selected = p->selected_hack;
1570 if (menu_items->data == widget)
1573 menu_items = menu_items->next;
1575 if (!menu_items) abort();
1577 new_mode = mode_menu_order[menu_index];
1579 /* Keep the same list element displayed as before; except if we're
1580 switching *to* "one screensaver" mode from any other mode, scroll
1581 to and select "the one".
1584 if (new_mode == ONE_HACK)
1585 list_elt = (p->selected_hack >= 0
1586 ? s->hack_number_to_list_elt[p->selected_hack]
1590 list_elt = selected_list_element (s);
1593 saver_mode old_mode = p->mode;
1595 populate_demo_window (s, list_elt);
1596 force_list_select_item (s, list, list_elt, True);
1597 p->mode = old_mode; /* put it back, so the init file gets written */
1600 pref_changed_cb (widget, user_data);
1602 if (old_selected != p->selected_hack)
1603 abort(); /* dammit, not again... */
1608 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1609 gint page_num, gpointer user_data)
1611 state *s = global_state_kludge; /* I hate C so much... */
1612 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1614 /* If we're switching to page 0, schedule the current hack to be run.
1615 Otherwise, schedule it to stop. */
1617 populate_demo_window (s, selected_list_element (s));
1619 schedule_preview (s, 0);
1624 list_activated_cb (GtkTreeView *list,
1626 GtkTreeViewColumn *column,
1633 g_return_if_fail (!gdk_pointer_is_grabbed ());
1635 str = gtk_tree_path_to_string (path);
1636 list_elt = strtol (str, NULL, 10);
1640 run_hack (s, list_elt, True);
1644 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1646 state *s = (state *)data;
1647 GtkTreeModel *model;
1653 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1656 path = gtk_tree_model_get_path (model, &iter);
1657 str = gtk_tree_path_to_string (path);
1658 list_elt = strtol (str, NULL, 10);
1660 gtk_tree_path_free (path);
1663 populate_demo_window (s, list_elt);
1664 flush_dialog_changes_and_save (s);
1667 #else /* !HAVE_GTK2 */
1669 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1670 list_select_cb that comes in
1671 *after* we've double-clicked.
1675 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1678 state *s = (state *) data;
1679 if (event->type == GDK_2BUTTON_PRESS)
1681 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1682 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1684 last_doubleclick_time = time ((time_t *) 0);
1687 run_hack (s, list_elt, True);
1695 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1697 state *s = (state *) data;
1698 time_t now = time ((time_t *) 0);
1700 if (now >= last_doubleclick_time + 2)
1702 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1703 populate_demo_window (s, list_elt);
1704 flush_dialog_changes_and_save (s);
1709 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1711 state *s = (state *) data;
1712 populate_demo_window (s, -1);
1713 flush_dialog_changes_and_save (s);
1716 #endif /* !HAVE_GTK2 */
1719 /* Called when the checkboxes that are in the left column of the
1720 scrolling list are clicked. This both populates the right pane
1721 (just as clicking on the label (really, listitem) does) and
1722 also syncs this checkbox with the right pane Enabled checkbox.
1727 GtkCellRendererToggle *toggle,
1729 #else /* !HAVE_GTK2 */
1731 #endif /* !HAVE_GTK2 */
1734 state *s = (state *) data;
1737 GtkScrolledWindow *scroller =
1738 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1739 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1740 GtkTreeModel *model = gtk_tree_view_get_model (list);
1741 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1744 #else /* !HAVE_GTK2 */
1745 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1746 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1748 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1749 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1750 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1751 #endif /* !HAVE_GTK2 */
1758 if (!gtk_tree_model_get_iter (model, &iter, path))
1760 g_warning ("bad path: %s", path_string);
1763 gtk_tree_path_free (path);
1765 gtk_tree_model_get (model, &iter,
1766 COL_ENABLED, &active,
1769 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1770 COL_ENABLED, !active,
1773 list_elt = strtol (path_string, NULL, 10);
1774 #else /* !HAVE_GTK2 */
1775 list_elt = gtk_list_child_position (list, line);
1776 #endif /* !HAVE_GTK2 */
1778 /* remember previous scroll position of the top of the list */
1779 adj = gtk_scrolled_window_get_vadjustment (scroller);
1780 scroll_top = adj->value;
1782 flush_dialog_changes_and_save (s);
1783 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1784 populate_demo_window (s, list_elt);
1786 /* restore the previous scroll position of the top of the list.
1787 this is weak, but I don't really know why it's moving... */
1788 gtk_adjustment_set_value (adj, scroll_top);
1794 GtkFileSelection *widget;
1795 } file_selection_data;
1800 store_image_directory (GtkWidget *button, gpointer user_data)
1802 file_selection_data *fsd = (file_selection_data *) user_data;
1803 state *s = fsd->state;
1804 GtkFileSelection *selector = fsd->widget;
1805 GtkWidget *top = s->toplevel_widget;
1806 saver_preferences *p = &s->prefs;
1807 const char *path = gtk_file_selection_get_filename (selector);
1809 if (p->image_directory && !strcmp(p->image_directory, path))
1810 return; /* no change */
1812 if (!directory_p (path))
1815 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1816 warning_dialog (GTK_WIDGET (top), b, False, 100);
1820 if (p->image_directory) free (p->image_directory);
1821 p->image_directory = normalize_directory (path);
1823 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1824 (p->image_directory ? p->image_directory : ""));
1825 demo_write_init_file (s, p);
1830 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1832 file_selection_data *fsd = (file_selection_data *) user_data;
1833 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1837 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1839 browse_image_dir_cancel (button, user_data);
1840 store_image_directory (button, user_data);
1844 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1846 browse_image_dir_cancel (widget, user_data);
1851 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1853 state *s = global_state_kludge; /* I hate C so much... */
1854 saver_preferences *p = &s->prefs;
1855 static file_selection_data *fsd = 0;
1857 GtkFileSelection *selector = GTK_FILE_SELECTION(
1858 gtk_file_selection_new ("Please select the image directory."));
1861 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1863 fsd->widget = selector;
1866 if (p->image_directory && *p->image_directory)
1867 gtk_file_selection_set_filename (selector, p->image_directory);
1869 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1870 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1872 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1873 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1875 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1876 GTK_SIGNAL_FUNC (browse_image_dir_close),
1879 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1881 gtk_window_set_modal (GTK_WINDOW (selector), True);
1882 gtk_widget_show (GTK_WIDGET (selector));
1887 settings_cb (GtkButton *button, gpointer user_data)
1889 state *s = global_state_kludge; /* I hate C so much... */
1890 int list_elt = selected_list_element (s);
1892 populate_demo_window (s, list_elt); /* reset the widget */
1893 populate_popup_window (s); /* create UI on popup window */
1894 gtk_widget_show (s->popup_widget);
1898 settings_sync_cmd_text (state *s)
1901 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1902 char *cmd_line = get_configurator_command_line (s->cdata);
1903 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1904 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1906 # endif /* HAVE_XML */
1910 settings_adv_cb (GtkButton *button, gpointer user_data)
1912 state *s = global_state_kludge; /* I hate C so much... */
1913 GtkNotebook *notebook =
1914 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1916 settings_sync_cmd_text (s);
1917 gtk_notebook_set_page (notebook, 1);
1921 settings_std_cb (GtkButton *button, gpointer user_data)
1923 state *s = global_state_kludge; /* I hate C so much... */
1924 GtkNotebook *notebook =
1925 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1927 /* Re-create UI to reflect the in-progress command-line settings. */
1928 populate_popup_window (s);
1930 gtk_notebook_set_page (notebook, 0);
1934 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1935 gint page_num, gpointer user_data)
1937 state *s = global_state_kludge; /* I hate C so much... */
1938 GtkWidget *adv = name_to_widget (s, "adv_button");
1939 GtkWidget *std = name_to_widget (s, "std_button");
1943 gtk_widget_show (adv);
1944 gtk_widget_hide (std);
1946 else if (page_num == 1)
1948 gtk_widget_hide (adv);
1949 gtk_widget_show (std);
1958 settings_cancel_cb (GtkButton *button, gpointer user_data)
1960 state *s = global_state_kludge; /* I hate C so much... */
1961 gtk_widget_hide (s->popup_widget);
1965 settings_ok_cb (GtkButton *button, gpointer user_data)
1967 state *s = global_state_kludge; /* I hate C so much... */
1968 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1969 int page = gtk_notebook_get_current_page (notebook);
1972 /* Regenerate the command-line from the widget contents before saving.
1973 But don't do this if we're looking at the command-line page already,
1974 or we will blow away what they typed... */
1975 settings_sync_cmd_text (s);
1977 flush_popup_changes_and_save (s);
1978 gtk_widget_hide (s->popup_widget);
1982 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1984 state *s = (state *) data;
1985 settings_cancel_cb (0, (gpointer) s);
1991 /* Populating the various widgets
1995 /* Returns the number of the last hack run by the server.
1998 server_current_hack (void)
2002 unsigned long nitems, bytesafter;
2004 Display *dpy = GDK_DISPLAY();
2005 int hack_number = -1;
2007 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2008 XA_SCREENSAVER_STATUS,
2009 0, 3, False, XA_INTEGER,
2010 &type, &format, &nitems, &bytesafter,
2011 (unsigned char **) &data)
2013 && type == XA_INTEGER
2016 hack_number = (int) data[2] - 1;
2018 if (data) free (data);
2024 /* Finds the number of the last hack to run, and makes that item be
2025 selected by default.
2028 scroll_to_current_hack (state *s)
2030 saver_preferences *p = &s->prefs;
2033 if (p->mode == ONE_HACK)
2034 hack_number = p->selected_hack;
2036 hack_number = server_current_hack ();
2038 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2040 int list_elt = s->hack_number_to_list_elt[hack_number];
2041 GtkWidget *list = name_to_widget (s, "list");
2042 force_list_select_item (s, list, list_elt, True);
2043 populate_demo_window (s, list_elt);
2049 on_path_p (const char *program)
2053 char *cmd = strdup (program);
2054 char *token = strchr (cmd, ' ');
2058 if (token) *token = 0;
2061 if (strchr (cmd, '/'))
2063 result = (0 == stat (cmd, &st));
2067 path = getenv("PATH");
2068 if (!path || !*path)
2072 path = strdup (path);
2073 token = strtok (path, ":");
2077 char *p2 = (char *) malloc (strlen (token) + L + 3);
2081 result = (0 == stat (p2, &st));
2084 token = strtok (0, ":");
2089 if (path) free (path);
2095 populate_hack_list (state *s)
2098 saver_preferences *p = &s->prefs;
2099 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2100 GtkListStore *model;
2101 GtkTreeSelection *selection;
2102 GtkCellRenderer *ren;
2106 g_object_get (G_OBJECT (list),
2111 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2112 g_object_set (G_OBJECT (list), "model", model, NULL);
2113 g_object_unref (model);
2115 ren = gtk_cell_renderer_toggle_new ();
2116 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2118 "active", COL_ENABLED,
2121 g_signal_connect (ren, "toggled",
2122 G_CALLBACK (list_checkbox_cb),
2125 ren = gtk_cell_renderer_text_new ();
2126 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2127 _("Screen Saver"), ren,
2131 g_signal_connect_after (list, "row_activated",
2132 G_CALLBACK (list_activated_cb),
2135 selection = gtk_tree_view_get_selection (list);
2136 g_signal_connect (selection, "changed",
2137 G_CALLBACK (list_select_changed_cb),
2142 for (i = 0; i < s->list_count; i++)
2144 int hack_number = s->list_elt_to_hack_number[i];
2145 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2147 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2149 if (!hack) continue;
2151 /* If we're to suppress uninstalled hacks, check $PATH now. */
2152 if (p->ignore_uninstalled_p && !available_p)
2155 pretty_name = (hack->name
2156 ? strdup (hack->name)
2157 : make_hack_name (hack->command));
2161 /* Make the text foreground be the color of insensitive widgets
2162 (but don't actually make it be insensitive, since we still
2163 want to be able to click on it.)
2165 GtkStyle *style = GTK_WIDGET (list)->style;
2166 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2167 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2168 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2170 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2171 /* " background=\"#%02X%02X%02X\"" */
2173 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2174 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2180 gtk_list_store_append (model, &iter);
2181 gtk_list_store_set (model, &iter,
2182 COL_ENABLED, hack->enabled_p,
2183 COL_NAME, pretty_name,
2188 #else /* !HAVE_GTK2 */
2190 saver_preferences *p = &s->prefs;
2191 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2193 for (i = 0; i < s->list_count; i++)
2195 int hack_number = s->list_elt_to_hack_number[i];
2196 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2198 /* A GtkList must contain only GtkListItems, but those can contain
2199 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2200 and a Label. We handle single and double click events on the
2201 line itself, for clicking on the text, but the interior checkbox
2202 also handles its own events.
2205 GtkWidget *line_hbox;
2206 GtkWidget *line_check;
2207 GtkWidget *line_label;
2209 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2211 if (!hack) continue;
2213 /* If we're to suppress uninstalled hacks, check $PATH now. */
2214 if (p->ignore_uninstalled_p && !available_p)
2217 pretty_name = (hack->name
2218 ? strdup (hack->name)
2219 : make_hack_name (hack->command));
2221 line = gtk_list_item_new ();
2222 line_hbox = gtk_hbox_new (FALSE, 0);
2223 line_check = gtk_check_button_new ();
2224 line_label = gtk_label_new (pretty_name);
2226 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2227 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2228 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2230 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2232 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2234 gtk_widget_show (line_check);
2235 gtk_widget_show (line_label);
2236 gtk_widget_show (line_hbox);
2237 gtk_widget_show (line);
2241 gtk_container_add (GTK_CONTAINER (list), line);
2242 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2243 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2246 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2247 GTK_SIGNAL_FUNC (list_checkbox_cb),
2250 gtk_widget_show (line);
2254 /* Make the widget be colored like insensitive widgets
2255 (but don't actually make it be insensitive, since we
2256 still want to be able to click on it.)
2258 GtkRcStyle *rc_style;
2261 gtk_widget_realize (GTK_WIDGET (line_label));
2263 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2264 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2266 rc_style = gtk_rc_style_new ();
2267 rc_style->fg[GTK_STATE_NORMAL] = fg;
2268 rc_style->bg[GTK_STATE_NORMAL] = bg;
2269 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2271 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2272 gtk_rc_style_unref (rc_style);
2276 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2277 GTK_SIGNAL_FUNC (list_select_cb),
2279 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2280 GTK_SIGNAL_FUNC (list_unselect_cb),
2282 #endif /* !HAVE_GTK2 */
2286 update_list_sensitivity (state *s)
2288 saver_preferences *p = &s->prefs;
2289 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2290 Bool checkable = (p->mode == RANDOM_HACKS);
2291 Bool blankable = (p->mode != DONT_BLANK);
2294 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2295 GtkWidget *use = name_to_widget (s, "use_col_frame");
2296 #endif /* HAVE_GTK2 */
2297 GtkWidget *scroller = name_to_widget (s, "scroller");
2298 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2299 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2302 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2303 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2304 #else /* !HAVE_GTK2 */
2305 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2306 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2308 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2309 #endif /* !HAVE_GTK2 */
2310 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2311 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2313 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2316 gtk_tree_view_column_set_visible (use, checkable);
2317 #else /* !HAVE_GTK2 */
2319 gtk_widget_show (use); /* the "Use" column header */
2321 gtk_widget_hide (use);
2325 GtkBin *line = GTK_BIN (kids->data);
2326 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2327 GtkWidget *line_check =
2328 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2331 gtk_widget_show (line_check);
2333 gtk_widget_hide (line_check);
2337 #endif /* !HAVE_GTK2 */
2342 populate_prefs_page (state *s)
2344 saver_preferences *p = &s->prefs;
2346 Bool can_lock_p = True;
2348 /* Disable all the "lock" controls if locking support was not provided
2349 at compile-time, or if running on MacOS. */
2350 # if defined(NO_LOCKING) || defined(__APPLE__)
2355 /* The file supports timeouts of less than a minute, but the GUI does
2356 not, so throttle the values to be at least one minute (since "0" is
2357 a bad rounding choice...)
2359 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2362 THROTTLE (passwd_timeout);
2365 # define FMT_MINUTES(NAME,N) \
2366 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2368 # define FMT_SECONDS(NAME,N) \
2369 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2371 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2372 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2373 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2374 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2375 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2376 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2377 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2382 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2383 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2386 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2387 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2388 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2389 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2390 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2391 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2392 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2393 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2394 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2395 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2396 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2398 # undef TOGGLE_ACTIVE
2400 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2401 (p->image_directory ? p->image_directory : ""));
2402 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2404 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2407 /* Map the `saver_mode' enum to mode menu to values. */
2409 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2412 for (i = 0; i < countof(mode_menu_order); i++)
2413 if (mode_menu_order[i] == p->mode)
2415 gtk_option_menu_set_history (opt, i);
2416 update_list_sensitivity (s);
2420 Bool found_any_writable_cells = False;
2421 Bool dpms_supported = False;
2423 Display *dpy = GDK_DISPLAY();
2424 int nscreens = ScreenCount(dpy);
2426 for (i = 0; i < nscreens; i++)
2428 Screen *s = ScreenOfDisplay (dpy, i);
2429 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2431 found_any_writable_cells = True;
2436 #ifdef HAVE_XF86VMODE_GAMMA
2437 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2440 #ifdef HAVE_DPMS_EXTENSION
2442 int op = 0, event = 0, error = 0;
2443 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2444 dpms_supported = True;
2446 #endif /* HAVE_DPMS_EXTENSION */
2449 # define SENSITIZE(NAME,SENSITIVEP) \
2450 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2452 /* Blanking and Locking
2454 SENSITIZE ("lock_button", can_lock_p);
2455 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2456 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2460 SENSITIZE ("dpms_frame", dpms_supported);
2461 SENSITIZE ("dpms_button", dpms_supported);
2462 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2463 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2464 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2465 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2466 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2467 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2468 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2469 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2470 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2474 SENSITIZE ("cmap_frame", found_any_writable_cells);
2475 SENSITIZE ("install_button", found_any_writable_cells);
2476 SENSITIZE ("fade_button", found_any_writable_cells);
2477 SENSITIZE ("unfade_button", found_any_writable_cells);
2479 SENSITIZE ("fade_label", (found_any_writable_cells &&
2480 (p->fade_p || p->unfade_p)));
2481 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2482 (p->fade_p || p->unfade_p)));
2490 populate_popup_window (state *s)
2492 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2493 char *doc_string = 0;
2495 /* #### not in Gtk 1.2
2496 gtk_label_set_selectable (doc);
2502 free_conf_data (s->cdata);
2507 saver_preferences *p = &s->prefs;
2508 int list_elt = selected_list_element (s);
2509 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2510 ? s->list_elt_to_hack_number[list_elt]
2512 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2515 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2516 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2517 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2518 s->cdata = load_configurator (cmd_line, s->debug_p);
2519 if (s->cdata && s->cdata->widget)
2520 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2525 doc_string = (s->cdata
2526 ? s->cdata->description
2528 # else /* !HAVE_XML */
2529 doc_string = _("Descriptions not available: no XML support compiled in.");
2530 # endif /* !HAVE_XML */
2532 gtk_label_set_text (doc, (doc_string
2534 : _("No description available.")));
2539 sensitize_demo_widgets (state *s, Bool sensitive_p)
2541 const char *names1[] = { "demo", "settings" };
2542 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2543 "visual", "visual_combo" };
2545 for (i = 0; i < countof(names1); i++)
2547 GtkWidget *w = name_to_widget (s, names1[i]);
2548 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2550 for (i = 0; i < countof(names2); i++)
2552 GtkWidget *w = name_to_widget (s, names2[i]);
2553 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2558 /* Even though we've given these text fields a maximum number of characters,
2559 their default size is still about 30 characters wide -- so measure out
2560 a string in their font, and resize them to just fit that.
2563 fix_text_entry_sizes (state *s)
2567 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2568 const char * const spinbuttons[] = {
2569 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2570 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2571 "dpms_off_spinbutton",
2572 "-fade_spinbutton" };
2576 for (i = 0; i < countof(spinbuttons); i++)
2578 const char *n = spinbuttons[i];
2580 while (*n == '-') n++, cols--;
2581 w = GTK_WIDGET (name_to_widget (s, n));
2582 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2583 gtk_widget_set_usize (w, width, -2);
2586 /* Now fix the width of the combo box.
2588 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2589 w = GTK_COMBO (w)->entry;
2590 width = gdk_string_width (w->style->font, "PseudoColor___");
2591 gtk_widget_set_usize (w, width, -2);
2593 /* Now fix the width of the file entry text.
2595 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2596 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2597 gtk_widget_set_usize (w, width, -2);
2599 /* Now fix the width of the command line text.
2601 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2602 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2603 gtk_widget_set_usize (w, width, -2);
2607 /* Now fix the height of the list widget:
2608 make it default to being around 10 text-lines high instead of 4.
2610 w = GTK_WIDGET (name_to_widget (s, "list"));
2614 int leading = 3; /* approximate is ok... */
2618 PangoFontMetrics *pain =
2619 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2620 w->style->font_desc,
2621 gtk_get_default_language ());
2622 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2623 pango_font_metrics_get_descent (pain));
2624 #else /* !HAVE_GTK2 */
2625 height = w->style->font->ascent + w->style->font->descent;
2626 #endif /* !HAVE_GTK2 */
2630 height += border * 2;
2631 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2632 gtk_widget_set_usize (w, -2, height);
2639 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2642 static char *up_arrow_xpm[] = {
2665 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2666 the end of the array (Gtk 1.2.5.) */
2667 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2668 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2671 static char *down_arrow_xpm[] = {
2694 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2695 the end of the array (Gtk 1.2.5.) */
2696 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2697 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2701 pixmapify_button (state *s, int down_p)
2705 GtkWidget *pixmapwid;
2709 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2710 style = gtk_widget_get_style (w);
2712 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2713 &style->bg[GTK_STATE_NORMAL],
2715 ? (gchar **) down_arrow_xpm
2716 : (gchar **) up_arrow_xpm));
2717 pixmapwid = gtk_pixmap_new (pixmap, mask);
2718 gtk_widget_show (pixmapwid);
2719 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2720 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2724 map_next_button_cb (GtkWidget *w, gpointer user_data)
2726 state *s = (state *) user_data;
2727 pixmapify_button (s, 1);
2731 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2733 state *s = (state *) user_data;
2734 pixmapify_button (s, 0);
2736 #endif /* !HAVE_GTK2 */
2739 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2743 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2744 GtkAllocation *allocation,
2748 GtkWidgetAuxInfo *aux_info;
2750 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2752 aux_info->width = allocation->width;
2753 aux_info->height = -2;
2757 gtk_widget_size_request (label, &req);
2761 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2764 eschew_gtk_lossage (GtkLabel *label)
2766 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2767 aux_info->width = GTK_WIDGET (label)->allocation.width;
2768 aux_info->height = -2;
2772 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2774 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2775 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2778 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2780 gtk_widget_queue_resize (GTK_WIDGET (label));
2785 populate_demo_window (state *s, int list_elt)
2787 saver_preferences *p = &s->prefs;
2790 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2791 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2792 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2793 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2794 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2796 if (p->mode == BLANK_ONLY)
2799 pretty_name = strdup (_("Blank Screen"));
2800 schedule_preview (s, 0);
2802 else if (p->mode == DONT_BLANK)
2805 pretty_name = strdup (_("Screen Saver Disabled"));
2806 schedule_preview (s, 0);
2810 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2811 ? s->list_elt_to_hack_number[list_elt]
2813 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2817 ? strdup (hack->name)
2818 : make_hack_name (hack->command))
2822 schedule_preview (s, hack->command);
2824 schedule_preview (s, 0);
2828 pretty_name = strdup (_("Preview"));
2830 gtk_frame_set_label (frame1, pretty_name);
2831 gtk_frame_set_label (frame2, pretty_name);
2833 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2834 gtk_entry_set_position (cmd, 0);
2838 sprintf (title, "%s: %.100s Settings",
2839 progclass, (pretty_name ? pretty_name : "???"));
2840 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2843 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2845 ? (hack->visual && *hack->visual
2850 sensitize_demo_widgets (s, (hack ? True : False));
2852 if (pretty_name) free (pretty_name);
2854 ensure_selected_item_visible (list);
2856 s->_selected_list_element = list_elt;
2861 widget_deleter (GtkWidget *widget, gpointer data)
2863 /* #### Well, I want to destroy these widgets, but if I do that, they get
2864 referenced again, and eventually I get a SEGV. So instead of
2865 destroying them, I'll just hide them, and leak a bunch of memory
2866 every time the disk file changes. Go go go Gtk!
2868 #### Ok, that's a lie, I get a crash even if I just hide the widget
2869 and don't ever delete it. Fuck!
2872 gtk_widget_destroy (widget);
2874 gtk_widget_hide (widget);
2879 static char **sort_hack_cmp_names_kludge;
2881 sort_hack_cmp (const void *a, const void *b)
2887 int aa = *(int *) a;
2888 int bb = *(int *) b;
2889 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
2890 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
2891 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
2897 initialize_sort_map (state *s)
2899 saver_preferences *p = &s->prefs;
2902 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2903 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2904 if (s->hacks_available_p) free (s->hacks_available_p);
2906 s->list_elt_to_hack_number = (int *)
2907 calloc (sizeof(int), p->screenhacks_count + 1);
2908 s->hack_number_to_list_elt = (int *)
2909 calloc (sizeof(int), p->screenhacks_count + 1);
2910 s->hacks_available_p = (Bool *)
2911 calloc (sizeof(Bool), p->screenhacks_count + 1);
2913 /* Check which hacks actually exist on $PATH
2915 for (i = 0; i < p->screenhacks_count; i++)
2917 screenhack *hack = p->screenhacks[i];
2918 s->hacks_available_p[i] = on_path_p (hack->command);
2921 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
2925 for (i = 0; i < p->screenhacks_count; i++)
2927 if (!p->ignore_uninstalled_p ||
2928 s->hacks_available_p[i])
2929 s->list_elt_to_hack_number[j++] = i;
2933 for (; j < p->screenhacks_count; j++)
2934 s->list_elt_to_hack_number[j] = -1;
2937 /* Generate list of sortable names (once)
2939 sort_hack_cmp_names_kludge = (char **)
2940 calloc (sizeof(char *), p->screenhacks_count);
2941 for (i = 0; i < p->screenhacks_count; i++)
2943 screenhack *hack = p->screenhacks[i];
2944 char *name = (hack->name && *hack->name
2945 ? strdup (hack->name)
2946 : make_hack_name (hack->command));
2948 for (str = name; *str; str++)
2949 *str = tolower(*str);
2950 sort_hack_cmp_names_kludge[i] = name;
2953 /* Sort list->hack map alphabetically
2955 qsort (s->list_elt_to_hack_number,
2956 p->screenhacks_count,
2957 sizeof(*s->list_elt_to_hack_number),
2962 for (i = 0; i < p->screenhacks_count; i++)
2963 free (sort_hack_cmp_names_kludge[i]);
2964 free (sort_hack_cmp_names_kludge);
2965 sort_hack_cmp_names_kludge = 0;
2967 /* Build inverse table */
2968 for (i = 0; i < p->screenhacks_count; i++)
2969 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2974 maybe_reload_init_file (state *s)
2976 saver_preferences *p = &s->prefs;
2979 static Bool reentrant_lock = False;
2980 if (reentrant_lock) return 0;
2981 reentrant_lock = True;
2983 if (init_file_changed_p (p))
2985 const char *f = init_file_name();
2990 if (!f || !*f) return 0;
2991 b = (char *) malloc (strlen(f) + 1024);
2994 "file \"%s\" has changed, reloading.\n"),
2996 warning_dialog (s->toplevel_widget, b, False, 100);
3000 initialize_sort_map (s);
3002 list_elt = selected_list_element (s);
3003 list = name_to_widget (s, "list");
3004 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3005 populate_hack_list (s);
3006 force_list_select_item (s, list, list_elt, True);
3007 populate_prefs_page (s);
3008 populate_demo_window (s, list_elt);
3009 ensure_selected_item_visible (list);
3014 reentrant_lock = False;
3020 /* Making the preview window have the right X visual (so that GL works.)
3023 static Visual *get_best_gl_visual (state *);
3026 x_visual_to_gdk_visual (Visual *xv)
3028 GList *gvs = gdk_list_visuals();
3029 if (!xv) return gdk_visual_get_system();
3030 for (; gvs; gvs = gvs->next)
3032 GdkVisual *gv = (GdkVisual *) gvs->data;
3033 if (xv == GDK_VISUAL_XVISUAL (gv))
3036 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3037 blurb(), (unsigned long) xv->visualid);
3042 clear_preview_window (state *s)
3047 if (!s->toplevel_widget) return; /* very early */
3048 p = name_to_widget (s, "preview");
3051 if (!window) return;
3053 /* Flush the widget background down into the window, in case a subproc
3055 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3056 gdk_window_clear (window);
3059 int list_elt = selected_list_element (s);
3060 int hack_number = (list_elt >= 0
3061 ? s->list_elt_to_hack_number[list_elt]
3063 Bool available_p = (hack_number >= 0
3064 ? s->hacks_available_p [hack_number]
3067 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3068 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3069 (s->running_preview_error_p
3070 ? (available_p ? 1 : 2)
3072 #else /* !HAVE_GTK2 */
3073 if (s->running_preview_error_p)
3075 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3076 const char * const lines2[] = { N_("Not"), N_("Installed") };
3077 int nlines = countof(lines1);
3078 int lh = p->style->font->ascent + p->style->font->descent;
3082 const char * const *lines = (available_p ? lines1 : lines2);
3084 gdk_window_get_size (window, &w, &h);
3085 y = (h - (lh * nlines)) / 2;
3086 y += p->style->font->ascent;
3087 for (i = 0; i < nlines; i++)
3089 int sw = gdk_string_width (p->style->font, _(lines[i]));
3090 int x = (w - sw) / 2;
3091 gdk_draw_string (window, p->style->font,
3092 p->style->fg_gc[GTK_STATE_NORMAL],
3097 #endif /* !HAVE_GTK2 */
3105 fix_preview_visual (state *s)
3107 GtkWidget *widget = name_to_widget (s, "preview");
3108 Visual *xvisual = get_best_gl_visual (s);
3109 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3110 GdkVisual *dvisual = gdk_visual_get_system();
3111 GdkColormap *cmap = (visual == dvisual
3112 ? gdk_colormap_get_system ()
3113 : gdk_colormap_new (visual, False));
3116 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3117 (visual == dvisual ? "default" : "non-default"),
3118 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3120 if (!GTK_WIDGET_REALIZED (widget) ||
3121 gtk_widget_get_visual (widget) != visual)
3123 gtk_widget_unrealize (widget);
3124 gtk_widget_set_visual (widget, visual);
3125 gtk_widget_set_colormap (widget, cmap);
3126 gtk_widget_realize (widget);
3129 /* Set the Widget colors to be white-on-black. */
3131 GdkWindow *window = widget->window;
3132 GtkStyle *style = gtk_style_copy (widget->style);
3133 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3134 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3135 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3136 GdkGC *fgc = gdk_gc_new(window);
3137 GdkGC *bgc = gdk_gc_new(window);
3138 if (!gdk_color_white (cmap, fg)) abort();
3139 if (!gdk_color_black (cmap, bg)) abort();
3140 gdk_gc_set_foreground (fgc, fg);
3141 gdk_gc_set_background (fgc, bg);
3142 gdk_gc_set_foreground (bgc, bg);
3143 gdk_gc_set_background (bgc, fg);
3144 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3145 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3146 gtk_widget_set_style (widget, style);
3148 /* For debugging purposes, put a title on the window (so that
3149 it can be easily found in the output of "xwininfo -tree".)
3151 gdk_window_set_title (window, "Preview");
3154 gtk_widget_show (widget);
3162 subproc_pretty_name (state *s)
3164 if (s->running_preview_cmd)
3166 char *ps = strdup (s->running_preview_cmd);
3167 char *ss = strchr (ps, ' ');
3169 ss = strrchr (ps, '/');
3180 return strdup ("???");
3185 reap_zombies (state *s)
3187 int wait_status = 0;
3189 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3193 if (pid == s->running_preview_pid)
3195 char *ss = subproc_pretty_name (s);
3196 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3197 (unsigned long) pid, ss);
3201 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3202 (unsigned long) pid);
3208 /* Mostly lifted from driver/subprocs.c */
3210 get_best_gl_visual (state *s)
3212 Display *dpy = GDK_DISPLAY();
3221 av[ac++] = "xscreensaver-gl-helper";
3226 perror ("error creating pipe:");
3233 switch ((int) (forked = fork ()))
3237 sprintf (buf, "%s: couldn't fork", blurb());
3245 close (in); /* don't need this one */
3246 close (ConnectionNumber (dpy)); /* close display fd */
3248 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3250 perror ("could not dup() a new stdout:");
3254 execvp (av[0], av); /* shouldn't return. */
3256 if (errno != ENOENT)
3258 /* Ignore "no such file or directory" errors, unless verbose.
3259 Issue all other exec errors, though. */
3260 sprintf (buf, "%s: running %s", blurb(), av[0]);
3264 /* Note that one must use _exit() instead of exit() in procs forked
3265 off of Gtk programs -- Gtk installs an atexit handler that has a
3266 copy of the X connection (which we've already closed, for safety.)
3267 If one uses exit() instead of _exit(), then one sometimes gets a
3268 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3270 _exit (1); /* exits fork */
3276 int wait_status = 0;
3278 FILE *f = fdopen (in, "r");
3282 close (out); /* don't need this one */
3285 fgets (buf, sizeof(buf)-1, f);
3288 /* Wait for the child to die. */
3289 waitpid (-1, &wait_status, 0);
3291 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3297 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3303 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3305 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3306 blurb(), av[0], result);
3318 kill_preview_subproc (state *s)
3320 s->running_preview_error_p = False;
3323 clear_preview_window (s);
3325 if (s->subproc_check_timer_id)
3327 gtk_timeout_remove (s->subproc_check_timer_id);
3328 s->subproc_check_timer_id = 0;
3329 s->subproc_check_countdown = 0;
3332 if (s->running_preview_pid)
3334 int status = kill (s->running_preview_pid, SIGTERM);
3335 char *ss = subproc_pretty_name (s);
3342 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3343 blurb(), (unsigned long) s->running_preview_pid, ss);
3348 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3349 blurb(), (unsigned long) s->running_preview_pid, ss);
3353 else if (s->debug_p)
3354 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3355 (unsigned long) s->running_preview_pid, ss);
3358 s->running_preview_pid = 0;
3359 if (s->running_preview_cmd) free (s->running_preview_cmd);
3360 s->running_preview_cmd = 0;
3367 /* Immediately and unconditionally launches the given process,
3368 after appending the -window-id option; sets running_preview_pid.
3371 launch_preview_subproc (state *s)
3373 saver_preferences *p = &s->prefs;
3377 const char *cmd = s->desired_preview_cmd;
3379 GtkWidget *pr = name_to_widget (s, "preview");
3380 GdkWindow *window = pr->window;
3382 s->running_preview_error_p = False;
3384 if (s->preview_suppressed_p)
3386 kill_preview_subproc (s);
3390 new_cmd = malloc (strlen (cmd) + 40);
3392 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3395 /* No window id? No command to run. */
3401 strcpy (new_cmd, cmd);
3402 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3406 kill_preview_subproc (s);
3409 s->running_preview_error_p = True;
3410 clear_preview_window (s);
3414 switch ((int) (forked = fork ()))
3419 sprintf (buf, "%s: couldn't fork", blurb());
3421 s->running_preview_error_p = True;
3427 close (ConnectionNumber (GDK_DISPLAY()));
3429 usleep (250000); /* pause for 1/4th second before launching, to give
3430 the previous program time to die and flush its X
3431 buffer, so we don't get leftover turds on the
3434 exec_command (p->shell, new_cmd, p->nice_inferior);
3435 /* Don't bother printing an error message when we are unable to
3436 exec subprocesses; we handle that by polling the pid later.
3438 Note that one must use _exit() instead of exit() in procs forked
3439 off of Gtk programs -- Gtk installs an atexit handler that has a
3440 copy of the X connection (which we've already closed, for safety.)
3441 If one uses exit() instead of _exit(), then one sometimes gets a
3442 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3444 _exit (1); /* exits child fork */
3449 if (s->running_preview_cmd) free (s->running_preview_cmd);
3450 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3451 s->running_preview_pid = forked;
3455 char *ss = subproc_pretty_name (s);
3456 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3457 (unsigned long) forked, ss);
3464 schedule_preview_check (s);
3467 if (new_cmd) free (new_cmd);
3472 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3475 hack_environment (state *s)
3477 static const char *def_path =
3478 # ifdef DEFAULT_PATH_PREFIX
3479 DEFAULT_PATH_PREFIX;
3484 Display *dpy = GDK_DISPLAY();
3485 const char *odpy = DisplayString (dpy);
3486 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3487 strcpy (ndpy, "DISPLAY=");
3488 strcat (ndpy, odpy);
3493 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3495 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3496 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3497 So we must leak it (and/or the previous setting). Yay.
3500 if (def_path && *def_path)
3502 const char *opath = getenv("PATH");
3503 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3504 strcpy (npath, "PATH=");
3505 strcat (npath, def_path);
3506 strcat (npath, ":");
3507 strcat (npath, opath);
3511 /* do not free(npath) -- see above */
3514 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3519 /* Called from a timer:
3520 Launches the currently-chosen subprocess, if it's not already running.
3521 If there's a different process running, kills it.
3524 update_subproc_timer (gpointer data)
3526 state *s = (state *) data;
3527 if (! s->desired_preview_cmd)
3528 kill_preview_subproc (s);
3529 else if (!s->running_preview_cmd ||
3530 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3531 launch_preview_subproc (s);
3533 s->subproc_timer_id = 0;
3534 return FALSE; /* do not re-execute timer */
3538 /* Call this when you think you might want a preview process running.
3539 It will set a timer that will actually launch that program a second
3540 from now, if you haven't changed your mind (to avoid double-click
3541 spazzing, etc.) `cmd' may be null meaning "no process".
3544 schedule_preview (state *s, const char *cmd)
3546 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3551 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3553 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3556 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3557 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3559 if (s->subproc_timer_id)
3560 gtk_timeout_remove (s->subproc_timer_id);
3561 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3565 /* Called from a timer:
3566 Checks to see if the subproc that should be running, actually is.
3569 check_subproc_timer (gpointer data)
3571 state *s = (state *) data;
3572 Bool again_p = True;
3574 if (s->running_preview_error_p || /* already dead */
3575 s->running_preview_pid <= 0)
3583 status = kill (s->running_preview_pid, 0);
3584 if (status < 0 && errno == ESRCH)
3585 s->running_preview_error_p = True;
3589 char *ss = subproc_pretty_name (s);
3590 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3591 (unsigned long) s->running_preview_pid, ss,
3592 (s->running_preview_error_p ? "dead" : "alive"));
3596 if (s->running_preview_error_p)
3598 clear_preview_window (s);
3603 /* Otherwise, it's currently alive. We might be checking again, or we
3604 might be satisfied. */
3606 if (--s->subproc_check_countdown <= 0)
3610 return TRUE; /* re-execute timer */
3613 s->subproc_check_timer_id = 0;
3614 s->subproc_check_countdown = 0;
3615 return FALSE; /* do not re-execute timer */
3620 /* Call this just after launching a subprocess.
3621 This sets a timer that will, five times a second for two seconds,
3622 check whether the program is still running. The assumption here
3623 is that if the process didn't stay up for more than a couple of
3624 seconds, then either the program doesn't exist, or it doesn't
3625 take a -window-id argument.
3628 schedule_preview_check (state *s)
3634 fprintf (stderr, "%s: scheduling check\n", blurb());
3636 if (s->subproc_check_timer_id)
3637 gtk_timeout_remove (s->subproc_check_timer_id);
3638 s->subproc_check_timer_id =
3639 gtk_timeout_add (1000 / ticks,
3640 check_subproc_timer, (gpointer) s);
3641 s->subproc_check_countdown = ticks * seconds;
3646 screen_blanked_p (void)
3650 unsigned long nitems, bytesafter;
3652 Display *dpy = GDK_DISPLAY();
3653 Bool blanked_p = False;
3655 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3656 XA_SCREENSAVER_STATUS,
3657 0, 3, False, XA_INTEGER,
3658 &type, &format, &nitems, &bytesafter,
3659 (unsigned char **) &data)
3661 && type == XA_INTEGER
3664 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3666 if (data) free (data);
3671 /* Wake up every now and then and see if the screen is blanked.
3672 If it is, kill off the small-window demo -- no point in wasting
3673 cycles by running two screensavers at once...
3676 check_blanked_timer (gpointer data)
3678 state *s = (state *) data;
3679 Bool blanked_p = screen_blanked_p ();
3680 if (blanked_p && s->running_preview_pid)
3683 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3684 kill_preview_subproc (s);
3687 return True; /* re-execute timer */
3691 /* Setting window manager icon
3695 init_icon (GdkWindow *window)
3697 GdkBitmap *mask = 0;
3700 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3701 (gchar **) logo_50_xpm);
3703 gdk_window_set_icon (window, 0, pixmap, mask);
3707 /* The main demo-mode command loop.
3712 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3713 XrmRepresentation *type, XrmValue *value, XPointer closure)
3716 for (i = 0; quarks[i]; i++)
3718 if (bindings[i] == XrmBindTightly)
3719 fprintf (stderr, (i == 0 ? "" : "."));
3720 else if (bindings[i] == XrmBindLoosely)
3721 fprintf (stderr, "*");
3723 fprintf (stderr, " ??? ");
3724 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3727 fprintf (stderr, ": %s\n", (char *) value->addr);
3735 the_network_is_not_the_computer (state *s)
3737 Display *dpy = GDK_DISPLAY();
3738 char *rversion = 0, *ruser = 0, *rhost = 0;
3739 char *luser, *lhost;
3741 struct passwd *p = getpwuid (getuid ());
3742 const char *d = DisplayString (dpy);
3744 # if defined(HAVE_UNAME)
3746 if (uname (&uts) < 0)
3747 lhost = "<UNKNOWN>";
3749 lhost = uts.nodename;
3751 strcpy (lhost, getenv("SYS$NODE"));
3752 # else /* !HAVE_UNAME && !VMS */
3753 strcat (lhost, "<UNKNOWN>");
3754 # endif /* !HAVE_UNAME && !VMS */
3756 if (p && p->pw_name)
3761 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3763 /* Make a buffer that's big enough for a number of copies of all the
3764 strings, plus some. */
3765 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3766 (ruser ? strlen(ruser) : 0) +
3767 (rhost ? strlen(rhost) : 0) +
3774 if (!rversion || !*rversion)
3778 "The XScreenSaver daemon doesn't seem to be running\n"
3779 "on display \"%s\". Launch it now?"),
3782 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3784 /* Warn that the two processes are running as different users.
3788 "%s is running as user \"%s\" on host \"%s\".\n"
3789 "But the xscreensaver managing display \"%s\"\n"
3790 "is running as user \"%s\" on host \"%s\".\n"
3792 "Since they are different users, they won't be reading/writing\n"
3793 "the same ~/.xscreensaver file, so %s isn't\n"
3794 "going to work right.\n"
3796 "You should either re-run %s as \"%s\", or re-run\n"
3797 "xscreensaver as \"%s\".\n"
3799 "Restart the xscreensaver daemon now?\n"),
3800 progname, luser, lhost,
3802 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3804 progname, (ruser ? ruser : "???"),
3807 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3809 /* Warn that the two processes are running on different hosts.
3813 "%s is running as user \"%s\" on host \"%s\".\n"
3814 "But the xscreensaver managing display \"%s\"\n"
3815 "is running as user \"%s\" on host \"%s\".\n"
3817 "If those two machines don't share a file system (that is,\n"
3818 "if they don't see the same ~%s/.xscreensaver file) then\n"
3819 "%s won't work right.\n"
3821 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3822 progname, luser, lhost,
3824 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3829 else if (!!strcmp (rversion, s->short_version))
3831 /* Warn that the version numbers don't match.
3835 "This is %s version %s.\n"
3836 "But the xscreensaver managing display \"%s\"\n"
3837 "is version %s. This could cause problems.\n"
3839 "Restart the xscreensaver daemon now?\n"),
3840 progname, s->short_version,
3847 warning_dialog (s->toplevel_widget, msg, True, 1);
3849 if (rversion) free (rversion);
3850 if (ruser) free (ruser);
3851 if (rhost) free (rhost);
3856 /* We use this error handler so that X errors are preceeded by the name
3857 of the program that generated them.
3860 demo_ehandler (Display *dpy, XErrorEvent *error)
3862 state *s = global_state_kludge; /* I hate C so much... */
3863 fprintf (stderr, "\nX error in %s:\n", blurb());
3864 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3865 kill_preview_subproc (s);
3871 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3872 of the program that generated them; and also that we can ignore one
3873 particular bogus error message that Gdk madly spews.
3876 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3877 const gchar *message, gpointer user_data)
3879 /* Ignore the message "Got event for unknown window: 0x...".
3880 Apparently some events are coming in for the xscreensaver window
3881 (presumably reply events related to the ClientMessage) and Gdk
3882 feels the need to complain about them. So, just suppress any
3883 messages that look like that one.
3885 if (strstr (message, "unknown window"))
3888 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3889 (log_domain ? log_domain : progclass),
3890 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3891 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3892 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3893 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3894 log_level == G_LOG_LEVEL_INFO ? "info" :
3895 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3897 ((!*message || message[strlen(message)-1] != '\n')
3902 static char *defaults[] = {
3903 #include "XScreenSaver_ad.h"
3908 #ifdef HAVE_CRAPPLET
3909 static struct poptOption crapplet_options[] = {
3910 {NULL, '\0', 0, NULL, 0}
3912 #endif /* HAVE_CRAPPLET */
3915 const char *usage = "[--display dpy] [--prefs]"
3916 # ifdef HAVE_CRAPPLET
3919 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
3922 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3924 state *s = (state *) user_data;
3925 Boolean oi = s->initializing_p;
3926 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3927 s->initializing_p = True;
3928 eschew_gtk_lossage (label);
3929 s->initializing_p = oi;
3935 print_widget_tree (GtkWidget *w, int depth)
3938 for (i = 0; i < depth; i++)
3939 fprintf (stderr, " ");
3940 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3942 if (GTK_IS_LIST (w))
3944 for (i = 0; i < depth+1; i++)
3945 fprintf (stderr, " ");
3946 fprintf (stderr, "...list kids...\n");
3948 else if (GTK_IS_CONTAINER (w))
3950 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3953 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3961 delayed_scroll_kludge (gpointer data)
3963 state *s = (state *) data;
3964 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
3965 ensure_selected_item_visible (w);
3967 /* Oh, this is just fucking lovely, too. */
3968 w = GTK_WIDGET (name_to_widget (s, "preview"));
3969 gtk_widget_hide (w);
3970 gtk_widget_show (w);
3972 return FALSE; /* do not re-execute timer */
3978 create_xscreensaver_demo (void)
3982 nb = name_to_widget (global_state_kludge, "preview_notebook");
3983 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
3985 return name_to_widget (global_state_kludge, "xscreensaver_demo");
3989 create_xscreensaver_settings_dialog (void)
3993 box = name_to_widget (global_state_kludge, "dialog_action_area");
3995 w = name_to_widget (global_state_kludge, "adv_button");
3996 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
3998 w = name_to_widget (global_state_kludge, "std_button");
3999 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4001 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4004 #endif /* HAVE_GTK2 */
4007 main (int argc, char **argv)
4011 saver_preferences *p;
4015 Widget toplevel_shell;
4016 char *real_progname = argv[0];
4018 Bool crapplet_p = False;
4022 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4023 textdomain (GETTEXT_PACKAGE);
4026 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4027 # else /* !HAVE_GTK2 */
4028 if (!setlocale (LC_ALL, ""))
4029 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4030 # endif /* !HAVE_GTK2 */
4032 #endif /* ENABLE_NLS */
4034 str = strrchr (real_progname, '/');
4035 if (str) real_progname = str+1;
4038 memset (s, 0, sizeof(*s));
4039 s->initializing_p = True;
4042 global_state_kludge = s; /* I hate C so much... */
4044 progname = real_progname;
4046 s->short_version = (char *) malloc (5);
4047 memcpy (s->short_version, screensaver_id + 17, 4);
4048 s->short_version [4] = 0;
4051 /* Register our error message logger for every ``log domain'' known.
4052 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4053 for all of the domains that seem to be in use.
4056 const char * const domains[] = { 0,
4057 "Gtk", "Gdk", "GLib", "GModule",
4058 "GThread", "Gnome", "GnomeUI" };
4059 for (i = 0; i < countof(domains); i++)
4060 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4063 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4066 const char *dir = DEFAULT_ICONDIR;
4067 if (*dir) add_pixmap_directory (dir);
4069 # endif /* !HAVE_GTK2 */
4070 #endif /* DEFAULT_ICONDIR */
4072 /* This is gross, but Gtk understands --display and not -display...
4074 for (i = 1; i < argc; i++)
4075 if (argv[i][0] && argv[i][1] &&
4076 !strncmp(argv[i], "-display", strlen(argv[i])))
4077 argv[i] = "--display";
4080 /* We need to parse this arg really early... Sigh. */
4081 for (i = 1; i < argc; i++)
4084 (!strcmp(argv[i], "--crapplet") ||
4085 !strcmp(argv[i], "--capplet")))
4087 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4090 for (j = i; j < argc; j++) /* remove it from the list */
4091 argv[j] = argv[j+1];
4093 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4094 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4096 fprintf (stderr, "%s: %s\n", real_progname, usage);
4098 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4101 (!strcmp(argv[i], "--debug") ||
4102 !strcmp(argv[i], "-debug") ||
4103 !strcmp(argv[i], "-d")))
4107 for (j = i; j < argc; j++) /* remove it from the list */
4108 argv[j] = argv[j+1];
4115 (!strcmp(argv[i], "--configdir")))
4119 hack_configuration_path = argv[i+1];
4120 for (j = i; j < argc; j++) /* remove them from the list */
4121 argv[j] = argv[j+2];
4125 if (0 != stat (hack_configuration_path, &st))
4128 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4132 else if (!S_ISDIR (st.st_mode))
4134 fprintf (stderr, "%s: not a directory: %s\n",
4135 blurb(), hack_configuration_path);
4143 fprintf (stderr, "%s: using config directory \"%s\"\n",
4144 progname, hack_configuration_path);
4147 /* Let Gtk open the X connection, then initialize Xt to use that
4148 same connection. Doctor Frankenstein would be proud.
4150 # ifdef HAVE_CRAPPLET
4153 GnomeClient *client;
4154 GnomeClientFlags flags = 0;
4156 int init_results = gnome_capplet_init ("screensaver-properties",
4158 argc, argv, NULL, 0, NULL);
4160 0 upon successful initialization;
4161 1 if --init-session-settings was passed on the cmdline;
4162 2 if --ignore was passed on the cmdline;
4165 So the 1 signifies just to init the settings, and quit, basically.
4166 (Meaning launch the xscreensaver daemon.)
4169 if (init_results < 0)
4172 g_error ("An initialization error occurred while "
4173 "starting xscreensaver-capplet.\n");
4175 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4176 real_progname, init_results);
4181 client = gnome_master_client ();
4184 flags = gnome_client_get_flags (client);
4186 if (flags & GNOME_CLIENT_IS_CONNECTED)
4189 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4190 gnome_client_get_id (client));
4193 char *session_args[20];
4195 session_args[i++] = real_progname;
4196 session_args[i++] = "--capplet";
4197 session_args[i++] = "--init-session-settings";
4198 session_args[i] = 0;
4199 gnome_client_set_priority (client, 20);
4200 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4201 gnome_client_set_restart_command (client, i, session_args);
4205 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4208 gnome_client_flush (client);
4211 if (init_results == 1)
4213 system ("xscreensaver -nosplash &");
4219 # endif /* HAVE_CRAPPLET */
4221 gtk_init (&argc, &argv);
4225 /* We must read exactly the same resources as xscreensaver.
4226 That means we must have both the same progclass *and* progname,
4227 at least as far as the resource database is concerned. So,
4228 put "xscreensaver" in argv[0] while initializing Xt.
4230 argv[0] = "xscreensaver";
4234 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4236 XtToolkitInitialize ();
4237 app = XtCreateApplicationContext ();
4238 dpy = GDK_DISPLAY();
4239 XtAppSetFallbackResources (app, defaults);
4240 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4241 toplevel_shell = XtAppCreateShell (progname, progclass,
4242 applicationShellWidgetClass,
4245 dpy = XtDisplay (toplevel_shell);
4246 db = XtDatabase (dpy);
4247 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4248 XSetErrorHandler (demo_ehandler);
4250 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4251 signal (SIGPIPE, SIG_IGN);
4253 /* After doing Xt-style command-line processing, complain about any
4254 unrecognized command-line arguments.
4256 for (i = 1; i < argc; i++)
4258 char *str = argv[i];
4259 if (str[0] == '-' && str[1] == '-')
4261 if (!strcmp (str, "-prefs"))
4263 else if (crapplet_p)
4264 /* There are lots of random args that we don't care about when we're
4265 started as a crapplet, so just ignore unknown args in that case. */
4269 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname, argv[i]);
4270 fprintf (stderr, "%s: %s\n", real_progname, usage);
4275 /* Load the init file, which may end up consulting the X resource database
4276 and the site-wide app-defaults file. Note that at this point, it's
4277 important that `progname' be "xscreensaver", rather than whatever
4282 hack_environment (s); /* must be before initialize_sort_map() */
4285 initialize_sort_map (s);
4287 /* Now that Xt has been initialized, and the resources have been read,
4288 we can set our `progname' variable to something more in line with
4291 progname = real_progname;
4295 /* Print out all the resources we read. */
4297 XrmName name = { 0 };
4298 XrmClass class = { 0 };
4300 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4306 /* Intern the atoms that xscreensaver_command() needs.
4308 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4309 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4310 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4311 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4312 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4313 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4314 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4315 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4316 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4317 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4318 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4319 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4320 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4323 /* Create the window and all its widgets.
4325 s->base_widget = create_xscreensaver_demo ();
4326 s->popup_widget = create_xscreensaver_settings_dialog ();
4327 s->toplevel_widget = s->base_widget;
4330 /* Set the main window's title. */
4332 char *base_title = _("Screensaver Preferences");
4333 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4334 char *s1, *s2, *s3, *s4;
4335 s1 = (char *) strchr(v, ' '); s1++;
4336 s2 = (char *) strchr(s1, ' ');
4337 s3 = (char *) strchr(v, '('); s3++;
4338 s4 = (char *) strchr(s3, ')');
4342 window_title = (char *) malloc (strlen (base_title) +
4343 strlen (progclass) +
4344 strlen (s1) + strlen (s3) +
4346 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4347 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4348 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4352 /* Adjust the (invisible) notebooks on the popup dialog... */
4354 GtkNotebook *notebook =
4355 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4356 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4360 gtk_widget_hide (std);
4361 # else /* !HAVE_XML */
4362 /* Make the advanced page be the only one available. */
4363 gtk_widget_set_sensitive (std, False);
4364 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4365 gtk_widget_hide (std);
4367 # endif /* !HAVE_XML */
4369 gtk_notebook_set_page (notebook, page);
4370 gtk_notebook_set_show_tabs (notebook, False);
4373 /* Various other widget initializations...
4375 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4376 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4378 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4379 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4382 populate_hack_list (s);
4383 populate_prefs_page (s);
4384 sensitize_demo_widgets (s, False);
4385 fix_text_entry_sizes (s);
4386 scroll_to_current_hack (s);
4388 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4389 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4393 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4394 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4396 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4397 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4399 #endif /* !HAVE_GTK2 */
4401 /* Hook up callbacks to the items on the mode menu. */
4403 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4404 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4405 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4406 for (; kids; kids = kids->next)
4407 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4408 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4413 /* Handle the -prefs command-line argument. */
4416 GtkNotebook *notebook =
4417 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4418 gtk_notebook_set_page (notebook, 1);
4421 # ifdef HAVE_CRAPPLET
4425 GtkWidget *outer_vbox;
4427 gtk_widget_hide (s->toplevel_widget);
4429 capplet = capplet_widget_new ();
4431 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4432 # ifdef HAVE_CRAPPLET_IMMEDIATE
4433 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4434 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4435 /* In crapplet-mode, take off the menubar. */
4436 gtk_widget_hide (name_to_widget (s, "menubar"));
4438 /* Reparent our top-level container to be a child of the capplet
4441 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4442 gtk_widget_ref (outer_vbox);
4443 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4445 GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4446 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4448 /* Find the window above us, and set the title and close handler. */
4450 GtkWidget *window = capplet;
4451 while (window && !GTK_IS_WINDOW (window))
4452 window = window->parent;
4455 gtk_window_set_title (GTK_WINDOW (window), window_title);
4456 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4457 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4462 s->toplevel_widget = capplet;
4464 # endif /* HAVE_CRAPPLET */
4467 /* The Gnome folks hate the menubar. I think it's important to have access
4468 to the commands on the File menu (Restart Daemon, etc.) and to the
4469 About and Documentation commands on the Help menu.
4473 gtk_widget_hide (name_to_widget (s, "menubar"));
4477 free (window_title);
4481 gtk_widget_show (s->toplevel_widget);
4482 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4483 fix_preview_visual (s);
4485 /* Realize page zero, so that we can diddle the scrollbar when the
4486 user tabs back to it -- otherwise, the current hack isn't scrolled
4487 to the first time they tab back there, when started with "-prefs".
4488 (Though it is if they then tab away, and back again.)
4490 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4491 #### understands this crap, explain to me how to make this work.
4493 gtk_widget_realize (name_to_widget (s, "demos_table"));
4496 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4499 /* Issue any warnings about the running xscreensaver daemon. */
4500 the_network_is_not_the_computer (s);
4503 /* Run the Gtk event loop, and not the Xt event loop. This means that
4504 if there were Xt timers or fds registered, they would never get serviced,
4505 and if there were any Xt widgets, they would never have events delivered.
4506 Fortunately, we're using Gtk for all of the UI, and only initialized
4507 Xt so that we could process the command line and use the X resource
4510 s->initializing_p = False;
4512 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4513 after we start up. Otherwise, it always appears scrolled to the top
4514 when in crapplet-mode. */
4515 gtk_timeout_add (500, delayed_scroll_kludge, s);
4519 /* Load every configurator in turn, to scan them for errors all at once. */
4522 for (i = 0; i < p->screenhacks_count; i++)
4524 screenhack *hack = p->screenhacks[i];
4525 conf_data *d = load_configurator (hack->command, False);
4526 if (d) free_conf_data (d);
4532 # ifdef HAVE_CRAPPLET
4534 capplet_gtk_main ();
4536 # endif /* HAVE_CRAPPLET */
4539 kill_preview_subproc (s);
4543 #endif /* HAVE_GTK -- whole file */