1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2007 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)))
155 /* You might think that to read an array of 32-bit quantities out of a
156 server-side property, you would pass an array of 32-bit data quantities
157 into XGetWindowProperty(). You would be wrong. You have to use an array
158 of longs, even if long is 64 bits (using 32 of each 64.)
163 char *progclass = "XScreenSaver";
166 /* The order of the items in the mode menu. */
167 static int mode_menu_order[] = {
168 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
173 char *short_version; /* version number of this xscreensaver build */
175 GtkWidget *toplevel_widget; /* the main window */
176 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
177 GtkWidget *popup_widget; /* the "Settings" dialog */
178 conf_data *cdata; /* private data for per-hack configuration */
181 GladeXML *glade_ui; /* Glade UI file */
182 #endif /* HAVE_GTK2 */
184 Bool debug_p; /* whether to print diagnostics */
185 Bool initializing_p; /* flag for breaking recursion loops */
186 Bool saving_p; /* flag for breaking recursion loops */
188 char *desired_preview_cmd; /* subprocess we intend to run */
189 char *running_preview_cmd; /* subprocess we are currently running */
190 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
191 Bool running_preview_error_p; /* whether the pid died abnormally */
193 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
194 int subproc_timer_id; /* timer to delay subproc launch */
195 int subproc_check_timer_id; /* timer to check whether it started up */
196 int subproc_check_countdown; /* how many more checks left */
198 int *list_elt_to_hack_number; /* table for sorting the hack list */
199 int *hack_number_to_list_elt; /* the inverse table */
200 Bool *hacks_available_p; /* whether hacks are on $PATH */
201 int total_available; /* how many are on $PATH */
202 int list_count; /* how many items are in the list: this may be
203 less than p->screenhacks_count, if some are
206 int _selected_list_element; /* don't use this: call
207 selected_list_element() instead */
209 int nscreens; /* How many X or Xinerama screens there are */
211 saver_preferences prefs;
216 /* Total fucking evilness due to the fact that it's rocket science to get
217 a closure object of our own down into the various widget callbacks. */
218 static state *global_state_kludge;
221 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
222 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
223 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
226 static void populate_demo_window (state *, int list_elt);
227 static void populate_prefs_page (state *);
228 static void populate_popup_window (state *);
230 static Bool flush_dialog_changes_and_save (state *);
231 static Bool flush_popup_changes_and_save (state *);
233 static int maybe_reload_init_file (state *);
234 static void await_xscreensaver (state *);
235 static Bool xscreensaver_running_p (state *);
236 static void sensitize_menu_items (state *s, Bool force_p);
237 static void force_dialog_repaint (state *s);
239 static void schedule_preview (state *, const char *cmd);
240 static void kill_preview_subproc (state *, Bool reset_p);
241 static void schedule_preview_check (state *);
244 /* Prototypes of functions used by the Glade-generated code,
247 void exit_menu_cb (GtkMenuItem *, gpointer user_data);
248 void about_menu_cb (GtkMenuItem *, gpointer user_data);
249 void doc_menu_cb (GtkMenuItem *, gpointer user_data);
250 void file_menu_cb (GtkMenuItem *, gpointer user_data);
251 void activate_menu_cb (GtkMenuItem *, gpointer user_data);
252 void lock_menu_cb (GtkMenuItem *, gpointer user_data);
253 void kill_menu_cb (GtkMenuItem *, gpointer user_data);
254 void restart_menu_cb (GtkWidget *, gpointer user_data);
255 void run_this_cb (GtkButton *, gpointer user_data);
256 void manual_cb (GtkButton *, gpointer user_data);
257 void run_next_cb (GtkButton *, gpointer user_data);
258 void run_prev_cb (GtkButton *, gpointer user_data);
259 void pref_changed_cb (GtkWidget *, gpointer user_data);
260 gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
261 void mode_menu_item_cb (GtkWidget *, gpointer user_data);
262 void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
263 gint page_num, gpointer user_data);
264 void browse_image_dir_cb (GtkButton *, gpointer user_data);
265 void browse_text_file_cb (GtkButton *, gpointer user_data);
266 void browse_text_program_cb (GtkButton *, gpointer user_data);
267 void settings_cb (GtkButton *, gpointer user_data);
268 void settings_adv_cb (GtkButton *, gpointer user_data);
269 void settings_std_cb (GtkButton *, gpointer user_data);
270 void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
271 gint page_num, gpointer user_data);
272 void settings_cancel_cb (GtkButton *, gpointer user_data);
273 void settings_ok_cb (GtkButton *, gpointer user_data);
276 /* Some random utility functions
279 const char *blurb (void);
284 time_t now = time ((time_t *) 0);
285 char *ct = (char *) ctime (&now);
286 static char buf[255];
287 int n = strlen(progname);
289 strncpy(buf, progname, n);
292 strncpy(buf+n, ct+11, 8);
293 strcpy(buf+n+9, ": ");
299 name_to_widget (state *s, const char *name)
309 /* First try to load the Glade file from the current directory;
310 if there isn't one there, check the installed directory.
312 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
313 const char * const files[] = { GLADE_FILE_NAME,
314 GLADE_DIR "/" GLADE_FILE_NAME };
316 for (i = 0; i < countof (files); i++)
319 if (!stat (files[i], &st))
321 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
328 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
329 "\tfrom " GLADE_DIR "/ or current directory.\n",
333 # undef GLADE_FILE_NAME
335 glade_xml_signal_autoconnect (s->glade_ui);
338 w = glade_xml_get_widget (s->glade_ui, name);
340 #else /* !HAVE_GTK2 */
342 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
345 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
347 #endif /* HAVE_GTK2 */
350 fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
356 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
357 Takes a scroller, viewport, or list as an argument.
360 ensure_selected_item_visible (GtkWidget *widget)
364 GtkTreeSelection *selection;
368 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
369 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
370 path = gtk_tree_path_new_first ();
372 path = gtk_tree_model_get_path (model, &iter);
374 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
376 gtk_tree_path_free (path);
378 #else /* !HAVE_GTK2 */
380 GtkScrolledWindow *scroller = 0;
382 GtkList *list_widget = 0;
386 GtkWidget *selected = 0;
389 gint parent_h, child_y, child_h, children_h, ignore;
390 double ratio_t, ratio_b;
392 if (GTK_IS_SCROLLED_WINDOW (widget))
394 scroller = GTK_SCROLLED_WINDOW (widget);
395 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
396 list_widget = GTK_LIST (GTK_BIN(vp)->child);
398 else if (GTK_IS_VIEWPORT (widget))
400 vp = GTK_VIEWPORT (widget);
401 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
402 list_widget = GTK_LIST (GTK_BIN(vp)->child);
404 else if (GTK_IS_LIST (widget))
406 list_widget = GTK_LIST (widget);
407 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
408 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
413 slist = list_widget->selection;
414 selected = (slist ? GTK_WIDGET (slist->data) : 0);
418 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
420 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
421 kids; kids = kids->next)
424 adj = gtk_scrolled_window_get_vadjustment (scroller);
426 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
427 &ignore, &ignore, &ignore, &parent_h, &ignore);
428 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
429 &ignore, &child_y, &ignore, &child_h, &ignore);
430 children_h = nkids * child_h;
432 ratio_t = ((double) child_y) / ((double) children_h);
433 ratio_b = ((double) child_y + child_h) / ((double) children_h);
435 if (adj->upper == 0.0) /* no items in list */
438 if (ratio_t < (adj->value / adj->upper) ||
439 ratio_b > ((adj->value + adj->page_size) / adj->upper))
442 int slop = parent_h * 0.75; /* how much to overshoot by */
444 if (ratio_t < (adj->value / adj->upper))
446 double ratio_w = ((double) parent_h) / ((double) children_h);
447 double ratio_l = (ratio_b - ratio_t);
448 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
451 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
453 target = ratio_t * adj->upper;
457 if (target > adj->upper - adj->page_size)
458 target = adj->upper - adj->page_size;
462 gtk_adjustment_set_value (adj, target);
464 #endif /* !HAVE_GTK2 */
468 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
470 GtkWidget *shell = GTK_WIDGET (user_data);
471 while (shell->parent)
472 shell = shell->parent;
473 gtk_widget_destroy (GTK_WIDGET (shell));
477 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
479 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
481 restart_menu_cb (widget, user_data);
482 warning_dialog_dismiss_cb (widget, user_data);
486 warning_dialog (GtkWidget *parent, const char *message,
487 Boolean restart_button_p, int center)
489 char *msg = strdup (message);
492 GtkWidget *dialog = gtk_dialog_new ();
493 GtkWidget *label = 0;
495 GtkWidget *cancel = 0;
498 while (parent && !parent->window)
499 parent = parent->parent;
502 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
504 fprintf (stderr, "%s: too early for dialog?\n", progname);
512 char *s = strchr (head, '\n');
515 sprintf (name, "label%d", i++);
518 label = gtk_label_new (head);
520 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
521 #endif /* HAVE_GTK2 */
526 GTK_WIDGET (label)->style =
527 gtk_style_copy (GTK_WIDGET (label)->style);
528 GTK_WIDGET (label)->style->font =
529 gdk_font_load (get_string_resource("warning_dialog.headingFont",
531 gtk_widget_set_style (GTK_WIDGET (label),
532 GTK_WIDGET (label)->style);
534 #endif /* !HAVE_GTK2 */
536 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
537 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
538 label, TRUE, TRUE, 0);
539 gtk_widget_show (label);
550 label = gtk_label_new ("");
551 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
552 label, TRUE, TRUE, 0);
553 gtk_widget_show (label);
555 label = gtk_hbutton_box_new ();
556 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
557 label, TRUE, TRUE, 0);
560 if (restart_button_p)
562 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
563 gtk_container_add (GTK_CONTAINER (label), cancel);
566 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
567 gtk_container_add (GTK_CONTAINER (label), ok);
569 #else /* !HAVE_GTK2 */
571 ok = gtk_button_new_with_label ("OK");
572 gtk_container_add (GTK_CONTAINER (label), ok);
574 if (restart_button_p)
576 cancel = gtk_button_new_with_label ("Cancel");
577 gtk_container_add (GTK_CONTAINER (label), cancel);
580 #endif /* !HAVE_GTK2 */
582 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
583 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
584 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
585 STFU GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
586 gtk_widget_show (ok);
587 gtk_widget_grab_focus (ok);
591 STFU GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
592 gtk_widget_show (cancel);
594 gtk_widget_show (label);
595 gtk_widget_show (dialog);
597 if (restart_button_p)
599 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
600 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
602 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
603 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
608 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
609 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
613 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
614 GTK_WIDGET (parent)->window);
617 gtk_window_present (GTK_WINDOW (dialog));
618 #else /* !HAVE_GTK2 */
619 gdk_window_show (GTK_WIDGET (dialog)->window);
620 gdk_window_raise (GTK_WIDGET (dialog)->window);
621 #endif /* !HAVE_GTK2 */
628 run_cmd (state *s, Atom command, int arg)
633 flush_dialog_changes_and_save (s);
634 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
636 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
637 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
644 sprintf (buf, "Error:\n\n%s", err);
646 strcpy (buf, "Unknown error!");
647 warning_dialog (s->toplevel_widget, buf, False, 100);
651 sensitize_menu_items (s, True);
652 force_dialog_repaint (s);
657 run_hack (state *s, int list_elt, Bool report_errors_p)
663 if (list_elt < 0) return;
664 hack_number = s->list_elt_to_hack_number[list_elt];
666 flush_dialog_changes_and_save (s);
667 schedule_preview (s, 0);
669 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
672 if (status < 0 && report_errors_p)
674 if (xscreensaver_running_p (s))
676 /* Kludge: ignore the spurious "window unexpectedly deleted"
678 if (err && strstr (err, "unexpectedly deleted"))
685 sprintf (buf, "Error:\n\n%s", err);
687 strcpy (buf, "Unknown error!");
688 warning_dialog (s->toplevel_widget, buf, False, 100);
693 /* The error is that the daemon isn't running;
696 const char *d = DisplayString (GDK_DISPLAY());
700 "The XScreenSaver daemon doesn't seem to be running\n"
701 "on display \"%s\". Launch it now?"),
703 warning_dialog (s->toplevel_widget, msg, True, 1);
709 sensitize_menu_items (s, False);
716 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
717 libglade work on Cygwin; apparently all Glade callbacks need this magic
718 extra declaration. I do not pretend to understand.
722 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
724 state *s = global_state_kludge; /* I hate C so much... */
725 flush_dialog_changes_and_save (s);
726 kill_preview_subproc (s, False);
731 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
733 state *s = (state *) data;
734 flush_dialog_changes_and_save (s);
741 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
744 char *vers = strdup (screensaver_id + 4);
747 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
749 s = strchr (vers, ',');
753 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
754 non-ASCII characters aren't allowed in localizable string keys."
755 (I don't want to just use (c) instead of © because that doesn't
756 look as good in the plain-old default Latin1 "C" locale.)
759 sprintf(copy, ("Copyright \xC2\xA9 1991-2006 %s"), s);
760 #else /* !HAVE_GTK2 */
761 sprintf(copy, ("Copyright \251 1991-2006 %s"), s);
762 #endif /* !HAVE_GTK2 */
764 sprintf (msg, "%s\n\n%s", copy, desc);
766 /* I can't make gnome_about_new() work here -- it starts dying in
767 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
768 then this might be the thing to do:
772 const gchar *auth[] = { 0 };
773 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
775 gtk_widget_show (about);
777 #else / * GTK but not GNOME * /
781 GdkColormap *colormap;
782 GdkPixmap *gdkpixmap;
785 GtkWidget *dialog = gtk_dialog_new ();
786 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
787 GtkWidget *parent = GTK_WIDGET (menuitem);
788 while (parent->parent)
789 parent = parent->parent;
791 hbox = gtk_hbox_new (FALSE, 20);
792 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
793 hbox, TRUE, TRUE, 0);
795 colormap = gtk_widget_get_colormap (parent);
797 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
798 (gchar **) logo_180_xpm);
799 icon = gtk_pixmap_new (gdkpixmap, mask);
800 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
802 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
804 vbox = gtk_vbox_new (FALSE, 0);
805 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
807 label1 = gtk_label_new (vers);
808 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
809 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
810 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
813 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
814 GTK_WIDGET (label1)->style->font =
815 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
816 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
817 #endif /* HAVE_GTK2 */
819 label2 = gtk_label_new (msg);
820 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
821 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
822 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
825 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
826 GTK_WIDGET (label2)->style->font =
827 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
828 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
829 #endif /* HAVE_GTK2 */
831 hb = gtk_hbutton_box_new ();
833 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
837 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
838 #else /* !HAVE_GTK2 */
839 ok = gtk_button_new_with_label (_("OK"));
840 #endif /* !HAVE_GTK2 */
841 gtk_container_add (GTK_CONTAINER (hb), ok);
843 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
844 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
845 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
847 gtk_widget_show (hbox);
848 gtk_widget_show (icon);
849 gtk_widget_show (vbox);
850 gtk_widget_show (label1);
851 gtk_widget_show (label2);
852 gtk_widget_show (hb);
853 gtk_widget_show (ok);
854 gtk_widget_show (dialog);
856 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
857 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
859 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
860 GTK_WIDGET (parent)->window);
861 gdk_window_show (GTK_WIDGET (dialog)->window);
862 gdk_window_raise (GTK_WIDGET (dialog)->window);
868 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
870 state *s = global_state_kludge; /* I hate C so much... */
871 saver_preferences *p = &s->prefs;
874 if (!p->help_url || !*p->help_url)
876 warning_dialog (s->toplevel_widget,
878 "No Help URL has been specified.\n"), False, 100);
882 help_command = (char *) malloc (strlen (p->load_url_command) +
883 (strlen (p->help_url) * 4) + 20);
884 strcpy (help_command, "( ");
885 sprintf (help_command + strlen(help_command),
887 p->help_url, p->help_url, p->help_url, p->help_url);
888 strcat (help_command, " ) &");
889 if (system (help_command) < 0)
890 fprintf (stderr, "%s: fork error\n", blurb());
896 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
898 state *s = global_state_kludge; /* I hate C so much... */
899 sensitize_menu_items (s, False);
904 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
906 state *s = global_state_kludge; /* I hate C so much... */
907 run_cmd (s, XA_ACTIVATE, 0);
912 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
914 state *s = global_state_kludge; /* I hate C so much... */
915 run_cmd (s, XA_LOCK, 0);
920 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
922 state *s = global_state_kludge; /* I hate C so much... */
923 run_cmd (s, XA_EXIT, 0);
928 restart_menu_cb (GtkWidget *widget, gpointer user_data)
930 state *s = global_state_kludge; /* I hate C so much... */
931 flush_dialog_changes_and_save (s);
932 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
934 if (system ("xscreensaver -nosplash &") < 0)
935 fprintf (stderr, "%s: fork error\n", blurb());
937 await_xscreensaver (s);
941 xscreensaver_running_p (state *s)
943 Display *dpy = GDK_DISPLAY();
945 server_xscreensaver_version (dpy, &rversion, 0, 0);
953 await_xscreensaver (state *s)
958 while (!ok && (--countdown > 0))
959 if (xscreensaver_running_p (s))
962 sleep (1); /* If it's not there yet, wait a second... */
964 sensitize_menu_items (s, True);
968 /* Timed out, no screensaver running. */
971 Bool root_p = (geteuid () == 0);
975 "The xscreensaver daemon did not start up properly.\n"
981 __extension__ /* don't warn about "string length is greater than
982 the length ISO C89 compilers are required to
983 support" in the following expression... */
986 _("You are running as root. This usually means that xscreensaver\n"
987 "was unable to contact your X server because access control is\n"
988 "turned on. Try running this command:\n"
990 " xhost +localhost\n"
992 "and then selecting `File / Restart Daemon'.\n"
994 "Note that turning off access control will allow anyone logged\n"
995 "on to this machine to access your screen, which might be\n"
996 "considered a security problem. Please read the xscreensaver\n"
997 "manual and FAQ for more information.\n"
999 "You shouldn't run X as root. Instead, you should log in as a\n"
1000 "normal user, and `su' as necessary."));
1002 strcat (buf, _("Please check your $PATH and permissions."));
1004 warning_dialog (s->toplevel_widget, buf, False, 1);
1007 force_dialog_repaint (s);
1012 selected_list_element (state *s)
1014 return s->_selected_list_element;
1019 demo_write_init_file (state *s, saver_preferences *p)
1021 Display *dpy = GDK_DISPLAY();
1024 /* #### try to figure out why shit keeps getting reordered... */
1025 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1029 if (!write_init_file (dpy, p, s->short_version, False))
1032 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1037 const char *f = init_file_name();
1039 warning_dialog (s->toplevel_widget,
1040 _("Error:\n\nCouldn't determine init file name!\n"),
1044 char *b = (char *) malloc (strlen(f) + 1024);
1045 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1046 warning_dialog (s->toplevel_widget, b, False, 100);
1054 G_MODULE_EXPORT void
1055 run_this_cb (GtkButton *button, gpointer user_data)
1057 state *s = global_state_kludge; /* I hate C so much... */
1058 int list_elt = selected_list_element (s);
1059 if (list_elt < 0) return;
1060 if (!flush_dialog_changes_and_save (s))
1061 run_hack (s, list_elt, True);
1065 G_MODULE_EXPORT void
1066 manual_cb (GtkButton *button, gpointer user_data)
1068 Display *dpy = GDK_DISPLAY();
1069 state *s = global_state_kludge; /* I hate C so much... */
1070 saver_preferences *p = &s->prefs;
1071 GtkWidget *list_widget = name_to_widget (s, "list");
1072 int list_elt = selected_list_element (s);
1074 char *name, *name2, *cmd, *str;
1076 if (list_elt < 0) return;
1077 hack_number = s->list_elt_to_hack_number[list_elt];
1079 flush_dialog_changes_and_save (s);
1080 ensure_selected_item_visible (list_widget);
1082 name = strdup (p->screenhacks[hack_number]->command);
1085 while (isspace (*name2)) name2++;
1087 while (*str && !isspace (*str)) str++;
1089 str = strrchr (name2, '/');
1090 if (str) name2 = str+1;
1092 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1095 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1096 strcpy (cmd2, "( ");
1097 sprintf (cmd2 + strlen (cmd2),
1099 name2, name2, name2, name2);
1100 strcat (cmd2, " ) &");
1101 if (system (cmd2) < 0)
1102 fprintf (stderr, "%s: fork error\n", blurb());
1107 warning_dialog (GTK_WIDGET (button),
1108 _("Error:\n\nno `manualCommand' resource set."),
1117 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1119 GtkWidget *parent = name_to_widget (s, "scroller");
1120 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
1123 GtkTreeModel *model;
1124 GtkTreeSelection *selection;
1125 #endif /* HAVE_GTK2 */
1127 if (!was) gtk_widget_set_sensitive (parent, True);
1129 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1130 STFU g_assert (model);
1131 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1133 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1134 gtk_tree_selection_select_iter (selection, &iter);
1136 #else /* !HAVE_GTK2 */
1137 gtk_list_select_item (GTK_LIST (list), list_elt);
1138 #endif /* !HAVE_GTK2 */
1139 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1140 if (!was) gtk_widget_set_sensitive (parent, False);
1144 G_MODULE_EXPORT void
1145 run_next_cb (GtkButton *button, gpointer user_data)
1147 state *s = global_state_kludge; /* I hate C so much... */
1148 /* saver_preferences *p = &s->prefs; */
1149 Bool ops = s->preview_suppressed_p;
1151 GtkWidget *list_widget = name_to_widget (s, "list");
1152 int list_elt = selected_list_element (s);
1159 if (list_elt >= s->list_count)
1162 s->preview_suppressed_p = True;
1164 flush_dialog_changes_and_save (s);
1165 force_list_select_item (s, list_widget, list_elt, True);
1166 populate_demo_window (s, list_elt);
1167 run_hack (s, list_elt, False);
1169 s->preview_suppressed_p = ops;
1173 G_MODULE_EXPORT void
1174 run_prev_cb (GtkButton *button, gpointer user_data)
1176 state *s = global_state_kludge; /* I hate C so much... */
1177 /* saver_preferences *p = &s->prefs; */
1178 Bool ops = s->preview_suppressed_p;
1180 GtkWidget *list_widget = name_to_widget (s, "list");
1181 int list_elt = selected_list_element (s);
1184 list_elt = s->list_count - 1;
1189 list_elt = s->list_count - 1;
1191 s->preview_suppressed_p = True;
1193 flush_dialog_changes_and_save (s);
1194 force_list_select_item (s, list_widget, list_elt, True);
1195 populate_demo_window (s, list_elt);
1196 run_hack (s, list_elt, False);
1198 s->preview_suppressed_p = ops;
1202 /* Writes the given settings into prefs.
1203 Returns true if there was a change, False otherwise.
1204 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1207 flush_changes (state *s,
1210 const char *command,
1213 saver_preferences *p = &s->prefs;
1214 Bool changed = False;
1217 if (list_elt < 0 || list_elt >= s->list_count)
1220 hack_number = s->list_elt_to_hack_number[list_elt];
1221 hack = p->screenhacks[hack_number];
1223 if (enabled_p != -1 &&
1224 enabled_p != hack->enabled_p)
1226 hack->enabled_p = enabled_p;
1229 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1230 blurb(), hack->name, enabled_p);
1235 if (!hack->command || !!strcmp (command, hack->command))
1237 if (hack->command) free (hack->command);
1238 hack->command = strdup (command);
1241 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1242 blurb(), hack->name, command);
1248 const char *ov = hack->visual;
1249 if (!ov || !*ov) ov = "any";
1250 if (!*visual) visual = "any";
1251 if (!!strcasecmp (visual, ov))
1253 if (hack->visual) free (hack->visual);
1254 hack->visual = strdup (visual);
1257 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1258 blurb(), hack->name, visual);
1266 /* Helper for the text fields that contain time specifications:
1267 this parses the text, and does error checking.
1270 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1275 if (!sec_p || strchr (line, ':'))
1276 value = parse_time ((char *) line, sec_p, True);
1280 if (sscanf (line, "%d%c", &value, &c) != 1)
1286 value *= 1000; /* Time measures in microseconds */
1292 "Unparsable time format: \"%s\"\n"),
1294 warning_dialog (s->toplevel_widget, b, False, 100);
1303 directory_p (const char *path)
1306 if (!path || !*path)
1308 else if (stat (path, &st))
1310 else if (!S_ISDIR (st.st_mode))
1317 file_p (const char *path)
1320 if (!path || !*path)
1322 else if (stat (path, &st))
1324 else if (S_ISDIR (st.st_mode))
1331 normalize_directory (const char *path)
1335 if (!path || !*path) return 0;
1337 p2 = (char *) malloc (L + 2);
1339 if (p2[L-1] == '/') /* remove trailing slash */
1342 for (s = p2; s && *s; s++)
1345 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1346 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1349 while (s0 > p2 && s0[-1] != '/')
1359 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1360 strcpy (s, s+2), s--;
1361 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1365 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1366 while (s[0] == '/' && s[1] == '/')
1369 /* and strip trailing whitespace for good measure. */
1371 while (isspace(p2[L-1]))
1384 } FlushForeachClosure;
1387 flush_checkbox (GtkTreeModel *model,
1392 FlushForeachClosure *closure = data;
1395 gtk_tree_model_get (model, iter,
1396 COL_ENABLED, &checked,
1399 if (flush_changes (closure->s, closure->i,
1401 *closure->changed = True;
1405 /* don't remove row */
1409 #endif /* HAVE_GTK2 */
1411 /* Flush out any changes made in the main dialog window (where changes
1412 take place immediately: clicking on a checkbox causes the init file
1413 to be written right away.)
1416 flush_dialog_changes_and_save (state *s)
1418 saver_preferences *p = &s->prefs;
1419 saver_preferences P2, *p2 = &P2;
1421 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1422 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1423 FlushForeachClosure closure;
1424 #else /* !HAVE_GTK2 */
1425 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1426 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1428 #endif /* !HAVE_GTK2 */
1430 Bool changed = False;
1433 if (s->saving_p) return False;
1438 /* Flush any checkbox changes in the list down into the prefs struct.
1442 closure.changed = &changed;
1444 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1446 #else /* !HAVE_GTK2 */
1448 for (i = 0; kids; kids = kids->next, i++)
1450 GtkWidget *line = GTK_WIDGET (kids->data);
1451 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1452 GtkWidget *line_check =
1453 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1455 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1457 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1460 #endif /* ~HAVE_GTK2 */
1462 /* Flush the non-hack-specific settings down into the prefs struct.
1465 # define SECONDS(FIELD,NAME) \
1466 w = name_to_widget (s, (NAME)); \
1467 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1469 # define MINUTES(FIELD,NAME) \
1470 w = name_to_widget (s, (NAME)); \
1471 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1473 # define CHECKBOX(FIELD,NAME) \
1474 w = name_to_widget (s, (NAME)); \
1475 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1477 # define PATHNAME(FIELD,NAME) \
1478 w = name_to_widget (s, (NAME)); \
1479 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1481 # define TEXT(FIELD,NAME) \
1482 w = name_to_widget (s, (NAME)); \
1483 (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1485 MINUTES (&p2->timeout, "timeout_spinbutton");
1486 MINUTES (&p2->cycle, "cycle_spinbutton");
1487 CHECKBOX (p2->lock_p, "lock_button");
1488 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1490 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1491 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1492 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1493 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1495 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1496 CHECKBOX (p2->grab_video_p, "grab_video_button");
1497 CHECKBOX (p2->random_image_p, "grab_image_button");
1498 PATHNAME (p2->image_directory, "image_text");
1501 CHECKBOX (p2->verbose_p, "verbose_button");
1502 CHECKBOX (p2->capture_stderr_p, "capture_button");
1503 CHECKBOX (p2->splash_p, "splash_button");
1508 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1509 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1510 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1511 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1512 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1513 TEXT (p2->text_literal, "text_entry");
1514 PATHNAME (p2->text_file, "text_file_entry");
1515 PATHNAME (p2->text_program, "text_program_entry");
1516 PATHNAME (p2->text_program, "text_program_entry");
1517 TEXT (p2->text_url, "text_url_entry");
1520 CHECKBOX (p2->install_cmap_p, "install_button");
1521 CHECKBOX (p2->fade_p, "fade_button");
1522 CHECKBOX (p2->unfade_p, "unfade_button");
1523 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1531 /* Warn if the image directory doesn't exist.
1533 if (p2->image_directory &&
1534 *p2->image_directory &&
1535 !directory_p (p2->image_directory))
1538 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1539 p2->image_directory);
1540 warning_dialog (s->toplevel_widget, b, False, 100);
1544 /* Map the mode menu to `saver_mode' enum values. */
1546 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1547 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1548 GtkWidget *selected = gtk_menu_get_active (menu);
1549 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1550 int menu_elt = g_list_index (kids, (gpointer) selected);
1551 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1552 p2->mode = mode_menu_order[menu_elt];
1555 if (p2->mode == ONE_HACK)
1557 int list_elt = selected_list_element (s);
1558 p2->selected_hack = (list_elt >= 0
1559 ? s->list_elt_to_hack_number[list_elt]
1563 # define COPY(field, name) \
1564 if (p->field != p2->field) { \
1567 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1569 p->field = p2->field
1572 COPY(selected_hack, "selected_hack");
1574 COPY(timeout, "timeout");
1575 COPY(cycle, "cycle");
1576 COPY(lock_p, "lock_p");
1577 COPY(lock_timeout, "lock_timeout");
1579 COPY(dpms_enabled_p, "dpms_enabled_p");
1580 COPY(dpms_standby, "dpms_standby");
1581 COPY(dpms_suspend, "dpms_suspend");
1582 COPY(dpms_off, "dpms_off");
1585 COPY(verbose_p, "verbose_p");
1586 COPY(capture_stderr_p, "capture_stderr_p");
1587 COPY(splash_p, "splash_p");
1590 COPY(tmode, "tmode");
1592 COPY(install_cmap_p, "install_cmap_p");
1593 COPY(fade_p, "fade_p");
1594 COPY(unfade_p, "unfade_p");
1595 COPY(fade_seconds, "fade_seconds");
1597 COPY(grab_desktop_p, "grab_desktop_p");
1598 COPY(grab_video_p, "grab_video_p");
1599 COPY(random_image_p, "random_image_p");
1603 # define COPYSTR(FIELD,NAME) \
1606 strcmp(p->FIELD, p2->FIELD)) \
1610 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1612 if (p->FIELD && p->FIELD != p2->FIELD) \
1614 p->FIELD = p2->FIELD; \
1617 COPYSTR(image_directory, "image_directory");
1618 COPYSTR(text_literal, "text_literal");
1619 COPYSTR(text_file, "text_file");
1620 COPYSTR(text_program, "text_program");
1621 COPYSTR(text_url, "text_url");
1624 populate_prefs_page (s);
1628 Display *dpy = GDK_DISPLAY();
1629 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1630 sync_server_dpms_settings (dpy, enabled_p,
1631 p->dpms_standby / 1000,
1632 p->dpms_suspend / 1000,
1636 changed = demo_write_init_file (s, p);
1639 s->saving_p = False;
1644 /* Flush out any changes made in the popup dialog box (where changes
1645 take place only when the OK button is clicked.)
1648 flush_popup_changes_and_save (state *s)
1650 Bool changed = False;
1651 saver_preferences *p = &s->prefs;
1652 int list_elt = selected_list_element (s);
1654 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1655 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1657 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1658 const char *command = gtk_entry_get_text (cmd);
1663 if (s->saving_p) return False;
1669 if (maybe_reload_init_file (s) != 0)
1675 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1677 if (!strcasecmp (visual, "")) visual = "";
1678 else if (!strcasecmp (visual, "any")) visual = "";
1679 else if (!strcasecmp (visual, "default")) visual = "Default";
1680 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1681 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1682 else if (!strcasecmp (visual, "best")) visual = "Best";
1683 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1684 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1685 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1686 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1687 else if (!strcasecmp (visual, "color")) visual = "Color";
1688 else if (!strcasecmp (visual, "gl")) visual = "GL";
1689 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1690 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1691 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1692 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1693 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1694 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1695 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1696 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1697 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1700 gdk_beep (); /* unparsable */
1702 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1705 changed = flush_changes (s, list_elt, -1, command, visual);
1708 changed = demo_write_init_file (s, p);
1710 /* Do this to re-launch the hack if (and only if) the command line
1712 populate_demo_window (s, selected_list_element (s));
1716 s->saving_p = False;
1721 G_MODULE_EXPORT void
1722 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1724 state *s = global_state_kludge; /* I hate C so much... */
1725 if (! s->initializing_p)
1727 s->initializing_p = True;
1728 flush_dialog_changes_and_save (s);
1729 s->initializing_p = False;
1733 G_MODULE_EXPORT gboolean
1734 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1736 pref_changed_cb (widget, user_data);
1740 /* Callback on menu items in the "mode" options menu.
1742 G_MODULE_EXPORT void
1743 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1745 state *s = (state *) user_data;
1746 saver_preferences *p = &s->prefs;
1747 GtkWidget *list = name_to_widget (s, "list");
1750 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1752 saver_mode new_mode;
1756 if (menu_items->data == widget)
1759 menu_items = menu_items->next;
1761 if (!menu_items) abort();
1763 new_mode = mode_menu_order[menu_index];
1765 /* Keep the same list element displayed as before; except if we're
1766 switching *to* "one screensaver" mode from any other mode, set
1767 "the one" to be that which is currently selected.
1769 list_elt = selected_list_element (s);
1770 if (new_mode == ONE_HACK)
1771 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1774 saver_mode old_mode = p->mode;
1776 populate_demo_window (s, list_elt);
1777 force_list_select_item (s, list, list_elt, True);
1778 p->mode = old_mode; /* put it back, so the init file gets written */
1781 pref_changed_cb (widget, user_data);
1785 G_MODULE_EXPORT void
1786 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1787 gint page_num, gpointer user_data)
1789 state *s = global_state_kludge; /* I hate C so much... */
1790 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1792 /* If we're switching to page 0, schedule the current hack to be run.
1793 Otherwise, schedule it to stop. */
1795 populate_demo_window (s, selected_list_element (s));
1797 schedule_preview (s, 0);
1802 list_activated_cb (GtkTreeView *list,
1804 GtkTreeViewColumn *column,
1811 STFU g_return_if_fail (!gdk_pointer_is_grabbed ());
1813 str = gtk_tree_path_to_string (path);
1814 list_elt = strtol (str, NULL, 10);
1818 run_hack (s, list_elt, True);
1822 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1824 state *s = (state *)data;
1825 GtkTreeModel *model;
1831 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1834 path = gtk_tree_model_get_path (model, &iter);
1835 str = gtk_tree_path_to_string (path);
1836 list_elt = strtol (str, NULL, 10);
1838 gtk_tree_path_free (path);
1841 populate_demo_window (s, list_elt);
1842 flush_dialog_changes_and_save (s);
1844 /* Re-populate the Settings window any time a new item is selected
1845 in the list, in case both windows are currently visible.
1847 populate_popup_window (s);
1850 #else /* !HAVE_GTK2 */
1852 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1853 list_select_cb that comes in
1854 *after* we've double-clicked.
1858 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1861 state *s = (state *) data;
1862 if (event->type == GDK_2BUTTON_PRESS)
1864 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1865 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1867 last_doubleclick_time = time ((time_t *) 0);
1870 run_hack (s, list_elt, True);
1878 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1880 state *s = (state *) data;
1881 time_t now = time ((time_t *) 0);
1883 if (now >= last_doubleclick_time + 2)
1885 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1886 populate_demo_window (s, list_elt);
1887 flush_dialog_changes_and_save (s);
1892 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1894 state *s = (state *) data;
1895 populate_demo_window (s, -1);
1896 flush_dialog_changes_and_save (s);
1899 #endif /* !HAVE_GTK2 */
1902 /* Called when the checkboxes that are in the left column of the
1903 scrolling list are clicked. This both populates the right pane
1904 (just as clicking on the label (really, listitem) does) and
1905 also syncs this checkbox with the right pane Enabled checkbox.
1910 GtkCellRendererToggle *toggle,
1912 #else /* !HAVE_GTK2 */
1914 #endif /* !HAVE_GTK2 */
1917 state *s = (state *) data;
1920 GtkScrolledWindow *scroller =
1921 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1922 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1923 GtkTreeModel *model = gtk_tree_view_get_model (list);
1924 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1927 #else /* !HAVE_GTK2 */
1928 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1929 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1931 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1932 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1933 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1934 #endif /* !HAVE_GTK2 */
1941 if (!gtk_tree_model_get_iter (model, &iter, path))
1943 g_warning ("bad path: %s", path_string);
1946 gtk_tree_path_free (path);
1948 gtk_tree_model_get (model, &iter,
1949 COL_ENABLED, &active,
1952 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1953 COL_ENABLED, !active,
1956 list_elt = strtol (path_string, NULL, 10);
1957 #else /* !HAVE_GTK2 */
1958 list_elt = gtk_list_child_position (list, line);
1959 #endif /* !HAVE_GTK2 */
1961 /* remember previous scroll position of the top of the list */
1962 adj = gtk_scrolled_window_get_vadjustment (scroller);
1963 scroll_top = adj->value;
1965 flush_dialog_changes_and_save (s);
1966 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1967 populate_demo_window (s, list_elt);
1969 /* restore the previous scroll position of the top of the list.
1970 this is weak, but I don't really know why it's moving... */
1971 gtk_adjustment_set_value (adj, scroll_top);
1977 GtkFileSelection *widget;
1978 } file_selection_data;
1983 store_image_directory (GtkWidget *button, gpointer user_data)
1985 file_selection_data *fsd = (file_selection_data *) user_data;
1986 state *s = fsd->state;
1987 GtkFileSelection *selector = fsd->widget;
1988 GtkWidget *top = s->toplevel_widget;
1989 saver_preferences *p = &s->prefs;
1990 const char *path = gtk_file_selection_get_filename (selector);
1992 if (p->image_directory && !strcmp(p->image_directory, path))
1993 return; /* no change */
1995 if (!directory_p (path))
1998 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1999 warning_dialog (GTK_WIDGET (top), b, False, 100);
2003 if (p->image_directory) free (p->image_directory);
2004 p->image_directory = normalize_directory (path);
2006 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2007 (p->image_directory ? p->image_directory : ""));
2008 demo_write_init_file (s, p);
2013 store_text_file (GtkWidget *button, gpointer user_data)
2015 file_selection_data *fsd = (file_selection_data *) user_data;
2016 state *s = fsd->state;
2017 GtkFileSelection *selector = fsd->widget;
2018 GtkWidget *top = s->toplevel_widget;
2019 saver_preferences *p = &s->prefs;
2020 const char *path = gtk_file_selection_get_filename (selector);
2022 if (p->text_file && !strcmp(p->text_file, path))
2023 return; /* no change */
2028 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2029 warning_dialog (GTK_WIDGET (top), b, False, 100);
2033 if (p->text_file) free (p->text_file);
2034 p->text_file = normalize_directory (path);
2036 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2037 (p->text_file ? p->text_file : ""));
2038 demo_write_init_file (s, p);
2043 store_text_program (GtkWidget *button, gpointer user_data)
2045 file_selection_data *fsd = (file_selection_data *) user_data;
2046 state *s = fsd->state;
2047 GtkFileSelection *selector = fsd->widget;
2048 /*GtkWidget *top = s->toplevel_widget;*/
2049 saver_preferences *p = &s->prefs;
2050 const char *path = gtk_file_selection_get_filename (selector);
2052 if (p->text_program && !strcmp(p->text_program, path))
2053 return; /* no change */
2059 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2060 warning_dialog (GTK_WIDGET (top), b, False, 100);
2065 if (p->text_program) free (p->text_program);
2066 p->text_program = normalize_directory (path);
2068 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2069 (p->text_program ? p->text_program : ""));
2070 demo_write_init_file (s, p);
2076 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2078 file_selection_data *fsd = (file_selection_data *) user_data;
2079 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2083 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2085 browse_image_dir_cancel (button, user_data);
2086 store_image_directory (button, user_data);
2090 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2092 browse_image_dir_cancel (button, user_data);
2093 store_text_file (button, user_data);
2097 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2099 browse_image_dir_cancel (button, user_data);
2100 store_text_program (button, user_data);
2104 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2106 browse_image_dir_cancel (widget, user_data);
2110 G_MODULE_EXPORT void
2111 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2113 state *s = global_state_kludge; /* I hate C so much... */
2114 saver_preferences *p = &s->prefs;
2115 static file_selection_data *fsd = 0;
2117 GtkFileSelection *selector = GTK_FILE_SELECTION(
2118 gtk_file_selection_new ("Please select the image directory."));
2121 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2123 fsd->widget = selector;
2126 if (p->image_directory && *p->image_directory)
2127 gtk_file_selection_set_filename (selector, p->image_directory);
2129 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2130 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2132 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2133 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2135 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2136 GTK_SIGNAL_FUNC (browse_image_dir_close),
2139 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2141 gtk_window_set_modal (GTK_WINDOW (selector), True);
2142 gtk_widget_show (GTK_WIDGET (selector));
2146 G_MODULE_EXPORT void
2147 browse_text_file_cb (GtkButton *button, gpointer user_data)
2149 state *s = global_state_kludge; /* I hate C so much... */
2150 saver_preferences *p = &s->prefs;
2151 static file_selection_data *fsd = 0;
2153 GtkFileSelection *selector = GTK_FILE_SELECTION(
2154 gtk_file_selection_new ("Please select a text file."));
2157 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2159 fsd->widget = selector;
2162 if (p->text_file && *p->text_file)
2163 gtk_file_selection_set_filename (selector, p->text_file);
2165 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2166 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2168 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2169 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2171 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2172 GTK_SIGNAL_FUNC (browse_image_dir_close),
2175 gtk_window_set_modal (GTK_WINDOW (selector), True);
2176 gtk_widget_show (GTK_WIDGET (selector));
2180 G_MODULE_EXPORT void
2181 browse_text_program_cb (GtkButton *button, gpointer user_data)
2183 state *s = global_state_kludge; /* I hate C so much... */
2184 saver_preferences *p = &s->prefs;
2185 static file_selection_data *fsd = 0;
2187 GtkFileSelection *selector = GTK_FILE_SELECTION(
2188 gtk_file_selection_new ("Please select a text-generating program."));
2191 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2193 fsd->widget = selector;
2196 if (p->text_program && *p->text_program)
2197 gtk_file_selection_set_filename (selector, p->text_program);
2199 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2200 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2202 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2203 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2205 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2206 GTK_SIGNAL_FUNC (browse_image_dir_close),
2209 gtk_window_set_modal (GTK_WINDOW (selector), True);
2210 gtk_widget_show (GTK_WIDGET (selector));
2217 G_MODULE_EXPORT void
2218 settings_cb (GtkButton *button, gpointer user_data)
2220 state *s = global_state_kludge; /* I hate C so much... */
2221 int list_elt = selected_list_element (s);
2223 populate_demo_window (s, list_elt); /* reset the widget */
2224 populate_popup_window (s); /* create UI on popup window */
2225 gtk_widget_show (s->popup_widget);
2229 settings_sync_cmd_text (state *s)
2232 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2233 char *cmd_line = get_configurator_command_line (s->cdata);
2234 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2235 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2237 # endif /* HAVE_XML */
2240 G_MODULE_EXPORT void
2241 settings_adv_cb (GtkButton *button, gpointer user_data)
2243 state *s = global_state_kludge; /* I hate C so much... */
2244 GtkNotebook *notebook =
2245 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2247 settings_sync_cmd_text (s);
2248 gtk_notebook_set_page (notebook, 1);
2251 G_MODULE_EXPORT void
2252 settings_std_cb (GtkButton *button, gpointer user_data)
2254 state *s = global_state_kludge; /* I hate C so much... */
2255 GtkNotebook *notebook =
2256 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2258 /* Re-create UI to reflect the in-progress command-line settings. */
2259 populate_popup_window (s);
2261 gtk_notebook_set_page (notebook, 0);
2264 G_MODULE_EXPORT void
2265 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2266 gint page_num, gpointer user_data)
2268 state *s = global_state_kludge; /* I hate C so much... */
2269 GtkWidget *adv = name_to_widget (s, "adv_button");
2270 GtkWidget *std = name_to_widget (s, "std_button");
2274 gtk_widget_show (adv);
2275 gtk_widget_hide (std);
2277 else if (page_num == 1)
2279 gtk_widget_hide (adv);
2280 gtk_widget_show (std);
2288 G_MODULE_EXPORT void
2289 settings_cancel_cb (GtkButton *button, gpointer user_data)
2291 state *s = global_state_kludge; /* I hate C so much... */
2292 gtk_widget_hide (s->popup_widget);
2295 G_MODULE_EXPORT void
2296 settings_ok_cb (GtkButton *button, gpointer user_data)
2298 state *s = global_state_kludge; /* I hate C so much... */
2299 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2300 int page = gtk_notebook_get_current_page (notebook);
2303 /* Regenerate the command-line from the widget contents before saving.
2304 But don't do this if we're looking at the command-line page already,
2305 or we will blow away what they typed... */
2306 settings_sync_cmd_text (s);
2308 flush_popup_changes_and_save (s);
2309 gtk_widget_hide (s->popup_widget);
2313 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2315 state *s = (state *) data;
2316 settings_cancel_cb (0, (gpointer) s);
2322 /* Populating the various widgets
2326 /* Returns the number of the last hack run by the server.
2329 server_current_hack (void)
2333 unsigned long nitems, bytesafter;
2334 unsigned char *dataP = 0;
2335 Display *dpy = GDK_DISPLAY();
2336 int hack_number = -1;
2338 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2339 XA_SCREENSAVER_STATUS,
2340 0, 3, False, XA_INTEGER,
2341 &type, &format, &nitems, &bytesafter,
2344 && type == XA_INTEGER
2348 PROP32 *data = (PROP32 *) dataP;
2349 hack_number = (int) data[2] - 1;
2352 if (dataP) XFree (dataP);
2358 /* Finds the number of the last hack that was run, and makes that item be
2359 selected by default.
2362 scroll_to_current_hack (state *s)
2364 saver_preferences *p = &s->prefs;
2365 int hack_number = -1;
2367 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2368 hack_number = p->selected_hack;
2369 if (hack_number < 0) /* otherwise, use the last-run */
2370 hack_number = server_current_hack ();
2371 if (hack_number < 0) /* failing that, last "one mode" */
2372 hack_number = p->selected_hack;
2373 if (hack_number < 0) /* failing that, newest hack. */
2375 /* We should only get here if the user does not have a .xscreensaver
2376 file, and the screen has not been blanked with a hack since X
2377 started up: in other words, this is probably a fresh install.
2379 Instead of just defaulting to hack #0 (in either "programs" or
2380 "alphabetical" order) let's try to default to the last runnable
2381 hack in the "programs" list: this is probably the hack that was
2382 most recently added to the xscreensaver distribution (and so
2383 it's probably the currently-coolest one!)
2385 hack_number = p->screenhacks_count-1;
2386 while (hack_number > 0 &&
2387 ! (s->hacks_available_p[hack_number] &&
2388 p->screenhacks[hack_number]->enabled_p))
2392 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2394 int list_elt = s->hack_number_to_list_elt[hack_number];
2395 GtkWidget *list = name_to_widget (s, "list");
2396 force_list_select_item (s, list, list_elt, True);
2397 populate_demo_window (s, list_elt);
2403 populate_hack_list (state *s)
2405 Display *dpy = GDK_DISPLAY();
2407 saver_preferences *p = &s->prefs;
2408 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2409 GtkListStore *model;
2410 GtkTreeSelection *selection;
2411 GtkCellRenderer *ren;
2415 g_object_get (G_OBJECT (list),
2420 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2421 g_object_set (G_OBJECT (list), "model", model, NULL);
2422 g_object_unref (model);
2424 ren = gtk_cell_renderer_toggle_new ();
2425 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2427 "active", COL_ENABLED,
2430 g_signal_connect (ren, "toggled",
2431 G_CALLBACK (list_checkbox_cb),
2434 ren = gtk_cell_renderer_text_new ();
2435 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2436 _("Screen Saver"), ren,
2440 g_signal_connect_after (list, "row_activated",
2441 G_CALLBACK (list_activated_cb),
2444 selection = gtk_tree_view_get_selection (list);
2445 g_signal_connect (selection, "changed",
2446 G_CALLBACK (list_select_changed_cb),
2451 for (i = 0; i < s->list_count; i++)
2453 int hack_number = s->list_elt_to_hack_number[i];
2454 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2456 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2458 if (!hack) continue;
2460 /* If we're to suppress uninstalled hacks, check $PATH now. */
2461 if (p->ignore_uninstalled_p && !available_p)
2464 pretty_name = (hack->name
2465 ? strdup (hack->name)
2466 : make_hack_name (dpy, hack->command));
2470 /* Make the text foreground be the color of insensitive widgets
2471 (but don't actually make it be insensitive, since we still
2472 want to be able to click on it.)
2474 GtkStyle *style = GTK_WIDGET (list)->style;
2475 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2476 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2477 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2479 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2480 /* " background=\"#%02X%02X%02X\"" */
2482 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2483 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2489 gtk_list_store_append (model, &iter);
2490 gtk_list_store_set (model, &iter,
2491 COL_ENABLED, hack->enabled_p,
2492 COL_NAME, pretty_name,
2497 #else /* !HAVE_GTK2 */
2499 saver_preferences *p = &s->prefs;
2500 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2502 for (i = 0; i < s->list_count; i++)
2504 int hack_number = s->list_elt_to_hack_number[i];
2505 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2507 /* A GtkList must contain only GtkListItems, but those can contain
2508 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2509 and a Label. We handle single and double click events on the
2510 line itself, for clicking on the text, but the interior checkbox
2511 also handles its own events.
2514 GtkWidget *line_hbox;
2515 GtkWidget *line_check;
2516 GtkWidget *line_label;
2518 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2520 if (!hack) continue;
2522 /* If we're to suppress uninstalled hacks, check $PATH now. */
2523 if (p->ignore_uninstalled_p && !available_p)
2526 pretty_name = (hack->name
2527 ? strdup (hack->name)
2528 : make_hack_name (hack->command));
2530 line = gtk_list_item_new ();
2531 line_hbox = gtk_hbox_new (FALSE, 0);
2532 line_check = gtk_check_button_new ();
2533 line_label = gtk_label_new (pretty_name);
2535 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2536 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2537 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2539 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2541 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2543 gtk_widget_show (line_check);
2544 gtk_widget_show (line_label);
2545 gtk_widget_show (line_hbox);
2546 gtk_widget_show (line);
2550 gtk_container_add (GTK_CONTAINER (list), line);
2551 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2552 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2555 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2556 GTK_SIGNAL_FUNC (list_checkbox_cb),
2559 gtk_widget_show (line);
2563 /* Make the widget be colored like insensitive widgets
2564 (but don't actually make it be insensitive, since we
2565 still want to be able to click on it.)
2567 GtkRcStyle *rc_style;
2570 gtk_widget_realize (GTK_WIDGET (line_label));
2572 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2573 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2575 rc_style = gtk_rc_style_new ();
2576 rc_style->fg[GTK_STATE_NORMAL] = fg;
2577 rc_style->bg[GTK_STATE_NORMAL] = bg;
2578 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2580 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2581 gtk_rc_style_unref (rc_style);
2585 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2586 GTK_SIGNAL_FUNC (list_select_cb),
2588 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2589 GTK_SIGNAL_FUNC (list_unselect_cb),
2591 #endif /* !HAVE_GTK2 */
2595 update_list_sensitivity (state *s)
2597 saver_preferences *p = &s->prefs;
2598 Bool sensitive = (p->mode == RANDOM_HACKS ||
2599 p->mode == RANDOM_HACKS_SAME ||
2600 p->mode == ONE_HACK);
2601 Bool checkable = (p->mode == RANDOM_HACKS ||
2602 p->mode == RANDOM_HACKS_SAME);
2603 Bool blankable = (p->mode != DONT_BLANK);
2606 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2607 GtkWidget *use = name_to_widget (s, "use_col_frame");
2608 #endif /* HAVE_GTK2 */
2609 GtkWidget *scroller = name_to_widget (s, "scroller");
2610 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2611 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2614 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2615 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2616 #else /* !HAVE_GTK2 */
2617 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2618 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2620 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2621 #endif /* !HAVE_GTK2 */
2622 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2623 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2625 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2628 gtk_tree_view_column_set_visible (use, checkable);
2629 #else /* !HAVE_GTK2 */
2631 gtk_widget_show (use); /* the "Use" column header */
2633 gtk_widget_hide (use);
2637 GtkBin *line = GTK_BIN (kids->data);
2638 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2639 GtkWidget *line_check =
2640 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2643 gtk_widget_show (line_check);
2645 gtk_widget_hide (line_check);
2649 #endif /* !HAVE_GTK2 */
2654 populate_prefs_page (state *s)
2656 saver_preferences *p = &s->prefs;
2658 Bool can_lock_p = True;
2660 /* Disable all the "lock" controls if locking support was not provided
2661 at compile-time, or if running on MacOS. */
2662 # if defined(NO_LOCKING) || defined(__APPLE__)
2667 /* If there is only one screen, the mode menu contains
2668 "random" but not "random-same".
2670 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2671 p->mode = RANDOM_HACKS;
2674 /* The file supports timeouts of less than a minute, but the GUI does
2675 not, so throttle the values to be at least one minute (since "0" is
2676 a bad rounding choice...)
2678 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2681 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2684 # define FMT_MINUTES(NAME,N) \
2685 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2687 # define FMT_SECONDS(NAME,N) \
2688 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2690 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2691 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2692 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2693 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2694 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2695 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2696 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2701 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2702 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2705 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2707 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2708 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2709 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2711 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2712 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2713 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2714 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2715 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2716 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2717 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2721 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2722 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2723 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2724 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2725 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2728 # undef TOGGLE_ACTIVE
2730 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2731 (p->image_directory ? p->image_directory : ""));
2732 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2734 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2737 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2738 (p->text_literal ? p->text_literal : ""));
2739 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2740 (p->text_file ? p->text_file : ""));
2741 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2742 (p->text_program ? p->text_program : ""));
2743 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2744 (p->text_url ? p->text_url : ""));
2746 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2747 p->tmode == TEXT_LITERAL);
2748 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2749 p->tmode == TEXT_FILE);
2750 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2751 p->tmode == TEXT_FILE);
2752 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2753 p->tmode == TEXT_PROGRAM);
2754 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2755 p->tmode == TEXT_PROGRAM);
2756 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2757 p->tmode == TEXT_URL);
2760 /* Map the `saver_mode' enum to mode menu to values. */
2762 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2765 for (i = 0; i < countof(mode_menu_order); i++)
2766 if (mode_menu_order[i] == p->mode)
2768 gtk_option_menu_set_history (opt, i);
2769 update_list_sensitivity (s);
2773 Bool found_any_writable_cells = False;
2774 Bool fading_possible = False;
2775 Bool dpms_supported = False;
2777 Display *dpy = GDK_DISPLAY();
2778 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2780 for (i = 0; i < nscreens; i++)
2782 Screen *s = ScreenOfDisplay (dpy, i);
2783 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2785 found_any_writable_cells = True;
2790 fading_possible = found_any_writable_cells;
2791 #ifdef HAVE_XF86VMODE_GAMMA
2792 fading_possible = True;
2795 #ifdef HAVE_DPMS_EXTENSION
2797 int op = 0, event = 0, error = 0;
2798 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2799 dpms_supported = True;
2801 #endif /* HAVE_DPMS_EXTENSION */
2804 # define SENSITIZE(NAME,SENSITIVEP) \
2805 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2807 /* Blanking and Locking
2809 SENSITIZE ("lock_button", can_lock_p);
2810 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2811 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2815 SENSITIZE ("dpms_frame", dpms_supported);
2816 SENSITIZE ("dpms_button", dpms_supported);
2817 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2818 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2819 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2820 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2821 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2822 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2823 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2824 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2825 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2829 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2830 SENSITIZE ("install_button", found_any_writable_cells);
2831 SENSITIZE ("fade_button", fading_possible);
2832 SENSITIZE ("unfade_button", fading_possible);
2834 SENSITIZE ("fade_label", (fading_possible &&
2835 (p->fade_p || p->unfade_p)));
2836 SENSITIZE ("fade_spinbutton", (fading_possible &&
2837 (p->fade_p || p->unfade_p)));
2845 populate_popup_window (state *s)
2847 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2848 char *doc_string = 0;
2850 /* #### not in Gtk 1.2
2851 gtk_label_set_selectable (doc);
2857 free_conf_data (s->cdata);
2862 saver_preferences *p = &s->prefs;
2863 int list_elt = selected_list_element (s);
2864 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2865 ? s->list_elt_to_hack_number[list_elt]
2867 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2870 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2871 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2872 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2873 s->cdata = load_configurator (cmd_line, s->debug_p);
2874 if (s->cdata && s->cdata->widget)
2875 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2880 doc_string = (s->cdata
2881 ? s->cdata->description
2883 # else /* !HAVE_XML */
2884 doc_string = _("Descriptions not available: no XML support compiled in.");
2885 # endif /* !HAVE_XML */
2887 gtk_label_set_text (doc, (doc_string
2889 : _("No description available.")));
2894 sensitize_demo_widgets (state *s, Bool sensitive_p)
2896 const char *names[] = { "demo", "settings",
2897 "cmd_label", "cmd_text", "manual",
2898 "visual", "visual_combo" };
2900 for (i = 0; i < countof(names); i++)
2902 GtkWidget *w = name_to_widget (s, names[i]);
2903 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2909 sensitize_menu_items (state *s, Bool force_p)
2911 static Bool running_p = False;
2912 static time_t last_checked = 0;
2913 time_t now = time ((time_t *) 0);
2914 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
2918 if (force_p || now > last_checked + 10) /* check every 10 seconds */
2920 running_p = xscreensaver_running_p (s);
2921 last_checked = time ((time_t *) 0);
2924 for (i = 0; i < countof(names); i++)
2926 GtkWidget *w = name_to_widget (s, names[i]);
2927 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
2932 /* When the File menu is de-posted after a "Restart Daemon" command,
2933 the window underneath doesn't repaint for some reason. I guess this
2934 is a bug in exposure handling in GTK or GDK. This works around it.
2937 force_dialog_repaint (state *s)
2940 /* Tell GDK to invalidate and repaint the whole window.
2942 GdkWindow *w = s->toplevel_widget->window;
2943 GdkRegion *region = gdk_region_new ();
2945 rect.x = rect.y = 0;
2946 rect.width = rect.height = 32767;
2947 gdk_region_union_with_rect (region, &rect);
2948 gdk_window_invalidate_region (w, region, True);
2949 gdk_region_destroy (region);
2950 gdk_window_process_updates (w, True);
2952 /* Force the server to send an exposure event by creating and then
2953 destroying a window as a child of the top level shell.
2955 Display *dpy = GDK_DISPLAY();
2956 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
2958 XWindowAttributes xgwa;
2959 XGetWindowAttributes (dpy, parent, &xgwa);
2960 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
2961 XMapRaised (dpy, w);
2962 XDestroyWindow (dpy, w);
2968 /* Even though we've given these text fields a maximum number of characters,
2969 their default size is still about 30 characters wide -- so measure out
2970 a string in their font, and resize them to just fit that.
2973 fix_text_entry_sizes (state *s)
2977 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2978 const char * const spinbuttons[] = {
2979 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2980 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2981 "dpms_off_spinbutton",
2982 "-fade_spinbutton" };
2986 for (i = 0; i < countof(spinbuttons); i++)
2988 const char *n = spinbuttons[i];
2990 while (*n == '-') n++, cols--;
2991 w = GTK_WIDGET (name_to_widget (s, n));
2992 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2993 gtk_widget_set_usize (w, width, -2);
2996 /* Now fix the width of the combo box.
2998 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2999 w = GTK_COMBO (w)->entry;
3000 width = gdk_string_width (w->style->font, "PseudoColor___");
3001 gtk_widget_set_usize (w, width, -2);
3003 /* Now fix the width of the file entry text.
3005 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3006 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3007 gtk_widget_set_usize (w, width, -2);
3009 /* Now fix the width of the command line text.
3011 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3012 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3013 gtk_widget_set_usize (w, width, -2);
3017 /* Now fix the height of the list widget:
3018 make it default to being around 10 text-lines high instead of 4.
3020 w = GTK_WIDGET (name_to_widget (s, "list"));
3024 int leading = 3; /* approximate is ok... */
3028 PangoFontMetrics *pain =
3029 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3030 w->style->font_desc,
3031 gtk_get_default_language ());
3032 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3033 pango_font_metrics_get_descent (pain));
3034 #else /* !HAVE_GTK2 */
3035 height = w->style->font->ascent + w->style->font->descent;
3036 #endif /* !HAVE_GTK2 */
3040 height += border * 2;
3041 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3042 gtk_widget_set_usize (w, -2, height);
3049 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3052 static char *up_arrow_xpm[] = {
3075 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3076 the end of the array (Gtk 1.2.5.) */
3077 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3078 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3081 static char *down_arrow_xpm[] = {
3104 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3105 the end of the array (Gtk 1.2.5.) */
3106 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3107 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3111 pixmapify_button (state *s, int down_p)
3115 GtkWidget *pixmapwid;
3119 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3120 style = gtk_widget_get_style (w);
3122 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3123 &style->bg[GTK_STATE_NORMAL],
3125 ? (gchar **) down_arrow_xpm
3126 : (gchar **) up_arrow_xpm));
3127 pixmapwid = gtk_pixmap_new (pixmap, mask);
3128 gtk_widget_show (pixmapwid);
3129 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3130 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3134 map_next_button_cb (GtkWidget *w, gpointer user_data)
3136 state *s = (state *) user_data;
3137 pixmapify_button (s, 1);
3141 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3143 state *s = (state *) user_data;
3144 pixmapify_button (s, 0);
3146 #endif /* !HAVE_GTK2 */
3150 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3154 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3155 GtkAllocation *allocation,
3159 GtkWidgetAuxInfo *aux_info;
3161 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3163 aux_info->width = allocation->width;
3164 aux_info->height = -2;
3168 gtk_widget_size_request (label, &req);
3171 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3174 eschew_gtk_lossage (GtkLabel *label)
3176 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3177 aux_info->width = GTK_WIDGET (label)->allocation.width;
3178 aux_info->height = -2;
3182 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3184 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3185 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3188 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3190 gtk_widget_queue_resize (GTK_WIDGET (label));
3192 #endif /* !HAVE_GTK2 */
3196 populate_demo_window (state *s, int list_elt)
3198 Display *dpy = GDK_DISPLAY();
3199 saver_preferences *p = &s->prefs;
3202 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3203 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
3204 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3205 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3206 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3208 if (p->mode == BLANK_ONLY)
3211 pretty_name = strdup (_("Blank Screen"));
3212 schedule_preview (s, 0);
3214 else if (p->mode == DONT_BLANK)
3217 pretty_name = strdup (_("Screen Saver Disabled"));
3218 schedule_preview (s, 0);
3222 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3223 ? s->list_elt_to_hack_number[list_elt]
3225 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3229 ? strdup (hack->name)
3230 : make_hack_name (dpy, hack->command))
3234 schedule_preview (s, hack->command);
3236 schedule_preview (s, 0);
3240 pretty_name = strdup (_("Preview"));
3242 gtk_frame_set_label (frame1, _(pretty_name));
3243 gtk_frame_set_label (frame2, _(pretty_name));
3245 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3246 gtk_entry_set_position (cmd, 0);
3250 sprintf (title, _("%s: %.100s Settings"),
3251 progclass, (pretty_name ? pretty_name : "???"));
3252 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3255 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3257 ? (hack->visual && *hack->visual
3262 sensitize_demo_widgets (s, (hack ? True : False));
3264 if (pretty_name) free (pretty_name);
3266 ensure_selected_item_visible (list);
3268 s->_selected_list_element = list_elt;
3273 widget_deleter (GtkWidget *widget, gpointer data)
3275 /* #### Well, I want to destroy these widgets, but if I do that, they get
3276 referenced again, and eventually I get a SEGV. So instead of
3277 destroying them, I'll just hide them, and leak a bunch of memory
3278 every time the disk file changes. Go go go Gtk!
3280 #### Ok, that's a lie, I get a crash even if I just hide the widget
3281 and don't ever delete it. Fuck!
3284 gtk_widget_destroy (widget);
3286 gtk_widget_hide (widget);
3291 static char **sort_hack_cmp_names_kludge;
3293 sort_hack_cmp (const void *a, const void *b)
3299 int aa = *(int *) a;
3300 int bb = *(int *) b;
3301 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3302 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3303 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3309 initialize_sort_map (state *s)
3311 Display *dpy = GDK_DISPLAY();
3312 saver_preferences *p = &s->prefs;
3315 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3316 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3317 if (s->hacks_available_p) free (s->hacks_available_p);
3319 s->list_elt_to_hack_number = (int *)
3320 calloc (sizeof(int), p->screenhacks_count + 1);
3321 s->hack_number_to_list_elt = (int *)
3322 calloc (sizeof(int), p->screenhacks_count + 1);
3323 s->hacks_available_p = (Bool *)
3324 calloc (sizeof(Bool), p->screenhacks_count + 1);
3325 s->total_available = 0;
3327 /* Check which hacks actually exist on $PATH
3329 for (i = 0; i < p->screenhacks_count; i++)
3331 screenhack *hack = p->screenhacks[i];
3332 int on = on_path_p (hack->command) ? 1 : 0;
3333 s->hacks_available_p[i] = on;
3334 s->total_available += on;
3337 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3341 for (i = 0; i < p->screenhacks_count; i++)
3343 if (!p->ignore_uninstalled_p ||
3344 s->hacks_available_p[i])
3345 s->list_elt_to_hack_number[j++] = i;
3349 for (; j < p->screenhacks_count; j++)
3350 s->list_elt_to_hack_number[j] = -1;
3353 /* Generate list of sortable names (once)
3355 sort_hack_cmp_names_kludge = (char **)
3356 calloc (sizeof(char *), p->screenhacks_count);
3357 for (i = 0; i < p->screenhacks_count; i++)
3359 screenhack *hack = p->screenhacks[i];
3360 char *name = (hack->name && *hack->name
3361 ? strdup (hack->name)
3362 : make_hack_name (dpy, hack->command));
3364 for (str = name; *str; str++)
3365 *str = tolower(*str);
3366 sort_hack_cmp_names_kludge[i] = name;
3369 /* Sort list->hack map alphabetically
3371 qsort (s->list_elt_to_hack_number,
3372 p->screenhacks_count,
3373 sizeof(*s->list_elt_to_hack_number),
3378 for (i = 0; i < p->screenhacks_count; i++)
3379 free (sort_hack_cmp_names_kludge[i]);
3380 free (sort_hack_cmp_names_kludge);
3381 sort_hack_cmp_names_kludge = 0;
3383 /* Build inverse table */
3384 for (i = 0; i < p->screenhacks_count; i++)
3386 int n = s->list_elt_to_hack_number[i];
3388 s->hack_number_to_list_elt[n] = i;
3394 maybe_reload_init_file (state *s)
3396 Display *dpy = GDK_DISPLAY();
3397 saver_preferences *p = &s->prefs;
3400 static Bool reentrant_lock = False;
3401 if (reentrant_lock) return 0;
3402 reentrant_lock = True;
3404 if (init_file_changed_p (p))
3406 const char *f = init_file_name();
3411 if (!f || !*f) return 0;
3412 b = (char *) malloc (strlen(f) + 1024);
3415 "file \"%s\" has changed, reloading.\n"),
3417 warning_dialog (s->toplevel_widget, b, False, 100);
3420 load_init_file (dpy, p);
3421 initialize_sort_map (s);
3423 list_elt = selected_list_element (s);
3424 list = name_to_widget (s, "list");
3425 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3426 populate_hack_list (s);
3427 force_list_select_item (s, list, list_elt, True);
3428 populate_prefs_page (s);
3429 populate_demo_window (s, list_elt);
3430 ensure_selected_item_visible (list);
3435 reentrant_lock = False;
3441 /* Making the preview window have the right X visual (so that GL works.)
3444 static Visual *get_best_gl_visual (state *);
3447 x_visual_to_gdk_visual (Visual *xv)
3449 GList *gvs = gdk_list_visuals();
3450 if (!xv) return gdk_visual_get_system();
3451 for (; gvs; gvs = gvs->next)
3453 GdkVisual *gv = (GdkVisual *) gvs->data;
3454 if (xv == GDK_VISUAL_XVISUAL (gv))
3457 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3458 blurb(), (unsigned long) xv->visualid);
3463 clear_preview_window (state *s)
3468 if (!s->toplevel_widget) return; /* very early */
3469 p = name_to_widget (s, "preview");
3472 if (!window) return;
3474 /* Flush the widget background down into the window, in case a subproc
3476 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3477 gdk_window_clear (window);
3480 int list_elt = selected_list_element (s);
3481 int hack_number = (list_elt >= 0
3482 ? s->list_elt_to_hack_number[list_elt]
3484 Bool available_p = (hack_number >= 0
3485 ? s->hacks_available_p [hack_number]
3487 Bool nothing_p = (s->total_available < 5);
3490 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3491 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3492 (s->running_preview_error_p
3493 ? (available_p ? 1 :
3496 #else /* !HAVE_GTK2 */
3497 if (s->running_preview_error_p)
3499 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3500 const char * const lines2[] = { N_("Not"), N_("Installed") };
3501 int nlines = countof(lines1);
3502 int lh = p->style->font->ascent + p->style->font->descent;
3506 const char * const *lines = (available_p ? lines1 : lines2);
3508 gdk_window_get_size (window, &w, &h);
3509 y = (h - (lh * nlines)) / 2;
3510 y += p->style->font->ascent;
3511 for (i = 0; i < nlines; i++)
3513 int sw = gdk_string_width (p->style->font, _(lines[i]));
3514 int x = (w - sw) / 2;
3515 gdk_draw_string (window, p->style->font,
3516 p->style->fg_gc[GTK_STATE_NORMAL],
3521 #endif /* !HAVE_GTK2 */
3529 reset_preview_window (state *s)
3531 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3532 when you kill one and re-start another on the same window. So maybe
3533 it's best to just always destroy and recreate the preview window
3534 when changing hacks, instead of always trying to reuse the same one?
3536 GtkWidget *pr = name_to_widget (s, "preview");
3537 if (GTK_WIDGET_REALIZED (pr))
3539 Window oid = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3541 gtk_widget_hide (pr);
3542 gtk_widget_unrealize (pr);
3543 gtk_widget_realize (pr);
3544 gtk_widget_show (pr);
3545 id = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3547 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3555 fix_preview_visual (state *s)
3557 GtkWidget *widget = name_to_widget (s, "preview");
3558 Visual *xvisual = get_best_gl_visual (s);
3559 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3560 GdkVisual *dvisual = gdk_visual_get_system();
3561 GdkColormap *cmap = (visual == dvisual
3562 ? gdk_colormap_get_system ()
3563 : gdk_colormap_new (visual, False));
3566 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3567 (visual == dvisual ? "default" : "non-default"),
3568 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3570 if (!GTK_WIDGET_REALIZED (widget) ||
3571 gtk_widget_get_visual (widget) != visual)
3573 gtk_widget_unrealize (widget);
3574 gtk_widget_set_visual (widget, visual);
3575 gtk_widget_set_colormap (widget, cmap);
3576 gtk_widget_realize (widget);
3579 /* Set the Widget colors to be white-on-black. */
3581 GdkWindow *window = widget->window;
3582 GtkStyle *style = gtk_style_copy (widget->style);
3583 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3584 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3585 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3586 GdkGC *fgc = gdk_gc_new(window);
3587 GdkGC *bgc = gdk_gc_new(window);
3588 if (!gdk_color_white (cmap, fg)) abort();
3589 if (!gdk_color_black (cmap, bg)) abort();
3590 gdk_gc_set_foreground (fgc, fg);
3591 gdk_gc_set_background (fgc, bg);
3592 gdk_gc_set_foreground (bgc, bg);
3593 gdk_gc_set_background (bgc, fg);
3594 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3595 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3596 gtk_widget_set_style (widget, style);
3598 /* For debugging purposes, put a title on the window (so that
3599 it can be easily found in the output of "xwininfo -tree".)
3601 gdk_window_set_title (window, "Preview");
3604 gtk_widget_show (widget);
3612 subproc_pretty_name (state *s)
3614 if (s->running_preview_cmd)
3616 char *ps = strdup (s->running_preview_cmd);
3617 char *ss = strchr (ps, ' ');
3619 ss = strrchr (ps, '/');
3630 return strdup ("???");
3635 reap_zombies (state *s)
3637 int wait_status = 0;
3639 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3643 if (pid == s->running_preview_pid)
3645 char *ss = subproc_pretty_name (s);
3646 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3647 (unsigned long) pid, ss);
3651 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3652 (unsigned long) pid);
3658 /* Mostly lifted from driver/subprocs.c */
3660 get_best_gl_visual (state *s)
3662 Display *dpy = GDK_DISPLAY();
3671 av[ac++] = "xscreensaver-gl-helper";
3676 perror ("error creating pipe:");
3683 switch ((int) (forked = fork ()))
3687 sprintf (buf, "%s: couldn't fork", blurb());
3695 close (in); /* don't need this one */
3696 close (ConnectionNumber (dpy)); /* close display fd */
3698 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3700 perror ("could not dup() a new stdout:");
3704 execvp (av[0], av); /* shouldn't return. */
3706 if (errno != ENOENT)
3708 /* Ignore "no such file or directory" errors, unless verbose.
3709 Issue all other exec errors, though. */
3710 sprintf (buf, "%s: running %s", blurb(), av[0]);
3714 /* Note that one must use _exit() instead of exit() in procs forked
3715 off of Gtk programs -- Gtk installs an atexit handler that has a
3716 copy of the X connection (which we've already closed, for safety.)
3717 If one uses exit() instead of _exit(), then one sometimes gets a
3718 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3720 _exit (1); /* exits fork */
3726 int wait_status = 0;
3728 FILE *f = fdopen (in, "r");
3732 close (out); /* don't need this one */
3735 if (!fgets (buf, sizeof(buf)-1, f))
3739 /* Wait for the child to die. */
3740 waitpid (-1, &wait_status, 0);
3742 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3748 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3754 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3756 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3757 blurb(), av[0], result);
3769 kill_preview_subproc (state *s, Bool reset_p)
3771 s->running_preview_error_p = False;
3774 clear_preview_window (s);
3776 if (s->subproc_check_timer_id)
3778 gtk_timeout_remove (s->subproc_check_timer_id);
3779 s->subproc_check_timer_id = 0;
3780 s->subproc_check_countdown = 0;
3783 if (s->running_preview_pid)
3785 int status = kill (s->running_preview_pid, SIGTERM);
3786 char *ss = subproc_pretty_name (s);
3793 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3794 blurb(), (unsigned long) s->running_preview_pid, ss);
3799 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3800 blurb(), (unsigned long) s->running_preview_pid, ss);
3806 waitpid(s->running_preview_pid, &endstatus, 0);
3808 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3809 (unsigned long) s->running_preview_pid, ss);
3813 s->running_preview_pid = 0;
3814 if (s->running_preview_cmd) free (s->running_preview_cmd);
3815 s->running_preview_cmd = 0;
3822 reset_preview_window (s);
3823 clear_preview_window (s);
3828 /* Immediately and unconditionally launches the given process,
3829 after appending the -window-id option; sets running_preview_pid.
3832 launch_preview_subproc (state *s)
3834 saver_preferences *p = &s->prefs;
3838 const char *cmd = s->desired_preview_cmd;
3840 GtkWidget *pr = name_to_widget (s, "preview");
3843 reset_preview_window (s);
3845 window = pr->window;
3847 s->running_preview_error_p = False;
3849 if (s->preview_suppressed_p)
3851 kill_preview_subproc (s, False);
3855 new_cmd = malloc (strlen (cmd) + 40);
3857 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3860 /* No window id? No command to run. */
3866 strcpy (new_cmd, cmd);
3867 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3871 kill_preview_subproc (s, False);
3874 s->running_preview_error_p = True;
3875 clear_preview_window (s);
3879 switch ((int) (forked = fork ()))
3884 sprintf (buf, "%s: couldn't fork", blurb());
3886 s->running_preview_error_p = True;
3892 close (ConnectionNumber (GDK_DISPLAY()));
3894 hack_subproc_environment (id, s->debug_p);
3896 usleep (250000); /* pause for 1/4th second before launching, to give
3897 the previous program time to die and flush its X
3898 buffer, so we don't get leftover turds on the
3901 exec_command (p->shell, new_cmd, p->nice_inferior);
3902 /* Don't bother printing an error message when we are unable to
3903 exec subprocesses; we handle that by polling the pid later.
3905 Note that one must use _exit() instead of exit() in procs forked
3906 off of Gtk programs -- Gtk installs an atexit handler that has a
3907 copy of the X connection (which we've already closed, for safety.)
3908 If one uses exit() instead of _exit(), then one sometimes gets a
3909 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3911 _exit (1); /* exits child fork */
3916 if (s->running_preview_cmd) free (s->running_preview_cmd);
3917 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3918 s->running_preview_pid = forked;
3922 char *ss = subproc_pretty_name (s);
3923 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3924 (unsigned long) forked, ss);
3931 schedule_preview_check (s);
3934 if (new_cmd) free (new_cmd);
3939 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3942 hack_environment (state *s)
3944 static const char *def_path =
3945 # ifdef DEFAULT_PATH_PREFIX
3946 DEFAULT_PATH_PREFIX;
3951 Display *dpy = GDK_DISPLAY();
3952 const char *odpy = DisplayString (dpy);
3953 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3954 strcpy (ndpy, "DISPLAY=");
3955 strcat (ndpy, odpy);
3960 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3962 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3963 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3964 So we must leak it (and/or the previous setting). Yay.
3967 if (def_path && *def_path)
3969 const char *opath = getenv("PATH");
3970 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3971 strcpy (npath, "PATH=");
3972 strcat (npath, def_path);
3973 strcat (npath, ":");
3974 strcat (npath, opath);
3978 /* do not free(npath) -- see above */
3981 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3987 hack_subproc_environment (Window preview_window_id, Bool debug_p)
3989 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
3990 necessary yet, but it will make programs work if we had invoked
3991 them with "-root" and not with "-window-id" -- which, of course,
3994 char *nssw = (char *) malloc (40);
3995 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
3997 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
3998 any more, right? It's not Posix, but everyone seems to have it. */
4003 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4005 /* do not free(nssw) -- see above */
4009 /* Called from a timer:
4010 Launches the currently-chosen subprocess, if it's not already running.
4011 If there's a different process running, kills it.
4014 update_subproc_timer (gpointer data)
4016 state *s = (state *) data;
4017 if (! s->desired_preview_cmd)
4018 kill_preview_subproc (s, True);
4019 else if (!s->running_preview_cmd ||
4020 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4021 launch_preview_subproc (s);
4023 s->subproc_timer_id = 0;
4024 return FALSE; /* do not re-execute timer */
4028 /* Call this when you think you might want a preview process running.
4029 It will set a timer that will actually launch that program a second
4030 from now, if you haven't changed your mind (to avoid double-click
4031 spazzing, etc.) `cmd' may be null meaning "no process".
4034 schedule_preview (state *s, const char *cmd)
4036 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4041 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4043 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4046 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4047 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4049 if (s->subproc_timer_id)
4050 gtk_timeout_remove (s->subproc_timer_id);
4051 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4055 /* Called from a timer:
4056 Checks to see if the subproc that should be running, actually is.
4059 check_subproc_timer (gpointer data)
4061 state *s = (state *) data;
4062 Bool again_p = True;
4064 if (s->running_preview_error_p || /* already dead */
4065 s->running_preview_pid <= 0)
4073 status = kill (s->running_preview_pid, 0);
4074 if (status < 0 && errno == ESRCH)
4075 s->running_preview_error_p = True;
4079 char *ss = subproc_pretty_name (s);
4080 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4081 (unsigned long) s->running_preview_pid, ss,
4082 (s->running_preview_error_p ? "dead" : "alive"));
4086 if (s->running_preview_error_p)
4088 clear_preview_window (s);
4093 /* Otherwise, it's currently alive. We might be checking again, or we
4094 might be satisfied. */
4096 if (--s->subproc_check_countdown <= 0)
4100 return TRUE; /* re-execute timer */
4103 s->subproc_check_timer_id = 0;
4104 s->subproc_check_countdown = 0;
4105 return FALSE; /* do not re-execute timer */
4110 /* Call this just after launching a subprocess.
4111 This sets a timer that will, five times a second for two seconds,
4112 check whether the program is still running. The assumption here
4113 is that if the process didn't stay up for more than a couple of
4114 seconds, then either the program doesn't exist, or it doesn't
4115 take a -window-id argument.
4118 schedule_preview_check (state *s)
4124 fprintf (stderr, "%s: scheduling check\n", blurb());
4126 if (s->subproc_check_timer_id)
4127 gtk_timeout_remove (s->subproc_check_timer_id);
4128 s->subproc_check_timer_id =
4129 gtk_timeout_add (1000 / ticks,
4130 check_subproc_timer, (gpointer) s);
4131 s->subproc_check_countdown = ticks * seconds;
4136 screen_blanked_p (void)
4140 unsigned long nitems, bytesafter;
4141 unsigned char *dataP = 0;
4142 Display *dpy = GDK_DISPLAY();
4143 Bool blanked_p = False;
4145 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4146 XA_SCREENSAVER_STATUS,
4147 0, 3, False, XA_INTEGER,
4148 &type, &format, &nitems, &bytesafter,
4151 && type == XA_INTEGER
4155 Atom *data = (Atom *) dataP;
4156 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4159 if (dataP) XFree (dataP);
4164 /* Wake up every now and then and see if the screen is blanked.
4165 If it is, kill off the small-window demo -- no point in wasting
4166 cycles by running two screensavers at once...
4169 check_blanked_timer (gpointer data)
4171 state *s = (state *) data;
4172 Bool blanked_p = screen_blanked_p ();
4173 if (blanked_p && s->running_preview_pid)
4176 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4177 kill_preview_subproc (s, True);
4180 return True; /* re-execute timer */
4184 /* How many screens are there (including Xinerama.)
4187 screen_count (Display *dpy)
4189 int nscreens = ScreenCount(dpy);
4190 # ifdef HAVE_XINERAMA
4193 int event_number, error_number;
4194 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4195 XineramaIsActive (dpy))
4197 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4198 if (xsi) XFree (xsi);
4201 # endif /* HAVE_XINERAMA */
4207 /* Setting window manager icon
4211 init_icon (GdkWindow *window)
4213 GdkBitmap *mask = 0;
4216 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4217 (gchar **) logo_50_xpm);
4219 gdk_window_set_icon (window, 0, pixmap, mask);
4223 /* The main demo-mode command loop.
4228 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4229 XrmRepresentation *type, XrmValue *value, XPointer closure)
4232 for (i = 0; quarks[i]; i++)
4234 if (bindings[i] == XrmBindTightly)
4235 fprintf (stderr, (i == 0 ? "" : "."));
4236 else if (bindings[i] == XrmBindLoosely)
4237 fprintf (stderr, "*");
4239 fprintf (stderr, " ??? ");
4240 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4243 fprintf (stderr, ": %s\n", (char *) value->addr);
4251 the_network_is_not_the_computer (state *s)
4253 Display *dpy = GDK_DISPLAY();
4254 char *rversion = 0, *ruser = 0, *rhost = 0;
4255 char *luser, *lhost;
4257 struct passwd *p = getpwuid (getuid ());
4258 const char *d = DisplayString (dpy);
4260 # if defined(HAVE_UNAME)
4262 if (uname (&uts) < 0)
4263 lhost = "<UNKNOWN>";
4265 lhost = uts.nodename;
4267 strcpy (lhost, getenv("SYS$NODE"));
4268 # else /* !HAVE_UNAME && !VMS */
4269 strcat (lhost, "<UNKNOWN>");
4270 # endif /* !HAVE_UNAME && !VMS */
4272 if (p && p->pw_name)
4277 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4279 /* Make a buffer that's big enough for a number of copies of all the
4280 strings, plus some. */
4281 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4282 (ruser ? strlen(ruser) : 0) +
4283 (rhost ? strlen(rhost) : 0) +
4290 if (!rversion || !*rversion)
4294 "The XScreenSaver daemon doesn't seem to be running\n"
4295 "on display \"%s\". Launch it now?"),
4298 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4300 /* Warn that the two processes are running as different users.
4304 "%s is running as user \"%s\" on host \"%s\".\n"
4305 "But the xscreensaver managing display \"%s\"\n"
4306 "is running as user \"%s\" on host \"%s\".\n"
4308 "Since they are different users, they won't be reading/writing\n"
4309 "the same ~/.xscreensaver file, so %s isn't\n"
4310 "going to work right.\n"
4312 "You should either re-run %s as \"%s\", or re-run\n"
4313 "xscreensaver as \"%s\".\n"
4315 "Restart the xscreensaver daemon now?\n"),
4316 progname, luser, lhost,
4318 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4320 progname, (ruser ? ruser : "???"),
4323 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4325 /* Warn that the two processes are running on different hosts.
4329 "%s is running as user \"%s\" on host \"%s\".\n"
4330 "But the xscreensaver managing display \"%s\"\n"
4331 "is running as user \"%s\" on host \"%s\".\n"
4333 "If those two machines don't share a file system (that is,\n"
4334 "if they don't see the same ~%s/.xscreensaver file) then\n"
4335 "%s won't work right.\n"
4337 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4338 progname, luser, lhost,
4340 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4345 else if (!!strcmp (rversion, s->short_version))
4347 /* Warn that the version numbers don't match.
4351 "This is %s version %s.\n"
4352 "But the xscreensaver managing display \"%s\"\n"
4353 "is version %s. This could cause problems.\n"
4355 "Restart the xscreensaver daemon now?\n"),
4356 progname, s->short_version,
4363 warning_dialog (s->toplevel_widget, msg, True, 1);
4365 if (rversion) free (rversion);
4366 if (ruser) free (ruser);
4367 if (rhost) free (rhost);
4372 /* We use this error handler so that X errors are preceeded by the name
4373 of the program that generated them.
4376 demo_ehandler (Display *dpy, XErrorEvent *error)
4378 state *s = global_state_kludge; /* I hate C so much... */
4379 fprintf (stderr, "\nX error in %s:\n", blurb());
4380 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4381 kill_preview_subproc (s, False);
4387 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4388 of the program that generated them; and also that we can ignore one
4389 particular bogus error message that Gdk madly spews.
4392 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4393 const gchar *message, gpointer user_data)
4395 /* Ignore the message "Got event for unknown window: 0x...".
4396 Apparently some events are coming in for the xscreensaver window
4397 (presumably reply events related to the ClientMessage) and Gdk
4398 feels the need to complain about them. So, just suppress any
4399 messages that look like that one.
4401 if (strstr (message, "unknown window"))
4404 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4405 (log_domain ? log_domain : progclass),
4406 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4407 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4408 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4409 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4410 log_level == G_LOG_LEVEL_INFO ? "info" :
4411 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4413 ((!*message || message[strlen(message)-1] != '\n')
4419 __extension__ /* shut up about "string length is greater than the length
4420 ISO C89 compilers are required to support" when including
4424 static char *defaults[] = {
4425 #include "XScreenSaver_ad.h"
4430 #ifdef HAVE_CRAPPLET
4431 static struct poptOption crapplet_options[] = {
4432 {NULL, '\0', 0, NULL, 0}
4434 #endif /* HAVE_CRAPPLET */
4437 const char *usage = "[--display dpy] [--prefs]"
4438 # ifdef HAVE_CRAPPLET
4441 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4444 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4446 state *s = (state *) user_data;
4447 Boolean oi = s->initializing_p;
4449 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4451 s->initializing_p = True;
4453 eschew_gtk_lossage (label);
4455 s->initializing_p = oi;
4461 print_widget_tree (GtkWidget *w, int depth)
4464 for (i = 0; i < depth; i++)
4465 fprintf (stderr, " ");
4466 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4468 if (GTK_IS_LIST (w))
4470 for (i = 0; i < depth+1; i++)
4471 fprintf (stderr, " ");
4472 fprintf (stderr, "...list kids...\n");
4474 else if (GTK_IS_CONTAINER (w))
4476 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4479 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4487 delayed_scroll_kludge (gpointer data)
4489 state *s = (state *) data;
4490 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4491 ensure_selected_item_visible (w);
4493 /* Oh, this is just fucking lovely, too. */
4494 w = GTK_WIDGET (name_to_widget (s, "preview"));
4495 gtk_widget_hide (w);
4496 gtk_widget_show (w);
4498 return FALSE; /* do not re-execute timer */
4504 create_xscreensaver_demo (void)
4508 nb = name_to_widget (global_state_kludge, "preview_notebook");
4509 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4511 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4515 create_xscreensaver_settings_dialog (void)
4519 box = name_to_widget (global_state_kludge, "dialog_action_area");
4521 w = name_to_widget (global_state_kludge, "adv_button");
4522 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4524 w = name_to_widget (global_state_kludge, "std_button");
4525 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4527 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4530 #endif /* HAVE_GTK2 */
4533 main (int argc, char **argv)
4537 saver_preferences *p;
4541 Widget toplevel_shell;
4542 char *real_progname = argv[0];
4545 Bool crapplet_p = False;
4549 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4550 textdomain (GETTEXT_PACKAGE);
4553 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4554 # else /* !HAVE_GTK2 */
4555 if (!setlocale (LC_ALL, ""))
4556 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4557 # endif /* !HAVE_GTK2 */
4559 #endif /* ENABLE_NLS */
4561 str = strrchr (real_progname, '/');
4562 if (str) real_progname = str+1;
4565 memset (s, 0, sizeof(*s));
4566 s->initializing_p = True;
4569 global_state_kludge = s; /* I hate C so much... */
4571 progname = real_progname;
4573 s->short_version = (char *) malloc (5);
4574 memcpy (s->short_version, screensaver_id + 17, 4);
4575 s->short_version [4] = 0;
4578 /* Register our error message logger for every ``log domain'' known.
4579 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4580 for all of the domains that seem to be in use.
4583 const char * const domains[] = { 0,
4584 "Gtk", "Gdk", "GLib", "GModule",
4585 "GThread", "Gnome", "GnomeUI" };
4586 for (i = 0; i < countof(domains); i++)
4587 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4590 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4593 const char *dir = DEFAULT_ICONDIR;
4594 if (*dir) add_pixmap_directory (dir);
4596 # endif /* !HAVE_GTK2 */
4597 #endif /* DEFAULT_ICONDIR */
4599 /* This is gross, but Gtk understands --display and not -display...
4601 for (i = 1; i < argc; i++)
4602 if (argv[i][0] && argv[i][1] &&
4603 !strncmp(argv[i], "-display", strlen(argv[i])))
4604 argv[i] = "--display";
4607 /* We need to parse this arg really early... Sigh. */
4608 for (i = 1; i < argc; i++)
4611 (!strcmp(argv[i], "--crapplet") ||
4612 !strcmp(argv[i], "--capplet")))
4614 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4617 for (j = i; j < argc; j++) /* remove it from the list */
4618 argv[j] = argv[j+1];
4620 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4621 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4623 fprintf (stderr, "%s: %s\n", real_progname, usage);
4625 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4628 (!strcmp(argv[i], "--debug") ||
4629 !strcmp(argv[i], "-debug") ||
4630 !strcmp(argv[i], "-d")))
4634 for (j = i; j < argc; j++) /* remove it from the list */
4635 argv[j] = argv[j+1];
4642 (!strcmp(argv[i], "-geometry") ||
4643 !strcmp(argv[i], "-geom") ||
4644 !strcmp(argv[i], "-geo") ||
4645 !strcmp(argv[i], "-g")))
4649 for (j = i; j < argc; j++) /* remove them from the list */
4650 argv[j] = argv[j+2];
4657 (!strcmp(argv[i], "--configdir")))
4661 hack_configuration_path = argv[i+1];
4662 for (j = i; j < argc; j++) /* remove them from the list */
4663 argv[j] = argv[j+2];
4667 if (0 != stat (hack_configuration_path, &st))
4670 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4674 else if (!S_ISDIR (st.st_mode))
4676 fprintf (stderr, "%s: not a directory: %s\n",
4677 blurb(), hack_configuration_path);
4685 fprintf (stderr, "%s: using config directory \"%s\"\n",
4686 progname, hack_configuration_path);
4689 /* Let Gtk open the X connection, then initialize Xt to use that
4690 same connection. Doctor Frankenstein would be proud.
4692 # ifdef HAVE_CRAPPLET
4695 GnomeClient *client;
4696 GnomeClientFlags flags = 0;
4698 int init_results = gnome_capplet_init ("screensaver-properties",
4700 argc, argv, NULL, 0, NULL);
4702 0 upon successful initialization;
4703 1 if --init-session-settings was passed on the cmdline;
4704 2 if --ignore was passed on the cmdline;
4707 So the 1 signifies just to init the settings, and quit, basically.
4708 (Meaning launch the xscreensaver daemon.)
4711 if (init_results < 0)
4714 g_error ("An initialization error occurred while "
4715 "starting xscreensaver-capplet.\n");
4717 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4718 real_progname, init_results);
4723 client = gnome_master_client ();
4726 flags = gnome_client_get_flags (client);
4728 if (flags & GNOME_CLIENT_IS_CONNECTED)
4731 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4732 gnome_client_get_id (client));
4735 char *session_args[20];
4737 session_args[i++] = real_progname;
4738 session_args[i++] = "--capplet";
4739 session_args[i++] = "--init-session-settings";
4740 session_args[i] = 0;
4741 gnome_client_set_priority (client, 20);
4742 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4743 gnome_client_set_restart_command (client, i, session_args);
4747 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4750 gnome_client_flush (client);
4753 if (init_results == 1)
4755 system ("xscreensaver -nosplash &");
4761 # endif /* HAVE_CRAPPLET */
4763 gtk_init (&argc, &argv);
4767 /* We must read exactly the same resources as xscreensaver.
4768 That means we must have both the same progclass *and* progname,
4769 at least as far as the resource database is concerned. So,
4770 put "xscreensaver" in argv[0] while initializing Xt.
4772 argv[0] = "xscreensaver";
4776 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4778 XtToolkitInitialize ();
4779 app = XtCreateApplicationContext ();
4780 dpy = GDK_DISPLAY();
4781 XtAppSetFallbackResources (app, defaults);
4782 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4783 toplevel_shell = XtAppCreateShell (progname, progclass,
4784 applicationShellWidgetClass,
4787 dpy = XtDisplay (toplevel_shell);
4788 db = XtDatabase (dpy);
4789 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4790 XSetErrorHandler (demo_ehandler);
4792 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4793 signal (SIGPIPE, SIG_IGN);
4795 /* After doing Xt-style command-line processing, complain about any
4796 unrecognized command-line arguments.
4798 for (i = 1; i < argc; i++)
4800 char *str = argv[i];
4801 if (str[0] == '-' && str[1] == '-')
4803 if (!strcmp (str, "-prefs"))
4805 else if (crapplet_p)
4806 /* There are lots of random args that we don't care about when we're
4807 started as a crapplet, so just ignore unknown args in that case. */
4811 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4813 fprintf (stderr, "%s: %s\n", real_progname, usage);
4818 /* Load the init file, which may end up consulting the X resource database
4819 and the site-wide app-defaults file. Note that at this point, it's
4820 important that `progname' be "xscreensaver", rather than whatever
4824 s->nscreens = screen_count (dpy);
4826 hack_environment (s); /* must be before initialize_sort_map() */
4828 load_init_file (dpy, p);
4829 initialize_sort_map (s);
4831 /* Now that Xt has been initialized, and the resources have been read,
4832 we can set our `progname' variable to something more in line with
4835 progname = real_progname;
4839 /* Print out all the resources we read. */
4841 XrmName name = { 0 };
4842 XrmClass class = { 0 };
4844 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4850 /* Intern the atoms that xscreensaver_command() needs.
4852 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4853 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4854 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4855 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4856 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4857 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4858 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4859 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4860 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4861 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4862 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4863 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4864 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4867 /* Create the window and all its widgets.
4869 s->base_widget = create_xscreensaver_demo ();
4870 s->popup_widget = create_xscreensaver_settings_dialog ();
4871 s->toplevel_widget = s->base_widget;
4874 /* Set the main window's title. */
4876 char *base_title = _("Screensaver Preferences");
4877 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4878 char *s1, *s2, *s3, *s4;
4879 s1 = (char *) strchr(v, ' '); s1++;
4880 s2 = (char *) strchr(s1, ' ');
4881 s3 = (char *) strchr(v, '('); s3++;
4882 s4 = (char *) strchr(s3, ')');
4886 window_title = (char *) malloc (strlen (base_title) +
4887 strlen (progclass) +
4888 strlen (s1) + strlen (s3) +
4890 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4891 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4892 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4896 /* Adjust the (invisible) notebooks on the popup dialog... */
4898 GtkNotebook *notebook =
4899 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4900 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4904 gtk_widget_hide (std);
4905 # else /* !HAVE_XML */
4906 /* Make the advanced page be the only one available. */
4907 gtk_widget_set_sensitive (std, False);
4908 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4909 gtk_widget_hide (std);
4911 # endif /* !HAVE_XML */
4913 gtk_notebook_set_page (notebook, page);
4914 gtk_notebook_set_show_tabs (notebook, False);
4917 /* Various other widget initializations...
4919 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4920 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4922 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4923 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4926 populate_hack_list (s);
4927 populate_prefs_page (s);
4928 sensitize_demo_widgets (s, False);
4929 fix_text_entry_sizes (s);
4930 scroll_to_current_hack (s);
4932 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4933 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4937 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4938 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4940 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4941 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4943 #endif /* !HAVE_GTK2 */
4945 /* Hook up callbacks to the items on the mode menu. */
4947 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4948 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4949 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4951 for (i = 0; kids; kids = kids->next, i++)
4953 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4954 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4957 /* The "random-same" mode menu item does not appear unless
4958 there are multple screens.
4960 if (s->nscreens <= 1 &&
4961 mode_menu_order[i] == RANDOM_HACKS_SAME)
4962 gtk_widget_hide (GTK_WIDGET (kids->data));
4965 if (s->nscreens <= 1) /* recompute option-menu size */
4967 gtk_widget_unrealize (GTK_WIDGET (menu));
4968 gtk_widget_realize (GTK_WIDGET (menu));
4973 /* Handle the -prefs command-line argument. */
4976 GtkNotebook *notebook =
4977 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4978 gtk_notebook_set_page (notebook, 1);
4981 # ifdef HAVE_CRAPPLET
4985 GtkWidget *outer_vbox;
4987 gtk_widget_hide (s->toplevel_widget);
4989 capplet = capplet_widget_new ();
4991 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4992 # ifdef HAVE_CRAPPLET_IMMEDIATE
4993 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4994 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4995 /* In crapplet-mode, take off the menubar. */
4996 gtk_widget_hide (name_to_widget (s, "menubar"));
4998 /* Reparent our top-level container to be a child of the capplet
5001 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5002 gtk_widget_ref (outer_vbox);
5003 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5005 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5006 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5008 /* Find the window above us, and set the title and close handler. */
5010 GtkWidget *window = capplet;
5011 while (window && !GTK_IS_WINDOW (window))
5012 window = window->parent;
5015 gtk_window_set_title (GTK_WINDOW (window), window_title);
5016 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5017 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5022 s->toplevel_widget = capplet;
5024 # endif /* HAVE_CRAPPLET */
5027 /* The Gnome folks hate the menubar. I think it's important to have access
5028 to the commands on the File menu (Restart Daemon, etc.) and to the
5029 About and Documentation commands on the Help menu.
5033 gtk_widget_hide (name_to_widget (s, "menubar"));
5037 free (window_title);
5041 /* After picking the default size, allow -geometry to override it. */
5043 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5046 gtk_widget_show (s->toplevel_widget);
5047 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
5048 fix_preview_visual (s);
5050 /* Realize page zero, so that we can diddle the scrollbar when the
5051 user tabs back to it -- otherwise, the current hack isn't scrolled
5052 to the first time they tab back there, when started with "-prefs".
5053 (Though it is if they then tab away, and back again.)
5055 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5056 #### understands this crap, explain to me how to make this work.
5058 gtk_widget_realize (name_to_widget (s, "demos_table"));
5061 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5064 /* Issue any warnings about the running xscreensaver daemon. */
5066 the_network_is_not_the_computer (s);
5069 /* Run the Gtk event loop, and not the Xt event loop. This means that
5070 if there were Xt timers or fds registered, they would never get serviced,
5071 and if there were any Xt widgets, they would never have events delivered.
5072 Fortunately, we're using Gtk for all of the UI, and only initialized
5073 Xt so that we could process the command line and use the X resource
5076 s->initializing_p = False;
5078 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5079 after we start up. Otherwise, it always appears scrolled to the top
5080 when in crapplet-mode. */
5081 gtk_timeout_add (500, delayed_scroll_kludge, s);
5085 /* Load every configurator in turn, to scan them for errors all at once. */
5089 for (i = 0; i < p->screenhacks_count; i++)
5091 screenhack *hack = p->screenhacks[i];
5092 conf_data *d = load_configurator (hack->command, s->debug_p);
5093 if (d) free_conf_data (d);
5099 # ifdef HAVE_CRAPPLET
5101 capplet_gtk_main ();
5103 # endif /* HAVE_CRAPPLET */
5106 kill_preview_subproc (s, False);
5110 #endif /* HAVE_GTK -- whole file */