1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2012 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #ifdef HAVE_GTK /* whole file */
19 #include <xscreensaver-intl.h>
28 # define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */
36 #endif /* ENABLE_NLS */
39 # include <pwd.h> /* for getpwuid() */
45 # include <sys/utsname.h> /* for uname() */
46 #endif /* HAVE_UNAME */
56 #ifdef HAVE_SYS_WAIT_H
57 # include <sys/wait.h> /* for waitpid() and associated macros */
61 #include <X11/Xproto.h> /* for CARD32 */
62 #include <X11/Xatom.h> /* for XA_INTEGER */
63 #include <X11/Intrinsic.h>
64 #include <X11/StringDefs.h>
66 /* We don't actually use any widget internals, but these are included
67 so that gdb will have debug info for the widgets... */
68 #include <X11/IntrinsicP.h>
69 #include <X11/ShellP.h>
73 # include <X11/Xmu/Error.h>
75 # include <Xmu/Error.h>
82 # include <X11/extensions/Xinerama.h>
83 #endif /* HAVE_XINERAMA */
89 # include <capplet-widget.h>
95 # include <glade/glade-xml.h>
97 #else /* !HAVE_GTK2 */
98 # define G_MODULE_EXPORT /**/
99 #endif /* !HAVE_GTK2 */
101 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
102 # define GLADE_DIR DEFAULT_ICONDIR
104 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
105 # define DEFAULT_ICONDIR GLADE_DIR
109 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
110 It is unused otherwise, so in that case, stub it out. */
111 static const char *hack_configuration_path = 0;
118 #include "resources.h" /* for parse_time() */
119 #include "visual.h" /* for has_writable_cells() */
120 #include "remote.h" /* for xscreensaver_command() */
123 #include "logo-50.xpm"
124 #include "logo-180.xpm"
126 #undef dgettext /* else these are defined twice... */
129 #include "demo-Gtk-widgets.h"
130 #include "demo-Gtk-support.h"
131 #include "demo-Gtk-conf.h"
143 #endif /* HAVE_GTK2 */
145 /* Deal with deprecation of direct access to struct fields on the way to GTK3
146 See http://live.gnome.org/GnomeGoals/UseGseal
148 #if GTK_CHECK_VERSION(2,14,0)
149 # define GET_PARENT(w) gtk_widget_get_parent (w)
150 # define GET_WINDOW(w) gtk_widget_get_window (w)
151 # define GET_ACTION_AREA(d) gtk_dialog_get_action_area (d)
152 # define GET_CONTENT_AREA(d) gtk_dialog_get_content_area (d)
153 # define GET_ADJ_VALUE(a) gtk_adjustment_get_value (a)
154 # define SET_ADJ_VALUE(a,v) gtk_adjustment_set_value (a, v)
155 # define SET_ADJ_UPPER(a,v) gtk_adjustment_set_upper (a, v)
157 # define GET_PARENT(w) ((w)->parent)
158 # define GET_WINDOW(w) ((w)->window)
159 # define GET_ACTION_AREA(d) ((d)->action_area)
160 # define GET_CONTENT_AREA(d) ((d)->vbox)
161 # define GET_ADJ_VALUE(a) ((a)->value)
162 # define SET_ADJ_VALUE(a,v) (a)->value = v
163 # define SET_ADJ_UPPER(a,v) (a)->upper = v
166 #if GTK_CHECK_VERSION(2,18,0)
167 # define SET_CAN_DEFAULT(w) gtk_widget_set_can_default ((w), TRUE)
168 # define GET_SENSITIVE(w) gtk_widget_get_sensitive (w)
170 # define SET_CAN_DEFAULT(w) GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_DEFAULT)
171 # define GET_SENSITIVE(w) GTK_WIDGET_IS_SENSITIVE (w)
174 #if GTK_CHECK_VERSION(2,20,0)
175 # define GET_REALIZED(w) gtk_widget_get_realized (w)
177 # define GET_REALIZED(w) GTK_WIDGET_REALIZED (w)
181 extern void exec_command (const char *shell, const char *command, int nice);
182 extern int on_path_p (const char *program);
184 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
187 #define countof(x) (sizeof((x))/sizeof((*x)))
190 /* You might think that to read an array of 32-bit quantities out of a
191 server-side property, you would pass an array of 32-bit data quantities
192 into XGetWindowProperty(). You would be wrong. You have to use an array
193 of longs, even if long is 64 bits (using 32 of each 64.)
198 char *progclass = "XScreenSaver";
201 /* The order of the items in the mode menu. */
202 static int mode_menu_order[] = {
203 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
208 char *short_version; /* version number of this xscreensaver build */
210 GtkWidget *toplevel_widget; /* the main window */
211 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
212 GtkWidget *popup_widget; /* the "Settings" dialog */
213 conf_data *cdata; /* private data for per-hack configuration */
216 GladeXML *glade_ui; /* Glade UI file */
217 #endif /* HAVE_GTK2 */
219 Bool debug_p; /* whether to print diagnostics */
220 Bool initializing_p; /* flag for breaking recursion loops */
221 Bool saving_p; /* flag for breaking recursion loops */
223 char *desired_preview_cmd; /* subprocess we intend to run */
224 char *running_preview_cmd; /* subprocess we are currently running */
225 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
226 Bool running_preview_error_p; /* whether the pid died abnormally */
228 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
229 int subproc_timer_id; /* timer to delay subproc launch */
230 int subproc_check_timer_id; /* timer to check whether it started up */
231 int subproc_check_countdown; /* how many more checks left */
233 int *list_elt_to_hack_number; /* table for sorting the hack list */
234 int *hack_number_to_list_elt; /* the inverse table */
235 Bool *hacks_available_p; /* whether hacks are on $PATH */
236 int total_available; /* how many are on $PATH */
237 int list_count; /* how many items are in the list: this may be
238 less than p->screenhacks_count, if some are
241 int _selected_list_element; /* don't use this: call
242 selected_list_element() instead */
244 int nscreens; /* How many X or Xinerama screens there are */
246 saver_preferences prefs;
251 /* Total fucking evilness due to the fact that it's rocket science to get
252 a closure object of our own down into the various widget callbacks. */
253 static state *global_state_kludge;
256 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
257 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
258 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
261 static void populate_demo_window (state *, int list_elt);
262 static void populate_prefs_page (state *);
263 static void populate_popup_window (state *);
265 static Bool flush_dialog_changes_and_save (state *);
266 static Bool flush_popup_changes_and_save (state *);
268 static int maybe_reload_init_file (state *);
269 static void await_xscreensaver (state *);
270 static Bool xscreensaver_running_p (state *);
271 static void sensitize_menu_items (state *s, Bool force_p);
272 static void force_dialog_repaint (state *s);
274 static void schedule_preview (state *, const char *cmd);
275 static void kill_preview_subproc (state *, Bool reset_p);
276 static void schedule_preview_check (state *);
279 /* Prototypes of functions used by the Glade-generated code,
282 void exit_menu_cb (GtkMenuItem *, gpointer user_data);
283 void about_menu_cb (GtkMenuItem *, gpointer user_data);
284 void doc_menu_cb (GtkMenuItem *, gpointer user_data);
285 void file_menu_cb (GtkMenuItem *, gpointer user_data);
286 void activate_menu_cb (GtkMenuItem *, gpointer user_data);
287 void lock_menu_cb (GtkMenuItem *, gpointer user_data);
288 void kill_menu_cb (GtkMenuItem *, gpointer user_data);
289 void restart_menu_cb (GtkWidget *, gpointer user_data);
290 void run_this_cb (GtkButton *, gpointer user_data);
291 void manual_cb (GtkButton *, gpointer user_data);
292 void run_next_cb (GtkButton *, gpointer user_data);
293 void run_prev_cb (GtkButton *, gpointer user_data);
294 void pref_changed_cb (GtkWidget *, gpointer user_data);
295 gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
296 void mode_menu_item_cb (GtkWidget *, gpointer user_data);
297 void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
298 gint page_num, gpointer user_data);
299 void browse_image_dir_cb (GtkButton *, gpointer user_data);
300 void browse_text_file_cb (GtkButton *, gpointer user_data);
301 void browse_text_program_cb (GtkButton *, gpointer user_data);
302 void settings_cb (GtkButton *, gpointer user_data);
303 void settings_adv_cb (GtkButton *, gpointer user_data);
304 void settings_std_cb (GtkButton *, gpointer user_data);
305 void settings_reset_cb (GtkButton *, gpointer user_data);
306 void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
307 gint page_num, gpointer user_data);
308 void settings_cancel_cb (GtkButton *, gpointer user_data);
309 void settings_ok_cb (GtkButton *, gpointer user_data);
311 static void kill_gnome_screensaver (void);
312 static void kill_kde_screensaver (void);
315 /* Some random utility functions
318 const char *blurb (void);
323 time_t now = time ((time_t *) 0);
324 char *ct = (char *) ctime (&now);
325 static char buf[255];
326 int n = strlen(progname);
328 strncpy(buf, progname, n);
331 strncpy(buf+n, ct+11, 8);
332 strcpy(buf+n+9, ": ");
338 name_to_widget (state *s, const char *name)
348 /* First try to load the Glade file from the current directory;
349 if there isn't one there, check the installed directory.
351 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
352 const char * const files[] = { GLADE_FILE_NAME,
353 GLADE_DIR "/" GLADE_FILE_NAME };
355 for (i = 0; i < countof (files); i++)
358 if (!stat (files[i], &st))
360 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
367 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
368 "\tfrom " GLADE_DIR "/ or current directory.\n",
372 # undef GLADE_FILE_NAME
374 glade_xml_signal_autoconnect (s->glade_ui);
377 w = glade_xml_get_widget (s->glade_ui, name);
379 #else /* !HAVE_GTK2 */
381 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
384 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
386 #endif /* HAVE_GTK2 */
389 fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
395 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
396 Takes a scroller, viewport, or list as an argument.
399 ensure_selected_item_visible (GtkWidget *widget)
403 GtkTreeSelection *selection;
407 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
408 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
409 path = gtk_tree_path_new_first ();
411 path = gtk_tree_model_get_path (model, &iter);
413 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
415 gtk_tree_path_free (path);
417 #else /* !HAVE_GTK2 */
419 GtkScrolledWindow *scroller = 0;
421 GtkList *list_widget = 0;
425 GtkWidget *selected = 0;
428 gint parent_h, child_y, child_h, children_h, ignore;
429 double ratio_t, ratio_b;
431 if (GTK_IS_SCROLLED_WINDOW (widget))
433 scroller = GTK_SCROLLED_WINDOW (widget);
434 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
435 list_widget = GTK_LIST (GTK_BIN(vp)->child);
437 else if (GTK_IS_VIEWPORT (widget))
439 vp = GTK_VIEWPORT (widget);
440 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
441 list_widget = GTK_LIST (GTK_BIN(vp)->child);
443 else if (GTK_IS_LIST (widget))
445 list_widget = GTK_LIST (widget);
446 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
447 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
452 slist = list_widget->selection;
453 selected = (slist ? GTK_WIDGET (slist->data) : 0);
457 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
459 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
460 kids; kids = kids->next)
463 adj = gtk_scrolled_window_get_vadjustment (scroller);
465 gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (vp)),
466 &ignore, &ignore, &ignore, &parent_h, &ignore);
467 gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (selected)),
468 &ignore, &child_y, &ignore, &child_h, &ignore);
469 children_h = nkids * child_h;
471 ratio_t = ((double) child_y) / ((double) children_h);
472 ratio_b = ((double) child_y + child_h) / ((double) children_h);
474 if (adj->upper == 0.0) /* no items in list */
477 if (ratio_t < (adj->value / adj->upper) ||
478 ratio_b > ((adj->value + adj->page_size) / adj->upper))
481 int slop = parent_h * 0.75; /* how much to overshoot by */
483 if (ratio_t < (adj->value / adj->upper))
485 double ratio_w = ((double) parent_h) / ((double) children_h);
486 double ratio_l = (ratio_b - ratio_t);
487 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
490 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
492 target = ratio_t * adj->upper;
496 if (target > adj->upper - adj->page_size)
497 target = adj->upper - adj->page_size;
501 gtk_adjustment_set_value (adj, target);
503 #endif /* !HAVE_GTK2 */
507 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
509 GtkWidget *shell = GTK_WIDGET (user_data);
510 while (GET_PARENT (shell))
511 shell = GET_PARENT (shell);
512 gtk_widget_destroy (GTK_WIDGET (shell));
516 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
518 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
520 restart_menu_cb (widget, user_data);
521 warning_dialog_dismiss_cb (widget, user_data);
524 static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data)
526 kill_gnome_screensaver ();
527 warning_dialog_dismiss_cb (widget, user_data);
530 static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data)
532 kill_kde_screensaver ();
533 warning_dialog_dismiss_cb (widget, user_data);
536 typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
539 warning_dialog (GtkWidget *parent, const char *message,
540 dialog_button button_type, int center)
542 char *msg = strdup (message);
545 GtkWidget *dialog = gtk_dialog_new ();
546 GtkWidget *label = 0;
548 GtkWidget *cancel = 0;
551 while (parent && !GET_WINDOW (parent))
552 parent = GET_PARENT (parent);
555 !GET_WINDOW (parent)) /* too early to pop up transient dialogs */
557 fprintf (stderr, "%s: too early for dialog?\n", progname);
565 char *s = strchr (head, '\n');
568 sprintf (name, "label%d", i++);
571 label = gtk_label_new (head);
573 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
574 #endif /* HAVE_GTK2 */
579 GTK_WIDGET (label)->style =
580 gtk_style_copy (GTK_WIDGET (label)->style);
581 GTK_WIDGET (label)->style->font =
582 gdk_font_load (get_string_resource("warning_dialog.headingFont",
584 gtk_widget_set_style (GTK_WIDGET (label),
585 GTK_WIDGET (label)->style);
587 #endif /* !HAVE_GTK2 */
589 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
590 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
591 label, TRUE, TRUE, 0);
592 gtk_widget_show (label);
603 label = gtk_label_new ("");
604 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
605 label, TRUE, TRUE, 0);
606 gtk_widget_show (label);
608 label = gtk_hbutton_box_new ();
609 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
610 label, TRUE, TRUE, 0);
613 if (button_type != D_NONE)
615 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
616 gtk_container_add (GTK_CONTAINER (label), cancel);
619 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
620 gtk_container_add (GTK_CONTAINER (label), ok);
622 #else /* !HAVE_GTK2 */
624 ok = gtk_button_new_with_label ("OK");
625 gtk_container_add (GTK_CONTAINER (label), ok);
627 if (button_type != D_NONE)
629 cancel = gtk_button_new_with_label ("Cancel");
630 gtk_container_add (GTK_CONTAINER (label), cancel);
633 #endif /* !HAVE_GTK2 */
635 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
636 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
637 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
638 SET_CAN_DEFAULT (ok);
639 gtk_widget_show (ok);
640 gtk_widget_grab_focus (ok);
644 SET_CAN_DEFAULT (cancel);
645 gtk_widget_show (cancel);
647 gtk_widget_show (label);
648 gtk_widget_show (dialog);
650 if (button_type != D_NONE)
653 switch (button_type) {
654 case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break;
655 case D_GNOME: fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb); break;
656 case D_KDE: fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb); break;
657 default: abort(); break;
659 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn,
661 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
662 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
667 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
668 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
672 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
673 GET_WINDOW (GTK_WIDGET (parent)));
676 gtk_window_present (GTK_WINDOW (dialog));
677 #else /* !HAVE_GTK2 */
678 gdk_window_show (GTK_WIDGET (dialog)->window);
679 gdk_window_raise (GTK_WIDGET (dialog)->window);
680 #endif /* !HAVE_GTK2 */
688 run_cmd (state *s, Atom command, int arg)
693 flush_dialog_changes_and_save (s);
694 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
696 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
697 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
704 sprintf (buf, "Error:\n\n%s", err);
706 strcpy (buf, "Unknown error!");
707 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
711 sensitize_menu_items (s, True);
712 force_dialog_repaint (s);
717 run_hack (state *s, int list_elt, Bool report_errors_p)
723 if (list_elt < 0) return;
724 hack_number = s->list_elt_to_hack_number[list_elt];
726 flush_dialog_changes_and_save (s);
727 schedule_preview (s, 0);
729 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
732 if (status < 0 && report_errors_p)
734 if (xscreensaver_running_p (s))
736 /* Kludge: ignore the spurious "window unexpectedly deleted"
738 if (err && strstr (err, "unexpectedly deleted"))
745 sprintf (buf, "Error:\n\n%s", err);
747 strcpy (buf, "Unknown error!");
748 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
753 /* The error is that the daemon isn't running;
756 const char *d = DisplayString (GDK_DISPLAY());
760 "The XScreenSaver daemon doesn't seem to be running\n"
761 "on display \"%s\". Launch it now?"),
763 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
769 sensitize_menu_items (s, False);
776 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
777 libglade work on Cygwin; apparently all Glade callbacks need this magic
778 extra declaration. I do not pretend to understand.
782 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
784 state *s = global_state_kludge; /* I hate C so much... */
785 flush_dialog_changes_and_save (s);
786 kill_preview_subproc (s, False);
791 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
793 state *s = (state *) data;
794 flush_dialog_changes_and_save (s);
801 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
804 char *vers = strdup (screensaver_id + 4);
807 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
809 s = strchr (vers, ',');
813 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
814 non-ASCII characters aren't allowed in localizable string keys."
815 (I don't want to just use (c) instead of © because that doesn't
816 look as good in the plain-old default Latin1 "C" locale.)
819 sprintf(copy, ("Copyright \xC2\xA9 1991-2008 %s"), s);
820 #else /* !HAVE_GTK2 */
821 sprintf(copy, ("Copyright \251 1991-2008 %s"), s);
822 #endif /* !HAVE_GTK2 */
824 sprintf (msg, "%s\n\n%s", copy, desc);
826 /* I can't make gnome_about_new() work here -- it starts dying in
827 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
828 then this might be the thing to do:
832 const gchar *auth[] = { 0 };
833 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
835 gtk_widget_show (about);
837 #else / * GTK but not GNOME * /
841 GdkColormap *colormap;
842 GdkPixmap *gdkpixmap;
845 GtkWidget *dialog = gtk_dialog_new ();
846 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
847 GtkWidget *parent = GTK_WIDGET (menuitem);
848 while (GET_PARENT (parent))
849 parent = GET_PARENT (parent);
851 hbox = gtk_hbox_new (FALSE, 20);
852 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
853 hbox, TRUE, TRUE, 0);
855 colormap = gtk_widget_get_colormap (parent);
857 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
858 (gchar **) logo_180_xpm);
859 icon = gtk_pixmap_new (gdkpixmap, mask);
860 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
862 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
864 vbox = gtk_vbox_new (FALSE, 0);
865 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
867 label1 = gtk_label_new (vers);
868 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
869 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
870 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
873 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
874 GTK_WIDGET (label1)->style->font =
875 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
876 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
877 #endif /* HAVE_GTK2 */
879 label2 = gtk_label_new (msg);
880 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
881 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
882 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
885 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
886 GTK_WIDGET (label2)->style->font =
887 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
888 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
889 #endif /* HAVE_GTK2 */
891 hb = gtk_hbutton_box_new ();
893 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
897 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
898 #else /* !HAVE_GTK2 */
899 ok = gtk_button_new_with_label (_("OK"));
900 #endif /* !HAVE_GTK2 */
901 gtk_container_add (GTK_CONTAINER (hb), ok);
903 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
904 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
905 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
907 gtk_widget_show (hbox);
908 gtk_widget_show (icon);
909 gtk_widget_show (vbox);
910 gtk_widget_show (label1);
911 gtk_widget_show (label2);
912 gtk_widget_show (hb);
913 gtk_widget_show (ok);
914 gtk_widget_show (dialog);
916 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
917 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
919 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
920 GET_WINDOW (GTK_WIDGET (parent)));
921 gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog)));
922 gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog)));
928 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
930 state *s = global_state_kludge; /* I hate C so much... */
931 saver_preferences *p = &s->prefs;
934 if (!p->help_url || !*p->help_url)
936 warning_dialog (s->toplevel_widget,
938 "No Help URL has been specified.\n"), D_NONE, 100);
942 help_command = (char *) malloc (strlen (p->load_url_command) +
943 (strlen (p->help_url) * 4) + 20);
944 strcpy (help_command, "( ");
945 sprintf (help_command + strlen(help_command),
947 p->help_url, p->help_url, p->help_url, p->help_url);
948 strcat (help_command, " ) &");
949 if (system (help_command) < 0)
950 fprintf (stderr, "%s: fork error\n", blurb());
956 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
958 state *s = global_state_kludge; /* I hate C so much... */
959 sensitize_menu_items (s, False);
964 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
966 state *s = global_state_kludge; /* I hate C so much... */
967 run_cmd (s, XA_ACTIVATE, 0);
972 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
974 state *s = global_state_kludge; /* I hate C so much... */
975 run_cmd (s, XA_LOCK, 0);
980 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
982 state *s = global_state_kludge; /* I hate C so much... */
983 run_cmd (s, XA_EXIT, 0);
988 restart_menu_cb (GtkWidget *widget, gpointer user_data)
990 state *s = global_state_kludge; /* I hate C so much... */
991 flush_dialog_changes_and_save (s);
992 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
994 if (system ("xscreensaver -nosplash &") < 0)
995 fprintf (stderr, "%s: fork error\n", blurb());
997 await_xscreensaver (s);
1001 xscreensaver_running_p (state *s)
1003 Display *dpy = GDK_DISPLAY();
1005 server_xscreensaver_version (dpy, &rversion, 0, 0);
1013 await_xscreensaver (state *s)
1018 while (!ok && (--countdown > 0))
1019 if (xscreensaver_running_p (s))
1022 sleep (1); /* If it's not there yet, wait a second... */
1024 sensitize_menu_items (s, True);
1028 /* Timed out, no screensaver running. */
1031 Bool root_p = (geteuid () == 0);
1035 "The xscreensaver daemon did not start up properly.\n"
1041 __extension__ /* don't warn about "string length is greater than
1042 the length ISO C89 compilers are required to
1043 support" in the following expression... */
1046 _("You are running as root. This usually means that xscreensaver\n"
1047 "was unable to contact your X server because access control is\n"
1048 "turned on. Try running this command:\n"
1050 " xhost +localhost\n"
1052 "and then selecting `File / Restart Daemon'.\n"
1054 "Note that turning off access control will allow anyone logged\n"
1055 "on to this machine to access your screen, which might be\n"
1056 "considered a security problem. Please read the xscreensaver\n"
1057 "manual and FAQ for more information.\n"
1059 "You shouldn't run X as root. Instead, you should log in as a\n"
1060 "normal user, and `su' as necessary."));
1062 strcat (buf, _("Please check your $PATH and permissions."));
1064 warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1067 force_dialog_repaint (s);
1072 selected_list_element (state *s)
1074 return s->_selected_list_element;
1079 demo_write_init_file (state *s, saver_preferences *p)
1081 Display *dpy = GDK_DISPLAY();
1084 /* #### try to figure out why shit keeps getting reordered... */
1085 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1089 if (!write_init_file (dpy, p, s->short_version, False))
1092 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1097 const char *f = init_file_name();
1099 warning_dialog (s->toplevel_widget,
1100 _("Error:\n\nCouldn't determine init file name!\n"),
1104 char *b = (char *) malloc (strlen(f) + 1024);
1105 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1106 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1114 G_MODULE_EXPORT void
1115 run_this_cb (GtkButton *button, gpointer user_data)
1117 state *s = global_state_kludge; /* I hate C so much... */
1118 int list_elt = selected_list_element (s);
1119 if (list_elt < 0) return;
1120 if (!flush_dialog_changes_and_save (s))
1121 run_hack (s, list_elt, True);
1125 G_MODULE_EXPORT void
1126 manual_cb (GtkButton *button, gpointer user_data)
1128 Display *dpy = GDK_DISPLAY();
1129 state *s = global_state_kludge; /* I hate C so much... */
1130 saver_preferences *p = &s->prefs;
1131 GtkWidget *list_widget = name_to_widget (s, "list");
1132 int list_elt = selected_list_element (s);
1134 char *name, *name2, *cmd, *str;
1136 if (list_elt < 0) return;
1137 hack_number = s->list_elt_to_hack_number[list_elt];
1139 flush_dialog_changes_and_save (s);
1140 ensure_selected_item_visible (list_widget);
1142 name = strdup (p->screenhacks[hack_number]->command);
1145 while (isspace (*name2)) name2++;
1147 while (*str && !isspace (*str)) str++;
1149 str = strrchr (name2, '/');
1150 if (str) name2 = str+1;
1152 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1155 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1156 strcpy (cmd2, "( ");
1157 sprintf (cmd2 + strlen (cmd2),
1159 name2, name2, name2, name2);
1160 strcat (cmd2, " ) &");
1161 if (system (cmd2) < 0)
1162 fprintf (stderr, "%s: fork error\n", blurb());
1167 warning_dialog (GTK_WIDGET (button),
1168 _("Error:\n\nno `manualCommand' resource set."),
1177 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1179 GtkWidget *parent = name_to_widget (s, "scroller");
1180 gboolean was = GET_SENSITIVE (parent);
1183 GtkTreeModel *model;
1184 GtkTreeSelection *selection;
1185 #endif /* HAVE_GTK2 */
1187 if (!was) gtk_widget_set_sensitive (parent, True);
1189 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1191 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1193 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1194 gtk_tree_selection_select_iter (selection, &iter);
1196 #else /* !HAVE_GTK2 */
1197 gtk_list_select_item (GTK_LIST (list), list_elt);
1198 #endif /* !HAVE_GTK2 */
1199 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1200 if (!was) gtk_widget_set_sensitive (parent, False);
1204 G_MODULE_EXPORT void
1205 run_next_cb (GtkButton *button, gpointer user_data)
1207 state *s = global_state_kludge; /* I hate C so much... */
1208 /* saver_preferences *p = &s->prefs; */
1209 Bool ops = s->preview_suppressed_p;
1211 GtkWidget *list_widget = name_to_widget (s, "list");
1212 int list_elt = selected_list_element (s);
1219 if (list_elt >= s->list_count)
1222 s->preview_suppressed_p = True;
1224 flush_dialog_changes_and_save (s);
1225 force_list_select_item (s, list_widget, list_elt, True);
1226 populate_demo_window (s, list_elt);
1227 run_hack (s, list_elt, False);
1229 s->preview_suppressed_p = ops;
1233 G_MODULE_EXPORT void
1234 run_prev_cb (GtkButton *button, gpointer user_data)
1236 state *s = global_state_kludge; /* I hate C so much... */
1237 /* saver_preferences *p = &s->prefs; */
1238 Bool ops = s->preview_suppressed_p;
1240 GtkWidget *list_widget = name_to_widget (s, "list");
1241 int list_elt = selected_list_element (s);
1244 list_elt = s->list_count - 1;
1249 list_elt = s->list_count - 1;
1251 s->preview_suppressed_p = True;
1253 flush_dialog_changes_and_save (s);
1254 force_list_select_item (s, list_widget, list_elt, True);
1255 populate_demo_window (s, list_elt);
1256 run_hack (s, list_elt, False);
1258 s->preview_suppressed_p = ops;
1262 /* Writes the given settings into prefs.
1263 Returns true if there was a change, False otherwise.
1264 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1267 flush_changes (state *s,
1270 const char *command,
1273 saver_preferences *p = &s->prefs;
1274 Bool changed = False;
1277 if (list_elt < 0 || list_elt >= s->list_count)
1280 hack_number = s->list_elt_to_hack_number[list_elt];
1281 hack = p->screenhacks[hack_number];
1283 if (enabled_p != -1 &&
1284 enabled_p != hack->enabled_p)
1286 hack->enabled_p = enabled_p;
1289 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1290 blurb(), hack->name, enabled_p);
1295 if (!hack->command || !!strcmp (command, hack->command))
1297 if (hack->command) free (hack->command);
1298 hack->command = strdup (command);
1301 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1302 blurb(), hack->name, command);
1308 const char *ov = hack->visual;
1309 if (!ov || !*ov) ov = "any";
1310 if (!*visual) visual = "any";
1311 if (!!strcasecmp (visual, ov))
1313 if (hack->visual) free (hack->visual);
1314 hack->visual = strdup (visual);
1317 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1318 blurb(), hack->name, visual);
1326 /* Helper for the text fields that contain time specifications:
1327 this parses the text, and does error checking.
1330 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1335 if (!sec_p || strchr (line, ':'))
1336 value = parse_time ((char *) line, sec_p, True);
1340 if (sscanf (line, "%d%c", &value, &c) != 1)
1346 value *= 1000; /* Time measures in microseconds */
1352 "Unparsable time format: \"%s\"\n"),
1354 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1363 directory_p (const char *path)
1366 if (!path || !*path)
1368 else if (stat (path, &st))
1370 else if (!S_ISDIR (st.st_mode))
1377 file_p (const char *path)
1380 if (!path || !*path)
1382 else if (stat (path, &st))
1384 else if (S_ISDIR (st.st_mode))
1391 normalize_directory (const char *path)
1395 if (!path || !*path) return 0;
1397 p2 = (char *) malloc (L + 2);
1399 if (p2[L-1] == '/') /* remove trailing slash */
1402 for (s = p2; s && *s; s++)
1405 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1406 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1409 while (s0 > p2 && s0[-1] != '/')
1415 /* strcpy (s0, s); */
1416 memmove(s0, s, strlen(s) + 1);
1420 else if (*s == '/' && !strncmp (s, "/./", 3)) { /* delete "/./" */
1421 /* strcpy (s, s+2), s--; */
1422 memmove(s, s+2, strlen(s+2) + 1);
1425 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1430 Normalize consecutive slashes.
1431 Ignore doubled slashes after ":" to avoid mangling URLs.
1434 for (s = p2; s && *s; s++){
1435 if (*s == ':') continue;
1436 if (!s[1] || !s[2]) continue;
1437 while (s[1] == '/' && s[2] == '/')
1438 /* strcpy (s+1, s+2); */
1439 memmove (s+1, s+2, strlen(s+2) + 1);
1442 /* and strip trailing whitespace for good measure. */
1444 while (isspace(p2[L-1]))
1457 } FlushForeachClosure;
1460 flush_checkbox (GtkTreeModel *model,
1465 FlushForeachClosure *closure = data;
1468 gtk_tree_model_get (model, iter,
1469 COL_ENABLED, &checked,
1472 if (flush_changes (closure->s, closure->i,
1474 *closure->changed = True;
1478 /* don't remove row */
1482 #endif /* HAVE_GTK2 */
1484 /* Flush out any changes made in the main dialog window (where changes
1485 take place immediately: clicking on a checkbox causes the init file
1486 to be written right away.)
1489 flush_dialog_changes_and_save (state *s)
1491 saver_preferences *p = &s->prefs;
1492 saver_preferences P2, *p2 = &P2;
1494 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1495 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1496 FlushForeachClosure closure;
1497 #else /* !HAVE_GTK2 */
1498 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1499 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1501 #endif /* !HAVE_GTK2 */
1502 static Bool already_warned_about_missing_image_directory = False; /* very long name... */
1504 Bool changed = False;
1507 if (s->saving_p) return False;
1512 /* Flush any checkbox changes in the list down into the prefs struct.
1516 closure.changed = &changed;
1518 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1520 #else /* !HAVE_GTK2 */
1522 for (i = 0; kids; kids = kids->next, i++)
1524 GtkWidget *line = GTK_WIDGET (kids->data);
1525 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1526 GtkWidget *line_check =
1527 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1529 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1531 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1534 #endif /* ~HAVE_GTK2 */
1536 /* Flush the non-hack-specific settings down into the prefs struct.
1539 # define SECONDS(FIELD,NAME) \
1540 w = name_to_widget (s, (NAME)); \
1541 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1543 # define MINUTES(FIELD,NAME) \
1544 w = name_to_widget (s, (NAME)); \
1545 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1547 # define CHECKBOX(FIELD,NAME) \
1548 w = name_to_widget (s, (NAME)); \
1549 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1551 # define PATHNAME(FIELD,NAME) \
1552 w = name_to_widget (s, (NAME)); \
1553 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1555 # define TEXT(FIELD,NAME) \
1556 w = name_to_widget (s, (NAME)); \
1557 (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1559 MINUTES (&p2->timeout, "timeout_spinbutton");
1560 MINUTES (&p2->cycle, "cycle_spinbutton");
1561 CHECKBOX (p2->lock_p, "lock_button");
1562 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1564 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1565 CHECKBOX (p2->dpms_quickoff_p, "dpms_quickoff_button");
1566 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1567 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1568 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1570 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1571 CHECKBOX (p2->grab_video_p, "grab_video_button");
1572 CHECKBOX (p2->random_image_p, "grab_image_button");
1573 PATHNAME (p2->image_directory, "image_text");
1576 CHECKBOX (p2->verbose_p, "verbose_button");
1577 CHECKBOX (p2->capture_stderr_p, "capture_button");
1578 CHECKBOX (p2->splash_p, "splash_button");
1583 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1584 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1585 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1586 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1587 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1588 TEXT (p2->text_literal, "text_entry");
1589 PATHNAME (p2->text_file, "text_file_entry");
1590 PATHNAME (p2->text_program, "text_program_entry");
1591 PATHNAME (p2->text_program, "text_program_entry");
1592 TEXT (p2->text_url, "text_url_entry");
1595 CHECKBOX (p2->install_cmap_p, "install_button");
1596 CHECKBOX (p2->fade_p, "fade_button");
1597 CHECKBOX (p2->unfade_p, "unfade_button");
1598 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1606 /* Warn if the image directory doesn't exist, when:
1607 - not being warned before
1608 - image directory is changed and the directory doesn't exist
1609 - image directory does not begin with http://
1611 if (p2->image_directory &&
1612 *p2->image_directory &&
1613 !directory_p (p2->image_directory) &&
1614 strncmp(p2->image_directory, "http://", 6) &&
1615 ( !already_warned_about_missing_image_directory ||
1616 ( p->image_directory &&
1617 *p->image_directory &&
1618 strcmp(p->image_directory, p2->image_directory)
1624 sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n",
1625 p2->image_directory);
1626 if (warning_dialog (s->toplevel_widget, b, D_NONE, 100))
1627 already_warned_about_missing_image_directory = True;
1631 /* Map the mode menu to `saver_mode' enum values. */
1633 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1634 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1635 GtkWidget *selected = gtk_menu_get_active (menu);
1636 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1637 int menu_elt = g_list_index (kids, (gpointer) selected);
1638 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1639 p2->mode = mode_menu_order[menu_elt];
1642 if (p2->mode == ONE_HACK)
1644 int list_elt = selected_list_element (s);
1645 p2->selected_hack = (list_elt >= 0
1646 ? s->list_elt_to_hack_number[list_elt]
1650 # define COPY(field, name) \
1651 if (p->field != p2->field) { \
1654 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1656 p->field = p2->field
1659 COPY(selected_hack, "selected_hack");
1661 COPY(timeout, "timeout");
1662 COPY(cycle, "cycle");
1663 COPY(lock_p, "lock_p");
1664 COPY(lock_timeout, "lock_timeout");
1666 COPY(dpms_enabled_p, "dpms_enabled_p");
1667 COPY(dpms_quickoff_p, "dpms_quickoff_enabled_p");
1668 COPY(dpms_standby, "dpms_standby");
1669 COPY(dpms_suspend, "dpms_suspend");
1670 COPY(dpms_off, "dpms_off");
1673 COPY(verbose_p, "verbose_p");
1674 COPY(capture_stderr_p, "capture_stderr_p");
1675 COPY(splash_p, "splash_p");
1678 COPY(tmode, "tmode");
1680 COPY(install_cmap_p, "install_cmap_p");
1681 COPY(fade_p, "fade_p");
1682 COPY(unfade_p, "unfade_p");
1683 COPY(fade_seconds, "fade_seconds");
1685 COPY(grab_desktop_p, "grab_desktop_p");
1686 COPY(grab_video_p, "grab_video_p");
1687 COPY(random_image_p, "random_image_p");
1691 # define COPYSTR(FIELD,NAME) \
1694 strcmp(p->FIELD, p2->FIELD)) \
1698 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1700 if (p->FIELD && p->FIELD != p2->FIELD) \
1702 p->FIELD = p2->FIELD; \
1705 COPYSTR(image_directory, "image_directory");
1706 COPYSTR(text_literal, "text_literal");
1707 COPYSTR(text_file, "text_file");
1708 COPYSTR(text_program, "text_program");
1709 COPYSTR(text_url, "text_url");
1712 populate_prefs_page (s);
1716 Display *dpy = GDK_DISPLAY();
1717 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1718 sync_server_dpms_settings (dpy, enabled_p,
1719 p->dpms_standby / 1000,
1720 p->dpms_suspend / 1000,
1724 changed = demo_write_init_file (s, p);
1727 s->saving_p = False;
1732 /* Flush out any changes made in the popup dialog box (where changes
1733 take place only when the OK button is clicked.)
1736 flush_popup_changes_and_save (state *s)
1738 Bool changed = False;
1739 saver_preferences *p = &s->prefs;
1740 int list_elt = selected_list_element (s);
1742 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1743 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1745 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1746 const char *command = gtk_entry_get_text (cmd);
1751 if (s->saving_p) return False;
1757 if (maybe_reload_init_file (s) != 0)
1763 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1765 if (!strcasecmp (visual, "")) visual = "";
1766 else if (!strcasecmp (visual, "any")) visual = "";
1767 else if (!strcasecmp (visual, "default")) visual = "Default";
1768 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1769 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1770 else if (!strcasecmp (visual, "best")) visual = "Best";
1771 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1772 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1773 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1774 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1775 else if (!strcasecmp (visual, "color")) visual = "Color";
1776 else if (!strcasecmp (visual, "gl")) visual = "GL";
1777 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1778 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1779 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1780 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1781 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1782 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1783 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1784 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1785 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1788 gdk_beep (); /* unparsable */
1790 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1793 changed = flush_changes (s, list_elt, -1, command, visual);
1796 changed = demo_write_init_file (s, p);
1798 /* Do this to re-launch the hack if (and only if) the command line
1800 populate_demo_window (s, selected_list_element (s));
1804 s->saving_p = False;
1809 G_MODULE_EXPORT void
1810 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1812 state *s = global_state_kludge; /* I hate C so much... */
1813 if (! s->initializing_p)
1815 s->initializing_p = True;
1816 flush_dialog_changes_and_save (s);
1817 s->initializing_p = False;
1821 G_MODULE_EXPORT gboolean
1822 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1824 pref_changed_cb (widget, user_data);
1828 /* Callback on menu items in the "mode" options menu.
1830 G_MODULE_EXPORT void
1831 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1833 state *s = (state *) user_data;
1834 saver_preferences *p = &s->prefs;
1835 GtkWidget *list = name_to_widget (s, "list");
1839 gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
1841 saver_mode new_mode;
1845 if (menu_items->data == widget)
1848 menu_items = menu_items->next;
1850 if (!menu_items) abort();
1852 new_mode = mode_menu_order[menu_index];
1854 /* Keep the same list element displayed as before; except if we're
1855 switching *to* "one screensaver" mode from any other mode, set
1856 "the one" to be that which is currently selected.
1858 list_elt = selected_list_element (s);
1859 if (new_mode == ONE_HACK)
1860 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1863 saver_mode old_mode = p->mode;
1865 populate_demo_window (s, list_elt);
1866 force_list_select_item (s, list, list_elt, True);
1867 p->mode = old_mode; /* put it back, so the init file gets written */
1870 pref_changed_cb (widget, user_data);
1874 G_MODULE_EXPORT void
1875 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1876 gint page_num, gpointer user_data)
1878 state *s = global_state_kludge; /* I hate C so much... */
1879 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1881 /* If we're switching to page 0, schedule the current hack to be run.
1882 Otherwise, schedule it to stop. */
1884 populate_demo_window (s, selected_list_element (s));
1886 schedule_preview (s, 0);
1891 list_activated_cb (GtkTreeView *list,
1893 GtkTreeViewColumn *column,
1900 g_return_if_fail (!gdk_pointer_is_grabbed ());
1902 str = gtk_tree_path_to_string (path);
1903 list_elt = strtol (str, NULL, 10);
1907 run_hack (s, list_elt, True);
1911 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1913 state *s = (state *)data;
1914 GtkTreeModel *model;
1920 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1923 path = gtk_tree_model_get_path (model, &iter);
1924 str = gtk_tree_path_to_string (path);
1925 list_elt = strtol (str, NULL, 10);
1927 gtk_tree_path_free (path);
1930 populate_demo_window (s, list_elt);
1931 flush_dialog_changes_and_save (s);
1933 /* Re-populate the Settings window any time a new item is selected
1934 in the list, in case both windows are currently visible.
1936 populate_popup_window (s);
1939 #else /* !HAVE_GTK2 */
1941 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1942 list_select_cb that comes in
1943 *after* we've double-clicked.
1947 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1950 state *s = (state *) data;
1951 if (event->type == GDK_2BUTTON_PRESS)
1953 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1954 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1956 last_doubleclick_time = time ((time_t *) 0);
1959 run_hack (s, list_elt, True);
1967 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1969 state *s = (state *) data;
1970 time_t now = time ((time_t *) 0);
1972 if (now >= last_doubleclick_time + 2)
1974 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1975 populate_demo_window (s, list_elt);
1976 flush_dialog_changes_and_save (s);
1981 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1983 state *s = (state *) data;
1984 populate_demo_window (s, -1);
1985 flush_dialog_changes_and_save (s);
1988 #endif /* !HAVE_GTK2 */
1991 /* Called when the checkboxes that are in the left column of the
1992 scrolling list are clicked. This both populates the right pane
1993 (just as clicking on the label (really, listitem) does) and
1994 also syncs this checkbox with the right pane Enabled checkbox.
1999 GtkCellRendererToggle *toggle,
2001 #else /* !HAVE_GTK2 */
2003 #endif /* !HAVE_GTK2 */
2006 state *s = (state *) data;
2009 GtkScrolledWindow *scroller =
2010 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
2011 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2012 GtkTreeModel *model = gtk_tree_view_get_model (list);
2013 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
2016 #else /* !HAVE_GTK2 */
2017 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
2018 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
2020 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
2021 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
2022 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
2023 #endif /* !HAVE_GTK2 */
2030 if (!gtk_tree_model_get_iter (model, &iter, path))
2032 g_warning ("bad path: %s", path_string);
2035 gtk_tree_path_free (path);
2037 gtk_tree_model_get (model, &iter,
2038 COL_ENABLED, &active,
2041 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2042 COL_ENABLED, !active,
2045 list_elt = strtol (path_string, NULL, 10);
2046 #else /* !HAVE_GTK2 */
2047 list_elt = gtk_list_child_position (list, line);
2048 #endif /* !HAVE_GTK2 */
2050 /* remember previous scroll position of the top of the list */
2051 adj = gtk_scrolled_window_get_vadjustment (scroller);
2052 scroll_top = GET_ADJ_VALUE (adj);
2054 flush_dialog_changes_and_save (s);
2055 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
2056 populate_demo_window (s, list_elt);
2058 /* restore the previous scroll position of the top of the list.
2059 this is weak, but I don't really know why it's moving... */
2060 gtk_adjustment_set_value (adj, scroll_top);
2066 GtkFileSelection *widget;
2067 } file_selection_data;
2072 store_image_directory (GtkWidget *button, gpointer user_data)
2074 file_selection_data *fsd = (file_selection_data *) user_data;
2075 state *s = fsd->state;
2076 GtkFileSelection *selector = fsd->widget;
2077 GtkWidget *top = s->toplevel_widget;
2078 saver_preferences *p = &s->prefs;
2079 const char *path = gtk_file_selection_get_filename (selector);
2081 if (p->image_directory && !strcmp(p->image_directory, path))
2082 return; /* no change */
2084 /* No warning for URLs. */
2085 if ((!directory_p (path)) && strncmp(path, "http://", 6))
2088 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2089 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2093 if (p->image_directory) free (p->image_directory);
2094 p->image_directory = normalize_directory (path);
2096 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2097 (p->image_directory ? p->image_directory : ""));
2098 demo_write_init_file (s, p);
2103 store_text_file (GtkWidget *button, gpointer user_data)
2105 file_selection_data *fsd = (file_selection_data *) user_data;
2106 state *s = fsd->state;
2107 GtkFileSelection *selector = fsd->widget;
2108 GtkWidget *top = s->toplevel_widget;
2109 saver_preferences *p = &s->prefs;
2110 const char *path = gtk_file_selection_get_filename (selector);
2112 if (p->text_file && !strcmp(p->text_file, path))
2113 return; /* no change */
2118 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2119 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2123 if (p->text_file) free (p->text_file);
2124 p->text_file = normalize_directory (path);
2126 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2127 (p->text_file ? p->text_file : ""));
2128 demo_write_init_file (s, p);
2133 store_text_program (GtkWidget *button, gpointer user_data)
2135 file_selection_data *fsd = (file_selection_data *) user_data;
2136 state *s = fsd->state;
2137 GtkFileSelection *selector = fsd->widget;
2138 /*GtkWidget *top = s->toplevel_widget;*/
2139 saver_preferences *p = &s->prefs;
2140 const char *path = gtk_file_selection_get_filename (selector);
2142 if (p->text_program && !strcmp(p->text_program, path))
2143 return; /* no change */
2149 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2150 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2155 if (p->text_program) free (p->text_program);
2156 p->text_program = normalize_directory (path);
2158 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2159 (p->text_program ? p->text_program : ""));
2160 demo_write_init_file (s, p);
2166 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2168 file_selection_data *fsd = (file_selection_data *) user_data;
2169 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2173 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2175 browse_image_dir_cancel (button, user_data);
2176 store_image_directory (button, user_data);
2180 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2182 browse_image_dir_cancel (button, user_data);
2183 store_text_file (button, user_data);
2187 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2189 browse_image_dir_cancel (button, user_data);
2190 store_text_program (button, user_data);
2194 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2196 browse_image_dir_cancel (widget, user_data);
2200 G_MODULE_EXPORT void
2201 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2203 state *s = global_state_kludge; /* I hate C so much... */
2204 saver_preferences *p = &s->prefs;
2205 static file_selection_data *fsd = 0;
2207 GtkFileSelection *selector = GTK_FILE_SELECTION(
2208 gtk_file_selection_new ("Please select the image directory."));
2211 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2213 fsd->widget = selector;
2216 if (p->image_directory && *p->image_directory)
2217 gtk_file_selection_set_filename (selector, p->image_directory);
2219 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2220 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2222 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2223 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2225 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2226 GTK_SIGNAL_FUNC (browse_image_dir_close),
2229 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2231 gtk_window_set_modal (GTK_WINDOW (selector), True);
2232 gtk_widget_show (GTK_WIDGET (selector));
2236 G_MODULE_EXPORT void
2237 browse_text_file_cb (GtkButton *button, gpointer user_data)
2239 state *s = global_state_kludge; /* I hate C so much... */
2240 saver_preferences *p = &s->prefs;
2241 static file_selection_data *fsd = 0;
2243 GtkFileSelection *selector = GTK_FILE_SELECTION(
2244 gtk_file_selection_new ("Please select a text file."));
2247 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2249 fsd->widget = selector;
2252 if (p->text_file && *p->text_file)
2253 gtk_file_selection_set_filename (selector, p->text_file);
2255 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2256 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2258 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2259 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2261 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2262 GTK_SIGNAL_FUNC (browse_image_dir_close),
2265 gtk_window_set_modal (GTK_WINDOW (selector), True);
2266 gtk_widget_show (GTK_WIDGET (selector));
2270 G_MODULE_EXPORT void
2271 browse_text_program_cb (GtkButton *button, gpointer user_data)
2273 state *s = global_state_kludge; /* I hate C so much... */
2274 saver_preferences *p = &s->prefs;
2275 static file_selection_data *fsd = 0;
2277 GtkFileSelection *selector = GTK_FILE_SELECTION(
2278 gtk_file_selection_new ("Please select a text-generating program."));
2281 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2283 fsd->widget = selector;
2286 if (p->text_program && *p->text_program)
2287 gtk_file_selection_set_filename (selector, p->text_program);
2289 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2290 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2292 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2293 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2295 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2296 GTK_SIGNAL_FUNC (browse_image_dir_close),
2299 gtk_window_set_modal (GTK_WINDOW (selector), True);
2300 gtk_widget_show (GTK_WIDGET (selector));
2307 G_MODULE_EXPORT void
2308 settings_cb (GtkButton *button, gpointer user_data)
2310 state *s = global_state_kludge; /* I hate C so much... */
2311 int list_elt = selected_list_element (s);
2313 populate_demo_window (s, list_elt); /* reset the widget */
2314 populate_popup_window (s); /* create UI on popup window */
2315 gtk_widget_show (s->popup_widget);
2319 settings_sync_cmd_text (state *s)
2322 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2323 char *cmd_line = get_configurator_command_line (s->cdata, False);
2324 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2325 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2327 # endif /* HAVE_XML */
2330 G_MODULE_EXPORT void
2331 settings_adv_cb (GtkButton *button, gpointer user_data)
2333 state *s = global_state_kludge; /* I hate C so much... */
2334 GtkNotebook *notebook =
2335 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2337 settings_sync_cmd_text (s);
2338 gtk_notebook_set_page (notebook, 1);
2341 G_MODULE_EXPORT void
2342 settings_std_cb (GtkButton *button, gpointer user_data)
2344 state *s = global_state_kludge; /* I hate C so much... */
2345 GtkNotebook *notebook =
2346 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2348 /* Re-create UI to reflect the in-progress command-line settings. */
2349 populate_popup_window (s);
2351 gtk_notebook_set_page (notebook, 0);
2354 G_MODULE_EXPORT void
2355 settings_reset_cb (GtkButton *button, gpointer user_data)
2358 state *s = global_state_kludge; /* I hate C so much... */
2359 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2360 char *cmd_line = get_configurator_command_line (s->cdata, True);
2361 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2362 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2364 populate_popup_window (s);
2365 # endif /* HAVE_XML */
2368 G_MODULE_EXPORT void
2369 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2370 gint page_num, gpointer user_data)
2372 state *s = global_state_kludge; /* I hate C so much... */
2373 GtkWidget *adv = name_to_widget (s, "adv_button");
2374 GtkWidget *std = name_to_widget (s, "std_button");
2378 gtk_widget_show (adv);
2379 gtk_widget_hide (std);
2381 else if (page_num == 1)
2383 gtk_widget_hide (adv);
2384 gtk_widget_show (std);
2392 G_MODULE_EXPORT void
2393 settings_cancel_cb (GtkButton *button, gpointer user_data)
2395 state *s = global_state_kludge; /* I hate C so much... */
2396 gtk_widget_hide (s->popup_widget);
2399 G_MODULE_EXPORT void
2400 settings_ok_cb (GtkButton *button, gpointer user_data)
2402 state *s = global_state_kludge; /* I hate C so much... */
2403 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2404 int page = gtk_notebook_get_current_page (notebook);
2407 /* Regenerate the command-line from the widget contents before saving.
2408 But don't do this if we're looking at the command-line page already,
2409 or we will blow away what they typed... */
2410 settings_sync_cmd_text (s);
2412 flush_popup_changes_and_save (s);
2413 gtk_widget_hide (s->popup_widget);
2417 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2419 state *s = (state *) data;
2420 settings_cancel_cb (0, (gpointer) s);
2426 /* Populating the various widgets
2430 /* Returns the number of the last hack run by the server.
2433 server_current_hack (void)
2437 unsigned long nitems, bytesafter;
2438 unsigned char *dataP = 0;
2439 Display *dpy = GDK_DISPLAY();
2440 int hack_number = -1;
2442 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2443 XA_SCREENSAVER_STATUS,
2444 0, 3, False, XA_INTEGER,
2445 &type, &format, &nitems, &bytesafter,
2448 && type == XA_INTEGER
2452 PROP32 *data = (PROP32 *) dataP;
2453 hack_number = (int) data[2] - 1;
2456 if (dataP) XFree (dataP);
2462 /* Finds the number of the last hack that was run, and makes that item be
2463 selected by default.
2466 scroll_to_current_hack (state *s)
2468 saver_preferences *p = &s->prefs;
2469 int hack_number = -1;
2471 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2472 hack_number = p->selected_hack;
2473 if (hack_number < 0) /* otherwise, use the last-run */
2474 hack_number = server_current_hack ();
2475 if (hack_number < 0) /* failing that, last "one mode" */
2476 hack_number = p->selected_hack;
2477 if (hack_number < 0) /* failing that, newest hack. */
2479 /* We should only get here if the user does not have a .xscreensaver
2480 file, and the screen has not been blanked with a hack since X
2481 started up: in other words, this is probably a fresh install.
2483 Instead of just defaulting to hack #0 (in either "programs" or
2484 "alphabetical" order) let's try to default to the last runnable
2485 hack in the "programs" list: this is probably the hack that was
2486 most recently added to the xscreensaver distribution (and so
2487 it's probably the currently-coolest one!)
2489 hack_number = p->screenhacks_count-1;
2490 while (hack_number > 0 &&
2491 ! (s->hacks_available_p[hack_number] &&
2492 p->screenhacks[hack_number]->enabled_p))
2496 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2498 int list_elt = s->hack_number_to_list_elt[hack_number];
2499 GtkWidget *list = name_to_widget (s, "list");
2500 force_list_select_item (s, list, list_elt, True);
2501 populate_demo_window (s, list_elt);
2507 populate_hack_list (state *s)
2509 Display *dpy = GDK_DISPLAY();
2511 saver_preferences *p = &s->prefs;
2512 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2513 GtkListStore *model;
2514 GtkTreeSelection *selection;
2515 GtkCellRenderer *ren;
2519 g_object_get (G_OBJECT (list),
2524 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2525 g_object_set (G_OBJECT (list), "model", model, NULL);
2526 g_object_unref (model);
2528 ren = gtk_cell_renderer_toggle_new ();
2529 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2531 "active", COL_ENABLED,
2534 g_signal_connect (ren, "toggled",
2535 G_CALLBACK (list_checkbox_cb),
2538 ren = gtk_cell_renderer_text_new ();
2539 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2540 _("Screen Saver"), ren,
2544 g_signal_connect_after (list, "row_activated",
2545 G_CALLBACK (list_activated_cb),
2548 selection = gtk_tree_view_get_selection (list);
2549 g_signal_connect (selection, "changed",
2550 G_CALLBACK (list_select_changed_cb),
2555 for (i = 0; i < s->list_count; i++)
2557 int hack_number = s->list_elt_to_hack_number[i];
2558 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2560 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2562 if (!hack) continue;
2564 /* If we're to suppress uninstalled hacks, check $PATH now. */
2565 if (p->ignore_uninstalled_p && !available_p)
2568 pretty_name = (hack->name
2569 ? strdup (hack->name)
2570 : make_hack_name (dpy, hack->command));
2574 /* Make the text foreground be the color of insensitive widgets
2575 (but don't actually make it be insensitive, since we still
2576 want to be able to click on it.)
2578 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
2579 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2580 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2581 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2583 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2584 /* " background=\"#%02X%02X%02X\"" */
2586 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2587 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2593 gtk_list_store_append (model, &iter);
2594 gtk_list_store_set (model, &iter,
2595 COL_ENABLED, hack->enabled_p,
2596 COL_NAME, pretty_name,
2601 #else /* !HAVE_GTK2 */
2603 saver_preferences *p = &s->prefs;
2604 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2606 for (i = 0; i < s->list_count; i++)
2608 int hack_number = s->list_elt_to_hack_number[i];
2609 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2611 /* A GtkList must contain only GtkListItems, but those can contain
2612 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2613 and a Label. We handle single and double click events on the
2614 line itself, for clicking on the text, but the interior checkbox
2615 also handles its own events.
2618 GtkWidget *line_hbox;
2619 GtkWidget *line_check;
2620 GtkWidget *line_label;
2622 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2624 if (!hack) continue;
2626 /* If we're to suppress uninstalled hacks, check $PATH now. */
2627 if (p->ignore_uninstalled_p && !available_p)
2630 pretty_name = (hack->name
2631 ? strdup (hack->name)
2632 : make_hack_name (hack->command));
2634 line = gtk_list_item_new ();
2635 line_hbox = gtk_hbox_new (FALSE, 0);
2636 line_check = gtk_check_button_new ();
2637 line_label = gtk_label_new (pretty_name);
2639 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2640 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2641 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2643 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2645 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2647 gtk_widget_show (line_check);
2648 gtk_widget_show (line_label);
2649 gtk_widget_show (line_hbox);
2650 gtk_widget_show (line);
2654 gtk_container_add (GTK_CONTAINER (list), line);
2655 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2656 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2659 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2660 GTK_SIGNAL_FUNC (list_checkbox_cb),
2663 gtk_widget_show (line);
2667 /* Make the widget be colored like insensitive widgets
2668 (but don't actually make it be insensitive, since we
2669 still want to be able to click on it.)
2671 GtkRcStyle *rc_style;
2674 gtk_widget_realize (GTK_WIDGET (line_label));
2676 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2677 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2679 rc_style = gtk_rc_style_new ();
2680 rc_style->fg[GTK_STATE_NORMAL] = fg;
2681 rc_style->bg[GTK_STATE_NORMAL] = bg;
2682 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2684 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2685 gtk_rc_style_unref (rc_style);
2689 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2690 GTK_SIGNAL_FUNC (list_select_cb),
2692 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2693 GTK_SIGNAL_FUNC (list_unselect_cb),
2695 #endif /* !HAVE_GTK2 */
2699 update_list_sensitivity (state *s)
2701 saver_preferences *p = &s->prefs;
2702 Bool sensitive = (p->mode == RANDOM_HACKS ||
2703 p->mode == RANDOM_HACKS_SAME ||
2704 p->mode == ONE_HACK);
2705 Bool checkable = (p->mode == RANDOM_HACKS ||
2706 p->mode == RANDOM_HACKS_SAME);
2707 Bool blankable = (p->mode != DONT_BLANK);
2710 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2711 GtkWidget *use = name_to_widget (s, "use_col_frame");
2712 #endif /* HAVE_GTK2 */
2713 GtkWidget *scroller = name_to_widget (s, "scroller");
2714 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2715 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2718 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2719 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2720 #else /* !HAVE_GTK2 */
2721 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2722 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2724 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2725 #endif /* !HAVE_GTK2 */
2726 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2727 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2729 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2732 gtk_tree_view_column_set_visible (use, checkable);
2733 #else /* !HAVE_GTK2 */
2735 gtk_widget_show (use); /* the "Use" column header */
2737 gtk_widget_hide (use);
2741 GtkBin *line = GTK_BIN (kids->data);
2742 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2743 GtkWidget *line_check =
2744 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2747 gtk_widget_show (line_check);
2749 gtk_widget_hide (line_check);
2753 #endif /* !HAVE_GTK2 */
2758 populate_prefs_page (state *s)
2760 saver_preferences *p = &s->prefs;
2762 Bool can_lock_p = True;
2764 /* Disable all the "lock" controls if locking support was not provided
2765 at compile-time, or if running on MacOS. */
2766 # if defined(NO_LOCKING) || defined(__APPLE__)
2771 /* If there is only one screen, the mode menu contains
2772 "random" but not "random-same".
2774 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2775 p->mode = RANDOM_HACKS;
2778 /* The file supports timeouts of less than a minute, but the GUI does
2779 not, so throttle the values to be at least one minute (since "0" is
2780 a bad rounding choice...)
2782 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2785 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2788 # define FMT_MINUTES(NAME,N) \
2789 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2791 # define FMT_SECONDS(NAME,N) \
2792 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2794 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2795 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2796 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2797 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2798 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2799 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2800 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2805 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2806 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2809 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2811 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2812 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2813 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2815 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2816 TOGGLE_ACTIVE ("dpms_quickoff_button", p->dpms_quickoff_p);
2817 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2818 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2819 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2820 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2821 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2822 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2826 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2827 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2828 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2829 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2830 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2833 # undef TOGGLE_ACTIVE
2835 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2836 (p->image_directory ? p->image_directory : ""));
2837 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2839 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2842 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2843 (p->text_literal ? p->text_literal : ""));
2844 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2845 (p->text_file ? p->text_file : ""));
2846 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2847 (p->text_program ? p->text_program : ""));
2848 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2849 (p->text_url ? p->text_url : ""));
2851 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2852 p->tmode == TEXT_LITERAL);
2853 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2854 p->tmode == TEXT_FILE);
2855 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2856 p->tmode == TEXT_FILE);
2857 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2858 p->tmode == TEXT_PROGRAM);
2859 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2860 p->tmode == TEXT_PROGRAM);
2861 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2862 p->tmode == TEXT_URL);
2865 /* Map the `saver_mode' enum to mode menu to values. */
2867 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2870 for (i = 0; i < countof(mode_menu_order); i++)
2871 if (mode_menu_order[i] == p->mode)
2873 gtk_option_menu_set_history (opt, i);
2874 update_list_sensitivity (s);
2878 Bool found_any_writable_cells = False;
2879 Bool fading_possible = False;
2880 Bool dpms_supported = False;
2882 Display *dpy = GDK_DISPLAY();
2883 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2885 for (i = 0; i < nscreens; i++)
2887 Screen *s = ScreenOfDisplay (dpy, i);
2888 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2890 found_any_writable_cells = True;
2895 fading_possible = found_any_writable_cells;
2896 #ifdef HAVE_XF86VMODE_GAMMA
2897 fading_possible = True;
2900 #ifdef HAVE_DPMS_EXTENSION
2902 int op = 0, event = 0, error = 0;
2903 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2904 dpms_supported = True;
2906 #endif /* HAVE_DPMS_EXTENSION */
2909 # define SENSITIZE(NAME,SENSITIVEP) \
2910 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2912 /* Blanking and Locking
2914 SENSITIZE ("lock_button", can_lock_p);
2915 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2916 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2921 SENSITIZE ("dpms_frame", dpms_supported);
2922 SENSITIZE ("dpms_button", dpms_supported);
2923 SENSITIZE ("dpms_quickoff_button", dpms_supported);
2925 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2926 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2927 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2928 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2929 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2930 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2931 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2932 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2933 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2937 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2938 SENSITIZE ("install_button", found_any_writable_cells);
2939 SENSITIZE ("fade_button", fading_possible);
2940 SENSITIZE ("unfade_button", fading_possible);
2942 SENSITIZE ("fade_label", (fading_possible &&
2943 (p->fade_p || p->unfade_p)));
2944 SENSITIZE ("fade_spinbutton", (fading_possible &&
2945 (p->fade_p || p->unfade_p)));
2953 populate_popup_window (state *s)
2955 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2956 char *doc_string = 0;
2958 /* #### not in Gtk 1.2
2959 gtk_label_set_selectable (doc);
2965 free_conf_data (s->cdata);
2970 saver_preferences *p = &s->prefs;
2971 int list_elt = selected_list_element (s);
2972 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2973 ? s->list_elt_to_hack_number[list_elt]
2975 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2978 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2979 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2980 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2981 s->cdata = load_configurator (cmd_line, s->debug_p);
2982 if (s->cdata && s->cdata->widget)
2983 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2988 doc_string = (s->cdata
2989 ? s->cdata->description
2991 # else /* !HAVE_XML */
2992 doc_string = _("Descriptions not available: no XML support compiled in.");
2993 # endif /* !HAVE_XML */
2995 gtk_label_set_text (doc, (doc_string
2997 : _("No description available.")));
3002 sensitize_demo_widgets (state *s, Bool sensitive_p)
3004 const char *names[] = { "demo", "settings",
3005 "cmd_label", "cmd_text", "manual",
3006 "visual", "visual_combo" };
3008 for (i = 0; i < countof(names); i++)
3010 GtkWidget *w = name_to_widget (s, names[i]);
3011 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
3017 sensitize_menu_items (state *s, Bool force_p)
3019 static Bool running_p = False;
3020 static time_t last_checked = 0;
3021 time_t now = time ((time_t *) 0);
3022 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
3026 if (force_p || now > last_checked + 10) /* check every 10 seconds */
3028 running_p = xscreensaver_running_p (s);
3029 last_checked = time ((time_t *) 0);
3032 for (i = 0; i < countof(names); i++)
3034 GtkWidget *w = name_to_widget (s, names[i]);
3035 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
3040 /* When the File menu is de-posted after a "Restart Daemon" command,
3041 the window underneath doesn't repaint for some reason. I guess this
3042 is a bug in exposure handling in GTK or GDK. This works around it.
3045 force_dialog_repaint (state *s)
3048 /* Tell GDK to invalidate and repaint the whole window.
3050 GdkWindow *w = GET_WINDOW (s->toplevel_widget);
3051 GdkRegion *region = gdk_region_new ();
3053 rect.x = rect.y = 0;
3054 rect.width = rect.height = 32767;
3055 gdk_region_union_with_rect (region, &rect);
3056 gdk_window_invalidate_region (w, region, True);
3057 gdk_region_destroy (region);
3058 gdk_window_process_updates (w, True);
3060 /* Force the server to send an exposure event by creating and then
3061 destroying a window as a child of the top level shell.
3063 Display *dpy = GDK_DISPLAY();
3064 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
3066 XWindowAttributes xgwa;
3067 XGetWindowAttributes (dpy, parent, &xgwa);
3068 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
3069 XMapRaised (dpy, w);
3070 XDestroyWindow (dpy, w);
3076 /* Even though we've given these text fields a maximum number of characters,
3077 their default size is still about 30 characters wide -- so measure out
3078 a string in their font, and resize them to just fit that.
3081 fix_text_entry_sizes (state *s)
3085 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
3086 const char * const spinbuttons[] = {
3087 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3088 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3089 "dpms_off_spinbutton",
3090 "-fade_spinbutton" };
3094 for (i = 0; i < countof(spinbuttons); i++)
3096 const char *n = spinbuttons[i];
3098 while (*n == '-') n++, cols--;
3099 w = GTK_WIDGET (name_to_widget (s, n));
3100 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3101 gtk_widget_set_usize (w, width, -2);
3104 /* Now fix the width of the combo box.
3106 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3107 w = GTK_COMBO (w)->entry;
3108 width = gdk_string_width (w->style->font, "PseudoColor___");
3109 gtk_widget_set_usize (w, width, -2);
3111 /* Now fix the width of the file entry text.
3113 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3114 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3115 gtk_widget_set_usize (w, width, -2);
3117 /* Now fix the width of the command line text.
3119 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3120 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3121 gtk_widget_set_usize (w, width, -2);
3125 /* Now fix the height of the list widget:
3126 make it default to being around 10 text-lines high instead of 4.
3128 w = GTK_WIDGET (name_to_widget (s, "list"));
3132 int leading = 3; /* approximate is ok... */
3136 PangoFontMetrics *pain =
3137 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3138 gtk_widget_get_style (w)->font_desc,
3139 gtk_get_default_language ());
3140 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3141 pango_font_metrics_get_descent (pain));
3142 #else /* !HAVE_GTK2 */
3143 height = w->style->font->ascent + w->style->font->descent;
3144 #endif /* !HAVE_GTK2 */
3148 height += border * 2;
3149 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3150 gtk_widget_set_usize (w, -2, height);
3157 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3160 static char *up_arrow_xpm[] = {
3183 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3184 the end of the array (Gtk 1.2.5.) */
3185 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3186 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3189 static char *down_arrow_xpm[] = {
3212 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3213 the end of the array (Gtk 1.2.5.) */
3214 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3215 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3219 pixmapify_button (state *s, int down_p)
3223 GtkWidget *pixmapwid;
3227 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3228 style = gtk_widget_get_style (w);
3230 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3231 &style->bg[GTK_STATE_NORMAL],
3233 ? (gchar **) down_arrow_xpm
3234 : (gchar **) up_arrow_xpm));
3235 pixmapwid = gtk_pixmap_new (pixmap, mask);
3236 gtk_widget_show (pixmapwid);
3237 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3238 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3242 map_next_button_cb (GtkWidget *w, gpointer user_data)
3244 state *s = (state *) user_data;
3245 pixmapify_button (s, 1);
3249 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3251 state *s = (state *) user_data;
3252 pixmapify_button (s, 0);
3254 #endif /* !HAVE_GTK2 */
3258 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3262 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3263 GtkAllocation *allocation,
3267 GtkWidgetAuxInfo *aux_info;
3269 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3271 aux_info->width = allocation->width;
3272 aux_info->height = -2;
3276 gtk_widget_size_request (label, &req);
3279 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3282 eschew_gtk_lossage (GtkLabel *label)
3284 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3285 aux_info->width = GTK_WIDGET (label)->allocation.width;
3286 aux_info->height = -2;
3290 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3292 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3293 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3296 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3298 gtk_widget_queue_resize (GTK_WIDGET (label));
3300 #endif /* !HAVE_GTK2 */
3304 populate_demo_window (state *s, int list_elt)
3306 Display *dpy = GDK_DISPLAY();
3307 saver_preferences *p = &s->prefs;
3310 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3311 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3312 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3313 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3314 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3316 if (p->mode == BLANK_ONLY)
3319 pretty_name = strdup (_("Blank Screen"));
3320 schedule_preview (s, 0);
3322 else if (p->mode == DONT_BLANK)
3325 pretty_name = strdup (_("Screen Saver Disabled"));
3326 schedule_preview (s, 0);
3330 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3331 ? s->list_elt_to_hack_number[list_elt]
3333 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3337 ? strdup (hack->name)
3338 : make_hack_name (dpy, hack->command))
3342 schedule_preview (s, hack->command);
3344 schedule_preview (s, 0);
3348 pretty_name = strdup (_("Preview"));
3350 gtk_frame_set_label (frame1, _(pretty_name));
3351 gtk_frame_set_label (frame2, _(pretty_name));
3353 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3354 gtk_entry_set_position (cmd, 0);
3358 sprintf (title, _("%s: %.100s Settings"),
3359 progclass, (pretty_name ? pretty_name : "???"));
3360 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3363 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3365 ? (hack->visual && *hack->visual
3370 sensitize_demo_widgets (s, (hack ? True : False));
3372 if (pretty_name) free (pretty_name);
3374 ensure_selected_item_visible (list);
3376 s->_selected_list_element = list_elt;
3381 widget_deleter (GtkWidget *widget, gpointer data)
3383 /* #### Well, I want to destroy these widgets, but if I do that, they get
3384 referenced again, and eventually I get a SEGV. So instead of
3385 destroying them, I'll just hide them, and leak a bunch of memory
3386 every time the disk file changes. Go go go Gtk!
3388 #### Ok, that's a lie, I get a crash even if I just hide the widget
3389 and don't ever delete it. Fuck!
3392 gtk_widget_destroy (widget);
3394 gtk_widget_hide (widget);
3399 static char **sort_hack_cmp_names_kludge;
3401 sort_hack_cmp (const void *a, const void *b)
3407 int aa = *(int *) a;
3408 int bb = *(int *) b;
3409 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3410 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3411 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3417 initialize_sort_map (state *s)
3419 Display *dpy = GDK_DISPLAY();
3420 saver_preferences *p = &s->prefs;
3423 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3424 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3425 if (s->hacks_available_p) free (s->hacks_available_p);
3427 s->list_elt_to_hack_number = (int *)
3428 calloc (sizeof(int), p->screenhacks_count + 1);
3429 s->hack_number_to_list_elt = (int *)
3430 calloc (sizeof(int), p->screenhacks_count + 1);
3431 s->hacks_available_p = (Bool *)
3432 calloc (sizeof(Bool), p->screenhacks_count + 1);
3433 s->total_available = 0;
3435 /* Check which hacks actually exist on $PATH
3437 for (i = 0; i < p->screenhacks_count; i++)
3439 screenhack *hack = p->screenhacks[i];
3440 int on = on_path_p (hack->command) ? 1 : 0;
3441 s->hacks_available_p[i] = on;
3442 s->total_available += on;
3445 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3449 for (i = 0; i < p->screenhacks_count; i++)
3451 if (!p->ignore_uninstalled_p ||
3452 s->hacks_available_p[i])
3453 s->list_elt_to_hack_number[j++] = i;
3457 for (; j < p->screenhacks_count; j++)
3458 s->list_elt_to_hack_number[j] = -1;
3461 /* Generate list of sortable names (once)
3463 sort_hack_cmp_names_kludge = (char **)
3464 calloc (sizeof(char *), p->screenhacks_count);
3465 for (i = 0; i < p->screenhacks_count; i++)
3467 screenhack *hack = p->screenhacks[i];
3468 char *name = (hack->name && *hack->name
3469 ? strdup (hack->name)
3470 : make_hack_name (dpy, hack->command));
3472 for (str = name; *str; str++)
3473 *str = tolower(*str);
3474 sort_hack_cmp_names_kludge[i] = name;
3477 /* Sort list->hack map alphabetically
3479 qsort (s->list_elt_to_hack_number,
3480 p->screenhacks_count,
3481 sizeof(*s->list_elt_to_hack_number),
3486 for (i = 0; i < p->screenhacks_count; i++)
3487 free (sort_hack_cmp_names_kludge[i]);
3488 free (sort_hack_cmp_names_kludge);
3489 sort_hack_cmp_names_kludge = 0;
3491 /* Build inverse table */
3492 for (i = 0; i < p->screenhacks_count; i++)
3494 int n = s->list_elt_to_hack_number[i];
3496 s->hack_number_to_list_elt[n] = i;
3502 maybe_reload_init_file (state *s)
3504 Display *dpy = GDK_DISPLAY();
3505 saver_preferences *p = &s->prefs;
3508 static Bool reentrant_lock = False;
3509 if (reentrant_lock) return 0;
3510 reentrant_lock = True;
3512 if (init_file_changed_p (p))
3514 const char *f = init_file_name();
3519 if (!f || !*f) return 0;
3520 b = (char *) malloc (strlen(f) + 1024);
3523 "file \"%s\" has changed, reloading.\n"),
3525 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3528 load_init_file (dpy, p);
3529 initialize_sort_map (s);
3531 list_elt = selected_list_element (s);
3532 list = name_to_widget (s, "list");
3533 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3534 populate_hack_list (s);
3535 force_list_select_item (s, list, list_elt, True);
3536 populate_prefs_page (s);
3537 populate_demo_window (s, list_elt);
3538 ensure_selected_item_visible (list);
3543 reentrant_lock = False;
3549 /* Making the preview window have the right X visual (so that GL works.)
3552 static Visual *get_best_gl_visual (state *);
3555 x_visual_to_gdk_visual (Visual *xv)
3557 GList *gvs = gdk_list_visuals();
3558 if (!xv) return gdk_visual_get_system();
3559 for (; gvs; gvs = gvs->next)
3561 GdkVisual *gv = (GdkVisual *) gvs->data;
3562 if (xv == GDK_VISUAL_XVISUAL (gv))
3565 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3566 blurb(), (unsigned long) xv->visualid);
3571 clear_preview_window (state *s)
3577 if (!s->toplevel_widget) return; /* very early */
3578 p = name_to_widget (s, "preview");
3579 window = GET_WINDOW (p);
3581 if (!window) return;
3583 /* Flush the widget background down into the window, in case a subproc
3585 style = gtk_widget_get_style (p);
3586 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
3587 gdk_window_clear (window);
3590 int list_elt = selected_list_element (s);
3591 int hack_number = (list_elt >= 0
3592 ? s->list_elt_to_hack_number[list_elt]
3594 Bool available_p = (hack_number >= 0
3595 ? s->hacks_available_p [hack_number]
3597 Bool nothing_p = (s->total_available < 5);
3600 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3601 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3602 (s->running_preview_error_p
3603 ? (available_p ? 1 :
3606 #else /* !HAVE_GTK2 */
3607 if (s->running_preview_error_p)
3609 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3610 const char * const lines2[] = { N_("Not"), N_("Installed") };
3611 int nlines = countof(lines1);
3612 int lh = p->style->font->ascent + p->style->font->descent;
3616 const char * const *lines = (available_p ? lines1 : lines2);
3618 gdk_window_get_size (window, &w, &h);
3619 y = (h - (lh * nlines)) / 2;
3620 y += p->style->font->ascent;
3621 for (i = 0; i < nlines; i++)
3623 int sw = gdk_string_width (p->style->font, _(lines[i]));
3624 int x = (w - sw) / 2;
3625 gdk_draw_string (window, p->style->font,
3626 p->style->fg_gc[GTK_STATE_NORMAL],
3631 #endif /* !HAVE_GTK2 */
3639 reset_preview_window (state *s)
3641 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3642 when you kill one and re-start another on the same window. So maybe
3643 it's best to just always destroy and recreate the preview window
3644 when changing hacks, instead of always trying to reuse the same one?
3646 GtkWidget *pr = name_to_widget (s, "preview");
3647 if (GET_REALIZED (pr))
3649 GdkWindow *window = GET_WINDOW (pr);
3650 Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3652 gtk_widget_hide (pr);
3653 gtk_widget_unrealize (pr);
3654 gtk_widget_realize (pr);
3655 gtk_widget_show (pr);
3656 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3658 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3666 fix_preview_visual (state *s)
3668 GtkWidget *widget = name_to_widget (s, "preview");
3669 Visual *xvisual = get_best_gl_visual (s);
3670 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3671 GdkVisual *dvisual = gdk_visual_get_system();
3672 GdkColormap *cmap = (visual == dvisual
3673 ? gdk_colormap_get_system ()
3674 : gdk_colormap_new (visual, False));
3677 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3678 (visual == dvisual ? "default" : "non-default"),
3679 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3681 if (!GET_REALIZED (widget) ||
3682 gtk_widget_get_visual (widget) != visual)
3684 gtk_widget_unrealize (widget);
3685 gtk_widget_set_visual (widget, visual);
3686 gtk_widget_set_colormap (widget, cmap);
3687 gtk_widget_realize (widget);
3690 /* Set the Widget colors to be white-on-black. */
3692 GdkWindow *window = GET_WINDOW (widget);
3693 GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
3694 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3695 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3696 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3697 GdkGC *fgc = gdk_gc_new(window);
3698 GdkGC *bgc = gdk_gc_new(window);
3699 if (!gdk_color_white (cmap, fg)) abort();
3700 if (!gdk_color_black (cmap, bg)) abort();
3701 gdk_gc_set_foreground (fgc, fg);
3702 gdk_gc_set_background (fgc, bg);
3703 gdk_gc_set_foreground (bgc, bg);
3704 gdk_gc_set_background (bgc, fg);
3705 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3706 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3707 gtk_widget_set_style (widget, style);
3709 /* For debugging purposes, put a title on the window (so that
3710 it can be easily found in the output of "xwininfo -tree".)
3712 gdk_window_set_title (window, "Preview");
3715 gtk_widget_show (widget);
3723 subproc_pretty_name (state *s)
3725 if (s->running_preview_cmd)
3727 char *ps = strdup (s->running_preview_cmd);
3728 char *ss = strchr (ps, ' ');
3730 ss = strrchr (ps, '/');
3741 return strdup ("???");
3746 reap_zombies (state *s)
3748 int wait_status = 0;
3750 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3754 if (pid == s->running_preview_pid)
3756 char *ss = subproc_pretty_name (s);
3757 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3758 (unsigned long) pid, ss);
3762 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3763 (unsigned long) pid);
3769 /* Mostly lifted from driver/subprocs.c */
3771 get_best_gl_visual (state *s)
3773 Display *dpy = GDK_DISPLAY();
3782 av[ac++] = "xscreensaver-gl-helper";
3787 perror ("error creating pipe:");
3794 switch ((int) (forked = fork ()))
3798 sprintf (buf, "%s: couldn't fork", blurb());
3806 close (in); /* don't need this one */
3807 close (ConnectionNumber (dpy)); /* close display fd */
3809 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3811 perror ("could not dup() a new stdout:");
3815 execvp (av[0], av); /* shouldn't return. */
3817 if (errno != ENOENT)
3819 /* Ignore "no such file or directory" errors, unless verbose.
3820 Issue all other exec errors, though. */
3821 sprintf (buf, "%s: running %s", blurb(), av[0]);
3825 /* Note that one must use _exit() instead of exit() in procs forked
3826 off of Gtk programs -- Gtk installs an atexit handler that has a
3827 copy of the X connection (which we've already closed, for safety.)
3828 If one uses exit() instead of _exit(), then one sometimes gets a
3829 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3831 _exit (1); /* exits fork */
3837 int wait_status = 0;
3839 FILE *f = fdopen (in, "r");
3843 close (out); /* don't need this one */
3846 if (!fgets (buf, sizeof(buf)-1, f))
3850 /* Wait for the child to die. */
3851 waitpid (-1, &wait_status, 0);
3853 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3859 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3865 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3867 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3868 blurb(), av[0], result);
3880 kill_preview_subproc (state *s, Bool reset_p)
3882 s->running_preview_error_p = False;
3885 clear_preview_window (s);
3887 if (s->subproc_check_timer_id)
3889 gtk_timeout_remove (s->subproc_check_timer_id);
3890 s->subproc_check_timer_id = 0;
3891 s->subproc_check_countdown = 0;
3894 if (s->running_preview_pid)
3896 int status = kill (s->running_preview_pid, SIGTERM);
3897 char *ss = subproc_pretty_name (s);
3904 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3905 blurb(), (unsigned long) s->running_preview_pid, ss);
3910 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3911 blurb(), (unsigned long) s->running_preview_pid, ss);
3917 waitpid(s->running_preview_pid, &endstatus, 0);
3919 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3920 (unsigned long) s->running_preview_pid, ss);
3924 s->running_preview_pid = 0;
3925 if (s->running_preview_cmd) free (s->running_preview_cmd);
3926 s->running_preview_cmd = 0;
3933 reset_preview_window (s);
3934 clear_preview_window (s);
3939 /* Immediately and unconditionally launches the given process,
3940 after appending the -window-id option; sets running_preview_pid.
3943 launch_preview_subproc (state *s)
3945 saver_preferences *p = &s->prefs;
3949 const char *cmd = s->desired_preview_cmd;
3951 GtkWidget *pr = name_to_widget (s, "preview");
3954 reset_preview_window (s);
3956 window = GET_WINDOW (pr);
3958 s->running_preview_error_p = False;
3960 if (s->preview_suppressed_p)
3962 kill_preview_subproc (s, False);
3966 new_cmd = malloc (strlen (cmd) + 40);
3968 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3971 /* No window id? No command to run. */
3977 strcpy (new_cmd, cmd);
3978 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3982 kill_preview_subproc (s, False);
3985 s->running_preview_error_p = True;
3986 clear_preview_window (s);
3990 switch ((int) (forked = fork ()))
3995 sprintf (buf, "%s: couldn't fork", blurb());
3997 s->running_preview_error_p = True;
4003 close (ConnectionNumber (GDK_DISPLAY()));
4005 hack_subproc_environment (id, s->debug_p);
4007 usleep (250000); /* pause for 1/4th second before launching, to give
4008 the previous program time to die and flush its X
4009 buffer, so we don't get leftover turds on the
4012 exec_command (p->shell, new_cmd, p->nice_inferior);
4013 /* Don't bother printing an error message when we are unable to
4014 exec subprocesses; we handle that by polling the pid later.
4016 Note that one must use _exit() instead of exit() in procs forked
4017 off of Gtk programs -- Gtk installs an atexit handler that has a
4018 copy of the X connection (which we've already closed, for safety.)
4019 If one uses exit() instead of _exit(), then one sometimes gets a
4020 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
4022 _exit (1); /* exits child fork */
4027 if (s->running_preview_cmd) free (s->running_preview_cmd);
4028 s->running_preview_cmd = strdup (s->desired_preview_cmd);
4029 s->running_preview_pid = forked;
4033 char *ss = subproc_pretty_name (s);
4034 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
4035 (unsigned long) forked, ss);
4042 schedule_preview_check (s);
4045 if (new_cmd) free (new_cmd);
4050 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
4053 hack_environment (state *s)
4055 static const char *def_path =
4056 # ifdef DEFAULT_PATH_PREFIX
4057 DEFAULT_PATH_PREFIX;
4062 Display *dpy = GDK_DISPLAY();
4063 const char *odpy = DisplayString (dpy);
4064 char *ndpy = (char *) malloc(strlen(odpy) + 20);
4065 strcpy (ndpy, "DISPLAY=");
4066 strcat (ndpy, odpy);
4071 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4073 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4074 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4075 So we must leak it (and/or the previous setting). Yay.
4078 if (def_path && *def_path)
4080 const char *opath = getenv("PATH");
4081 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4082 strcpy (npath, "PATH=");
4083 strcat (npath, def_path);
4084 strcat (npath, ":");
4085 strcat (npath, opath);
4089 /* do not free(npath) -- see above */
4092 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4098 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4100 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4101 necessary yet, but it will make programs work if we had invoked
4102 them with "-root" and not with "-window-id" -- which, of course,
4105 char *nssw = (char *) malloc (40);
4106 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4108 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4109 any more, right? It's not Posix, but everyone seems to have it. */
4114 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4116 /* do not free(nssw) -- see above */
4120 /* Called from a timer:
4121 Launches the currently-chosen subprocess, if it's not already running.
4122 If there's a different process running, kills it.
4125 update_subproc_timer (gpointer data)
4127 state *s = (state *) data;
4128 if (! s->desired_preview_cmd)
4129 kill_preview_subproc (s, True);
4130 else if (!s->running_preview_cmd ||
4131 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4132 launch_preview_subproc (s);
4134 s->subproc_timer_id = 0;
4135 return FALSE; /* do not re-execute timer */
4139 settings_timer (gpointer data)
4146 /* Call this when you think you might want a preview process running.
4147 It will set a timer that will actually launch that program a second
4148 from now, if you haven't changed your mind (to avoid double-click
4149 spazzing, etc.) `cmd' may be null meaning "no process".
4152 schedule_preview (state *s, const char *cmd)
4154 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4159 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4161 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4164 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4165 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4167 if (s->subproc_timer_id)
4168 gtk_timeout_remove (s->subproc_timer_id);
4169 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4173 /* Called from a timer:
4174 Checks to see if the subproc that should be running, actually is.
4177 check_subproc_timer (gpointer data)
4179 state *s = (state *) data;
4180 Bool again_p = True;
4182 if (s->running_preview_error_p || /* already dead */
4183 s->running_preview_pid <= 0)
4191 status = kill (s->running_preview_pid, 0);
4192 if (status < 0 && errno == ESRCH)
4193 s->running_preview_error_p = True;
4197 char *ss = subproc_pretty_name (s);
4198 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4199 (unsigned long) s->running_preview_pid, ss,
4200 (s->running_preview_error_p ? "dead" : "alive"));
4204 if (s->running_preview_error_p)
4206 clear_preview_window (s);
4211 /* Otherwise, it's currently alive. We might be checking again, or we
4212 might be satisfied. */
4214 if (--s->subproc_check_countdown <= 0)
4218 return TRUE; /* re-execute timer */
4221 s->subproc_check_timer_id = 0;
4222 s->subproc_check_countdown = 0;
4223 return FALSE; /* do not re-execute timer */
4228 /* Call this just after launching a subprocess.
4229 This sets a timer that will, five times a second for two seconds,
4230 check whether the program is still running. The assumption here
4231 is that if the process didn't stay up for more than a couple of
4232 seconds, then either the program doesn't exist, or it doesn't
4233 take a -window-id argument.
4236 schedule_preview_check (state *s)
4242 fprintf (stderr, "%s: scheduling check\n", blurb());
4244 if (s->subproc_check_timer_id)
4245 gtk_timeout_remove (s->subproc_check_timer_id);
4246 s->subproc_check_timer_id =
4247 gtk_timeout_add (1000 / ticks,
4248 check_subproc_timer, (gpointer) s);
4249 s->subproc_check_countdown = ticks * seconds;
4254 screen_blanked_p (void)
4258 unsigned long nitems, bytesafter;
4259 unsigned char *dataP = 0;
4260 Display *dpy = GDK_DISPLAY();
4261 Bool blanked_p = False;
4263 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4264 XA_SCREENSAVER_STATUS,
4265 0, 3, False, XA_INTEGER,
4266 &type, &format, &nitems, &bytesafter,
4269 && type == XA_INTEGER
4273 Atom *data = (Atom *) dataP;
4274 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4277 if (dataP) XFree (dataP);
4282 /* Wake up every now and then and see if the screen is blanked.
4283 If it is, kill off the small-window demo -- no point in wasting
4284 cycles by running two screensavers at once...
4287 check_blanked_timer (gpointer data)
4289 state *s = (state *) data;
4290 Bool blanked_p = screen_blanked_p ();
4291 if (blanked_p && s->running_preview_pid)
4294 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4295 kill_preview_subproc (s, True);
4298 return True; /* re-execute timer */
4302 /* How many screens are there (including Xinerama.)
4305 screen_count (Display *dpy)
4307 int nscreens = ScreenCount(dpy);
4308 # ifdef HAVE_XINERAMA
4311 int event_number, error_number;
4312 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4313 XineramaIsActive (dpy))
4315 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4316 if (xsi) XFree (xsi);
4319 # endif /* HAVE_XINERAMA */
4325 /* Setting window manager icon
4329 init_icon (GdkWindow *window)
4331 GdkBitmap *mask = 0;
4334 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4335 (gchar **) logo_50_xpm);
4337 gdk_window_set_icon (window, 0, pixmap, mask);
4341 /* The main demo-mode command loop.
4346 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4347 XrmRepresentation *type, XrmValue *value, XPointer closure)
4350 for (i = 0; quarks[i]; i++)
4352 if (bindings[i] == XrmBindTightly)
4353 fprintf (stderr, (i == 0 ? "" : "."));
4354 else if (bindings[i] == XrmBindLoosely)
4355 fprintf (stderr, "*");
4357 fprintf (stderr, " ??? ");
4358 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4361 fprintf (stderr, ": %s\n", (char *) value->addr);
4369 gnome_screensaver_window (Screen *screen)
4371 Display *dpy = DisplayOfScreen (screen);
4372 Window root = RootWindowOfScreen (screen);
4373 Window parent, *kids;
4375 Window gnome_window = 0;
4378 if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4380 for (i = 0; i < nkids; i++)
4384 unsigned long nitems, bytesafter;
4385 unsigned char *name;
4386 if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4387 False, XA_STRING, &type, &format, &nitems,
4391 && !strcmp ((char *) name, "gnome-screensaver"))
4393 gnome_window = kids[i];
4398 if (kids) XFree ((char *) kids);
4399 return gnome_window;
4403 gnome_screensaver_active_p (void)
4405 Display *dpy = GDK_DISPLAY();
4406 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4407 return (w ? True : False);
4411 kill_gnome_screensaver (void)
4413 Display *dpy = GDK_DISPLAY();
4414 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4415 if (w) XKillClient (dpy, (XID) w);
4419 kde_screensaver_active_p (void)
4421 FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4424 fgets (buf, sizeof(buf)-1, p);
4426 if (!strcmp (buf, "true\n"))
4433 kill_kde_screensaver (void)
4435 system ("dcop kdesktop KScreensaverIface enable false");
4440 the_network_is_not_the_computer (state *s)
4442 Display *dpy = GDK_DISPLAY();
4443 char *rversion = 0, *ruser = 0, *rhost = 0;
4444 char *luser, *lhost;
4446 struct passwd *p = getpwuid (getuid ());
4447 const char *d = DisplayString (dpy);
4449 # if defined(HAVE_UNAME)
4451 if (uname (&uts) < 0)
4452 lhost = "<UNKNOWN>";
4454 lhost = uts.nodename;
4456 strcpy (lhost, getenv("SYS$NODE"));
4457 # else /* !HAVE_UNAME && !VMS */
4458 strcat (lhost, "<UNKNOWN>");
4459 # endif /* !HAVE_UNAME && !VMS */
4461 if (p && p->pw_name)
4466 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4468 /* Make a buffer that's big enough for a number of copies of all the
4469 strings, plus some. */
4470 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4471 (ruser ? strlen(ruser) : 0) +
4472 (rhost ? strlen(rhost) : 0) +
4479 if (!rversion || !*rversion)
4483 "The XScreenSaver daemon doesn't seem to be running\n"
4484 "on display \"%s\". Launch it now?"),
4487 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4489 /* Warn that the two processes are running as different users.
4493 "%s is running as user \"%s\" on host \"%s\".\n"
4494 "But the xscreensaver managing display \"%s\"\n"
4495 "is running as user \"%s\" on host \"%s\".\n"
4497 "Since they are different users, they won't be reading/writing\n"
4498 "the same ~/.xscreensaver file, so %s isn't\n"
4499 "going to work right.\n"
4501 "You should either re-run %s as \"%s\", or re-run\n"
4502 "xscreensaver as \"%s\".\n"
4504 "Restart the xscreensaver daemon now?\n"),
4505 progname, luser, lhost,
4507 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4509 progname, (ruser ? ruser : "???"),
4512 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4514 /* Warn that the two processes are running on different hosts.
4518 "%s is running as user \"%s\" on host \"%s\".\n"
4519 "But the xscreensaver managing display \"%s\"\n"
4520 "is running as user \"%s\" on host \"%s\".\n"
4522 "If those two machines don't share a file system (that is,\n"
4523 "if they don't see the same ~%s/.xscreensaver file) then\n"
4524 "%s won't work right.\n"
4526 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4527 progname, luser, lhost,
4529 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4534 else if (!!strcmp (rversion, s->short_version))
4536 /* Warn that the version numbers don't match.
4540 "This is %s version %s.\n"
4541 "But the xscreensaver managing display \"%s\"\n"
4542 "is version %s. This could cause problems.\n"
4544 "Restart the xscreensaver daemon now?\n"),
4545 progname, s->short_version,
4552 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4554 if (rversion) free (rversion);
4555 if (ruser) free (ruser);
4556 if (rhost) free (rhost);
4560 /* Note: since these dialogs are not modal, they will stack up.
4561 So we do this check *after* popping up the "xscreensaver is not
4562 running" dialog so that these are on top. Good enough.
4565 if (gnome_screensaver_active_p ())
4566 warning_dialog (s->toplevel_widget,
4568 "The GNOME screensaver daemon appears to be running.\n"
4569 "It must be stopped for XScreenSaver to work properly.\n"
4571 "Stop the GNOME screen saver daemon now?\n"),
4574 if (kde_screensaver_active_p ())
4575 warning_dialog (s->toplevel_widget,
4577 "The KDE screen saver daemon appears to be running.\n"
4578 "It must be stopped for XScreenSaver to work properly.\n"
4580 "Stop the KDE screen saver daemon now?\n"),
4585 /* We use this error handler so that X errors are preceeded by the name
4586 of the program that generated them.
4589 demo_ehandler (Display *dpy, XErrorEvent *error)
4591 state *s = global_state_kludge; /* I hate C so much... */
4592 fprintf (stderr, "\nX error in %s:\n", blurb());
4593 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4594 kill_preview_subproc (s, False);
4600 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4601 of the program that generated them; and also that we can ignore one
4602 particular bogus error message that Gdk madly spews.
4605 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4606 const gchar *message, gpointer user_data)
4608 /* Ignore the message "Got event for unknown window: 0x...".
4609 Apparently some events are coming in for the xscreensaver window
4610 (presumably reply events related to the ClientMessage) and Gdk
4611 feels the need to complain about them. So, just suppress any
4612 messages that look like that one.
4614 if (strstr (message, "unknown window"))
4617 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4618 (log_domain ? log_domain : progclass),
4619 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4620 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4621 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4622 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4623 log_level == G_LOG_LEVEL_INFO ? "info" :
4624 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4626 ((!*message || message[strlen(message)-1] != '\n')
4632 __extension__ /* shut up about "string length is greater than the length
4633 ISO C89 compilers are required to support" when including
4638 static char *defaults[] = {
4639 #include "XScreenSaver_ad.h"
4644 #ifdef HAVE_CRAPPLET
4645 static struct poptOption crapplet_options[] = {
4646 {NULL, '\0', 0, NULL, 0}
4648 #endif /* HAVE_CRAPPLET */
4651 const char *usage = "[--display dpy] [--prefs | --settings]"
4652 # ifdef HAVE_CRAPPLET
4655 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4658 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4660 state *s = (state *) user_data;
4661 Boolean oi = s->initializing_p;
4663 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4665 s->initializing_p = True;
4667 eschew_gtk_lossage (label);
4669 s->initializing_p = oi;
4675 print_widget_tree (GtkWidget *w, int depth)
4678 for (i = 0; i < depth; i++)
4679 fprintf (stderr, " ");
4680 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4682 if (GTK_IS_LIST (w))
4684 for (i = 0; i < depth+1; i++)
4685 fprintf (stderr, " ");
4686 fprintf (stderr, "...list kids...\n");
4688 else if (GTK_IS_CONTAINER (w))
4690 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4693 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4701 delayed_scroll_kludge (gpointer data)
4703 state *s = (state *) data;
4704 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4705 ensure_selected_item_visible (w);
4707 /* Oh, this is just fucking lovely, too. */
4708 w = GTK_WIDGET (name_to_widget (s, "preview"));
4709 gtk_widget_hide (w);
4710 gtk_widget_show (w);
4712 return FALSE; /* do not re-execute timer */
4718 create_xscreensaver_demo (void)
4722 nb = name_to_widget (global_state_kludge, "preview_notebook");
4723 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4725 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4729 create_xscreensaver_settings_dialog (void)
4733 box = name_to_widget (global_state_kludge, "dialog_action_area");
4735 w = name_to_widget (global_state_kludge, "adv_button");
4736 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4738 w = name_to_widget (global_state_kludge, "std_button");
4739 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4741 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4744 #endif /* HAVE_GTK2 */
4747 main (int argc, char **argv)
4751 saver_preferences *p;
4752 Bool prefs_p = False;
4753 Bool settings_p = False;
4756 Widget toplevel_shell;
4757 char *real_progname = argv[0];
4760 Bool crapplet_p = False;
4764 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4765 textdomain (GETTEXT_PACKAGE);
4768 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4769 # else /* !HAVE_GTK2 */
4770 if (!setlocale (LC_ALL, ""))
4771 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4772 # endif /* !HAVE_GTK2 */
4774 #endif /* ENABLE_NLS */
4776 str = strrchr (real_progname, '/');
4777 if (str) real_progname = str+1;
4780 memset (s, 0, sizeof(*s));
4781 s->initializing_p = True;
4784 global_state_kludge = s; /* I hate C so much... */
4786 progname = real_progname;
4788 s->short_version = (char *) malloc (5);
4789 memcpy (s->short_version, screensaver_id + 17, 4);
4790 s->short_version [4] = 0;
4793 /* Register our error message logger for every ``log domain'' known.
4794 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4795 for all of the domains that seem to be in use.
4798 const char * const domains[] = { 0,
4799 "Gtk", "Gdk", "GLib", "GModule",
4800 "GThread", "Gnome", "GnomeUI" };
4801 for (i = 0; i < countof(domains); i++)
4802 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4805 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4808 const char *dir = DEFAULT_ICONDIR;
4809 if (*dir) add_pixmap_directory (dir);
4811 # endif /* !HAVE_GTK2 */
4812 #endif /* DEFAULT_ICONDIR */
4814 /* This is gross, but Gtk understands --display and not -display...
4816 for (i = 1; i < argc; i++)
4817 if (argv[i][0] && argv[i][1] &&
4818 !strncmp(argv[i], "-display", strlen(argv[i])))
4819 argv[i] = "--display";
4822 /* We need to parse this arg really early... Sigh. */
4823 for (i = 1; i < argc; i++)
4826 (!strcmp(argv[i], "--crapplet") ||
4827 !strcmp(argv[i], "--capplet")))
4829 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4832 for (j = i; j < argc; j++) /* remove it from the list */
4833 argv[j] = argv[j+1];
4835 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4836 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4838 fprintf (stderr, "%s: %s\n", real_progname, usage);
4840 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4843 (!strcmp(argv[i], "--debug") ||
4844 !strcmp(argv[i], "-debug") ||
4845 !strcmp(argv[i], "-d")))
4849 for (j = i; j < argc; j++) /* remove it from the list */
4850 argv[j] = argv[j+1];
4857 (!strcmp(argv[i], "-geometry") ||
4858 !strcmp(argv[i], "-geom") ||
4859 !strcmp(argv[i], "-geo") ||
4860 !strcmp(argv[i], "-g")))
4864 for (j = i; j < argc; j++) /* remove them from the list */
4865 argv[j] = argv[j+2];
4872 (!strcmp(argv[i], "--configdir")))
4876 hack_configuration_path = argv[i+1];
4877 for (j = i; j < argc; j++) /* remove them from the list */
4878 argv[j] = argv[j+2];
4882 if (0 != stat (hack_configuration_path, &st))
4885 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4889 else if (!S_ISDIR (st.st_mode))
4891 fprintf (stderr, "%s: not a directory: %s\n",
4892 blurb(), hack_configuration_path);
4900 fprintf (stderr, "%s: using config directory \"%s\"\n",
4901 progname, hack_configuration_path);
4904 /* Let Gtk open the X connection, then initialize Xt to use that
4905 same connection. Doctor Frankenstein would be proud.
4907 # ifdef HAVE_CRAPPLET
4910 GnomeClient *client;
4911 GnomeClientFlags flags = 0;
4913 int init_results = gnome_capplet_init ("screensaver-properties",
4915 argc, argv, NULL, 0, NULL);
4917 0 upon successful initialization;
4918 1 if --init-session-settings was passed on the cmdline;
4919 2 if --ignore was passed on the cmdline;
4922 So the 1 signifies just to init the settings, and quit, basically.
4923 (Meaning launch the xscreensaver daemon.)
4926 if (init_results < 0)
4929 g_error ("An initialization error occurred while "
4930 "starting xscreensaver-capplet.\n");
4932 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4933 real_progname, init_results);
4938 client = gnome_master_client ();
4941 flags = gnome_client_get_flags (client);
4943 if (flags & GNOME_CLIENT_IS_CONNECTED)
4946 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4947 gnome_client_get_id (client));
4950 char *session_args[20];
4952 session_args[i++] = real_progname;
4953 session_args[i++] = "--capplet";
4954 session_args[i++] = "--init-session-settings";
4955 session_args[i] = 0;
4956 gnome_client_set_priority (client, 20);
4957 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4958 gnome_client_set_restart_command (client, i, session_args);
4962 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4965 gnome_client_flush (client);
4968 if (init_results == 1)
4970 system ("xscreensaver -nosplash &");
4976 # endif /* HAVE_CRAPPLET */
4978 gtk_init (&argc, &argv);
4982 /* We must read exactly the same resources as xscreensaver.
4983 That means we must have both the same progclass *and* progname,
4984 at least as far as the resource database is concerned. So,
4985 put "xscreensaver" in argv[0] while initializing Xt.
4987 argv[0] = "xscreensaver";
4991 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4993 XtToolkitInitialize ();
4994 app = XtCreateApplicationContext ();
4995 dpy = GDK_DISPLAY();
4996 XtAppSetFallbackResources (app, defaults);
4997 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4998 toplevel_shell = XtAppCreateShell (progname, progclass,
4999 applicationShellWidgetClass,
5002 dpy = XtDisplay (toplevel_shell);
5003 db = XtDatabase (dpy);
5004 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
5005 XSetErrorHandler (demo_ehandler);
5007 /* Let's just ignore these. They seem to confuse Irix Gtk... */
5008 signal (SIGPIPE, SIG_IGN);
5010 /* After doing Xt-style command-line processing, complain about any
5011 unrecognized command-line arguments.
5013 for (i = 1; i < argc; i++)
5015 char *str = argv[i];
5016 if (str[0] == '-' && str[1] == '-')
5018 if (!strcmp (str, "-prefs"))
5020 else if (!strcmp (str, "-settings"))
5022 else if (crapplet_p)
5023 /* There are lots of random args that we don't care about when we're
5024 started as a crapplet, so just ignore unknown args in that case. */
5028 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
5030 fprintf (stderr, "%s: %s\n", real_progname, usage);
5035 /* Load the init file, which may end up consulting the X resource database
5036 and the site-wide app-defaults file. Note that at this point, it's
5037 important that `progname' be "xscreensaver", rather than whatever
5041 s->nscreens = screen_count (dpy);
5043 hack_environment (s); /* must be before initialize_sort_map() */
5045 load_init_file (dpy, p);
5046 initialize_sort_map (s);
5048 /* Now that Xt has been initialized, and the resources have been read,
5049 we can set our `progname' variable to something more in line with
5052 progname = real_progname;
5056 /* Print out all the resources we read. */
5058 XrmName name = { 0 };
5059 XrmClass class = { 0 };
5061 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
5067 /* Intern the atoms that xscreensaver_command() needs.
5069 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
5070 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
5071 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
5072 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5073 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5074 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5075 XA_SELECT = XInternAtom (dpy, "SELECT", False);
5076 XA_DEMO = XInternAtom (dpy, "DEMO", False);
5077 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5078 XA_BLANK = XInternAtom (dpy, "BLANK", False);
5079 XA_LOCK = XInternAtom (dpy, "LOCK", False);
5080 XA_EXIT = XInternAtom (dpy, "EXIT", False);
5081 XA_RESTART = XInternAtom (dpy, "RESTART", False);
5084 /* Create the window and all its widgets.
5086 s->base_widget = create_xscreensaver_demo ();
5087 s->popup_widget = create_xscreensaver_settings_dialog ();
5088 s->toplevel_widget = s->base_widget;
5091 /* Set the main window's title. */
5093 char *base_title = _("Screensaver Preferences");
5094 char *v = (char *) strdup(strchr(screensaver_id, ' '));
5095 char *s1, *s2, *s3, *s4;
5096 s1 = (char *) strchr(v, ' '); s1++;
5097 s2 = (char *) strchr(s1, ' ');
5098 s3 = (char *) strchr(v, '('); s3++;
5099 s4 = (char *) strchr(s3, ')');
5103 window_title = (char *) malloc (strlen (base_title) +
5104 strlen (progclass) +
5105 strlen (s1) + strlen (s3) +
5107 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
5108 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5109 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
5113 /* Adjust the (invisible) notebooks on the popup dialog... */
5115 GtkNotebook *notebook =
5116 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5117 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5121 gtk_widget_hide (std);
5122 # else /* !HAVE_XML */
5123 /* Make the advanced page be the only one available. */
5124 gtk_widget_set_sensitive (std, False);
5125 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5126 gtk_widget_hide (std);
5127 std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5128 gtk_widget_hide (std);
5130 # endif /* !HAVE_XML */
5132 gtk_notebook_set_page (notebook, page);
5133 gtk_notebook_set_show_tabs (notebook, False);
5136 /* Various other widget initializations...
5138 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5139 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5141 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5142 GTK_SIGNAL_FUNC (wm_popup_close_cb),
5145 populate_hack_list (s);
5146 populate_prefs_page (s);
5147 sensitize_demo_widgets (s, False);
5148 fix_text_entry_sizes (s);
5149 scroll_to_current_hack (s);
5151 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5152 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5156 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5157 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5159 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5160 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5162 #endif /* !HAVE_GTK2 */
5164 /* Hook up callbacks to the items on the mode menu. */
5166 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5167 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5168 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5170 for (i = 0; kids; kids = kids->next, i++)
5172 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5173 GTK_SIGNAL_FUNC (mode_menu_item_cb),
5176 /* The "random-same" mode menu item does not appear unless
5177 there are multple screens.
5179 if (s->nscreens <= 1 &&
5180 mode_menu_order[i] == RANDOM_HACKS_SAME)
5181 gtk_widget_hide (GTK_WIDGET (kids->data));
5184 if (s->nscreens <= 1) /* recompute option-menu size */
5186 gtk_widget_unrealize (GTK_WIDGET (menu));
5187 gtk_widget_realize (GTK_WIDGET (menu));
5192 /* Handle the -prefs command-line argument. */
5195 GtkNotebook *notebook =
5196 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5197 gtk_notebook_set_page (notebook, 1);
5200 # ifdef HAVE_CRAPPLET
5204 GtkWidget *outer_vbox;
5206 gtk_widget_hide (s->toplevel_widget);
5208 capplet = capplet_widget_new ();
5210 /* Make there be a "Close" button instead of "OK" and "Cancel" */
5211 # ifdef HAVE_CRAPPLET_IMMEDIATE
5212 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5213 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5214 /* In crapplet-mode, take off the menubar. */
5215 gtk_widget_hide (name_to_widget (s, "menubar"));
5217 /* Reparent our top-level container to be a child of the capplet
5220 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5221 gtk_widget_ref (outer_vbox);
5222 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5224 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5225 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5227 /* Find the window above us, and set the title and close handler. */
5229 GtkWidget *window = capplet;
5230 while (window && !GTK_IS_WINDOW (window))
5231 window = GET_PARENT (window);
5234 gtk_window_set_title (GTK_WINDOW (window), window_title);
5235 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5236 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5241 s->toplevel_widget = capplet;
5243 # endif /* HAVE_CRAPPLET */
5246 /* The Gnome folks hate the menubar. I think it's important to have access
5247 to the commands on the File menu (Restart Daemon, etc.) and to the
5248 About and Documentation commands on the Help menu.
5252 gtk_widget_hide (name_to_widget (s, "menubar"));
5256 free (window_title);
5260 /* After picking the default size, allow -geometry to override it. */
5262 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5265 gtk_widget_show (s->toplevel_widget);
5266 init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */
5267 fix_preview_visual (s);
5269 /* Realize page zero, so that we can diddle the scrollbar when the
5270 user tabs back to it -- otherwise, the current hack isn't scrolled
5271 to the first time they tab back there, when started with "-prefs".
5272 (Though it is if they then tab away, and back again.)
5274 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5275 #### understands this crap, explain to me how to make this work.
5277 gtk_widget_realize (name_to_widget (s, "demos_table"));
5280 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5283 /* Handle the --settings command-line argument. */
5285 gtk_timeout_add (500, settings_timer, 0);
5288 /* Issue any warnings about the running xscreensaver daemon. */
5290 the_network_is_not_the_computer (s);
5293 /* Run the Gtk event loop, and not the Xt event loop. This means that
5294 if there were Xt timers or fds registered, they would never get serviced,
5295 and if there were any Xt widgets, they would never have events delivered.
5296 Fortunately, we're using Gtk for all of the UI, and only initialized
5297 Xt so that we could process the command line and use the X resource
5300 s->initializing_p = False;
5302 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5303 after we start up. Otherwise, it always appears scrolled to the top
5304 when in crapplet-mode. */
5305 gtk_timeout_add (500, delayed_scroll_kludge, s);
5309 /* Load every configurator in turn, to scan them for errors all at once. */
5313 for (i = 0; i < p->screenhacks_count; i++)
5315 screenhack *hack = p->screenhacks[i];
5316 conf_data *d = load_configurator (hack->command, s->debug_p);
5317 if (d) free_conf_data (d);
5323 # ifdef HAVE_CRAPPLET
5325 capplet_gtk_main ();
5327 # endif /* HAVE_CRAPPLET */
5330 kill_preview_subproc (s, False);
5334 #endif /* HAVE_GTK -- whole file */