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>
28 # define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */
36 #endif /* ENABLE_NLS */
39 # include <pwd.h> /* for getpwuid() */
45 # include <sys/utsname.h> /* for uname() */
46 #endif /* HAVE_UNAME */
56 #ifdef HAVE_SYS_WAIT_H
57 # include <sys/wait.h> /* for waitpid() and associated macros */
61 #include <X11/Xproto.h> /* for CARD32 */
62 #include <X11/Xatom.h> /* for XA_INTEGER */
63 #include <X11/Intrinsic.h>
64 #include <X11/StringDefs.h>
66 /* We don't actually use any widget internals, but these are included
67 so that gdb will have debug info for the widgets... */
68 #include <X11/IntrinsicP.h>
69 #include <X11/ShellP.h>
73 # include <X11/Xmu/Error.h>
75 # include <Xmu/Error.h>
85 # include <capplet-widget.h>
91 #include <glade/glade-xml.h>
92 #endif /* HAVE_GTK2 */
94 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
95 # define GLADE_DIR DEFAULT_ICONDIR
97 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
98 # define DEFAULT_ICONDIR GLADE_DIR
102 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
103 It is unused otherwise, so in that case, stub it out. */
104 static const char *hack_configuration_path = 0;
111 #include "resources.h" /* for parse_time() */
112 #include "visual.h" /* for has_writable_cells() */
113 #include "remote.h" /* for xscreensaver_command() */
116 #include "logo-50.xpm"
117 #include "logo-180.xpm"
119 #include "demo-Gtk-widgets.h"
120 #include "demo-Gtk-support.h"
121 #include "demo-Gtk-conf.h"
133 #endif /* HAVE_GTK2 */
136 extern void exec_command (const char *shell, const char *command, int nice);
139 #define countof(x) (sizeof((x))/sizeof((*x)))
143 char *progclass = "XScreenSaver";
146 /* The order of the items in the mode menu. */
147 static int mode_menu_order[] = {
148 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS };
153 char *short_version; /* version number of this xscreensaver build */
155 GtkWidget *toplevel_widget; /* the main window */
156 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
157 GtkWidget *popup_widget; /* the "Settings" dialog */
158 conf_data *cdata; /* private data for per-hack configuration */
161 GladeXML *glade_ui; /* Glade UI file */
162 #endif /* HAVE_GTK2 */
164 Bool debug_p; /* whether to print diagnostics */
165 Bool initializing_p; /* flag for breaking recursion loops */
166 Bool saving_p; /* flag for breaking recursion loops */
168 char *desired_preview_cmd; /* subprocess we intend to run */
169 char *running_preview_cmd; /* subprocess we are currently running */
170 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
171 Bool running_preview_error_p; /* whether the pid died abnormally */
173 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
174 int subproc_timer_id; /* timer to delay subproc launch */
175 int subproc_check_timer_id; /* timer to check whether it started up */
176 int subproc_check_countdown; /* how many more checks left */
178 int *list_elt_to_hack_number; /* table for sorting the hack list */
179 int *hack_number_to_list_elt; /* the inverse table */
180 Bool *hacks_available_p; /* whether hacks are on $PATH */
181 int list_count; /* how many items are in the list: this may be
182 less than p->screenhacks_count, if some are
185 int _selected_list_element; /* don't use this: call
186 selected_list_element() instead */
188 saver_preferences prefs;
193 /* Total fucking evilness due to the fact that it's rocket science to get
194 a closure object of our own down into the various widget callbacks. */
195 static state *global_state_kludge;
198 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
199 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
200 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
203 static void populate_demo_window (state *, int list_elt);
204 static void populate_prefs_page (state *);
205 static void populate_popup_window (state *);
207 static Bool flush_dialog_changes_and_save (state *);
208 static Bool flush_popup_changes_and_save (state *);
210 static int maybe_reload_init_file (state *);
211 static void await_xscreensaver (state *);
213 static void schedule_preview (state *, const char *cmd);
214 static void kill_preview_subproc (state *);
215 static void schedule_preview_check (state *);
219 /* Some random utility functions
225 time_t now = time ((time_t *) 0);
226 char *ct = (char *) ctime (&now);
227 static char buf[255];
228 int n = strlen(progname);
230 strncpy(buf, progname, n);
233 strncpy(buf+n, ct+11, 8);
234 strcpy(buf+n+9, ": ");
240 name_to_widget (state *s, const char *name)
250 /* First try to load the Glade file from the current directory;
251 if there isn't one there, check the installed directory.
253 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
254 const char * const files[] = { GLADE_FILE_NAME,
255 GLADE_DIR "/" GLADE_FILE_NAME };
257 for (i = 0; i < countof (files); i++)
260 if (!stat (files[i], &st))
262 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
269 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
270 "\tfrom " GLADE_DIR "/ or current directory.\n",
274 # undef GLADE_FILE_NAME
276 glade_xml_signal_autoconnect (s->glade_ui);
279 w = glade_xml_get_widget (s->glade_ui, name);
281 #else /* !HAVE_GTK2 */
283 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
286 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
288 #endif /* HAVE_GTK2 */
291 fprintf (stderr, "%s: no widget \"%s\"\n", blurb(), name);
296 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
297 Takes a scroller, viewport, or list as an argument.
300 ensure_selected_item_visible (GtkWidget *widget)
304 GtkTreeSelection *selection;
308 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
309 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
310 path = gtk_tree_path_new_first ();
312 path = gtk_tree_model_get_path (model, &iter);
314 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
316 gtk_tree_path_free (path);
318 #else /* !HAVE_GTK2 */
320 GtkScrolledWindow *scroller = 0;
322 GtkList *list_widget = 0;
326 GtkWidget *selected = 0;
329 gint parent_h, child_y, child_h, children_h, ignore;
330 double ratio_t, ratio_b;
332 if (GTK_IS_SCROLLED_WINDOW (widget))
334 scroller = GTK_SCROLLED_WINDOW (widget);
335 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
336 list_widget = GTK_LIST (GTK_BIN(vp)->child);
338 else if (GTK_IS_VIEWPORT (widget))
340 vp = GTK_VIEWPORT (widget);
341 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
342 list_widget = GTK_LIST (GTK_BIN(vp)->child);
344 else if (GTK_IS_LIST (widget))
346 list_widget = GTK_LIST (widget);
347 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
348 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
353 slist = list_widget->selection;
354 selected = (slist ? GTK_WIDGET (slist->data) : 0);
358 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
360 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
361 kids; kids = kids->next)
364 adj = gtk_scrolled_window_get_vadjustment (scroller);
366 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
367 &ignore, &ignore, &ignore, &parent_h, &ignore);
368 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
369 &ignore, &child_y, &ignore, &child_h, &ignore);
370 children_h = nkids * child_h;
372 ratio_t = ((double) child_y) / ((double) children_h);
373 ratio_b = ((double) child_y + child_h) / ((double) children_h);
375 if (adj->upper == 0.0) /* no items in list */
378 if (ratio_t < (adj->value / adj->upper) ||
379 ratio_b > ((adj->value + adj->page_size) / adj->upper))
382 int slop = parent_h * 0.75; /* how much to overshoot by */
384 if (ratio_t < (adj->value / adj->upper))
386 double ratio_w = ((double) parent_h) / ((double) children_h);
387 double ratio_l = (ratio_b - ratio_t);
388 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
391 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
393 target = ratio_t * adj->upper;
397 if (target > adj->upper - adj->page_size)
398 target = adj->upper - adj->page_size;
402 gtk_adjustment_set_value (adj, target);
404 #endif /* !HAVE_GTK2 */
408 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
410 GtkWidget *shell = GTK_WIDGET (user_data);
411 while (shell->parent)
412 shell = shell->parent;
413 gtk_widget_destroy (GTK_WIDGET (shell));
417 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
419 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
421 restart_menu_cb (widget, user_data);
422 warning_dialog_dismiss_cb (widget, user_data);
426 warning_dialog (GtkWidget *parent, const char *message,
427 Boolean restart_button_p, int center)
429 char *msg = strdup (message);
432 GtkWidget *dialog = gtk_dialog_new ();
433 GtkWidget *label = 0;
435 GtkWidget *cancel = 0;
438 while (parent && !parent->window)
439 parent = parent->parent;
442 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
444 fprintf (stderr, "%s: too early for dialog?\n", progname);
452 char *s = strchr (head, '\n');
455 sprintf (name, "label%d", i++);
458 label = gtk_label_new (head);
460 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
461 #endif /* HAVE_GTK2 */
466 GTK_WIDGET (label)->style =
467 gtk_style_copy (GTK_WIDGET (label)->style);
468 GTK_WIDGET (label)->style->font =
469 gdk_font_load (get_string_resource("warning_dialog.headingFont",
471 gtk_widget_set_style (GTK_WIDGET (label),
472 GTK_WIDGET (label)->style);
474 #endif /* !HAVE_GTK2 */
476 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
477 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
478 label, TRUE, TRUE, 0);
479 gtk_widget_show (label);
490 label = gtk_label_new ("");
491 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
492 label, TRUE, TRUE, 0);
493 gtk_widget_show (label);
495 label = gtk_hbutton_box_new ();
496 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
497 label, TRUE, TRUE, 0);
500 if (restart_button_p)
502 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
503 gtk_container_add (GTK_CONTAINER (label), cancel);
506 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
507 gtk_container_add (GTK_CONTAINER (label), ok);
509 #else /* !HAVE_GTK2 */
511 ok = gtk_button_new_with_label ("OK");
512 gtk_container_add (GTK_CONTAINER (label), ok);
514 if (restart_button_p)
516 cancel = gtk_button_new_with_label ("Cancel");
517 gtk_container_add (GTK_CONTAINER (label), cancel);
520 #endif /* !HAVE_GTK2 */
522 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
523 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
524 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
525 STFU GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
526 gtk_widget_show (ok);
527 gtk_widget_grab_focus (ok);
531 STFU GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
532 gtk_widget_show (cancel);
534 gtk_widget_show (label);
535 gtk_widget_show (dialog);
537 if (restart_button_p)
539 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
540 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
542 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
543 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
548 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
549 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
553 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
554 GTK_WIDGET (parent)->window);
557 gtk_window_present (GTK_WINDOW (dialog));
558 #else /* !HAVE_GTK2 */
559 gdk_window_show (GTK_WIDGET (dialog)->window);
560 gdk_window_raise (GTK_WIDGET (dialog)->window);
561 #endif /* !HAVE_GTK2 */
568 run_cmd (state *s, Atom command, int arg)
573 flush_dialog_changes_and_save (s);
574 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
579 sprintf (buf, "Error:\n\n%s", err);
581 strcpy (buf, "Unknown error!");
582 warning_dialog (s->toplevel_widget, buf, False, 100);
589 run_hack (state *s, int list_elt, Bool report_errors_p)
592 if (list_elt < 0) return;
593 hack_number = s->list_elt_to_hack_number[list_elt];
595 flush_dialog_changes_and_save (s);
596 schedule_preview (s, 0);
598 run_cmd (s, XA_DEMO, hack_number + 1);
602 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
614 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
616 state *s = global_state_kludge; /* I hate C so much... */
617 flush_dialog_changes_and_save (s);
618 kill_preview_subproc (s);
623 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
625 state *s = (state *) data;
626 flush_dialog_changes_and_save (s);
633 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
636 char *vers = strdup (screensaver_id + 4);
639 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
641 s = strchr (vers, ',');
645 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
646 non-ASCII characters aren't allowed in localizable string keys."
647 (I don't want to just use (c) instead of © because that doesn't
648 look as good in the plain-old default Latin1 "C" locale.)
651 sprintf(copy, ("Copyright \xC2\xA9 1991-2003 %s"), s);
652 #else /* !HAVE_GTK2 */
653 sprintf(copy, ("Copyright \251 1991-2003 %s"), s);
654 #endif /* !HAVE_GTK2 */
656 sprintf (msg, "%s\n\n%s", copy, desc);
658 /* I can't make gnome_about_new() work here -- it starts dying in
659 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
660 then this might be the thing to do:
664 const gchar *auth[] = { 0 };
665 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
667 gtk_widget_show (about);
669 #else / * GTK but not GNOME * /
673 GdkColormap *colormap;
674 GdkPixmap *gdkpixmap;
677 GtkWidget *dialog = gtk_dialog_new ();
678 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
679 GtkWidget *parent = GTK_WIDGET (menuitem);
680 while (parent->parent)
681 parent = parent->parent;
683 hbox = gtk_hbox_new (FALSE, 20);
684 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
685 hbox, TRUE, TRUE, 0);
687 colormap = gtk_widget_get_colormap (parent);
689 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
690 (gchar **) logo_180_xpm);
691 icon = gtk_pixmap_new (gdkpixmap, mask);
692 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
694 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
696 vbox = gtk_vbox_new (FALSE, 0);
697 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
699 label1 = gtk_label_new (vers);
700 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
701 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
702 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
705 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
706 GTK_WIDGET (label1)->style->font =
707 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
708 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
709 #endif /* HAVE_GTK2 */
711 label2 = gtk_label_new (msg);
712 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
713 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
714 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
717 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
718 GTK_WIDGET (label2)->style->font =
719 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
720 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
721 #endif /* HAVE_GTK2 */
723 hb = gtk_hbutton_box_new ();
725 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
729 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
730 #else /* !HAVE_GTK2 */
731 ok = gtk_button_new_with_label (_("OK"));
732 #endif /* !HAVE_GTK2 */
733 gtk_container_add (GTK_CONTAINER (hb), ok);
735 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
736 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
737 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
739 gtk_widget_show (hbox);
740 gtk_widget_show (icon);
741 gtk_widget_show (vbox);
742 gtk_widget_show (label1);
743 gtk_widget_show (label2);
744 gtk_widget_show (hb);
745 gtk_widget_show (ok);
746 gtk_widget_show (dialog);
748 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
749 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
751 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
752 GTK_WIDGET (parent)->window);
753 gdk_window_show (GTK_WIDGET (dialog)->window);
754 gdk_window_raise (GTK_WIDGET (dialog)->window);
760 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
762 state *s = global_state_kludge; /* I hate C so much... */
763 saver_preferences *p = &s->prefs;
766 if (!p->help_url || !*p->help_url)
768 warning_dialog (s->toplevel_widget,
770 "No Help URL has been specified.\n"), False, 100);
774 help_command = (char *) malloc (strlen (p->load_url_command) +
775 (strlen (p->help_url) * 2) + 20);
776 strcpy (help_command, "( ");
777 sprintf (help_command + strlen(help_command),
778 p->load_url_command, p->help_url, p->help_url);
779 strcat (help_command, " ) &");
780 system (help_command);
786 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
788 state *s = global_state_kludge; /* I hate C so much... */
789 run_cmd (s, XA_ACTIVATE, 0);
794 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
796 state *s = global_state_kludge; /* I hate C so much... */
797 run_cmd (s, XA_LOCK, 0);
802 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
804 state *s = global_state_kludge; /* I hate C so much... */
805 run_cmd (s, XA_EXIT, 0);
810 restart_menu_cb (GtkWidget *widget, gpointer user_data)
812 state *s = global_state_kludge; /* I hate C so much... */
813 flush_dialog_changes_and_save (s);
814 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
816 system ("xscreensaver -nosplash &");
818 await_xscreensaver (s);
822 await_xscreensaver (state *s)
826 Display *dpy = GDK_DISPLAY();
827 /* GtkWidget *dialog = 0;*/
830 while (!rversion && (--countdown > 0))
832 /* Check for the version of the running xscreensaver... */
833 server_xscreensaver_version (dpy, &rversion, 0, 0);
835 /* If it's not there yet, wait a second... */
840 /* if (dialog) gtk_widget_destroy (dialog);*/
849 /* Timed out, no screensaver running. */
852 Bool root_p = (geteuid () == 0);
856 "The xscreensaver daemon did not start up properly.\n"
862 __extension__ /* don't warn about "string length is greater than
863 the length ISO C89 compilers are required to
864 support" in the following expression... */
867 _("You are running as root. This usually means that xscreensaver\n"
868 "was unable to contact your X server because access control is\n"
869 "turned on. Try running this command:\n"
871 " xhost +localhost\n"
873 "and then selecting `File / Restart Daemon'.\n"
875 "Note that turning off access control will allow anyone logged\n"
876 "on to this machine to access your screen, which might be\n"
877 "considered a security problem. Please read the xscreensaver\n"
878 "manual and FAQ for more information.\n"
880 "You shouldn't run X as root. Instead, you should log in as a\n"
881 "normal user, and `su' as necessary."));
883 strcat (buf, _("Please check your $PATH and permissions."));
885 warning_dialog (s->toplevel_widget, buf, False, 1);
891 selected_list_element (state *s)
893 return s->_selected_list_element;
898 demo_write_init_file (state *s, saver_preferences *p)
902 /* #### try to figure out why shit keeps getting reordered... */
903 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
907 if (!write_init_file (p, s->short_version, False))
910 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
915 const char *f = init_file_name();
917 warning_dialog (s->toplevel_widget,
918 _("Error:\n\nCouldn't determine init file name!\n"),
922 char *b = (char *) malloc (strlen(f) + 1024);
923 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
924 warning_dialog (s->toplevel_widget, b, False, 100);
933 run_this_cb (GtkButton *button, gpointer user_data)
935 state *s = global_state_kludge; /* I hate C so much... */
936 int list_elt = selected_list_element (s);
937 if (list_elt < 0) return;
938 if (!flush_dialog_changes_and_save (s))
939 run_hack (s, list_elt, True);
944 manual_cb (GtkButton *button, gpointer user_data)
946 state *s = global_state_kludge; /* I hate C so much... */
947 saver_preferences *p = &s->prefs;
948 GtkWidget *list_widget = name_to_widget (s, "list");
949 int list_elt = selected_list_element (s);
951 char *name, *name2, *cmd, *str;
952 if (list_elt < 0) return;
953 hack_number = s->list_elt_to_hack_number[list_elt];
955 flush_dialog_changes_and_save (s);
956 ensure_selected_item_visible (list_widget);
958 name = strdup (p->screenhacks[hack_number]->command);
960 while (isspace (*name2)) name2++;
962 while (*str && !isspace (*str)) str++;
964 str = strrchr (name2, '/');
965 if (str) name = str+1;
967 cmd = get_string_resource ("manualCommand", "ManualCommand");
970 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
972 sprintf (cmd2 + strlen (cmd2),
974 name2, name2, name2, name2);
975 strcat (cmd2, " ) &");
981 warning_dialog (GTK_WIDGET (button),
982 _("Error:\n\nno `manualCommand' resource set."),
991 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
993 GtkWidget *parent = name_to_widget (s, "scroller");
994 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
998 GtkTreeSelection *selection;
999 #endif /* HAVE_GTK2 */
1001 if (!was) gtk_widget_set_sensitive (parent, True);
1003 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1004 STFU g_assert (model);
1005 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
1006 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1007 gtk_tree_selection_select_iter (selection, &iter);
1008 #else /* !HAVE_GTK2 */
1009 gtk_list_select_item (GTK_LIST (list), list_elt);
1010 #endif /* !HAVE_GTK2 */
1011 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1012 if (!was) gtk_widget_set_sensitive (parent, False);
1017 run_next_cb (GtkButton *button, gpointer user_data)
1019 state *s = global_state_kludge; /* I hate C so much... */
1020 /* saver_preferences *p = &s->prefs; */
1021 Bool ops = s->preview_suppressed_p;
1023 GtkWidget *list_widget = name_to_widget (s, "list");
1024 int list_elt = selected_list_element (s);
1031 if (list_elt >= s->list_count)
1034 s->preview_suppressed_p = True;
1036 flush_dialog_changes_and_save (s);
1037 force_list_select_item (s, list_widget, list_elt, True);
1038 populate_demo_window (s, list_elt);
1039 run_hack (s, list_elt, False);
1041 s->preview_suppressed_p = ops;
1046 run_prev_cb (GtkButton *button, gpointer user_data)
1048 state *s = global_state_kludge; /* I hate C so much... */
1049 /* saver_preferences *p = &s->prefs; */
1050 Bool ops = s->preview_suppressed_p;
1052 GtkWidget *list_widget = name_to_widget (s, "list");
1053 int list_elt = selected_list_element (s);
1056 list_elt = s->list_count - 1;
1061 list_elt = s->list_count - 1;
1063 s->preview_suppressed_p = True;
1065 flush_dialog_changes_and_save (s);
1066 force_list_select_item (s, list_widget, list_elt, True);
1067 populate_demo_window (s, list_elt);
1068 run_hack (s, list_elt, False);
1070 s->preview_suppressed_p = ops;
1074 /* Writes the given settings into prefs.
1075 Returns true if there was a change, False otherwise.
1076 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1079 flush_changes (state *s,
1082 const char *command,
1085 saver_preferences *p = &s->prefs;
1086 Bool changed = False;
1089 if (list_elt < 0 || list_elt >= s->list_count)
1092 hack_number = s->list_elt_to_hack_number[list_elt];
1093 hack = p->screenhacks[hack_number];
1095 if (enabled_p != -1 &&
1096 enabled_p != hack->enabled_p)
1098 hack->enabled_p = enabled_p;
1101 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1102 blurb(), hack->name, enabled_p);
1107 if (!hack->command || !!strcmp (command, hack->command))
1109 if (hack->command) free (hack->command);
1110 hack->command = strdup (command);
1113 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1114 blurb(), hack->name, command);
1120 const char *ov = hack->visual;
1121 if (!ov || !*ov) ov = "any";
1122 if (!*visual) visual = "any";
1123 if (!!strcasecmp (visual, ov))
1125 if (hack->visual) free (hack->visual);
1126 hack->visual = strdup (visual);
1129 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1130 blurb(), hack->name, visual);
1138 /* Helper for the text fields that contain time specifications:
1139 this parses the text, and does error checking.
1142 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1147 if (!sec_p || strchr (line, ':'))
1148 value = parse_time ((char *) line, sec_p, True);
1152 if (sscanf (line, "%d%c", &value, &c) != 1)
1158 value *= 1000; /* Time measures in microseconds */
1164 "Unparsable time format: \"%s\"\n"),
1166 warning_dialog (s->toplevel_widget, b, False, 100);
1175 directory_p (const char *path)
1178 if (!path || !*path)
1180 else if (stat (path, &st))
1182 else if (!S_ISDIR (st.st_mode))
1189 normalize_directory (const char *path)
1193 if (!path || !*path) return 0;
1195 p2 = (char *) malloc (L + 2);
1197 if (p2[L-1] == '/') /* remove trailing slash */
1200 for (s = p2; s && *s; s++)
1203 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1204 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1207 while (s0 > p2 && s0[-1] != '/')
1217 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1218 strcpy (s, s+2), s--;
1219 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1223 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1224 while (s[0] == '/' && s[1] == '/')
1227 /* and strip trailing whitespace for good measure. */
1229 while (isspace(p2[L-1]))
1242 } FlushForeachClosure;
1245 flush_checkbox (GtkTreeModel *model,
1250 FlushForeachClosure *closure = data;
1253 gtk_tree_model_get (model, iter,
1254 COL_ENABLED, &checked,
1257 if (flush_changes (closure->s, closure->i,
1259 *closure->changed = True;
1263 /* don't remove row */
1267 #endif /* HAVE_GTK2 */
1269 /* Flush out any changes made in the main dialog window (where changes
1270 take place immediately: clicking on a checkbox causes the init file
1271 to be written right away.)
1274 flush_dialog_changes_and_save (state *s)
1276 saver_preferences *p = &s->prefs;
1277 saver_preferences P2, *p2 = &P2;
1279 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1280 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1281 FlushForeachClosure closure;
1282 #else /* !HAVE_GTK2 */
1283 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1284 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1286 #endif /* !HAVE_GTK2 */
1288 Bool changed = False;
1291 if (s->saving_p) return False;
1296 /* Flush any checkbox changes in the list down into the prefs struct.
1300 closure.changed = &changed;
1302 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1304 #else /* !HAVE_GTK2 */
1306 for (i = 0; kids; kids = kids->next, i++)
1308 GtkWidget *line = GTK_WIDGET (kids->data);
1309 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1310 GtkWidget *line_check =
1311 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1313 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1315 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1318 #endif /* ~HAVE_GTK2 */
1320 /* Flush the non-hack-specific settings down into the prefs struct.
1323 # define SECONDS(FIELD,NAME) \
1324 w = name_to_widget (s, (NAME)); \
1325 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1327 # define MINUTES(FIELD,NAME) \
1328 w = name_to_widget (s, (NAME)); \
1329 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1331 # define CHECKBOX(FIELD,NAME) \
1332 w = name_to_widget (s, (NAME)); \
1333 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1335 # define PATHNAME(FIELD,NAME) \
1336 w = name_to_widget (s, (NAME)); \
1337 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1339 MINUTES (&p2->timeout, "timeout_spinbutton");
1340 MINUTES (&p2->cycle, "cycle_spinbutton");
1341 CHECKBOX (p2->lock_p, "lock_button");
1342 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1344 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1345 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1346 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1347 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1349 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1350 CHECKBOX (p2->grab_video_p, "grab_video_button");
1351 CHECKBOX (p2->random_image_p, "grab_image_button");
1352 PATHNAME (p2->image_directory, "image_text");
1354 CHECKBOX (p2->verbose_p, "verbose_button");
1355 CHECKBOX (p2->capture_stderr_p, "capture_button");
1356 CHECKBOX (p2->splash_p, "splash_button");
1358 CHECKBOX (p2->install_cmap_p, "install_button");
1359 CHECKBOX (p2->fade_p, "fade_button");
1360 CHECKBOX (p2->unfade_p, "unfade_button");
1361 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1368 /* Warn if the image directory doesn't exist.
1370 if (p2->image_directory &&
1371 *p2->image_directory &&
1372 !directory_p (p2->image_directory))
1375 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1376 p2->image_directory);
1377 warning_dialog (s->toplevel_widget, b, False, 100);
1381 /* Map the mode menu to `saver_mode' enum values. */
1383 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1384 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1385 GtkWidget *selected = gtk_menu_get_active (menu);
1386 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1387 int menu_elt = g_list_index (kids, (gpointer) selected);
1388 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1389 p2->mode = mode_menu_order[menu_elt];
1392 if (p2->mode == ONE_HACK)
1394 int list_elt = selected_list_element (s);
1395 p2->selected_hack = (list_elt >= 0
1396 ? s->list_elt_to_hack_number[list_elt]
1400 # define COPY(field, name) \
1401 if (p->field != p2->field) { \
1404 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1406 p->field = p2->field
1409 COPY(selected_hack, "selected_hack");
1411 COPY(timeout, "timeout");
1412 COPY(cycle, "cycle");
1413 COPY(lock_p, "lock_p");
1414 COPY(lock_timeout, "lock_timeout");
1416 COPY(dpms_enabled_p, "dpms_enabled_p");
1417 COPY(dpms_standby, "dpms_standby");
1418 COPY(dpms_suspend, "dpms_suspend");
1419 COPY(dpms_off, "dpms_off");
1421 COPY(verbose_p, "verbose_p");
1422 COPY(capture_stderr_p, "capture_stderr_p");
1423 COPY(splash_p, "splash_p");
1425 COPY(install_cmap_p, "install_cmap_p");
1426 COPY(fade_p, "fade_p");
1427 COPY(unfade_p, "unfade_p");
1428 COPY(fade_seconds, "fade_seconds");
1430 COPY(grab_desktop_p, "grab_desktop_p");
1431 COPY(grab_video_p, "grab_video_p");
1432 COPY(random_image_p, "random_image_p");
1436 if (!p->image_directory ||
1437 !p2->image_directory ||
1438 strcmp(p->image_directory, p2->image_directory))
1442 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1443 blurb(), p2->image_directory);
1445 if (p->image_directory && p->image_directory != p2->image_directory)
1446 free (p->image_directory);
1447 p->image_directory = p2->image_directory;
1448 p2->image_directory = 0;
1450 populate_prefs_page (s);
1454 Display *dpy = GDK_DISPLAY();
1455 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1456 sync_server_dpms_settings (dpy, enabled_p,
1457 p->dpms_standby / 1000,
1458 p->dpms_suspend / 1000,
1462 changed = demo_write_init_file (s, p);
1465 s->saving_p = False;
1470 /* Flush out any changes made in the popup dialog box (where changes
1471 take place only when the OK button is clicked.)
1474 flush_popup_changes_and_save (state *s)
1476 Bool changed = False;
1477 saver_preferences *p = &s->prefs;
1478 int list_elt = selected_list_element (s);
1480 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1481 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1483 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1484 const char *command = gtk_entry_get_text (cmd);
1489 if (s->saving_p) return False;
1495 if (maybe_reload_init_file (s) != 0)
1501 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1503 if (!strcasecmp (visual, "")) visual = "";
1504 else if (!strcasecmp (visual, "any")) visual = "";
1505 else if (!strcasecmp (visual, "default")) visual = "Default";
1506 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1507 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1508 else if (!strcasecmp (visual, "best")) visual = "Best";
1509 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1510 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1511 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1512 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1513 else if (!strcasecmp (visual, "color")) visual = "Color";
1514 else if (!strcasecmp (visual, "gl")) visual = "GL";
1515 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1516 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1517 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1518 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1519 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1520 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1521 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1522 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1523 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1526 gdk_beep (); /* unparsable */
1528 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1531 changed = flush_changes (s, list_elt, -1, command, visual);
1534 changed = demo_write_init_file (s, p);
1536 /* Do this to re-launch the hack if (and only if) the command line
1538 populate_demo_window (s, selected_list_element (s));
1542 s->saving_p = False;
1548 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1550 state *s = global_state_kludge; /* I hate C so much... */
1551 if (! s->initializing_p)
1553 s->initializing_p = True;
1554 flush_dialog_changes_and_save (s);
1555 s->initializing_p = False;
1560 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1562 pref_changed_cb (widget, user_data);
1566 /* Callback on menu items in the "mode" options menu.
1569 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1571 state *s = (state *) user_data;
1572 saver_preferences *p = &s->prefs;
1573 GtkWidget *list = name_to_widget (s, "list");
1576 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1578 saver_mode new_mode;
1579 int old_selected = p->selected_hack;
1583 if (menu_items->data == widget)
1586 menu_items = menu_items->next;
1588 if (!menu_items) abort();
1590 new_mode = mode_menu_order[menu_index];
1592 /* Keep the same list element displayed as before; except if we're
1593 switching *to* "one screensaver" mode from any other mode, scroll
1594 to and select "the one".
1597 if (new_mode == ONE_HACK)
1598 list_elt = (p->selected_hack >= 0
1599 ? s->hack_number_to_list_elt[p->selected_hack]
1603 list_elt = selected_list_element (s);
1606 saver_mode old_mode = p->mode;
1608 populate_demo_window (s, list_elt);
1609 force_list_select_item (s, list, list_elt, True);
1610 p->mode = old_mode; /* put it back, so the init file gets written */
1613 pref_changed_cb (widget, user_data);
1615 if (old_selected != p->selected_hack)
1616 abort(); /* dammit, not again... */
1621 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1622 gint page_num, gpointer user_data)
1624 state *s = global_state_kludge; /* I hate C so much... */
1625 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1627 /* If we're switching to page 0, schedule the current hack to be run.
1628 Otherwise, schedule it to stop. */
1630 populate_demo_window (s, selected_list_element (s));
1632 schedule_preview (s, 0);
1637 list_activated_cb (GtkTreeView *list,
1639 GtkTreeViewColumn *column,
1646 STFU g_return_if_fail (!gdk_pointer_is_grabbed ());
1648 str = gtk_tree_path_to_string (path);
1649 list_elt = strtol (str, NULL, 10);
1653 run_hack (s, list_elt, True);
1657 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1659 state *s = (state *)data;
1660 GtkTreeModel *model;
1666 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1669 path = gtk_tree_model_get_path (model, &iter);
1670 str = gtk_tree_path_to_string (path);
1671 list_elt = strtol (str, NULL, 10);
1673 gtk_tree_path_free (path);
1676 populate_demo_window (s, list_elt);
1677 flush_dialog_changes_and_save (s);
1680 #else /* !HAVE_GTK2 */
1682 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1683 list_select_cb that comes in
1684 *after* we've double-clicked.
1688 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1691 state *s = (state *) data;
1692 if (event->type == GDK_2BUTTON_PRESS)
1694 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1695 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1697 last_doubleclick_time = time ((time_t *) 0);
1700 run_hack (s, list_elt, True);
1708 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1710 state *s = (state *) data;
1711 time_t now = time ((time_t *) 0);
1713 if (now >= last_doubleclick_time + 2)
1715 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1716 populate_demo_window (s, list_elt);
1717 flush_dialog_changes_and_save (s);
1722 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1724 state *s = (state *) data;
1725 populate_demo_window (s, -1);
1726 flush_dialog_changes_and_save (s);
1729 #endif /* !HAVE_GTK2 */
1732 /* Called when the checkboxes that are in the left column of the
1733 scrolling list are clicked. This both populates the right pane
1734 (just as clicking on the label (really, listitem) does) and
1735 also syncs this checkbox with the right pane Enabled checkbox.
1740 GtkCellRendererToggle *toggle,
1742 #else /* !HAVE_GTK2 */
1744 #endif /* !HAVE_GTK2 */
1747 state *s = (state *) data;
1750 GtkScrolledWindow *scroller =
1751 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1752 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1753 GtkTreeModel *model = gtk_tree_view_get_model (list);
1754 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1757 #else /* !HAVE_GTK2 */
1758 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1759 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1761 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1762 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1763 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1764 #endif /* !HAVE_GTK2 */
1771 if (!gtk_tree_model_get_iter (model, &iter, path))
1773 g_warning ("bad path: %s", path_string);
1776 gtk_tree_path_free (path);
1778 gtk_tree_model_get (model, &iter,
1779 COL_ENABLED, &active,
1782 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1783 COL_ENABLED, !active,
1786 list_elt = strtol (path_string, NULL, 10);
1787 #else /* !HAVE_GTK2 */
1788 list_elt = gtk_list_child_position (list, line);
1789 #endif /* !HAVE_GTK2 */
1791 /* remember previous scroll position of the top of the list */
1792 adj = gtk_scrolled_window_get_vadjustment (scroller);
1793 scroll_top = adj->value;
1795 flush_dialog_changes_and_save (s);
1796 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1797 populate_demo_window (s, list_elt);
1799 /* restore the previous scroll position of the top of the list.
1800 this is weak, but I don't really know why it's moving... */
1801 gtk_adjustment_set_value (adj, scroll_top);
1807 GtkFileSelection *widget;
1808 } file_selection_data;
1813 store_image_directory (GtkWidget *button, gpointer user_data)
1815 file_selection_data *fsd = (file_selection_data *) user_data;
1816 state *s = fsd->state;
1817 GtkFileSelection *selector = fsd->widget;
1818 GtkWidget *top = s->toplevel_widget;
1819 saver_preferences *p = &s->prefs;
1820 const char *path = gtk_file_selection_get_filename (selector);
1822 if (p->image_directory && !strcmp(p->image_directory, path))
1823 return; /* no change */
1825 if (!directory_p (path))
1828 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1829 warning_dialog (GTK_WIDGET (top), b, False, 100);
1833 if (p->image_directory) free (p->image_directory);
1834 p->image_directory = normalize_directory (path);
1836 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1837 (p->image_directory ? p->image_directory : ""));
1838 demo_write_init_file (s, p);
1843 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1845 file_selection_data *fsd = (file_selection_data *) user_data;
1846 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1850 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1852 browse_image_dir_cancel (button, user_data);
1853 store_image_directory (button, user_data);
1857 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1859 browse_image_dir_cancel (widget, user_data);
1864 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1866 state *s = global_state_kludge; /* I hate C so much... */
1867 saver_preferences *p = &s->prefs;
1868 static file_selection_data *fsd = 0;
1870 GtkFileSelection *selector = GTK_FILE_SELECTION(
1871 gtk_file_selection_new ("Please select the image directory."));
1874 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1876 fsd->widget = selector;
1879 if (p->image_directory && *p->image_directory)
1880 gtk_file_selection_set_filename (selector, p->image_directory);
1882 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1883 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1885 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1886 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1888 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1889 GTK_SIGNAL_FUNC (browse_image_dir_close),
1892 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1894 gtk_window_set_modal (GTK_WINDOW (selector), True);
1895 gtk_widget_show (GTK_WIDGET (selector));
1900 settings_cb (GtkButton *button, gpointer user_data)
1902 state *s = global_state_kludge; /* I hate C so much... */
1903 int list_elt = selected_list_element (s);
1905 populate_demo_window (s, list_elt); /* reset the widget */
1906 populate_popup_window (s); /* create UI on popup window */
1907 gtk_widget_show (s->popup_widget);
1911 settings_sync_cmd_text (state *s)
1914 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1915 char *cmd_line = get_configurator_command_line (s->cdata);
1916 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1917 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1919 # endif /* HAVE_XML */
1923 settings_adv_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 settings_sync_cmd_text (s);
1930 gtk_notebook_set_page (notebook, 1);
1934 settings_std_cb (GtkButton *button, gpointer user_data)
1936 state *s = global_state_kludge; /* I hate C so much... */
1937 GtkNotebook *notebook =
1938 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1940 /* Re-create UI to reflect the in-progress command-line settings. */
1941 populate_popup_window (s);
1943 gtk_notebook_set_page (notebook, 0);
1947 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1948 gint page_num, gpointer user_data)
1950 state *s = global_state_kludge; /* I hate C so much... */
1951 GtkWidget *adv = name_to_widget (s, "adv_button");
1952 GtkWidget *std = name_to_widget (s, "std_button");
1956 gtk_widget_show (adv);
1957 gtk_widget_hide (std);
1959 else if (page_num == 1)
1961 gtk_widget_hide (adv);
1962 gtk_widget_show (std);
1971 settings_cancel_cb (GtkButton *button, gpointer user_data)
1973 state *s = global_state_kludge; /* I hate C so much... */
1974 gtk_widget_hide (s->popup_widget);
1978 settings_ok_cb (GtkButton *button, gpointer user_data)
1980 state *s = global_state_kludge; /* I hate C so much... */
1981 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1982 int page = gtk_notebook_get_current_page (notebook);
1985 /* Regenerate the command-line from the widget contents before saving.
1986 But don't do this if we're looking at the command-line page already,
1987 or we will blow away what they typed... */
1988 settings_sync_cmd_text (s);
1990 flush_popup_changes_and_save (s);
1991 gtk_widget_hide (s->popup_widget);
1995 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1997 state *s = (state *) data;
1998 settings_cancel_cb (0, (gpointer) s);
2004 /* Populating the various widgets
2008 /* Returns the number of the last hack run by the server.
2011 server_current_hack (void)
2015 unsigned long nitems, bytesafter;
2017 Display *dpy = GDK_DISPLAY();
2018 int hack_number = -1;
2020 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2021 XA_SCREENSAVER_STATUS,
2022 0, 3, False, XA_INTEGER,
2023 &type, &format, &nitems, &bytesafter,
2024 (unsigned char **) &data)
2026 && type == XA_INTEGER
2029 hack_number = (int) data[2] - 1;
2031 if (data) free (data);
2037 /* Finds the number of the last hack to run, and makes that item be
2038 selected by default.
2041 scroll_to_current_hack (state *s)
2043 saver_preferences *p = &s->prefs;
2046 if (p->mode == ONE_HACK)
2047 hack_number = p->selected_hack;
2049 hack_number = server_current_hack ();
2051 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2053 int list_elt = s->hack_number_to_list_elt[hack_number];
2054 GtkWidget *list = name_to_widget (s, "list");
2055 force_list_select_item (s, list, list_elt, True);
2056 populate_demo_window (s, list_elt);
2062 on_path_p (const char *program)
2066 char *cmd = strdup (program);
2067 char *token = strchr (cmd, ' ');
2071 if (token) *token = 0;
2074 if (strchr (cmd, '/'))
2076 result = (0 == stat (cmd, &st));
2080 path = getenv("PATH");
2081 if (!path || !*path)
2085 path = strdup (path);
2086 token = strtok (path, ":");
2090 char *p2 = (char *) malloc (strlen (token) + L + 3);
2094 result = (0 == stat (p2, &st));
2097 token = strtok (0, ":");
2102 if (path) free (path);
2108 populate_hack_list (state *s)
2111 saver_preferences *p = &s->prefs;
2112 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2113 GtkListStore *model;
2114 GtkTreeSelection *selection;
2115 GtkCellRenderer *ren;
2119 g_object_get (G_OBJECT (list),
2124 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2125 g_object_set (G_OBJECT (list), "model", model, NULL);
2126 g_object_unref (model);
2128 ren = gtk_cell_renderer_toggle_new ();
2129 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2131 "active", COL_ENABLED,
2134 g_signal_connect (ren, "toggled",
2135 G_CALLBACK (list_checkbox_cb),
2138 ren = gtk_cell_renderer_text_new ();
2139 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2140 _("Screen Saver"), ren,
2144 g_signal_connect_after (list, "row_activated",
2145 G_CALLBACK (list_activated_cb),
2148 selection = gtk_tree_view_get_selection (list);
2149 g_signal_connect (selection, "changed",
2150 G_CALLBACK (list_select_changed_cb),
2155 for (i = 0; i < s->list_count; i++)
2157 int hack_number = s->list_elt_to_hack_number[i];
2158 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2160 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2162 if (!hack) continue;
2164 /* If we're to suppress uninstalled hacks, check $PATH now. */
2165 if (p->ignore_uninstalled_p && !available_p)
2168 pretty_name = (hack->name
2169 ? strdup (hack->name)
2170 : make_hack_name (hack->command));
2174 /* Make the text foreground be the color of insensitive widgets
2175 (but don't actually make it be insensitive, since we still
2176 want to be able to click on it.)
2178 GtkStyle *style = GTK_WIDGET (list)->style;
2179 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2180 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2181 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2183 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2184 /* " background=\"#%02X%02X%02X\"" */
2186 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2187 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2193 gtk_list_store_append (model, &iter);
2194 gtk_list_store_set (model, &iter,
2195 COL_ENABLED, hack->enabled_p,
2196 COL_NAME, pretty_name,
2201 #else /* !HAVE_GTK2 */
2203 saver_preferences *p = &s->prefs;
2204 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2206 for (i = 0; i < s->list_count; i++)
2208 int hack_number = s->list_elt_to_hack_number[i];
2209 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2211 /* A GtkList must contain only GtkListItems, but those can contain
2212 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2213 and a Label. We handle single and double click events on the
2214 line itself, for clicking on the text, but the interior checkbox
2215 also handles its own events.
2218 GtkWidget *line_hbox;
2219 GtkWidget *line_check;
2220 GtkWidget *line_label;
2222 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2224 if (!hack) continue;
2226 /* If we're to suppress uninstalled hacks, check $PATH now. */
2227 if (p->ignore_uninstalled_p && !available_p)
2230 pretty_name = (hack->name
2231 ? strdup (hack->name)
2232 : make_hack_name (hack->command));
2234 line = gtk_list_item_new ();
2235 line_hbox = gtk_hbox_new (FALSE, 0);
2236 line_check = gtk_check_button_new ();
2237 line_label = gtk_label_new (pretty_name);
2239 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2240 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2241 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2243 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2245 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2247 gtk_widget_show (line_check);
2248 gtk_widget_show (line_label);
2249 gtk_widget_show (line_hbox);
2250 gtk_widget_show (line);
2254 gtk_container_add (GTK_CONTAINER (list), line);
2255 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2256 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2259 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2260 GTK_SIGNAL_FUNC (list_checkbox_cb),
2263 gtk_widget_show (line);
2267 /* Make the widget be colored like insensitive widgets
2268 (but don't actually make it be insensitive, since we
2269 still want to be able to click on it.)
2271 GtkRcStyle *rc_style;
2274 gtk_widget_realize (GTK_WIDGET (line_label));
2276 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2277 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2279 rc_style = gtk_rc_style_new ();
2280 rc_style->fg[GTK_STATE_NORMAL] = fg;
2281 rc_style->bg[GTK_STATE_NORMAL] = bg;
2282 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2284 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2285 gtk_rc_style_unref (rc_style);
2289 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2290 GTK_SIGNAL_FUNC (list_select_cb),
2292 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2293 GTK_SIGNAL_FUNC (list_unselect_cb),
2295 #endif /* !HAVE_GTK2 */
2299 update_list_sensitivity (state *s)
2301 saver_preferences *p = &s->prefs;
2302 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2303 Bool checkable = (p->mode == RANDOM_HACKS);
2304 Bool blankable = (p->mode != DONT_BLANK);
2307 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2308 GtkWidget *use = name_to_widget (s, "use_col_frame");
2309 #endif /* HAVE_GTK2 */
2310 GtkWidget *scroller = name_to_widget (s, "scroller");
2311 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2312 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2315 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2316 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2317 #else /* !HAVE_GTK2 */
2318 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2319 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2321 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2322 #endif /* !HAVE_GTK2 */
2323 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2324 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2326 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2329 gtk_tree_view_column_set_visible (use, checkable);
2330 #else /* !HAVE_GTK2 */
2332 gtk_widget_show (use); /* the "Use" column header */
2334 gtk_widget_hide (use);
2338 GtkBin *line = GTK_BIN (kids->data);
2339 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2340 GtkWidget *line_check =
2341 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2344 gtk_widget_show (line_check);
2346 gtk_widget_hide (line_check);
2350 #endif /* !HAVE_GTK2 */
2355 populate_prefs_page (state *s)
2357 saver_preferences *p = &s->prefs;
2359 Bool can_lock_p = True;
2361 /* Disable all the "lock" controls if locking support was not provided
2362 at compile-time, or if running on MacOS. */
2363 # if defined(NO_LOCKING) || defined(__APPLE__)
2368 /* The file supports timeouts of less than a minute, but the GUI does
2369 not, so throttle the values to be at least one minute (since "0" is
2370 a bad rounding choice...)
2372 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2375 THROTTLE (passwd_timeout);
2378 # define FMT_MINUTES(NAME,N) \
2379 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2381 # define FMT_SECONDS(NAME,N) \
2382 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2384 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2385 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2386 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2387 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2388 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2389 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2390 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2395 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2396 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2399 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2400 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2401 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2402 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2403 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2404 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2405 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2406 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2407 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2408 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2409 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2411 # undef TOGGLE_ACTIVE
2413 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2414 (p->image_directory ? p->image_directory : ""));
2415 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2417 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2420 /* Map the `saver_mode' enum to mode menu to values. */
2422 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2425 for (i = 0; i < countof(mode_menu_order); i++)
2426 if (mode_menu_order[i] == p->mode)
2428 gtk_option_menu_set_history (opt, i);
2429 update_list_sensitivity (s);
2433 Bool found_any_writable_cells = False;
2434 Bool dpms_supported = False;
2436 Display *dpy = GDK_DISPLAY();
2437 int nscreens = ScreenCount(dpy);
2439 for (i = 0; i < nscreens; i++)
2441 Screen *s = ScreenOfDisplay (dpy, i);
2442 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2444 found_any_writable_cells = True;
2449 #ifdef HAVE_XF86VMODE_GAMMA
2450 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2453 #ifdef HAVE_DPMS_EXTENSION
2455 int op = 0, event = 0, error = 0;
2456 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2457 dpms_supported = True;
2459 #endif /* HAVE_DPMS_EXTENSION */
2462 # define SENSITIZE(NAME,SENSITIVEP) \
2463 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2465 /* Blanking and Locking
2467 SENSITIZE ("lock_button", can_lock_p);
2468 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2469 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2473 SENSITIZE ("dpms_frame", dpms_supported);
2474 SENSITIZE ("dpms_button", dpms_supported);
2475 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2476 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2477 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2478 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2479 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2480 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2481 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2482 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2483 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2487 SENSITIZE ("cmap_frame", found_any_writable_cells);
2488 SENSITIZE ("install_button", found_any_writable_cells);
2489 SENSITIZE ("fade_button", found_any_writable_cells);
2490 SENSITIZE ("unfade_button", found_any_writable_cells);
2492 SENSITIZE ("fade_label", (found_any_writable_cells &&
2493 (p->fade_p || p->unfade_p)));
2494 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2495 (p->fade_p || p->unfade_p)));
2503 populate_popup_window (state *s)
2505 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2506 char *doc_string = 0;
2508 /* #### not in Gtk 1.2
2509 gtk_label_set_selectable (doc);
2515 free_conf_data (s->cdata);
2520 saver_preferences *p = &s->prefs;
2521 int list_elt = selected_list_element (s);
2522 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2523 ? s->list_elt_to_hack_number[list_elt]
2525 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2528 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2529 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2530 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2531 s->cdata = load_configurator (cmd_line, s->debug_p);
2532 if (s->cdata && s->cdata->widget)
2533 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2538 doc_string = (s->cdata
2539 ? s->cdata->description
2541 # else /* !HAVE_XML */
2542 doc_string = _("Descriptions not available: no XML support compiled in.");
2543 # endif /* !HAVE_XML */
2545 gtk_label_set_text (doc, (doc_string
2547 : _("No description available.")));
2552 sensitize_demo_widgets (state *s, Bool sensitive_p)
2554 const char *names1[] = { "demo", "settings" };
2555 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2556 "visual", "visual_combo" };
2558 for (i = 0; i < countof(names1); i++)
2560 GtkWidget *w = name_to_widget (s, names1[i]);
2561 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2563 for (i = 0; i < countof(names2); i++)
2565 GtkWidget *w = name_to_widget (s, names2[i]);
2566 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2571 /* Even though we've given these text fields a maximum number of characters,
2572 their default size is still about 30 characters wide -- so measure out
2573 a string in their font, and resize them to just fit that.
2576 fix_text_entry_sizes (state *s)
2580 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2581 const char * const spinbuttons[] = {
2582 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2583 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2584 "dpms_off_spinbutton",
2585 "-fade_spinbutton" };
2589 for (i = 0; i < countof(spinbuttons); i++)
2591 const char *n = spinbuttons[i];
2593 while (*n == '-') n++, cols--;
2594 w = GTK_WIDGET (name_to_widget (s, n));
2595 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2596 gtk_widget_set_usize (w, width, -2);
2599 /* Now fix the width of the combo box.
2601 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2602 w = GTK_COMBO (w)->entry;
2603 width = gdk_string_width (w->style->font, "PseudoColor___");
2604 gtk_widget_set_usize (w, width, -2);
2606 /* Now fix the width of the file entry text.
2608 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2609 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2610 gtk_widget_set_usize (w, width, -2);
2612 /* Now fix the width of the command line text.
2614 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2615 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2616 gtk_widget_set_usize (w, width, -2);
2620 /* Now fix the height of the list widget:
2621 make it default to being around 10 text-lines high instead of 4.
2623 w = GTK_WIDGET (name_to_widget (s, "list"));
2627 int leading = 3; /* approximate is ok... */
2631 PangoFontMetrics *pain =
2632 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2633 w->style->font_desc,
2634 gtk_get_default_language ());
2635 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2636 pango_font_metrics_get_descent (pain));
2637 #else /* !HAVE_GTK2 */
2638 height = w->style->font->ascent + w->style->font->descent;
2639 #endif /* !HAVE_GTK2 */
2643 height += border * 2;
2644 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2645 gtk_widget_set_usize (w, -2, height);
2652 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2655 static char *up_arrow_xpm[] = {
2678 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2679 the end of the array (Gtk 1.2.5.) */
2680 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2681 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2684 static char *down_arrow_xpm[] = {
2707 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2708 the end of the array (Gtk 1.2.5.) */
2709 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2710 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2714 pixmapify_button (state *s, int down_p)
2718 GtkWidget *pixmapwid;
2722 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2723 style = gtk_widget_get_style (w);
2725 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2726 &style->bg[GTK_STATE_NORMAL],
2728 ? (gchar **) down_arrow_xpm
2729 : (gchar **) up_arrow_xpm));
2730 pixmapwid = gtk_pixmap_new (pixmap, mask);
2731 gtk_widget_show (pixmapwid);
2732 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2733 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2737 map_next_button_cb (GtkWidget *w, gpointer user_data)
2739 state *s = (state *) user_data;
2740 pixmapify_button (s, 1);
2744 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2746 state *s = (state *) user_data;
2747 pixmapify_button (s, 0);
2749 #endif /* !HAVE_GTK2 */
2752 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2756 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2757 GtkAllocation *allocation,
2761 GtkWidgetAuxInfo *aux_info;
2763 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2765 aux_info->width = allocation->width;
2766 aux_info->height = -2;
2770 gtk_widget_size_request (label, &req);
2774 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2777 eschew_gtk_lossage (GtkLabel *label)
2779 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2780 aux_info->width = GTK_WIDGET (label)->allocation.width;
2781 aux_info->height = -2;
2785 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2787 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2788 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2791 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2793 gtk_widget_queue_resize (GTK_WIDGET (label));
2798 populate_demo_window (state *s, int list_elt)
2800 saver_preferences *p = &s->prefs;
2803 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2804 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2805 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2806 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2807 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2809 if (p->mode == BLANK_ONLY)
2812 pretty_name = strdup (_("Blank Screen"));
2813 schedule_preview (s, 0);
2815 else if (p->mode == DONT_BLANK)
2818 pretty_name = strdup (_("Screen Saver Disabled"));
2819 schedule_preview (s, 0);
2823 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2824 ? s->list_elt_to_hack_number[list_elt]
2826 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2830 ? strdup (hack->name)
2831 : make_hack_name (hack->command))
2835 schedule_preview (s, hack->command);
2837 schedule_preview (s, 0);
2841 pretty_name = strdup (_("Preview"));
2843 gtk_frame_set_label (frame1, pretty_name);
2844 gtk_frame_set_label (frame2, pretty_name);
2846 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2847 gtk_entry_set_position (cmd, 0);
2851 sprintf (title, "%s: %.100s Settings",
2852 progclass, (pretty_name ? pretty_name : "???"));
2853 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2856 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2858 ? (hack->visual && *hack->visual
2863 sensitize_demo_widgets (s, (hack ? True : False));
2865 if (pretty_name) free (pretty_name);
2867 ensure_selected_item_visible (list);
2869 s->_selected_list_element = list_elt;
2874 widget_deleter (GtkWidget *widget, gpointer data)
2876 /* #### Well, I want to destroy these widgets, but if I do that, they get
2877 referenced again, and eventually I get a SEGV. So instead of
2878 destroying them, I'll just hide them, and leak a bunch of memory
2879 every time the disk file changes. Go go go Gtk!
2881 #### Ok, that's a lie, I get a crash even if I just hide the widget
2882 and don't ever delete it. Fuck!
2885 gtk_widget_destroy (widget);
2887 gtk_widget_hide (widget);
2892 static char **sort_hack_cmp_names_kludge;
2894 sort_hack_cmp (const void *a, const void *b)
2900 int aa = *(int *) a;
2901 int bb = *(int *) b;
2902 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
2903 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
2904 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
2910 initialize_sort_map (state *s)
2912 saver_preferences *p = &s->prefs;
2915 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2916 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2917 if (s->hacks_available_p) free (s->hacks_available_p);
2919 s->list_elt_to_hack_number = (int *)
2920 calloc (sizeof(int), p->screenhacks_count + 1);
2921 s->hack_number_to_list_elt = (int *)
2922 calloc (sizeof(int), p->screenhacks_count + 1);
2923 s->hacks_available_p = (Bool *)
2924 calloc (sizeof(Bool), p->screenhacks_count + 1);
2926 /* Check which hacks actually exist on $PATH
2928 for (i = 0; i < p->screenhacks_count; i++)
2930 screenhack *hack = p->screenhacks[i];
2931 s->hacks_available_p[i] = on_path_p (hack->command);
2934 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
2938 for (i = 0; i < p->screenhacks_count; i++)
2940 if (!p->ignore_uninstalled_p ||
2941 s->hacks_available_p[i])
2942 s->list_elt_to_hack_number[j++] = i;
2946 for (; j < p->screenhacks_count; j++)
2947 s->list_elt_to_hack_number[j] = -1;
2950 /* Generate list of sortable names (once)
2952 sort_hack_cmp_names_kludge = (char **)
2953 calloc (sizeof(char *), p->screenhacks_count);
2954 for (i = 0; i < p->screenhacks_count; i++)
2956 screenhack *hack = p->screenhacks[i];
2957 char *name = (hack->name && *hack->name
2958 ? strdup (hack->name)
2959 : make_hack_name (hack->command));
2961 for (str = name; *str; str++)
2962 *str = tolower(*str);
2963 sort_hack_cmp_names_kludge[i] = name;
2966 /* Sort list->hack map alphabetically
2968 qsort (s->list_elt_to_hack_number,
2969 p->screenhacks_count,
2970 sizeof(*s->list_elt_to_hack_number),
2975 for (i = 0; i < p->screenhacks_count; i++)
2976 free (sort_hack_cmp_names_kludge[i]);
2977 free (sort_hack_cmp_names_kludge);
2978 sort_hack_cmp_names_kludge = 0;
2980 /* Build inverse table */
2981 for (i = 0; i < p->screenhacks_count; i++)
2982 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2987 maybe_reload_init_file (state *s)
2989 saver_preferences *p = &s->prefs;
2992 static Bool reentrant_lock = False;
2993 if (reentrant_lock) return 0;
2994 reentrant_lock = True;
2996 if (init_file_changed_p (p))
2998 const char *f = init_file_name();
3003 if (!f || !*f) return 0;
3004 b = (char *) malloc (strlen(f) + 1024);
3007 "file \"%s\" has changed, reloading.\n"),
3009 warning_dialog (s->toplevel_widget, b, False, 100);
3013 initialize_sort_map (s);
3015 list_elt = selected_list_element (s);
3016 list = name_to_widget (s, "list");
3017 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3018 populate_hack_list (s);
3019 force_list_select_item (s, list, list_elt, True);
3020 populate_prefs_page (s);
3021 populate_demo_window (s, list_elt);
3022 ensure_selected_item_visible (list);
3027 reentrant_lock = False;
3033 /* Making the preview window have the right X visual (so that GL works.)
3036 static Visual *get_best_gl_visual (state *);
3039 x_visual_to_gdk_visual (Visual *xv)
3041 GList *gvs = gdk_list_visuals();
3042 if (!xv) return gdk_visual_get_system();
3043 for (; gvs; gvs = gvs->next)
3045 GdkVisual *gv = (GdkVisual *) gvs->data;
3046 if (xv == GDK_VISUAL_XVISUAL (gv))
3049 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3050 blurb(), (unsigned long) xv->visualid);
3055 clear_preview_window (state *s)
3060 if (!s->toplevel_widget) return; /* very early */
3061 p = name_to_widget (s, "preview");
3064 if (!window) return;
3066 /* Flush the widget background down into the window, in case a subproc
3068 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3069 gdk_window_clear (window);
3072 int list_elt = selected_list_element (s);
3073 int hack_number = (list_elt >= 0
3074 ? s->list_elt_to_hack_number[list_elt]
3076 Bool available_p = (hack_number >= 0
3077 ? s->hacks_available_p [hack_number]
3080 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3081 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3082 (s->running_preview_error_p
3083 ? (available_p ? 1 : 2)
3085 #else /* !HAVE_GTK2 */
3086 if (s->running_preview_error_p)
3088 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3089 const char * const lines2[] = { N_("Not"), N_("Installed") };
3090 int nlines = countof(lines1);
3091 int lh = p->style->font->ascent + p->style->font->descent;
3095 const char * const *lines = (available_p ? lines1 : lines2);
3097 gdk_window_get_size (window, &w, &h);
3098 y = (h - (lh * nlines)) / 2;
3099 y += p->style->font->ascent;
3100 for (i = 0; i < nlines; i++)
3102 int sw = gdk_string_width (p->style->font, _(lines[i]));
3103 int x = (w - sw) / 2;
3104 gdk_draw_string (window, p->style->font,
3105 p->style->fg_gc[GTK_STATE_NORMAL],
3110 #endif /* !HAVE_GTK2 */
3118 fix_preview_visual (state *s)
3120 GtkWidget *widget = name_to_widget (s, "preview");
3121 Visual *xvisual = get_best_gl_visual (s);
3122 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3123 GdkVisual *dvisual = gdk_visual_get_system();
3124 GdkColormap *cmap = (visual == dvisual
3125 ? gdk_colormap_get_system ()
3126 : gdk_colormap_new (visual, False));
3129 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3130 (visual == dvisual ? "default" : "non-default"),
3131 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3133 if (!GTK_WIDGET_REALIZED (widget) ||
3134 gtk_widget_get_visual (widget) != visual)
3136 gtk_widget_unrealize (widget);
3137 gtk_widget_set_visual (widget, visual);
3138 gtk_widget_set_colormap (widget, cmap);
3139 gtk_widget_realize (widget);
3142 /* Set the Widget colors to be white-on-black. */
3144 GdkWindow *window = widget->window;
3145 GtkStyle *style = gtk_style_copy (widget->style);
3146 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3147 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3148 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3149 GdkGC *fgc = gdk_gc_new(window);
3150 GdkGC *bgc = gdk_gc_new(window);
3151 if (!gdk_color_white (cmap, fg)) abort();
3152 if (!gdk_color_black (cmap, bg)) abort();
3153 gdk_gc_set_foreground (fgc, fg);
3154 gdk_gc_set_background (fgc, bg);
3155 gdk_gc_set_foreground (bgc, bg);
3156 gdk_gc_set_background (bgc, fg);
3157 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3158 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3159 gtk_widget_set_style (widget, style);
3161 /* For debugging purposes, put a title on the window (so that
3162 it can be easily found in the output of "xwininfo -tree".)
3164 gdk_window_set_title (window, "Preview");
3167 gtk_widget_show (widget);
3175 subproc_pretty_name (state *s)
3177 if (s->running_preview_cmd)
3179 char *ps = strdup (s->running_preview_cmd);
3180 char *ss = strchr (ps, ' ');
3182 ss = strrchr (ps, '/');
3193 return strdup ("???");
3198 reap_zombies (state *s)
3200 int wait_status = 0;
3202 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3206 if (pid == s->running_preview_pid)
3208 char *ss = subproc_pretty_name (s);
3209 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3210 (unsigned long) pid, ss);
3214 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3215 (unsigned long) pid);
3221 /* Mostly lifted from driver/subprocs.c */
3223 get_best_gl_visual (state *s)
3225 Display *dpy = GDK_DISPLAY();
3234 av[ac++] = "xscreensaver-gl-helper";
3239 perror ("error creating pipe:");
3246 switch ((int) (forked = fork ()))
3250 sprintf (buf, "%s: couldn't fork", blurb());
3258 close (in); /* don't need this one */
3259 close (ConnectionNumber (dpy)); /* close display fd */
3261 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3263 perror ("could not dup() a new stdout:");
3267 execvp (av[0], av); /* shouldn't return. */
3269 if (errno != ENOENT)
3271 /* Ignore "no such file or directory" errors, unless verbose.
3272 Issue all other exec errors, though. */
3273 sprintf (buf, "%s: running %s", blurb(), av[0]);
3277 /* Note that one must use _exit() instead of exit() in procs forked
3278 off of Gtk programs -- Gtk installs an atexit handler that has a
3279 copy of the X connection (which we've already closed, for safety.)
3280 If one uses exit() instead of _exit(), then one sometimes gets a
3281 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3283 _exit (1); /* exits fork */
3289 int wait_status = 0;
3291 FILE *f = fdopen (in, "r");
3295 close (out); /* don't need this one */
3298 fgets (buf, sizeof(buf)-1, f);
3301 /* Wait for the child to die. */
3302 waitpid (-1, &wait_status, 0);
3304 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3310 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3316 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3318 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3319 blurb(), av[0], result);
3331 kill_preview_subproc (state *s)
3333 s->running_preview_error_p = False;
3336 clear_preview_window (s);
3338 if (s->subproc_check_timer_id)
3340 gtk_timeout_remove (s->subproc_check_timer_id);
3341 s->subproc_check_timer_id = 0;
3342 s->subproc_check_countdown = 0;
3345 if (s->running_preview_pid)
3347 int status = kill (s->running_preview_pid, SIGTERM);
3348 char *ss = subproc_pretty_name (s);
3355 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3356 blurb(), (unsigned long) s->running_preview_pid, ss);
3361 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3362 blurb(), (unsigned long) s->running_preview_pid, ss);
3366 else if (s->debug_p)
3367 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3368 (unsigned long) s->running_preview_pid, ss);
3371 s->running_preview_pid = 0;
3372 if (s->running_preview_cmd) free (s->running_preview_cmd);
3373 s->running_preview_cmd = 0;
3380 /* Immediately and unconditionally launches the given process,
3381 after appending the -window-id option; sets running_preview_pid.
3384 launch_preview_subproc (state *s)
3386 saver_preferences *p = &s->prefs;
3390 const char *cmd = s->desired_preview_cmd;
3392 GtkWidget *pr = name_to_widget (s, "preview");
3393 GdkWindow *window = pr->window;
3395 s->running_preview_error_p = False;
3397 if (s->preview_suppressed_p)
3399 kill_preview_subproc (s);
3403 new_cmd = malloc (strlen (cmd) + 40);
3405 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3408 /* No window id? No command to run. */
3414 strcpy (new_cmd, cmd);
3415 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3419 kill_preview_subproc (s);
3422 s->running_preview_error_p = True;
3423 clear_preview_window (s);
3427 switch ((int) (forked = fork ()))
3432 sprintf (buf, "%s: couldn't fork", blurb());
3434 s->running_preview_error_p = True;
3440 close (ConnectionNumber (GDK_DISPLAY()));
3442 usleep (250000); /* pause for 1/4th second before launching, to give
3443 the previous program time to die and flush its X
3444 buffer, so we don't get leftover turds on the
3447 exec_command (p->shell, new_cmd, p->nice_inferior);
3448 /* Don't bother printing an error message when we are unable to
3449 exec subprocesses; we handle that by polling the pid later.
3451 Note that one must use _exit() instead of exit() in procs forked
3452 off of Gtk programs -- Gtk installs an atexit handler that has a
3453 copy of the X connection (which we've already closed, for safety.)
3454 If one uses exit() instead of _exit(), then one sometimes gets a
3455 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3457 _exit (1); /* exits child fork */
3462 if (s->running_preview_cmd) free (s->running_preview_cmd);
3463 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3464 s->running_preview_pid = forked;
3468 char *ss = subproc_pretty_name (s);
3469 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3470 (unsigned long) forked, ss);
3477 schedule_preview_check (s);
3480 if (new_cmd) free (new_cmd);
3485 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3488 hack_environment (state *s)
3490 static const char *def_path =
3491 # ifdef DEFAULT_PATH_PREFIX
3492 DEFAULT_PATH_PREFIX;
3497 Display *dpy = GDK_DISPLAY();
3498 const char *odpy = DisplayString (dpy);
3499 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3500 strcpy (ndpy, "DISPLAY=");
3501 strcat (ndpy, odpy);
3506 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3508 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3509 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3510 So we must leak it (and/or the previous setting). Yay.
3513 if (def_path && *def_path)
3515 const char *opath = getenv("PATH");
3516 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3517 strcpy (npath, "PATH=");
3518 strcat (npath, def_path);
3519 strcat (npath, ":");
3520 strcat (npath, opath);
3524 /* do not free(npath) -- see above */
3527 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3532 /* Called from a timer:
3533 Launches the currently-chosen subprocess, if it's not already running.
3534 If there's a different process running, kills it.
3537 update_subproc_timer (gpointer data)
3539 state *s = (state *) data;
3540 if (! s->desired_preview_cmd)
3541 kill_preview_subproc (s);
3542 else if (!s->running_preview_cmd ||
3543 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3544 launch_preview_subproc (s);
3546 s->subproc_timer_id = 0;
3547 return FALSE; /* do not re-execute timer */
3551 /* Call this when you think you might want a preview process running.
3552 It will set a timer that will actually launch that program a second
3553 from now, if you haven't changed your mind (to avoid double-click
3554 spazzing, etc.) `cmd' may be null meaning "no process".
3557 schedule_preview (state *s, const char *cmd)
3559 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3564 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3566 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3569 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3570 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3572 if (s->subproc_timer_id)
3573 gtk_timeout_remove (s->subproc_timer_id);
3574 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3578 /* Called from a timer:
3579 Checks to see if the subproc that should be running, actually is.
3582 check_subproc_timer (gpointer data)
3584 state *s = (state *) data;
3585 Bool again_p = True;
3587 if (s->running_preview_error_p || /* already dead */
3588 s->running_preview_pid <= 0)
3596 status = kill (s->running_preview_pid, 0);
3597 if (status < 0 && errno == ESRCH)
3598 s->running_preview_error_p = True;
3602 char *ss = subproc_pretty_name (s);
3603 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3604 (unsigned long) s->running_preview_pid, ss,
3605 (s->running_preview_error_p ? "dead" : "alive"));
3609 if (s->running_preview_error_p)
3611 clear_preview_window (s);
3616 /* Otherwise, it's currently alive. We might be checking again, or we
3617 might be satisfied. */
3619 if (--s->subproc_check_countdown <= 0)
3623 return TRUE; /* re-execute timer */
3626 s->subproc_check_timer_id = 0;
3627 s->subproc_check_countdown = 0;
3628 return FALSE; /* do not re-execute timer */
3633 /* Call this just after launching a subprocess.
3634 This sets a timer that will, five times a second for two seconds,
3635 check whether the program is still running. The assumption here
3636 is that if the process didn't stay up for more than a couple of
3637 seconds, then either the program doesn't exist, or it doesn't
3638 take a -window-id argument.
3641 schedule_preview_check (state *s)
3647 fprintf (stderr, "%s: scheduling check\n", blurb());
3649 if (s->subproc_check_timer_id)
3650 gtk_timeout_remove (s->subproc_check_timer_id);
3651 s->subproc_check_timer_id =
3652 gtk_timeout_add (1000 / ticks,
3653 check_subproc_timer, (gpointer) s);
3654 s->subproc_check_countdown = ticks * seconds;
3659 screen_blanked_p (void)
3663 unsigned long nitems, bytesafter;
3665 Display *dpy = GDK_DISPLAY();
3666 Bool blanked_p = False;
3668 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3669 XA_SCREENSAVER_STATUS,
3670 0, 3, False, XA_INTEGER,
3671 &type, &format, &nitems, &bytesafter,
3672 (unsigned char **) &data)
3674 && type == XA_INTEGER
3677 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3679 if (data) free (data);
3684 /* Wake up every now and then and see if the screen is blanked.
3685 If it is, kill off the small-window demo -- no point in wasting
3686 cycles by running two screensavers at once...
3689 check_blanked_timer (gpointer data)
3691 state *s = (state *) data;
3692 Bool blanked_p = screen_blanked_p ();
3693 if (blanked_p && s->running_preview_pid)
3696 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3697 kill_preview_subproc (s);
3700 return True; /* re-execute timer */
3704 /* Setting window manager icon
3708 init_icon (GdkWindow *window)
3710 GdkBitmap *mask = 0;
3713 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3714 (gchar **) logo_50_xpm);
3716 gdk_window_set_icon (window, 0, pixmap, mask);
3720 /* The main demo-mode command loop.
3725 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3726 XrmRepresentation *type, XrmValue *value, XPointer closure)
3729 for (i = 0; quarks[i]; i++)
3731 if (bindings[i] == XrmBindTightly)
3732 fprintf (stderr, (i == 0 ? "" : "."));
3733 else if (bindings[i] == XrmBindLoosely)
3734 fprintf (stderr, "*");
3736 fprintf (stderr, " ??? ");
3737 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3740 fprintf (stderr, ": %s\n", (char *) value->addr);
3748 the_network_is_not_the_computer (state *s)
3750 Display *dpy = GDK_DISPLAY();
3751 char *rversion = 0, *ruser = 0, *rhost = 0;
3752 char *luser, *lhost;
3754 struct passwd *p = getpwuid (getuid ());
3755 const char *d = DisplayString (dpy);
3757 # if defined(HAVE_UNAME)
3759 if (uname (&uts) < 0)
3760 lhost = "<UNKNOWN>";
3762 lhost = uts.nodename;
3764 strcpy (lhost, getenv("SYS$NODE"));
3765 # else /* !HAVE_UNAME && !VMS */
3766 strcat (lhost, "<UNKNOWN>");
3767 # endif /* !HAVE_UNAME && !VMS */
3769 if (p && p->pw_name)
3774 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3776 /* Make a buffer that's big enough for a number of copies of all the
3777 strings, plus some. */
3778 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3779 (ruser ? strlen(ruser) : 0) +
3780 (rhost ? strlen(rhost) : 0) +
3787 if (!rversion || !*rversion)
3791 "The XScreenSaver daemon doesn't seem to be running\n"
3792 "on display \"%s\". Launch it now?"),
3795 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3797 /* Warn that the two processes are running as different users.
3801 "%s is running as user \"%s\" on host \"%s\".\n"
3802 "But the xscreensaver managing display \"%s\"\n"
3803 "is running as user \"%s\" on host \"%s\".\n"
3805 "Since they are different users, they won't be reading/writing\n"
3806 "the same ~/.xscreensaver file, so %s isn't\n"
3807 "going to work right.\n"
3809 "You should either re-run %s as \"%s\", or re-run\n"
3810 "xscreensaver as \"%s\".\n"
3812 "Restart the xscreensaver daemon now?\n"),
3813 progname, luser, lhost,
3815 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3817 progname, (ruser ? ruser : "???"),
3820 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3822 /* Warn that the two processes are running on different hosts.
3826 "%s is running as user \"%s\" on host \"%s\".\n"
3827 "But the xscreensaver managing display \"%s\"\n"
3828 "is running as user \"%s\" on host \"%s\".\n"
3830 "If those two machines don't share a file system (that is,\n"
3831 "if they don't see the same ~%s/.xscreensaver file) then\n"
3832 "%s won't work right.\n"
3834 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3835 progname, luser, lhost,
3837 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3842 else if (!!strcmp (rversion, s->short_version))
3844 /* Warn that the version numbers don't match.
3848 "This is %s version %s.\n"
3849 "But the xscreensaver managing display \"%s\"\n"
3850 "is version %s. This could cause problems.\n"
3852 "Restart the xscreensaver daemon now?\n"),
3853 progname, s->short_version,
3860 warning_dialog (s->toplevel_widget, msg, True, 1);
3862 if (rversion) free (rversion);
3863 if (ruser) free (ruser);
3864 if (rhost) free (rhost);
3869 /* We use this error handler so that X errors are preceeded by the name
3870 of the program that generated them.
3873 demo_ehandler (Display *dpy, XErrorEvent *error)
3875 state *s = global_state_kludge; /* I hate C so much... */
3876 fprintf (stderr, "\nX error in %s:\n", blurb());
3877 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3878 kill_preview_subproc (s);
3884 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3885 of the program that generated them; and also that we can ignore one
3886 particular bogus error message that Gdk madly spews.
3889 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3890 const gchar *message, gpointer user_data)
3892 /* Ignore the message "Got event for unknown window: 0x...".
3893 Apparently some events are coming in for the xscreensaver window
3894 (presumably reply events related to the ClientMessage) and Gdk
3895 feels the need to complain about them. So, just suppress any
3896 messages that look like that one.
3898 if (strstr (message, "unknown window"))
3901 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3902 (log_domain ? log_domain : progclass),
3903 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3904 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3905 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3906 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3907 log_level == G_LOG_LEVEL_INFO ? "info" :
3908 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3910 ((!*message || message[strlen(message)-1] != '\n')
3916 __extension__ /* shut up about "string length is greater than the length
3917 ISO C89 compilers are required to support" when including
3921 static char *defaults[] = {
3922 #include "XScreenSaver_ad.h"
3927 #ifdef HAVE_CRAPPLET
3928 static struct poptOption crapplet_options[] = {
3929 {NULL, '\0', 0, NULL, 0}
3931 #endif /* HAVE_CRAPPLET */
3934 const char *usage = "[--display dpy] [--prefs]"
3935 # ifdef HAVE_CRAPPLET
3938 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
3941 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3943 state *s = (state *) user_data;
3944 Boolean oi = s->initializing_p;
3945 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3946 s->initializing_p = True;
3947 eschew_gtk_lossage (label);
3948 s->initializing_p = oi;
3954 print_widget_tree (GtkWidget *w, int depth)
3957 for (i = 0; i < depth; i++)
3958 fprintf (stderr, " ");
3959 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3961 if (GTK_IS_LIST (w))
3963 for (i = 0; i < depth+1; i++)
3964 fprintf (stderr, " ");
3965 fprintf (stderr, "...list kids...\n");
3967 else if (GTK_IS_CONTAINER (w))
3969 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3972 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3980 delayed_scroll_kludge (gpointer data)
3982 state *s = (state *) data;
3983 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
3984 ensure_selected_item_visible (w);
3986 /* Oh, this is just fucking lovely, too. */
3987 w = GTK_WIDGET (name_to_widget (s, "preview"));
3988 gtk_widget_hide (w);
3989 gtk_widget_show (w);
3991 return FALSE; /* do not re-execute timer */
3997 create_xscreensaver_demo (void)
4001 nb = name_to_widget (global_state_kludge, "preview_notebook");
4002 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4004 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4008 create_xscreensaver_settings_dialog (void)
4012 box = name_to_widget (global_state_kludge, "dialog_action_area");
4014 w = name_to_widget (global_state_kludge, "adv_button");
4015 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4017 w = name_to_widget (global_state_kludge, "std_button");
4018 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4020 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4023 #endif /* HAVE_GTK2 */
4026 main (int argc, char **argv)
4030 saver_preferences *p;
4034 Widget toplevel_shell;
4035 char *real_progname = argv[0];
4037 Bool crapplet_p = False;
4041 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4042 textdomain (GETTEXT_PACKAGE);
4045 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4046 # else /* !HAVE_GTK2 */
4047 if (!setlocale (LC_ALL, ""))
4048 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4049 # endif /* !HAVE_GTK2 */
4051 #endif /* ENABLE_NLS */
4053 str = strrchr (real_progname, '/');
4054 if (str) real_progname = str+1;
4057 memset (s, 0, sizeof(*s));
4058 s->initializing_p = True;
4061 global_state_kludge = s; /* I hate C so much... */
4063 progname = real_progname;
4065 s->short_version = (char *) malloc (5);
4066 memcpy (s->short_version, screensaver_id + 17, 4);
4067 s->short_version [4] = 0;
4070 /* Register our error message logger for every ``log domain'' known.
4071 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4072 for all of the domains that seem to be in use.
4075 const char * const domains[] = { 0,
4076 "Gtk", "Gdk", "GLib", "GModule",
4077 "GThread", "Gnome", "GnomeUI" };
4078 for (i = 0; i < countof(domains); i++)
4079 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4082 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4085 const char *dir = DEFAULT_ICONDIR;
4086 if (*dir) add_pixmap_directory (dir);
4088 # endif /* !HAVE_GTK2 */
4089 #endif /* DEFAULT_ICONDIR */
4091 /* This is gross, but Gtk understands --display and not -display...
4093 for (i = 1; i < argc; i++)
4094 if (argv[i][0] && argv[i][1] &&
4095 !strncmp(argv[i], "-display", strlen(argv[i])))
4096 argv[i] = "--display";
4099 /* We need to parse this arg really early... Sigh. */
4100 for (i = 1; i < argc; i++)
4103 (!strcmp(argv[i], "--crapplet") ||
4104 !strcmp(argv[i], "--capplet")))
4106 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4109 for (j = i; j < argc; j++) /* remove it from the list */
4110 argv[j] = argv[j+1];
4112 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4113 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4115 fprintf (stderr, "%s: %s\n", real_progname, usage);
4117 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4120 (!strcmp(argv[i], "--debug") ||
4121 !strcmp(argv[i], "-debug") ||
4122 !strcmp(argv[i], "-d")))
4126 for (j = i; j < argc; j++) /* remove it from the list */
4127 argv[j] = argv[j+1];
4134 (!strcmp(argv[i], "--configdir")))
4138 hack_configuration_path = argv[i+1];
4139 for (j = i; j < argc; j++) /* remove them from the list */
4140 argv[j] = argv[j+2];
4144 if (0 != stat (hack_configuration_path, &st))
4147 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4151 else if (!S_ISDIR (st.st_mode))
4153 fprintf (stderr, "%s: not a directory: %s\n",
4154 blurb(), hack_configuration_path);
4162 fprintf (stderr, "%s: using config directory \"%s\"\n",
4163 progname, hack_configuration_path);
4166 /* Let Gtk open the X connection, then initialize Xt to use that
4167 same connection. Doctor Frankenstein would be proud.
4169 # ifdef HAVE_CRAPPLET
4172 GnomeClient *client;
4173 GnomeClientFlags flags = 0;
4175 int init_results = gnome_capplet_init ("screensaver-properties",
4177 argc, argv, NULL, 0, NULL);
4179 0 upon successful initialization;
4180 1 if --init-session-settings was passed on the cmdline;
4181 2 if --ignore was passed on the cmdline;
4184 So the 1 signifies just to init the settings, and quit, basically.
4185 (Meaning launch the xscreensaver daemon.)
4188 if (init_results < 0)
4191 g_error ("An initialization error occurred while "
4192 "starting xscreensaver-capplet.\n");
4194 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4195 real_progname, init_results);
4200 client = gnome_master_client ();
4203 flags = gnome_client_get_flags (client);
4205 if (flags & GNOME_CLIENT_IS_CONNECTED)
4208 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4209 gnome_client_get_id (client));
4212 char *session_args[20];
4214 session_args[i++] = real_progname;
4215 session_args[i++] = "--capplet";
4216 session_args[i++] = "--init-session-settings";
4217 session_args[i] = 0;
4218 gnome_client_set_priority (client, 20);
4219 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4220 gnome_client_set_restart_command (client, i, session_args);
4224 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4227 gnome_client_flush (client);
4230 if (init_results == 1)
4232 system ("xscreensaver -nosplash &");
4238 # endif /* HAVE_CRAPPLET */
4240 gtk_init (&argc, &argv);
4244 /* We must read exactly the same resources as xscreensaver.
4245 That means we must have both the same progclass *and* progname,
4246 at least as far as the resource database is concerned. So,
4247 put "xscreensaver" in argv[0] while initializing Xt.
4249 argv[0] = "xscreensaver";
4253 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4255 XtToolkitInitialize ();
4256 app = XtCreateApplicationContext ();
4257 dpy = GDK_DISPLAY();
4258 XtAppSetFallbackResources (app, defaults);
4259 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4260 toplevel_shell = XtAppCreateShell (progname, progclass,
4261 applicationShellWidgetClass,
4264 dpy = XtDisplay (toplevel_shell);
4265 db = XtDatabase (dpy);
4266 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4267 XSetErrorHandler (demo_ehandler);
4269 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4270 signal (SIGPIPE, SIG_IGN);
4272 /* After doing Xt-style command-line processing, complain about any
4273 unrecognized command-line arguments.
4275 for (i = 1; i < argc; i++)
4277 char *str = argv[i];
4278 if (str[0] == '-' && str[1] == '-')
4280 if (!strcmp (str, "-prefs"))
4282 else if (crapplet_p)
4283 /* There are lots of random args that we don't care about when we're
4284 started as a crapplet, so just ignore unknown args in that case. */
4288 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname, argv[i]);
4289 fprintf (stderr, "%s: %s\n", real_progname, usage);
4294 /* Load the init file, which may end up consulting the X resource database
4295 and the site-wide app-defaults file. Note that at this point, it's
4296 important that `progname' be "xscreensaver", rather than whatever
4301 hack_environment (s); /* must be before initialize_sort_map() */
4304 initialize_sort_map (s);
4306 /* Now that Xt has been initialized, and the resources have been read,
4307 we can set our `progname' variable to something more in line with
4310 progname = real_progname;
4314 /* Print out all the resources we read. */
4316 XrmName name = { 0 };
4317 XrmClass class = { 0 };
4319 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4325 /* Intern the atoms that xscreensaver_command() needs.
4327 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4328 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4329 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4330 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4331 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4332 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4333 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4334 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4335 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4336 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4337 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4338 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4339 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4342 /* Create the window and all its widgets.
4344 s->base_widget = create_xscreensaver_demo ();
4345 s->popup_widget = create_xscreensaver_settings_dialog ();
4346 s->toplevel_widget = s->base_widget;
4349 /* Set the main window's title. */
4351 char *base_title = _("Screensaver Preferences");
4352 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4353 char *s1, *s2, *s3, *s4;
4354 s1 = (char *) strchr(v, ' '); s1++;
4355 s2 = (char *) strchr(s1, ' ');
4356 s3 = (char *) strchr(v, '('); s3++;
4357 s4 = (char *) strchr(s3, ')');
4361 window_title = (char *) malloc (strlen (base_title) +
4362 strlen (progclass) +
4363 strlen (s1) + strlen (s3) +
4365 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4366 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4367 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4371 /* Adjust the (invisible) notebooks on the popup dialog... */
4373 GtkNotebook *notebook =
4374 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4375 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4379 gtk_widget_hide (std);
4380 # else /* !HAVE_XML */
4381 /* Make the advanced page be the only one available. */
4382 gtk_widget_set_sensitive (std, False);
4383 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4384 gtk_widget_hide (std);
4386 # endif /* !HAVE_XML */
4388 gtk_notebook_set_page (notebook, page);
4389 gtk_notebook_set_show_tabs (notebook, False);
4392 /* Various other widget initializations...
4394 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4395 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4397 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4398 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4401 populate_hack_list (s);
4402 populate_prefs_page (s);
4403 sensitize_demo_widgets (s, False);
4404 fix_text_entry_sizes (s);
4405 scroll_to_current_hack (s);
4407 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4408 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4412 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4413 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4415 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4416 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4418 #endif /* !HAVE_GTK2 */
4420 /* Hook up callbacks to the items on the mode menu. */
4422 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4423 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4424 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4425 for (; kids; kids = kids->next)
4426 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4427 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4432 /* Handle the -prefs command-line argument. */
4435 GtkNotebook *notebook =
4436 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4437 gtk_notebook_set_page (notebook, 1);
4440 # ifdef HAVE_CRAPPLET
4444 GtkWidget *outer_vbox;
4446 gtk_widget_hide (s->toplevel_widget);
4448 capplet = capplet_widget_new ();
4450 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4451 # ifdef HAVE_CRAPPLET_IMMEDIATE
4452 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4453 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4454 /* In crapplet-mode, take off the menubar. */
4455 gtk_widget_hide (name_to_widget (s, "menubar"));
4457 /* Reparent our top-level container to be a child of the capplet
4460 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4461 gtk_widget_ref (outer_vbox);
4462 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4464 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4465 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4467 /* Find the window above us, and set the title and close handler. */
4469 GtkWidget *window = capplet;
4470 while (window && !GTK_IS_WINDOW (window))
4471 window = window->parent;
4474 gtk_window_set_title (GTK_WINDOW (window), window_title);
4475 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4476 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4481 s->toplevel_widget = capplet;
4483 # endif /* HAVE_CRAPPLET */
4486 /* The Gnome folks hate the menubar. I think it's important to have access
4487 to the commands on the File menu (Restart Daemon, etc.) and to the
4488 About and Documentation commands on the Help menu.
4492 gtk_widget_hide (name_to_widget (s, "menubar"));
4496 free (window_title);
4500 gtk_widget_show (s->toplevel_widget);
4501 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4502 fix_preview_visual (s);
4504 /* Realize page zero, so that we can diddle the scrollbar when the
4505 user tabs back to it -- otherwise, the current hack isn't scrolled
4506 to the first time they tab back there, when started with "-prefs".
4507 (Though it is if they then tab away, and back again.)
4509 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4510 #### understands this crap, explain to me how to make this work.
4512 gtk_widget_realize (name_to_widget (s, "demos_table"));
4515 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4518 /* Issue any warnings about the running xscreensaver daemon. */
4519 the_network_is_not_the_computer (s);
4522 /* Run the Gtk event loop, and not the Xt event loop. This means that
4523 if there were Xt timers or fds registered, they would never get serviced,
4524 and if there were any Xt widgets, they would never have events delivered.
4525 Fortunately, we're using Gtk for all of the UI, and only initialized
4526 Xt so that we could process the command line and use the X resource
4529 s->initializing_p = False;
4531 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4532 after we start up. Otherwise, it always appears scrolled to the top
4533 when in crapplet-mode. */
4534 gtk_timeout_add (500, delayed_scroll_kludge, s);
4538 /* Load every configurator in turn, to scan them for errors all at once. */
4541 for (i = 0; i < p->screenhacks_count; i++)
4543 screenhack *hack = p->screenhacks[i];
4544 conf_data *d = load_configurator (hack->command, False);
4545 if (d) free_conf_data (d);
4551 # ifdef HAVE_CRAPPLET
4553 capplet_gtk_main ();
4555 # endif /* HAVE_CRAPPLET */
4558 kill_preview_subproc (s);
4562 #endif /* HAVE_GTK -- whole file */