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 */
688 run_cmd (state *s, Atom command, int arg)
693 flush_dialog_changes_and_save (s);
694 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
696 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
697 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
704 sprintf (buf, "Error:\n\n%s", err);
706 strcpy (buf, "Unknown error!");
707 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
711 sensitize_menu_items (s, True);
712 force_dialog_repaint (s);
717 run_hack (state *s, int list_elt, Bool report_errors_p)
723 if (list_elt < 0) return;
724 hack_number = s->list_elt_to_hack_number[list_elt];
726 flush_dialog_changes_and_save (s);
727 schedule_preview (s, 0);
729 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
732 if (status < 0 && report_errors_p)
734 if (xscreensaver_running_p (s))
736 /* Kludge: ignore the spurious "window unexpectedly deleted"
738 if (err && strstr (err, "unexpectedly deleted"))
745 sprintf (buf, "Error:\n\n%s", err);
747 strcpy (buf, "Unknown error!");
748 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
753 /* The error is that the daemon isn't running;
756 const char *d = DisplayString (GDK_DISPLAY());
760 "The XScreenSaver daemon doesn't seem to be running\n"
761 "on display \"%s\". Launch it now?"),
763 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
769 sensitize_menu_items (s, False);
776 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
777 libglade work on Cygwin; apparently all Glade callbacks need this magic
778 extra declaration. I do not pretend to understand.
782 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
784 state *s = global_state_kludge; /* I hate C so much... */
785 flush_dialog_changes_and_save (s);
786 kill_preview_subproc (s, False);
791 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
793 state *s = (state *) data;
794 flush_dialog_changes_and_save (s);
801 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
804 char *vers = strdup (screensaver_id + 4);
807 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
809 s = strchr (vers, ',');
813 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
814 non-ASCII characters aren't allowed in localizable string keys."
815 (I don't want to just use (c) instead of © because that doesn't
816 look as good in the plain-old default Latin1 "C" locale.)
819 sprintf(copy, ("Copyright \xC2\xA9 1991-2008 %s"), s);
820 #else /* !HAVE_GTK2 */
821 sprintf(copy, ("Copyright \251 1991-2008 %s"), s);
822 #endif /* !HAVE_GTK2 */
824 sprintf (msg, "%s\n\n%s", copy, desc);
826 /* I can't make gnome_about_new() work here -- it starts dying in
827 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
828 then this might be the thing to do:
832 const gchar *auth[] = { 0 };
833 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
835 gtk_widget_show (about);
837 #else / * GTK but not GNOME * /
841 GdkColormap *colormap;
842 GdkPixmap *gdkpixmap;
845 GtkWidget *dialog = gtk_dialog_new ();
846 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
847 GtkWidget *parent = GTK_WIDGET (menuitem);
848 while (GET_PARENT (parent))
849 parent = GET_PARENT (parent);
851 hbox = gtk_hbox_new (FALSE, 20);
852 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
853 hbox, TRUE, TRUE, 0);
855 colormap = gtk_widget_get_colormap (parent);
857 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
858 (gchar **) logo_180_xpm);
859 icon = gtk_pixmap_new (gdkpixmap, mask);
860 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
862 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
864 vbox = gtk_vbox_new (FALSE, 0);
865 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
867 label1 = gtk_label_new (vers);
868 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
869 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
870 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
873 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
874 GTK_WIDGET (label1)->style->font =
875 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
876 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
877 #endif /* HAVE_GTK2 */
879 label2 = gtk_label_new (msg);
880 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
881 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
882 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
885 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
886 GTK_WIDGET (label2)->style->font =
887 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
888 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
889 #endif /* HAVE_GTK2 */
891 hb = gtk_hbutton_box_new ();
893 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
897 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
898 #else /* !HAVE_GTK2 */
899 ok = gtk_button_new_with_label (_("OK"));
900 #endif /* !HAVE_GTK2 */
901 gtk_container_add (GTK_CONTAINER (hb), ok);
903 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
904 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
905 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
907 gtk_widget_show (hbox);
908 gtk_widget_show (icon);
909 gtk_widget_show (vbox);
910 gtk_widget_show (label1);
911 gtk_widget_show (label2);
912 gtk_widget_show (hb);
913 gtk_widget_show (ok);
914 gtk_widget_show (dialog);
916 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
917 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
919 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
920 GET_WINDOW (GTK_WIDGET (parent)));
921 gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog)));
922 gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog)));
928 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
930 state *s = global_state_kludge; /* I hate C so much... */
931 saver_preferences *p = &s->prefs;
934 if (!p->help_url || !*p->help_url)
936 warning_dialog (s->toplevel_widget,
938 "No Help URL has been specified.\n"), D_NONE, 100);
942 help_command = (char *) malloc (strlen (p->load_url_command) +
943 (strlen (p->help_url) * 4) + 20);
944 strcpy (help_command, "( ");
945 sprintf (help_command + strlen(help_command),
947 p->help_url, p->help_url, p->help_url, p->help_url);
948 strcat (help_command, " ) &");
949 if (system (help_command) < 0)
950 fprintf (stderr, "%s: fork error\n", blurb());
956 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
958 state *s = global_state_kludge; /* I hate C so much... */
959 sensitize_menu_items (s, False);
964 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
966 state *s = global_state_kludge; /* I hate C so much... */
967 run_cmd (s, XA_ACTIVATE, 0);
972 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
974 state *s = global_state_kludge; /* I hate C so much... */
975 run_cmd (s, XA_LOCK, 0);
980 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
982 state *s = global_state_kludge; /* I hate C so much... */
983 run_cmd (s, XA_EXIT, 0);
988 restart_menu_cb (GtkWidget *widget, gpointer user_data)
990 state *s = global_state_kludge; /* I hate C so much... */
991 flush_dialog_changes_and_save (s);
992 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
994 if (system ("xscreensaver -nosplash &") < 0)
995 fprintf (stderr, "%s: fork error\n", blurb());
997 await_xscreensaver (s);
1001 xscreensaver_running_p (state *s)
1003 Display *dpy = GDK_DISPLAY();
1005 server_xscreensaver_version (dpy, &rversion, 0, 0);
1013 await_xscreensaver (state *s)
1018 while (!ok && (--countdown > 0))
1019 if (xscreensaver_running_p (s))
1022 sleep (1); /* If it's not there yet, wait a second... */
1024 sensitize_menu_items (s, True);
1028 /* Timed out, no screensaver running. */
1031 Bool root_p = (geteuid () == 0);
1035 "The xscreensaver daemon did not start up properly.\n"
1041 __extension__ /* don't warn about "string length is greater than
1042 the length ISO C89 compilers are required to
1043 support" in the following expression... */
1046 _("You are running as root. This usually means that xscreensaver\n"
1047 "was unable to contact your X server because access control is\n"
1048 "turned on. Try running this command:\n"
1050 " xhost +localhost\n"
1052 "and then selecting `File / Restart Daemon'.\n"
1054 "Note that turning off access control will allow anyone logged\n"
1055 "on to this machine to access your screen, which might be\n"
1056 "considered a security problem. Please read the xscreensaver\n"
1057 "manual and FAQ for more information.\n"
1059 "You shouldn't run X as root. Instead, you should log in as a\n"
1060 "normal user, and `su' as necessary."));
1062 strcat (buf, _("Please check your $PATH and permissions."));
1064 warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1067 force_dialog_repaint (s);
1072 selected_list_element (state *s)
1074 return s->_selected_list_element;
1079 demo_write_init_file (state *s, saver_preferences *p)
1081 Display *dpy = GDK_DISPLAY();
1084 /* #### try to figure out why shit keeps getting reordered... */
1085 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1089 if (!write_init_file (dpy, p, s->short_version, False))
1092 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1097 const char *f = init_file_name();
1099 warning_dialog (s->toplevel_widget,
1100 _("Error:\n\nCouldn't determine init file name!\n"),
1104 char *b = (char *) malloc (strlen(f) + 1024);
1105 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1106 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1114 G_MODULE_EXPORT void
1115 run_this_cb (GtkButton *button, gpointer user_data)
1117 state *s = global_state_kludge; /* I hate C so much... */
1118 int list_elt = selected_list_element (s);
1119 if (list_elt < 0) return;
1120 if (!flush_dialog_changes_and_save (s))
1121 run_hack (s, list_elt, True);
1125 G_MODULE_EXPORT void
1126 manual_cb (GtkButton *button, gpointer user_data)
1128 Display *dpy = GDK_DISPLAY();
1129 state *s = global_state_kludge; /* I hate C so much... */
1130 saver_preferences *p = &s->prefs;
1131 GtkWidget *list_widget = name_to_widget (s, "list");
1132 int list_elt = selected_list_element (s);
1134 char *name, *name2, *cmd, *str;
1136 if (list_elt < 0) return;
1137 hack_number = s->list_elt_to_hack_number[list_elt];
1139 flush_dialog_changes_and_save (s);
1140 ensure_selected_item_visible (list_widget);
1142 name = strdup (p->screenhacks[hack_number]->command);
1145 while (isspace (*name2)) name2++;
1147 while (*str && !isspace (*str)) str++;
1149 str = strrchr (name2, '/');
1150 if (str) name2 = str+1;
1152 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1155 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1156 strcpy (cmd2, "( ");
1157 sprintf (cmd2 + strlen (cmd2),
1159 name2, name2, name2, name2);
1160 strcat (cmd2, " ) &");
1161 if (system (cmd2) < 0)
1162 fprintf (stderr, "%s: fork error\n", blurb());
1167 warning_dialog (GTK_WIDGET (button),
1168 _("Error:\n\nno `manualCommand' resource set."),
1177 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1179 GtkWidget *parent = name_to_widget (s, "scroller");
1180 gboolean was = GET_SENSITIVE (parent);
1183 GtkTreeModel *model;
1184 GtkTreeSelection *selection;
1185 #endif /* HAVE_GTK2 */
1187 if (!was) gtk_widget_set_sensitive (parent, True);
1189 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1191 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1193 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1194 gtk_tree_selection_select_iter (selection, &iter);
1196 #else /* !HAVE_GTK2 */
1197 gtk_list_select_item (GTK_LIST (list), list_elt);
1198 #endif /* !HAVE_GTK2 */
1199 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1200 if (!was) gtk_widget_set_sensitive (parent, False);
1204 G_MODULE_EXPORT void
1205 run_next_cb (GtkButton *button, gpointer user_data)
1207 state *s = global_state_kludge; /* I hate C so much... */
1208 /* saver_preferences *p = &s->prefs; */
1209 Bool ops = s->preview_suppressed_p;
1211 GtkWidget *list_widget = name_to_widget (s, "list");
1212 int list_elt = selected_list_element (s);
1219 if (list_elt >= s->list_count)
1222 s->preview_suppressed_p = True;
1224 flush_dialog_changes_and_save (s);
1225 force_list_select_item (s, list_widget, list_elt, True);
1226 populate_demo_window (s, list_elt);
1227 run_hack (s, list_elt, False);
1229 s->preview_suppressed_p = ops;
1233 G_MODULE_EXPORT void
1234 run_prev_cb (GtkButton *button, gpointer user_data)
1236 state *s = global_state_kludge; /* I hate C so much... */
1237 /* saver_preferences *p = &s->prefs; */
1238 Bool ops = s->preview_suppressed_p;
1240 GtkWidget *list_widget = name_to_widget (s, "list");
1241 int list_elt = selected_list_element (s);
1244 list_elt = s->list_count - 1;
1249 list_elt = s->list_count - 1;
1251 s->preview_suppressed_p = True;
1253 flush_dialog_changes_and_save (s);
1254 force_list_select_item (s, list_widget, list_elt, True);
1255 populate_demo_window (s, list_elt);
1256 run_hack (s, list_elt, False);
1258 s->preview_suppressed_p = ops;
1262 /* Writes the given settings into prefs.
1263 Returns true if there was a change, False otherwise.
1264 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1267 flush_changes (state *s,
1270 const char *command,
1273 saver_preferences *p = &s->prefs;
1274 Bool changed = False;
1277 if (list_elt < 0 || list_elt >= s->list_count)
1280 hack_number = s->list_elt_to_hack_number[list_elt];
1281 hack = p->screenhacks[hack_number];
1283 if (enabled_p != -1 &&
1284 enabled_p != hack->enabled_p)
1286 hack->enabled_p = enabled_p;
1289 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1290 blurb(), hack->name, enabled_p);
1295 if (!hack->command || !!strcmp (command, hack->command))
1297 if (hack->command) free (hack->command);
1298 hack->command = strdup (command);
1301 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1302 blurb(), hack->name, command);
1308 const char *ov = hack->visual;
1309 if (!ov || !*ov) ov = "any";
1310 if (!*visual) visual = "any";
1311 if (!!strcasecmp (visual, ov))
1313 if (hack->visual) free (hack->visual);
1314 hack->visual = strdup (visual);
1317 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1318 blurb(), hack->name, visual);
1326 /* Helper for the text fields that contain time specifications:
1327 this parses the text, and does error checking.
1330 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1335 if (!sec_p || strchr (line, ':'))
1336 value = parse_time ((char *) line, sec_p, True);
1340 if (sscanf (line, "%d%c", &value, &c) != 1)
1346 value *= 1000; /* Time measures in microseconds */
1352 "Unparsable time format: \"%s\"\n"),
1354 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1363 directory_p (const char *path)
1366 if (!path || !*path)
1368 else if (stat (path, &st))
1370 else if (!S_ISDIR (st.st_mode))
1377 file_p (const char *path)
1380 if (!path || !*path)
1382 else if (stat (path, &st))
1384 else if (S_ISDIR (st.st_mode))
1391 normalize_directory (const char *path)
1395 if (!path || !*path) return 0;
1397 p2 = (char *) malloc (L + 2);
1399 if (p2[L-1] == '/') /* remove trailing slash */
1402 for (s = p2; s && *s; s++)
1405 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1406 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1409 while (s0 > p2 && s0[-1] != '/')
1419 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1420 strcpy (s, s+2), s--;
1421 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1425 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1426 while (s[0] == '/' && s[1] == '/')
1429 /* and strip trailing whitespace for good measure. */
1431 while (isspace(p2[L-1]))
1444 } FlushForeachClosure;
1447 flush_checkbox (GtkTreeModel *model,
1452 FlushForeachClosure *closure = data;
1455 gtk_tree_model_get (model, iter,
1456 COL_ENABLED, &checked,
1459 if (flush_changes (closure->s, closure->i,
1461 *closure->changed = True;
1465 /* don't remove row */
1469 #endif /* HAVE_GTK2 */
1471 /* Flush out any changes made in the main dialog window (where changes
1472 take place immediately: clicking on a checkbox causes the init file
1473 to be written right away.)
1476 flush_dialog_changes_and_save (state *s)
1478 saver_preferences *p = &s->prefs;
1479 saver_preferences P2, *p2 = &P2;
1481 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1482 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1483 FlushForeachClosure closure;
1484 #else /* !HAVE_GTK2 */
1485 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1486 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1488 #endif /* !HAVE_GTK2 */
1489 static Bool already_warned_about_missing_image_directory = False; /* very long name... */
1491 Bool changed = False;
1494 if (s->saving_p) return False;
1499 /* Flush any checkbox changes in the list down into the prefs struct.
1503 closure.changed = &changed;
1505 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1507 #else /* !HAVE_GTK2 */
1509 for (i = 0; kids; kids = kids->next, i++)
1511 GtkWidget *line = GTK_WIDGET (kids->data);
1512 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1513 GtkWidget *line_check =
1514 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1516 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1518 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1521 #endif /* ~HAVE_GTK2 */
1523 /* Flush the non-hack-specific settings down into the prefs struct.
1526 # define SECONDS(FIELD,NAME) \
1527 w = name_to_widget (s, (NAME)); \
1528 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1530 # define MINUTES(FIELD,NAME) \
1531 w = name_to_widget (s, (NAME)); \
1532 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1534 # define CHECKBOX(FIELD,NAME) \
1535 w = name_to_widget (s, (NAME)); \
1536 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1538 # define PATHNAME(FIELD,NAME) \
1539 w = name_to_widget (s, (NAME)); \
1540 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1542 # define TEXT(FIELD,NAME) \
1543 w = name_to_widget (s, (NAME)); \
1544 (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1546 MINUTES (&p2->timeout, "timeout_spinbutton");
1547 MINUTES (&p2->cycle, "cycle_spinbutton");
1548 CHECKBOX (p2->lock_p, "lock_button");
1549 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1551 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1552 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1553 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1554 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1556 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1557 CHECKBOX (p2->grab_video_p, "grab_video_button");
1558 CHECKBOX (p2->random_image_p, "grab_image_button");
1559 PATHNAME (p2->image_directory, "image_text");
1562 CHECKBOX (p2->verbose_p, "verbose_button");
1563 CHECKBOX (p2->capture_stderr_p, "capture_button");
1564 CHECKBOX (p2->splash_p, "splash_button");
1569 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1570 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1571 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1572 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1573 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1574 TEXT (p2->text_literal, "text_entry");
1575 PATHNAME (p2->text_file, "text_file_entry");
1576 PATHNAME (p2->text_program, "text_program_entry");
1577 PATHNAME (p2->text_program, "text_program_entry");
1578 TEXT (p2->text_url, "text_url_entry");
1581 CHECKBOX (p2->install_cmap_p, "install_button");
1582 CHECKBOX (p2->fade_p, "fade_button");
1583 CHECKBOX (p2->unfade_p, "unfade_button");
1584 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1592 /* Warn if the image directory doesn't exist, when:
1593 - not being warned before
1594 - image directory is changed and the directory doesn't exist
1596 if (p2->image_directory &&
1597 *p2->image_directory &&
1598 !directory_p (p2->image_directory) &&
1599 ( !already_warned_about_missing_image_directory ||
1600 ( p->image_directory &&
1601 *p->image_directory &&
1602 strcmp(p->image_directory, p2->image_directory)
1608 sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n",
1609 p2->image_directory);
1610 if (warning_dialog (s->toplevel_widget, b, D_NONE, 100))
1611 already_warned_about_missing_image_directory = True;
1615 /* Map the mode menu to `saver_mode' enum values. */
1617 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1618 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1619 GtkWidget *selected = gtk_menu_get_active (menu);
1620 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1621 int menu_elt = g_list_index (kids, (gpointer) selected);
1622 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1623 p2->mode = mode_menu_order[menu_elt];
1626 if (p2->mode == ONE_HACK)
1628 int list_elt = selected_list_element (s);
1629 p2->selected_hack = (list_elt >= 0
1630 ? s->list_elt_to_hack_number[list_elt]
1634 # define COPY(field, name) \
1635 if (p->field != p2->field) { \
1638 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1640 p->field = p2->field
1643 COPY(selected_hack, "selected_hack");
1645 COPY(timeout, "timeout");
1646 COPY(cycle, "cycle");
1647 COPY(lock_p, "lock_p");
1648 COPY(lock_timeout, "lock_timeout");
1650 COPY(dpms_enabled_p, "dpms_enabled_p");
1651 COPY(dpms_standby, "dpms_standby");
1652 COPY(dpms_suspend, "dpms_suspend");
1653 COPY(dpms_off, "dpms_off");
1656 COPY(verbose_p, "verbose_p");
1657 COPY(capture_stderr_p, "capture_stderr_p");
1658 COPY(splash_p, "splash_p");
1661 COPY(tmode, "tmode");
1663 COPY(install_cmap_p, "install_cmap_p");
1664 COPY(fade_p, "fade_p");
1665 COPY(unfade_p, "unfade_p");
1666 COPY(fade_seconds, "fade_seconds");
1668 COPY(grab_desktop_p, "grab_desktop_p");
1669 COPY(grab_video_p, "grab_video_p");
1670 COPY(random_image_p, "random_image_p");
1674 # define COPYSTR(FIELD,NAME) \
1677 strcmp(p->FIELD, p2->FIELD)) \
1681 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1683 if (p->FIELD && p->FIELD != p2->FIELD) \
1685 p->FIELD = p2->FIELD; \
1688 COPYSTR(image_directory, "image_directory");
1689 COPYSTR(text_literal, "text_literal");
1690 COPYSTR(text_file, "text_file");
1691 COPYSTR(text_program, "text_program");
1692 COPYSTR(text_url, "text_url");
1695 populate_prefs_page (s);
1699 Display *dpy = GDK_DISPLAY();
1700 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1701 sync_server_dpms_settings (dpy, enabled_p,
1702 p->dpms_standby / 1000,
1703 p->dpms_suspend / 1000,
1707 changed = demo_write_init_file (s, p);
1710 s->saving_p = False;
1715 /* Flush out any changes made in the popup dialog box (where changes
1716 take place only when the OK button is clicked.)
1719 flush_popup_changes_and_save (state *s)
1721 Bool changed = False;
1722 saver_preferences *p = &s->prefs;
1723 int list_elt = selected_list_element (s);
1725 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1726 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1728 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1729 const char *command = gtk_entry_get_text (cmd);
1734 if (s->saving_p) return False;
1740 if (maybe_reload_init_file (s) != 0)
1746 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1748 if (!strcasecmp (visual, "")) visual = "";
1749 else if (!strcasecmp (visual, "any")) visual = "";
1750 else if (!strcasecmp (visual, "default")) visual = "Default";
1751 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1752 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1753 else if (!strcasecmp (visual, "best")) visual = "Best";
1754 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1755 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1756 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1757 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1758 else if (!strcasecmp (visual, "color")) visual = "Color";
1759 else if (!strcasecmp (visual, "gl")) visual = "GL";
1760 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1761 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1762 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1763 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1764 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1765 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1766 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1767 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1768 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1771 gdk_beep (); /* unparsable */
1773 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1776 changed = flush_changes (s, list_elt, -1, command, visual);
1779 changed = demo_write_init_file (s, p);
1781 /* Do this to re-launch the hack if (and only if) the command line
1783 populate_demo_window (s, selected_list_element (s));
1787 s->saving_p = False;
1792 G_MODULE_EXPORT void
1793 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1795 state *s = global_state_kludge; /* I hate C so much... */
1796 if (! s->initializing_p)
1798 s->initializing_p = True;
1799 flush_dialog_changes_and_save (s);
1800 s->initializing_p = False;
1804 G_MODULE_EXPORT gboolean
1805 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1807 pref_changed_cb (widget, user_data);
1811 /* Callback on menu items in the "mode" options menu.
1813 G_MODULE_EXPORT void
1814 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1816 state *s = (state *) user_data;
1817 saver_preferences *p = &s->prefs;
1818 GtkWidget *list = name_to_widget (s, "list");
1822 gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
1824 saver_mode new_mode;
1828 if (menu_items->data == widget)
1831 menu_items = menu_items->next;
1833 if (!menu_items) abort();
1835 new_mode = mode_menu_order[menu_index];
1837 /* Keep the same list element displayed as before; except if we're
1838 switching *to* "one screensaver" mode from any other mode, set
1839 "the one" to be that which is currently selected.
1841 list_elt = selected_list_element (s);
1842 if (new_mode == ONE_HACK)
1843 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1846 saver_mode old_mode = p->mode;
1848 populate_demo_window (s, list_elt);
1849 force_list_select_item (s, list, list_elt, True);
1850 p->mode = old_mode; /* put it back, so the init file gets written */
1853 pref_changed_cb (widget, user_data);
1857 G_MODULE_EXPORT void
1858 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1859 gint page_num, gpointer user_data)
1861 state *s = global_state_kludge; /* I hate C so much... */
1862 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1864 /* If we're switching to page 0, schedule the current hack to be run.
1865 Otherwise, schedule it to stop. */
1867 populate_demo_window (s, selected_list_element (s));
1869 schedule_preview (s, 0);
1874 list_activated_cb (GtkTreeView *list,
1876 GtkTreeViewColumn *column,
1883 g_return_if_fail (!gdk_pointer_is_grabbed ());
1885 str = gtk_tree_path_to_string (path);
1886 list_elt = strtol (str, NULL, 10);
1890 run_hack (s, list_elt, True);
1894 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1896 state *s = (state *)data;
1897 GtkTreeModel *model;
1903 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1906 path = gtk_tree_model_get_path (model, &iter);
1907 str = gtk_tree_path_to_string (path);
1908 list_elt = strtol (str, NULL, 10);
1910 gtk_tree_path_free (path);
1913 populate_demo_window (s, list_elt);
1914 flush_dialog_changes_and_save (s);
1916 /* Re-populate the Settings window any time a new item is selected
1917 in the list, in case both windows are currently visible.
1919 populate_popup_window (s);
1922 #else /* !HAVE_GTK2 */
1924 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1925 list_select_cb that comes in
1926 *after* we've double-clicked.
1930 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1933 state *s = (state *) data;
1934 if (event->type == GDK_2BUTTON_PRESS)
1936 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1937 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1939 last_doubleclick_time = time ((time_t *) 0);
1942 run_hack (s, list_elt, True);
1950 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1952 state *s = (state *) data;
1953 time_t now = time ((time_t *) 0);
1955 if (now >= last_doubleclick_time + 2)
1957 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1958 populate_demo_window (s, list_elt);
1959 flush_dialog_changes_and_save (s);
1964 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1966 state *s = (state *) data;
1967 populate_demo_window (s, -1);
1968 flush_dialog_changes_and_save (s);
1971 #endif /* !HAVE_GTK2 */
1974 /* Called when the checkboxes that are in the left column of the
1975 scrolling list are clicked. This both populates the right pane
1976 (just as clicking on the label (really, listitem) does) and
1977 also syncs this checkbox with the right pane Enabled checkbox.
1982 GtkCellRendererToggle *toggle,
1984 #else /* !HAVE_GTK2 */
1986 #endif /* !HAVE_GTK2 */
1989 state *s = (state *) data;
1992 GtkScrolledWindow *scroller =
1993 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1994 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1995 GtkTreeModel *model = gtk_tree_view_get_model (list);
1996 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1999 #else /* !HAVE_GTK2 */
2000 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
2001 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
2003 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
2004 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
2005 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
2006 #endif /* !HAVE_GTK2 */
2013 if (!gtk_tree_model_get_iter (model, &iter, path))
2015 g_warning ("bad path: %s", path_string);
2018 gtk_tree_path_free (path);
2020 gtk_tree_model_get (model, &iter,
2021 COL_ENABLED, &active,
2024 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2025 COL_ENABLED, !active,
2028 list_elt = strtol (path_string, NULL, 10);
2029 #else /* !HAVE_GTK2 */
2030 list_elt = gtk_list_child_position (list, line);
2031 #endif /* !HAVE_GTK2 */
2033 /* remember previous scroll position of the top of the list */
2034 adj = gtk_scrolled_window_get_vadjustment (scroller);
2035 scroll_top = GET_ADJ_VALUE (adj);
2037 flush_dialog_changes_and_save (s);
2038 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
2039 populate_demo_window (s, list_elt);
2041 /* restore the previous scroll position of the top of the list.
2042 this is weak, but I don't really know why it's moving... */
2043 gtk_adjustment_set_value (adj, scroll_top);
2049 GtkFileSelection *widget;
2050 } file_selection_data;
2055 store_image_directory (GtkWidget *button, gpointer user_data)
2057 file_selection_data *fsd = (file_selection_data *) user_data;
2058 state *s = fsd->state;
2059 GtkFileSelection *selector = fsd->widget;
2060 GtkWidget *top = s->toplevel_widget;
2061 saver_preferences *p = &s->prefs;
2062 const char *path = gtk_file_selection_get_filename (selector);
2064 if (p->image_directory && !strcmp(p->image_directory, path))
2065 return; /* no change */
2067 if (!directory_p (path))
2070 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2071 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2075 if (p->image_directory) free (p->image_directory);
2076 p->image_directory = normalize_directory (path);
2078 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2079 (p->image_directory ? p->image_directory : ""));
2080 demo_write_init_file (s, p);
2085 store_text_file (GtkWidget *button, gpointer user_data)
2087 file_selection_data *fsd = (file_selection_data *) user_data;
2088 state *s = fsd->state;
2089 GtkFileSelection *selector = fsd->widget;
2090 GtkWidget *top = s->toplevel_widget;
2091 saver_preferences *p = &s->prefs;
2092 const char *path = gtk_file_selection_get_filename (selector);
2094 if (p->text_file && !strcmp(p->text_file, path))
2095 return; /* no change */
2100 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2101 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2105 if (p->text_file) free (p->text_file);
2106 p->text_file = normalize_directory (path);
2108 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2109 (p->text_file ? p->text_file : ""));
2110 demo_write_init_file (s, p);
2115 store_text_program (GtkWidget *button, gpointer user_data)
2117 file_selection_data *fsd = (file_selection_data *) user_data;
2118 state *s = fsd->state;
2119 GtkFileSelection *selector = fsd->widget;
2120 /*GtkWidget *top = s->toplevel_widget;*/
2121 saver_preferences *p = &s->prefs;
2122 const char *path = gtk_file_selection_get_filename (selector);
2124 if (p->text_program && !strcmp(p->text_program, path))
2125 return; /* no change */
2131 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2132 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2137 if (p->text_program) free (p->text_program);
2138 p->text_program = normalize_directory (path);
2140 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2141 (p->text_program ? p->text_program : ""));
2142 demo_write_init_file (s, p);
2148 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2150 file_selection_data *fsd = (file_selection_data *) user_data;
2151 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2155 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2157 browse_image_dir_cancel (button, user_data);
2158 store_image_directory (button, user_data);
2162 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2164 browse_image_dir_cancel (button, user_data);
2165 store_text_file (button, user_data);
2169 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2171 browse_image_dir_cancel (button, user_data);
2172 store_text_program (button, user_data);
2176 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2178 browse_image_dir_cancel (widget, user_data);
2182 G_MODULE_EXPORT void
2183 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2185 state *s = global_state_kludge; /* I hate C so much... */
2186 saver_preferences *p = &s->prefs;
2187 static file_selection_data *fsd = 0;
2189 GtkFileSelection *selector = GTK_FILE_SELECTION(
2190 gtk_file_selection_new ("Please select the image directory."));
2193 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2195 fsd->widget = selector;
2198 if (p->image_directory && *p->image_directory)
2199 gtk_file_selection_set_filename (selector, p->image_directory);
2201 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2202 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2204 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2205 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2207 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2208 GTK_SIGNAL_FUNC (browse_image_dir_close),
2211 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2213 gtk_window_set_modal (GTK_WINDOW (selector), True);
2214 gtk_widget_show (GTK_WIDGET (selector));
2218 G_MODULE_EXPORT void
2219 browse_text_file_cb (GtkButton *button, gpointer user_data)
2221 state *s = global_state_kludge; /* I hate C so much... */
2222 saver_preferences *p = &s->prefs;
2223 static file_selection_data *fsd = 0;
2225 GtkFileSelection *selector = GTK_FILE_SELECTION(
2226 gtk_file_selection_new ("Please select a text file."));
2229 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2231 fsd->widget = selector;
2234 if (p->text_file && *p->text_file)
2235 gtk_file_selection_set_filename (selector, p->text_file);
2237 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2238 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2240 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2241 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2243 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2244 GTK_SIGNAL_FUNC (browse_image_dir_close),
2247 gtk_window_set_modal (GTK_WINDOW (selector), True);
2248 gtk_widget_show (GTK_WIDGET (selector));
2252 G_MODULE_EXPORT void
2253 browse_text_program_cb (GtkButton *button, gpointer user_data)
2255 state *s = global_state_kludge; /* I hate C so much... */
2256 saver_preferences *p = &s->prefs;
2257 static file_selection_data *fsd = 0;
2259 GtkFileSelection *selector = GTK_FILE_SELECTION(
2260 gtk_file_selection_new ("Please select a text-generating program."));
2263 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2265 fsd->widget = selector;
2268 if (p->text_program && *p->text_program)
2269 gtk_file_selection_set_filename (selector, p->text_program);
2271 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2272 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2274 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2275 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2277 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2278 GTK_SIGNAL_FUNC (browse_image_dir_close),
2281 gtk_window_set_modal (GTK_WINDOW (selector), True);
2282 gtk_widget_show (GTK_WIDGET (selector));
2289 G_MODULE_EXPORT void
2290 settings_cb (GtkButton *button, gpointer user_data)
2292 state *s = global_state_kludge; /* I hate C so much... */
2293 int list_elt = selected_list_element (s);
2295 populate_demo_window (s, list_elt); /* reset the widget */
2296 populate_popup_window (s); /* create UI on popup window */
2297 gtk_widget_show (s->popup_widget);
2301 settings_sync_cmd_text (state *s)
2304 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2305 char *cmd_line = get_configurator_command_line (s->cdata, False);
2306 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2307 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2309 # endif /* HAVE_XML */
2312 G_MODULE_EXPORT void
2313 settings_adv_cb (GtkButton *button, gpointer user_data)
2315 state *s = global_state_kludge; /* I hate C so much... */
2316 GtkNotebook *notebook =
2317 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2319 settings_sync_cmd_text (s);
2320 gtk_notebook_set_page (notebook, 1);
2323 G_MODULE_EXPORT void
2324 settings_std_cb (GtkButton *button, gpointer user_data)
2326 state *s = global_state_kludge; /* I hate C so much... */
2327 GtkNotebook *notebook =
2328 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2330 /* Re-create UI to reflect the in-progress command-line settings. */
2331 populate_popup_window (s);
2333 gtk_notebook_set_page (notebook, 0);
2336 G_MODULE_EXPORT void
2337 settings_reset_cb (GtkButton *button, gpointer user_data)
2340 state *s = global_state_kludge; /* I hate C so much... */
2341 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2342 char *cmd_line = get_configurator_command_line (s->cdata, True);
2343 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2344 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2346 populate_popup_window (s);
2347 # endif /* HAVE_XML */
2350 G_MODULE_EXPORT void
2351 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2352 gint page_num, gpointer user_data)
2354 state *s = global_state_kludge; /* I hate C so much... */
2355 GtkWidget *adv = name_to_widget (s, "adv_button");
2356 GtkWidget *std = name_to_widget (s, "std_button");
2360 gtk_widget_show (adv);
2361 gtk_widget_hide (std);
2363 else if (page_num == 1)
2365 gtk_widget_hide (adv);
2366 gtk_widget_show (std);
2374 G_MODULE_EXPORT void
2375 settings_cancel_cb (GtkButton *button, gpointer user_data)
2377 state *s = global_state_kludge; /* I hate C so much... */
2378 gtk_widget_hide (s->popup_widget);
2381 G_MODULE_EXPORT void
2382 settings_ok_cb (GtkButton *button, gpointer user_data)
2384 state *s = global_state_kludge; /* I hate C so much... */
2385 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2386 int page = gtk_notebook_get_current_page (notebook);
2389 /* Regenerate the command-line from the widget contents before saving.
2390 But don't do this if we're looking at the command-line page already,
2391 or we will blow away what they typed... */
2392 settings_sync_cmd_text (s);
2394 flush_popup_changes_and_save (s);
2395 gtk_widget_hide (s->popup_widget);
2399 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2401 state *s = (state *) data;
2402 settings_cancel_cb (0, (gpointer) s);
2408 /* Populating the various widgets
2412 /* Returns the number of the last hack run by the server.
2415 server_current_hack (void)
2419 unsigned long nitems, bytesafter;
2420 unsigned char *dataP = 0;
2421 Display *dpy = GDK_DISPLAY();
2422 int hack_number = -1;
2424 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2425 XA_SCREENSAVER_STATUS,
2426 0, 3, False, XA_INTEGER,
2427 &type, &format, &nitems, &bytesafter,
2430 && type == XA_INTEGER
2434 PROP32 *data = (PROP32 *) dataP;
2435 hack_number = (int) data[2] - 1;
2438 if (dataP) XFree (dataP);
2444 /* Finds the number of the last hack that was run, and makes that item be
2445 selected by default.
2448 scroll_to_current_hack (state *s)
2450 saver_preferences *p = &s->prefs;
2451 int hack_number = -1;
2453 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2454 hack_number = p->selected_hack;
2455 if (hack_number < 0) /* otherwise, use the last-run */
2456 hack_number = server_current_hack ();
2457 if (hack_number < 0) /* failing that, last "one mode" */
2458 hack_number = p->selected_hack;
2459 if (hack_number < 0) /* failing that, newest hack. */
2461 /* We should only get here if the user does not have a .xscreensaver
2462 file, and the screen has not been blanked with a hack since X
2463 started up: in other words, this is probably a fresh install.
2465 Instead of just defaulting to hack #0 (in either "programs" or
2466 "alphabetical" order) let's try to default to the last runnable
2467 hack in the "programs" list: this is probably the hack that was
2468 most recently added to the xscreensaver distribution (and so
2469 it's probably the currently-coolest one!)
2471 hack_number = p->screenhacks_count-1;
2472 while (hack_number > 0 &&
2473 ! (s->hacks_available_p[hack_number] &&
2474 p->screenhacks[hack_number]->enabled_p))
2478 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2480 int list_elt = s->hack_number_to_list_elt[hack_number];
2481 GtkWidget *list = name_to_widget (s, "list");
2482 force_list_select_item (s, list, list_elt, True);
2483 populate_demo_window (s, list_elt);
2489 populate_hack_list (state *s)
2491 Display *dpy = GDK_DISPLAY();
2493 saver_preferences *p = &s->prefs;
2494 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2495 GtkListStore *model;
2496 GtkTreeSelection *selection;
2497 GtkCellRenderer *ren;
2501 g_object_get (G_OBJECT (list),
2506 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2507 g_object_set (G_OBJECT (list), "model", model, NULL);
2508 g_object_unref (model);
2510 ren = gtk_cell_renderer_toggle_new ();
2511 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2513 "active", COL_ENABLED,
2516 g_signal_connect (ren, "toggled",
2517 G_CALLBACK (list_checkbox_cb),
2520 ren = gtk_cell_renderer_text_new ();
2521 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2522 _("Screen Saver"), ren,
2526 g_signal_connect_after (list, "row_activated",
2527 G_CALLBACK (list_activated_cb),
2530 selection = gtk_tree_view_get_selection (list);
2531 g_signal_connect (selection, "changed",
2532 G_CALLBACK (list_select_changed_cb),
2537 for (i = 0; i < s->list_count; i++)
2539 int hack_number = s->list_elt_to_hack_number[i];
2540 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2542 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2544 if (!hack) continue;
2546 /* If we're to suppress uninstalled hacks, check $PATH now. */
2547 if (p->ignore_uninstalled_p && !available_p)
2550 pretty_name = (hack->name
2551 ? strdup (hack->name)
2552 : make_hack_name (dpy, hack->command));
2556 /* Make the text foreground be the color of insensitive widgets
2557 (but don't actually make it be insensitive, since we still
2558 want to be able to click on it.)
2560 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
2561 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2562 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2563 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2565 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2566 /* " background=\"#%02X%02X%02X\"" */
2568 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2569 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2575 gtk_list_store_append (model, &iter);
2576 gtk_list_store_set (model, &iter,
2577 COL_ENABLED, hack->enabled_p,
2578 COL_NAME, pretty_name,
2583 #else /* !HAVE_GTK2 */
2585 saver_preferences *p = &s->prefs;
2586 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2588 for (i = 0; i < s->list_count; i++)
2590 int hack_number = s->list_elt_to_hack_number[i];
2591 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2593 /* A GtkList must contain only GtkListItems, but those can contain
2594 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2595 and a Label. We handle single and double click events on the
2596 line itself, for clicking on the text, but the interior checkbox
2597 also handles its own events.
2600 GtkWidget *line_hbox;
2601 GtkWidget *line_check;
2602 GtkWidget *line_label;
2604 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2606 if (!hack) continue;
2608 /* If we're to suppress uninstalled hacks, check $PATH now. */
2609 if (p->ignore_uninstalled_p && !available_p)
2612 pretty_name = (hack->name
2613 ? strdup (hack->name)
2614 : make_hack_name (hack->command));
2616 line = gtk_list_item_new ();
2617 line_hbox = gtk_hbox_new (FALSE, 0);
2618 line_check = gtk_check_button_new ();
2619 line_label = gtk_label_new (pretty_name);
2621 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2622 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2623 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2625 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2627 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2629 gtk_widget_show (line_check);
2630 gtk_widget_show (line_label);
2631 gtk_widget_show (line_hbox);
2632 gtk_widget_show (line);
2636 gtk_container_add (GTK_CONTAINER (list), line);
2637 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2638 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2641 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2642 GTK_SIGNAL_FUNC (list_checkbox_cb),
2645 gtk_widget_show (line);
2649 /* Make the widget be colored like insensitive widgets
2650 (but don't actually make it be insensitive, since we
2651 still want to be able to click on it.)
2653 GtkRcStyle *rc_style;
2656 gtk_widget_realize (GTK_WIDGET (line_label));
2658 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2659 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2661 rc_style = gtk_rc_style_new ();
2662 rc_style->fg[GTK_STATE_NORMAL] = fg;
2663 rc_style->bg[GTK_STATE_NORMAL] = bg;
2664 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2666 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2667 gtk_rc_style_unref (rc_style);
2671 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2672 GTK_SIGNAL_FUNC (list_select_cb),
2674 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2675 GTK_SIGNAL_FUNC (list_unselect_cb),
2677 #endif /* !HAVE_GTK2 */
2681 update_list_sensitivity (state *s)
2683 saver_preferences *p = &s->prefs;
2684 Bool sensitive = (p->mode == RANDOM_HACKS ||
2685 p->mode == RANDOM_HACKS_SAME ||
2686 p->mode == ONE_HACK);
2687 Bool checkable = (p->mode == RANDOM_HACKS ||
2688 p->mode == RANDOM_HACKS_SAME);
2689 Bool blankable = (p->mode != DONT_BLANK);
2692 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2693 GtkWidget *use = name_to_widget (s, "use_col_frame");
2694 #endif /* HAVE_GTK2 */
2695 GtkWidget *scroller = name_to_widget (s, "scroller");
2696 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2697 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2700 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2701 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2702 #else /* !HAVE_GTK2 */
2703 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2704 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2706 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2707 #endif /* !HAVE_GTK2 */
2708 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2709 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2711 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2714 gtk_tree_view_column_set_visible (use, checkable);
2715 #else /* !HAVE_GTK2 */
2717 gtk_widget_show (use); /* the "Use" column header */
2719 gtk_widget_hide (use);
2723 GtkBin *line = GTK_BIN (kids->data);
2724 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2725 GtkWidget *line_check =
2726 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2729 gtk_widget_show (line_check);
2731 gtk_widget_hide (line_check);
2735 #endif /* !HAVE_GTK2 */
2740 populate_prefs_page (state *s)
2742 saver_preferences *p = &s->prefs;
2744 Bool can_lock_p = True;
2746 /* Disable all the "lock" controls if locking support was not provided
2747 at compile-time, or if running on MacOS. */
2748 # if defined(NO_LOCKING) || defined(__APPLE__)
2753 /* If there is only one screen, the mode menu contains
2754 "random" but not "random-same".
2756 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2757 p->mode = RANDOM_HACKS;
2760 /* The file supports timeouts of less than a minute, but the GUI does
2761 not, so throttle the values to be at least one minute (since "0" is
2762 a bad rounding choice...)
2764 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2767 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2770 # define FMT_MINUTES(NAME,N) \
2771 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2773 # define FMT_SECONDS(NAME,N) \
2774 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2776 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2777 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2778 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2779 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2780 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2781 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2782 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2787 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2788 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2791 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2793 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2794 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2795 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2797 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2798 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2799 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2800 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2801 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2802 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2803 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2807 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2808 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2809 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2810 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2811 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2814 # undef TOGGLE_ACTIVE
2816 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2817 (p->image_directory ? p->image_directory : ""));
2818 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2820 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2823 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2824 (p->text_literal ? p->text_literal : ""));
2825 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2826 (p->text_file ? p->text_file : ""));
2827 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2828 (p->text_program ? p->text_program : ""));
2829 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2830 (p->text_url ? p->text_url : ""));
2832 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2833 p->tmode == TEXT_LITERAL);
2834 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2835 p->tmode == TEXT_FILE);
2836 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2837 p->tmode == TEXT_FILE);
2838 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2839 p->tmode == TEXT_PROGRAM);
2840 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2841 p->tmode == TEXT_PROGRAM);
2842 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2843 p->tmode == TEXT_URL);
2846 /* Map the `saver_mode' enum to mode menu to values. */
2848 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2851 for (i = 0; i < countof(mode_menu_order); i++)
2852 if (mode_menu_order[i] == p->mode)
2854 gtk_option_menu_set_history (opt, i);
2855 update_list_sensitivity (s);
2859 Bool found_any_writable_cells = False;
2860 Bool fading_possible = False;
2861 Bool dpms_supported = False;
2863 Display *dpy = GDK_DISPLAY();
2864 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2866 for (i = 0; i < nscreens; i++)
2868 Screen *s = ScreenOfDisplay (dpy, i);
2869 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2871 found_any_writable_cells = True;
2876 fading_possible = found_any_writable_cells;
2877 #ifdef HAVE_XF86VMODE_GAMMA
2878 fading_possible = True;
2881 #ifdef HAVE_DPMS_EXTENSION
2883 int op = 0, event = 0, error = 0;
2884 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2885 dpms_supported = True;
2887 #endif /* HAVE_DPMS_EXTENSION */
2890 # define SENSITIZE(NAME,SENSITIVEP) \
2891 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2893 /* Blanking and Locking
2895 SENSITIZE ("lock_button", can_lock_p);
2896 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2897 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2901 SENSITIZE ("dpms_frame", dpms_supported);
2902 SENSITIZE ("dpms_button", dpms_supported);
2903 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2904 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2905 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2906 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2907 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2908 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2909 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2910 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2911 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2915 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2916 SENSITIZE ("install_button", found_any_writable_cells);
2917 SENSITIZE ("fade_button", fading_possible);
2918 SENSITIZE ("unfade_button", fading_possible);
2920 SENSITIZE ("fade_label", (fading_possible &&
2921 (p->fade_p || p->unfade_p)));
2922 SENSITIZE ("fade_spinbutton", (fading_possible &&
2923 (p->fade_p || p->unfade_p)));
2931 populate_popup_window (state *s)
2933 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2934 char *doc_string = 0;
2936 /* #### not in Gtk 1.2
2937 gtk_label_set_selectable (doc);
2943 free_conf_data (s->cdata);
2948 saver_preferences *p = &s->prefs;
2949 int list_elt = selected_list_element (s);
2950 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2951 ? s->list_elt_to_hack_number[list_elt]
2953 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2956 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2957 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2958 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2959 s->cdata = load_configurator (cmd_line, s->debug_p);
2960 if (s->cdata && s->cdata->widget)
2961 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2966 doc_string = (s->cdata
2967 ? s->cdata->description
2969 # else /* !HAVE_XML */
2970 doc_string = _("Descriptions not available: no XML support compiled in.");
2971 # endif /* !HAVE_XML */
2973 gtk_label_set_text (doc, (doc_string
2975 : _("No description available.")));
2980 sensitize_demo_widgets (state *s, Bool sensitive_p)
2982 const char *names[] = { "demo", "settings",
2983 "cmd_label", "cmd_text", "manual",
2984 "visual", "visual_combo" };
2986 for (i = 0; i < countof(names); i++)
2988 GtkWidget *w = name_to_widget (s, names[i]);
2989 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2995 sensitize_menu_items (state *s, Bool force_p)
2997 static Bool running_p = False;
2998 static time_t last_checked = 0;
2999 time_t now = time ((time_t *) 0);
3000 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
3004 if (force_p || now > last_checked + 10) /* check every 10 seconds */
3006 running_p = xscreensaver_running_p (s);
3007 last_checked = time ((time_t *) 0);
3010 for (i = 0; i < countof(names); i++)
3012 GtkWidget *w = name_to_widget (s, names[i]);
3013 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
3018 /* When the File menu is de-posted after a "Restart Daemon" command,
3019 the window underneath doesn't repaint for some reason. I guess this
3020 is a bug in exposure handling in GTK or GDK. This works around it.
3023 force_dialog_repaint (state *s)
3026 /* Tell GDK to invalidate and repaint the whole window.
3028 GdkWindow *w = GET_WINDOW (s->toplevel_widget);
3029 GdkRegion *region = gdk_region_new ();
3031 rect.x = rect.y = 0;
3032 rect.width = rect.height = 32767;
3033 gdk_region_union_with_rect (region, &rect);
3034 gdk_window_invalidate_region (w, region, True);
3035 gdk_region_destroy (region);
3036 gdk_window_process_updates (w, True);
3038 /* Force the server to send an exposure event by creating and then
3039 destroying a window as a child of the top level shell.
3041 Display *dpy = GDK_DISPLAY();
3042 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
3044 XWindowAttributes xgwa;
3045 XGetWindowAttributes (dpy, parent, &xgwa);
3046 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
3047 XMapRaised (dpy, w);
3048 XDestroyWindow (dpy, w);
3054 /* Even though we've given these text fields a maximum number of characters,
3055 their default size is still about 30 characters wide -- so measure out
3056 a string in their font, and resize them to just fit that.
3059 fix_text_entry_sizes (state *s)
3063 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
3064 const char * const spinbuttons[] = {
3065 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3066 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3067 "dpms_off_spinbutton",
3068 "-fade_spinbutton" };
3072 for (i = 0; i < countof(spinbuttons); i++)
3074 const char *n = spinbuttons[i];
3076 while (*n == '-') n++, cols--;
3077 w = GTK_WIDGET (name_to_widget (s, n));
3078 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3079 gtk_widget_set_usize (w, width, -2);
3082 /* Now fix the width of the combo box.
3084 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3085 w = GTK_COMBO (w)->entry;
3086 width = gdk_string_width (w->style->font, "PseudoColor___");
3087 gtk_widget_set_usize (w, width, -2);
3089 /* Now fix the width of the file entry text.
3091 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3092 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3093 gtk_widget_set_usize (w, width, -2);
3095 /* Now fix the width of the command line text.
3097 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3098 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3099 gtk_widget_set_usize (w, width, -2);
3103 /* Now fix the height of the list widget:
3104 make it default to being around 10 text-lines high instead of 4.
3106 w = GTK_WIDGET (name_to_widget (s, "list"));
3110 int leading = 3; /* approximate is ok... */
3114 PangoFontMetrics *pain =
3115 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3116 gtk_widget_get_style (w)->font_desc,
3117 gtk_get_default_language ());
3118 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3119 pango_font_metrics_get_descent (pain));
3120 #else /* !HAVE_GTK2 */
3121 height = w->style->font->ascent + w->style->font->descent;
3122 #endif /* !HAVE_GTK2 */
3126 height += border * 2;
3127 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3128 gtk_widget_set_usize (w, -2, height);
3135 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3138 static char *up_arrow_xpm[] = {
3161 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3162 the end of the array (Gtk 1.2.5.) */
3163 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3164 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3167 static char *down_arrow_xpm[] = {
3190 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3191 the end of the array (Gtk 1.2.5.) */
3192 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3193 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3197 pixmapify_button (state *s, int down_p)
3201 GtkWidget *pixmapwid;
3205 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3206 style = gtk_widget_get_style (w);
3208 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3209 &style->bg[GTK_STATE_NORMAL],
3211 ? (gchar **) down_arrow_xpm
3212 : (gchar **) up_arrow_xpm));
3213 pixmapwid = gtk_pixmap_new (pixmap, mask);
3214 gtk_widget_show (pixmapwid);
3215 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3216 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3220 map_next_button_cb (GtkWidget *w, gpointer user_data)
3222 state *s = (state *) user_data;
3223 pixmapify_button (s, 1);
3227 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3229 state *s = (state *) user_data;
3230 pixmapify_button (s, 0);
3232 #endif /* !HAVE_GTK2 */
3236 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3240 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3241 GtkAllocation *allocation,
3245 GtkWidgetAuxInfo *aux_info;
3247 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3249 aux_info->width = allocation->width;
3250 aux_info->height = -2;
3254 gtk_widget_size_request (label, &req);
3257 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3260 eschew_gtk_lossage (GtkLabel *label)
3262 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3263 aux_info->width = GTK_WIDGET (label)->allocation.width;
3264 aux_info->height = -2;
3268 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3270 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3271 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3274 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3276 gtk_widget_queue_resize (GTK_WIDGET (label));
3278 #endif /* !HAVE_GTK2 */
3282 populate_demo_window (state *s, int list_elt)
3284 Display *dpy = GDK_DISPLAY();
3285 saver_preferences *p = &s->prefs;
3288 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3289 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3290 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3291 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3292 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3294 if (p->mode == BLANK_ONLY)
3297 pretty_name = strdup (_("Blank Screen"));
3298 schedule_preview (s, 0);
3300 else if (p->mode == DONT_BLANK)
3303 pretty_name = strdup (_("Screen Saver Disabled"));
3304 schedule_preview (s, 0);
3308 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3309 ? s->list_elt_to_hack_number[list_elt]
3311 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3315 ? strdup (hack->name)
3316 : make_hack_name (dpy, hack->command))
3320 schedule_preview (s, hack->command);
3322 schedule_preview (s, 0);
3326 pretty_name = strdup (_("Preview"));
3328 gtk_frame_set_label (frame1, _(pretty_name));
3329 gtk_frame_set_label (frame2, _(pretty_name));
3331 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3332 gtk_entry_set_position (cmd, 0);
3336 sprintf (title, _("%s: %.100s Settings"),
3337 progclass, (pretty_name ? pretty_name : "???"));
3338 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3341 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3343 ? (hack->visual && *hack->visual
3348 sensitize_demo_widgets (s, (hack ? True : False));
3350 if (pretty_name) free (pretty_name);
3352 ensure_selected_item_visible (list);
3354 s->_selected_list_element = list_elt;
3359 widget_deleter (GtkWidget *widget, gpointer data)
3361 /* #### Well, I want to destroy these widgets, but if I do that, they get
3362 referenced again, and eventually I get a SEGV. So instead of
3363 destroying them, I'll just hide them, and leak a bunch of memory
3364 every time the disk file changes. Go go go Gtk!
3366 #### Ok, that's a lie, I get a crash even if I just hide the widget
3367 and don't ever delete it. Fuck!
3370 gtk_widget_destroy (widget);
3372 gtk_widget_hide (widget);
3377 static char **sort_hack_cmp_names_kludge;
3379 sort_hack_cmp (const void *a, const void *b)
3385 int aa = *(int *) a;
3386 int bb = *(int *) b;
3387 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3388 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3389 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3395 initialize_sort_map (state *s)
3397 Display *dpy = GDK_DISPLAY();
3398 saver_preferences *p = &s->prefs;
3401 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3402 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3403 if (s->hacks_available_p) free (s->hacks_available_p);
3405 s->list_elt_to_hack_number = (int *)
3406 calloc (sizeof(int), p->screenhacks_count + 1);
3407 s->hack_number_to_list_elt = (int *)
3408 calloc (sizeof(int), p->screenhacks_count + 1);
3409 s->hacks_available_p = (Bool *)
3410 calloc (sizeof(Bool), p->screenhacks_count + 1);
3411 s->total_available = 0;
3413 /* Check which hacks actually exist on $PATH
3415 for (i = 0; i < p->screenhacks_count; i++)
3417 screenhack *hack = p->screenhacks[i];
3418 int on = on_path_p (hack->command) ? 1 : 0;
3419 s->hacks_available_p[i] = on;
3420 s->total_available += on;
3423 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3427 for (i = 0; i < p->screenhacks_count; i++)
3429 if (!p->ignore_uninstalled_p ||
3430 s->hacks_available_p[i])
3431 s->list_elt_to_hack_number[j++] = i;
3435 for (; j < p->screenhacks_count; j++)
3436 s->list_elt_to_hack_number[j] = -1;
3439 /* Generate list of sortable names (once)
3441 sort_hack_cmp_names_kludge = (char **)
3442 calloc (sizeof(char *), p->screenhacks_count);
3443 for (i = 0; i < p->screenhacks_count; i++)
3445 screenhack *hack = p->screenhacks[i];
3446 char *name = (hack->name && *hack->name
3447 ? strdup (hack->name)
3448 : make_hack_name (dpy, hack->command));
3450 for (str = name; *str; str++)
3451 *str = tolower(*str);
3452 sort_hack_cmp_names_kludge[i] = name;
3455 /* Sort list->hack map alphabetically
3457 qsort (s->list_elt_to_hack_number,
3458 p->screenhacks_count,
3459 sizeof(*s->list_elt_to_hack_number),
3464 for (i = 0; i < p->screenhacks_count; i++)
3465 free (sort_hack_cmp_names_kludge[i]);
3466 free (sort_hack_cmp_names_kludge);
3467 sort_hack_cmp_names_kludge = 0;
3469 /* Build inverse table */
3470 for (i = 0; i < p->screenhacks_count; i++)
3472 int n = s->list_elt_to_hack_number[i];
3474 s->hack_number_to_list_elt[n] = i;
3480 maybe_reload_init_file (state *s)
3482 Display *dpy = GDK_DISPLAY();
3483 saver_preferences *p = &s->prefs;
3486 static Bool reentrant_lock = False;
3487 if (reentrant_lock) return 0;
3488 reentrant_lock = True;
3490 if (init_file_changed_p (p))
3492 const char *f = init_file_name();
3497 if (!f || !*f) return 0;
3498 b = (char *) malloc (strlen(f) + 1024);
3501 "file \"%s\" has changed, reloading.\n"),
3503 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3506 load_init_file (dpy, p);
3507 initialize_sort_map (s);
3509 list_elt = selected_list_element (s);
3510 list = name_to_widget (s, "list");
3511 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3512 populate_hack_list (s);
3513 force_list_select_item (s, list, list_elt, True);
3514 populate_prefs_page (s);
3515 populate_demo_window (s, list_elt);
3516 ensure_selected_item_visible (list);
3521 reentrant_lock = False;
3527 /* Making the preview window have the right X visual (so that GL works.)
3530 static Visual *get_best_gl_visual (state *);
3533 x_visual_to_gdk_visual (Visual *xv)
3535 GList *gvs = gdk_list_visuals();
3536 if (!xv) return gdk_visual_get_system();
3537 for (; gvs; gvs = gvs->next)
3539 GdkVisual *gv = (GdkVisual *) gvs->data;
3540 if (xv == GDK_VISUAL_XVISUAL (gv))
3543 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3544 blurb(), (unsigned long) xv->visualid);
3549 clear_preview_window (state *s)
3555 if (!s->toplevel_widget) return; /* very early */
3556 p = name_to_widget (s, "preview");
3557 window = GET_WINDOW (p);
3559 if (!window) return;
3561 /* Flush the widget background down into the window, in case a subproc
3563 style = gtk_widget_get_style (p);
3564 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
3565 gdk_window_clear (window);
3568 int list_elt = selected_list_element (s);
3569 int hack_number = (list_elt >= 0
3570 ? s->list_elt_to_hack_number[list_elt]
3572 Bool available_p = (hack_number >= 0
3573 ? s->hacks_available_p [hack_number]
3575 Bool nothing_p = (s->total_available < 5);
3578 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3579 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3580 (s->running_preview_error_p
3581 ? (available_p ? 1 :
3584 #else /* !HAVE_GTK2 */
3585 if (s->running_preview_error_p)
3587 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3588 const char * const lines2[] = { N_("Not"), N_("Installed") };
3589 int nlines = countof(lines1);
3590 int lh = p->style->font->ascent + p->style->font->descent;
3594 const char * const *lines = (available_p ? lines1 : lines2);
3596 gdk_window_get_size (window, &w, &h);
3597 y = (h - (lh * nlines)) / 2;
3598 y += p->style->font->ascent;
3599 for (i = 0; i < nlines; i++)
3601 int sw = gdk_string_width (p->style->font, _(lines[i]));
3602 int x = (w - sw) / 2;
3603 gdk_draw_string (window, p->style->font,
3604 p->style->fg_gc[GTK_STATE_NORMAL],
3609 #endif /* !HAVE_GTK2 */
3617 reset_preview_window (state *s)
3619 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3620 when you kill one and re-start another on the same window. So maybe
3621 it's best to just always destroy and recreate the preview window
3622 when changing hacks, instead of always trying to reuse the same one?
3624 GtkWidget *pr = name_to_widget (s, "preview");
3625 if (GET_REALIZED (pr))
3627 GdkWindow *window = GET_WINDOW (pr);
3628 Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3630 gtk_widget_hide (pr);
3631 gtk_widget_unrealize (pr);
3632 gtk_widget_realize (pr);
3633 gtk_widget_show (pr);
3634 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3636 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3644 fix_preview_visual (state *s)
3646 GtkWidget *widget = name_to_widget (s, "preview");
3647 Visual *xvisual = get_best_gl_visual (s);
3648 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3649 GdkVisual *dvisual = gdk_visual_get_system();
3650 GdkColormap *cmap = (visual == dvisual
3651 ? gdk_colormap_get_system ()
3652 : gdk_colormap_new (visual, False));
3655 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3656 (visual == dvisual ? "default" : "non-default"),
3657 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3659 if (!GET_REALIZED (widget) ||
3660 gtk_widget_get_visual (widget) != visual)
3662 gtk_widget_unrealize (widget);
3663 gtk_widget_set_visual (widget, visual);
3664 gtk_widget_set_colormap (widget, cmap);
3665 gtk_widget_realize (widget);
3668 /* Set the Widget colors to be white-on-black. */
3670 GdkWindow *window = GET_WINDOW (widget);
3671 GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
3672 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3673 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3674 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3675 GdkGC *fgc = gdk_gc_new(window);
3676 GdkGC *bgc = gdk_gc_new(window);
3677 if (!gdk_color_white (cmap, fg)) abort();
3678 if (!gdk_color_black (cmap, bg)) abort();
3679 gdk_gc_set_foreground (fgc, fg);
3680 gdk_gc_set_background (fgc, bg);
3681 gdk_gc_set_foreground (bgc, bg);
3682 gdk_gc_set_background (bgc, fg);
3683 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3684 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3685 gtk_widget_set_style (widget, style);
3687 /* For debugging purposes, put a title on the window (so that
3688 it can be easily found in the output of "xwininfo -tree".)
3690 gdk_window_set_title (window, "Preview");
3693 gtk_widget_show (widget);
3701 subproc_pretty_name (state *s)
3703 if (s->running_preview_cmd)
3705 char *ps = strdup (s->running_preview_cmd);
3706 char *ss = strchr (ps, ' ');
3708 ss = strrchr (ps, '/');
3719 return strdup ("???");
3724 reap_zombies (state *s)
3726 int wait_status = 0;
3728 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3732 if (pid == s->running_preview_pid)
3734 char *ss = subproc_pretty_name (s);
3735 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3736 (unsigned long) pid, ss);
3740 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3741 (unsigned long) pid);
3747 /* Mostly lifted from driver/subprocs.c */
3749 get_best_gl_visual (state *s)
3751 Display *dpy = GDK_DISPLAY();
3760 av[ac++] = "xscreensaver-gl-helper";
3765 perror ("error creating pipe:");
3772 switch ((int) (forked = fork ()))
3776 sprintf (buf, "%s: couldn't fork", blurb());
3784 close (in); /* don't need this one */
3785 close (ConnectionNumber (dpy)); /* close display fd */
3787 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3789 perror ("could not dup() a new stdout:");
3793 execvp (av[0], av); /* shouldn't return. */
3795 if (errno != ENOENT)
3797 /* Ignore "no such file or directory" errors, unless verbose.
3798 Issue all other exec errors, though. */
3799 sprintf (buf, "%s: running %s", blurb(), av[0]);
3803 /* Note that one must use _exit() instead of exit() in procs forked
3804 off of Gtk programs -- Gtk installs an atexit handler that has a
3805 copy of the X connection (which we've already closed, for safety.)
3806 If one uses exit() instead of _exit(), then one sometimes gets a
3807 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3809 _exit (1); /* exits fork */
3815 int wait_status = 0;
3817 FILE *f = fdopen (in, "r");
3821 close (out); /* don't need this one */
3824 if (!fgets (buf, sizeof(buf)-1, f))
3828 /* Wait for the child to die. */
3829 waitpid (-1, &wait_status, 0);
3831 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3837 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3843 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3845 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3846 blurb(), av[0], result);
3858 kill_preview_subproc (state *s, Bool reset_p)
3860 s->running_preview_error_p = False;
3863 clear_preview_window (s);
3865 if (s->subproc_check_timer_id)
3867 gtk_timeout_remove (s->subproc_check_timer_id);
3868 s->subproc_check_timer_id = 0;
3869 s->subproc_check_countdown = 0;
3872 if (s->running_preview_pid)
3874 int status = kill (s->running_preview_pid, SIGTERM);
3875 char *ss = subproc_pretty_name (s);
3882 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3883 blurb(), (unsigned long) s->running_preview_pid, ss);
3888 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3889 blurb(), (unsigned long) s->running_preview_pid, ss);
3895 waitpid(s->running_preview_pid, &endstatus, 0);
3897 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3898 (unsigned long) s->running_preview_pid, ss);
3902 s->running_preview_pid = 0;
3903 if (s->running_preview_cmd) free (s->running_preview_cmd);
3904 s->running_preview_cmd = 0;
3911 reset_preview_window (s);
3912 clear_preview_window (s);
3917 /* Immediately and unconditionally launches the given process,
3918 after appending the -window-id option; sets running_preview_pid.
3921 launch_preview_subproc (state *s)
3923 saver_preferences *p = &s->prefs;
3927 const char *cmd = s->desired_preview_cmd;
3929 GtkWidget *pr = name_to_widget (s, "preview");
3932 reset_preview_window (s);
3934 window = GET_WINDOW (pr);
3936 s->running_preview_error_p = False;
3938 if (s->preview_suppressed_p)
3940 kill_preview_subproc (s, False);
3944 new_cmd = malloc (strlen (cmd) + 40);
3946 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3949 /* No window id? No command to run. */
3955 strcpy (new_cmd, cmd);
3956 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3960 kill_preview_subproc (s, False);
3963 s->running_preview_error_p = True;
3964 clear_preview_window (s);
3968 switch ((int) (forked = fork ()))
3973 sprintf (buf, "%s: couldn't fork", blurb());
3975 s->running_preview_error_p = True;
3981 close (ConnectionNumber (GDK_DISPLAY()));
3983 hack_subproc_environment (id, s->debug_p);
3985 usleep (250000); /* pause for 1/4th second before launching, to give
3986 the previous program time to die and flush its X
3987 buffer, so we don't get leftover turds on the
3990 exec_command (p->shell, new_cmd, p->nice_inferior);
3991 /* Don't bother printing an error message when we are unable to
3992 exec subprocesses; we handle that by polling the pid later.
3994 Note that one must use _exit() instead of exit() in procs forked
3995 off of Gtk programs -- Gtk installs an atexit handler that has a
3996 copy of the X connection (which we've already closed, for safety.)
3997 If one uses exit() instead of _exit(), then one sometimes gets a
3998 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
4000 _exit (1); /* exits child fork */
4005 if (s->running_preview_cmd) free (s->running_preview_cmd);
4006 s->running_preview_cmd = strdup (s->desired_preview_cmd);
4007 s->running_preview_pid = forked;
4011 char *ss = subproc_pretty_name (s);
4012 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
4013 (unsigned long) forked, ss);
4020 schedule_preview_check (s);
4023 if (new_cmd) free (new_cmd);
4028 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
4031 hack_environment (state *s)
4033 static const char *def_path =
4034 # ifdef DEFAULT_PATH_PREFIX
4035 DEFAULT_PATH_PREFIX;
4040 Display *dpy = GDK_DISPLAY();
4041 const char *odpy = DisplayString (dpy);
4042 char *ndpy = (char *) malloc(strlen(odpy) + 20);
4043 strcpy (ndpy, "DISPLAY=");
4044 strcat (ndpy, odpy);
4049 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4051 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4052 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4053 So we must leak it (and/or the previous setting). Yay.
4056 if (def_path && *def_path)
4058 const char *opath = getenv("PATH");
4059 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4060 strcpy (npath, "PATH=");
4061 strcat (npath, def_path);
4062 strcat (npath, ":");
4063 strcat (npath, opath);
4067 /* do not free(npath) -- see above */
4070 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4076 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4078 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4079 necessary yet, but it will make programs work if we had invoked
4080 them with "-root" and not with "-window-id" -- which, of course,
4083 char *nssw = (char *) malloc (40);
4084 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4086 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4087 any more, right? It's not Posix, but everyone seems to have it. */
4092 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4094 /* do not free(nssw) -- see above */
4098 /* Called from a timer:
4099 Launches the currently-chosen subprocess, if it's not already running.
4100 If there's a different process running, kills it.
4103 update_subproc_timer (gpointer data)
4105 state *s = (state *) data;
4106 if (! s->desired_preview_cmd)
4107 kill_preview_subproc (s, True);
4108 else if (!s->running_preview_cmd ||
4109 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4110 launch_preview_subproc (s);
4112 s->subproc_timer_id = 0;
4113 return FALSE; /* do not re-execute timer */
4117 settings_timer (gpointer data)
4124 /* Call this when you think you might want a preview process running.
4125 It will set a timer that will actually launch that program a second
4126 from now, if you haven't changed your mind (to avoid double-click
4127 spazzing, etc.) `cmd' may be null meaning "no process".
4130 schedule_preview (state *s, const char *cmd)
4132 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4137 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4139 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4142 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4143 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4145 if (s->subproc_timer_id)
4146 gtk_timeout_remove (s->subproc_timer_id);
4147 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4151 /* Called from a timer:
4152 Checks to see if the subproc that should be running, actually is.
4155 check_subproc_timer (gpointer data)
4157 state *s = (state *) data;
4158 Bool again_p = True;
4160 if (s->running_preview_error_p || /* already dead */
4161 s->running_preview_pid <= 0)
4169 status = kill (s->running_preview_pid, 0);
4170 if (status < 0 && errno == ESRCH)
4171 s->running_preview_error_p = True;
4175 char *ss = subproc_pretty_name (s);
4176 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4177 (unsigned long) s->running_preview_pid, ss,
4178 (s->running_preview_error_p ? "dead" : "alive"));
4182 if (s->running_preview_error_p)
4184 clear_preview_window (s);
4189 /* Otherwise, it's currently alive. We might be checking again, or we
4190 might be satisfied. */
4192 if (--s->subproc_check_countdown <= 0)
4196 return TRUE; /* re-execute timer */
4199 s->subproc_check_timer_id = 0;
4200 s->subproc_check_countdown = 0;
4201 return FALSE; /* do not re-execute timer */
4206 /* Call this just after launching a subprocess.
4207 This sets a timer that will, five times a second for two seconds,
4208 check whether the program is still running. The assumption here
4209 is that if the process didn't stay up for more than a couple of
4210 seconds, then either the program doesn't exist, or it doesn't
4211 take a -window-id argument.
4214 schedule_preview_check (state *s)
4220 fprintf (stderr, "%s: scheduling check\n", blurb());
4222 if (s->subproc_check_timer_id)
4223 gtk_timeout_remove (s->subproc_check_timer_id);
4224 s->subproc_check_timer_id =
4225 gtk_timeout_add (1000 / ticks,
4226 check_subproc_timer, (gpointer) s);
4227 s->subproc_check_countdown = ticks * seconds;
4232 screen_blanked_p (void)
4236 unsigned long nitems, bytesafter;
4237 unsigned char *dataP = 0;
4238 Display *dpy = GDK_DISPLAY();
4239 Bool blanked_p = False;
4241 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4242 XA_SCREENSAVER_STATUS,
4243 0, 3, False, XA_INTEGER,
4244 &type, &format, &nitems, &bytesafter,
4247 && type == XA_INTEGER
4251 Atom *data = (Atom *) dataP;
4252 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4255 if (dataP) XFree (dataP);
4260 /* Wake up every now and then and see if the screen is blanked.
4261 If it is, kill off the small-window demo -- no point in wasting
4262 cycles by running two screensavers at once...
4265 check_blanked_timer (gpointer data)
4267 state *s = (state *) data;
4268 Bool blanked_p = screen_blanked_p ();
4269 if (blanked_p && s->running_preview_pid)
4272 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4273 kill_preview_subproc (s, True);
4276 return True; /* re-execute timer */
4280 /* How many screens are there (including Xinerama.)
4283 screen_count (Display *dpy)
4285 int nscreens = ScreenCount(dpy);
4286 # ifdef HAVE_XINERAMA
4289 int event_number, error_number;
4290 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4291 XineramaIsActive (dpy))
4293 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4294 if (xsi) XFree (xsi);
4297 # endif /* HAVE_XINERAMA */
4303 /* Setting window manager icon
4307 init_icon (GdkWindow *window)
4309 GdkBitmap *mask = 0;
4312 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4313 (gchar **) logo_50_xpm);
4315 gdk_window_set_icon (window, 0, pixmap, mask);
4319 /* The main demo-mode command loop.
4324 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4325 XrmRepresentation *type, XrmValue *value, XPointer closure)
4328 for (i = 0; quarks[i]; i++)
4330 if (bindings[i] == XrmBindTightly)
4331 fprintf (stderr, (i == 0 ? "" : "."));
4332 else if (bindings[i] == XrmBindLoosely)
4333 fprintf (stderr, "*");
4335 fprintf (stderr, " ??? ");
4336 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4339 fprintf (stderr, ": %s\n", (char *) value->addr);
4347 gnome_screensaver_window (Screen *screen)
4349 Display *dpy = DisplayOfScreen (screen);
4350 Window root = RootWindowOfScreen (screen);
4351 Window parent, *kids;
4353 Window gnome_window = 0;
4356 if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4358 for (i = 0; i < nkids; i++)
4362 unsigned long nitems, bytesafter;
4363 unsigned char *name;
4364 if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4365 False, XA_STRING, &type, &format, &nitems,
4369 && !strcmp ((char *) name, "gnome-screensaver"))
4371 gnome_window = kids[i];
4376 if (kids) XFree ((char *) kids);
4377 return gnome_window;
4381 gnome_screensaver_active_p (void)
4383 Display *dpy = GDK_DISPLAY();
4384 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4385 return (w ? True : False);
4389 kill_gnome_screensaver (void)
4391 Display *dpy = GDK_DISPLAY();
4392 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4393 if (w) XKillClient (dpy, (XID) w);
4397 kde_screensaver_active_p (void)
4399 FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4402 fgets (buf, sizeof(buf)-1, p);
4404 if (!strcmp (buf, "true\n"))
4411 kill_kde_screensaver (void)
4413 system ("dcop kdesktop KScreensaverIface enable false");
4418 the_network_is_not_the_computer (state *s)
4420 Display *dpy = GDK_DISPLAY();
4421 char *rversion = 0, *ruser = 0, *rhost = 0;
4422 char *luser, *lhost;
4424 struct passwd *p = getpwuid (getuid ());
4425 const char *d = DisplayString (dpy);
4427 # if defined(HAVE_UNAME)
4429 if (uname (&uts) < 0)
4430 lhost = "<UNKNOWN>";
4432 lhost = uts.nodename;
4434 strcpy (lhost, getenv("SYS$NODE"));
4435 # else /* !HAVE_UNAME && !VMS */
4436 strcat (lhost, "<UNKNOWN>");
4437 # endif /* !HAVE_UNAME && !VMS */
4439 if (p && p->pw_name)
4444 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4446 /* Make a buffer that's big enough for a number of copies of all the
4447 strings, plus some. */
4448 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4449 (ruser ? strlen(ruser) : 0) +
4450 (rhost ? strlen(rhost) : 0) +
4457 if (!rversion || !*rversion)
4461 "The XScreenSaver daemon doesn't seem to be running\n"
4462 "on display \"%s\". Launch it now?"),
4465 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4467 /* Warn that the two processes are running as different users.
4471 "%s is running as user \"%s\" on host \"%s\".\n"
4472 "But the xscreensaver managing display \"%s\"\n"
4473 "is running as user \"%s\" on host \"%s\".\n"
4475 "Since they are different users, they won't be reading/writing\n"
4476 "the same ~/.xscreensaver file, so %s isn't\n"
4477 "going to work right.\n"
4479 "You should either re-run %s as \"%s\", or re-run\n"
4480 "xscreensaver as \"%s\".\n"
4482 "Restart the xscreensaver daemon now?\n"),
4483 progname, luser, lhost,
4485 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4487 progname, (ruser ? ruser : "???"),
4490 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4492 /* Warn that the two processes are running on different hosts.
4496 "%s is running as user \"%s\" on host \"%s\".\n"
4497 "But the xscreensaver managing display \"%s\"\n"
4498 "is running as user \"%s\" on host \"%s\".\n"
4500 "If those two machines don't share a file system (that is,\n"
4501 "if they don't see the same ~%s/.xscreensaver file) then\n"
4502 "%s won't work right.\n"
4504 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4505 progname, luser, lhost,
4507 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4512 else if (!!strcmp (rversion, s->short_version))
4514 /* Warn that the version numbers don't match.
4518 "This is %s version %s.\n"
4519 "But the xscreensaver managing display \"%s\"\n"
4520 "is version %s. This could cause problems.\n"
4522 "Restart the xscreensaver daemon now?\n"),
4523 progname, s->short_version,
4530 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4532 if (rversion) free (rversion);
4533 if (ruser) free (ruser);
4534 if (rhost) free (rhost);
4538 /* Note: since these dialogs are not modal, they will stack up.
4539 So we do this check *after* popping up the "xscreensaver is not
4540 running" dialog so that these are on top. Good enough.
4543 if (gnome_screensaver_active_p ())
4544 warning_dialog (s->toplevel_widget,
4546 "The GNOME screensaver daemon appears to be running.\n"
4547 "It must be stopped for XScreenSaver to work properly.\n"
4549 "Stop the GNOME screen saver daemon now?\n"),
4552 if (kde_screensaver_active_p ())
4553 warning_dialog (s->toplevel_widget,
4555 "The KDE screen saver daemon appears to be running.\n"
4556 "It must be stopped for XScreenSaver to work properly.\n"
4558 "Stop the KDE screen saver daemon now?\n"),
4563 /* We use this error handler so that X errors are preceeded by the name
4564 of the program that generated them.
4567 demo_ehandler (Display *dpy, XErrorEvent *error)
4569 state *s = global_state_kludge; /* I hate C so much... */
4570 fprintf (stderr, "\nX error in %s:\n", blurb());
4571 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4572 kill_preview_subproc (s, False);
4578 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4579 of the program that generated them; and also that we can ignore one
4580 particular bogus error message that Gdk madly spews.
4583 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4584 const gchar *message, gpointer user_data)
4586 /* Ignore the message "Got event for unknown window: 0x...".
4587 Apparently some events are coming in for the xscreensaver window
4588 (presumably reply events related to the ClientMessage) and Gdk
4589 feels the need to complain about them. So, just suppress any
4590 messages that look like that one.
4592 if (strstr (message, "unknown window"))
4595 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4596 (log_domain ? log_domain : progclass),
4597 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4598 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4599 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4600 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4601 log_level == G_LOG_LEVEL_INFO ? "info" :
4602 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4604 ((!*message || message[strlen(message)-1] != '\n')
4610 __extension__ /* shut up about "string length is greater than the length
4611 ISO C89 compilers are required to support" when including
4616 static char *defaults[] = {
4617 #include "XScreenSaver_ad.h"
4622 #ifdef HAVE_CRAPPLET
4623 static struct poptOption crapplet_options[] = {
4624 {NULL, '\0', 0, NULL, 0}
4626 #endif /* HAVE_CRAPPLET */
4629 const char *usage = "[--display dpy] [--prefs | --settings]"
4630 # ifdef HAVE_CRAPPLET
4633 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4636 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4638 state *s = (state *) user_data;
4639 Boolean oi = s->initializing_p;
4641 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4643 s->initializing_p = True;
4645 eschew_gtk_lossage (label);
4647 s->initializing_p = oi;
4653 print_widget_tree (GtkWidget *w, int depth)
4656 for (i = 0; i < depth; i++)
4657 fprintf (stderr, " ");
4658 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4660 if (GTK_IS_LIST (w))
4662 for (i = 0; i < depth+1; i++)
4663 fprintf (stderr, " ");
4664 fprintf (stderr, "...list kids...\n");
4666 else if (GTK_IS_CONTAINER (w))
4668 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4671 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4679 delayed_scroll_kludge (gpointer data)
4681 state *s = (state *) data;
4682 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4683 ensure_selected_item_visible (w);
4685 /* Oh, this is just fucking lovely, too. */
4686 w = GTK_WIDGET (name_to_widget (s, "preview"));
4687 gtk_widget_hide (w);
4688 gtk_widget_show (w);
4690 return FALSE; /* do not re-execute timer */
4696 create_xscreensaver_demo (void)
4700 nb = name_to_widget (global_state_kludge, "preview_notebook");
4701 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4703 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4707 create_xscreensaver_settings_dialog (void)
4711 box = name_to_widget (global_state_kludge, "dialog_action_area");
4713 w = name_to_widget (global_state_kludge, "adv_button");
4714 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4716 w = name_to_widget (global_state_kludge, "std_button");
4717 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4719 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4722 #endif /* HAVE_GTK2 */
4725 main (int argc, char **argv)
4729 saver_preferences *p;
4730 Bool prefs_p = False;
4731 Bool settings_p = False;
4734 Widget toplevel_shell;
4735 char *real_progname = argv[0];
4738 Bool crapplet_p = False;
4742 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4743 textdomain (GETTEXT_PACKAGE);
4746 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4747 # else /* !HAVE_GTK2 */
4748 if (!setlocale (LC_ALL, ""))
4749 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4750 # endif /* !HAVE_GTK2 */
4752 #endif /* ENABLE_NLS */
4754 str = strrchr (real_progname, '/');
4755 if (str) real_progname = str+1;
4758 memset (s, 0, sizeof(*s));
4759 s->initializing_p = True;
4762 global_state_kludge = s; /* I hate C so much... */
4764 progname = real_progname;
4766 s->short_version = (char *) malloc (5);
4767 memcpy (s->short_version, screensaver_id + 17, 4);
4768 s->short_version [4] = 0;
4771 /* Register our error message logger for every ``log domain'' known.
4772 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4773 for all of the domains that seem to be in use.
4776 const char * const domains[] = { 0,
4777 "Gtk", "Gdk", "GLib", "GModule",
4778 "GThread", "Gnome", "GnomeUI" };
4779 for (i = 0; i < countof(domains); i++)
4780 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4783 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4786 const char *dir = DEFAULT_ICONDIR;
4787 if (*dir) add_pixmap_directory (dir);
4789 # endif /* !HAVE_GTK2 */
4790 #endif /* DEFAULT_ICONDIR */
4792 /* This is gross, but Gtk understands --display and not -display...
4794 for (i = 1; i < argc; i++)
4795 if (argv[i][0] && argv[i][1] &&
4796 !strncmp(argv[i], "-display", strlen(argv[i])))
4797 argv[i] = "--display";
4800 /* We need to parse this arg really early... Sigh. */
4801 for (i = 1; i < argc; i++)
4804 (!strcmp(argv[i], "--crapplet") ||
4805 !strcmp(argv[i], "--capplet")))
4807 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4810 for (j = i; j < argc; j++) /* remove it from the list */
4811 argv[j] = argv[j+1];
4813 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4814 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4816 fprintf (stderr, "%s: %s\n", real_progname, usage);
4818 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4821 (!strcmp(argv[i], "--debug") ||
4822 !strcmp(argv[i], "-debug") ||
4823 !strcmp(argv[i], "-d")))
4827 for (j = i; j < argc; j++) /* remove it from the list */
4828 argv[j] = argv[j+1];
4835 (!strcmp(argv[i], "-geometry") ||
4836 !strcmp(argv[i], "-geom") ||
4837 !strcmp(argv[i], "-geo") ||
4838 !strcmp(argv[i], "-g")))
4842 for (j = i; j < argc; j++) /* remove them from the list */
4843 argv[j] = argv[j+2];
4850 (!strcmp(argv[i], "--configdir")))
4854 hack_configuration_path = argv[i+1];
4855 for (j = i; j < argc; j++) /* remove them from the list */
4856 argv[j] = argv[j+2];
4860 if (0 != stat (hack_configuration_path, &st))
4863 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4867 else if (!S_ISDIR (st.st_mode))
4869 fprintf (stderr, "%s: not a directory: %s\n",
4870 blurb(), hack_configuration_path);
4878 fprintf (stderr, "%s: using config directory \"%s\"\n",
4879 progname, hack_configuration_path);
4882 /* Let Gtk open the X connection, then initialize Xt to use that
4883 same connection. Doctor Frankenstein would be proud.
4885 # ifdef HAVE_CRAPPLET
4888 GnomeClient *client;
4889 GnomeClientFlags flags = 0;
4891 int init_results = gnome_capplet_init ("screensaver-properties",
4893 argc, argv, NULL, 0, NULL);
4895 0 upon successful initialization;
4896 1 if --init-session-settings was passed on the cmdline;
4897 2 if --ignore was passed on the cmdline;
4900 So the 1 signifies just to init the settings, and quit, basically.
4901 (Meaning launch the xscreensaver daemon.)
4904 if (init_results < 0)
4907 g_error ("An initialization error occurred while "
4908 "starting xscreensaver-capplet.\n");
4910 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4911 real_progname, init_results);
4916 client = gnome_master_client ();
4919 flags = gnome_client_get_flags (client);
4921 if (flags & GNOME_CLIENT_IS_CONNECTED)
4924 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4925 gnome_client_get_id (client));
4928 char *session_args[20];
4930 session_args[i++] = real_progname;
4931 session_args[i++] = "--capplet";
4932 session_args[i++] = "--init-session-settings";
4933 session_args[i] = 0;
4934 gnome_client_set_priority (client, 20);
4935 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4936 gnome_client_set_restart_command (client, i, session_args);
4940 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4943 gnome_client_flush (client);
4946 if (init_results == 1)
4948 system ("xscreensaver -nosplash &");
4954 # endif /* HAVE_CRAPPLET */
4956 gtk_init (&argc, &argv);
4960 /* We must read exactly the same resources as xscreensaver.
4961 That means we must have both the same progclass *and* progname,
4962 at least as far as the resource database is concerned. So,
4963 put "xscreensaver" in argv[0] while initializing Xt.
4965 argv[0] = "xscreensaver";
4969 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4971 XtToolkitInitialize ();
4972 app = XtCreateApplicationContext ();
4973 dpy = GDK_DISPLAY();
4974 XtAppSetFallbackResources (app, defaults);
4975 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4976 toplevel_shell = XtAppCreateShell (progname, progclass,
4977 applicationShellWidgetClass,
4980 dpy = XtDisplay (toplevel_shell);
4981 db = XtDatabase (dpy);
4982 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4983 XSetErrorHandler (demo_ehandler);
4985 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4986 signal (SIGPIPE, SIG_IGN);
4988 /* After doing Xt-style command-line processing, complain about any
4989 unrecognized command-line arguments.
4991 for (i = 1; i < argc; i++)
4993 char *str = argv[i];
4994 if (str[0] == '-' && str[1] == '-')
4996 if (!strcmp (str, "-prefs"))
4998 else if (!strcmp (str, "-settings"))
5000 else if (crapplet_p)
5001 /* There are lots of random args that we don't care about when we're
5002 started as a crapplet, so just ignore unknown args in that case. */
5006 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
5008 fprintf (stderr, "%s: %s\n", real_progname, usage);
5013 /* Load the init file, which may end up consulting the X resource database
5014 and the site-wide app-defaults file. Note that at this point, it's
5015 important that `progname' be "xscreensaver", rather than whatever
5019 s->nscreens = screen_count (dpy);
5021 hack_environment (s); /* must be before initialize_sort_map() */
5023 load_init_file (dpy, p);
5024 initialize_sort_map (s);
5026 /* Now that Xt has been initialized, and the resources have been read,
5027 we can set our `progname' variable to something more in line with
5030 progname = real_progname;
5034 /* Print out all the resources we read. */
5036 XrmName name = { 0 };
5037 XrmClass class = { 0 };
5039 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
5045 /* Intern the atoms that xscreensaver_command() needs.
5047 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
5048 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
5049 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
5050 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5051 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5052 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5053 XA_SELECT = XInternAtom (dpy, "SELECT", False);
5054 XA_DEMO = XInternAtom (dpy, "DEMO", False);
5055 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5056 XA_BLANK = XInternAtom (dpy, "BLANK", False);
5057 XA_LOCK = XInternAtom (dpy, "LOCK", False);
5058 XA_EXIT = XInternAtom (dpy, "EXIT", False);
5059 XA_RESTART = XInternAtom (dpy, "RESTART", False);
5062 /* Create the window and all its widgets.
5064 s->base_widget = create_xscreensaver_demo ();
5065 s->popup_widget = create_xscreensaver_settings_dialog ();
5066 s->toplevel_widget = s->base_widget;
5069 /* Set the main window's title. */
5071 char *base_title = _("Screensaver Preferences");
5072 char *v = (char *) strdup(strchr(screensaver_id, ' '));
5073 char *s1, *s2, *s3, *s4;
5074 s1 = (char *) strchr(v, ' '); s1++;
5075 s2 = (char *) strchr(s1, ' ');
5076 s3 = (char *) strchr(v, '('); s3++;
5077 s4 = (char *) strchr(s3, ')');
5081 window_title = (char *) malloc (strlen (base_title) +
5082 strlen (progclass) +
5083 strlen (s1) + strlen (s3) +
5085 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
5086 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5087 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
5091 /* Adjust the (invisible) notebooks on the popup dialog... */
5093 GtkNotebook *notebook =
5094 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5095 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5099 gtk_widget_hide (std);
5100 # else /* !HAVE_XML */
5101 /* Make the advanced page be the only one available. */
5102 gtk_widget_set_sensitive (std, False);
5103 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5104 gtk_widget_hide (std);
5105 std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5106 gtk_widget_hide (std);
5108 # endif /* !HAVE_XML */
5110 gtk_notebook_set_page (notebook, page);
5111 gtk_notebook_set_show_tabs (notebook, False);
5114 /* Various other widget initializations...
5116 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5117 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5119 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5120 GTK_SIGNAL_FUNC (wm_popup_close_cb),
5123 populate_hack_list (s);
5124 populate_prefs_page (s);
5125 sensitize_demo_widgets (s, False);
5126 fix_text_entry_sizes (s);
5127 scroll_to_current_hack (s);
5129 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5130 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5134 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5135 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5137 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5138 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5140 #endif /* !HAVE_GTK2 */
5142 /* Hook up callbacks to the items on the mode menu. */
5144 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5145 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5146 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5148 for (i = 0; kids; kids = kids->next, i++)
5150 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5151 GTK_SIGNAL_FUNC (mode_menu_item_cb),
5154 /* The "random-same" mode menu item does not appear unless
5155 there are multple screens.
5157 if (s->nscreens <= 1 &&
5158 mode_menu_order[i] == RANDOM_HACKS_SAME)
5159 gtk_widget_hide (GTK_WIDGET (kids->data));
5162 if (s->nscreens <= 1) /* recompute option-menu size */
5164 gtk_widget_unrealize (GTK_WIDGET (menu));
5165 gtk_widget_realize (GTK_WIDGET (menu));
5170 /* Handle the -prefs command-line argument. */
5173 GtkNotebook *notebook =
5174 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5175 gtk_notebook_set_page (notebook, 1);
5178 # ifdef HAVE_CRAPPLET
5182 GtkWidget *outer_vbox;
5184 gtk_widget_hide (s->toplevel_widget);
5186 capplet = capplet_widget_new ();
5188 /* Make there be a "Close" button instead of "OK" and "Cancel" */
5189 # ifdef HAVE_CRAPPLET_IMMEDIATE
5190 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5191 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5192 /* In crapplet-mode, take off the menubar. */
5193 gtk_widget_hide (name_to_widget (s, "menubar"));
5195 /* Reparent our top-level container to be a child of the capplet
5198 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5199 gtk_widget_ref (outer_vbox);
5200 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5202 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5203 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5205 /* Find the window above us, and set the title and close handler. */
5207 GtkWidget *window = capplet;
5208 while (window && !GTK_IS_WINDOW (window))
5209 window = GET_PARENT (window);
5212 gtk_window_set_title (GTK_WINDOW (window), window_title);
5213 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5214 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5219 s->toplevel_widget = capplet;
5221 # endif /* HAVE_CRAPPLET */
5224 /* The Gnome folks hate the menubar. I think it's important to have access
5225 to the commands on the File menu (Restart Daemon, etc.) and to the
5226 About and Documentation commands on the Help menu.
5230 gtk_widget_hide (name_to_widget (s, "menubar"));
5234 free (window_title);
5238 /* After picking the default size, allow -geometry to override it. */
5240 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5243 gtk_widget_show (s->toplevel_widget);
5244 init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */
5245 fix_preview_visual (s);
5247 /* Realize page zero, so that we can diddle the scrollbar when the
5248 user tabs back to it -- otherwise, the current hack isn't scrolled
5249 to the first time they tab back there, when started with "-prefs".
5250 (Though it is if they then tab away, and back again.)
5252 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5253 #### understands this crap, explain to me how to make this work.
5255 gtk_widget_realize (name_to_widget (s, "demos_table"));
5258 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5261 /* Handle the --settings command-line argument. */
5263 gtk_timeout_add (500, settings_timer, 0);
5266 /* Issue any warnings about the running xscreensaver daemon. */
5268 the_network_is_not_the_computer (s);
5271 /* Run the Gtk event loop, and not the Xt event loop. This means that
5272 if there were Xt timers or fds registered, they would never get serviced,
5273 and if there were any Xt widgets, they would never have events delivered.
5274 Fortunately, we're using Gtk for all of the UI, and only initialized
5275 Xt so that we could process the command line and use the X resource
5278 s->initializing_p = False;
5280 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5281 after we start up. Otherwise, it always appears scrolled to the top
5282 when in crapplet-mode. */
5283 gtk_timeout_add (500, delayed_scroll_kludge, s);
5287 /* Load every configurator in turn, to scan them for errors all at once. */
5291 for (i = 0; i < p->screenhacks_count; i++)
5293 screenhack *hack = p->screenhacks[i];
5294 conf_data *d = load_configurator (hack->command, s->debug_p);
5295 if (d) free_conf_data (d);
5301 # ifdef HAVE_CRAPPLET
5303 capplet_gtk_main ();
5305 # endif /* HAVE_CRAPPLET */
5308 kill_preview_subproc (s, False);
5312 #endif /* HAVE_GTK -- whole file */