1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2013 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #ifdef HAVE_GTK /* whole file */
19 #include <xscreensaver-intl.h>
28 # define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */
36 #endif /* ENABLE_NLS */
39 # include <pwd.h> /* for getpwuid() */
45 # include <sys/utsname.h> /* for uname() */
46 #endif /* HAVE_UNAME */
56 #ifdef HAVE_SYS_WAIT_H
57 # include <sys/wait.h> /* for waitpid() and associated macros */
61 #include <X11/Xproto.h> /* for CARD32 */
62 #include <X11/Xatom.h> /* for XA_INTEGER */
63 #include <X11/Intrinsic.h>
64 #include <X11/StringDefs.h>
66 /* We don't actually use any widget internals, but these are included
67 so that gdb will have debug info for the widgets... */
68 #include <X11/IntrinsicP.h>
69 #include <X11/ShellP.h>
73 # include <X11/Xmu/Error.h>
75 # include <Xmu/Error.h>
82 # include <X11/extensions/Xinerama.h>
83 #endif /* HAVE_XINERAMA */
89 # include <capplet-widget.h>
95 # include <glade/glade-xml.h>
97 #else /* !HAVE_GTK2 */
98 # define G_MODULE_EXPORT /**/
99 #endif /* !HAVE_GTK2 */
101 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
102 # define GLADE_DIR DEFAULT_ICONDIR
104 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
105 # define DEFAULT_ICONDIR GLADE_DIR
109 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
110 It is unused otherwise, so in that case, stub it out. */
111 static const char *hack_configuration_path = 0;
118 #include "resources.h" /* for parse_time() */
119 #include "visual.h" /* for has_writable_cells() */
120 #include "remote.h" /* for xscreensaver_command() */
123 #include "logo-50.xpm"
124 #include "logo-180.xpm"
126 #undef dgettext /* else these are defined twice... */
129 #include "demo-Gtk-widgets.h"
130 #include "demo-Gtk-support.h"
131 #include "demo-Gtk-conf.h"
143 #endif /* HAVE_GTK2 */
145 /* Deal with deprecation of direct access to struct fields on the way to GTK3
146 See http://live.gnome.org/GnomeGoals/UseGseal
148 #if GTK_CHECK_VERSION(2,14,0)
149 # define GET_PARENT(w) gtk_widget_get_parent (w)
150 # define GET_WINDOW(w) gtk_widget_get_window (w)
151 # define GET_ACTION_AREA(d) gtk_dialog_get_action_area (d)
152 # define GET_CONTENT_AREA(d) gtk_dialog_get_content_area (d)
153 # define GET_ADJ_VALUE(a) gtk_adjustment_get_value (a)
154 # define SET_ADJ_VALUE(a,v) gtk_adjustment_set_value (a, v)
155 # define SET_ADJ_UPPER(a,v) gtk_adjustment_set_upper (a, v)
157 # define GET_PARENT(w) ((w)->parent)
158 # define GET_WINDOW(w) ((w)->window)
159 # define GET_ACTION_AREA(d) ((d)->action_area)
160 # define GET_CONTENT_AREA(d) ((d)->vbox)
161 # define GET_ADJ_VALUE(a) ((a)->value)
162 # define SET_ADJ_VALUE(a,v) (a)->value = v
163 # define SET_ADJ_UPPER(a,v) (a)->upper = v
166 #if GTK_CHECK_VERSION(2,18,0)
167 # define SET_CAN_DEFAULT(w) gtk_widget_set_can_default ((w), TRUE)
168 # define GET_SENSITIVE(w) gtk_widget_get_sensitive (w)
170 # define SET_CAN_DEFAULT(w) GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_DEFAULT)
171 # define GET_SENSITIVE(w) GTK_WIDGET_IS_SENSITIVE (w)
174 #if GTK_CHECK_VERSION(2,20,0)
175 # define GET_REALIZED(w) gtk_widget_get_realized (w)
177 # define GET_REALIZED(w) GTK_WIDGET_REALIZED (w)
181 extern void exec_command (const char *shell, const char *command, int nice);
182 extern int on_path_p (const char *program);
184 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
187 #define countof(x) (sizeof((x))/sizeof((*x)))
190 /* You might think that to read an array of 32-bit quantities out of a
191 server-side property, you would pass an array of 32-bit data quantities
192 into XGetWindowProperty(). You would be wrong. You have to use an array
193 of longs, even if long is 64 bits (using 32 of each 64.)
198 char *progclass = "XScreenSaver";
201 /* The order of the items in the mode menu. */
202 static int mode_menu_order[] = {
203 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
208 char *short_version; /* version number of this xscreensaver build */
210 GtkWidget *toplevel_widget; /* the main window */
211 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
212 GtkWidget *popup_widget; /* the "Settings" dialog */
213 conf_data *cdata; /* private data for per-hack configuration */
216 GladeXML *glade_ui; /* Glade UI file */
217 #endif /* HAVE_GTK2 */
219 Bool debug_p; /* whether to print diagnostics */
220 Bool initializing_p; /* flag for breaking recursion loops */
221 Bool saving_p; /* flag for breaking recursion loops */
223 char *desired_preview_cmd; /* subprocess we intend to run */
224 char *running_preview_cmd; /* subprocess we are currently running */
225 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
226 Bool running_preview_error_p; /* whether the pid died abnormally */
228 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
229 int subproc_timer_id; /* timer to delay subproc launch */
230 int subproc_check_timer_id; /* timer to check whether it started up */
231 int subproc_check_countdown; /* how many more checks left */
233 int *list_elt_to_hack_number; /* table for sorting the hack list */
234 int *hack_number_to_list_elt; /* the inverse table */
235 Bool *hacks_available_p; /* whether hacks are on $PATH */
236 int total_available; /* how many are on $PATH */
237 int list_count; /* how many items are in the list: this may be
238 less than p->screenhacks_count, if some are
241 int _selected_list_element; /* don't use this: call
242 selected_list_element() instead */
244 int nscreens; /* How many X or Xinerama screens there are */
246 saver_preferences prefs;
251 /* Total fucking evilness due to the fact that it's rocket science to get
252 a closure object of our own down into the various widget callbacks. */
253 static state *global_state_kludge;
256 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
257 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
258 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
261 static void populate_demo_window (state *, int list_elt);
262 static void populate_prefs_page (state *);
263 static void populate_popup_window (state *);
265 static Bool flush_dialog_changes_and_save (state *);
266 static Bool flush_popup_changes_and_save (state *);
268 static int maybe_reload_init_file (state *);
269 static void await_xscreensaver (state *);
270 static Bool xscreensaver_running_p (state *);
271 static void sensitize_menu_items (state *s, Bool force_p);
272 static void force_dialog_repaint (state *s);
274 static void schedule_preview (state *, const char *cmd);
275 static void kill_preview_subproc (state *, Bool reset_p);
276 static void schedule_preview_check (state *);
279 /* Prototypes of functions used by the Glade-generated code,
282 void exit_menu_cb (GtkMenuItem *, gpointer user_data);
283 void about_menu_cb (GtkMenuItem *, gpointer user_data);
284 void doc_menu_cb (GtkMenuItem *, gpointer user_data);
285 void file_menu_cb (GtkMenuItem *, gpointer user_data);
286 void activate_menu_cb (GtkMenuItem *, gpointer user_data);
287 void lock_menu_cb (GtkMenuItem *, gpointer user_data);
288 void kill_menu_cb (GtkMenuItem *, gpointer user_data);
289 void restart_menu_cb (GtkWidget *, gpointer user_data);
290 void run_this_cb (GtkButton *, gpointer user_data);
291 void manual_cb (GtkButton *, gpointer user_data);
292 void run_next_cb (GtkButton *, gpointer user_data);
293 void run_prev_cb (GtkButton *, gpointer user_data);
294 void pref_changed_cb (GtkWidget *, gpointer user_data);
295 gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
296 void mode_menu_item_cb (GtkWidget *, gpointer user_data);
297 void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
298 gint page_num, gpointer user_data);
299 void browse_image_dir_cb (GtkButton *, gpointer user_data);
300 void browse_text_file_cb (GtkButton *, gpointer user_data);
301 void browse_text_program_cb (GtkButton *, gpointer user_data);
302 void settings_cb (GtkButton *, gpointer user_data);
303 void settings_adv_cb (GtkButton *, gpointer user_data);
304 void settings_std_cb (GtkButton *, gpointer user_data);
305 void settings_reset_cb (GtkButton *, gpointer user_data);
306 void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
307 gint page_num, gpointer user_data);
308 void settings_cancel_cb (GtkButton *, gpointer user_data);
309 void settings_ok_cb (GtkButton *, gpointer user_data);
311 static void kill_gnome_screensaver (void);
312 static void kill_kde_screensaver (void);
315 /* Some random utility functions
318 const char *blurb (void);
323 time_t now = time ((time_t *) 0);
324 char *ct = (char *) ctime (&now);
325 static char buf[255];
326 int n = strlen(progname);
328 strncpy(buf, progname, n);
331 strncpy(buf+n, ct+11, 8);
332 strcpy(buf+n+9, ": ");
338 name_to_widget (state *s, const char *name)
348 /* First try to load the Glade file from the current directory;
349 if there isn't one there, check the installed directory.
351 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
352 const char * const files[] = { GLADE_FILE_NAME,
353 GLADE_DIR "/" GLADE_FILE_NAME };
355 for (i = 0; i < countof (files); i++)
358 if (!stat (files[i], &st))
360 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
367 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
368 "\tfrom " GLADE_DIR "/ or current directory.\n",
372 # undef GLADE_FILE_NAME
374 glade_xml_signal_autoconnect (s->glade_ui);
377 w = glade_xml_get_widget (s->glade_ui, name);
379 #else /* !HAVE_GTK2 */
381 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
384 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
386 #endif /* HAVE_GTK2 */
389 fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
395 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
396 Takes a scroller, viewport, or list as an argument.
399 ensure_selected_item_visible (GtkWidget *widget)
403 GtkTreeSelection *selection;
407 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
408 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
409 path = gtk_tree_path_new_first ();
411 path = gtk_tree_model_get_path (model, &iter);
413 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
415 gtk_tree_path_free (path);
417 #else /* !HAVE_GTK2 */
419 GtkScrolledWindow *scroller = 0;
421 GtkList *list_widget = 0;
425 GtkWidget *selected = 0;
428 gint parent_h, child_y, child_h, children_h, ignore;
429 double ratio_t, ratio_b;
431 if (GTK_IS_SCROLLED_WINDOW (widget))
433 scroller = GTK_SCROLLED_WINDOW (widget);
434 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
435 list_widget = GTK_LIST (GTK_BIN(vp)->child);
437 else if (GTK_IS_VIEWPORT (widget))
439 vp = GTK_VIEWPORT (widget);
440 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
441 list_widget = GTK_LIST (GTK_BIN(vp)->child);
443 else if (GTK_IS_LIST (widget))
445 list_widget = GTK_LIST (widget);
446 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
447 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
452 slist = list_widget->selection;
453 selected = (slist ? GTK_WIDGET (slist->data) : 0);
457 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
459 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
460 kids; kids = kids->next)
463 adj = gtk_scrolled_window_get_vadjustment (scroller);
465 gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (vp)),
466 &ignore, &ignore, &ignore, &parent_h, &ignore);
467 gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (selected)),
468 &ignore, &child_y, &ignore, &child_h, &ignore);
469 children_h = nkids * child_h;
471 ratio_t = ((double) child_y) / ((double) children_h);
472 ratio_b = ((double) child_y + child_h) / ((double) children_h);
474 if (adj->upper == 0.0) /* no items in list */
477 if (ratio_t < (adj->value / adj->upper) ||
478 ratio_b > ((adj->value + adj->page_size) / adj->upper))
481 int slop = parent_h * 0.75; /* how much to overshoot by */
483 if (ratio_t < (adj->value / adj->upper))
485 double ratio_w = ((double) parent_h) / ((double) children_h);
486 double ratio_l = (ratio_b - ratio_t);
487 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
490 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
492 target = ratio_t * adj->upper;
496 if (target > adj->upper - adj->page_size)
497 target = adj->upper - adj->page_size;
501 gtk_adjustment_set_value (adj, target);
503 #endif /* !HAVE_GTK2 */
507 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
509 GtkWidget *shell = GTK_WIDGET (user_data);
510 while (GET_PARENT (shell))
511 shell = GET_PARENT (shell);
512 gtk_widget_destroy (GTK_WIDGET (shell));
516 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
518 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
520 restart_menu_cb (widget, user_data);
521 warning_dialog_dismiss_cb (widget, user_data);
524 static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data)
526 kill_gnome_screensaver ();
527 warning_dialog_dismiss_cb (widget, user_data);
530 static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data)
532 kill_kde_screensaver ();
533 warning_dialog_dismiss_cb (widget, user_data);
536 typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
539 warning_dialog (GtkWidget *parent, const char *message,
540 dialog_button button_type, int center)
542 char *msg = strdup (message);
545 GtkWidget *dialog = gtk_dialog_new ();
546 GtkWidget *label = 0;
548 GtkWidget *cancel = 0;
551 while (parent && !GET_WINDOW (parent))
552 parent = GET_PARENT (parent);
555 !GET_WINDOW (parent)) /* too early to pop up transient dialogs */
557 fprintf (stderr, "%s: too early for dialog?\n", progname);
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);
808 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
810 s = strchr (vers, ',');
815 s2 = strrchr (vers, '-');
817 strncpy (year, s2, 4);
820 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
821 non-ASCII characters aren't allowed in localizable string keys."
822 (I don't want to just use (c) instead of © because that doesn't
823 look as good in the plain-old default Latin1 "C" locale.)
826 sprintf(copy, ("Copyright \xC2\xA9 1991-%s %s"), year, s);
827 #else /* !HAVE_GTK2 */
828 sprintf(copy, ("Copyright \251 1991-%s %s"), year, s);
829 #endif /* !HAVE_GTK2 */
831 sprintf (msg, "%s\n\n%s", copy, desc);
833 /* I can't make gnome_about_new() work here -- it starts dying in
834 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
835 then this might be the thing to do:
839 const gchar *auth[] = { 0 };
840 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
842 gtk_widget_show (about);
844 #else / * GTK but not GNOME * /
848 GdkColormap *colormap;
849 GdkPixmap *gdkpixmap;
852 GtkWidget *dialog = gtk_dialog_new ();
853 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
854 GtkWidget *parent = GTK_WIDGET (menuitem);
855 while (GET_PARENT (parent))
856 parent = GET_PARENT (parent);
858 hbox = gtk_hbox_new (FALSE, 20);
859 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
860 hbox, TRUE, TRUE, 0);
862 colormap = gtk_widget_get_colormap (parent);
864 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
865 (gchar **) logo_180_xpm);
866 icon = gtk_pixmap_new (gdkpixmap, mask);
867 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
869 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
871 vbox = gtk_vbox_new (FALSE, 0);
872 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
874 label1 = gtk_label_new (vers);
875 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
876 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
877 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
880 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
881 GTK_WIDGET (label1)->style->font =
882 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
883 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
884 #endif /* HAVE_GTK2 */
886 label2 = gtk_label_new (msg);
887 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
888 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
889 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
892 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
893 GTK_WIDGET (label2)->style->font =
894 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
895 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
896 #endif /* HAVE_GTK2 */
898 hb = gtk_hbutton_box_new ();
900 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
904 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
905 #else /* !HAVE_GTK2 */
906 ok = gtk_button_new_with_label (_("OK"));
907 #endif /* !HAVE_GTK2 */
908 gtk_container_add (GTK_CONTAINER (hb), ok);
910 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
911 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
912 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
914 gtk_widget_show (hbox);
915 gtk_widget_show (icon);
916 gtk_widget_show (vbox);
917 gtk_widget_show (label1);
918 gtk_widget_show (label2);
919 gtk_widget_show (hb);
920 gtk_widget_show (ok);
921 gtk_widget_show (dialog);
923 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
924 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
926 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
927 GET_WINDOW (GTK_WIDGET (parent)));
928 gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog)));
929 gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog)));
935 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
937 state *s = global_state_kludge; /* I hate C so much... */
938 saver_preferences *p = &s->prefs;
941 if (!p->help_url || !*p->help_url)
943 warning_dialog (s->toplevel_widget,
945 "No Help URL has been specified.\n"), D_NONE, 100);
949 help_command = (char *) malloc (strlen (p->load_url_command) +
950 (strlen (p->help_url) * 4) + 20);
951 strcpy (help_command, "( ");
952 sprintf (help_command + strlen(help_command),
954 p->help_url, p->help_url, p->help_url, p->help_url);
955 strcat (help_command, " ) &");
956 if (system (help_command) < 0)
957 fprintf (stderr, "%s: fork error\n", blurb());
963 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
965 state *s = global_state_kludge; /* I hate C so much... */
966 sensitize_menu_items (s, False);
971 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
973 state *s = global_state_kludge; /* I hate C so much... */
974 run_cmd (s, XA_ACTIVATE, 0);
979 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
981 state *s = global_state_kludge; /* I hate C so much... */
982 run_cmd (s, XA_LOCK, 0);
987 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
989 state *s = global_state_kludge; /* I hate C so much... */
990 run_cmd (s, XA_EXIT, 0);
995 restart_menu_cb (GtkWidget *widget, gpointer user_data)
997 state *s = global_state_kludge; /* I hate C so much... */
998 flush_dialog_changes_and_save (s);
999 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
1001 if (system ("xscreensaver -nosplash &") < 0)
1002 fprintf (stderr, "%s: fork error\n", blurb());
1004 await_xscreensaver (s);
1008 xscreensaver_running_p (state *s)
1010 Display *dpy = GDK_DISPLAY();
1012 server_xscreensaver_version (dpy, &rversion, 0, 0);
1020 await_xscreensaver (state *s)
1025 while (!ok && (--countdown > 0))
1026 if (xscreensaver_running_p (s))
1029 sleep (1); /* If it's not there yet, wait a second... */
1031 sensitize_menu_items (s, True);
1035 /* Timed out, no screensaver running. */
1038 Bool root_p = (geteuid () == 0);
1042 "The xscreensaver daemon did not start up properly.\n"
1048 __extension__ /* don't warn about "string length is greater than
1049 the length ISO C89 compilers are required to
1050 support" in the following expression... */
1053 _("You are running as root. This usually means that xscreensaver\n"
1054 "was unable to contact your X server because access control is\n"
1055 "turned on. Try running this command:\n"
1057 " xhost +localhost\n"
1059 "and then selecting `File / Restart Daemon'.\n"
1061 "Note that turning off access control will allow anyone logged\n"
1062 "on to this machine to access your screen, which might be\n"
1063 "considered a security problem. Please read the xscreensaver\n"
1064 "manual and FAQ for more information.\n"
1066 "You shouldn't run X as root. Instead, you should log in as a\n"
1067 "normal user, and `su' as necessary."));
1069 strcat (buf, _("Please check your $PATH and permissions."));
1071 warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1074 force_dialog_repaint (s);
1079 selected_list_element (state *s)
1081 return s->_selected_list_element;
1086 demo_write_init_file (state *s, saver_preferences *p)
1088 Display *dpy = GDK_DISPLAY();
1091 /* #### try to figure out why shit keeps getting reordered... */
1092 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1096 if (!write_init_file (dpy, p, s->short_version, False))
1099 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1104 const char *f = init_file_name();
1106 warning_dialog (s->toplevel_widget,
1107 _("Error:\n\nCouldn't determine init file name!\n"),
1111 char *b = (char *) malloc (strlen(f) + 1024);
1112 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1113 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1121 G_MODULE_EXPORT void
1122 run_this_cb (GtkButton *button, gpointer user_data)
1124 state *s = global_state_kludge; /* I hate C so much... */
1125 int list_elt = selected_list_element (s);
1126 if (list_elt < 0) return;
1127 if (!flush_dialog_changes_and_save (s))
1128 run_hack (s, list_elt, True);
1132 G_MODULE_EXPORT void
1133 manual_cb (GtkButton *button, gpointer user_data)
1135 Display *dpy = GDK_DISPLAY();
1136 state *s = global_state_kludge; /* I hate C so much... */
1137 saver_preferences *p = &s->prefs;
1138 GtkWidget *list_widget = name_to_widget (s, "list");
1139 int list_elt = selected_list_element (s);
1141 char *name, *name2, *cmd, *str;
1143 if (list_elt < 0) return;
1144 hack_number = s->list_elt_to_hack_number[list_elt];
1146 flush_dialog_changes_and_save (s);
1147 ensure_selected_item_visible (list_widget);
1149 name = strdup (p->screenhacks[hack_number]->command);
1152 while (isspace (*name2)) name2++;
1154 while (*str && !isspace (*str)) str++;
1156 str = strrchr (name2, '/');
1157 if (str) name2 = str+1;
1159 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1162 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1163 strcpy (cmd2, "( ");
1164 sprintf (cmd2 + strlen (cmd2),
1166 name2, name2, name2, name2);
1167 strcat (cmd2, " ) &");
1168 if (system (cmd2) < 0)
1169 fprintf (stderr, "%s: fork error\n", blurb());
1174 warning_dialog (GTK_WIDGET (button),
1175 _("Error:\n\nno `manualCommand' resource set."),
1184 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1186 GtkWidget *parent = name_to_widget (s, "scroller");
1187 gboolean was = GET_SENSITIVE (parent);
1190 GtkTreeModel *model;
1191 GtkTreeSelection *selection;
1192 #endif /* HAVE_GTK2 */
1194 if (!was) gtk_widget_set_sensitive (parent, True);
1196 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1198 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1200 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1201 gtk_tree_selection_select_iter (selection, &iter);
1203 #else /* !HAVE_GTK2 */
1204 gtk_list_select_item (GTK_LIST (list), list_elt);
1205 #endif /* !HAVE_GTK2 */
1206 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1207 if (!was) gtk_widget_set_sensitive (parent, False);
1211 G_MODULE_EXPORT void
1212 run_next_cb (GtkButton *button, gpointer user_data)
1214 state *s = global_state_kludge; /* I hate C so much... */
1215 /* saver_preferences *p = &s->prefs; */
1216 Bool ops = s->preview_suppressed_p;
1218 GtkWidget *list_widget = name_to_widget (s, "list");
1219 int list_elt = selected_list_element (s);
1226 if (list_elt >= s->list_count)
1229 s->preview_suppressed_p = True;
1231 flush_dialog_changes_and_save (s);
1232 force_list_select_item (s, list_widget, list_elt, True);
1233 populate_demo_window (s, list_elt);
1234 run_hack (s, list_elt, False);
1236 s->preview_suppressed_p = ops;
1240 G_MODULE_EXPORT void
1241 run_prev_cb (GtkButton *button, gpointer user_data)
1243 state *s = global_state_kludge; /* I hate C so much... */
1244 /* saver_preferences *p = &s->prefs; */
1245 Bool ops = s->preview_suppressed_p;
1247 GtkWidget *list_widget = name_to_widget (s, "list");
1248 int list_elt = selected_list_element (s);
1251 list_elt = s->list_count - 1;
1256 list_elt = s->list_count - 1;
1258 s->preview_suppressed_p = True;
1260 flush_dialog_changes_and_save (s);
1261 force_list_select_item (s, list_widget, list_elt, True);
1262 populate_demo_window (s, list_elt);
1263 run_hack (s, list_elt, False);
1265 s->preview_suppressed_p = ops;
1269 /* Writes the given settings into prefs.
1270 Returns true if there was a change, False otherwise.
1271 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1274 flush_changes (state *s,
1277 const char *command,
1280 saver_preferences *p = &s->prefs;
1281 Bool changed = False;
1284 if (list_elt < 0 || list_elt >= s->list_count)
1287 hack_number = s->list_elt_to_hack_number[list_elt];
1288 hack = p->screenhacks[hack_number];
1290 if (enabled_p != -1 &&
1291 enabled_p != hack->enabled_p)
1293 hack->enabled_p = enabled_p;
1296 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1297 blurb(), hack->name, enabled_p);
1302 if (!hack->command || !!strcmp (command, hack->command))
1304 if (hack->command) free (hack->command);
1305 hack->command = strdup (command);
1308 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1309 blurb(), hack->name, command);
1315 const char *ov = hack->visual;
1316 if (!ov || !*ov) ov = "any";
1317 if (!*visual) visual = "any";
1318 if (!!strcasecmp (visual, ov))
1320 if (hack->visual) free (hack->visual);
1321 hack->visual = strdup (visual);
1324 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1325 blurb(), hack->name, visual);
1333 /* Helper for the text fields that contain time specifications:
1334 this parses the text, and does error checking.
1337 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1342 if (!sec_p || strchr (line, ':'))
1343 value = parse_time ((char *) line, sec_p, True);
1347 if (sscanf (line, "%d%c", &value, &c) != 1)
1353 value *= 1000; /* Time measures in microseconds */
1359 "Unparsable time format: \"%s\"\n"),
1361 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1370 directory_p (const char *path)
1373 if (!path || !*path)
1375 else if (stat (path, &st))
1377 else if (!S_ISDIR (st.st_mode))
1384 file_p (const char *path)
1387 if (!path || !*path)
1389 else if (stat (path, &st))
1391 else if (S_ISDIR (st.st_mode))
1398 normalize_directory (const char *path)
1402 if (!path || !*path) return 0;
1404 p2 = (char *) malloc (L + 2);
1406 if (p2[L-1] == '/') /* remove trailing slash */
1409 for (s = p2; s && *s; s++)
1412 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1413 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1416 while (s0 > p2 && s0[-1] != '/')
1422 /* strcpy (s0, s); */
1423 memmove(s0, s, strlen(s) + 1);
1427 else if (*s == '/' && !strncmp (s, "/./", 3)) { /* delete "/./" */
1428 /* strcpy (s, s+2), s--; */
1429 memmove(s, s+2, strlen(s+2) + 1);
1432 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1437 Normalize consecutive slashes.
1438 Ignore doubled slashes after ":" to avoid mangling URLs.
1441 for (s = p2; s && *s; s++){
1442 if (*s == ':') continue;
1443 if (!s[1] || !s[2]) continue;
1444 while (s[1] == '/' && s[2] == '/')
1445 /* strcpy (s+1, s+2); */
1446 memmove (s+1, s+2, strlen(s+2) + 1);
1449 /* and strip trailing whitespace for good measure. */
1451 while (isspace(p2[L-1]))
1464 } FlushForeachClosure;
1467 flush_checkbox (GtkTreeModel *model,
1472 FlushForeachClosure *closure = data;
1475 gtk_tree_model_get (model, iter,
1476 COL_ENABLED, &checked,
1479 if (flush_changes (closure->s, closure->i,
1481 *closure->changed = True;
1485 /* don't remove row */
1489 #endif /* HAVE_GTK2 */
1491 /* Flush out any changes made in the main dialog window (where changes
1492 take place immediately: clicking on a checkbox causes the init file
1493 to be written right away.)
1496 flush_dialog_changes_and_save (state *s)
1498 saver_preferences *p = &s->prefs;
1499 saver_preferences P2, *p2 = &P2;
1501 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1502 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1503 FlushForeachClosure closure;
1504 #else /* !HAVE_GTK2 */
1505 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1506 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1508 #endif /* !HAVE_GTK2 */
1509 static Bool already_warned_about_missing_image_directory = False; /* very long name... */
1511 Bool changed = False;
1514 if (s->saving_p) return False;
1519 /* Flush any checkbox changes in the list down into the prefs struct.
1523 closure.changed = &changed;
1525 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1527 #else /* !HAVE_GTK2 */
1529 for (i = 0; kids; kids = kids->next, i++)
1531 GtkWidget *line = GTK_WIDGET (kids->data);
1532 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1533 GtkWidget *line_check =
1534 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1536 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1538 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1541 #endif /* ~HAVE_GTK2 */
1543 /* Flush the non-hack-specific settings down into the prefs struct.
1546 # define SECONDS(FIELD,NAME) \
1547 w = name_to_widget (s, (NAME)); \
1548 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1550 # define MINUTES(FIELD,NAME) \
1551 w = name_to_widget (s, (NAME)); \
1552 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1554 # define CHECKBOX(FIELD,NAME) \
1555 w = name_to_widget (s, (NAME)); \
1556 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1558 # define PATHNAME(FIELD,NAME) \
1559 w = name_to_widget (s, (NAME)); \
1560 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1562 # define TEXT(FIELD,NAME) \
1563 w = name_to_widget (s, (NAME)); \
1564 (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1566 MINUTES (&p2->timeout, "timeout_spinbutton");
1567 MINUTES (&p2->cycle, "cycle_spinbutton");
1568 CHECKBOX (p2->lock_p, "lock_button");
1569 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1571 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1572 CHECKBOX (p2->dpms_quickoff_p, "dpms_quickoff_button");
1573 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1574 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1575 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1577 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1578 CHECKBOX (p2->grab_video_p, "grab_video_button");
1579 CHECKBOX (p2->random_image_p, "grab_image_button");
1580 PATHNAME (p2->image_directory, "image_text");
1583 CHECKBOX (p2->verbose_p, "verbose_button");
1584 CHECKBOX (p2->capture_stderr_p, "capture_button");
1585 CHECKBOX (p2->splash_p, "splash_button");
1590 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1591 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1592 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1593 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1594 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1595 TEXT (p2->text_literal, "text_entry");
1596 PATHNAME (p2->text_file, "text_file_entry");
1597 PATHNAME (p2->text_program, "text_program_entry");
1598 PATHNAME (p2->text_program, "text_program_entry");
1599 TEXT (p2->text_url, "text_url_entry");
1602 CHECKBOX (p2->install_cmap_p, "install_button");
1603 CHECKBOX (p2->fade_p, "fade_button");
1604 CHECKBOX (p2->unfade_p, "unfade_button");
1605 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1613 /* Warn if the image directory doesn't exist, when:
1614 - not being warned before
1615 - image directory is changed and the directory doesn't exist
1616 - image directory does not begin with http://
1618 if (p2->image_directory &&
1619 *p2->image_directory &&
1620 !directory_p (p2->image_directory) &&
1621 strncmp(p2->image_directory, "http://", 6) &&
1622 ( !already_warned_about_missing_image_directory ||
1623 ( p->image_directory &&
1624 *p->image_directory &&
1625 strcmp(p->image_directory, p2->image_directory)
1631 sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n",
1632 p2->image_directory);
1633 if (warning_dialog (s->toplevel_widget, b, D_NONE, 100))
1634 already_warned_about_missing_image_directory = True;
1638 /* Map the mode menu to `saver_mode' enum values. */
1640 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1641 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1642 GtkWidget *selected = gtk_menu_get_active (menu);
1643 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1644 int menu_elt = g_list_index (kids, (gpointer) selected);
1645 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1646 p2->mode = mode_menu_order[menu_elt];
1649 if (p2->mode == ONE_HACK)
1651 int list_elt = selected_list_element (s);
1652 p2->selected_hack = (list_elt >= 0
1653 ? s->list_elt_to_hack_number[list_elt]
1657 # define COPY(field, name) \
1658 if (p->field != p2->field) { \
1661 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1663 p->field = p2->field
1666 COPY(selected_hack, "selected_hack");
1668 COPY(timeout, "timeout");
1669 COPY(cycle, "cycle");
1670 COPY(lock_p, "lock_p");
1671 COPY(lock_timeout, "lock_timeout");
1673 COPY(dpms_enabled_p, "dpms_enabled_p");
1674 COPY(dpms_quickoff_p, "dpms_quickoff_enabled_p");
1675 COPY(dpms_standby, "dpms_standby");
1676 COPY(dpms_suspend, "dpms_suspend");
1677 COPY(dpms_off, "dpms_off");
1680 COPY(verbose_p, "verbose_p");
1681 COPY(capture_stderr_p, "capture_stderr_p");
1682 COPY(splash_p, "splash_p");
1685 COPY(tmode, "tmode");
1687 COPY(install_cmap_p, "install_cmap_p");
1688 COPY(fade_p, "fade_p");
1689 COPY(unfade_p, "unfade_p");
1690 COPY(fade_seconds, "fade_seconds");
1692 COPY(grab_desktop_p, "grab_desktop_p");
1693 COPY(grab_video_p, "grab_video_p");
1694 COPY(random_image_p, "random_image_p");
1698 # define COPYSTR(FIELD,NAME) \
1701 strcmp(p->FIELD, p2->FIELD)) \
1705 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1707 if (p->FIELD && p->FIELD != p2->FIELD) \
1709 p->FIELD = p2->FIELD; \
1712 COPYSTR(image_directory, "image_directory");
1713 COPYSTR(text_literal, "text_literal");
1714 COPYSTR(text_file, "text_file");
1715 COPYSTR(text_program, "text_program");
1716 COPYSTR(text_url, "text_url");
1719 populate_prefs_page (s);
1723 Display *dpy = GDK_DISPLAY();
1724 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1725 sync_server_dpms_settings (dpy, enabled_p,
1726 p->dpms_standby / 1000,
1727 p->dpms_suspend / 1000,
1731 changed = demo_write_init_file (s, p);
1734 s->saving_p = False;
1739 /* Flush out any changes made in the popup dialog box (where changes
1740 take place only when the OK button is clicked.)
1743 flush_popup_changes_and_save (state *s)
1745 Bool changed = False;
1746 saver_preferences *p = &s->prefs;
1747 int list_elt = selected_list_element (s);
1749 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1750 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1752 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1753 const char *command = gtk_entry_get_text (cmd);
1758 if (s->saving_p) return False;
1764 if (maybe_reload_init_file (s) != 0)
1770 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1772 if (!strcasecmp (visual, "")) visual = "";
1773 else if (!strcasecmp (visual, "any")) visual = "";
1774 else if (!strcasecmp (visual, "default")) visual = "Default";
1775 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1776 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1777 else if (!strcasecmp (visual, "best")) visual = "Best";
1778 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1779 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1780 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1781 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1782 else if (!strcasecmp (visual, "color")) visual = "Color";
1783 else if (!strcasecmp (visual, "gl")) visual = "GL";
1784 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1785 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1786 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1787 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1788 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1789 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1790 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1791 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1792 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1795 gdk_beep (); /* unparsable */
1797 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1800 changed = flush_changes (s, list_elt, -1, command, visual);
1803 changed = demo_write_init_file (s, p);
1805 /* Do this to re-launch the hack if (and only if) the command line
1807 populate_demo_window (s, selected_list_element (s));
1811 s->saving_p = False;
1816 G_MODULE_EXPORT void
1817 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1819 state *s = global_state_kludge; /* I hate C so much... */
1820 if (! s->initializing_p)
1822 s->initializing_p = True;
1823 flush_dialog_changes_and_save (s);
1824 s->initializing_p = False;
1828 G_MODULE_EXPORT gboolean
1829 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1831 pref_changed_cb (widget, user_data);
1835 /* Callback on menu items in the "mode" options menu.
1837 G_MODULE_EXPORT void
1838 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1840 state *s = (state *) user_data;
1841 saver_preferences *p = &s->prefs;
1842 GtkWidget *list = name_to_widget (s, "list");
1846 gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
1848 saver_mode new_mode;
1852 if (menu_items->data == widget)
1855 menu_items = menu_items->next;
1857 if (!menu_items) abort();
1859 new_mode = mode_menu_order[menu_index];
1861 /* Keep the same list element displayed as before; except if we're
1862 switching *to* "one screensaver" mode from any other mode, set
1863 "the one" to be that which is currently selected.
1865 list_elt = selected_list_element (s);
1866 if (new_mode == ONE_HACK)
1867 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1870 saver_mode old_mode = p->mode;
1872 populate_demo_window (s, list_elt);
1873 force_list_select_item (s, list, list_elt, True);
1874 p->mode = old_mode; /* put it back, so the init file gets written */
1877 pref_changed_cb (widget, user_data);
1881 G_MODULE_EXPORT void
1882 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1883 gint page_num, gpointer user_data)
1885 state *s = global_state_kludge; /* I hate C so much... */
1886 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1888 /* If we're switching to page 0, schedule the current hack to be run.
1889 Otherwise, schedule it to stop. */
1891 populate_demo_window (s, selected_list_element (s));
1893 schedule_preview (s, 0);
1898 list_activated_cb (GtkTreeView *list,
1900 GtkTreeViewColumn *column,
1907 g_return_if_fail (!gdk_pointer_is_grabbed ());
1909 str = gtk_tree_path_to_string (path);
1910 list_elt = strtol (str, NULL, 10);
1914 run_hack (s, list_elt, True);
1918 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1920 state *s = (state *)data;
1921 GtkTreeModel *model;
1927 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1930 path = gtk_tree_model_get_path (model, &iter);
1931 str = gtk_tree_path_to_string (path);
1932 list_elt = strtol (str, NULL, 10);
1934 gtk_tree_path_free (path);
1937 populate_demo_window (s, list_elt);
1938 flush_dialog_changes_and_save (s);
1940 /* Re-populate the Settings window any time a new item is selected
1941 in the list, in case both windows are currently visible.
1943 populate_popup_window (s);
1946 #else /* !HAVE_GTK2 */
1948 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1949 list_select_cb that comes in
1950 *after* we've double-clicked.
1954 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1957 state *s = (state *) data;
1958 if (event->type == GDK_2BUTTON_PRESS)
1960 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1961 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1963 last_doubleclick_time = time ((time_t *) 0);
1966 run_hack (s, list_elt, True);
1974 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1976 state *s = (state *) data;
1977 time_t now = time ((time_t *) 0);
1979 if (now >= last_doubleclick_time + 2)
1981 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1982 populate_demo_window (s, list_elt);
1983 flush_dialog_changes_and_save (s);
1988 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1990 state *s = (state *) data;
1991 populate_demo_window (s, -1);
1992 flush_dialog_changes_and_save (s);
1995 #endif /* !HAVE_GTK2 */
1998 /* Called when the checkboxes that are in the left column of the
1999 scrolling list are clicked. This both populates the right pane
2000 (just as clicking on the label (really, listitem) does) and
2001 also syncs this checkbox with the right pane Enabled checkbox.
2006 GtkCellRendererToggle *toggle,
2008 #else /* !HAVE_GTK2 */
2010 #endif /* !HAVE_GTK2 */
2013 state *s = (state *) data;
2016 GtkScrolledWindow *scroller =
2017 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
2018 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2019 GtkTreeModel *model = gtk_tree_view_get_model (list);
2020 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
2023 #else /* !HAVE_GTK2 */
2024 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
2025 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
2027 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
2028 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
2029 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
2030 #endif /* !HAVE_GTK2 */
2037 if (!gtk_tree_model_get_iter (model, &iter, path))
2039 g_warning ("bad path: %s", path_string);
2042 gtk_tree_path_free (path);
2044 gtk_tree_model_get (model, &iter,
2045 COL_ENABLED, &active,
2048 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2049 COL_ENABLED, !active,
2052 list_elt = strtol (path_string, NULL, 10);
2053 #else /* !HAVE_GTK2 */
2054 list_elt = gtk_list_child_position (list, line);
2055 #endif /* !HAVE_GTK2 */
2057 /* remember previous scroll position of the top of the list */
2058 adj = gtk_scrolled_window_get_vadjustment (scroller);
2059 scroll_top = GET_ADJ_VALUE (adj);
2061 flush_dialog_changes_and_save (s);
2062 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
2063 populate_demo_window (s, list_elt);
2065 /* restore the previous scroll position of the top of the list.
2066 this is weak, but I don't really know why it's moving... */
2067 gtk_adjustment_set_value (adj, scroll_top);
2073 GtkFileSelection *widget;
2074 } file_selection_data;
2079 store_image_directory (GtkWidget *button, gpointer user_data)
2081 file_selection_data *fsd = (file_selection_data *) user_data;
2082 state *s = fsd->state;
2083 GtkFileSelection *selector = fsd->widget;
2084 GtkWidget *top = s->toplevel_widget;
2085 saver_preferences *p = &s->prefs;
2086 const char *path = gtk_file_selection_get_filename (selector);
2088 if (p->image_directory && !strcmp(p->image_directory, path))
2089 return; /* no change */
2091 /* No warning for URLs. */
2092 if ((!directory_p (path)) && strncmp(path, "http://", 6))
2095 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2096 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2100 if (p->image_directory) free (p->image_directory);
2101 p->image_directory = normalize_directory (path);
2103 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2104 (p->image_directory ? p->image_directory : ""));
2105 demo_write_init_file (s, p);
2110 store_text_file (GtkWidget *button, gpointer user_data)
2112 file_selection_data *fsd = (file_selection_data *) user_data;
2113 state *s = fsd->state;
2114 GtkFileSelection *selector = fsd->widget;
2115 GtkWidget *top = s->toplevel_widget;
2116 saver_preferences *p = &s->prefs;
2117 const char *path = gtk_file_selection_get_filename (selector);
2119 if (p->text_file && !strcmp(p->text_file, path))
2120 return; /* no change */
2125 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2126 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2130 if (p->text_file) free (p->text_file);
2131 p->text_file = normalize_directory (path);
2133 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2134 (p->text_file ? p->text_file : ""));
2135 demo_write_init_file (s, p);
2140 store_text_program (GtkWidget *button, gpointer user_data)
2142 file_selection_data *fsd = (file_selection_data *) user_data;
2143 state *s = fsd->state;
2144 GtkFileSelection *selector = fsd->widget;
2145 /*GtkWidget *top = s->toplevel_widget;*/
2146 saver_preferences *p = &s->prefs;
2147 const char *path = gtk_file_selection_get_filename (selector);
2149 if (p->text_program && !strcmp(p->text_program, path))
2150 return; /* no change */
2156 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2157 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2162 if (p->text_program) free (p->text_program);
2163 p->text_program = normalize_directory (path);
2165 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2166 (p->text_program ? p->text_program : ""));
2167 demo_write_init_file (s, p);
2173 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2175 file_selection_data *fsd = (file_selection_data *) user_data;
2176 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2180 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2182 browse_image_dir_cancel (button, user_data);
2183 store_image_directory (button, user_data);
2187 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2189 browse_image_dir_cancel (button, user_data);
2190 store_text_file (button, user_data);
2194 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2196 browse_image_dir_cancel (button, user_data);
2197 store_text_program (button, user_data);
2201 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2203 browse_image_dir_cancel (widget, user_data);
2207 G_MODULE_EXPORT void
2208 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2210 state *s = global_state_kludge; /* I hate C so much... */
2211 saver_preferences *p = &s->prefs;
2212 static file_selection_data *fsd = 0;
2214 GtkFileSelection *selector = GTK_FILE_SELECTION(
2215 gtk_file_selection_new ("Please select the image directory."));
2218 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2220 fsd->widget = selector;
2223 if (p->image_directory && *p->image_directory)
2224 gtk_file_selection_set_filename (selector, p->image_directory);
2226 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2227 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2229 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2230 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2232 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2233 GTK_SIGNAL_FUNC (browse_image_dir_close),
2236 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2238 gtk_window_set_modal (GTK_WINDOW (selector), True);
2239 gtk_widget_show (GTK_WIDGET (selector));
2243 G_MODULE_EXPORT void
2244 browse_text_file_cb (GtkButton *button, gpointer user_data)
2246 state *s = global_state_kludge; /* I hate C so much... */
2247 saver_preferences *p = &s->prefs;
2248 static file_selection_data *fsd = 0;
2250 GtkFileSelection *selector = GTK_FILE_SELECTION(
2251 gtk_file_selection_new ("Please select a text file."));
2254 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2256 fsd->widget = selector;
2259 if (p->text_file && *p->text_file)
2260 gtk_file_selection_set_filename (selector, p->text_file);
2262 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2263 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2265 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2266 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2268 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2269 GTK_SIGNAL_FUNC (browse_image_dir_close),
2272 gtk_window_set_modal (GTK_WINDOW (selector), True);
2273 gtk_widget_show (GTK_WIDGET (selector));
2277 G_MODULE_EXPORT void
2278 browse_text_program_cb (GtkButton *button, gpointer user_data)
2280 state *s = global_state_kludge; /* I hate C so much... */
2281 saver_preferences *p = &s->prefs;
2282 static file_selection_data *fsd = 0;
2284 GtkFileSelection *selector = GTK_FILE_SELECTION(
2285 gtk_file_selection_new ("Please select a text-generating program."));
2288 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2290 fsd->widget = selector;
2293 if (p->text_program && *p->text_program)
2294 gtk_file_selection_set_filename (selector, p->text_program);
2296 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2297 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2299 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2300 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2302 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2303 GTK_SIGNAL_FUNC (browse_image_dir_close),
2306 gtk_window_set_modal (GTK_WINDOW (selector), True);
2307 gtk_widget_show (GTK_WIDGET (selector));
2314 G_MODULE_EXPORT void
2315 settings_cb (GtkButton *button, gpointer user_data)
2317 state *s = global_state_kludge; /* I hate C so much... */
2318 int list_elt = selected_list_element (s);
2320 populate_demo_window (s, list_elt); /* reset the widget */
2321 populate_popup_window (s); /* create UI on popup window */
2322 gtk_widget_show (s->popup_widget);
2326 settings_sync_cmd_text (state *s)
2329 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2330 char *cmd_line = get_configurator_command_line (s->cdata, False);
2331 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2332 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2334 # endif /* HAVE_XML */
2337 G_MODULE_EXPORT void
2338 settings_adv_cb (GtkButton *button, gpointer user_data)
2340 state *s = global_state_kludge; /* I hate C so much... */
2341 GtkNotebook *notebook =
2342 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2344 settings_sync_cmd_text (s);
2345 gtk_notebook_set_page (notebook, 1);
2348 G_MODULE_EXPORT void
2349 settings_std_cb (GtkButton *button, gpointer user_data)
2351 state *s = global_state_kludge; /* I hate C so much... */
2352 GtkNotebook *notebook =
2353 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2355 /* Re-create UI to reflect the in-progress command-line settings. */
2356 populate_popup_window (s);
2358 gtk_notebook_set_page (notebook, 0);
2361 G_MODULE_EXPORT void
2362 settings_reset_cb (GtkButton *button, gpointer user_data)
2365 state *s = global_state_kludge; /* I hate C so much... */
2366 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2367 char *cmd_line = get_configurator_command_line (s->cdata, True);
2368 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2369 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2371 populate_popup_window (s);
2372 # endif /* HAVE_XML */
2375 G_MODULE_EXPORT void
2376 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2377 gint page_num, gpointer user_data)
2379 state *s = global_state_kludge; /* I hate C so much... */
2380 GtkWidget *adv = name_to_widget (s, "adv_button");
2381 GtkWidget *std = name_to_widget (s, "std_button");
2385 gtk_widget_show (adv);
2386 gtk_widget_hide (std);
2388 else if (page_num == 1)
2390 gtk_widget_hide (adv);
2391 gtk_widget_show (std);
2399 G_MODULE_EXPORT void
2400 settings_cancel_cb (GtkButton *button, gpointer user_data)
2402 state *s = global_state_kludge; /* I hate C so much... */
2403 gtk_widget_hide (s->popup_widget);
2406 G_MODULE_EXPORT void
2407 settings_ok_cb (GtkButton *button, gpointer user_data)
2409 state *s = global_state_kludge; /* I hate C so much... */
2410 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2411 int page = gtk_notebook_get_current_page (notebook);
2414 /* Regenerate the command-line from the widget contents before saving.
2415 But don't do this if we're looking at the command-line page already,
2416 or we will blow away what they typed... */
2417 settings_sync_cmd_text (s);
2419 flush_popup_changes_and_save (s);
2420 gtk_widget_hide (s->popup_widget);
2424 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2426 state *s = (state *) data;
2427 settings_cancel_cb (0, (gpointer) s);
2433 /* Populating the various widgets
2437 /* Returns the number of the last hack run by the server.
2440 server_current_hack (void)
2444 unsigned long nitems, bytesafter;
2445 unsigned char *dataP = 0;
2446 Display *dpy = GDK_DISPLAY();
2447 int hack_number = -1;
2449 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2450 XA_SCREENSAVER_STATUS,
2451 0, 3, False, XA_INTEGER,
2452 &type, &format, &nitems, &bytesafter,
2455 && type == XA_INTEGER
2459 PROP32 *data = (PROP32 *) dataP;
2460 hack_number = (int) data[2] - 1;
2463 if (dataP) XFree (dataP);
2469 /* Finds the number of the last hack that was run, and makes that item be
2470 selected by default.
2473 scroll_to_current_hack (state *s)
2475 saver_preferences *p = &s->prefs;
2476 int hack_number = -1;
2478 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2479 hack_number = p->selected_hack;
2480 if (hack_number < 0) /* otherwise, use the last-run */
2481 hack_number = server_current_hack ();
2482 if (hack_number < 0) /* failing that, last "one mode" */
2483 hack_number = p->selected_hack;
2484 if (hack_number < 0) /* failing that, newest hack. */
2486 /* We should only get here if the user does not have a .xscreensaver
2487 file, and the screen has not been blanked with a hack since X
2488 started up: in other words, this is probably a fresh install.
2490 Instead of just defaulting to hack #0 (in either "programs" or
2491 "alphabetical" order) let's try to default to the last runnable
2492 hack in the "programs" list: this is probably the hack that was
2493 most recently added to the xscreensaver distribution (and so
2494 it's probably the currently-coolest one!)
2496 hack_number = p->screenhacks_count-1;
2497 while (hack_number > 0 &&
2498 ! (s->hacks_available_p[hack_number] &&
2499 p->screenhacks[hack_number]->enabled_p))
2503 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2505 int list_elt = s->hack_number_to_list_elt[hack_number];
2506 GtkWidget *list = name_to_widget (s, "list");
2507 force_list_select_item (s, list, list_elt, True);
2508 populate_demo_window (s, list_elt);
2514 populate_hack_list (state *s)
2516 Display *dpy = GDK_DISPLAY();
2518 saver_preferences *p = &s->prefs;
2519 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2520 GtkListStore *model;
2521 GtkTreeSelection *selection;
2522 GtkCellRenderer *ren;
2526 g_object_get (G_OBJECT (list),
2531 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2532 g_object_set (G_OBJECT (list), "model", model, NULL);
2533 g_object_unref (model);
2535 ren = gtk_cell_renderer_toggle_new ();
2536 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2538 "active", COL_ENABLED,
2541 g_signal_connect (ren, "toggled",
2542 G_CALLBACK (list_checkbox_cb),
2545 ren = gtk_cell_renderer_text_new ();
2546 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2547 _("Screen Saver"), ren,
2551 g_signal_connect_after (list, "row_activated",
2552 G_CALLBACK (list_activated_cb),
2555 selection = gtk_tree_view_get_selection (list);
2556 g_signal_connect (selection, "changed",
2557 G_CALLBACK (list_select_changed_cb),
2562 for (i = 0; i < s->list_count; i++)
2564 int hack_number = s->list_elt_to_hack_number[i];
2565 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2567 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2569 if (!hack) continue;
2571 /* If we're to suppress uninstalled hacks, check $PATH now. */
2572 if (p->ignore_uninstalled_p && !available_p)
2575 pretty_name = (hack->name
2576 ? strdup (hack->name)
2577 : make_hack_name (dpy, hack->command));
2581 /* Make the text foreground be the color of insensitive widgets
2582 (but don't actually make it be insensitive, since we still
2583 want to be able to click on it.)
2585 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
2586 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2587 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2588 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2590 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2591 /* " background=\"#%02X%02X%02X\"" */
2593 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2594 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2600 gtk_list_store_append (model, &iter);
2601 gtk_list_store_set (model, &iter,
2602 COL_ENABLED, hack->enabled_p,
2603 COL_NAME, pretty_name,
2608 #else /* !HAVE_GTK2 */
2610 saver_preferences *p = &s->prefs;
2611 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2613 for (i = 0; i < s->list_count; i++)
2615 int hack_number = s->list_elt_to_hack_number[i];
2616 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2618 /* A GtkList must contain only GtkListItems, but those can contain
2619 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2620 and a Label. We handle single and double click events on the
2621 line itself, for clicking on the text, but the interior checkbox
2622 also handles its own events.
2625 GtkWidget *line_hbox;
2626 GtkWidget *line_check;
2627 GtkWidget *line_label;
2629 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2631 if (!hack) continue;
2633 /* If we're to suppress uninstalled hacks, check $PATH now. */
2634 if (p->ignore_uninstalled_p && !available_p)
2637 pretty_name = (hack->name
2638 ? strdup (hack->name)
2639 : make_hack_name (hack->command));
2641 line = gtk_list_item_new ();
2642 line_hbox = gtk_hbox_new (FALSE, 0);
2643 line_check = gtk_check_button_new ();
2644 line_label = gtk_label_new (pretty_name);
2646 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2647 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2648 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2650 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2652 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2654 gtk_widget_show (line_check);
2655 gtk_widget_show (line_label);
2656 gtk_widget_show (line_hbox);
2657 gtk_widget_show (line);
2661 gtk_container_add (GTK_CONTAINER (list), line);
2662 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2663 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2666 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2667 GTK_SIGNAL_FUNC (list_checkbox_cb),
2670 gtk_widget_show (line);
2674 /* Make the widget be colored like insensitive widgets
2675 (but don't actually make it be insensitive, since we
2676 still want to be able to click on it.)
2678 GtkRcStyle *rc_style;
2681 gtk_widget_realize (GTK_WIDGET (line_label));
2683 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2684 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2686 rc_style = gtk_rc_style_new ();
2687 rc_style->fg[GTK_STATE_NORMAL] = fg;
2688 rc_style->bg[GTK_STATE_NORMAL] = bg;
2689 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2691 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2692 gtk_rc_style_unref (rc_style);
2696 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2697 GTK_SIGNAL_FUNC (list_select_cb),
2699 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2700 GTK_SIGNAL_FUNC (list_unselect_cb),
2702 #endif /* !HAVE_GTK2 */
2706 update_list_sensitivity (state *s)
2708 saver_preferences *p = &s->prefs;
2709 Bool sensitive = (p->mode == RANDOM_HACKS ||
2710 p->mode == RANDOM_HACKS_SAME ||
2711 p->mode == ONE_HACK);
2712 Bool checkable = (p->mode == RANDOM_HACKS ||
2713 p->mode == RANDOM_HACKS_SAME);
2714 Bool blankable = (p->mode != DONT_BLANK);
2717 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2718 GtkWidget *use = name_to_widget (s, "use_col_frame");
2719 #endif /* HAVE_GTK2 */
2720 GtkWidget *scroller = name_to_widget (s, "scroller");
2721 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2722 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2725 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2726 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2727 #else /* !HAVE_GTK2 */
2728 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2729 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2731 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2732 #endif /* !HAVE_GTK2 */
2733 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2734 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2736 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2739 gtk_tree_view_column_set_visible (use, checkable);
2740 #else /* !HAVE_GTK2 */
2742 gtk_widget_show (use); /* the "Use" column header */
2744 gtk_widget_hide (use);
2748 GtkBin *line = GTK_BIN (kids->data);
2749 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2750 GtkWidget *line_check =
2751 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2754 gtk_widget_show (line_check);
2756 gtk_widget_hide (line_check);
2760 #endif /* !HAVE_GTK2 */
2765 populate_prefs_page (state *s)
2767 saver_preferences *p = &s->prefs;
2769 Bool can_lock_p = True;
2771 /* Disable all the "lock" controls if locking support was not provided
2772 at compile-time, or if running on MacOS. */
2773 # if defined(NO_LOCKING) || defined(__APPLE__)
2778 /* If there is only one screen, the mode menu contains
2779 "random" but not "random-same".
2781 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2782 p->mode = RANDOM_HACKS;
2785 /* The file supports timeouts of less than a minute, but the GUI does
2786 not, so throttle the values to be at least one minute (since "0" is
2787 a bad rounding choice...)
2789 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2792 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2795 # define FMT_MINUTES(NAME,N) \
2796 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2798 # define FMT_SECONDS(NAME,N) \
2799 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2801 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2802 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2803 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2804 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2805 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2806 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2807 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2812 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2813 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2816 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2818 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2819 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2820 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2822 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2823 TOGGLE_ACTIVE ("dpms_quickoff_button", p->dpms_quickoff_p);
2824 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2825 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2826 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2827 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2828 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2829 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2833 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2834 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2835 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2836 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2837 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2840 # undef TOGGLE_ACTIVE
2842 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2843 (p->image_directory ? p->image_directory : ""));
2844 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2846 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2849 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2850 (p->text_literal ? p->text_literal : ""));
2851 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2852 (p->text_file ? p->text_file : ""));
2853 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2854 (p->text_program ? p->text_program : ""));
2855 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2856 (p->text_url ? p->text_url : ""));
2858 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2859 p->tmode == TEXT_LITERAL);
2860 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2861 p->tmode == TEXT_FILE);
2862 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2863 p->tmode == TEXT_FILE);
2864 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2865 p->tmode == TEXT_PROGRAM);
2866 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2867 p->tmode == TEXT_PROGRAM);
2868 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2869 p->tmode == TEXT_URL);
2872 /* Map the `saver_mode' enum to mode menu to values. */
2874 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2877 for (i = 0; i < countof(mode_menu_order); i++)
2878 if (mode_menu_order[i] == p->mode)
2880 gtk_option_menu_set_history (opt, i);
2881 update_list_sensitivity (s);
2885 Bool found_any_writable_cells = False;
2886 Bool fading_possible = False;
2887 Bool dpms_supported = False;
2889 Display *dpy = GDK_DISPLAY();
2890 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2892 for (i = 0; i < nscreens; i++)
2894 Screen *s = ScreenOfDisplay (dpy, i);
2895 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2897 found_any_writable_cells = True;
2902 fading_possible = found_any_writable_cells;
2903 #ifdef HAVE_XF86VMODE_GAMMA
2904 fading_possible = True;
2907 #ifdef HAVE_DPMS_EXTENSION
2909 int op = 0, event = 0, error = 0;
2910 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2911 dpms_supported = True;
2913 #endif /* HAVE_DPMS_EXTENSION */
2916 # define SENSITIZE(NAME,SENSITIVEP) \
2917 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2919 /* Blanking and Locking
2921 SENSITIZE ("lock_button", can_lock_p);
2922 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2923 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2928 SENSITIZE ("dpms_frame", dpms_supported);
2929 SENSITIZE ("dpms_button", dpms_supported);
2930 SENSITIZE ("dpms_quickoff_button", dpms_supported);
2932 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2933 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2934 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2935 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2936 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2937 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2938 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2939 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2940 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2944 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2945 SENSITIZE ("install_button", found_any_writable_cells);
2946 SENSITIZE ("fade_button", fading_possible);
2947 SENSITIZE ("unfade_button", fading_possible);
2949 SENSITIZE ("fade_label", (fading_possible &&
2950 (p->fade_p || p->unfade_p)));
2951 SENSITIZE ("fade_spinbutton", (fading_possible &&
2952 (p->fade_p || p->unfade_p)));
2960 populate_popup_window (state *s)
2962 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2963 char *doc_string = 0;
2965 /* #### not in Gtk 1.2
2966 gtk_label_set_selectable (doc);
2972 free_conf_data (s->cdata);
2977 saver_preferences *p = &s->prefs;
2978 int list_elt = selected_list_element (s);
2979 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2980 ? s->list_elt_to_hack_number[list_elt]
2982 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2985 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2986 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2987 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2988 s->cdata = load_configurator (cmd_line, s->debug_p);
2989 if (s->cdata && s->cdata->widget)
2990 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2995 doc_string = (s->cdata
2996 ? s->cdata->description
2998 # else /* !HAVE_XML */
2999 doc_string = _("Descriptions not available: no XML support compiled in.");
3000 # endif /* !HAVE_XML */
3002 gtk_label_set_text (doc, (doc_string
3004 : _("No description available.")));
3009 sensitize_demo_widgets (state *s, Bool sensitive_p)
3011 const char *names[] = { "demo", "settings",
3012 "cmd_label", "cmd_text", "manual",
3013 "visual", "visual_combo" };
3015 for (i = 0; i < countof(names); i++)
3017 GtkWidget *w = name_to_widget (s, names[i]);
3018 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
3024 sensitize_menu_items (state *s, Bool force_p)
3026 static Bool running_p = False;
3027 static time_t last_checked = 0;
3028 time_t now = time ((time_t *) 0);
3029 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
3033 if (force_p || now > last_checked + 10) /* check every 10 seconds */
3035 running_p = xscreensaver_running_p (s);
3036 last_checked = time ((time_t *) 0);
3039 for (i = 0; i < countof(names); i++)
3041 GtkWidget *w = name_to_widget (s, names[i]);
3042 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
3047 /* When the File menu is de-posted after a "Restart Daemon" command,
3048 the window underneath doesn't repaint for some reason. I guess this
3049 is a bug in exposure handling in GTK or GDK. This works around it.
3052 force_dialog_repaint (state *s)
3055 /* Tell GDK to invalidate and repaint the whole window.
3057 GdkWindow *w = GET_WINDOW (s->toplevel_widget);
3058 GdkRegion *region = gdk_region_new ();
3060 rect.x = rect.y = 0;
3061 rect.width = rect.height = 32767;
3062 gdk_region_union_with_rect (region, &rect);
3063 gdk_window_invalidate_region (w, region, True);
3064 gdk_region_destroy (region);
3065 gdk_window_process_updates (w, True);
3067 /* Force the server to send an exposure event by creating and then
3068 destroying a window as a child of the top level shell.
3070 Display *dpy = GDK_DISPLAY();
3071 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
3073 XWindowAttributes xgwa;
3074 XGetWindowAttributes (dpy, parent, &xgwa);
3075 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
3076 XMapRaised (dpy, w);
3077 XDestroyWindow (dpy, w);
3083 /* Even though we've given these text fields a maximum number of characters,
3084 their default size is still about 30 characters wide -- so measure out
3085 a string in their font, and resize them to just fit that.
3088 fix_text_entry_sizes (state *s)
3092 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
3093 const char * const spinbuttons[] = {
3094 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3095 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3096 "dpms_off_spinbutton",
3097 "-fade_spinbutton" };
3101 for (i = 0; i < countof(spinbuttons); i++)
3103 const char *n = spinbuttons[i];
3105 while (*n == '-') n++, cols--;
3106 w = GTK_WIDGET (name_to_widget (s, n));
3107 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3108 gtk_widget_set_usize (w, width, -2);
3111 /* Now fix the width of the combo box.
3113 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3114 w = GTK_COMBO (w)->entry;
3115 width = gdk_string_width (w->style->font, "PseudoColor___");
3116 gtk_widget_set_usize (w, width, -2);
3118 /* Now fix the width of the file entry text.
3120 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3121 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3122 gtk_widget_set_usize (w, width, -2);
3124 /* Now fix the width of the command line text.
3126 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3127 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3128 gtk_widget_set_usize (w, width, -2);
3132 /* Now fix the height of the list widget:
3133 make it default to being around 10 text-lines high instead of 4.
3135 w = GTK_WIDGET (name_to_widget (s, "list"));
3139 int leading = 3; /* approximate is ok... */
3143 PangoFontMetrics *pain =
3144 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3145 gtk_widget_get_style (w)->font_desc,
3146 gtk_get_default_language ());
3147 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3148 pango_font_metrics_get_descent (pain));
3149 #else /* !HAVE_GTK2 */
3150 height = w->style->font->ascent + w->style->font->descent;
3151 #endif /* !HAVE_GTK2 */
3155 height += border * 2;
3156 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3157 gtk_widget_set_usize (w, -2, height);
3164 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3167 static char *up_arrow_xpm[] = {
3190 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3191 the end of the array (Gtk 1.2.5.) */
3192 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3193 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3196 static char *down_arrow_xpm[] = {
3219 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3220 the end of the array (Gtk 1.2.5.) */
3221 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3222 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3226 pixmapify_button (state *s, int down_p)
3230 GtkWidget *pixmapwid;
3234 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3235 style = gtk_widget_get_style (w);
3237 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3238 &style->bg[GTK_STATE_NORMAL],
3240 ? (gchar **) down_arrow_xpm
3241 : (gchar **) up_arrow_xpm));
3242 pixmapwid = gtk_pixmap_new (pixmap, mask);
3243 gtk_widget_show (pixmapwid);
3244 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3245 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3249 map_next_button_cb (GtkWidget *w, gpointer user_data)
3251 state *s = (state *) user_data;
3252 pixmapify_button (s, 1);
3256 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3258 state *s = (state *) user_data;
3259 pixmapify_button (s, 0);
3261 #endif /* !HAVE_GTK2 */
3265 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3269 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3270 GtkAllocation *allocation,
3274 GtkWidgetAuxInfo *aux_info;
3276 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3278 aux_info->width = allocation->width;
3279 aux_info->height = -2;
3283 gtk_widget_size_request (label, &req);
3286 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3289 eschew_gtk_lossage (GtkLabel *label)
3291 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3292 aux_info->width = GTK_WIDGET (label)->allocation.width;
3293 aux_info->height = -2;
3297 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3299 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3300 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3303 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3305 gtk_widget_queue_resize (GTK_WIDGET (label));
3307 #endif /* !HAVE_GTK2 */
3311 populate_demo_window (state *s, int list_elt)
3313 Display *dpy = GDK_DISPLAY();
3314 saver_preferences *p = &s->prefs;
3317 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3318 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3319 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3320 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3321 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3323 if (p->mode == BLANK_ONLY)
3326 pretty_name = strdup (_("Blank Screen"));
3327 schedule_preview (s, 0);
3329 else if (p->mode == DONT_BLANK)
3332 pretty_name = strdup (_("Screen Saver Disabled"));
3333 schedule_preview (s, 0);
3337 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3338 ? s->list_elt_to_hack_number[list_elt]
3340 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3344 ? strdup (hack->name)
3345 : make_hack_name (dpy, hack->command))
3349 schedule_preview (s, hack->command);
3351 schedule_preview (s, 0);
3355 pretty_name = strdup (_("Preview"));
3357 gtk_frame_set_label (frame1, _(pretty_name));
3358 gtk_frame_set_label (frame2, _(pretty_name));
3360 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3361 gtk_entry_set_position (cmd, 0);
3365 sprintf (title, _("%s: %.100s Settings"),
3366 progclass, (pretty_name ? pretty_name : "???"));
3367 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3370 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3372 ? (hack->visual && *hack->visual
3377 sensitize_demo_widgets (s, (hack ? True : False));
3379 if (pretty_name) free (pretty_name);
3381 ensure_selected_item_visible (list);
3383 s->_selected_list_element = list_elt;
3388 widget_deleter (GtkWidget *widget, gpointer data)
3390 /* #### Well, I want to destroy these widgets, but if I do that, they get
3391 referenced again, and eventually I get a SEGV. So instead of
3392 destroying them, I'll just hide them, and leak a bunch of memory
3393 every time the disk file changes. Go go go Gtk!
3395 #### Ok, that's a lie, I get a crash even if I just hide the widget
3396 and don't ever delete it. Fuck!
3399 gtk_widget_destroy (widget);
3401 gtk_widget_hide (widget);
3406 static char **sort_hack_cmp_names_kludge;
3408 sort_hack_cmp (const void *a, const void *b)
3414 int aa = *(int *) a;
3415 int bb = *(int *) b;
3416 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3417 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3418 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3424 initialize_sort_map (state *s)
3426 Display *dpy = GDK_DISPLAY();
3427 saver_preferences *p = &s->prefs;
3430 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3431 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3432 if (s->hacks_available_p) free (s->hacks_available_p);
3434 s->list_elt_to_hack_number = (int *)
3435 calloc (sizeof(int), p->screenhacks_count + 1);
3436 s->hack_number_to_list_elt = (int *)
3437 calloc (sizeof(int), p->screenhacks_count + 1);
3438 s->hacks_available_p = (Bool *)
3439 calloc (sizeof(Bool), p->screenhacks_count + 1);
3440 s->total_available = 0;
3442 /* Check which hacks actually exist on $PATH
3444 for (i = 0; i < p->screenhacks_count; i++)
3446 screenhack *hack = p->screenhacks[i];
3447 int on = on_path_p (hack->command) ? 1 : 0;
3448 s->hacks_available_p[i] = on;
3449 s->total_available += on;
3452 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3456 for (i = 0; i < p->screenhacks_count; i++)
3458 if (!p->ignore_uninstalled_p ||
3459 s->hacks_available_p[i])
3460 s->list_elt_to_hack_number[j++] = i;
3464 for (; j < p->screenhacks_count; j++)
3465 s->list_elt_to_hack_number[j] = -1;
3468 /* Generate list of sortable names (once)
3470 sort_hack_cmp_names_kludge = (char **)
3471 calloc (sizeof(char *), p->screenhacks_count);
3472 for (i = 0; i < p->screenhacks_count; i++)
3474 screenhack *hack = p->screenhacks[i];
3475 char *name = (hack->name && *hack->name
3476 ? strdup (hack->name)
3477 : make_hack_name (dpy, hack->command));
3479 for (str = name; *str; str++)
3480 *str = tolower(*str);
3481 sort_hack_cmp_names_kludge[i] = name;
3484 /* Sort list->hack map alphabetically
3486 qsort (s->list_elt_to_hack_number,
3487 p->screenhacks_count,
3488 sizeof(*s->list_elt_to_hack_number),
3493 for (i = 0; i < p->screenhacks_count; i++)
3494 free (sort_hack_cmp_names_kludge[i]);
3495 free (sort_hack_cmp_names_kludge);
3496 sort_hack_cmp_names_kludge = 0;
3498 /* Build inverse table */
3499 for (i = 0; i < p->screenhacks_count; i++)
3501 int n = s->list_elt_to_hack_number[i];
3503 s->hack_number_to_list_elt[n] = i;
3509 maybe_reload_init_file (state *s)
3511 Display *dpy = GDK_DISPLAY();
3512 saver_preferences *p = &s->prefs;
3515 static Bool reentrant_lock = False;
3516 if (reentrant_lock) return 0;
3517 reentrant_lock = True;
3519 if (init_file_changed_p (p))
3521 const char *f = init_file_name();
3526 if (!f || !*f) return 0;
3527 b = (char *) malloc (strlen(f) + 1024);
3530 "file \"%s\" has changed, reloading.\n"),
3532 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3535 load_init_file (dpy, p);
3536 initialize_sort_map (s);
3538 list_elt = selected_list_element (s);
3539 list = name_to_widget (s, "list");
3540 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3541 populate_hack_list (s);
3542 force_list_select_item (s, list, list_elt, True);
3543 populate_prefs_page (s);
3544 populate_demo_window (s, list_elt);
3545 ensure_selected_item_visible (list);
3550 reentrant_lock = False;
3556 /* Making the preview window have the right X visual (so that GL works.)
3559 static Visual *get_best_gl_visual (state *);
3562 x_visual_to_gdk_visual (Visual *xv)
3564 GList *gvs = gdk_list_visuals();
3565 if (!xv) return gdk_visual_get_system();
3566 for (; gvs; gvs = gvs->next)
3568 GdkVisual *gv = (GdkVisual *) gvs->data;
3569 if (xv == GDK_VISUAL_XVISUAL (gv))
3572 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3573 blurb(), (unsigned long) xv->visualid);
3578 clear_preview_window (state *s)
3584 if (!s->toplevel_widget) return; /* very early */
3585 p = name_to_widget (s, "preview");
3586 window = GET_WINDOW (p);
3588 if (!window) return;
3590 /* Flush the widget background down into the window, in case a subproc
3592 style = gtk_widget_get_style (p);
3593 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
3594 gdk_window_clear (window);
3597 int list_elt = selected_list_element (s);
3598 int hack_number = (list_elt >= 0
3599 ? s->list_elt_to_hack_number[list_elt]
3601 Bool available_p = (hack_number >= 0
3602 ? s->hacks_available_p [hack_number]
3604 Bool nothing_p = (s->total_available < 5);
3607 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3608 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3609 (s->running_preview_error_p
3610 ? (available_p ? 1 :
3613 #else /* !HAVE_GTK2 */
3614 if (s->running_preview_error_p)
3616 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3617 const char * const lines2[] = { N_("Not"), N_("Installed") };
3618 int nlines = countof(lines1);
3619 int lh = p->style->font->ascent + p->style->font->descent;
3623 const char * const *lines = (available_p ? lines1 : lines2);
3625 gdk_window_get_size (window, &w, &h);
3626 y = (h - (lh * nlines)) / 2;
3627 y += p->style->font->ascent;
3628 for (i = 0; i < nlines; i++)
3630 int sw = gdk_string_width (p->style->font, _(lines[i]));
3631 int x = (w - sw) / 2;
3632 gdk_draw_string (window, p->style->font,
3633 p->style->fg_gc[GTK_STATE_NORMAL],
3638 #endif /* !HAVE_GTK2 */
3646 reset_preview_window (state *s)
3648 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3649 when you kill one and re-start another on the same window. So maybe
3650 it's best to just always destroy and recreate the preview window
3651 when changing hacks, instead of always trying to reuse the same one?
3653 GtkWidget *pr = name_to_widget (s, "preview");
3654 if (GET_REALIZED (pr))
3656 GdkWindow *window = GET_WINDOW (pr);
3657 Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3659 gtk_widget_hide (pr);
3660 gtk_widget_unrealize (pr);
3661 gtk_widget_realize (pr);
3662 gtk_widget_show (pr);
3663 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3665 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3673 fix_preview_visual (state *s)
3675 GtkWidget *widget = name_to_widget (s, "preview");
3676 Visual *xvisual = get_best_gl_visual (s);
3677 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3678 GdkVisual *dvisual = gdk_visual_get_system();
3679 GdkColormap *cmap = (visual == dvisual
3680 ? gdk_colormap_get_system ()
3681 : gdk_colormap_new (visual, False));
3684 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3685 (visual == dvisual ? "default" : "non-default"),
3686 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3688 if (!GET_REALIZED (widget) ||
3689 gtk_widget_get_visual (widget) != visual)
3691 gtk_widget_unrealize (widget);
3692 gtk_widget_set_visual (widget, visual);
3693 gtk_widget_set_colormap (widget, cmap);
3694 gtk_widget_realize (widget);
3697 /* Set the Widget colors to be white-on-black. */
3699 GdkWindow *window = GET_WINDOW (widget);
3700 GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
3701 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3702 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3703 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3704 GdkGC *fgc = gdk_gc_new(window);
3705 GdkGC *bgc = gdk_gc_new(window);
3706 if (!gdk_color_white (cmap, fg)) abort();
3707 if (!gdk_color_black (cmap, bg)) abort();
3708 gdk_gc_set_foreground (fgc, fg);
3709 gdk_gc_set_background (fgc, bg);
3710 gdk_gc_set_foreground (bgc, bg);
3711 gdk_gc_set_background (bgc, fg);
3712 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3713 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3714 gtk_widget_set_style (widget, style);
3716 /* For debugging purposes, put a title on the window (so that
3717 it can be easily found in the output of "xwininfo -tree".)
3719 gdk_window_set_title (window, "Preview");
3722 gtk_widget_show (widget);
3730 subproc_pretty_name (state *s)
3732 if (s->running_preview_cmd)
3734 char *ps = strdup (s->running_preview_cmd);
3735 char *ss = strchr (ps, ' ');
3737 ss = strrchr (ps, '/');
3748 return strdup ("???");
3753 reap_zombies (state *s)
3755 int wait_status = 0;
3757 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3761 if (pid == s->running_preview_pid)
3763 char *ss = subproc_pretty_name (s);
3764 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3765 (unsigned long) pid, ss);
3769 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3770 (unsigned long) pid);
3776 /* Mostly lifted from driver/subprocs.c */
3778 get_best_gl_visual (state *s)
3780 Display *dpy = GDK_DISPLAY();
3789 av[ac++] = "xscreensaver-gl-helper";
3794 perror ("error creating pipe:");
3801 switch ((int) (forked = fork ()))
3805 sprintf (buf, "%s: couldn't fork", blurb());
3813 close (in); /* don't need this one */
3814 close (ConnectionNumber (dpy)); /* close display fd */
3816 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3818 perror ("could not dup() a new stdout:");
3822 execvp (av[0], av); /* shouldn't return. */
3824 if (errno != ENOENT)
3826 /* Ignore "no such file or directory" errors, unless verbose.
3827 Issue all other exec errors, though. */
3828 sprintf (buf, "%s: running %s", blurb(), av[0]);
3832 /* Note that one must use _exit() instead of exit() in procs forked
3833 off of Gtk programs -- Gtk installs an atexit handler that has a
3834 copy of the X connection (which we've already closed, for safety.)
3835 If one uses exit() instead of _exit(), then one sometimes gets a
3836 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3838 _exit (1); /* exits fork */
3844 int wait_status = 0;
3846 FILE *f = fdopen (in, "r");
3850 close (out); /* don't need this one */
3853 if (!fgets (buf, sizeof(buf)-1, f))
3857 /* Wait for the child to die. */
3858 waitpid (-1, &wait_status, 0);
3860 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3866 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3872 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3874 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3875 blurb(), av[0], result);
3887 kill_preview_subproc (state *s, Bool reset_p)
3889 s->running_preview_error_p = False;
3892 clear_preview_window (s);
3894 if (s->subproc_check_timer_id)
3896 gtk_timeout_remove (s->subproc_check_timer_id);
3897 s->subproc_check_timer_id = 0;
3898 s->subproc_check_countdown = 0;
3901 if (s->running_preview_pid)
3903 int status = kill (s->running_preview_pid, SIGTERM);
3904 char *ss = subproc_pretty_name (s);
3911 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3912 blurb(), (unsigned long) s->running_preview_pid, ss);
3917 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3918 blurb(), (unsigned long) s->running_preview_pid, ss);
3924 waitpid(s->running_preview_pid, &endstatus, 0);
3926 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3927 (unsigned long) s->running_preview_pid, ss);
3931 s->running_preview_pid = 0;
3932 if (s->running_preview_cmd) free (s->running_preview_cmd);
3933 s->running_preview_cmd = 0;
3940 reset_preview_window (s);
3941 clear_preview_window (s);
3946 /* Immediately and unconditionally launches the given process,
3947 after appending the -window-id option; sets running_preview_pid.
3950 launch_preview_subproc (state *s)
3952 saver_preferences *p = &s->prefs;
3956 const char *cmd = s->desired_preview_cmd;
3958 GtkWidget *pr = name_to_widget (s, "preview");
3961 reset_preview_window (s);
3963 window = GET_WINDOW (pr);
3965 s->running_preview_error_p = False;
3967 if (s->preview_suppressed_p)
3969 kill_preview_subproc (s, False);
3973 new_cmd = malloc (strlen (cmd) + 40);
3975 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3978 /* No window id? No command to run. */
3984 strcpy (new_cmd, cmd);
3985 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3989 kill_preview_subproc (s, False);
3992 s->running_preview_error_p = True;
3993 clear_preview_window (s);
3997 switch ((int) (forked = fork ()))
4002 sprintf (buf, "%s: couldn't fork", blurb());
4004 s->running_preview_error_p = True;
4010 close (ConnectionNumber (GDK_DISPLAY()));
4012 hack_subproc_environment (id, s->debug_p);
4014 usleep (250000); /* pause for 1/4th second before launching, to give
4015 the previous program time to die and flush its X
4016 buffer, so we don't get leftover turds on the
4019 exec_command (p->shell, new_cmd, p->nice_inferior);
4020 /* Don't bother printing an error message when we are unable to
4021 exec subprocesses; we handle that by polling the pid later.
4023 Note that one must use _exit() instead of exit() in procs forked
4024 off of Gtk programs -- Gtk installs an atexit handler that has a
4025 copy of the X connection (which we've already closed, for safety.)
4026 If one uses exit() instead of _exit(), then one sometimes gets a
4027 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
4029 _exit (1); /* exits child fork */
4034 if (s->running_preview_cmd) free (s->running_preview_cmd);
4035 s->running_preview_cmd = strdup (s->desired_preview_cmd);
4036 s->running_preview_pid = forked;
4040 char *ss = subproc_pretty_name (s);
4041 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
4042 (unsigned long) forked, ss);
4049 schedule_preview_check (s);
4052 if (new_cmd) free (new_cmd);
4057 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
4060 hack_environment (state *s)
4062 static const char *def_path =
4063 # ifdef DEFAULT_PATH_PREFIX
4064 DEFAULT_PATH_PREFIX;
4069 Display *dpy = GDK_DISPLAY();
4070 const char *odpy = DisplayString (dpy);
4071 char *ndpy = (char *) malloc(strlen(odpy) + 20);
4072 strcpy (ndpy, "DISPLAY=");
4073 strcat (ndpy, odpy);
4078 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4080 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4081 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4082 So we must leak it (and/or the previous setting). Yay.
4085 if (def_path && *def_path)
4087 const char *opath = getenv("PATH");
4088 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4089 strcpy (npath, "PATH=");
4090 strcat (npath, def_path);
4091 strcat (npath, ":");
4092 strcat (npath, opath);
4096 /* do not free(npath) -- see above */
4099 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4105 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4107 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4108 necessary yet, but it will make programs work if we had invoked
4109 them with "-root" and not with "-window-id" -- which, of course,
4112 char *nssw = (char *) malloc (40);
4113 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4115 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4116 any more, right? It's not Posix, but everyone seems to have it. */
4121 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4123 /* do not free(nssw) -- see above */
4127 /* Called from a timer:
4128 Launches the currently-chosen subprocess, if it's not already running.
4129 If there's a different process running, kills it.
4132 update_subproc_timer (gpointer data)
4134 state *s = (state *) data;
4135 if (! s->desired_preview_cmd)
4136 kill_preview_subproc (s, True);
4137 else if (!s->running_preview_cmd ||
4138 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4139 launch_preview_subproc (s);
4141 s->subproc_timer_id = 0;
4142 return FALSE; /* do not re-execute timer */
4146 settings_timer (gpointer data)
4153 /* Call this when you think you might want a preview process running.
4154 It will set a timer that will actually launch that program a second
4155 from now, if you haven't changed your mind (to avoid double-click
4156 spazzing, etc.) `cmd' may be null meaning "no process".
4159 schedule_preview (state *s, const char *cmd)
4161 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4166 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4168 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4171 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4172 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4174 if (s->subproc_timer_id)
4175 gtk_timeout_remove (s->subproc_timer_id);
4176 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4180 /* Called from a timer:
4181 Checks to see if the subproc that should be running, actually is.
4184 check_subproc_timer (gpointer data)
4186 state *s = (state *) data;
4187 Bool again_p = True;
4189 if (s->running_preview_error_p || /* already dead */
4190 s->running_preview_pid <= 0)
4198 status = kill (s->running_preview_pid, 0);
4199 if (status < 0 && errno == ESRCH)
4200 s->running_preview_error_p = True;
4204 char *ss = subproc_pretty_name (s);
4205 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4206 (unsigned long) s->running_preview_pid, ss,
4207 (s->running_preview_error_p ? "dead" : "alive"));
4211 if (s->running_preview_error_p)
4213 clear_preview_window (s);
4218 /* Otherwise, it's currently alive. We might be checking again, or we
4219 might be satisfied. */
4221 if (--s->subproc_check_countdown <= 0)
4225 return TRUE; /* re-execute timer */
4228 s->subproc_check_timer_id = 0;
4229 s->subproc_check_countdown = 0;
4230 return FALSE; /* do not re-execute timer */
4235 /* Call this just after launching a subprocess.
4236 This sets a timer that will, five times a second for two seconds,
4237 check whether the program is still running. The assumption here
4238 is that if the process didn't stay up for more than a couple of
4239 seconds, then either the program doesn't exist, or it doesn't
4240 take a -window-id argument.
4243 schedule_preview_check (state *s)
4249 fprintf (stderr, "%s: scheduling check\n", blurb());
4251 if (s->subproc_check_timer_id)
4252 gtk_timeout_remove (s->subproc_check_timer_id);
4253 s->subproc_check_timer_id =
4254 gtk_timeout_add (1000 / ticks,
4255 check_subproc_timer, (gpointer) s);
4256 s->subproc_check_countdown = ticks * seconds;
4261 screen_blanked_p (void)
4265 unsigned long nitems, bytesafter;
4266 unsigned char *dataP = 0;
4267 Display *dpy = GDK_DISPLAY();
4268 Bool blanked_p = False;
4270 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4271 XA_SCREENSAVER_STATUS,
4272 0, 3, False, XA_INTEGER,
4273 &type, &format, &nitems, &bytesafter,
4276 && type == XA_INTEGER
4280 Atom *data = (Atom *) dataP;
4281 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4284 if (dataP) XFree (dataP);
4289 /* Wake up every now and then and see if the screen is blanked.
4290 If it is, kill off the small-window demo -- no point in wasting
4291 cycles by running two screensavers at once...
4294 check_blanked_timer (gpointer data)
4296 state *s = (state *) data;
4297 Bool blanked_p = screen_blanked_p ();
4298 if (blanked_p && s->running_preview_pid)
4301 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4302 kill_preview_subproc (s, True);
4305 return True; /* re-execute timer */
4309 /* How many screens are there (including Xinerama.)
4312 screen_count (Display *dpy)
4314 int nscreens = ScreenCount(dpy);
4315 # ifdef HAVE_XINERAMA
4318 int event_number, error_number;
4319 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4320 XineramaIsActive (dpy))
4322 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4323 if (xsi) XFree (xsi);
4326 # endif /* HAVE_XINERAMA */
4332 /* Setting window manager icon
4336 init_icon (GdkWindow *window)
4338 GdkBitmap *mask = 0;
4341 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4342 (gchar **) logo_50_xpm);
4344 gdk_window_set_icon (window, 0, pixmap, mask);
4348 /* The main demo-mode command loop.
4353 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4354 XrmRepresentation *type, XrmValue *value, XPointer closure)
4357 for (i = 0; quarks[i]; i++)
4359 if (bindings[i] == XrmBindTightly)
4360 fprintf (stderr, (i == 0 ? "" : "."));
4361 else if (bindings[i] == XrmBindLoosely)
4362 fprintf (stderr, "*");
4364 fprintf (stderr, " ??? ");
4365 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4368 fprintf (stderr, ": %s\n", (char *) value->addr);
4376 gnome_screensaver_window (Screen *screen)
4378 Display *dpy = DisplayOfScreen (screen);
4379 Window root = RootWindowOfScreen (screen);
4380 Window parent, *kids;
4382 Window gnome_window = 0;
4385 if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4387 for (i = 0; i < nkids; i++)
4391 unsigned long nitems, bytesafter;
4392 unsigned char *name;
4393 if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4394 False, XA_STRING, &type, &format, &nitems,
4398 && !strcmp ((char *) name, "gnome-screensaver"))
4400 gnome_window = kids[i];
4405 if (kids) XFree ((char *) kids);
4406 return gnome_window;
4410 gnome_screensaver_active_p (void)
4412 Display *dpy = GDK_DISPLAY();
4413 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4414 return (w ? True : False);
4418 kill_gnome_screensaver (void)
4420 Display *dpy = GDK_DISPLAY();
4421 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4422 if (w) XKillClient (dpy, (XID) w);
4426 kde_screensaver_active_p (void)
4428 FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4431 fgets (buf, sizeof(buf)-1, p);
4433 if (!strcmp (buf, "true\n"))
4440 kill_kde_screensaver (void)
4442 system ("dcop kdesktop KScreensaverIface enable false");
4447 the_network_is_not_the_computer (state *s)
4449 Display *dpy = GDK_DISPLAY();
4450 char *rversion = 0, *ruser = 0, *rhost = 0;
4451 char *luser, *lhost;
4453 struct passwd *p = getpwuid (getuid ());
4454 const char *d = DisplayString (dpy);
4456 # if defined(HAVE_UNAME)
4458 if (uname (&uts) < 0)
4459 lhost = "<UNKNOWN>";
4461 lhost = uts.nodename;
4463 strcpy (lhost, getenv("SYS$NODE"));
4464 # else /* !HAVE_UNAME && !VMS */
4465 strcat (lhost, "<UNKNOWN>");
4466 # endif /* !HAVE_UNAME && !VMS */
4468 if (p && p->pw_name)
4473 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4475 /* Make a buffer that's big enough for a number of copies of all the
4476 strings, plus some. */
4477 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4478 (ruser ? strlen(ruser) : 0) +
4479 (rhost ? strlen(rhost) : 0) +
4486 if (!rversion || !*rversion)
4490 "The XScreenSaver daemon doesn't seem to be running\n"
4491 "on display \"%s\". Launch it now?"),
4494 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4496 /* Warn that the two processes are running as different users.
4500 "%s is running as user \"%s\" on host \"%s\".\n"
4501 "But the xscreensaver managing display \"%s\"\n"
4502 "is running as user \"%s\" on host \"%s\".\n"
4504 "Since they are different users, they won't be reading/writing\n"
4505 "the same ~/.xscreensaver file, so %s isn't\n"
4506 "going to work right.\n"
4508 "You should either re-run %s as \"%s\", or re-run\n"
4509 "xscreensaver as \"%s\".\n"
4511 "Restart the xscreensaver daemon now?\n"),
4512 progname, luser, lhost,
4514 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4516 progname, (ruser ? ruser : "???"),
4519 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4521 /* Warn that the two processes are running on different hosts.
4525 "%s is running as user \"%s\" on host \"%s\".\n"
4526 "But the xscreensaver managing display \"%s\"\n"
4527 "is running as user \"%s\" on host \"%s\".\n"
4529 "If those two machines don't share a file system (that is,\n"
4530 "if they don't see the same ~%s/.xscreensaver file) then\n"
4531 "%s won't work right.\n"
4533 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4534 progname, luser, lhost,
4536 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4541 else if (!!strcmp (rversion, s->short_version))
4543 /* Warn that the version numbers don't match.
4547 "This is %s version %s.\n"
4548 "But the xscreensaver managing display \"%s\"\n"
4549 "is version %s. This could cause problems.\n"
4551 "Restart the xscreensaver daemon now?\n"),
4552 progname, s->short_version,
4559 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4561 if (rversion) free (rversion);
4562 if (ruser) free (ruser);
4563 if (rhost) free (rhost);
4567 /* Note: since these dialogs are not modal, they will stack up.
4568 So we do this check *after* popping up the "xscreensaver is not
4569 running" dialog so that these are on top. Good enough.
4572 if (gnome_screensaver_active_p ())
4573 warning_dialog (s->toplevel_widget,
4575 "The GNOME screensaver daemon appears to be running.\n"
4576 "It must be stopped for XScreenSaver to work properly.\n"
4578 "Stop the GNOME screen saver daemon now?\n"),
4581 if (kde_screensaver_active_p ())
4582 warning_dialog (s->toplevel_widget,
4584 "The KDE screen saver daemon appears to be running.\n"
4585 "It must be stopped for XScreenSaver to work properly.\n"
4587 "Stop the KDE screen saver daemon now?\n"),
4592 /* We use this error handler so that X errors are preceeded by the name
4593 of the program that generated them.
4596 demo_ehandler (Display *dpy, XErrorEvent *error)
4598 state *s = global_state_kludge; /* I hate C so much... */
4599 fprintf (stderr, "\nX error in %s:\n", blurb());
4600 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4601 kill_preview_subproc (s, False);
4607 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4608 of the program that generated them; and also that we can ignore one
4609 particular bogus error message that Gdk madly spews.
4612 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4613 const gchar *message, gpointer user_data)
4615 /* Ignore the message "Got event for unknown window: 0x...".
4616 Apparently some events are coming in for the xscreensaver window
4617 (presumably reply events related to the ClientMessage) and Gdk
4618 feels the need to complain about them. So, just suppress any
4619 messages that look like that one.
4621 if (strstr (message, "unknown window"))
4624 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4625 (log_domain ? log_domain : progclass),
4626 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4627 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4628 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4629 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4630 log_level == G_LOG_LEVEL_INFO ? "info" :
4631 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4633 ((!*message || message[strlen(message)-1] != '\n')
4639 __extension__ /* shut up about "string length is greater than the length
4640 ISO C89 compilers are required to support" when including
4645 static char *defaults[] = {
4646 #include "XScreenSaver_ad.h"
4651 #ifdef HAVE_CRAPPLET
4652 static struct poptOption crapplet_options[] = {
4653 {NULL, '\0', 0, NULL, 0}
4655 #endif /* HAVE_CRAPPLET */
4658 const char *usage = "[--display dpy] [--prefs | --settings]"
4659 # ifdef HAVE_CRAPPLET
4662 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4665 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4667 state *s = (state *) user_data;
4668 Boolean oi = s->initializing_p;
4670 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4672 s->initializing_p = True;
4674 eschew_gtk_lossage (label);
4676 s->initializing_p = oi;
4682 print_widget_tree (GtkWidget *w, int depth)
4685 for (i = 0; i < depth; i++)
4686 fprintf (stderr, " ");
4687 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4689 if (GTK_IS_LIST (w))
4691 for (i = 0; i < depth+1; i++)
4692 fprintf (stderr, " ");
4693 fprintf (stderr, "...list kids...\n");
4695 else if (GTK_IS_CONTAINER (w))
4697 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4700 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4708 delayed_scroll_kludge (gpointer data)
4710 state *s = (state *) data;
4711 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4712 ensure_selected_item_visible (w);
4714 /* Oh, this is just fucking lovely, too. */
4715 w = GTK_WIDGET (name_to_widget (s, "preview"));
4716 gtk_widget_hide (w);
4717 gtk_widget_show (w);
4719 return FALSE; /* do not re-execute timer */
4725 create_xscreensaver_demo (void)
4729 nb = name_to_widget (global_state_kludge, "preview_notebook");
4730 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4732 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4736 create_xscreensaver_settings_dialog (void)
4740 box = name_to_widget (global_state_kludge, "dialog_action_area");
4742 w = name_to_widget (global_state_kludge, "adv_button");
4743 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4745 w = name_to_widget (global_state_kludge, "std_button");
4746 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4748 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4751 #endif /* HAVE_GTK2 */
4754 main (int argc, char **argv)
4758 saver_preferences *p;
4759 Bool prefs_p = False;
4760 Bool settings_p = False;
4763 Widget toplevel_shell;
4764 char *real_progname = argv[0];
4767 Bool crapplet_p = False;
4771 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4772 textdomain (GETTEXT_PACKAGE);
4775 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4776 # else /* !HAVE_GTK2 */
4777 if (!setlocale (LC_ALL, ""))
4778 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4779 # endif /* !HAVE_GTK2 */
4781 #endif /* ENABLE_NLS */
4783 str = strrchr (real_progname, '/');
4784 if (str) real_progname = str+1;
4787 memset (s, 0, sizeof(*s));
4788 s->initializing_p = True;
4791 global_state_kludge = s; /* I hate C so much... */
4793 progname = real_progname;
4795 s->short_version = (char *) malloc (5);
4796 memcpy (s->short_version, screensaver_id + 17, 4);
4797 s->short_version [4] = 0;
4800 /* Register our error message logger for every ``log domain'' known.
4801 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4802 for all of the domains that seem to be in use.
4805 const char * const domains[] = { 0,
4806 "Gtk", "Gdk", "GLib", "GModule",
4807 "GThread", "Gnome", "GnomeUI" };
4808 for (i = 0; i < countof(domains); i++)
4809 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4812 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4815 const char *dir = DEFAULT_ICONDIR;
4816 if (*dir) add_pixmap_directory (dir);
4818 # endif /* !HAVE_GTK2 */
4819 #endif /* DEFAULT_ICONDIR */
4821 /* This is gross, but Gtk understands --display and not -display...
4823 for (i = 1; i < argc; i++)
4824 if (argv[i][0] && argv[i][1] &&
4825 !strncmp(argv[i], "-display", strlen(argv[i])))
4826 argv[i] = "--display";
4829 /* We need to parse this arg really early... Sigh. */
4830 for (i = 1; i < argc; i++)
4833 (!strcmp(argv[i], "--crapplet") ||
4834 !strcmp(argv[i], "--capplet")))
4836 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4839 for (j = i; j < argc; j++) /* remove it from the list */
4840 argv[j] = argv[j+1];
4842 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4843 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4845 fprintf (stderr, "%s: %s\n", real_progname, usage);
4847 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4850 (!strcmp(argv[i], "--debug") ||
4851 !strcmp(argv[i], "-debug") ||
4852 !strcmp(argv[i], "-d")))
4856 for (j = i; j < argc; j++) /* remove it from the list */
4857 argv[j] = argv[j+1];
4864 (!strcmp(argv[i], "-geometry") ||
4865 !strcmp(argv[i], "-geom") ||
4866 !strcmp(argv[i], "-geo") ||
4867 !strcmp(argv[i], "-g")))
4871 for (j = i; j < argc; j++) /* remove them from the list */
4872 argv[j] = argv[j+2];
4879 (!strcmp(argv[i], "--configdir")))
4883 hack_configuration_path = argv[i+1];
4884 for (j = i; j < argc; j++) /* remove them from the list */
4885 argv[j] = argv[j+2];
4889 if (0 != stat (hack_configuration_path, &st))
4892 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4896 else if (!S_ISDIR (st.st_mode))
4898 fprintf (stderr, "%s: not a directory: %s\n",
4899 blurb(), hack_configuration_path);
4907 fprintf (stderr, "%s: using config directory \"%s\"\n",
4908 progname, hack_configuration_path);
4911 /* Let Gtk open the X connection, then initialize Xt to use that
4912 same connection. Doctor Frankenstein would be proud.
4914 # ifdef HAVE_CRAPPLET
4917 GnomeClient *client;
4918 GnomeClientFlags flags = 0;
4920 int init_results = gnome_capplet_init ("screensaver-properties",
4922 argc, argv, NULL, 0, NULL);
4924 0 upon successful initialization;
4925 1 if --init-session-settings was passed on the cmdline;
4926 2 if --ignore was passed on the cmdline;
4929 So the 1 signifies just to init the settings, and quit, basically.
4930 (Meaning launch the xscreensaver daemon.)
4933 if (init_results < 0)
4936 g_error ("An initialization error occurred while "
4937 "starting xscreensaver-capplet.\n");
4939 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4940 real_progname, init_results);
4945 client = gnome_master_client ();
4948 flags = gnome_client_get_flags (client);
4950 if (flags & GNOME_CLIENT_IS_CONNECTED)
4953 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4954 gnome_client_get_id (client));
4957 char *session_args[20];
4959 session_args[i++] = real_progname;
4960 session_args[i++] = "--capplet";
4961 session_args[i++] = "--init-session-settings";
4962 session_args[i] = 0;
4963 gnome_client_set_priority (client, 20);
4964 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4965 gnome_client_set_restart_command (client, i, session_args);
4969 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4972 gnome_client_flush (client);
4975 if (init_results == 1)
4977 system ("xscreensaver -nosplash &");
4983 # endif /* HAVE_CRAPPLET */
4985 gtk_init (&argc, &argv);
4989 /* We must read exactly the same resources as xscreensaver.
4990 That means we must have both the same progclass *and* progname,
4991 at least as far as the resource database is concerned. So,
4992 put "xscreensaver" in argv[0] while initializing Xt.
4994 argv[0] = "xscreensaver";
4998 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
5000 XtToolkitInitialize ();
5001 app = XtCreateApplicationContext ();
5002 dpy = GDK_DISPLAY();
5003 XtAppSetFallbackResources (app, defaults);
5004 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
5005 toplevel_shell = XtAppCreateShell (progname, progclass,
5006 applicationShellWidgetClass,
5009 dpy = XtDisplay (toplevel_shell);
5010 db = XtDatabase (dpy);
5011 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
5012 XSetErrorHandler (demo_ehandler);
5014 /* Let's just ignore these. They seem to confuse Irix Gtk... */
5015 signal (SIGPIPE, SIG_IGN);
5017 /* After doing Xt-style command-line processing, complain about any
5018 unrecognized command-line arguments.
5020 for (i = 1; i < argc; i++)
5022 char *str = argv[i];
5023 if (str[0] == '-' && str[1] == '-')
5025 if (!strcmp (str, "-prefs"))
5027 else if (!strcmp (str, "-settings"))
5029 else if (crapplet_p)
5030 /* There are lots of random args that we don't care about when we're
5031 started as a crapplet, so just ignore unknown args in that case. */
5035 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
5037 fprintf (stderr, "%s: %s\n", real_progname, usage);
5042 /* Load the init file, which may end up consulting the X resource database
5043 and the site-wide app-defaults file. Note that at this point, it's
5044 important that `progname' be "xscreensaver", rather than whatever
5048 s->nscreens = screen_count (dpy);
5050 hack_environment (s); /* must be before initialize_sort_map() */
5052 load_init_file (dpy, p);
5053 initialize_sort_map (s);
5055 /* Now that Xt has been initialized, and the resources have been read,
5056 we can set our `progname' variable to something more in line with
5059 progname = real_progname;
5063 /* Print out all the resources we read. */
5065 XrmName name = { 0 };
5066 XrmClass class = { 0 };
5068 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
5074 /* Intern the atoms that xscreensaver_command() needs.
5076 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
5077 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
5078 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
5079 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5080 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5081 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5082 XA_SELECT = XInternAtom (dpy, "SELECT", False);
5083 XA_DEMO = XInternAtom (dpy, "DEMO", False);
5084 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5085 XA_BLANK = XInternAtom (dpy, "BLANK", False);
5086 XA_LOCK = XInternAtom (dpy, "LOCK", False);
5087 XA_EXIT = XInternAtom (dpy, "EXIT", False);
5088 XA_RESTART = XInternAtom (dpy, "RESTART", False);
5091 /* Create the window and all its widgets.
5093 s->base_widget = create_xscreensaver_demo ();
5094 s->popup_widget = create_xscreensaver_settings_dialog ();
5095 s->toplevel_widget = s->base_widget;
5098 /* Set the main window's title. */
5100 char *base_title = _("Screensaver Preferences");
5101 char *v = (char *) strdup(strchr(screensaver_id, ' '));
5102 char *s1, *s2, *s3, *s4;
5103 s1 = (char *) strchr(v, ' '); s1++;
5104 s2 = (char *) strchr(s1, ' ');
5105 s3 = (char *) strchr(v, '('); s3++;
5106 s4 = (char *) strchr(s3, ')');
5110 window_title = (char *) malloc (strlen (base_title) +
5111 strlen (progclass) +
5112 strlen (s1) + strlen (s3) +
5114 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
5115 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5116 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
5120 /* Adjust the (invisible) notebooks on the popup dialog... */
5122 GtkNotebook *notebook =
5123 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5124 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5128 gtk_widget_hide (std);
5129 # else /* !HAVE_XML */
5130 /* Make the advanced page be the only one available. */
5131 gtk_widget_set_sensitive (std, False);
5132 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5133 gtk_widget_hide (std);
5134 std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5135 gtk_widget_hide (std);
5137 # endif /* !HAVE_XML */
5139 gtk_notebook_set_page (notebook, page);
5140 gtk_notebook_set_show_tabs (notebook, False);
5143 /* Various other widget initializations...
5145 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5146 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5148 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5149 GTK_SIGNAL_FUNC (wm_popup_close_cb),
5152 populate_hack_list (s);
5153 populate_prefs_page (s);
5154 sensitize_demo_widgets (s, False);
5155 fix_text_entry_sizes (s);
5156 scroll_to_current_hack (s);
5158 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5159 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5163 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5164 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5166 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5167 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5169 #endif /* !HAVE_GTK2 */
5171 /* Hook up callbacks to the items on the mode menu. */
5173 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5174 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5175 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5177 for (i = 0; kids; kids = kids->next, i++)
5179 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5180 GTK_SIGNAL_FUNC (mode_menu_item_cb),
5183 /* The "random-same" mode menu item does not appear unless
5184 there are multple screens.
5186 if (s->nscreens <= 1 &&
5187 mode_menu_order[i] == RANDOM_HACKS_SAME)
5188 gtk_widget_hide (GTK_WIDGET (kids->data));
5191 if (s->nscreens <= 1) /* recompute option-menu size */
5193 gtk_widget_unrealize (GTK_WIDGET (menu));
5194 gtk_widget_realize (GTK_WIDGET (menu));
5199 /* Handle the -prefs command-line argument. */
5202 GtkNotebook *notebook =
5203 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5204 gtk_notebook_set_page (notebook, 1);
5207 # ifdef HAVE_CRAPPLET
5211 GtkWidget *outer_vbox;
5213 gtk_widget_hide (s->toplevel_widget);
5215 capplet = capplet_widget_new ();
5217 /* Make there be a "Close" button instead of "OK" and "Cancel" */
5218 # ifdef HAVE_CRAPPLET_IMMEDIATE
5219 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5220 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5221 /* In crapplet-mode, take off the menubar. */
5222 gtk_widget_hide (name_to_widget (s, "menubar"));
5224 /* Reparent our top-level container to be a child of the capplet
5227 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5228 gtk_widget_ref (outer_vbox);
5229 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5231 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5232 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5234 /* Find the window above us, and set the title and close handler. */
5236 GtkWidget *window = capplet;
5237 while (window && !GTK_IS_WINDOW (window))
5238 window = GET_PARENT (window);
5241 gtk_window_set_title (GTK_WINDOW (window), window_title);
5242 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5243 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5248 s->toplevel_widget = capplet;
5250 # endif /* HAVE_CRAPPLET */
5253 /* The Gnome folks hate the menubar. I think it's important to have access
5254 to the commands on the File menu (Restart Daemon, etc.) and to the
5255 About and Documentation commands on the Help menu.
5259 gtk_widget_hide (name_to_widget (s, "menubar"));
5263 free (window_title);
5267 /* After picking the default size, allow -geometry to override it. */
5269 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5272 gtk_widget_show (s->toplevel_widget);
5273 init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */
5274 fix_preview_visual (s);
5276 /* Realize page zero, so that we can diddle the scrollbar when the
5277 user tabs back to it -- otherwise, the current hack isn't scrolled
5278 to the first time they tab back there, when started with "-prefs".
5279 (Though it is if they then tab away, and back again.)
5281 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5282 #### understands this crap, explain to me how to make this work.
5284 gtk_widget_realize (name_to_widget (s, "demos_table"));
5287 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5290 /* Handle the --settings command-line argument. */
5292 gtk_timeout_add (500, settings_timer, 0);
5295 /* Issue any warnings about the running xscreensaver daemon. */
5297 the_network_is_not_the_computer (s);
5301 warning_dialog (s->toplevel_widget,
5303 "This version of xscreensaver is VERY OLD!\n"
5306 "http://www.jwz.org/xscreensaver/\n"
5308 "(If this is the latest version that your distro ships, then\n"
5309 "your distro is doing you a disservice. Build from source.)\n"
5314 /* Run the Gtk event loop, and not the Xt event loop. This means that
5315 if there were Xt timers or fds registered, they would never get serviced,
5316 and if there were any Xt widgets, they would never have events delivered.
5317 Fortunately, we're using Gtk for all of the UI, and only initialized
5318 Xt so that we could process the command line and use the X resource
5321 s->initializing_p = False;
5323 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5324 after we start up. Otherwise, it always appears scrolled to the top
5325 when in crapplet-mode. */
5326 gtk_timeout_add (500, delayed_scroll_kludge, s);
5330 /* Load every configurator in turn, to scan them for errors all at once. */
5334 for (i = 0; i < p->screenhacks_count; i++)
5336 screenhack *hack = p->screenhacks[i];
5337 conf_data *d = load_configurator (hack->command, s->debug_p);
5338 if (d) free_conf_data (d);
5344 # ifdef HAVE_CRAPPLET
5346 capplet_gtk_main ();
5348 # endif /* HAVE_CRAPPLET */
5351 kill_preview_subproc (s, False);
5355 #endif /* HAVE_GTK -- whole file */