1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2004 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 #undef dgettext /* else these are defined twice... */
122 #include "demo-Gtk-widgets.h"
123 #include "demo-Gtk-support.h"
124 #include "demo-Gtk-conf.h"
136 #endif /* HAVE_GTK2 */
139 extern void exec_command (const char *shell, const char *command, int nice);
141 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
144 #define countof(x) (sizeof((x))/sizeof((*x)))
148 char *progclass = "XScreenSaver";
151 /* The order of the items in the mode menu. */
152 static int mode_menu_order[] = {
153 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS };
158 char *short_version; /* version number of this xscreensaver build */
160 GtkWidget *toplevel_widget; /* the main window */
161 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
162 GtkWidget *popup_widget; /* the "Settings" dialog */
163 conf_data *cdata; /* private data for per-hack configuration */
166 GladeXML *glade_ui; /* Glade UI file */
167 #endif /* HAVE_GTK2 */
169 Bool debug_p; /* whether to print diagnostics */
170 Bool initializing_p; /* flag for breaking recursion loops */
171 Bool saving_p; /* flag for breaking recursion loops */
173 char *desired_preview_cmd; /* subprocess we intend to run */
174 char *running_preview_cmd; /* subprocess we are currently running */
175 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
176 Bool running_preview_error_p; /* whether the pid died abnormally */
178 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
179 int subproc_timer_id; /* timer to delay subproc launch */
180 int subproc_check_timer_id; /* timer to check whether it started up */
181 int subproc_check_countdown; /* how many more checks left */
183 int *list_elt_to_hack_number; /* table for sorting the hack list */
184 int *hack_number_to_list_elt; /* the inverse table */
185 Bool *hacks_available_p; /* whether hacks are on $PATH */
186 int list_count; /* how many items are in the list: this may be
187 less than p->screenhacks_count, if some are
190 int _selected_list_element; /* don't use this: call
191 selected_list_element() instead */
193 saver_preferences prefs;
198 /* Total fucking evilness due to the fact that it's rocket science to get
199 a closure object of our own down into the various widget callbacks. */
200 static state *global_state_kludge;
203 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
204 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
205 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
208 static void populate_demo_window (state *, int list_elt);
209 static void populate_prefs_page (state *);
210 static void populate_popup_window (state *);
212 static Bool flush_dialog_changes_and_save (state *);
213 static Bool flush_popup_changes_and_save (state *);
215 static int maybe_reload_init_file (state *);
216 static void await_xscreensaver (state *);
218 static void schedule_preview (state *, const char *cmd);
219 static void kill_preview_subproc (state *);
220 static void schedule_preview_check (state *);
224 /* Some random utility functions
230 time_t now = time ((time_t *) 0);
231 char *ct = (char *) ctime (&now);
232 static char buf[255];
233 int n = strlen(progname);
235 strncpy(buf, progname, n);
238 strncpy(buf+n, ct+11, 8);
239 strcpy(buf+n+9, ": ");
245 name_to_widget (state *s, const char *name)
255 /* First try to load the Glade file from the current directory;
256 if there isn't one there, check the installed directory.
258 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
259 const char * const files[] = { GLADE_FILE_NAME,
260 GLADE_DIR "/" GLADE_FILE_NAME };
262 for (i = 0; i < countof (files); i++)
265 if (!stat (files[i], &st))
267 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
274 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
275 "\tfrom " GLADE_DIR "/ or current directory.\n",
279 # undef GLADE_FILE_NAME
281 glade_xml_signal_autoconnect (s->glade_ui);
284 w = glade_xml_get_widget (s->glade_ui, name);
286 #else /* !HAVE_GTK2 */
288 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
291 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
293 #endif /* HAVE_GTK2 */
296 fprintf (stderr, "%s: no widget \"%s\"\n", blurb(), name);
301 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
302 Takes a scroller, viewport, or list as an argument.
305 ensure_selected_item_visible (GtkWidget *widget)
309 GtkTreeSelection *selection;
313 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
314 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
315 path = gtk_tree_path_new_first ();
317 path = gtk_tree_model_get_path (model, &iter);
319 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
321 gtk_tree_path_free (path);
323 #else /* !HAVE_GTK2 */
325 GtkScrolledWindow *scroller = 0;
327 GtkList *list_widget = 0;
331 GtkWidget *selected = 0;
334 gint parent_h, child_y, child_h, children_h, ignore;
335 double ratio_t, ratio_b;
337 if (GTK_IS_SCROLLED_WINDOW (widget))
339 scroller = GTK_SCROLLED_WINDOW (widget);
340 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
341 list_widget = GTK_LIST (GTK_BIN(vp)->child);
343 else if (GTK_IS_VIEWPORT (widget))
345 vp = GTK_VIEWPORT (widget);
346 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
347 list_widget = GTK_LIST (GTK_BIN(vp)->child);
349 else if (GTK_IS_LIST (widget))
351 list_widget = GTK_LIST (widget);
352 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
353 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
358 slist = list_widget->selection;
359 selected = (slist ? GTK_WIDGET (slist->data) : 0);
363 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
365 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
366 kids; kids = kids->next)
369 adj = gtk_scrolled_window_get_vadjustment (scroller);
371 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
372 &ignore, &ignore, &ignore, &parent_h, &ignore);
373 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
374 &ignore, &child_y, &ignore, &child_h, &ignore);
375 children_h = nkids * child_h;
377 ratio_t = ((double) child_y) / ((double) children_h);
378 ratio_b = ((double) child_y + child_h) / ((double) children_h);
380 if (adj->upper == 0.0) /* no items in list */
383 if (ratio_t < (adj->value / adj->upper) ||
384 ratio_b > ((adj->value + adj->page_size) / adj->upper))
387 int slop = parent_h * 0.75; /* how much to overshoot by */
389 if (ratio_t < (adj->value / adj->upper))
391 double ratio_w = ((double) parent_h) / ((double) children_h);
392 double ratio_l = (ratio_b - ratio_t);
393 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
396 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
398 target = ratio_t * adj->upper;
402 if (target > adj->upper - adj->page_size)
403 target = adj->upper - adj->page_size;
407 gtk_adjustment_set_value (adj, target);
409 #endif /* !HAVE_GTK2 */
413 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
415 GtkWidget *shell = GTK_WIDGET (user_data);
416 while (shell->parent)
417 shell = shell->parent;
418 gtk_widget_destroy (GTK_WIDGET (shell));
422 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
424 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
426 restart_menu_cb (widget, user_data);
427 warning_dialog_dismiss_cb (widget, user_data);
431 warning_dialog (GtkWidget *parent, const char *message,
432 Boolean restart_button_p, int center)
434 char *msg = strdup (message);
437 GtkWidget *dialog = gtk_dialog_new ();
438 GtkWidget *label = 0;
440 GtkWidget *cancel = 0;
443 while (parent && !parent->window)
444 parent = parent->parent;
447 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
449 fprintf (stderr, "%s: too early for dialog?\n", progname);
457 char *s = strchr (head, '\n');
460 sprintf (name, "label%d", i++);
463 label = gtk_label_new (head);
465 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
466 #endif /* HAVE_GTK2 */
471 GTK_WIDGET (label)->style =
472 gtk_style_copy (GTK_WIDGET (label)->style);
473 GTK_WIDGET (label)->style->font =
474 gdk_font_load (get_string_resource("warning_dialog.headingFont",
476 gtk_widget_set_style (GTK_WIDGET (label),
477 GTK_WIDGET (label)->style);
479 #endif /* !HAVE_GTK2 */
481 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
482 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
483 label, TRUE, TRUE, 0);
484 gtk_widget_show (label);
495 label = gtk_label_new ("");
496 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
497 label, TRUE, TRUE, 0);
498 gtk_widget_show (label);
500 label = gtk_hbutton_box_new ();
501 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
502 label, TRUE, TRUE, 0);
505 if (restart_button_p)
507 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
508 gtk_container_add (GTK_CONTAINER (label), cancel);
511 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
512 gtk_container_add (GTK_CONTAINER (label), ok);
514 #else /* !HAVE_GTK2 */
516 ok = gtk_button_new_with_label ("OK");
517 gtk_container_add (GTK_CONTAINER (label), ok);
519 if (restart_button_p)
521 cancel = gtk_button_new_with_label ("Cancel");
522 gtk_container_add (GTK_CONTAINER (label), cancel);
525 #endif /* !HAVE_GTK2 */
527 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
528 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
529 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
530 STFU GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
531 gtk_widget_show (ok);
532 gtk_widget_grab_focus (ok);
536 STFU GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
537 gtk_widget_show (cancel);
539 gtk_widget_show (label);
540 gtk_widget_show (dialog);
542 if (restart_button_p)
544 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
545 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
547 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
548 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
553 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
554 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
558 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
559 GTK_WIDGET (parent)->window);
562 gtk_window_present (GTK_WINDOW (dialog));
563 #else /* !HAVE_GTK2 */
564 gdk_window_show (GTK_WIDGET (dialog)->window);
565 gdk_window_raise (GTK_WIDGET (dialog)->window);
566 #endif /* !HAVE_GTK2 */
573 run_cmd (state *s, Atom command, int arg)
578 flush_dialog_changes_and_save (s);
579 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
584 sprintf (buf, "Error:\n\n%s", err);
586 strcpy (buf, "Unknown error!");
587 warning_dialog (s->toplevel_widget, buf, False, 100);
594 run_hack (state *s, int list_elt, Bool report_errors_p)
597 if (list_elt < 0) return;
598 hack_number = s->list_elt_to_hack_number[list_elt];
600 flush_dialog_changes_and_save (s);
601 schedule_preview (s, 0);
603 run_cmd (s, XA_DEMO, hack_number + 1);
607 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
619 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
621 state *s = global_state_kludge; /* I hate C so much... */
622 flush_dialog_changes_and_save (s);
623 kill_preview_subproc (s);
628 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
630 state *s = (state *) data;
631 flush_dialog_changes_and_save (s);
638 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
641 char *vers = strdup (screensaver_id + 4);
644 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
646 s = strchr (vers, ',');
650 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
651 non-ASCII characters aren't allowed in localizable string keys."
652 (I don't want to just use (c) instead of © because that doesn't
653 look as good in the plain-old default Latin1 "C" locale.)
656 sprintf(copy, ("Copyright \xC2\xA9 1991-2004 %s"), s);
657 #else /* !HAVE_GTK2 */
658 sprintf(copy, ("Copyright \251 1991-2004 %s"), s);
659 #endif /* !HAVE_GTK2 */
661 sprintf (msg, "%s\n\n%s", copy, desc);
663 /* I can't make gnome_about_new() work here -- it starts dying in
664 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
665 then this might be the thing to do:
669 const gchar *auth[] = { 0 };
670 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
672 gtk_widget_show (about);
674 #else / * GTK but not GNOME * /
678 GdkColormap *colormap;
679 GdkPixmap *gdkpixmap;
682 GtkWidget *dialog = gtk_dialog_new ();
683 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
684 GtkWidget *parent = GTK_WIDGET (menuitem);
685 while (parent->parent)
686 parent = parent->parent;
688 hbox = gtk_hbox_new (FALSE, 20);
689 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
690 hbox, TRUE, TRUE, 0);
692 colormap = gtk_widget_get_colormap (parent);
694 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
695 (gchar **) logo_180_xpm);
696 icon = gtk_pixmap_new (gdkpixmap, mask);
697 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
699 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
701 vbox = gtk_vbox_new (FALSE, 0);
702 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
704 label1 = gtk_label_new (vers);
705 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
706 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
707 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
710 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
711 GTK_WIDGET (label1)->style->font =
712 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
713 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
714 #endif /* HAVE_GTK2 */
716 label2 = gtk_label_new (msg);
717 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
718 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
719 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
722 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
723 GTK_WIDGET (label2)->style->font =
724 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
725 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
726 #endif /* HAVE_GTK2 */
728 hb = gtk_hbutton_box_new ();
730 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
734 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
735 #else /* !HAVE_GTK2 */
736 ok = gtk_button_new_with_label (_("OK"));
737 #endif /* !HAVE_GTK2 */
738 gtk_container_add (GTK_CONTAINER (hb), ok);
740 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
741 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
742 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
744 gtk_widget_show (hbox);
745 gtk_widget_show (icon);
746 gtk_widget_show (vbox);
747 gtk_widget_show (label1);
748 gtk_widget_show (label2);
749 gtk_widget_show (hb);
750 gtk_widget_show (ok);
751 gtk_widget_show (dialog);
753 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
754 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
756 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
757 GTK_WIDGET (parent)->window);
758 gdk_window_show (GTK_WIDGET (dialog)->window);
759 gdk_window_raise (GTK_WIDGET (dialog)->window);
765 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
767 state *s = global_state_kludge; /* I hate C so much... */
768 saver_preferences *p = &s->prefs;
771 if (!p->help_url || !*p->help_url)
773 warning_dialog (s->toplevel_widget,
775 "No Help URL has been specified.\n"), False, 100);
779 help_command = (char *) malloc (strlen (p->load_url_command) +
780 (strlen (p->help_url) * 2) + 20);
781 strcpy (help_command, "( ");
782 sprintf (help_command + strlen(help_command),
783 p->load_url_command, p->help_url, p->help_url);
784 strcat (help_command, " ) &");
785 system (help_command);
791 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
793 state *s = global_state_kludge; /* I hate C so much... */
794 run_cmd (s, XA_ACTIVATE, 0);
799 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
801 state *s = global_state_kludge; /* I hate C so much... */
802 run_cmd (s, XA_LOCK, 0);
807 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
809 state *s = global_state_kludge; /* I hate C so much... */
810 run_cmd (s, XA_EXIT, 0);
815 restart_menu_cb (GtkWidget *widget, gpointer user_data)
817 state *s = global_state_kludge; /* I hate C so much... */
818 flush_dialog_changes_and_save (s);
819 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
821 system ("xscreensaver -nosplash &");
823 await_xscreensaver (s);
827 await_xscreensaver (state *s)
831 Display *dpy = GDK_DISPLAY();
832 /* GtkWidget *dialog = 0;*/
835 while (!rversion && (--countdown > 0))
837 /* Check for the version of the running xscreensaver... */
838 server_xscreensaver_version (dpy, &rversion, 0, 0);
840 /* If it's not there yet, wait a second... */
845 /* if (dialog) gtk_widget_destroy (dialog);*/
854 /* Timed out, no screensaver running. */
857 Bool root_p = (geteuid () == 0);
861 "The xscreensaver daemon did not start up properly.\n"
867 __extension__ /* don't warn about "string length is greater than
868 the length ISO C89 compilers are required to
869 support" in the following expression... */
872 _("You are running as root. This usually means that xscreensaver\n"
873 "was unable to contact your X server because access control is\n"
874 "turned on. Try running this command:\n"
876 " xhost +localhost\n"
878 "and then selecting `File / Restart Daemon'.\n"
880 "Note that turning off access control will allow anyone logged\n"
881 "on to this machine to access your screen, which might be\n"
882 "considered a security problem. Please read the xscreensaver\n"
883 "manual and FAQ for more information.\n"
885 "You shouldn't run X as root. Instead, you should log in as a\n"
886 "normal user, and `su' as necessary."));
888 strcat (buf, _("Please check your $PATH and permissions."));
890 warning_dialog (s->toplevel_widget, buf, False, 1);
896 selected_list_element (state *s)
898 return s->_selected_list_element;
903 demo_write_init_file (state *s, saver_preferences *p)
907 /* #### try to figure out why shit keeps getting reordered... */
908 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
912 if (!write_init_file (p, s->short_version, False))
915 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
920 const char *f = init_file_name();
922 warning_dialog (s->toplevel_widget,
923 _("Error:\n\nCouldn't determine init file name!\n"),
927 char *b = (char *) malloc (strlen(f) + 1024);
928 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
929 warning_dialog (s->toplevel_widget, b, False, 100);
938 run_this_cb (GtkButton *button, gpointer user_data)
940 state *s = global_state_kludge; /* I hate C so much... */
941 int list_elt = selected_list_element (s);
942 if (list_elt < 0) return;
943 if (!flush_dialog_changes_and_save (s))
944 run_hack (s, list_elt, True);
949 manual_cb (GtkButton *button, gpointer user_data)
951 state *s = global_state_kludge; /* I hate C so much... */
952 saver_preferences *p = &s->prefs;
953 GtkWidget *list_widget = name_to_widget (s, "list");
954 int list_elt = selected_list_element (s);
956 char *name, *name2, *cmd, *str;
957 if (list_elt < 0) return;
958 hack_number = s->list_elt_to_hack_number[list_elt];
960 flush_dialog_changes_and_save (s);
961 ensure_selected_item_visible (list_widget);
963 name = strdup (p->screenhacks[hack_number]->command);
965 while (isspace (*name2)) name2++;
967 while (*str && !isspace (*str)) str++;
969 str = strrchr (name2, '/');
970 if (str) name = str+1;
972 cmd = get_string_resource ("manualCommand", "ManualCommand");
975 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
977 sprintf (cmd2 + strlen (cmd2),
979 name2, name2, name2, name2);
980 strcat (cmd2, " ) &");
986 warning_dialog (GTK_WIDGET (button),
987 _("Error:\n\nno `manualCommand' resource set."),
996 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
998 GtkWidget *parent = name_to_widget (s, "scroller");
999 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
1002 GtkTreeModel *model;
1003 GtkTreeSelection *selection;
1004 #endif /* HAVE_GTK2 */
1006 if (!was) gtk_widget_set_sensitive (parent, True);
1008 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1009 STFU g_assert (model);
1010 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
1011 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1012 gtk_tree_selection_select_iter (selection, &iter);
1013 #else /* !HAVE_GTK2 */
1014 gtk_list_select_item (GTK_LIST (list), list_elt);
1015 #endif /* !HAVE_GTK2 */
1016 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1017 if (!was) gtk_widget_set_sensitive (parent, False);
1022 run_next_cb (GtkButton *button, gpointer user_data)
1024 state *s = global_state_kludge; /* I hate C so much... */
1025 /* saver_preferences *p = &s->prefs; */
1026 Bool ops = s->preview_suppressed_p;
1028 GtkWidget *list_widget = name_to_widget (s, "list");
1029 int list_elt = selected_list_element (s);
1036 if (list_elt >= s->list_count)
1039 s->preview_suppressed_p = True;
1041 flush_dialog_changes_and_save (s);
1042 force_list_select_item (s, list_widget, list_elt, True);
1043 populate_demo_window (s, list_elt);
1044 run_hack (s, list_elt, False);
1046 s->preview_suppressed_p = ops;
1051 run_prev_cb (GtkButton *button, gpointer user_data)
1053 state *s = global_state_kludge; /* I hate C so much... */
1054 /* saver_preferences *p = &s->prefs; */
1055 Bool ops = s->preview_suppressed_p;
1057 GtkWidget *list_widget = name_to_widget (s, "list");
1058 int list_elt = selected_list_element (s);
1061 list_elt = s->list_count - 1;
1066 list_elt = s->list_count - 1;
1068 s->preview_suppressed_p = True;
1070 flush_dialog_changes_and_save (s);
1071 force_list_select_item (s, list_widget, list_elt, True);
1072 populate_demo_window (s, list_elt);
1073 run_hack (s, list_elt, False);
1075 s->preview_suppressed_p = ops;
1079 /* Writes the given settings into prefs.
1080 Returns true if there was a change, False otherwise.
1081 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1084 flush_changes (state *s,
1087 const char *command,
1090 saver_preferences *p = &s->prefs;
1091 Bool changed = False;
1094 if (list_elt < 0 || list_elt >= s->list_count)
1097 hack_number = s->list_elt_to_hack_number[list_elt];
1098 hack = p->screenhacks[hack_number];
1100 if (enabled_p != -1 &&
1101 enabled_p != hack->enabled_p)
1103 hack->enabled_p = enabled_p;
1106 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1107 blurb(), hack->name, enabled_p);
1112 if (!hack->command || !!strcmp (command, hack->command))
1114 if (hack->command) free (hack->command);
1115 hack->command = strdup (command);
1118 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1119 blurb(), hack->name, command);
1125 const char *ov = hack->visual;
1126 if (!ov || !*ov) ov = "any";
1127 if (!*visual) visual = "any";
1128 if (!!strcasecmp (visual, ov))
1130 if (hack->visual) free (hack->visual);
1131 hack->visual = strdup (visual);
1134 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1135 blurb(), hack->name, visual);
1143 /* Helper for the text fields that contain time specifications:
1144 this parses the text, and does error checking.
1147 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1152 if (!sec_p || strchr (line, ':'))
1153 value = parse_time ((char *) line, sec_p, True);
1157 if (sscanf (line, "%d%c", &value, &c) != 1)
1163 value *= 1000; /* Time measures in microseconds */
1169 "Unparsable time format: \"%s\"\n"),
1171 warning_dialog (s->toplevel_widget, b, False, 100);
1180 directory_p (const char *path)
1183 if (!path || !*path)
1185 else if (stat (path, &st))
1187 else if (!S_ISDIR (st.st_mode))
1194 normalize_directory (const char *path)
1198 if (!path || !*path) return 0;
1200 p2 = (char *) malloc (L + 2);
1202 if (p2[L-1] == '/') /* remove trailing slash */
1205 for (s = p2; s && *s; s++)
1208 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1209 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1212 while (s0 > p2 && s0[-1] != '/')
1222 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1223 strcpy (s, s+2), s--;
1224 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1228 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1229 while (s[0] == '/' && s[1] == '/')
1232 /* and strip trailing whitespace for good measure. */
1234 while (isspace(p2[L-1]))
1247 } FlushForeachClosure;
1250 flush_checkbox (GtkTreeModel *model,
1255 FlushForeachClosure *closure = data;
1258 gtk_tree_model_get (model, iter,
1259 COL_ENABLED, &checked,
1262 if (flush_changes (closure->s, closure->i,
1264 *closure->changed = True;
1268 /* don't remove row */
1272 #endif /* HAVE_GTK2 */
1274 /* Flush out any changes made in the main dialog window (where changes
1275 take place immediately: clicking on a checkbox causes the init file
1276 to be written right away.)
1279 flush_dialog_changes_and_save (state *s)
1281 saver_preferences *p = &s->prefs;
1282 saver_preferences P2, *p2 = &P2;
1284 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1285 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1286 FlushForeachClosure closure;
1287 #else /* !HAVE_GTK2 */
1288 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1289 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1291 #endif /* !HAVE_GTK2 */
1293 Bool changed = False;
1296 if (s->saving_p) return False;
1301 /* Flush any checkbox changes in the list down into the prefs struct.
1305 closure.changed = &changed;
1307 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1309 #else /* !HAVE_GTK2 */
1311 for (i = 0; kids; kids = kids->next, i++)
1313 GtkWidget *line = GTK_WIDGET (kids->data);
1314 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1315 GtkWidget *line_check =
1316 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1318 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1320 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1323 #endif /* ~HAVE_GTK2 */
1325 /* Flush the non-hack-specific settings down into the prefs struct.
1328 # define SECONDS(FIELD,NAME) \
1329 w = name_to_widget (s, (NAME)); \
1330 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1332 # define MINUTES(FIELD,NAME) \
1333 w = name_to_widget (s, (NAME)); \
1334 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1336 # define CHECKBOX(FIELD,NAME) \
1337 w = name_to_widget (s, (NAME)); \
1338 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1340 # define PATHNAME(FIELD,NAME) \
1341 w = name_to_widget (s, (NAME)); \
1342 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1344 MINUTES (&p2->timeout, "timeout_spinbutton");
1345 MINUTES (&p2->cycle, "cycle_spinbutton");
1346 CHECKBOX (p2->lock_p, "lock_button");
1347 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1349 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1350 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1351 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1352 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1354 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1355 CHECKBOX (p2->grab_video_p, "grab_video_button");
1356 CHECKBOX (p2->random_image_p, "grab_image_button");
1357 PATHNAME (p2->image_directory, "image_text");
1359 CHECKBOX (p2->verbose_p, "verbose_button");
1360 CHECKBOX (p2->capture_stderr_p, "capture_button");
1361 CHECKBOX (p2->splash_p, "splash_button");
1363 CHECKBOX (p2->install_cmap_p, "install_button");
1364 CHECKBOX (p2->fade_p, "fade_button");
1365 CHECKBOX (p2->unfade_p, "unfade_button");
1366 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1373 /* Warn if the image directory doesn't exist.
1375 if (p2->image_directory &&
1376 *p2->image_directory &&
1377 !directory_p (p2->image_directory))
1380 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1381 p2->image_directory);
1382 warning_dialog (s->toplevel_widget, b, False, 100);
1386 /* Map the mode menu to `saver_mode' enum values. */
1388 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1389 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1390 GtkWidget *selected = gtk_menu_get_active (menu);
1391 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1392 int menu_elt = g_list_index (kids, (gpointer) selected);
1393 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1394 p2->mode = mode_menu_order[menu_elt];
1397 if (p2->mode == ONE_HACK)
1399 int list_elt = selected_list_element (s);
1400 p2->selected_hack = (list_elt >= 0
1401 ? s->list_elt_to_hack_number[list_elt]
1405 # define COPY(field, name) \
1406 if (p->field != p2->field) { \
1409 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1411 p->field = p2->field
1414 COPY(selected_hack, "selected_hack");
1416 COPY(timeout, "timeout");
1417 COPY(cycle, "cycle");
1418 COPY(lock_p, "lock_p");
1419 COPY(lock_timeout, "lock_timeout");
1421 COPY(dpms_enabled_p, "dpms_enabled_p");
1422 COPY(dpms_standby, "dpms_standby");
1423 COPY(dpms_suspend, "dpms_suspend");
1424 COPY(dpms_off, "dpms_off");
1426 COPY(verbose_p, "verbose_p");
1427 COPY(capture_stderr_p, "capture_stderr_p");
1428 COPY(splash_p, "splash_p");
1430 COPY(install_cmap_p, "install_cmap_p");
1431 COPY(fade_p, "fade_p");
1432 COPY(unfade_p, "unfade_p");
1433 COPY(fade_seconds, "fade_seconds");
1435 COPY(grab_desktop_p, "grab_desktop_p");
1436 COPY(grab_video_p, "grab_video_p");
1437 COPY(random_image_p, "random_image_p");
1441 if (!p->image_directory ||
1442 !p2->image_directory ||
1443 strcmp(p->image_directory, p2->image_directory))
1447 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1448 blurb(), p2->image_directory);
1450 if (p->image_directory && p->image_directory != p2->image_directory)
1451 free (p->image_directory);
1452 p->image_directory = p2->image_directory;
1453 p2->image_directory = 0;
1455 populate_prefs_page (s);
1459 Display *dpy = GDK_DISPLAY();
1460 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1461 sync_server_dpms_settings (dpy, enabled_p,
1462 p->dpms_standby / 1000,
1463 p->dpms_suspend / 1000,
1467 changed = demo_write_init_file (s, p);
1470 s->saving_p = False;
1475 /* Flush out any changes made in the popup dialog box (where changes
1476 take place only when the OK button is clicked.)
1479 flush_popup_changes_and_save (state *s)
1481 Bool changed = False;
1482 saver_preferences *p = &s->prefs;
1483 int list_elt = selected_list_element (s);
1485 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1486 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1488 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1489 const char *command = gtk_entry_get_text (cmd);
1494 if (s->saving_p) return False;
1500 if (maybe_reload_init_file (s) != 0)
1506 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1508 if (!strcasecmp (visual, "")) visual = "";
1509 else if (!strcasecmp (visual, "any")) visual = "";
1510 else if (!strcasecmp (visual, "default")) visual = "Default";
1511 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1512 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1513 else if (!strcasecmp (visual, "best")) visual = "Best";
1514 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1515 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1516 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1517 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1518 else if (!strcasecmp (visual, "color")) visual = "Color";
1519 else if (!strcasecmp (visual, "gl")) visual = "GL";
1520 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1521 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1522 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1523 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1524 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1525 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1526 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1527 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1528 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1531 gdk_beep (); /* unparsable */
1533 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1536 changed = flush_changes (s, list_elt, -1, command, visual);
1539 changed = demo_write_init_file (s, p);
1541 /* Do this to re-launch the hack if (and only if) the command line
1543 populate_demo_window (s, selected_list_element (s));
1547 s->saving_p = False;
1553 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1555 state *s = global_state_kludge; /* I hate C so much... */
1556 if (! s->initializing_p)
1558 s->initializing_p = True;
1559 flush_dialog_changes_and_save (s);
1560 s->initializing_p = False;
1565 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1567 pref_changed_cb (widget, user_data);
1571 /* Callback on menu items in the "mode" options menu.
1574 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1576 state *s = (state *) user_data;
1577 saver_preferences *p = &s->prefs;
1578 GtkWidget *list = name_to_widget (s, "list");
1581 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1583 saver_mode new_mode;
1587 if (menu_items->data == widget)
1590 menu_items = menu_items->next;
1592 if (!menu_items) abort();
1594 new_mode = mode_menu_order[menu_index];
1596 /* Keep the same list element displayed as before; except if we're
1597 switching *to* "one screensaver" mode from any other mode, set
1598 "the one" to be that which is currently selected.
1600 list_elt = selected_list_element (s);
1601 if (new_mode == ONE_HACK)
1602 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1605 saver_mode old_mode = p->mode;
1607 populate_demo_window (s, list_elt);
1608 force_list_select_item (s, list, list_elt, True);
1609 p->mode = old_mode; /* put it back, so the init file gets written */
1612 pref_changed_cb (widget, user_data);
1617 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1618 gint page_num, gpointer user_data)
1620 state *s = global_state_kludge; /* I hate C so much... */
1621 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1623 /* If we're switching to page 0, schedule the current hack to be run.
1624 Otherwise, schedule it to stop. */
1626 populate_demo_window (s, selected_list_element (s));
1628 schedule_preview (s, 0);
1633 list_activated_cb (GtkTreeView *list,
1635 GtkTreeViewColumn *column,
1642 STFU g_return_if_fail (!gdk_pointer_is_grabbed ());
1644 str = gtk_tree_path_to_string (path);
1645 list_elt = strtol (str, NULL, 10);
1649 run_hack (s, list_elt, True);
1653 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1655 state *s = (state *)data;
1656 GtkTreeModel *model;
1662 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1665 path = gtk_tree_model_get_path (model, &iter);
1666 str = gtk_tree_path_to_string (path);
1667 list_elt = strtol (str, NULL, 10);
1669 gtk_tree_path_free (path);
1672 populate_demo_window (s, list_elt);
1673 flush_dialog_changes_and_save (s);
1676 #else /* !HAVE_GTK2 */
1678 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1679 list_select_cb that comes in
1680 *after* we've double-clicked.
1684 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1687 state *s = (state *) data;
1688 if (event->type == GDK_2BUTTON_PRESS)
1690 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1691 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1693 last_doubleclick_time = time ((time_t *) 0);
1696 run_hack (s, list_elt, True);
1704 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1706 state *s = (state *) data;
1707 time_t now = time ((time_t *) 0);
1709 if (now >= last_doubleclick_time + 2)
1711 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1712 populate_demo_window (s, list_elt);
1713 flush_dialog_changes_and_save (s);
1718 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1720 state *s = (state *) data;
1721 populate_demo_window (s, -1);
1722 flush_dialog_changes_and_save (s);
1725 #endif /* !HAVE_GTK2 */
1728 /* Called when the checkboxes that are in the left column of the
1729 scrolling list are clicked. This both populates the right pane
1730 (just as clicking on the label (really, listitem) does) and
1731 also syncs this checkbox with the right pane Enabled checkbox.
1736 GtkCellRendererToggle *toggle,
1738 #else /* !HAVE_GTK2 */
1740 #endif /* !HAVE_GTK2 */
1743 state *s = (state *) data;
1746 GtkScrolledWindow *scroller =
1747 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1748 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1749 GtkTreeModel *model = gtk_tree_view_get_model (list);
1750 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1753 #else /* !HAVE_GTK2 */
1754 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1755 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1757 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1758 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1759 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1760 #endif /* !HAVE_GTK2 */
1767 if (!gtk_tree_model_get_iter (model, &iter, path))
1769 g_warning ("bad path: %s", path_string);
1772 gtk_tree_path_free (path);
1774 gtk_tree_model_get (model, &iter,
1775 COL_ENABLED, &active,
1778 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1779 COL_ENABLED, !active,
1782 list_elt = strtol (path_string, NULL, 10);
1783 #else /* !HAVE_GTK2 */
1784 list_elt = gtk_list_child_position (list, line);
1785 #endif /* !HAVE_GTK2 */
1787 /* remember previous scroll position of the top of the list */
1788 adj = gtk_scrolled_window_get_vadjustment (scroller);
1789 scroll_top = adj->value;
1791 flush_dialog_changes_and_save (s);
1792 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1793 populate_demo_window (s, list_elt);
1795 /* restore the previous scroll position of the top of the list.
1796 this is weak, but I don't really know why it's moving... */
1797 gtk_adjustment_set_value (adj, scroll_top);
1803 GtkFileSelection *widget;
1804 } file_selection_data;
1809 store_image_directory (GtkWidget *button, gpointer user_data)
1811 file_selection_data *fsd = (file_selection_data *) user_data;
1812 state *s = fsd->state;
1813 GtkFileSelection *selector = fsd->widget;
1814 GtkWidget *top = s->toplevel_widget;
1815 saver_preferences *p = &s->prefs;
1816 const char *path = gtk_file_selection_get_filename (selector);
1818 if (p->image_directory && !strcmp(p->image_directory, path))
1819 return; /* no change */
1821 if (!directory_p (path))
1824 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1825 warning_dialog (GTK_WIDGET (top), b, False, 100);
1829 if (p->image_directory) free (p->image_directory);
1830 p->image_directory = normalize_directory (path);
1832 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1833 (p->image_directory ? p->image_directory : ""));
1834 demo_write_init_file (s, p);
1839 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1841 file_selection_data *fsd = (file_selection_data *) user_data;
1842 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1846 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1848 browse_image_dir_cancel (button, user_data);
1849 store_image_directory (button, user_data);
1853 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1855 browse_image_dir_cancel (widget, user_data);
1860 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1862 state *s = global_state_kludge; /* I hate C so much... */
1863 saver_preferences *p = &s->prefs;
1864 static file_selection_data *fsd = 0;
1866 GtkFileSelection *selector = GTK_FILE_SELECTION(
1867 gtk_file_selection_new ("Please select the image directory."));
1870 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1872 fsd->widget = selector;
1875 if (p->image_directory && *p->image_directory)
1876 gtk_file_selection_set_filename (selector, p->image_directory);
1878 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1879 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1881 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1882 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1884 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1885 GTK_SIGNAL_FUNC (browse_image_dir_close),
1888 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1890 gtk_window_set_modal (GTK_WINDOW (selector), True);
1891 gtk_widget_show (GTK_WIDGET (selector));
1896 settings_cb (GtkButton *button, gpointer user_data)
1898 state *s = global_state_kludge; /* I hate C so much... */
1899 int list_elt = selected_list_element (s);
1901 populate_demo_window (s, list_elt); /* reset the widget */
1902 populate_popup_window (s); /* create UI on popup window */
1903 gtk_widget_show (s->popup_widget);
1907 settings_sync_cmd_text (state *s)
1910 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1911 char *cmd_line = get_configurator_command_line (s->cdata);
1912 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1913 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1915 # endif /* HAVE_XML */
1919 settings_adv_cb (GtkButton *button, gpointer user_data)
1921 state *s = global_state_kludge; /* I hate C so much... */
1922 GtkNotebook *notebook =
1923 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1925 settings_sync_cmd_text (s);
1926 gtk_notebook_set_page (notebook, 1);
1930 settings_std_cb (GtkButton *button, gpointer user_data)
1932 state *s = global_state_kludge; /* I hate C so much... */
1933 GtkNotebook *notebook =
1934 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1936 /* Re-create UI to reflect the in-progress command-line settings. */
1937 populate_popup_window (s);
1939 gtk_notebook_set_page (notebook, 0);
1943 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1944 gint page_num, gpointer user_data)
1946 state *s = global_state_kludge; /* I hate C so much... */
1947 GtkWidget *adv = name_to_widget (s, "adv_button");
1948 GtkWidget *std = name_to_widget (s, "std_button");
1952 gtk_widget_show (adv);
1953 gtk_widget_hide (std);
1955 else if (page_num == 1)
1957 gtk_widget_hide (adv);
1958 gtk_widget_show (std);
1967 settings_cancel_cb (GtkButton *button, gpointer user_data)
1969 state *s = global_state_kludge; /* I hate C so much... */
1970 gtk_widget_hide (s->popup_widget);
1974 settings_ok_cb (GtkButton *button, gpointer user_data)
1976 state *s = global_state_kludge; /* I hate C so much... */
1977 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1978 int page = gtk_notebook_get_current_page (notebook);
1981 /* Regenerate the command-line from the widget contents before saving.
1982 But don't do this if we're looking at the command-line page already,
1983 or we will blow away what they typed... */
1984 settings_sync_cmd_text (s);
1986 flush_popup_changes_and_save (s);
1987 gtk_widget_hide (s->popup_widget);
1991 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1993 state *s = (state *) data;
1994 settings_cancel_cb (0, (gpointer) s);
2000 /* Populating the various widgets
2004 /* Returns the number of the last hack run by the server.
2007 server_current_hack (void)
2011 unsigned long nitems, bytesafter;
2013 Display *dpy = GDK_DISPLAY();
2014 int hack_number = -1;
2016 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2017 XA_SCREENSAVER_STATUS,
2018 0, 3, False, XA_INTEGER,
2019 &type, &format, &nitems, &bytesafter,
2020 (unsigned char **) &data)
2022 && type == XA_INTEGER
2025 hack_number = (int) data[2] - 1;
2027 if (data) free (data);
2033 /* Finds the number of the last hack to run, and makes that item be
2034 selected by default.
2037 scroll_to_current_hack (state *s)
2039 saver_preferences *p = &s->prefs;
2042 if (p->mode == ONE_HACK)
2043 hack_number = p->selected_hack;
2045 hack_number = server_current_hack ();
2047 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2049 int list_elt = s->hack_number_to_list_elt[hack_number];
2050 GtkWidget *list = name_to_widget (s, "list");
2051 force_list_select_item (s, list, list_elt, True);
2052 populate_demo_window (s, list_elt);
2058 on_path_p (const char *program)
2062 char *cmd = strdup (program);
2063 char *token = strchr (cmd, ' ');
2067 if (token) *token = 0;
2070 if (strchr (cmd, '/'))
2072 result = (0 == stat (cmd, &st));
2076 path = getenv("PATH");
2077 if (!path || !*path)
2081 path = strdup (path);
2082 token = strtok (path, ":");
2086 char *p2 = (char *) malloc (strlen (token) + L + 3);
2090 result = (0 == stat (p2, &st));
2093 token = strtok (0, ":");
2098 if (path) free (path);
2104 populate_hack_list (state *s)
2107 saver_preferences *p = &s->prefs;
2108 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2109 GtkListStore *model;
2110 GtkTreeSelection *selection;
2111 GtkCellRenderer *ren;
2115 g_object_get (G_OBJECT (list),
2120 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2121 g_object_set (G_OBJECT (list), "model", model, NULL);
2122 g_object_unref (model);
2124 ren = gtk_cell_renderer_toggle_new ();
2125 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2127 "active", COL_ENABLED,
2130 g_signal_connect (ren, "toggled",
2131 G_CALLBACK (list_checkbox_cb),
2134 ren = gtk_cell_renderer_text_new ();
2135 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2136 _("Screen Saver"), ren,
2140 g_signal_connect_after (list, "row_activated",
2141 G_CALLBACK (list_activated_cb),
2144 selection = gtk_tree_view_get_selection (list);
2145 g_signal_connect (selection, "changed",
2146 G_CALLBACK (list_select_changed_cb),
2151 for (i = 0; i < s->list_count; i++)
2153 int hack_number = s->list_elt_to_hack_number[i];
2154 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2156 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2158 if (!hack) continue;
2160 /* If we're to suppress uninstalled hacks, check $PATH now. */
2161 if (p->ignore_uninstalled_p && !available_p)
2164 pretty_name = (hack->name
2165 ? strdup (hack->name)
2166 : make_hack_name (hack->command));
2170 /* Make the text foreground be the color of insensitive widgets
2171 (but don't actually make it be insensitive, since we still
2172 want to be able to click on it.)
2174 GtkStyle *style = GTK_WIDGET (list)->style;
2175 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2176 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2177 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2179 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2180 /* " background=\"#%02X%02X%02X\"" */
2182 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2183 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2189 gtk_list_store_append (model, &iter);
2190 gtk_list_store_set (model, &iter,
2191 COL_ENABLED, hack->enabled_p,
2192 COL_NAME, pretty_name,
2197 #else /* !HAVE_GTK2 */
2199 saver_preferences *p = &s->prefs;
2200 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2202 for (i = 0; i < s->list_count; i++)
2204 int hack_number = s->list_elt_to_hack_number[i];
2205 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2207 /* A GtkList must contain only GtkListItems, but those can contain
2208 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2209 and a Label. We handle single and double click events on the
2210 line itself, for clicking on the text, but the interior checkbox
2211 also handles its own events.
2214 GtkWidget *line_hbox;
2215 GtkWidget *line_check;
2216 GtkWidget *line_label;
2218 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2220 if (!hack) continue;
2222 /* If we're to suppress uninstalled hacks, check $PATH now. */
2223 if (p->ignore_uninstalled_p && !available_p)
2226 pretty_name = (hack->name
2227 ? strdup (hack->name)
2228 : make_hack_name (hack->command));
2230 line = gtk_list_item_new ();
2231 line_hbox = gtk_hbox_new (FALSE, 0);
2232 line_check = gtk_check_button_new ();
2233 line_label = gtk_label_new (pretty_name);
2235 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2236 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2237 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2239 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2241 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2243 gtk_widget_show (line_check);
2244 gtk_widget_show (line_label);
2245 gtk_widget_show (line_hbox);
2246 gtk_widget_show (line);
2250 gtk_container_add (GTK_CONTAINER (list), line);
2251 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2252 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2255 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2256 GTK_SIGNAL_FUNC (list_checkbox_cb),
2259 gtk_widget_show (line);
2263 /* Make the widget be colored like insensitive widgets
2264 (but don't actually make it be insensitive, since we
2265 still want to be able to click on it.)
2267 GtkRcStyle *rc_style;
2270 gtk_widget_realize (GTK_WIDGET (line_label));
2272 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2273 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2275 rc_style = gtk_rc_style_new ();
2276 rc_style->fg[GTK_STATE_NORMAL] = fg;
2277 rc_style->bg[GTK_STATE_NORMAL] = bg;
2278 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2280 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2281 gtk_rc_style_unref (rc_style);
2285 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2286 GTK_SIGNAL_FUNC (list_select_cb),
2288 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2289 GTK_SIGNAL_FUNC (list_unselect_cb),
2291 #endif /* !HAVE_GTK2 */
2295 update_list_sensitivity (state *s)
2297 saver_preferences *p = &s->prefs;
2298 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2299 Bool checkable = (p->mode == RANDOM_HACKS);
2300 Bool blankable = (p->mode != DONT_BLANK);
2303 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2304 GtkWidget *use = name_to_widget (s, "use_col_frame");
2305 #endif /* HAVE_GTK2 */
2306 GtkWidget *scroller = name_to_widget (s, "scroller");
2307 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2308 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2311 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2312 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2313 #else /* !HAVE_GTK2 */
2314 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2315 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2317 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2318 #endif /* !HAVE_GTK2 */
2319 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2320 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2322 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2325 gtk_tree_view_column_set_visible (use, checkable);
2326 #else /* !HAVE_GTK2 */
2328 gtk_widget_show (use); /* the "Use" column header */
2330 gtk_widget_hide (use);
2334 GtkBin *line = GTK_BIN (kids->data);
2335 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2336 GtkWidget *line_check =
2337 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2340 gtk_widget_show (line_check);
2342 gtk_widget_hide (line_check);
2346 #endif /* !HAVE_GTK2 */
2351 populate_prefs_page (state *s)
2353 saver_preferences *p = &s->prefs;
2355 Bool can_lock_p = True;
2357 /* Disable all the "lock" controls if locking support was not provided
2358 at compile-time, or if running on MacOS. */
2359 # if defined(NO_LOCKING) || defined(__APPLE__)
2364 /* The file supports timeouts of less than a minute, but the GUI does
2365 not, so throttle the values to be at least one minute (since "0" is
2366 a bad rounding choice...)
2368 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2371 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2374 # define FMT_MINUTES(NAME,N) \
2375 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2377 # define FMT_SECONDS(NAME,N) \
2378 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2380 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2381 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2382 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2383 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2384 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2385 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2386 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2391 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2392 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2395 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2396 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2397 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2398 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2399 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2400 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2401 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2402 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2403 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2404 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2405 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2407 # undef TOGGLE_ACTIVE
2409 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2410 (p->image_directory ? p->image_directory : ""));
2411 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2413 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2416 /* Map the `saver_mode' enum to mode menu to values. */
2418 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2421 for (i = 0; i < countof(mode_menu_order); i++)
2422 if (mode_menu_order[i] == p->mode)
2424 gtk_option_menu_set_history (opt, i);
2425 update_list_sensitivity (s);
2429 Bool found_any_writable_cells = False;
2430 Bool dpms_supported = False;
2432 Display *dpy = GDK_DISPLAY();
2433 int nscreens = ScreenCount(dpy);
2435 for (i = 0; i < nscreens; i++)
2437 Screen *s = ScreenOfDisplay (dpy, i);
2438 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2440 found_any_writable_cells = True;
2445 #ifdef HAVE_XF86VMODE_GAMMA
2446 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2449 #ifdef HAVE_DPMS_EXTENSION
2451 int op = 0, event = 0, error = 0;
2452 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2453 dpms_supported = True;
2455 #endif /* HAVE_DPMS_EXTENSION */
2458 # define SENSITIZE(NAME,SENSITIVEP) \
2459 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2461 /* Blanking and Locking
2463 SENSITIZE ("lock_button", can_lock_p);
2464 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2465 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2469 SENSITIZE ("dpms_frame", dpms_supported);
2470 SENSITIZE ("dpms_button", dpms_supported);
2471 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2472 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2473 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2474 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2475 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2476 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2477 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2478 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2479 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2483 SENSITIZE ("cmap_frame", found_any_writable_cells);
2484 SENSITIZE ("install_button", found_any_writable_cells);
2485 SENSITIZE ("fade_button", found_any_writable_cells);
2486 SENSITIZE ("unfade_button", found_any_writable_cells);
2488 SENSITIZE ("fade_label", (found_any_writable_cells &&
2489 (p->fade_p || p->unfade_p)));
2490 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2491 (p->fade_p || p->unfade_p)));
2499 populate_popup_window (state *s)
2501 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2502 char *doc_string = 0;
2504 /* #### not in Gtk 1.2
2505 gtk_label_set_selectable (doc);
2511 free_conf_data (s->cdata);
2516 saver_preferences *p = &s->prefs;
2517 int list_elt = selected_list_element (s);
2518 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2519 ? s->list_elt_to_hack_number[list_elt]
2521 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2524 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2525 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2526 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2527 s->cdata = load_configurator (cmd_line, s->debug_p);
2528 if (s->cdata && s->cdata->widget)
2529 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2534 doc_string = (s->cdata
2535 ? s->cdata->description
2537 # else /* !HAVE_XML */
2538 doc_string = _("Descriptions not available: no XML support compiled in.");
2539 # endif /* !HAVE_XML */
2541 gtk_label_set_text (doc, (doc_string
2543 : _("No description available.")));
2548 sensitize_demo_widgets (state *s, Bool sensitive_p)
2550 const char *names1[] = { "demo", "settings" };
2551 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2552 "visual", "visual_combo" };
2554 for (i = 0; i < countof(names1); i++)
2556 GtkWidget *w = name_to_widget (s, names1[i]);
2557 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2559 for (i = 0; i < countof(names2); i++)
2561 GtkWidget *w = name_to_widget (s, names2[i]);
2562 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2567 /* Even though we've given these text fields a maximum number of characters,
2568 their default size is still about 30 characters wide -- so measure out
2569 a string in their font, and resize them to just fit that.
2572 fix_text_entry_sizes (state *s)
2576 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2577 const char * const spinbuttons[] = {
2578 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2579 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2580 "dpms_off_spinbutton",
2581 "-fade_spinbutton" };
2585 for (i = 0; i < countof(spinbuttons); i++)
2587 const char *n = spinbuttons[i];
2589 while (*n == '-') n++, cols--;
2590 w = GTK_WIDGET (name_to_widget (s, n));
2591 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2592 gtk_widget_set_usize (w, width, -2);
2595 /* Now fix the width of the combo box.
2597 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2598 w = GTK_COMBO (w)->entry;
2599 width = gdk_string_width (w->style->font, "PseudoColor___");
2600 gtk_widget_set_usize (w, width, -2);
2602 /* Now fix the width of the file entry text.
2604 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2605 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2606 gtk_widget_set_usize (w, width, -2);
2608 /* Now fix the width of the command line text.
2610 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2611 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2612 gtk_widget_set_usize (w, width, -2);
2616 /* Now fix the height of the list widget:
2617 make it default to being around 10 text-lines high instead of 4.
2619 w = GTK_WIDGET (name_to_widget (s, "list"));
2623 int leading = 3; /* approximate is ok... */
2627 PangoFontMetrics *pain =
2628 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2629 w->style->font_desc,
2630 gtk_get_default_language ());
2631 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2632 pango_font_metrics_get_descent (pain));
2633 #else /* !HAVE_GTK2 */
2634 height = w->style->font->ascent + w->style->font->descent;
2635 #endif /* !HAVE_GTK2 */
2639 height += border * 2;
2640 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2641 gtk_widget_set_usize (w, -2, height);
2648 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2651 static char *up_arrow_xpm[] = {
2674 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2675 the end of the array (Gtk 1.2.5.) */
2676 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2677 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2680 static char *down_arrow_xpm[] = {
2703 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2704 the end of the array (Gtk 1.2.5.) */
2705 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2706 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2710 pixmapify_button (state *s, int down_p)
2714 GtkWidget *pixmapwid;
2718 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2719 style = gtk_widget_get_style (w);
2721 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2722 &style->bg[GTK_STATE_NORMAL],
2724 ? (gchar **) down_arrow_xpm
2725 : (gchar **) up_arrow_xpm));
2726 pixmapwid = gtk_pixmap_new (pixmap, mask);
2727 gtk_widget_show (pixmapwid);
2728 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2729 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2733 map_next_button_cb (GtkWidget *w, gpointer user_data)
2735 state *s = (state *) user_data;
2736 pixmapify_button (s, 1);
2740 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2742 state *s = (state *) user_data;
2743 pixmapify_button (s, 0);
2745 #endif /* !HAVE_GTK2 */
2748 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2752 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2753 GtkAllocation *allocation,
2757 GtkWidgetAuxInfo *aux_info;
2759 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2761 aux_info->width = allocation->width;
2762 aux_info->height = -2;
2766 gtk_widget_size_request (label, &req);
2770 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2773 eschew_gtk_lossage (GtkLabel *label)
2775 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2776 aux_info->width = GTK_WIDGET (label)->allocation.width;
2777 aux_info->height = -2;
2781 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2783 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2784 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2787 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2789 gtk_widget_queue_resize (GTK_WIDGET (label));
2794 populate_demo_window (state *s, int list_elt)
2796 saver_preferences *p = &s->prefs;
2799 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2800 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2801 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2802 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2803 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2805 if (p->mode == BLANK_ONLY)
2808 pretty_name = strdup (_("Blank Screen"));
2809 schedule_preview (s, 0);
2811 else if (p->mode == DONT_BLANK)
2814 pretty_name = strdup (_("Screen Saver Disabled"));
2815 schedule_preview (s, 0);
2819 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2820 ? s->list_elt_to_hack_number[list_elt]
2822 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2826 ? strdup (hack->name)
2827 : make_hack_name (hack->command))
2831 schedule_preview (s, hack->command);
2833 schedule_preview (s, 0);
2837 pretty_name = strdup (_("Preview"));
2839 gtk_frame_set_label (frame1, _(pretty_name));
2840 gtk_frame_set_label (frame2, _(pretty_name));
2842 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2843 gtk_entry_set_position (cmd, 0);
2847 sprintf (title, _("%s: %.100s Settings"),
2848 progclass, (pretty_name ? pretty_name : "???"));
2849 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2852 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2854 ? (hack->visual && *hack->visual
2859 sensitize_demo_widgets (s, (hack ? True : False));
2861 if (pretty_name) free (pretty_name);
2863 ensure_selected_item_visible (list);
2865 s->_selected_list_element = list_elt;
2870 widget_deleter (GtkWidget *widget, gpointer data)
2872 /* #### Well, I want to destroy these widgets, but if I do that, they get
2873 referenced again, and eventually I get a SEGV. So instead of
2874 destroying them, I'll just hide them, and leak a bunch of memory
2875 every time the disk file changes. Go go go Gtk!
2877 #### Ok, that's a lie, I get a crash even if I just hide the widget
2878 and don't ever delete it. Fuck!
2881 gtk_widget_destroy (widget);
2883 gtk_widget_hide (widget);
2888 static char **sort_hack_cmp_names_kludge;
2890 sort_hack_cmp (const void *a, const void *b)
2896 int aa = *(int *) a;
2897 int bb = *(int *) b;
2898 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
2899 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
2900 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
2906 initialize_sort_map (state *s)
2908 saver_preferences *p = &s->prefs;
2911 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2912 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2913 if (s->hacks_available_p) free (s->hacks_available_p);
2915 s->list_elt_to_hack_number = (int *)
2916 calloc (sizeof(int), p->screenhacks_count + 1);
2917 s->hack_number_to_list_elt = (int *)
2918 calloc (sizeof(int), p->screenhacks_count + 1);
2919 s->hacks_available_p = (Bool *)
2920 calloc (sizeof(Bool), p->screenhacks_count + 1);
2922 /* Check which hacks actually exist on $PATH
2924 for (i = 0; i < p->screenhacks_count; i++)
2926 screenhack *hack = p->screenhacks[i];
2927 s->hacks_available_p[i] = on_path_p (hack->command);
2930 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
2934 for (i = 0; i < p->screenhacks_count; i++)
2936 if (!p->ignore_uninstalled_p ||
2937 s->hacks_available_p[i])
2938 s->list_elt_to_hack_number[j++] = i;
2942 for (; j < p->screenhacks_count; j++)
2943 s->list_elt_to_hack_number[j] = -1;
2946 /* Generate list of sortable names (once)
2948 sort_hack_cmp_names_kludge = (char **)
2949 calloc (sizeof(char *), p->screenhacks_count);
2950 for (i = 0; i < p->screenhacks_count; i++)
2952 screenhack *hack = p->screenhacks[i];
2953 char *name = (hack->name && *hack->name
2954 ? strdup (hack->name)
2955 : make_hack_name (hack->command));
2957 for (str = name; *str; str++)
2958 *str = tolower(*str);
2959 sort_hack_cmp_names_kludge[i] = name;
2962 /* Sort list->hack map alphabetically
2964 qsort (s->list_elt_to_hack_number,
2965 p->screenhacks_count,
2966 sizeof(*s->list_elt_to_hack_number),
2971 for (i = 0; i < p->screenhacks_count; i++)
2972 free (sort_hack_cmp_names_kludge[i]);
2973 free (sort_hack_cmp_names_kludge);
2974 sort_hack_cmp_names_kludge = 0;
2976 /* Build inverse table */
2977 for (i = 0; i < p->screenhacks_count; i++)
2978 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2983 maybe_reload_init_file (state *s)
2985 saver_preferences *p = &s->prefs;
2988 static Bool reentrant_lock = False;
2989 if (reentrant_lock) return 0;
2990 reentrant_lock = True;
2992 if (init_file_changed_p (p))
2994 const char *f = init_file_name();
2999 if (!f || !*f) return 0;
3000 b = (char *) malloc (strlen(f) + 1024);
3003 "file \"%s\" has changed, reloading.\n"),
3005 warning_dialog (s->toplevel_widget, b, False, 100);
3009 initialize_sort_map (s);
3011 list_elt = selected_list_element (s);
3012 list = name_to_widget (s, "list");
3013 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3014 populate_hack_list (s);
3015 force_list_select_item (s, list, list_elt, True);
3016 populate_prefs_page (s);
3017 populate_demo_window (s, list_elt);
3018 ensure_selected_item_visible (list);
3023 reentrant_lock = False;
3029 /* Making the preview window have the right X visual (so that GL works.)
3032 static Visual *get_best_gl_visual (state *);
3035 x_visual_to_gdk_visual (Visual *xv)
3037 GList *gvs = gdk_list_visuals();
3038 if (!xv) return gdk_visual_get_system();
3039 for (; gvs; gvs = gvs->next)
3041 GdkVisual *gv = (GdkVisual *) gvs->data;
3042 if (xv == GDK_VISUAL_XVISUAL (gv))
3045 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3046 blurb(), (unsigned long) xv->visualid);
3051 clear_preview_window (state *s)
3056 if (!s->toplevel_widget) return; /* very early */
3057 p = name_to_widget (s, "preview");
3060 if (!window) return;
3062 /* Flush the widget background down into the window, in case a subproc
3064 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3065 gdk_window_clear (window);
3068 int list_elt = selected_list_element (s);
3069 int hack_number = (list_elt >= 0
3070 ? s->list_elt_to_hack_number[list_elt]
3072 Bool available_p = (hack_number >= 0
3073 ? s->hacks_available_p [hack_number]
3076 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3077 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3078 (s->running_preview_error_p
3079 ? (available_p ? 1 : 2)
3081 #else /* !HAVE_GTK2 */
3082 if (s->running_preview_error_p)
3084 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3085 const char * const lines2[] = { N_("Not"), N_("Installed") };
3086 int nlines = countof(lines1);
3087 int lh = p->style->font->ascent + p->style->font->descent;
3091 const char * const *lines = (available_p ? lines1 : lines2);
3093 gdk_window_get_size (window, &w, &h);
3094 y = (h - (lh * nlines)) / 2;
3095 y += p->style->font->ascent;
3096 for (i = 0; i < nlines; i++)
3098 int sw = gdk_string_width (p->style->font, _(lines[i]));
3099 int x = (w - sw) / 2;
3100 gdk_draw_string (window, p->style->font,
3101 p->style->fg_gc[GTK_STATE_NORMAL],
3106 #endif /* !HAVE_GTK2 */
3114 fix_preview_visual (state *s)
3116 GtkWidget *widget = name_to_widget (s, "preview");
3117 Visual *xvisual = get_best_gl_visual (s);
3118 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3119 GdkVisual *dvisual = gdk_visual_get_system();
3120 GdkColormap *cmap = (visual == dvisual
3121 ? gdk_colormap_get_system ()
3122 : gdk_colormap_new (visual, False));
3125 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3126 (visual == dvisual ? "default" : "non-default"),
3127 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3129 if (!GTK_WIDGET_REALIZED (widget) ||
3130 gtk_widget_get_visual (widget) != visual)
3132 gtk_widget_unrealize (widget);
3133 gtk_widget_set_visual (widget, visual);
3134 gtk_widget_set_colormap (widget, cmap);
3135 gtk_widget_realize (widget);
3138 /* Set the Widget colors to be white-on-black. */
3140 GdkWindow *window = widget->window;
3141 GtkStyle *style = gtk_style_copy (widget->style);
3142 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3143 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3144 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3145 GdkGC *fgc = gdk_gc_new(window);
3146 GdkGC *bgc = gdk_gc_new(window);
3147 if (!gdk_color_white (cmap, fg)) abort();
3148 if (!gdk_color_black (cmap, bg)) abort();
3149 gdk_gc_set_foreground (fgc, fg);
3150 gdk_gc_set_background (fgc, bg);
3151 gdk_gc_set_foreground (bgc, bg);
3152 gdk_gc_set_background (bgc, fg);
3153 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3154 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3155 gtk_widget_set_style (widget, style);
3157 /* For debugging purposes, put a title on the window (so that
3158 it can be easily found in the output of "xwininfo -tree".)
3160 gdk_window_set_title (window, "Preview");
3163 gtk_widget_show (widget);
3171 subproc_pretty_name (state *s)
3173 if (s->running_preview_cmd)
3175 char *ps = strdup (s->running_preview_cmd);
3176 char *ss = strchr (ps, ' ');
3178 ss = strrchr (ps, '/');
3189 return strdup ("???");
3194 reap_zombies (state *s)
3196 int wait_status = 0;
3198 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3202 if (pid == s->running_preview_pid)
3204 char *ss = subproc_pretty_name (s);
3205 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3206 (unsigned long) pid, ss);
3210 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3211 (unsigned long) pid);
3217 /* Mostly lifted from driver/subprocs.c */
3219 get_best_gl_visual (state *s)
3221 Display *dpy = GDK_DISPLAY();
3230 av[ac++] = "xscreensaver-gl-helper";
3235 perror ("error creating pipe:");
3242 switch ((int) (forked = fork ()))
3246 sprintf (buf, "%s: couldn't fork", blurb());
3254 close (in); /* don't need this one */
3255 close (ConnectionNumber (dpy)); /* close display fd */
3257 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3259 perror ("could not dup() a new stdout:");
3263 execvp (av[0], av); /* shouldn't return. */
3265 if (errno != ENOENT)
3267 /* Ignore "no such file or directory" errors, unless verbose.
3268 Issue all other exec errors, though. */
3269 sprintf (buf, "%s: running %s", blurb(), av[0]);
3273 /* Note that one must use _exit() instead of exit() in procs forked
3274 off of Gtk programs -- Gtk installs an atexit handler that has a
3275 copy of the X connection (which we've already closed, for safety.)
3276 If one uses exit() instead of _exit(), then one sometimes gets a
3277 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3279 _exit (1); /* exits fork */
3285 int wait_status = 0;
3287 FILE *f = fdopen (in, "r");
3291 close (out); /* don't need this one */
3294 fgets (buf, sizeof(buf)-1, f);
3297 /* Wait for the child to die. */
3298 waitpid (-1, &wait_status, 0);
3300 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3306 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3312 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3314 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3315 blurb(), av[0], result);
3327 kill_preview_subproc (state *s)
3329 s->running_preview_error_p = False;
3332 clear_preview_window (s);
3334 if (s->subproc_check_timer_id)
3336 gtk_timeout_remove (s->subproc_check_timer_id);
3337 s->subproc_check_timer_id = 0;
3338 s->subproc_check_countdown = 0;
3341 if (s->running_preview_pid)
3343 int status = kill (s->running_preview_pid, SIGTERM);
3344 char *ss = subproc_pretty_name (s);
3351 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3352 blurb(), (unsigned long) s->running_preview_pid, ss);
3357 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3358 blurb(), (unsigned long) s->running_preview_pid, ss);
3362 else if (s->debug_p)
3363 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3364 (unsigned long) s->running_preview_pid, ss);
3367 s->running_preview_pid = 0;
3368 if (s->running_preview_cmd) free (s->running_preview_cmd);
3369 s->running_preview_cmd = 0;
3376 /* Immediately and unconditionally launches the given process,
3377 after appending the -window-id option; sets running_preview_pid.
3380 launch_preview_subproc (state *s)
3382 saver_preferences *p = &s->prefs;
3386 const char *cmd = s->desired_preview_cmd;
3388 GtkWidget *pr = name_to_widget (s, "preview");
3389 GdkWindow *window = pr->window;
3391 s->running_preview_error_p = False;
3393 if (s->preview_suppressed_p)
3395 kill_preview_subproc (s);
3399 new_cmd = malloc (strlen (cmd) + 40);
3401 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3404 /* No window id? No command to run. */
3410 strcpy (new_cmd, cmd);
3411 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3415 kill_preview_subproc (s);
3418 s->running_preview_error_p = True;
3419 clear_preview_window (s);
3423 switch ((int) (forked = fork ()))
3428 sprintf (buf, "%s: couldn't fork", blurb());
3430 s->running_preview_error_p = True;
3436 close (ConnectionNumber (GDK_DISPLAY()));
3438 hack_subproc_environment (id, s->debug_p);
3440 usleep (250000); /* pause for 1/4th second before launching, to give
3441 the previous program time to die and flush its X
3442 buffer, so we don't get leftover turds on the
3445 exec_command (p->shell, new_cmd, p->nice_inferior);
3446 /* Don't bother printing an error message when we are unable to
3447 exec subprocesses; we handle that by polling the pid later.
3449 Note that one must use _exit() instead of exit() in procs forked
3450 off of Gtk programs -- Gtk installs an atexit handler that has a
3451 copy of the X connection (which we've already closed, for safety.)
3452 If one uses exit() instead of _exit(), then one sometimes gets a
3453 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3455 _exit (1); /* exits child fork */
3460 if (s->running_preview_cmd) free (s->running_preview_cmd);
3461 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3462 s->running_preview_pid = forked;
3466 char *ss = subproc_pretty_name (s);
3467 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3468 (unsigned long) forked, ss);
3475 schedule_preview_check (s);
3478 if (new_cmd) free (new_cmd);
3483 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3486 hack_environment (state *s)
3488 static const char *def_path =
3489 # ifdef DEFAULT_PATH_PREFIX
3490 DEFAULT_PATH_PREFIX;
3495 Display *dpy = GDK_DISPLAY();
3496 const char *odpy = DisplayString (dpy);
3497 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3498 strcpy (ndpy, "DISPLAY=");
3499 strcat (ndpy, odpy);
3504 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3506 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3507 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3508 So we must leak it (and/or the previous setting). Yay.
3511 if (def_path && *def_path)
3513 const char *opath = getenv("PATH");
3514 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3515 strcpy (npath, "PATH=");
3516 strcat (npath, def_path);
3517 strcat (npath, ":");
3518 strcat (npath, opath);
3522 /* do not free(npath) -- see above */
3525 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3531 hack_subproc_environment (Window preview_window_id, Bool debug_p)
3533 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
3534 necessary yet, but it will make programs work if we had invoked
3535 them with "-root" and not with "-window-id" -- which, of course,
3538 char *nssw = (char *) malloc (40);
3539 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
3541 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
3542 any more, right? It's not Posix, but everyone seems to have it. */
3547 fprintf (stderr, "%s: %s\n", blurb(), nssw);
3549 /* do not free(nssw) -- see above */
3553 /* Called from a timer:
3554 Launches the currently-chosen subprocess, if it's not already running.
3555 If there's a different process running, kills it.
3558 update_subproc_timer (gpointer data)
3560 state *s = (state *) data;
3561 if (! s->desired_preview_cmd)
3562 kill_preview_subproc (s);
3563 else if (!s->running_preview_cmd ||
3564 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3565 launch_preview_subproc (s);
3567 s->subproc_timer_id = 0;
3568 return FALSE; /* do not re-execute timer */
3572 /* Call this when you think you might want a preview process running.
3573 It will set a timer that will actually launch that program a second
3574 from now, if you haven't changed your mind (to avoid double-click
3575 spazzing, etc.) `cmd' may be null meaning "no process".
3578 schedule_preview (state *s, const char *cmd)
3580 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3585 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3587 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3590 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3591 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3593 if (s->subproc_timer_id)
3594 gtk_timeout_remove (s->subproc_timer_id);
3595 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3599 /* Called from a timer:
3600 Checks to see if the subproc that should be running, actually is.
3603 check_subproc_timer (gpointer data)
3605 state *s = (state *) data;
3606 Bool again_p = True;
3608 if (s->running_preview_error_p || /* already dead */
3609 s->running_preview_pid <= 0)
3617 status = kill (s->running_preview_pid, 0);
3618 if (status < 0 && errno == ESRCH)
3619 s->running_preview_error_p = True;
3623 char *ss = subproc_pretty_name (s);
3624 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3625 (unsigned long) s->running_preview_pid, ss,
3626 (s->running_preview_error_p ? "dead" : "alive"));
3630 if (s->running_preview_error_p)
3632 clear_preview_window (s);
3637 /* Otherwise, it's currently alive. We might be checking again, or we
3638 might be satisfied. */
3640 if (--s->subproc_check_countdown <= 0)
3644 return TRUE; /* re-execute timer */
3647 s->subproc_check_timer_id = 0;
3648 s->subproc_check_countdown = 0;
3649 return FALSE; /* do not re-execute timer */
3654 /* Call this just after launching a subprocess.
3655 This sets a timer that will, five times a second for two seconds,
3656 check whether the program is still running. The assumption here
3657 is that if the process didn't stay up for more than a couple of
3658 seconds, then either the program doesn't exist, or it doesn't
3659 take a -window-id argument.
3662 schedule_preview_check (state *s)
3668 fprintf (stderr, "%s: scheduling check\n", blurb());
3670 if (s->subproc_check_timer_id)
3671 gtk_timeout_remove (s->subproc_check_timer_id);
3672 s->subproc_check_timer_id =
3673 gtk_timeout_add (1000 / ticks,
3674 check_subproc_timer, (gpointer) s);
3675 s->subproc_check_countdown = ticks * seconds;
3680 screen_blanked_p (void)
3684 unsigned long nitems, bytesafter;
3686 Display *dpy = GDK_DISPLAY();
3687 Bool blanked_p = False;
3689 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3690 XA_SCREENSAVER_STATUS,
3691 0, 3, False, XA_INTEGER,
3692 &type, &format, &nitems, &bytesafter,
3693 (unsigned char **) &data)
3695 && type == XA_INTEGER
3698 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3700 if (data) free (data);
3705 /* Wake up every now and then and see if the screen is blanked.
3706 If it is, kill off the small-window demo -- no point in wasting
3707 cycles by running two screensavers at once...
3710 check_blanked_timer (gpointer data)
3712 state *s = (state *) data;
3713 Bool blanked_p = screen_blanked_p ();
3714 if (blanked_p && s->running_preview_pid)
3717 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3718 kill_preview_subproc (s);
3721 return True; /* re-execute timer */
3725 /* Setting window manager icon
3729 init_icon (GdkWindow *window)
3731 GdkBitmap *mask = 0;
3734 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3735 (gchar **) logo_50_xpm);
3737 gdk_window_set_icon (window, 0, pixmap, mask);
3741 /* The main demo-mode command loop.
3746 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3747 XrmRepresentation *type, XrmValue *value, XPointer closure)
3750 for (i = 0; quarks[i]; i++)
3752 if (bindings[i] == XrmBindTightly)
3753 fprintf (stderr, (i == 0 ? "" : "."));
3754 else if (bindings[i] == XrmBindLoosely)
3755 fprintf (stderr, "*");
3757 fprintf (stderr, " ??? ");
3758 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3761 fprintf (stderr, ": %s\n", (char *) value->addr);
3769 the_network_is_not_the_computer (state *s)
3771 Display *dpy = GDK_DISPLAY();
3772 char *rversion = 0, *ruser = 0, *rhost = 0;
3773 char *luser, *lhost;
3775 struct passwd *p = getpwuid (getuid ());
3776 const char *d = DisplayString (dpy);
3778 # if defined(HAVE_UNAME)
3780 if (uname (&uts) < 0)
3781 lhost = "<UNKNOWN>";
3783 lhost = uts.nodename;
3785 strcpy (lhost, getenv("SYS$NODE"));
3786 # else /* !HAVE_UNAME && !VMS */
3787 strcat (lhost, "<UNKNOWN>");
3788 # endif /* !HAVE_UNAME && !VMS */
3790 if (p && p->pw_name)
3795 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3797 /* Make a buffer that's big enough for a number of copies of all the
3798 strings, plus some. */
3799 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3800 (ruser ? strlen(ruser) : 0) +
3801 (rhost ? strlen(rhost) : 0) +
3808 if (!rversion || !*rversion)
3812 "The XScreenSaver daemon doesn't seem to be running\n"
3813 "on display \"%s\". Launch it now?"),
3816 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3818 /* Warn that the two processes are running as different users.
3822 "%s is running as user \"%s\" on host \"%s\".\n"
3823 "But the xscreensaver managing display \"%s\"\n"
3824 "is running as user \"%s\" on host \"%s\".\n"
3826 "Since they are different users, they won't be reading/writing\n"
3827 "the same ~/.xscreensaver file, so %s isn't\n"
3828 "going to work right.\n"
3830 "You should either re-run %s as \"%s\", or re-run\n"
3831 "xscreensaver as \"%s\".\n"
3833 "Restart the xscreensaver daemon now?\n"),
3834 progname, luser, lhost,
3836 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3838 progname, (ruser ? ruser : "???"),
3841 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3843 /* Warn that the two processes are running on different hosts.
3847 "%s is running as user \"%s\" on host \"%s\".\n"
3848 "But the xscreensaver managing display \"%s\"\n"
3849 "is running as user \"%s\" on host \"%s\".\n"
3851 "If those two machines don't share a file system (that is,\n"
3852 "if they don't see the same ~%s/.xscreensaver file) then\n"
3853 "%s won't work right.\n"
3855 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3856 progname, luser, lhost,
3858 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3863 else if (!!strcmp (rversion, s->short_version))
3865 /* Warn that the version numbers don't match.
3869 "This is %s version %s.\n"
3870 "But the xscreensaver managing display \"%s\"\n"
3871 "is version %s. This could cause problems.\n"
3873 "Restart the xscreensaver daemon now?\n"),
3874 progname, s->short_version,
3881 warning_dialog (s->toplevel_widget, msg, True, 1);
3883 if (rversion) free (rversion);
3884 if (ruser) free (ruser);
3885 if (rhost) free (rhost);
3890 /* We use this error handler so that X errors are preceeded by the name
3891 of the program that generated them.
3894 demo_ehandler (Display *dpy, XErrorEvent *error)
3896 state *s = global_state_kludge; /* I hate C so much... */
3897 fprintf (stderr, "\nX error in %s:\n", blurb());
3898 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3899 kill_preview_subproc (s);
3905 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3906 of the program that generated them; and also that we can ignore one
3907 particular bogus error message that Gdk madly spews.
3910 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3911 const gchar *message, gpointer user_data)
3913 /* Ignore the message "Got event for unknown window: 0x...".
3914 Apparently some events are coming in for the xscreensaver window
3915 (presumably reply events related to the ClientMessage) and Gdk
3916 feels the need to complain about them. So, just suppress any
3917 messages that look like that one.
3919 if (strstr (message, "unknown window"))
3922 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3923 (log_domain ? log_domain : progclass),
3924 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3925 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3926 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3927 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3928 log_level == G_LOG_LEVEL_INFO ? "info" :
3929 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3931 ((!*message || message[strlen(message)-1] != '\n')
3937 __extension__ /* shut up about "string length is greater than the length
3938 ISO C89 compilers are required to support" when including
3942 static char *defaults[] = {
3943 #include "XScreenSaver_ad.h"
3948 #ifdef HAVE_CRAPPLET
3949 static struct poptOption crapplet_options[] = {
3950 {NULL, '\0', 0, NULL, 0}
3952 #endif /* HAVE_CRAPPLET */
3955 const char *usage = "[--display dpy] [--prefs]"
3956 # ifdef HAVE_CRAPPLET
3959 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
3962 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3964 state *s = (state *) user_data;
3965 Boolean oi = s->initializing_p;
3966 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3967 s->initializing_p = True;
3968 eschew_gtk_lossage (label);
3969 s->initializing_p = oi;
3975 print_widget_tree (GtkWidget *w, int depth)
3978 for (i = 0; i < depth; i++)
3979 fprintf (stderr, " ");
3980 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3982 if (GTK_IS_LIST (w))
3984 for (i = 0; i < depth+1; i++)
3985 fprintf (stderr, " ");
3986 fprintf (stderr, "...list kids...\n");
3988 else if (GTK_IS_CONTAINER (w))
3990 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3993 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4001 delayed_scroll_kludge (gpointer data)
4003 state *s = (state *) data;
4004 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4005 ensure_selected_item_visible (w);
4007 /* Oh, this is just fucking lovely, too. */
4008 w = GTK_WIDGET (name_to_widget (s, "preview"));
4009 gtk_widget_hide (w);
4010 gtk_widget_show (w);
4012 return FALSE; /* do not re-execute timer */
4018 create_xscreensaver_demo (void)
4022 nb = name_to_widget (global_state_kludge, "preview_notebook");
4023 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4025 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4029 create_xscreensaver_settings_dialog (void)
4033 box = name_to_widget (global_state_kludge, "dialog_action_area");
4035 w = name_to_widget (global_state_kludge, "adv_button");
4036 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4038 w = name_to_widget (global_state_kludge, "std_button");
4039 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4041 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4044 #endif /* HAVE_GTK2 */
4047 main (int argc, char **argv)
4051 saver_preferences *p;
4055 Widget toplevel_shell;
4056 char *real_progname = argv[0];
4059 Bool crapplet_p = False;
4063 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4064 textdomain (GETTEXT_PACKAGE);
4067 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4068 # else /* !HAVE_GTK2 */
4069 if (!setlocale (LC_ALL, ""))
4070 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4071 # endif /* !HAVE_GTK2 */
4073 #endif /* ENABLE_NLS */
4075 str = strrchr (real_progname, '/');
4076 if (str) real_progname = str+1;
4079 memset (s, 0, sizeof(*s));
4080 s->initializing_p = True;
4083 global_state_kludge = s; /* I hate C so much... */
4085 progname = real_progname;
4087 s->short_version = (char *) malloc (5);
4088 memcpy (s->short_version, screensaver_id + 17, 4);
4089 s->short_version [4] = 0;
4092 /* Register our error message logger for every ``log domain'' known.
4093 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4094 for all of the domains that seem to be in use.
4097 const char * const domains[] = { 0,
4098 "Gtk", "Gdk", "GLib", "GModule",
4099 "GThread", "Gnome", "GnomeUI" };
4100 for (i = 0; i < countof(domains); i++)
4101 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4104 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4107 const char *dir = DEFAULT_ICONDIR;
4108 if (*dir) add_pixmap_directory (dir);
4110 # endif /* !HAVE_GTK2 */
4111 #endif /* DEFAULT_ICONDIR */
4113 /* This is gross, but Gtk understands --display and not -display...
4115 for (i = 1; i < argc; i++)
4116 if (argv[i][0] && argv[i][1] &&
4117 !strncmp(argv[i], "-display", strlen(argv[i])))
4118 argv[i] = "--display";
4121 /* We need to parse this arg really early... Sigh. */
4122 for (i = 1; i < argc; i++)
4125 (!strcmp(argv[i], "--crapplet") ||
4126 !strcmp(argv[i], "--capplet")))
4128 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4131 for (j = i; j < argc; j++) /* remove it from the list */
4132 argv[j] = argv[j+1];
4134 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4135 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4137 fprintf (stderr, "%s: %s\n", real_progname, usage);
4139 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4142 (!strcmp(argv[i], "--debug") ||
4143 !strcmp(argv[i], "-debug") ||
4144 !strcmp(argv[i], "-d")))
4148 for (j = i; j < argc; j++) /* remove it from the list */
4149 argv[j] = argv[j+1];
4156 (!strcmp(argv[i], "-geometry") ||
4157 !strcmp(argv[i], "-geom") ||
4158 !strcmp(argv[i], "-geo") ||
4159 !strcmp(argv[i], "-g")))
4163 for (j = i; j < argc; j++) /* remove them from the list */
4164 argv[j] = argv[j+2];
4171 (!strcmp(argv[i], "--configdir")))
4175 hack_configuration_path = argv[i+1];
4176 for (j = i; j < argc; j++) /* remove them from the list */
4177 argv[j] = argv[j+2];
4181 if (0 != stat (hack_configuration_path, &st))
4184 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4188 else if (!S_ISDIR (st.st_mode))
4190 fprintf (stderr, "%s: not a directory: %s\n",
4191 blurb(), hack_configuration_path);
4199 fprintf (stderr, "%s: using config directory \"%s\"\n",
4200 progname, hack_configuration_path);
4203 /* Let Gtk open the X connection, then initialize Xt to use that
4204 same connection. Doctor Frankenstein would be proud.
4206 # ifdef HAVE_CRAPPLET
4209 GnomeClient *client;
4210 GnomeClientFlags flags = 0;
4212 int init_results = gnome_capplet_init ("screensaver-properties",
4214 argc, argv, NULL, 0, NULL);
4216 0 upon successful initialization;
4217 1 if --init-session-settings was passed on the cmdline;
4218 2 if --ignore was passed on the cmdline;
4221 So the 1 signifies just to init the settings, and quit, basically.
4222 (Meaning launch the xscreensaver daemon.)
4225 if (init_results < 0)
4228 g_error ("An initialization error occurred while "
4229 "starting xscreensaver-capplet.\n");
4231 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4232 real_progname, init_results);
4237 client = gnome_master_client ();
4240 flags = gnome_client_get_flags (client);
4242 if (flags & GNOME_CLIENT_IS_CONNECTED)
4245 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4246 gnome_client_get_id (client));
4249 char *session_args[20];
4251 session_args[i++] = real_progname;
4252 session_args[i++] = "--capplet";
4253 session_args[i++] = "--init-session-settings";
4254 session_args[i] = 0;
4255 gnome_client_set_priority (client, 20);
4256 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4257 gnome_client_set_restart_command (client, i, session_args);
4261 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4264 gnome_client_flush (client);
4267 if (init_results == 1)
4269 system ("xscreensaver -nosplash &");
4275 # endif /* HAVE_CRAPPLET */
4277 gtk_init (&argc, &argv);
4281 /* We must read exactly the same resources as xscreensaver.
4282 That means we must have both the same progclass *and* progname,
4283 at least as far as the resource database is concerned. So,
4284 put "xscreensaver" in argv[0] while initializing Xt.
4286 argv[0] = "xscreensaver";
4290 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4292 XtToolkitInitialize ();
4293 app = XtCreateApplicationContext ();
4294 dpy = GDK_DISPLAY();
4295 XtAppSetFallbackResources (app, defaults);
4296 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4297 toplevel_shell = XtAppCreateShell (progname, progclass,
4298 applicationShellWidgetClass,
4301 dpy = XtDisplay (toplevel_shell);
4302 db = XtDatabase (dpy);
4303 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4304 XSetErrorHandler (demo_ehandler);
4306 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4307 signal (SIGPIPE, SIG_IGN);
4309 /* After doing Xt-style command-line processing, complain about any
4310 unrecognized command-line arguments.
4312 for (i = 1; i < argc; i++)
4314 char *str = argv[i];
4315 if (str[0] == '-' && str[1] == '-')
4317 if (!strcmp (str, "-prefs"))
4319 else if (crapplet_p)
4320 /* There are lots of random args that we don't care about when we're
4321 started as a crapplet, so just ignore unknown args in that case. */
4325 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4327 fprintf (stderr, "%s: %s\n", real_progname, usage);
4332 /* Load the init file, which may end up consulting the X resource database
4333 and the site-wide app-defaults file. Note that at this point, it's
4334 important that `progname' be "xscreensaver", rather than whatever
4339 hack_environment (s); /* must be before initialize_sort_map() */
4342 initialize_sort_map (s);
4344 /* Now that Xt has been initialized, and the resources have been read,
4345 we can set our `progname' variable to something more in line with
4348 progname = real_progname;
4352 /* Print out all the resources we read. */
4354 XrmName name = { 0 };
4355 XrmClass class = { 0 };
4357 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4363 /* Intern the atoms that xscreensaver_command() needs.
4365 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4366 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4367 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4368 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4369 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4370 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4371 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4372 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4373 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4374 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4375 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4376 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4377 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4380 /* Create the window and all its widgets.
4382 s->base_widget = create_xscreensaver_demo ();
4383 s->popup_widget = create_xscreensaver_settings_dialog ();
4384 s->toplevel_widget = s->base_widget;
4387 /* Set the main window's title. */
4389 char *base_title = _("Screensaver Preferences");
4390 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4391 char *s1, *s2, *s3, *s4;
4392 s1 = (char *) strchr(v, ' '); s1++;
4393 s2 = (char *) strchr(s1, ' ');
4394 s3 = (char *) strchr(v, '('); s3++;
4395 s4 = (char *) strchr(s3, ')');
4399 window_title = (char *) malloc (strlen (base_title) +
4400 strlen (progclass) +
4401 strlen (s1) + strlen (s3) +
4403 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4404 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4405 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4409 /* Adjust the (invisible) notebooks on the popup dialog... */
4411 GtkNotebook *notebook =
4412 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4413 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4417 gtk_widget_hide (std);
4418 # else /* !HAVE_XML */
4419 /* Make the advanced page be the only one available. */
4420 gtk_widget_set_sensitive (std, False);
4421 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4422 gtk_widget_hide (std);
4424 # endif /* !HAVE_XML */
4426 gtk_notebook_set_page (notebook, page);
4427 gtk_notebook_set_show_tabs (notebook, False);
4430 /* Various other widget initializations...
4432 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4433 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4435 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4436 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4439 populate_hack_list (s);
4440 populate_prefs_page (s);
4441 sensitize_demo_widgets (s, False);
4442 fix_text_entry_sizes (s);
4443 scroll_to_current_hack (s);
4445 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4446 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4450 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4451 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4453 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4454 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4456 #endif /* !HAVE_GTK2 */
4458 /* Hook up callbacks to the items on the mode menu. */
4460 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4461 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4462 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4463 for (; kids; kids = kids->next)
4464 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4465 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4470 /* Handle the -prefs command-line argument. */
4473 GtkNotebook *notebook =
4474 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4475 gtk_notebook_set_page (notebook, 1);
4478 # ifdef HAVE_CRAPPLET
4482 GtkWidget *outer_vbox;
4484 gtk_widget_hide (s->toplevel_widget);
4486 capplet = capplet_widget_new ();
4488 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4489 # ifdef HAVE_CRAPPLET_IMMEDIATE
4490 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4491 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4492 /* In crapplet-mode, take off the menubar. */
4493 gtk_widget_hide (name_to_widget (s, "menubar"));
4495 /* Reparent our top-level container to be a child of the capplet
4498 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4499 gtk_widget_ref (outer_vbox);
4500 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4502 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4503 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4505 /* Find the window above us, and set the title and close handler. */
4507 GtkWidget *window = capplet;
4508 while (window && !GTK_IS_WINDOW (window))
4509 window = window->parent;
4512 gtk_window_set_title (GTK_WINDOW (window), window_title);
4513 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4514 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4519 s->toplevel_widget = capplet;
4521 # endif /* HAVE_CRAPPLET */
4524 /* The Gnome folks hate the menubar. I think it's important to have access
4525 to the commands on the File menu (Restart Daemon, etc.) and to the
4526 About and Documentation commands on the Help menu.
4530 gtk_widget_hide (name_to_widget (s, "menubar"));
4534 free (window_title);
4538 /* After picking the default size, allow -geometry to override it. */
4540 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
4543 gtk_widget_show (s->toplevel_widget);
4544 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4545 fix_preview_visual (s);
4547 /* Realize page zero, so that we can diddle the scrollbar when the
4548 user tabs back to it -- otherwise, the current hack isn't scrolled
4549 to the first time they tab back there, when started with "-prefs".
4550 (Though it is if they then tab away, and back again.)
4552 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4553 #### understands this crap, explain to me how to make this work.
4555 gtk_widget_realize (name_to_widget (s, "demos_table"));
4558 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4561 /* Issue any warnings about the running xscreensaver daemon. */
4562 the_network_is_not_the_computer (s);
4565 /* Run the Gtk event loop, and not the Xt event loop. This means that
4566 if there were Xt timers or fds registered, they would never get serviced,
4567 and if there were any Xt widgets, they would never have events delivered.
4568 Fortunately, we're using Gtk for all of the UI, and only initialized
4569 Xt so that we could process the command line and use the X resource
4572 s->initializing_p = False;
4574 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4575 after we start up. Otherwise, it always appears scrolled to the top
4576 when in crapplet-mode. */
4577 gtk_timeout_add (500, delayed_scroll_kludge, s);
4581 /* Load every configurator in turn, to scan them for errors all at once. */
4584 for (i = 0; i < p->screenhacks_count; i++)
4586 screenhack *hack = p->screenhacks[i];
4587 conf_data *d = load_configurator (hack->command, False);
4588 if (d) free_conf_data (d);
4594 # ifdef HAVE_CRAPPLET
4596 capplet_gtk_main ();
4598 # endif /* HAVE_CRAPPLET */
4601 kill_preview_subproc (s);
4605 #endif /* HAVE_GTK -- whole file */