1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2013 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);
566 char *s = strchr (head, '\n');
569 sprintf (name, "label%d", i++);
572 label = gtk_label_new (head);
574 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
575 #endif /* HAVE_GTK2 */
580 GTK_WIDGET (label)->style =
581 gtk_style_copy (GTK_WIDGET (label)->style);
582 GTK_WIDGET (label)->style->font =
583 gdk_font_load (get_string_resource("warning_dialog.headingFont",
585 gtk_widget_set_style (GTK_WIDGET (label),
586 GTK_WIDGET (label)->style);
588 #endif /* !HAVE_GTK2 */
590 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
591 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
592 label, TRUE, TRUE, 0);
593 gtk_widget_show (label);
604 label = gtk_label_new ("");
605 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
606 label, TRUE, TRUE, 0);
607 gtk_widget_show (label);
609 label = gtk_hbutton_box_new ();
610 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
611 label, TRUE, TRUE, 0);
614 if (button_type != D_NONE)
616 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
617 gtk_container_add (GTK_CONTAINER (label), cancel);
620 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
621 gtk_container_add (GTK_CONTAINER (label), ok);
623 #else /* !HAVE_GTK2 */
625 ok = gtk_button_new_with_label ("OK");
626 gtk_container_add (GTK_CONTAINER (label), ok);
628 if (button_type != D_NONE)
630 cancel = gtk_button_new_with_label ("Cancel");
631 gtk_container_add (GTK_CONTAINER (label), cancel);
634 #endif /* !HAVE_GTK2 */
636 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
637 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
638 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
639 SET_CAN_DEFAULT (ok);
640 gtk_widget_show (ok);
641 gtk_widget_grab_focus (ok);
645 SET_CAN_DEFAULT (cancel);
646 gtk_widget_show (cancel);
648 gtk_widget_show (label);
649 gtk_widget_show (dialog);
651 if (button_type != D_NONE)
654 switch (button_type) {
655 case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break;
656 case D_GNOME: fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb); break;
657 case D_KDE: fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb); break;
658 default: abort(); break;
660 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn,
662 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
663 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
668 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
669 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
673 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
674 GET_WINDOW (GTK_WIDGET (parent)));
677 gtk_window_present (GTK_WINDOW (dialog));
678 #else /* !HAVE_GTK2 */
679 gdk_window_show (GTK_WIDGET (dialog)->window);
680 gdk_window_raise (GTK_WIDGET (dialog)->window);
681 #endif /* !HAVE_GTK2 */
689 run_cmd (state *s, Atom command, int arg)
694 flush_dialog_changes_and_save (s);
695 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
697 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
698 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
705 sprintf (buf, "Error:\n\n%s", err);
707 strcpy (buf, "Unknown error!");
708 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
712 sensitize_menu_items (s, True);
713 force_dialog_repaint (s);
718 run_hack (state *s, int list_elt, Bool report_errors_p)
724 if (list_elt < 0) return;
725 hack_number = s->list_elt_to_hack_number[list_elt];
727 flush_dialog_changes_and_save (s);
728 schedule_preview (s, 0);
730 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
733 if (status < 0 && report_errors_p)
735 if (xscreensaver_running_p (s))
737 /* Kludge: ignore the spurious "window unexpectedly deleted"
739 if (err && strstr (err, "unexpectedly deleted"))
746 sprintf (buf, "Error:\n\n%s", err);
748 strcpy (buf, "Unknown error!");
749 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
754 /* The error is that the daemon isn't running;
757 const char *d = DisplayString (GDK_DISPLAY());
761 "The XScreenSaver daemon doesn't seem to be running\n"
762 "on display \"%s\". Launch it now?"),
764 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
770 sensitize_menu_items (s, False);
777 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
778 libglade work on Cygwin; apparently all Glade callbacks need this magic
779 extra declaration. I do not pretend to understand.
783 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
785 state *s = global_state_kludge; /* I hate C so much... */
786 flush_dialog_changes_and_save (s);
787 kill_preview_subproc (s, False);
792 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
794 state *s = (state *) data;
795 flush_dialog_changes_and_save (s);
802 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
805 char *vers = strdup (screensaver_id + 4);
809 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
811 s = strchr (vers, ',');
816 s2 = strrchr (vers, '-');
818 strncpy (year, s2, 4);
821 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
822 non-ASCII characters aren't allowed in localizable string keys."
823 (I don't want to just use (c) instead of © because that doesn't
824 look as good in the plain-old default Latin1 "C" locale.)
827 sprintf(copy, ("Copyright \xC2\xA9 1991-%s %s"), year, s);
828 #else /* !HAVE_GTK2 */
829 sprintf(copy, ("Copyright \251 1991-%s %s"), year, s);
830 #endif /* !HAVE_GTK2 */
832 sprintf (msg, "%s\n\n%s", copy, desc);
834 /* I can't make gnome_about_new() work here -- it starts dying in
835 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
836 then this might be the thing to do:
840 const gchar *auth[] = { 0 };
841 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
843 gtk_widget_show (about);
845 #else / * GTK but not GNOME * /
849 GdkColormap *colormap;
850 GdkPixmap *gdkpixmap;
853 GtkWidget *dialog = gtk_dialog_new ();
854 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
855 GtkWidget *parent = GTK_WIDGET (menuitem);
856 while (GET_PARENT (parent))
857 parent = GET_PARENT (parent);
859 hbox = gtk_hbox_new (FALSE, 20);
860 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
861 hbox, TRUE, TRUE, 0);
863 colormap = gtk_widget_get_colormap (parent);
865 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
866 (gchar **) logo_180_xpm);
867 icon = gtk_pixmap_new (gdkpixmap, mask);
868 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
870 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
872 vbox = gtk_vbox_new (FALSE, 0);
873 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
875 label1 = gtk_label_new (vers);
876 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
877 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
878 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
881 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
882 GTK_WIDGET (label1)->style->font =
883 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
884 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
885 #endif /* HAVE_GTK2 */
887 label2 = gtk_label_new (msg);
888 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
889 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
890 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
893 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
894 GTK_WIDGET (label2)->style->font =
895 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
896 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
897 #endif /* HAVE_GTK2 */
899 hb = gtk_hbutton_box_new ();
901 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
905 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
906 #else /* !HAVE_GTK2 */
907 ok = gtk_button_new_with_label (_("OK"));
908 #endif /* !HAVE_GTK2 */
909 gtk_container_add (GTK_CONTAINER (hb), ok);
911 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
912 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
913 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
915 gtk_widget_show (hbox);
916 gtk_widget_show (icon);
917 gtk_widget_show (vbox);
918 gtk_widget_show (label1);
919 gtk_widget_show (label2);
920 gtk_widget_show (hb);
921 gtk_widget_show (ok);
922 gtk_widget_show (dialog);
924 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
925 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
927 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
928 GET_WINDOW (GTK_WIDGET (parent)));
929 gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog)));
930 gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog)));
936 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
938 state *s = global_state_kludge; /* I hate C so much... */
939 saver_preferences *p = &s->prefs;
942 if (!p->help_url || !*p->help_url)
944 warning_dialog (s->toplevel_widget,
946 "No Help URL has been specified.\n"), D_NONE, 100);
950 help_command = (char *) malloc (strlen (p->load_url_command) +
951 (strlen (p->help_url) * 4) + 20);
952 strcpy (help_command, "( ");
953 sprintf (help_command + strlen(help_command),
955 p->help_url, p->help_url, p->help_url, p->help_url);
956 strcat (help_command, " ) &");
957 if (system (help_command) < 0)
958 fprintf (stderr, "%s: fork error\n", blurb());
964 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
966 state *s = global_state_kludge; /* I hate C so much... */
967 sensitize_menu_items (s, False);
972 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
974 state *s = global_state_kludge; /* I hate C so much... */
975 run_cmd (s, XA_ACTIVATE, 0);
980 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
982 state *s = global_state_kludge; /* I hate C so much... */
983 run_cmd (s, XA_LOCK, 0);
988 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
990 state *s = global_state_kludge; /* I hate C so much... */
991 run_cmd (s, XA_EXIT, 0);
996 restart_menu_cb (GtkWidget *widget, gpointer user_data)
998 state *s = global_state_kludge; /* I hate C so much... */
999 flush_dialog_changes_and_save (s);
1000 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
1002 if (system ("xscreensaver -nosplash &") < 0)
1003 fprintf (stderr, "%s: fork error\n", blurb());
1005 await_xscreensaver (s);
1009 xscreensaver_running_p (state *s)
1011 Display *dpy = GDK_DISPLAY();
1013 server_xscreensaver_version (dpy, &rversion, 0, 0);
1021 await_xscreensaver (state *s)
1026 while (!ok && (--countdown > 0))
1027 if (xscreensaver_running_p (s))
1030 sleep (1); /* If it's not there yet, wait a second... */
1032 sensitize_menu_items (s, True);
1036 /* Timed out, no screensaver running. */
1039 Bool root_p = (geteuid () == 0);
1043 "The xscreensaver daemon did not start up properly.\n"
1049 __extension__ /* don't warn about "string length is greater than
1050 the length ISO C89 compilers are required to
1051 support" in the following expression... */
1054 _("You are running as root. This usually means that xscreensaver\n"
1055 "was unable to contact your X server because access control is\n"
1056 "turned on. Try running this command:\n"
1058 " xhost +localhost\n"
1060 "and then selecting `File / Restart Daemon'.\n"
1062 "Note that turning off access control will allow anyone logged\n"
1063 "on to this machine to access your screen, which might be\n"
1064 "considered a security problem. Please read the xscreensaver\n"
1065 "manual and FAQ for more information.\n"
1067 "You shouldn't run X as root. Instead, you should log in as a\n"
1068 "normal user, and `su' as necessary."));
1070 strcat (buf, _("Please check your $PATH and permissions."));
1072 warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1075 force_dialog_repaint (s);
1080 selected_list_element (state *s)
1082 return s->_selected_list_element;
1087 demo_write_init_file (state *s, saver_preferences *p)
1089 Display *dpy = GDK_DISPLAY();
1092 /* #### try to figure out why shit keeps getting reordered... */
1093 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1097 if (!write_init_file (dpy, p, s->short_version, False))
1100 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1105 const char *f = init_file_name();
1107 warning_dialog (s->toplevel_widget,
1108 _("Error:\n\nCouldn't determine init file name!\n"),
1112 char *b = (char *) malloc (strlen(f) + 1024);
1113 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1114 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1122 G_MODULE_EXPORT void
1123 run_this_cb (GtkButton *button, gpointer user_data)
1125 state *s = global_state_kludge; /* I hate C so much... */
1126 int list_elt = selected_list_element (s);
1127 if (list_elt < 0) return;
1128 if (!flush_dialog_changes_and_save (s))
1129 run_hack (s, list_elt, True);
1133 G_MODULE_EXPORT void
1134 manual_cb (GtkButton *button, gpointer user_data)
1136 Display *dpy = GDK_DISPLAY();
1137 state *s = global_state_kludge; /* I hate C so much... */
1138 saver_preferences *p = &s->prefs;
1139 GtkWidget *list_widget = name_to_widget (s, "list");
1140 int list_elt = selected_list_element (s);
1142 char *name, *name2, *cmd, *str;
1144 if (list_elt < 0) return;
1145 hack_number = s->list_elt_to_hack_number[list_elt];
1147 flush_dialog_changes_and_save (s);
1148 ensure_selected_item_visible (list_widget);
1150 name = strdup (p->screenhacks[hack_number]->command);
1153 while (isspace (*name2)) name2++;
1155 while (*str && !isspace (*str)) str++;
1157 str = strrchr (name2, '/');
1158 if (str) name2 = str+1;
1160 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1163 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1164 strcpy (cmd2, "( ");
1165 sprintf (cmd2 + strlen (cmd2),
1167 name2, name2, name2, name2);
1168 strcat (cmd2, " ) &");
1169 if (system (cmd2) < 0)
1170 fprintf (stderr, "%s: fork error\n", blurb());
1175 warning_dialog (GTK_WIDGET (button),
1176 _("Error:\n\nno `manualCommand' resource set."),
1185 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1187 GtkWidget *parent = name_to_widget (s, "scroller");
1188 gboolean was = GET_SENSITIVE (parent);
1191 GtkTreeModel *model;
1192 GtkTreeSelection *selection;
1193 #endif /* HAVE_GTK2 */
1195 if (!was) gtk_widget_set_sensitive (parent, True);
1197 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1199 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1201 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1202 gtk_tree_selection_select_iter (selection, &iter);
1204 #else /* !HAVE_GTK2 */
1205 gtk_list_select_item (GTK_LIST (list), list_elt);
1206 #endif /* !HAVE_GTK2 */
1207 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1208 if (!was) gtk_widget_set_sensitive (parent, False);
1212 G_MODULE_EXPORT void
1213 run_next_cb (GtkButton *button, gpointer user_data)
1215 state *s = global_state_kludge; /* I hate C so much... */
1216 /* saver_preferences *p = &s->prefs; */
1217 Bool ops = s->preview_suppressed_p;
1219 GtkWidget *list_widget = name_to_widget (s, "list");
1220 int list_elt = selected_list_element (s);
1227 if (list_elt >= s->list_count)
1230 s->preview_suppressed_p = True;
1232 flush_dialog_changes_and_save (s);
1233 force_list_select_item (s, list_widget, list_elt, True);
1234 populate_demo_window (s, list_elt);
1235 run_hack (s, list_elt, False);
1237 s->preview_suppressed_p = ops;
1241 G_MODULE_EXPORT void
1242 run_prev_cb (GtkButton *button, gpointer user_data)
1244 state *s = global_state_kludge; /* I hate C so much... */
1245 /* saver_preferences *p = &s->prefs; */
1246 Bool ops = s->preview_suppressed_p;
1248 GtkWidget *list_widget = name_to_widget (s, "list");
1249 int list_elt = selected_list_element (s);
1252 list_elt = s->list_count - 1;
1257 list_elt = s->list_count - 1;
1259 s->preview_suppressed_p = True;
1261 flush_dialog_changes_and_save (s);
1262 force_list_select_item (s, list_widget, list_elt, True);
1263 populate_demo_window (s, list_elt);
1264 run_hack (s, list_elt, False);
1266 s->preview_suppressed_p = ops;
1270 /* Writes the given settings into prefs.
1271 Returns true if there was a change, False otherwise.
1272 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1275 flush_changes (state *s,
1278 const char *command,
1281 saver_preferences *p = &s->prefs;
1282 Bool changed = False;
1285 if (list_elt < 0 || list_elt >= s->list_count)
1288 hack_number = s->list_elt_to_hack_number[list_elt];
1289 hack = p->screenhacks[hack_number];
1291 if (enabled_p != -1 &&
1292 enabled_p != hack->enabled_p)
1294 hack->enabled_p = enabled_p;
1297 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1298 blurb(), hack->name, enabled_p);
1303 if (!hack->command || !!strcmp (command, hack->command))
1305 if (hack->command) free (hack->command);
1306 hack->command = strdup (command);
1309 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1310 blurb(), hack->name, command);
1316 const char *ov = hack->visual;
1317 if (!ov || !*ov) ov = "any";
1318 if (!*visual) visual = "any";
1319 if (!!strcasecmp (visual, ov))
1321 if (hack->visual) free (hack->visual);
1322 hack->visual = strdup (visual);
1325 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1326 blurb(), hack->name, visual);
1334 /* Helper for the text fields that contain time specifications:
1335 this parses the text, and does error checking.
1338 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1343 if (!sec_p || strchr (line, ':'))
1344 value = parse_time ((char *) line, sec_p, True);
1348 if (sscanf (line, "%d%c", &value, &c) != 1)
1354 value *= 1000; /* Time measures in microseconds */
1360 "Unparsable time format: \"%s\"\n"),
1362 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1371 directory_p (const char *path)
1374 if (!path || !*path)
1376 else if (stat (path, &st))
1378 else if (!S_ISDIR (st.st_mode))
1385 file_p (const char *path)
1388 if (!path || !*path)
1390 else if (stat (path, &st))
1392 else if (S_ISDIR (st.st_mode))
1399 normalize_directory (const char *path)
1403 if (!path || !*path) return 0;
1405 p2 = (char *) malloc (L + 2);
1407 if (p2[L-1] == '/') /* remove trailing slash */
1410 for (s = p2; s && *s; s++)
1413 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1414 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1417 while (s0 > p2 && s0[-1] != '/')
1423 /* strcpy (s0, s); */
1424 memmove(s0, s, strlen(s) + 1);
1428 else if (*s == '/' && !strncmp (s, "/./", 3)) { /* delete "/./" */
1429 /* strcpy (s, s+2), s--; */
1430 memmove(s, s+2, strlen(s+2) + 1);
1433 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1438 Normalize consecutive slashes.
1439 Ignore doubled slashes after ":" to avoid mangling URLs.
1442 for (s = p2; s && *s; s++){
1443 if (*s == ':') continue;
1444 if (!s[1] || !s[2]) continue;
1445 while (s[1] == '/' && s[2] == '/')
1446 /* strcpy (s+1, s+2); */
1447 memmove (s+1, s+2, strlen(s+2) + 1);
1450 /* and strip trailing whitespace for good measure. */
1452 while (isspace(p2[L-1]))
1465 } FlushForeachClosure;
1468 flush_checkbox (GtkTreeModel *model,
1473 FlushForeachClosure *closure = data;
1476 gtk_tree_model_get (model, iter,
1477 COL_ENABLED, &checked,
1480 if (flush_changes (closure->s, closure->i,
1482 *closure->changed = True;
1486 /* don't remove row */
1490 #endif /* HAVE_GTK2 */
1492 /* Flush out any changes made in the main dialog window (where changes
1493 take place immediately: clicking on a checkbox causes the init file
1494 to be written right away.)
1497 flush_dialog_changes_and_save (state *s)
1499 saver_preferences *p = &s->prefs;
1500 saver_preferences P2, *p2 = &P2;
1502 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1503 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1504 FlushForeachClosure closure;
1505 #else /* !HAVE_GTK2 */
1506 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1507 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1509 #endif /* !HAVE_GTK2 */
1510 static Bool already_warned_about_missing_image_directory = False; /* very long name... */
1512 Bool changed = False;
1515 if (s->saving_p) return False;
1520 /* Flush any checkbox changes in the list down into the prefs struct.
1524 closure.changed = &changed;
1526 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1528 #else /* !HAVE_GTK2 */
1530 for (i = 0; kids; kids = kids->next, i++)
1532 GtkWidget *line = GTK_WIDGET (kids->data);
1533 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1534 GtkWidget *line_check =
1535 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1537 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1539 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1542 #endif /* ~HAVE_GTK2 */
1544 /* Flush the non-hack-specific settings down into the prefs struct.
1547 # define SECONDS(FIELD,NAME) \
1548 w = name_to_widget (s, (NAME)); \
1549 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1551 # define MINUTES(FIELD,NAME) \
1552 w = name_to_widget (s, (NAME)); \
1553 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1555 # define CHECKBOX(FIELD,NAME) \
1556 w = name_to_widget (s, (NAME)); \
1557 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1559 # define PATHNAME(FIELD,NAME) \
1560 w = name_to_widget (s, (NAME)); \
1561 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1563 # define TEXT(FIELD,NAME) \
1564 w = name_to_widget (s, (NAME)); \
1565 (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1567 MINUTES (&p2->timeout, "timeout_spinbutton");
1568 MINUTES (&p2->cycle, "cycle_spinbutton");
1569 CHECKBOX (p2->lock_p, "lock_button");
1570 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1572 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1573 CHECKBOX (p2->dpms_quickoff_p, "dpms_quickoff_button");
1574 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1575 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1576 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1578 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1579 CHECKBOX (p2->grab_video_p, "grab_video_button");
1580 CHECKBOX (p2->random_image_p, "grab_image_button");
1581 PATHNAME (p2->image_directory, "image_text");
1584 CHECKBOX (p2->verbose_p, "verbose_button");
1585 CHECKBOX (p2->capture_stderr_p, "capture_button");
1586 CHECKBOX (p2->splash_p, "splash_button");
1591 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1592 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1593 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1594 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1595 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1596 TEXT (p2->text_literal, "text_entry");
1597 PATHNAME (p2->text_file, "text_file_entry");
1598 PATHNAME (p2->text_program, "text_program_entry");
1599 PATHNAME (p2->text_program, "text_program_entry");
1600 TEXT (p2->text_url, "text_url_entry");
1603 CHECKBOX (p2->install_cmap_p, "install_button");
1604 CHECKBOX (p2->fade_p, "fade_button");
1605 CHECKBOX (p2->unfade_p, "unfade_button");
1606 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1614 /* Warn if the image directory doesn't exist, when:
1615 - not being warned before
1616 - image directory is changed and the directory doesn't exist
1617 - image directory does not begin with http://
1619 if (p2->image_directory &&
1620 *p2->image_directory &&
1621 !directory_p (p2->image_directory) &&
1622 strncmp(p2->image_directory, "http://", 6) &&
1623 ( !already_warned_about_missing_image_directory ||
1624 ( p->image_directory &&
1625 *p->image_directory &&
1626 strcmp(p->image_directory, p2->image_directory)
1632 sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n",
1633 p2->image_directory);
1634 if (warning_dialog (s->toplevel_widget, b, D_NONE, 100))
1635 already_warned_about_missing_image_directory = True;
1639 /* Map the mode menu to `saver_mode' enum values. */
1641 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1642 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1643 GtkWidget *selected = gtk_menu_get_active (menu);
1644 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1645 int menu_elt = g_list_index (kids, (gpointer) selected);
1646 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1647 p2->mode = mode_menu_order[menu_elt];
1650 if (p2->mode == ONE_HACK)
1652 int list_elt = selected_list_element (s);
1653 p2->selected_hack = (list_elt >= 0
1654 ? s->list_elt_to_hack_number[list_elt]
1658 # define COPY(field, name) \
1659 if (p->field != p2->field) { \
1662 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1664 p->field = p2->field
1667 COPY(selected_hack, "selected_hack");
1669 COPY(timeout, "timeout");
1670 COPY(cycle, "cycle");
1671 COPY(lock_p, "lock_p");
1672 COPY(lock_timeout, "lock_timeout");
1674 COPY(dpms_enabled_p, "dpms_enabled_p");
1675 COPY(dpms_quickoff_p, "dpms_quickoff_enabled_p");
1676 COPY(dpms_standby, "dpms_standby");
1677 COPY(dpms_suspend, "dpms_suspend");
1678 COPY(dpms_off, "dpms_off");
1681 COPY(verbose_p, "verbose_p");
1682 COPY(capture_stderr_p, "capture_stderr_p");
1683 COPY(splash_p, "splash_p");
1686 COPY(tmode, "tmode");
1688 COPY(install_cmap_p, "install_cmap_p");
1689 COPY(fade_p, "fade_p");
1690 COPY(unfade_p, "unfade_p");
1691 COPY(fade_seconds, "fade_seconds");
1693 COPY(grab_desktop_p, "grab_desktop_p");
1694 COPY(grab_video_p, "grab_video_p");
1695 COPY(random_image_p, "random_image_p");
1699 # define COPYSTR(FIELD,NAME) \
1702 strcmp(p->FIELD, p2->FIELD)) \
1706 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1708 if (p->FIELD && p->FIELD != p2->FIELD) \
1710 p->FIELD = p2->FIELD; \
1713 COPYSTR(image_directory, "image_directory");
1714 COPYSTR(text_literal, "text_literal");
1715 COPYSTR(text_file, "text_file");
1716 COPYSTR(text_program, "text_program");
1717 COPYSTR(text_url, "text_url");
1720 populate_prefs_page (s);
1724 Display *dpy = GDK_DISPLAY();
1725 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1726 sync_server_dpms_settings (dpy, enabled_p, p->dpms_quickoff_p,
1727 p->dpms_standby / 1000,
1728 p->dpms_suspend / 1000,
1732 changed = demo_write_init_file (s, p);
1735 s->saving_p = False;
1740 /* Flush out any changes made in the popup dialog box (where changes
1741 take place only when the OK button is clicked.)
1744 flush_popup_changes_and_save (state *s)
1746 Bool changed = False;
1747 saver_preferences *p = &s->prefs;
1748 int list_elt = selected_list_element (s);
1750 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1751 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1753 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1754 const char *command = gtk_entry_get_text (cmd);
1759 if (s->saving_p) return False;
1765 if (maybe_reload_init_file (s) != 0)
1771 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1773 if (!strcasecmp (visual, "")) visual = "";
1774 else if (!strcasecmp (visual, "any")) visual = "";
1775 else if (!strcasecmp (visual, "default")) visual = "Default";
1776 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1777 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1778 else if (!strcasecmp (visual, "best")) visual = "Best";
1779 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1780 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1781 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1782 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1783 else if (!strcasecmp (visual, "color")) visual = "Color";
1784 else if (!strcasecmp (visual, "gl")) visual = "GL";
1785 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1786 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1787 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1788 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1789 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1790 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1791 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1792 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1793 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1796 gdk_beep (); /* unparsable */
1798 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1801 changed = flush_changes (s, list_elt, -1, command, visual);
1804 changed = demo_write_init_file (s, p);
1806 /* Do this to re-launch the hack if (and only if) the command line
1808 populate_demo_window (s, selected_list_element (s));
1812 s->saving_p = False;
1817 G_MODULE_EXPORT void
1818 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1820 state *s = global_state_kludge; /* I hate C so much... */
1821 if (! s->initializing_p)
1823 s->initializing_p = True;
1824 flush_dialog_changes_and_save (s);
1825 s->initializing_p = False;
1829 G_MODULE_EXPORT gboolean
1830 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1832 pref_changed_cb (widget, user_data);
1836 /* Callback on menu items in the "mode" options menu.
1838 G_MODULE_EXPORT void
1839 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1841 state *s = (state *) user_data;
1842 saver_preferences *p = &s->prefs;
1843 GtkWidget *list = name_to_widget (s, "list");
1847 gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
1849 saver_mode new_mode;
1853 if (menu_items->data == widget)
1856 menu_items = menu_items->next;
1858 if (!menu_items) abort();
1860 new_mode = mode_menu_order[menu_index];
1862 /* Keep the same list element displayed as before; except if we're
1863 switching *to* "one screensaver" mode from any other mode, set
1864 "the one" to be that which is currently selected.
1866 list_elt = selected_list_element (s);
1867 if (new_mode == ONE_HACK)
1868 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1871 saver_mode old_mode = p->mode;
1873 populate_demo_window (s, list_elt);
1874 force_list_select_item (s, list, list_elt, True);
1875 p->mode = old_mode; /* put it back, so the init file gets written */
1878 pref_changed_cb (widget, user_data);
1882 G_MODULE_EXPORT void
1883 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1884 gint page_num, gpointer user_data)
1886 state *s = global_state_kludge; /* I hate C so much... */
1887 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1889 /* If we're switching to page 0, schedule the current hack to be run.
1890 Otherwise, schedule it to stop. */
1892 populate_demo_window (s, selected_list_element (s));
1894 schedule_preview (s, 0);
1899 list_activated_cb (GtkTreeView *list,
1901 GtkTreeViewColumn *column,
1908 g_return_if_fail (!gdk_pointer_is_grabbed ());
1910 str = gtk_tree_path_to_string (path);
1911 list_elt = strtol (str, NULL, 10);
1915 run_hack (s, list_elt, True);
1919 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1921 state *s = (state *)data;
1922 GtkTreeModel *model;
1928 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1931 path = gtk_tree_model_get_path (model, &iter);
1932 str = gtk_tree_path_to_string (path);
1933 list_elt = strtol (str, NULL, 10);
1935 gtk_tree_path_free (path);
1938 populate_demo_window (s, list_elt);
1939 flush_dialog_changes_and_save (s);
1941 /* Re-populate the Settings window any time a new item is selected
1942 in the list, in case both windows are currently visible.
1944 populate_popup_window (s);
1947 #else /* !HAVE_GTK2 */
1949 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1950 list_select_cb that comes in
1951 *after* we've double-clicked.
1955 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1958 state *s = (state *) data;
1959 if (event->type == GDK_2BUTTON_PRESS)
1961 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1962 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1964 last_doubleclick_time = time ((time_t *) 0);
1967 run_hack (s, list_elt, True);
1975 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1977 state *s = (state *) data;
1978 time_t now = time ((time_t *) 0);
1980 if (now >= last_doubleclick_time + 2)
1982 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1983 populate_demo_window (s, list_elt);
1984 flush_dialog_changes_and_save (s);
1989 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1991 state *s = (state *) data;
1992 populate_demo_window (s, -1);
1993 flush_dialog_changes_and_save (s);
1996 #endif /* !HAVE_GTK2 */
1999 /* Called when the checkboxes that are in the left column of the
2000 scrolling list are clicked. This both populates the right pane
2001 (just as clicking on the label (really, listitem) does) and
2002 also syncs this checkbox with the right pane Enabled checkbox.
2007 GtkCellRendererToggle *toggle,
2009 #else /* !HAVE_GTK2 */
2011 #endif /* !HAVE_GTK2 */
2014 state *s = (state *) data;
2017 GtkScrolledWindow *scroller =
2018 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
2019 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2020 GtkTreeModel *model = gtk_tree_view_get_model (list);
2021 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
2024 #else /* !HAVE_GTK2 */
2025 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
2026 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
2028 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
2029 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
2030 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
2031 #endif /* !HAVE_GTK2 */
2038 if (!gtk_tree_model_get_iter (model, &iter, path))
2040 g_warning ("bad path: %s", path_string);
2043 gtk_tree_path_free (path);
2045 gtk_tree_model_get (model, &iter,
2046 COL_ENABLED, &active,
2049 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2050 COL_ENABLED, !active,
2053 list_elt = strtol (path_string, NULL, 10);
2054 #else /* !HAVE_GTK2 */
2055 list_elt = gtk_list_child_position (list, line);
2056 #endif /* !HAVE_GTK2 */
2058 /* remember previous scroll position of the top of the list */
2059 adj = gtk_scrolled_window_get_vadjustment (scroller);
2060 scroll_top = GET_ADJ_VALUE (adj);
2062 flush_dialog_changes_and_save (s);
2063 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
2064 populate_demo_window (s, list_elt);
2066 /* restore the previous scroll position of the top of the list.
2067 this is weak, but I don't really know why it's moving... */
2068 gtk_adjustment_set_value (adj, scroll_top);
2074 GtkFileSelection *widget;
2075 } file_selection_data;
2080 store_image_directory (GtkWidget *button, gpointer user_data)
2082 file_selection_data *fsd = (file_selection_data *) user_data;
2083 state *s = fsd->state;
2084 GtkFileSelection *selector = fsd->widget;
2085 GtkWidget *top = s->toplevel_widget;
2086 saver_preferences *p = &s->prefs;
2087 const char *path = gtk_file_selection_get_filename (selector);
2089 if (p->image_directory && !strcmp(p->image_directory, path))
2090 return; /* no change */
2092 /* No warning for URLs. */
2093 if ((!directory_p (path)) && strncmp(path, "http://", 6))
2096 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2097 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2101 if (p->image_directory) free (p->image_directory);
2102 p->image_directory = normalize_directory (path);
2104 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2105 (p->image_directory ? p->image_directory : ""));
2106 demo_write_init_file (s, p);
2111 store_text_file (GtkWidget *button, gpointer user_data)
2113 file_selection_data *fsd = (file_selection_data *) user_data;
2114 state *s = fsd->state;
2115 GtkFileSelection *selector = fsd->widget;
2116 GtkWidget *top = s->toplevel_widget;
2117 saver_preferences *p = &s->prefs;
2118 const char *path = gtk_file_selection_get_filename (selector);
2120 if (p->text_file && !strcmp(p->text_file, path))
2121 return; /* no change */
2126 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2127 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2131 if (p->text_file) free (p->text_file);
2132 p->text_file = normalize_directory (path);
2134 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2135 (p->text_file ? p->text_file : ""));
2136 demo_write_init_file (s, p);
2141 store_text_program (GtkWidget *button, gpointer user_data)
2143 file_selection_data *fsd = (file_selection_data *) user_data;
2144 state *s = fsd->state;
2145 GtkFileSelection *selector = fsd->widget;
2146 /*GtkWidget *top = s->toplevel_widget;*/
2147 saver_preferences *p = &s->prefs;
2148 const char *path = gtk_file_selection_get_filename (selector);
2150 if (p->text_program && !strcmp(p->text_program, path))
2151 return; /* no change */
2157 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2158 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2163 if (p->text_program) free (p->text_program);
2164 p->text_program = normalize_directory (path);
2166 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2167 (p->text_program ? p->text_program : ""));
2168 demo_write_init_file (s, p);
2174 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2176 file_selection_data *fsd = (file_selection_data *) user_data;
2177 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2181 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2183 browse_image_dir_cancel (button, user_data);
2184 store_image_directory (button, user_data);
2188 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2190 browse_image_dir_cancel (button, user_data);
2191 store_text_file (button, user_data);
2195 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2197 browse_image_dir_cancel (button, user_data);
2198 store_text_program (button, user_data);
2202 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2204 browse_image_dir_cancel (widget, user_data);
2208 G_MODULE_EXPORT void
2209 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2211 state *s = global_state_kludge; /* I hate C so much... */
2212 saver_preferences *p = &s->prefs;
2213 static file_selection_data *fsd = 0;
2215 GtkFileSelection *selector = GTK_FILE_SELECTION(
2216 gtk_file_selection_new ("Please select the image directory."));
2219 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2221 fsd->widget = selector;
2224 if (p->image_directory && *p->image_directory)
2225 gtk_file_selection_set_filename (selector, p->image_directory);
2227 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2228 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2230 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2231 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2233 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2234 GTK_SIGNAL_FUNC (browse_image_dir_close),
2237 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2239 gtk_window_set_modal (GTK_WINDOW (selector), True);
2240 gtk_widget_show (GTK_WIDGET (selector));
2244 G_MODULE_EXPORT void
2245 browse_text_file_cb (GtkButton *button, gpointer user_data)
2247 state *s = global_state_kludge; /* I hate C so much... */
2248 saver_preferences *p = &s->prefs;
2249 static file_selection_data *fsd = 0;
2251 GtkFileSelection *selector = GTK_FILE_SELECTION(
2252 gtk_file_selection_new ("Please select a text file."));
2255 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2257 fsd->widget = selector;
2260 if (p->text_file && *p->text_file)
2261 gtk_file_selection_set_filename (selector, p->text_file);
2263 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2264 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2266 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2267 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2269 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2270 GTK_SIGNAL_FUNC (browse_image_dir_close),
2273 gtk_window_set_modal (GTK_WINDOW (selector), True);
2274 gtk_widget_show (GTK_WIDGET (selector));
2278 G_MODULE_EXPORT void
2279 browse_text_program_cb (GtkButton *button, gpointer user_data)
2281 state *s = global_state_kludge; /* I hate C so much... */
2282 saver_preferences *p = &s->prefs;
2283 static file_selection_data *fsd = 0;
2285 GtkFileSelection *selector = GTK_FILE_SELECTION(
2286 gtk_file_selection_new ("Please select a text-generating program."));
2289 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2291 fsd->widget = selector;
2294 if (p->text_program && *p->text_program)
2295 gtk_file_selection_set_filename (selector, p->text_program);
2297 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2298 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2300 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2301 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2303 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2304 GTK_SIGNAL_FUNC (browse_image_dir_close),
2307 gtk_window_set_modal (GTK_WINDOW (selector), True);
2308 gtk_widget_show (GTK_WIDGET (selector));
2315 G_MODULE_EXPORT void
2316 settings_cb (GtkButton *button, gpointer user_data)
2318 state *s = global_state_kludge; /* I hate C so much... */
2319 int list_elt = selected_list_element (s);
2321 populate_demo_window (s, list_elt); /* reset the widget */
2322 populate_popup_window (s); /* create UI on popup window */
2323 gtk_widget_show (s->popup_widget);
2327 settings_sync_cmd_text (state *s)
2330 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2331 char *cmd_line = get_configurator_command_line (s->cdata, False);
2332 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2333 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2335 # endif /* HAVE_XML */
2338 G_MODULE_EXPORT void
2339 settings_adv_cb (GtkButton *button, gpointer user_data)
2341 state *s = global_state_kludge; /* I hate C so much... */
2342 GtkNotebook *notebook =
2343 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2345 settings_sync_cmd_text (s);
2346 gtk_notebook_set_page (notebook, 1);
2349 G_MODULE_EXPORT void
2350 settings_std_cb (GtkButton *button, gpointer user_data)
2352 state *s = global_state_kludge; /* I hate C so much... */
2353 GtkNotebook *notebook =
2354 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2356 /* Re-create UI to reflect the in-progress command-line settings. */
2357 populate_popup_window (s);
2359 gtk_notebook_set_page (notebook, 0);
2362 G_MODULE_EXPORT void
2363 settings_reset_cb (GtkButton *button, gpointer user_data)
2366 state *s = global_state_kludge; /* I hate C so much... */
2367 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2368 char *cmd_line = get_configurator_command_line (s->cdata, True);
2369 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2370 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2372 populate_popup_window (s);
2373 # endif /* HAVE_XML */
2376 G_MODULE_EXPORT void
2377 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2378 gint page_num, gpointer user_data)
2380 state *s = global_state_kludge; /* I hate C so much... */
2381 GtkWidget *adv = name_to_widget (s, "adv_button");
2382 GtkWidget *std = name_to_widget (s, "std_button");
2386 gtk_widget_show (adv);
2387 gtk_widget_hide (std);
2389 else if (page_num == 1)
2391 gtk_widget_hide (adv);
2392 gtk_widget_show (std);
2400 G_MODULE_EXPORT void
2401 settings_cancel_cb (GtkButton *button, gpointer user_data)
2403 state *s = global_state_kludge; /* I hate C so much... */
2404 gtk_widget_hide (s->popup_widget);
2407 G_MODULE_EXPORT void
2408 settings_ok_cb (GtkButton *button, gpointer user_data)
2410 state *s = global_state_kludge; /* I hate C so much... */
2411 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2412 int page = gtk_notebook_get_current_page (notebook);
2415 /* Regenerate the command-line from the widget contents before saving.
2416 But don't do this if we're looking at the command-line page already,
2417 or we will blow away what they typed... */
2418 settings_sync_cmd_text (s);
2420 flush_popup_changes_and_save (s);
2421 gtk_widget_hide (s->popup_widget);
2425 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2427 state *s = (state *) data;
2428 settings_cancel_cb (0, (gpointer) s);
2434 /* Populating the various widgets
2438 /* Returns the number of the last hack run by the server.
2441 server_current_hack (void)
2445 unsigned long nitems, bytesafter;
2446 unsigned char *dataP = 0;
2447 Display *dpy = GDK_DISPLAY();
2448 int hack_number = -1;
2450 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2451 XA_SCREENSAVER_STATUS,
2452 0, 3, False, XA_INTEGER,
2453 &type, &format, &nitems, &bytesafter,
2456 && type == XA_INTEGER
2460 PROP32 *data = (PROP32 *) dataP;
2461 hack_number = (int) data[2] - 1;
2464 if (dataP) XFree (dataP);
2470 /* Finds the number of the last hack that was run, and makes that item be
2471 selected by default.
2474 scroll_to_current_hack (state *s)
2476 saver_preferences *p = &s->prefs;
2477 int hack_number = -1;
2479 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2480 hack_number = p->selected_hack;
2481 if (hack_number < 0) /* otherwise, use the last-run */
2482 hack_number = server_current_hack ();
2483 if (hack_number < 0) /* failing that, last "one mode" */
2484 hack_number = p->selected_hack;
2485 if (hack_number < 0) /* failing that, newest hack. */
2487 /* We should only get here if the user does not have a .xscreensaver
2488 file, and the screen has not been blanked with a hack since X
2489 started up: in other words, this is probably a fresh install.
2491 Instead of just defaulting to hack #0 (in either "programs" or
2492 "alphabetical" order) let's try to default to the last runnable
2493 hack in the "programs" list: this is probably the hack that was
2494 most recently added to the xscreensaver distribution (and so
2495 it's probably the currently-coolest one!)
2497 hack_number = p->screenhacks_count-1;
2498 while (hack_number > 0 &&
2499 ! (s->hacks_available_p[hack_number] &&
2500 p->screenhacks[hack_number]->enabled_p))
2504 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2506 int list_elt = s->hack_number_to_list_elt[hack_number];
2507 GtkWidget *list = name_to_widget (s, "list");
2508 force_list_select_item (s, list, list_elt, True);
2509 populate_demo_window (s, list_elt);
2515 populate_hack_list (state *s)
2517 Display *dpy = GDK_DISPLAY();
2519 saver_preferences *p = &s->prefs;
2520 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2521 GtkListStore *model;
2522 GtkTreeSelection *selection;
2523 GtkCellRenderer *ren;
2527 g_object_get (G_OBJECT (list),
2532 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2533 g_object_set (G_OBJECT (list), "model", model, NULL);
2534 g_object_unref (model);
2536 ren = gtk_cell_renderer_toggle_new ();
2537 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2539 "active", COL_ENABLED,
2542 g_signal_connect (ren, "toggled",
2543 G_CALLBACK (list_checkbox_cb),
2546 ren = gtk_cell_renderer_text_new ();
2547 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2548 _("Screen Saver"), ren,
2552 g_signal_connect_after (list, "row_activated",
2553 G_CALLBACK (list_activated_cb),
2556 selection = gtk_tree_view_get_selection (list);
2557 g_signal_connect (selection, "changed",
2558 G_CALLBACK (list_select_changed_cb),
2563 for (i = 0; i < s->list_count; i++)
2565 int hack_number = s->list_elt_to_hack_number[i];
2566 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2568 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2570 if (!hack) continue;
2572 /* If we're to suppress uninstalled hacks, check $PATH now. */
2573 if (p->ignore_uninstalled_p && !available_p)
2576 pretty_name = (hack->name
2577 ? strdup (hack->name)
2578 : make_hack_name (dpy, hack->command));
2582 /* Make the text foreground be the color of insensitive widgets
2583 (but don't actually make it be insensitive, since we still
2584 want to be able to click on it.)
2586 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
2587 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2588 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2589 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2591 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2592 /* " background=\"#%02X%02X%02X\"" */
2594 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2595 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2601 gtk_list_store_append (model, &iter);
2602 gtk_list_store_set (model, &iter,
2603 COL_ENABLED, hack->enabled_p,
2604 COL_NAME, pretty_name,
2609 #else /* !HAVE_GTK2 */
2611 saver_preferences *p = &s->prefs;
2612 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2614 for (i = 0; i < s->list_count; i++)
2616 int hack_number = s->list_elt_to_hack_number[i];
2617 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2619 /* A GtkList must contain only GtkListItems, but those can contain
2620 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2621 and a Label. We handle single and double click events on the
2622 line itself, for clicking on the text, but the interior checkbox
2623 also handles its own events.
2626 GtkWidget *line_hbox;
2627 GtkWidget *line_check;
2628 GtkWidget *line_label;
2630 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2632 if (!hack) continue;
2634 /* If we're to suppress uninstalled hacks, check $PATH now. */
2635 if (p->ignore_uninstalled_p && !available_p)
2638 pretty_name = (hack->name
2639 ? strdup (hack->name)
2640 : make_hack_name (hack->command));
2642 line = gtk_list_item_new ();
2643 line_hbox = gtk_hbox_new (FALSE, 0);
2644 line_check = gtk_check_button_new ();
2645 line_label = gtk_label_new (pretty_name);
2647 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2648 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2649 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2651 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2653 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2655 gtk_widget_show (line_check);
2656 gtk_widget_show (line_label);
2657 gtk_widget_show (line_hbox);
2658 gtk_widget_show (line);
2662 gtk_container_add (GTK_CONTAINER (list), line);
2663 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2664 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2667 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2668 GTK_SIGNAL_FUNC (list_checkbox_cb),
2671 gtk_widget_show (line);
2675 /* Make the widget be colored like insensitive widgets
2676 (but don't actually make it be insensitive, since we
2677 still want to be able to click on it.)
2679 GtkRcStyle *rc_style;
2682 gtk_widget_realize (GTK_WIDGET (line_label));
2684 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2685 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2687 rc_style = gtk_rc_style_new ();
2688 rc_style->fg[GTK_STATE_NORMAL] = fg;
2689 rc_style->bg[GTK_STATE_NORMAL] = bg;
2690 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2692 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2693 gtk_rc_style_unref (rc_style);
2697 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2698 GTK_SIGNAL_FUNC (list_select_cb),
2700 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2701 GTK_SIGNAL_FUNC (list_unselect_cb),
2703 #endif /* !HAVE_GTK2 */
2707 update_list_sensitivity (state *s)
2709 saver_preferences *p = &s->prefs;
2710 Bool sensitive = (p->mode == RANDOM_HACKS ||
2711 p->mode == RANDOM_HACKS_SAME ||
2712 p->mode == ONE_HACK);
2713 Bool checkable = (p->mode == RANDOM_HACKS ||
2714 p->mode == RANDOM_HACKS_SAME);
2715 Bool blankable = (p->mode != DONT_BLANK);
2718 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2719 GtkWidget *use = name_to_widget (s, "use_col_frame");
2720 #endif /* HAVE_GTK2 */
2721 GtkWidget *scroller = name_to_widget (s, "scroller");
2722 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2723 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2726 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2727 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2728 #else /* !HAVE_GTK2 */
2729 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2730 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2732 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2733 #endif /* !HAVE_GTK2 */
2734 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2735 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2737 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2740 gtk_tree_view_column_set_visible (use, checkable);
2741 #else /* !HAVE_GTK2 */
2743 gtk_widget_show (use); /* the "Use" column header */
2745 gtk_widget_hide (use);
2749 GtkBin *line = GTK_BIN (kids->data);
2750 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2751 GtkWidget *line_check =
2752 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2755 gtk_widget_show (line_check);
2757 gtk_widget_hide (line_check);
2761 #endif /* !HAVE_GTK2 */
2766 populate_prefs_page (state *s)
2768 saver_preferences *p = &s->prefs;
2770 Bool can_lock_p = True;
2772 /* Disable all the "lock" controls if locking support was not provided
2773 at compile-time, or if running on MacOS. */
2774 # if defined(NO_LOCKING) || defined(__APPLE__)
2779 /* If there is only one screen, the mode menu contains
2780 "random" but not "random-same".
2782 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2783 p->mode = RANDOM_HACKS;
2786 /* The file supports timeouts of less than a minute, but the GUI does
2787 not, so throttle the values to be at least one minute (since "0" is
2788 a bad rounding choice...)
2790 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2793 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2796 # define FMT_MINUTES(NAME,N) \
2797 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2799 # define FMT_SECONDS(NAME,N) \
2800 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2802 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2803 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2804 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2805 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2806 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2807 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2808 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2813 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2814 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2817 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2819 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2820 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2821 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2823 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2824 TOGGLE_ACTIVE ("dpms_quickoff_button", p->dpms_quickoff_p);
2825 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2826 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2827 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2828 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2829 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2830 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2834 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2835 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2836 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2837 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2838 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2841 # undef TOGGLE_ACTIVE
2843 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2844 (p->image_directory ? p->image_directory : ""));
2845 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2847 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2850 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2851 (p->text_literal ? p->text_literal : ""));
2852 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2853 (p->text_file ? p->text_file : ""));
2854 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2855 (p->text_program ? p->text_program : ""));
2856 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2857 (p->text_url ? p->text_url : ""));
2859 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2860 p->tmode == TEXT_LITERAL);
2861 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2862 p->tmode == TEXT_FILE);
2863 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2864 p->tmode == TEXT_FILE);
2865 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2866 p->tmode == TEXT_PROGRAM);
2867 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2868 p->tmode == TEXT_PROGRAM);
2869 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2870 p->tmode == TEXT_URL);
2873 /* Map the `saver_mode' enum to mode menu to values. */
2875 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2878 for (i = 0; i < countof(mode_menu_order); i++)
2879 if (mode_menu_order[i] == p->mode)
2881 gtk_option_menu_set_history (opt, i);
2882 update_list_sensitivity (s);
2886 Bool found_any_writable_cells = False;
2887 Bool fading_possible = False;
2888 Bool dpms_supported = False;
2890 Display *dpy = GDK_DISPLAY();
2891 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2893 for (i = 0; i < nscreens; i++)
2895 Screen *s = ScreenOfDisplay (dpy, i);
2896 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2898 found_any_writable_cells = True;
2903 fading_possible = found_any_writable_cells;
2904 #ifdef HAVE_XF86VMODE_GAMMA
2905 fading_possible = True;
2908 #ifdef HAVE_DPMS_EXTENSION
2910 int op = 0, event = 0, error = 0;
2911 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2912 dpms_supported = True;
2914 #endif /* HAVE_DPMS_EXTENSION */
2917 # define SENSITIZE(NAME,SENSITIVEP) \
2918 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2920 /* Blanking and Locking
2922 SENSITIZE ("lock_button", can_lock_p);
2923 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2924 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2929 SENSITIZE ("dpms_frame", dpms_supported);
2930 SENSITIZE ("dpms_button", dpms_supported);
2931 SENSITIZE ("dpms_quickoff_button", dpms_supported);
2933 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2934 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2935 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2936 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2937 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2938 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2939 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2940 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2941 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2945 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2946 SENSITIZE ("install_button", found_any_writable_cells);
2947 SENSITIZE ("fade_button", fading_possible);
2948 SENSITIZE ("unfade_button", fading_possible);
2950 SENSITIZE ("fade_label", (fading_possible &&
2951 (p->fade_p || p->unfade_p)));
2952 SENSITIZE ("fade_spinbutton", (fading_possible &&
2953 (p->fade_p || p->unfade_p)));
2961 populate_popup_window (state *s)
2963 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2964 char *doc_string = 0;
2966 /* #### not in Gtk 1.2
2967 gtk_label_set_selectable (doc);
2973 free_conf_data (s->cdata);
2978 saver_preferences *p = &s->prefs;
2979 int list_elt = selected_list_element (s);
2980 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2981 ? s->list_elt_to_hack_number[list_elt]
2983 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2986 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2987 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2988 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2989 s->cdata = load_configurator (cmd_line, s->debug_p);
2990 if (s->cdata && s->cdata->widget)
2991 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2996 doc_string = (s->cdata
2997 ? s->cdata->description
2999 # else /* !HAVE_XML */
3000 doc_string = _("Descriptions not available: no XML support compiled in.");
3001 # endif /* !HAVE_XML */
3003 gtk_label_set_text (doc, (doc_string
3005 : _("No description available.")));
3010 sensitize_demo_widgets (state *s, Bool sensitive_p)
3012 const char *names[] = { "demo", "settings",
3013 "cmd_label", "cmd_text", "manual",
3014 "visual", "visual_combo" };
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), sensitive_p);
3025 sensitize_menu_items (state *s, Bool force_p)
3027 static Bool running_p = False;
3028 static time_t last_checked = 0;
3029 time_t now = time ((time_t *) 0);
3030 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
3034 if (force_p || now > last_checked + 10) /* check every 10 seconds */
3036 running_p = xscreensaver_running_p (s);
3037 last_checked = time ((time_t *) 0);
3040 for (i = 0; i < countof(names); i++)
3042 GtkWidget *w = name_to_widget (s, names[i]);
3043 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
3048 /* When the File menu is de-posted after a "Restart Daemon" command,
3049 the window underneath doesn't repaint for some reason. I guess this
3050 is a bug in exposure handling in GTK or GDK. This works around it.
3053 force_dialog_repaint (state *s)
3056 /* Tell GDK to invalidate and repaint the whole window.
3058 GdkWindow *w = GET_WINDOW (s->toplevel_widget);
3059 GdkRegion *region = gdk_region_new ();
3061 rect.x = rect.y = 0;
3062 rect.width = rect.height = 32767;
3063 gdk_region_union_with_rect (region, &rect);
3064 gdk_window_invalidate_region (w, region, True);
3065 gdk_region_destroy (region);
3066 gdk_window_process_updates (w, True);
3068 /* Force the server to send an exposure event by creating and then
3069 destroying a window as a child of the top level shell.
3071 Display *dpy = GDK_DISPLAY();
3072 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
3074 XWindowAttributes xgwa;
3075 XGetWindowAttributes (dpy, parent, &xgwa);
3076 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
3077 XMapRaised (dpy, w);
3078 XDestroyWindow (dpy, w);
3084 /* Even though we've given these text fields a maximum number of characters,
3085 their default size is still about 30 characters wide -- so measure out
3086 a string in their font, and resize them to just fit that.
3089 fix_text_entry_sizes (state *s)
3093 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
3094 const char * const spinbuttons[] = {
3095 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3096 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3097 "dpms_off_spinbutton",
3098 "-fade_spinbutton" };
3102 for (i = 0; i < countof(spinbuttons); i++)
3104 const char *n = spinbuttons[i];
3106 while (*n == '-') n++, cols--;
3107 w = GTK_WIDGET (name_to_widget (s, n));
3108 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3109 gtk_widget_set_usize (w, width, -2);
3112 /* Now fix the width of the combo box.
3114 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3115 w = GTK_COMBO (w)->entry;
3116 width = gdk_string_width (w->style->font, "PseudoColor___");
3117 gtk_widget_set_usize (w, width, -2);
3119 /* Now fix the width of the file entry text.
3121 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3122 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3123 gtk_widget_set_usize (w, width, -2);
3125 /* Now fix the width of the command line text.
3127 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3128 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3129 gtk_widget_set_usize (w, width, -2);
3133 /* Now fix the height of the list widget:
3134 make it default to being around 10 text-lines high instead of 4.
3136 w = GTK_WIDGET (name_to_widget (s, "list"));
3140 int leading = 3; /* approximate is ok... */
3144 PangoFontMetrics *pain =
3145 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3146 gtk_widget_get_style (w)->font_desc,
3147 gtk_get_default_language ());
3148 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3149 pango_font_metrics_get_descent (pain));
3150 #else /* !HAVE_GTK2 */
3151 height = w->style->font->ascent + w->style->font->descent;
3152 #endif /* !HAVE_GTK2 */
3156 height += border * 2;
3157 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3158 gtk_widget_set_usize (w, -2, height);
3165 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3168 static char *up_arrow_xpm[] = {
3191 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3192 the end of the array (Gtk 1.2.5.) */
3193 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3194 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3197 static char *down_arrow_xpm[] = {
3220 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3221 the end of the array (Gtk 1.2.5.) */
3222 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3223 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3227 pixmapify_button (state *s, int down_p)
3231 GtkWidget *pixmapwid;
3235 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3236 style = gtk_widget_get_style (w);
3238 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3239 &style->bg[GTK_STATE_NORMAL],
3241 ? (gchar **) down_arrow_xpm
3242 : (gchar **) up_arrow_xpm));
3243 pixmapwid = gtk_pixmap_new (pixmap, mask);
3244 gtk_widget_show (pixmapwid);
3245 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3246 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3250 map_next_button_cb (GtkWidget *w, gpointer user_data)
3252 state *s = (state *) user_data;
3253 pixmapify_button (s, 1);
3257 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3259 state *s = (state *) user_data;
3260 pixmapify_button (s, 0);
3262 #endif /* !HAVE_GTK2 */
3266 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3270 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3271 GtkAllocation *allocation,
3275 GtkWidgetAuxInfo *aux_info;
3277 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3279 aux_info->width = allocation->width;
3280 aux_info->height = -2;
3284 gtk_widget_size_request (label, &req);
3287 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3290 eschew_gtk_lossage (GtkLabel *label)
3292 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3293 aux_info->width = GTK_WIDGET (label)->allocation.width;
3294 aux_info->height = -2;
3298 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3300 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3301 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3304 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3306 gtk_widget_queue_resize (GTK_WIDGET (label));
3308 #endif /* !HAVE_GTK2 */
3312 populate_demo_window (state *s, int list_elt)
3314 Display *dpy = GDK_DISPLAY();
3315 saver_preferences *p = &s->prefs;
3318 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3319 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3320 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3321 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3322 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3324 if (p->mode == BLANK_ONLY)
3327 pretty_name = strdup (_("Blank Screen"));
3328 schedule_preview (s, 0);
3330 else if (p->mode == DONT_BLANK)
3333 pretty_name = strdup (_("Screen Saver Disabled"));
3334 schedule_preview (s, 0);
3338 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3339 ? s->list_elt_to_hack_number[list_elt]
3341 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3345 ? strdup (hack->name)
3346 : make_hack_name (dpy, hack->command))
3350 schedule_preview (s, hack->command);
3352 schedule_preview (s, 0);
3356 pretty_name = strdup (_("Preview"));
3358 gtk_frame_set_label (frame1, _(pretty_name));
3359 gtk_frame_set_label (frame2, _(pretty_name));
3361 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3362 gtk_entry_set_position (cmd, 0);
3366 sprintf (title, _("%s: %.100s Settings"),
3367 progclass, (pretty_name ? pretty_name : "???"));
3368 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3371 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3373 ? (hack->visual && *hack->visual
3378 sensitize_demo_widgets (s, (hack ? True : False));
3380 if (pretty_name) free (pretty_name);
3382 ensure_selected_item_visible (list);
3384 s->_selected_list_element = list_elt;
3389 widget_deleter (GtkWidget *widget, gpointer data)
3391 /* #### Well, I want to destroy these widgets, but if I do that, they get
3392 referenced again, and eventually I get a SEGV. So instead of
3393 destroying them, I'll just hide them, and leak a bunch of memory
3394 every time the disk file changes. Go go go Gtk!
3396 #### Ok, that's a lie, I get a crash even if I just hide the widget
3397 and don't ever delete it. Fuck!
3400 gtk_widget_destroy (widget);
3402 gtk_widget_hide (widget);
3407 static char **sort_hack_cmp_names_kludge;
3409 sort_hack_cmp (const void *a, const void *b)
3415 int aa = *(int *) a;
3416 int bb = *(int *) b;
3417 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3418 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3419 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3425 initialize_sort_map (state *s)
3427 Display *dpy = GDK_DISPLAY();
3428 saver_preferences *p = &s->prefs;
3431 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3432 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3433 if (s->hacks_available_p) free (s->hacks_available_p);
3435 s->list_elt_to_hack_number = (int *)
3436 calloc (sizeof(int), p->screenhacks_count + 1);
3437 s->hack_number_to_list_elt = (int *)
3438 calloc (sizeof(int), p->screenhacks_count + 1);
3439 s->hacks_available_p = (Bool *)
3440 calloc (sizeof(Bool), p->screenhacks_count + 1);
3441 s->total_available = 0;
3443 /* Check which hacks actually exist on $PATH
3445 for (i = 0; i < p->screenhacks_count; i++)
3447 screenhack *hack = p->screenhacks[i];
3448 int on = on_path_p (hack->command) ? 1 : 0;
3449 s->hacks_available_p[i] = on;
3450 s->total_available += on;
3453 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3457 for (i = 0; i < p->screenhacks_count; i++)
3459 if (!p->ignore_uninstalled_p ||
3460 s->hacks_available_p[i])
3461 s->list_elt_to_hack_number[j++] = i;
3465 for (; j < p->screenhacks_count; j++)
3466 s->list_elt_to_hack_number[j] = -1;
3469 /* Generate list of sortable names (once)
3471 sort_hack_cmp_names_kludge = (char **)
3472 calloc (sizeof(char *), p->screenhacks_count);
3473 for (i = 0; i < p->screenhacks_count; i++)
3475 screenhack *hack = p->screenhacks[i];
3476 char *name = (hack->name && *hack->name
3477 ? strdup (hack->name)
3478 : make_hack_name (dpy, hack->command));
3480 for (str = name; *str; str++)
3481 *str = tolower(*str);
3482 sort_hack_cmp_names_kludge[i] = name;
3485 /* Sort list->hack map alphabetically
3487 qsort (s->list_elt_to_hack_number,
3488 p->screenhacks_count,
3489 sizeof(*s->list_elt_to_hack_number),
3494 for (i = 0; i < p->screenhacks_count; i++)
3495 free (sort_hack_cmp_names_kludge[i]);
3496 free (sort_hack_cmp_names_kludge);
3497 sort_hack_cmp_names_kludge = 0;
3499 /* Build inverse table */
3500 for (i = 0; i < p->screenhacks_count; i++)
3502 int n = s->list_elt_to_hack_number[i];
3504 s->hack_number_to_list_elt[n] = i;
3510 maybe_reload_init_file (state *s)
3512 Display *dpy = GDK_DISPLAY();
3513 saver_preferences *p = &s->prefs;
3516 static Bool reentrant_lock = False;
3517 if (reentrant_lock) return 0;
3518 reentrant_lock = True;
3520 if (init_file_changed_p (p))
3522 const char *f = init_file_name();
3527 if (!f || !*f) return 0;
3528 b = (char *) malloc (strlen(f) + 1024);
3531 "file \"%s\" has changed, reloading.\n"),
3533 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3536 load_init_file (dpy, p);
3537 initialize_sort_map (s);
3539 list_elt = selected_list_element (s);
3540 list = name_to_widget (s, "list");
3541 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3542 populate_hack_list (s);
3543 force_list_select_item (s, list, list_elt, True);
3544 populate_prefs_page (s);
3545 populate_demo_window (s, list_elt);
3546 ensure_selected_item_visible (list);
3551 reentrant_lock = False;
3557 /* Making the preview window have the right X visual (so that GL works.)
3560 static Visual *get_best_gl_visual (state *);
3563 x_visual_to_gdk_visual (Visual *xv)
3565 GList *gvs = gdk_list_visuals();
3566 if (!xv) return gdk_visual_get_system();
3567 for (; gvs; gvs = gvs->next)
3569 GdkVisual *gv = (GdkVisual *) gvs->data;
3570 if (xv == GDK_VISUAL_XVISUAL (gv))
3573 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3574 blurb(), (unsigned long) xv->visualid);
3579 clear_preview_window (state *s)
3585 if (!s->toplevel_widget) return; /* very early */
3586 p = name_to_widget (s, "preview");
3587 window = GET_WINDOW (p);
3589 if (!window) return;
3591 /* Flush the widget background down into the window, in case a subproc
3593 style = gtk_widget_get_style (p);
3594 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
3595 gdk_window_clear (window);
3598 int list_elt = selected_list_element (s);
3599 int hack_number = (list_elt >= 0
3600 ? s->list_elt_to_hack_number[list_elt]
3602 Bool available_p = (hack_number >= 0
3603 ? s->hacks_available_p [hack_number]
3605 Bool nothing_p = (s->total_available < 5);
3608 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3609 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3610 (s->running_preview_error_p
3611 ? (available_p ? 1 :
3614 #else /* !HAVE_GTK2 */
3615 if (s->running_preview_error_p)
3617 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3618 const char * const lines2[] = { N_("Not"), N_("Installed") };
3619 int nlines = countof(lines1);
3620 int lh = p->style->font->ascent + p->style->font->descent;
3624 const char * const *lines = (available_p ? lines1 : lines2);
3626 gdk_window_get_size (window, &w, &h);
3627 y = (h - (lh * nlines)) / 2;
3628 y += p->style->font->ascent;
3629 for (i = 0; i < nlines; i++)
3631 int sw = gdk_string_width (p->style->font, _(lines[i]));
3632 int x = (w - sw) / 2;
3633 gdk_draw_string (window, p->style->font,
3634 p->style->fg_gc[GTK_STATE_NORMAL],
3639 #endif /* !HAVE_GTK2 */
3647 reset_preview_window (state *s)
3649 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3650 when you kill one and re-start another on the same window. So maybe
3651 it's best to just always destroy and recreate the preview window
3652 when changing hacks, instead of always trying to reuse the same one?
3654 GtkWidget *pr = name_to_widget (s, "preview");
3655 if (GET_REALIZED (pr))
3657 GdkWindow *window = GET_WINDOW (pr);
3658 Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3660 gtk_widget_hide (pr);
3661 gtk_widget_unrealize (pr);
3662 gtk_widget_realize (pr);
3663 gtk_widget_show (pr);
3664 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3666 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3674 fix_preview_visual (state *s)
3676 GtkWidget *widget = name_to_widget (s, "preview");
3677 Visual *xvisual = get_best_gl_visual (s);
3678 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3679 GdkVisual *dvisual = gdk_visual_get_system();
3680 GdkColormap *cmap = (visual == dvisual
3681 ? gdk_colormap_get_system ()
3682 : gdk_colormap_new (visual, False));
3685 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3686 (visual == dvisual ? "default" : "non-default"),
3687 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3689 if (!GET_REALIZED (widget) ||
3690 gtk_widget_get_visual (widget) != visual)
3692 gtk_widget_unrealize (widget);
3693 gtk_widget_set_visual (widget, visual);
3694 gtk_widget_set_colormap (widget, cmap);
3695 gtk_widget_realize (widget);
3698 /* Set the Widget colors to be white-on-black. */
3700 GdkWindow *window = GET_WINDOW (widget);
3701 GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
3702 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3703 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3704 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3705 GdkGC *fgc = gdk_gc_new(window);
3706 GdkGC *bgc = gdk_gc_new(window);
3707 if (!gdk_color_white (cmap, fg)) abort();
3708 if (!gdk_color_black (cmap, bg)) abort();
3709 gdk_gc_set_foreground (fgc, fg);
3710 gdk_gc_set_background (fgc, bg);
3711 gdk_gc_set_foreground (bgc, bg);
3712 gdk_gc_set_background (bgc, fg);
3713 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3714 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3715 gtk_widget_set_style (widget, style);
3717 /* For debugging purposes, put a title on the window (so that
3718 it can be easily found in the output of "xwininfo -tree".)
3720 gdk_window_set_title (window, "Preview");
3723 gtk_widget_show (widget);
3731 subproc_pretty_name (state *s)
3733 if (s->running_preview_cmd)
3735 char *ps = strdup (s->running_preview_cmd);
3736 char *ss = strchr (ps, ' ');
3738 ss = strrchr (ps, '/');
3749 return strdup ("???");
3754 reap_zombies (state *s)
3756 int wait_status = 0;
3758 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3762 if (pid == s->running_preview_pid)
3764 char *ss = subproc_pretty_name (s);
3765 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3766 (unsigned long) pid, ss);
3770 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3771 (unsigned long) pid);
3777 /* Mostly lifted from driver/subprocs.c */
3779 get_best_gl_visual (state *s)
3781 Display *dpy = GDK_DISPLAY();
3790 av[ac++] = "xscreensaver-gl-helper";
3795 perror ("error creating pipe:");
3802 switch ((int) (forked = fork ()))
3806 sprintf (buf, "%s: couldn't fork", blurb());
3814 close (in); /* don't need this one */
3815 close (ConnectionNumber (dpy)); /* close display fd */
3817 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3819 perror ("could not dup() a new stdout:");
3823 execvp (av[0], av); /* shouldn't return. */
3825 if (errno != ENOENT)
3827 /* Ignore "no such file or directory" errors, unless verbose.
3828 Issue all other exec errors, though. */
3829 sprintf (buf, "%s: running %s", blurb(), av[0]);
3833 /* Note that one must use _exit() instead of exit() in procs forked
3834 off of Gtk programs -- Gtk installs an atexit handler that has a
3835 copy of the X connection (which we've already closed, for safety.)
3836 If one uses exit() instead of _exit(), then one sometimes gets a
3837 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3839 _exit (1); /* exits fork */
3845 int wait_status = 0;
3847 FILE *f = fdopen (in, "r");
3851 close (out); /* don't need this one */
3854 if (!fgets (buf, sizeof(buf)-1, f))
3858 /* Wait for the child to die. */
3859 waitpid (-1, &wait_status, 0);
3861 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3867 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3873 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3875 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3876 blurb(), av[0], result);
3888 kill_preview_subproc (state *s, Bool reset_p)
3890 s->running_preview_error_p = False;
3893 clear_preview_window (s);
3895 if (s->subproc_check_timer_id)
3897 gtk_timeout_remove (s->subproc_check_timer_id);
3898 s->subproc_check_timer_id = 0;
3899 s->subproc_check_countdown = 0;
3902 if (s->running_preview_pid)
3904 int status = kill (s->running_preview_pid, SIGTERM);
3905 char *ss = subproc_pretty_name (s);
3912 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3913 blurb(), (unsigned long) s->running_preview_pid, ss);
3918 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3919 blurb(), (unsigned long) s->running_preview_pid, ss);
3925 waitpid(s->running_preview_pid, &endstatus, 0);
3927 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3928 (unsigned long) s->running_preview_pid, ss);
3932 s->running_preview_pid = 0;
3933 if (s->running_preview_cmd) free (s->running_preview_cmd);
3934 s->running_preview_cmd = 0;
3941 reset_preview_window (s);
3942 clear_preview_window (s);
3947 /* Immediately and unconditionally launches the given process,
3948 after appending the -window-id option; sets running_preview_pid.
3951 launch_preview_subproc (state *s)
3953 saver_preferences *p = &s->prefs;
3957 const char *cmd = s->desired_preview_cmd;
3959 GtkWidget *pr = name_to_widget (s, "preview");
3962 reset_preview_window (s);
3964 window = GET_WINDOW (pr);
3966 s->running_preview_error_p = False;
3968 if (s->preview_suppressed_p)
3970 kill_preview_subproc (s, False);
3974 new_cmd = malloc (strlen (cmd) + 40);
3976 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3979 /* No window id? No command to run. */
3985 strcpy (new_cmd, cmd);
3986 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3990 kill_preview_subproc (s, False);
3993 s->running_preview_error_p = True;
3994 clear_preview_window (s);
3998 switch ((int) (forked = fork ()))
4003 sprintf (buf, "%s: couldn't fork", blurb());
4005 s->running_preview_error_p = True;
4011 close (ConnectionNumber (GDK_DISPLAY()));
4013 hack_subproc_environment (id, s->debug_p);
4015 usleep (250000); /* pause for 1/4th second before launching, to give
4016 the previous program time to die and flush its X
4017 buffer, so we don't get leftover turds on the
4020 exec_command (p->shell, new_cmd, p->nice_inferior);
4021 /* Don't bother printing an error message when we are unable to
4022 exec subprocesses; we handle that by polling the pid later.
4024 Note that one must use _exit() instead of exit() in procs forked
4025 off of Gtk programs -- Gtk installs an atexit handler that has a
4026 copy of the X connection (which we've already closed, for safety.)
4027 If one uses exit() instead of _exit(), then one sometimes gets a
4028 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
4030 _exit (1); /* exits child fork */
4035 if (s->running_preview_cmd) free (s->running_preview_cmd);
4036 s->running_preview_cmd = strdup (s->desired_preview_cmd);
4037 s->running_preview_pid = forked;
4041 char *ss = subproc_pretty_name (s);
4042 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
4043 (unsigned long) forked, ss);
4050 schedule_preview_check (s);
4053 if (new_cmd) free (new_cmd);
4058 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
4061 hack_environment (state *s)
4063 static const char *def_path =
4064 # ifdef DEFAULT_PATH_PREFIX
4065 DEFAULT_PATH_PREFIX;
4070 Display *dpy = GDK_DISPLAY();
4071 const char *odpy = DisplayString (dpy);
4072 char *ndpy = (char *) malloc(strlen(odpy) + 20);
4073 strcpy (ndpy, "DISPLAY=");
4074 strcat (ndpy, odpy);
4079 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4081 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4082 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4083 So we must leak it (and/or the previous setting). Yay.
4086 if (def_path && *def_path)
4088 const char *opath = getenv("PATH");
4089 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4090 strcpy (npath, "PATH=");
4091 strcat (npath, def_path);
4092 strcat (npath, ":");
4093 strcat (npath, opath);
4097 /* do not free(npath) -- see above */
4100 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4106 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4108 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4109 necessary yet, but it will make programs work if we had invoked
4110 them with "-root" and not with "-window-id" -- which, of course,
4113 char *nssw = (char *) malloc (40);
4114 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4116 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4117 any more, right? It's not Posix, but everyone seems to have it. */
4122 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4124 /* do not free(nssw) -- see above */
4128 /* Called from a timer:
4129 Launches the currently-chosen subprocess, if it's not already running.
4130 If there's a different process running, kills it.
4133 update_subproc_timer (gpointer data)
4135 state *s = (state *) data;
4136 if (! s->desired_preview_cmd)
4137 kill_preview_subproc (s, True);
4138 else if (!s->running_preview_cmd ||
4139 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4140 launch_preview_subproc (s);
4142 s->subproc_timer_id = 0;
4143 return FALSE; /* do not re-execute timer */
4147 settings_timer (gpointer data)
4154 /* Call this when you think you might want a preview process running.
4155 It will set a timer that will actually launch that program a second
4156 from now, if you haven't changed your mind (to avoid double-click
4157 spazzing, etc.) `cmd' may be null meaning "no process".
4160 schedule_preview (state *s, const char *cmd)
4162 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4167 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4169 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4172 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4173 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4175 if (s->subproc_timer_id)
4176 gtk_timeout_remove (s->subproc_timer_id);
4177 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4181 /* Called from a timer:
4182 Checks to see if the subproc that should be running, actually is.
4185 check_subproc_timer (gpointer data)
4187 state *s = (state *) data;
4188 Bool again_p = True;
4190 if (s->running_preview_error_p || /* already dead */
4191 s->running_preview_pid <= 0)
4199 status = kill (s->running_preview_pid, 0);
4200 if (status < 0 && errno == ESRCH)
4201 s->running_preview_error_p = True;
4205 char *ss = subproc_pretty_name (s);
4206 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4207 (unsigned long) s->running_preview_pid, ss,
4208 (s->running_preview_error_p ? "dead" : "alive"));
4212 if (s->running_preview_error_p)
4214 clear_preview_window (s);
4219 /* Otherwise, it's currently alive. We might be checking again, or we
4220 might be satisfied. */
4222 if (--s->subproc_check_countdown <= 0)
4226 return TRUE; /* re-execute timer */
4229 s->subproc_check_timer_id = 0;
4230 s->subproc_check_countdown = 0;
4231 return FALSE; /* do not re-execute timer */
4236 /* Call this just after launching a subprocess.
4237 This sets a timer that will, five times a second for two seconds,
4238 check whether the program is still running. The assumption here
4239 is that if the process didn't stay up for more than a couple of
4240 seconds, then either the program doesn't exist, or it doesn't
4241 take a -window-id argument.
4244 schedule_preview_check (state *s)
4250 fprintf (stderr, "%s: scheduling check\n", blurb());
4252 if (s->subproc_check_timer_id)
4253 gtk_timeout_remove (s->subproc_check_timer_id);
4254 s->subproc_check_timer_id =
4255 gtk_timeout_add (1000 / ticks,
4256 check_subproc_timer, (gpointer) s);
4257 s->subproc_check_countdown = ticks * seconds;
4262 screen_blanked_p (void)
4266 unsigned long nitems, bytesafter;
4267 unsigned char *dataP = 0;
4268 Display *dpy = GDK_DISPLAY();
4269 Bool blanked_p = False;
4271 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4272 XA_SCREENSAVER_STATUS,
4273 0, 3, False, XA_INTEGER,
4274 &type, &format, &nitems, &bytesafter,
4277 && type == XA_INTEGER
4281 Atom *data = (Atom *) dataP;
4282 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4285 if (dataP) XFree (dataP);
4290 /* Wake up every now and then and see if the screen is blanked.
4291 If it is, kill off the small-window demo -- no point in wasting
4292 cycles by running two screensavers at once...
4295 check_blanked_timer (gpointer data)
4297 state *s = (state *) data;
4298 Bool blanked_p = screen_blanked_p ();
4299 if (blanked_p && s->running_preview_pid)
4302 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4303 kill_preview_subproc (s, True);
4306 return True; /* re-execute timer */
4310 /* How many screens are there (including Xinerama.)
4313 screen_count (Display *dpy)
4315 int nscreens = ScreenCount(dpy);
4316 # ifdef HAVE_XINERAMA
4319 int event_number, error_number;
4320 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4321 XineramaIsActive (dpy))
4323 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4324 if (xsi) XFree (xsi);
4327 # endif /* HAVE_XINERAMA */
4333 /* Setting window manager icon
4337 init_icon (GdkWindow *window)
4339 GdkBitmap *mask = 0;
4341 gdk_pixmap_create_from_xpm_d (window, &mask, 0,
4342 (gchar **) logo_50_xpm);
4344 gdk_window_set_icon (window, 0, pixmap, mask);
4348 /* The main demo-mode command loop.
4353 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4354 XrmRepresentation *type, XrmValue *value, XPointer closure)
4357 for (i = 0; quarks[i]; i++)
4359 if (bindings[i] == XrmBindTightly)
4360 fprintf (stderr, (i == 0 ? "" : "."));
4361 else if (bindings[i] == XrmBindLoosely)
4362 fprintf (stderr, "*");
4364 fprintf (stderr, " ??? ");
4365 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4368 fprintf (stderr, ": %s\n", (char *) value->addr);
4376 gnome_screensaver_window (Screen *screen)
4378 Display *dpy = DisplayOfScreen (screen);
4379 Window root = RootWindowOfScreen (screen);
4380 Window parent, *kids;
4382 Window gnome_window = 0;
4385 if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4387 for (i = 0; i < nkids; i++)
4391 unsigned long nitems, bytesafter;
4392 unsigned char *name;
4393 if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4394 False, XA_STRING, &type, &format, &nitems,
4398 && !strcmp ((char *) name, "gnome-screensaver"))
4400 gnome_window = kids[i];
4405 if (kids) XFree ((char *) kids);
4406 return gnome_window;
4410 gnome_screensaver_active_p (void)
4412 Display *dpy = GDK_DISPLAY();
4413 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4414 return (w ? True : False);
4418 kill_gnome_screensaver (void)
4420 Display *dpy = GDK_DISPLAY();
4421 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4422 if (w) XKillClient (dpy, (XID) w);
4426 kde_screensaver_active_p (void)
4428 FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4431 fgets (buf, sizeof(buf)-1, p);
4433 if (!strcmp (buf, "true\n"))
4440 kill_kde_screensaver (void)
4442 system ("dcop kdesktop KScreensaverIface enable false");
4447 the_network_is_not_the_computer (state *s)
4449 Display *dpy = GDK_DISPLAY();
4450 char *rversion = 0, *ruser = 0, *rhost = 0;
4451 char *luser, *lhost;
4453 struct passwd *p = getpwuid (getuid ());
4454 const char *d = DisplayString (dpy);
4456 # if defined(HAVE_UNAME)
4458 if (uname (&uts) < 0)
4459 lhost = "<UNKNOWN>";
4461 lhost = uts.nodename;
4463 strcpy (lhost, getenv("SYS$NODE"));
4464 # else /* !HAVE_UNAME && !VMS */
4465 strcat (lhost, "<UNKNOWN>");
4466 # endif /* !HAVE_UNAME && !VMS */
4468 if (p && p->pw_name)
4473 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4475 /* Make a buffer that's big enough for a number of copies of all the
4476 strings, plus some. */
4477 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4478 (ruser ? strlen(ruser) : 0) +
4479 (rhost ? strlen(rhost) : 0) +
4486 if (!rversion || !*rversion)
4490 "The XScreenSaver daemon doesn't seem to be running\n"
4491 "on display \"%s\". Launch it now?"),
4494 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4496 /* Warn that the two processes are running as different users.
4500 "%s is running as user \"%s\" on host \"%s\".\n"
4501 "But the xscreensaver managing display \"%s\"\n"
4502 "is running as user \"%s\" on host \"%s\".\n"
4504 "Since they are different users, they won't be reading/writing\n"
4505 "the same ~/.xscreensaver file, so %s isn't\n"
4506 "going to work right.\n"
4508 "You should either re-run %s as \"%s\", or re-run\n"
4509 "xscreensaver as \"%s\".\n"
4511 "Restart the xscreensaver daemon now?\n"),
4512 progname, luser, lhost,
4514 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4516 progname, (ruser ? ruser : "???"),
4519 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4521 /* Warn that the two processes are running on different hosts.
4525 "%s is running as user \"%s\" on host \"%s\".\n"
4526 "But the xscreensaver managing display \"%s\"\n"
4527 "is running as user \"%s\" on host \"%s\".\n"
4529 "If those two machines don't share a file system (that is,\n"
4530 "if they don't see the same ~%s/.xscreensaver file) then\n"
4531 "%s won't work right.\n"
4533 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4534 progname, luser, lhost,
4536 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4541 else if (!!strcmp (rversion, s->short_version))
4543 /* Warn that the version numbers don't match.
4547 "This is %s version %s.\n"
4548 "But the xscreensaver managing display \"%s\"\n"
4549 "is version %s. This could cause problems.\n"
4551 "Restart the xscreensaver daemon now?\n"),
4552 progname, s->short_version,
4559 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4561 if (rversion) free (rversion);
4562 if (ruser) free (ruser);
4563 if (rhost) free (rhost);
4567 /* Note: since these dialogs are not modal, they will stack up.
4568 So we do this check *after* popping up the "xscreensaver is not
4569 running" dialog so that these are on top. Good enough.
4572 if (gnome_screensaver_active_p ())
4573 warning_dialog (s->toplevel_widget,
4575 "The GNOME screensaver daemon appears to be running.\n"
4576 "It must be stopped for XScreenSaver to work properly.\n"
4578 "Stop the GNOME screen saver daemon now?\n"),
4581 if (kde_screensaver_active_p ())
4582 warning_dialog (s->toplevel_widget,
4584 "The KDE screen saver daemon appears to be running.\n"
4585 "It must be stopped for XScreenSaver to work properly.\n"
4587 "Stop the KDE screen saver daemon now?\n"),
4592 /* We use this error handler so that X errors are preceeded by the name
4593 of the program that generated them.
4596 demo_ehandler (Display *dpy, XErrorEvent *error)
4598 state *s = global_state_kludge; /* I hate C so much... */
4599 fprintf (stderr, "\nX error in %s:\n", blurb());
4600 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4601 kill_preview_subproc (s, False);
4607 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4608 of the program that generated them; and also that we can ignore one
4609 particular bogus error message that Gdk madly spews.
4612 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4613 const gchar *message, gpointer user_data)
4615 /* Ignore the message "Got event for unknown window: 0x...".
4616 Apparently some events are coming in for the xscreensaver window
4617 (presumably reply events related to the ClientMessage) and Gdk
4618 feels the need to complain about them. So, just suppress any
4619 messages that look like that one.
4621 if (strstr (message, "unknown window"))
4624 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4625 (log_domain ? log_domain : progclass),
4626 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4627 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4628 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4629 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4630 log_level == G_LOG_LEVEL_INFO ? "info" :
4631 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4633 ((!*message || message[strlen(message)-1] != '\n')
4639 __extension__ /* shut up about "string length is greater than the length
4640 ISO C89 compilers are required to support" when including
4645 static char *defaults[] = {
4646 #include "XScreenSaver_ad.h"
4651 #ifdef HAVE_CRAPPLET
4652 static struct poptOption crapplet_options[] = {
4653 {NULL, '\0', 0, NULL, 0}
4655 #endif /* HAVE_CRAPPLET */
4658 const char *usage = "[--display dpy] [--prefs | --settings]"
4659 # ifdef HAVE_CRAPPLET
4662 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4665 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4667 state *s = (state *) user_data;
4668 Boolean oi = s->initializing_p;
4670 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4672 s->initializing_p = True;
4674 eschew_gtk_lossage (label);
4676 s->initializing_p = oi;
4682 print_widget_tree (GtkWidget *w, int depth)
4685 for (i = 0; i < depth; i++)
4686 fprintf (stderr, " ");
4687 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4689 if (GTK_IS_LIST (w))
4691 for (i = 0; i < depth+1; i++)
4692 fprintf (stderr, " ");
4693 fprintf (stderr, "...list kids...\n");
4695 else if (GTK_IS_CONTAINER (w))
4697 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4700 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4708 delayed_scroll_kludge (gpointer data)
4710 state *s = (state *) data;
4711 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4712 ensure_selected_item_visible (w);
4714 /* Oh, this is just fucking lovely, too. */
4715 w = GTK_WIDGET (name_to_widget (s, "preview"));
4716 gtk_widget_hide (w);
4717 gtk_widget_show (w);
4719 return FALSE; /* do not re-execute timer */
4725 create_xscreensaver_demo (void)
4729 nb = name_to_widget (global_state_kludge, "preview_notebook");
4730 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4732 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4736 create_xscreensaver_settings_dialog (void)
4740 box = name_to_widget (global_state_kludge, "dialog_action_area");
4742 w = name_to_widget (global_state_kludge, "adv_button");
4743 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4745 w = name_to_widget (global_state_kludge, "std_button");
4746 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4748 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4751 #endif /* HAVE_GTK2 */
4754 main (int argc, char **argv)
4758 saver_preferences *p;
4759 Bool prefs_p = False;
4760 Bool settings_p = False;
4763 Widget toplevel_shell;
4764 char *real_progname = argv[0];
4767 Bool crapplet_p = False;
4771 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4772 textdomain (GETTEXT_PACKAGE);
4775 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4776 # else /* !HAVE_GTK2 */
4777 if (!setlocale (LC_ALL, ""))
4778 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4779 # endif /* !HAVE_GTK2 */
4781 #endif /* ENABLE_NLS */
4783 str = strrchr (real_progname, '/');
4784 if (str) real_progname = str+1;
4787 memset (s, 0, sizeof(*s));
4788 s->initializing_p = True;
4791 global_state_kludge = s; /* I hate C so much... */
4793 progname = real_progname;
4795 s->short_version = (char *) malloc (5);
4796 memcpy (s->short_version, screensaver_id + 17, 4);
4797 s->short_version [4] = 0;
4800 /* Register our error message logger for every ``log domain'' known.
4801 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4802 for all of the domains that seem to be in use.
4805 const char * const domains[] = { 0,
4806 "Gtk", "Gdk", "GLib", "GModule",
4807 "GThread", "Gnome", "GnomeUI" };
4808 for (i = 0; i < countof(domains); i++)
4809 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4812 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4815 const char *dir = DEFAULT_ICONDIR;
4816 if (*dir) add_pixmap_directory (dir);
4818 # endif /* !HAVE_GTK2 */
4819 #endif /* DEFAULT_ICONDIR */
4821 /* This is gross, but Gtk understands --display and not -display...
4823 for (i = 1; i < argc; i++)
4824 if (argv[i][0] && argv[i][1] &&
4825 !strncmp(argv[i], "-display", strlen(argv[i])))
4826 argv[i] = "--display";
4829 /* We need to parse this arg really early... Sigh. */
4830 for (i = 1; i < argc; i++)
4833 (!strcmp(argv[i], "--crapplet") ||
4834 !strcmp(argv[i], "--capplet")))
4836 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4839 for (j = i; j < argc; j++) /* remove it from the list */
4840 argv[j] = argv[j+1];
4842 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4843 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4845 fprintf (stderr, "%s: %s\n", real_progname, usage);
4847 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4850 (!strcmp(argv[i], "--debug") ||
4851 !strcmp(argv[i], "-debug") ||
4852 !strcmp(argv[i], "-d")))
4856 for (j = i; j < argc; j++) /* remove it from the list */
4857 argv[j] = argv[j+1];
4864 (!strcmp(argv[i], "-geometry") ||
4865 !strcmp(argv[i], "-geom") ||
4866 !strcmp(argv[i], "-geo") ||
4867 !strcmp(argv[i], "-g")))
4871 for (j = i; j < argc; j++) /* remove them from the list */
4872 argv[j] = argv[j+2];
4879 (!strcmp(argv[i], "--configdir")))
4883 hack_configuration_path = argv[i+1];
4884 for (j = i; j < argc; j++) /* remove them from the list */
4885 argv[j] = argv[j+2];
4889 if (0 != stat (hack_configuration_path, &st))
4892 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4896 else if (!S_ISDIR (st.st_mode))
4898 fprintf (stderr, "%s: not a directory: %s\n",
4899 blurb(), hack_configuration_path);
4907 fprintf (stderr, "%s: using config directory \"%s\"\n",
4908 progname, hack_configuration_path);
4911 /* Let Gtk open the X connection, then initialize Xt to use that
4912 same connection. Doctor Frankenstein would be proud.
4914 # ifdef HAVE_CRAPPLET
4917 GnomeClient *client;
4918 GnomeClientFlags flags = 0;
4920 int init_results = gnome_capplet_init ("screensaver-properties",
4922 argc, argv, NULL, 0, NULL);
4924 0 upon successful initialization;
4925 1 if --init-session-settings was passed on the cmdline;
4926 2 if --ignore was passed on the cmdline;
4929 So the 1 signifies just to init the settings, and quit, basically.
4930 (Meaning launch the xscreensaver daemon.)
4933 if (init_results < 0)
4936 g_error ("An initialization error occurred while "
4937 "starting xscreensaver-capplet.\n");
4939 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4940 real_progname, init_results);
4945 client = gnome_master_client ();
4948 flags = gnome_client_get_flags (client);
4950 if (flags & GNOME_CLIENT_IS_CONNECTED)
4953 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4954 gnome_client_get_id (client));
4957 char *session_args[20];
4959 session_args[i++] = real_progname;
4960 session_args[i++] = "--capplet";
4961 session_args[i++] = "--init-session-settings";
4962 session_args[i] = 0;
4963 gnome_client_set_priority (client, 20);
4964 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4965 gnome_client_set_restart_command (client, i, session_args);
4969 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4972 gnome_client_flush (client);
4975 if (init_results == 1)
4977 system ("xscreensaver -nosplash &");
4983 # endif /* HAVE_CRAPPLET */
4985 gtk_init (&argc, &argv);
4989 /* We must read exactly the same resources as xscreensaver.
4990 That means we must have both the same progclass *and* progname,
4991 at least as far as the resource database is concerned. So,
4992 put "xscreensaver" in argv[0] while initializing Xt.
4994 argv[0] = "xscreensaver";
4998 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
5000 XtToolkitInitialize ();
5001 app = XtCreateApplicationContext ();
5002 dpy = GDK_DISPLAY();
5003 XtAppSetFallbackResources (app, defaults);
5004 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
5005 toplevel_shell = XtAppCreateShell (progname, progclass,
5006 applicationShellWidgetClass,
5009 dpy = XtDisplay (toplevel_shell);
5010 db = XtDatabase (dpy);
5011 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
5012 XSetErrorHandler (demo_ehandler);
5014 /* Let's just ignore these. They seem to confuse Irix Gtk... */
5015 signal (SIGPIPE, SIG_IGN);
5017 /* After doing Xt-style command-line processing, complain about any
5018 unrecognized command-line arguments.
5020 for (i = 1; i < argc; i++)
5022 char *str = argv[i];
5023 if (str[0] == '-' && str[1] == '-')
5025 if (!strcmp (str, "-prefs"))
5027 else if (!strcmp (str, "-settings"))
5029 else if (crapplet_p)
5030 /* There are lots of random args that we don't care about when we're
5031 started as a crapplet, so just ignore unknown args in that case. */
5035 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
5037 fprintf (stderr, "%s: %s\n", real_progname, usage);
5042 /* Load the init file, which may end up consulting the X resource database
5043 and the site-wide app-defaults file. Note that at this point, it's
5044 important that `progname' be "xscreensaver", rather than whatever
5048 s->nscreens = screen_count (dpy);
5050 hack_environment (s); /* must be before initialize_sort_map() */
5052 load_init_file (dpy, p);
5053 initialize_sort_map (s);
5055 /* Now that Xt has been initialized, and the resources have been read,
5056 we can set our `progname' variable to something more in line with
5059 progname = real_progname;
5063 /* Print out all the resources we read. */
5065 XrmName name = { 0 };
5066 XrmClass class = { 0 };
5068 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
5074 /* Intern the atoms that xscreensaver_command() needs.
5076 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
5077 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
5078 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
5079 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5080 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5081 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5082 XA_SELECT = XInternAtom (dpy, "SELECT", False);
5083 XA_DEMO = XInternAtom (dpy, "DEMO", False);
5084 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5085 XA_BLANK = XInternAtom (dpy, "BLANK", False);
5086 XA_LOCK = XInternAtom (dpy, "LOCK", False);
5087 XA_EXIT = XInternAtom (dpy, "EXIT", False);
5088 XA_RESTART = XInternAtom (dpy, "RESTART", False);
5091 /* Create the window and all its widgets.
5093 s->base_widget = create_xscreensaver_demo ();
5094 s->popup_widget = create_xscreensaver_settings_dialog ();
5095 s->toplevel_widget = s->base_widget;
5098 /* Set the main window's title. */
5100 char *base_title = _("Screensaver Preferences");
5101 char *v = (char *) strdup(strchr(screensaver_id, ' '));
5102 char *s1, *s2, *s3, *s4;
5103 s1 = (char *) strchr(v, ' '); s1++;
5104 s2 = (char *) strchr(s1, ' ');
5105 s3 = (char *) strchr(v, '('); s3++;
5106 s4 = (char *) strchr(s3, ')');
5110 window_title = (char *) malloc (strlen (base_title) +
5111 strlen (progclass) +
5112 strlen (s1) + strlen (s3) +
5114 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
5115 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5116 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
5120 /* Adjust the (invisible) notebooks on the popup dialog... */
5122 GtkNotebook *notebook =
5123 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5124 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5128 gtk_widget_hide (std);
5129 # else /* !HAVE_XML */
5130 /* Make the advanced page be the only one available. */
5131 gtk_widget_set_sensitive (std, False);
5132 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5133 gtk_widget_hide (std);
5134 std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5135 gtk_widget_hide (std);
5137 # endif /* !HAVE_XML */
5139 gtk_notebook_set_page (notebook, page);
5140 gtk_notebook_set_show_tabs (notebook, False);
5143 /* Various other widget initializations...
5145 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5146 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5148 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5149 GTK_SIGNAL_FUNC (wm_popup_close_cb),
5152 populate_hack_list (s);
5153 populate_prefs_page (s);
5154 sensitize_demo_widgets (s, False);
5155 fix_text_entry_sizes (s);
5156 scroll_to_current_hack (s);
5158 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5159 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5163 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5164 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5166 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5167 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5169 #endif /* !HAVE_GTK2 */
5171 /* Hook up callbacks to the items on the mode menu. */
5173 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5174 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5175 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5177 for (i = 0; kids; kids = kids->next, i++)
5179 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5180 GTK_SIGNAL_FUNC (mode_menu_item_cb),
5183 /* The "random-same" mode menu item does not appear unless
5184 there are multple screens.
5186 if (s->nscreens <= 1 &&
5187 mode_menu_order[i] == RANDOM_HACKS_SAME)
5188 gtk_widget_hide (GTK_WIDGET (kids->data));
5191 if (s->nscreens <= 1) /* recompute option-menu size */
5193 gtk_widget_unrealize (GTK_WIDGET (menu));
5194 gtk_widget_realize (GTK_WIDGET (menu));
5199 /* Handle the -prefs command-line argument. */
5202 GtkNotebook *notebook =
5203 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5204 gtk_notebook_set_page (notebook, 1);
5207 # ifdef HAVE_CRAPPLET
5211 GtkWidget *outer_vbox;
5213 gtk_widget_hide (s->toplevel_widget);
5215 capplet = capplet_widget_new ();
5217 /* Make there be a "Close" button instead of "OK" and "Cancel" */
5218 # ifdef HAVE_CRAPPLET_IMMEDIATE
5219 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5220 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5221 /* In crapplet-mode, take off the menubar. */
5222 gtk_widget_hide (name_to_widget (s, "menubar"));
5224 /* Reparent our top-level container to be a child of the capplet
5227 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5228 gtk_widget_ref (outer_vbox);
5229 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5231 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5232 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5234 /* Find the window above us, and set the title and close handler. */
5236 GtkWidget *window = capplet;
5237 while (window && !GTK_IS_WINDOW (window))
5238 window = GET_PARENT (window);
5241 gtk_window_set_title (GTK_WINDOW (window), window_title);
5242 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5243 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5248 s->toplevel_widget = capplet;
5250 # endif /* HAVE_CRAPPLET */
5253 /* The Gnome folks hate the menubar. I think it's important to have access
5254 to the commands on the File menu (Restart Daemon, etc.) and to the
5255 About and Documentation commands on the Help menu.
5259 gtk_widget_hide (name_to_widget (s, "menubar"));
5263 free (window_title);
5267 /* After picking the default size, allow -geometry to override it. */
5269 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5272 gtk_widget_show (s->toplevel_widget);
5273 init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */
5274 fix_preview_visual (s);
5276 /* Realize page zero, so that we can diddle the scrollbar when the
5277 user tabs back to it -- otherwise, the current hack isn't scrolled
5278 to the first time they tab back there, when started with "-prefs".
5279 (Though it is if they then tab away, and back again.)
5281 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5282 #### understands this crap, explain to me how to make this work.
5284 gtk_widget_realize (name_to_widget (s, "demos_table"));
5287 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5290 /* Handle the --settings command-line argument. */
5292 gtk_timeout_add (500, settings_timer, 0);
5295 /* Issue any warnings about the running xscreensaver daemon. */
5297 the_network_is_not_the_computer (s);
5301 warning_dialog (s->toplevel_widget,
5303 "This version of xscreensaver is VERY OLD!\n"
5306 "http://www.jwz.org/xscreensaver/\n"
5308 "(If this is the latest version that your distro ships, then\n"
5309 "your distro is doing you a disservice. Build from source.)\n"
5314 /* Run the Gtk event loop, and not the Xt event loop. This means that
5315 if there were Xt timers or fds registered, they would never get serviced,
5316 and if there were any Xt widgets, they would never have events delivered.
5317 Fortunately, we're using Gtk for all of the UI, and only initialized
5318 Xt so that we could process the command line and use the X resource
5321 s->initializing_p = False;
5323 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5324 after we start up. Otherwise, it always appears scrolled to the top
5325 when in crapplet-mode. */
5326 gtk_timeout_add (500, delayed_scroll_kludge, s);
5330 /* Load every configurator in turn, to scan them for errors all at once. */
5334 for (i = 0; i < p->screenhacks_count; i++)
5336 screenhack *hack = p->screenhacks[i];
5337 conf_data *d = load_configurator (hack->command, s->debug_p);
5338 if (d) free_conf_data (d);
5344 # ifdef HAVE_CRAPPLET
5346 capplet_gtk_main ();
5348 # endif /* HAVE_CRAPPLET */
5351 kill_preview_subproc (s, False);
5355 #endif /* HAVE_GTK -- whole file */