1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2006 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>
82 # include <X11/extensions/Xinerama.h>
83 #endif /* HAVE_XINERAMA */
89 # include <capplet-widget.h>
95 # include <glade/glade-xml.h>
97 #else /* !HAVE_GTK2 */
98 # define G_MODULE_EXPORT /**/
99 #endif /* !HAVE_GTK2 */
101 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
102 # define GLADE_DIR DEFAULT_ICONDIR
104 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
105 # define DEFAULT_ICONDIR GLADE_DIR
109 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
110 It is unused otherwise, so in that case, stub it out. */
111 static const char *hack_configuration_path = 0;
118 #include "resources.h" /* for parse_time() */
119 #include "visual.h" /* for has_writable_cells() */
120 #include "remote.h" /* for xscreensaver_command() */
123 #include "logo-50.xpm"
124 #include "logo-180.xpm"
126 #undef dgettext /* else these are defined twice... */
129 #include "demo-Gtk-widgets.h"
130 #include "demo-Gtk-support.h"
131 #include "demo-Gtk-conf.h"
143 #endif /* HAVE_GTK2 */
146 extern void exec_command (const char *shell, const char *command, int nice);
147 extern int on_path_p (const char *program);
149 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
152 #define countof(x) (sizeof((x))/sizeof((*x)))
156 char *progclass = "XScreenSaver";
159 /* The order of the items in the mode menu. */
160 static int mode_menu_order[] = {
161 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
166 char *short_version; /* version number of this xscreensaver build */
168 GtkWidget *toplevel_widget; /* the main window */
169 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
170 GtkWidget *popup_widget; /* the "Settings" dialog */
171 conf_data *cdata; /* private data for per-hack configuration */
174 GladeXML *glade_ui; /* Glade UI file */
175 #endif /* HAVE_GTK2 */
177 Bool debug_p; /* whether to print diagnostics */
178 Bool initializing_p; /* flag for breaking recursion loops */
179 Bool saving_p; /* flag for breaking recursion loops */
181 char *desired_preview_cmd; /* subprocess we intend to run */
182 char *running_preview_cmd; /* subprocess we are currently running */
183 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
184 Bool running_preview_error_p; /* whether the pid died abnormally */
186 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
187 int subproc_timer_id; /* timer to delay subproc launch */
188 int subproc_check_timer_id; /* timer to check whether it started up */
189 int subproc_check_countdown; /* how many more checks left */
191 int *list_elt_to_hack_number; /* table for sorting the hack list */
192 int *hack_number_to_list_elt; /* the inverse table */
193 Bool *hacks_available_p; /* whether hacks are on $PATH */
194 int total_available; /* how many are on $PATH */
195 int list_count; /* how many items are in the list: this may be
196 less than p->screenhacks_count, if some are
199 int _selected_list_element; /* don't use this: call
200 selected_list_element() instead */
202 int nscreens; /* How many X or Xinerama screens there are */
204 saver_preferences prefs;
209 /* Total fucking evilness due to the fact that it's rocket science to get
210 a closure object of our own down into the various widget callbacks. */
211 static state *global_state_kludge;
214 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
215 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
216 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
219 static void populate_demo_window (state *, int list_elt);
220 static void populate_prefs_page (state *);
221 static void populate_popup_window (state *);
223 static Bool flush_dialog_changes_and_save (state *);
224 static Bool flush_popup_changes_and_save (state *);
226 static int maybe_reload_init_file (state *);
227 static void await_xscreensaver (state *);
228 static Bool xscreensaver_running_p (state *);
229 static void sensitize_menu_items (state *s, Bool force_p);
230 static void force_dialog_repaint (state *s);
232 static void schedule_preview (state *, const char *cmd);
233 static void kill_preview_subproc (state *, Bool reset_p);
234 static void schedule_preview_check (state *);
238 /* Some random utility functions
244 time_t now = time ((time_t *) 0);
245 char *ct = (char *) ctime (&now);
246 static char buf[255];
247 int n = strlen(progname);
249 strncpy(buf, progname, n);
252 strncpy(buf+n, ct+11, 8);
253 strcpy(buf+n+9, ": ");
259 name_to_widget (state *s, const char *name)
269 /* First try to load the Glade file from the current directory;
270 if there isn't one there, check the installed directory.
272 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
273 const char * const files[] = { GLADE_FILE_NAME,
274 GLADE_DIR "/" GLADE_FILE_NAME };
276 for (i = 0; i < countof (files); i++)
279 if (!stat (files[i], &st))
281 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
288 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
289 "\tfrom " GLADE_DIR "/ or current directory.\n",
293 # undef GLADE_FILE_NAME
295 glade_xml_signal_autoconnect (s->glade_ui);
298 w = glade_xml_get_widget (s->glade_ui, name);
300 #else /* !HAVE_GTK2 */
302 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
305 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
307 #endif /* HAVE_GTK2 */
310 fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
316 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
317 Takes a scroller, viewport, or list as an argument.
320 ensure_selected_item_visible (GtkWidget *widget)
324 GtkTreeSelection *selection;
328 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
329 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
330 path = gtk_tree_path_new_first ();
332 path = gtk_tree_model_get_path (model, &iter);
334 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
336 gtk_tree_path_free (path);
338 #else /* !HAVE_GTK2 */
340 GtkScrolledWindow *scroller = 0;
342 GtkList *list_widget = 0;
346 GtkWidget *selected = 0;
349 gint parent_h, child_y, child_h, children_h, ignore;
350 double ratio_t, ratio_b;
352 if (GTK_IS_SCROLLED_WINDOW (widget))
354 scroller = GTK_SCROLLED_WINDOW (widget);
355 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
356 list_widget = GTK_LIST (GTK_BIN(vp)->child);
358 else if (GTK_IS_VIEWPORT (widget))
360 vp = GTK_VIEWPORT (widget);
361 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
362 list_widget = GTK_LIST (GTK_BIN(vp)->child);
364 else if (GTK_IS_LIST (widget))
366 list_widget = GTK_LIST (widget);
367 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
368 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
373 slist = list_widget->selection;
374 selected = (slist ? GTK_WIDGET (slist->data) : 0);
378 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
380 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
381 kids; kids = kids->next)
384 adj = gtk_scrolled_window_get_vadjustment (scroller);
386 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
387 &ignore, &ignore, &ignore, &parent_h, &ignore);
388 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
389 &ignore, &child_y, &ignore, &child_h, &ignore);
390 children_h = nkids * child_h;
392 ratio_t = ((double) child_y) / ((double) children_h);
393 ratio_b = ((double) child_y + child_h) / ((double) children_h);
395 if (adj->upper == 0.0) /* no items in list */
398 if (ratio_t < (adj->value / adj->upper) ||
399 ratio_b > ((adj->value + adj->page_size) / adj->upper))
402 int slop = parent_h * 0.75; /* how much to overshoot by */
404 if (ratio_t < (adj->value / adj->upper))
406 double ratio_w = ((double) parent_h) / ((double) children_h);
407 double ratio_l = (ratio_b - ratio_t);
408 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
411 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
413 target = ratio_t * adj->upper;
417 if (target > adj->upper - adj->page_size)
418 target = adj->upper - adj->page_size;
422 gtk_adjustment_set_value (adj, target);
424 #endif /* !HAVE_GTK2 */
428 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
430 GtkWidget *shell = GTK_WIDGET (user_data);
431 while (shell->parent)
432 shell = shell->parent;
433 gtk_widget_destroy (GTK_WIDGET (shell));
437 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
439 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
441 restart_menu_cb (widget, user_data);
442 warning_dialog_dismiss_cb (widget, user_data);
446 warning_dialog (GtkWidget *parent, const char *message,
447 Boolean restart_button_p, int center)
449 char *msg = strdup (message);
452 GtkWidget *dialog = gtk_dialog_new ();
453 GtkWidget *label = 0;
455 GtkWidget *cancel = 0;
458 while (parent && !parent->window)
459 parent = parent->parent;
462 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
464 fprintf (stderr, "%s: too early for dialog?\n", progname);
472 char *s = strchr (head, '\n');
475 sprintf (name, "label%d", i++);
478 label = gtk_label_new (head);
480 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
481 #endif /* HAVE_GTK2 */
486 GTK_WIDGET (label)->style =
487 gtk_style_copy (GTK_WIDGET (label)->style);
488 GTK_WIDGET (label)->style->font =
489 gdk_font_load (get_string_resource("warning_dialog.headingFont",
491 gtk_widget_set_style (GTK_WIDGET (label),
492 GTK_WIDGET (label)->style);
494 #endif /* !HAVE_GTK2 */
496 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
497 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
498 label, TRUE, TRUE, 0);
499 gtk_widget_show (label);
510 label = gtk_label_new ("");
511 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
512 label, TRUE, TRUE, 0);
513 gtk_widget_show (label);
515 label = gtk_hbutton_box_new ();
516 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
517 label, TRUE, TRUE, 0);
520 if (restart_button_p)
522 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
523 gtk_container_add (GTK_CONTAINER (label), cancel);
526 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
527 gtk_container_add (GTK_CONTAINER (label), ok);
529 #else /* !HAVE_GTK2 */
531 ok = gtk_button_new_with_label ("OK");
532 gtk_container_add (GTK_CONTAINER (label), ok);
534 if (restart_button_p)
536 cancel = gtk_button_new_with_label ("Cancel");
537 gtk_container_add (GTK_CONTAINER (label), cancel);
540 #endif /* !HAVE_GTK2 */
542 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
543 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
544 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
545 STFU GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
546 gtk_widget_show (ok);
547 gtk_widget_grab_focus (ok);
551 STFU GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
552 gtk_widget_show (cancel);
554 gtk_widget_show (label);
555 gtk_widget_show (dialog);
557 if (restart_button_p)
559 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
560 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
562 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
563 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
568 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
569 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
573 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
574 GTK_WIDGET (parent)->window);
577 gtk_window_present (GTK_WINDOW (dialog));
578 #else /* !HAVE_GTK2 */
579 gdk_window_show (GTK_WIDGET (dialog)->window);
580 gdk_window_raise (GTK_WIDGET (dialog)->window);
581 #endif /* !HAVE_GTK2 */
588 run_cmd (state *s, Atom command, int arg)
593 flush_dialog_changes_and_save (s);
594 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
596 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
597 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
604 sprintf (buf, "Error:\n\n%s", err);
606 strcpy (buf, "Unknown error!");
607 warning_dialog (s->toplevel_widget, buf, False, 100);
611 sensitize_menu_items (s, True);
612 force_dialog_repaint (s);
617 run_hack (state *s, int list_elt, Bool report_errors_p)
623 if (list_elt < 0) return;
624 hack_number = s->list_elt_to_hack_number[list_elt];
626 flush_dialog_changes_and_save (s);
627 schedule_preview (s, 0);
629 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
632 if (status < 0 && report_errors_p)
634 if (xscreensaver_running_p (s))
636 /* Kludge: ignore the spurious "window unexpectedly deleted"
638 if (err && strstr (err, "unexpectedly deleted"))
645 sprintf (buf, "Error:\n\n%s", err);
647 strcpy (buf, "Unknown error!");
648 warning_dialog (s->toplevel_widget, buf, False, 100);
653 /* The error is that the daemon isn't running;
656 const char *d = DisplayString (GDK_DISPLAY());
660 "The XScreenSaver daemon doesn't seem to be running\n"
661 "on display \"%s\". Launch it now?"),
663 warning_dialog (s->toplevel_widget, msg, True, 1);
669 sensitize_menu_items (s, False);
676 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
677 libglade work on Cygwin; apparently all Glade callbacks need this magic
678 extra declaration. I do not pretend to understand.
682 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
684 state *s = global_state_kludge; /* I hate C so much... */
685 flush_dialog_changes_and_save (s);
686 kill_preview_subproc (s, False);
691 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
693 state *s = (state *) data;
694 flush_dialog_changes_and_save (s);
701 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
704 char *vers = strdup (screensaver_id + 4);
707 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
709 s = strchr (vers, ',');
713 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
714 non-ASCII characters aren't allowed in localizable string keys."
715 (I don't want to just use (c) instead of © because that doesn't
716 look as good in the plain-old default Latin1 "C" locale.)
719 sprintf(copy, ("Copyright \xC2\xA9 1991-2005 %s"), s);
720 #else /* !HAVE_GTK2 */
721 sprintf(copy, ("Copyright \251 1991-2005 %s"), s);
722 #endif /* !HAVE_GTK2 */
724 sprintf (msg, "%s\n\n%s", copy, desc);
726 /* I can't make gnome_about_new() work here -- it starts dying in
727 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
728 then this might be the thing to do:
732 const gchar *auth[] = { 0 };
733 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
735 gtk_widget_show (about);
737 #else / * GTK but not GNOME * /
741 GdkColormap *colormap;
742 GdkPixmap *gdkpixmap;
745 GtkWidget *dialog = gtk_dialog_new ();
746 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
747 GtkWidget *parent = GTK_WIDGET (menuitem);
748 while (parent->parent)
749 parent = parent->parent;
751 hbox = gtk_hbox_new (FALSE, 20);
752 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
753 hbox, TRUE, TRUE, 0);
755 colormap = gtk_widget_get_colormap (parent);
757 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
758 (gchar **) logo_180_xpm);
759 icon = gtk_pixmap_new (gdkpixmap, mask);
760 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
762 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
764 vbox = gtk_vbox_new (FALSE, 0);
765 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
767 label1 = gtk_label_new (vers);
768 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
769 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
770 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
773 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
774 GTK_WIDGET (label1)->style->font =
775 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
776 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
777 #endif /* HAVE_GTK2 */
779 label2 = gtk_label_new (msg);
780 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
781 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
782 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
785 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
786 GTK_WIDGET (label2)->style->font =
787 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
788 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
789 #endif /* HAVE_GTK2 */
791 hb = gtk_hbutton_box_new ();
793 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
797 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
798 #else /* !HAVE_GTK2 */
799 ok = gtk_button_new_with_label (_("OK"));
800 #endif /* !HAVE_GTK2 */
801 gtk_container_add (GTK_CONTAINER (hb), ok);
803 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
804 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
805 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
807 gtk_widget_show (hbox);
808 gtk_widget_show (icon);
809 gtk_widget_show (vbox);
810 gtk_widget_show (label1);
811 gtk_widget_show (label2);
812 gtk_widget_show (hb);
813 gtk_widget_show (ok);
814 gtk_widget_show (dialog);
816 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
817 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
819 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
820 GTK_WIDGET (parent)->window);
821 gdk_window_show (GTK_WIDGET (dialog)->window);
822 gdk_window_raise (GTK_WIDGET (dialog)->window);
828 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
830 state *s = global_state_kludge; /* I hate C so much... */
831 saver_preferences *p = &s->prefs;
834 if (!p->help_url || !*p->help_url)
836 warning_dialog (s->toplevel_widget,
838 "No Help URL has been specified.\n"), False, 100);
842 help_command = (char *) malloc (strlen (p->load_url_command) +
843 (strlen (p->help_url) * 4) + 20);
844 strcpy (help_command, "( ");
845 sprintf (help_command + strlen(help_command),
847 p->help_url, p->help_url, p->help_url, p->help_url);
848 strcat (help_command, " ) &");
849 if (system (help_command) < 0)
850 fprintf (stderr, "%s: fork error\n", blurb());
856 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
858 state *s = global_state_kludge; /* I hate C so much... */
859 sensitize_menu_items (s, False);
864 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
866 state *s = global_state_kludge; /* I hate C so much... */
867 run_cmd (s, XA_ACTIVATE, 0);
872 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
874 state *s = global_state_kludge; /* I hate C so much... */
875 run_cmd (s, XA_LOCK, 0);
880 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
882 state *s = global_state_kludge; /* I hate C so much... */
883 run_cmd (s, XA_EXIT, 0);
888 restart_menu_cb (GtkWidget *widget, gpointer user_data)
890 state *s = global_state_kludge; /* I hate C so much... */
891 flush_dialog_changes_and_save (s);
892 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
894 if (system ("xscreensaver -nosplash &") < 0)
895 fprintf (stderr, "%s: fork error\n", blurb());
897 await_xscreensaver (s);
901 xscreensaver_running_p (state *s)
903 Display *dpy = GDK_DISPLAY();
905 server_xscreensaver_version (dpy, &rversion, 0, 0);
913 await_xscreensaver (state *s)
918 while (!ok && (--countdown > 0))
919 if (xscreensaver_running_p (s))
922 sleep (1); /* If it's not there yet, wait a second... */
924 sensitize_menu_items (s, True);
928 /* Timed out, no screensaver running. */
931 Bool root_p = (geteuid () == 0);
935 "The xscreensaver daemon did not start up properly.\n"
941 __extension__ /* don't warn about "string length is greater than
942 the length ISO C89 compilers are required to
943 support" in the following expression... */
946 _("You are running as root. This usually means that xscreensaver\n"
947 "was unable to contact your X server because access control is\n"
948 "turned on. Try running this command:\n"
950 " xhost +localhost\n"
952 "and then selecting `File / Restart Daemon'.\n"
954 "Note that turning off access control will allow anyone logged\n"
955 "on to this machine to access your screen, which might be\n"
956 "considered a security problem. Please read the xscreensaver\n"
957 "manual and FAQ for more information.\n"
959 "You shouldn't run X as root. Instead, you should log in as a\n"
960 "normal user, and `su' as necessary."));
962 strcat (buf, _("Please check your $PATH and permissions."));
964 warning_dialog (s->toplevel_widget, buf, False, 1);
967 force_dialog_repaint (s);
972 selected_list_element (state *s)
974 return s->_selected_list_element;
979 demo_write_init_file (state *s, saver_preferences *p)
983 /* #### try to figure out why shit keeps getting reordered... */
984 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
988 if (!write_init_file (p, s->short_version, False))
991 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
996 const char *f = init_file_name();
998 warning_dialog (s->toplevel_widget,
999 _("Error:\n\nCouldn't determine init file name!\n"),
1003 char *b = (char *) malloc (strlen(f) + 1024);
1004 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1005 warning_dialog (s->toplevel_widget, b, False, 100);
1013 G_MODULE_EXPORT void
1014 run_this_cb (GtkButton *button, gpointer user_data)
1016 state *s = global_state_kludge; /* I hate C so much... */
1017 int list_elt = selected_list_element (s);
1018 if (list_elt < 0) return;
1019 if (!flush_dialog_changes_and_save (s))
1020 run_hack (s, list_elt, True);
1024 G_MODULE_EXPORT void
1025 manual_cb (GtkButton *button, gpointer user_data)
1027 state *s = global_state_kludge; /* I hate C so much... */
1028 saver_preferences *p = &s->prefs;
1029 GtkWidget *list_widget = name_to_widget (s, "list");
1030 int list_elt = selected_list_element (s);
1032 char *name, *name2, *cmd, *str;
1034 if (list_elt < 0) return;
1035 hack_number = s->list_elt_to_hack_number[list_elt];
1037 flush_dialog_changes_and_save (s);
1038 ensure_selected_item_visible (list_widget);
1040 name = strdup (p->screenhacks[hack_number]->command);
1043 while (isspace (*name2)) name2++;
1045 while (*str && !isspace (*str)) str++;
1047 str = strrchr (name2, '/');
1048 if (str) name = str+1;
1050 cmd = get_string_resource ("manualCommand", "ManualCommand");
1053 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1054 strcpy (cmd2, "( ");
1055 sprintf (cmd2 + strlen (cmd2),
1057 name2, name2, name2, name2);
1058 strcat (cmd2, " ) &");
1059 if (system (cmd2) < 0)
1060 fprintf (stderr, "%s: fork error\n", blurb());
1065 warning_dialog (GTK_WIDGET (button),
1066 _("Error:\n\nno `manualCommand' resource set."),
1075 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1077 GtkWidget *parent = name_to_widget (s, "scroller");
1078 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
1081 GtkTreeModel *model;
1082 GtkTreeSelection *selection;
1083 #endif /* HAVE_GTK2 */
1085 if (!was) gtk_widget_set_sensitive (parent, True);
1087 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1088 STFU g_assert (model);
1089 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1091 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1092 gtk_tree_selection_select_iter (selection, &iter);
1094 #else /* !HAVE_GTK2 */
1095 gtk_list_select_item (GTK_LIST (list), list_elt);
1096 #endif /* !HAVE_GTK2 */
1097 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1098 if (!was) gtk_widget_set_sensitive (parent, False);
1102 G_MODULE_EXPORT void
1103 run_next_cb (GtkButton *button, gpointer user_data)
1105 state *s = global_state_kludge; /* I hate C so much... */
1106 /* saver_preferences *p = &s->prefs; */
1107 Bool ops = s->preview_suppressed_p;
1109 GtkWidget *list_widget = name_to_widget (s, "list");
1110 int list_elt = selected_list_element (s);
1117 if (list_elt >= s->list_count)
1120 s->preview_suppressed_p = True;
1122 flush_dialog_changes_and_save (s);
1123 force_list_select_item (s, list_widget, list_elt, True);
1124 populate_demo_window (s, list_elt);
1125 run_hack (s, list_elt, False);
1127 s->preview_suppressed_p = ops;
1131 G_MODULE_EXPORT void
1132 run_prev_cb (GtkButton *button, gpointer user_data)
1134 state *s = global_state_kludge; /* I hate C so much... */
1135 /* saver_preferences *p = &s->prefs; */
1136 Bool ops = s->preview_suppressed_p;
1138 GtkWidget *list_widget = name_to_widget (s, "list");
1139 int list_elt = selected_list_element (s);
1142 list_elt = s->list_count - 1;
1147 list_elt = s->list_count - 1;
1149 s->preview_suppressed_p = True;
1151 flush_dialog_changes_and_save (s);
1152 force_list_select_item (s, list_widget, list_elt, True);
1153 populate_demo_window (s, list_elt);
1154 run_hack (s, list_elt, False);
1156 s->preview_suppressed_p = ops;
1160 /* Writes the given settings into prefs.
1161 Returns true if there was a change, False otherwise.
1162 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1165 flush_changes (state *s,
1168 const char *command,
1171 saver_preferences *p = &s->prefs;
1172 Bool changed = False;
1175 if (list_elt < 0 || list_elt >= s->list_count)
1178 hack_number = s->list_elt_to_hack_number[list_elt];
1179 hack = p->screenhacks[hack_number];
1181 if (enabled_p != -1 &&
1182 enabled_p != hack->enabled_p)
1184 hack->enabled_p = enabled_p;
1187 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1188 blurb(), hack->name, enabled_p);
1193 if (!hack->command || !!strcmp (command, hack->command))
1195 if (hack->command) free (hack->command);
1196 hack->command = strdup (command);
1199 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1200 blurb(), hack->name, command);
1206 const char *ov = hack->visual;
1207 if (!ov || !*ov) ov = "any";
1208 if (!*visual) visual = "any";
1209 if (!!strcasecmp (visual, ov))
1211 if (hack->visual) free (hack->visual);
1212 hack->visual = strdup (visual);
1215 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1216 blurb(), hack->name, visual);
1224 /* Helper for the text fields that contain time specifications:
1225 this parses the text, and does error checking.
1228 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1233 if (!sec_p || strchr (line, ':'))
1234 value = parse_time ((char *) line, sec_p, True);
1238 if (sscanf (line, "%d%c", &value, &c) != 1)
1244 value *= 1000; /* Time measures in microseconds */
1250 "Unparsable time format: \"%s\"\n"),
1252 warning_dialog (s->toplevel_widget, b, False, 100);
1261 directory_p (const char *path)
1264 if (!path || !*path)
1266 else if (stat (path, &st))
1268 else if (!S_ISDIR (st.st_mode))
1275 file_p (const char *path)
1278 if (!path || !*path)
1280 else if (stat (path, &st))
1282 else if (S_ISDIR (st.st_mode))
1289 normalize_directory (const char *path)
1293 if (!path || !*path) return 0;
1295 p2 = (char *) malloc (L + 2);
1297 if (p2[L-1] == '/') /* remove trailing slash */
1300 for (s = p2; s && *s; s++)
1303 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1304 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1307 while (s0 > p2 && s0[-1] != '/')
1317 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1318 strcpy (s, s+2), s--;
1319 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1323 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1324 while (s[0] == '/' && s[1] == '/')
1327 /* and strip trailing whitespace for good measure. */
1329 while (isspace(p2[L-1]))
1342 } FlushForeachClosure;
1345 flush_checkbox (GtkTreeModel *model,
1350 FlushForeachClosure *closure = data;
1353 gtk_tree_model_get (model, iter,
1354 COL_ENABLED, &checked,
1357 if (flush_changes (closure->s, closure->i,
1359 *closure->changed = True;
1363 /* don't remove row */
1367 #endif /* HAVE_GTK2 */
1369 /* Flush out any changes made in the main dialog window (where changes
1370 take place immediately: clicking on a checkbox causes the init file
1371 to be written right away.)
1374 flush_dialog_changes_and_save (state *s)
1376 saver_preferences *p = &s->prefs;
1377 saver_preferences P2, *p2 = &P2;
1379 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1380 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1381 FlushForeachClosure closure;
1382 #else /* !HAVE_GTK2 */
1383 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1384 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1386 #endif /* !HAVE_GTK2 */
1388 Bool changed = False;
1391 if (s->saving_p) return False;
1396 /* Flush any checkbox changes in the list down into the prefs struct.
1400 closure.changed = &changed;
1402 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1404 #else /* !HAVE_GTK2 */
1406 for (i = 0; kids; kids = kids->next, i++)
1408 GtkWidget *line = GTK_WIDGET (kids->data);
1409 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1410 GtkWidget *line_check =
1411 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1413 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1415 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1418 #endif /* ~HAVE_GTK2 */
1420 /* Flush the non-hack-specific settings down into the prefs struct.
1423 # define SECONDS(FIELD,NAME) \
1424 w = name_to_widget (s, (NAME)); \
1425 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1427 # define MINUTES(FIELD,NAME) \
1428 w = name_to_widget (s, (NAME)); \
1429 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1431 # define CHECKBOX(FIELD,NAME) \
1432 w = name_to_widget (s, (NAME)); \
1433 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1435 # define PATHNAME(FIELD,NAME) \
1436 w = name_to_widget (s, (NAME)); \
1437 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1439 # define TEXT(FIELD,NAME) \
1440 w = name_to_widget (s, (NAME)); \
1441 (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1443 MINUTES (&p2->timeout, "timeout_spinbutton");
1444 MINUTES (&p2->cycle, "cycle_spinbutton");
1445 CHECKBOX (p2->lock_p, "lock_button");
1446 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1448 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1449 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1450 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1451 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1453 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1454 CHECKBOX (p2->grab_video_p, "grab_video_button");
1455 CHECKBOX (p2->random_image_p, "grab_image_button");
1456 PATHNAME (p2->image_directory, "image_text");
1459 CHECKBOX (p2->verbose_p, "verbose_button");
1460 CHECKBOX (p2->capture_stderr_p, "capture_button");
1461 CHECKBOX (p2->splash_p, "splash_button");
1466 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1467 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1468 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1469 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1470 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1471 TEXT (p2->text_literal, "text_entry");
1472 PATHNAME (p2->text_file, "text_file_entry");
1473 PATHNAME (p2->text_program, "text_program_entry");
1474 PATHNAME (p2->text_program, "text_program_entry");
1475 TEXT (p2->text_url, "text_url_entry");
1478 CHECKBOX (p2->install_cmap_p, "install_button");
1479 CHECKBOX (p2->fade_p, "fade_button");
1480 CHECKBOX (p2->unfade_p, "unfade_button");
1481 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1489 /* Warn if the image directory doesn't exist.
1491 if (p2->image_directory &&
1492 *p2->image_directory &&
1493 !directory_p (p2->image_directory))
1496 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1497 p2->image_directory);
1498 warning_dialog (s->toplevel_widget, b, False, 100);
1502 /* Map the mode menu to `saver_mode' enum values. */
1504 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1505 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1506 GtkWidget *selected = gtk_menu_get_active (menu);
1507 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1508 int menu_elt = g_list_index (kids, (gpointer) selected);
1509 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1510 p2->mode = mode_menu_order[menu_elt];
1513 if (p2->mode == ONE_HACK)
1515 int list_elt = selected_list_element (s);
1516 p2->selected_hack = (list_elt >= 0
1517 ? s->list_elt_to_hack_number[list_elt]
1521 # define COPY(field, name) \
1522 if (p->field != p2->field) { \
1525 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1527 p->field = p2->field
1530 COPY(selected_hack, "selected_hack");
1532 COPY(timeout, "timeout");
1533 COPY(cycle, "cycle");
1534 COPY(lock_p, "lock_p");
1535 COPY(lock_timeout, "lock_timeout");
1537 COPY(dpms_enabled_p, "dpms_enabled_p");
1538 COPY(dpms_standby, "dpms_standby");
1539 COPY(dpms_suspend, "dpms_suspend");
1540 COPY(dpms_off, "dpms_off");
1543 COPY(verbose_p, "verbose_p");
1544 COPY(capture_stderr_p, "capture_stderr_p");
1545 COPY(splash_p, "splash_p");
1548 COPY(tmode, "tmode");
1550 COPY(install_cmap_p, "install_cmap_p");
1551 COPY(fade_p, "fade_p");
1552 COPY(unfade_p, "unfade_p");
1553 COPY(fade_seconds, "fade_seconds");
1555 COPY(grab_desktop_p, "grab_desktop_p");
1556 COPY(grab_video_p, "grab_video_p");
1557 COPY(random_image_p, "random_image_p");
1561 # define COPYSTR(FIELD,NAME) \
1564 strcmp(p->FIELD, p2->FIELD)) \
1568 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1570 if (p->FIELD && p->FIELD != p2->FIELD) \
1572 p->FIELD = p2->FIELD; \
1575 COPYSTR(image_directory, "image_directory");
1576 COPYSTR(text_literal, "text_literal");
1577 COPYSTR(text_file, "text_file");
1578 COPYSTR(text_program, "text_program");
1579 COPYSTR(text_url, "text_url");
1582 populate_prefs_page (s);
1586 Display *dpy = GDK_DISPLAY();
1587 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1588 sync_server_dpms_settings (dpy, enabled_p,
1589 p->dpms_standby / 1000,
1590 p->dpms_suspend / 1000,
1594 changed = demo_write_init_file (s, p);
1597 s->saving_p = False;
1602 /* Flush out any changes made in the popup dialog box (where changes
1603 take place only when the OK button is clicked.)
1606 flush_popup_changes_and_save (state *s)
1608 Bool changed = False;
1609 saver_preferences *p = &s->prefs;
1610 int list_elt = selected_list_element (s);
1612 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1613 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1615 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1616 const char *command = gtk_entry_get_text (cmd);
1621 if (s->saving_p) return False;
1627 if (maybe_reload_init_file (s) != 0)
1633 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1635 if (!strcasecmp (visual, "")) visual = "";
1636 else if (!strcasecmp (visual, "any")) visual = "";
1637 else if (!strcasecmp (visual, "default")) visual = "Default";
1638 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1639 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1640 else if (!strcasecmp (visual, "best")) visual = "Best";
1641 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1642 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1643 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1644 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1645 else if (!strcasecmp (visual, "color")) visual = "Color";
1646 else if (!strcasecmp (visual, "gl")) visual = "GL";
1647 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1648 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1649 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1650 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1651 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1652 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1653 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1654 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1655 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1658 gdk_beep (); /* unparsable */
1660 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1663 changed = flush_changes (s, list_elt, -1, command, visual);
1666 changed = demo_write_init_file (s, p);
1668 /* Do this to re-launch the hack if (and only if) the command line
1670 populate_demo_window (s, selected_list_element (s));
1674 s->saving_p = False;
1679 G_MODULE_EXPORT void
1680 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1682 state *s = global_state_kludge; /* I hate C so much... */
1683 if (! s->initializing_p)
1685 s->initializing_p = True;
1686 flush_dialog_changes_and_save (s);
1687 s->initializing_p = False;
1691 G_MODULE_EXPORT gboolean
1692 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1694 pref_changed_cb (widget, user_data);
1698 /* Callback on menu items in the "mode" options menu.
1701 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1703 state *s = (state *) user_data;
1704 saver_preferences *p = &s->prefs;
1705 GtkWidget *list = name_to_widget (s, "list");
1708 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1710 saver_mode new_mode;
1714 if (menu_items->data == widget)
1717 menu_items = menu_items->next;
1719 if (!menu_items) abort();
1721 new_mode = mode_menu_order[menu_index];
1723 /* Keep the same list element displayed as before; except if we're
1724 switching *to* "one screensaver" mode from any other mode, set
1725 "the one" to be that which is currently selected.
1727 list_elt = selected_list_element (s);
1728 if (new_mode == ONE_HACK)
1729 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1732 saver_mode old_mode = p->mode;
1734 populate_demo_window (s, list_elt);
1735 force_list_select_item (s, list, list_elt, True);
1736 p->mode = old_mode; /* put it back, so the init file gets written */
1739 pref_changed_cb (widget, user_data);
1743 G_MODULE_EXPORT void
1744 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1745 gint page_num, gpointer user_data)
1747 state *s = global_state_kludge; /* I hate C so much... */
1748 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1750 /* If we're switching to page 0, schedule the current hack to be run.
1751 Otherwise, schedule it to stop. */
1753 populate_demo_window (s, selected_list_element (s));
1755 schedule_preview (s, 0);
1760 list_activated_cb (GtkTreeView *list,
1762 GtkTreeViewColumn *column,
1769 STFU g_return_if_fail (!gdk_pointer_is_grabbed ());
1771 str = gtk_tree_path_to_string (path);
1772 list_elt = strtol (str, NULL, 10);
1776 run_hack (s, list_elt, True);
1780 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1782 state *s = (state *)data;
1783 GtkTreeModel *model;
1789 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1792 path = gtk_tree_model_get_path (model, &iter);
1793 str = gtk_tree_path_to_string (path);
1794 list_elt = strtol (str, NULL, 10);
1796 gtk_tree_path_free (path);
1799 populate_demo_window (s, list_elt);
1800 flush_dialog_changes_and_save (s);
1802 /* Re-populate the Settings window any time a new item is selected
1803 in the list, in case both windows are currently visible.
1805 populate_popup_window (s);
1808 #else /* !HAVE_GTK2 */
1810 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1811 list_select_cb that comes in
1812 *after* we've double-clicked.
1816 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1819 state *s = (state *) data;
1820 if (event->type == GDK_2BUTTON_PRESS)
1822 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1823 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1825 last_doubleclick_time = time ((time_t *) 0);
1828 run_hack (s, list_elt, True);
1836 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1838 state *s = (state *) data;
1839 time_t now = time ((time_t *) 0);
1841 if (now >= last_doubleclick_time + 2)
1843 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1844 populate_demo_window (s, list_elt);
1845 flush_dialog_changes_and_save (s);
1850 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1852 state *s = (state *) data;
1853 populate_demo_window (s, -1);
1854 flush_dialog_changes_and_save (s);
1857 #endif /* !HAVE_GTK2 */
1860 /* Called when the checkboxes that are in the left column of the
1861 scrolling list are clicked. This both populates the right pane
1862 (just as clicking on the label (really, listitem) does) and
1863 also syncs this checkbox with the right pane Enabled checkbox.
1868 GtkCellRendererToggle *toggle,
1870 #else /* !HAVE_GTK2 */
1872 #endif /* !HAVE_GTK2 */
1875 state *s = (state *) data;
1878 GtkScrolledWindow *scroller =
1879 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1880 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1881 GtkTreeModel *model = gtk_tree_view_get_model (list);
1882 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1885 #else /* !HAVE_GTK2 */
1886 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1887 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1889 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1890 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1891 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1892 #endif /* !HAVE_GTK2 */
1899 if (!gtk_tree_model_get_iter (model, &iter, path))
1901 g_warning ("bad path: %s", path_string);
1904 gtk_tree_path_free (path);
1906 gtk_tree_model_get (model, &iter,
1907 COL_ENABLED, &active,
1910 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1911 COL_ENABLED, !active,
1914 list_elt = strtol (path_string, NULL, 10);
1915 #else /* !HAVE_GTK2 */
1916 list_elt = gtk_list_child_position (list, line);
1917 #endif /* !HAVE_GTK2 */
1919 /* remember previous scroll position of the top of the list */
1920 adj = gtk_scrolled_window_get_vadjustment (scroller);
1921 scroll_top = adj->value;
1923 flush_dialog_changes_and_save (s);
1924 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1925 populate_demo_window (s, list_elt);
1927 /* restore the previous scroll position of the top of the list.
1928 this is weak, but I don't really know why it's moving... */
1929 gtk_adjustment_set_value (adj, scroll_top);
1935 GtkFileSelection *widget;
1936 } file_selection_data;
1941 store_image_directory (GtkWidget *button, gpointer user_data)
1943 file_selection_data *fsd = (file_selection_data *) user_data;
1944 state *s = fsd->state;
1945 GtkFileSelection *selector = fsd->widget;
1946 GtkWidget *top = s->toplevel_widget;
1947 saver_preferences *p = &s->prefs;
1948 const char *path = gtk_file_selection_get_filename (selector);
1950 if (p->image_directory && !strcmp(p->image_directory, path))
1951 return; /* no change */
1953 if (!directory_p (path))
1956 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1957 warning_dialog (GTK_WIDGET (top), b, False, 100);
1961 if (p->image_directory) free (p->image_directory);
1962 p->image_directory = normalize_directory (path);
1964 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1965 (p->image_directory ? p->image_directory : ""));
1966 demo_write_init_file (s, p);
1971 store_text_file (GtkWidget *button, gpointer user_data)
1973 file_selection_data *fsd = (file_selection_data *) user_data;
1974 state *s = fsd->state;
1975 GtkFileSelection *selector = fsd->widget;
1976 GtkWidget *top = s->toplevel_widget;
1977 saver_preferences *p = &s->prefs;
1978 const char *path = gtk_file_selection_get_filename (selector);
1980 if (p->text_file && !strcmp(p->text_file, path))
1981 return; /* no change */
1986 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
1987 warning_dialog (GTK_WIDGET (top), b, False, 100);
1991 if (p->text_file) free (p->text_file);
1992 p->text_file = normalize_directory (path);
1994 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
1995 (p->text_file ? p->text_file : ""));
1996 demo_write_init_file (s, p);
2001 store_text_program (GtkWidget *button, gpointer user_data)
2003 file_selection_data *fsd = (file_selection_data *) user_data;
2004 state *s = fsd->state;
2005 GtkFileSelection *selector = fsd->widget;
2006 /*GtkWidget *top = s->toplevel_widget;*/
2007 saver_preferences *p = &s->prefs;
2008 const char *path = gtk_file_selection_get_filename (selector);
2010 if (p->text_program && !strcmp(p->text_program, path))
2011 return; /* no change */
2017 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2018 warning_dialog (GTK_WIDGET (top), b, False, 100);
2023 if (p->text_program) free (p->text_program);
2024 p->text_program = normalize_directory (path);
2026 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2027 (p->text_program ? p->text_program : ""));
2028 demo_write_init_file (s, p);
2034 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2036 file_selection_data *fsd = (file_selection_data *) user_data;
2037 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2041 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2043 browse_image_dir_cancel (button, user_data);
2044 store_image_directory (button, user_data);
2048 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2050 browse_image_dir_cancel (button, user_data);
2051 store_text_file (button, user_data);
2055 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2057 browse_image_dir_cancel (button, user_data);
2058 store_text_program (button, user_data);
2062 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2064 browse_image_dir_cancel (widget, user_data);
2068 G_MODULE_EXPORT void
2069 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2071 state *s = global_state_kludge; /* I hate C so much... */
2072 saver_preferences *p = &s->prefs;
2073 static file_selection_data *fsd = 0;
2075 GtkFileSelection *selector = GTK_FILE_SELECTION(
2076 gtk_file_selection_new ("Please select the image directory."));
2079 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2081 fsd->widget = selector;
2084 if (p->image_directory && *p->image_directory)
2085 gtk_file_selection_set_filename (selector, p->image_directory);
2087 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2088 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2090 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2091 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2093 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2094 GTK_SIGNAL_FUNC (browse_image_dir_close),
2097 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2099 gtk_window_set_modal (GTK_WINDOW (selector), True);
2100 gtk_widget_show (GTK_WIDGET (selector));
2104 G_MODULE_EXPORT void
2105 browse_text_file_cb (GtkButton *button, gpointer user_data)
2107 state *s = global_state_kludge; /* I hate C so much... */
2108 saver_preferences *p = &s->prefs;
2109 static file_selection_data *fsd = 0;
2111 GtkFileSelection *selector = GTK_FILE_SELECTION(
2112 gtk_file_selection_new ("Please select a text file."));
2115 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2117 fsd->widget = selector;
2120 if (p->text_file && *p->text_file)
2121 gtk_file_selection_set_filename (selector, p->text_file);
2123 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2124 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2126 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2127 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2129 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2130 GTK_SIGNAL_FUNC (browse_image_dir_close),
2133 gtk_window_set_modal (GTK_WINDOW (selector), True);
2134 gtk_widget_show (GTK_WIDGET (selector));
2138 G_MODULE_EXPORT void
2139 browse_text_program_cb (GtkButton *button, gpointer user_data)
2141 state *s = global_state_kludge; /* I hate C so much... */
2142 saver_preferences *p = &s->prefs;
2143 static file_selection_data *fsd = 0;
2145 GtkFileSelection *selector = GTK_FILE_SELECTION(
2146 gtk_file_selection_new ("Please select a text-generating program."));
2149 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2151 fsd->widget = selector;
2154 if (p->text_program && *p->text_program)
2155 gtk_file_selection_set_filename (selector, p->text_program);
2157 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2158 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2160 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2161 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2163 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2164 GTK_SIGNAL_FUNC (browse_image_dir_close),
2167 gtk_window_set_modal (GTK_WINDOW (selector), True);
2168 gtk_widget_show (GTK_WIDGET (selector));
2175 G_MODULE_EXPORT void
2176 settings_cb (GtkButton *button, gpointer user_data)
2178 state *s = global_state_kludge; /* I hate C so much... */
2179 int list_elt = selected_list_element (s);
2181 populate_demo_window (s, list_elt); /* reset the widget */
2182 populate_popup_window (s); /* create UI on popup window */
2183 gtk_widget_show (s->popup_widget);
2187 settings_sync_cmd_text (state *s)
2190 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2191 char *cmd_line = get_configurator_command_line (s->cdata);
2192 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2193 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2195 # endif /* HAVE_XML */
2198 G_MODULE_EXPORT void
2199 settings_adv_cb (GtkButton *button, gpointer user_data)
2201 state *s = global_state_kludge; /* I hate C so much... */
2202 GtkNotebook *notebook =
2203 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2205 settings_sync_cmd_text (s);
2206 gtk_notebook_set_page (notebook, 1);
2209 G_MODULE_EXPORT void
2210 settings_std_cb (GtkButton *button, gpointer user_data)
2212 state *s = global_state_kludge; /* I hate C so much... */
2213 GtkNotebook *notebook =
2214 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2216 /* Re-create UI to reflect the in-progress command-line settings. */
2217 populate_popup_window (s);
2219 gtk_notebook_set_page (notebook, 0);
2222 G_MODULE_EXPORT void
2223 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2224 gint page_num, gpointer user_data)
2226 state *s = global_state_kludge; /* I hate C so much... */
2227 GtkWidget *adv = name_to_widget (s, "adv_button");
2228 GtkWidget *std = name_to_widget (s, "std_button");
2232 gtk_widget_show (adv);
2233 gtk_widget_hide (std);
2235 else if (page_num == 1)
2237 gtk_widget_hide (adv);
2238 gtk_widget_show (std);
2246 G_MODULE_EXPORT void
2247 settings_cancel_cb (GtkButton *button, gpointer user_data)
2249 state *s = global_state_kludge; /* I hate C so much... */
2250 gtk_widget_hide (s->popup_widget);
2253 G_MODULE_EXPORT void
2254 settings_ok_cb (GtkButton *button, gpointer user_data)
2256 state *s = global_state_kludge; /* I hate C so much... */
2257 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2258 int page = gtk_notebook_get_current_page (notebook);
2261 /* Regenerate the command-line from the widget contents before saving.
2262 But don't do this if we're looking at the command-line page already,
2263 or we will blow away what they typed... */
2264 settings_sync_cmd_text (s);
2266 flush_popup_changes_and_save (s);
2267 gtk_widget_hide (s->popup_widget);
2271 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2273 state *s = (state *) data;
2274 settings_cancel_cb (0, (gpointer) s);
2280 /* Populating the various widgets
2284 /* Returns the number of the last hack run by the server.
2287 server_current_hack (void)
2291 unsigned long nitems, bytesafter;
2292 unsigned char *dataP = 0;
2293 Display *dpy = GDK_DISPLAY();
2294 int hack_number = -1;
2296 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2297 XA_SCREENSAVER_STATUS,
2298 0, 3, False, XA_INTEGER,
2299 &type, &format, &nitems, &bytesafter,
2302 && type == XA_INTEGER
2306 CARD32 *data = (CARD32 *) dataP;
2307 hack_number = (int) data[2] - 1;
2310 if (dataP) XFree (dataP);
2316 /* Finds the number of the last hack that was run, and makes that item be
2317 selected by default.
2320 scroll_to_current_hack (state *s)
2322 saver_preferences *p = &s->prefs;
2323 int hack_number = -1;
2325 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2326 hack_number = p->selected_hack;
2327 if (hack_number < 0) /* otherwise, use the last-run */
2328 hack_number = server_current_hack ();
2329 if (hack_number < 0) /* failing that, last "one mode" */
2330 hack_number = p->selected_hack;
2331 if (hack_number < 0) /* failing that, newest hack. */
2333 /* We should only get here if the user does not have a .xscreensaver
2334 file, and the screen has not been blanked with a hack since X
2335 started up: in other words, this is probably a fresh install.
2337 Instead of just defaulting to hack #0 (in either "programs" or
2338 "alphabetical" order) let's try to default to the last runnable
2339 hack in the "programs" list: this is probably the hack that was
2340 most recently added to the xscreensaver distribution (and so
2341 it's probably the currently-coolest one!)
2343 hack_number = p->screenhacks_count-1;
2344 while (hack_number > 0 &&
2345 ! (s->hacks_available_p[hack_number] &&
2346 p->screenhacks[hack_number]->enabled_p))
2350 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2352 int list_elt = s->hack_number_to_list_elt[hack_number];
2353 GtkWidget *list = name_to_widget (s, "list");
2354 force_list_select_item (s, list, list_elt, True);
2355 populate_demo_window (s, list_elt);
2361 populate_hack_list (state *s)
2364 saver_preferences *p = &s->prefs;
2365 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2366 GtkListStore *model;
2367 GtkTreeSelection *selection;
2368 GtkCellRenderer *ren;
2372 g_object_get (G_OBJECT (list),
2377 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2378 g_object_set (G_OBJECT (list), "model", model, NULL);
2379 g_object_unref (model);
2381 ren = gtk_cell_renderer_toggle_new ();
2382 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2384 "active", COL_ENABLED,
2387 g_signal_connect (ren, "toggled",
2388 G_CALLBACK (list_checkbox_cb),
2391 ren = gtk_cell_renderer_text_new ();
2392 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2393 _("Screen Saver"), ren,
2397 g_signal_connect_after (list, "row_activated",
2398 G_CALLBACK (list_activated_cb),
2401 selection = gtk_tree_view_get_selection (list);
2402 g_signal_connect (selection, "changed",
2403 G_CALLBACK (list_select_changed_cb),
2408 for (i = 0; i < s->list_count; i++)
2410 int hack_number = s->list_elt_to_hack_number[i];
2411 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2413 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2415 if (!hack) continue;
2417 /* If we're to suppress uninstalled hacks, check $PATH now. */
2418 if (p->ignore_uninstalled_p && !available_p)
2421 pretty_name = (hack->name
2422 ? strdup (hack->name)
2423 : make_hack_name (hack->command));
2427 /* Make the text foreground be the color of insensitive widgets
2428 (but don't actually make it be insensitive, since we still
2429 want to be able to click on it.)
2431 GtkStyle *style = GTK_WIDGET (list)->style;
2432 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2433 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2434 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2436 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2437 /* " background=\"#%02X%02X%02X\"" */
2439 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2440 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2446 gtk_list_store_append (model, &iter);
2447 gtk_list_store_set (model, &iter,
2448 COL_ENABLED, hack->enabled_p,
2449 COL_NAME, pretty_name,
2454 #else /* !HAVE_GTK2 */
2456 saver_preferences *p = &s->prefs;
2457 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2459 for (i = 0; i < s->list_count; i++)
2461 int hack_number = s->list_elt_to_hack_number[i];
2462 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2464 /* A GtkList must contain only GtkListItems, but those can contain
2465 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2466 and a Label. We handle single and double click events on the
2467 line itself, for clicking on the text, but the interior checkbox
2468 also handles its own events.
2471 GtkWidget *line_hbox;
2472 GtkWidget *line_check;
2473 GtkWidget *line_label;
2475 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2477 if (!hack) continue;
2479 /* If we're to suppress uninstalled hacks, check $PATH now. */
2480 if (p->ignore_uninstalled_p && !available_p)
2483 pretty_name = (hack->name
2484 ? strdup (hack->name)
2485 : make_hack_name (hack->command));
2487 line = gtk_list_item_new ();
2488 line_hbox = gtk_hbox_new (FALSE, 0);
2489 line_check = gtk_check_button_new ();
2490 line_label = gtk_label_new (pretty_name);
2492 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2493 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2494 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2496 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2498 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2500 gtk_widget_show (line_check);
2501 gtk_widget_show (line_label);
2502 gtk_widget_show (line_hbox);
2503 gtk_widget_show (line);
2507 gtk_container_add (GTK_CONTAINER (list), line);
2508 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2509 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2512 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2513 GTK_SIGNAL_FUNC (list_checkbox_cb),
2516 gtk_widget_show (line);
2520 /* Make the widget be colored like insensitive widgets
2521 (but don't actually make it be insensitive, since we
2522 still want to be able to click on it.)
2524 GtkRcStyle *rc_style;
2527 gtk_widget_realize (GTK_WIDGET (line_label));
2529 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2530 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2532 rc_style = gtk_rc_style_new ();
2533 rc_style->fg[GTK_STATE_NORMAL] = fg;
2534 rc_style->bg[GTK_STATE_NORMAL] = bg;
2535 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2537 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2538 gtk_rc_style_unref (rc_style);
2542 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2543 GTK_SIGNAL_FUNC (list_select_cb),
2545 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2546 GTK_SIGNAL_FUNC (list_unselect_cb),
2548 #endif /* !HAVE_GTK2 */
2552 update_list_sensitivity (state *s)
2554 saver_preferences *p = &s->prefs;
2555 Bool sensitive = (p->mode == RANDOM_HACKS ||
2556 p->mode == RANDOM_HACKS_SAME ||
2557 p->mode == ONE_HACK);
2558 Bool checkable = (p->mode == RANDOM_HACKS ||
2559 p->mode == RANDOM_HACKS_SAME);
2560 Bool blankable = (p->mode != DONT_BLANK);
2563 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2564 GtkWidget *use = name_to_widget (s, "use_col_frame");
2565 #endif /* HAVE_GTK2 */
2566 GtkWidget *scroller = name_to_widget (s, "scroller");
2567 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2568 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2571 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2572 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2573 #else /* !HAVE_GTK2 */
2574 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2575 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2577 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2578 #endif /* !HAVE_GTK2 */
2579 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2580 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2582 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2585 gtk_tree_view_column_set_visible (use, checkable);
2586 #else /* !HAVE_GTK2 */
2588 gtk_widget_show (use); /* the "Use" column header */
2590 gtk_widget_hide (use);
2594 GtkBin *line = GTK_BIN (kids->data);
2595 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2596 GtkWidget *line_check =
2597 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2600 gtk_widget_show (line_check);
2602 gtk_widget_hide (line_check);
2606 #endif /* !HAVE_GTK2 */
2611 populate_prefs_page (state *s)
2613 saver_preferences *p = &s->prefs;
2615 Bool can_lock_p = True;
2617 /* Disable all the "lock" controls if locking support was not provided
2618 at compile-time, or if running on MacOS. */
2619 # if defined(NO_LOCKING) || defined(__APPLE__)
2624 /* If there is only one screen, the mode menu contains
2625 "random" but not "random-same".
2627 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2628 p->mode = RANDOM_HACKS;
2631 /* The file supports timeouts of less than a minute, but the GUI does
2632 not, so throttle the values to be at least one minute (since "0" is
2633 a bad rounding choice...)
2635 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2638 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2641 # define FMT_MINUTES(NAME,N) \
2642 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2644 # define FMT_SECONDS(NAME,N) \
2645 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2647 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2648 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2649 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2650 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2651 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2652 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2653 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2658 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2659 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2662 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2664 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2665 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2666 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2668 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2669 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2670 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2671 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2672 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2673 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2674 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2678 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2679 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2680 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2681 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2682 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2685 # undef TOGGLE_ACTIVE
2687 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2688 (p->image_directory ? p->image_directory : ""));
2689 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2691 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2694 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2695 (p->text_literal ? p->text_literal : ""));
2696 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2697 (p->text_file ? p->text_file : ""));
2698 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2699 (p->text_program ? p->text_program : ""));
2700 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2701 (p->text_url ? p->text_url : ""));
2703 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2704 p->tmode == TEXT_LITERAL);
2705 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2706 p->tmode == TEXT_FILE);
2707 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2708 p->tmode == TEXT_FILE);
2709 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2710 p->tmode == TEXT_PROGRAM);
2711 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2712 p->tmode == TEXT_PROGRAM);
2713 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2714 p->tmode == TEXT_URL);
2717 /* Map the `saver_mode' enum to mode menu to values. */
2719 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2722 for (i = 0; i < countof(mode_menu_order); i++)
2723 if (mode_menu_order[i] == p->mode)
2725 gtk_option_menu_set_history (opt, i);
2726 update_list_sensitivity (s);
2730 Bool found_any_writable_cells = False;
2731 Bool fading_possible = False;
2732 Bool dpms_supported = False;
2734 Display *dpy = GDK_DISPLAY();
2735 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2737 for (i = 0; i < nscreens; i++)
2739 Screen *s = ScreenOfDisplay (dpy, i);
2740 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2742 found_any_writable_cells = True;
2747 fading_possible = found_any_writable_cells;
2748 #ifdef HAVE_XF86VMODE_GAMMA
2749 fading_possible = True;
2752 #ifdef HAVE_DPMS_EXTENSION
2754 int op = 0, event = 0, error = 0;
2755 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2756 dpms_supported = True;
2758 #endif /* HAVE_DPMS_EXTENSION */
2761 # define SENSITIZE(NAME,SENSITIVEP) \
2762 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2764 /* Blanking and Locking
2766 SENSITIZE ("lock_button", can_lock_p);
2767 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2768 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2772 SENSITIZE ("dpms_frame", dpms_supported);
2773 SENSITIZE ("dpms_button", dpms_supported);
2774 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2775 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2776 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2777 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2778 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2779 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2780 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2781 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2782 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2786 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2787 SENSITIZE ("install_button", found_any_writable_cells);
2788 SENSITIZE ("fade_button", fading_possible);
2789 SENSITIZE ("unfade_button", fading_possible);
2791 SENSITIZE ("fade_label", (fading_possible &&
2792 (p->fade_p || p->unfade_p)));
2793 SENSITIZE ("fade_spinbutton", (fading_possible &&
2794 (p->fade_p || p->unfade_p)));
2802 populate_popup_window (state *s)
2804 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2805 char *doc_string = 0;
2807 /* #### not in Gtk 1.2
2808 gtk_label_set_selectable (doc);
2814 free_conf_data (s->cdata);
2819 saver_preferences *p = &s->prefs;
2820 int list_elt = selected_list_element (s);
2821 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2822 ? s->list_elt_to_hack_number[list_elt]
2824 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2827 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2828 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2829 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2830 s->cdata = load_configurator (cmd_line, s->debug_p);
2831 if (s->cdata && s->cdata->widget)
2832 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2837 doc_string = (s->cdata
2838 ? s->cdata->description
2840 # else /* !HAVE_XML */
2841 doc_string = _("Descriptions not available: no XML support compiled in.");
2842 # endif /* !HAVE_XML */
2844 gtk_label_set_text (doc, (doc_string
2846 : _("No description available.")));
2851 sensitize_demo_widgets (state *s, Bool sensitive_p)
2853 const char *names[] = { "demo", "settings",
2854 "cmd_label", "cmd_text", "manual",
2855 "visual", "visual_combo" };
2857 for (i = 0; i < countof(names); i++)
2859 GtkWidget *w = name_to_widget (s, names[i]);
2860 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2866 sensitize_menu_items (state *s, Bool force_p)
2868 static Bool running_p = False;
2869 static time_t last_checked = 0;
2870 time_t now = time ((time_t *) 0);
2871 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
2875 if (force_p || now > last_checked + 10) /* check every 10 seconds */
2877 running_p = xscreensaver_running_p (s);
2878 last_checked = time ((time_t *) 0);
2881 for (i = 0; i < countof(names); i++)
2883 GtkWidget *w = name_to_widget (s, names[i]);
2884 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
2889 /* When the File menu is de-posted after a "Restart Daemon" command,
2890 the window underneath doesn't repaint for some reason. I guess this
2891 is a bug in exposure handling in GTK or GDK. This works around it.
2894 force_dialog_repaint (state *s)
2897 /* Tell GDK to invalidate and repaint the whole window.
2899 GdkWindow *w = s->toplevel_widget->window;
2900 GdkRegion *region = gdk_region_new ();
2902 rect.x = rect.y = 0;
2903 rect.width = rect.height = 32767;
2904 gdk_region_union_with_rect (region, &rect);
2905 gdk_window_invalidate_region (w, region, True);
2906 gdk_region_destroy (region);
2907 gdk_window_process_updates (w, True);
2909 /* Force the server to send an exposure event by creating and then
2910 destroying a window as a child of the top level shell.
2912 Display *dpy = GDK_DISPLAY();
2913 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
2915 XWindowAttributes xgwa;
2916 XGetWindowAttributes (dpy, parent, &xgwa);
2917 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
2918 XMapRaised (dpy, w);
2919 XDestroyWindow (dpy, w);
2925 /* Even though we've given these text fields a maximum number of characters,
2926 their default size is still about 30 characters wide -- so measure out
2927 a string in their font, and resize them to just fit that.
2930 fix_text_entry_sizes (state *s)
2934 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2935 const char * const spinbuttons[] = {
2936 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2937 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2938 "dpms_off_spinbutton",
2939 "-fade_spinbutton" };
2943 for (i = 0; i < countof(spinbuttons); i++)
2945 const char *n = spinbuttons[i];
2947 while (*n == '-') n++, cols--;
2948 w = GTK_WIDGET (name_to_widget (s, n));
2949 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2950 gtk_widget_set_usize (w, width, -2);
2953 /* Now fix the width of the combo box.
2955 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2956 w = GTK_COMBO (w)->entry;
2957 width = gdk_string_width (w->style->font, "PseudoColor___");
2958 gtk_widget_set_usize (w, width, -2);
2960 /* Now fix the width of the file entry text.
2962 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2963 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2964 gtk_widget_set_usize (w, width, -2);
2966 /* Now fix the width of the command line text.
2968 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2969 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2970 gtk_widget_set_usize (w, width, -2);
2974 /* Now fix the height of the list widget:
2975 make it default to being around 10 text-lines high instead of 4.
2977 w = GTK_WIDGET (name_to_widget (s, "list"));
2981 int leading = 3; /* approximate is ok... */
2985 PangoFontMetrics *pain =
2986 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2987 w->style->font_desc,
2988 gtk_get_default_language ());
2989 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2990 pango_font_metrics_get_descent (pain));
2991 #else /* !HAVE_GTK2 */
2992 height = w->style->font->ascent + w->style->font->descent;
2993 #endif /* !HAVE_GTK2 */
2997 height += border * 2;
2998 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2999 gtk_widget_set_usize (w, -2, height);
3006 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3009 static char *up_arrow_xpm[] = {
3032 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3033 the end of the array (Gtk 1.2.5.) */
3034 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3035 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3038 static char *down_arrow_xpm[] = {
3061 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3062 the end of the array (Gtk 1.2.5.) */
3063 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3064 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3068 pixmapify_button (state *s, int down_p)
3072 GtkWidget *pixmapwid;
3076 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3077 style = gtk_widget_get_style (w);
3079 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3080 &style->bg[GTK_STATE_NORMAL],
3082 ? (gchar **) down_arrow_xpm
3083 : (gchar **) up_arrow_xpm));
3084 pixmapwid = gtk_pixmap_new (pixmap, mask);
3085 gtk_widget_show (pixmapwid);
3086 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3087 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3091 map_next_button_cb (GtkWidget *w, gpointer user_data)
3093 state *s = (state *) user_data;
3094 pixmapify_button (s, 1);
3098 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3100 state *s = (state *) user_data;
3101 pixmapify_button (s, 0);
3103 #endif /* !HAVE_GTK2 */
3107 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3111 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3112 GtkAllocation *allocation,
3116 GtkWidgetAuxInfo *aux_info;
3118 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3120 aux_info->width = allocation->width;
3121 aux_info->height = -2;
3125 gtk_widget_size_request (label, &req);
3128 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3131 eschew_gtk_lossage (GtkLabel *label)
3133 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3134 aux_info->width = GTK_WIDGET (label)->allocation.width;
3135 aux_info->height = -2;
3139 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3141 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3142 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3145 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3147 gtk_widget_queue_resize (GTK_WIDGET (label));
3149 #endif /* !HAVE_GTK2 */
3153 populate_demo_window (state *s, int list_elt)
3155 saver_preferences *p = &s->prefs;
3158 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3159 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
3160 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3161 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3162 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3164 if (p->mode == BLANK_ONLY)
3167 pretty_name = strdup (_("Blank Screen"));
3168 schedule_preview (s, 0);
3170 else if (p->mode == DONT_BLANK)
3173 pretty_name = strdup (_("Screen Saver Disabled"));
3174 schedule_preview (s, 0);
3178 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3179 ? s->list_elt_to_hack_number[list_elt]
3181 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3185 ? strdup (hack->name)
3186 : make_hack_name (hack->command))
3190 schedule_preview (s, hack->command);
3192 schedule_preview (s, 0);
3196 pretty_name = strdup (_("Preview"));
3198 gtk_frame_set_label (frame1, _(pretty_name));
3199 gtk_frame_set_label (frame2, _(pretty_name));
3201 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3202 gtk_entry_set_position (cmd, 0);
3206 sprintf (title, _("%s: %.100s Settings"),
3207 progclass, (pretty_name ? pretty_name : "???"));
3208 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3211 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3213 ? (hack->visual && *hack->visual
3218 sensitize_demo_widgets (s, (hack ? True : False));
3220 if (pretty_name) free (pretty_name);
3222 ensure_selected_item_visible (list);
3224 s->_selected_list_element = list_elt;
3229 widget_deleter (GtkWidget *widget, gpointer data)
3231 /* #### Well, I want to destroy these widgets, but if I do that, they get
3232 referenced again, and eventually I get a SEGV. So instead of
3233 destroying them, I'll just hide them, and leak a bunch of memory
3234 every time the disk file changes. Go go go Gtk!
3236 #### Ok, that's a lie, I get a crash even if I just hide the widget
3237 and don't ever delete it. Fuck!
3240 gtk_widget_destroy (widget);
3242 gtk_widget_hide (widget);
3247 static char **sort_hack_cmp_names_kludge;
3249 sort_hack_cmp (const void *a, const void *b)
3255 int aa = *(int *) a;
3256 int bb = *(int *) b;
3257 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3258 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3259 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3265 initialize_sort_map (state *s)
3267 saver_preferences *p = &s->prefs;
3270 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3271 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3272 if (s->hacks_available_p) free (s->hacks_available_p);
3274 s->list_elt_to_hack_number = (int *)
3275 calloc (sizeof(int), p->screenhacks_count + 1);
3276 s->hack_number_to_list_elt = (int *)
3277 calloc (sizeof(int), p->screenhacks_count + 1);
3278 s->hacks_available_p = (Bool *)
3279 calloc (sizeof(Bool), p->screenhacks_count + 1);
3280 s->total_available = 0;
3282 /* Check which hacks actually exist on $PATH
3284 for (i = 0; i < p->screenhacks_count; i++)
3286 screenhack *hack = p->screenhacks[i];
3287 int on = on_path_p (hack->command) ? 1 : 0;
3288 s->hacks_available_p[i] = on;
3289 s->total_available += on;
3292 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3296 for (i = 0; i < p->screenhacks_count; i++)
3298 if (!p->ignore_uninstalled_p ||
3299 s->hacks_available_p[i])
3300 s->list_elt_to_hack_number[j++] = i;
3304 for (; j < p->screenhacks_count; j++)
3305 s->list_elt_to_hack_number[j] = -1;
3308 /* Generate list of sortable names (once)
3310 sort_hack_cmp_names_kludge = (char **)
3311 calloc (sizeof(char *), p->screenhacks_count);
3312 for (i = 0; i < p->screenhacks_count; i++)
3314 screenhack *hack = p->screenhacks[i];
3315 char *name = (hack->name && *hack->name
3316 ? strdup (hack->name)
3317 : make_hack_name (hack->command));
3319 for (str = name; *str; str++)
3320 *str = tolower(*str);
3321 sort_hack_cmp_names_kludge[i] = name;
3324 /* Sort list->hack map alphabetically
3326 qsort (s->list_elt_to_hack_number,
3327 p->screenhacks_count,
3328 sizeof(*s->list_elt_to_hack_number),
3333 for (i = 0; i < p->screenhacks_count; i++)
3334 free (sort_hack_cmp_names_kludge[i]);
3335 free (sort_hack_cmp_names_kludge);
3336 sort_hack_cmp_names_kludge = 0;
3338 /* Build inverse table */
3339 for (i = 0; i < p->screenhacks_count; i++)
3341 int n = s->list_elt_to_hack_number[i];
3343 s->hack_number_to_list_elt[n] = i;
3349 maybe_reload_init_file (state *s)
3351 saver_preferences *p = &s->prefs;
3354 static Bool reentrant_lock = False;
3355 if (reentrant_lock) return 0;
3356 reentrant_lock = True;
3358 if (init_file_changed_p (p))
3360 const char *f = init_file_name();
3365 if (!f || !*f) return 0;
3366 b = (char *) malloc (strlen(f) + 1024);
3369 "file \"%s\" has changed, reloading.\n"),
3371 warning_dialog (s->toplevel_widget, b, False, 100);
3375 initialize_sort_map (s);
3377 list_elt = selected_list_element (s);
3378 list = name_to_widget (s, "list");
3379 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3380 populate_hack_list (s);
3381 force_list_select_item (s, list, list_elt, True);
3382 populate_prefs_page (s);
3383 populate_demo_window (s, list_elt);
3384 ensure_selected_item_visible (list);
3389 reentrant_lock = False;
3395 /* Making the preview window have the right X visual (so that GL works.)
3398 static Visual *get_best_gl_visual (state *);
3401 x_visual_to_gdk_visual (Visual *xv)
3403 GList *gvs = gdk_list_visuals();
3404 if (!xv) return gdk_visual_get_system();
3405 for (; gvs; gvs = gvs->next)
3407 GdkVisual *gv = (GdkVisual *) gvs->data;
3408 if (xv == GDK_VISUAL_XVISUAL (gv))
3411 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3412 blurb(), (unsigned long) xv->visualid);
3417 clear_preview_window (state *s)
3422 if (!s->toplevel_widget) return; /* very early */
3423 p = name_to_widget (s, "preview");
3426 if (!window) return;
3428 /* Flush the widget background down into the window, in case a subproc
3430 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3431 gdk_window_clear (window);
3434 int list_elt = selected_list_element (s);
3435 int hack_number = (list_elt >= 0
3436 ? s->list_elt_to_hack_number[list_elt]
3438 Bool available_p = (hack_number >= 0
3439 ? s->hacks_available_p [hack_number]
3441 Bool nothing_p = (s->total_available < 5);
3444 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3445 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3446 (s->running_preview_error_p
3447 ? (available_p ? 1 :
3450 #else /* !HAVE_GTK2 */
3451 if (s->running_preview_error_p)
3453 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3454 const char * const lines2[] = { N_("Not"), N_("Installed") };
3455 int nlines = countof(lines1);
3456 int lh = p->style->font->ascent + p->style->font->descent;
3460 const char * const *lines = (available_p ? lines1 : lines2);
3462 gdk_window_get_size (window, &w, &h);
3463 y = (h - (lh * nlines)) / 2;
3464 y += p->style->font->ascent;
3465 for (i = 0; i < nlines; i++)
3467 int sw = gdk_string_width (p->style->font, _(lines[i]));
3468 int x = (w - sw) / 2;
3469 gdk_draw_string (window, p->style->font,
3470 p->style->fg_gc[GTK_STATE_NORMAL],
3475 #endif /* !HAVE_GTK2 */
3483 reset_preview_window (state *s)
3485 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3486 when you kill one and re-start another on the same window. So maybe
3487 it's best to just always destroy and recreate the preview window
3488 when changing hacks, instead of always trying to reuse the same one?
3490 GtkWidget *pr = name_to_widget (s, "preview");
3491 if (GTK_WIDGET_REALIZED (pr))
3493 Window oid = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3495 gtk_widget_hide (pr);
3496 gtk_widget_unrealize (pr);
3497 gtk_widget_realize (pr);
3498 gtk_widget_show (pr);
3499 id = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3501 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3509 fix_preview_visual (state *s)
3511 GtkWidget *widget = name_to_widget (s, "preview");
3512 Visual *xvisual = get_best_gl_visual (s);
3513 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3514 GdkVisual *dvisual = gdk_visual_get_system();
3515 GdkColormap *cmap = (visual == dvisual
3516 ? gdk_colormap_get_system ()
3517 : gdk_colormap_new (visual, False));
3520 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3521 (visual == dvisual ? "default" : "non-default"),
3522 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3524 if (!GTK_WIDGET_REALIZED (widget) ||
3525 gtk_widget_get_visual (widget) != visual)
3527 gtk_widget_unrealize (widget);
3528 gtk_widget_set_visual (widget, visual);
3529 gtk_widget_set_colormap (widget, cmap);
3530 gtk_widget_realize (widget);
3533 /* Set the Widget colors to be white-on-black. */
3535 GdkWindow *window = widget->window;
3536 GtkStyle *style = gtk_style_copy (widget->style);
3537 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3538 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3539 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3540 GdkGC *fgc = gdk_gc_new(window);
3541 GdkGC *bgc = gdk_gc_new(window);
3542 if (!gdk_color_white (cmap, fg)) abort();
3543 if (!gdk_color_black (cmap, bg)) abort();
3544 gdk_gc_set_foreground (fgc, fg);
3545 gdk_gc_set_background (fgc, bg);
3546 gdk_gc_set_foreground (bgc, bg);
3547 gdk_gc_set_background (bgc, fg);
3548 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3549 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3550 gtk_widget_set_style (widget, style);
3552 /* For debugging purposes, put a title on the window (so that
3553 it can be easily found in the output of "xwininfo -tree".)
3555 gdk_window_set_title (window, "Preview");
3558 gtk_widget_show (widget);
3566 subproc_pretty_name (state *s)
3568 if (s->running_preview_cmd)
3570 char *ps = strdup (s->running_preview_cmd);
3571 char *ss = strchr (ps, ' ');
3573 ss = strrchr (ps, '/');
3584 return strdup ("???");
3589 reap_zombies (state *s)
3591 int wait_status = 0;
3593 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3597 if (pid == s->running_preview_pid)
3599 char *ss = subproc_pretty_name (s);
3600 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3601 (unsigned long) pid, ss);
3605 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3606 (unsigned long) pid);
3612 /* Mostly lifted from driver/subprocs.c */
3614 get_best_gl_visual (state *s)
3616 Display *dpy = GDK_DISPLAY();
3625 av[ac++] = "xscreensaver-gl-helper";
3630 perror ("error creating pipe:");
3637 switch ((int) (forked = fork ()))
3641 sprintf (buf, "%s: couldn't fork", blurb());
3649 close (in); /* don't need this one */
3650 close (ConnectionNumber (dpy)); /* close display fd */
3652 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3654 perror ("could not dup() a new stdout:");
3658 execvp (av[0], av); /* shouldn't return. */
3660 if (errno != ENOENT)
3662 /* Ignore "no such file or directory" errors, unless verbose.
3663 Issue all other exec errors, though. */
3664 sprintf (buf, "%s: running %s", blurb(), av[0]);
3668 /* Note that one must use _exit() instead of exit() in procs forked
3669 off of Gtk programs -- Gtk installs an atexit handler that has a
3670 copy of the X connection (which we've already closed, for safety.)
3671 If one uses exit() instead of _exit(), then one sometimes gets a
3672 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3674 _exit (1); /* exits fork */
3680 int wait_status = 0;
3682 FILE *f = fdopen (in, "r");
3686 close (out); /* don't need this one */
3689 if (!fgets (buf, sizeof(buf)-1, f))
3693 /* Wait for the child to die. */
3694 waitpid (-1, &wait_status, 0);
3696 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3702 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3708 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3710 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3711 blurb(), av[0], result);
3723 kill_preview_subproc (state *s, Bool reset_p)
3725 s->running_preview_error_p = False;
3728 clear_preview_window (s);
3730 if (s->subproc_check_timer_id)
3732 gtk_timeout_remove (s->subproc_check_timer_id);
3733 s->subproc_check_timer_id = 0;
3734 s->subproc_check_countdown = 0;
3737 if (s->running_preview_pid)
3739 int status = kill (s->running_preview_pid, SIGTERM);
3740 char *ss = subproc_pretty_name (s);
3747 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3748 blurb(), (unsigned long) s->running_preview_pid, ss);
3753 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3754 blurb(), (unsigned long) s->running_preview_pid, ss);
3758 else if (s->debug_p)
3759 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3760 (unsigned long) s->running_preview_pid, ss);
3763 s->running_preview_pid = 0;
3764 if (s->running_preview_cmd) free (s->running_preview_cmd);
3765 s->running_preview_cmd = 0;
3772 reset_preview_window (s);
3773 clear_preview_window (s);
3778 /* Immediately and unconditionally launches the given process,
3779 after appending the -window-id option; sets running_preview_pid.
3782 launch_preview_subproc (state *s)
3784 saver_preferences *p = &s->prefs;
3788 const char *cmd = s->desired_preview_cmd;
3790 GtkWidget *pr = name_to_widget (s, "preview");
3793 reset_preview_window (s);
3795 window = pr->window;
3797 s->running_preview_error_p = False;
3799 if (s->preview_suppressed_p)
3801 kill_preview_subproc (s, False);
3805 new_cmd = malloc (strlen (cmd) + 40);
3807 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3810 /* No window id? No command to run. */
3816 strcpy (new_cmd, cmd);
3817 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3821 kill_preview_subproc (s, False);
3824 s->running_preview_error_p = True;
3825 clear_preview_window (s);
3829 switch ((int) (forked = fork ()))
3834 sprintf (buf, "%s: couldn't fork", blurb());
3836 s->running_preview_error_p = True;
3842 close (ConnectionNumber (GDK_DISPLAY()));
3844 hack_subproc_environment (id, s->debug_p);
3846 usleep (250000); /* pause for 1/4th second before launching, to give
3847 the previous program time to die and flush its X
3848 buffer, so we don't get leftover turds on the
3851 exec_command (p->shell, new_cmd, p->nice_inferior);
3852 /* Don't bother printing an error message when we are unable to
3853 exec subprocesses; we handle that by polling the pid later.
3855 Note that one must use _exit() instead of exit() in procs forked
3856 off of Gtk programs -- Gtk installs an atexit handler that has a
3857 copy of the X connection (which we've already closed, for safety.)
3858 If one uses exit() instead of _exit(), then one sometimes gets a
3859 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3861 _exit (1); /* exits child fork */
3866 if (s->running_preview_cmd) free (s->running_preview_cmd);
3867 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3868 s->running_preview_pid = forked;
3872 char *ss = subproc_pretty_name (s);
3873 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3874 (unsigned long) forked, ss);
3881 schedule_preview_check (s);
3884 if (new_cmd) free (new_cmd);
3889 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3892 hack_environment (state *s)
3894 static const char *def_path =
3895 # ifdef DEFAULT_PATH_PREFIX
3896 DEFAULT_PATH_PREFIX;
3901 Display *dpy = GDK_DISPLAY();
3902 const char *odpy = DisplayString (dpy);
3903 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3904 strcpy (ndpy, "DISPLAY=");
3905 strcat (ndpy, odpy);
3910 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3912 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3913 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3914 So we must leak it (and/or the previous setting). Yay.
3917 if (def_path && *def_path)
3919 const char *opath = getenv("PATH");
3920 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3921 strcpy (npath, "PATH=");
3922 strcat (npath, def_path);
3923 strcat (npath, ":");
3924 strcat (npath, opath);
3928 /* do not free(npath) -- see above */
3931 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3937 hack_subproc_environment (Window preview_window_id, Bool debug_p)
3939 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
3940 necessary yet, but it will make programs work if we had invoked
3941 them with "-root" and not with "-window-id" -- which, of course,
3944 char *nssw = (char *) malloc (40);
3945 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
3947 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
3948 any more, right? It's not Posix, but everyone seems to have it. */
3953 fprintf (stderr, "%s: %s\n", blurb(), nssw);
3955 /* do not free(nssw) -- see above */
3959 /* Called from a timer:
3960 Launches the currently-chosen subprocess, if it's not already running.
3961 If there's a different process running, kills it.
3964 update_subproc_timer (gpointer data)
3966 state *s = (state *) data;
3967 if (! s->desired_preview_cmd)
3968 kill_preview_subproc (s, True);
3969 else if (!s->running_preview_cmd ||
3970 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3971 launch_preview_subproc (s);
3973 s->subproc_timer_id = 0;
3974 return FALSE; /* do not re-execute timer */
3978 /* Call this when you think you might want a preview process running.
3979 It will set a timer that will actually launch that program a second
3980 from now, if you haven't changed your mind (to avoid double-click
3981 spazzing, etc.) `cmd' may be null meaning "no process".
3984 schedule_preview (state *s, const char *cmd)
3986 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3991 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3993 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3996 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3997 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3999 if (s->subproc_timer_id)
4000 gtk_timeout_remove (s->subproc_timer_id);
4001 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4005 /* Called from a timer:
4006 Checks to see if the subproc that should be running, actually is.
4009 check_subproc_timer (gpointer data)
4011 state *s = (state *) data;
4012 Bool again_p = True;
4014 if (s->running_preview_error_p || /* already dead */
4015 s->running_preview_pid <= 0)
4023 status = kill (s->running_preview_pid, 0);
4024 if (status < 0 && errno == ESRCH)
4025 s->running_preview_error_p = True;
4029 char *ss = subproc_pretty_name (s);
4030 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4031 (unsigned long) s->running_preview_pid, ss,
4032 (s->running_preview_error_p ? "dead" : "alive"));
4036 if (s->running_preview_error_p)
4038 clear_preview_window (s);
4043 /* Otherwise, it's currently alive. We might be checking again, or we
4044 might be satisfied. */
4046 if (--s->subproc_check_countdown <= 0)
4050 return TRUE; /* re-execute timer */
4053 s->subproc_check_timer_id = 0;
4054 s->subproc_check_countdown = 0;
4055 return FALSE; /* do not re-execute timer */
4060 /* Call this just after launching a subprocess.
4061 This sets a timer that will, five times a second for two seconds,
4062 check whether the program is still running. The assumption here
4063 is that if the process didn't stay up for more than a couple of
4064 seconds, then either the program doesn't exist, or it doesn't
4065 take a -window-id argument.
4068 schedule_preview_check (state *s)
4074 fprintf (stderr, "%s: scheduling check\n", blurb());
4076 if (s->subproc_check_timer_id)
4077 gtk_timeout_remove (s->subproc_check_timer_id);
4078 s->subproc_check_timer_id =
4079 gtk_timeout_add (1000 / ticks,
4080 check_subproc_timer, (gpointer) s);
4081 s->subproc_check_countdown = ticks * seconds;
4086 screen_blanked_p (void)
4090 unsigned long nitems, bytesafter;
4091 unsigned char *dataP = 0;
4092 Display *dpy = GDK_DISPLAY();
4093 Bool blanked_p = False;
4095 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4096 XA_SCREENSAVER_STATUS,
4097 0, 3, False, XA_INTEGER,
4098 &type, &format, &nitems, &bytesafter,
4101 && type == XA_INTEGER
4105 Atom *data = (Atom *) dataP;
4106 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4109 if (dataP) XFree (dataP);
4114 /* Wake up every now and then and see if the screen is blanked.
4115 If it is, kill off the small-window demo -- no point in wasting
4116 cycles by running two screensavers at once...
4119 check_blanked_timer (gpointer data)
4121 state *s = (state *) data;
4122 Bool blanked_p = screen_blanked_p ();
4123 if (blanked_p && s->running_preview_pid)
4126 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4127 kill_preview_subproc (s, True);
4130 return True; /* re-execute timer */
4134 /* How many screens are there (including Xinerama.)
4137 screen_count (Display *dpy)
4139 int nscreens = ScreenCount(dpy);
4140 # ifdef HAVE_XINERAMA
4143 int event_number, error_number;
4144 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4145 XineramaIsActive (dpy))
4147 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4148 if (xsi) XFree (xsi);
4151 # endif /* HAVE_XINERAMA */
4157 /* Setting window manager icon
4161 init_icon (GdkWindow *window)
4163 GdkBitmap *mask = 0;
4166 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4167 (gchar **) logo_50_xpm);
4169 gdk_window_set_icon (window, 0, pixmap, mask);
4173 /* The main demo-mode command loop.
4178 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4179 XrmRepresentation *type, XrmValue *value, XPointer closure)
4182 for (i = 0; quarks[i]; i++)
4184 if (bindings[i] == XrmBindTightly)
4185 fprintf (stderr, (i == 0 ? "" : "."));
4186 else if (bindings[i] == XrmBindLoosely)
4187 fprintf (stderr, "*");
4189 fprintf (stderr, " ??? ");
4190 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4193 fprintf (stderr, ": %s\n", (char *) value->addr);
4201 the_network_is_not_the_computer (state *s)
4203 Display *dpy = GDK_DISPLAY();
4204 char *rversion = 0, *ruser = 0, *rhost = 0;
4205 char *luser, *lhost;
4207 struct passwd *p = getpwuid (getuid ());
4208 const char *d = DisplayString (dpy);
4210 # if defined(HAVE_UNAME)
4212 if (uname (&uts) < 0)
4213 lhost = "<UNKNOWN>";
4215 lhost = uts.nodename;
4217 strcpy (lhost, getenv("SYS$NODE"));
4218 # else /* !HAVE_UNAME && !VMS */
4219 strcat (lhost, "<UNKNOWN>");
4220 # endif /* !HAVE_UNAME && !VMS */
4222 if (p && p->pw_name)
4227 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4229 /* Make a buffer that's big enough for a number of copies of all the
4230 strings, plus some. */
4231 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4232 (ruser ? strlen(ruser) : 0) +
4233 (rhost ? strlen(rhost) : 0) +
4240 if (!rversion || !*rversion)
4244 "The XScreenSaver daemon doesn't seem to be running\n"
4245 "on display \"%s\". Launch it now?"),
4248 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4250 /* Warn that the two processes are running as different users.
4254 "%s is running as user \"%s\" on host \"%s\".\n"
4255 "But the xscreensaver managing display \"%s\"\n"
4256 "is running as user \"%s\" on host \"%s\".\n"
4258 "Since they are different users, they won't be reading/writing\n"
4259 "the same ~/.xscreensaver file, so %s isn't\n"
4260 "going to work right.\n"
4262 "You should either re-run %s as \"%s\", or re-run\n"
4263 "xscreensaver as \"%s\".\n"
4265 "Restart the xscreensaver daemon now?\n"),
4266 progname, luser, lhost,
4268 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4270 progname, (ruser ? ruser : "???"),
4273 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4275 /* Warn that the two processes are running on different hosts.
4279 "%s is running as user \"%s\" on host \"%s\".\n"
4280 "But the xscreensaver managing display \"%s\"\n"
4281 "is running as user \"%s\" on host \"%s\".\n"
4283 "If those two machines don't share a file system (that is,\n"
4284 "if they don't see the same ~%s/.xscreensaver file) then\n"
4285 "%s won't work right.\n"
4287 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4288 progname, luser, lhost,
4290 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4295 else if (!!strcmp (rversion, s->short_version))
4297 /* Warn that the version numbers don't match.
4301 "This is %s version %s.\n"
4302 "But the xscreensaver managing display \"%s\"\n"
4303 "is version %s. This could cause problems.\n"
4305 "Restart the xscreensaver daemon now?\n"),
4306 progname, s->short_version,
4313 warning_dialog (s->toplevel_widget, msg, True, 1);
4315 if (rversion) free (rversion);
4316 if (ruser) free (ruser);
4317 if (rhost) free (rhost);
4322 /* We use this error handler so that X errors are preceeded by the name
4323 of the program that generated them.
4326 demo_ehandler (Display *dpy, XErrorEvent *error)
4328 state *s = global_state_kludge; /* I hate C so much... */
4329 fprintf (stderr, "\nX error in %s:\n", blurb());
4330 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4331 kill_preview_subproc (s, False);
4337 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4338 of the program that generated them; and also that we can ignore one
4339 particular bogus error message that Gdk madly spews.
4342 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4343 const gchar *message, gpointer user_data)
4345 /* Ignore the message "Got event for unknown window: 0x...".
4346 Apparently some events are coming in for the xscreensaver window
4347 (presumably reply events related to the ClientMessage) and Gdk
4348 feels the need to complain about them. So, just suppress any
4349 messages that look like that one.
4351 if (strstr (message, "unknown window"))
4354 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4355 (log_domain ? log_domain : progclass),
4356 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4357 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4358 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4359 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4360 log_level == G_LOG_LEVEL_INFO ? "info" :
4361 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4363 ((!*message || message[strlen(message)-1] != '\n')
4369 __extension__ /* shut up about "string length is greater than the length
4370 ISO C89 compilers are required to support" when including
4374 static char *defaults[] = {
4375 #include "XScreenSaver_ad.h"
4380 #ifdef HAVE_CRAPPLET
4381 static struct poptOption crapplet_options[] = {
4382 {NULL, '\0', 0, NULL, 0}
4384 #endif /* HAVE_CRAPPLET */
4387 const char *usage = "[--display dpy] [--prefs]"
4388 # ifdef HAVE_CRAPPLET
4391 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4394 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4396 state *s = (state *) user_data;
4397 Boolean oi = s->initializing_p;
4399 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4401 s->initializing_p = True;
4403 eschew_gtk_lossage (label);
4405 s->initializing_p = oi;
4411 print_widget_tree (GtkWidget *w, int depth)
4414 for (i = 0; i < depth; i++)
4415 fprintf (stderr, " ");
4416 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4418 if (GTK_IS_LIST (w))
4420 for (i = 0; i < depth+1; i++)
4421 fprintf (stderr, " ");
4422 fprintf (stderr, "...list kids...\n");
4424 else if (GTK_IS_CONTAINER (w))
4426 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4429 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4437 delayed_scroll_kludge (gpointer data)
4439 state *s = (state *) data;
4440 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4441 ensure_selected_item_visible (w);
4443 /* Oh, this is just fucking lovely, too. */
4444 w = GTK_WIDGET (name_to_widget (s, "preview"));
4445 gtk_widget_hide (w);
4446 gtk_widget_show (w);
4448 return FALSE; /* do not re-execute timer */
4454 create_xscreensaver_demo (void)
4458 nb = name_to_widget (global_state_kludge, "preview_notebook");
4459 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4461 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4465 create_xscreensaver_settings_dialog (void)
4469 box = name_to_widget (global_state_kludge, "dialog_action_area");
4471 w = name_to_widget (global_state_kludge, "adv_button");
4472 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4474 w = name_to_widget (global_state_kludge, "std_button");
4475 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4477 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4480 #endif /* HAVE_GTK2 */
4483 main (int argc, char **argv)
4487 saver_preferences *p;
4491 Widget toplevel_shell;
4492 char *real_progname = argv[0];
4495 Bool crapplet_p = False;
4499 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4500 textdomain (GETTEXT_PACKAGE);
4503 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4504 # else /* !HAVE_GTK2 */
4505 if (!setlocale (LC_ALL, ""))
4506 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4507 # endif /* !HAVE_GTK2 */
4509 #endif /* ENABLE_NLS */
4511 str = strrchr (real_progname, '/');
4512 if (str) real_progname = str+1;
4515 memset (s, 0, sizeof(*s));
4516 s->initializing_p = True;
4519 global_state_kludge = s; /* I hate C so much... */
4521 progname = real_progname;
4523 s->short_version = (char *) malloc (5);
4524 memcpy (s->short_version, screensaver_id + 17, 4);
4525 s->short_version [4] = 0;
4528 /* Register our error message logger for every ``log domain'' known.
4529 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4530 for all of the domains that seem to be in use.
4533 const char * const domains[] = { 0,
4534 "Gtk", "Gdk", "GLib", "GModule",
4535 "GThread", "Gnome", "GnomeUI" };
4536 for (i = 0; i < countof(domains); i++)
4537 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4540 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4543 const char *dir = DEFAULT_ICONDIR;
4544 if (*dir) add_pixmap_directory (dir);
4546 # endif /* !HAVE_GTK2 */
4547 #endif /* DEFAULT_ICONDIR */
4549 /* This is gross, but Gtk understands --display and not -display...
4551 for (i = 1; i < argc; i++)
4552 if (argv[i][0] && argv[i][1] &&
4553 !strncmp(argv[i], "-display", strlen(argv[i])))
4554 argv[i] = "--display";
4557 /* We need to parse this arg really early... Sigh. */
4558 for (i = 1; i < argc; i++)
4561 (!strcmp(argv[i], "--crapplet") ||
4562 !strcmp(argv[i], "--capplet")))
4564 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4567 for (j = i; j < argc; j++) /* remove it from the list */
4568 argv[j] = argv[j+1];
4570 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4571 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4573 fprintf (stderr, "%s: %s\n", real_progname, usage);
4575 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4578 (!strcmp(argv[i], "--debug") ||
4579 !strcmp(argv[i], "-debug") ||
4580 !strcmp(argv[i], "-d")))
4584 for (j = i; j < argc; j++) /* remove it from the list */
4585 argv[j] = argv[j+1];
4592 (!strcmp(argv[i], "-geometry") ||
4593 !strcmp(argv[i], "-geom") ||
4594 !strcmp(argv[i], "-geo") ||
4595 !strcmp(argv[i], "-g")))
4599 for (j = i; j < argc; j++) /* remove them from the list */
4600 argv[j] = argv[j+2];
4607 (!strcmp(argv[i], "--configdir")))
4611 hack_configuration_path = argv[i+1];
4612 for (j = i; j < argc; j++) /* remove them from the list */
4613 argv[j] = argv[j+2];
4617 if (0 != stat (hack_configuration_path, &st))
4620 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4624 else if (!S_ISDIR (st.st_mode))
4626 fprintf (stderr, "%s: not a directory: %s\n",
4627 blurb(), hack_configuration_path);
4635 fprintf (stderr, "%s: using config directory \"%s\"\n",
4636 progname, hack_configuration_path);
4639 /* Let Gtk open the X connection, then initialize Xt to use that
4640 same connection. Doctor Frankenstein would be proud.
4642 # ifdef HAVE_CRAPPLET
4645 GnomeClient *client;
4646 GnomeClientFlags flags = 0;
4648 int init_results = gnome_capplet_init ("screensaver-properties",
4650 argc, argv, NULL, 0, NULL);
4652 0 upon successful initialization;
4653 1 if --init-session-settings was passed on the cmdline;
4654 2 if --ignore was passed on the cmdline;
4657 So the 1 signifies just to init the settings, and quit, basically.
4658 (Meaning launch the xscreensaver daemon.)
4661 if (init_results < 0)
4664 g_error ("An initialization error occurred while "
4665 "starting xscreensaver-capplet.\n");
4667 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4668 real_progname, init_results);
4673 client = gnome_master_client ();
4676 flags = gnome_client_get_flags (client);
4678 if (flags & GNOME_CLIENT_IS_CONNECTED)
4681 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4682 gnome_client_get_id (client));
4685 char *session_args[20];
4687 session_args[i++] = real_progname;
4688 session_args[i++] = "--capplet";
4689 session_args[i++] = "--init-session-settings";
4690 session_args[i] = 0;
4691 gnome_client_set_priority (client, 20);
4692 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4693 gnome_client_set_restart_command (client, i, session_args);
4697 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4700 gnome_client_flush (client);
4703 if (init_results == 1)
4705 system ("xscreensaver -nosplash &");
4711 # endif /* HAVE_CRAPPLET */
4713 gtk_init (&argc, &argv);
4717 /* We must read exactly the same resources as xscreensaver.
4718 That means we must have both the same progclass *and* progname,
4719 at least as far as the resource database is concerned. So,
4720 put "xscreensaver" in argv[0] while initializing Xt.
4722 argv[0] = "xscreensaver";
4726 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4728 XtToolkitInitialize ();
4729 app = XtCreateApplicationContext ();
4730 dpy = GDK_DISPLAY();
4731 XtAppSetFallbackResources (app, defaults);
4732 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4733 toplevel_shell = XtAppCreateShell (progname, progclass,
4734 applicationShellWidgetClass,
4737 dpy = XtDisplay (toplevel_shell);
4738 db = XtDatabase (dpy);
4739 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4740 XSetErrorHandler (demo_ehandler);
4742 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4743 signal (SIGPIPE, SIG_IGN);
4745 /* After doing Xt-style command-line processing, complain about any
4746 unrecognized command-line arguments.
4748 for (i = 1; i < argc; i++)
4750 char *str = argv[i];
4751 if (str[0] == '-' && str[1] == '-')
4753 if (!strcmp (str, "-prefs"))
4755 else if (crapplet_p)
4756 /* There are lots of random args that we don't care about when we're
4757 started as a crapplet, so just ignore unknown args in that case. */
4761 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4763 fprintf (stderr, "%s: %s\n", real_progname, usage);
4768 /* Load the init file, which may end up consulting the X resource database
4769 and the site-wide app-defaults file. Note that at this point, it's
4770 important that `progname' be "xscreensaver", rather than whatever
4774 s->nscreens = screen_count (dpy);
4776 hack_environment (s); /* must be before initialize_sort_map() */
4779 initialize_sort_map (s);
4781 /* Now that Xt has been initialized, and the resources have been read,
4782 we can set our `progname' variable to something more in line with
4785 progname = real_progname;
4789 /* Print out all the resources we read. */
4791 XrmName name = { 0 };
4792 XrmClass class = { 0 };
4794 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4800 /* Intern the atoms that xscreensaver_command() needs.
4802 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4803 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4804 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4805 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4806 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4807 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4808 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4809 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4810 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4811 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4812 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4813 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4814 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4817 /* Create the window and all its widgets.
4819 s->base_widget = create_xscreensaver_demo ();
4820 s->popup_widget = create_xscreensaver_settings_dialog ();
4821 s->toplevel_widget = s->base_widget;
4824 /* Set the main window's title. */
4826 char *base_title = _("Screensaver Preferences");
4827 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4828 char *s1, *s2, *s3, *s4;
4829 s1 = (char *) strchr(v, ' '); s1++;
4830 s2 = (char *) strchr(s1, ' ');
4831 s3 = (char *) strchr(v, '('); s3++;
4832 s4 = (char *) strchr(s3, ')');
4836 window_title = (char *) malloc (strlen (base_title) +
4837 strlen (progclass) +
4838 strlen (s1) + strlen (s3) +
4840 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4841 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4842 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4846 /* Adjust the (invisible) notebooks on the popup dialog... */
4848 GtkNotebook *notebook =
4849 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4850 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4854 gtk_widget_hide (std);
4855 # else /* !HAVE_XML */
4856 /* Make the advanced page be the only one available. */
4857 gtk_widget_set_sensitive (std, False);
4858 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4859 gtk_widget_hide (std);
4861 # endif /* !HAVE_XML */
4863 gtk_notebook_set_page (notebook, page);
4864 gtk_notebook_set_show_tabs (notebook, False);
4867 /* Various other widget initializations...
4869 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4870 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4872 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4873 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4876 populate_hack_list (s);
4877 populate_prefs_page (s);
4878 sensitize_demo_widgets (s, False);
4879 fix_text_entry_sizes (s);
4880 scroll_to_current_hack (s);
4882 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4883 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4887 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4888 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4890 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4891 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4893 #endif /* !HAVE_GTK2 */
4895 /* Hook up callbacks to the items on the mode menu. */
4897 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4898 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4899 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4901 for (i = 0; kids; kids = kids->next, i++)
4903 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4904 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4907 /* The "random-same" mode menu item does not appear unless
4908 there are multple screens.
4910 if (s->nscreens <= 1 &&
4911 mode_menu_order[i] == RANDOM_HACKS_SAME)
4912 gtk_widget_hide (GTK_WIDGET (kids->data));
4915 if (s->nscreens <= 1) /* recompute option-menu size */
4917 gtk_widget_unrealize (GTK_WIDGET (menu));
4918 gtk_widget_realize (GTK_WIDGET (menu));
4923 /* Handle the -prefs command-line argument. */
4926 GtkNotebook *notebook =
4927 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4928 gtk_notebook_set_page (notebook, 1);
4931 # ifdef HAVE_CRAPPLET
4935 GtkWidget *outer_vbox;
4937 gtk_widget_hide (s->toplevel_widget);
4939 capplet = capplet_widget_new ();
4941 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4942 # ifdef HAVE_CRAPPLET_IMMEDIATE
4943 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4944 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4945 /* In crapplet-mode, take off the menubar. */
4946 gtk_widget_hide (name_to_widget (s, "menubar"));
4948 /* Reparent our top-level container to be a child of the capplet
4951 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4952 gtk_widget_ref (outer_vbox);
4953 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4955 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4956 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4958 /* Find the window above us, and set the title and close handler. */
4960 GtkWidget *window = capplet;
4961 while (window && !GTK_IS_WINDOW (window))
4962 window = window->parent;
4965 gtk_window_set_title (GTK_WINDOW (window), window_title);
4966 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4967 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4972 s->toplevel_widget = capplet;
4974 # endif /* HAVE_CRAPPLET */
4977 /* The Gnome folks hate the menubar. I think it's important to have access
4978 to the commands on the File menu (Restart Daemon, etc.) and to the
4979 About and Documentation commands on the Help menu.
4983 gtk_widget_hide (name_to_widget (s, "menubar"));
4987 free (window_title);
4991 /* After picking the default size, allow -geometry to override it. */
4993 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
4996 gtk_widget_show (s->toplevel_widget);
4997 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4998 fix_preview_visual (s);
5000 /* Realize page zero, so that we can diddle the scrollbar when the
5001 user tabs back to it -- otherwise, the current hack isn't scrolled
5002 to the first time they tab back there, when started with "-prefs".
5003 (Though it is if they then tab away, and back again.)
5005 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5006 #### understands this crap, explain to me how to make this work.
5008 gtk_widget_realize (name_to_widget (s, "demos_table"));
5011 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5014 /* Issue any warnings about the running xscreensaver daemon. */
5015 the_network_is_not_the_computer (s);
5018 /* Run the Gtk event loop, and not the Xt event loop. This means that
5019 if there were Xt timers or fds registered, they would never get serviced,
5020 and if there were any Xt widgets, they would never have events delivered.
5021 Fortunately, we're using Gtk for all of the UI, and only initialized
5022 Xt so that we could process the command line and use the X resource
5025 s->initializing_p = False;
5027 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5028 after we start up. Otherwise, it always appears scrolled to the top
5029 when in crapplet-mode. */
5030 gtk_timeout_add (500, delayed_scroll_kludge, s);
5034 /* Load every configurator in turn, to scan them for errors all at once. */
5037 for (i = 0; i < p->screenhacks_count; i++)
5039 screenhack *hack = p->screenhacks[i];
5040 conf_data *d = load_configurator (hack->command, False);
5041 if (d) free_conf_data (d);
5047 # ifdef HAVE_CRAPPLET
5049 capplet_gtk_main ();
5051 # endif /* HAVE_CRAPPLET */
5054 kill_preview_subproc (s, False);
5058 #endif /* HAVE_GTK -- whole file */