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))
305 path = gtk_tree_model_get_path (model, &iter);
308 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (widget),
309 path, NULL, FALSE, 0.0, 0.0);
311 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
313 #else /* !HAVE_GTK2 */
315 GtkScrolledWindow *scroller = 0;
317 GtkList *list_widget = 0;
321 GtkWidget *selected = 0;
324 gint parent_h, child_y, child_h, children_h, ignore;
325 double ratio_t, ratio_b;
327 if (GTK_IS_SCROLLED_WINDOW (widget))
329 scroller = GTK_SCROLLED_WINDOW (widget);
330 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
331 list_widget = GTK_LIST (GTK_BIN(vp)->child);
333 else if (GTK_IS_VIEWPORT (widget))
335 vp = GTK_VIEWPORT (widget);
336 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
337 list_widget = GTK_LIST (GTK_BIN(vp)->child);
339 else if (GTK_IS_LIST (widget))
341 list_widget = GTK_LIST (widget);
342 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
343 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
348 slist = list_widget->selection;
349 selected = (slist ? GTK_WIDGET (slist->data) : 0);
353 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
355 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
356 kids; kids = kids->next)
359 adj = gtk_scrolled_window_get_vadjustment (scroller);
361 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
362 &ignore, &ignore, &ignore, &parent_h, &ignore);
363 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
364 &ignore, &child_y, &ignore, &child_h, &ignore);
365 children_h = nkids * child_h;
367 ratio_t = ((double) child_y) / ((double) children_h);
368 ratio_b = ((double) child_y + child_h) / ((double) children_h);
370 if (adj->upper == 0.0) /* no items in list */
373 if (ratio_t < (adj->value / adj->upper) ||
374 ratio_b > ((adj->value + adj->page_size) / adj->upper))
377 int slop = parent_h * 0.75; /* how much to overshoot by */
379 if (ratio_t < (adj->value / adj->upper))
381 double ratio_w = ((double) parent_h) / ((double) children_h);
382 double ratio_l = (ratio_b - ratio_t);
383 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
386 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
388 target = ratio_t * adj->upper;
392 if (target > adj->upper - adj->page_size)
393 target = adj->upper - adj->page_size;
397 gtk_adjustment_set_value (adj, target);
399 #endif /* !HAVE_GTK2 */
403 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
405 GtkWidget *shell = GTK_WIDGET (user_data);
406 while (shell->parent)
407 shell = shell->parent;
408 gtk_widget_destroy (GTK_WIDGET (shell));
412 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
414 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
416 restart_menu_cb (widget, user_data);
417 warning_dialog_dismiss_cb (widget, user_data);
421 warning_dialog (GtkWidget *parent, const char *message,
422 Boolean restart_button_p, int center)
424 char *msg = strdup (message);
427 GtkWidget *dialog = gtk_dialog_new ();
428 GtkWidget *label = 0;
430 GtkWidget *cancel = 0;
433 while (parent && !parent->window)
434 parent = parent->parent;
436 if (!GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
438 fprintf (stderr, "%s: too early for dialog?\n", progname);
446 char *s = strchr (head, '\n');
449 sprintf (name, "label%d", i++);
452 label = gtk_label_new (head);
454 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
455 #endif /* HAVE_GTK2 */
460 GTK_WIDGET (label)->style =
461 gtk_style_copy (GTK_WIDGET (label)->style);
462 GTK_WIDGET (label)->style->font =
463 gdk_font_load (get_string_resource("warning_dialog.headingFont",
465 gtk_widget_set_style (GTK_WIDGET (label),
466 GTK_WIDGET (label)->style);
468 #endif /* !HAVE_GTK2 */
470 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
471 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
472 label, TRUE, TRUE, 0);
473 gtk_widget_show (label);
484 label = gtk_label_new ("");
485 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
486 label, TRUE, TRUE, 0);
487 gtk_widget_show (label);
489 label = gtk_hbutton_box_new ();
490 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
491 label, TRUE, TRUE, 0);
494 if (restart_button_p)
496 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
497 gtk_container_add (GTK_CONTAINER (label), cancel);
500 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
501 gtk_container_add (GTK_CONTAINER (label), ok);
503 #else /* !HAVE_GTK2 */
505 ok = gtk_button_new_with_label ("OK");
506 gtk_container_add (GTK_CONTAINER (label), ok);
508 if (restart_button_p)
510 cancel = gtk_button_new_with_label ("Cancel");
511 gtk_container_add (GTK_CONTAINER (label), cancel);
514 #endif /* !HAVE_GTK2 */
516 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
517 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
518 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
519 GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
520 gtk_widget_show (ok);
521 gtk_widget_grab_focus (ok);
525 GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
526 gtk_widget_show (cancel);
528 gtk_widget_show (label);
529 gtk_widget_show (dialog);
531 if (restart_button_p)
533 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
534 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
536 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
537 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
542 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
543 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
547 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
548 GTK_WIDGET (parent)->window);
551 gtk_window_present (GTK_WINDOW (dialog));
552 #else /* !HAVE_GTK2 */
553 gdk_window_show (GTK_WIDGET (dialog)->window);
554 gdk_window_raise (GTK_WIDGET (dialog)->window);
555 #endif /* !HAVE_GTK2 */
562 run_cmd (state *s, Atom command, int arg)
567 flush_dialog_changes_and_save (s);
568 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
573 sprintf (buf, "Error:\n\n%s", err);
575 strcpy (buf, "Unknown error!");
576 warning_dialog (s->toplevel_widget, buf, False, 100);
583 run_hack (state *s, int list_elt, Bool report_errors_p)
586 if (list_elt < 0) return;
587 hack_number = s->list_elt_to_hack_number[list_elt];
589 flush_dialog_changes_and_save (s);
590 schedule_preview (s, 0);
592 run_cmd (s, XA_DEMO, hack_number + 1);
596 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
608 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
610 state *s = global_state_kludge; /* I hate C so much... */
611 flush_dialog_changes_and_save (s);
612 kill_preview_subproc (s);
617 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
619 state *s = (state *) data;
620 flush_dialog_changes_and_save (s);
627 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
630 char *vers = strdup (screensaver_id + 4);
633 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
635 s = strchr (vers, ',');
639 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
640 non-ASCII characters aren't allowed in localizable string keys."
641 (I don't want to just use (c) instead of © because that doesn't
642 look as good in the plain-old default Latin1 "C" locale.)
645 sprintf(copy, ("Copyright \xC2\xA9 1991-2002 %s"), s);
646 #else /* !HAVE_GTK2 */
647 sprintf(copy, ("Copyright \251 1991-2002 %s"), s);
648 #endif /* !HAVE_GTK2 */
650 sprintf (msg, "%s\n\n%s", copy, desc);
652 /* I can't make gnome_about_new() work here -- it starts dying in
653 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
654 then this might be the thing to do:
658 const gchar *auth[] = { 0 };
659 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
661 gtk_widget_show (about);
663 #else / * GTK but not GNOME * /
667 GdkColormap *colormap;
668 GdkPixmap *gdkpixmap;
671 GtkWidget *dialog = gtk_dialog_new ();
672 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
673 GtkWidget *parent = GTK_WIDGET (menuitem);
674 while (parent->parent)
675 parent = parent->parent;
677 hbox = gtk_hbox_new (FALSE, 20);
678 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
679 hbox, TRUE, TRUE, 0);
681 colormap = gtk_widget_get_colormap (parent);
683 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
684 (gchar **) logo_180_xpm);
685 icon = gtk_pixmap_new (gdkpixmap, mask);
686 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
688 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
690 vbox = gtk_vbox_new (FALSE, 0);
691 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
693 label1 = gtk_label_new (vers);
694 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
695 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
696 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
699 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
700 GTK_WIDGET (label1)->style->font =
701 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
702 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
703 #endif /* HAVE_GTK2 */
705 label2 = gtk_label_new (msg);
706 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
707 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
708 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
711 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
712 GTK_WIDGET (label2)->style->font =
713 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
714 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
715 #endif /* HAVE_GTK2 */
717 hb = gtk_hbutton_box_new ();
719 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
723 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
724 #else /* !HAVE_GTK2 */
725 ok = gtk_button_new_with_label (_("OK"));
726 #endif /* !HAVE_GTK2 */
727 gtk_container_add (GTK_CONTAINER (hb), ok);
729 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
730 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
731 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
733 gtk_widget_show (hbox);
734 gtk_widget_show (icon);
735 gtk_widget_show (vbox);
736 gtk_widget_show (label1);
737 gtk_widget_show (label2);
738 gtk_widget_show (hb);
739 gtk_widget_show (ok);
740 gtk_widget_show (dialog);
742 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
743 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
745 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
746 GTK_WIDGET (parent)->window);
747 gdk_window_show (GTK_WIDGET (dialog)->window);
748 gdk_window_raise (GTK_WIDGET (dialog)->window);
754 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
756 state *s = global_state_kludge; /* I hate C so much... */
757 saver_preferences *p = &s->prefs;
760 if (!p->help_url || !*p->help_url)
762 warning_dialog (s->toplevel_widget,
764 "No Help URL has been specified.\n"), False, 100);
768 help_command = (char *) malloc (strlen (p->load_url_command) +
769 (strlen (p->help_url) * 2) + 20);
770 strcpy (help_command, "( ");
771 sprintf (help_command + strlen(help_command),
772 p->load_url_command, p->help_url, p->help_url);
773 strcat (help_command, " ) &");
774 fprintf(stderr, "## %s\n", help_command);
775 system (help_command);
781 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
783 state *s = global_state_kludge; /* I hate C so much... */
784 run_cmd (s, XA_ACTIVATE, 0);
789 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
791 state *s = global_state_kludge; /* I hate C so much... */
792 run_cmd (s, XA_LOCK, 0);
797 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
799 state *s = global_state_kludge; /* I hate C so much... */
800 run_cmd (s, XA_EXIT, 0);
805 restart_menu_cb (GtkWidget *widget, gpointer user_data)
807 state *s = global_state_kludge; /* I hate C so much... */
808 flush_dialog_changes_and_save (s);
809 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
811 system ("xscreensaver -nosplash &");
813 await_xscreensaver (s);
817 await_xscreensaver (state *s)
821 Display *dpy = GDK_DISPLAY();
822 /* GtkWidget *dialog = 0;*/
825 while (!rversion && (--countdown > 0))
827 /* Check for the version of the running xscreensaver... */
828 server_xscreensaver_version (dpy, &rversion, 0, 0);
830 /* If it's not there yet, wait a second... */
835 /* if (dialog) gtk_widget_destroy (dialog);*/
844 /* Timed out, no screensaver running. */
847 Bool root_p = (geteuid () == 0);
851 "The xscreensaver daemon did not start up properly.\n"
856 _("You are running as root. This usually means that xscreensaver\n"
857 "was unable to contact your X server because access control is\n"
858 "turned on. Try running this command:\n"
860 " xhost +localhost\n"
862 "and then selecting `File / Restart Daemon'.\n"
864 "Note that turning off access control will allow anyone logged\n"
865 "on to this machine to access your screen, which might be\n"
866 "considered a security problem. Please read the xscreensaver\n"
867 "manual and FAQ for more information.\n"
869 "You shouldn't run X as root. Instead, you should log in as a\n"
870 "normal user, and `su' as necessary."));
872 strcat (buf, _("Please check your $PATH and permissions."));
874 warning_dialog (s->toplevel_widget, buf, False, 1);
880 selected_list_element (state *s)
882 return s->_selected_list_element;
887 demo_write_init_file (state *s, saver_preferences *p)
891 /* #### try to figure out why shit keeps getting reordered... */
892 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
896 if (!write_init_file (p, s->short_version, False))
899 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
904 const char *f = init_file_name();
906 warning_dialog (s->toplevel_widget,
907 _("Error:\n\nCouldn't determine init file name!\n"),
911 char *b = (char *) malloc (strlen(f) + 1024);
912 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
913 warning_dialog (s->toplevel_widget, b, False, 100);
922 run_this_cb (GtkButton *button, gpointer user_data)
924 state *s = global_state_kludge; /* I hate C so much... */
925 int list_elt = selected_list_element (s);
926 if (list_elt < 0) return;
927 if (!flush_dialog_changes_and_save (s))
928 run_hack (s, list_elt, True);
933 manual_cb (GtkButton *button, gpointer user_data)
935 state *s = global_state_kludge; /* I hate C so much... */
936 saver_preferences *p = &s->prefs;
937 GtkWidget *list_widget = name_to_widget (s, "list");
938 int list_elt = selected_list_element (s);
940 char *name, *name2, *cmd, *str;
941 if (list_elt < 0) return;
942 hack_number = s->list_elt_to_hack_number[list_elt];
944 flush_dialog_changes_and_save (s);
945 ensure_selected_item_visible (list_widget);
947 name = strdup (p->screenhacks[hack_number]->command);
949 while (isspace (*name2)) name2++;
951 while (*str && !isspace (*str)) str++;
953 str = strrchr (name2, '/');
954 if (str) name = str+1;
956 cmd = get_string_resource ("manualCommand", "ManualCommand");
959 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
961 sprintf (cmd2 + strlen (cmd2),
963 name2, name2, name2, name2);
964 strcat (cmd2, " ) &");
970 warning_dialog (GTK_WIDGET (button),
971 _("Error:\n\nno `manualCommand' resource set."),
980 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
982 GtkWidget *parent = name_to_widget (s, "scroller");
983 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
987 GtkTreeSelection *selection;
988 #endif /* HAVE_GTK2 */
990 if (!was) gtk_widget_set_sensitive (parent, True);
992 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
994 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
995 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
996 gtk_tree_selection_select_iter (selection, &iter);
997 #else /* !HAVE_GTK2 */
998 gtk_list_select_item (GTK_LIST (list), list_elt);
999 #endif /* !HAVE_GTK2 */
1000 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1001 if (!was) gtk_widget_set_sensitive (parent, False);
1006 run_next_cb (GtkButton *button, gpointer user_data)
1008 state *s = global_state_kludge; /* I hate C so much... */
1009 /* saver_preferences *p = &s->prefs; */
1010 Bool ops = s->preview_suppressed_p;
1012 GtkWidget *list_widget = name_to_widget (s, "list");
1013 int list_elt = selected_list_element (s);
1020 if (list_elt >= s->list_count)
1023 s->preview_suppressed_p = True;
1025 flush_dialog_changes_and_save (s);
1026 force_list_select_item (s, list_widget, list_elt, True);
1027 populate_demo_window (s, list_elt);
1028 run_hack (s, list_elt, False);
1030 s->preview_suppressed_p = ops;
1035 run_prev_cb (GtkButton *button, gpointer user_data)
1037 state *s = global_state_kludge; /* I hate C so much... */
1038 /* saver_preferences *p = &s->prefs; */
1039 Bool ops = s->preview_suppressed_p;
1041 GtkWidget *list_widget = name_to_widget (s, "list");
1042 int list_elt = selected_list_element (s);
1045 list_elt = s->list_count - 1;
1050 list_elt = s->list_count - 1;
1052 s->preview_suppressed_p = True;
1054 flush_dialog_changes_and_save (s);
1055 force_list_select_item (s, list_widget, list_elt, True);
1056 populate_demo_window (s, list_elt);
1057 run_hack (s, list_elt, False);
1059 s->preview_suppressed_p = ops;
1063 /* Writes the given settings into prefs.
1064 Returns true if there was a change, False otherwise.
1065 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1068 flush_changes (state *s,
1071 const char *command,
1074 saver_preferences *p = &s->prefs;
1075 Bool changed = False;
1078 if (list_elt < 0 || list_elt >= s->list_count)
1081 hack_number = s->list_elt_to_hack_number[list_elt];
1082 hack = p->screenhacks[hack_number];
1084 if (enabled_p != -1 &&
1085 enabled_p != hack->enabled_p)
1087 hack->enabled_p = enabled_p;
1090 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1091 blurb(), hack->name, enabled_p);
1096 if (!hack->command || !!strcmp (command, hack->command))
1098 if (hack->command) free (hack->command);
1099 hack->command = strdup (command);
1102 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1103 blurb(), hack->name, command);
1109 const char *ov = hack->visual;
1110 if (!ov || !*ov) ov = "any";
1111 if (!*visual) visual = "any";
1112 if (!!strcasecmp (visual, ov))
1114 if (hack->visual) free (hack->visual);
1115 hack->visual = strdup (visual);
1118 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1119 blurb(), hack->name, visual);
1127 /* Helper for the text fields that contain time specifications:
1128 this parses the text, and does error checking.
1131 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1136 if (!sec_p || strchr (line, ':'))
1137 value = parse_time ((char *) line, sec_p, True);
1141 if (sscanf (line, "%u%c", &value, &c) != 1)
1147 value *= 1000; /* Time measures in microseconds */
1153 "Unparsable time format: \"%s\"\n"),
1155 warning_dialog (s->toplevel_widget, b, False, 100);
1164 directory_p (const char *path)
1167 if (!path || !*path)
1169 else if (stat (path, &st))
1171 else if (!S_ISDIR (st.st_mode))
1178 normalize_directory (const char *path)
1182 if (!path || !*path) return 0;
1184 p2 = (char *) malloc (L + 2);
1186 if (p2[L-1] == '/') /* remove trailing slash */
1189 for (s = p2; s && *s; s++)
1192 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1193 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1196 while (s0 > p2 && s0[-1] != '/')
1206 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1207 strcpy (s, s+2), s--;
1208 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1212 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1213 while (s[0] == '/' && s[1] == '/')
1216 /* and strip trailing whitespace for good measure. */
1218 while (isspace(p2[L-1]))
1231 } FlushForeachClosure;
1234 flush_checkbox (GtkTreeModel *model,
1239 FlushForeachClosure *closure = data;
1242 gtk_tree_model_get (model, iter,
1243 COL_ENABLED, &checked,
1246 if (flush_changes (closure->s, closure->i,
1248 *closure->changed = True;
1252 /* don't remove row */
1256 #endif /* HAVE_GTK2 */
1258 /* Flush out any changes made in the main dialog window (where changes
1259 take place immediately: clicking on a checkbox causes the init file
1260 to be written right away.)
1263 flush_dialog_changes_and_save (state *s)
1265 saver_preferences *p = &s->prefs;
1266 saver_preferences P2, *p2 = &P2;
1268 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1269 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1270 FlushForeachClosure closure;
1271 #else /* !HAVE_GTK2 */
1272 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1273 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1275 #endif /* !HAVE_GTK2 */
1277 Bool changed = False;
1280 if (s->saving_p) return False;
1285 /* Flush any checkbox changes in the list down into the prefs struct.
1289 closure.changed = &changed;
1291 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1293 #else /* !HAVE_GTK2 */
1295 for (i = 0; kids; kids = kids->next, i++)
1297 GtkWidget *line = GTK_WIDGET (kids->data);
1298 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1299 GtkWidget *line_check =
1300 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1302 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1304 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1307 #endif /* ~HAVE_GTK2 */
1309 /* Flush the non-hack-specific settings down into the prefs struct.
1312 # define SECONDS(FIELD,NAME) \
1313 w = name_to_widget (s, (NAME)); \
1314 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1316 # define MINUTES(FIELD,NAME) \
1317 w = name_to_widget (s, (NAME)); \
1318 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1320 # define CHECKBOX(FIELD,NAME) \
1321 w = name_to_widget (s, (NAME)); \
1322 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1324 # define PATHNAME(FIELD,NAME) \
1325 w = name_to_widget (s, (NAME)); \
1326 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1328 MINUTES (&p2->timeout, "timeout_spinbutton");
1329 MINUTES (&p2->cycle, "cycle_spinbutton");
1330 CHECKBOX (p2->lock_p, "lock_button");
1331 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1333 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1334 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1335 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1336 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1338 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1339 CHECKBOX (p2->grab_video_p, "grab_video_button");
1340 CHECKBOX (p2->random_image_p, "grab_image_button");
1341 PATHNAME (p2->image_directory, "image_text");
1343 CHECKBOX (p2->verbose_p, "verbose_button");
1344 CHECKBOX (p2->capture_stderr_p, "capture_button");
1345 CHECKBOX (p2->splash_p, "splash_button");
1347 CHECKBOX (p2->install_cmap_p, "install_button");
1348 CHECKBOX (p2->fade_p, "fade_button");
1349 CHECKBOX (p2->unfade_p, "unfade_button");
1350 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1357 /* Warn if the image directory doesn't exist.
1359 if (p2->image_directory &&
1360 *p2->image_directory &&
1361 !directory_p (p2->image_directory))
1364 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1365 p2->image_directory);
1366 warning_dialog (s->toplevel_widget, b, False, 100);
1370 /* Map the mode menu to `saver_mode' enum values. */
1372 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1373 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1374 GtkWidget *selected = gtk_menu_get_active (menu);
1375 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1376 int menu_elt = g_list_index (kids, (gpointer) selected);
1377 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1378 p2->mode = mode_menu_order[menu_elt];
1381 if (p2->mode == ONE_HACK)
1383 int list_elt = selected_list_element (s);
1384 p2->selected_hack = (list_elt >= 0
1385 ? s->list_elt_to_hack_number[list_elt]
1389 # define COPY(field, name) \
1390 if (p->field != p2->field) { \
1393 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1395 p->field = p2->field
1398 COPY(selected_hack, "selected_hack");
1400 COPY(timeout, "timeout");
1401 COPY(cycle, "cycle");
1402 COPY(lock_p, "lock_p");
1403 COPY(lock_timeout, "lock_timeout");
1405 COPY(dpms_enabled_p, "dpms_enabled_p");
1406 COPY(dpms_standby, "dpms_standby");
1407 COPY(dpms_suspend, "dpms_suspend");
1408 COPY(dpms_off, "dpms_off");
1410 COPY(verbose_p, "verbose_p");
1411 COPY(capture_stderr_p, "capture_stderr_p");
1412 COPY(splash_p, "splash_p");
1414 COPY(install_cmap_p, "install_cmap_p");
1415 COPY(fade_p, "fade_p");
1416 COPY(unfade_p, "unfade_p");
1417 COPY(fade_seconds, "fade_seconds");
1419 COPY(grab_desktop_p, "grab_desktop_p");
1420 COPY(grab_video_p, "grab_video_p");
1421 COPY(random_image_p, "random_image_p");
1425 if (!p->image_directory ||
1426 !p2->image_directory ||
1427 strcmp(p->image_directory, p2->image_directory))
1431 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1432 blurb(), p2->image_directory);
1434 if (p->image_directory && p->image_directory != p2->image_directory)
1435 free (p->image_directory);
1436 p->image_directory = p2->image_directory;
1437 p2->image_directory = 0;
1439 populate_prefs_page (s);
1443 Display *dpy = GDK_DISPLAY();
1444 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1445 sync_server_dpms_settings (dpy, enabled_p,
1446 p->dpms_standby / 1000,
1447 p->dpms_suspend / 1000,
1451 changed = demo_write_init_file (s, p);
1454 s->saving_p = False;
1459 /* Flush out any changes made in the popup dialog box (where changes
1460 take place only when the OK button is clicked.)
1463 flush_popup_changes_and_save (state *s)
1465 Bool changed = False;
1466 saver_preferences *p = &s->prefs;
1467 int list_elt = selected_list_element (s);
1469 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1470 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1472 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1473 const char *command = gtk_entry_get_text (cmd);
1478 if (s->saving_p) return False;
1484 if (maybe_reload_init_file (s) != 0)
1490 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1492 if (!strcasecmp (visual, "")) visual = "";
1493 else if (!strcasecmp (visual, "any")) visual = "";
1494 else if (!strcasecmp (visual, "default")) visual = "Default";
1495 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1496 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1497 else if (!strcasecmp (visual, "best")) visual = "Best";
1498 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1499 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1500 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1501 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1502 else if (!strcasecmp (visual, "color")) visual = "Color";
1503 else if (!strcasecmp (visual, "gl")) visual = "GL";
1504 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1505 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1506 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1507 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1508 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1509 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1510 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1511 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
1512 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1515 gdk_beep (); /* unparsable */
1517 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1520 changed = flush_changes (s, list_elt, -1, command, visual);
1523 changed = demo_write_init_file (s, p);
1525 /* Do this to re-launch the hack if (and only if) the command line
1527 populate_demo_window (s, selected_list_element (s));
1531 s->saving_p = False;
1537 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1539 state *s = global_state_kludge; /* I hate C so much... */
1540 if (! s->initializing_p)
1542 s->initializing_p = True;
1543 flush_dialog_changes_and_save (s);
1544 s->initializing_p = False;
1549 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1551 pref_changed_cb (widget, user_data);
1555 /* Callback on menu items in the "mode" options menu.
1558 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1560 state *s = (state *) user_data;
1561 saver_preferences *p = &s->prefs;
1562 GtkWidget *list = name_to_widget (s, "list");
1565 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1567 saver_mode new_mode;
1568 int old_selected = p->selected_hack;
1572 if (menu_items->data == widget)
1575 menu_items = menu_items->next;
1577 if (!menu_items) abort();
1579 new_mode = mode_menu_order[menu_index];
1581 /* Keep the same list element displayed as before; except if we're
1582 switching *to* "one screensaver" mode from any other mode, scroll
1583 to and select "the one".
1586 if (new_mode == ONE_HACK)
1587 list_elt = (p->selected_hack >= 0
1588 ? s->hack_number_to_list_elt[p->selected_hack]
1592 list_elt = selected_list_element (s);
1595 saver_mode old_mode = p->mode;
1597 populate_demo_window (s, list_elt);
1598 force_list_select_item (s, list, list_elt, True);
1599 p->mode = old_mode; /* put it back, so the init file gets written */
1602 pref_changed_cb (widget, user_data);
1604 if (old_selected != p->selected_hack)
1605 abort(); /* dammit, not again... */
1610 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1611 gint page_num, gpointer user_data)
1613 state *s = global_state_kludge; /* I hate C so much... */
1614 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1616 /* If we're switching to page 0, schedule the current hack to be run.
1617 Otherwise, schedule it to stop. */
1619 populate_demo_window (s, selected_list_element (s));
1621 schedule_preview (s, 0);
1626 list_activated_cb (GtkTreeView *list,
1628 GtkTreeViewColumn *column,
1635 g_return_if_fail (!gdk_pointer_is_grabbed ());
1637 str = gtk_tree_path_to_string (path);
1638 list_elt = strtol (str, NULL, 10);
1642 run_hack (s, list_elt, True);
1646 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1648 state *s = (state *)data;
1649 GtkTreeModel *model;
1655 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1658 path = gtk_tree_model_get_path (model, &iter);
1659 str = gtk_tree_path_to_string (path);
1660 list_elt = strtol (str, NULL, 10);
1662 gtk_tree_path_free (path);
1665 populate_demo_window (s, list_elt);
1666 flush_dialog_changes_and_save (s);
1669 #else /* !HAVE_GTK2 */
1671 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1672 list_select_cb that comes in
1673 *after* we've double-clicked.
1677 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1680 state *s = (state *) data;
1681 if (event->type == GDK_2BUTTON_PRESS)
1683 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1684 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1686 last_doubleclick_time = time ((time_t *) 0);
1689 run_hack (s, list_elt, True);
1697 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1699 state *s = (state *) data;
1700 time_t now = time ((time_t *) 0);
1702 if (now >= last_doubleclick_time + 2)
1704 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1705 populate_demo_window (s, list_elt);
1706 flush_dialog_changes_and_save (s);
1711 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1713 state *s = (state *) data;
1714 populate_demo_window (s, -1);
1715 flush_dialog_changes_and_save (s);
1718 #endif /* !HAVE_GTK2 */
1721 /* Called when the checkboxes that are in the left column of the
1722 scrolling list are clicked. This both populates the right pane
1723 (just as clicking on the label (really, listitem) does) and
1724 also syncs this checkbox with the right pane Enabled checkbox.
1729 GtkCellRendererToggle *toggle,
1731 #else /* !HAVE_GTK2 */
1733 #endif /* !HAVE_GTK2 */
1736 state *s = (state *) data;
1739 GtkScrolledWindow *scroller =
1740 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1741 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1742 GtkTreeModel *model = gtk_tree_view_get_model (list);
1743 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1746 #else /* !HAVE_GTK2 */
1747 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1748 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1750 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1751 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1752 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1753 #endif /* !HAVE_GTK2 */
1760 if (!gtk_tree_model_get_iter (model, &iter, path))
1762 g_warning ("bad path: %s", path_string);
1765 gtk_tree_path_free (path);
1767 gtk_tree_model_get (model, &iter,
1768 COL_ENABLED, &active,
1771 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1772 COL_ENABLED, !active,
1775 list_elt = strtol (path_string, NULL, 10);
1776 #else /* !HAVE_GTK2 */
1777 list_elt = gtk_list_child_position (list, line);
1778 #endif /* !HAVE_GTK2 */
1780 /* remember previous scroll position of the top of the list */
1781 adj = gtk_scrolled_window_get_vadjustment (scroller);
1782 scroll_top = adj->value;
1784 flush_dialog_changes_and_save (s);
1785 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1786 populate_demo_window (s, list_elt);
1788 /* restore the previous scroll position of the top of the list.
1789 this is weak, but I don't really know why it's moving... */
1790 gtk_adjustment_set_value (adj, scroll_top);
1796 GtkFileSelection *widget;
1797 } file_selection_data;
1802 store_image_directory (GtkWidget *button, gpointer user_data)
1804 file_selection_data *fsd = (file_selection_data *) user_data;
1805 state *s = fsd->state;
1806 GtkFileSelection *selector = fsd->widget;
1807 GtkWidget *top = s->toplevel_widget;
1808 saver_preferences *p = &s->prefs;
1809 const char *path = gtk_file_selection_get_filename (selector);
1811 if (p->image_directory && !strcmp(p->image_directory, path))
1812 return; /* no change */
1814 if (!directory_p (path))
1817 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1818 warning_dialog (GTK_WIDGET (top), b, False, 100);
1822 if (p->image_directory) free (p->image_directory);
1823 p->image_directory = normalize_directory (path);
1825 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1826 (p->image_directory ? p->image_directory : ""));
1827 demo_write_init_file (s, p);
1832 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1834 file_selection_data *fsd = (file_selection_data *) user_data;
1835 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1839 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1841 browse_image_dir_cancel (button, user_data);
1842 store_image_directory (button, user_data);
1846 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1848 browse_image_dir_cancel (widget, user_data);
1853 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1855 state *s = global_state_kludge; /* I hate C so much... */
1856 saver_preferences *p = &s->prefs;
1857 static file_selection_data *fsd = 0;
1859 GtkFileSelection *selector = GTK_FILE_SELECTION(
1860 gtk_file_selection_new ("Please select the image directory."));
1863 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1865 fsd->widget = selector;
1868 if (p->image_directory && *p->image_directory)
1869 gtk_file_selection_set_filename (selector, p->image_directory);
1871 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1872 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1874 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1875 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1877 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1878 GTK_SIGNAL_FUNC (browse_image_dir_close),
1881 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1883 gtk_window_set_modal (GTK_WINDOW (selector), True);
1884 gtk_widget_show (GTK_WIDGET (selector));
1889 settings_cb (GtkButton *button, gpointer user_data)
1891 state *s = global_state_kludge; /* I hate C so much... */
1892 int list_elt = selected_list_element (s);
1894 populate_demo_window (s, list_elt); /* reset the widget */
1895 populate_popup_window (s); /* create UI on popup window */
1896 gtk_widget_show (s->popup_widget);
1900 settings_sync_cmd_text (state *s)
1903 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1904 char *cmd_line = get_configurator_command_line (s->cdata);
1905 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1906 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1908 # endif /* HAVE_XML */
1912 settings_adv_cb (GtkButton *button, gpointer user_data)
1914 state *s = global_state_kludge; /* I hate C so much... */
1915 GtkNotebook *notebook =
1916 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1918 settings_sync_cmd_text (s);
1919 gtk_notebook_set_page (notebook, 1);
1923 settings_std_cb (GtkButton *button, gpointer user_data)
1925 state *s = global_state_kludge; /* I hate C so much... */
1926 GtkNotebook *notebook =
1927 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1929 /* Re-create UI to reflect the in-progress command-line settings. */
1930 populate_popup_window (s);
1932 gtk_notebook_set_page (notebook, 0);
1936 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1937 gint page_num, gpointer user_data)
1939 state *s = global_state_kludge; /* I hate C so much... */
1940 GtkWidget *adv = name_to_widget (s, "adv_button");
1941 GtkWidget *std = name_to_widget (s, "std_button");
1945 gtk_widget_show (adv);
1946 gtk_widget_hide (std);
1948 else if (page_num == 1)
1950 gtk_widget_hide (adv);
1951 gtk_widget_show (std);
1960 settings_cancel_cb (GtkButton *button, gpointer user_data)
1962 state *s = global_state_kludge; /* I hate C so much... */
1963 gtk_widget_hide (s->popup_widget);
1967 settings_ok_cb (GtkButton *button, gpointer user_data)
1969 state *s = global_state_kludge; /* I hate C so much... */
1970 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1971 int page = gtk_notebook_get_current_page (notebook);
1974 /* Regenerate the command-line from the widget contents before saving.
1975 But don't do this if we're looking at the command-line page already,
1976 or we will blow away what they typed... */
1977 settings_sync_cmd_text (s);
1979 flush_popup_changes_and_save (s);
1980 gtk_widget_hide (s->popup_widget);
1984 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1986 state *s = (state *) data;
1987 settings_cancel_cb (0, (gpointer) s);
1993 /* Populating the various widgets
1997 /* Returns the number of the last hack run by the server.
2000 server_current_hack (void)
2004 unsigned long nitems, bytesafter;
2006 Display *dpy = GDK_DISPLAY();
2007 int hack_number = -1;
2009 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2010 XA_SCREENSAVER_STATUS,
2011 0, 3, False, XA_INTEGER,
2012 &type, &format, &nitems, &bytesafter,
2013 (unsigned char **) &data)
2015 && type == XA_INTEGER
2018 hack_number = (int) data[2] - 1;
2020 if (data) free (data);
2026 /* Finds the number of the last hack to run, and makes that item be
2027 selected by default.
2030 scroll_to_current_hack (state *s)
2032 saver_preferences *p = &s->prefs;
2035 if (p->mode == ONE_HACK)
2036 hack_number = p->selected_hack;
2038 hack_number = server_current_hack ();
2040 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2042 int list_elt = s->hack_number_to_list_elt[hack_number];
2043 GtkWidget *list = name_to_widget (s, "list");
2044 force_list_select_item (s, list, list_elt, True);
2045 populate_demo_window (s, list_elt);
2051 on_path_p (const char *program)
2055 char *cmd = strdup (program);
2056 char *token = strchr (cmd, ' ');
2060 if (token) *token = 0;
2063 if (strchr (cmd, '/'))
2065 result = (0 == stat (cmd, &st));
2069 path = getenv("PATH");
2070 if (!path || !*path)
2074 path = strdup (path);
2075 token = strtok (path, ":");
2079 char *p2 = (char *) malloc (strlen (token) + L + 3);
2083 result = (0 == stat (p2, &st));
2086 token = strtok (0, ":");
2091 if (path) free (path);
2097 populate_hack_list (state *s)
2100 saver_preferences *p = &s->prefs;
2101 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2102 GtkListStore *model;
2103 GtkTreeSelection *selection;
2104 GtkCellRenderer *ren;
2108 g_object_get (G_OBJECT (list),
2113 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2114 g_object_set (G_OBJECT (list), "model", model, NULL);
2115 g_object_unref (model);
2117 ren = gtk_cell_renderer_toggle_new ();
2118 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2120 "active", COL_ENABLED,
2123 g_signal_connect (ren, "toggled",
2124 G_CALLBACK (list_checkbox_cb),
2127 ren = gtk_cell_renderer_text_new ();
2128 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2129 _("Screen Saver"), ren,
2133 g_signal_connect_after (list, "row_activated",
2134 G_CALLBACK (list_activated_cb),
2137 selection = gtk_tree_view_get_selection (list);
2138 g_signal_connect (selection, "changed",
2139 G_CALLBACK (list_select_changed_cb),
2144 for (i = 0; i < s->list_count; i++)
2146 int hack_number = s->list_elt_to_hack_number[i];
2147 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2149 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2151 if (!hack) continue;
2153 /* If we're to suppress uninstalled hacks, check $PATH now. */
2154 if (p->ignore_uninstalled_p && !available_p)
2157 pretty_name = (hack->name
2158 ? strdup (hack->name)
2159 : make_hack_name (hack->command));
2163 /* Make the text foreground be the color of insensitive widgets
2164 (but don't actually make it be insensitive, since we still
2165 want to be able to click on it.)
2167 GtkStyle *style = GTK_WIDGET (list)->style;
2168 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2169 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2170 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2172 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2173 /* " background=\"#%02X%02X%02X\"" */
2175 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2176 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2182 gtk_list_store_append (model, &iter);
2183 gtk_list_store_set (model, &iter,
2184 COL_ENABLED, hack->enabled_p,
2185 COL_NAME, pretty_name,
2190 #else /* !HAVE_GTK2 */
2192 saver_preferences *p = &s->prefs;
2193 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2195 for (i = 0; i < s->list_count; i++)
2197 int hack_number = s->list_elt_to_hack_number[i];
2198 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2200 /* A GtkList must contain only GtkListItems, but those can contain
2201 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2202 and a Label. We handle single and double click events on the
2203 line itself, for clicking on the text, but the interior checkbox
2204 also handles its own events.
2207 GtkWidget *line_hbox;
2208 GtkWidget *line_check;
2209 GtkWidget *line_label;
2211 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2213 if (!hack) continue;
2215 /* If we're to suppress uninstalled hacks, check $PATH now. */
2216 if (p->ignore_uninstalled_p && !available_p)
2219 pretty_name = (hack->name
2220 ? strdup (hack->name)
2221 : make_hack_name (hack->command));
2223 line = gtk_list_item_new ();
2224 line_hbox = gtk_hbox_new (FALSE, 0);
2225 line_check = gtk_check_button_new ();
2226 line_label = gtk_label_new (pretty_name);
2228 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2229 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2230 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2232 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2234 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2236 gtk_widget_show (line_check);
2237 gtk_widget_show (line_label);
2238 gtk_widget_show (line_hbox);
2239 gtk_widget_show (line);
2243 gtk_container_add (GTK_CONTAINER (list), line);
2244 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2245 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2248 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2249 GTK_SIGNAL_FUNC (list_checkbox_cb),
2252 gtk_widget_show (line);
2256 /* Make the widget be colored like insensitive widgets
2257 (but don't actually make it be insensitive, since we
2258 still want to be able to click on it.)
2260 GtkRcStyle *rc_style;
2263 gtk_widget_realize (GTK_WIDGET (line_label));
2265 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2266 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2268 rc_style = gtk_rc_style_new ();
2269 rc_style->fg[GTK_STATE_NORMAL] = fg;
2270 rc_style->bg[GTK_STATE_NORMAL] = bg;
2271 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2273 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2274 gtk_rc_style_unref (rc_style);
2278 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2279 GTK_SIGNAL_FUNC (list_select_cb),
2281 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2282 GTK_SIGNAL_FUNC (list_unselect_cb),
2284 #endif /* !HAVE_GTK2 */
2288 update_list_sensitivity (state *s)
2290 saver_preferences *p = &s->prefs;
2291 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2292 Bool checkable = (p->mode == RANDOM_HACKS);
2293 Bool blankable = (p->mode != DONT_BLANK);
2296 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2297 GtkWidget *use = name_to_widget (s, "use_col_frame");
2298 #endif /* HAVE_GTK2 */
2299 GtkWidget *scroller = name_to_widget (s, "scroller");
2300 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2301 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2304 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2305 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2306 #else /* !HAVE_GTK2 */
2307 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2308 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2310 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2311 #endif /* !HAVE_GTK2 */
2312 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2313 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2315 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2318 gtk_tree_view_column_set_visible (use, checkable);
2319 #else /* !HAVE_GTK2 */
2321 gtk_widget_show (use); /* the "Use" column header */
2323 gtk_widget_hide (use);
2327 GtkBin *line = GTK_BIN (kids->data);
2328 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2329 GtkWidget *line_check =
2330 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2333 gtk_widget_show (line_check);
2335 gtk_widget_hide (line_check);
2339 #endif /* !HAVE_GTK2 */
2344 populate_prefs_page (state *s)
2346 saver_preferences *p = &s->prefs;
2348 Bool can_lock_p = True;
2350 /* Disable all the "lock" controls if locking support was not provided
2351 at compile-time, or if running on MacOS. */
2352 # if defined(NO_LOCKING) || defined(__APPLE__)
2357 /* The file supports timeouts of less than a minute, but the GUI does
2358 not, so throttle the values to be at least one minute (since "0" is
2359 a bad rounding choice...)
2361 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2364 THROTTLE (passwd_timeout);
2367 # define FMT_MINUTES(NAME,N) \
2368 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2370 # define FMT_SECONDS(NAME,N) \
2371 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2373 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2374 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2375 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2376 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2377 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2378 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2379 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2384 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2385 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2388 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2389 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2390 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2391 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2392 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2393 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2394 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2395 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2396 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2397 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2398 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2400 # undef TOGGLE_ACTIVE
2402 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2403 (p->image_directory ? p->image_directory : ""));
2404 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2406 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2409 /* Map the `saver_mode' enum to mode menu to values. */
2411 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2414 for (i = 0; i < countof(mode_menu_order); i++)
2415 if (mode_menu_order[i] == p->mode)
2417 gtk_option_menu_set_history (opt, i);
2418 update_list_sensitivity (s);
2422 Bool found_any_writable_cells = False;
2423 Bool dpms_supported = False;
2425 Display *dpy = GDK_DISPLAY();
2426 int nscreens = ScreenCount(dpy);
2428 for (i = 0; i < nscreens; i++)
2430 Screen *s = ScreenOfDisplay (dpy, i);
2431 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2433 found_any_writable_cells = True;
2438 #ifdef HAVE_XF86VMODE_GAMMA
2439 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2442 #ifdef HAVE_DPMS_EXTENSION
2444 int op = 0, event = 0, error = 0;
2445 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2446 dpms_supported = True;
2448 #endif /* HAVE_DPMS_EXTENSION */
2451 # define SENSITIZE(NAME,SENSITIVEP) \
2452 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2454 /* Blanking and Locking
2456 SENSITIZE ("lock_button", can_lock_p);
2457 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2458 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2462 SENSITIZE ("dpms_frame", dpms_supported);
2463 SENSITIZE ("dpms_button", dpms_supported);
2464 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2465 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2466 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2467 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2468 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2469 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2470 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2471 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2472 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2476 SENSITIZE ("cmap_frame", found_any_writable_cells);
2477 SENSITIZE ("install_button", found_any_writable_cells);
2478 SENSITIZE ("fade_button", found_any_writable_cells);
2479 SENSITIZE ("unfade_button", found_any_writable_cells);
2481 SENSITIZE ("fade_label", (found_any_writable_cells &&
2482 (p->fade_p || p->unfade_p)));
2483 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2484 (p->fade_p || p->unfade_p)));
2492 populate_popup_window (state *s)
2494 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2495 char *doc_string = 0;
2497 /* #### not in Gtk 1.2
2498 gtk_label_set_selectable (doc);
2504 free_conf_data (s->cdata);
2509 saver_preferences *p = &s->prefs;
2510 int list_elt = selected_list_element (s);
2511 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2512 ? s->list_elt_to_hack_number[list_elt]
2514 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2517 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2518 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2519 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2520 s->cdata = load_configurator (cmd_line, s->debug_p);
2521 if (s->cdata && s->cdata->widget)
2522 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2527 doc_string = (s->cdata
2528 ? s->cdata->description
2530 # else /* !HAVE_XML */
2531 doc_string = _("Descriptions not available: no XML support compiled in.");
2532 # endif /* !HAVE_XML */
2534 gtk_label_set_text (doc, (doc_string
2536 : _("No description available.")));
2541 sensitize_demo_widgets (state *s, Bool sensitive_p)
2543 const char *names1[] = { "demo", "settings" };
2544 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2545 "visual", "visual_combo" };
2547 for (i = 0; i < countof(names1); i++)
2549 GtkWidget *w = name_to_widget (s, names1[i]);
2550 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2552 for (i = 0; i < countof(names2); i++)
2554 GtkWidget *w = name_to_widget (s, names2[i]);
2555 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2560 /* Even though we've given these text fields a maximum number of characters,
2561 their default size is still about 30 characters wide -- so measure out
2562 a string in their font, and resize them to just fit that.
2565 fix_text_entry_sizes (state *s)
2569 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2570 const char * const spinbuttons[] = {
2571 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2572 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2573 "dpms_off_spinbutton",
2574 "-fade_spinbutton" };
2578 for (i = 0; i < countof(spinbuttons); i++)
2580 const char *n = spinbuttons[i];
2582 while (*n == '-') n++, cols--;
2583 w = GTK_WIDGET (name_to_widget (s, n));
2584 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2585 gtk_widget_set_usize (w, width, -2);
2588 /* Now fix the width of the combo box.
2590 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2591 w = GTK_COMBO (w)->entry;
2592 width = gdk_string_width (w->style->font, "PseudoColor___");
2593 gtk_widget_set_usize (w, width, -2);
2595 /* Now fix the width of the file entry text.
2597 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2598 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2599 gtk_widget_set_usize (w, width, -2);
2601 /* Now fix the width of the command line text.
2603 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2604 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2605 gtk_widget_set_usize (w, width, -2);
2609 /* Now fix the height of the list widget:
2610 make it default to being around 10 text-lines high instead of 4.
2612 w = GTK_WIDGET (name_to_widget (s, "list"));
2616 int leading = 3; /* approximate is ok... */
2620 PangoFontMetrics *pain =
2621 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2622 w->style->font_desc,
2623 gtk_get_default_language ());
2624 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2625 pango_font_metrics_get_descent (pain));
2626 #else /* !HAVE_GTK2 */
2627 height = w->style->font->ascent + w->style->font->descent;
2628 #endif /* !HAVE_GTK2 */
2632 height += border * 2;
2633 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2634 gtk_widget_set_usize (w, -2, height);
2641 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2644 static char *up_arrow_xpm[] = {
2667 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2668 the end of the array (Gtk 1.2.5.) */
2669 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2670 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2673 static char *down_arrow_xpm[] = {
2696 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2697 the end of the array (Gtk 1.2.5.) */
2698 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2699 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2703 pixmapify_button (state *s, int down_p)
2707 GtkWidget *pixmapwid;
2711 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2712 style = gtk_widget_get_style (w);
2714 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2715 &style->bg[GTK_STATE_NORMAL],
2717 ? (gchar **) down_arrow_xpm
2718 : (gchar **) up_arrow_xpm));
2719 pixmapwid = gtk_pixmap_new (pixmap, mask);
2720 gtk_widget_show (pixmapwid);
2721 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2722 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2726 map_next_button_cb (GtkWidget *w, gpointer user_data)
2728 state *s = (state *) user_data;
2729 pixmapify_button (s, 1);
2733 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2735 state *s = (state *) user_data;
2736 pixmapify_button (s, 0);
2738 #endif /* !HAVE_GTK2 */
2741 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2745 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2746 GtkAllocation *allocation,
2750 GtkWidgetAuxInfo *aux_info;
2752 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2754 aux_info->width = allocation->width;
2755 aux_info->height = -2;
2759 gtk_widget_size_request (label, &req);
2763 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2766 eschew_gtk_lossage (GtkLabel *label)
2768 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2769 aux_info->width = GTK_WIDGET (label)->allocation.width;
2770 aux_info->height = -2;
2774 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2776 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2777 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2780 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2782 gtk_widget_queue_resize (GTK_WIDGET (label));
2787 populate_demo_window (state *s, int list_elt)
2789 saver_preferences *p = &s->prefs;
2792 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2793 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2794 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2795 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2796 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2798 if (p->mode == BLANK_ONLY)
2801 pretty_name = strdup (_("Blank Screen"));
2802 schedule_preview (s, 0);
2804 else if (p->mode == DONT_BLANK)
2807 pretty_name = strdup (_("Screen Saver Disabled"));
2808 schedule_preview (s, 0);
2812 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2813 ? s->list_elt_to_hack_number[list_elt]
2815 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2819 ? strdup (hack->name)
2820 : make_hack_name (hack->command))
2824 schedule_preview (s, hack->command);
2826 schedule_preview (s, 0);
2830 pretty_name = strdup (_("Preview"));
2832 gtk_frame_set_label (frame1, pretty_name);
2833 gtk_frame_set_label (frame2, pretty_name);
2835 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2836 gtk_entry_set_position (cmd, 0);
2840 sprintf (title, "%s: %.100s Settings",
2841 progclass, (pretty_name ? pretty_name : "???"));
2842 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2845 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2847 ? (hack->visual && *hack->visual
2852 sensitize_demo_widgets (s, (hack ? True : False));
2854 if (pretty_name) free (pretty_name);
2856 ensure_selected_item_visible (list);
2858 s->_selected_list_element = list_elt;
2863 widget_deleter (GtkWidget *widget, gpointer data)
2865 /* #### Well, I want to destroy these widgets, but if I do that, they get
2866 referenced again, and eventually I get a SEGV. So instead of
2867 destroying them, I'll just hide them, and leak a bunch of memory
2868 every time the disk file changes. Go go go Gtk!
2870 #### Ok, that's a lie, I get a crash even if I just hide the widget
2871 and don't ever delete it. Fuck!
2874 gtk_widget_destroy (widget);
2876 gtk_widget_hide (widget);
2881 static char **sort_hack_cmp_names_kludge;
2883 sort_hack_cmp (const void *a, const void *b)
2889 int aa = *(int *) a;
2890 int bb = *(int *) b;
2891 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
2892 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
2893 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
2899 initialize_sort_map (state *s)
2901 saver_preferences *p = &s->prefs;
2904 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2905 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2906 if (s->hacks_available_p) free (s->hacks_available_p);
2908 s->list_elt_to_hack_number = (int *)
2909 calloc (sizeof(int), p->screenhacks_count + 1);
2910 s->hack_number_to_list_elt = (int *)
2911 calloc (sizeof(int), p->screenhacks_count + 1);
2912 s->hacks_available_p = (Bool *)
2913 calloc (sizeof(Bool), p->screenhacks_count + 1);
2915 /* Check which hacks actually exist on $PATH
2917 for (i = 0; i < p->screenhacks_count; i++)
2919 screenhack *hack = p->screenhacks[i];
2920 s->hacks_available_p[i] = on_path_p (hack->command);
2923 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
2927 for (i = 0; i < p->screenhacks_count; i++)
2929 if (!p->ignore_uninstalled_p ||
2930 s->hacks_available_p[i])
2931 s->list_elt_to_hack_number[j++] = i;
2935 for (; j < p->screenhacks_count; j++)
2936 s->list_elt_to_hack_number[j] = -1;
2939 /* Generate list of sortable names (once)
2941 sort_hack_cmp_names_kludge = (char **)
2942 calloc (sizeof(char *), p->screenhacks_count);
2943 for (i = 0; i < p->screenhacks_count; i++)
2945 screenhack *hack = p->screenhacks[i];
2946 char *name = (hack->name && *hack->name
2947 ? strdup (hack->name)
2948 : make_hack_name (hack->command));
2950 for (str = name; *str; str++)
2951 *str = tolower(*str);
2952 sort_hack_cmp_names_kludge[i] = name;
2955 /* Sort list->hack map alphabetically
2957 qsort (s->list_elt_to_hack_number,
2958 p->screenhacks_count,
2959 sizeof(*s->list_elt_to_hack_number),
2964 for (i = 0; i < p->screenhacks_count; i++)
2965 free (sort_hack_cmp_names_kludge[i]);
2966 free (sort_hack_cmp_names_kludge);
2967 sort_hack_cmp_names_kludge = 0;
2969 /* Build inverse table */
2970 for (i = 0; i < p->screenhacks_count; i++)
2971 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2976 maybe_reload_init_file (state *s)
2978 saver_preferences *p = &s->prefs;
2981 static Bool reentrant_lock = False;
2982 if (reentrant_lock) return 0;
2983 reentrant_lock = True;
2985 if (init_file_changed_p (p))
2987 const char *f = init_file_name();
2992 if (!f || !*f) return 0;
2993 b = (char *) malloc (strlen(f) + 1024);
2996 "file \"%s\" has changed, reloading.\n"),
2998 warning_dialog (s->toplevel_widget, b, False, 100);
3002 initialize_sort_map (s);
3004 list_elt = selected_list_element (s);
3005 list = name_to_widget (s, "list");
3006 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3007 populate_hack_list (s);
3008 force_list_select_item (s, list, list_elt, True);
3009 populate_prefs_page (s);
3010 populate_demo_window (s, list_elt);
3011 ensure_selected_item_visible (list);
3016 reentrant_lock = False;
3022 /* Making the preview window have the right X visual (so that GL works.)
3025 static Visual *get_best_gl_visual (state *);
3028 x_visual_to_gdk_visual (Visual *xv)
3030 GList *gvs = gdk_list_visuals();
3031 if (!xv) return gdk_visual_get_system();
3032 for (; gvs; gvs = gvs->next)
3034 GdkVisual *gv = (GdkVisual *) gvs->data;
3035 if (xv == GDK_VISUAL_XVISUAL (gv))
3038 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3039 blurb(), (unsigned long) xv->visualid);
3044 clear_preview_window (state *s)
3049 if (!s->toplevel_widget) return; /* very early */
3050 p = name_to_widget (s, "preview");
3053 if (!window) return;
3055 /* Flush the widget background down into the window, in case a subproc
3057 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3058 gdk_window_clear (window);
3061 int list_elt = selected_list_element (s);
3062 int hack_number = (list_elt >= 0
3063 ? s->list_elt_to_hack_number[list_elt]
3065 Bool available_p = (hack_number >= 0
3066 ? s->hacks_available_p [hack_number]
3069 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3070 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3071 (s->running_preview_error_p
3072 ? (available_p ? 1 : 2)
3074 #else /* !HAVE_GTK2 */
3075 if (s->running_preview_error_p)
3077 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3078 const char * const lines2[] = { N_("Not"), N_("Installed") };
3079 int nlines = countof(lines1);
3080 int lh = p->style->font->ascent + p->style->font->descent;
3084 const char * const *lines = (available_p ? lines1 : lines2);
3086 gdk_window_get_size (window, &w, &h);
3087 y = (h - (lh * nlines)) / 2;
3088 y += p->style->font->ascent;
3089 for (i = 0; i < nlines; i++)
3091 int sw = gdk_string_width (p->style->font, _(lines[i]));
3092 int x = (w - sw) / 2;
3093 gdk_draw_string (window, p->style->font,
3094 p->style->fg_gc[GTK_STATE_NORMAL],
3099 #endif /* !HAVE_GTK2 */
3107 fix_preview_visual (state *s)
3109 GtkWidget *widget = name_to_widget (s, "preview");
3110 Visual *xvisual = get_best_gl_visual (s);
3111 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3112 GdkVisual *dvisual = gdk_visual_get_system();
3113 GdkColormap *cmap = (visual == dvisual
3114 ? gdk_colormap_get_system ()
3115 : gdk_colormap_new (visual, False));
3118 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3119 (visual == dvisual ? "default" : "non-default"),
3120 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3122 if (!GTK_WIDGET_REALIZED (widget) ||
3123 gtk_widget_get_visual (widget) != visual)
3125 gtk_widget_unrealize (widget);
3126 gtk_widget_set_visual (widget, visual);
3127 gtk_widget_set_colormap (widget, cmap);
3128 gtk_widget_realize (widget);
3131 /* Set the Widget colors to be white-on-black. */
3133 GdkWindow *window = widget->window;
3134 GtkStyle *style = gtk_style_copy (widget->style);
3135 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3136 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3137 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3138 GdkGC *fgc = gdk_gc_new(window);
3139 GdkGC *bgc = gdk_gc_new(window);
3140 if (!gdk_color_white (cmap, fg)) abort();
3141 if (!gdk_color_black (cmap, bg)) abort();
3142 gdk_gc_set_foreground (fgc, fg);
3143 gdk_gc_set_background (fgc, bg);
3144 gdk_gc_set_foreground (bgc, bg);
3145 gdk_gc_set_background (bgc, fg);
3146 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3147 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3148 gtk_widget_set_style (widget, style);
3150 /* For debugging purposes, put a title on the window (so that
3151 it can be easily found in the output of "xwininfo -tree".)
3153 gdk_window_set_title (window, "Preview");
3156 gtk_widget_show (widget);
3164 subproc_pretty_name (state *s)
3166 if (s->running_preview_cmd)
3168 char *ps = strdup (s->running_preview_cmd);
3169 char *ss = strchr (ps, ' ');
3171 ss = strrchr (ps, '/');
3182 return strdup ("???");
3187 reap_zombies (state *s)
3189 int wait_status = 0;
3191 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3195 if (pid == s->running_preview_pid)
3197 char *ss = subproc_pretty_name (s);
3198 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3199 (unsigned long) pid, ss);
3203 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3204 (unsigned long) pid);
3210 /* Mostly lifted from driver/subprocs.c */
3212 get_best_gl_visual (state *s)
3214 Display *dpy = GDK_DISPLAY();
3223 av[ac++] = "xscreensaver-gl-helper";
3228 perror ("error creating pipe:");
3235 switch ((int) (forked = fork ()))
3239 sprintf (buf, "%s: couldn't fork", blurb());
3247 close (in); /* don't need this one */
3248 close (ConnectionNumber (dpy)); /* close display fd */
3250 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3252 perror ("could not dup() a new stdout:");
3256 execvp (av[0], av); /* shouldn't return. */
3258 if (errno != ENOENT)
3260 /* Ignore "no such file or directory" errors, unless verbose.
3261 Issue all other exec errors, though. */
3262 sprintf (buf, "%s: running %s", blurb(), av[0]);
3266 /* Note that one must use _exit() instead of exit() in procs forked
3267 off of Gtk programs -- Gtk installs an atexit handler that has a
3268 copy of the X connection (which we've already closed, for safety.)
3269 If one uses exit() instead of _exit(), then one sometimes gets a
3270 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3272 _exit (1); /* exits fork */
3278 int wait_status = 0;
3280 FILE *f = fdopen (in, "r");
3284 close (out); /* don't need this one */
3287 fgets (buf, sizeof(buf)-1, f);
3290 /* Wait for the child to die. */
3291 waitpid (-1, &wait_status, 0);
3293 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3299 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3305 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3307 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3308 blurb(), av[0], result);
3320 kill_preview_subproc (state *s)
3322 s->running_preview_error_p = False;
3325 clear_preview_window (s);
3327 if (s->subproc_check_timer_id)
3329 gtk_timeout_remove (s->subproc_check_timer_id);
3330 s->subproc_check_timer_id = 0;
3331 s->subproc_check_countdown = 0;
3334 if (s->running_preview_pid)
3336 int status = kill (s->running_preview_pid, SIGTERM);
3337 char *ss = subproc_pretty_name (s);
3344 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3345 blurb(), (unsigned long) s->running_preview_pid, ss);
3350 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3351 blurb(), (unsigned long) s->running_preview_pid, ss);
3355 else if (s->debug_p)
3356 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3357 (unsigned long) s->running_preview_pid, ss);
3360 s->running_preview_pid = 0;
3361 if (s->running_preview_cmd) free (s->running_preview_cmd);
3362 s->running_preview_cmd = 0;
3369 /* Immediately and unconditionally launches the given process,
3370 after appending the -window-id option; sets running_preview_pid.
3373 launch_preview_subproc (state *s)
3375 saver_preferences *p = &s->prefs;
3379 const char *cmd = s->desired_preview_cmd;
3381 GtkWidget *pr = name_to_widget (s, "preview");
3382 GdkWindow *window = pr->window;
3384 s->running_preview_error_p = False;
3386 if (s->preview_suppressed_p)
3388 kill_preview_subproc (s);
3392 new_cmd = malloc (strlen (cmd) + 40);
3394 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3397 /* No window id? No command to run. */
3403 strcpy (new_cmd, cmd);
3404 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3408 kill_preview_subproc (s);
3411 s->running_preview_error_p = True;
3412 clear_preview_window (s);
3416 switch ((int) (forked = fork ()))
3421 sprintf (buf, "%s: couldn't fork", blurb());
3423 s->running_preview_error_p = True;
3429 close (ConnectionNumber (GDK_DISPLAY()));
3431 usleep (250000); /* pause for 1/4th second before launching, to give
3432 the previous program time to die and flush its X
3433 buffer, so we don't get leftover turds on the
3436 exec_command (p->shell, new_cmd, p->nice_inferior);
3437 /* Don't bother printing an error message when we are unable to
3438 exec subprocesses; we handle that by polling the pid later.
3440 Note that one must use _exit() instead of exit() in procs forked
3441 off of Gtk programs -- Gtk installs an atexit handler that has a
3442 copy of the X connection (which we've already closed, for safety.)
3443 If one uses exit() instead of _exit(), then one sometimes gets a
3444 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3446 _exit (1); /* exits child fork */
3451 if (s->running_preview_cmd) free (s->running_preview_cmd);
3452 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3453 s->running_preview_pid = forked;
3457 char *ss = subproc_pretty_name (s);
3458 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3459 (unsigned long) forked, ss);
3466 schedule_preview_check (s);
3469 if (new_cmd) free (new_cmd);
3474 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3477 hack_environment (state *s)
3479 static const char *def_path =
3480 # ifdef DEFAULT_PATH_PREFIX
3481 DEFAULT_PATH_PREFIX;
3486 Display *dpy = GDK_DISPLAY();
3487 const char *odpy = DisplayString (dpy);
3488 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3489 strcpy (ndpy, "DISPLAY=");
3490 strcat (ndpy, odpy);
3495 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3497 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3498 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3499 So we must leak it (and/or the previous setting). Yay.
3502 if (def_path && *def_path)
3504 const char *opath = getenv("PATH");
3505 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3506 strcpy (npath, "PATH=");
3507 strcat (npath, def_path);
3508 strcat (npath, ":");
3509 strcat (npath, opath);
3513 /* do not free(npath) -- see above */
3516 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3521 /* Called from a timer:
3522 Launches the currently-chosen subprocess, if it's not already running.
3523 If there's a different process running, kills it.
3526 update_subproc_timer (gpointer data)
3528 state *s = (state *) data;
3529 if (! s->desired_preview_cmd)
3530 kill_preview_subproc (s);
3531 else if (!s->running_preview_cmd ||
3532 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3533 launch_preview_subproc (s);
3535 s->subproc_timer_id = 0;
3536 return FALSE; /* do not re-execute timer */
3540 /* Call this when you think you might want a preview process running.
3541 It will set a timer that will actually launch that program a second
3542 from now, if you haven't changed your mind (to avoid double-click
3543 spazzing, etc.) `cmd' may be null meaning "no process".
3546 schedule_preview (state *s, const char *cmd)
3548 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3553 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3555 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3558 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3559 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3561 if (s->subproc_timer_id)
3562 gtk_timeout_remove (s->subproc_timer_id);
3563 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3567 /* Called from a timer:
3568 Checks to see if the subproc that should be running, actually is.
3571 check_subproc_timer (gpointer data)
3573 state *s = (state *) data;
3574 Bool again_p = True;
3576 if (s->running_preview_error_p || /* already dead */
3577 s->running_preview_pid <= 0)
3585 status = kill (s->running_preview_pid, 0);
3586 if (status < 0 && errno == ESRCH)
3587 s->running_preview_error_p = True;
3591 char *ss = subproc_pretty_name (s);
3592 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3593 (unsigned long) s->running_preview_pid, ss,
3594 (s->running_preview_error_p ? "dead" : "alive"));
3598 if (s->running_preview_error_p)
3600 clear_preview_window (s);
3605 /* Otherwise, it's currently alive. We might be checking again, or we
3606 might be satisfied. */
3608 if (--s->subproc_check_countdown <= 0)
3612 return TRUE; /* re-execute timer */
3615 s->subproc_check_timer_id = 0;
3616 s->subproc_check_countdown = 0;
3617 return FALSE; /* do not re-execute timer */
3622 /* Call this just after launching a subprocess.
3623 This sets a timer that will, five times a second for two seconds,
3624 check whether the program is still running. The assumption here
3625 is that if the process didn't stay up for more than a couple of
3626 seconds, then either the program doesn't exist, or it doesn't
3627 take a -window-id argument.
3630 schedule_preview_check (state *s)
3636 fprintf (stderr, "%s: scheduling check\n", blurb());
3638 if (s->subproc_check_timer_id)
3639 gtk_timeout_remove (s->subproc_check_timer_id);
3640 s->subproc_check_timer_id =
3641 gtk_timeout_add (1000 / ticks,
3642 check_subproc_timer, (gpointer) s);
3643 s->subproc_check_countdown = ticks * seconds;
3648 screen_blanked_p (void)
3652 unsigned long nitems, bytesafter;
3654 Display *dpy = GDK_DISPLAY();
3655 Bool blanked_p = False;
3657 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3658 XA_SCREENSAVER_STATUS,
3659 0, 3, False, XA_INTEGER,
3660 &type, &format, &nitems, &bytesafter,
3661 (unsigned char **) &data)
3663 && type == XA_INTEGER
3666 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3668 if (data) free (data);
3673 /* Wake up every now and then and see if the screen is blanked.
3674 If it is, kill off the small-window demo -- no point in wasting
3675 cycles by running two screensavers at once...
3678 check_blanked_timer (gpointer data)
3680 state *s = (state *) data;
3681 Bool blanked_p = screen_blanked_p ();
3682 if (blanked_p && s->running_preview_pid)
3685 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3686 kill_preview_subproc (s);
3689 return True; /* re-execute timer */
3693 /* Setting window manager icon
3697 init_icon (GdkWindow *window)
3699 GdkBitmap *mask = 0;
3702 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3703 (gchar **) logo_50_xpm);
3705 gdk_window_set_icon (window, 0, pixmap, mask);
3709 /* The main demo-mode command loop.
3714 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3715 XrmRepresentation *type, XrmValue *value, XPointer closure)
3718 for (i = 0; quarks[i]; i++)
3720 if (bindings[i] == XrmBindTightly)
3721 fprintf (stderr, (i == 0 ? "" : "."));
3722 else if (bindings[i] == XrmBindLoosely)
3723 fprintf (stderr, "*");
3725 fprintf (stderr, " ??? ");
3726 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3729 fprintf (stderr, ": %s\n", (char *) value->addr);
3737 the_network_is_not_the_computer (state *s)
3739 Display *dpy = GDK_DISPLAY();
3740 char *rversion = 0, *ruser = 0, *rhost = 0;
3741 char *luser, *lhost;
3743 struct passwd *p = getpwuid (getuid ());
3744 const char *d = DisplayString (dpy);
3746 # if defined(HAVE_UNAME)
3748 if (uname (&uts) < 0)
3749 lhost = "<UNKNOWN>";
3751 lhost = uts.nodename;
3753 strcpy (lhost, getenv("SYS$NODE"));
3754 # else /* !HAVE_UNAME && !VMS */
3755 strcat (lhost, "<UNKNOWN>");
3756 # endif /* !HAVE_UNAME && !VMS */
3758 if (p && p->pw_name)
3763 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3765 /* Make a buffer that's big enough for a number of copies of all the
3766 strings, plus some. */
3767 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3768 (ruser ? strlen(ruser) : 0) +
3769 (rhost ? strlen(rhost) : 0) +
3776 if (!rversion || !*rversion)
3780 "The XScreenSaver daemon doesn't seem to be running\n"
3781 "on display \"%s\". Launch it now?"),
3784 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3786 /* Warn that the two processes are running as different users.
3790 "%s is running as user \"%s\" on host \"%s\".\n"
3791 "But the xscreensaver managing display \"%s\"\n"
3792 "is running as user \"%s\" on host \"%s\".\n"
3794 "Since they are different users, they won't be reading/writing\n"
3795 "the same ~/.xscreensaver file, so %s isn't\n"
3796 "going to work right.\n"
3798 "You should either re-run %s as \"%s\", or re-run\n"
3799 "xscreensaver as \"%s\".\n"
3801 "Restart the xscreensaver daemon now?\n"),
3802 progname, luser, lhost,
3804 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3806 progname, (ruser ? ruser : "???"),
3809 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3811 /* Warn that the two processes are running on different hosts.
3815 "%s is running as user \"%s\" on host \"%s\".\n"
3816 "But the xscreensaver managing display \"%s\"\n"
3817 "is running as user \"%s\" on host \"%s\".\n"
3819 "If those two machines don't share a file system (that is,\n"
3820 "if they don't see the same ~%s/.xscreensaver file) then\n"
3821 "%s won't work right.\n"
3823 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3824 progname, luser, lhost,
3826 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3831 else if (!!strcmp (rversion, s->short_version))
3833 /* Warn that the version numbers don't match.
3837 "This is %s version %s.\n"
3838 "But the xscreensaver managing display \"%s\"\n"
3839 "is version %s. This could cause problems.\n"
3841 "Restart the xscreensaver daemon now?\n"),
3842 progname, s->short_version,
3849 warning_dialog (s->toplevel_widget, msg, True, 1);
3851 if (rversion) free (rversion);
3852 if (ruser) free (ruser);
3853 if (rhost) free (rhost);
3858 /* We use this error handler so that X errors are preceeded by the name
3859 of the program that generated them.
3862 demo_ehandler (Display *dpy, XErrorEvent *error)
3864 state *s = global_state_kludge; /* I hate C so much... */
3865 fprintf (stderr, "\nX error in %s:\n", blurb());
3866 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3867 kill_preview_subproc (s);
3873 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3874 of the program that generated them; and also that we can ignore one
3875 particular bogus error message that Gdk madly spews.
3878 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3879 const gchar *message, gpointer user_data)
3881 /* Ignore the message "Got event for unknown window: 0x...".
3882 Apparently some events are coming in for the xscreensaver window
3883 (presumably reply events related to the ClientMessage) and Gdk
3884 feels the need to complain about them. So, just suppress any
3885 messages that look like that one.
3887 if (strstr (message, "unknown window"))
3890 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3891 (log_domain ? log_domain : progclass),
3892 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3893 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3894 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3895 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3896 log_level == G_LOG_LEVEL_INFO ? "info" :
3897 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3899 ((!*message || message[strlen(message)-1] != '\n')
3904 static char *defaults[] = {
3905 #include "XScreenSaver_ad.h"
3910 #ifdef HAVE_CRAPPLET
3911 static struct poptOption crapplet_options[] = {
3912 {NULL, '\0', 0, NULL, 0}
3914 #endif /* HAVE_CRAPPLET */
3917 const char *usage = "[--display dpy] [--prefs]"
3918 # ifdef HAVE_CRAPPLET
3921 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
3924 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3926 state *s = (state *) user_data;
3927 Boolean oi = s->initializing_p;
3928 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3929 s->initializing_p = True;
3930 eschew_gtk_lossage (label);
3931 s->initializing_p = oi;
3937 print_widget_tree (GtkWidget *w, int depth)
3940 for (i = 0; i < depth; i++)
3941 fprintf (stderr, " ");
3942 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3944 if (GTK_IS_LIST (w))
3946 for (i = 0; i < depth+1; i++)
3947 fprintf (stderr, " ");
3948 fprintf (stderr, "...list kids...\n");
3950 else if (GTK_IS_CONTAINER (w))
3952 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3955 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3963 delayed_scroll_kludge (gpointer data)
3965 state *s = (state *) data;
3966 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
3967 ensure_selected_item_visible (w);
3969 /* Oh, this is just fucking lovely, too. */
3970 w = GTK_WIDGET (name_to_widget (s, "preview"));
3971 gtk_widget_hide (w);
3972 gtk_widget_show (w);
3974 return FALSE; /* do not re-execute timer */
3980 create_xscreensaver_demo (void)
3984 nb = name_to_widget (global_state_kludge, "preview_notebook");
3985 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
3987 return name_to_widget (global_state_kludge, "xscreensaver_demo");
3991 create_xscreensaver_settings_dialog (void)
3995 box = name_to_widget (global_state_kludge, "dialog_action_area");
3997 w = name_to_widget (global_state_kludge, "adv_button");
3998 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4000 w = name_to_widget (global_state_kludge, "std_button");
4001 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4003 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4006 #endif /* HAVE_GTK2 */
4009 main (int argc, char **argv)
4013 saver_preferences *p;
4017 Widget toplevel_shell;
4018 char *real_progname = argv[0];
4020 Bool crapplet_p = False;
4024 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4025 textdomain (GETTEXT_PACKAGE);
4028 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4029 # else /* !HAVE_GTK2 */
4030 if (!setlocale (LC_ALL, ""))
4031 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4032 # endif /* !HAVE_GTK2 */
4034 #endif /* ENABLE_NLS */
4036 str = strrchr (real_progname, '/');
4037 if (str) real_progname = str+1;
4040 memset (s, 0, sizeof(*s));
4041 s->initializing_p = True;
4044 global_state_kludge = s; /* I hate C so much... */
4046 progname = real_progname;
4048 s->short_version = (char *) malloc (5);
4049 memcpy (s->short_version, screensaver_id + 17, 4);
4050 s->short_version [4] = 0;
4053 /* Register our error message logger for every ``log domain'' known.
4054 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4055 for all of the domains that seem to be in use.
4058 const char * const domains[] = { 0,
4059 "Gtk", "Gdk", "GLib", "GModule",
4060 "GThread", "Gnome", "GnomeUI" };
4061 for (i = 0; i < countof(domains); i++)
4062 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4065 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4068 const char *dir = DEFAULT_ICONDIR;
4069 if (*dir) add_pixmap_directory (dir);
4071 # endif /* !HAVE_GTK2 */
4072 #endif /* DEFAULT_ICONDIR */
4074 /* This is gross, but Gtk understands --display and not -display...
4076 for (i = 1; i < argc; i++)
4077 if (argv[i][0] && argv[i][1] &&
4078 !strncmp(argv[i], "-display", strlen(argv[i])))
4079 argv[i] = "--display";
4082 /* We need to parse this arg really early... Sigh. */
4083 for (i = 1; i < argc; i++)
4086 (!strcmp(argv[i], "--crapplet") ||
4087 !strcmp(argv[i], "--capplet")))
4089 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4092 for (j = i; j < argc; j++) /* remove it from the list */
4093 argv[j] = argv[j+1];
4095 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4096 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4098 fprintf (stderr, "%s: %s\n", real_progname, usage);
4100 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4103 (!strcmp(argv[i], "--debug") ||
4104 !strcmp(argv[i], "-debug") ||
4105 !strcmp(argv[i], "-d")))
4109 for (j = i; j < argc; j++) /* remove it from the list */
4110 argv[j] = argv[j+1];
4117 (!strcmp(argv[i], "--configdir")))
4121 hack_configuration_path = argv[i+1];
4122 for (j = i; j < argc; j++) /* remove them from the list */
4123 argv[j] = argv[j+2];
4127 if (0 != stat (hack_configuration_path, &st))
4130 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4134 else if (!S_ISDIR (st.st_mode))
4136 fprintf (stderr, "%s: not a directory: %s\n",
4137 blurb(), hack_configuration_path);
4145 fprintf (stderr, "%s: using config directory \"%s\"\n",
4146 progname, hack_configuration_path);
4149 /* Let Gtk open the X connection, then initialize Xt to use that
4150 same connection. Doctor Frankenstein would be proud.
4152 # ifdef HAVE_CRAPPLET
4155 GnomeClient *client;
4156 GnomeClientFlags flags = 0;
4158 int init_results = gnome_capplet_init ("screensaver-properties",
4160 argc, argv, NULL, 0, NULL);
4162 0 upon successful initialization;
4163 1 if --init-session-settings was passed on the cmdline;
4164 2 if --ignore was passed on the cmdline;
4167 So the 1 signifies just to init the settings, and quit, basically.
4168 (Meaning launch the xscreensaver daemon.)
4171 if (init_results < 0)
4174 g_error ("An initialization error occurred while "
4175 "starting xscreensaver-capplet.\n");
4177 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4178 real_progname, init_results);
4183 client = gnome_master_client ();
4186 flags = gnome_client_get_flags (client);
4188 if (flags & GNOME_CLIENT_IS_CONNECTED)
4191 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4192 gnome_client_get_id (client));
4195 char *session_args[20];
4197 session_args[i++] = real_progname;
4198 session_args[i++] = "--capplet";
4199 session_args[i++] = "--init-session-settings";
4200 session_args[i] = 0;
4201 gnome_client_set_priority (client, 20);
4202 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4203 gnome_client_set_restart_command (client, i, session_args);
4207 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4210 gnome_client_flush (client);
4213 if (init_results == 1)
4215 system ("xscreensaver -nosplash &");
4221 # endif /* HAVE_CRAPPLET */
4223 gtk_init (&argc, &argv);
4227 /* We must read exactly the same resources as xscreensaver.
4228 That means we must have both the same progclass *and* progname,
4229 at least as far as the resource database is concerned. So,
4230 put "xscreensaver" in argv[0] while initializing Xt.
4232 argv[0] = "xscreensaver";
4236 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4238 XtToolkitInitialize ();
4239 app = XtCreateApplicationContext ();
4240 dpy = GDK_DISPLAY();
4241 XtAppSetFallbackResources (app, defaults);
4242 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4243 toplevel_shell = XtAppCreateShell (progname, progclass,
4244 applicationShellWidgetClass,
4247 dpy = XtDisplay (toplevel_shell);
4248 db = XtDatabase (dpy);
4249 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4250 XSetErrorHandler (demo_ehandler);
4252 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4253 signal (SIGPIPE, SIG_IGN);
4255 /* After doing Xt-style command-line processing, complain about any
4256 unrecognized command-line arguments.
4258 for (i = 1; i < argc; i++)
4260 char *str = argv[i];
4261 if (str[0] == '-' && str[1] == '-')
4263 if (!strcmp (str, "-prefs"))
4265 else if (crapplet_p)
4266 /* There are lots of random args that we don't care about when we're
4267 started as a crapplet, so just ignore unknown args in that case. */
4271 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname, argv[i]);
4272 fprintf (stderr, "%s: %s\n", real_progname, usage);
4277 /* Load the init file, which may end up consulting the X resource database
4278 and the site-wide app-defaults file. Note that at this point, it's
4279 important that `progname' be "xscreensaver", rather than whatever
4284 hack_environment (s); /* must be before initialize_sort_map() */
4287 initialize_sort_map (s);
4289 /* Now that Xt has been initialized, and the resources have been read,
4290 we can set our `progname' variable to something more in line with
4293 progname = real_progname;
4297 /* Print out all the resources we read. */
4299 XrmName name = { 0 };
4300 XrmClass class = { 0 };
4302 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4308 /* Intern the atoms that xscreensaver_command() needs.
4310 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4311 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4312 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4313 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4314 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4315 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4316 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4317 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4318 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4319 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4320 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4321 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4322 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4325 /* Create the window and all its widgets.
4327 s->base_widget = create_xscreensaver_demo ();
4328 s->popup_widget = create_xscreensaver_settings_dialog ();
4329 s->toplevel_widget = s->base_widget;
4332 /* Set the main window's title. */
4334 char *base_title = _("Screensaver Preferences");
4335 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4336 char *s1, *s2, *s3, *s4;
4337 s1 = (char *) strchr(v, ' '); s1++;
4338 s2 = (char *) strchr(s1, ' ');
4339 s3 = (char *) strchr(v, '('); s3++;
4340 s4 = (char *) strchr(s3, ')');
4344 window_title = (char *) malloc (strlen (base_title) +
4345 strlen (progclass) +
4346 strlen (s1) + strlen (s3) +
4348 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4349 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4350 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4354 /* Adjust the (invisible) notebooks on the popup dialog... */
4356 GtkNotebook *notebook =
4357 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4358 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4362 gtk_widget_hide (std);
4363 # else /* !HAVE_XML */
4364 /* Make the advanced page be the only one available. */
4365 gtk_widget_set_sensitive (std, False);
4366 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4367 gtk_widget_hide (std);
4369 # endif /* !HAVE_XML */
4371 gtk_notebook_set_page (notebook, page);
4372 gtk_notebook_set_show_tabs (notebook, False);
4375 /* Various other widget initializations...
4377 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4378 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4380 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4381 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4384 populate_hack_list (s);
4385 populate_prefs_page (s);
4386 sensitize_demo_widgets (s, False);
4387 fix_text_entry_sizes (s);
4388 scroll_to_current_hack (s);
4390 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4391 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4395 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4396 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4398 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4399 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4401 #endif /* !HAVE_GTK2 */
4403 /* Hook up callbacks to the items on the mode menu. */
4405 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4406 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4407 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4408 for (; kids; kids = kids->next)
4409 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4410 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4415 /* Handle the -prefs command-line argument. */
4418 GtkNotebook *notebook =
4419 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4420 gtk_notebook_set_page (notebook, 1);
4423 # ifdef HAVE_CRAPPLET
4427 GtkWidget *outer_vbox;
4429 gtk_widget_hide (s->toplevel_widget);
4431 capplet = capplet_widget_new ();
4433 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4434 # ifdef HAVE_CRAPPLET_IMMEDIATE
4435 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4436 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4437 /* In crapplet-mode, take off the menubar. */
4438 gtk_widget_hide (name_to_widget (s, "menubar"));
4440 /* Reparent our top-level container to be a child of the capplet
4443 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4444 gtk_widget_ref (outer_vbox);
4445 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4447 GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4448 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4450 /* Find the window above us, and set the title and close handler. */
4452 GtkWidget *window = capplet;
4453 while (window && !GTK_IS_WINDOW (window))
4454 window = window->parent;
4457 gtk_window_set_title (GTK_WINDOW (window), window_title);
4458 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4459 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4464 s->toplevel_widget = capplet;
4466 # endif /* HAVE_CRAPPLET */
4469 /* The Gnome folks hate the menubar. I think it's important to have access
4470 to the commands on the File menu (Restart Daemon, etc.) and to the
4471 About and Documentation commands on the Help menu.
4475 gtk_widget_hide (name_to_widget (s, "menubar"));
4479 free (window_title);
4483 gtk_widget_show (s->toplevel_widget);
4484 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4485 fix_preview_visual (s);
4487 /* Realize page zero, so that we can diddle the scrollbar when the
4488 user tabs back to it -- otherwise, the current hack isn't scrolled
4489 to the first time they tab back there, when started with "-prefs".
4490 (Though it is if they then tab away, and back again.)
4492 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4493 #### understands this crap, explain to me how to make this work.
4495 gtk_widget_realize (name_to_widget (s, "demos_table"));
4498 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4501 /* Issue any warnings about the running xscreensaver daemon. */
4502 the_network_is_not_the_computer (s);
4505 /* Run the Gtk event loop, and not the Xt event loop. This means that
4506 if there were Xt timers or fds registered, they would never get serviced,
4507 and if there were any Xt widgets, they would never have events delivered.
4508 Fortunately, we're using Gtk for all of the UI, and only initialized
4509 Xt so that we could process the command line and use the X resource
4512 s->initializing_p = False;
4514 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4515 after we start up. Otherwise, it always appears scrolled to the top
4516 when in crapplet-mode. */
4517 gtk_timeout_add (500, delayed_scroll_kludge, s);
4521 /* Load every configurator in turn, to scan them for errors all at once. */
4524 for (i = 0; i < p->screenhacks_count; i++)
4526 screenhack *hack = p->screenhacks[i];
4527 conf_data *d = load_configurator (hack->command, False);
4528 if (d) free_conf_data (d);
4534 # ifdef HAVE_CRAPPLET
4536 capplet_gtk_main ();
4538 # endif /* HAVE_CRAPPLET */
4541 kill_preview_subproc (s);
4545 #endif /* HAVE_GTK -- whole file */