1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2006 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #ifdef HAVE_GTK /* whole file */
19 #include <xscreensaver-intl.h>
28 # define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */
36 #endif /* ENABLE_NLS */
39 # include <pwd.h> /* for getpwuid() */
45 # include <sys/utsname.h> /* for uname() */
46 #endif /* HAVE_UNAME */
56 #ifdef HAVE_SYS_WAIT_H
57 # include <sys/wait.h> /* for waitpid() and associated macros */
61 #include <X11/Xproto.h> /* for CARD32 */
62 #include <X11/Xatom.h> /* for XA_INTEGER */
63 #include <X11/Intrinsic.h>
64 #include <X11/StringDefs.h>
66 /* We don't actually use any widget internals, but these are included
67 so that gdb will have debug info for the widgets... */
68 #include <X11/IntrinsicP.h>
69 #include <X11/ShellP.h>
73 # include <X11/Xmu/Error.h>
75 # include <Xmu/Error.h>
82 # include <X11/extensions/Xinerama.h>
83 #endif /* HAVE_XINERAMA */
89 # include <capplet-widget.h>
95 # include <glade/glade-xml.h>
97 #else /* !HAVE_GTK2 */
98 # define G_MODULE_EXPORT /**/
99 #endif /* !HAVE_GTK2 */
101 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
102 # define GLADE_DIR DEFAULT_ICONDIR
104 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
105 # define DEFAULT_ICONDIR GLADE_DIR
109 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
110 It is unused otherwise, so in that case, stub it out. */
111 static const char *hack_configuration_path = 0;
118 #include "resources.h" /* for parse_time() */
119 #include "visual.h" /* for has_writable_cells() */
120 #include "remote.h" /* for xscreensaver_command() */
123 #include "logo-50.xpm"
124 #include "logo-180.xpm"
126 #undef dgettext /* else these are defined twice... */
129 #include "demo-Gtk-widgets.h"
130 #include "demo-Gtk-support.h"
131 #include "demo-Gtk-conf.h"
143 #endif /* HAVE_GTK2 */
146 extern void exec_command (const char *shell, const char *command, int nice);
147 extern int on_path_p (const char *program);
149 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
152 #define countof(x) (sizeof((x))/sizeof((*x)))
156 char *progclass = "XScreenSaver";
159 /* The order of the items in the mode menu. */
160 static int mode_menu_order[] = {
161 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
166 char *short_version; /* version number of this xscreensaver build */
168 GtkWidget *toplevel_widget; /* the main window */
169 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
170 GtkWidget *popup_widget; /* the "Settings" dialog */
171 conf_data *cdata; /* private data for per-hack configuration */
174 GladeXML *glade_ui; /* Glade UI file */
175 #endif /* HAVE_GTK2 */
177 Bool debug_p; /* whether to print diagnostics */
178 Bool initializing_p; /* flag for breaking recursion loops */
179 Bool saving_p; /* flag for breaking recursion loops */
181 char *desired_preview_cmd; /* subprocess we intend to run */
182 char *running_preview_cmd; /* subprocess we are currently running */
183 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
184 Bool running_preview_error_p; /* whether the pid died abnormally */
186 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
187 int subproc_timer_id; /* timer to delay subproc launch */
188 int subproc_check_timer_id; /* timer to check whether it started up */
189 int subproc_check_countdown; /* how many more checks left */
191 int *list_elt_to_hack_number; /* table for sorting the hack list */
192 int *hack_number_to_list_elt; /* the inverse table */
193 Bool *hacks_available_p; /* whether hacks are on $PATH */
194 int total_available; /* how many are on $PATH */
195 int list_count; /* how many items are in the list: this may be
196 less than p->screenhacks_count, if some are
199 int _selected_list_element; /* don't use this: call
200 selected_list_element() instead */
202 int nscreens; /* How many X or Xinerama screens there are */
204 saver_preferences prefs;
209 /* Total fucking evilness due to the fact that it's rocket science to get
210 a closure object of our own down into the various widget callbacks. */
211 static state *global_state_kludge;
214 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
215 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
216 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
219 static void populate_demo_window (state *, int list_elt);
220 static void populate_prefs_page (state *);
221 static void populate_popup_window (state *);
223 static Bool flush_dialog_changes_and_save (state *);
224 static Bool flush_popup_changes_and_save (state *);
226 static int maybe_reload_init_file (state *);
227 static void await_xscreensaver (state *);
228 static Bool xscreensaver_running_p (state *);
229 static void sensitize_menu_items (state *s, Bool force_p);
230 static void force_dialog_repaint (state *s);
232 static void schedule_preview (state *, const char *cmd);
233 static void kill_preview_subproc (state *, Bool reset_p);
234 static void schedule_preview_check (state *);
237 /* Prototypes of functions used by the Glade-generated code,
240 void exit_menu_cb (GtkMenuItem *, gpointer user_data);
241 void about_menu_cb (GtkMenuItem *, gpointer user_data);
242 void doc_menu_cb (GtkMenuItem *, gpointer user_data);
243 void file_menu_cb (GtkMenuItem *, gpointer user_data);
244 void activate_menu_cb (GtkMenuItem *, gpointer user_data);
245 void lock_menu_cb (GtkMenuItem *, gpointer user_data);
246 void kill_menu_cb (GtkMenuItem *, gpointer user_data);
247 void restart_menu_cb (GtkWidget *, gpointer user_data);
248 void run_this_cb (GtkButton *, gpointer user_data);
249 void manual_cb (GtkButton *, gpointer user_data);
250 void run_next_cb (GtkButton *, gpointer user_data);
251 void run_prev_cb (GtkButton *, gpointer user_data);
252 void pref_changed_cb (GtkWidget *, gpointer user_data);
253 gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
254 void mode_menu_item_cb (GtkWidget *, gpointer user_data);
255 void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
256 gint page_num, gpointer user_data);
257 void browse_image_dir_cb (GtkButton *, gpointer user_data);
258 void browse_text_file_cb (GtkButton *, gpointer user_data);
259 void browse_text_program_cb (GtkButton *, gpointer user_data);
260 void settings_cb (GtkButton *, gpointer user_data);
261 void settings_adv_cb (GtkButton *, gpointer user_data);
262 void settings_std_cb (GtkButton *, gpointer user_data);
263 void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
264 gint page_num, gpointer user_data);
265 void settings_cancel_cb (GtkButton *, gpointer user_data);
266 void settings_ok_cb (GtkButton *, gpointer user_data);
269 /* Some random utility functions
272 const char *blurb (void);
277 time_t now = time ((time_t *) 0);
278 char *ct = (char *) ctime (&now);
279 static char buf[255];
280 int n = strlen(progname);
282 strncpy(buf, progname, n);
285 strncpy(buf+n, ct+11, 8);
286 strcpy(buf+n+9, ": ");
292 name_to_widget (state *s, const char *name)
302 /* First try to load the Glade file from the current directory;
303 if there isn't one there, check the installed directory.
305 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
306 const char * const files[] = { GLADE_FILE_NAME,
307 GLADE_DIR "/" GLADE_FILE_NAME };
309 for (i = 0; i < countof (files); i++)
312 if (!stat (files[i], &st))
314 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
321 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
322 "\tfrom " GLADE_DIR "/ or current directory.\n",
326 # undef GLADE_FILE_NAME
328 glade_xml_signal_autoconnect (s->glade_ui);
331 w = glade_xml_get_widget (s->glade_ui, name);
333 #else /* !HAVE_GTK2 */
335 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
338 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
340 #endif /* HAVE_GTK2 */
343 fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
349 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
350 Takes a scroller, viewport, or list as an argument.
353 ensure_selected_item_visible (GtkWidget *widget)
357 GtkTreeSelection *selection;
361 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
362 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
363 path = gtk_tree_path_new_first ();
365 path = gtk_tree_model_get_path (model, &iter);
367 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
369 gtk_tree_path_free (path);
371 #else /* !HAVE_GTK2 */
373 GtkScrolledWindow *scroller = 0;
375 GtkList *list_widget = 0;
379 GtkWidget *selected = 0;
382 gint parent_h, child_y, child_h, children_h, ignore;
383 double ratio_t, ratio_b;
385 if (GTK_IS_SCROLLED_WINDOW (widget))
387 scroller = GTK_SCROLLED_WINDOW (widget);
388 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
389 list_widget = GTK_LIST (GTK_BIN(vp)->child);
391 else if (GTK_IS_VIEWPORT (widget))
393 vp = GTK_VIEWPORT (widget);
394 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
395 list_widget = GTK_LIST (GTK_BIN(vp)->child);
397 else if (GTK_IS_LIST (widget))
399 list_widget = GTK_LIST (widget);
400 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
401 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
406 slist = list_widget->selection;
407 selected = (slist ? GTK_WIDGET (slist->data) : 0);
411 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
413 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
414 kids; kids = kids->next)
417 adj = gtk_scrolled_window_get_vadjustment (scroller);
419 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
420 &ignore, &ignore, &ignore, &parent_h, &ignore);
421 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
422 &ignore, &child_y, &ignore, &child_h, &ignore);
423 children_h = nkids * child_h;
425 ratio_t = ((double) child_y) / ((double) children_h);
426 ratio_b = ((double) child_y + child_h) / ((double) children_h);
428 if (adj->upper == 0.0) /* no items in list */
431 if (ratio_t < (adj->value / adj->upper) ||
432 ratio_b > ((adj->value + adj->page_size) / adj->upper))
435 int slop = parent_h * 0.75; /* how much to overshoot by */
437 if (ratio_t < (adj->value / adj->upper))
439 double ratio_w = ((double) parent_h) / ((double) children_h);
440 double ratio_l = (ratio_b - ratio_t);
441 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
444 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
446 target = ratio_t * adj->upper;
450 if (target > adj->upper - adj->page_size)
451 target = adj->upper - adj->page_size;
455 gtk_adjustment_set_value (adj, target);
457 #endif /* !HAVE_GTK2 */
461 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
463 GtkWidget *shell = GTK_WIDGET (user_data);
464 while (shell->parent)
465 shell = shell->parent;
466 gtk_widget_destroy (GTK_WIDGET (shell));
470 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
472 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
474 restart_menu_cb (widget, user_data);
475 warning_dialog_dismiss_cb (widget, user_data);
479 warning_dialog (GtkWidget *parent, const char *message,
480 Boolean restart_button_p, int center)
482 char *msg = strdup (message);
485 GtkWidget *dialog = gtk_dialog_new ();
486 GtkWidget *label = 0;
488 GtkWidget *cancel = 0;
491 while (parent && !parent->window)
492 parent = parent->parent;
495 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
497 fprintf (stderr, "%s: too early for dialog?\n", progname);
505 char *s = strchr (head, '\n');
508 sprintf (name, "label%d", i++);
511 label = gtk_label_new (head);
513 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
514 #endif /* HAVE_GTK2 */
519 GTK_WIDGET (label)->style =
520 gtk_style_copy (GTK_WIDGET (label)->style);
521 GTK_WIDGET (label)->style->font =
522 gdk_font_load (get_string_resource("warning_dialog.headingFont",
524 gtk_widget_set_style (GTK_WIDGET (label),
525 GTK_WIDGET (label)->style);
527 #endif /* !HAVE_GTK2 */
529 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
530 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
531 label, TRUE, TRUE, 0);
532 gtk_widget_show (label);
543 label = gtk_label_new ("");
544 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
545 label, TRUE, TRUE, 0);
546 gtk_widget_show (label);
548 label = gtk_hbutton_box_new ();
549 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
550 label, TRUE, TRUE, 0);
553 if (restart_button_p)
555 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
556 gtk_container_add (GTK_CONTAINER (label), cancel);
559 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
560 gtk_container_add (GTK_CONTAINER (label), ok);
562 #else /* !HAVE_GTK2 */
564 ok = gtk_button_new_with_label ("OK");
565 gtk_container_add (GTK_CONTAINER (label), ok);
567 if (restart_button_p)
569 cancel = gtk_button_new_with_label ("Cancel");
570 gtk_container_add (GTK_CONTAINER (label), cancel);
573 #endif /* !HAVE_GTK2 */
575 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
576 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
577 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
578 STFU GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
579 gtk_widget_show (ok);
580 gtk_widget_grab_focus (ok);
584 STFU GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
585 gtk_widget_show (cancel);
587 gtk_widget_show (label);
588 gtk_widget_show (dialog);
590 if (restart_button_p)
592 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
593 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
595 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
596 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
601 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
602 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
606 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
607 GTK_WIDGET (parent)->window);
610 gtk_window_present (GTK_WINDOW (dialog));
611 #else /* !HAVE_GTK2 */
612 gdk_window_show (GTK_WIDGET (dialog)->window);
613 gdk_window_raise (GTK_WIDGET (dialog)->window);
614 #endif /* !HAVE_GTK2 */
621 run_cmd (state *s, Atom command, int arg)
626 flush_dialog_changes_and_save (s);
627 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
629 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
630 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
637 sprintf (buf, "Error:\n\n%s", err);
639 strcpy (buf, "Unknown error!");
640 warning_dialog (s->toplevel_widget, buf, False, 100);
644 sensitize_menu_items (s, True);
645 force_dialog_repaint (s);
650 run_hack (state *s, int list_elt, Bool report_errors_p)
656 if (list_elt < 0) return;
657 hack_number = s->list_elt_to_hack_number[list_elt];
659 flush_dialog_changes_and_save (s);
660 schedule_preview (s, 0);
662 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
665 if (status < 0 && report_errors_p)
667 if (xscreensaver_running_p (s))
669 /* Kludge: ignore the spurious "window unexpectedly deleted"
671 if (err && strstr (err, "unexpectedly deleted"))
678 sprintf (buf, "Error:\n\n%s", err);
680 strcpy (buf, "Unknown error!");
681 warning_dialog (s->toplevel_widget, buf, False, 100);
686 /* The error is that the daemon isn't running;
689 const char *d = DisplayString (GDK_DISPLAY());
693 "The XScreenSaver daemon doesn't seem to be running\n"
694 "on display \"%s\". Launch it now?"),
696 warning_dialog (s->toplevel_widget, msg, True, 1);
702 sensitize_menu_items (s, False);
709 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
710 libglade work on Cygwin; apparently all Glade callbacks need this magic
711 extra declaration. I do not pretend to understand.
715 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
717 state *s = global_state_kludge; /* I hate C so much... */
718 flush_dialog_changes_and_save (s);
719 kill_preview_subproc (s, False);
724 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
726 state *s = (state *) data;
727 flush_dialog_changes_and_save (s);
734 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
737 char *vers = strdup (screensaver_id + 4);
740 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
742 s = strchr (vers, ',');
746 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
747 non-ASCII characters aren't allowed in localizable string keys."
748 (I don't want to just use (c) instead of © because that doesn't
749 look as good in the plain-old default Latin1 "C" locale.)
752 sprintf(copy, ("Copyright \xC2\xA9 1991-2006 %s"), s);
753 #else /* !HAVE_GTK2 */
754 sprintf(copy, ("Copyright \251 1991-2006 %s"), s);
755 #endif /* !HAVE_GTK2 */
757 sprintf (msg, "%s\n\n%s", copy, desc);
759 /* I can't make gnome_about_new() work here -- it starts dying in
760 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
761 then this might be the thing to do:
765 const gchar *auth[] = { 0 };
766 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
768 gtk_widget_show (about);
770 #else / * GTK but not GNOME * /
774 GdkColormap *colormap;
775 GdkPixmap *gdkpixmap;
778 GtkWidget *dialog = gtk_dialog_new ();
779 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
780 GtkWidget *parent = GTK_WIDGET (menuitem);
781 while (parent->parent)
782 parent = parent->parent;
784 hbox = gtk_hbox_new (FALSE, 20);
785 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
786 hbox, TRUE, TRUE, 0);
788 colormap = gtk_widget_get_colormap (parent);
790 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
791 (gchar **) logo_180_xpm);
792 icon = gtk_pixmap_new (gdkpixmap, mask);
793 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
795 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
797 vbox = gtk_vbox_new (FALSE, 0);
798 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
800 label1 = gtk_label_new (vers);
801 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
802 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
803 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
806 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
807 GTK_WIDGET (label1)->style->font =
808 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
809 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
810 #endif /* HAVE_GTK2 */
812 label2 = gtk_label_new (msg);
813 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
814 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
815 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
818 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
819 GTK_WIDGET (label2)->style->font =
820 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
821 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
822 #endif /* HAVE_GTK2 */
824 hb = gtk_hbutton_box_new ();
826 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
830 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
831 #else /* !HAVE_GTK2 */
832 ok = gtk_button_new_with_label (_("OK"));
833 #endif /* !HAVE_GTK2 */
834 gtk_container_add (GTK_CONTAINER (hb), ok);
836 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
837 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
838 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
840 gtk_widget_show (hbox);
841 gtk_widget_show (icon);
842 gtk_widget_show (vbox);
843 gtk_widget_show (label1);
844 gtk_widget_show (label2);
845 gtk_widget_show (hb);
846 gtk_widget_show (ok);
847 gtk_widget_show (dialog);
849 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
850 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
852 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
853 GTK_WIDGET (parent)->window);
854 gdk_window_show (GTK_WIDGET (dialog)->window);
855 gdk_window_raise (GTK_WIDGET (dialog)->window);
861 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
863 state *s = global_state_kludge; /* I hate C so much... */
864 saver_preferences *p = &s->prefs;
867 if (!p->help_url || !*p->help_url)
869 warning_dialog (s->toplevel_widget,
871 "No Help URL has been specified.\n"), False, 100);
875 help_command = (char *) malloc (strlen (p->load_url_command) +
876 (strlen (p->help_url) * 4) + 20);
877 strcpy (help_command, "( ");
878 sprintf (help_command + strlen(help_command),
880 p->help_url, p->help_url, p->help_url, p->help_url);
881 strcat (help_command, " ) &");
882 if (system (help_command) < 0)
883 fprintf (stderr, "%s: fork error\n", blurb());
889 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
891 state *s = global_state_kludge; /* I hate C so much... */
892 sensitize_menu_items (s, False);
897 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
899 state *s = global_state_kludge; /* I hate C so much... */
900 run_cmd (s, XA_ACTIVATE, 0);
905 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
907 state *s = global_state_kludge; /* I hate C so much... */
908 run_cmd (s, XA_LOCK, 0);
913 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
915 state *s = global_state_kludge; /* I hate C so much... */
916 run_cmd (s, XA_EXIT, 0);
921 restart_menu_cb (GtkWidget *widget, gpointer user_data)
923 state *s = global_state_kludge; /* I hate C so much... */
924 flush_dialog_changes_and_save (s);
925 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
927 if (system ("xscreensaver -nosplash &") < 0)
928 fprintf (stderr, "%s: fork error\n", blurb());
930 await_xscreensaver (s);
934 xscreensaver_running_p (state *s)
936 Display *dpy = GDK_DISPLAY();
938 server_xscreensaver_version (dpy, &rversion, 0, 0);
946 await_xscreensaver (state *s)
951 while (!ok && (--countdown > 0))
952 if (xscreensaver_running_p (s))
955 sleep (1); /* If it's not there yet, wait a second... */
957 sensitize_menu_items (s, True);
961 /* Timed out, no screensaver running. */
964 Bool root_p = (geteuid () == 0);
968 "The xscreensaver daemon did not start up properly.\n"
974 __extension__ /* don't warn about "string length is greater than
975 the length ISO C89 compilers are required to
976 support" in the following expression... */
979 _("You are running as root. This usually means that xscreensaver\n"
980 "was unable to contact your X server because access control is\n"
981 "turned on. Try running this command:\n"
983 " xhost +localhost\n"
985 "and then selecting `File / Restart Daemon'.\n"
987 "Note that turning off access control will allow anyone logged\n"
988 "on to this machine to access your screen, which might be\n"
989 "considered a security problem. Please read the xscreensaver\n"
990 "manual and FAQ for more information.\n"
992 "You shouldn't run X as root. Instead, you should log in as a\n"
993 "normal user, and `su' as necessary."));
995 strcat (buf, _("Please check your $PATH and permissions."));
997 warning_dialog (s->toplevel_widget, buf, False, 1);
1000 force_dialog_repaint (s);
1005 selected_list_element (state *s)
1007 return s->_selected_list_element;
1012 demo_write_init_file (state *s, saver_preferences *p)
1014 Display *dpy = GDK_DISPLAY();
1017 /* #### try to figure out why shit keeps getting reordered... */
1018 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1022 if (!write_init_file (dpy, p, s->short_version, False))
1025 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1030 const char *f = init_file_name();
1032 warning_dialog (s->toplevel_widget,
1033 _("Error:\n\nCouldn't determine init file name!\n"),
1037 char *b = (char *) malloc (strlen(f) + 1024);
1038 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1039 warning_dialog (s->toplevel_widget, b, False, 100);
1047 G_MODULE_EXPORT void
1048 run_this_cb (GtkButton *button, gpointer user_data)
1050 state *s = global_state_kludge; /* I hate C so much... */
1051 int list_elt = selected_list_element (s);
1052 if (list_elt < 0) return;
1053 if (!flush_dialog_changes_and_save (s))
1054 run_hack (s, list_elt, True);
1058 G_MODULE_EXPORT void
1059 manual_cb (GtkButton *button, gpointer user_data)
1061 Display *dpy = GDK_DISPLAY();
1062 state *s = global_state_kludge; /* I hate C so much... */
1063 saver_preferences *p = &s->prefs;
1064 GtkWidget *list_widget = name_to_widget (s, "list");
1065 int list_elt = selected_list_element (s);
1067 char *name, *name2, *cmd, *str;
1069 if (list_elt < 0) return;
1070 hack_number = s->list_elt_to_hack_number[list_elt];
1072 flush_dialog_changes_and_save (s);
1073 ensure_selected_item_visible (list_widget);
1075 name = strdup (p->screenhacks[hack_number]->command);
1078 while (isspace (*name2)) name2++;
1080 while (*str && !isspace (*str)) str++;
1082 str = strrchr (name2, '/');
1083 if (str) name2 = str+1;
1085 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1088 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1089 strcpy (cmd2, "( ");
1090 sprintf (cmd2 + strlen (cmd2),
1092 name2, name2, name2, name2);
1093 strcat (cmd2, " ) &");
1094 if (system (cmd2) < 0)
1095 fprintf (stderr, "%s: fork error\n", blurb());
1100 warning_dialog (GTK_WIDGET (button),
1101 _("Error:\n\nno `manualCommand' resource set."),
1110 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1112 GtkWidget *parent = name_to_widget (s, "scroller");
1113 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
1116 GtkTreeModel *model;
1117 GtkTreeSelection *selection;
1118 #endif /* HAVE_GTK2 */
1120 if (!was) gtk_widget_set_sensitive (parent, True);
1122 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1123 STFU g_assert (model);
1124 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1126 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1127 gtk_tree_selection_select_iter (selection, &iter);
1129 #else /* !HAVE_GTK2 */
1130 gtk_list_select_item (GTK_LIST (list), list_elt);
1131 #endif /* !HAVE_GTK2 */
1132 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1133 if (!was) gtk_widget_set_sensitive (parent, False);
1137 G_MODULE_EXPORT void
1138 run_next_cb (GtkButton *button, gpointer user_data)
1140 state *s = global_state_kludge; /* I hate C so much... */
1141 /* saver_preferences *p = &s->prefs; */
1142 Bool ops = s->preview_suppressed_p;
1144 GtkWidget *list_widget = name_to_widget (s, "list");
1145 int list_elt = selected_list_element (s);
1152 if (list_elt >= s->list_count)
1155 s->preview_suppressed_p = True;
1157 flush_dialog_changes_and_save (s);
1158 force_list_select_item (s, list_widget, list_elt, True);
1159 populate_demo_window (s, list_elt);
1160 run_hack (s, list_elt, False);
1162 s->preview_suppressed_p = ops;
1166 G_MODULE_EXPORT void
1167 run_prev_cb (GtkButton *button, gpointer user_data)
1169 state *s = global_state_kludge; /* I hate C so much... */
1170 /* saver_preferences *p = &s->prefs; */
1171 Bool ops = s->preview_suppressed_p;
1173 GtkWidget *list_widget = name_to_widget (s, "list");
1174 int list_elt = selected_list_element (s);
1177 list_elt = s->list_count - 1;
1182 list_elt = s->list_count - 1;
1184 s->preview_suppressed_p = True;
1186 flush_dialog_changes_and_save (s);
1187 force_list_select_item (s, list_widget, list_elt, True);
1188 populate_demo_window (s, list_elt);
1189 run_hack (s, list_elt, False);
1191 s->preview_suppressed_p = ops;
1195 /* Writes the given settings into prefs.
1196 Returns true if there was a change, False otherwise.
1197 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1200 flush_changes (state *s,
1203 const char *command,
1206 saver_preferences *p = &s->prefs;
1207 Bool changed = False;
1210 if (list_elt < 0 || list_elt >= s->list_count)
1213 hack_number = s->list_elt_to_hack_number[list_elt];
1214 hack = p->screenhacks[hack_number];
1216 if (enabled_p != -1 &&
1217 enabled_p != hack->enabled_p)
1219 hack->enabled_p = enabled_p;
1222 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1223 blurb(), hack->name, enabled_p);
1228 if (!hack->command || !!strcmp (command, hack->command))
1230 if (hack->command) free (hack->command);
1231 hack->command = strdup (command);
1234 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1235 blurb(), hack->name, command);
1241 const char *ov = hack->visual;
1242 if (!ov || !*ov) ov = "any";
1243 if (!*visual) visual = "any";
1244 if (!!strcasecmp (visual, ov))
1246 if (hack->visual) free (hack->visual);
1247 hack->visual = strdup (visual);
1250 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1251 blurb(), hack->name, visual);
1259 /* Helper for the text fields that contain time specifications:
1260 this parses the text, and does error checking.
1263 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1268 if (!sec_p || strchr (line, ':'))
1269 value = parse_time ((char *) line, sec_p, True);
1273 if (sscanf (line, "%d%c", &value, &c) != 1)
1279 value *= 1000; /* Time measures in microseconds */
1285 "Unparsable time format: \"%s\"\n"),
1287 warning_dialog (s->toplevel_widget, b, False, 100);
1296 directory_p (const char *path)
1299 if (!path || !*path)
1301 else if (stat (path, &st))
1303 else if (!S_ISDIR (st.st_mode))
1310 file_p (const char *path)
1313 if (!path || !*path)
1315 else if (stat (path, &st))
1317 else if (S_ISDIR (st.st_mode))
1324 normalize_directory (const char *path)
1328 if (!path || !*path) return 0;
1330 p2 = (char *) malloc (L + 2);
1332 if (p2[L-1] == '/') /* remove trailing slash */
1335 for (s = p2; s && *s; s++)
1338 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1339 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1342 while (s0 > p2 && s0[-1] != '/')
1352 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1353 strcpy (s, s+2), s--;
1354 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1358 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1359 while (s[0] == '/' && s[1] == '/')
1362 /* and strip trailing whitespace for good measure. */
1364 while (isspace(p2[L-1]))
1377 } FlushForeachClosure;
1380 flush_checkbox (GtkTreeModel *model,
1385 FlushForeachClosure *closure = data;
1388 gtk_tree_model_get (model, iter,
1389 COL_ENABLED, &checked,
1392 if (flush_changes (closure->s, closure->i,
1394 *closure->changed = True;
1398 /* don't remove row */
1402 #endif /* HAVE_GTK2 */
1404 /* Flush out any changes made in the main dialog window (where changes
1405 take place immediately: clicking on a checkbox causes the init file
1406 to be written right away.)
1409 flush_dialog_changes_and_save (state *s)
1411 saver_preferences *p = &s->prefs;
1412 saver_preferences P2, *p2 = &P2;
1414 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1415 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1416 FlushForeachClosure closure;
1417 #else /* !HAVE_GTK2 */
1418 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1419 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1421 #endif /* !HAVE_GTK2 */
1423 Bool changed = False;
1426 if (s->saving_p) return False;
1431 /* Flush any checkbox changes in the list down into the prefs struct.
1435 closure.changed = &changed;
1437 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1439 #else /* !HAVE_GTK2 */
1441 for (i = 0; kids; kids = kids->next, i++)
1443 GtkWidget *line = GTK_WIDGET (kids->data);
1444 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1445 GtkWidget *line_check =
1446 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1448 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1450 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1453 #endif /* ~HAVE_GTK2 */
1455 /* Flush the non-hack-specific settings down into the prefs struct.
1458 # define SECONDS(FIELD,NAME) \
1459 w = name_to_widget (s, (NAME)); \
1460 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1462 # define MINUTES(FIELD,NAME) \
1463 w = name_to_widget (s, (NAME)); \
1464 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1466 # define CHECKBOX(FIELD,NAME) \
1467 w = name_to_widget (s, (NAME)); \
1468 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1470 # define PATHNAME(FIELD,NAME) \
1471 w = name_to_widget (s, (NAME)); \
1472 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1474 # define TEXT(FIELD,NAME) \
1475 w = name_to_widget (s, (NAME)); \
1476 (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1478 MINUTES (&p2->timeout, "timeout_spinbutton");
1479 MINUTES (&p2->cycle, "cycle_spinbutton");
1480 CHECKBOX (p2->lock_p, "lock_button");
1481 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1483 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1484 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1485 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1486 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1488 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1489 CHECKBOX (p2->grab_video_p, "grab_video_button");
1490 CHECKBOX (p2->random_image_p, "grab_image_button");
1491 PATHNAME (p2->image_directory, "image_text");
1494 CHECKBOX (p2->verbose_p, "verbose_button");
1495 CHECKBOX (p2->capture_stderr_p, "capture_button");
1496 CHECKBOX (p2->splash_p, "splash_button");
1501 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1502 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1503 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1504 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1505 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1506 TEXT (p2->text_literal, "text_entry");
1507 PATHNAME (p2->text_file, "text_file_entry");
1508 PATHNAME (p2->text_program, "text_program_entry");
1509 PATHNAME (p2->text_program, "text_program_entry");
1510 TEXT (p2->text_url, "text_url_entry");
1513 CHECKBOX (p2->install_cmap_p, "install_button");
1514 CHECKBOX (p2->fade_p, "fade_button");
1515 CHECKBOX (p2->unfade_p, "unfade_button");
1516 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1524 /* Warn if the image directory doesn't exist.
1526 if (p2->image_directory &&
1527 *p2->image_directory &&
1528 !directory_p (p2->image_directory))
1531 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1532 p2->image_directory);
1533 warning_dialog (s->toplevel_widget, b, False, 100);
1537 /* Map the mode menu to `saver_mode' enum values. */
1539 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1540 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1541 GtkWidget *selected = gtk_menu_get_active (menu);
1542 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1543 int menu_elt = g_list_index (kids, (gpointer) selected);
1544 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1545 p2->mode = mode_menu_order[menu_elt];
1548 if (p2->mode == ONE_HACK)
1550 int list_elt = selected_list_element (s);
1551 p2->selected_hack = (list_elt >= 0
1552 ? s->list_elt_to_hack_number[list_elt]
1556 # define COPY(field, name) \
1557 if (p->field != p2->field) { \
1560 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1562 p->field = p2->field
1565 COPY(selected_hack, "selected_hack");
1567 COPY(timeout, "timeout");
1568 COPY(cycle, "cycle");
1569 COPY(lock_p, "lock_p");
1570 COPY(lock_timeout, "lock_timeout");
1572 COPY(dpms_enabled_p, "dpms_enabled_p");
1573 COPY(dpms_standby, "dpms_standby");
1574 COPY(dpms_suspend, "dpms_suspend");
1575 COPY(dpms_off, "dpms_off");
1578 COPY(verbose_p, "verbose_p");
1579 COPY(capture_stderr_p, "capture_stderr_p");
1580 COPY(splash_p, "splash_p");
1583 COPY(tmode, "tmode");
1585 COPY(install_cmap_p, "install_cmap_p");
1586 COPY(fade_p, "fade_p");
1587 COPY(unfade_p, "unfade_p");
1588 COPY(fade_seconds, "fade_seconds");
1590 COPY(grab_desktop_p, "grab_desktop_p");
1591 COPY(grab_video_p, "grab_video_p");
1592 COPY(random_image_p, "random_image_p");
1596 # define COPYSTR(FIELD,NAME) \
1599 strcmp(p->FIELD, p2->FIELD)) \
1603 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1605 if (p->FIELD && p->FIELD != p2->FIELD) \
1607 p->FIELD = p2->FIELD; \
1610 COPYSTR(image_directory, "image_directory");
1611 COPYSTR(text_literal, "text_literal");
1612 COPYSTR(text_file, "text_file");
1613 COPYSTR(text_program, "text_program");
1614 COPYSTR(text_url, "text_url");
1617 populate_prefs_page (s);
1621 Display *dpy = GDK_DISPLAY();
1622 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1623 sync_server_dpms_settings (dpy, enabled_p,
1624 p->dpms_standby / 1000,
1625 p->dpms_suspend / 1000,
1629 changed = demo_write_init_file (s, p);
1632 s->saving_p = False;
1637 /* Flush out any changes made in the popup dialog box (where changes
1638 take place only when the OK button is clicked.)
1641 flush_popup_changes_and_save (state *s)
1643 Bool changed = False;
1644 saver_preferences *p = &s->prefs;
1645 int list_elt = selected_list_element (s);
1647 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1648 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1650 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1651 const char *command = gtk_entry_get_text (cmd);
1656 if (s->saving_p) return False;
1662 if (maybe_reload_init_file (s) != 0)
1668 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1670 if (!strcasecmp (visual, "")) visual = "";
1671 else if (!strcasecmp (visual, "any")) visual = "";
1672 else if (!strcasecmp (visual, "default")) visual = "Default";
1673 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1674 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1675 else if (!strcasecmp (visual, "best")) visual = "Best";
1676 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1677 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1678 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1679 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1680 else if (!strcasecmp (visual, "color")) visual = "Color";
1681 else if (!strcasecmp (visual, "gl")) visual = "GL";
1682 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1683 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1684 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1685 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1686 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1687 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1688 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1689 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1690 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1693 gdk_beep (); /* unparsable */
1695 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1698 changed = flush_changes (s, list_elt, -1, command, visual);
1701 changed = demo_write_init_file (s, p);
1703 /* Do this to re-launch the hack if (and only if) the command line
1705 populate_demo_window (s, selected_list_element (s));
1709 s->saving_p = False;
1714 G_MODULE_EXPORT void
1715 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1717 state *s = global_state_kludge; /* I hate C so much... */
1718 if (! s->initializing_p)
1720 s->initializing_p = True;
1721 flush_dialog_changes_and_save (s);
1722 s->initializing_p = False;
1726 G_MODULE_EXPORT gboolean
1727 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1729 pref_changed_cb (widget, user_data);
1733 /* Callback on menu items in the "mode" options menu.
1735 G_MODULE_EXPORT void
1736 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1738 state *s = (state *) user_data;
1739 saver_preferences *p = &s->prefs;
1740 GtkWidget *list = name_to_widget (s, "list");
1743 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1745 saver_mode new_mode;
1749 if (menu_items->data == widget)
1752 menu_items = menu_items->next;
1754 if (!menu_items) abort();
1756 new_mode = mode_menu_order[menu_index];
1758 /* Keep the same list element displayed as before; except if we're
1759 switching *to* "one screensaver" mode from any other mode, set
1760 "the one" to be that which is currently selected.
1762 list_elt = selected_list_element (s);
1763 if (new_mode == ONE_HACK)
1764 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1767 saver_mode old_mode = p->mode;
1769 populate_demo_window (s, list_elt);
1770 force_list_select_item (s, list, list_elt, True);
1771 p->mode = old_mode; /* put it back, so the init file gets written */
1774 pref_changed_cb (widget, user_data);
1778 G_MODULE_EXPORT void
1779 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1780 gint page_num, gpointer user_data)
1782 state *s = global_state_kludge; /* I hate C so much... */
1783 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1785 /* If we're switching to page 0, schedule the current hack to be run.
1786 Otherwise, schedule it to stop. */
1788 populate_demo_window (s, selected_list_element (s));
1790 schedule_preview (s, 0);
1795 list_activated_cb (GtkTreeView *list,
1797 GtkTreeViewColumn *column,
1804 STFU g_return_if_fail (!gdk_pointer_is_grabbed ());
1806 str = gtk_tree_path_to_string (path);
1807 list_elt = strtol (str, NULL, 10);
1811 run_hack (s, list_elt, True);
1815 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1817 state *s = (state *)data;
1818 GtkTreeModel *model;
1824 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1827 path = gtk_tree_model_get_path (model, &iter);
1828 str = gtk_tree_path_to_string (path);
1829 list_elt = strtol (str, NULL, 10);
1831 gtk_tree_path_free (path);
1834 populate_demo_window (s, list_elt);
1835 flush_dialog_changes_and_save (s);
1837 /* Re-populate the Settings window any time a new item is selected
1838 in the list, in case both windows are currently visible.
1840 populate_popup_window (s);
1843 #else /* !HAVE_GTK2 */
1845 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1846 list_select_cb that comes in
1847 *after* we've double-clicked.
1851 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1854 state *s = (state *) data;
1855 if (event->type == GDK_2BUTTON_PRESS)
1857 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1858 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1860 last_doubleclick_time = time ((time_t *) 0);
1863 run_hack (s, list_elt, True);
1871 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1873 state *s = (state *) data;
1874 time_t now = time ((time_t *) 0);
1876 if (now >= last_doubleclick_time + 2)
1878 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1879 populate_demo_window (s, list_elt);
1880 flush_dialog_changes_and_save (s);
1885 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1887 state *s = (state *) data;
1888 populate_demo_window (s, -1);
1889 flush_dialog_changes_and_save (s);
1892 #endif /* !HAVE_GTK2 */
1895 /* Called when the checkboxes that are in the left column of the
1896 scrolling list are clicked. This both populates the right pane
1897 (just as clicking on the label (really, listitem) does) and
1898 also syncs this checkbox with the right pane Enabled checkbox.
1903 GtkCellRendererToggle *toggle,
1905 #else /* !HAVE_GTK2 */
1907 #endif /* !HAVE_GTK2 */
1910 state *s = (state *) data;
1913 GtkScrolledWindow *scroller =
1914 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1915 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1916 GtkTreeModel *model = gtk_tree_view_get_model (list);
1917 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1920 #else /* !HAVE_GTK2 */
1921 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1922 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1924 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1925 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1926 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1927 #endif /* !HAVE_GTK2 */
1934 if (!gtk_tree_model_get_iter (model, &iter, path))
1936 g_warning ("bad path: %s", path_string);
1939 gtk_tree_path_free (path);
1941 gtk_tree_model_get (model, &iter,
1942 COL_ENABLED, &active,
1945 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1946 COL_ENABLED, !active,
1949 list_elt = strtol (path_string, NULL, 10);
1950 #else /* !HAVE_GTK2 */
1951 list_elt = gtk_list_child_position (list, line);
1952 #endif /* !HAVE_GTK2 */
1954 /* remember previous scroll position of the top of the list */
1955 adj = gtk_scrolled_window_get_vadjustment (scroller);
1956 scroll_top = adj->value;
1958 flush_dialog_changes_and_save (s);
1959 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1960 populate_demo_window (s, list_elt);
1962 /* restore the previous scroll position of the top of the list.
1963 this is weak, but I don't really know why it's moving... */
1964 gtk_adjustment_set_value (adj, scroll_top);
1970 GtkFileSelection *widget;
1971 } file_selection_data;
1976 store_image_directory (GtkWidget *button, gpointer user_data)
1978 file_selection_data *fsd = (file_selection_data *) user_data;
1979 state *s = fsd->state;
1980 GtkFileSelection *selector = fsd->widget;
1981 GtkWidget *top = s->toplevel_widget;
1982 saver_preferences *p = &s->prefs;
1983 const char *path = gtk_file_selection_get_filename (selector);
1985 if (p->image_directory && !strcmp(p->image_directory, path))
1986 return; /* no change */
1988 if (!directory_p (path))
1991 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1992 warning_dialog (GTK_WIDGET (top), b, False, 100);
1996 if (p->image_directory) free (p->image_directory);
1997 p->image_directory = normalize_directory (path);
1999 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2000 (p->image_directory ? p->image_directory : ""));
2001 demo_write_init_file (s, p);
2006 store_text_file (GtkWidget *button, gpointer user_data)
2008 file_selection_data *fsd = (file_selection_data *) user_data;
2009 state *s = fsd->state;
2010 GtkFileSelection *selector = fsd->widget;
2011 GtkWidget *top = s->toplevel_widget;
2012 saver_preferences *p = &s->prefs;
2013 const char *path = gtk_file_selection_get_filename (selector);
2015 if (p->text_file && !strcmp(p->text_file, path))
2016 return; /* no change */
2021 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2022 warning_dialog (GTK_WIDGET (top), b, False, 100);
2026 if (p->text_file) free (p->text_file);
2027 p->text_file = normalize_directory (path);
2029 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2030 (p->text_file ? p->text_file : ""));
2031 demo_write_init_file (s, p);
2036 store_text_program (GtkWidget *button, gpointer user_data)
2038 file_selection_data *fsd = (file_selection_data *) user_data;
2039 state *s = fsd->state;
2040 GtkFileSelection *selector = fsd->widget;
2041 /*GtkWidget *top = s->toplevel_widget;*/
2042 saver_preferences *p = &s->prefs;
2043 const char *path = gtk_file_selection_get_filename (selector);
2045 if (p->text_program && !strcmp(p->text_program, path))
2046 return; /* no change */
2052 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2053 warning_dialog (GTK_WIDGET (top), b, False, 100);
2058 if (p->text_program) free (p->text_program);
2059 p->text_program = normalize_directory (path);
2061 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2062 (p->text_program ? p->text_program : ""));
2063 demo_write_init_file (s, p);
2069 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2071 file_selection_data *fsd = (file_selection_data *) user_data;
2072 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2076 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2078 browse_image_dir_cancel (button, user_data);
2079 store_image_directory (button, user_data);
2083 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2085 browse_image_dir_cancel (button, user_data);
2086 store_text_file (button, user_data);
2090 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2092 browse_image_dir_cancel (button, user_data);
2093 store_text_program (button, user_data);
2097 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2099 browse_image_dir_cancel (widget, user_data);
2103 G_MODULE_EXPORT void
2104 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2106 state *s = global_state_kludge; /* I hate C so much... */
2107 saver_preferences *p = &s->prefs;
2108 static file_selection_data *fsd = 0;
2110 GtkFileSelection *selector = GTK_FILE_SELECTION(
2111 gtk_file_selection_new ("Please select the image directory."));
2114 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2116 fsd->widget = selector;
2119 if (p->image_directory && *p->image_directory)
2120 gtk_file_selection_set_filename (selector, p->image_directory);
2122 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2123 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2125 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2126 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2128 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2129 GTK_SIGNAL_FUNC (browse_image_dir_close),
2132 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2134 gtk_window_set_modal (GTK_WINDOW (selector), True);
2135 gtk_widget_show (GTK_WIDGET (selector));
2139 G_MODULE_EXPORT void
2140 browse_text_file_cb (GtkButton *button, gpointer user_data)
2142 state *s = global_state_kludge; /* I hate C so much... */
2143 saver_preferences *p = &s->prefs;
2144 static file_selection_data *fsd = 0;
2146 GtkFileSelection *selector = GTK_FILE_SELECTION(
2147 gtk_file_selection_new ("Please select a text file."));
2150 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2152 fsd->widget = selector;
2155 if (p->text_file && *p->text_file)
2156 gtk_file_selection_set_filename (selector, p->text_file);
2158 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2159 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2161 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2162 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2164 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2165 GTK_SIGNAL_FUNC (browse_image_dir_close),
2168 gtk_window_set_modal (GTK_WINDOW (selector), True);
2169 gtk_widget_show (GTK_WIDGET (selector));
2173 G_MODULE_EXPORT void
2174 browse_text_program_cb (GtkButton *button, gpointer user_data)
2176 state *s = global_state_kludge; /* I hate C so much... */
2177 saver_preferences *p = &s->prefs;
2178 static file_selection_data *fsd = 0;
2180 GtkFileSelection *selector = GTK_FILE_SELECTION(
2181 gtk_file_selection_new ("Please select a text-generating program."));
2184 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2186 fsd->widget = selector;
2189 if (p->text_program && *p->text_program)
2190 gtk_file_selection_set_filename (selector, p->text_program);
2192 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2193 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2195 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2196 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2198 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2199 GTK_SIGNAL_FUNC (browse_image_dir_close),
2202 gtk_window_set_modal (GTK_WINDOW (selector), True);
2203 gtk_widget_show (GTK_WIDGET (selector));
2210 G_MODULE_EXPORT void
2211 settings_cb (GtkButton *button, gpointer user_data)
2213 state *s = global_state_kludge; /* I hate C so much... */
2214 int list_elt = selected_list_element (s);
2216 populate_demo_window (s, list_elt); /* reset the widget */
2217 populate_popup_window (s); /* create UI on popup window */
2218 gtk_widget_show (s->popup_widget);
2222 settings_sync_cmd_text (state *s)
2225 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2226 char *cmd_line = get_configurator_command_line (s->cdata);
2227 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2228 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2230 # endif /* HAVE_XML */
2233 G_MODULE_EXPORT void
2234 settings_adv_cb (GtkButton *button, gpointer user_data)
2236 state *s = global_state_kludge; /* I hate C so much... */
2237 GtkNotebook *notebook =
2238 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2240 settings_sync_cmd_text (s);
2241 gtk_notebook_set_page (notebook, 1);
2244 G_MODULE_EXPORT void
2245 settings_std_cb (GtkButton *button, gpointer user_data)
2247 state *s = global_state_kludge; /* I hate C so much... */
2248 GtkNotebook *notebook =
2249 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2251 /* Re-create UI to reflect the in-progress command-line settings. */
2252 populate_popup_window (s);
2254 gtk_notebook_set_page (notebook, 0);
2257 G_MODULE_EXPORT void
2258 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2259 gint page_num, gpointer user_data)
2261 state *s = global_state_kludge; /* I hate C so much... */
2262 GtkWidget *adv = name_to_widget (s, "adv_button");
2263 GtkWidget *std = name_to_widget (s, "std_button");
2267 gtk_widget_show (adv);
2268 gtk_widget_hide (std);
2270 else if (page_num == 1)
2272 gtk_widget_hide (adv);
2273 gtk_widget_show (std);
2281 G_MODULE_EXPORT void
2282 settings_cancel_cb (GtkButton *button, gpointer user_data)
2284 state *s = global_state_kludge; /* I hate C so much... */
2285 gtk_widget_hide (s->popup_widget);
2288 G_MODULE_EXPORT void
2289 settings_ok_cb (GtkButton *button, gpointer user_data)
2291 state *s = global_state_kludge; /* I hate C so much... */
2292 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2293 int page = gtk_notebook_get_current_page (notebook);
2296 /* Regenerate the command-line from the widget contents before saving.
2297 But don't do this if we're looking at the command-line page already,
2298 or we will blow away what they typed... */
2299 settings_sync_cmd_text (s);
2301 flush_popup_changes_and_save (s);
2302 gtk_widget_hide (s->popup_widget);
2306 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2308 state *s = (state *) data;
2309 settings_cancel_cb (0, (gpointer) s);
2315 /* Populating the various widgets
2319 /* Returns the number of the last hack run by the server.
2322 server_current_hack (void)
2326 unsigned long nitems, bytesafter;
2327 unsigned char *dataP = 0;
2328 Display *dpy = GDK_DISPLAY();
2329 int hack_number = -1;
2331 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2332 XA_SCREENSAVER_STATUS,
2333 0, 3, False, XA_INTEGER,
2334 &type, &format, &nitems, &bytesafter,
2337 && type == XA_INTEGER
2341 CARD32 *data = (CARD32 *) dataP;
2342 hack_number = (int) data[2] - 1;
2345 if (dataP) XFree (dataP);
2351 /* Finds the number of the last hack that was run, and makes that item be
2352 selected by default.
2355 scroll_to_current_hack (state *s)
2357 saver_preferences *p = &s->prefs;
2358 int hack_number = -1;
2360 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2361 hack_number = p->selected_hack;
2362 if (hack_number < 0) /* otherwise, use the last-run */
2363 hack_number = server_current_hack ();
2364 if (hack_number < 0) /* failing that, last "one mode" */
2365 hack_number = p->selected_hack;
2366 if (hack_number < 0) /* failing that, newest hack. */
2368 /* We should only get here if the user does not have a .xscreensaver
2369 file, and the screen has not been blanked with a hack since X
2370 started up: in other words, this is probably a fresh install.
2372 Instead of just defaulting to hack #0 (in either "programs" or
2373 "alphabetical" order) let's try to default to the last runnable
2374 hack in the "programs" list: this is probably the hack that was
2375 most recently added to the xscreensaver distribution (and so
2376 it's probably the currently-coolest one!)
2378 hack_number = p->screenhacks_count-1;
2379 while (hack_number > 0 &&
2380 ! (s->hacks_available_p[hack_number] &&
2381 p->screenhacks[hack_number]->enabled_p))
2385 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2387 int list_elt = s->hack_number_to_list_elt[hack_number];
2388 GtkWidget *list = name_to_widget (s, "list");
2389 force_list_select_item (s, list, list_elt, True);
2390 populate_demo_window (s, list_elt);
2396 populate_hack_list (state *s)
2398 Display *dpy = GDK_DISPLAY();
2400 saver_preferences *p = &s->prefs;
2401 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2402 GtkListStore *model;
2403 GtkTreeSelection *selection;
2404 GtkCellRenderer *ren;
2408 g_object_get (G_OBJECT (list),
2413 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2414 g_object_set (G_OBJECT (list), "model", model, NULL);
2415 g_object_unref (model);
2417 ren = gtk_cell_renderer_toggle_new ();
2418 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2420 "active", COL_ENABLED,
2423 g_signal_connect (ren, "toggled",
2424 G_CALLBACK (list_checkbox_cb),
2427 ren = gtk_cell_renderer_text_new ();
2428 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2429 _("Screen Saver"), ren,
2433 g_signal_connect_after (list, "row_activated",
2434 G_CALLBACK (list_activated_cb),
2437 selection = gtk_tree_view_get_selection (list);
2438 g_signal_connect (selection, "changed",
2439 G_CALLBACK (list_select_changed_cb),
2444 for (i = 0; i < s->list_count; i++)
2446 int hack_number = s->list_elt_to_hack_number[i];
2447 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2449 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2451 if (!hack) continue;
2453 /* If we're to suppress uninstalled hacks, check $PATH now. */
2454 if (p->ignore_uninstalled_p && !available_p)
2457 pretty_name = (hack->name
2458 ? strdup (hack->name)
2459 : make_hack_name (dpy, hack->command));
2463 /* Make the text foreground be the color of insensitive widgets
2464 (but don't actually make it be insensitive, since we still
2465 want to be able to click on it.)
2467 GtkStyle *style = GTK_WIDGET (list)->style;
2468 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2469 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2470 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2472 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2473 /* " background=\"#%02X%02X%02X\"" */
2475 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2476 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2482 gtk_list_store_append (model, &iter);
2483 gtk_list_store_set (model, &iter,
2484 COL_ENABLED, hack->enabled_p,
2485 COL_NAME, pretty_name,
2490 #else /* !HAVE_GTK2 */
2492 saver_preferences *p = &s->prefs;
2493 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2495 for (i = 0; i < s->list_count; i++)
2497 int hack_number = s->list_elt_to_hack_number[i];
2498 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2500 /* A GtkList must contain only GtkListItems, but those can contain
2501 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2502 and a Label. We handle single and double click events on the
2503 line itself, for clicking on the text, but the interior checkbox
2504 also handles its own events.
2507 GtkWidget *line_hbox;
2508 GtkWidget *line_check;
2509 GtkWidget *line_label;
2511 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2513 if (!hack) continue;
2515 /* If we're to suppress uninstalled hacks, check $PATH now. */
2516 if (p->ignore_uninstalled_p && !available_p)
2519 pretty_name = (hack->name
2520 ? strdup (hack->name)
2521 : make_hack_name (hack->command));
2523 line = gtk_list_item_new ();
2524 line_hbox = gtk_hbox_new (FALSE, 0);
2525 line_check = gtk_check_button_new ();
2526 line_label = gtk_label_new (pretty_name);
2528 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2529 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2530 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2532 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2534 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2536 gtk_widget_show (line_check);
2537 gtk_widget_show (line_label);
2538 gtk_widget_show (line_hbox);
2539 gtk_widget_show (line);
2543 gtk_container_add (GTK_CONTAINER (list), line);
2544 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2545 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2548 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2549 GTK_SIGNAL_FUNC (list_checkbox_cb),
2552 gtk_widget_show (line);
2556 /* Make the widget be colored like insensitive widgets
2557 (but don't actually make it be insensitive, since we
2558 still want to be able to click on it.)
2560 GtkRcStyle *rc_style;
2563 gtk_widget_realize (GTK_WIDGET (line_label));
2565 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2566 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2568 rc_style = gtk_rc_style_new ();
2569 rc_style->fg[GTK_STATE_NORMAL] = fg;
2570 rc_style->bg[GTK_STATE_NORMAL] = bg;
2571 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2573 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2574 gtk_rc_style_unref (rc_style);
2578 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2579 GTK_SIGNAL_FUNC (list_select_cb),
2581 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2582 GTK_SIGNAL_FUNC (list_unselect_cb),
2584 #endif /* !HAVE_GTK2 */
2588 update_list_sensitivity (state *s)
2590 saver_preferences *p = &s->prefs;
2591 Bool sensitive = (p->mode == RANDOM_HACKS ||
2592 p->mode == RANDOM_HACKS_SAME ||
2593 p->mode == ONE_HACK);
2594 Bool checkable = (p->mode == RANDOM_HACKS ||
2595 p->mode == RANDOM_HACKS_SAME);
2596 Bool blankable = (p->mode != DONT_BLANK);
2599 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2600 GtkWidget *use = name_to_widget (s, "use_col_frame");
2601 #endif /* HAVE_GTK2 */
2602 GtkWidget *scroller = name_to_widget (s, "scroller");
2603 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2604 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2607 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2608 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2609 #else /* !HAVE_GTK2 */
2610 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2611 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2613 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2614 #endif /* !HAVE_GTK2 */
2615 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2616 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2618 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2621 gtk_tree_view_column_set_visible (use, checkable);
2622 #else /* !HAVE_GTK2 */
2624 gtk_widget_show (use); /* the "Use" column header */
2626 gtk_widget_hide (use);
2630 GtkBin *line = GTK_BIN (kids->data);
2631 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2632 GtkWidget *line_check =
2633 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2636 gtk_widget_show (line_check);
2638 gtk_widget_hide (line_check);
2642 #endif /* !HAVE_GTK2 */
2647 populate_prefs_page (state *s)
2649 saver_preferences *p = &s->prefs;
2651 Bool can_lock_p = True;
2653 /* Disable all the "lock" controls if locking support was not provided
2654 at compile-time, or if running on MacOS. */
2655 # if defined(NO_LOCKING) || defined(__APPLE__)
2660 /* If there is only one screen, the mode menu contains
2661 "random" but not "random-same".
2663 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2664 p->mode = RANDOM_HACKS;
2667 /* The file supports timeouts of less than a minute, but the GUI does
2668 not, so throttle the values to be at least one minute (since "0" is
2669 a bad rounding choice...)
2671 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2674 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2677 # define FMT_MINUTES(NAME,N) \
2678 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2680 # define FMT_SECONDS(NAME,N) \
2681 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2683 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2684 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2685 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2686 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2687 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2688 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2689 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2694 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2695 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2698 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2700 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2701 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2702 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2704 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2705 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2706 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2707 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2708 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2709 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2710 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2714 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2715 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2716 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2717 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2718 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2721 # undef TOGGLE_ACTIVE
2723 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2724 (p->image_directory ? p->image_directory : ""));
2725 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2727 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2730 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2731 (p->text_literal ? p->text_literal : ""));
2732 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2733 (p->text_file ? p->text_file : ""));
2734 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2735 (p->text_program ? p->text_program : ""));
2736 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2737 (p->text_url ? p->text_url : ""));
2739 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2740 p->tmode == TEXT_LITERAL);
2741 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2742 p->tmode == TEXT_FILE);
2743 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2744 p->tmode == TEXT_FILE);
2745 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2746 p->tmode == TEXT_PROGRAM);
2747 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2748 p->tmode == TEXT_PROGRAM);
2749 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2750 p->tmode == TEXT_URL);
2753 /* Map the `saver_mode' enum to mode menu to values. */
2755 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2758 for (i = 0; i < countof(mode_menu_order); i++)
2759 if (mode_menu_order[i] == p->mode)
2761 gtk_option_menu_set_history (opt, i);
2762 update_list_sensitivity (s);
2766 Bool found_any_writable_cells = False;
2767 Bool fading_possible = False;
2768 Bool dpms_supported = False;
2770 Display *dpy = GDK_DISPLAY();
2771 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2773 for (i = 0; i < nscreens; i++)
2775 Screen *s = ScreenOfDisplay (dpy, i);
2776 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2778 found_any_writable_cells = True;
2783 fading_possible = found_any_writable_cells;
2784 #ifdef HAVE_XF86VMODE_GAMMA
2785 fading_possible = True;
2788 #ifdef HAVE_DPMS_EXTENSION
2790 int op = 0, event = 0, error = 0;
2791 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2792 dpms_supported = True;
2794 #endif /* HAVE_DPMS_EXTENSION */
2797 # define SENSITIZE(NAME,SENSITIVEP) \
2798 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2800 /* Blanking and Locking
2802 SENSITIZE ("lock_button", can_lock_p);
2803 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2804 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2808 SENSITIZE ("dpms_frame", dpms_supported);
2809 SENSITIZE ("dpms_button", dpms_supported);
2810 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2811 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2812 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2813 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2814 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2815 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2816 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2817 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2818 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2822 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2823 SENSITIZE ("install_button", found_any_writable_cells);
2824 SENSITIZE ("fade_button", fading_possible);
2825 SENSITIZE ("unfade_button", fading_possible);
2827 SENSITIZE ("fade_label", (fading_possible &&
2828 (p->fade_p || p->unfade_p)));
2829 SENSITIZE ("fade_spinbutton", (fading_possible &&
2830 (p->fade_p || p->unfade_p)));
2838 populate_popup_window (state *s)
2840 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2841 char *doc_string = 0;
2843 /* #### not in Gtk 1.2
2844 gtk_label_set_selectable (doc);
2850 free_conf_data (s->cdata);
2855 saver_preferences *p = &s->prefs;
2856 int list_elt = selected_list_element (s);
2857 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2858 ? s->list_elt_to_hack_number[list_elt]
2860 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2863 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2864 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2865 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2866 s->cdata = load_configurator (cmd_line, s->debug_p);
2867 if (s->cdata && s->cdata->widget)
2868 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2873 doc_string = (s->cdata
2874 ? s->cdata->description
2876 # else /* !HAVE_XML */
2877 doc_string = _("Descriptions not available: no XML support compiled in.");
2878 # endif /* !HAVE_XML */
2880 gtk_label_set_text (doc, (doc_string
2882 : _("No description available.")));
2887 sensitize_demo_widgets (state *s, Bool sensitive_p)
2889 const char *names[] = { "demo", "settings",
2890 "cmd_label", "cmd_text", "manual",
2891 "visual", "visual_combo" };
2893 for (i = 0; i < countof(names); i++)
2895 GtkWidget *w = name_to_widget (s, names[i]);
2896 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2902 sensitize_menu_items (state *s, Bool force_p)
2904 static Bool running_p = False;
2905 static time_t last_checked = 0;
2906 time_t now = time ((time_t *) 0);
2907 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
2911 if (force_p || now > last_checked + 10) /* check every 10 seconds */
2913 running_p = xscreensaver_running_p (s);
2914 last_checked = time ((time_t *) 0);
2917 for (i = 0; i < countof(names); i++)
2919 GtkWidget *w = name_to_widget (s, names[i]);
2920 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
2925 /* When the File menu is de-posted after a "Restart Daemon" command,
2926 the window underneath doesn't repaint for some reason. I guess this
2927 is a bug in exposure handling in GTK or GDK. This works around it.
2930 force_dialog_repaint (state *s)
2933 /* Tell GDK to invalidate and repaint the whole window.
2935 GdkWindow *w = s->toplevel_widget->window;
2936 GdkRegion *region = gdk_region_new ();
2938 rect.x = rect.y = 0;
2939 rect.width = rect.height = 32767;
2940 gdk_region_union_with_rect (region, &rect);
2941 gdk_window_invalidate_region (w, region, True);
2942 gdk_region_destroy (region);
2943 gdk_window_process_updates (w, True);
2945 /* Force the server to send an exposure event by creating and then
2946 destroying a window as a child of the top level shell.
2948 Display *dpy = GDK_DISPLAY();
2949 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
2951 XWindowAttributes xgwa;
2952 XGetWindowAttributes (dpy, parent, &xgwa);
2953 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
2954 XMapRaised (dpy, w);
2955 XDestroyWindow (dpy, w);
2961 /* Even though we've given these text fields a maximum number of characters,
2962 their default size is still about 30 characters wide -- so measure out
2963 a string in their font, and resize them to just fit that.
2966 fix_text_entry_sizes (state *s)
2970 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2971 const char * const spinbuttons[] = {
2972 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2973 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2974 "dpms_off_spinbutton",
2975 "-fade_spinbutton" };
2979 for (i = 0; i < countof(spinbuttons); i++)
2981 const char *n = spinbuttons[i];
2983 while (*n == '-') n++, cols--;
2984 w = GTK_WIDGET (name_to_widget (s, n));
2985 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2986 gtk_widget_set_usize (w, width, -2);
2989 /* Now fix the width of the combo box.
2991 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2992 w = GTK_COMBO (w)->entry;
2993 width = gdk_string_width (w->style->font, "PseudoColor___");
2994 gtk_widget_set_usize (w, width, -2);
2996 /* Now fix the width of the file entry text.
2998 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2999 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3000 gtk_widget_set_usize (w, width, -2);
3002 /* Now fix the width of the command line text.
3004 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3005 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3006 gtk_widget_set_usize (w, width, -2);
3010 /* Now fix the height of the list widget:
3011 make it default to being around 10 text-lines high instead of 4.
3013 w = GTK_WIDGET (name_to_widget (s, "list"));
3017 int leading = 3; /* approximate is ok... */
3021 PangoFontMetrics *pain =
3022 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3023 w->style->font_desc,
3024 gtk_get_default_language ());
3025 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3026 pango_font_metrics_get_descent (pain));
3027 #else /* !HAVE_GTK2 */
3028 height = w->style->font->ascent + w->style->font->descent;
3029 #endif /* !HAVE_GTK2 */
3033 height += border * 2;
3034 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3035 gtk_widget_set_usize (w, -2, height);
3042 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3045 static char *up_arrow_xpm[] = {
3068 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3069 the end of the array (Gtk 1.2.5.) */
3070 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3071 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3074 static char *down_arrow_xpm[] = {
3097 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3098 the end of the array (Gtk 1.2.5.) */
3099 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3100 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3104 pixmapify_button (state *s, int down_p)
3108 GtkWidget *pixmapwid;
3112 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3113 style = gtk_widget_get_style (w);
3115 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3116 &style->bg[GTK_STATE_NORMAL],
3118 ? (gchar **) down_arrow_xpm
3119 : (gchar **) up_arrow_xpm));
3120 pixmapwid = gtk_pixmap_new (pixmap, mask);
3121 gtk_widget_show (pixmapwid);
3122 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3123 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3127 map_next_button_cb (GtkWidget *w, gpointer user_data)
3129 state *s = (state *) user_data;
3130 pixmapify_button (s, 1);
3134 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3136 state *s = (state *) user_data;
3137 pixmapify_button (s, 0);
3139 #endif /* !HAVE_GTK2 */
3143 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3147 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3148 GtkAllocation *allocation,
3152 GtkWidgetAuxInfo *aux_info;
3154 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3156 aux_info->width = allocation->width;
3157 aux_info->height = -2;
3161 gtk_widget_size_request (label, &req);
3164 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3167 eschew_gtk_lossage (GtkLabel *label)
3169 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3170 aux_info->width = GTK_WIDGET (label)->allocation.width;
3171 aux_info->height = -2;
3175 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3177 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3178 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3181 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3183 gtk_widget_queue_resize (GTK_WIDGET (label));
3185 #endif /* !HAVE_GTK2 */
3189 populate_demo_window (state *s, int list_elt)
3191 Display *dpy = GDK_DISPLAY();
3192 saver_preferences *p = &s->prefs;
3195 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3196 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
3197 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3198 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3199 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3201 if (p->mode == BLANK_ONLY)
3204 pretty_name = strdup (_("Blank Screen"));
3205 schedule_preview (s, 0);
3207 else if (p->mode == DONT_BLANK)
3210 pretty_name = strdup (_("Screen Saver Disabled"));
3211 schedule_preview (s, 0);
3215 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3216 ? s->list_elt_to_hack_number[list_elt]
3218 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3222 ? strdup (hack->name)
3223 : make_hack_name (dpy, hack->command))
3227 schedule_preview (s, hack->command);
3229 schedule_preview (s, 0);
3233 pretty_name = strdup (_("Preview"));
3235 gtk_frame_set_label (frame1, _(pretty_name));
3236 gtk_frame_set_label (frame2, _(pretty_name));
3238 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3239 gtk_entry_set_position (cmd, 0);
3243 sprintf (title, _("%s: %.100s Settings"),
3244 progclass, (pretty_name ? pretty_name : "???"));
3245 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3248 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3250 ? (hack->visual && *hack->visual
3255 sensitize_demo_widgets (s, (hack ? True : False));
3257 if (pretty_name) free (pretty_name);
3259 ensure_selected_item_visible (list);
3261 s->_selected_list_element = list_elt;
3266 widget_deleter (GtkWidget *widget, gpointer data)
3268 /* #### Well, I want to destroy these widgets, but if I do that, they get
3269 referenced again, and eventually I get a SEGV. So instead of
3270 destroying them, I'll just hide them, and leak a bunch of memory
3271 every time the disk file changes. Go go go Gtk!
3273 #### Ok, that's a lie, I get a crash even if I just hide the widget
3274 and don't ever delete it. Fuck!
3277 gtk_widget_destroy (widget);
3279 gtk_widget_hide (widget);
3284 static char **sort_hack_cmp_names_kludge;
3286 sort_hack_cmp (const void *a, const void *b)
3292 int aa = *(int *) a;
3293 int bb = *(int *) b;
3294 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3295 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3296 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3302 initialize_sort_map (state *s)
3304 Display *dpy = GDK_DISPLAY();
3305 saver_preferences *p = &s->prefs;
3308 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3309 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3310 if (s->hacks_available_p) free (s->hacks_available_p);
3312 s->list_elt_to_hack_number = (int *)
3313 calloc (sizeof(int), p->screenhacks_count + 1);
3314 s->hack_number_to_list_elt = (int *)
3315 calloc (sizeof(int), p->screenhacks_count + 1);
3316 s->hacks_available_p = (Bool *)
3317 calloc (sizeof(Bool), p->screenhacks_count + 1);
3318 s->total_available = 0;
3320 /* Check which hacks actually exist on $PATH
3322 for (i = 0; i < p->screenhacks_count; i++)
3324 screenhack *hack = p->screenhacks[i];
3325 int on = on_path_p (hack->command) ? 1 : 0;
3326 s->hacks_available_p[i] = on;
3327 s->total_available += on;
3330 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3334 for (i = 0; i < p->screenhacks_count; i++)
3336 if (!p->ignore_uninstalled_p ||
3337 s->hacks_available_p[i])
3338 s->list_elt_to_hack_number[j++] = i;
3342 for (; j < p->screenhacks_count; j++)
3343 s->list_elt_to_hack_number[j] = -1;
3346 /* Generate list of sortable names (once)
3348 sort_hack_cmp_names_kludge = (char **)
3349 calloc (sizeof(char *), p->screenhacks_count);
3350 for (i = 0; i < p->screenhacks_count; i++)
3352 screenhack *hack = p->screenhacks[i];
3353 char *name = (hack->name && *hack->name
3354 ? strdup (hack->name)
3355 : make_hack_name (dpy, hack->command));
3357 for (str = name; *str; str++)
3358 *str = tolower(*str);
3359 sort_hack_cmp_names_kludge[i] = name;
3362 /* Sort list->hack map alphabetically
3364 qsort (s->list_elt_to_hack_number,
3365 p->screenhacks_count,
3366 sizeof(*s->list_elt_to_hack_number),
3371 for (i = 0; i < p->screenhacks_count; i++)
3372 free (sort_hack_cmp_names_kludge[i]);
3373 free (sort_hack_cmp_names_kludge);
3374 sort_hack_cmp_names_kludge = 0;
3376 /* Build inverse table */
3377 for (i = 0; i < p->screenhacks_count; i++)
3379 int n = s->list_elt_to_hack_number[i];
3381 s->hack_number_to_list_elt[n] = i;
3387 maybe_reload_init_file (state *s)
3389 Display *dpy = GDK_DISPLAY();
3390 saver_preferences *p = &s->prefs;
3393 static Bool reentrant_lock = False;
3394 if (reentrant_lock) return 0;
3395 reentrant_lock = True;
3397 if (init_file_changed_p (p))
3399 const char *f = init_file_name();
3404 if (!f || !*f) return 0;
3405 b = (char *) malloc (strlen(f) + 1024);
3408 "file \"%s\" has changed, reloading.\n"),
3410 warning_dialog (s->toplevel_widget, b, False, 100);
3413 load_init_file (dpy, p);
3414 initialize_sort_map (s);
3416 list_elt = selected_list_element (s);
3417 list = name_to_widget (s, "list");
3418 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3419 populate_hack_list (s);
3420 force_list_select_item (s, list, list_elt, True);
3421 populate_prefs_page (s);
3422 populate_demo_window (s, list_elt);
3423 ensure_selected_item_visible (list);
3428 reentrant_lock = False;
3434 /* Making the preview window have the right X visual (so that GL works.)
3437 static Visual *get_best_gl_visual (state *);
3440 x_visual_to_gdk_visual (Visual *xv)
3442 GList *gvs = gdk_list_visuals();
3443 if (!xv) return gdk_visual_get_system();
3444 for (; gvs; gvs = gvs->next)
3446 GdkVisual *gv = (GdkVisual *) gvs->data;
3447 if (xv == GDK_VISUAL_XVISUAL (gv))
3450 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3451 blurb(), (unsigned long) xv->visualid);
3456 clear_preview_window (state *s)
3461 if (!s->toplevel_widget) return; /* very early */
3462 p = name_to_widget (s, "preview");
3465 if (!window) return;
3467 /* Flush the widget background down into the window, in case a subproc
3469 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3470 gdk_window_clear (window);
3473 int list_elt = selected_list_element (s);
3474 int hack_number = (list_elt >= 0
3475 ? s->list_elt_to_hack_number[list_elt]
3477 Bool available_p = (hack_number >= 0
3478 ? s->hacks_available_p [hack_number]
3480 Bool nothing_p = (s->total_available < 5);
3483 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3484 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3485 (s->running_preview_error_p
3486 ? (available_p ? 1 :
3489 #else /* !HAVE_GTK2 */
3490 if (s->running_preview_error_p)
3492 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3493 const char * const lines2[] = { N_("Not"), N_("Installed") };
3494 int nlines = countof(lines1);
3495 int lh = p->style->font->ascent + p->style->font->descent;
3499 const char * const *lines = (available_p ? lines1 : lines2);
3501 gdk_window_get_size (window, &w, &h);
3502 y = (h - (lh * nlines)) / 2;
3503 y += p->style->font->ascent;
3504 for (i = 0; i < nlines; i++)
3506 int sw = gdk_string_width (p->style->font, _(lines[i]));
3507 int x = (w - sw) / 2;
3508 gdk_draw_string (window, p->style->font,
3509 p->style->fg_gc[GTK_STATE_NORMAL],
3514 #endif /* !HAVE_GTK2 */
3522 reset_preview_window (state *s)
3524 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3525 when you kill one and re-start another on the same window. So maybe
3526 it's best to just always destroy and recreate the preview window
3527 when changing hacks, instead of always trying to reuse the same one?
3529 GtkWidget *pr = name_to_widget (s, "preview");
3530 if (GTK_WIDGET_REALIZED (pr))
3532 Window oid = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3534 gtk_widget_hide (pr);
3535 gtk_widget_unrealize (pr);
3536 gtk_widget_realize (pr);
3537 gtk_widget_show (pr);
3538 id = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3540 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3548 fix_preview_visual (state *s)
3550 GtkWidget *widget = name_to_widget (s, "preview");
3551 Visual *xvisual = get_best_gl_visual (s);
3552 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3553 GdkVisual *dvisual = gdk_visual_get_system();
3554 GdkColormap *cmap = (visual == dvisual
3555 ? gdk_colormap_get_system ()
3556 : gdk_colormap_new (visual, False));
3559 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3560 (visual == dvisual ? "default" : "non-default"),
3561 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3563 if (!GTK_WIDGET_REALIZED (widget) ||
3564 gtk_widget_get_visual (widget) != visual)
3566 gtk_widget_unrealize (widget);
3567 gtk_widget_set_visual (widget, visual);
3568 gtk_widget_set_colormap (widget, cmap);
3569 gtk_widget_realize (widget);
3572 /* Set the Widget colors to be white-on-black. */
3574 GdkWindow *window = widget->window;
3575 GtkStyle *style = gtk_style_copy (widget->style);
3576 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3577 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3578 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3579 GdkGC *fgc = gdk_gc_new(window);
3580 GdkGC *bgc = gdk_gc_new(window);
3581 if (!gdk_color_white (cmap, fg)) abort();
3582 if (!gdk_color_black (cmap, bg)) abort();
3583 gdk_gc_set_foreground (fgc, fg);
3584 gdk_gc_set_background (fgc, bg);
3585 gdk_gc_set_foreground (bgc, bg);
3586 gdk_gc_set_background (bgc, fg);
3587 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3588 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3589 gtk_widget_set_style (widget, style);
3591 /* For debugging purposes, put a title on the window (so that
3592 it can be easily found in the output of "xwininfo -tree".)
3594 gdk_window_set_title (window, "Preview");
3597 gtk_widget_show (widget);
3605 subproc_pretty_name (state *s)
3607 if (s->running_preview_cmd)
3609 char *ps = strdup (s->running_preview_cmd);
3610 char *ss = strchr (ps, ' ');
3612 ss = strrchr (ps, '/');
3623 return strdup ("???");
3628 reap_zombies (state *s)
3630 int wait_status = 0;
3632 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3636 if (pid == s->running_preview_pid)
3638 char *ss = subproc_pretty_name (s);
3639 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3640 (unsigned long) pid, ss);
3644 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3645 (unsigned long) pid);
3651 /* Mostly lifted from driver/subprocs.c */
3653 get_best_gl_visual (state *s)
3655 Display *dpy = GDK_DISPLAY();
3664 av[ac++] = "xscreensaver-gl-helper";
3669 perror ("error creating pipe:");
3676 switch ((int) (forked = fork ()))
3680 sprintf (buf, "%s: couldn't fork", blurb());
3688 close (in); /* don't need this one */
3689 close (ConnectionNumber (dpy)); /* close display fd */
3691 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3693 perror ("could not dup() a new stdout:");
3697 execvp (av[0], av); /* shouldn't return. */
3699 if (errno != ENOENT)
3701 /* Ignore "no such file or directory" errors, unless verbose.
3702 Issue all other exec errors, though. */
3703 sprintf (buf, "%s: running %s", blurb(), av[0]);
3707 /* Note that one must use _exit() instead of exit() in procs forked
3708 off of Gtk programs -- Gtk installs an atexit handler that has a
3709 copy of the X connection (which we've already closed, for safety.)
3710 If one uses exit() instead of _exit(), then one sometimes gets a
3711 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3713 _exit (1); /* exits fork */
3719 int wait_status = 0;
3721 FILE *f = fdopen (in, "r");
3725 close (out); /* don't need this one */
3728 if (!fgets (buf, sizeof(buf)-1, f))
3732 /* Wait for the child to die. */
3733 waitpid (-1, &wait_status, 0);
3735 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3741 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3747 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3749 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3750 blurb(), av[0], result);
3762 kill_preview_subproc (state *s, Bool reset_p)
3764 s->running_preview_error_p = False;
3767 clear_preview_window (s);
3769 if (s->subproc_check_timer_id)
3771 gtk_timeout_remove (s->subproc_check_timer_id);
3772 s->subproc_check_timer_id = 0;
3773 s->subproc_check_countdown = 0;
3776 if (s->running_preview_pid)
3778 int status = kill (s->running_preview_pid, SIGTERM);
3779 char *ss = subproc_pretty_name (s);
3786 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3787 blurb(), (unsigned long) s->running_preview_pid, ss);
3792 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3793 blurb(), (unsigned long) s->running_preview_pid, ss);
3799 waitpid(s->running_preview_pid, &endstatus, 0);
3801 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3802 (unsigned long) s->running_preview_pid, ss);
3806 s->running_preview_pid = 0;
3807 if (s->running_preview_cmd) free (s->running_preview_cmd);
3808 s->running_preview_cmd = 0;
3815 reset_preview_window (s);
3816 clear_preview_window (s);
3821 /* Immediately and unconditionally launches the given process,
3822 after appending the -window-id option; sets running_preview_pid.
3825 launch_preview_subproc (state *s)
3827 saver_preferences *p = &s->prefs;
3831 const char *cmd = s->desired_preview_cmd;
3833 GtkWidget *pr = name_to_widget (s, "preview");
3836 reset_preview_window (s);
3838 window = pr->window;
3840 s->running_preview_error_p = False;
3842 if (s->preview_suppressed_p)
3844 kill_preview_subproc (s, False);
3848 new_cmd = malloc (strlen (cmd) + 40);
3850 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3853 /* No window id? No command to run. */
3859 strcpy (new_cmd, cmd);
3860 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3864 kill_preview_subproc (s, False);
3867 s->running_preview_error_p = True;
3868 clear_preview_window (s);
3872 switch ((int) (forked = fork ()))
3877 sprintf (buf, "%s: couldn't fork", blurb());
3879 s->running_preview_error_p = True;
3885 close (ConnectionNumber (GDK_DISPLAY()));
3887 hack_subproc_environment (id, s->debug_p);
3889 usleep (250000); /* pause for 1/4th second before launching, to give
3890 the previous program time to die and flush its X
3891 buffer, so we don't get leftover turds on the
3894 exec_command (p->shell, new_cmd, p->nice_inferior);
3895 /* Don't bother printing an error message when we are unable to
3896 exec subprocesses; we handle that by polling the pid later.
3898 Note that one must use _exit() instead of exit() in procs forked
3899 off of Gtk programs -- Gtk installs an atexit handler that has a
3900 copy of the X connection (which we've already closed, for safety.)
3901 If one uses exit() instead of _exit(), then one sometimes gets a
3902 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3904 _exit (1); /* exits child fork */
3909 if (s->running_preview_cmd) free (s->running_preview_cmd);
3910 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3911 s->running_preview_pid = forked;
3915 char *ss = subproc_pretty_name (s);
3916 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3917 (unsigned long) forked, ss);
3924 schedule_preview_check (s);
3927 if (new_cmd) free (new_cmd);
3932 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3935 hack_environment (state *s)
3937 static const char *def_path =
3938 # ifdef DEFAULT_PATH_PREFIX
3939 DEFAULT_PATH_PREFIX;
3944 Display *dpy = GDK_DISPLAY();
3945 const char *odpy = DisplayString (dpy);
3946 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3947 strcpy (ndpy, "DISPLAY=");
3948 strcat (ndpy, odpy);
3953 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3955 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3956 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3957 So we must leak it (and/or the previous setting). Yay.
3960 if (def_path && *def_path)
3962 const char *opath = getenv("PATH");
3963 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3964 strcpy (npath, "PATH=");
3965 strcat (npath, def_path);
3966 strcat (npath, ":");
3967 strcat (npath, opath);
3971 /* do not free(npath) -- see above */
3974 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3980 hack_subproc_environment (Window preview_window_id, Bool debug_p)
3982 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
3983 necessary yet, but it will make programs work if we had invoked
3984 them with "-root" and not with "-window-id" -- which, of course,
3987 char *nssw = (char *) malloc (40);
3988 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
3990 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
3991 any more, right? It's not Posix, but everyone seems to have it. */
3996 fprintf (stderr, "%s: %s\n", blurb(), nssw);
3998 /* do not free(nssw) -- see above */
4002 /* Called from a timer:
4003 Launches the currently-chosen subprocess, if it's not already running.
4004 If there's a different process running, kills it.
4007 update_subproc_timer (gpointer data)
4009 state *s = (state *) data;
4010 if (! s->desired_preview_cmd)
4011 kill_preview_subproc (s, True);
4012 else if (!s->running_preview_cmd ||
4013 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4014 launch_preview_subproc (s);
4016 s->subproc_timer_id = 0;
4017 return FALSE; /* do not re-execute timer */
4021 /* Call this when you think you might want a preview process running.
4022 It will set a timer that will actually launch that program a second
4023 from now, if you haven't changed your mind (to avoid double-click
4024 spazzing, etc.) `cmd' may be null meaning "no process".
4027 schedule_preview (state *s, const char *cmd)
4029 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4034 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4036 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4039 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4040 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4042 if (s->subproc_timer_id)
4043 gtk_timeout_remove (s->subproc_timer_id);
4044 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4048 /* Called from a timer:
4049 Checks to see if the subproc that should be running, actually is.
4052 check_subproc_timer (gpointer data)
4054 state *s = (state *) data;
4055 Bool again_p = True;
4057 if (s->running_preview_error_p || /* already dead */
4058 s->running_preview_pid <= 0)
4066 status = kill (s->running_preview_pid, 0);
4067 if (status < 0 && errno == ESRCH)
4068 s->running_preview_error_p = True;
4072 char *ss = subproc_pretty_name (s);
4073 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4074 (unsigned long) s->running_preview_pid, ss,
4075 (s->running_preview_error_p ? "dead" : "alive"));
4079 if (s->running_preview_error_p)
4081 clear_preview_window (s);
4086 /* Otherwise, it's currently alive. We might be checking again, or we
4087 might be satisfied. */
4089 if (--s->subproc_check_countdown <= 0)
4093 return TRUE; /* re-execute timer */
4096 s->subproc_check_timer_id = 0;
4097 s->subproc_check_countdown = 0;
4098 return FALSE; /* do not re-execute timer */
4103 /* Call this just after launching a subprocess.
4104 This sets a timer that will, five times a second for two seconds,
4105 check whether the program is still running. The assumption here
4106 is that if the process didn't stay up for more than a couple of
4107 seconds, then either the program doesn't exist, or it doesn't
4108 take a -window-id argument.
4111 schedule_preview_check (state *s)
4117 fprintf (stderr, "%s: scheduling check\n", blurb());
4119 if (s->subproc_check_timer_id)
4120 gtk_timeout_remove (s->subproc_check_timer_id);
4121 s->subproc_check_timer_id =
4122 gtk_timeout_add (1000 / ticks,
4123 check_subproc_timer, (gpointer) s);
4124 s->subproc_check_countdown = ticks * seconds;
4129 screen_blanked_p (void)
4133 unsigned long nitems, bytesafter;
4134 unsigned char *dataP = 0;
4135 Display *dpy = GDK_DISPLAY();
4136 Bool blanked_p = False;
4138 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4139 XA_SCREENSAVER_STATUS,
4140 0, 3, False, XA_INTEGER,
4141 &type, &format, &nitems, &bytesafter,
4144 && type == XA_INTEGER
4148 Atom *data = (Atom *) dataP;
4149 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4152 if (dataP) XFree (dataP);
4157 /* Wake up every now and then and see if the screen is blanked.
4158 If it is, kill off the small-window demo -- no point in wasting
4159 cycles by running two screensavers at once...
4162 check_blanked_timer (gpointer data)
4164 state *s = (state *) data;
4165 Bool blanked_p = screen_blanked_p ();
4166 if (blanked_p && s->running_preview_pid)
4169 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4170 kill_preview_subproc (s, True);
4173 return True; /* re-execute timer */
4177 /* How many screens are there (including Xinerama.)
4180 screen_count (Display *dpy)
4182 int nscreens = ScreenCount(dpy);
4183 # ifdef HAVE_XINERAMA
4186 int event_number, error_number;
4187 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4188 XineramaIsActive (dpy))
4190 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4191 if (xsi) XFree (xsi);
4194 # endif /* HAVE_XINERAMA */
4200 /* Setting window manager icon
4204 init_icon (GdkWindow *window)
4206 GdkBitmap *mask = 0;
4209 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4210 (gchar **) logo_50_xpm);
4212 gdk_window_set_icon (window, 0, pixmap, mask);
4216 /* The main demo-mode command loop.
4221 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4222 XrmRepresentation *type, XrmValue *value, XPointer closure)
4225 for (i = 0; quarks[i]; i++)
4227 if (bindings[i] == XrmBindTightly)
4228 fprintf (stderr, (i == 0 ? "" : "."));
4229 else if (bindings[i] == XrmBindLoosely)
4230 fprintf (stderr, "*");
4232 fprintf (stderr, " ??? ");
4233 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4236 fprintf (stderr, ": %s\n", (char *) value->addr);
4244 the_network_is_not_the_computer (state *s)
4246 Display *dpy = GDK_DISPLAY();
4247 char *rversion = 0, *ruser = 0, *rhost = 0;
4248 char *luser, *lhost;
4250 struct passwd *p = getpwuid (getuid ());
4251 const char *d = DisplayString (dpy);
4253 # if defined(HAVE_UNAME)
4255 if (uname (&uts) < 0)
4256 lhost = "<UNKNOWN>";
4258 lhost = uts.nodename;
4260 strcpy (lhost, getenv("SYS$NODE"));
4261 # else /* !HAVE_UNAME && !VMS */
4262 strcat (lhost, "<UNKNOWN>");
4263 # endif /* !HAVE_UNAME && !VMS */
4265 if (p && p->pw_name)
4270 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4272 /* Make a buffer that's big enough for a number of copies of all the
4273 strings, plus some. */
4274 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4275 (ruser ? strlen(ruser) : 0) +
4276 (rhost ? strlen(rhost) : 0) +
4283 if (!rversion || !*rversion)
4287 "The XScreenSaver daemon doesn't seem to be running\n"
4288 "on display \"%s\". Launch it now?"),
4291 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4293 /* Warn that the two processes are running as different users.
4297 "%s is running as user \"%s\" on host \"%s\".\n"
4298 "But the xscreensaver managing display \"%s\"\n"
4299 "is running as user \"%s\" on host \"%s\".\n"
4301 "Since they are different users, they won't be reading/writing\n"
4302 "the same ~/.xscreensaver file, so %s isn't\n"
4303 "going to work right.\n"
4305 "You should either re-run %s as \"%s\", or re-run\n"
4306 "xscreensaver as \"%s\".\n"
4308 "Restart the xscreensaver daemon now?\n"),
4309 progname, luser, lhost,
4311 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4313 progname, (ruser ? ruser : "???"),
4316 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4318 /* Warn that the two processes are running on different hosts.
4322 "%s is running as user \"%s\" on host \"%s\".\n"
4323 "But the xscreensaver managing display \"%s\"\n"
4324 "is running as user \"%s\" on host \"%s\".\n"
4326 "If those two machines don't share a file system (that is,\n"
4327 "if they don't see the same ~%s/.xscreensaver file) then\n"
4328 "%s won't work right.\n"
4330 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4331 progname, luser, lhost,
4333 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4338 else if (!!strcmp (rversion, s->short_version))
4340 /* Warn that the version numbers don't match.
4344 "This is %s version %s.\n"
4345 "But the xscreensaver managing display \"%s\"\n"
4346 "is version %s. This could cause problems.\n"
4348 "Restart the xscreensaver daemon now?\n"),
4349 progname, s->short_version,
4356 warning_dialog (s->toplevel_widget, msg, True, 1);
4358 if (rversion) free (rversion);
4359 if (ruser) free (ruser);
4360 if (rhost) free (rhost);
4365 /* We use this error handler so that X errors are preceeded by the name
4366 of the program that generated them.
4369 demo_ehandler (Display *dpy, XErrorEvent *error)
4371 state *s = global_state_kludge; /* I hate C so much... */
4372 fprintf (stderr, "\nX error in %s:\n", blurb());
4373 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4374 kill_preview_subproc (s, False);
4380 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4381 of the program that generated them; and also that we can ignore one
4382 particular bogus error message that Gdk madly spews.
4385 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4386 const gchar *message, gpointer user_data)
4388 /* Ignore the message "Got event for unknown window: 0x...".
4389 Apparently some events are coming in for the xscreensaver window
4390 (presumably reply events related to the ClientMessage) and Gdk
4391 feels the need to complain about them. So, just suppress any
4392 messages that look like that one.
4394 if (strstr (message, "unknown window"))
4397 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4398 (log_domain ? log_domain : progclass),
4399 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4400 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4401 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4402 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4403 log_level == G_LOG_LEVEL_INFO ? "info" :
4404 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4406 ((!*message || message[strlen(message)-1] != '\n')
4412 __extension__ /* shut up about "string length is greater than the length
4413 ISO C89 compilers are required to support" when including
4417 static char *defaults[] = {
4418 #include "XScreenSaver_ad.h"
4423 #ifdef HAVE_CRAPPLET
4424 static struct poptOption crapplet_options[] = {
4425 {NULL, '\0', 0, NULL, 0}
4427 #endif /* HAVE_CRAPPLET */
4430 const char *usage = "[--display dpy] [--prefs]"
4431 # ifdef HAVE_CRAPPLET
4434 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4437 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4439 state *s = (state *) user_data;
4440 Boolean oi = s->initializing_p;
4442 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4444 s->initializing_p = True;
4446 eschew_gtk_lossage (label);
4448 s->initializing_p = oi;
4454 print_widget_tree (GtkWidget *w, int depth)
4457 for (i = 0; i < depth; i++)
4458 fprintf (stderr, " ");
4459 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4461 if (GTK_IS_LIST (w))
4463 for (i = 0; i < depth+1; i++)
4464 fprintf (stderr, " ");
4465 fprintf (stderr, "...list kids...\n");
4467 else if (GTK_IS_CONTAINER (w))
4469 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4472 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4480 delayed_scroll_kludge (gpointer data)
4482 state *s = (state *) data;
4483 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4484 ensure_selected_item_visible (w);
4486 /* Oh, this is just fucking lovely, too. */
4487 w = GTK_WIDGET (name_to_widget (s, "preview"));
4488 gtk_widget_hide (w);
4489 gtk_widget_show (w);
4491 return FALSE; /* do not re-execute timer */
4497 create_xscreensaver_demo (void)
4501 nb = name_to_widget (global_state_kludge, "preview_notebook");
4502 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4504 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4508 create_xscreensaver_settings_dialog (void)
4512 box = name_to_widget (global_state_kludge, "dialog_action_area");
4514 w = name_to_widget (global_state_kludge, "adv_button");
4515 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4517 w = name_to_widget (global_state_kludge, "std_button");
4518 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4520 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4523 #endif /* HAVE_GTK2 */
4526 main (int argc, char **argv)
4530 saver_preferences *p;
4534 Widget toplevel_shell;
4535 char *real_progname = argv[0];
4538 Bool crapplet_p = False;
4542 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4543 textdomain (GETTEXT_PACKAGE);
4546 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4547 # else /* !HAVE_GTK2 */
4548 if (!setlocale (LC_ALL, ""))
4549 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4550 # endif /* !HAVE_GTK2 */
4552 #endif /* ENABLE_NLS */
4554 str = strrchr (real_progname, '/');
4555 if (str) real_progname = str+1;
4558 memset (s, 0, sizeof(*s));
4559 s->initializing_p = True;
4562 global_state_kludge = s; /* I hate C so much... */
4564 progname = real_progname;
4566 s->short_version = (char *) malloc (5);
4567 memcpy (s->short_version, screensaver_id + 17, 4);
4568 s->short_version [4] = 0;
4571 /* Register our error message logger for every ``log domain'' known.
4572 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4573 for all of the domains that seem to be in use.
4576 const char * const domains[] = { 0,
4577 "Gtk", "Gdk", "GLib", "GModule",
4578 "GThread", "Gnome", "GnomeUI" };
4579 for (i = 0; i < countof(domains); i++)
4580 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4583 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4586 const char *dir = DEFAULT_ICONDIR;
4587 if (*dir) add_pixmap_directory (dir);
4589 # endif /* !HAVE_GTK2 */
4590 #endif /* DEFAULT_ICONDIR */
4592 /* This is gross, but Gtk understands --display and not -display...
4594 for (i = 1; i < argc; i++)
4595 if (argv[i][0] && argv[i][1] &&
4596 !strncmp(argv[i], "-display", strlen(argv[i])))
4597 argv[i] = "--display";
4600 /* We need to parse this arg really early... Sigh. */
4601 for (i = 1; i < argc; i++)
4604 (!strcmp(argv[i], "--crapplet") ||
4605 !strcmp(argv[i], "--capplet")))
4607 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4610 for (j = i; j < argc; j++) /* remove it from the list */
4611 argv[j] = argv[j+1];
4613 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4614 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4616 fprintf (stderr, "%s: %s\n", real_progname, usage);
4618 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4621 (!strcmp(argv[i], "--debug") ||
4622 !strcmp(argv[i], "-debug") ||
4623 !strcmp(argv[i], "-d")))
4627 for (j = i; j < argc; j++) /* remove it from the list */
4628 argv[j] = argv[j+1];
4635 (!strcmp(argv[i], "-geometry") ||
4636 !strcmp(argv[i], "-geom") ||
4637 !strcmp(argv[i], "-geo") ||
4638 !strcmp(argv[i], "-g")))
4642 for (j = i; j < argc; j++) /* remove them from the list */
4643 argv[j] = argv[j+2];
4650 (!strcmp(argv[i], "--configdir")))
4654 hack_configuration_path = argv[i+1];
4655 for (j = i; j < argc; j++) /* remove them from the list */
4656 argv[j] = argv[j+2];
4660 if (0 != stat (hack_configuration_path, &st))
4663 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4667 else if (!S_ISDIR (st.st_mode))
4669 fprintf (stderr, "%s: not a directory: %s\n",
4670 blurb(), hack_configuration_path);
4678 fprintf (stderr, "%s: using config directory \"%s\"\n",
4679 progname, hack_configuration_path);
4682 /* Let Gtk open the X connection, then initialize Xt to use that
4683 same connection. Doctor Frankenstein would be proud.
4685 # ifdef HAVE_CRAPPLET
4688 GnomeClient *client;
4689 GnomeClientFlags flags = 0;
4691 int init_results = gnome_capplet_init ("screensaver-properties",
4693 argc, argv, NULL, 0, NULL);
4695 0 upon successful initialization;
4696 1 if --init-session-settings was passed on the cmdline;
4697 2 if --ignore was passed on the cmdline;
4700 So the 1 signifies just to init the settings, and quit, basically.
4701 (Meaning launch the xscreensaver daemon.)
4704 if (init_results < 0)
4707 g_error ("An initialization error occurred while "
4708 "starting xscreensaver-capplet.\n");
4710 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4711 real_progname, init_results);
4716 client = gnome_master_client ();
4719 flags = gnome_client_get_flags (client);
4721 if (flags & GNOME_CLIENT_IS_CONNECTED)
4724 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4725 gnome_client_get_id (client));
4728 char *session_args[20];
4730 session_args[i++] = real_progname;
4731 session_args[i++] = "--capplet";
4732 session_args[i++] = "--init-session-settings";
4733 session_args[i] = 0;
4734 gnome_client_set_priority (client, 20);
4735 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4736 gnome_client_set_restart_command (client, i, session_args);
4740 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4743 gnome_client_flush (client);
4746 if (init_results == 1)
4748 system ("xscreensaver -nosplash &");
4754 # endif /* HAVE_CRAPPLET */
4756 gtk_init (&argc, &argv);
4760 /* We must read exactly the same resources as xscreensaver.
4761 That means we must have both the same progclass *and* progname,
4762 at least as far as the resource database is concerned. So,
4763 put "xscreensaver" in argv[0] while initializing Xt.
4765 argv[0] = "xscreensaver";
4769 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4771 XtToolkitInitialize ();
4772 app = XtCreateApplicationContext ();
4773 dpy = GDK_DISPLAY();
4774 XtAppSetFallbackResources (app, defaults);
4775 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4776 toplevel_shell = XtAppCreateShell (progname, progclass,
4777 applicationShellWidgetClass,
4780 dpy = XtDisplay (toplevel_shell);
4781 db = XtDatabase (dpy);
4782 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4783 XSetErrorHandler (demo_ehandler);
4785 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4786 signal (SIGPIPE, SIG_IGN);
4788 /* After doing Xt-style command-line processing, complain about any
4789 unrecognized command-line arguments.
4791 for (i = 1; i < argc; i++)
4793 char *str = argv[i];
4794 if (str[0] == '-' && str[1] == '-')
4796 if (!strcmp (str, "-prefs"))
4798 else if (crapplet_p)
4799 /* There are lots of random args that we don't care about when we're
4800 started as a crapplet, so just ignore unknown args in that case. */
4804 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4806 fprintf (stderr, "%s: %s\n", real_progname, usage);
4811 /* Load the init file, which may end up consulting the X resource database
4812 and the site-wide app-defaults file. Note that at this point, it's
4813 important that `progname' be "xscreensaver", rather than whatever
4817 s->nscreens = screen_count (dpy);
4819 hack_environment (s); /* must be before initialize_sort_map() */
4821 load_init_file (dpy, p);
4822 initialize_sort_map (s);
4824 /* Now that Xt has been initialized, and the resources have been read,
4825 we can set our `progname' variable to something more in line with
4828 progname = real_progname;
4832 /* Print out all the resources we read. */
4834 XrmName name = { 0 };
4835 XrmClass class = { 0 };
4837 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4843 /* Intern the atoms that xscreensaver_command() needs.
4845 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4846 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4847 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4848 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4849 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4850 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4851 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4852 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4853 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4854 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4855 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4856 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4857 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4860 /* Create the window and all its widgets.
4862 s->base_widget = create_xscreensaver_demo ();
4863 s->popup_widget = create_xscreensaver_settings_dialog ();
4864 s->toplevel_widget = s->base_widget;
4867 /* Set the main window's title. */
4869 char *base_title = _("Screensaver Preferences");
4870 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4871 char *s1, *s2, *s3, *s4;
4872 s1 = (char *) strchr(v, ' '); s1++;
4873 s2 = (char *) strchr(s1, ' ');
4874 s3 = (char *) strchr(v, '('); s3++;
4875 s4 = (char *) strchr(s3, ')');
4879 window_title = (char *) malloc (strlen (base_title) +
4880 strlen (progclass) +
4881 strlen (s1) + strlen (s3) +
4883 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4884 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4885 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4889 /* Adjust the (invisible) notebooks on the popup dialog... */
4891 GtkNotebook *notebook =
4892 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4893 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4897 gtk_widget_hide (std);
4898 # else /* !HAVE_XML */
4899 /* Make the advanced page be the only one available. */
4900 gtk_widget_set_sensitive (std, False);
4901 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4902 gtk_widget_hide (std);
4904 # endif /* !HAVE_XML */
4906 gtk_notebook_set_page (notebook, page);
4907 gtk_notebook_set_show_tabs (notebook, False);
4910 /* Various other widget initializations...
4912 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4913 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4915 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4916 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4919 populate_hack_list (s);
4920 populate_prefs_page (s);
4921 sensitize_demo_widgets (s, False);
4922 fix_text_entry_sizes (s);
4923 scroll_to_current_hack (s);
4925 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4926 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4930 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4931 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4933 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4934 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4936 #endif /* !HAVE_GTK2 */
4938 /* Hook up callbacks to the items on the mode menu. */
4940 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4941 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4942 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4944 for (i = 0; kids; kids = kids->next, i++)
4946 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4947 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4950 /* The "random-same" mode menu item does not appear unless
4951 there are multple screens.
4953 if (s->nscreens <= 1 &&
4954 mode_menu_order[i] == RANDOM_HACKS_SAME)
4955 gtk_widget_hide (GTK_WIDGET (kids->data));
4958 if (s->nscreens <= 1) /* recompute option-menu size */
4960 gtk_widget_unrealize (GTK_WIDGET (menu));
4961 gtk_widget_realize (GTK_WIDGET (menu));
4966 /* Handle the -prefs command-line argument. */
4969 GtkNotebook *notebook =
4970 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4971 gtk_notebook_set_page (notebook, 1);
4974 # ifdef HAVE_CRAPPLET
4978 GtkWidget *outer_vbox;
4980 gtk_widget_hide (s->toplevel_widget);
4982 capplet = capplet_widget_new ();
4984 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4985 # ifdef HAVE_CRAPPLET_IMMEDIATE
4986 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4987 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4988 /* In crapplet-mode, take off the menubar. */
4989 gtk_widget_hide (name_to_widget (s, "menubar"));
4991 /* Reparent our top-level container to be a child of the capplet
4994 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4995 gtk_widget_ref (outer_vbox);
4996 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4998 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4999 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5001 /* Find the window above us, and set the title and close handler. */
5003 GtkWidget *window = capplet;
5004 while (window && !GTK_IS_WINDOW (window))
5005 window = window->parent;
5008 gtk_window_set_title (GTK_WINDOW (window), window_title);
5009 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5010 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5015 s->toplevel_widget = capplet;
5017 # endif /* HAVE_CRAPPLET */
5020 /* The Gnome folks hate the menubar. I think it's important to have access
5021 to the commands on the File menu (Restart Daemon, etc.) and to the
5022 About and Documentation commands on the Help menu.
5026 gtk_widget_hide (name_to_widget (s, "menubar"));
5030 free (window_title);
5034 /* After picking the default size, allow -geometry to override it. */
5036 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5039 gtk_widget_show (s->toplevel_widget);
5040 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
5041 fix_preview_visual (s);
5043 /* Realize page zero, so that we can diddle the scrollbar when the
5044 user tabs back to it -- otherwise, the current hack isn't scrolled
5045 to the first time they tab back there, when started with "-prefs".
5046 (Though it is if they then tab away, and back again.)
5048 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5049 #### understands this crap, explain to me how to make this work.
5051 gtk_widget_realize (name_to_widget (s, "demos_table"));
5054 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5057 /* Issue any warnings about the running xscreensaver daemon. */
5059 the_network_is_not_the_computer (s);
5062 /* Run the Gtk event loop, and not the Xt event loop. This means that
5063 if there were Xt timers or fds registered, they would never get serviced,
5064 and if there were any Xt widgets, they would never have events delivered.
5065 Fortunately, we're using Gtk for all of the UI, and only initialized
5066 Xt so that we could process the command line and use the X resource
5069 s->initializing_p = False;
5071 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5072 after we start up. Otherwise, it always appears scrolled to the top
5073 when in crapplet-mode. */
5074 gtk_timeout_add (500, delayed_scroll_kludge, s);
5078 /* Load every configurator in turn, to scan them for errors all at once. */
5082 for (i = 0; i < p->screenhacks_count; i++)
5084 screenhack *hack = p->screenhacks[i];
5085 conf_data *d = load_configurator (hack->command, s->debug_p);
5086 if (d) free_conf_data (d);
5092 # ifdef HAVE_CRAPPLET
5094 capplet_gtk_main ();
5096 # endif /* HAVE_CRAPPLET */
5099 kill_preview_subproc (s, False);
5103 #endif /* HAVE_GTK -- whole file */