1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2008 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #ifdef HAVE_GTK /* whole file */
19 #include <xscreensaver-intl.h>
28 # define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */
36 #endif /* ENABLE_NLS */
39 # include <pwd.h> /* for getpwuid() */
45 # include <sys/utsname.h> /* for uname() */
46 #endif /* HAVE_UNAME */
56 #ifdef HAVE_SYS_WAIT_H
57 # include <sys/wait.h> /* for waitpid() and associated macros */
61 #include <X11/Xproto.h> /* for CARD32 */
62 #include <X11/Xatom.h> /* for XA_INTEGER */
63 #include <X11/Intrinsic.h>
64 #include <X11/StringDefs.h>
66 /* We don't actually use any widget internals, but these are included
67 so that gdb will have debug info for the widgets... */
68 #include <X11/IntrinsicP.h>
69 #include <X11/ShellP.h>
73 # include <X11/Xmu/Error.h>
75 # include <Xmu/Error.h>
82 # include <X11/extensions/Xinerama.h>
83 #endif /* HAVE_XINERAMA */
89 # include <capplet-widget.h>
95 # include <glade/glade-xml.h>
97 #else /* !HAVE_GTK2 */
98 # define G_MODULE_EXPORT /**/
99 #endif /* !HAVE_GTK2 */
101 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
102 # define GLADE_DIR DEFAULT_ICONDIR
104 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
105 # define DEFAULT_ICONDIR GLADE_DIR
109 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
110 It is unused otherwise, so in that case, stub it out. */
111 static const char *hack_configuration_path = 0;
118 #include "resources.h" /* for parse_time() */
119 #include "visual.h" /* for has_writable_cells() */
120 #include "remote.h" /* for xscreensaver_command() */
123 #include "logo-50.xpm"
124 #include "logo-180.xpm"
126 #undef dgettext /* else these are defined twice... */
129 #include "demo-Gtk-widgets.h"
130 #include "demo-Gtk-support.h"
131 #include "demo-Gtk-conf.h"
143 #endif /* HAVE_GTK2 */
146 extern void exec_command (const char *shell, const char *command, int nice);
147 extern int on_path_p (const char *program);
149 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
152 #define countof(x) (sizeof((x))/sizeof((*x)))
155 /* You might think that to read an array of 32-bit quantities out of a
156 server-side property, you would pass an array of 32-bit data quantities
157 into XGetWindowProperty(). You would be wrong. You have to use an array
158 of longs, even if long is 64 bits (using 32 of each 64.)
163 char *progclass = "XScreenSaver";
166 /* The order of the items in the mode menu. */
167 static int mode_menu_order[] = {
168 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
173 char *short_version; /* version number of this xscreensaver build */
175 GtkWidget *toplevel_widget; /* the main window */
176 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
177 GtkWidget *popup_widget; /* the "Settings" dialog */
178 conf_data *cdata; /* private data for per-hack configuration */
181 GladeXML *glade_ui; /* Glade UI file */
182 #endif /* HAVE_GTK2 */
184 Bool debug_p; /* whether to print diagnostics */
185 Bool initializing_p; /* flag for breaking recursion loops */
186 Bool saving_p; /* flag for breaking recursion loops */
188 char *desired_preview_cmd; /* subprocess we intend to run */
189 char *running_preview_cmd; /* subprocess we are currently running */
190 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
191 Bool running_preview_error_p; /* whether the pid died abnormally */
193 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
194 int subproc_timer_id; /* timer to delay subproc launch */
195 int subproc_check_timer_id; /* timer to check whether it started up */
196 int subproc_check_countdown; /* how many more checks left */
198 int *list_elt_to_hack_number; /* table for sorting the hack list */
199 int *hack_number_to_list_elt; /* the inverse table */
200 Bool *hacks_available_p; /* whether hacks are on $PATH */
201 int total_available; /* how many are on $PATH */
202 int list_count; /* how many items are in the list: this may be
203 less than p->screenhacks_count, if some are
206 int _selected_list_element; /* don't use this: call
207 selected_list_element() instead */
209 int nscreens; /* How many X or Xinerama screens there are */
211 saver_preferences prefs;
216 /* Total fucking evilness due to the fact that it's rocket science to get
217 a closure object of our own down into the various widget callbacks. */
218 static state *global_state_kludge;
221 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
222 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
223 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
226 static void populate_demo_window (state *, int list_elt);
227 static void populate_prefs_page (state *);
228 static void populate_popup_window (state *);
230 static Bool flush_dialog_changes_and_save (state *);
231 static Bool flush_popup_changes_and_save (state *);
233 static int maybe_reload_init_file (state *);
234 static void await_xscreensaver (state *);
235 static Bool xscreensaver_running_p (state *);
236 static void sensitize_menu_items (state *s, Bool force_p);
237 static void force_dialog_repaint (state *s);
239 static void schedule_preview (state *, const char *cmd);
240 static void kill_preview_subproc (state *, Bool reset_p);
241 static void schedule_preview_check (state *);
244 /* Prototypes of functions used by the Glade-generated code,
247 void exit_menu_cb (GtkMenuItem *, gpointer user_data);
248 void about_menu_cb (GtkMenuItem *, gpointer user_data);
249 void doc_menu_cb (GtkMenuItem *, gpointer user_data);
250 void file_menu_cb (GtkMenuItem *, gpointer user_data);
251 void activate_menu_cb (GtkMenuItem *, gpointer user_data);
252 void lock_menu_cb (GtkMenuItem *, gpointer user_data);
253 void kill_menu_cb (GtkMenuItem *, gpointer user_data);
254 void restart_menu_cb (GtkWidget *, gpointer user_data);
255 void run_this_cb (GtkButton *, gpointer user_data);
256 void manual_cb (GtkButton *, gpointer user_data);
257 void run_next_cb (GtkButton *, gpointer user_data);
258 void run_prev_cb (GtkButton *, gpointer user_data);
259 void pref_changed_cb (GtkWidget *, gpointer user_data);
260 gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
261 void mode_menu_item_cb (GtkWidget *, gpointer user_data);
262 void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
263 gint page_num, gpointer user_data);
264 void browse_image_dir_cb (GtkButton *, gpointer user_data);
265 void browse_text_file_cb (GtkButton *, gpointer user_data);
266 void browse_text_program_cb (GtkButton *, gpointer user_data);
267 void settings_cb (GtkButton *, gpointer user_data);
268 void settings_adv_cb (GtkButton *, gpointer user_data);
269 void settings_std_cb (GtkButton *, gpointer user_data);
270 void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
271 gint page_num, gpointer user_data);
272 void settings_cancel_cb (GtkButton *, gpointer user_data);
273 void settings_ok_cb (GtkButton *, gpointer user_data);
275 static void kill_gnome_screensaver (void);
276 static void kill_kde_screensaver (void);
279 /* Some random utility functions
282 const char *blurb (void);
287 time_t now = time ((time_t *) 0);
288 char *ct = (char *) ctime (&now);
289 static char buf[255];
290 int n = strlen(progname);
292 strncpy(buf, progname, n);
295 strncpy(buf+n, ct+11, 8);
296 strcpy(buf+n+9, ": ");
302 name_to_widget (state *s, const char *name)
312 /* First try to load the Glade file from the current directory;
313 if there isn't one there, check the installed directory.
315 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
316 const char * const files[] = { GLADE_FILE_NAME,
317 GLADE_DIR "/" GLADE_FILE_NAME };
319 for (i = 0; i < countof (files); i++)
322 if (!stat (files[i], &st))
324 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
331 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
332 "\tfrom " GLADE_DIR "/ or current directory.\n",
336 # undef GLADE_FILE_NAME
338 glade_xml_signal_autoconnect (s->glade_ui);
341 w = glade_xml_get_widget (s->glade_ui, name);
343 #else /* !HAVE_GTK2 */
345 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
348 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
350 #endif /* HAVE_GTK2 */
353 fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
359 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
360 Takes a scroller, viewport, or list as an argument.
363 ensure_selected_item_visible (GtkWidget *widget)
367 GtkTreeSelection *selection;
371 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
372 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
373 path = gtk_tree_path_new_first ();
375 path = gtk_tree_model_get_path (model, &iter);
377 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
379 gtk_tree_path_free (path);
381 #else /* !HAVE_GTK2 */
383 GtkScrolledWindow *scroller = 0;
385 GtkList *list_widget = 0;
389 GtkWidget *selected = 0;
392 gint parent_h, child_y, child_h, children_h, ignore;
393 double ratio_t, ratio_b;
395 if (GTK_IS_SCROLLED_WINDOW (widget))
397 scroller = GTK_SCROLLED_WINDOW (widget);
398 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
399 list_widget = GTK_LIST (GTK_BIN(vp)->child);
401 else if (GTK_IS_VIEWPORT (widget))
403 vp = GTK_VIEWPORT (widget);
404 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
405 list_widget = GTK_LIST (GTK_BIN(vp)->child);
407 else if (GTK_IS_LIST (widget))
409 list_widget = GTK_LIST (widget);
410 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
411 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
416 slist = list_widget->selection;
417 selected = (slist ? GTK_WIDGET (slist->data) : 0);
421 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
423 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
424 kids; kids = kids->next)
427 adj = gtk_scrolled_window_get_vadjustment (scroller);
429 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
430 &ignore, &ignore, &ignore, &parent_h, &ignore);
431 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
432 &ignore, &child_y, &ignore, &child_h, &ignore);
433 children_h = nkids * child_h;
435 ratio_t = ((double) child_y) / ((double) children_h);
436 ratio_b = ((double) child_y + child_h) / ((double) children_h);
438 if (adj->upper == 0.0) /* no items in list */
441 if (ratio_t < (adj->value / adj->upper) ||
442 ratio_b > ((adj->value + adj->page_size) / adj->upper))
445 int slop = parent_h * 0.75; /* how much to overshoot by */
447 if (ratio_t < (adj->value / adj->upper))
449 double ratio_w = ((double) parent_h) / ((double) children_h);
450 double ratio_l = (ratio_b - ratio_t);
451 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
454 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
456 target = ratio_t * adj->upper;
460 if (target > adj->upper - adj->page_size)
461 target = adj->upper - adj->page_size;
465 gtk_adjustment_set_value (adj, target);
467 #endif /* !HAVE_GTK2 */
471 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
473 GtkWidget *shell = GTK_WIDGET (user_data);
474 while (shell->parent)
475 shell = shell->parent;
476 gtk_widget_destroy (GTK_WIDGET (shell));
480 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
482 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
484 restart_menu_cb (widget, user_data);
485 warning_dialog_dismiss_cb (widget, user_data);
488 static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data)
490 kill_gnome_screensaver ();
491 warning_dialog_dismiss_cb (widget, user_data);
494 static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data)
496 kill_kde_screensaver ();
497 warning_dialog_dismiss_cb (widget, user_data);
500 typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
503 warning_dialog (GtkWidget *parent, const char *message,
504 dialog_button button_type, int center)
506 char *msg = strdup (message);
509 GtkWidget *dialog = gtk_dialog_new ();
510 GtkWidget *label = 0;
512 GtkWidget *cancel = 0;
515 while (parent && !parent->window)
516 parent = parent->parent;
519 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
521 fprintf (stderr, "%s: too early for dialog?\n", progname);
529 char *s = strchr (head, '\n');
532 sprintf (name, "label%d", i++);
535 label = gtk_label_new (head);
537 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
538 #endif /* HAVE_GTK2 */
543 GTK_WIDGET (label)->style =
544 gtk_style_copy (GTK_WIDGET (label)->style);
545 GTK_WIDGET (label)->style->font =
546 gdk_font_load (get_string_resource("warning_dialog.headingFont",
548 gtk_widget_set_style (GTK_WIDGET (label),
549 GTK_WIDGET (label)->style);
551 #endif /* !HAVE_GTK2 */
553 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
554 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
555 label, TRUE, TRUE, 0);
556 gtk_widget_show (label);
567 label = gtk_label_new ("");
568 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
569 label, TRUE, TRUE, 0);
570 gtk_widget_show (label);
572 label = gtk_hbutton_box_new ();
573 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
574 label, TRUE, TRUE, 0);
577 if (button_type != D_NONE)
579 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
580 gtk_container_add (GTK_CONTAINER (label), cancel);
583 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
584 gtk_container_add (GTK_CONTAINER (label), ok);
586 #else /* !HAVE_GTK2 */
588 ok = gtk_button_new_with_label ("OK");
589 gtk_container_add (GTK_CONTAINER (label), ok);
591 if (button_type != D_NONE)
593 cancel = gtk_button_new_with_label ("Cancel");
594 gtk_container_add (GTK_CONTAINER (label), cancel);
597 #endif /* !HAVE_GTK2 */
599 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
600 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
601 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
602 GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
603 gtk_widget_show (ok);
604 gtk_widget_grab_focus (ok);
608 GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
609 gtk_widget_show (cancel);
611 gtk_widget_show (label);
612 gtk_widget_show (dialog);
614 if (button_type != D_NONE)
617 switch (button_type) {
618 case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break;
619 case D_GNOME: fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb); break;
620 case D_KDE: fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb); break;
621 default: abort(); break;
623 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn,
625 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
626 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
631 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
632 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
636 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
637 GTK_WIDGET (parent)->window);
640 gtk_window_present (GTK_WINDOW (dialog));
641 #else /* !HAVE_GTK2 */
642 gdk_window_show (GTK_WIDGET (dialog)->window);
643 gdk_window_raise (GTK_WIDGET (dialog)->window);
644 #endif /* !HAVE_GTK2 */
651 run_cmd (state *s, Atom command, int arg)
656 flush_dialog_changes_and_save (s);
657 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
659 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
660 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
667 sprintf (buf, "Error:\n\n%s", err);
669 strcpy (buf, "Unknown error!");
670 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
674 sensitize_menu_items (s, True);
675 force_dialog_repaint (s);
680 run_hack (state *s, int list_elt, Bool report_errors_p)
686 if (list_elt < 0) return;
687 hack_number = s->list_elt_to_hack_number[list_elt];
689 flush_dialog_changes_and_save (s);
690 schedule_preview (s, 0);
692 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
695 if (status < 0 && report_errors_p)
697 if (xscreensaver_running_p (s))
699 /* Kludge: ignore the spurious "window unexpectedly deleted"
701 if (err && strstr (err, "unexpectedly deleted"))
708 sprintf (buf, "Error:\n\n%s", err);
710 strcpy (buf, "Unknown error!");
711 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
716 /* The error is that the daemon isn't running;
719 const char *d = DisplayString (GDK_DISPLAY());
723 "The XScreenSaver daemon doesn't seem to be running\n"
724 "on display \"%s\". Launch it now?"),
726 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
732 sensitize_menu_items (s, False);
739 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
740 libglade work on Cygwin; apparently all Glade callbacks need this magic
741 extra declaration. I do not pretend to understand.
745 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
747 state *s = global_state_kludge; /* I hate C so much... */
748 flush_dialog_changes_and_save (s);
749 kill_preview_subproc (s, False);
754 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
756 state *s = (state *) data;
757 flush_dialog_changes_and_save (s);
764 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
767 char *vers = strdup (screensaver_id + 4);
770 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
772 s = strchr (vers, ',');
776 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
777 non-ASCII characters aren't allowed in localizable string keys."
778 (I don't want to just use (c) instead of © because that doesn't
779 look as good in the plain-old default Latin1 "C" locale.)
782 sprintf(copy, ("Copyright \xC2\xA9 1991-2008 %s"), s);
783 #else /* !HAVE_GTK2 */
784 sprintf(copy, ("Copyright \251 1991-2008 %s"), s);
785 #endif /* !HAVE_GTK2 */
787 sprintf (msg, "%s\n\n%s", copy, desc);
789 /* I can't make gnome_about_new() work here -- it starts dying in
790 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
791 then this might be the thing to do:
795 const gchar *auth[] = { 0 };
796 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
798 gtk_widget_show (about);
800 #else / * GTK but not GNOME * /
804 GdkColormap *colormap;
805 GdkPixmap *gdkpixmap;
808 GtkWidget *dialog = gtk_dialog_new ();
809 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
810 GtkWidget *parent = GTK_WIDGET (menuitem);
811 while (parent->parent)
812 parent = parent->parent;
814 hbox = gtk_hbox_new (FALSE, 20);
815 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
816 hbox, TRUE, TRUE, 0);
818 colormap = gtk_widget_get_colormap (parent);
820 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
821 (gchar **) logo_180_xpm);
822 icon = gtk_pixmap_new (gdkpixmap, mask);
823 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
825 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
827 vbox = gtk_vbox_new (FALSE, 0);
828 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
830 label1 = gtk_label_new (vers);
831 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
832 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
833 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
836 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
837 GTK_WIDGET (label1)->style->font =
838 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
839 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
840 #endif /* HAVE_GTK2 */
842 label2 = gtk_label_new (msg);
843 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
844 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
845 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
848 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
849 GTK_WIDGET (label2)->style->font =
850 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
851 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
852 #endif /* HAVE_GTK2 */
854 hb = gtk_hbutton_box_new ();
856 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
860 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
861 #else /* !HAVE_GTK2 */
862 ok = gtk_button_new_with_label (_("OK"));
863 #endif /* !HAVE_GTK2 */
864 gtk_container_add (GTK_CONTAINER (hb), ok);
866 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
867 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
868 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
870 gtk_widget_show (hbox);
871 gtk_widget_show (icon);
872 gtk_widget_show (vbox);
873 gtk_widget_show (label1);
874 gtk_widget_show (label2);
875 gtk_widget_show (hb);
876 gtk_widget_show (ok);
877 gtk_widget_show (dialog);
879 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
880 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
882 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
883 GTK_WIDGET (parent)->window);
884 gdk_window_show (GTK_WIDGET (dialog)->window);
885 gdk_window_raise (GTK_WIDGET (dialog)->window);
891 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
893 state *s = global_state_kludge; /* I hate C so much... */
894 saver_preferences *p = &s->prefs;
897 if (!p->help_url || !*p->help_url)
899 warning_dialog (s->toplevel_widget,
901 "No Help URL has been specified.\n"), D_NONE, 100);
905 help_command = (char *) malloc (strlen (p->load_url_command) +
906 (strlen (p->help_url) * 4) + 20);
907 strcpy (help_command, "( ");
908 sprintf (help_command + strlen(help_command),
910 p->help_url, p->help_url, p->help_url, p->help_url);
911 strcat (help_command, " ) &");
912 if (system (help_command) < 0)
913 fprintf (stderr, "%s: fork error\n", blurb());
919 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
921 state *s = global_state_kludge; /* I hate C so much... */
922 sensitize_menu_items (s, False);
927 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
929 state *s = global_state_kludge; /* I hate C so much... */
930 run_cmd (s, XA_ACTIVATE, 0);
935 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
937 state *s = global_state_kludge; /* I hate C so much... */
938 run_cmd (s, XA_LOCK, 0);
943 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
945 state *s = global_state_kludge; /* I hate C so much... */
946 run_cmd (s, XA_EXIT, 0);
951 restart_menu_cb (GtkWidget *widget, gpointer user_data)
953 state *s = global_state_kludge; /* I hate C so much... */
954 flush_dialog_changes_and_save (s);
955 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
957 if (system ("xscreensaver -nosplash &") < 0)
958 fprintf (stderr, "%s: fork error\n", blurb());
960 await_xscreensaver (s);
964 xscreensaver_running_p (state *s)
966 Display *dpy = GDK_DISPLAY();
968 server_xscreensaver_version (dpy, &rversion, 0, 0);
976 await_xscreensaver (state *s)
981 while (!ok && (--countdown > 0))
982 if (xscreensaver_running_p (s))
985 sleep (1); /* If it's not there yet, wait a second... */
987 sensitize_menu_items (s, True);
991 /* Timed out, no screensaver running. */
994 Bool root_p = (geteuid () == 0);
998 "The xscreensaver daemon did not start up properly.\n"
1004 __extension__ /* don't warn about "string length is greater than
1005 the length ISO C89 compilers are required to
1006 support" in the following expression... */
1009 _("You are running as root. This usually means that xscreensaver\n"
1010 "was unable to contact your X server because access control is\n"
1011 "turned on. Try running this command:\n"
1013 " xhost +localhost\n"
1015 "and then selecting `File / Restart Daemon'.\n"
1017 "Note that turning off access control will allow anyone logged\n"
1018 "on to this machine to access your screen, which might be\n"
1019 "considered a security problem. Please read the xscreensaver\n"
1020 "manual and FAQ for more information.\n"
1022 "You shouldn't run X as root. Instead, you should log in as a\n"
1023 "normal user, and `su' as necessary."));
1025 strcat (buf, _("Please check your $PATH and permissions."));
1027 warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1030 force_dialog_repaint (s);
1035 selected_list_element (state *s)
1037 return s->_selected_list_element;
1042 demo_write_init_file (state *s, saver_preferences *p)
1044 Display *dpy = GDK_DISPLAY();
1047 /* #### try to figure out why shit keeps getting reordered... */
1048 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1052 if (!write_init_file (dpy, p, s->short_version, False))
1055 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1060 const char *f = init_file_name();
1062 warning_dialog (s->toplevel_widget,
1063 _("Error:\n\nCouldn't determine init file name!\n"),
1067 char *b = (char *) malloc (strlen(f) + 1024);
1068 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1069 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1077 G_MODULE_EXPORT void
1078 run_this_cb (GtkButton *button, gpointer user_data)
1080 state *s = global_state_kludge; /* I hate C so much... */
1081 int list_elt = selected_list_element (s);
1082 if (list_elt < 0) return;
1083 if (!flush_dialog_changes_and_save (s))
1084 run_hack (s, list_elt, True);
1088 G_MODULE_EXPORT void
1089 manual_cb (GtkButton *button, gpointer user_data)
1091 Display *dpy = GDK_DISPLAY();
1092 state *s = global_state_kludge; /* I hate C so much... */
1093 saver_preferences *p = &s->prefs;
1094 GtkWidget *list_widget = name_to_widget (s, "list");
1095 int list_elt = selected_list_element (s);
1097 char *name, *name2, *cmd, *str;
1099 if (list_elt < 0) return;
1100 hack_number = s->list_elt_to_hack_number[list_elt];
1102 flush_dialog_changes_and_save (s);
1103 ensure_selected_item_visible (list_widget);
1105 name = strdup (p->screenhacks[hack_number]->command);
1108 while (isspace (*name2)) name2++;
1110 while (*str && !isspace (*str)) str++;
1112 str = strrchr (name2, '/');
1113 if (str) name2 = str+1;
1115 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1118 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1119 strcpy (cmd2, "( ");
1120 sprintf (cmd2 + strlen (cmd2),
1122 name2, name2, name2, name2);
1123 strcat (cmd2, " ) &");
1124 if (system (cmd2) < 0)
1125 fprintf (stderr, "%s: fork error\n", blurb());
1130 warning_dialog (GTK_WIDGET (button),
1131 _("Error:\n\nno `manualCommand' resource set."),
1140 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1142 GtkWidget *parent = name_to_widget (s, "scroller");
1143 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
1146 GtkTreeModel *model;
1147 GtkTreeSelection *selection;
1148 #endif /* HAVE_GTK2 */
1150 if (!was) gtk_widget_set_sensitive (parent, True);
1152 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1154 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1156 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1157 gtk_tree_selection_select_iter (selection, &iter);
1159 #else /* !HAVE_GTK2 */
1160 gtk_list_select_item (GTK_LIST (list), list_elt);
1161 #endif /* !HAVE_GTK2 */
1162 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1163 if (!was) gtk_widget_set_sensitive (parent, False);
1167 G_MODULE_EXPORT void
1168 run_next_cb (GtkButton *button, gpointer user_data)
1170 state *s = global_state_kludge; /* I hate C so much... */
1171 /* saver_preferences *p = &s->prefs; */
1172 Bool ops = s->preview_suppressed_p;
1174 GtkWidget *list_widget = name_to_widget (s, "list");
1175 int list_elt = selected_list_element (s);
1182 if (list_elt >= s->list_count)
1185 s->preview_suppressed_p = True;
1187 flush_dialog_changes_and_save (s);
1188 force_list_select_item (s, list_widget, list_elt, True);
1189 populate_demo_window (s, list_elt);
1190 run_hack (s, list_elt, False);
1192 s->preview_suppressed_p = ops;
1196 G_MODULE_EXPORT void
1197 run_prev_cb (GtkButton *button, gpointer user_data)
1199 state *s = global_state_kludge; /* I hate C so much... */
1200 /* saver_preferences *p = &s->prefs; */
1201 Bool ops = s->preview_suppressed_p;
1203 GtkWidget *list_widget = name_to_widget (s, "list");
1204 int list_elt = selected_list_element (s);
1207 list_elt = s->list_count - 1;
1212 list_elt = s->list_count - 1;
1214 s->preview_suppressed_p = True;
1216 flush_dialog_changes_and_save (s);
1217 force_list_select_item (s, list_widget, list_elt, True);
1218 populate_demo_window (s, list_elt);
1219 run_hack (s, list_elt, False);
1221 s->preview_suppressed_p = ops;
1225 /* Writes the given settings into prefs.
1226 Returns true if there was a change, False otherwise.
1227 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1230 flush_changes (state *s,
1233 const char *command,
1236 saver_preferences *p = &s->prefs;
1237 Bool changed = False;
1240 if (list_elt < 0 || list_elt >= s->list_count)
1243 hack_number = s->list_elt_to_hack_number[list_elt];
1244 hack = p->screenhacks[hack_number];
1246 if (enabled_p != -1 &&
1247 enabled_p != hack->enabled_p)
1249 hack->enabled_p = enabled_p;
1252 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1253 blurb(), hack->name, enabled_p);
1258 if (!hack->command || !!strcmp (command, hack->command))
1260 if (hack->command) free (hack->command);
1261 hack->command = strdup (command);
1264 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1265 blurb(), hack->name, command);
1271 const char *ov = hack->visual;
1272 if (!ov || !*ov) ov = "any";
1273 if (!*visual) visual = "any";
1274 if (!!strcasecmp (visual, ov))
1276 if (hack->visual) free (hack->visual);
1277 hack->visual = strdup (visual);
1280 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1281 blurb(), hack->name, visual);
1289 /* Helper for the text fields that contain time specifications:
1290 this parses the text, and does error checking.
1293 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1298 if (!sec_p || strchr (line, ':'))
1299 value = parse_time ((char *) line, sec_p, True);
1303 if (sscanf (line, "%d%c", &value, &c) != 1)
1309 value *= 1000; /* Time measures in microseconds */
1315 "Unparsable time format: \"%s\"\n"),
1317 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1326 directory_p (const char *path)
1329 if (!path || !*path)
1331 else if (stat (path, &st))
1333 else if (!S_ISDIR (st.st_mode))
1340 file_p (const char *path)
1343 if (!path || !*path)
1345 else if (stat (path, &st))
1347 else if (S_ISDIR (st.st_mode))
1354 normalize_directory (const char *path)
1358 if (!path || !*path) return 0;
1360 p2 = (char *) malloc (L + 2);
1362 if (p2[L-1] == '/') /* remove trailing slash */
1365 for (s = p2; s && *s; s++)
1368 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1369 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1372 while (s0 > p2 && s0[-1] != '/')
1382 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1383 strcpy (s, s+2), s--;
1384 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1388 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1389 while (s[0] == '/' && s[1] == '/')
1392 /* and strip trailing whitespace for good measure. */
1394 while (isspace(p2[L-1]))
1407 } FlushForeachClosure;
1410 flush_checkbox (GtkTreeModel *model,
1415 FlushForeachClosure *closure = data;
1418 gtk_tree_model_get (model, iter,
1419 COL_ENABLED, &checked,
1422 if (flush_changes (closure->s, closure->i,
1424 *closure->changed = True;
1428 /* don't remove row */
1432 #endif /* HAVE_GTK2 */
1434 /* Flush out any changes made in the main dialog window (where changes
1435 take place immediately: clicking on a checkbox causes the init file
1436 to be written right away.)
1439 flush_dialog_changes_and_save (state *s)
1441 saver_preferences *p = &s->prefs;
1442 saver_preferences P2, *p2 = &P2;
1444 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1445 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1446 FlushForeachClosure closure;
1447 #else /* !HAVE_GTK2 */
1448 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1449 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1451 #endif /* !HAVE_GTK2 */
1453 Bool changed = False;
1456 if (s->saving_p) return False;
1461 /* Flush any checkbox changes in the list down into the prefs struct.
1465 closure.changed = &changed;
1467 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1469 #else /* !HAVE_GTK2 */
1471 for (i = 0; kids; kids = kids->next, i++)
1473 GtkWidget *line = GTK_WIDGET (kids->data);
1474 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1475 GtkWidget *line_check =
1476 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1478 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1480 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1483 #endif /* ~HAVE_GTK2 */
1485 /* Flush the non-hack-specific settings down into the prefs struct.
1488 # define SECONDS(FIELD,NAME) \
1489 w = name_to_widget (s, (NAME)); \
1490 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1492 # define MINUTES(FIELD,NAME) \
1493 w = name_to_widget (s, (NAME)); \
1494 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1496 # define CHECKBOX(FIELD,NAME) \
1497 w = name_to_widget (s, (NAME)); \
1498 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1500 # define PATHNAME(FIELD,NAME) \
1501 w = name_to_widget (s, (NAME)); \
1502 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1504 # define TEXT(FIELD,NAME) \
1505 w = name_to_widget (s, (NAME)); \
1506 (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1508 MINUTES (&p2->timeout, "timeout_spinbutton");
1509 MINUTES (&p2->cycle, "cycle_spinbutton");
1510 CHECKBOX (p2->lock_p, "lock_button");
1511 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1513 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1514 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1515 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1516 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1518 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1519 CHECKBOX (p2->grab_video_p, "grab_video_button");
1520 CHECKBOX (p2->random_image_p, "grab_image_button");
1521 PATHNAME (p2->image_directory, "image_text");
1524 CHECKBOX (p2->verbose_p, "verbose_button");
1525 CHECKBOX (p2->capture_stderr_p, "capture_button");
1526 CHECKBOX (p2->splash_p, "splash_button");
1531 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1532 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1533 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1534 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1535 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1536 TEXT (p2->text_literal, "text_entry");
1537 PATHNAME (p2->text_file, "text_file_entry");
1538 PATHNAME (p2->text_program, "text_program_entry");
1539 PATHNAME (p2->text_program, "text_program_entry");
1540 TEXT (p2->text_url, "text_url_entry");
1543 CHECKBOX (p2->install_cmap_p, "install_button");
1544 CHECKBOX (p2->fade_p, "fade_button");
1545 CHECKBOX (p2->unfade_p, "unfade_button");
1546 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1554 /* Warn if the image directory doesn't exist.
1556 if (p2->image_directory &&
1557 *p2->image_directory &&
1558 !directory_p (p2->image_directory))
1561 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1562 p2->image_directory);
1563 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1567 /* Map the mode menu to `saver_mode' enum values. */
1569 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1570 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1571 GtkWidget *selected = gtk_menu_get_active (menu);
1572 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1573 int menu_elt = g_list_index (kids, (gpointer) selected);
1574 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1575 p2->mode = mode_menu_order[menu_elt];
1578 if (p2->mode == ONE_HACK)
1580 int list_elt = selected_list_element (s);
1581 p2->selected_hack = (list_elt >= 0
1582 ? s->list_elt_to_hack_number[list_elt]
1586 # define COPY(field, name) \
1587 if (p->field != p2->field) { \
1590 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1592 p->field = p2->field
1595 COPY(selected_hack, "selected_hack");
1597 COPY(timeout, "timeout");
1598 COPY(cycle, "cycle");
1599 COPY(lock_p, "lock_p");
1600 COPY(lock_timeout, "lock_timeout");
1602 COPY(dpms_enabled_p, "dpms_enabled_p");
1603 COPY(dpms_standby, "dpms_standby");
1604 COPY(dpms_suspend, "dpms_suspend");
1605 COPY(dpms_off, "dpms_off");
1608 COPY(verbose_p, "verbose_p");
1609 COPY(capture_stderr_p, "capture_stderr_p");
1610 COPY(splash_p, "splash_p");
1613 COPY(tmode, "tmode");
1615 COPY(install_cmap_p, "install_cmap_p");
1616 COPY(fade_p, "fade_p");
1617 COPY(unfade_p, "unfade_p");
1618 COPY(fade_seconds, "fade_seconds");
1620 COPY(grab_desktop_p, "grab_desktop_p");
1621 COPY(grab_video_p, "grab_video_p");
1622 COPY(random_image_p, "random_image_p");
1626 # define COPYSTR(FIELD,NAME) \
1629 strcmp(p->FIELD, p2->FIELD)) \
1633 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1635 if (p->FIELD && p->FIELD != p2->FIELD) \
1637 p->FIELD = p2->FIELD; \
1640 COPYSTR(image_directory, "image_directory");
1641 COPYSTR(text_literal, "text_literal");
1642 COPYSTR(text_file, "text_file");
1643 COPYSTR(text_program, "text_program");
1644 COPYSTR(text_url, "text_url");
1647 populate_prefs_page (s);
1651 Display *dpy = GDK_DISPLAY();
1652 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1653 sync_server_dpms_settings (dpy, enabled_p,
1654 p->dpms_standby / 1000,
1655 p->dpms_suspend / 1000,
1659 changed = demo_write_init_file (s, p);
1662 s->saving_p = False;
1667 /* Flush out any changes made in the popup dialog box (where changes
1668 take place only when the OK button is clicked.)
1671 flush_popup_changes_and_save (state *s)
1673 Bool changed = False;
1674 saver_preferences *p = &s->prefs;
1675 int list_elt = selected_list_element (s);
1677 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1678 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1680 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1681 const char *command = gtk_entry_get_text (cmd);
1686 if (s->saving_p) return False;
1692 if (maybe_reload_init_file (s) != 0)
1698 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1700 if (!strcasecmp (visual, "")) visual = "";
1701 else if (!strcasecmp (visual, "any")) visual = "";
1702 else if (!strcasecmp (visual, "default")) visual = "Default";
1703 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1704 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1705 else if (!strcasecmp (visual, "best")) visual = "Best";
1706 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1707 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1708 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1709 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1710 else if (!strcasecmp (visual, "color")) visual = "Color";
1711 else if (!strcasecmp (visual, "gl")) visual = "GL";
1712 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1713 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1714 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1715 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1716 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1717 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1718 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1719 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1720 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1723 gdk_beep (); /* unparsable */
1725 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1728 changed = flush_changes (s, list_elt, -1, command, visual);
1731 changed = demo_write_init_file (s, p);
1733 /* Do this to re-launch the hack if (and only if) the command line
1735 populate_demo_window (s, selected_list_element (s));
1739 s->saving_p = False;
1744 G_MODULE_EXPORT void
1745 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1747 state *s = global_state_kludge; /* I hate C so much... */
1748 if (! s->initializing_p)
1750 s->initializing_p = True;
1751 flush_dialog_changes_and_save (s);
1752 s->initializing_p = False;
1756 G_MODULE_EXPORT gboolean
1757 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1759 pref_changed_cb (widget, user_data);
1763 /* Callback on menu items in the "mode" options menu.
1765 G_MODULE_EXPORT void
1766 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1768 state *s = (state *) user_data;
1769 saver_preferences *p = &s->prefs;
1770 GtkWidget *list = name_to_widget (s, "list");
1773 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1775 saver_mode new_mode;
1779 if (menu_items->data == widget)
1782 menu_items = menu_items->next;
1784 if (!menu_items) abort();
1786 new_mode = mode_menu_order[menu_index];
1788 /* Keep the same list element displayed as before; except if we're
1789 switching *to* "one screensaver" mode from any other mode, set
1790 "the one" to be that which is currently selected.
1792 list_elt = selected_list_element (s);
1793 if (new_mode == ONE_HACK)
1794 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1797 saver_mode old_mode = p->mode;
1799 populate_demo_window (s, list_elt);
1800 force_list_select_item (s, list, list_elt, True);
1801 p->mode = old_mode; /* put it back, so the init file gets written */
1804 pref_changed_cb (widget, user_data);
1808 G_MODULE_EXPORT void
1809 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1810 gint page_num, gpointer user_data)
1812 state *s = global_state_kludge; /* I hate C so much... */
1813 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1815 /* If we're switching to page 0, schedule the current hack to be run.
1816 Otherwise, schedule it to stop. */
1818 populate_demo_window (s, selected_list_element (s));
1820 schedule_preview (s, 0);
1825 list_activated_cb (GtkTreeView *list,
1827 GtkTreeViewColumn *column,
1834 g_return_if_fail (!gdk_pointer_is_grabbed ());
1836 str = gtk_tree_path_to_string (path);
1837 list_elt = strtol (str, NULL, 10);
1841 run_hack (s, list_elt, True);
1845 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1847 state *s = (state *)data;
1848 GtkTreeModel *model;
1854 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1857 path = gtk_tree_model_get_path (model, &iter);
1858 str = gtk_tree_path_to_string (path);
1859 list_elt = strtol (str, NULL, 10);
1861 gtk_tree_path_free (path);
1864 populate_demo_window (s, list_elt);
1865 flush_dialog_changes_and_save (s);
1867 /* Re-populate the Settings window any time a new item is selected
1868 in the list, in case both windows are currently visible.
1870 populate_popup_window (s);
1873 #else /* !HAVE_GTK2 */
1875 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1876 list_select_cb that comes in
1877 *after* we've double-clicked.
1881 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1884 state *s = (state *) data;
1885 if (event->type == GDK_2BUTTON_PRESS)
1887 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1888 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1890 last_doubleclick_time = time ((time_t *) 0);
1893 run_hack (s, list_elt, True);
1901 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1903 state *s = (state *) data;
1904 time_t now = time ((time_t *) 0);
1906 if (now >= last_doubleclick_time + 2)
1908 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1909 populate_demo_window (s, list_elt);
1910 flush_dialog_changes_and_save (s);
1915 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1917 state *s = (state *) data;
1918 populate_demo_window (s, -1);
1919 flush_dialog_changes_and_save (s);
1922 #endif /* !HAVE_GTK2 */
1925 /* Called when the checkboxes that are in the left column of the
1926 scrolling list are clicked. This both populates the right pane
1927 (just as clicking on the label (really, listitem) does) and
1928 also syncs this checkbox with the right pane Enabled checkbox.
1933 GtkCellRendererToggle *toggle,
1935 #else /* !HAVE_GTK2 */
1937 #endif /* !HAVE_GTK2 */
1940 state *s = (state *) data;
1943 GtkScrolledWindow *scroller =
1944 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1945 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1946 GtkTreeModel *model = gtk_tree_view_get_model (list);
1947 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1950 #else /* !HAVE_GTK2 */
1951 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1952 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1954 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1955 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1956 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1957 #endif /* !HAVE_GTK2 */
1964 if (!gtk_tree_model_get_iter (model, &iter, path))
1966 g_warning ("bad path: %s", path_string);
1969 gtk_tree_path_free (path);
1971 gtk_tree_model_get (model, &iter,
1972 COL_ENABLED, &active,
1975 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1976 COL_ENABLED, !active,
1979 list_elt = strtol (path_string, NULL, 10);
1980 #else /* !HAVE_GTK2 */
1981 list_elt = gtk_list_child_position (list, line);
1982 #endif /* !HAVE_GTK2 */
1984 /* remember previous scroll position of the top of the list */
1985 adj = gtk_scrolled_window_get_vadjustment (scroller);
1986 scroll_top = adj->value;
1988 flush_dialog_changes_and_save (s);
1989 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1990 populate_demo_window (s, list_elt);
1992 /* restore the previous scroll position of the top of the list.
1993 this is weak, but I don't really know why it's moving... */
1994 gtk_adjustment_set_value (adj, scroll_top);
2000 GtkFileSelection *widget;
2001 } file_selection_data;
2006 store_image_directory (GtkWidget *button, gpointer user_data)
2008 file_selection_data *fsd = (file_selection_data *) user_data;
2009 state *s = fsd->state;
2010 GtkFileSelection *selector = fsd->widget;
2011 GtkWidget *top = s->toplevel_widget;
2012 saver_preferences *p = &s->prefs;
2013 const char *path = gtk_file_selection_get_filename (selector);
2015 if (p->image_directory && !strcmp(p->image_directory, path))
2016 return; /* no change */
2018 if (!directory_p (path))
2021 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2022 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2026 if (p->image_directory) free (p->image_directory);
2027 p->image_directory = normalize_directory (path);
2029 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2030 (p->image_directory ? p->image_directory : ""));
2031 demo_write_init_file (s, p);
2036 store_text_file (GtkWidget *button, gpointer user_data)
2038 file_selection_data *fsd = (file_selection_data *) user_data;
2039 state *s = fsd->state;
2040 GtkFileSelection *selector = fsd->widget;
2041 GtkWidget *top = s->toplevel_widget;
2042 saver_preferences *p = &s->prefs;
2043 const char *path = gtk_file_selection_get_filename (selector);
2045 if (p->text_file && !strcmp(p->text_file, path))
2046 return; /* no change */
2051 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2052 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2056 if (p->text_file) free (p->text_file);
2057 p->text_file = normalize_directory (path);
2059 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2060 (p->text_file ? p->text_file : ""));
2061 demo_write_init_file (s, p);
2066 store_text_program (GtkWidget *button, gpointer user_data)
2068 file_selection_data *fsd = (file_selection_data *) user_data;
2069 state *s = fsd->state;
2070 GtkFileSelection *selector = fsd->widget;
2071 /*GtkWidget *top = s->toplevel_widget;*/
2072 saver_preferences *p = &s->prefs;
2073 const char *path = gtk_file_selection_get_filename (selector);
2075 if (p->text_program && !strcmp(p->text_program, path))
2076 return; /* no change */
2082 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2083 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2088 if (p->text_program) free (p->text_program);
2089 p->text_program = normalize_directory (path);
2091 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2092 (p->text_program ? p->text_program : ""));
2093 demo_write_init_file (s, p);
2099 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2101 file_selection_data *fsd = (file_selection_data *) user_data;
2102 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2106 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2108 browse_image_dir_cancel (button, user_data);
2109 store_image_directory (button, user_data);
2113 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2115 browse_image_dir_cancel (button, user_data);
2116 store_text_file (button, user_data);
2120 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2122 browse_image_dir_cancel (button, user_data);
2123 store_text_program (button, user_data);
2127 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2129 browse_image_dir_cancel (widget, user_data);
2133 G_MODULE_EXPORT void
2134 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2136 state *s = global_state_kludge; /* I hate C so much... */
2137 saver_preferences *p = &s->prefs;
2138 static file_selection_data *fsd = 0;
2140 GtkFileSelection *selector = GTK_FILE_SELECTION(
2141 gtk_file_selection_new ("Please select the image directory."));
2144 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2146 fsd->widget = selector;
2149 if (p->image_directory && *p->image_directory)
2150 gtk_file_selection_set_filename (selector, p->image_directory);
2152 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2153 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2155 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2156 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2158 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2159 GTK_SIGNAL_FUNC (browse_image_dir_close),
2162 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2164 gtk_window_set_modal (GTK_WINDOW (selector), True);
2165 gtk_widget_show (GTK_WIDGET (selector));
2169 G_MODULE_EXPORT void
2170 browse_text_file_cb (GtkButton *button, gpointer user_data)
2172 state *s = global_state_kludge; /* I hate C so much... */
2173 saver_preferences *p = &s->prefs;
2174 static file_selection_data *fsd = 0;
2176 GtkFileSelection *selector = GTK_FILE_SELECTION(
2177 gtk_file_selection_new ("Please select a text file."));
2180 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2182 fsd->widget = selector;
2185 if (p->text_file && *p->text_file)
2186 gtk_file_selection_set_filename (selector, p->text_file);
2188 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2189 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2191 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2192 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2194 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2195 GTK_SIGNAL_FUNC (browse_image_dir_close),
2198 gtk_window_set_modal (GTK_WINDOW (selector), True);
2199 gtk_widget_show (GTK_WIDGET (selector));
2203 G_MODULE_EXPORT void
2204 browse_text_program_cb (GtkButton *button, gpointer user_data)
2206 state *s = global_state_kludge; /* I hate C so much... */
2207 saver_preferences *p = &s->prefs;
2208 static file_selection_data *fsd = 0;
2210 GtkFileSelection *selector = GTK_FILE_SELECTION(
2211 gtk_file_selection_new ("Please select a text-generating program."));
2214 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2216 fsd->widget = selector;
2219 if (p->text_program && *p->text_program)
2220 gtk_file_selection_set_filename (selector, p->text_program);
2222 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2223 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2225 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2226 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2228 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2229 GTK_SIGNAL_FUNC (browse_image_dir_close),
2232 gtk_window_set_modal (GTK_WINDOW (selector), True);
2233 gtk_widget_show (GTK_WIDGET (selector));
2240 G_MODULE_EXPORT void
2241 settings_cb (GtkButton *button, gpointer user_data)
2243 state *s = global_state_kludge; /* I hate C so much... */
2244 int list_elt = selected_list_element (s);
2246 populate_demo_window (s, list_elt); /* reset the widget */
2247 populate_popup_window (s); /* create UI on popup window */
2248 gtk_widget_show (s->popup_widget);
2252 settings_sync_cmd_text (state *s)
2255 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2256 char *cmd_line = get_configurator_command_line (s->cdata);
2257 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2258 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2260 # endif /* HAVE_XML */
2263 G_MODULE_EXPORT void
2264 settings_adv_cb (GtkButton *button, gpointer user_data)
2266 state *s = global_state_kludge; /* I hate C so much... */
2267 GtkNotebook *notebook =
2268 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2270 settings_sync_cmd_text (s);
2271 gtk_notebook_set_page (notebook, 1);
2274 G_MODULE_EXPORT void
2275 settings_std_cb (GtkButton *button, gpointer user_data)
2277 state *s = global_state_kludge; /* I hate C so much... */
2278 GtkNotebook *notebook =
2279 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2281 /* Re-create UI to reflect the in-progress command-line settings. */
2282 populate_popup_window (s);
2284 gtk_notebook_set_page (notebook, 0);
2287 G_MODULE_EXPORT void
2288 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2289 gint page_num, gpointer user_data)
2291 state *s = global_state_kludge; /* I hate C so much... */
2292 GtkWidget *adv = name_to_widget (s, "adv_button");
2293 GtkWidget *std = name_to_widget (s, "std_button");
2297 gtk_widget_show (adv);
2298 gtk_widget_hide (std);
2300 else if (page_num == 1)
2302 gtk_widget_hide (adv);
2303 gtk_widget_show (std);
2311 G_MODULE_EXPORT void
2312 settings_cancel_cb (GtkButton *button, gpointer user_data)
2314 state *s = global_state_kludge; /* I hate C so much... */
2315 gtk_widget_hide (s->popup_widget);
2318 G_MODULE_EXPORT void
2319 settings_ok_cb (GtkButton *button, gpointer user_data)
2321 state *s = global_state_kludge; /* I hate C so much... */
2322 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2323 int page = gtk_notebook_get_current_page (notebook);
2326 /* Regenerate the command-line from the widget contents before saving.
2327 But don't do this if we're looking at the command-line page already,
2328 or we will blow away what they typed... */
2329 settings_sync_cmd_text (s);
2331 flush_popup_changes_and_save (s);
2332 gtk_widget_hide (s->popup_widget);
2336 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2338 state *s = (state *) data;
2339 settings_cancel_cb (0, (gpointer) s);
2345 /* Populating the various widgets
2349 /* Returns the number of the last hack run by the server.
2352 server_current_hack (void)
2356 unsigned long nitems, bytesafter;
2357 unsigned char *dataP = 0;
2358 Display *dpy = GDK_DISPLAY();
2359 int hack_number = -1;
2361 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2362 XA_SCREENSAVER_STATUS,
2363 0, 3, False, XA_INTEGER,
2364 &type, &format, &nitems, &bytesafter,
2367 && type == XA_INTEGER
2371 PROP32 *data = (PROP32 *) dataP;
2372 hack_number = (int) data[2] - 1;
2375 if (dataP) XFree (dataP);
2381 /* Finds the number of the last hack that was run, and makes that item be
2382 selected by default.
2385 scroll_to_current_hack (state *s)
2387 saver_preferences *p = &s->prefs;
2388 int hack_number = -1;
2390 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2391 hack_number = p->selected_hack;
2392 if (hack_number < 0) /* otherwise, use the last-run */
2393 hack_number = server_current_hack ();
2394 if (hack_number < 0) /* failing that, last "one mode" */
2395 hack_number = p->selected_hack;
2396 if (hack_number < 0) /* failing that, newest hack. */
2398 /* We should only get here if the user does not have a .xscreensaver
2399 file, and the screen has not been blanked with a hack since X
2400 started up: in other words, this is probably a fresh install.
2402 Instead of just defaulting to hack #0 (in either "programs" or
2403 "alphabetical" order) let's try to default to the last runnable
2404 hack in the "programs" list: this is probably the hack that was
2405 most recently added to the xscreensaver distribution (and so
2406 it's probably the currently-coolest one!)
2408 hack_number = p->screenhacks_count-1;
2409 while (hack_number > 0 &&
2410 ! (s->hacks_available_p[hack_number] &&
2411 p->screenhacks[hack_number]->enabled_p))
2415 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2417 int list_elt = s->hack_number_to_list_elt[hack_number];
2418 GtkWidget *list = name_to_widget (s, "list");
2419 force_list_select_item (s, list, list_elt, True);
2420 populate_demo_window (s, list_elt);
2426 populate_hack_list (state *s)
2428 Display *dpy = GDK_DISPLAY();
2430 saver_preferences *p = &s->prefs;
2431 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2432 GtkListStore *model;
2433 GtkTreeSelection *selection;
2434 GtkCellRenderer *ren;
2438 g_object_get (G_OBJECT (list),
2443 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2444 g_object_set (G_OBJECT (list), "model", model, NULL);
2445 g_object_unref (model);
2447 ren = gtk_cell_renderer_toggle_new ();
2448 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2450 "active", COL_ENABLED,
2453 g_signal_connect (ren, "toggled",
2454 G_CALLBACK (list_checkbox_cb),
2457 ren = gtk_cell_renderer_text_new ();
2458 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2459 _("Screen Saver"), ren,
2463 g_signal_connect_after (list, "row_activated",
2464 G_CALLBACK (list_activated_cb),
2467 selection = gtk_tree_view_get_selection (list);
2468 g_signal_connect (selection, "changed",
2469 G_CALLBACK (list_select_changed_cb),
2474 for (i = 0; i < s->list_count; i++)
2476 int hack_number = s->list_elt_to_hack_number[i];
2477 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2479 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2481 if (!hack) continue;
2483 /* If we're to suppress uninstalled hacks, check $PATH now. */
2484 if (p->ignore_uninstalled_p && !available_p)
2487 pretty_name = (hack->name
2488 ? strdup (hack->name)
2489 : make_hack_name (dpy, hack->command));
2493 /* Make the text foreground be the color of insensitive widgets
2494 (but don't actually make it be insensitive, since we still
2495 want to be able to click on it.)
2497 GtkStyle *style = GTK_WIDGET (list)->style;
2498 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2499 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2500 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2502 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2503 /* " background=\"#%02X%02X%02X\"" */
2505 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2506 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2512 gtk_list_store_append (model, &iter);
2513 gtk_list_store_set (model, &iter,
2514 COL_ENABLED, hack->enabled_p,
2515 COL_NAME, pretty_name,
2520 #else /* !HAVE_GTK2 */
2522 saver_preferences *p = &s->prefs;
2523 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2525 for (i = 0; i < s->list_count; i++)
2527 int hack_number = s->list_elt_to_hack_number[i];
2528 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2530 /* A GtkList must contain only GtkListItems, but those can contain
2531 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2532 and a Label. We handle single and double click events on the
2533 line itself, for clicking on the text, but the interior checkbox
2534 also handles its own events.
2537 GtkWidget *line_hbox;
2538 GtkWidget *line_check;
2539 GtkWidget *line_label;
2541 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2543 if (!hack) continue;
2545 /* If we're to suppress uninstalled hacks, check $PATH now. */
2546 if (p->ignore_uninstalled_p && !available_p)
2549 pretty_name = (hack->name
2550 ? strdup (hack->name)
2551 : make_hack_name (hack->command));
2553 line = gtk_list_item_new ();
2554 line_hbox = gtk_hbox_new (FALSE, 0);
2555 line_check = gtk_check_button_new ();
2556 line_label = gtk_label_new (pretty_name);
2558 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2559 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2560 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2562 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2564 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2566 gtk_widget_show (line_check);
2567 gtk_widget_show (line_label);
2568 gtk_widget_show (line_hbox);
2569 gtk_widget_show (line);
2573 gtk_container_add (GTK_CONTAINER (list), line);
2574 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2575 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2578 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2579 GTK_SIGNAL_FUNC (list_checkbox_cb),
2582 gtk_widget_show (line);
2586 /* Make the widget be colored like insensitive widgets
2587 (but don't actually make it be insensitive, since we
2588 still want to be able to click on it.)
2590 GtkRcStyle *rc_style;
2593 gtk_widget_realize (GTK_WIDGET (line_label));
2595 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2596 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2598 rc_style = gtk_rc_style_new ();
2599 rc_style->fg[GTK_STATE_NORMAL] = fg;
2600 rc_style->bg[GTK_STATE_NORMAL] = bg;
2601 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2603 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2604 gtk_rc_style_unref (rc_style);
2608 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2609 GTK_SIGNAL_FUNC (list_select_cb),
2611 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2612 GTK_SIGNAL_FUNC (list_unselect_cb),
2614 #endif /* !HAVE_GTK2 */
2618 update_list_sensitivity (state *s)
2620 saver_preferences *p = &s->prefs;
2621 Bool sensitive = (p->mode == RANDOM_HACKS ||
2622 p->mode == RANDOM_HACKS_SAME ||
2623 p->mode == ONE_HACK);
2624 Bool checkable = (p->mode == RANDOM_HACKS ||
2625 p->mode == RANDOM_HACKS_SAME);
2626 Bool blankable = (p->mode != DONT_BLANK);
2629 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2630 GtkWidget *use = name_to_widget (s, "use_col_frame");
2631 #endif /* HAVE_GTK2 */
2632 GtkWidget *scroller = name_to_widget (s, "scroller");
2633 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2634 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2637 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2638 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2639 #else /* !HAVE_GTK2 */
2640 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2641 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2643 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2644 #endif /* !HAVE_GTK2 */
2645 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2646 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2648 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2651 gtk_tree_view_column_set_visible (use, checkable);
2652 #else /* !HAVE_GTK2 */
2654 gtk_widget_show (use); /* the "Use" column header */
2656 gtk_widget_hide (use);
2660 GtkBin *line = GTK_BIN (kids->data);
2661 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2662 GtkWidget *line_check =
2663 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2666 gtk_widget_show (line_check);
2668 gtk_widget_hide (line_check);
2672 #endif /* !HAVE_GTK2 */
2677 populate_prefs_page (state *s)
2679 saver_preferences *p = &s->prefs;
2681 Bool can_lock_p = True;
2683 /* Disable all the "lock" controls if locking support was not provided
2684 at compile-time, or if running on MacOS. */
2685 # if defined(NO_LOCKING) || defined(__APPLE__)
2690 /* If there is only one screen, the mode menu contains
2691 "random" but not "random-same".
2693 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2694 p->mode = RANDOM_HACKS;
2697 /* The file supports timeouts of less than a minute, but the GUI does
2698 not, so throttle the values to be at least one minute (since "0" is
2699 a bad rounding choice...)
2701 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2704 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2707 # define FMT_MINUTES(NAME,N) \
2708 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2710 # define FMT_SECONDS(NAME,N) \
2711 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2713 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2714 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2715 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2716 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2717 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2718 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2719 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2724 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2725 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2728 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2730 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2731 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2732 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2734 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2735 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2736 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2737 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2738 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2739 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2740 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2744 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2745 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2746 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2747 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2748 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2751 # undef TOGGLE_ACTIVE
2753 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2754 (p->image_directory ? p->image_directory : ""));
2755 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2757 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2760 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2761 (p->text_literal ? p->text_literal : ""));
2762 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2763 (p->text_file ? p->text_file : ""));
2764 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2765 (p->text_program ? p->text_program : ""));
2766 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2767 (p->text_url ? p->text_url : ""));
2769 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2770 p->tmode == TEXT_LITERAL);
2771 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2772 p->tmode == TEXT_FILE);
2773 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2774 p->tmode == TEXT_FILE);
2775 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2776 p->tmode == TEXT_PROGRAM);
2777 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2778 p->tmode == TEXT_PROGRAM);
2779 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2780 p->tmode == TEXT_URL);
2783 /* Map the `saver_mode' enum to mode menu to values. */
2785 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2788 for (i = 0; i < countof(mode_menu_order); i++)
2789 if (mode_menu_order[i] == p->mode)
2791 gtk_option_menu_set_history (opt, i);
2792 update_list_sensitivity (s);
2796 Bool found_any_writable_cells = False;
2797 Bool fading_possible = False;
2798 Bool dpms_supported = False;
2800 Display *dpy = GDK_DISPLAY();
2801 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2803 for (i = 0; i < nscreens; i++)
2805 Screen *s = ScreenOfDisplay (dpy, i);
2806 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2808 found_any_writable_cells = True;
2813 fading_possible = found_any_writable_cells;
2814 #ifdef HAVE_XF86VMODE_GAMMA
2815 fading_possible = True;
2818 #ifdef HAVE_DPMS_EXTENSION
2820 int op = 0, event = 0, error = 0;
2821 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2822 dpms_supported = True;
2824 #endif /* HAVE_DPMS_EXTENSION */
2827 # define SENSITIZE(NAME,SENSITIVEP) \
2828 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2830 /* Blanking and Locking
2832 SENSITIZE ("lock_button", can_lock_p);
2833 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2834 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2838 SENSITIZE ("dpms_frame", dpms_supported);
2839 SENSITIZE ("dpms_button", dpms_supported);
2840 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2841 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2842 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2843 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2844 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2845 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2846 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2847 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2848 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2852 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2853 SENSITIZE ("install_button", found_any_writable_cells);
2854 SENSITIZE ("fade_button", fading_possible);
2855 SENSITIZE ("unfade_button", fading_possible);
2857 SENSITIZE ("fade_label", (fading_possible &&
2858 (p->fade_p || p->unfade_p)));
2859 SENSITIZE ("fade_spinbutton", (fading_possible &&
2860 (p->fade_p || p->unfade_p)));
2868 populate_popup_window (state *s)
2870 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2871 char *doc_string = 0;
2873 /* #### not in Gtk 1.2
2874 gtk_label_set_selectable (doc);
2880 free_conf_data (s->cdata);
2885 saver_preferences *p = &s->prefs;
2886 int list_elt = selected_list_element (s);
2887 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2888 ? s->list_elt_to_hack_number[list_elt]
2890 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2893 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2894 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2895 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2896 s->cdata = load_configurator (cmd_line, s->debug_p);
2897 if (s->cdata && s->cdata->widget)
2898 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2903 doc_string = (s->cdata
2904 ? s->cdata->description
2906 # else /* !HAVE_XML */
2907 doc_string = _("Descriptions not available: no XML support compiled in.");
2908 # endif /* !HAVE_XML */
2910 gtk_label_set_text (doc, (doc_string
2912 : _("No description available.")));
2917 sensitize_demo_widgets (state *s, Bool sensitive_p)
2919 const char *names[] = { "demo", "settings",
2920 "cmd_label", "cmd_text", "manual",
2921 "visual", "visual_combo" };
2923 for (i = 0; i < countof(names); i++)
2925 GtkWidget *w = name_to_widget (s, names[i]);
2926 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2932 sensitize_menu_items (state *s, Bool force_p)
2934 static Bool running_p = False;
2935 static time_t last_checked = 0;
2936 time_t now = time ((time_t *) 0);
2937 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
2941 if (force_p || now > last_checked + 10) /* check every 10 seconds */
2943 running_p = xscreensaver_running_p (s);
2944 last_checked = time ((time_t *) 0);
2947 for (i = 0; i < countof(names); i++)
2949 GtkWidget *w = name_to_widget (s, names[i]);
2950 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
2955 /* When the File menu is de-posted after a "Restart Daemon" command,
2956 the window underneath doesn't repaint for some reason. I guess this
2957 is a bug in exposure handling in GTK or GDK. This works around it.
2960 force_dialog_repaint (state *s)
2963 /* Tell GDK to invalidate and repaint the whole window.
2965 GdkWindow *w = s->toplevel_widget->window;
2966 GdkRegion *region = gdk_region_new ();
2968 rect.x = rect.y = 0;
2969 rect.width = rect.height = 32767;
2970 gdk_region_union_with_rect (region, &rect);
2971 gdk_window_invalidate_region (w, region, True);
2972 gdk_region_destroy (region);
2973 gdk_window_process_updates (w, True);
2975 /* Force the server to send an exposure event by creating and then
2976 destroying a window as a child of the top level shell.
2978 Display *dpy = GDK_DISPLAY();
2979 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
2981 XWindowAttributes xgwa;
2982 XGetWindowAttributes (dpy, parent, &xgwa);
2983 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
2984 XMapRaised (dpy, w);
2985 XDestroyWindow (dpy, w);
2991 /* Even though we've given these text fields a maximum number of characters,
2992 their default size is still about 30 characters wide -- so measure out
2993 a string in their font, and resize them to just fit that.
2996 fix_text_entry_sizes (state *s)
3000 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
3001 const char * const spinbuttons[] = {
3002 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3003 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3004 "dpms_off_spinbutton",
3005 "-fade_spinbutton" };
3009 for (i = 0; i < countof(spinbuttons); i++)
3011 const char *n = spinbuttons[i];
3013 while (*n == '-') n++, cols--;
3014 w = GTK_WIDGET (name_to_widget (s, n));
3015 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3016 gtk_widget_set_usize (w, width, -2);
3019 /* Now fix the width of the combo box.
3021 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3022 w = GTK_COMBO (w)->entry;
3023 width = gdk_string_width (w->style->font, "PseudoColor___");
3024 gtk_widget_set_usize (w, width, -2);
3026 /* Now fix the width of the file entry text.
3028 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3029 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3030 gtk_widget_set_usize (w, width, -2);
3032 /* Now fix the width of the command line text.
3034 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3035 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3036 gtk_widget_set_usize (w, width, -2);
3040 /* Now fix the height of the list widget:
3041 make it default to being around 10 text-lines high instead of 4.
3043 w = GTK_WIDGET (name_to_widget (s, "list"));
3047 int leading = 3; /* approximate is ok... */
3051 PangoFontMetrics *pain =
3052 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3053 w->style->font_desc,
3054 gtk_get_default_language ());
3055 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3056 pango_font_metrics_get_descent (pain));
3057 #else /* !HAVE_GTK2 */
3058 height = w->style->font->ascent + w->style->font->descent;
3059 #endif /* !HAVE_GTK2 */
3063 height += border * 2;
3064 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3065 gtk_widget_set_usize (w, -2, height);
3072 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3075 static char *up_arrow_xpm[] = {
3098 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3099 the end of the array (Gtk 1.2.5.) */
3100 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3101 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3104 static char *down_arrow_xpm[] = {
3127 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3128 the end of the array (Gtk 1.2.5.) */
3129 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3130 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3134 pixmapify_button (state *s, int down_p)
3138 GtkWidget *pixmapwid;
3142 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3143 style = gtk_widget_get_style (w);
3145 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3146 &style->bg[GTK_STATE_NORMAL],
3148 ? (gchar **) down_arrow_xpm
3149 : (gchar **) up_arrow_xpm));
3150 pixmapwid = gtk_pixmap_new (pixmap, mask);
3151 gtk_widget_show (pixmapwid);
3152 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3153 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3157 map_next_button_cb (GtkWidget *w, gpointer user_data)
3159 state *s = (state *) user_data;
3160 pixmapify_button (s, 1);
3164 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3166 state *s = (state *) user_data;
3167 pixmapify_button (s, 0);
3169 #endif /* !HAVE_GTK2 */
3173 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3177 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3178 GtkAllocation *allocation,
3182 GtkWidgetAuxInfo *aux_info;
3184 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3186 aux_info->width = allocation->width;
3187 aux_info->height = -2;
3191 gtk_widget_size_request (label, &req);
3194 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3197 eschew_gtk_lossage (GtkLabel *label)
3199 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3200 aux_info->width = GTK_WIDGET (label)->allocation.width;
3201 aux_info->height = -2;
3205 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3207 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3208 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3211 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3213 gtk_widget_queue_resize (GTK_WIDGET (label));
3215 #endif /* !HAVE_GTK2 */
3219 populate_demo_window (state *s, int list_elt)
3221 Display *dpy = GDK_DISPLAY();
3222 saver_preferences *p = &s->prefs;
3225 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3226 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
3227 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3228 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3229 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3231 if (p->mode == BLANK_ONLY)
3234 pretty_name = strdup (_("Blank Screen"));
3235 schedule_preview (s, 0);
3237 else if (p->mode == DONT_BLANK)
3240 pretty_name = strdup (_("Screen Saver Disabled"));
3241 schedule_preview (s, 0);
3245 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3246 ? s->list_elt_to_hack_number[list_elt]
3248 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3252 ? strdup (hack->name)
3253 : make_hack_name (dpy, hack->command))
3257 schedule_preview (s, hack->command);
3259 schedule_preview (s, 0);
3263 pretty_name = strdup (_("Preview"));
3265 gtk_frame_set_label (frame1, _(pretty_name));
3266 gtk_frame_set_label (frame2, _(pretty_name));
3268 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3269 gtk_entry_set_position (cmd, 0);
3273 sprintf (title, _("%s: %.100s Settings"),
3274 progclass, (pretty_name ? pretty_name : "???"));
3275 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3278 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3280 ? (hack->visual && *hack->visual
3285 sensitize_demo_widgets (s, (hack ? True : False));
3287 if (pretty_name) free (pretty_name);
3289 ensure_selected_item_visible (list);
3291 s->_selected_list_element = list_elt;
3296 widget_deleter (GtkWidget *widget, gpointer data)
3298 /* #### Well, I want to destroy these widgets, but if I do that, they get
3299 referenced again, and eventually I get a SEGV. So instead of
3300 destroying them, I'll just hide them, and leak a bunch of memory
3301 every time the disk file changes. Go go go Gtk!
3303 #### Ok, that's a lie, I get a crash even if I just hide the widget
3304 and don't ever delete it. Fuck!
3307 gtk_widget_destroy (widget);
3309 gtk_widget_hide (widget);
3314 static char **sort_hack_cmp_names_kludge;
3316 sort_hack_cmp (const void *a, const void *b)
3322 int aa = *(int *) a;
3323 int bb = *(int *) b;
3324 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3325 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3326 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3332 initialize_sort_map (state *s)
3334 Display *dpy = GDK_DISPLAY();
3335 saver_preferences *p = &s->prefs;
3338 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3339 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3340 if (s->hacks_available_p) free (s->hacks_available_p);
3342 s->list_elt_to_hack_number = (int *)
3343 calloc (sizeof(int), p->screenhacks_count + 1);
3344 s->hack_number_to_list_elt = (int *)
3345 calloc (sizeof(int), p->screenhacks_count + 1);
3346 s->hacks_available_p = (Bool *)
3347 calloc (sizeof(Bool), p->screenhacks_count + 1);
3348 s->total_available = 0;
3350 /* Check which hacks actually exist on $PATH
3352 for (i = 0; i < p->screenhacks_count; i++)
3354 screenhack *hack = p->screenhacks[i];
3355 int on = on_path_p (hack->command) ? 1 : 0;
3356 s->hacks_available_p[i] = on;
3357 s->total_available += on;
3360 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3364 for (i = 0; i < p->screenhacks_count; i++)
3366 if (!p->ignore_uninstalled_p ||
3367 s->hacks_available_p[i])
3368 s->list_elt_to_hack_number[j++] = i;
3372 for (; j < p->screenhacks_count; j++)
3373 s->list_elt_to_hack_number[j] = -1;
3376 /* Generate list of sortable names (once)
3378 sort_hack_cmp_names_kludge = (char **)
3379 calloc (sizeof(char *), p->screenhacks_count);
3380 for (i = 0; i < p->screenhacks_count; i++)
3382 screenhack *hack = p->screenhacks[i];
3383 char *name = (hack->name && *hack->name
3384 ? strdup (hack->name)
3385 : make_hack_name (dpy, hack->command));
3387 for (str = name; *str; str++)
3388 *str = tolower(*str);
3389 sort_hack_cmp_names_kludge[i] = name;
3392 /* Sort list->hack map alphabetically
3394 qsort (s->list_elt_to_hack_number,
3395 p->screenhacks_count,
3396 sizeof(*s->list_elt_to_hack_number),
3401 for (i = 0; i < p->screenhacks_count; i++)
3402 free (sort_hack_cmp_names_kludge[i]);
3403 free (sort_hack_cmp_names_kludge);
3404 sort_hack_cmp_names_kludge = 0;
3406 /* Build inverse table */
3407 for (i = 0; i < p->screenhacks_count; i++)
3409 int n = s->list_elt_to_hack_number[i];
3411 s->hack_number_to_list_elt[n] = i;
3417 maybe_reload_init_file (state *s)
3419 Display *dpy = GDK_DISPLAY();
3420 saver_preferences *p = &s->prefs;
3423 static Bool reentrant_lock = False;
3424 if (reentrant_lock) return 0;
3425 reentrant_lock = True;
3427 if (init_file_changed_p (p))
3429 const char *f = init_file_name();
3434 if (!f || !*f) return 0;
3435 b = (char *) malloc (strlen(f) + 1024);
3438 "file \"%s\" has changed, reloading.\n"),
3440 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3443 load_init_file (dpy, p);
3444 initialize_sort_map (s);
3446 list_elt = selected_list_element (s);
3447 list = name_to_widget (s, "list");
3448 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3449 populate_hack_list (s);
3450 force_list_select_item (s, list, list_elt, True);
3451 populate_prefs_page (s);
3452 populate_demo_window (s, list_elt);
3453 ensure_selected_item_visible (list);
3458 reentrant_lock = False;
3464 /* Making the preview window have the right X visual (so that GL works.)
3467 static Visual *get_best_gl_visual (state *);
3470 x_visual_to_gdk_visual (Visual *xv)
3472 GList *gvs = gdk_list_visuals();
3473 if (!xv) return gdk_visual_get_system();
3474 for (; gvs; gvs = gvs->next)
3476 GdkVisual *gv = (GdkVisual *) gvs->data;
3477 if (xv == GDK_VISUAL_XVISUAL (gv))
3480 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3481 blurb(), (unsigned long) xv->visualid);
3486 clear_preview_window (state *s)
3491 if (!s->toplevel_widget) return; /* very early */
3492 p = name_to_widget (s, "preview");
3495 if (!window) return;
3497 /* Flush the widget background down into the window, in case a subproc
3499 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3500 gdk_window_clear (window);
3503 int list_elt = selected_list_element (s);
3504 int hack_number = (list_elt >= 0
3505 ? s->list_elt_to_hack_number[list_elt]
3507 Bool available_p = (hack_number >= 0
3508 ? s->hacks_available_p [hack_number]
3510 Bool nothing_p = (s->total_available < 5);
3513 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3514 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3515 (s->running_preview_error_p
3516 ? (available_p ? 1 :
3519 #else /* !HAVE_GTK2 */
3520 if (s->running_preview_error_p)
3522 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3523 const char * const lines2[] = { N_("Not"), N_("Installed") };
3524 int nlines = countof(lines1);
3525 int lh = p->style->font->ascent + p->style->font->descent;
3529 const char * const *lines = (available_p ? lines1 : lines2);
3531 gdk_window_get_size (window, &w, &h);
3532 y = (h - (lh * nlines)) / 2;
3533 y += p->style->font->ascent;
3534 for (i = 0; i < nlines; i++)
3536 int sw = gdk_string_width (p->style->font, _(lines[i]));
3537 int x = (w - sw) / 2;
3538 gdk_draw_string (window, p->style->font,
3539 p->style->fg_gc[GTK_STATE_NORMAL],
3544 #endif /* !HAVE_GTK2 */
3552 reset_preview_window (state *s)
3554 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3555 when you kill one and re-start another on the same window. So maybe
3556 it's best to just always destroy and recreate the preview window
3557 when changing hacks, instead of always trying to reuse the same one?
3559 GtkWidget *pr = name_to_widget (s, "preview");
3560 if (GTK_WIDGET_REALIZED (pr))
3562 Window oid = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3564 gtk_widget_hide (pr);
3565 gtk_widget_unrealize (pr);
3566 gtk_widget_realize (pr);
3567 gtk_widget_show (pr);
3568 id = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3570 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3578 fix_preview_visual (state *s)
3580 GtkWidget *widget = name_to_widget (s, "preview");
3581 Visual *xvisual = get_best_gl_visual (s);
3582 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3583 GdkVisual *dvisual = gdk_visual_get_system();
3584 GdkColormap *cmap = (visual == dvisual
3585 ? gdk_colormap_get_system ()
3586 : gdk_colormap_new (visual, False));
3589 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3590 (visual == dvisual ? "default" : "non-default"),
3591 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3593 if (!GTK_WIDGET_REALIZED (widget) ||
3594 gtk_widget_get_visual (widget) != visual)
3596 gtk_widget_unrealize (widget);
3597 gtk_widget_set_visual (widget, visual);
3598 gtk_widget_set_colormap (widget, cmap);
3599 gtk_widget_realize (widget);
3602 /* Set the Widget colors to be white-on-black. */
3604 GdkWindow *window = widget->window;
3605 GtkStyle *style = gtk_style_copy (widget->style);
3606 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3607 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3608 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3609 GdkGC *fgc = gdk_gc_new(window);
3610 GdkGC *bgc = gdk_gc_new(window);
3611 if (!gdk_color_white (cmap, fg)) abort();
3612 if (!gdk_color_black (cmap, bg)) abort();
3613 gdk_gc_set_foreground (fgc, fg);
3614 gdk_gc_set_background (fgc, bg);
3615 gdk_gc_set_foreground (bgc, bg);
3616 gdk_gc_set_background (bgc, fg);
3617 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3618 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3619 gtk_widget_set_style (widget, style);
3621 /* For debugging purposes, put a title on the window (so that
3622 it can be easily found in the output of "xwininfo -tree".)
3624 gdk_window_set_title (window, "Preview");
3627 gtk_widget_show (widget);
3635 subproc_pretty_name (state *s)
3637 if (s->running_preview_cmd)
3639 char *ps = strdup (s->running_preview_cmd);
3640 char *ss = strchr (ps, ' ');
3642 ss = strrchr (ps, '/');
3653 return strdup ("???");
3658 reap_zombies (state *s)
3660 int wait_status = 0;
3662 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3666 if (pid == s->running_preview_pid)
3668 char *ss = subproc_pretty_name (s);
3669 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3670 (unsigned long) pid, ss);
3674 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3675 (unsigned long) pid);
3681 /* Mostly lifted from driver/subprocs.c */
3683 get_best_gl_visual (state *s)
3685 Display *dpy = GDK_DISPLAY();
3694 av[ac++] = "xscreensaver-gl-helper";
3699 perror ("error creating pipe:");
3706 switch ((int) (forked = fork ()))
3710 sprintf (buf, "%s: couldn't fork", blurb());
3718 close (in); /* don't need this one */
3719 close (ConnectionNumber (dpy)); /* close display fd */
3721 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3723 perror ("could not dup() a new stdout:");
3727 execvp (av[0], av); /* shouldn't return. */
3729 if (errno != ENOENT)
3731 /* Ignore "no such file or directory" errors, unless verbose.
3732 Issue all other exec errors, though. */
3733 sprintf (buf, "%s: running %s", blurb(), av[0]);
3737 /* Note that one must use _exit() instead of exit() in procs forked
3738 off of Gtk programs -- Gtk installs an atexit handler that has a
3739 copy of the X connection (which we've already closed, for safety.)
3740 If one uses exit() instead of _exit(), then one sometimes gets a
3741 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3743 _exit (1); /* exits fork */
3749 int wait_status = 0;
3751 FILE *f = fdopen (in, "r");
3755 close (out); /* don't need this one */
3758 if (!fgets (buf, sizeof(buf)-1, f))
3762 /* Wait for the child to die. */
3763 waitpid (-1, &wait_status, 0);
3765 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3771 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3777 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3779 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3780 blurb(), av[0], result);
3792 kill_preview_subproc (state *s, Bool reset_p)
3794 s->running_preview_error_p = False;
3797 clear_preview_window (s);
3799 if (s->subproc_check_timer_id)
3801 gtk_timeout_remove (s->subproc_check_timer_id);
3802 s->subproc_check_timer_id = 0;
3803 s->subproc_check_countdown = 0;
3806 if (s->running_preview_pid)
3808 int status = kill (s->running_preview_pid, SIGTERM);
3809 char *ss = subproc_pretty_name (s);
3816 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3817 blurb(), (unsigned long) s->running_preview_pid, ss);
3822 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3823 blurb(), (unsigned long) s->running_preview_pid, ss);
3829 waitpid(s->running_preview_pid, &endstatus, 0);
3831 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3832 (unsigned long) s->running_preview_pid, ss);
3836 s->running_preview_pid = 0;
3837 if (s->running_preview_cmd) free (s->running_preview_cmd);
3838 s->running_preview_cmd = 0;
3845 reset_preview_window (s);
3846 clear_preview_window (s);
3851 /* Immediately and unconditionally launches the given process,
3852 after appending the -window-id option; sets running_preview_pid.
3855 launch_preview_subproc (state *s)
3857 saver_preferences *p = &s->prefs;
3861 const char *cmd = s->desired_preview_cmd;
3863 GtkWidget *pr = name_to_widget (s, "preview");
3866 reset_preview_window (s);
3868 window = pr->window;
3870 s->running_preview_error_p = False;
3872 if (s->preview_suppressed_p)
3874 kill_preview_subproc (s, False);
3878 new_cmd = malloc (strlen (cmd) + 40);
3880 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3883 /* No window id? No command to run. */
3889 strcpy (new_cmd, cmd);
3890 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3894 kill_preview_subproc (s, False);
3897 s->running_preview_error_p = True;
3898 clear_preview_window (s);
3902 switch ((int) (forked = fork ()))
3907 sprintf (buf, "%s: couldn't fork", blurb());
3909 s->running_preview_error_p = True;
3915 close (ConnectionNumber (GDK_DISPLAY()));
3917 hack_subproc_environment (id, s->debug_p);
3919 usleep (250000); /* pause for 1/4th second before launching, to give
3920 the previous program time to die and flush its X
3921 buffer, so we don't get leftover turds on the
3924 exec_command (p->shell, new_cmd, p->nice_inferior);
3925 /* Don't bother printing an error message when we are unable to
3926 exec subprocesses; we handle that by polling the pid later.
3928 Note that one must use _exit() instead of exit() in procs forked
3929 off of Gtk programs -- Gtk installs an atexit handler that has a
3930 copy of the X connection (which we've already closed, for safety.)
3931 If one uses exit() instead of _exit(), then one sometimes gets a
3932 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3934 _exit (1); /* exits child fork */
3939 if (s->running_preview_cmd) free (s->running_preview_cmd);
3940 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3941 s->running_preview_pid = forked;
3945 char *ss = subproc_pretty_name (s);
3946 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3947 (unsigned long) forked, ss);
3954 schedule_preview_check (s);
3957 if (new_cmd) free (new_cmd);
3962 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3965 hack_environment (state *s)
3967 static const char *def_path =
3968 # ifdef DEFAULT_PATH_PREFIX
3969 DEFAULT_PATH_PREFIX;
3974 Display *dpy = GDK_DISPLAY();
3975 const char *odpy = DisplayString (dpy);
3976 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3977 strcpy (ndpy, "DISPLAY=");
3978 strcat (ndpy, odpy);
3983 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3985 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3986 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3987 So we must leak it (and/or the previous setting). Yay.
3990 if (def_path && *def_path)
3992 const char *opath = getenv("PATH");
3993 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3994 strcpy (npath, "PATH=");
3995 strcat (npath, def_path);
3996 strcat (npath, ":");
3997 strcat (npath, opath);
4001 /* do not free(npath) -- see above */
4004 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4010 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4012 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4013 necessary yet, but it will make programs work if we had invoked
4014 them with "-root" and not with "-window-id" -- which, of course,
4017 char *nssw = (char *) malloc (40);
4018 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4020 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4021 any more, right? It's not Posix, but everyone seems to have it. */
4026 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4028 /* do not free(nssw) -- see above */
4032 /* Called from a timer:
4033 Launches the currently-chosen subprocess, if it's not already running.
4034 If there's a different process running, kills it.
4037 update_subproc_timer (gpointer data)
4039 state *s = (state *) data;
4040 if (! s->desired_preview_cmd)
4041 kill_preview_subproc (s, True);
4042 else if (!s->running_preview_cmd ||
4043 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4044 launch_preview_subproc (s);
4046 s->subproc_timer_id = 0;
4047 return FALSE; /* do not re-execute timer */
4051 /* Call this when you think you might want a preview process running.
4052 It will set a timer that will actually launch that program a second
4053 from now, if you haven't changed your mind (to avoid double-click
4054 spazzing, etc.) `cmd' may be null meaning "no process".
4057 schedule_preview (state *s, const char *cmd)
4059 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4064 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4066 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4069 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4070 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4072 if (s->subproc_timer_id)
4073 gtk_timeout_remove (s->subproc_timer_id);
4074 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4078 /* Called from a timer:
4079 Checks to see if the subproc that should be running, actually is.
4082 check_subproc_timer (gpointer data)
4084 state *s = (state *) data;
4085 Bool again_p = True;
4087 if (s->running_preview_error_p || /* already dead */
4088 s->running_preview_pid <= 0)
4096 status = kill (s->running_preview_pid, 0);
4097 if (status < 0 && errno == ESRCH)
4098 s->running_preview_error_p = True;
4102 char *ss = subproc_pretty_name (s);
4103 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4104 (unsigned long) s->running_preview_pid, ss,
4105 (s->running_preview_error_p ? "dead" : "alive"));
4109 if (s->running_preview_error_p)
4111 clear_preview_window (s);
4116 /* Otherwise, it's currently alive. We might be checking again, or we
4117 might be satisfied. */
4119 if (--s->subproc_check_countdown <= 0)
4123 return TRUE; /* re-execute timer */
4126 s->subproc_check_timer_id = 0;
4127 s->subproc_check_countdown = 0;
4128 return FALSE; /* do not re-execute timer */
4133 /* Call this just after launching a subprocess.
4134 This sets a timer that will, five times a second for two seconds,
4135 check whether the program is still running. The assumption here
4136 is that if the process didn't stay up for more than a couple of
4137 seconds, then either the program doesn't exist, or it doesn't
4138 take a -window-id argument.
4141 schedule_preview_check (state *s)
4147 fprintf (stderr, "%s: scheduling check\n", blurb());
4149 if (s->subproc_check_timer_id)
4150 gtk_timeout_remove (s->subproc_check_timer_id);
4151 s->subproc_check_timer_id =
4152 gtk_timeout_add (1000 / ticks,
4153 check_subproc_timer, (gpointer) s);
4154 s->subproc_check_countdown = ticks * seconds;
4159 screen_blanked_p (void)
4163 unsigned long nitems, bytesafter;
4164 unsigned char *dataP = 0;
4165 Display *dpy = GDK_DISPLAY();
4166 Bool blanked_p = False;
4168 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4169 XA_SCREENSAVER_STATUS,
4170 0, 3, False, XA_INTEGER,
4171 &type, &format, &nitems, &bytesafter,
4174 && type == XA_INTEGER
4178 Atom *data = (Atom *) dataP;
4179 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4182 if (dataP) XFree (dataP);
4187 /* Wake up every now and then and see if the screen is blanked.
4188 If it is, kill off the small-window demo -- no point in wasting
4189 cycles by running two screensavers at once...
4192 check_blanked_timer (gpointer data)
4194 state *s = (state *) data;
4195 Bool blanked_p = screen_blanked_p ();
4196 if (blanked_p && s->running_preview_pid)
4199 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4200 kill_preview_subproc (s, True);
4203 return True; /* re-execute timer */
4207 /* How many screens are there (including Xinerama.)
4210 screen_count (Display *dpy)
4212 int nscreens = ScreenCount(dpy);
4213 # ifdef HAVE_XINERAMA
4216 int event_number, error_number;
4217 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4218 XineramaIsActive (dpy))
4220 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4221 if (xsi) XFree (xsi);
4224 # endif /* HAVE_XINERAMA */
4230 /* Setting window manager icon
4234 init_icon (GdkWindow *window)
4236 GdkBitmap *mask = 0;
4239 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4240 (gchar **) logo_50_xpm);
4242 gdk_window_set_icon (window, 0, pixmap, mask);
4246 /* The main demo-mode command loop.
4251 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4252 XrmRepresentation *type, XrmValue *value, XPointer closure)
4255 for (i = 0; quarks[i]; i++)
4257 if (bindings[i] == XrmBindTightly)
4258 fprintf (stderr, (i == 0 ? "" : "."));
4259 else if (bindings[i] == XrmBindLoosely)
4260 fprintf (stderr, "*");
4262 fprintf (stderr, " ??? ");
4263 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4266 fprintf (stderr, ": %s\n", (char *) value->addr);
4274 gnome_screensaver_window (Screen *screen)
4276 Display *dpy = DisplayOfScreen (screen);
4277 Window root = RootWindowOfScreen (screen);
4278 Window parent, *kids;
4280 Window gnome_window = 0;
4283 if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4285 for (i = 0; i < nkids; i++)
4289 unsigned long nitems, bytesafter;
4290 unsigned char *name;
4291 if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4292 False, XA_STRING, &type, &format, &nitems,
4296 && !strcmp ((char *) name, "gnome-screensaver"))
4298 gnome_window = kids[i];
4303 if (kids) XFree ((char *) kids);
4304 return gnome_window;
4308 gnome_screensaver_active_p (void)
4310 Display *dpy = GDK_DISPLAY();
4311 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4312 return (w ? True : False);
4316 kill_gnome_screensaver (void)
4318 Display *dpy = GDK_DISPLAY();
4319 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4320 if (w) XKillClient (dpy, (XID) w);
4324 kde_screensaver_active_p (void)
4326 FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4329 fgets (buf, sizeof(buf)-1, p);
4331 if (!strcmp (buf, "true\n"))
4338 kill_kde_screensaver (void)
4340 system ("dcop kdesktop KScreensaverIface enable false");
4345 the_network_is_not_the_computer (state *s)
4347 Display *dpy = GDK_DISPLAY();
4348 char *rversion = 0, *ruser = 0, *rhost = 0;
4349 char *luser, *lhost;
4351 struct passwd *p = getpwuid (getuid ());
4352 const char *d = DisplayString (dpy);
4354 # if defined(HAVE_UNAME)
4356 if (uname (&uts) < 0)
4357 lhost = "<UNKNOWN>";
4359 lhost = uts.nodename;
4361 strcpy (lhost, getenv("SYS$NODE"));
4362 # else /* !HAVE_UNAME && !VMS */
4363 strcat (lhost, "<UNKNOWN>");
4364 # endif /* !HAVE_UNAME && !VMS */
4366 if (p && p->pw_name)
4371 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4373 /* Make a buffer that's big enough for a number of copies of all the
4374 strings, plus some. */
4375 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4376 (ruser ? strlen(ruser) : 0) +
4377 (rhost ? strlen(rhost) : 0) +
4384 if (!rversion || !*rversion)
4388 "The XScreenSaver daemon doesn't seem to be running\n"
4389 "on display \"%s\". Launch it now?"),
4392 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4394 /* Warn that the two processes are running as different users.
4398 "%s is running as user \"%s\" on host \"%s\".\n"
4399 "But the xscreensaver managing display \"%s\"\n"
4400 "is running as user \"%s\" on host \"%s\".\n"
4402 "Since they are different users, they won't be reading/writing\n"
4403 "the same ~/.xscreensaver file, so %s isn't\n"
4404 "going to work right.\n"
4406 "You should either re-run %s as \"%s\", or re-run\n"
4407 "xscreensaver as \"%s\".\n"
4409 "Restart the xscreensaver daemon now?\n"),
4410 progname, luser, lhost,
4412 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4414 progname, (ruser ? ruser : "???"),
4417 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4419 /* Warn that the two processes are running on different hosts.
4423 "%s is running as user \"%s\" on host \"%s\".\n"
4424 "But the xscreensaver managing display \"%s\"\n"
4425 "is running as user \"%s\" on host \"%s\".\n"
4427 "If those two machines don't share a file system (that is,\n"
4428 "if they don't see the same ~%s/.xscreensaver file) then\n"
4429 "%s won't work right.\n"
4431 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4432 progname, luser, lhost,
4434 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4439 else if (!!strcmp (rversion, s->short_version))
4441 /* Warn that the version numbers don't match.
4445 "This is %s version %s.\n"
4446 "But the xscreensaver managing display \"%s\"\n"
4447 "is version %s. This could cause problems.\n"
4449 "Restart the xscreensaver daemon now?\n"),
4450 progname, s->short_version,
4457 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4459 if (rversion) free (rversion);
4460 if (ruser) free (ruser);
4461 if (rhost) free (rhost);
4465 /* Note: since these dialogs are not modal, they will stack up.
4466 So we do this check *after* popping up the "xscreensaver is not
4467 running" dialog so that these are on top. Good enough.
4470 if (gnome_screensaver_active_p ())
4471 warning_dialog (s->toplevel_widget,
4473 "The GNOME screensaver daemon appears to be running.\n"
4474 "It must be stopped for XScreenSaver to work properly.\n"
4476 "Stop the GNOME screen saver daemon now?\n"),
4479 if (kde_screensaver_active_p ())
4480 warning_dialog (s->toplevel_widget,
4482 "The KDE screen saver daemon appears to be running.\n"
4483 "It must be stopped for XScreenSaver to work properly.\n"
4485 "Stop the KDE screen saver daemon now?\n"),
4490 /* We use this error handler so that X errors are preceeded by the name
4491 of the program that generated them.
4494 demo_ehandler (Display *dpy, XErrorEvent *error)
4496 state *s = global_state_kludge; /* I hate C so much... */
4497 fprintf (stderr, "\nX error in %s:\n", blurb());
4498 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4499 kill_preview_subproc (s, False);
4505 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4506 of the program that generated them; and also that we can ignore one
4507 particular bogus error message that Gdk madly spews.
4510 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4511 const gchar *message, gpointer user_data)
4513 /* Ignore the message "Got event for unknown window: 0x...".
4514 Apparently some events are coming in for the xscreensaver window
4515 (presumably reply events related to the ClientMessage) and Gdk
4516 feels the need to complain about them. So, just suppress any
4517 messages that look like that one.
4519 if (strstr (message, "unknown window"))
4522 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4523 (log_domain ? log_domain : progclass),
4524 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4525 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4526 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4527 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4528 log_level == G_LOG_LEVEL_INFO ? "info" :
4529 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4531 ((!*message || message[strlen(message)-1] != '\n')
4537 __extension__ /* shut up about "string length is greater than the length
4538 ISO C89 compilers are required to support" when including
4543 static char *defaults[] = {
4544 #include "XScreenSaver_ad.h"
4549 #ifdef HAVE_CRAPPLET
4550 static struct poptOption crapplet_options[] = {
4551 {NULL, '\0', 0, NULL, 0}
4553 #endif /* HAVE_CRAPPLET */
4556 const char *usage = "[--display dpy] [--prefs]"
4557 # ifdef HAVE_CRAPPLET
4560 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4563 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4565 state *s = (state *) user_data;
4566 Boolean oi = s->initializing_p;
4568 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4570 s->initializing_p = True;
4572 eschew_gtk_lossage (label);
4574 s->initializing_p = oi;
4580 print_widget_tree (GtkWidget *w, int depth)
4583 for (i = 0; i < depth; i++)
4584 fprintf (stderr, " ");
4585 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4587 if (GTK_IS_LIST (w))
4589 for (i = 0; i < depth+1; i++)
4590 fprintf (stderr, " ");
4591 fprintf (stderr, "...list kids...\n");
4593 else if (GTK_IS_CONTAINER (w))
4595 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4598 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4606 delayed_scroll_kludge (gpointer data)
4608 state *s = (state *) data;
4609 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4610 ensure_selected_item_visible (w);
4612 /* Oh, this is just fucking lovely, too. */
4613 w = GTK_WIDGET (name_to_widget (s, "preview"));
4614 gtk_widget_hide (w);
4615 gtk_widget_show (w);
4617 return FALSE; /* do not re-execute timer */
4623 create_xscreensaver_demo (void)
4627 nb = name_to_widget (global_state_kludge, "preview_notebook");
4628 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4630 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4634 create_xscreensaver_settings_dialog (void)
4638 box = name_to_widget (global_state_kludge, "dialog_action_area");
4640 w = name_to_widget (global_state_kludge, "adv_button");
4641 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4643 w = name_to_widget (global_state_kludge, "std_button");
4644 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4646 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4649 #endif /* HAVE_GTK2 */
4652 main (int argc, char **argv)
4656 saver_preferences *p;
4660 Widget toplevel_shell;
4661 char *real_progname = argv[0];
4664 Bool crapplet_p = False;
4668 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4669 textdomain (GETTEXT_PACKAGE);
4672 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4673 # else /* !HAVE_GTK2 */
4674 if (!setlocale (LC_ALL, ""))
4675 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4676 # endif /* !HAVE_GTK2 */
4678 #endif /* ENABLE_NLS */
4680 str = strrchr (real_progname, '/');
4681 if (str) real_progname = str+1;
4684 memset (s, 0, sizeof(*s));
4685 s->initializing_p = True;
4688 global_state_kludge = s; /* I hate C so much... */
4690 progname = real_progname;
4692 s->short_version = (char *) malloc (5);
4693 memcpy (s->short_version, screensaver_id + 17, 4);
4694 s->short_version [4] = 0;
4697 /* Register our error message logger for every ``log domain'' known.
4698 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4699 for all of the domains that seem to be in use.
4702 const char * const domains[] = { 0,
4703 "Gtk", "Gdk", "GLib", "GModule",
4704 "GThread", "Gnome", "GnomeUI" };
4705 for (i = 0; i < countof(domains); i++)
4706 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4709 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4712 const char *dir = DEFAULT_ICONDIR;
4713 if (*dir) add_pixmap_directory (dir);
4715 # endif /* !HAVE_GTK2 */
4716 #endif /* DEFAULT_ICONDIR */
4718 /* This is gross, but Gtk understands --display and not -display...
4720 for (i = 1; i < argc; i++)
4721 if (argv[i][0] && argv[i][1] &&
4722 !strncmp(argv[i], "-display", strlen(argv[i])))
4723 argv[i] = "--display";
4726 /* We need to parse this arg really early... Sigh. */
4727 for (i = 1; i < argc; i++)
4730 (!strcmp(argv[i], "--crapplet") ||
4731 !strcmp(argv[i], "--capplet")))
4733 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4736 for (j = i; j < argc; j++) /* remove it from the list */
4737 argv[j] = argv[j+1];
4739 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4740 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4742 fprintf (stderr, "%s: %s\n", real_progname, usage);
4744 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4747 (!strcmp(argv[i], "--debug") ||
4748 !strcmp(argv[i], "-debug") ||
4749 !strcmp(argv[i], "-d")))
4753 for (j = i; j < argc; j++) /* remove it from the list */
4754 argv[j] = argv[j+1];
4761 (!strcmp(argv[i], "-geometry") ||
4762 !strcmp(argv[i], "-geom") ||
4763 !strcmp(argv[i], "-geo") ||
4764 !strcmp(argv[i], "-g")))
4768 for (j = i; j < argc; j++) /* remove them from the list */
4769 argv[j] = argv[j+2];
4776 (!strcmp(argv[i], "--configdir")))
4780 hack_configuration_path = argv[i+1];
4781 for (j = i; j < argc; j++) /* remove them from the list */
4782 argv[j] = argv[j+2];
4786 if (0 != stat (hack_configuration_path, &st))
4789 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4793 else if (!S_ISDIR (st.st_mode))
4795 fprintf (stderr, "%s: not a directory: %s\n",
4796 blurb(), hack_configuration_path);
4804 fprintf (stderr, "%s: using config directory \"%s\"\n",
4805 progname, hack_configuration_path);
4808 /* Let Gtk open the X connection, then initialize Xt to use that
4809 same connection. Doctor Frankenstein would be proud.
4811 # ifdef HAVE_CRAPPLET
4814 GnomeClient *client;
4815 GnomeClientFlags flags = 0;
4817 int init_results = gnome_capplet_init ("screensaver-properties",
4819 argc, argv, NULL, 0, NULL);
4821 0 upon successful initialization;
4822 1 if --init-session-settings was passed on the cmdline;
4823 2 if --ignore was passed on the cmdline;
4826 So the 1 signifies just to init the settings, and quit, basically.
4827 (Meaning launch the xscreensaver daemon.)
4830 if (init_results < 0)
4833 g_error ("An initialization error occurred while "
4834 "starting xscreensaver-capplet.\n");
4836 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4837 real_progname, init_results);
4842 client = gnome_master_client ();
4845 flags = gnome_client_get_flags (client);
4847 if (flags & GNOME_CLIENT_IS_CONNECTED)
4850 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4851 gnome_client_get_id (client));
4854 char *session_args[20];
4856 session_args[i++] = real_progname;
4857 session_args[i++] = "--capplet";
4858 session_args[i++] = "--init-session-settings";
4859 session_args[i] = 0;
4860 gnome_client_set_priority (client, 20);
4861 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4862 gnome_client_set_restart_command (client, i, session_args);
4866 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4869 gnome_client_flush (client);
4872 if (init_results == 1)
4874 system ("xscreensaver -nosplash &");
4880 # endif /* HAVE_CRAPPLET */
4882 gtk_init (&argc, &argv);
4886 /* We must read exactly the same resources as xscreensaver.
4887 That means we must have both the same progclass *and* progname,
4888 at least as far as the resource database is concerned. So,
4889 put "xscreensaver" in argv[0] while initializing Xt.
4891 argv[0] = "xscreensaver";
4895 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4897 XtToolkitInitialize ();
4898 app = XtCreateApplicationContext ();
4899 dpy = GDK_DISPLAY();
4900 XtAppSetFallbackResources (app, defaults);
4901 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4902 toplevel_shell = XtAppCreateShell (progname, progclass,
4903 applicationShellWidgetClass,
4906 dpy = XtDisplay (toplevel_shell);
4907 db = XtDatabase (dpy);
4908 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4909 XSetErrorHandler (demo_ehandler);
4911 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4912 signal (SIGPIPE, SIG_IGN);
4914 /* After doing Xt-style command-line processing, complain about any
4915 unrecognized command-line arguments.
4917 for (i = 1; i < argc; i++)
4919 char *str = argv[i];
4920 if (str[0] == '-' && str[1] == '-')
4922 if (!strcmp (str, "-prefs"))
4924 else if (crapplet_p)
4925 /* There are lots of random args that we don't care about when we're
4926 started as a crapplet, so just ignore unknown args in that case. */
4930 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4932 fprintf (stderr, "%s: %s\n", real_progname, usage);
4937 /* Load the init file, which may end up consulting the X resource database
4938 and the site-wide app-defaults file. Note that at this point, it's
4939 important that `progname' be "xscreensaver", rather than whatever
4943 s->nscreens = screen_count (dpy);
4945 hack_environment (s); /* must be before initialize_sort_map() */
4947 load_init_file (dpy, p);
4948 initialize_sort_map (s);
4950 /* Now that Xt has been initialized, and the resources have been read,
4951 we can set our `progname' variable to something more in line with
4954 progname = real_progname;
4958 /* Print out all the resources we read. */
4960 XrmName name = { 0 };
4961 XrmClass class = { 0 };
4963 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4969 /* Intern the atoms that xscreensaver_command() needs.
4971 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4972 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4973 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4974 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4975 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4976 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4977 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4978 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4979 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4980 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4981 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4982 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4983 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4986 /* Create the window and all its widgets.
4988 s->base_widget = create_xscreensaver_demo ();
4989 s->popup_widget = create_xscreensaver_settings_dialog ();
4990 s->toplevel_widget = s->base_widget;
4993 /* Set the main window's title. */
4995 char *base_title = _("Screensaver Preferences");
4996 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4997 char *s1, *s2, *s3, *s4;
4998 s1 = (char *) strchr(v, ' '); s1++;
4999 s2 = (char *) strchr(s1, ' ');
5000 s3 = (char *) strchr(v, '('); s3++;
5001 s4 = (char *) strchr(s3, ')');
5005 window_title = (char *) malloc (strlen (base_title) +
5006 strlen (progclass) +
5007 strlen (s1) + strlen (s3) +
5009 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
5010 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5011 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
5015 /* Adjust the (invisible) notebooks on the popup dialog... */
5017 GtkNotebook *notebook =
5018 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5019 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5023 gtk_widget_hide (std);
5024 # else /* !HAVE_XML */
5025 /* Make the advanced page be the only one available. */
5026 gtk_widget_set_sensitive (std, False);
5027 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5028 gtk_widget_hide (std);
5030 # endif /* !HAVE_XML */
5032 gtk_notebook_set_page (notebook, page);
5033 gtk_notebook_set_show_tabs (notebook, False);
5036 /* Various other widget initializations...
5038 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5039 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5041 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5042 GTK_SIGNAL_FUNC (wm_popup_close_cb),
5045 populate_hack_list (s);
5046 populate_prefs_page (s);
5047 sensitize_demo_widgets (s, False);
5048 fix_text_entry_sizes (s);
5049 scroll_to_current_hack (s);
5051 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5052 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5056 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5057 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5059 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5060 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5062 #endif /* !HAVE_GTK2 */
5064 /* Hook up callbacks to the items on the mode menu. */
5066 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5067 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5068 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5070 for (i = 0; kids; kids = kids->next, i++)
5072 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5073 GTK_SIGNAL_FUNC (mode_menu_item_cb),
5076 /* The "random-same" mode menu item does not appear unless
5077 there are multple screens.
5079 if (s->nscreens <= 1 &&
5080 mode_menu_order[i] == RANDOM_HACKS_SAME)
5081 gtk_widget_hide (GTK_WIDGET (kids->data));
5084 if (s->nscreens <= 1) /* recompute option-menu size */
5086 gtk_widget_unrealize (GTK_WIDGET (menu));
5087 gtk_widget_realize (GTK_WIDGET (menu));
5092 /* Handle the -prefs command-line argument. */
5095 GtkNotebook *notebook =
5096 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5097 gtk_notebook_set_page (notebook, 1);
5100 # ifdef HAVE_CRAPPLET
5104 GtkWidget *outer_vbox;
5106 gtk_widget_hide (s->toplevel_widget);
5108 capplet = capplet_widget_new ();
5110 /* Make there be a "Close" button instead of "OK" and "Cancel" */
5111 # ifdef HAVE_CRAPPLET_IMMEDIATE
5112 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5113 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5114 /* In crapplet-mode, take off the menubar. */
5115 gtk_widget_hide (name_to_widget (s, "menubar"));
5117 /* Reparent our top-level container to be a child of the capplet
5120 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5121 gtk_widget_ref (outer_vbox);
5122 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5124 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5125 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5127 /* Find the window above us, and set the title and close handler. */
5129 GtkWidget *window = capplet;
5130 while (window && !GTK_IS_WINDOW (window))
5131 window = window->parent;
5134 gtk_window_set_title (GTK_WINDOW (window), window_title);
5135 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5136 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5141 s->toplevel_widget = capplet;
5143 # endif /* HAVE_CRAPPLET */
5146 /* The Gnome folks hate the menubar. I think it's important to have access
5147 to the commands on the File menu (Restart Daemon, etc.) and to the
5148 About and Documentation commands on the Help menu.
5152 gtk_widget_hide (name_to_widget (s, "menubar"));
5156 free (window_title);
5160 /* After picking the default size, allow -geometry to override it. */
5162 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5165 gtk_widget_show (s->toplevel_widget);
5166 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
5167 fix_preview_visual (s);
5169 /* Realize page zero, so that we can diddle the scrollbar when the
5170 user tabs back to it -- otherwise, the current hack isn't scrolled
5171 to the first time they tab back there, when started with "-prefs".
5172 (Though it is if they then tab away, and back again.)
5174 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5175 #### understands this crap, explain to me how to make this work.
5177 gtk_widget_realize (name_to_widget (s, "demos_table"));
5180 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5183 /* Issue any warnings about the running xscreensaver daemon. */
5185 the_network_is_not_the_computer (s);
5188 /* Run the Gtk event loop, and not the Xt event loop. This means that
5189 if there were Xt timers or fds registered, they would never get serviced,
5190 and if there were any Xt widgets, they would never have events delivered.
5191 Fortunately, we're using Gtk for all of the UI, and only initialized
5192 Xt so that we could process the command line and use the X resource
5195 s->initializing_p = False;
5197 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5198 after we start up. Otherwise, it always appears scrolled to the top
5199 when in crapplet-mode. */
5200 gtk_timeout_add (500, delayed_scroll_kludge, s);
5204 /* Load every configurator in turn, to scan them for errors all at once. */
5208 for (i = 0; i < p->screenhacks_count; i++)
5210 screenhack *hack = p->screenhacks[i];
5211 conf_data *d = load_configurator (hack->command, s->debug_p);
5212 if (d) free_conf_data (d);
5218 # ifdef HAVE_CRAPPLET
5220 capplet_gtk_main ();
5222 # endif /* HAVE_CRAPPLET */
5225 kill_preview_subproc (s, False);
5229 #endif /* HAVE_GTK -- whole file */