1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2008 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 */
145 /* Deal with deprecation of direct access to struct fields on the way to GTK3
146 See http://live.gnome.org/GnomeGoals/UseGseal
148 #if GTK_CHECK_VERSION(2,14,0)
149 # define GET_PARENT(w) gtk_widget_get_parent (w)
150 # define GET_WINDOW(w) gtk_widget_get_window (w)
151 # define GET_ACTION_AREA(d) gtk_dialog_get_action_area (d)
152 # define GET_CONTENT_AREA(d) gtk_dialog_get_content_area (d)
153 # define GET_ADJ_VALUE(a) gtk_adjustment_get_value (a)
154 # define SET_ADJ_VALUE(a,v) gtk_adjustment_set_value (a, v)
155 # define SET_ADJ_UPPER(a,v) gtk_adjustment_set_upper (a, v)
157 # define GET_PARENT(w) ((w)->parent)
158 # define GET_WINDOW(w) ((w)->window)
159 # define GET_ACTION_AREA(d) ((d)->action_area)
160 # define GET_CONTENT_AREA(d) ((d)->vbox)
161 # define GET_ADJ_VALUE(a) ((a)->value)
162 # define SET_ADJ_VALUE(a,v) (a)->value = v
163 # define SET_ADJ_UPPER(a,v) (a)->upper = v
166 #if GTK_CHECK_VERSION(2,18,0)
167 # define SET_CAN_DEFAULT(w) gtk_widget_set_can_default ((w), TRUE)
168 # define GET_SENSITIVE(w) gtk_widget_get_sensitive (w)
170 # define SET_CAN_DEFAULT(w) GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_DEFAULT)
171 # define GET_SENSITIVE(w) GTK_WIDGET_IS_SENSITIVE (w)
174 #if GTK_CHECK_VERSION(2,20,0)
175 # define GET_REALIZED(w) gtk_widget_get_realized (w)
177 # define GET_REALIZED(w) GTK_WIDGET_REALIZED (w)
181 extern void exec_command (const char *shell, const char *command, int nice);
182 extern int on_path_p (const char *program);
184 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
187 #define countof(x) (sizeof((x))/sizeof((*x)))
190 /* You might think that to read an array of 32-bit quantities out of a
191 server-side property, you would pass an array of 32-bit data quantities
192 into XGetWindowProperty(). You would be wrong. You have to use an array
193 of longs, even if long is 64 bits (using 32 of each 64.)
198 char *progclass = "XScreenSaver";
201 /* The order of the items in the mode menu. */
202 static int mode_menu_order[] = {
203 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
208 char *short_version; /* version number of this xscreensaver build */
210 GtkWidget *toplevel_widget; /* the main window */
211 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
212 GtkWidget *popup_widget; /* the "Settings" dialog */
213 conf_data *cdata; /* private data for per-hack configuration */
216 GladeXML *glade_ui; /* Glade UI file */
217 #endif /* HAVE_GTK2 */
219 Bool debug_p; /* whether to print diagnostics */
220 Bool initializing_p; /* flag for breaking recursion loops */
221 Bool saving_p; /* flag for breaking recursion loops */
223 char *desired_preview_cmd; /* subprocess we intend to run */
224 char *running_preview_cmd; /* subprocess we are currently running */
225 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
226 Bool running_preview_error_p; /* whether the pid died abnormally */
228 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
229 int subproc_timer_id; /* timer to delay subproc launch */
230 int subproc_check_timer_id; /* timer to check whether it started up */
231 int subproc_check_countdown; /* how many more checks left */
233 int *list_elt_to_hack_number; /* table for sorting the hack list */
234 int *hack_number_to_list_elt; /* the inverse table */
235 Bool *hacks_available_p; /* whether hacks are on $PATH */
236 int total_available; /* how many are on $PATH */
237 int list_count; /* how many items are in the list: this may be
238 less than p->screenhacks_count, if some are
241 int _selected_list_element; /* don't use this: call
242 selected_list_element() instead */
244 int nscreens; /* How many X or Xinerama screens there are */
246 saver_preferences prefs;
251 /* Total fucking evilness due to the fact that it's rocket science to get
252 a closure object of our own down into the various widget callbacks. */
253 static state *global_state_kludge;
256 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
257 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
258 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
261 static void populate_demo_window (state *, int list_elt);
262 static void populate_prefs_page (state *);
263 static void populate_popup_window (state *);
265 static Bool flush_dialog_changes_and_save (state *);
266 static Bool flush_popup_changes_and_save (state *);
268 static int maybe_reload_init_file (state *);
269 static void await_xscreensaver (state *);
270 static Bool xscreensaver_running_p (state *);
271 static void sensitize_menu_items (state *s, Bool force_p);
272 static void force_dialog_repaint (state *s);
274 static void schedule_preview (state *, const char *cmd);
275 static void kill_preview_subproc (state *, Bool reset_p);
276 static void schedule_preview_check (state *);
279 /* Prototypes of functions used by the Glade-generated code,
282 void exit_menu_cb (GtkMenuItem *, gpointer user_data);
283 void about_menu_cb (GtkMenuItem *, gpointer user_data);
284 void doc_menu_cb (GtkMenuItem *, gpointer user_data);
285 void file_menu_cb (GtkMenuItem *, gpointer user_data);
286 void activate_menu_cb (GtkMenuItem *, gpointer user_data);
287 void lock_menu_cb (GtkMenuItem *, gpointer user_data);
288 void kill_menu_cb (GtkMenuItem *, gpointer user_data);
289 void restart_menu_cb (GtkWidget *, gpointer user_data);
290 void run_this_cb (GtkButton *, gpointer user_data);
291 void manual_cb (GtkButton *, gpointer user_data);
292 void run_next_cb (GtkButton *, gpointer user_data);
293 void run_prev_cb (GtkButton *, gpointer user_data);
294 void pref_changed_cb (GtkWidget *, gpointer user_data);
295 gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
296 void mode_menu_item_cb (GtkWidget *, gpointer user_data);
297 void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
298 gint page_num, gpointer user_data);
299 void browse_image_dir_cb (GtkButton *, gpointer user_data);
300 void browse_text_file_cb (GtkButton *, gpointer user_data);
301 void browse_text_program_cb (GtkButton *, gpointer user_data);
302 void settings_cb (GtkButton *, gpointer user_data);
303 void settings_adv_cb (GtkButton *, gpointer user_data);
304 void settings_std_cb (GtkButton *, gpointer user_data);
305 void settings_reset_cb (GtkButton *, gpointer user_data);
306 void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
307 gint page_num, gpointer user_data);
308 void settings_cancel_cb (GtkButton *, gpointer user_data);
309 void settings_ok_cb (GtkButton *, gpointer user_data);
311 static void kill_gnome_screensaver (void);
312 static void kill_kde_screensaver (void);
315 /* Some random utility functions
318 const char *blurb (void);
323 time_t now = time ((time_t *) 0);
324 char *ct = (char *) ctime (&now);
325 static char buf[255];
326 int n = strlen(progname);
328 strncpy(buf, progname, n);
331 strncpy(buf+n, ct+11, 8);
332 strcpy(buf+n+9, ": ");
338 name_to_widget (state *s, const char *name)
348 /* First try to load the Glade file from the current directory;
349 if there isn't one there, check the installed directory.
351 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
352 const char * const files[] = { GLADE_FILE_NAME,
353 GLADE_DIR "/" GLADE_FILE_NAME };
355 for (i = 0; i < countof (files); i++)
358 if (!stat (files[i], &st))
360 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
367 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
368 "\tfrom " GLADE_DIR "/ or current directory.\n",
372 # undef GLADE_FILE_NAME
374 glade_xml_signal_autoconnect (s->glade_ui);
377 w = glade_xml_get_widget (s->glade_ui, name);
379 #else /* !HAVE_GTK2 */
381 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
384 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
386 #endif /* HAVE_GTK2 */
389 fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
395 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
396 Takes a scroller, viewport, or list as an argument.
399 ensure_selected_item_visible (GtkWidget *widget)
403 GtkTreeSelection *selection;
407 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
408 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
409 path = gtk_tree_path_new_first ();
411 path = gtk_tree_model_get_path (model, &iter);
413 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
415 gtk_tree_path_free (path);
417 #else /* !HAVE_GTK2 */
419 GtkScrolledWindow *scroller = 0;
421 GtkList *list_widget = 0;
425 GtkWidget *selected = 0;
428 gint parent_h, child_y, child_h, children_h, ignore;
429 double ratio_t, ratio_b;
431 if (GTK_IS_SCROLLED_WINDOW (widget))
433 scroller = GTK_SCROLLED_WINDOW (widget);
434 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
435 list_widget = GTK_LIST (GTK_BIN(vp)->child);
437 else if (GTK_IS_VIEWPORT (widget))
439 vp = GTK_VIEWPORT (widget);
440 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
441 list_widget = GTK_LIST (GTK_BIN(vp)->child);
443 else if (GTK_IS_LIST (widget))
445 list_widget = GTK_LIST (widget);
446 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
447 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
452 slist = list_widget->selection;
453 selected = (slist ? GTK_WIDGET (slist->data) : 0);
457 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
459 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
460 kids; kids = kids->next)
463 adj = gtk_scrolled_window_get_vadjustment (scroller);
465 gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (vp)),
466 &ignore, &ignore, &ignore, &parent_h, &ignore);
467 gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (selected)),
468 &ignore, &child_y, &ignore, &child_h, &ignore);
469 children_h = nkids * child_h;
471 ratio_t = ((double) child_y) / ((double) children_h);
472 ratio_b = ((double) child_y + child_h) / ((double) children_h);
474 if (adj->upper == 0.0) /* no items in list */
477 if (ratio_t < (adj->value / adj->upper) ||
478 ratio_b > ((adj->value + adj->page_size) / adj->upper))
481 int slop = parent_h * 0.75; /* how much to overshoot by */
483 if (ratio_t < (adj->value / adj->upper))
485 double ratio_w = ((double) parent_h) / ((double) children_h);
486 double ratio_l = (ratio_b - ratio_t);
487 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
490 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
492 target = ratio_t * adj->upper;
496 if (target > adj->upper - adj->page_size)
497 target = adj->upper - adj->page_size;
501 gtk_adjustment_set_value (adj, target);
503 #endif /* !HAVE_GTK2 */
507 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
509 GtkWidget *shell = GTK_WIDGET (user_data);
510 while (GET_PARENT (shell))
511 shell = GET_PARENT (shell);
512 gtk_widget_destroy (GTK_WIDGET (shell));
516 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
518 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
520 restart_menu_cb (widget, user_data);
521 warning_dialog_dismiss_cb (widget, user_data);
524 static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data)
526 kill_gnome_screensaver ();
527 warning_dialog_dismiss_cb (widget, user_data);
530 static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data)
532 kill_kde_screensaver ();
533 warning_dialog_dismiss_cb (widget, user_data);
536 typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
539 warning_dialog (GtkWidget *parent, const char *message,
540 dialog_button button_type, int center)
542 char *msg = strdup (message);
545 GtkWidget *dialog = gtk_dialog_new ();
546 GtkWidget *label = 0;
548 GtkWidget *cancel = 0;
551 while (parent && !GET_WINDOW (parent))
552 parent = GET_PARENT (parent);
555 !GET_WINDOW (parent)) /* too early to pop up transient dialogs */
557 fprintf (stderr, "%s: too early for dialog?\n", progname);
565 char *s = strchr (head, '\n');
568 sprintf (name, "label%d", i++);
571 label = gtk_label_new (head);
573 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
574 #endif /* HAVE_GTK2 */
579 GTK_WIDGET (label)->style =
580 gtk_style_copy (GTK_WIDGET (label)->style);
581 GTK_WIDGET (label)->style->font =
582 gdk_font_load (get_string_resource("warning_dialog.headingFont",
584 gtk_widget_set_style (GTK_WIDGET (label),
585 GTK_WIDGET (label)->style);
587 #endif /* !HAVE_GTK2 */
589 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
590 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
591 label, TRUE, TRUE, 0);
592 gtk_widget_show (label);
603 label = gtk_label_new ("");
604 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
605 label, TRUE, TRUE, 0);
606 gtk_widget_show (label);
608 label = gtk_hbutton_box_new ();
609 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
610 label, TRUE, TRUE, 0);
613 if (button_type != D_NONE)
615 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
616 gtk_container_add (GTK_CONTAINER (label), cancel);
619 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
620 gtk_container_add (GTK_CONTAINER (label), ok);
622 #else /* !HAVE_GTK2 */
624 ok = gtk_button_new_with_label ("OK");
625 gtk_container_add (GTK_CONTAINER (label), ok);
627 if (button_type != D_NONE)
629 cancel = gtk_button_new_with_label ("Cancel");
630 gtk_container_add (GTK_CONTAINER (label), cancel);
633 #endif /* !HAVE_GTK2 */
635 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
636 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
637 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
638 SET_CAN_DEFAULT (ok);
639 gtk_widget_show (ok);
640 gtk_widget_grab_focus (ok);
644 SET_CAN_DEFAULT (cancel);
645 gtk_widget_show (cancel);
647 gtk_widget_show (label);
648 gtk_widget_show (dialog);
650 if (button_type != D_NONE)
653 switch (button_type) {
654 case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break;
655 case D_GNOME: fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb); break;
656 case D_KDE: fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb); break;
657 default: abort(); break;
659 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn,
661 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
662 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
667 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
668 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
672 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
673 GET_WINDOW (GTK_WIDGET (parent)));
676 gtk_window_present (GTK_WINDOW (dialog));
677 #else /* !HAVE_GTK2 */
678 gdk_window_show (GTK_WIDGET (dialog)->window);
679 gdk_window_raise (GTK_WIDGET (dialog)->window);
680 #endif /* !HAVE_GTK2 */
687 run_cmd (state *s, Atom command, int arg)
692 flush_dialog_changes_and_save (s);
693 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
695 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
696 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
703 sprintf (buf, "Error:\n\n%s", err);
705 strcpy (buf, "Unknown error!");
706 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
710 sensitize_menu_items (s, True);
711 force_dialog_repaint (s);
716 run_hack (state *s, int list_elt, Bool report_errors_p)
722 if (list_elt < 0) return;
723 hack_number = s->list_elt_to_hack_number[list_elt];
725 flush_dialog_changes_and_save (s);
726 schedule_preview (s, 0);
728 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
731 if (status < 0 && report_errors_p)
733 if (xscreensaver_running_p (s))
735 /* Kludge: ignore the spurious "window unexpectedly deleted"
737 if (err && strstr (err, "unexpectedly deleted"))
744 sprintf (buf, "Error:\n\n%s", err);
746 strcpy (buf, "Unknown error!");
747 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
752 /* The error is that the daemon isn't running;
755 const char *d = DisplayString (GDK_DISPLAY());
759 "The XScreenSaver daemon doesn't seem to be running\n"
760 "on display \"%s\". Launch it now?"),
762 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
768 sensitize_menu_items (s, False);
775 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
776 libglade work on Cygwin; apparently all Glade callbacks need this magic
777 extra declaration. I do not pretend to understand.
781 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
783 state *s = global_state_kludge; /* I hate C so much... */
784 flush_dialog_changes_and_save (s);
785 kill_preview_subproc (s, False);
790 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
792 state *s = (state *) data;
793 flush_dialog_changes_and_save (s);
800 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
803 char *vers = strdup (screensaver_id + 4);
806 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
808 s = strchr (vers, ',');
812 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
813 non-ASCII characters aren't allowed in localizable string keys."
814 (I don't want to just use (c) instead of © because that doesn't
815 look as good in the plain-old default Latin1 "C" locale.)
818 sprintf(copy, ("Copyright \xC2\xA9 1991-2008 %s"), s);
819 #else /* !HAVE_GTK2 */
820 sprintf(copy, ("Copyright \251 1991-2008 %s"), s);
821 #endif /* !HAVE_GTK2 */
823 sprintf (msg, "%s\n\n%s", copy, desc);
825 /* I can't make gnome_about_new() work here -- it starts dying in
826 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
827 then this might be the thing to do:
831 const gchar *auth[] = { 0 };
832 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
834 gtk_widget_show (about);
836 #else / * GTK but not GNOME * /
840 GdkColormap *colormap;
841 GdkPixmap *gdkpixmap;
844 GtkWidget *dialog = gtk_dialog_new ();
845 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
846 GtkWidget *parent = GTK_WIDGET (menuitem);
847 while (GET_PARENT (parent))
848 parent = GET_PARENT (parent);
850 hbox = gtk_hbox_new (FALSE, 20);
851 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
852 hbox, TRUE, TRUE, 0);
854 colormap = gtk_widget_get_colormap (parent);
856 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
857 (gchar **) logo_180_xpm);
858 icon = gtk_pixmap_new (gdkpixmap, mask);
859 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
861 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
863 vbox = gtk_vbox_new (FALSE, 0);
864 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
866 label1 = gtk_label_new (vers);
867 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
868 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
869 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
872 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
873 GTK_WIDGET (label1)->style->font =
874 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
875 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
876 #endif /* HAVE_GTK2 */
878 label2 = gtk_label_new (msg);
879 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
880 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
881 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
884 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
885 GTK_WIDGET (label2)->style->font =
886 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
887 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
888 #endif /* HAVE_GTK2 */
890 hb = gtk_hbutton_box_new ();
892 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
896 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
897 #else /* !HAVE_GTK2 */
898 ok = gtk_button_new_with_label (_("OK"));
899 #endif /* !HAVE_GTK2 */
900 gtk_container_add (GTK_CONTAINER (hb), ok);
902 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
903 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
904 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
906 gtk_widget_show (hbox);
907 gtk_widget_show (icon);
908 gtk_widget_show (vbox);
909 gtk_widget_show (label1);
910 gtk_widget_show (label2);
911 gtk_widget_show (hb);
912 gtk_widget_show (ok);
913 gtk_widget_show (dialog);
915 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
916 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
918 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
919 GET_WINDOW (GTK_WIDGET (parent)));
920 gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog)));
921 gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog)));
927 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
929 state *s = global_state_kludge; /* I hate C so much... */
930 saver_preferences *p = &s->prefs;
933 if (!p->help_url || !*p->help_url)
935 warning_dialog (s->toplevel_widget,
937 "No Help URL has been specified.\n"), D_NONE, 100);
941 help_command = (char *) malloc (strlen (p->load_url_command) +
942 (strlen (p->help_url) * 4) + 20);
943 strcpy (help_command, "( ");
944 sprintf (help_command + strlen(help_command),
946 p->help_url, p->help_url, p->help_url, p->help_url);
947 strcat (help_command, " ) &");
948 if (system (help_command) < 0)
949 fprintf (stderr, "%s: fork error\n", blurb());
955 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
957 state *s = global_state_kludge; /* I hate C so much... */
958 sensitize_menu_items (s, False);
963 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
965 state *s = global_state_kludge; /* I hate C so much... */
966 run_cmd (s, XA_ACTIVATE, 0);
971 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
973 state *s = global_state_kludge; /* I hate C so much... */
974 run_cmd (s, XA_LOCK, 0);
979 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
981 state *s = global_state_kludge; /* I hate C so much... */
982 run_cmd (s, XA_EXIT, 0);
987 restart_menu_cb (GtkWidget *widget, gpointer user_data)
989 state *s = global_state_kludge; /* I hate C so much... */
990 flush_dialog_changes_and_save (s);
991 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
993 if (system ("xscreensaver -nosplash &") < 0)
994 fprintf (stderr, "%s: fork error\n", blurb());
996 await_xscreensaver (s);
1000 xscreensaver_running_p (state *s)
1002 Display *dpy = GDK_DISPLAY();
1004 server_xscreensaver_version (dpy, &rversion, 0, 0);
1012 await_xscreensaver (state *s)
1017 while (!ok && (--countdown > 0))
1018 if (xscreensaver_running_p (s))
1021 sleep (1); /* If it's not there yet, wait a second... */
1023 sensitize_menu_items (s, True);
1027 /* Timed out, no screensaver running. */
1030 Bool root_p = (geteuid () == 0);
1034 "The xscreensaver daemon did not start up properly.\n"
1040 __extension__ /* don't warn about "string length is greater than
1041 the length ISO C89 compilers are required to
1042 support" in the following expression... */
1045 _("You are running as root. This usually means that xscreensaver\n"
1046 "was unable to contact your X server because access control is\n"
1047 "turned on. Try running this command:\n"
1049 " xhost +localhost\n"
1051 "and then selecting `File / Restart Daemon'.\n"
1053 "Note that turning off access control will allow anyone logged\n"
1054 "on to this machine to access your screen, which might be\n"
1055 "considered a security problem. Please read the xscreensaver\n"
1056 "manual and FAQ for more information.\n"
1058 "You shouldn't run X as root. Instead, you should log in as a\n"
1059 "normal user, and `su' as necessary."));
1061 strcat (buf, _("Please check your $PATH and permissions."));
1063 warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1066 force_dialog_repaint (s);
1071 selected_list_element (state *s)
1073 return s->_selected_list_element;
1078 demo_write_init_file (state *s, saver_preferences *p)
1080 Display *dpy = GDK_DISPLAY();
1083 /* #### try to figure out why shit keeps getting reordered... */
1084 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1088 if (!write_init_file (dpy, p, s->short_version, False))
1091 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1096 const char *f = init_file_name();
1098 warning_dialog (s->toplevel_widget,
1099 _("Error:\n\nCouldn't determine init file name!\n"),
1103 char *b = (char *) malloc (strlen(f) + 1024);
1104 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1105 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1113 G_MODULE_EXPORT void
1114 run_this_cb (GtkButton *button, gpointer user_data)
1116 state *s = global_state_kludge; /* I hate C so much... */
1117 int list_elt = selected_list_element (s);
1118 if (list_elt < 0) return;
1119 if (!flush_dialog_changes_and_save (s))
1120 run_hack (s, list_elt, True);
1124 G_MODULE_EXPORT void
1125 manual_cb (GtkButton *button, gpointer user_data)
1127 Display *dpy = GDK_DISPLAY();
1128 state *s = global_state_kludge; /* I hate C so much... */
1129 saver_preferences *p = &s->prefs;
1130 GtkWidget *list_widget = name_to_widget (s, "list");
1131 int list_elt = selected_list_element (s);
1133 char *name, *name2, *cmd, *str;
1135 if (list_elt < 0) return;
1136 hack_number = s->list_elt_to_hack_number[list_elt];
1138 flush_dialog_changes_and_save (s);
1139 ensure_selected_item_visible (list_widget);
1141 name = strdup (p->screenhacks[hack_number]->command);
1144 while (isspace (*name2)) name2++;
1146 while (*str && !isspace (*str)) str++;
1148 str = strrchr (name2, '/');
1149 if (str) name2 = str+1;
1151 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1154 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1155 strcpy (cmd2, "( ");
1156 sprintf (cmd2 + strlen (cmd2),
1158 name2, name2, name2, name2);
1159 strcat (cmd2, " ) &");
1160 if (system (cmd2) < 0)
1161 fprintf (stderr, "%s: fork error\n", blurb());
1166 warning_dialog (GTK_WIDGET (button),
1167 _("Error:\n\nno `manualCommand' resource set."),
1176 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1178 GtkWidget *parent = name_to_widget (s, "scroller");
1179 gboolean was = GET_SENSITIVE (parent);
1182 GtkTreeModel *model;
1183 GtkTreeSelection *selection;
1184 #endif /* HAVE_GTK2 */
1186 if (!was) gtk_widget_set_sensitive (parent, True);
1188 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1190 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1192 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1193 gtk_tree_selection_select_iter (selection, &iter);
1195 #else /* !HAVE_GTK2 */
1196 gtk_list_select_item (GTK_LIST (list), list_elt);
1197 #endif /* !HAVE_GTK2 */
1198 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1199 if (!was) gtk_widget_set_sensitive (parent, False);
1203 G_MODULE_EXPORT void
1204 run_next_cb (GtkButton *button, gpointer user_data)
1206 state *s = global_state_kludge; /* I hate C so much... */
1207 /* saver_preferences *p = &s->prefs; */
1208 Bool ops = s->preview_suppressed_p;
1210 GtkWidget *list_widget = name_to_widget (s, "list");
1211 int list_elt = selected_list_element (s);
1218 if (list_elt >= s->list_count)
1221 s->preview_suppressed_p = True;
1223 flush_dialog_changes_and_save (s);
1224 force_list_select_item (s, list_widget, list_elt, True);
1225 populate_demo_window (s, list_elt);
1226 run_hack (s, list_elt, False);
1228 s->preview_suppressed_p = ops;
1232 G_MODULE_EXPORT void
1233 run_prev_cb (GtkButton *button, gpointer user_data)
1235 state *s = global_state_kludge; /* I hate C so much... */
1236 /* saver_preferences *p = &s->prefs; */
1237 Bool ops = s->preview_suppressed_p;
1239 GtkWidget *list_widget = name_to_widget (s, "list");
1240 int list_elt = selected_list_element (s);
1243 list_elt = s->list_count - 1;
1248 list_elt = s->list_count - 1;
1250 s->preview_suppressed_p = True;
1252 flush_dialog_changes_and_save (s);
1253 force_list_select_item (s, list_widget, list_elt, True);
1254 populate_demo_window (s, list_elt);
1255 run_hack (s, list_elt, False);
1257 s->preview_suppressed_p = ops;
1261 /* Writes the given settings into prefs.
1262 Returns true if there was a change, False otherwise.
1263 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1266 flush_changes (state *s,
1269 const char *command,
1272 saver_preferences *p = &s->prefs;
1273 Bool changed = False;
1276 if (list_elt < 0 || list_elt >= s->list_count)
1279 hack_number = s->list_elt_to_hack_number[list_elt];
1280 hack = p->screenhacks[hack_number];
1282 if (enabled_p != -1 &&
1283 enabled_p != hack->enabled_p)
1285 hack->enabled_p = enabled_p;
1288 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1289 blurb(), hack->name, enabled_p);
1294 if (!hack->command || !!strcmp (command, hack->command))
1296 if (hack->command) free (hack->command);
1297 hack->command = strdup (command);
1300 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1301 blurb(), hack->name, command);
1307 const char *ov = hack->visual;
1308 if (!ov || !*ov) ov = "any";
1309 if (!*visual) visual = "any";
1310 if (!!strcasecmp (visual, ov))
1312 if (hack->visual) free (hack->visual);
1313 hack->visual = strdup (visual);
1316 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1317 blurb(), hack->name, visual);
1325 /* Helper for the text fields that contain time specifications:
1326 this parses the text, and does error checking.
1329 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1334 if (!sec_p || strchr (line, ':'))
1335 value = parse_time ((char *) line, sec_p, True);
1339 if (sscanf (line, "%d%c", &value, &c) != 1)
1345 value *= 1000; /* Time measures in microseconds */
1351 "Unparsable time format: \"%s\"\n"),
1353 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1362 directory_p (const char *path)
1365 if (!path || !*path)
1367 else if (stat (path, &st))
1369 else if (!S_ISDIR (st.st_mode))
1376 file_p (const char *path)
1379 if (!path || !*path)
1381 else if (stat (path, &st))
1383 else if (S_ISDIR (st.st_mode))
1390 normalize_directory (const char *path)
1394 if (!path || !*path) return 0;
1396 p2 = (char *) malloc (L + 2);
1398 if (p2[L-1] == '/') /* remove trailing slash */
1401 for (s = p2; s && *s; s++)
1404 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1405 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1408 while (s0 > p2 && s0[-1] != '/')
1418 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1419 strcpy (s, s+2), s--;
1420 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1424 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1425 while (s[0] == '/' && s[1] == '/')
1428 /* and strip trailing whitespace for good measure. */
1430 while (isspace(p2[L-1]))
1443 } FlushForeachClosure;
1446 flush_checkbox (GtkTreeModel *model,
1451 FlushForeachClosure *closure = data;
1454 gtk_tree_model_get (model, iter,
1455 COL_ENABLED, &checked,
1458 if (flush_changes (closure->s, closure->i,
1460 *closure->changed = True;
1464 /* don't remove row */
1468 #endif /* HAVE_GTK2 */
1470 /* Flush out any changes made in the main dialog window (where changes
1471 take place immediately: clicking on a checkbox causes the init file
1472 to be written right away.)
1475 flush_dialog_changes_and_save (state *s)
1477 saver_preferences *p = &s->prefs;
1478 saver_preferences P2, *p2 = &P2;
1480 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1481 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1482 FlushForeachClosure closure;
1483 #else /* !HAVE_GTK2 */
1484 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1485 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1487 #endif /* !HAVE_GTK2 */
1489 Bool changed = False;
1492 if (s->saving_p) return False;
1497 /* Flush any checkbox changes in the list down into the prefs struct.
1501 closure.changed = &changed;
1503 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1505 #else /* !HAVE_GTK2 */
1507 for (i = 0; kids; kids = kids->next, i++)
1509 GtkWidget *line = GTK_WIDGET (kids->data);
1510 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1511 GtkWidget *line_check =
1512 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1514 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1516 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1519 #endif /* ~HAVE_GTK2 */
1521 /* Flush the non-hack-specific settings down into the prefs struct.
1524 # define SECONDS(FIELD,NAME) \
1525 w = name_to_widget (s, (NAME)); \
1526 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1528 # define MINUTES(FIELD,NAME) \
1529 w = name_to_widget (s, (NAME)); \
1530 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1532 # define CHECKBOX(FIELD,NAME) \
1533 w = name_to_widget (s, (NAME)); \
1534 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1536 # define PATHNAME(FIELD,NAME) \
1537 w = name_to_widget (s, (NAME)); \
1538 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1540 # define TEXT(FIELD,NAME) \
1541 w = name_to_widget (s, (NAME)); \
1542 (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1544 MINUTES (&p2->timeout, "timeout_spinbutton");
1545 MINUTES (&p2->cycle, "cycle_spinbutton");
1546 CHECKBOX (p2->lock_p, "lock_button");
1547 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1549 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1550 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1551 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1552 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1554 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1555 CHECKBOX (p2->grab_video_p, "grab_video_button");
1556 CHECKBOX (p2->random_image_p, "grab_image_button");
1557 PATHNAME (p2->image_directory, "image_text");
1560 CHECKBOX (p2->verbose_p, "verbose_button");
1561 CHECKBOX (p2->capture_stderr_p, "capture_button");
1562 CHECKBOX (p2->splash_p, "splash_button");
1567 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1568 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1569 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1570 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1571 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1572 TEXT (p2->text_literal, "text_entry");
1573 PATHNAME (p2->text_file, "text_file_entry");
1574 PATHNAME (p2->text_program, "text_program_entry");
1575 PATHNAME (p2->text_program, "text_program_entry");
1576 TEXT (p2->text_url, "text_url_entry");
1579 CHECKBOX (p2->install_cmap_p, "install_button");
1580 CHECKBOX (p2->fade_p, "fade_button");
1581 CHECKBOX (p2->unfade_p, "unfade_button");
1582 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1590 /* Warn if the image directory doesn't exist.
1592 if (p2->image_directory &&
1593 *p2->image_directory &&
1594 !directory_p (p2->image_directory))
1597 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1598 p2->image_directory);
1599 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1603 /* Map the mode menu to `saver_mode' enum values. */
1605 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1606 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1607 GtkWidget *selected = gtk_menu_get_active (menu);
1608 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1609 int menu_elt = g_list_index (kids, (gpointer) selected);
1610 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1611 p2->mode = mode_menu_order[menu_elt];
1614 if (p2->mode == ONE_HACK)
1616 int list_elt = selected_list_element (s);
1617 p2->selected_hack = (list_elt >= 0
1618 ? s->list_elt_to_hack_number[list_elt]
1622 # define COPY(field, name) \
1623 if (p->field != p2->field) { \
1626 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1628 p->field = p2->field
1631 COPY(selected_hack, "selected_hack");
1633 COPY(timeout, "timeout");
1634 COPY(cycle, "cycle");
1635 COPY(lock_p, "lock_p");
1636 COPY(lock_timeout, "lock_timeout");
1638 COPY(dpms_enabled_p, "dpms_enabled_p");
1639 COPY(dpms_standby, "dpms_standby");
1640 COPY(dpms_suspend, "dpms_suspend");
1641 COPY(dpms_off, "dpms_off");
1644 COPY(verbose_p, "verbose_p");
1645 COPY(capture_stderr_p, "capture_stderr_p");
1646 COPY(splash_p, "splash_p");
1649 COPY(tmode, "tmode");
1651 COPY(install_cmap_p, "install_cmap_p");
1652 COPY(fade_p, "fade_p");
1653 COPY(unfade_p, "unfade_p");
1654 COPY(fade_seconds, "fade_seconds");
1656 COPY(grab_desktop_p, "grab_desktop_p");
1657 COPY(grab_video_p, "grab_video_p");
1658 COPY(random_image_p, "random_image_p");
1662 # define COPYSTR(FIELD,NAME) \
1665 strcmp(p->FIELD, p2->FIELD)) \
1669 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1671 if (p->FIELD && p->FIELD != p2->FIELD) \
1673 p->FIELD = p2->FIELD; \
1676 COPYSTR(image_directory, "image_directory");
1677 COPYSTR(text_literal, "text_literal");
1678 COPYSTR(text_file, "text_file");
1679 COPYSTR(text_program, "text_program");
1680 COPYSTR(text_url, "text_url");
1683 populate_prefs_page (s);
1687 Display *dpy = GDK_DISPLAY();
1688 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1689 sync_server_dpms_settings (dpy, enabled_p,
1690 p->dpms_standby / 1000,
1691 p->dpms_suspend / 1000,
1695 changed = demo_write_init_file (s, p);
1698 s->saving_p = False;
1703 /* Flush out any changes made in the popup dialog box (where changes
1704 take place only when the OK button is clicked.)
1707 flush_popup_changes_and_save (state *s)
1709 Bool changed = False;
1710 saver_preferences *p = &s->prefs;
1711 int list_elt = selected_list_element (s);
1713 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1714 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1716 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1717 const char *command = gtk_entry_get_text (cmd);
1722 if (s->saving_p) return False;
1728 if (maybe_reload_init_file (s) != 0)
1734 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1736 if (!strcasecmp (visual, "")) visual = "";
1737 else if (!strcasecmp (visual, "any")) visual = "";
1738 else if (!strcasecmp (visual, "default")) visual = "Default";
1739 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1740 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1741 else if (!strcasecmp (visual, "best")) visual = "Best";
1742 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1743 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1744 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1745 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1746 else if (!strcasecmp (visual, "color")) visual = "Color";
1747 else if (!strcasecmp (visual, "gl")) visual = "GL";
1748 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1749 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1750 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1751 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1752 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1753 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1754 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1755 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1756 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1759 gdk_beep (); /* unparsable */
1761 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1764 changed = flush_changes (s, list_elt, -1, command, visual);
1767 changed = demo_write_init_file (s, p);
1769 /* Do this to re-launch the hack if (and only if) the command line
1771 populate_demo_window (s, selected_list_element (s));
1775 s->saving_p = False;
1780 G_MODULE_EXPORT void
1781 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1783 state *s = global_state_kludge; /* I hate C so much... */
1784 if (! s->initializing_p)
1786 s->initializing_p = True;
1787 flush_dialog_changes_and_save (s);
1788 s->initializing_p = False;
1792 G_MODULE_EXPORT gboolean
1793 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1795 pref_changed_cb (widget, user_data);
1799 /* Callback on menu items in the "mode" options menu.
1801 G_MODULE_EXPORT void
1802 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1804 state *s = (state *) user_data;
1805 saver_preferences *p = &s->prefs;
1806 GtkWidget *list = name_to_widget (s, "list");
1810 gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
1812 saver_mode new_mode;
1816 if (menu_items->data == widget)
1819 menu_items = menu_items->next;
1821 if (!menu_items) abort();
1823 new_mode = mode_menu_order[menu_index];
1825 /* Keep the same list element displayed as before; except if we're
1826 switching *to* "one screensaver" mode from any other mode, set
1827 "the one" to be that which is currently selected.
1829 list_elt = selected_list_element (s);
1830 if (new_mode == ONE_HACK)
1831 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1834 saver_mode old_mode = p->mode;
1836 populate_demo_window (s, list_elt);
1837 force_list_select_item (s, list, list_elt, True);
1838 p->mode = old_mode; /* put it back, so the init file gets written */
1841 pref_changed_cb (widget, user_data);
1845 G_MODULE_EXPORT void
1846 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1847 gint page_num, gpointer user_data)
1849 state *s = global_state_kludge; /* I hate C so much... */
1850 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1852 /* If we're switching to page 0, schedule the current hack to be run.
1853 Otherwise, schedule it to stop. */
1855 populate_demo_window (s, selected_list_element (s));
1857 schedule_preview (s, 0);
1862 list_activated_cb (GtkTreeView *list,
1864 GtkTreeViewColumn *column,
1871 g_return_if_fail (!gdk_pointer_is_grabbed ());
1873 str = gtk_tree_path_to_string (path);
1874 list_elt = strtol (str, NULL, 10);
1878 run_hack (s, list_elt, True);
1882 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1884 state *s = (state *)data;
1885 GtkTreeModel *model;
1891 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1894 path = gtk_tree_model_get_path (model, &iter);
1895 str = gtk_tree_path_to_string (path);
1896 list_elt = strtol (str, NULL, 10);
1898 gtk_tree_path_free (path);
1901 populate_demo_window (s, list_elt);
1902 flush_dialog_changes_and_save (s);
1904 /* Re-populate the Settings window any time a new item is selected
1905 in the list, in case both windows are currently visible.
1907 populate_popup_window (s);
1910 #else /* !HAVE_GTK2 */
1912 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1913 list_select_cb that comes in
1914 *after* we've double-clicked.
1918 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1921 state *s = (state *) data;
1922 if (event->type == GDK_2BUTTON_PRESS)
1924 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1925 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1927 last_doubleclick_time = time ((time_t *) 0);
1930 run_hack (s, list_elt, True);
1938 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1940 state *s = (state *) data;
1941 time_t now = time ((time_t *) 0);
1943 if (now >= last_doubleclick_time + 2)
1945 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1946 populate_demo_window (s, list_elt);
1947 flush_dialog_changes_and_save (s);
1952 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1954 state *s = (state *) data;
1955 populate_demo_window (s, -1);
1956 flush_dialog_changes_and_save (s);
1959 #endif /* !HAVE_GTK2 */
1962 /* Called when the checkboxes that are in the left column of the
1963 scrolling list are clicked. This both populates the right pane
1964 (just as clicking on the label (really, listitem) does) and
1965 also syncs this checkbox with the right pane Enabled checkbox.
1970 GtkCellRendererToggle *toggle,
1972 #else /* !HAVE_GTK2 */
1974 #endif /* !HAVE_GTK2 */
1977 state *s = (state *) data;
1980 GtkScrolledWindow *scroller =
1981 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1982 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1983 GtkTreeModel *model = gtk_tree_view_get_model (list);
1984 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1987 #else /* !HAVE_GTK2 */
1988 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1989 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1991 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1992 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1993 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1994 #endif /* !HAVE_GTK2 */
2001 if (!gtk_tree_model_get_iter (model, &iter, path))
2003 g_warning ("bad path: %s", path_string);
2006 gtk_tree_path_free (path);
2008 gtk_tree_model_get (model, &iter,
2009 COL_ENABLED, &active,
2012 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2013 COL_ENABLED, !active,
2016 list_elt = strtol (path_string, NULL, 10);
2017 #else /* !HAVE_GTK2 */
2018 list_elt = gtk_list_child_position (list, line);
2019 #endif /* !HAVE_GTK2 */
2021 /* remember previous scroll position of the top of the list */
2022 adj = gtk_scrolled_window_get_vadjustment (scroller);
2023 scroll_top = GET_ADJ_VALUE (adj);
2025 flush_dialog_changes_and_save (s);
2026 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
2027 populate_demo_window (s, list_elt);
2029 /* restore the previous scroll position of the top of the list.
2030 this is weak, but I don't really know why it's moving... */
2031 gtk_adjustment_set_value (adj, scroll_top);
2037 GtkFileSelection *widget;
2038 } file_selection_data;
2043 store_image_directory (GtkWidget *button, gpointer user_data)
2045 file_selection_data *fsd = (file_selection_data *) user_data;
2046 state *s = fsd->state;
2047 GtkFileSelection *selector = fsd->widget;
2048 GtkWidget *top = s->toplevel_widget;
2049 saver_preferences *p = &s->prefs;
2050 const char *path = gtk_file_selection_get_filename (selector);
2052 if (p->image_directory && !strcmp(p->image_directory, path))
2053 return; /* no change */
2055 if (!directory_p (path))
2058 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2059 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2063 if (p->image_directory) free (p->image_directory);
2064 p->image_directory = normalize_directory (path);
2066 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2067 (p->image_directory ? p->image_directory : ""));
2068 demo_write_init_file (s, p);
2073 store_text_file (GtkWidget *button, gpointer user_data)
2075 file_selection_data *fsd = (file_selection_data *) user_data;
2076 state *s = fsd->state;
2077 GtkFileSelection *selector = fsd->widget;
2078 GtkWidget *top = s->toplevel_widget;
2079 saver_preferences *p = &s->prefs;
2080 const char *path = gtk_file_selection_get_filename (selector);
2082 if (p->text_file && !strcmp(p->text_file, path))
2083 return; /* no change */
2088 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2089 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2093 if (p->text_file) free (p->text_file);
2094 p->text_file = normalize_directory (path);
2096 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2097 (p->text_file ? p->text_file : ""));
2098 demo_write_init_file (s, p);
2103 store_text_program (GtkWidget *button, gpointer user_data)
2105 file_selection_data *fsd = (file_selection_data *) user_data;
2106 state *s = fsd->state;
2107 GtkFileSelection *selector = fsd->widget;
2108 /*GtkWidget *top = s->toplevel_widget;*/
2109 saver_preferences *p = &s->prefs;
2110 const char *path = gtk_file_selection_get_filename (selector);
2112 if (p->text_program && !strcmp(p->text_program, path))
2113 return; /* no change */
2119 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2120 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2125 if (p->text_program) free (p->text_program);
2126 p->text_program = normalize_directory (path);
2128 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2129 (p->text_program ? p->text_program : ""));
2130 demo_write_init_file (s, p);
2136 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2138 file_selection_data *fsd = (file_selection_data *) user_data;
2139 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2143 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2145 browse_image_dir_cancel (button, user_data);
2146 store_image_directory (button, user_data);
2150 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2152 browse_image_dir_cancel (button, user_data);
2153 store_text_file (button, user_data);
2157 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2159 browse_image_dir_cancel (button, user_data);
2160 store_text_program (button, user_data);
2164 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2166 browse_image_dir_cancel (widget, user_data);
2170 G_MODULE_EXPORT void
2171 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2173 state *s = global_state_kludge; /* I hate C so much... */
2174 saver_preferences *p = &s->prefs;
2175 static file_selection_data *fsd = 0;
2177 GtkFileSelection *selector = GTK_FILE_SELECTION(
2178 gtk_file_selection_new ("Please select the image directory."));
2181 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2183 fsd->widget = selector;
2186 if (p->image_directory && *p->image_directory)
2187 gtk_file_selection_set_filename (selector, p->image_directory);
2189 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2190 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2192 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2193 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2195 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2196 GTK_SIGNAL_FUNC (browse_image_dir_close),
2199 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2201 gtk_window_set_modal (GTK_WINDOW (selector), True);
2202 gtk_widget_show (GTK_WIDGET (selector));
2206 G_MODULE_EXPORT void
2207 browse_text_file_cb (GtkButton *button, gpointer user_data)
2209 state *s = global_state_kludge; /* I hate C so much... */
2210 saver_preferences *p = &s->prefs;
2211 static file_selection_data *fsd = 0;
2213 GtkFileSelection *selector = GTK_FILE_SELECTION(
2214 gtk_file_selection_new ("Please select a text file."));
2217 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2219 fsd->widget = selector;
2222 if (p->text_file && *p->text_file)
2223 gtk_file_selection_set_filename (selector, p->text_file);
2225 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2226 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2228 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2229 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2231 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2232 GTK_SIGNAL_FUNC (browse_image_dir_close),
2235 gtk_window_set_modal (GTK_WINDOW (selector), True);
2236 gtk_widget_show (GTK_WIDGET (selector));
2240 G_MODULE_EXPORT void
2241 browse_text_program_cb (GtkButton *button, gpointer user_data)
2243 state *s = global_state_kludge; /* I hate C so much... */
2244 saver_preferences *p = &s->prefs;
2245 static file_selection_data *fsd = 0;
2247 GtkFileSelection *selector = GTK_FILE_SELECTION(
2248 gtk_file_selection_new ("Please select a text-generating program."));
2251 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2253 fsd->widget = selector;
2256 if (p->text_program && *p->text_program)
2257 gtk_file_selection_set_filename (selector, p->text_program);
2259 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2260 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2262 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2263 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2265 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2266 GTK_SIGNAL_FUNC (browse_image_dir_close),
2269 gtk_window_set_modal (GTK_WINDOW (selector), True);
2270 gtk_widget_show (GTK_WIDGET (selector));
2277 G_MODULE_EXPORT void
2278 settings_cb (GtkButton *button, gpointer user_data)
2280 state *s = global_state_kludge; /* I hate C so much... */
2281 int list_elt = selected_list_element (s);
2283 populate_demo_window (s, list_elt); /* reset the widget */
2284 populate_popup_window (s); /* create UI on popup window */
2285 gtk_widget_show (s->popup_widget);
2289 settings_sync_cmd_text (state *s)
2292 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2293 char *cmd_line = get_configurator_command_line (s->cdata, False);
2294 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2295 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2297 # endif /* HAVE_XML */
2300 G_MODULE_EXPORT void
2301 settings_adv_cb (GtkButton *button, gpointer user_data)
2303 state *s = global_state_kludge; /* I hate C so much... */
2304 GtkNotebook *notebook =
2305 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2307 settings_sync_cmd_text (s);
2308 gtk_notebook_set_page (notebook, 1);
2311 G_MODULE_EXPORT void
2312 settings_std_cb (GtkButton *button, gpointer user_data)
2314 state *s = global_state_kludge; /* I hate C so much... */
2315 GtkNotebook *notebook =
2316 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2318 /* Re-create UI to reflect the in-progress command-line settings. */
2319 populate_popup_window (s);
2321 gtk_notebook_set_page (notebook, 0);
2324 G_MODULE_EXPORT void
2325 settings_reset_cb (GtkButton *button, gpointer user_data)
2328 state *s = global_state_kludge; /* I hate C so much... */
2329 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2330 char *cmd_line = get_configurator_command_line (s->cdata, True);
2331 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2332 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2334 populate_popup_window (s);
2335 # endif /* HAVE_XML */
2338 G_MODULE_EXPORT void
2339 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2340 gint page_num, gpointer user_data)
2342 state *s = global_state_kludge; /* I hate C so much... */
2343 GtkWidget *adv = name_to_widget (s, "adv_button");
2344 GtkWidget *std = name_to_widget (s, "std_button");
2348 gtk_widget_show (adv);
2349 gtk_widget_hide (std);
2351 else if (page_num == 1)
2353 gtk_widget_hide (adv);
2354 gtk_widget_show (std);
2362 G_MODULE_EXPORT void
2363 settings_cancel_cb (GtkButton *button, gpointer user_data)
2365 state *s = global_state_kludge; /* I hate C so much... */
2366 gtk_widget_hide (s->popup_widget);
2369 G_MODULE_EXPORT void
2370 settings_ok_cb (GtkButton *button, gpointer user_data)
2372 state *s = global_state_kludge; /* I hate C so much... */
2373 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2374 int page = gtk_notebook_get_current_page (notebook);
2377 /* Regenerate the command-line from the widget contents before saving.
2378 But don't do this if we're looking at the command-line page already,
2379 or we will blow away what they typed... */
2380 settings_sync_cmd_text (s);
2382 flush_popup_changes_and_save (s);
2383 gtk_widget_hide (s->popup_widget);
2387 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2389 state *s = (state *) data;
2390 settings_cancel_cb (0, (gpointer) s);
2396 /* Populating the various widgets
2400 /* Returns the number of the last hack run by the server.
2403 server_current_hack (void)
2407 unsigned long nitems, bytesafter;
2408 unsigned char *dataP = 0;
2409 Display *dpy = GDK_DISPLAY();
2410 int hack_number = -1;
2412 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2413 XA_SCREENSAVER_STATUS,
2414 0, 3, False, XA_INTEGER,
2415 &type, &format, &nitems, &bytesafter,
2418 && type == XA_INTEGER
2422 PROP32 *data = (PROP32 *) dataP;
2423 hack_number = (int) data[2] - 1;
2426 if (dataP) XFree (dataP);
2432 /* Finds the number of the last hack that was run, and makes that item be
2433 selected by default.
2436 scroll_to_current_hack (state *s)
2438 saver_preferences *p = &s->prefs;
2439 int hack_number = -1;
2441 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2442 hack_number = p->selected_hack;
2443 if (hack_number < 0) /* otherwise, use the last-run */
2444 hack_number = server_current_hack ();
2445 if (hack_number < 0) /* failing that, last "one mode" */
2446 hack_number = p->selected_hack;
2447 if (hack_number < 0) /* failing that, newest hack. */
2449 /* We should only get here if the user does not have a .xscreensaver
2450 file, and the screen has not been blanked with a hack since X
2451 started up: in other words, this is probably a fresh install.
2453 Instead of just defaulting to hack #0 (in either "programs" or
2454 "alphabetical" order) let's try to default to the last runnable
2455 hack in the "programs" list: this is probably the hack that was
2456 most recently added to the xscreensaver distribution (and so
2457 it's probably the currently-coolest one!)
2459 hack_number = p->screenhacks_count-1;
2460 while (hack_number > 0 &&
2461 ! (s->hacks_available_p[hack_number] &&
2462 p->screenhacks[hack_number]->enabled_p))
2466 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2468 int list_elt = s->hack_number_to_list_elt[hack_number];
2469 GtkWidget *list = name_to_widget (s, "list");
2470 force_list_select_item (s, list, list_elt, True);
2471 populate_demo_window (s, list_elt);
2477 populate_hack_list (state *s)
2479 Display *dpy = GDK_DISPLAY();
2481 saver_preferences *p = &s->prefs;
2482 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2483 GtkListStore *model;
2484 GtkTreeSelection *selection;
2485 GtkCellRenderer *ren;
2489 g_object_get (G_OBJECT (list),
2494 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2495 g_object_set (G_OBJECT (list), "model", model, NULL);
2496 g_object_unref (model);
2498 ren = gtk_cell_renderer_toggle_new ();
2499 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2501 "active", COL_ENABLED,
2504 g_signal_connect (ren, "toggled",
2505 G_CALLBACK (list_checkbox_cb),
2508 ren = gtk_cell_renderer_text_new ();
2509 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2510 _("Screen Saver"), ren,
2514 g_signal_connect_after (list, "row_activated",
2515 G_CALLBACK (list_activated_cb),
2518 selection = gtk_tree_view_get_selection (list);
2519 g_signal_connect (selection, "changed",
2520 G_CALLBACK (list_select_changed_cb),
2525 for (i = 0; i < s->list_count; i++)
2527 int hack_number = s->list_elt_to_hack_number[i];
2528 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2530 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2532 if (!hack) continue;
2534 /* If we're to suppress uninstalled hacks, check $PATH now. */
2535 if (p->ignore_uninstalled_p && !available_p)
2538 pretty_name = (hack->name
2539 ? strdup (hack->name)
2540 : make_hack_name (dpy, hack->command));
2544 /* Make the text foreground be the color of insensitive widgets
2545 (but don't actually make it be insensitive, since we still
2546 want to be able to click on it.)
2548 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
2549 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2550 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2551 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2553 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2554 /* " background=\"#%02X%02X%02X\"" */
2556 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2557 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2563 gtk_list_store_append (model, &iter);
2564 gtk_list_store_set (model, &iter,
2565 COL_ENABLED, hack->enabled_p,
2566 COL_NAME, pretty_name,
2571 #else /* !HAVE_GTK2 */
2573 saver_preferences *p = &s->prefs;
2574 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2576 for (i = 0; i < s->list_count; i++)
2578 int hack_number = s->list_elt_to_hack_number[i];
2579 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2581 /* A GtkList must contain only GtkListItems, but those can contain
2582 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2583 and a Label. We handle single and double click events on the
2584 line itself, for clicking on the text, but the interior checkbox
2585 also handles its own events.
2588 GtkWidget *line_hbox;
2589 GtkWidget *line_check;
2590 GtkWidget *line_label;
2592 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2594 if (!hack) continue;
2596 /* If we're to suppress uninstalled hacks, check $PATH now. */
2597 if (p->ignore_uninstalled_p && !available_p)
2600 pretty_name = (hack->name
2601 ? strdup (hack->name)
2602 : make_hack_name (hack->command));
2604 line = gtk_list_item_new ();
2605 line_hbox = gtk_hbox_new (FALSE, 0);
2606 line_check = gtk_check_button_new ();
2607 line_label = gtk_label_new (pretty_name);
2609 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2610 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2611 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2613 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2615 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2617 gtk_widget_show (line_check);
2618 gtk_widget_show (line_label);
2619 gtk_widget_show (line_hbox);
2620 gtk_widget_show (line);
2624 gtk_container_add (GTK_CONTAINER (list), line);
2625 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2626 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2629 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2630 GTK_SIGNAL_FUNC (list_checkbox_cb),
2633 gtk_widget_show (line);
2637 /* Make the widget be colored like insensitive widgets
2638 (but don't actually make it be insensitive, since we
2639 still want to be able to click on it.)
2641 GtkRcStyle *rc_style;
2644 gtk_widget_realize (GTK_WIDGET (line_label));
2646 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2647 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2649 rc_style = gtk_rc_style_new ();
2650 rc_style->fg[GTK_STATE_NORMAL] = fg;
2651 rc_style->bg[GTK_STATE_NORMAL] = bg;
2652 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2654 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2655 gtk_rc_style_unref (rc_style);
2659 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2660 GTK_SIGNAL_FUNC (list_select_cb),
2662 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2663 GTK_SIGNAL_FUNC (list_unselect_cb),
2665 #endif /* !HAVE_GTK2 */
2669 update_list_sensitivity (state *s)
2671 saver_preferences *p = &s->prefs;
2672 Bool sensitive = (p->mode == RANDOM_HACKS ||
2673 p->mode == RANDOM_HACKS_SAME ||
2674 p->mode == ONE_HACK);
2675 Bool checkable = (p->mode == RANDOM_HACKS ||
2676 p->mode == RANDOM_HACKS_SAME);
2677 Bool blankable = (p->mode != DONT_BLANK);
2680 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2681 GtkWidget *use = name_to_widget (s, "use_col_frame");
2682 #endif /* HAVE_GTK2 */
2683 GtkWidget *scroller = name_to_widget (s, "scroller");
2684 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2685 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2688 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2689 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2690 #else /* !HAVE_GTK2 */
2691 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2692 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2694 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2695 #endif /* !HAVE_GTK2 */
2696 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2697 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2699 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2702 gtk_tree_view_column_set_visible (use, checkable);
2703 #else /* !HAVE_GTK2 */
2705 gtk_widget_show (use); /* the "Use" column header */
2707 gtk_widget_hide (use);
2711 GtkBin *line = GTK_BIN (kids->data);
2712 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2713 GtkWidget *line_check =
2714 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2717 gtk_widget_show (line_check);
2719 gtk_widget_hide (line_check);
2723 #endif /* !HAVE_GTK2 */
2728 populate_prefs_page (state *s)
2730 saver_preferences *p = &s->prefs;
2732 Bool can_lock_p = True;
2734 /* Disable all the "lock" controls if locking support was not provided
2735 at compile-time, or if running on MacOS. */
2736 # if defined(NO_LOCKING) || defined(__APPLE__)
2741 /* If there is only one screen, the mode menu contains
2742 "random" but not "random-same".
2744 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2745 p->mode = RANDOM_HACKS;
2748 /* The file supports timeouts of less than a minute, but the GUI does
2749 not, so throttle the values to be at least one minute (since "0" is
2750 a bad rounding choice...)
2752 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2755 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2758 # define FMT_MINUTES(NAME,N) \
2759 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2761 # define FMT_SECONDS(NAME,N) \
2762 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2764 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2765 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2766 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2767 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2768 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2769 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2770 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2775 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2776 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2779 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2781 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2782 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2783 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2785 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2786 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2787 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2788 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2789 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2790 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2791 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2795 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2796 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2797 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2798 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2799 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2802 # undef TOGGLE_ACTIVE
2804 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2805 (p->image_directory ? p->image_directory : ""));
2806 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2808 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2811 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2812 (p->text_literal ? p->text_literal : ""));
2813 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2814 (p->text_file ? p->text_file : ""));
2815 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2816 (p->text_program ? p->text_program : ""));
2817 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2818 (p->text_url ? p->text_url : ""));
2820 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2821 p->tmode == TEXT_LITERAL);
2822 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2823 p->tmode == TEXT_FILE);
2824 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2825 p->tmode == TEXT_FILE);
2826 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2827 p->tmode == TEXT_PROGRAM);
2828 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2829 p->tmode == TEXT_PROGRAM);
2830 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2831 p->tmode == TEXT_URL);
2834 /* Map the `saver_mode' enum to mode menu to values. */
2836 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2839 for (i = 0; i < countof(mode_menu_order); i++)
2840 if (mode_menu_order[i] == p->mode)
2842 gtk_option_menu_set_history (opt, i);
2843 update_list_sensitivity (s);
2847 Bool found_any_writable_cells = False;
2848 Bool fading_possible = False;
2849 Bool dpms_supported = False;
2851 Display *dpy = GDK_DISPLAY();
2852 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2854 for (i = 0; i < nscreens; i++)
2856 Screen *s = ScreenOfDisplay (dpy, i);
2857 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2859 found_any_writable_cells = True;
2864 fading_possible = found_any_writable_cells;
2865 #ifdef HAVE_XF86VMODE_GAMMA
2866 fading_possible = True;
2869 #ifdef HAVE_DPMS_EXTENSION
2871 int op = 0, event = 0, error = 0;
2872 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2873 dpms_supported = True;
2875 #endif /* HAVE_DPMS_EXTENSION */
2878 # define SENSITIZE(NAME,SENSITIVEP) \
2879 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2881 /* Blanking and Locking
2883 SENSITIZE ("lock_button", can_lock_p);
2884 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2885 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2889 SENSITIZE ("dpms_frame", dpms_supported);
2890 SENSITIZE ("dpms_button", dpms_supported);
2891 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2892 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2893 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2894 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2895 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2896 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2897 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2898 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2899 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2903 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2904 SENSITIZE ("install_button", found_any_writable_cells);
2905 SENSITIZE ("fade_button", fading_possible);
2906 SENSITIZE ("unfade_button", fading_possible);
2908 SENSITIZE ("fade_label", (fading_possible &&
2909 (p->fade_p || p->unfade_p)));
2910 SENSITIZE ("fade_spinbutton", (fading_possible &&
2911 (p->fade_p || p->unfade_p)));
2919 populate_popup_window (state *s)
2921 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2922 char *doc_string = 0;
2924 /* #### not in Gtk 1.2
2925 gtk_label_set_selectable (doc);
2931 free_conf_data (s->cdata);
2936 saver_preferences *p = &s->prefs;
2937 int list_elt = selected_list_element (s);
2938 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2939 ? s->list_elt_to_hack_number[list_elt]
2941 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2944 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2945 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2946 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2947 s->cdata = load_configurator (cmd_line, s->debug_p);
2948 if (s->cdata && s->cdata->widget)
2949 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2954 doc_string = (s->cdata
2955 ? s->cdata->description
2957 # else /* !HAVE_XML */
2958 doc_string = _("Descriptions not available: no XML support compiled in.");
2959 # endif /* !HAVE_XML */
2961 gtk_label_set_text (doc, (doc_string
2963 : _("No description available.")));
2968 sensitize_demo_widgets (state *s, Bool sensitive_p)
2970 const char *names[] = { "demo", "settings",
2971 "cmd_label", "cmd_text", "manual",
2972 "visual", "visual_combo" };
2974 for (i = 0; i < countof(names); i++)
2976 GtkWidget *w = name_to_widget (s, names[i]);
2977 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2983 sensitize_menu_items (state *s, Bool force_p)
2985 static Bool running_p = False;
2986 static time_t last_checked = 0;
2987 time_t now = time ((time_t *) 0);
2988 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
2992 if (force_p || now > last_checked + 10) /* check every 10 seconds */
2994 running_p = xscreensaver_running_p (s);
2995 last_checked = time ((time_t *) 0);
2998 for (i = 0; i < countof(names); i++)
3000 GtkWidget *w = name_to_widget (s, names[i]);
3001 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
3006 /* When the File menu is de-posted after a "Restart Daemon" command,
3007 the window underneath doesn't repaint for some reason. I guess this
3008 is a bug in exposure handling in GTK or GDK. This works around it.
3011 force_dialog_repaint (state *s)
3014 /* Tell GDK to invalidate and repaint the whole window.
3016 GdkWindow *w = GET_WINDOW (s->toplevel_widget);
3017 GdkRegion *region = gdk_region_new ();
3019 rect.x = rect.y = 0;
3020 rect.width = rect.height = 32767;
3021 gdk_region_union_with_rect (region, &rect);
3022 gdk_window_invalidate_region (w, region, True);
3023 gdk_region_destroy (region);
3024 gdk_window_process_updates (w, True);
3026 /* Force the server to send an exposure event by creating and then
3027 destroying a window as a child of the top level shell.
3029 Display *dpy = GDK_DISPLAY();
3030 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
3032 XWindowAttributes xgwa;
3033 XGetWindowAttributes (dpy, parent, &xgwa);
3034 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
3035 XMapRaised (dpy, w);
3036 XDestroyWindow (dpy, w);
3042 /* Even though we've given these text fields a maximum number of characters,
3043 their default size is still about 30 characters wide -- so measure out
3044 a string in their font, and resize them to just fit that.
3047 fix_text_entry_sizes (state *s)
3051 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
3052 const char * const spinbuttons[] = {
3053 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3054 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3055 "dpms_off_spinbutton",
3056 "-fade_spinbutton" };
3060 for (i = 0; i < countof(spinbuttons); i++)
3062 const char *n = spinbuttons[i];
3064 while (*n == '-') n++, cols--;
3065 w = GTK_WIDGET (name_to_widget (s, n));
3066 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3067 gtk_widget_set_usize (w, width, -2);
3070 /* Now fix the width of the combo box.
3072 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3073 w = GTK_COMBO (w)->entry;
3074 width = gdk_string_width (w->style->font, "PseudoColor___");
3075 gtk_widget_set_usize (w, width, -2);
3077 /* Now fix the width of the file entry text.
3079 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3080 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3081 gtk_widget_set_usize (w, width, -2);
3083 /* Now fix the width of the command line text.
3085 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3086 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3087 gtk_widget_set_usize (w, width, -2);
3091 /* Now fix the height of the list widget:
3092 make it default to being around 10 text-lines high instead of 4.
3094 w = GTK_WIDGET (name_to_widget (s, "list"));
3098 int leading = 3; /* approximate is ok... */
3102 PangoFontMetrics *pain =
3103 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3104 gtk_widget_get_style (w)->font_desc,
3105 gtk_get_default_language ());
3106 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3107 pango_font_metrics_get_descent (pain));
3108 #else /* !HAVE_GTK2 */
3109 height = w->style->font->ascent + w->style->font->descent;
3110 #endif /* !HAVE_GTK2 */
3114 height += border * 2;
3115 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3116 gtk_widget_set_usize (w, -2, height);
3123 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3126 static char *up_arrow_xpm[] = {
3149 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3150 the end of the array (Gtk 1.2.5.) */
3151 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3152 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3155 static char *down_arrow_xpm[] = {
3178 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3179 the end of the array (Gtk 1.2.5.) */
3180 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3181 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3185 pixmapify_button (state *s, int down_p)
3189 GtkWidget *pixmapwid;
3193 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3194 style = gtk_widget_get_style (w);
3196 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3197 &style->bg[GTK_STATE_NORMAL],
3199 ? (gchar **) down_arrow_xpm
3200 : (gchar **) up_arrow_xpm));
3201 pixmapwid = gtk_pixmap_new (pixmap, mask);
3202 gtk_widget_show (pixmapwid);
3203 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3204 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3208 map_next_button_cb (GtkWidget *w, gpointer user_data)
3210 state *s = (state *) user_data;
3211 pixmapify_button (s, 1);
3215 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3217 state *s = (state *) user_data;
3218 pixmapify_button (s, 0);
3220 #endif /* !HAVE_GTK2 */
3224 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3228 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3229 GtkAllocation *allocation,
3233 GtkWidgetAuxInfo *aux_info;
3235 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3237 aux_info->width = allocation->width;
3238 aux_info->height = -2;
3242 gtk_widget_size_request (label, &req);
3245 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3248 eschew_gtk_lossage (GtkLabel *label)
3250 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3251 aux_info->width = GTK_WIDGET (label)->allocation.width;
3252 aux_info->height = -2;
3256 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3258 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3259 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3262 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3264 gtk_widget_queue_resize (GTK_WIDGET (label));
3266 #endif /* !HAVE_GTK2 */
3270 populate_demo_window (state *s, int list_elt)
3272 Display *dpy = GDK_DISPLAY();
3273 saver_preferences *p = &s->prefs;
3276 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3277 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3278 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3279 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3280 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3282 if (p->mode == BLANK_ONLY)
3285 pretty_name = strdup (_("Blank Screen"));
3286 schedule_preview (s, 0);
3288 else if (p->mode == DONT_BLANK)
3291 pretty_name = strdup (_("Screen Saver Disabled"));
3292 schedule_preview (s, 0);
3296 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3297 ? s->list_elt_to_hack_number[list_elt]
3299 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3303 ? strdup (hack->name)
3304 : make_hack_name (dpy, hack->command))
3308 schedule_preview (s, hack->command);
3310 schedule_preview (s, 0);
3314 pretty_name = strdup (_("Preview"));
3316 gtk_frame_set_label (frame1, _(pretty_name));
3317 gtk_frame_set_label (frame2, _(pretty_name));
3319 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3320 gtk_entry_set_position (cmd, 0);
3324 sprintf (title, _("%s: %.100s Settings"),
3325 progclass, (pretty_name ? pretty_name : "???"));
3326 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3329 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3331 ? (hack->visual && *hack->visual
3336 sensitize_demo_widgets (s, (hack ? True : False));
3338 if (pretty_name) free (pretty_name);
3340 ensure_selected_item_visible (list);
3342 s->_selected_list_element = list_elt;
3347 widget_deleter (GtkWidget *widget, gpointer data)
3349 /* #### Well, I want to destroy these widgets, but if I do that, they get
3350 referenced again, and eventually I get a SEGV. So instead of
3351 destroying them, I'll just hide them, and leak a bunch of memory
3352 every time the disk file changes. Go go go Gtk!
3354 #### Ok, that's a lie, I get a crash even if I just hide the widget
3355 and don't ever delete it. Fuck!
3358 gtk_widget_destroy (widget);
3360 gtk_widget_hide (widget);
3365 static char **sort_hack_cmp_names_kludge;
3367 sort_hack_cmp (const void *a, const void *b)
3373 int aa = *(int *) a;
3374 int bb = *(int *) b;
3375 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3376 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3377 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3383 initialize_sort_map (state *s)
3385 Display *dpy = GDK_DISPLAY();
3386 saver_preferences *p = &s->prefs;
3389 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3390 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3391 if (s->hacks_available_p) free (s->hacks_available_p);
3393 s->list_elt_to_hack_number = (int *)
3394 calloc (sizeof(int), p->screenhacks_count + 1);
3395 s->hack_number_to_list_elt = (int *)
3396 calloc (sizeof(int), p->screenhacks_count + 1);
3397 s->hacks_available_p = (Bool *)
3398 calloc (sizeof(Bool), p->screenhacks_count + 1);
3399 s->total_available = 0;
3401 /* Check which hacks actually exist on $PATH
3403 for (i = 0; i < p->screenhacks_count; i++)
3405 screenhack *hack = p->screenhacks[i];
3406 int on = on_path_p (hack->command) ? 1 : 0;
3407 s->hacks_available_p[i] = on;
3408 s->total_available += on;
3411 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3415 for (i = 0; i < p->screenhacks_count; i++)
3417 if (!p->ignore_uninstalled_p ||
3418 s->hacks_available_p[i])
3419 s->list_elt_to_hack_number[j++] = i;
3423 for (; j < p->screenhacks_count; j++)
3424 s->list_elt_to_hack_number[j] = -1;
3427 /* Generate list of sortable names (once)
3429 sort_hack_cmp_names_kludge = (char **)
3430 calloc (sizeof(char *), p->screenhacks_count);
3431 for (i = 0; i < p->screenhacks_count; i++)
3433 screenhack *hack = p->screenhacks[i];
3434 char *name = (hack->name && *hack->name
3435 ? strdup (hack->name)
3436 : make_hack_name (dpy, hack->command));
3438 for (str = name; *str; str++)
3439 *str = tolower(*str);
3440 sort_hack_cmp_names_kludge[i] = name;
3443 /* Sort list->hack map alphabetically
3445 qsort (s->list_elt_to_hack_number,
3446 p->screenhacks_count,
3447 sizeof(*s->list_elt_to_hack_number),
3452 for (i = 0; i < p->screenhacks_count; i++)
3453 free (sort_hack_cmp_names_kludge[i]);
3454 free (sort_hack_cmp_names_kludge);
3455 sort_hack_cmp_names_kludge = 0;
3457 /* Build inverse table */
3458 for (i = 0; i < p->screenhacks_count; i++)
3460 int n = s->list_elt_to_hack_number[i];
3462 s->hack_number_to_list_elt[n] = i;
3468 maybe_reload_init_file (state *s)
3470 Display *dpy = GDK_DISPLAY();
3471 saver_preferences *p = &s->prefs;
3474 static Bool reentrant_lock = False;
3475 if (reentrant_lock) return 0;
3476 reentrant_lock = True;
3478 if (init_file_changed_p (p))
3480 const char *f = init_file_name();
3485 if (!f || !*f) return 0;
3486 b = (char *) malloc (strlen(f) + 1024);
3489 "file \"%s\" has changed, reloading.\n"),
3491 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3494 load_init_file (dpy, p);
3495 initialize_sort_map (s);
3497 list_elt = selected_list_element (s);
3498 list = name_to_widget (s, "list");
3499 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3500 populate_hack_list (s);
3501 force_list_select_item (s, list, list_elt, True);
3502 populate_prefs_page (s);
3503 populate_demo_window (s, list_elt);
3504 ensure_selected_item_visible (list);
3509 reentrant_lock = False;
3515 /* Making the preview window have the right X visual (so that GL works.)
3518 static Visual *get_best_gl_visual (state *);
3521 x_visual_to_gdk_visual (Visual *xv)
3523 GList *gvs = gdk_list_visuals();
3524 if (!xv) return gdk_visual_get_system();
3525 for (; gvs; gvs = gvs->next)
3527 GdkVisual *gv = (GdkVisual *) gvs->data;
3528 if (xv == GDK_VISUAL_XVISUAL (gv))
3531 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3532 blurb(), (unsigned long) xv->visualid);
3537 clear_preview_window (state *s)
3543 if (!s->toplevel_widget) return; /* very early */
3544 p = name_to_widget (s, "preview");
3545 window = GET_WINDOW (p);
3547 if (!window) return;
3549 /* Flush the widget background down into the window, in case a subproc
3551 style = gtk_widget_get_style (p);
3552 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
3553 gdk_window_clear (window);
3556 int list_elt = selected_list_element (s);
3557 int hack_number = (list_elt >= 0
3558 ? s->list_elt_to_hack_number[list_elt]
3560 Bool available_p = (hack_number >= 0
3561 ? s->hacks_available_p [hack_number]
3563 Bool nothing_p = (s->total_available < 5);
3566 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3567 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3568 (s->running_preview_error_p
3569 ? (available_p ? 1 :
3572 #else /* !HAVE_GTK2 */
3573 if (s->running_preview_error_p)
3575 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3576 const char * const lines2[] = { N_("Not"), N_("Installed") };
3577 int nlines = countof(lines1);
3578 int lh = p->style->font->ascent + p->style->font->descent;
3582 const char * const *lines = (available_p ? lines1 : lines2);
3584 gdk_window_get_size (window, &w, &h);
3585 y = (h - (lh * nlines)) / 2;
3586 y += p->style->font->ascent;
3587 for (i = 0; i < nlines; i++)
3589 int sw = gdk_string_width (p->style->font, _(lines[i]));
3590 int x = (w - sw) / 2;
3591 gdk_draw_string (window, p->style->font,
3592 p->style->fg_gc[GTK_STATE_NORMAL],
3597 #endif /* !HAVE_GTK2 */
3605 reset_preview_window (state *s)
3607 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3608 when you kill one and re-start another on the same window. So maybe
3609 it's best to just always destroy and recreate the preview window
3610 when changing hacks, instead of always trying to reuse the same one?
3612 GtkWidget *pr = name_to_widget (s, "preview");
3613 if (GET_REALIZED (pr))
3615 GdkWindow *window = GET_WINDOW (pr);
3616 Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3618 gtk_widget_hide (pr);
3619 gtk_widget_unrealize (pr);
3620 gtk_widget_realize (pr);
3621 gtk_widget_show (pr);
3622 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3624 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3632 fix_preview_visual (state *s)
3634 GtkWidget *widget = name_to_widget (s, "preview");
3635 Visual *xvisual = get_best_gl_visual (s);
3636 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3637 GdkVisual *dvisual = gdk_visual_get_system();
3638 GdkColormap *cmap = (visual == dvisual
3639 ? gdk_colormap_get_system ()
3640 : gdk_colormap_new (visual, False));
3643 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3644 (visual == dvisual ? "default" : "non-default"),
3645 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3647 if (!GET_REALIZED (widget) ||
3648 gtk_widget_get_visual (widget) != visual)
3650 gtk_widget_unrealize (widget);
3651 gtk_widget_set_visual (widget, visual);
3652 gtk_widget_set_colormap (widget, cmap);
3653 gtk_widget_realize (widget);
3656 /* Set the Widget colors to be white-on-black. */
3658 GdkWindow *window = GET_WINDOW (widget);
3659 GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
3660 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3661 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3662 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3663 GdkGC *fgc = gdk_gc_new(window);
3664 GdkGC *bgc = gdk_gc_new(window);
3665 if (!gdk_color_white (cmap, fg)) abort();
3666 if (!gdk_color_black (cmap, bg)) abort();
3667 gdk_gc_set_foreground (fgc, fg);
3668 gdk_gc_set_background (fgc, bg);
3669 gdk_gc_set_foreground (bgc, bg);
3670 gdk_gc_set_background (bgc, fg);
3671 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3672 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3673 gtk_widget_set_style (widget, style);
3675 /* For debugging purposes, put a title on the window (so that
3676 it can be easily found in the output of "xwininfo -tree".)
3678 gdk_window_set_title (window, "Preview");
3681 gtk_widget_show (widget);
3689 subproc_pretty_name (state *s)
3691 if (s->running_preview_cmd)
3693 char *ps = strdup (s->running_preview_cmd);
3694 char *ss = strchr (ps, ' ');
3696 ss = strrchr (ps, '/');
3707 return strdup ("???");
3712 reap_zombies (state *s)
3714 int wait_status = 0;
3716 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3720 if (pid == s->running_preview_pid)
3722 char *ss = subproc_pretty_name (s);
3723 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3724 (unsigned long) pid, ss);
3728 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3729 (unsigned long) pid);
3735 /* Mostly lifted from driver/subprocs.c */
3737 get_best_gl_visual (state *s)
3739 Display *dpy = GDK_DISPLAY();
3748 av[ac++] = "xscreensaver-gl-helper";
3753 perror ("error creating pipe:");
3760 switch ((int) (forked = fork ()))
3764 sprintf (buf, "%s: couldn't fork", blurb());
3772 close (in); /* don't need this one */
3773 close (ConnectionNumber (dpy)); /* close display fd */
3775 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3777 perror ("could not dup() a new stdout:");
3781 execvp (av[0], av); /* shouldn't return. */
3783 if (errno != ENOENT)
3785 /* Ignore "no such file or directory" errors, unless verbose.
3786 Issue all other exec errors, though. */
3787 sprintf (buf, "%s: running %s", blurb(), av[0]);
3791 /* Note that one must use _exit() instead of exit() in procs forked
3792 off of Gtk programs -- Gtk installs an atexit handler that has a
3793 copy of the X connection (which we've already closed, for safety.)
3794 If one uses exit() instead of _exit(), then one sometimes gets a
3795 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3797 _exit (1); /* exits fork */
3803 int wait_status = 0;
3805 FILE *f = fdopen (in, "r");
3809 close (out); /* don't need this one */
3812 if (!fgets (buf, sizeof(buf)-1, f))
3816 /* Wait for the child to die. */
3817 waitpid (-1, &wait_status, 0);
3819 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3825 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3831 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3833 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3834 blurb(), av[0], result);
3846 kill_preview_subproc (state *s, Bool reset_p)
3848 s->running_preview_error_p = False;
3851 clear_preview_window (s);
3853 if (s->subproc_check_timer_id)
3855 gtk_timeout_remove (s->subproc_check_timer_id);
3856 s->subproc_check_timer_id = 0;
3857 s->subproc_check_countdown = 0;
3860 if (s->running_preview_pid)
3862 int status = kill (s->running_preview_pid, SIGTERM);
3863 char *ss = subproc_pretty_name (s);
3870 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3871 blurb(), (unsigned long) s->running_preview_pid, ss);
3876 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3877 blurb(), (unsigned long) s->running_preview_pid, ss);
3883 waitpid(s->running_preview_pid, &endstatus, 0);
3885 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3886 (unsigned long) s->running_preview_pid, ss);
3890 s->running_preview_pid = 0;
3891 if (s->running_preview_cmd) free (s->running_preview_cmd);
3892 s->running_preview_cmd = 0;
3899 reset_preview_window (s);
3900 clear_preview_window (s);
3905 /* Immediately and unconditionally launches the given process,
3906 after appending the -window-id option; sets running_preview_pid.
3909 launch_preview_subproc (state *s)
3911 saver_preferences *p = &s->prefs;
3915 const char *cmd = s->desired_preview_cmd;
3917 GtkWidget *pr = name_to_widget (s, "preview");
3920 reset_preview_window (s);
3922 window = GET_WINDOW (pr);
3924 s->running_preview_error_p = False;
3926 if (s->preview_suppressed_p)
3928 kill_preview_subproc (s, False);
3932 new_cmd = malloc (strlen (cmd) + 40);
3934 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3937 /* No window id? No command to run. */
3943 strcpy (new_cmd, cmd);
3944 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3948 kill_preview_subproc (s, False);
3951 s->running_preview_error_p = True;
3952 clear_preview_window (s);
3956 switch ((int) (forked = fork ()))
3961 sprintf (buf, "%s: couldn't fork", blurb());
3963 s->running_preview_error_p = True;
3969 close (ConnectionNumber (GDK_DISPLAY()));
3971 hack_subproc_environment (id, s->debug_p);
3973 usleep (250000); /* pause for 1/4th second before launching, to give
3974 the previous program time to die and flush its X
3975 buffer, so we don't get leftover turds on the
3978 exec_command (p->shell, new_cmd, p->nice_inferior);
3979 /* Don't bother printing an error message when we are unable to
3980 exec subprocesses; we handle that by polling the pid later.
3982 Note that one must use _exit() instead of exit() in procs forked
3983 off of Gtk programs -- Gtk installs an atexit handler that has a
3984 copy of the X connection (which we've already closed, for safety.)
3985 If one uses exit() instead of _exit(), then one sometimes gets a
3986 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3988 _exit (1); /* exits child fork */
3993 if (s->running_preview_cmd) free (s->running_preview_cmd);
3994 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3995 s->running_preview_pid = forked;
3999 char *ss = subproc_pretty_name (s);
4000 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
4001 (unsigned long) forked, ss);
4008 schedule_preview_check (s);
4011 if (new_cmd) free (new_cmd);
4016 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
4019 hack_environment (state *s)
4021 static const char *def_path =
4022 # ifdef DEFAULT_PATH_PREFIX
4023 DEFAULT_PATH_PREFIX;
4028 Display *dpy = GDK_DISPLAY();
4029 const char *odpy = DisplayString (dpy);
4030 char *ndpy = (char *) malloc(strlen(odpy) + 20);
4031 strcpy (ndpy, "DISPLAY=");
4032 strcat (ndpy, odpy);
4037 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4039 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4040 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4041 So we must leak it (and/or the previous setting). Yay.
4044 if (def_path && *def_path)
4046 const char *opath = getenv("PATH");
4047 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4048 strcpy (npath, "PATH=");
4049 strcat (npath, def_path);
4050 strcat (npath, ":");
4051 strcat (npath, opath);
4055 /* do not free(npath) -- see above */
4058 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4064 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4066 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4067 necessary yet, but it will make programs work if we had invoked
4068 them with "-root" and not with "-window-id" -- which, of course,
4071 char *nssw = (char *) malloc (40);
4072 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4074 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4075 any more, right? It's not Posix, but everyone seems to have it. */
4080 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4082 /* do not free(nssw) -- see above */
4086 /* Called from a timer:
4087 Launches the currently-chosen subprocess, if it's not already running.
4088 If there's a different process running, kills it.
4091 update_subproc_timer (gpointer data)
4093 state *s = (state *) data;
4094 if (! s->desired_preview_cmd)
4095 kill_preview_subproc (s, True);
4096 else if (!s->running_preview_cmd ||
4097 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4098 launch_preview_subproc (s);
4100 s->subproc_timer_id = 0;
4101 return FALSE; /* do not re-execute timer */
4105 settings_timer (gpointer data)
4112 /* Call this when you think you might want a preview process running.
4113 It will set a timer that will actually launch that program a second
4114 from now, if you haven't changed your mind (to avoid double-click
4115 spazzing, etc.) `cmd' may be null meaning "no process".
4118 schedule_preview (state *s, const char *cmd)
4120 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4125 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4127 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4130 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4131 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4133 if (s->subproc_timer_id)
4134 gtk_timeout_remove (s->subproc_timer_id);
4135 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4139 /* Called from a timer:
4140 Checks to see if the subproc that should be running, actually is.
4143 check_subproc_timer (gpointer data)
4145 state *s = (state *) data;
4146 Bool again_p = True;
4148 if (s->running_preview_error_p || /* already dead */
4149 s->running_preview_pid <= 0)
4157 status = kill (s->running_preview_pid, 0);
4158 if (status < 0 && errno == ESRCH)
4159 s->running_preview_error_p = True;
4163 char *ss = subproc_pretty_name (s);
4164 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4165 (unsigned long) s->running_preview_pid, ss,
4166 (s->running_preview_error_p ? "dead" : "alive"));
4170 if (s->running_preview_error_p)
4172 clear_preview_window (s);
4177 /* Otherwise, it's currently alive. We might be checking again, or we
4178 might be satisfied. */
4180 if (--s->subproc_check_countdown <= 0)
4184 return TRUE; /* re-execute timer */
4187 s->subproc_check_timer_id = 0;
4188 s->subproc_check_countdown = 0;
4189 return FALSE; /* do not re-execute timer */
4194 /* Call this just after launching a subprocess.
4195 This sets a timer that will, five times a second for two seconds,
4196 check whether the program is still running. The assumption here
4197 is that if the process didn't stay up for more than a couple of
4198 seconds, then either the program doesn't exist, or it doesn't
4199 take a -window-id argument.
4202 schedule_preview_check (state *s)
4208 fprintf (stderr, "%s: scheduling check\n", blurb());
4210 if (s->subproc_check_timer_id)
4211 gtk_timeout_remove (s->subproc_check_timer_id);
4212 s->subproc_check_timer_id =
4213 gtk_timeout_add (1000 / ticks,
4214 check_subproc_timer, (gpointer) s);
4215 s->subproc_check_countdown = ticks * seconds;
4220 screen_blanked_p (void)
4224 unsigned long nitems, bytesafter;
4225 unsigned char *dataP = 0;
4226 Display *dpy = GDK_DISPLAY();
4227 Bool blanked_p = False;
4229 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4230 XA_SCREENSAVER_STATUS,
4231 0, 3, False, XA_INTEGER,
4232 &type, &format, &nitems, &bytesafter,
4235 && type == XA_INTEGER
4239 Atom *data = (Atom *) dataP;
4240 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4243 if (dataP) XFree (dataP);
4248 /* Wake up every now and then and see if the screen is blanked.
4249 If it is, kill off the small-window demo -- no point in wasting
4250 cycles by running two screensavers at once...
4253 check_blanked_timer (gpointer data)
4255 state *s = (state *) data;
4256 Bool blanked_p = screen_blanked_p ();
4257 if (blanked_p && s->running_preview_pid)
4260 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4261 kill_preview_subproc (s, True);
4264 return True; /* re-execute timer */
4268 /* How many screens are there (including Xinerama.)
4271 screen_count (Display *dpy)
4273 int nscreens = ScreenCount(dpy);
4274 # ifdef HAVE_XINERAMA
4277 int event_number, error_number;
4278 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4279 XineramaIsActive (dpy))
4281 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4282 if (xsi) XFree (xsi);
4285 # endif /* HAVE_XINERAMA */
4291 /* Setting window manager icon
4295 init_icon (GdkWindow *window)
4297 GdkBitmap *mask = 0;
4300 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4301 (gchar **) logo_50_xpm);
4303 gdk_window_set_icon (window, 0, pixmap, mask);
4307 /* The main demo-mode command loop.
4312 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4313 XrmRepresentation *type, XrmValue *value, XPointer closure)
4316 for (i = 0; quarks[i]; i++)
4318 if (bindings[i] == XrmBindTightly)
4319 fprintf (stderr, (i == 0 ? "" : "."));
4320 else if (bindings[i] == XrmBindLoosely)
4321 fprintf (stderr, "*");
4323 fprintf (stderr, " ??? ");
4324 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4327 fprintf (stderr, ": %s\n", (char *) value->addr);
4335 gnome_screensaver_window (Screen *screen)
4337 Display *dpy = DisplayOfScreen (screen);
4338 Window root = RootWindowOfScreen (screen);
4339 Window parent, *kids;
4341 Window gnome_window = 0;
4344 if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4346 for (i = 0; i < nkids; i++)
4350 unsigned long nitems, bytesafter;
4351 unsigned char *name;
4352 if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4353 False, XA_STRING, &type, &format, &nitems,
4357 && !strcmp ((char *) name, "gnome-screensaver"))
4359 gnome_window = kids[i];
4364 if (kids) XFree ((char *) kids);
4365 return gnome_window;
4369 gnome_screensaver_active_p (void)
4371 Display *dpy = GDK_DISPLAY();
4372 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4373 return (w ? True : False);
4377 kill_gnome_screensaver (void)
4379 Display *dpy = GDK_DISPLAY();
4380 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4381 if (w) XKillClient (dpy, (XID) w);
4385 kde_screensaver_active_p (void)
4387 FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4390 fgets (buf, sizeof(buf)-1, p);
4392 if (!strcmp (buf, "true\n"))
4399 kill_kde_screensaver (void)
4401 system ("dcop kdesktop KScreensaverIface enable false");
4406 the_network_is_not_the_computer (state *s)
4408 Display *dpy = GDK_DISPLAY();
4409 char *rversion = 0, *ruser = 0, *rhost = 0;
4410 char *luser, *lhost;
4412 struct passwd *p = getpwuid (getuid ());
4413 const char *d = DisplayString (dpy);
4415 # if defined(HAVE_UNAME)
4417 if (uname (&uts) < 0)
4418 lhost = "<UNKNOWN>";
4420 lhost = uts.nodename;
4422 strcpy (lhost, getenv("SYS$NODE"));
4423 # else /* !HAVE_UNAME && !VMS */
4424 strcat (lhost, "<UNKNOWN>");
4425 # endif /* !HAVE_UNAME && !VMS */
4427 if (p && p->pw_name)
4432 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4434 /* Make a buffer that's big enough for a number of copies of all the
4435 strings, plus some. */
4436 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4437 (ruser ? strlen(ruser) : 0) +
4438 (rhost ? strlen(rhost) : 0) +
4445 if (!rversion || !*rversion)
4449 "The XScreenSaver daemon doesn't seem to be running\n"
4450 "on display \"%s\". Launch it now?"),
4453 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4455 /* Warn that the two processes are running as different users.
4459 "%s is running as user \"%s\" on host \"%s\".\n"
4460 "But the xscreensaver managing display \"%s\"\n"
4461 "is running as user \"%s\" on host \"%s\".\n"
4463 "Since they are different users, they won't be reading/writing\n"
4464 "the same ~/.xscreensaver file, so %s isn't\n"
4465 "going to work right.\n"
4467 "You should either re-run %s as \"%s\", or re-run\n"
4468 "xscreensaver as \"%s\".\n"
4470 "Restart the xscreensaver daemon now?\n"),
4471 progname, luser, lhost,
4473 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4475 progname, (ruser ? ruser : "???"),
4478 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4480 /* Warn that the two processes are running on different hosts.
4484 "%s is running as user \"%s\" on host \"%s\".\n"
4485 "But the xscreensaver managing display \"%s\"\n"
4486 "is running as user \"%s\" on host \"%s\".\n"
4488 "If those two machines don't share a file system (that is,\n"
4489 "if they don't see the same ~%s/.xscreensaver file) then\n"
4490 "%s won't work right.\n"
4492 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4493 progname, luser, lhost,
4495 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4500 else if (!!strcmp (rversion, s->short_version))
4502 /* Warn that the version numbers don't match.
4506 "This is %s version %s.\n"
4507 "But the xscreensaver managing display \"%s\"\n"
4508 "is version %s. This could cause problems.\n"
4510 "Restart the xscreensaver daemon now?\n"),
4511 progname, s->short_version,
4518 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4520 if (rversion) free (rversion);
4521 if (ruser) free (ruser);
4522 if (rhost) free (rhost);
4526 /* Note: since these dialogs are not modal, they will stack up.
4527 So we do this check *after* popping up the "xscreensaver is not
4528 running" dialog so that these are on top. Good enough.
4531 if (gnome_screensaver_active_p ())
4532 warning_dialog (s->toplevel_widget,
4534 "The GNOME screensaver daemon appears to be running.\n"
4535 "It must be stopped for XScreenSaver to work properly.\n"
4537 "Stop the GNOME screen saver daemon now?\n"),
4540 if (kde_screensaver_active_p ())
4541 warning_dialog (s->toplevel_widget,
4543 "The KDE screen saver daemon appears to be running.\n"
4544 "It must be stopped for XScreenSaver to work properly.\n"
4546 "Stop the KDE screen saver daemon now?\n"),
4551 /* We use this error handler so that X errors are preceeded by the name
4552 of the program that generated them.
4555 demo_ehandler (Display *dpy, XErrorEvent *error)
4557 state *s = global_state_kludge; /* I hate C so much... */
4558 fprintf (stderr, "\nX error in %s:\n", blurb());
4559 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4560 kill_preview_subproc (s, False);
4566 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4567 of the program that generated them; and also that we can ignore one
4568 particular bogus error message that Gdk madly spews.
4571 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4572 const gchar *message, gpointer user_data)
4574 /* Ignore the message "Got event for unknown window: 0x...".
4575 Apparently some events are coming in for the xscreensaver window
4576 (presumably reply events related to the ClientMessage) and Gdk
4577 feels the need to complain about them. So, just suppress any
4578 messages that look like that one.
4580 if (strstr (message, "unknown window"))
4583 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4584 (log_domain ? log_domain : progclass),
4585 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4586 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4587 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4588 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4589 log_level == G_LOG_LEVEL_INFO ? "info" :
4590 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4592 ((!*message || message[strlen(message)-1] != '\n')
4598 __extension__ /* shut up about "string length is greater than the length
4599 ISO C89 compilers are required to support" when including
4604 static char *defaults[] = {
4605 #include "XScreenSaver_ad.h"
4610 #ifdef HAVE_CRAPPLET
4611 static struct poptOption crapplet_options[] = {
4612 {NULL, '\0', 0, NULL, 0}
4614 #endif /* HAVE_CRAPPLET */
4617 const char *usage = "[--display dpy] [--prefs | --settings]"
4618 # ifdef HAVE_CRAPPLET
4621 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4624 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4626 state *s = (state *) user_data;
4627 Boolean oi = s->initializing_p;
4629 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4631 s->initializing_p = True;
4633 eschew_gtk_lossage (label);
4635 s->initializing_p = oi;
4641 print_widget_tree (GtkWidget *w, int depth)
4644 for (i = 0; i < depth; i++)
4645 fprintf (stderr, " ");
4646 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4648 if (GTK_IS_LIST (w))
4650 for (i = 0; i < depth+1; i++)
4651 fprintf (stderr, " ");
4652 fprintf (stderr, "...list kids...\n");
4654 else if (GTK_IS_CONTAINER (w))
4656 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4659 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4667 delayed_scroll_kludge (gpointer data)
4669 state *s = (state *) data;
4670 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4671 ensure_selected_item_visible (w);
4673 /* Oh, this is just fucking lovely, too. */
4674 w = GTK_WIDGET (name_to_widget (s, "preview"));
4675 gtk_widget_hide (w);
4676 gtk_widget_show (w);
4678 return FALSE; /* do not re-execute timer */
4684 create_xscreensaver_demo (void)
4688 nb = name_to_widget (global_state_kludge, "preview_notebook");
4689 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4691 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4695 create_xscreensaver_settings_dialog (void)
4699 box = name_to_widget (global_state_kludge, "dialog_action_area");
4701 w = name_to_widget (global_state_kludge, "adv_button");
4702 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4704 w = name_to_widget (global_state_kludge, "std_button");
4705 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4707 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4710 #endif /* HAVE_GTK2 */
4713 main (int argc, char **argv)
4717 saver_preferences *p;
4718 Bool prefs_p = False;
4719 Bool settings_p = False;
4722 Widget toplevel_shell;
4723 char *real_progname = argv[0];
4726 Bool crapplet_p = False;
4730 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4731 textdomain (GETTEXT_PACKAGE);
4734 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4735 # else /* !HAVE_GTK2 */
4736 if (!setlocale (LC_ALL, ""))
4737 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4738 # endif /* !HAVE_GTK2 */
4740 #endif /* ENABLE_NLS */
4742 str = strrchr (real_progname, '/');
4743 if (str) real_progname = str+1;
4746 memset (s, 0, sizeof(*s));
4747 s->initializing_p = True;
4750 global_state_kludge = s; /* I hate C so much... */
4752 progname = real_progname;
4754 s->short_version = (char *) malloc (5);
4755 memcpy (s->short_version, screensaver_id + 17, 4);
4756 s->short_version [4] = 0;
4759 /* Register our error message logger for every ``log domain'' known.
4760 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4761 for all of the domains that seem to be in use.
4764 const char * const domains[] = { 0,
4765 "Gtk", "Gdk", "GLib", "GModule",
4766 "GThread", "Gnome", "GnomeUI" };
4767 for (i = 0; i < countof(domains); i++)
4768 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4771 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4774 const char *dir = DEFAULT_ICONDIR;
4775 if (*dir) add_pixmap_directory (dir);
4777 # endif /* !HAVE_GTK2 */
4778 #endif /* DEFAULT_ICONDIR */
4780 /* This is gross, but Gtk understands --display and not -display...
4782 for (i = 1; i < argc; i++)
4783 if (argv[i][0] && argv[i][1] &&
4784 !strncmp(argv[i], "-display", strlen(argv[i])))
4785 argv[i] = "--display";
4788 /* We need to parse this arg really early... Sigh. */
4789 for (i = 1; i < argc; i++)
4792 (!strcmp(argv[i], "--crapplet") ||
4793 !strcmp(argv[i], "--capplet")))
4795 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4798 for (j = i; j < argc; j++) /* remove it from the list */
4799 argv[j] = argv[j+1];
4801 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4802 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4804 fprintf (stderr, "%s: %s\n", real_progname, usage);
4806 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4809 (!strcmp(argv[i], "--debug") ||
4810 !strcmp(argv[i], "-debug") ||
4811 !strcmp(argv[i], "-d")))
4815 for (j = i; j < argc; j++) /* remove it from the list */
4816 argv[j] = argv[j+1];
4823 (!strcmp(argv[i], "-geometry") ||
4824 !strcmp(argv[i], "-geom") ||
4825 !strcmp(argv[i], "-geo") ||
4826 !strcmp(argv[i], "-g")))
4830 for (j = i; j < argc; j++) /* remove them from the list */
4831 argv[j] = argv[j+2];
4838 (!strcmp(argv[i], "--configdir")))
4842 hack_configuration_path = argv[i+1];
4843 for (j = i; j < argc; j++) /* remove them from the list */
4844 argv[j] = argv[j+2];
4848 if (0 != stat (hack_configuration_path, &st))
4851 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4855 else if (!S_ISDIR (st.st_mode))
4857 fprintf (stderr, "%s: not a directory: %s\n",
4858 blurb(), hack_configuration_path);
4866 fprintf (stderr, "%s: using config directory \"%s\"\n",
4867 progname, hack_configuration_path);
4870 /* Let Gtk open the X connection, then initialize Xt to use that
4871 same connection. Doctor Frankenstein would be proud.
4873 # ifdef HAVE_CRAPPLET
4876 GnomeClient *client;
4877 GnomeClientFlags flags = 0;
4879 int init_results = gnome_capplet_init ("screensaver-properties",
4881 argc, argv, NULL, 0, NULL);
4883 0 upon successful initialization;
4884 1 if --init-session-settings was passed on the cmdline;
4885 2 if --ignore was passed on the cmdline;
4888 So the 1 signifies just to init the settings, and quit, basically.
4889 (Meaning launch the xscreensaver daemon.)
4892 if (init_results < 0)
4895 g_error ("An initialization error occurred while "
4896 "starting xscreensaver-capplet.\n");
4898 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4899 real_progname, init_results);
4904 client = gnome_master_client ();
4907 flags = gnome_client_get_flags (client);
4909 if (flags & GNOME_CLIENT_IS_CONNECTED)
4912 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4913 gnome_client_get_id (client));
4916 char *session_args[20];
4918 session_args[i++] = real_progname;
4919 session_args[i++] = "--capplet";
4920 session_args[i++] = "--init-session-settings";
4921 session_args[i] = 0;
4922 gnome_client_set_priority (client, 20);
4923 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4924 gnome_client_set_restart_command (client, i, session_args);
4928 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4931 gnome_client_flush (client);
4934 if (init_results == 1)
4936 system ("xscreensaver -nosplash &");
4942 # endif /* HAVE_CRAPPLET */
4944 gtk_init (&argc, &argv);
4948 /* We must read exactly the same resources as xscreensaver.
4949 That means we must have both the same progclass *and* progname,
4950 at least as far as the resource database is concerned. So,
4951 put "xscreensaver" in argv[0] while initializing Xt.
4953 argv[0] = "xscreensaver";
4957 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4959 XtToolkitInitialize ();
4960 app = XtCreateApplicationContext ();
4961 dpy = GDK_DISPLAY();
4962 XtAppSetFallbackResources (app, defaults);
4963 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4964 toplevel_shell = XtAppCreateShell (progname, progclass,
4965 applicationShellWidgetClass,
4968 dpy = XtDisplay (toplevel_shell);
4969 db = XtDatabase (dpy);
4970 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4971 XSetErrorHandler (demo_ehandler);
4973 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4974 signal (SIGPIPE, SIG_IGN);
4976 /* After doing Xt-style command-line processing, complain about any
4977 unrecognized command-line arguments.
4979 for (i = 1; i < argc; i++)
4981 char *str = argv[i];
4982 if (str[0] == '-' && str[1] == '-')
4984 if (!strcmp (str, "-prefs"))
4986 else if (!strcmp (str, "-settings"))
4988 else if (crapplet_p)
4989 /* There are lots of random args that we don't care about when we're
4990 started as a crapplet, so just ignore unknown args in that case. */
4994 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4996 fprintf (stderr, "%s: %s\n", real_progname, usage);
5001 /* Load the init file, which may end up consulting the X resource database
5002 and the site-wide app-defaults file. Note that at this point, it's
5003 important that `progname' be "xscreensaver", rather than whatever
5007 s->nscreens = screen_count (dpy);
5009 hack_environment (s); /* must be before initialize_sort_map() */
5011 load_init_file (dpy, p);
5012 initialize_sort_map (s);
5014 /* Now that Xt has been initialized, and the resources have been read,
5015 we can set our `progname' variable to something more in line with
5018 progname = real_progname;
5022 /* Print out all the resources we read. */
5024 XrmName name = { 0 };
5025 XrmClass class = { 0 };
5027 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
5033 /* Intern the atoms that xscreensaver_command() needs.
5035 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
5036 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
5037 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
5038 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5039 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5040 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5041 XA_SELECT = XInternAtom (dpy, "SELECT", False);
5042 XA_DEMO = XInternAtom (dpy, "DEMO", False);
5043 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5044 XA_BLANK = XInternAtom (dpy, "BLANK", False);
5045 XA_LOCK = XInternAtom (dpy, "LOCK", False);
5046 XA_EXIT = XInternAtom (dpy, "EXIT", False);
5047 XA_RESTART = XInternAtom (dpy, "RESTART", False);
5050 /* Create the window and all its widgets.
5052 s->base_widget = create_xscreensaver_demo ();
5053 s->popup_widget = create_xscreensaver_settings_dialog ();
5054 s->toplevel_widget = s->base_widget;
5057 /* Set the main window's title. */
5059 char *base_title = _("Screensaver Preferences");
5060 char *v = (char *) strdup(strchr(screensaver_id, ' '));
5061 char *s1, *s2, *s3, *s4;
5062 s1 = (char *) strchr(v, ' '); s1++;
5063 s2 = (char *) strchr(s1, ' ');
5064 s3 = (char *) strchr(v, '('); s3++;
5065 s4 = (char *) strchr(s3, ')');
5069 window_title = (char *) malloc (strlen (base_title) +
5070 strlen (progclass) +
5071 strlen (s1) + strlen (s3) +
5073 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
5074 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5075 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
5079 /* Adjust the (invisible) notebooks on the popup dialog... */
5081 GtkNotebook *notebook =
5082 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5083 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5087 gtk_widget_hide (std);
5088 # else /* !HAVE_XML */
5089 /* Make the advanced page be the only one available. */
5090 gtk_widget_set_sensitive (std, False);
5091 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5092 gtk_widget_hide (std);
5093 std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5094 gtk_widget_hide (std);
5096 # endif /* !HAVE_XML */
5098 gtk_notebook_set_page (notebook, page);
5099 gtk_notebook_set_show_tabs (notebook, False);
5102 /* Various other widget initializations...
5104 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5105 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5107 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5108 GTK_SIGNAL_FUNC (wm_popup_close_cb),
5111 populate_hack_list (s);
5112 populate_prefs_page (s);
5113 sensitize_demo_widgets (s, False);
5114 fix_text_entry_sizes (s);
5115 scroll_to_current_hack (s);
5117 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5118 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5122 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5123 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5125 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5126 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5128 #endif /* !HAVE_GTK2 */
5130 /* Hook up callbacks to the items on the mode menu. */
5132 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5133 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5134 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5136 for (i = 0; kids; kids = kids->next, i++)
5138 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5139 GTK_SIGNAL_FUNC (mode_menu_item_cb),
5142 /* The "random-same" mode menu item does not appear unless
5143 there are multple screens.
5145 if (s->nscreens <= 1 &&
5146 mode_menu_order[i] == RANDOM_HACKS_SAME)
5147 gtk_widget_hide (GTK_WIDGET (kids->data));
5150 if (s->nscreens <= 1) /* recompute option-menu size */
5152 gtk_widget_unrealize (GTK_WIDGET (menu));
5153 gtk_widget_realize (GTK_WIDGET (menu));
5158 /* Handle the -prefs command-line argument. */
5161 GtkNotebook *notebook =
5162 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5163 gtk_notebook_set_page (notebook, 1);
5166 # ifdef HAVE_CRAPPLET
5170 GtkWidget *outer_vbox;
5172 gtk_widget_hide (s->toplevel_widget);
5174 capplet = capplet_widget_new ();
5176 /* Make there be a "Close" button instead of "OK" and "Cancel" */
5177 # ifdef HAVE_CRAPPLET_IMMEDIATE
5178 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5179 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5180 /* In crapplet-mode, take off the menubar. */
5181 gtk_widget_hide (name_to_widget (s, "menubar"));
5183 /* Reparent our top-level container to be a child of the capplet
5186 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5187 gtk_widget_ref (outer_vbox);
5188 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5190 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5191 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5193 /* Find the window above us, and set the title and close handler. */
5195 GtkWidget *window = capplet;
5196 while (window && !GTK_IS_WINDOW (window))
5197 window = GET_PARENT (window);
5200 gtk_window_set_title (GTK_WINDOW (window), window_title);
5201 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5202 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5207 s->toplevel_widget = capplet;
5209 # endif /* HAVE_CRAPPLET */
5212 /* The Gnome folks hate the menubar. I think it's important to have access
5213 to the commands on the File menu (Restart Daemon, etc.) and to the
5214 About and Documentation commands on the Help menu.
5218 gtk_widget_hide (name_to_widget (s, "menubar"));
5222 free (window_title);
5226 /* After picking the default size, allow -geometry to override it. */
5228 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5231 gtk_widget_show (s->toplevel_widget);
5232 init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */
5233 fix_preview_visual (s);
5235 /* Realize page zero, so that we can diddle the scrollbar when the
5236 user tabs back to it -- otherwise, the current hack isn't scrolled
5237 to the first time they tab back there, when started with "-prefs".
5238 (Though it is if they then tab away, and back again.)
5240 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5241 #### understands this crap, explain to me how to make this work.
5243 gtk_widget_realize (name_to_widget (s, "demos_table"));
5246 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5249 /* Handle the --settings command-line argument. */
5251 gtk_timeout_add (500, settings_timer, 0);
5254 /* Issue any warnings about the running xscreensaver daemon. */
5256 the_network_is_not_the_computer (s);
5259 /* Run the Gtk event loop, and not the Xt event loop. This means that
5260 if there were Xt timers or fds registered, they would never get serviced,
5261 and if there were any Xt widgets, they would never have events delivered.
5262 Fortunately, we're using Gtk for all of the UI, and only initialized
5263 Xt so that we could process the command line and use the X resource
5266 s->initializing_p = False;
5268 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5269 after we start up. Otherwise, it always appears scrolled to the top
5270 when in crapplet-mode. */
5271 gtk_timeout_add (500, delayed_scroll_kludge, s);
5275 /* Load every configurator in turn, to scan them for errors all at once. */
5279 for (i = 0; i < p->screenhacks_count; i++)
5281 screenhack *hack = p->screenhacks[i];
5282 conf_data *d = load_configurator (hack->command, s->debug_p);
5283 if (d) free_conf_data (d);
5289 # ifdef HAVE_CRAPPLET
5291 capplet_gtk_main ();
5293 # endif /* HAVE_CRAPPLET */
5296 kill_preview_subproc (s, False);
5300 #endif /* HAVE_GTK -- whole file */