1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2011 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 CHECKBOX (p2->dpms_quickoff_p, "dpms_quickoff_button");
1553 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1554 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1555 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1557 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1558 CHECKBOX (p2->grab_video_p, "grab_video_button");
1559 CHECKBOX (p2->random_image_p, "grab_image_button");
1560 PATHNAME (p2->image_directory, "image_text");
1563 CHECKBOX (p2->verbose_p, "verbose_button");
1564 CHECKBOX (p2->capture_stderr_p, "capture_button");
1565 CHECKBOX (p2->splash_p, "splash_button");
1570 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1571 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1572 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1573 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1574 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1575 TEXT (p2->text_literal, "text_entry");
1576 PATHNAME (p2->text_file, "text_file_entry");
1577 PATHNAME (p2->text_program, "text_program_entry");
1578 PATHNAME (p2->text_program, "text_program_entry");
1579 TEXT (p2->text_url, "text_url_entry");
1582 CHECKBOX (p2->install_cmap_p, "install_button");
1583 CHECKBOX (p2->fade_p, "fade_button");
1584 CHECKBOX (p2->unfade_p, "unfade_button");
1585 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1593 /* Warn if the image directory doesn't exist, when:
1594 - not being warned before
1595 - image directory is changed and the directory doesn't exist
1597 if (p2->image_directory &&
1598 *p2->image_directory &&
1599 !directory_p (p2->image_directory) &&
1600 ( !already_warned_about_missing_image_directory ||
1601 ( p->image_directory &&
1602 *p->image_directory &&
1603 strcmp(p->image_directory, p2->image_directory)
1609 sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n",
1610 p2->image_directory);
1611 if (warning_dialog (s->toplevel_widget, b, D_NONE, 100))
1612 already_warned_about_missing_image_directory = True;
1616 /* Map the mode menu to `saver_mode' enum values. */
1618 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1619 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1620 GtkWidget *selected = gtk_menu_get_active (menu);
1621 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1622 int menu_elt = g_list_index (kids, (gpointer) selected);
1623 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1624 p2->mode = mode_menu_order[menu_elt];
1627 if (p2->mode == ONE_HACK)
1629 int list_elt = selected_list_element (s);
1630 p2->selected_hack = (list_elt >= 0
1631 ? s->list_elt_to_hack_number[list_elt]
1635 # define COPY(field, name) \
1636 if (p->field != p2->field) { \
1639 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1641 p->field = p2->field
1644 COPY(selected_hack, "selected_hack");
1646 COPY(timeout, "timeout");
1647 COPY(cycle, "cycle");
1648 COPY(lock_p, "lock_p");
1649 COPY(lock_timeout, "lock_timeout");
1651 COPY(dpms_enabled_p, "dpms_enabled_p");
1652 COPY(dpms_quickoff_p, "dpms_quickoff_enabled_p");
1653 COPY(dpms_standby, "dpms_standby");
1654 COPY(dpms_suspend, "dpms_suspend");
1655 COPY(dpms_off, "dpms_off");
1658 COPY(verbose_p, "verbose_p");
1659 COPY(capture_stderr_p, "capture_stderr_p");
1660 COPY(splash_p, "splash_p");
1663 COPY(tmode, "tmode");
1665 COPY(install_cmap_p, "install_cmap_p");
1666 COPY(fade_p, "fade_p");
1667 COPY(unfade_p, "unfade_p");
1668 COPY(fade_seconds, "fade_seconds");
1670 COPY(grab_desktop_p, "grab_desktop_p");
1671 COPY(grab_video_p, "grab_video_p");
1672 COPY(random_image_p, "random_image_p");
1676 # define COPYSTR(FIELD,NAME) \
1679 strcmp(p->FIELD, p2->FIELD)) \
1683 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1685 if (p->FIELD && p->FIELD != p2->FIELD) \
1687 p->FIELD = p2->FIELD; \
1690 COPYSTR(image_directory, "image_directory");
1691 COPYSTR(text_literal, "text_literal");
1692 COPYSTR(text_file, "text_file");
1693 COPYSTR(text_program, "text_program");
1694 COPYSTR(text_url, "text_url");
1697 populate_prefs_page (s);
1701 Display *dpy = GDK_DISPLAY();
1702 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1703 sync_server_dpms_settings (dpy, enabled_p,
1704 p->dpms_standby / 1000,
1705 p->dpms_suspend / 1000,
1709 changed = demo_write_init_file (s, p);
1712 s->saving_p = False;
1717 /* Flush out any changes made in the popup dialog box (where changes
1718 take place only when the OK button is clicked.)
1721 flush_popup_changes_and_save (state *s)
1723 Bool changed = False;
1724 saver_preferences *p = &s->prefs;
1725 int list_elt = selected_list_element (s);
1727 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1728 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1730 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1731 const char *command = gtk_entry_get_text (cmd);
1736 if (s->saving_p) return False;
1742 if (maybe_reload_init_file (s) != 0)
1748 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1750 if (!strcasecmp (visual, "")) visual = "";
1751 else if (!strcasecmp (visual, "any")) visual = "";
1752 else if (!strcasecmp (visual, "default")) visual = "Default";
1753 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1754 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1755 else if (!strcasecmp (visual, "best")) visual = "Best";
1756 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1757 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1758 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1759 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1760 else if (!strcasecmp (visual, "color")) visual = "Color";
1761 else if (!strcasecmp (visual, "gl")) visual = "GL";
1762 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1763 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1764 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1765 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1766 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1767 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1768 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1769 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1770 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1773 gdk_beep (); /* unparsable */
1775 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1778 changed = flush_changes (s, list_elt, -1, command, visual);
1781 changed = demo_write_init_file (s, p);
1783 /* Do this to re-launch the hack if (and only if) the command line
1785 populate_demo_window (s, selected_list_element (s));
1789 s->saving_p = False;
1794 G_MODULE_EXPORT void
1795 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1797 state *s = global_state_kludge; /* I hate C so much... */
1798 if (! s->initializing_p)
1800 s->initializing_p = True;
1801 flush_dialog_changes_and_save (s);
1802 s->initializing_p = False;
1806 G_MODULE_EXPORT gboolean
1807 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1809 pref_changed_cb (widget, user_data);
1813 /* Callback on menu items in the "mode" options menu.
1815 G_MODULE_EXPORT void
1816 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1818 state *s = (state *) user_data;
1819 saver_preferences *p = &s->prefs;
1820 GtkWidget *list = name_to_widget (s, "list");
1824 gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
1826 saver_mode new_mode;
1830 if (menu_items->data == widget)
1833 menu_items = menu_items->next;
1835 if (!menu_items) abort();
1837 new_mode = mode_menu_order[menu_index];
1839 /* Keep the same list element displayed as before; except if we're
1840 switching *to* "one screensaver" mode from any other mode, set
1841 "the one" to be that which is currently selected.
1843 list_elt = selected_list_element (s);
1844 if (new_mode == ONE_HACK)
1845 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1848 saver_mode old_mode = p->mode;
1850 populate_demo_window (s, list_elt);
1851 force_list_select_item (s, list, list_elt, True);
1852 p->mode = old_mode; /* put it back, so the init file gets written */
1855 pref_changed_cb (widget, user_data);
1859 G_MODULE_EXPORT void
1860 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1861 gint page_num, gpointer user_data)
1863 state *s = global_state_kludge; /* I hate C so much... */
1864 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1866 /* If we're switching to page 0, schedule the current hack to be run.
1867 Otherwise, schedule it to stop. */
1869 populate_demo_window (s, selected_list_element (s));
1871 schedule_preview (s, 0);
1876 list_activated_cb (GtkTreeView *list,
1878 GtkTreeViewColumn *column,
1885 g_return_if_fail (!gdk_pointer_is_grabbed ());
1887 str = gtk_tree_path_to_string (path);
1888 list_elt = strtol (str, NULL, 10);
1892 run_hack (s, list_elt, True);
1896 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1898 state *s = (state *)data;
1899 GtkTreeModel *model;
1905 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1908 path = gtk_tree_model_get_path (model, &iter);
1909 str = gtk_tree_path_to_string (path);
1910 list_elt = strtol (str, NULL, 10);
1912 gtk_tree_path_free (path);
1915 populate_demo_window (s, list_elt);
1916 flush_dialog_changes_and_save (s);
1918 /* Re-populate the Settings window any time a new item is selected
1919 in the list, in case both windows are currently visible.
1921 populate_popup_window (s);
1924 #else /* !HAVE_GTK2 */
1926 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1927 list_select_cb that comes in
1928 *after* we've double-clicked.
1932 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1935 state *s = (state *) data;
1936 if (event->type == GDK_2BUTTON_PRESS)
1938 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1939 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1941 last_doubleclick_time = time ((time_t *) 0);
1944 run_hack (s, list_elt, True);
1952 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1954 state *s = (state *) data;
1955 time_t now = time ((time_t *) 0);
1957 if (now >= last_doubleclick_time + 2)
1959 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1960 populate_demo_window (s, list_elt);
1961 flush_dialog_changes_and_save (s);
1966 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1968 state *s = (state *) data;
1969 populate_demo_window (s, -1);
1970 flush_dialog_changes_and_save (s);
1973 #endif /* !HAVE_GTK2 */
1976 /* Called when the checkboxes that are in the left column of the
1977 scrolling list are clicked. This both populates the right pane
1978 (just as clicking on the label (really, listitem) does) and
1979 also syncs this checkbox with the right pane Enabled checkbox.
1984 GtkCellRendererToggle *toggle,
1986 #else /* !HAVE_GTK2 */
1988 #endif /* !HAVE_GTK2 */
1991 state *s = (state *) data;
1994 GtkScrolledWindow *scroller =
1995 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1996 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1997 GtkTreeModel *model = gtk_tree_view_get_model (list);
1998 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
2001 #else /* !HAVE_GTK2 */
2002 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
2003 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
2005 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
2006 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
2007 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
2008 #endif /* !HAVE_GTK2 */
2015 if (!gtk_tree_model_get_iter (model, &iter, path))
2017 g_warning ("bad path: %s", path_string);
2020 gtk_tree_path_free (path);
2022 gtk_tree_model_get (model, &iter,
2023 COL_ENABLED, &active,
2026 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2027 COL_ENABLED, !active,
2030 list_elt = strtol (path_string, NULL, 10);
2031 #else /* !HAVE_GTK2 */
2032 list_elt = gtk_list_child_position (list, line);
2033 #endif /* !HAVE_GTK2 */
2035 /* remember previous scroll position of the top of the list */
2036 adj = gtk_scrolled_window_get_vadjustment (scroller);
2037 scroll_top = GET_ADJ_VALUE (adj);
2039 flush_dialog_changes_and_save (s);
2040 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
2041 populate_demo_window (s, list_elt);
2043 /* restore the previous scroll position of the top of the list.
2044 this is weak, but I don't really know why it's moving... */
2045 gtk_adjustment_set_value (adj, scroll_top);
2051 GtkFileSelection *widget;
2052 } file_selection_data;
2057 store_image_directory (GtkWidget *button, gpointer user_data)
2059 file_selection_data *fsd = (file_selection_data *) user_data;
2060 state *s = fsd->state;
2061 GtkFileSelection *selector = fsd->widget;
2062 GtkWidget *top = s->toplevel_widget;
2063 saver_preferences *p = &s->prefs;
2064 const char *path = gtk_file_selection_get_filename (selector);
2066 if (p->image_directory && !strcmp(p->image_directory, path))
2067 return; /* no change */
2069 if (!directory_p (path))
2072 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2073 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2077 if (p->image_directory) free (p->image_directory);
2078 p->image_directory = normalize_directory (path);
2080 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2081 (p->image_directory ? p->image_directory : ""));
2082 demo_write_init_file (s, p);
2087 store_text_file (GtkWidget *button, gpointer user_data)
2089 file_selection_data *fsd = (file_selection_data *) user_data;
2090 state *s = fsd->state;
2091 GtkFileSelection *selector = fsd->widget;
2092 GtkWidget *top = s->toplevel_widget;
2093 saver_preferences *p = &s->prefs;
2094 const char *path = gtk_file_selection_get_filename (selector);
2096 if (p->text_file && !strcmp(p->text_file, path))
2097 return; /* no change */
2102 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2103 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2107 if (p->text_file) free (p->text_file);
2108 p->text_file = normalize_directory (path);
2110 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2111 (p->text_file ? p->text_file : ""));
2112 demo_write_init_file (s, p);
2117 store_text_program (GtkWidget *button, gpointer user_data)
2119 file_selection_data *fsd = (file_selection_data *) user_data;
2120 state *s = fsd->state;
2121 GtkFileSelection *selector = fsd->widget;
2122 /*GtkWidget *top = s->toplevel_widget;*/
2123 saver_preferences *p = &s->prefs;
2124 const char *path = gtk_file_selection_get_filename (selector);
2126 if (p->text_program && !strcmp(p->text_program, path))
2127 return; /* no change */
2133 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2134 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2139 if (p->text_program) free (p->text_program);
2140 p->text_program = normalize_directory (path);
2142 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2143 (p->text_program ? p->text_program : ""));
2144 demo_write_init_file (s, p);
2150 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2152 file_selection_data *fsd = (file_selection_data *) user_data;
2153 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2157 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2159 browse_image_dir_cancel (button, user_data);
2160 store_image_directory (button, user_data);
2164 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2166 browse_image_dir_cancel (button, user_data);
2167 store_text_file (button, user_data);
2171 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2173 browse_image_dir_cancel (button, user_data);
2174 store_text_program (button, user_data);
2178 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2180 browse_image_dir_cancel (widget, user_data);
2184 G_MODULE_EXPORT void
2185 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2187 state *s = global_state_kludge; /* I hate C so much... */
2188 saver_preferences *p = &s->prefs;
2189 static file_selection_data *fsd = 0;
2191 GtkFileSelection *selector = GTK_FILE_SELECTION(
2192 gtk_file_selection_new ("Please select the image directory."));
2195 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2197 fsd->widget = selector;
2200 if (p->image_directory && *p->image_directory)
2201 gtk_file_selection_set_filename (selector, p->image_directory);
2203 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2204 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2206 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2207 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2209 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2210 GTK_SIGNAL_FUNC (browse_image_dir_close),
2213 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2215 gtk_window_set_modal (GTK_WINDOW (selector), True);
2216 gtk_widget_show (GTK_WIDGET (selector));
2220 G_MODULE_EXPORT void
2221 browse_text_file_cb (GtkButton *button, gpointer user_data)
2223 state *s = global_state_kludge; /* I hate C so much... */
2224 saver_preferences *p = &s->prefs;
2225 static file_selection_data *fsd = 0;
2227 GtkFileSelection *selector = GTK_FILE_SELECTION(
2228 gtk_file_selection_new ("Please select a text file."));
2231 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2233 fsd->widget = selector;
2236 if (p->text_file && *p->text_file)
2237 gtk_file_selection_set_filename (selector, p->text_file);
2239 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2240 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2242 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2243 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2245 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2246 GTK_SIGNAL_FUNC (browse_image_dir_close),
2249 gtk_window_set_modal (GTK_WINDOW (selector), True);
2250 gtk_widget_show (GTK_WIDGET (selector));
2254 G_MODULE_EXPORT void
2255 browse_text_program_cb (GtkButton *button, gpointer user_data)
2257 state *s = global_state_kludge; /* I hate C so much... */
2258 saver_preferences *p = &s->prefs;
2259 static file_selection_data *fsd = 0;
2261 GtkFileSelection *selector = GTK_FILE_SELECTION(
2262 gtk_file_selection_new ("Please select a text-generating program."));
2265 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2267 fsd->widget = selector;
2270 if (p->text_program && *p->text_program)
2271 gtk_file_selection_set_filename (selector, p->text_program);
2273 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2274 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2276 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2277 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2279 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2280 GTK_SIGNAL_FUNC (browse_image_dir_close),
2283 gtk_window_set_modal (GTK_WINDOW (selector), True);
2284 gtk_widget_show (GTK_WIDGET (selector));
2291 G_MODULE_EXPORT void
2292 settings_cb (GtkButton *button, gpointer user_data)
2294 state *s = global_state_kludge; /* I hate C so much... */
2295 int list_elt = selected_list_element (s);
2297 populate_demo_window (s, list_elt); /* reset the widget */
2298 populate_popup_window (s); /* create UI on popup window */
2299 gtk_widget_show (s->popup_widget);
2303 settings_sync_cmd_text (state *s)
2306 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2307 char *cmd_line = get_configurator_command_line (s->cdata, False);
2308 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2309 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2311 # endif /* HAVE_XML */
2314 G_MODULE_EXPORT void
2315 settings_adv_cb (GtkButton *button, gpointer user_data)
2317 state *s = global_state_kludge; /* I hate C so much... */
2318 GtkNotebook *notebook =
2319 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2321 settings_sync_cmd_text (s);
2322 gtk_notebook_set_page (notebook, 1);
2325 G_MODULE_EXPORT void
2326 settings_std_cb (GtkButton *button, gpointer user_data)
2328 state *s = global_state_kludge; /* I hate C so much... */
2329 GtkNotebook *notebook =
2330 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2332 /* Re-create UI to reflect the in-progress command-line settings. */
2333 populate_popup_window (s);
2335 gtk_notebook_set_page (notebook, 0);
2338 G_MODULE_EXPORT void
2339 settings_reset_cb (GtkButton *button, gpointer user_data)
2342 state *s = global_state_kludge; /* I hate C so much... */
2343 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2344 char *cmd_line = get_configurator_command_line (s->cdata, True);
2345 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2346 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2348 populate_popup_window (s);
2349 # endif /* HAVE_XML */
2352 G_MODULE_EXPORT void
2353 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2354 gint page_num, gpointer user_data)
2356 state *s = global_state_kludge; /* I hate C so much... */
2357 GtkWidget *adv = name_to_widget (s, "adv_button");
2358 GtkWidget *std = name_to_widget (s, "std_button");
2362 gtk_widget_show (adv);
2363 gtk_widget_hide (std);
2365 else if (page_num == 1)
2367 gtk_widget_hide (adv);
2368 gtk_widget_show (std);
2376 G_MODULE_EXPORT void
2377 settings_cancel_cb (GtkButton *button, gpointer user_data)
2379 state *s = global_state_kludge; /* I hate C so much... */
2380 gtk_widget_hide (s->popup_widget);
2383 G_MODULE_EXPORT void
2384 settings_ok_cb (GtkButton *button, gpointer user_data)
2386 state *s = global_state_kludge; /* I hate C so much... */
2387 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2388 int page = gtk_notebook_get_current_page (notebook);
2391 /* Regenerate the command-line from the widget contents before saving.
2392 But don't do this if we're looking at the command-line page already,
2393 or we will blow away what they typed... */
2394 settings_sync_cmd_text (s);
2396 flush_popup_changes_and_save (s);
2397 gtk_widget_hide (s->popup_widget);
2401 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2403 state *s = (state *) data;
2404 settings_cancel_cb (0, (gpointer) s);
2410 /* Populating the various widgets
2414 /* Returns the number of the last hack run by the server.
2417 server_current_hack (void)
2421 unsigned long nitems, bytesafter;
2422 unsigned char *dataP = 0;
2423 Display *dpy = GDK_DISPLAY();
2424 int hack_number = -1;
2426 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2427 XA_SCREENSAVER_STATUS,
2428 0, 3, False, XA_INTEGER,
2429 &type, &format, &nitems, &bytesafter,
2432 && type == XA_INTEGER
2436 PROP32 *data = (PROP32 *) dataP;
2437 hack_number = (int) data[2] - 1;
2440 if (dataP) XFree (dataP);
2446 /* Finds the number of the last hack that was run, and makes that item be
2447 selected by default.
2450 scroll_to_current_hack (state *s)
2452 saver_preferences *p = &s->prefs;
2453 int hack_number = -1;
2455 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2456 hack_number = p->selected_hack;
2457 if (hack_number < 0) /* otherwise, use the last-run */
2458 hack_number = server_current_hack ();
2459 if (hack_number < 0) /* failing that, last "one mode" */
2460 hack_number = p->selected_hack;
2461 if (hack_number < 0) /* failing that, newest hack. */
2463 /* We should only get here if the user does not have a .xscreensaver
2464 file, and the screen has not been blanked with a hack since X
2465 started up: in other words, this is probably a fresh install.
2467 Instead of just defaulting to hack #0 (in either "programs" or
2468 "alphabetical" order) let's try to default to the last runnable
2469 hack in the "programs" list: this is probably the hack that was
2470 most recently added to the xscreensaver distribution (and so
2471 it's probably the currently-coolest one!)
2473 hack_number = p->screenhacks_count-1;
2474 while (hack_number > 0 &&
2475 ! (s->hacks_available_p[hack_number] &&
2476 p->screenhacks[hack_number]->enabled_p))
2480 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2482 int list_elt = s->hack_number_to_list_elt[hack_number];
2483 GtkWidget *list = name_to_widget (s, "list");
2484 force_list_select_item (s, list, list_elt, True);
2485 populate_demo_window (s, list_elt);
2491 populate_hack_list (state *s)
2493 Display *dpy = GDK_DISPLAY();
2495 saver_preferences *p = &s->prefs;
2496 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2497 GtkListStore *model;
2498 GtkTreeSelection *selection;
2499 GtkCellRenderer *ren;
2503 g_object_get (G_OBJECT (list),
2508 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2509 g_object_set (G_OBJECT (list), "model", model, NULL);
2510 g_object_unref (model);
2512 ren = gtk_cell_renderer_toggle_new ();
2513 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2515 "active", COL_ENABLED,
2518 g_signal_connect (ren, "toggled",
2519 G_CALLBACK (list_checkbox_cb),
2522 ren = gtk_cell_renderer_text_new ();
2523 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2524 _("Screen Saver"), ren,
2528 g_signal_connect_after (list, "row_activated",
2529 G_CALLBACK (list_activated_cb),
2532 selection = gtk_tree_view_get_selection (list);
2533 g_signal_connect (selection, "changed",
2534 G_CALLBACK (list_select_changed_cb),
2539 for (i = 0; i < s->list_count; i++)
2541 int hack_number = s->list_elt_to_hack_number[i];
2542 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2544 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2546 if (!hack) continue;
2548 /* If we're to suppress uninstalled hacks, check $PATH now. */
2549 if (p->ignore_uninstalled_p && !available_p)
2552 pretty_name = (hack->name
2553 ? strdup (hack->name)
2554 : make_hack_name (dpy, hack->command));
2558 /* Make the text foreground be the color of insensitive widgets
2559 (but don't actually make it be insensitive, since we still
2560 want to be able to click on it.)
2562 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
2563 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2564 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2565 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2567 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2568 /* " background=\"#%02X%02X%02X\"" */
2570 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2571 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2577 gtk_list_store_append (model, &iter);
2578 gtk_list_store_set (model, &iter,
2579 COL_ENABLED, hack->enabled_p,
2580 COL_NAME, pretty_name,
2585 #else /* !HAVE_GTK2 */
2587 saver_preferences *p = &s->prefs;
2588 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2590 for (i = 0; i < s->list_count; i++)
2592 int hack_number = s->list_elt_to_hack_number[i];
2593 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2595 /* A GtkList must contain only GtkListItems, but those can contain
2596 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2597 and a Label. We handle single and double click events on the
2598 line itself, for clicking on the text, but the interior checkbox
2599 also handles its own events.
2602 GtkWidget *line_hbox;
2603 GtkWidget *line_check;
2604 GtkWidget *line_label;
2606 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2608 if (!hack) continue;
2610 /* If we're to suppress uninstalled hacks, check $PATH now. */
2611 if (p->ignore_uninstalled_p && !available_p)
2614 pretty_name = (hack->name
2615 ? strdup (hack->name)
2616 : make_hack_name (hack->command));
2618 line = gtk_list_item_new ();
2619 line_hbox = gtk_hbox_new (FALSE, 0);
2620 line_check = gtk_check_button_new ();
2621 line_label = gtk_label_new (pretty_name);
2623 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2624 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2625 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2627 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2629 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2631 gtk_widget_show (line_check);
2632 gtk_widget_show (line_label);
2633 gtk_widget_show (line_hbox);
2634 gtk_widget_show (line);
2638 gtk_container_add (GTK_CONTAINER (list), line);
2639 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2640 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2643 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2644 GTK_SIGNAL_FUNC (list_checkbox_cb),
2647 gtk_widget_show (line);
2651 /* Make the widget be colored like insensitive widgets
2652 (but don't actually make it be insensitive, since we
2653 still want to be able to click on it.)
2655 GtkRcStyle *rc_style;
2658 gtk_widget_realize (GTK_WIDGET (line_label));
2660 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2661 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2663 rc_style = gtk_rc_style_new ();
2664 rc_style->fg[GTK_STATE_NORMAL] = fg;
2665 rc_style->bg[GTK_STATE_NORMAL] = bg;
2666 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2668 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2669 gtk_rc_style_unref (rc_style);
2673 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2674 GTK_SIGNAL_FUNC (list_select_cb),
2676 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2677 GTK_SIGNAL_FUNC (list_unselect_cb),
2679 #endif /* !HAVE_GTK2 */
2683 update_list_sensitivity (state *s)
2685 saver_preferences *p = &s->prefs;
2686 Bool sensitive = (p->mode == RANDOM_HACKS ||
2687 p->mode == RANDOM_HACKS_SAME ||
2688 p->mode == ONE_HACK);
2689 Bool checkable = (p->mode == RANDOM_HACKS ||
2690 p->mode == RANDOM_HACKS_SAME);
2691 Bool blankable = (p->mode != DONT_BLANK);
2694 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2695 GtkWidget *use = name_to_widget (s, "use_col_frame");
2696 #endif /* HAVE_GTK2 */
2697 GtkWidget *scroller = name_to_widget (s, "scroller");
2698 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2699 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2702 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2703 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2704 #else /* !HAVE_GTK2 */
2705 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2706 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2708 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2709 #endif /* !HAVE_GTK2 */
2710 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2711 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2713 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2716 gtk_tree_view_column_set_visible (use, checkable);
2717 #else /* !HAVE_GTK2 */
2719 gtk_widget_show (use); /* the "Use" column header */
2721 gtk_widget_hide (use);
2725 GtkBin *line = GTK_BIN (kids->data);
2726 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2727 GtkWidget *line_check =
2728 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2731 gtk_widget_show (line_check);
2733 gtk_widget_hide (line_check);
2737 #endif /* !HAVE_GTK2 */
2742 populate_prefs_page (state *s)
2744 saver_preferences *p = &s->prefs;
2746 Bool can_lock_p = True;
2748 /* Disable all the "lock" controls if locking support was not provided
2749 at compile-time, or if running on MacOS. */
2750 # if defined(NO_LOCKING) || defined(__APPLE__)
2755 /* If there is only one screen, the mode menu contains
2756 "random" but not "random-same".
2758 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2759 p->mode = RANDOM_HACKS;
2762 /* The file supports timeouts of less than a minute, but the GUI does
2763 not, so throttle the values to be at least one minute (since "0" is
2764 a bad rounding choice...)
2766 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2769 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2772 # define FMT_MINUTES(NAME,N) \
2773 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2775 # define FMT_SECONDS(NAME,N) \
2776 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2778 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2779 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2780 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2781 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2782 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2783 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2784 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2789 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2790 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2793 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2795 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2796 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2797 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2799 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2800 TOGGLE_ACTIVE ("dpms_quickoff_button", p->dpms_quickoff_p);
2801 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2802 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2803 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2804 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2805 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2806 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2810 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2811 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2812 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2813 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2814 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2817 # undef TOGGLE_ACTIVE
2819 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2820 (p->image_directory ? p->image_directory : ""));
2821 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2823 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2826 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2827 (p->text_literal ? p->text_literal : ""));
2828 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2829 (p->text_file ? p->text_file : ""));
2830 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2831 (p->text_program ? p->text_program : ""));
2832 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2833 (p->text_url ? p->text_url : ""));
2835 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2836 p->tmode == TEXT_LITERAL);
2837 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2838 p->tmode == TEXT_FILE);
2839 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2840 p->tmode == TEXT_FILE);
2841 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2842 p->tmode == TEXT_PROGRAM);
2843 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2844 p->tmode == TEXT_PROGRAM);
2845 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2846 p->tmode == TEXT_URL);
2849 /* Map the `saver_mode' enum to mode menu to values. */
2851 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2854 for (i = 0; i < countof(mode_menu_order); i++)
2855 if (mode_menu_order[i] == p->mode)
2857 gtk_option_menu_set_history (opt, i);
2858 update_list_sensitivity (s);
2862 Bool found_any_writable_cells = False;
2863 Bool fading_possible = False;
2864 Bool dpms_supported = False;
2866 Display *dpy = GDK_DISPLAY();
2867 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2869 for (i = 0; i < nscreens; i++)
2871 Screen *s = ScreenOfDisplay (dpy, i);
2872 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2874 found_any_writable_cells = True;
2879 fading_possible = found_any_writable_cells;
2880 #ifdef HAVE_XF86VMODE_GAMMA
2881 fading_possible = True;
2884 #ifdef HAVE_DPMS_EXTENSION
2886 int op = 0, event = 0, error = 0;
2887 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2888 dpms_supported = True;
2890 #endif /* HAVE_DPMS_EXTENSION */
2893 # define SENSITIZE(NAME,SENSITIVEP) \
2894 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2896 /* Blanking and Locking
2898 SENSITIZE ("lock_button", can_lock_p);
2899 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2900 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2905 SENSITIZE ("dpms_frame", dpms_supported);
2906 SENSITIZE ("dpms_button", dpms_supported);
2907 SENSITIZE ("dpms_quickoff_button", dpms_supported);
2909 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2910 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2911 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2912 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2913 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2914 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2915 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2916 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2917 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2921 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2922 SENSITIZE ("install_button", found_any_writable_cells);
2923 SENSITIZE ("fade_button", fading_possible);
2924 SENSITIZE ("unfade_button", fading_possible);
2926 SENSITIZE ("fade_label", (fading_possible &&
2927 (p->fade_p || p->unfade_p)));
2928 SENSITIZE ("fade_spinbutton", (fading_possible &&
2929 (p->fade_p || p->unfade_p)));
2937 populate_popup_window (state *s)
2939 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2940 char *doc_string = 0;
2942 /* #### not in Gtk 1.2
2943 gtk_label_set_selectable (doc);
2949 free_conf_data (s->cdata);
2954 saver_preferences *p = &s->prefs;
2955 int list_elt = selected_list_element (s);
2956 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2957 ? s->list_elt_to_hack_number[list_elt]
2959 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2962 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2963 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2964 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2965 s->cdata = load_configurator (cmd_line, s->debug_p);
2966 if (s->cdata && s->cdata->widget)
2967 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2972 doc_string = (s->cdata
2973 ? s->cdata->description
2975 # else /* !HAVE_XML */
2976 doc_string = _("Descriptions not available: no XML support compiled in.");
2977 # endif /* !HAVE_XML */
2979 gtk_label_set_text (doc, (doc_string
2981 : _("No description available.")));
2986 sensitize_demo_widgets (state *s, Bool sensitive_p)
2988 const char *names[] = { "demo", "settings",
2989 "cmd_label", "cmd_text", "manual",
2990 "visual", "visual_combo" };
2992 for (i = 0; i < countof(names); i++)
2994 GtkWidget *w = name_to_widget (s, names[i]);
2995 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
3001 sensitize_menu_items (state *s, Bool force_p)
3003 static Bool running_p = False;
3004 static time_t last_checked = 0;
3005 time_t now = time ((time_t *) 0);
3006 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
3010 if (force_p || now > last_checked + 10) /* check every 10 seconds */
3012 running_p = xscreensaver_running_p (s);
3013 last_checked = time ((time_t *) 0);
3016 for (i = 0; i < countof(names); i++)
3018 GtkWidget *w = name_to_widget (s, names[i]);
3019 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
3024 /* When the File menu is de-posted after a "Restart Daemon" command,
3025 the window underneath doesn't repaint for some reason. I guess this
3026 is a bug in exposure handling in GTK or GDK. This works around it.
3029 force_dialog_repaint (state *s)
3032 /* Tell GDK to invalidate and repaint the whole window.
3034 GdkWindow *w = GET_WINDOW (s->toplevel_widget);
3035 GdkRegion *region = gdk_region_new ();
3037 rect.x = rect.y = 0;
3038 rect.width = rect.height = 32767;
3039 gdk_region_union_with_rect (region, &rect);
3040 gdk_window_invalidate_region (w, region, True);
3041 gdk_region_destroy (region);
3042 gdk_window_process_updates (w, True);
3044 /* Force the server to send an exposure event by creating and then
3045 destroying a window as a child of the top level shell.
3047 Display *dpy = GDK_DISPLAY();
3048 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
3050 XWindowAttributes xgwa;
3051 XGetWindowAttributes (dpy, parent, &xgwa);
3052 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
3053 XMapRaised (dpy, w);
3054 XDestroyWindow (dpy, w);
3060 /* Even though we've given these text fields a maximum number of characters,
3061 their default size is still about 30 characters wide -- so measure out
3062 a string in their font, and resize them to just fit that.
3065 fix_text_entry_sizes (state *s)
3069 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
3070 const char * const spinbuttons[] = {
3071 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3072 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3073 "dpms_off_spinbutton",
3074 "-fade_spinbutton" };
3078 for (i = 0; i < countof(spinbuttons); i++)
3080 const char *n = spinbuttons[i];
3082 while (*n == '-') n++, cols--;
3083 w = GTK_WIDGET (name_to_widget (s, n));
3084 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3085 gtk_widget_set_usize (w, width, -2);
3088 /* Now fix the width of the combo box.
3090 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3091 w = GTK_COMBO (w)->entry;
3092 width = gdk_string_width (w->style->font, "PseudoColor___");
3093 gtk_widget_set_usize (w, width, -2);
3095 /* Now fix the width of the file entry text.
3097 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3098 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3099 gtk_widget_set_usize (w, width, -2);
3101 /* Now fix the width of the command line text.
3103 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3104 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3105 gtk_widget_set_usize (w, width, -2);
3109 /* Now fix the height of the list widget:
3110 make it default to being around 10 text-lines high instead of 4.
3112 w = GTK_WIDGET (name_to_widget (s, "list"));
3116 int leading = 3; /* approximate is ok... */
3120 PangoFontMetrics *pain =
3121 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3122 gtk_widget_get_style (w)->font_desc,
3123 gtk_get_default_language ());
3124 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3125 pango_font_metrics_get_descent (pain));
3126 #else /* !HAVE_GTK2 */
3127 height = w->style->font->ascent + w->style->font->descent;
3128 #endif /* !HAVE_GTK2 */
3132 height += border * 2;
3133 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3134 gtk_widget_set_usize (w, -2, height);
3141 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3144 static char *up_arrow_xpm[] = {
3167 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3168 the end of the array (Gtk 1.2.5.) */
3169 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3170 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3173 static char *down_arrow_xpm[] = {
3196 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3197 the end of the array (Gtk 1.2.5.) */
3198 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3199 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3203 pixmapify_button (state *s, int down_p)
3207 GtkWidget *pixmapwid;
3211 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3212 style = gtk_widget_get_style (w);
3214 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3215 &style->bg[GTK_STATE_NORMAL],
3217 ? (gchar **) down_arrow_xpm
3218 : (gchar **) up_arrow_xpm));
3219 pixmapwid = gtk_pixmap_new (pixmap, mask);
3220 gtk_widget_show (pixmapwid);
3221 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3222 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3226 map_next_button_cb (GtkWidget *w, gpointer user_data)
3228 state *s = (state *) user_data;
3229 pixmapify_button (s, 1);
3233 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3235 state *s = (state *) user_data;
3236 pixmapify_button (s, 0);
3238 #endif /* !HAVE_GTK2 */
3242 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3246 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3247 GtkAllocation *allocation,
3251 GtkWidgetAuxInfo *aux_info;
3253 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3255 aux_info->width = allocation->width;
3256 aux_info->height = -2;
3260 gtk_widget_size_request (label, &req);
3263 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3266 eschew_gtk_lossage (GtkLabel *label)
3268 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3269 aux_info->width = GTK_WIDGET (label)->allocation.width;
3270 aux_info->height = -2;
3274 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3276 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3277 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3280 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3282 gtk_widget_queue_resize (GTK_WIDGET (label));
3284 #endif /* !HAVE_GTK2 */
3288 populate_demo_window (state *s, int list_elt)
3290 Display *dpy = GDK_DISPLAY();
3291 saver_preferences *p = &s->prefs;
3294 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3295 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3296 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3297 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3298 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3300 if (p->mode == BLANK_ONLY)
3303 pretty_name = strdup (_("Blank Screen"));
3304 schedule_preview (s, 0);
3306 else if (p->mode == DONT_BLANK)
3309 pretty_name = strdup (_("Screen Saver Disabled"));
3310 schedule_preview (s, 0);
3314 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3315 ? s->list_elt_to_hack_number[list_elt]
3317 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3321 ? strdup (hack->name)
3322 : make_hack_name (dpy, hack->command))
3326 schedule_preview (s, hack->command);
3328 schedule_preview (s, 0);
3332 pretty_name = strdup (_("Preview"));
3334 gtk_frame_set_label (frame1, _(pretty_name));
3335 gtk_frame_set_label (frame2, _(pretty_name));
3337 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3338 gtk_entry_set_position (cmd, 0);
3342 sprintf (title, _("%s: %.100s Settings"),
3343 progclass, (pretty_name ? pretty_name : "???"));
3344 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3347 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3349 ? (hack->visual && *hack->visual
3354 sensitize_demo_widgets (s, (hack ? True : False));
3356 if (pretty_name) free (pretty_name);
3358 ensure_selected_item_visible (list);
3360 s->_selected_list_element = list_elt;
3365 widget_deleter (GtkWidget *widget, gpointer data)
3367 /* #### Well, I want to destroy these widgets, but if I do that, they get
3368 referenced again, and eventually I get a SEGV. So instead of
3369 destroying them, I'll just hide them, and leak a bunch of memory
3370 every time the disk file changes. Go go go Gtk!
3372 #### Ok, that's a lie, I get a crash even if I just hide the widget
3373 and don't ever delete it. Fuck!
3376 gtk_widget_destroy (widget);
3378 gtk_widget_hide (widget);
3383 static char **sort_hack_cmp_names_kludge;
3385 sort_hack_cmp (const void *a, const void *b)
3391 int aa = *(int *) a;
3392 int bb = *(int *) b;
3393 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3394 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3395 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3401 initialize_sort_map (state *s)
3403 Display *dpy = GDK_DISPLAY();
3404 saver_preferences *p = &s->prefs;
3407 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3408 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3409 if (s->hacks_available_p) free (s->hacks_available_p);
3411 s->list_elt_to_hack_number = (int *)
3412 calloc (sizeof(int), p->screenhacks_count + 1);
3413 s->hack_number_to_list_elt = (int *)
3414 calloc (sizeof(int), p->screenhacks_count + 1);
3415 s->hacks_available_p = (Bool *)
3416 calloc (sizeof(Bool), p->screenhacks_count + 1);
3417 s->total_available = 0;
3419 /* Check which hacks actually exist on $PATH
3421 for (i = 0; i < p->screenhacks_count; i++)
3423 screenhack *hack = p->screenhacks[i];
3424 int on = on_path_p (hack->command) ? 1 : 0;
3425 s->hacks_available_p[i] = on;
3426 s->total_available += on;
3429 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3433 for (i = 0; i < p->screenhacks_count; i++)
3435 if (!p->ignore_uninstalled_p ||
3436 s->hacks_available_p[i])
3437 s->list_elt_to_hack_number[j++] = i;
3441 for (; j < p->screenhacks_count; j++)
3442 s->list_elt_to_hack_number[j] = -1;
3445 /* Generate list of sortable names (once)
3447 sort_hack_cmp_names_kludge = (char **)
3448 calloc (sizeof(char *), p->screenhacks_count);
3449 for (i = 0; i < p->screenhacks_count; i++)
3451 screenhack *hack = p->screenhacks[i];
3452 char *name = (hack->name && *hack->name
3453 ? strdup (hack->name)
3454 : make_hack_name (dpy, hack->command));
3456 for (str = name; *str; str++)
3457 *str = tolower(*str);
3458 sort_hack_cmp_names_kludge[i] = name;
3461 /* Sort list->hack map alphabetically
3463 qsort (s->list_elt_to_hack_number,
3464 p->screenhacks_count,
3465 sizeof(*s->list_elt_to_hack_number),
3470 for (i = 0; i < p->screenhacks_count; i++)
3471 free (sort_hack_cmp_names_kludge[i]);
3472 free (sort_hack_cmp_names_kludge);
3473 sort_hack_cmp_names_kludge = 0;
3475 /* Build inverse table */
3476 for (i = 0; i < p->screenhacks_count; i++)
3478 int n = s->list_elt_to_hack_number[i];
3480 s->hack_number_to_list_elt[n] = i;
3486 maybe_reload_init_file (state *s)
3488 Display *dpy = GDK_DISPLAY();
3489 saver_preferences *p = &s->prefs;
3492 static Bool reentrant_lock = False;
3493 if (reentrant_lock) return 0;
3494 reentrant_lock = True;
3496 if (init_file_changed_p (p))
3498 const char *f = init_file_name();
3503 if (!f || !*f) return 0;
3504 b = (char *) malloc (strlen(f) + 1024);
3507 "file \"%s\" has changed, reloading.\n"),
3509 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3512 load_init_file (dpy, p);
3513 initialize_sort_map (s);
3515 list_elt = selected_list_element (s);
3516 list = name_to_widget (s, "list");
3517 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3518 populate_hack_list (s);
3519 force_list_select_item (s, list, list_elt, True);
3520 populate_prefs_page (s);
3521 populate_demo_window (s, list_elt);
3522 ensure_selected_item_visible (list);
3527 reentrant_lock = False;
3533 /* Making the preview window have the right X visual (so that GL works.)
3536 static Visual *get_best_gl_visual (state *);
3539 x_visual_to_gdk_visual (Visual *xv)
3541 GList *gvs = gdk_list_visuals();
3542 if (!xv) return gdk_visual_get_system();
3543 for (; gvs; gvs = gvs->next)
3545 GdkVisual *gv = (GdkVisual *) gvs->data;
3546 if (xv == GDK_VISUAL_XVISUAL (gv))
3549 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3550 blurb(), (unsigned long) xv->visualid);
3555 clear_preview_window (state *s)
3561 if (!s->toplevel_widget) return; /* very early */
3562 p = name_to_widget (s, "preview");
3563 window = GET_WINDOW (p);
3565 if (!window) return;
3567 /* Flush the widget background down into the window, in case a subproc
3569 style = gtk_widget_get_style (p);
3570 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
3571 gdk_window_clear (window);
3574 int list_elt = selected_list_element (s);
3575 int hack_number = (list_elt >= 0
3576 ? s->list_elt_to_hack_number[list_elt]
3578 Bool available_p = (hack_number >= 0
3579 ? s->hacks_available_p [hack_number]
3581 Bool nothing_p = (s->total_available < 5);
3584 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3585 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3586 (s->running_preview_error_p
3587 ? (available_p ? 1 :
3590 #else /* !HAVE_GTK2 */
3591 if (s->running_preview_error_p)
3593 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3594 const char * const lines2[] = { N_("Not"), N_("Installed") };
3595 int nlines = countof(lines1);
3596 int lh = p->style->font->ascent + p->style->font->descent;
3600 const char * const *lines = (available_p ? lines1 : lines2);
3602 gdk_window_get_size (window, &w, &h);
3603 y = (h - (lh * nlines)) / 2;
3604 y += p->style->font->ascent;
3605 for (i = 0; i < nlines; i++)
3607 int sw = gdk_string_width (p->style->font, _(lines[i]));
3608 int x = (w - sw) / 2;
3609 gdk_draw_string (window, p->style->font,
3610 p->style->fg_gc[GTK_STATE_NORMAL],
3615 #endif /* !HAVE_GTK2 */
3623 reset_preview_window (state *s)
3625 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3626 when you kill one and re-start another on the same window. So maybe
3627 it's best to just always destroy and recreate the preview window
3628 when changing hacks, instead of always trying to reuse the same one?
3630 GtkWidget *pr = name_to_widget (s, "preview");
3631 if (GET_REALIZED (pr))
3633 GdkWindow *window = GET_WINDOW (pr);
3634 Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3636 gtk_widget_hide (pr);
3637 gtk_widget_unrealize (pr);
3638 gtk_widget_realize (pr);
3639 gtk_widget_show (pr);
3640 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3642 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3650 fix_preview_visual (state *s)
3652 GtkWidget *widget = name_to_widget (s, "preview");
3653 Visual *xvisual = get_best_gl_visual (s);
3654 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3655 GdkVisual *dvisual = gdk_visual_get_system();
3656 GdkColormap *cmap = (visual == dvisual
3657 ? gdk_colormap_get_system ()
3658 : gdk_colormap_new (visual, False));
3661 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3662 (visual == dvisual ? "default" : "non-default"),
3663 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3665 if (!GET_REALIZED (widget) ||
3666 gtk_widget_get_visual (widget) != visual)
3668 gtk_widget_unrealize (widget);
3669 gtk_widget_set_visual (widget, visual);
3670 gtk_widget_set_colormap (widget, cmap);
3671 gtk_widget_realize (widget);
3674 /* Set the Widget colors to be white-on-black. */
3676 GdkWindow *window = GET_WINDOW (widget);
3677 GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
3678 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3679 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3680 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3681 GdkGC *fgc = gdk_gc_new(window);
3682 GdkGC *bgc = gdk_gc_new(window);
3683 if (!gdk_color_white (cmap, fg)) abort();
3684 if (!gdk_color_black (cmap, bg)) abort();
3685 gdk_gc_set_foreground (fgc, fg);
3686 gdk_gc_set_background (fgc, bg);
3687 gdk_gc_set_foreground (bgc, bg);
3688 gdk_gc_set_background (bgc, fg);
3689 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3690 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3691 gtk_widget_set_style (widget, style);
3693 /* For debugging purposes, put a title on the window (so that
3694 it can be easily found in the output of "xwininfo -tree".)
3696 gdk_window_set_title (window, "Preview");
3699 gtk_widget_show (widget);
3707 subproc_pretty_name (state *s)
3709 if (s->running_preview_cmd)
3711 char *ps = strdup (s->running_preview_cmd);
3712 char *ss = strchr (ps, ' ');
3714 ss = strrchr (ps, '/');
3725 return strdup ("???");
3730 reap_zombies (state *s)
3732 int wait_status = 0;
3734 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3738 if (pid == s->running_preview_pid)
3740 char *ss = subproc_pretty_name (s);
3741 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3742 (unsigned long) pid, ss);
3746 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3747 (unsigned long) pid);
3753 /* Mostly lifted from driver/subprocs.c */
3755 get_best_gl_visual (state *s)
3757 Display *dpy = GDK_DISPLAY();
3766 av[ac++] = "xscreensaver-gl-helper";
3771 perror ("error creating pipe:");
3778 switch ((int) (forked = fork ()))
3782 sprintf (buf, "%s: couldn't fork", blurb());
3790 close (in); /* don't need this one */
3791 close (ConnectionNumber (dpy)); /* close display fd */
3793 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3795 perror ("could not dup() a new stdout:");
3799 execvp (av[0], av); /* shouldn't return. */
3801 if (errno != ENOENT)
3803 /* Ignore "no such file or directory" errors, unless verbose.
3804 Issue all other exec errors, though. */
3805 sprintf (buf, "%s: running %s", blurb(), av[0]);
3809 /* Note that one must use _exit() instead of exit() in procs forked
3810 off of Gtk programs -- Gtk installs an atexit handler that has a
3811 copy of the X connection (which we've already closed, for safety.)
3812 If one uses exit() instead of _exit(), then one sometimes gets a
3813 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3815 _exit (1); /* exits fork */
3821 int wait_status = 0;
3823 FILE *f = fdopen (in, "r");
3827 close (out); /* don't need this one */
3830 if (!fgets (buf, sizeof(buf)-1, f))
3834 /* Wait for the child to die. */
3835 waitpid (-1, &wait_status, 0);
3837 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3843 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3849 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3851 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3852 blurb(), av[0], result);
3864 kill_preview_subproc (state *s, Bool reset_p)
3866 s->running_preview_error_p = False;
3869 clear_preview_window (s);
3871 if (s->subproc_check_timer_id)
3873 gtk_timeout_remove (s->subproc_check_timer_id);
3874 s->subproc_check_timer_id = 0;
3875 s->subproc_check_countdown = 0;
3878 if (s->running_preview_pid)
3880 int status = kill (s->running_preview_pid, SIGTERM);
3881 char *ss = subproc_pretty_name (s);
3888 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3889 blurb(), (unsigned long) s->running_preview_pid, ss);
3894 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3895 blurb(), (unsigned long) s->running_preview_pid, ss);
3901 waitpid(s->running_preview_pid, &endstatus, 0);
3903 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3904 (unsigned long) s->running_preview_pid, ss);
3908 s->running_preview_pid = 0;
3909 if (s->running_preview_cmd) free (s->running_preview_cmd);
3910 s->running_preview_cmd = 0;
3917 reset_preview_window (s);
3918 clear_preview_window (s);
3923 /* Immediately and unconditionally launches the given process,
3924 after appending the -window-id option; sets running_preview_pid.
3927 launch_preview_subproc (state *s)
3929 saver_preferences *p = &s->prefs;
3933 const char *cmd = s->desired_preview_cmd;
3935 GtkWidget *pr = name_to_widget (s, "preview");
3938 reset_preview_window (s);
3940 window = GET_WINDOW (pr);
3942 s->running_preview_error_p = False;
3944 if (s->preview_suppressed_p)
3946 kill_preview_subproc (s, False);
3950 new_cmd = malloc (strlen (cmd) + 40);
3952 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3955 /* No window id? No command to run. */
3961 strcpy (new_cmd, cmd);
3962 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3966 kill_preview_subproc (s, False);
3969 s->running_preview_error_p = True;
3970 clear_preview_window (s);
3974 switch ((int) (forked = fork ()))
3979 sprintf (buf, "%s: couldn't fork", blurb());
3981 s->running_preview_error_p = True;
3987 close (ConnectionNumber (GDK_DISPLAY()));
3989 hack_subproc_environment (id, s->debug_p);
3991 usleep (250000); /* pause for 1/4th second before launching, to give
3992 the previous program time to die and flush its X
3993 buffer, so we don't get leftover turds on the
3996 exec_command (p->shell, new_cmd, p->nice_inferior);
3997 /* Don't bother printing an error message when we are unable to
3998 exec subprocesses; we handle that by polling the pid later.
4000 Note that one must use _exit() instead of exit() in procs forked
4001 off of Gtk programs -- Gtk installs an atexit handler that has a
4002 copy of the X connection (which we've already closed, for safety.)
4003 If one uses exit() instead of _exit(), then one sometimes gets a
4004 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
4006 _exit (1); /* exits child fork */
4011 if (s->running_preview_cmd) free (s->running_preview_cmd);
4012 s->running_preview_cmd = strdup (s->desired_preview_cmd);
4013 s->running_preview_pid = forked;
4017 char *ss = subproc_pretty_name (s);
4018 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
4019 (unsigned long) forked, ss);
4026 schedule_preview_check (s);
4029 if (new_cmd) free (new_cmd);
4034 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
4037 hack_environment (state *s)
4039 static const char *def_path =
4040 # ifdef DEFAULT_PATH_PREFIX
4041 DEFAULT_PATH_PREFIX;
4046 Display *dpy = GDK_DISPLAY();
4047 const char *odpy = DisplayString (dpy);
4048 char *ndpy = (char *) malloc(strlen(odpy) + 20);
4049 strcpy (ndpy, "DISPLAY=");
4050 strcat (ndpy, odpy);
4055 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4057 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4058 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4059 So we must leak it (and/or the previous setting). Yay.
4062 if (def_path && *def_path)
4064 const char *opath = getenv("PATH");
4065 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4066 strcpy (npath, "PATH=");
4067 strcat (npath, def_path);
4068 strcat (npath, ":");
4069 strcat (npath, opath);
4073 /* do not free(npath) -- see above */
4076 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4082 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4084 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4085 necessary yet, but it will make programs work if we had invoked
4086 them with "-root" and not with "-window-id" -- which, of course,
4089 char *nssw = (char *) malloc (40);
4090 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4092 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4093 any more, right? It's not Posix, but everyone seems to have it. */
4098 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4100 /* do not free(nssw) -- see above */
4104 /* Called from a timer:
4105 Launches the currently-chosen subprocess, if it's not already running.
4106 If there's a different process running, kills it.
4109 update_subproc_timer (gpointer data)
4111 state *s = (state *) data;
4112 if (! s->desired_preview_cmd)
4113 kill_preview_subproc (s, True);
4114 else if (!s->running_preview_cmd ||
4115 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4116 launch_preview_subproc (s);
4118 s->subproc_timer_id = 0;
4119 return FALSE; /* do not re-execute timer */
4123 settings_timer (gpointer data)
4130 /* Call this when you think you might want a preview process running.
4131 It will set a timer that will actually launch that program a second
4132 from now, if you haven't changed your mind (to avoid double-click
4133 spazzing, etc.) `cmd' may be null meaning "no process".
4136 schedule_preview (state *s, const char *cmd)
4138 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4143 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4145 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4148 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4149 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4151 if (s->subproc_timer_id)
4152 gtk_timeout_remove (s->subproc_timer_id);
4153 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4157 /* Called from a timer:
4158 Checks to see if the subproc that should be running, actually is.
4161 check_subproc_timer (gpointer data)
4163 state *s = (state *) data;
4164 Bool again_p = True;
4166 if (s->running_preview_error_p || /* already dead */
4167 s->running_preview_pid <= 0)
4175 status = kill (s->running_preview_pid, 0);
4176 if (status < 0 && errno == ESRCH)
4177 s->running_preview_error_p = True;
4181 char *ss = subproc_pretty_name (s);
4182 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4183 (unsigned long) s->running_preview_pid, ss,
4184 (s->running_preview_error_p ? "dead" : "alive"));
4188 if (s->running_preview_error_p)
4190 clear_preview_window (s);
4195 /* Otherwise, it's currently alive. We might be checking again, or we
4196 might be satisfied. */
4198 if (--s->subproc_check_countdown <= 0)
4202 return TRUE; /* re-execute timer */
4205 s->subproc_check_timer_id = 0;
4206 s->subproc_check_countdown = 0;
4207 return FALSE; /* do not re-execute timer */
4212 /* Call this just after launching a subprocess.
4213 This sets a timer that will, five times a second for two seconds,
4214 check whether the program is still running. The assumption here
4215 is that if the process didn't stay up for more than a couple of
4216 seconds, then either the program doesn't exist, or it doesn't
4217 take a -window-id argument.
4220 schedule_preview_check (state *s)
4226 fprintf (stderr, "%s: scheduling check\n", blurb());
4228 if (s->subproc_check_timer_id)
4229 gtk_timeout_remove (s->subproc_check_timer_id);
4230 s->subproc_check_timer_id =
4231 gtk_timeout_add (1000 / ticks,
4232 check_subproc_timer, (gpointer) s);
4233 s->subproc_check_countdown = ticks * seconds;
4238 screen_blanked_p (void)
4242 unsigned long nitems, bytesafter;
4243 unsigned char *dataP = 0;
4244 Display *dpy = GDK_DISPLAY();
4245 Bool blanked_p = False;
4247 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4248 XA_SCREENSAVER_STATUS,
4249 0, 3, False, XA_INTEGER,
4250 &type, &format, &nitems, &bytesafter,
4253 && type == XA_INTEGER
4257 Atom *data = (Atom *) dataP;
4258 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4261 if (dataP) XFree (dataP);
4266 /* Wake up every now and then and see if the screen is blanked.
4267 If it is, kill off the small-window demo -- no point in wasting
4268 cycles by running two screensavers at once...
4271 check_blanked_timer (gpointer data)
4273 state *s = (state *) data;
4274 Bool blanked_p = screen_blanked_p ();
4275 if (blanked_p && s->running_preview_pid)
4278 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4279 kill_preview_subproc (s, True);
4282 return True; /* re-execute timer */
4286 /* How many screens are there (including Xinerama.)
4289 screen_count (Display *dpy)
4291 int nscreens = ScreenCount(dpy);
4292 # ifdef HAVE_XINERAMA
4295 int event_number, error_number;
4296 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4297 XineramaIsActive (dpy))
4299 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4300 if (xsi) XFree (xsi);
4303 # endif /* HAVE_XINERAMA */
4309 /* Setting window manager icon
4313 init_icon (GdkWindow *window)
4315 GdkBitmap *mask = 0;
4318 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4319 (gchar **) logo_50_xpm);
4321 gdk_window_set_icon (window, 0, pixmap, mask);
4325 /* The main demo-mode command loop.
4330 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4331 XrmRepresentation *type, XrmValue *value, XPointer closure)
4334 for (i = 0; quarks[i]; i++)
4336 if (bindings[i] == XrmBindTightly)
4337 fprintf (stderr, (i == 0 ? "" : "."));
4338 else if (bindings[i] == XrmBindLoosely)
4339 fprintf (stderr, "*");
4341 fprintf (stderr, " ??? ");
4342 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4345 fprintf (stderr, ": %s\n", (char *) value->addr);
4353 gnome_screensaver_window (Screen *screen)
4355 Display *dpy = DisplayOfScreen (screen);
4356 Window root = RootWindowOfScreen (screen);
4357 Window parent, *kids;
4359 Window gnome_window = 0;
4362 if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4364 for (i = 0; i < nkids; i++)
4368 unsigned long nitems, bytesafter;
4369 unsigned char *name;
4370 if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4371 False, XA_STRING, &type, &format, &nitems,
4375 && !strcmp ((char *) name, "gnome-screensaver"))
4377 gnome_window = kids[i];
4382 if (kids) XFree ((char *) kids);
4383 return gnome_window;
4387 gnome_screensaver_active_p (void)
4389 Display *dpy = GDK_DISPLAY();
4390 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4391 return (w ? True : False);
4395 kill_gnome_screensaver (void)
4397 Display *dpy = GDK_DISPLAY();
4398 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4399 if (w) XKillClient (dpy, (XID) w);
4403 kde_screensaver_active_p (void)
4405 FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4408 fgets (buf, sizeof(buf)-1, p);
4410 if (!strcmp (buf, "true\n"))
4417 kill_kde_screensaver (void)
4419 system ("dcop kdesktop KScreensaverIface enable false");
4424 the_network_is_not_the_computer (state *s)
4426 Display *dpy = GDK_DISPLAY();
4427 char *rversion = 0, *ruser = 0, *rhost = 0;
4428 char *luser, *lhost;
4430 struct passwd *p = getpwuid (getuid ());
4431 const char *d = DisplayString (dpy);
4433 # if defined(HAVE_UNAME)
4435 if (uname (&uts) < 0)
4436 lhost = "<UNKNOWN>";
4438 lhost = uts.nodename;
4440 strcpy (lhost, getenv("SYS$NODE"));
4441 # else /* !HAVE_UNAME && !VMS */
4442 strcat (lhost, "<UNKNOWN>");
4443 # endif /* !HAVE_UNAME && !VMS */
4445 if (p && p->pw_name)
4450 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4452 /* Make a buffer that's big enough for a number of copies of all the
4453 strings, plus some. */
4454 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4455 (ruser ? strlen(ruser) : 0) +
4456 (rhost ? strlen(rhost) : 0) +
4463 if (!rversion || !*rversion)
4467 "The XScreenSaver daemon doesn't seem to be running\n"
4468 "on display \"%s\". Launch it now?"),
4471 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4473 /* Warn that the two processes are running as different users.
4477 "%s is running as user \"%s\" on host \"%s\".\n"
4478 "But the xscreensaver managing display \"%s\"\n"
4479 "is running as user \"%s\" on host \"%s\".\n"
4481 "Since they are different users, they won't be reading/writing\n"
4482 "the same ~/.xscreensaver file, so %s isn't\n"
4483 "going to work right.\n"
4485 "You should either re-run %s as \"%s\", or re-run\n"
4486 "xscreensaver as \"%s\".\n"
4488 "Restart the xscreensaver daemon now?\n"),
4489 progname, luser, lhost,
4491 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4493 progname, (ruser ? ruser : "???"),
4496 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4498 /* Warn that the two processes are running on different hosts.
4502 "%s is running as user \"%s\" on host \"%s\".\n"
4503 "But the xscreensaver managing display \"%s\"\n"
4504 "is running as user \"%s\" on host \"%s\".\n"
4506 "If those two machines don't share a file system (that is,\n"
4507 "if they don't see the same ~%s/.xscreensaver file) then\n"
4508 "%s won't work right.\n"
4510 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4511 progname, luser, lhost,
4513 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4518 else if (!!strcmp (rversion, s->short_version))
4520 /* Warn that the version numbers don't match.
4524 "This is %s version %s.\n"
4525 "But the xscreensaver managing display \"%s\"\n"
4526 "is version %s. This could cause problems.\n"
4528 "Restart the xscreensaver daemon now?\n"),
4529 progname, s->short_version,
4536 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4538 if (rversion) free (rversion);
4539 if (ruser) free (ruser);
4540 if (rhost) free (rhost);
4544 /* Note: since these dialogs are not modal, they will stack up.
4545 So we do this check *after* popping up the "xscreensaver is not
4546 running" dialog so that these are on top. Good enough.
4549 if (gnome_screensaver_active_p ())
4550 warning_dialog (s->toplevel_widget,
4552 "The GNOME screensaver daemon appears to be running.\n"
4553 "It must be stopped for XScreenSaver to work properly.\n"
4555 "Stop the GNOME screen saver daemon now?\n"),
4558 if (kde_screensaver_active_p ())
4559 warning_dialog (s->toplevel_widget,
4561 "The KDE screen saver daemon appears to be running.\n"
4562 "It must be stopped for XScreenSaver to work properly.\n"
4564 "Stop the KDE screen saver daemon now?\n"),
4569 /* We use this error handler so that X errors are preceeded by the name
4570 of the program that generated them.
4573 demo_ehandler (Display *dpy, XErrorEvent *error)
4575 state *s = global_state_kludge; /* I hate C so much... */
4576 fprintf (stderr, "\nX error in %s:\n", blurb());
4577 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4578 kill_preview_subproc (s, False);
4584 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4585 of the program that generated them; and also that we can ignore one
4586 particular bogus error message that Gdk madly spews.
4589 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4590 const gchar *message, gpointer user_data)
4592 /* Ignore the message "Got event for unknown window: 0x...".
4593 Apparently some events are coming in for the xscreensaver window
4594 (presumably reply events related to the ClientMessage) and Gdk
4595 feels the need to complain about them. So, just suppress any
4596 messages that look like that one.
4598 if (strstr (message, "unknown window"))
4601 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4602 (log_domain ? log_domain : progclass),
4603 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4604 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4605 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4606 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4607 log_level == G_LOG_LEVEL_INFO ? "info" :
4608 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4610 ((!*message || message[strlen(message)-1] != '\n')
4616 __extension__ /* shut up about "string length is greater than the length
4617 ISO C89 compilers are required to support" when including
4622 static char *defaults[] = {
4623 #include "XScreenSaver_ad.h"
4628 #ifdef HAVE_CRAPPLET
4629 static struct poptOption crapplet_options[] = {
4630 {NULL, '\0', 0, NULL, 0}
4632 #endif /* HAVE_CRAPPLET */
4635 const char *usage = "[--display dpy] [--prefs | --settings]"
4636 # ifdef HAVE_CRAPPLET
4639 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4642 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4644 state *s = (state *) user_data;
4645 Boolean oi = s->initializing_p;
4647 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4649 s->initializing_p = True;
4651 eschew_gtk_lossage (label);
4653 s->initializing_p = oi;
4659 print_widget_tree (GtkWidget *w, int depth)
4662 for (i = 0; i < depth; i++)
4663 fprintf (stderr, " ");
4664 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4666 if (GTK_IS_LIST (w))
4668 for (i = 0; i < depth+1; i++)
4669 fprintf (stderr, " ");
4670 fprintf (stderr, "...list kids...\n");
4672 else if (GTK_IS_CONTAINER (w))
4674 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4677 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4685 delayed_scroll_kludge (gpointer data)
4687 state *s = (state *) data;
4688 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4689 ensure_selected_item_visible (w);
4691 /* Oh, this is just fucking lovely, too. */
4692 w = GTK_WIDGET (name_to_widget (s, "preview"));
4693 gtk_widget_hide (w);
4694 gtk_widget_show (w);
4696 return FALSE; /* do not re-execute timer */
4702 create_xscreensaver_demo (void)
4706 nb = name_to_widget (global_state_kludge, "preview_notebook");
4707 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4709 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4713 create_xscreensaver_settings_dialog (void)
4717 box = name_to_widget (global_state_kludge, "dialog_action_area");
4719 w = name_to_widget (global_state_kludge, "adv_button");
4720 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4722 w = name_to_widget (global_state_kludge, "std_button");
4723 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4725 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4728 #endif /* HAVE_GTK2 */
4731 main (int argc, char **argv)
4735 saver_preferences *p;
4736 Bool prefs_p = False;
4737 Bool settings_p = False;
4740 Widget toplevel_shell;
4741 char *real_progname = argv[0];
4744 Bool crapplet_p = False;
4748 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4749 textdomain (GETTEXT_PACKAGE);
4752 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4753 # else /* !HAVE_GTK2 */
4754 if (!setlocale (LC_ALL, ""))
4755 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4756 # endif /* !HAVE_GTK2 */
4758 #endif /* ENABLE_NLS */
4760 str = strrchr (real_progname, '/');
4761 if (str) real_progname = str+1;
4764 memset (s, 0, sizeof(*s));
4765 s->initializing_p = True;
4768 global_state_kludge = s; /* I hate C so much... */
4770 progname = real_progname;
4772 s->short_version = (char *) malloc (5);
4773 memcpy (s->short_version, screensaver_id + 17, 4);
4774 s->short_version [4] = 0;
4777 /* Register our error message logger for every ``log domain'' known.
4778 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4779 for all of the domains that seem to be in use.
4782 const char * const domains[] = { 0,
4783 "Gtk", "Gdk", "GLib", "GModule",
4784 "GThread", "Gnome", "GnomeUI" };
4785 for (i = 0; i < countof(domains); i++)
4786 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4789 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4792 const char *dir = DEFAULT_ICONDIR;
4793 if (*dir) add_pixmap_directory (dir);
4795 # endif /* !HAVE_GTK2 */
4796 #endif /* DEFAULT_ICONDIR */
4798 /* This is gross, but Gtk understands --display and not -display...
4800 for (i = 1; i < argc; i++)
4801 if (argv[i][0] && argv[i][1] &&
4802 !strncmp(argv[i], "-display", strlen(argv[i])))
4803 argv[i] = "--display";
4806 /* We need to parse this arg really early... Sigh. */
4807 for (i = 1; i < argc; i++)
4810 (!strcmp(argv[i], "--crapplet") ||
4811 !strcmp(argv[i], "--capplet")))
4813 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4816 for (j = i; j < argc; j++) /* remove it from the list */
4817 argv[j] = argv[j+1];
4819 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4820 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4822 fprintf (stderr, "%s: %s\n", real_progname, usage);
4824 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4827 (!strcmp(argv[i], "--debug") ||
4828 !strcmp(argv[i], "-debug") ||
4829 !strcmp(argv[i], "-d")))
4833 for (j = i; j < argc; j++) /* remove it from the list */
4834 argv[j] = argv[j+1];
4841 (!strcmp(argv[i], "-geometry") ||
4842 !strcmp(argv[i], "-geom") ||
4843 !strcmp(argv[i], "-geo") ||
4844 !strcmp(argv[i], "-g")))
4848 for (j = i; j < argc; j++) /* remove them from the list */
4849 argv[j] = argv[j+2];
4856 (!strcmp(argv[i], "--configdir")))
4860 hack_configuration_path = argv[i+1];
4861 for (j = i; j < argc; j++) /* remove them from the list */
4862 argv[j] = argv[j+2];
4866 if (0 != stat (hack_configuration_path, &st))
4869 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4873 else if (!S_ISDIR (st.st_mode))
4875 fprintf (stderr, "%s: not a directory: %s\n",
4876 blurb(), hack_configuration_path);
4884 fprintf (stderr, "%s: using config directory \"%s\"\n",
4885 progname, hack_configuration_path);
4888 /* Let Gtk open the X connection, then initialize Xt to use that
4889 same connection. Doctor Frankenstein would be proud.
4891 # ifdef HAVE_CRAPPLET
4894 GnomeClient *client;
4895 GnomeClientFlags flags = 0;
4897 int init_results = gnome_capplet_init ("screensaver-properties",
4899 argc, argv, NULL, 0, NULL);
4901 0 upon successful initialization;
4902 1 if --init-session-settings was passed on the cmdline;
4903 2 if --ignore was passed on the cmdline;
4906 So the 1 signifies just to init the settings, and quit, basically.
4907 (Meaning launch the xscreensaver daemon.)
4910 if (init_results < 0)
4913 g_error ("An initialization error occurred while "
4914 "starting xscreensaver-capplet.\n");
4916 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4917 real_progname, init_results);
4922 client = gnome_master_client ();
4925 flags = gnome_client_get_flags (client);
4927 if (flags & GNOME_CLIENT_IS_CONNECTED)
4930 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4931 gnome_client_get_id (client));
4934 char *session_args[20];
4936 session_args[i++] = real_progname;
4937 session_args[i++] = "--capplet";
4938 session_args[i++] = "--init-session-settings";
4939 session_args[i] = 0;
4940 gnome_client_set_priority (client, 20);
4941 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4942 gnome_client_set_restart_command (client, i, session_args);
4946 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4949 gnome_client_flush (client);
4952 if (init_results == 1)
4954 system ("xscreensaver -nosplash &");
4960 # endif /* HAVE_CRAPPLET */
4962 gtk_init (&argc, &argv);
4966 /* We must read exactly the same resources as xscreensaver.
4967 That means we must have both the same progclass *and* progname,
4968 at least as far as the resource database is concerned. So,
4969 put "xscreensaver" in argv[0] while initializing Xt.
4971 argv[0] = "xscreensaver";
4975 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4977 XtToolkitInitialize ();
4978 app = XtCreateApplicationContext ();
4979 dpy = GDK_DISPLAY();
4980 XtAppSetFallbackResources (app, defaults);
4981 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4982 toplevel_shell = XtAppCreateShell (progname, progclass,
4983 applicationShellWidgetClass,
4986 dpy = XtDisplay (toplevel_shell);
4987 db = XtDatabase (dpy);
4988 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4989 XSetErrorHandler (demo_ehandler);
4991 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4992 signal (SIGPIPE, SIG_IGN);
4994 /* After doing Xt-style command-line processing, complain about any
4995 unrecognized command-line arguments.
4997 for (i = 1; i < argc; i++)
4999 char *str = argv[i];
5000 if (str[0] == '-' && str[1] == '-')
5002 if (!strcmp (str, "-prefs"))
5004 else if (!strcmp (str, "-settings"))
5006 else if (crapplet_p)
5007 /* There are lots of random args that we don't care about when we're
5008 started as a crapplet, so just ignore unknown args in that case. */
5012 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
5014 fprintf (stderr, "%s: %s\n", real_progname, usage);
5019 /* Load the init file, which may end up consulting the X resource database
5020 and the site-wide app-defaults file. Note that at this point, it's
5021 important that `progname' be "xscreensaver", rather than whatever
5025 s->nscreens = screen_count (dpy);
5027 hack_environment (s); /* must be before initialize_sort_map() */
5029 load_init_file (dpy, p);
5030 initialize_sort_map (s);
5032 /* Now that Xt has been initialized, and the resources have been read,
5033 we can set our `progname' variable to something more in line with
5036 progname = real_progname;
5040 /* Print out all the resources we read. */
5042 XrmName name = { 0 };
5043 XrmClass class = { 0 };
5045 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
5051 /* Intern the atoms that xscreensaver_command() needs.
5053 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
5054 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
5055 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
5056 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5057 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5058 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5059 XA_SELECT = XInternAtom (dpy, "SELECT", False);
5060 XA_DEMO = XInternAtom (dpy, "DEMO", False);
5061 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5062 XA_BLANK = XInternAtom (dpy, "BLANK", False);
5063 XA_LOCK = XInternAtom (dpy, "LOCK", False);
5064 XA_EXIT = XInternAtom (dpy, "EXIT", False);
5065 XA_RESTART = XInternAtom (dpy, "RESTART", False);
5068 /* Create the window and all its widgets.
5070 s->base_widget = create_xscreensaver_demo ();
5071 s->popup_widget = create_xscreensaver_settings_dialog ();
5072 s->toplevel_widget = s->base_widget;
5075 /* Set the main window's title. */
5077 char *base_title = _("Screensaver Preferences");
5078 char *v = (char *) strdup(strchr(screensaver_id, ' '));
5079 char *s1, *s2, *s3, *s4;
5080 s1 = (char *) strchr(v, ' '); s1++;
5081 s2 = (char *) strchr(s1, ' ');
5082 s3 = (char *) strchr(v, '('); s3++;
5083 s4 = (char *) strchr(s3, ')');
5087 window_title = (char *) malloc (strlen (base_title) +
5088 strlen (progclass) +
5089 strlen (s1) + strlen (s3) +
5091 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
5092 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5093 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
5097 /* Adjust the (invisible) notebooks on the popup dialog... */
5099 GtkNotebook *notebook =
5100 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5101 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5105 gtk_widget_hide (std);
5106 # else /* !HAVE_XML */
5107 /* Make the advanced page be the only one available. */
5108 gtk_widget_set_sensitive (std, False);
5109 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5110 gtk_widget_hide (std);
5111 std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5112 gtk_widget_hide (std);
5114 # endif /* !HAVE_XML */
5116 gtk_notebook_set_page (notebook, page);
5117 gtk_notebook_set_show_tabs (notebook, False);
5120 /* Various other widget initializations...
5122 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5123 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5125 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5126 GTK_SIGNAL_FUNC (wm_popup_close_cb),
5129 populate_hack_list (s);
5130 populate_prefs_page (s);
5131 sensitize_demo_widgets (s, False);
5132 fix_text_entry_sizes (s);
5133 scroll_to_current_hack (s);
5135 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5136 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5140 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5141 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5143 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5144 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5146 #endif /* !HAVE_GTK2 */
5148 /* Hook up callbacks to the items on the mode menu. */
5150 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5151 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5152 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5154 for (i = 0; kids; kids = kids->next, i++)
5156 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5157 GTK_SIGNAL_FUNC (mode_menu_item_cb),
5160 /* The "random-same" mode menu item does not appear unless
5161 there are multple screens.
5163 if (s->nscreens <= 1 &&
5164 mode_menu_order[i] == RANDOM_HACKS_SAME)
5165 gtk_widget_hide (GTK_WIDGET (kids->data));
5168 if (s->nscreens <= 1) /* recompute option-menu size */
5170 gtk_widget_unrealize (GTK_WIDGET (menu));
5171 gtk_widget_realize (GTK_WIDGET (menu));
5176 /* Handle the -prefs command-line argument. */
5179 GtkNotebook *notebook =
5180 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5181 gtk_notebook_set_page (notebook, 1);
5184 # ifdef HAVE_CRAPPLET
5188 GtkWidget *outer_vbox;
5190 gtk_widget_hide (s->toplevel_widget);
5192 capplet = capplet_widget_new ();
5194 /* Make there be a "Close" button instead of "OK" and "Cancel" */
5195 # ifdef HAVE_CRAPPLET_IMMEDIATE
5196 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5197 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5198 /* In crapplet-mode, take off the menubar. */
5199 gtk_widget_hide (name_to_widget (s, "menubar"));
5201 /* Reparent our top-level container to be a child of the capplet
5204 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5205 gtk_widget_ref (outer_vbox);
5206 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5208 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5209 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5211 /* Find the window above us, and set the title and close handler. */
5213 GtkWidget *window = capplet;
5214 while (window && !GTK_IS_WINDOW (window))
5215 window = GET_PARENT (window);
5218 gtk_window_set_title (GTK_WINDOW (window), window_title);
5219 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5220 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5225 s->toplevel_widget = capplet;
5227 # endif /* HAVE_CRAPPLET */
5230 /* The Gnome folks hate the menubar. I think it's important to have access
5231 to the commands on the File menu (Restart Daemon, etc.) and to the
5232 About and Documentation commands on the Help menu.
5236 gtk_widget_hide (name_to_widget (s, "menubar"));
5240 free (window_title);
5244 /* After picking the default size, allow -geometry to override it. */
5246 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5249 gtk_widget_show (s->toplevel_widget);
5250 init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */
5251 fix_preview_visual (s);
5253 /* Realize page zero, so that we can diddle the scrollbar when the
5254 user tabs back to it -- otherwise, the current hack isn't scrolled
5255 to the first time they tab back there, when started with "-prefs".
5256 (Though it is if they then tab away, and back again.)
5258 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5259 #### understands this crap, explain to me how to make this work.
5261 gtk_widget_realize (name_to_widget (s, "demos_table"));
5264 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5267 /* Handle the --settings command-line argument. */
5269 gtk_timeout_add (500, settings_timer, 0);
5272 /* Issue any warnings about the running xscreensaver daemon. */
5274 the_network_is_not_the_computer (s);
5277 /* Run the Gtk event loop, and not the Xt event loop. This means that
5278 if there were Xt timers or fds registered, they would never get serviced,
5279 and if there were any Xt widgets, they would never have events delivered.
5280 Fortunately, we're using Gtk for all of the UI, and only initialized
5281 Xt so that we could process the command line and use the X resource
5284 s->initializing_p = False;
5286 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5287 after we start up. Otherwise, it always appears scrolled to the top
5288 when in crapplet-mode. */
5289 gtk_timeout_add (500, delayed_scroll_kludge, s);
5293 /* Load every configurator in turn, to scan them for errors all at once. */
5297 for (i = 0; i < p->screenhacks_count; i++)
5299 screenhack *hack = p->screenhacks[i];
5300 conf_data *d = load_configurator (hack->command, s->debug_p);
5301 if (d) free_conf_data (d);
5307 # ifdef HAVE_CRAPPLET
5309 capplet_gtk_main ();
5311 # endif /* HAVE_CRAPPLET */
5314 kill_preview_subproc (s, False);
5318 #endif /* HAVE_GTK -- whole file */