1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2004 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>
85 # include <capplet-widget.h>
91 # include <glade/glade-xml.h>
93 #else /* !HAVE_GTK2 */
94 # define G_MODULE_EXPORT /**/
95 #endif /* !HAVE_GTK2 */
97 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
98 # define GLADE_DIR DEFAULT_ICONDIR
100 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
101 # define DEFAULT_ICONDIR GLADE_DIR
105 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
106 It is unused otherwise, so in that case, stub it out. */
107 static const char *hack_configuration_path = 0;
114 #include "resources.h" /* for parse_time() */
115 #include "visual.h" /* for has_writable_cells() */
116 #include "remote.h" /* for xscreensaver_command() */
119 #include "logo-50.xpm"
120 #include "logo-180.xpm"
122 #undef dgettext /* else these are defined twice... */
125 #include "demo-Gtk-widgets.h"
126 #include "demo-Gtk-support.h"
127 #include "demo-Gtk-conf.h"
139 #endif /* HAVE_GTK2 */
142 extern void exec_command (const char *shell, const char *command, int nice);
144 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
147 #define countof(x) (sizeof((x))/sizeof((*x)))
151 char *progclass = "XScreenSaver";
154 /* The order of the items in the mode menu. */
155 static int mode_menu_order[] = {
156 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS };
161 char *short_version; /* version number of this xscreensaver build */
163 GtkWidget *toplevel_widget; /* the main window */
164 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
165 GtkWidget *popup_widget; /* the "Settings" dialog */
166 conf_data *cdata; /* private data for per-hack configuration */
169 GladeXML *glade_ui; /* Glade UI file */
170 #endif /* HAVE_GTK2 */
172 Bool debug_p; /* whether to print diagnostics */
173 Bool initializing_p; /* flag for breaking recursion loops */
174 Bool saving_p; /* flag for breaking recursion loops */
176 char *desired_preview_cmd; /* subprocess we intend to run */
177 char *running_preview_cmd; /* subprocess we are currently running */
178 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
179 Bool running_preview_error_p; /* whether the pid died abnormally */
181 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
182 int subproc_timer_id; /* timer to delay subproc launch */
183 int subproc_check_timer_id; /* timer to check whether it started up */
184 int subproc_check_countdown; /* how many more checks left */
186 int *list_elt_to_hack_number; /* table for sorting the hack list */
187 int *hack_number_to_list_elt; /* the inverse table */
188 Bool *hacks_available_p; /* whether hacks are on $PATH */
189 int list_count; /* how many items are in the list: this may be
190 less than p->screenhacks_count, if some are
193 int _selected_list_element; /* don't use this: call
194 selected_list_element() instead */
196 saver_preferences prefs;
201 /* Total fucking evilness due to the fact that it's rocket science to get
202 a closure object of our own down into the various widget callbacks. */
203 static state *global_state_kludge;
206 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
207 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
208 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
211 static void populate_demo_window (state *, int list_elt);
212 static void populate_prefs_page (state *);
213 static void populate_popup_window (state *);
215 static Bool flush_dialog_changes_and_save (state *);
216 static Bool flush_popup_changes_and_save (state *);
218 static int maybe_reload_init_file (state *);
219 static void await_xscreensaver (state *);
221 static void schedule_preview (state *, const char *cmd);
222 static void kill_preview_subproc (state *, Bool reset_p);
223 static void schedule_preview_check (state *);
227 /* Some random utility functions
233 time_t now = time ((time_t *) 0);
234 char *ct = (char *) ctime (&now);
235 static char buf[255];
236 int n = strlen(progname);
238 strncpy(buf, progname, n);
241 strncpy(buf+n, ct+11, 8);
242 strcpy(buf+n+9, ": ");
248 name_to_widget (state *s, const char *name)
258 /* First try to load the Glade file from the current directory;
259 if there isn't one there, check the installed directory.
261 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
262 const char * const files[] = { GLADE_FILE_NAME,
263 GLADE_DIR "/" GLADE_FILE_NAME };
265 for (i = 0; i < countof (files); i++)
268 if (!stat (files[i], &st))
270 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
277 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
278 "\tfrom " GLADE_DIR "/ or current directory.\n",
282 # undef GLADE_FILE_NAME
284 glade_xml_signal_autoconnect (s->glade_ui);
287 w = glade_xml_get_widget (s->glade_ui, name);
289 #else /* !HAVE_GTK2 */
291 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
294 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
296 #endif /* HAVE_GTK2 */
299 fprintf (stderr, "%s: no widget \"%s\"\n", blurb(), name);
304 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
305 Takes a scroller, viewport, or list as an argument.
308 ensure_selected_item_visible (GtkWidget *widget)
312 GtkTreeSelection *selection;
316 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
317 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
318 path = gtk_tree_path_new_first ();
320 path = gtk_tree_model_get_path (model, &iter);
322 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
324 gtk_tree_path_free (path);
326 #else /* !HAVE_GTK2 */
328 GtkScrolledWindow *scroller = 0;
330 GtkList *list_widget = 0;
334 GtkWidget *selected = 0;
337 gint parent_h, child_y, child_h, children_h, ignore;
338 double ratio_t, ratio_b;
340 if (GTK_IS_SCROLLED_WINDOW (widget))
342 scroller = GTK_SCROLLED_WINDOW (widget);
343 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
344 list_widget = GTK_LIST (GTK_BIN(vp)->child);
346 else if (GTK_IS_VIEWPORT (widget))
348 vp = GTK_VIEWPORT (widget);
349 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
350 list_widget = GTK_LIST (GTK_BIN(vp)->child);
352 else if (GTK_IS_LIST (widget))
354 list_widget = GTK_LIST (widget);
355 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
356 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
361 slist = list_widget->selection;
362 selected = (slist ? GTK_WIDGET (slist->data) : 0);
366 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
368 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
369 kids; kids = kids->next)
372 adj = gtk_scrolled_window_get_vadjustment (scroller);
374 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
375 &ignore, &ignore, &ignore, &parent_h, &ignore);
376 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
377 &ignore, &child_y, &ignore, &child_h, &ignore);
378 children_h = nkids * child_h;
380 ratio_t = ((double) child_y) / ((double) children_h);
381 ratio_b = ((double) child_y + child_h) / ((double) children_h);
383 if (adj->upper == 0.0) /* no items in list */
386 if (ratio_t < (adj->value / adj->upper) ||
387 ratio_b > ((adj->value + adj->page_size) / adj->upper))
390 int slop = parent_h * 0.75; /* how much to overshoot by */
392 if (ratio_t < (adj->value / adj->upper))
394 double ratio_w = ((double) parent_h) / ((double) children_h);
395 double ratio_l = (ratio_b - ratio_t);
396 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
399 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
401 target = ratio_t * adj->upper;
405 if (target > adj->upper - adj->page_size)
406 target = adj->upper - adj->page_size;
410 gtk_adjustment_set_value (adj, target);
412 #endif /* !HAVE_GTK2 */
416 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
418 GtkWidget *shell = GTK_WIDGET (user_data);
419 while (shell->parent)
420 shell = shell->parent;
421 gtk_widget_destroy (GTK_WIDGET (shell));
425 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
427 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
429 restart_menu_cb (widget, user_data);
430 warning_dialog_dismiss_cb (widget, user_data);
434 warning_dialog (GtkWidget *parent, const char *message,
435 Boolean restart_button_p, int center)
437 char *msg = strdup (message);
440 GtkWidget *dialog = gtk_dialog_new ();
441 GtkWidget *label = 0;
443 GtkWidget *cancel = 0;
446 while (parent && !parent->window)
447 parent = parent->parent;
450 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
452 fprintf (stderr, "%s: too early for dialog?\n", progname);
460 char *s = strchr (head, '\n');
463 sprintf (name, "label%d", i++);
466 label = gtk_label_new (head);
468 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
469 #endif /* HAVE_GTK2 */
474 GTK_WIDGET (label)->style =
475 gtk_style_copy (GTK_WIDGET (label)->style);
476 GTK_WIDGET (label)->style->font =
477 gdk_font_load (get_string_resource("warning_dialog.headingFont",
479 gtk_widget_set_style (GTK_WIDGET (label),
480 GTK_WIDGET (label)->style);
482 #endif /* !HAVE_GTK2 */
484 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
485 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
486 label, TRUE, TRUE, 0);
487 gtk_widget_show (label);
498 label = gtk_label_new ("");
499 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
500 label, TRUE, TRUE, 0);
501 gtk_widget_show (label);
503 label = gtk_hbutton_box_new ();
504 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
505 label, TRUE, TRUE, 0);
508 if (restart_button_p)
510 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
511 gtk_container_add (GTK_CONTAINER (label), cancel);
514 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
515 gtk_container_add (GTK_CONTAINER (label), ok);
517 #else /* !HAVE_GTK2 */
519 ok = gtk_button_new_with_label ("OK");
520 gtk_container_add (GTK_CONTAINER (label), ok);
522 if (restart_button_p)
524 cancel = gtk_button_new_with_label ("Cancel");
525 gtk_container_add (GTK_CONTAINER (label), cancel);
528 #endif /* !HAVE_GTK2 */
530 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
531 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
532 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
533 STFU GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
534 gtk_widget_show (ok);
535 gtk_widget_grab_focus (ok);
539 STFU GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
540 gtk_widget_show (cancel);
542 gtk_widget_show (label);
543 gtk_widget_show (dialog);
545 if (restart_button_p)
547 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
548 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
550 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
551 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
556 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
557 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
561 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
562 GTK_WIDGET (parent)->window);
565 gtk_window_present (GTK_WINDOW (dialog));
566 #else /* !HAVE_GTK2 */
567 gdk_window_show (GTK_WIDGET (dialog)->window);
568 gdk_window_raise (GTK_WIDGET (dialog)->window);
569 #endif /* !HAVE_GTK2 */
576 run_cmd (state *s, Atom command, int arg)
581 flush_dialog_changes_and_save (s);
582 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
584 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
585 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
592 sprintf (buf, "Error:\n\n%s", err);
594 strcpy (buf, "Unknown error!");
595 warning_dialog (s->toplevel_widget, buf, False, 100);
602 run_hack (state *s, int list_elt, Bool report_errors_p)
605 if (list_elt < 0) return;
606 hack_number = s->list_elt_to_hack_number[list_elt];
608 flush_dialog_changes_and_save (s);
609 schedule_preview (s, 0);
611 run_cmd (s, XA_DEMO, hack_number + 1);
615 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
625 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
626 libglade work on Cygwin; apparently all Glade callbacks need this magic
627 extra declaration. I do not pretend to understand.
631 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
633 state *s = global_state_kludge; /* I hate C so much... */
634 flush_dialog_changes_and_save (s);
635 kill_preview_subproc (s, False);
640 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
642 state *s = (state *) data;
643 flush_dialog_changes_and_save (s);
650 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
653 char *vers = strdup (screensaver_id + 4);
656 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
658 s = strchr (vers, ',');
662 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
663 non-ASCII characters aren't allowed in localizable string keys."
664 (I don't want to just use (c) instead of © because that doesn't
665 look as good in the plain-old default Latin1 "C" locale.)
668 sprintf(copy, ("Copyright \xC2\xA9 1991-2004 %s"), s);
669 #else /* !HAVE_GTK2 */
670 sprintf(copy, ("Copyright \251 1991-2004 %s"), s);
671 #endif /* !HAVE_GTK2 */
673 sprintf (msg, "%s\n\n%s", copy, desc);
675 /* I can't make gnome_about_new() work here -- it starts dying in
676 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
677 then this might be the thing to do:
681 const gchar *auth[] = { 0 };
682 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
684 gtk_widget_show (about);
686 #else / * GTK but not GNOME * /
690 GdkColormap *colormap;
691 GdkPixmap *gdkpixmap;
694 GtkWidget *dialog = gtk_dialog_new ();
695 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
696 GtkWidget *parent = GTK_WIDGET (menuitem);
697 while (parent->parent)
698 parent = parent->parent;
700 hbox = gtk_hbox_new (FALSE, 20);
701 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
702 hbox, TRUE, TRUE, 0);
704 colormap = gtk_widget_get_colormap (parent);
706 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
707 (gchar **) logo_180_xpm);
708 icon = gtk_pixmap_new (gdkpixmap, mask);
709 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
711 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
713 vbox = gtk_vbox_new (FALSE, 0);
714 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
716 label1 = gtk_label_new (vers);
717 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
718 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
719 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
722 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
723 GTK_WIDGET (label1)->style->font =
724 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
725 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
726 #endif /* HAVE_GTK2 */
728 label2 = gtk_label_new (msg);
729 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
730 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
731 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
734 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
735 GTK_WIDGET (label2)->style->font =
736 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
737 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
738 #endif /* HAVE_GTK2 */
740 hb = gtk_hbutton_box_new ();
742 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
746 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
747 #else /* !HAVE_GTK2 */
748 ok = gtk_button_new_with_label (_("OK"));
749 #endif /* !HAVE_GTK2 */
750 gtk_container_add (GTK_CONTAINER (hb), ok);
752 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
753 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
754 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
756 gtk_widget_show (hbox);
757 gtk_widget_show (icon);
758 gtk_widget_show (vbox);
759 gtk_widget_show (label1);
760 gtk_widget_show (label2);
761 gtk_widget_show (hb);
762 gtk_widget_show (ok);
763 gtk_widget_show (dialog);
765 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
766 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
768 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
769 GTK_WIDGET (parent)->window);
770 gdk_window_show (GTK_WIDGET (dialog)->window);
771 gdk_window_raise (GTK_WIDGET (dialog)->window);
777 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
779 state *s = global_state_kludge; /* I hate C so much... */
780 saver_preferences *p = &s->prefs;
783 if (!p->help_url || !*p->help_url)
785 warning_dialog (s->toplevel_widget,
787 "No Help URL has been specified.\n"), False, 100);
791 help_command = (char *) malloc (strlen (p->load_url_command) +
792 (strlen (p->help_url) * 2) + 20);
793 strcpy (help_command, "( ");
794 sprintf (help_command + strlen(help_command),
795 p->load_url_command, p->help_url, p->help_url);
796 strcat (help_command, " ) &");
797 system (help_command);
803 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
805 state *s = global_state_kludge; /* I hate C so much... */
806 run_cmd (s, XA_ACTIVATE, 0);
811 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
813 state *s = global_state_kludge; /* I hate C so much... */
814 run_cmd (s, XA_LOCK, 0);
819 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
821 state *s = global_state_kludge; /* I hate C so much... */
822 run_cmd (s, XA_EXIT, 0);
827 restart_menu_cb (GtkWidget *widget, gpointer user_data)
829 state *s = global_state_kludge; /* I hate C so much... */
830 flush_dialog_changes_and_save (s);
831 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
833 system ("xscreensaver -nosplash &");
835 await_xscreensaver (s);
839 await_xscreensaver (state *s)
843 Display *dpy = GDK_DISPLAY();
844 /* GtkWidget *dialog = 0;*/
847 while (!rversion && (--countdown > 0))
849 /* Check for the version of the running xscreensaver... */
850 server_xscreensaver_version (dpy, &rversion, 0, 0);
852 /* If it's not there yet, wait a second... */
857 /* if (dialog) gtk_widget_destroy (dialog);*/
866 /* Timed out, no screensaver running. */
869 Bool root_p = (geteuid () == 0);
873 "The xscreensaver daemon did not start up properly.\n"
879 __extension__ /* don't warn about "string length is greater than
880 the length ISO C89 compilers are required to
881 support" in the following expression... */
884 _("You are running as root. This usually means that xscreensaver\n"
885 "was unable to contact your X server because access control is\n"
886 "turned on. Try running this command:\n"
888 " xhost +localhost\n"
890 "and then selecting `File / Restart Daemon'.\n"
892 "Note that turning off access control will allow anyone logged\n"
893 "on to this machine to access your screen, which might be\n"
894 "considered a security problem. Please read the xscreensaver\n"
895 "manual and FAQ for more information.\n"
897 "You shouldn't run X as root. Instead, you should log in as a\n"
898 "normal user, and `su' as necessary."));
900 strcat (buf, _("Please check your $PATH and permissions."));
902 warning_dialog (s->toplevel_widget, buf, False, 1);
908 selected_list_element (state *s)
910 return s->_selected_list_element;
915 demo_write_init_file (state *s, saver_preferences *p)
919 /* #### try to figure out why shit keeps getting reordered... */
920 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
924 if (!write_init_file (p, s->short_version, False))
927 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
932 const char *f = init_file_name();
934 warning_dialog (s->toplevel_widget,
935 _("Error:\n\nCouldn't determine init file name!\n"),
939 char *b = (char *) malloc (strlen(f) + 1024);
940 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
941 warning_dialog (s->toplevel_widget, b, False, 100);
950 run_this_cb (GtkButton *button, gpointer user_data)
952 state *s = global_state_kludge; /* I hate C so much... */
953 int list_elt = selected_list_element (s);
954 if (list_elt < 0) return;
955 if (!flush_dialog_changes_and_save (s))
956 run_hack (s, list_elt, True);
961 manual_cb (GtkButton *button, gpointer user_data)
963 state *s = global_state_kludge; /* I hate C so much... */
964 saver_preferences *p = &s->prefs;
965 GtkWidget *list_widget = name_to_widget (s, "list");
966 int list_elt = selected_list_element (s);
968 char *name, *name2, *cmd, *str;
969 if (list_elt < 0) return;
970 hack_number = s->list_elt_to_hack_number[list_elt];
972 flush_dialog_changes_and_save (s);
973 ensure_selected_item_visible (list_widget);
975 name = strdup (p->screenhacks[hack_number]->command);
977 while (isspace (*name2)) name2++;
979 while (*str && !isspace (*str)) str++;
981 str = strrchr (name2, '/');
982 if (str) name = str+1;
984 cmd = get_string_resource ("manualCommand", "ManualCommand");
987 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
989 sprintf (cmd2 + strlen (cmd2),
991 name2, name2, name2, name2);
992 strcat (cmd2, " ) &");
998 warning_dialog (GTK_WIDGET (button),
999 _("Error:\n\nno `manualCommand' resource set."),
1008 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1010 GtkWidget *parent = name_to_widget (s, "scroller");
1011 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
1014 GtkTreeModel *model;
1015 GtkTreeSelection *selection;
1016 #endif /* HAVE_GTK2 */
1018 if (!was) gtk_widget_set_sensitive (parent, True);
1020 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1021 STFU g_assert (model);
1022 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
1023 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1024 gtk_tree_selection_select_iter (selection, &iter);
1025 #else /* !HAVE_GTK2 */
1026 gtk_list_select_item (GTK_LIST (list), list_elt);
1027 #endif /* !HAVE_GTK2 */
1028 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1029 if (!was) gtk_widget_set_sensitive (parent, False);
1033 G_MODULE_EXPORT void
1034 run_next_cb (GtkButton *button, gpointer user_data)
1036 state *s = global_state_kludge; /* I hate C so much... */
1037 /* saver_preferences *p = &s->prefs; */
1038 Bool ops = s->preview_suppressed_p;
1040 GtkWidget *list_widget = name_to_widget (s, "list");
1041 int list_elt = selected_list_element (s);
1048 if (list_elt >= s->list_count)
1051 s->preview_suppressed_p = True;
1053 flush_dialog_changes_and_save (s);
1054 force_list_select_item (s, list_widget, list_elt, True);
1055 populate_demo_window (s, list_elt);
1056 run_hack (s, list_elt, False);
1058 s->preview_suppressed_p = ops;
1062 G_MODULE_EXPORT void
1063 run_prev_cb (GtkButton *button, gpointer user_data)
1065 state *s = global_state_kludge; /* I hate C so much... */
1066 /* saver_preferences *p = &s->prefs; */
1067 Bool ops = s->preview_suppressed_p;
1069 GtkWidget *list_widget = name_to_widget (s, "list");
1070 int list_elt = selected_list_element (s);
1073 list_elt = s->list_count - 1;
1078 list_elt = s->list_count - 1;
1080 s->preview_suppressed_p = True;
1082 flush_dialog_changes_and_save (s);
1083 force_list_select_item (s, list_widget, list_elt, True);
1084 populate_demo_window (s, list_elt);
1085 run_hack (s, list_elt, False);
1087 s->preview_suppressed_p = ops;
1091 /* Writes the given settings into prefs.
1092 Returns true if there was a change, False otherwise.
1093 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1096 flush_changes (state *s,
1099 const char *command,
1102 saver_preferences *p = &s->prefs;
1103 Bool changed = False;
1106 if (list_elt < 0 || list_elt >= s->list_count)
1109 hack_number = s->list_elt_to_hack_number[list_elt];
1110 hack = p->screenhacks[hack_number];
1112 if (enabled_p != -1 &&
1113 enabled_p != hack->enabled_p)
1115 hack->enabled_p = enabled_p;
1118 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1119 blurb(), hack->name, enabled_p);
1124 if (!hack->command || !!strcmp (command, hack->command))
1126 if (hack->command) free (hack->command);
1127 hack->command = strdup (command);
1130 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1131 blurb(), hack->name, command);
1137 const char *ov = hack->visual;
1138 if (!ov || !*ov) ov = "any";
1139 if (!*visual) visual = "any";
1140 if (!!strcasecmp (visual, ov))
1142 if (hack->visual) free (hack->visual);
1143 hack->visual = strdup (visual);
1146 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1147 blurb(), hack->name, visual);
1155 /* Helper for the text fields that contain time specifications:
1156 this parses the text, and does error checking.
1159 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1164 if (!sec_p || strchr (line, ':'))
1165 value = parse_time ((char *) line, sec_p, True);
1169 if (sscanf (line, "%d%c", &value, &c) != 1)
1175 value *= 1000; /* Time measures in microseconds */
1181 "Unparsable time format: \"%s\"\n"),
1183 warning_dialog (s->toplevel_widget, b, False, 100);
1192 directory_p (const char *path)
1195 if (!path || !*path)
1197 else if (stat (path, &st))
1199 else if (!S_ISDIR (st.st_mode))
1206 normalize_directory (const char *path)
1210 if (!path || !*path) return 0;
1212 p2 = (char *) malloc (L + 2);
1214 if (p2[L-1] == '/') /* remove trailing slash */
1217 for (s = p2; s && *s; s++)
1220 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1221 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1224 while (s0 > p2 && s0[-1] != '/')
1234 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1235 strcpy (s, s+2), s--;
1236 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1240 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1241 while (s[0] == '/' && s[1] == '/')
1244 /* and strip trailing whitespace for good measure. */
1246 while (isspace(p2[L-1]))
1259 } FlushForeachClosure;
1262 flush_checkbox (GtkTreeModel *model,
1267 FlushForeachClosure *closure = data;
1270 gtk_tree_model_get (model, iter,
1271 COL_ENABLED, &checked,
1274 if (flush_changes (closure->s, closure->i,
1276 *closure->changed = True;
1280 /* don't remove row */
1284 #endif /* HAVE_GTK2 */
1286 /* Flush out any changes made in the main dialog window (where changes
1287 take place immediately: clicking on a checkbox causes the init file
1288 to be written right away.)
1291 flush_dialog_changes_and_save (state *s)
1293 saver_preferences *p = &s->prefs;
1294 saver_preferences P2, *p2 = &P2;
1296 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1297 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1298 FlushForeachClosure closure;
1299 #else /* !HAVE_GTK2 */
1300 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1301 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1303 #endif /* !HAVE_GTK2 */
1305 Bool changed = False;
1308 if (s->saving_p) return False;
1313 /* Flush any checkbox changes in the list down into the prefs struct.
1317 closure.changed = &changed;
1319 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1321 #else /* !HAVE_GTK2 */
1323 for (i = 0; kids; kids = kids->next, i++)
1325 GtkWidget *line = GTK_WIDGET (kids->data);
1326 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1327 GtkWidget *line_check =
1328 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1330 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1332 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1335 #endif /* ~HAVE_GTK2 */
1337 /* Flush the non-hack-specific settings down into the prefs struct.
1340 # define SECONDS(FIELD,NAME) \
1341 w = name_to_widget (s, (NAME)); \
1342 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1344 # define MINUTES(FIELD,NAME) \
1345 w = name_to_widget (s, (NAME)); \
1346 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1348 # define CHECKBOX(FIELD,NAME) \
1349 w = name_to_widget (s, (NAME)); \
1350 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1352 # define PATHNAME(FIELD,NAME) \
1353 w = name_to_widget (s, (NAME)); \
1354 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1356 MINUTES (&p2->timeout, "timeout_spinbutton");
1357 MINUTES (&p2->cycle, "cycle_spinbutton");
1358 CHECKBOX (p2->lock_p, "lock_button");
1359 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1361 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1362 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1363 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1364 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1366 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1367 CHECKBOX (p2->grab_video_p, "grab_video_button");
1368 CHECKBOX (p2->random_image_p, "grab_image_button");
1369 PATHNAME (p2->image_directory, "image_text");
1371 CHECKBOX (p2->verbose_p, "verbose_button");
1372 CHECKBOX (p2->capture_stderr_p, "capture_button");
1373 CHECKBOX (p2->splash_p, "splash_button");
1375 CHECKBOX (p2->install_cmap_p, "install_button");
1376 CHECKBOX (p2->fade_p, "fade_button");
1377 CHECKBOX (p2->unfade_p, "unfade_button");
1378 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1385 /* Warn if the image directory doesn't exist.
1387 if (p2->image_directory &&
1388 *p2->image_directory &&
1389 !directory_p (p2->image_directory))
1392 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1393 p2->image_directory);
1394 warning_dialog (s->toplevel_widget, b, False, 100);
1398 /* Map the mode menu to `saver_mode' enum values. */
1400 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1401 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1402 GtkWidget *selected = gtk_menu_get_active (menu);
1403 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1404 int menu_elt = g_list_index (kids, (gpointer) selected);
1405 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1406 p2->mode = mode_menu_order[menu_elt];
1409 if (p2->mode == ONE_HACK)
1411 int list_elt = selected_list_element (s);
1412 p2->selected_hack = (list_elt >= 0
1413 ? s->list_elt_to_hack_number[list_elt]
1417 # define COPY(field, name) \
1418 if (p->field != p2->field) { \
1421 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1423 p->field = p2->field
1426 COPY(selected_hack, "selected_hack");
1428 COPY(timeout, "timeout");
1429 COPY(cycle, "cycle");
1430 COPY(lock_p, "lock_p");
1431 COPY(lock_timeout, "lock_timeout");
1433 COPY(dpms_enabled_p, "dpms_enabled_p");
1434 COPY(dpms_standby, "dpms_standby");
1435 COPY(dpms_suspend, "dpms_suspend");
1436 COPY(dpms_off, "dpms_off");
1438 COPY(verbose_p, "verbose_p");
1439 COPY(capture_stderr_p, "capture_stderr_p");
1440 COPY(splash_p, "splash_p");
1442 COPY(install_cmap_p, "install_cmap_p");
1443 COPY(fade_p, "fade_p");
1444 COPY(unfade_p, "unfade_p");
1445 COPY(fade_seconds, "fade_seconds");
1447 COPY(grab_desktop_p, "grab_desktop_p");
1448 COPY(grab_video_p, "grab_video_p");
1449 COPY(random_image_p, "random_image_p");
1453 if (!p->image_directory ||
1454 !p2->image_directory ||
1455 strcmp(p->image_directory, p2->image_directory))
1459 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1460 blurb(), p2->image_directory);
1462 if (p->image_directory && p->image_directory != p2->image_directory)
1463 free (p->image_directory);
1464 p->image_directory = p2->image_directory;
1465 p2->image_directory = 0;
1467 populate_prefs_page (s);
1471 Display *dpy = GDK_DISPLAY();
1472 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1473 sync_server_dpms_settings (dpy, enabled_p,
1474 p->dpms_standby / 1000,
1475 p->dpms_suspend / 1000,
1479 changed = demo_write_init_file (s, p);
1482 s->saving_p = False;
1487 /* Flush out any changes made in the popup dialog box (where changes
1488 take place only when the OK button is clicked.)
1491 flush_popup_changes_and_save (state *s)
1493 Bool changed = False;
1494 saver_preferences *p = &s->prefs;
1495 int list_elt = selected_list_element (s);
1497 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1498 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1500 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1501 const char *command = gtk_entry_get_text (cmd);
1506 if (s->saving_p) return False;
1512 if (maybe_reload_init_file (s) != 0)
1518 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1520 if (!strcasecmp (visual, "")) visual = "";
1521 else if (!strcasecmp (visual, "any")) visual = "";
1522 else if (!strcasecmp (visual, "default")) visual = "Default";
1523 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1524 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1525 else if (!strcasecmp (visual, "best")) visual = "Best";
1526 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1527 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1528 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1529 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1530 else if (!strcasecmp (visual, "color")) visual = "Color";
1531 else if (!strcasecmp (visual, "gl")) visual = "GL";
1532 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1533 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1534 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1535 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1536 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1537 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1538 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1539 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1540 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1543 gdk_beep (); /* unparsable */
1545 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1548 changed = flush_changes (s, list_elt, -1, command, visual);
1551 changed = demo_write_init_file (s, p);
1553 /* Do this to re-launch the hack if (and only if) the command line
1555 populate_demo_window (s, selected_list_element (s));
1559 s->saving_p = False;
1564 G_MODULE_EXPORT void
1565 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1567 state *s = global_state_kludge; /* I hate C so much... */
1568 if (! s->initializing_p)
1570 s->initializing_p = True;
1571 flush_dialog_changes_and_save (s);
1572 s->initializing_p = False;
1576 G_MODULE_EXPORT gboolean
1577 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1579 pref_changed_cb (widget, user_data);
1583 /* Callback on menu items in the "mode" options menu.
1586 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1588 state *s = (state *) user_data;
1589 saver_preferences *p = &s->prefs;
1590 GtkWidget *list = name_to_widget (s, "list");
1593 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1595 saver_mode new_mode;
1599 if (menu_items->data == widget)
1602 menu_items = menu_items->next;
1604 if (!menu_items) abort();
1606 new_mode = mode_menu_order[menu_index];
1608 /* Keep the same list element displayed as before; except if we're
1609 switching *to* "one screensaver" mode from any other mode, set
1610 "the one" to be that which is currently selected.
1612 list_elt = selected_list_element (s);
1613 if (new_mode == ONE_HACK)
1614 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1617 saver_mode old_mode = p->mode;
1619 populate_demo_window (s, list_elt);
1620 force_list_select_item (s, list, list_elt, True);
1621 p->mode = old_mode; /* put it back, so the init file gets written */
1624 pref_changed_cb (widget, user_data);
1628 G_MODULE_EXPORT void
1629 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1630 gint page_num, gpointer user_data)
1632 state *s = global_state_kludge; /* I hate C so much... */
1633 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1635 /* If we're switching to page 0, schedule the current hack to be run.
1636 Otherwise, schedule it to stop. */
1638 populate_demo_window (s, selected_list_element (s));
1640 schedule_preview (s, 0);
1645 list_activated_cb (GtkTreeView *list,
1647 GtkTreeViewColumn *column,
1654 STFU g_return_if_fail (!gdk_pointer_is_grabbed ());
1656 str = gtk_tree_path_to_string (path);
1657 list_elt = strtol (str, NULL, 10);
1661 run_hack (s, list_elt, True);
1665 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1667 state *s = (state *)data;
1668 GtkTreeModel *model;
1674 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1677 path = gtk_tree_model_get_path (model, &iter);
1678 str = gtk_tree_path_to_string (path);
1679 list_elt = strtol (str, NULL, 10);
1681 gtk_tree_path_free (path);
1684 populate_demo_window (s, list_elt);
1685 flush_dialog_changes_and_save (s);
1688 #else /* !HAVE_GTK2 */
1690 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1691 list_select_cb that comes in
1692 *after* we've double-clicked.
1696 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1699 state *s = (state *) data;
1700 if (event->type == GDK_2BUTTON_PRESS)
1702 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1703 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1705 last_doubleclick_time = time ((time_t *) 0);
1708 run_hack (s, list_elt, True);
1716 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1718 state *s = (state *) data;
1719 time_t now = time ((time_t *) 0);
1721 if (now >= last_doubleclick_time + 2)
1723 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1724 populate_demo_window (s, list_elt);
1725 flush_dialog_changes_and_save (s);
1730 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1732 state *s = (state *) data;
1733 populate_demo_window (s, -1);
1734 flush_dialog_changes_and_save (s);
1737 #endif /* !HAVE_GTK2 */
1740 /* Called when the checkboxes that are in the left column of the
1741 scrolling list are clicked. This both populates the right pane
1742 (just as clicking on the label (really, listitem) does) and
1743 also syncs this checkbox with the right pane Enabled checkbox.
1748 GtkCellRendererToggle *toggle,
1750 #else /* !HAVE_GTK2 */
1752 #endif /* !HAVE_GTK2 */
1755 state *s = (state *) data;
1758 GtkScrolledWindow *scroller =
1759 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1760 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1761 GtkTreeModel *model = gtk_tree_view_get_model (list);
1762 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1765 #else /* !HAVE_GTK2 */
1766 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1767 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1769 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1770 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1771 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1772 #endif /* !HAVE_GTK2 */
1779 if (!gtk_tree_model_get_iter (model, &iter, path))
1781 g_warning ("bad path: %s", path_string);
1784 gtk_tree_path_free (path);
1786 gtk_tree_model_get (model, &iter,
1787 COL_ENABLED, &active,
1790 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1791 COL_ENABLED, !active,
1794 list_elt = strtol (path_string, NULL, 10);
1795 #else /* !HAVE_GTK2 */
1796 list_elt = gtk_list_child_position (list, line);
1797 #endif /* !HAVE_GTK2 */
1799 /* remember previous scroll position of the top of the list */
1800 adj = gtk_scrolled_window_get_vadjustment (scroller);
1801 scroll_top = adj->value;
1803 flush_dialog_changes_and_save (s);
1804 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1805 populate_demo_window (s, list_elt);
1807 /* restore the previous scroll position of the top of the list.
1808 this is weak, but I don't really know why it's moving... */
1809 gtk_adjustment_set_value (adj, scroll_top);
1815 GtkFileSelection *widget;
1816 } file_selection_data;
1821 store_image_directory (GtkWidget *button, gpointer user_data)
1823 file_selection_data *fsd = (file_selection_data *) user_data;
1824 state *s = fsd->state;
1825 GtkFileSelection *selector = fsd->widget;
1826 GtkWidget *top = s->toplevel_widget;
1827 saver_preferences *p = &s->prefs;
1828 const char *path = gtk_file_selection_get_filename (selector);
1830 if (p->image_directory && !strcmp(p->image_directory, path))
1831 return; /* no change */
1833 if (!directory_p (path))
1836 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1837 warning_dialog (GTK_WIDGET (top), b, False, 100);
1841 if (p->image_directory) free (p->image_directory);
1842 p->image_directory = normalize_directory (path);
1844 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1845 (p->image_directory ? p->image_directory : ""));
1846 demo_write_init_file (s, p);
1851 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1853 file_selection_data *fsd = (file_selection_data *) user_data;
1854 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1858 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1860 browse_image_dir_cancel (button, user_data);
1861 store_image_directory (button, user_data);
1865 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1867 browse_image_dir_cancel (widget, user_data);
1871 G_MODULE_EXPORT void
1872 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1874 state *s = global_state_kludge; /* I hate C so much... */
1875 saver_preferences *p = &s->prefs;
1876 static file_selection_data *fsd = 0;
1878 GtkFileSelection *selector = GTK_FILE_SELECTION(
1879 gtk_file_selection_new ("Please select the image directory."));
1882 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1884 fsd->widget = selector;
1887 if (p->image_directory && *p->image_directory)
1888 gtk_file_selection_set_filename (selector, p->image_directory);
1890 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1891 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1893 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1894 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1896 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1897 GTK_SIGNAL_FUNC (browse_image_dir_close),
1900 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1902 gtk_window_set_modal (GTK_WINDOW (selector), True);
1903 gtk_widget_show (GTK_WIDGET (selector));
1907 G_MODULE_EXPORT void
1908 settings_cb (GtkButton *button, gpointer user_data)
1910 state *s = global_state_kludge; /* I hate C so much... */
1911 int list_elt = selected_list_element (s);
1913 populate_demo_window (s, list_elt); /* reset the widget */
1914 populate_popup_window (s); /* create UI on popup window */
1915 gtk_widget_show (s->popup_widget);
1919 settings_sync_cmd_text (state *s)
1922 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1923 char *cmd_line = get_configurator_command_line (s->cdata);
1924 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1925 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1927 # endif /* HAVE_XML */
1930 G_MODULE_EXPORT void
1931 settings_adv_cb (GtkButton *button, gpointer user_data)
1933 state *s = global_state_kludge; /* I hate C so much... */
1934 GtkNotebook *notebook =
1935 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1937 settings_sync_cmd_text (s);
1938 gtk_notebook_set_page (notebook, 1);
1941 G_MODULE_EXPORT void
1942 settings_std_cb (GtkButton *button, gpointer user_data)
1944 state *s = global_state_kludge; /* I hate C so much... */
1945 GtkNotebook *notebook =
1946 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1948 /* Re-create UI to reflect the in-progress command-line settings. */
1949 populate_popup_window (s);
1951 gtk_notebook_set_page (notebook, 0);
1954 G_MODULE_EXPORT void
1955 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1956 gint page_num, gpointer user_data)
1958 state *s = global_state_kludge; /* I hate C so much... */
1959 GtkWidget *adv = name_to_widget (s, "adv_button");
1960 GtkWidget *std = name_to_widget (s, "std_button");
1964 gtk_widget_show (adv);
1965 gtk_widget_hide (std);
1967 else if (page_num == 1)
1969 gtk_widget_hide (adv);
1970 gtk_widget_show (std);
1978 G_MODULE_EXPORT void
1979 settings_cancel_cb (GtkButton *button, gpointer user_data)
1981 state *s = global_state_kludge; /* I hate C so much... */
1982 gtk_widget_hide (s->popup_widget);
1985 G_MODULE_EXPORT void
1986 settings_ok_cb (GtkButton *button, gpointer user_data)
1988 state *s = global_state_kludge; /* I hate C so much... */
1989 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1990 int page = gtk_notebook_get_current_page (notebook);
1993 /* Regenerate the command-line from the widget contents before saving.
1994 But don't do this if we're looking at the command-line page already,
1995 or we will blow away what they typed... */
1996 settings_sync_cmd_text (s);
1998 flush_popup_changes_and_save (s);
1999 gtk_widget_hide (s->popup_widget);
2003 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2005 state *s = (state *) data;
2006 settings_cancel_cb (0, (gpointer) s);
2012 /* Populating the various widgets
2016 /* Returns the number of the last hack run by the server.
2019 server_current_hack (void)
2023 unsigned long nitems, bytesafter;
2024 unsigned char *dataP = 0;
2025 Display *dpy = GDK_DISPLAY();
2026 int hack_number = -1;
2028 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2029 XA_SCREENSAVER_STATUS,
2030 0, 3, False, XA_INTEGER,
2031 &type, &format, &nitems, &bytesafter,
2034 && type == XA_INTEGER
2038 CARD32 *data = (CARD32 *) dataP;
2039 hack_number = (int) data[2] - 1;
2042 if (dataP) XFree (dataP);
2048 /* Finds the number of the last hack to run, and makes that item be
2049 selected by default.
2052 scroll_to_current_hack (state *s)
2054 saver_preferences *p = &s->prefs;
2057 if (p->mode == ONE_HACK)
2058 hack_number = p->selected_hack;
2060 hack_number = server_current_hack ();
2062 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2064 int list_elt = s->hack_number_to_list_elt[hack_number];
2065 GtkWidget *list = name_to_widget (s, "list");
2066 force_list_select_item (s, list, list_elt, True);
2067 populate_demo_window (s, list_elt);
2073 on_path_p (const char *program)
2077 char *cmd = strdup (program);
2078 char *token = strchr (cmd, ' ');
2082 if (token) *token = 0;
2085 if (strchr (cmd, '/'))
2087 result = (0 == stat (cmd, &st));
2091 path = getenv("PATH");
2092 if (!path || !*path)
2096 path = strdup (path);
2097 token = strtok (path, ":");
2101 char *p2 = (char *) malloc (strlen (token) + L + 3);
2105 result = (0 == stat (p2, &st));
2108 token = strtok (0, ":");
2113 if (path) free (path);
2119 populate_hack_list (state *s)
2122 saver_preferences *p = &s->prefs;
2123 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2124 GtkListStore *model;
2125 GtkTreeSelection *selection;
2126 GtkCellRenderer *ren;
2130 g_object_get (G_OBJECT (list),
2135 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2136 g_object_set (G_OBJECT (list), "model", model, NULL);
2137 g_object_unref (model);
2139 ren = gtk_cell_renderer_toggle_new ();
2140 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2142 "active", COL_ENABLED,
2145 g_signal_connect (ren, "toggled",
2146 G_CALLBACK (list_checkbox_cb),
2149 ren = gtk_cell_renderer_text_new ();
2150 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2151 _("Screen Saver"), ren,
2155 g_signal_connect_after (list, "row_activated",
2156 G_CALLBACK (list_activated_cb),
2159 selection = gtk_tree_view_get_selection (list);
2160 g_signal_connect (selection, "changed",
2161 G_CALLBACK (list_select_changed_cb),
2166 for (i = 0; i < s->list_count; i++)
2168 int hack_number = s->list_elt_to_hack_number[i];
2169 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2171 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2173 if (!hack) continue;
2175 /* If we're to suppress uninstalled hacks, check $PATH now. */
2176 if (p->ignore_uninstalled_p && !available_p)
2179 pretty_name = (hack->name
2180 ? strdup (hack->name)
2181 : make_hack_name (hack->command));
2185 /* Make the text foreground be the color of insensitive widgets
2186 (but don't actually make it be insensitive, since we still
2187 want to be able to click on it.)
2189 GtkStyle *style = GTK_WIDGET (list)->style;
2190 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2191 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2192 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2194 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2195 /* " background=\"#%02X%02X%02X\"" */
2197 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2198 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2204 gtk_list_store_append (model, &iter);
2205 gtk_list_store_set (model, &iter,
2206 COL_ENABLED, hack->enabled_p,
2207 COL_NAME, pretty_name,
2212 #else /* !HAVE_GTK2 */
2214 saver_preferences *p = &s->prefs;
2215 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2217 for (i = 0; i < s->list_count; i++)
2219 int hack_number = s->list_elt_to_hack_number[i];
2220 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2222 /* A GtkList must contain only GtkListItems, but those can contain
2223 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2224 and a Label. We handle single and double click events on the
2225 line itself, for clicking on the text, but the interior checkbox
2226 also handles its own events.
2229 GtkWidget *line_hbox;
2230 GtkWidget *line_check;
2231 GtkWidget *line_label;
2233 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2235 if (!hack) continue;
2237 /* If we're to suppress uninstalled hacks, check $PATH now. */
2238 if (p->ignore_uninstalled_p && !available_p)
2241 pretty_name = (hack->name
2242 ? strdup (hack->name)
2243 : make_hack_name (hack->command));
2245 line = gtk_list_item_new ();
2246 line_hbox = gtk_hbox_new (FALSE, 0);
2247 line_check = gtk_check_button_new ();
2248 line_label = gtk_label_new (pretty_name);
2250 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2251 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2252 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2254 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2256 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2258 gtk_widget_show (line_check);
2259 gtk_widget_show (line_label);
2260 gtk_widget_show (line_hbox);
2261 gtk_widget_show (line);
2265 gtk_container_add (GTK_CONTAINER (list), line);
2266 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2267 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2270 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2271 GTK_SIGNAL_FUNC (list_checkbox_cb),
2274 gtk_widget_show (line);
2278 /* Make the widget be colored like insensitive widgets
2279 (but don't actually make it be insensitive, since we
2280 still want to be able to click on it.)
2282 GtkRcStyle *rc_style;
2285 gtk_widget_realize (GTK_WIDGET (line_label));
2287 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2288 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2290 rc_style = gtk_rc_style_new ();
2291 rc_style->fg[GTK_STATE_NORMAL] = fg;
2292 rc_style->bg[GTK_STATE_NORMAL] = bg;
2293 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2295 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2296 gtk_rc_style_unref (rc_style);
2300 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2301 GTK_SIGNAL_FUNC (list_select_cb),
2303 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2304 GTK_SIGNAL_FUNC (list_unselect_cb),
2306 #endif /* !HAVE_GTK2 */
2310 update_list_sensitivity (state *s)
2312 saver_preferences *p = &s->prefs;
2313 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2314 Bool checkable = (p->mode == RANDOM_HACKS);
2315 Bool blankable = (p->mode != DONT_BLANK);
2318 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2319 GtkWidget *use = name_to_widget (s, "use_col_frame");
2320 #endif /* HAVE_GTK2 */
2321 GtkWidget *scroller = name_to_widget (s, "scroller");
2322 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2323 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2326 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2327 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2328 #else /* !HAVE_GTK2 */
2329 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2330 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2332 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2333 #endif /* !HAVE_GTK2 */
2334 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2335 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2337 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2340 gtk_tree_view_column_set_visible (use, checkable);
2341 #else /* !HAVE_GTK2 */
2343 gtk_widget_show (use); /* the "Use" column header */
2345 gtk_widget_hide (use);
2349 GtkBin *line = GTK_BIN (kids->data);
2350 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2351 GtkWidget *line_check =
2352 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2355 gtk_widget_show (line_check);
2357 gtk_widget_hide (line_check);
2361 #endif /* !HAVE_GTK2 */
2366 populate_prefs_page (state *s)
2368 saver_preferences *p = &s->prefs;
2370 Bool can_lock_p = True;
2372 /* Disable all the "lock" controls if locking support was not provided
2373 at compile-time, or if running on MacOS. */
2374 # if defined(NO_LOCKING) || defined(__APPLE__)
2379 /* The file supports timeouts of less than a minute, but the GUI does
2380 not, so throttle the values to be at least one minute (since "0" is
2381 a bad rounding choice...)
2383 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2386 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2389 # define FMT_MINUTES(NAME,N) \
2390 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2392 # define FMT_SECONDS(NAME,N) \
2393 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2395 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2396 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2397 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2398 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2399 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2400 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2401 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2406 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2407 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2410 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2411 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2412 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2413 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2414 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2415 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2416 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2417 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2418 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2419 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2420 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2422 # undef TOGGLE_ACTIVE
2424 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2425 (p->image_directory ? p->image_directory : ""));
2426 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2428 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2431 /* Map the `saver_mode' enum to mode menu to values. */
2433 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2436 for (i = 0; i < countof(mode_menu_order); i++)
2437 if (mode_menu_order[i] == p->mode)
2439 gtk_option_menu_set_history (opt, i);
2440 update_list_sensitivity (s);
2444 Bool found_any_writable_cells = False;
2445 Bool dpms_supported = False;
2447 Display *dpy = GDK_DISPLAY();
2448 int nscreens = ScreenCount(dpy);
2450 for (i = 0; i < nscreens; i++)
2452 Screen *s = ScreenOfDisplay (dpy, i);
2453 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2455 found_any_writable_cells = True;
2460 #ifdef HAVE_XF86VMODE_GAMMA
2461 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2464 #ifdef HAVE_DPMS_EXTENSION
2466 int op = 0, event = 0, error = 0;
2467 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2468 dpms_supported = True;
2470 #endif /* HAVE_DPMS_EXTENSION */
2473 # define SENSITIZE(NAME,SENSITIVEP) \
2474 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2476 /* Blanking and Locking
2478 SENSITIZE ("lock_button", can_lock_p);
2479 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2480 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2484 SENSITIZE ("dpms_frame", dpms_supported);
2485 SENSITIZE ("dpms_button", dpms_supported);
2486 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2487 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2488 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2489 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2490 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2491 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2492 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2493 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2494 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2498 SENSITIZE ("cmap_frame", found_any_writable_cells);
2499 SENSITIZE ("install_button", found_any_writable_cells);
2500 SENSITIZE ("fade_button", found_any_writable_cells);
2501 SENSITIZE ("unfade_button", found_any_writable_cells);
2503 SENSITIZE ("fade_label", (found_any_writable_cells &&
2504 (p->fade_p || p->unfade_p)));
2505 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2506 (p->fade_p || p->unfade_p)));
2514 populate_popup_window (state *s)
2516 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2517 char *doc_string = 0;
2519 /* #### not in Gtk 1.2
2520 gtk_label_set_selectable (doc);
2526 free_conf_data (s->cdata);
2531 saver_preferences *p = &s->prefs;
2532 int list_elt = selected_list_element (s);
2533 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2534 ? s->list_elt_to_hack_number[list_elt]
2536 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2539 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2540 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2541 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2542 s->cdata = load_configurator (cmd_line, s->debug_p);
2543 if (s->cdata && s->cdata->widget)
2544 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2549 doc_string = (s->cdata
2550 ? s->cdata->description
2552 # else /* !HAVE_XML */
2553 doc_string = _("Descriptions not available: no XML support compiled in.");
2554 # endif /* !HAVE_XML */
2556 gtk_label_set_text (doc, (doc_string
2558 : _("No description available.")));
2563 sensitize_demo_widgets (state *s, Bool sensitive_p)
2565 const char *names1[] = { "demo", "settings" };
2566 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2567 "visual", "visual_combo" };
2569 for (i = 0; i < countof(names1); i++)
2571 GtkWidget *w = name_to_widget (s, names1[i]);
2572 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2574 for (i = 0; i < countof(names2); i++)
2576 GtkWidget *w = name_to_widget (s, names2[i]);
2577 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2582 /* Even though we've given these text fields a maximum number of characters,
2583 their default size is still about 30 characters wide -- so measure out
2584 a string in their font, and resize them to just fit that.
2587 fix_text_entry_sizes (state *s)
2591 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2592 const char * const spinbuttons[] = {
2593 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2594 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2595 "dpms_off_spinbutton",
2596 "-fade_spinbutton" };
2600 for (i = 0; i < countof(spinbuttons); i++)
2602 const char *n = spinbuttons[i];
2604 while (*n == '-') n++, cols--;
2605 w = GTK_WIDGET (name_to_widget (s, n));
2606 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2607 gtk_widget_set_usize (w, width, -2);
2610 /* Now fix the width of the combo box.
2612 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2613 w = GTK_COMBO (w)->entry;
2614 width = gdk_string_width (w->style->font, "PseudoColor___");
2615 gtk_widget_set_usize (w, width, -2);
2617 /* Now fix the width of the file entry text.
2619 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2620 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2621 gtk_widget_set_usize (w, width, -2);
2623 /* Now fix the width of the command line text.
2625 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2626 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2627 gtk_widget_set_usize (w, width, -2);
2631 /* Now fix the height of the list widget:
2632 make it default to being around 10 text-lines high instead of 4.
2634 w = GTK_WIDGET (name_to_widget (s, "list"));
2638 int leading = 3; /* approximate is ok... */
2642 PangoFontMetrics *pain =
2643 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2644 w->style->font_desc,
2645 gtk_get_default_language ());
2646 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2647 pango_font_metrics_get_descent (pain));
2648 #else /* !HAVE_GTK2 */
2649 height = w->style->font->ascent + w->style->font->descent;
2650 #endif /* !HAVE_GTK2 */
2654 height += border * 2;
2655 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2656 gtk_widget_set_usize (w, -2, height);
2663 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2666 static char *up_arrow_xpm[] = {
2689 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2690 the end of the array (Gtk 1.2.5.) */
2691 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2692 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2695 static char *down_arrow_xpm[] = {
2718 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2719 the end of the array (Gtk 1.2.5.) */
2720 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2721 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2725 pixmapify_button (state *s, int down_p)
2729 GtkWidget *pixmapwid;
2733 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2734 style = gtk_widget_get_style (w);
2736 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2737 &style->bg[GTK_STATE_NORMAL],
2739 ? (gchar **) down_arrow_xpm
2740 : (gchar **) up_arrow_xpm));
2741 pixmapwid = gtk_pixmap_new (pixmap, mask);
2742 gtk_widget_show (pixmapwid);
2743 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2744 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2748 map_next_button_cb (GtkWidget *w, gpointer user_data)
2750 state *s = (state *) user_data;
2751 pixmapify_button (s, 1);
2755 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2757 state *s = (state *) user_data;
2758 pixmapify_button (s, 0);
2760 #endif /* !HAVE_GTK2 */
2763 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2767 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2768 GtkAllocation *allocation,
2772 GtkWidgetAuxInfo *aux_info;
2774 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2776 aux_info->width = allocation->width;
2777 aux_info->height = -2;
2781 gtk_widget_size_request (label, &req);
2785 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2788 eschew_gtk_lossage (GtkLabel *label)
2790 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2791 aux_info->width = GTK_WIDGET (label)->allocation.width;
2792 aux_info->height = -2;
2796 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2798 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2799 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2802 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2804 gtk_widget_queue_resize (GTK_WIDGET (label));
2809 populate_demo_window (state *s, int list_elt)
2811 saver_preferences *p = &s->prefs;
2814 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2815 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2816 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2817 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2818 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2820 if (p->mode == BLANK_ONLY)
2823 pretty_name = strdup (_("Blank Screen"));
2824 schedule_preview (s, 0);
2826 else if (p->mode == DONT_BLANK)
2829 pretty_name = strdup (_("Screen Saver Disabled"));
2830 schedule_preview (s, 0);
2834 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2835 ? s->list_elt_to_hack_number[list_elt]
2837 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2841 ? strdup (hack->name)
2842 : make_hack_name (hack->command))
2846 schedule_preview (s, hack->command);
2848 schedule_preview (s, 0);
2852 pretty_name = strdup (_("Preview"));
2854 gtk_frame_set_label (frame1, _(pretty_name));
2855 gtk_frame_set_label (frame2, _(pretty_name));
2857 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2858 gtk_entry_set_position (cmd, 0);
2862 sprintf (title, _("%s: %.100s Settings"),
2863 progclass, (pretty_name ? pretty_name : "???"));
2864 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2867 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2869 ? (hack->visual && *hack->visual
2874 sensitize_demo_widgets (s, (hack ? True : False));
2876 if (pretty_name) free (pretty_name);
2878 ensure_selected_item_visible (list);
2880 s->_selected_list_element = list_elt;
2885 widget_deleter (GtkWidget *widget, gpointer data)
2887 /* #### Well, I want to destroy these widgets, but if I do that, they get
2888 referenced again, and eventually I get a SEGV. So instead of
2889 destroying them, I'll just hide them, and leak a bunch of memory
2890 every time the disk file changes. Go go go Gtk!
2892 #### Ok, that's a lie, I get a crash even if I just hide the widget
2893 and don't ever delete it. Fuck!
2896 gtk_widget_destroy (widget);
2898 gtk_widget_hide (widget);
2903 static char **sort_hack_cmp_names_kludge;
2905 sort_hack_cmp (const void *a, const void *b)
2911 int aa = *(int *) a;
2912 int bb = *(int *) b;
2913 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
2914 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
2915 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
2921 initialize_sort_map (state *s)
2923 saver_preferences *p = &s->prefs;
2926 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2927 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2928 if (s->hacks_available_p) free (s->hacks_available_p);
2930 s->list_elt_to_hack_number = (int *)
2931 calloc (sizeof(int), p->screenhacks_count + 1);
2932 s->hack_number_to_list_elt = (int *)
2933 calloc (sizeof(int), p->screenhacks_count + 1);
2934 s->hacks_available_p = (Bool *)
2935 calloc (sizeof(Bool), p->screenhacks_count + 1);
2937 /* Check which hacks actually exist on $PATH
2939 for (i = 0; i < p->screenhacks_count; i++)
2941 screenhack *hack = p->screenhacks[i];
2942 s->hacks_available_p[i] = on_path_p (hack->command);
2945 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
2949 for (i = 0; i < p->screenhacks_count; i++)
2951 if (!p->ignore_uninstalled_p ||
2952 s->hacks_available_p[i])
2953 s->list_elt_to_hack_number[j++] = i;
2957 for (; j < p->screenhacks_count; j++)
2958 s->list_elt_to_hack_number[j] = -1;
2961 /* Generate list of sortable names (once)
2963 sort_hack_cmp_names_kludge = (char **)
2964 calloc (sizeof(char *), p->screenhacks_count);
2965 for (i = 0; i < p->screenhacks_count; i++)
2967 screenhack *hack = p->screenhacks[i];
2968 char *name = (hack->name && *hack->name
2969 ? strdup (hack->name)
2970 : make_hack_name (hack->command));
2972 for (str = name; *str; str++)
2973 *str = tolower(*str);
2974 sort_hack_cmp_names_kludge[i] = name;
2977 /* Sort list->hack map alphabetically
2979 qsort (s->list_elt_to_hack_number,
2980 p->screenhacks_count,
2981 sizeof(*s->list_elt_to_hack_number),
2986 for (i = 0; i < p->screenhacks_count; i++)
2987 free (sort_hack_cmp_names_kludge[i]);
2988 free (sort_hack_cmp_names_kludge);
2989 sort_hack_cmp_names_kludge = 0;
2991 /* Build inverse table */
2992 for (i = 0; i < p->screenhacks_count; i++)
2993 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2998 maybe_reload_init_file (state *s)
3000 saver_preferences *p = &s->prefs;
3003 static Bool reentrant_lock = False;
3004 if (reentrant_lock) return 0;
3005 reentrant_lock = True;
3007 if (init_file_changed_p (p))
3009 const char *f = init_file_name();
3014 if (!f || !*f) return 0;
3015 b = (char *) malloc (strlen(f) + 1024);
3018 "file \"%s\" has changed, reloading.\n"),
3020 warning_dialog (s->toplevel_widget, b, False, 100);
3024 initialize_sort_map (s);
3026 list_elt = selected_list_element (s);
3027 list = name_to_widget (s, "list");
3028 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3029 populate_hack_list (s);
3030 force_list_select_item (s, list, list_elt, True);
3031 populate_prefs_page (s);
3032 populate_demo_window (s, list_elt);
3033 ensure_selected_item_visible (list);
3038 reentrant_lock = False;
3044 /* Making the preview window have the right X visual (so that GL works.)
3047 static Visual *get_best_gl_visual (state *);
3050 x_visual_to_gdk_visual (Visual *xv)
3052 GList *gvs = gdk_list_visuals();
3053 if (!xv) return gdk_visual_get_system();
3054 for (; gvs; gvs = gvs->next)
3056 GdkVisual *gv = (GdkVisual *) gvs->data;
3057 if (xv == GDK_VISUAL_XVISUAL (gv))
3060 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3061 blurb(), (unsigned long) xv->visualid);
3066 clear_preview_window (state *s)
3071 if (!s->toplevel_widget) return; /* very early */
3072 p = name_to_widget (s, "preview");
3075 if (!window) return;
3077 /* Flush the widget background down into the window, in case a subproc
3079 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3080 gdk_window_clear (window);
3083 int list_elt = selected_list_element (s);
3084 int hack_number = (list_elt >= 0
3085 ? s->list_elt_to_hack_number[list_elt]
3087 Bool available_p = (hack_number >= 0
3088 ? s->hacks_available_p [hack_number]
3091 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3092 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3093 (s->running_preview_error_p
3094 ? (available_p ? 1 : 2)
3096 #else /* !HAVE_GTK2 */
3097 if (s->running_preview_error_p)
3099 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3100 const char * const lines2[] = { N_("Not"), N_("Installed") };
3101 int nlines = countof(lines1);
3102 int lh = p->style->font->ascent + p->style->font->descent;
3106 const char * const *lines = (available_p ? lines1 : lines2);
3108 gdk_window_get_size (window, &w, &h);
3109 y = (h - (lh * nlines)) / 2;
3110 y += p->style->font->ascent;
3111 for (i = 0; i < nlines; i++)
3113 int sw = gdk_string_width (p->style->font, _(lines[i]));
3114 int x = (w - sw) / 2;
3115 gdk_draw_string (window, p->style->font,
3116 p->style->fg_gc[GTK_STATE_NORMAL],
3121 #endif /* !HAVE_GTK2 */
3129 reset_preview_window (state *s)
3131 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3132 when you kill one and re-start another on the same window. So maybe
3133 it's best to just always destroy and recreate the preview window
3134 when changing hacks, instead of always trying to reuse the same one?
3136 GtkWidget *pr = name_to_widget (s, "preview");
3137 if (GTK_WIDGET_REALIZED (pr))
3139 Window oid = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3141 gtk_widget_hide (pr);
3142 gtk_widget_unrealize (pr);
3143 gtk_widget_realize (pr);
3144 gtk_widget_show (pr);
3145 id = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3147 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3155 fix_preview_visual (state *s)
3157 GtkWidget *widget = name_to_widget (s, "preview");
3158 Visual *xvisual = get_best_gl_visual (s);
3159 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3160 GdkVisual *dvisual = gdk_visual_get_system();
3161 GdkColormap *cmap = (visual == dvisual
3162 ? gdk_colormap_get_system ()
3163 : gdk_colormap_new (visual, False));
3166 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3167 (visual == dvisual ? "default" : "non-default"),
3168 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3170 if (!GTK_WIDGET_REALIZED (widget) ||
3171 gtk_widget_get_visual (widget) != visual)
3173 gtk_widget_unrealize (widget);
3174 gtk_widget_set_visual (widget, visual);
3175 gtk_widget_set_colormap (widget, cmap);
3176 gtk_widget_realize (widget);
3179 /* Set the Widget colors to be white-on-black. */
3181 GdkWindow *window = widget->window;
3182 GtkStyle *style = gtk_style_copy (widget->style);
3183 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3184 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3185 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3186 GdkGC *fgc = gdk_gc_new(window);
3187 GdkGC *bgc = gdk_gc_new(window);
3188 if (!gdk_color_white (cmap, fg)) abort();
3189 if (!gdk_color_black (cmap, bg)) abort();
3190 gdk_gc_set_foreground (fgc, fg);
3191 gdk_gc_set_background (fgc, bg);
3192 gdk_gc_set_foreground (bgc, bg);
3193 gdk_gc_set_background (bgc, fg);
3194 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3195 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3196 gtk_widget_set_style (widget, style);
3198 /* For debugging purposes, put a title on the window (so that
3199 it can be easily found in the output of "xwininfo -tree".)
3201 gdk_window_set_title (window, "Preview");
3204 gtk_widget_show (widget);
3212 subproc_pretty_name (state *s)
3214 if (s->running_preview_cmd)
3216 char *ps = strdup (s->running_preview_cmd);
3217 char *ss = strchr (ps, ' ');
3219 ss = strrchr (ps, '/');
3230 return strdup ("???");
3235 reap_zombies (state *s)
3237 int wait_status = 0;
3239 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3243 if (pid == s->running_preview_pid)
3245 char *ss = subproc_pretty_name (s);
3246 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3247 (unsigned long) pid, ss);
3251 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3252 (unsigned long) pid);
3258 /* Mostly lifted from driver/subprocs.c */
3260 get_best_gl_visual (state *s)
3262 Display *dpy = GDK_DISPLAY();
3271 av[ac++] = "xscreensaver-gl-helper";
3276 perror ("error creating pipe:");
3283 switch ((int) (forked = fork ()))
3287 sprintf (buf, "%s: couldn't fork", blurb());
3295 close (in); /* don't need this one */
3296 close (ConnectionNumber (dpy)); /* close display fd */
3298 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3300 perror ("could not dup() a new stdout:");
3304 execvp (av[0], av); /* shouldn't return. */
3306 if (errno != ENOENT)
3308 /* Ignore "no such file or directory" errors, unless verbose.
3309 Issue all other exec errors, though. */
3310 sprintf (buf, "%s: running %s", blurb(), av[0]);
3314 /* Note that one must use _exit() instead of exit() in procs forked
3315 off of Gtk programs -- Gtk installs an atexit handler that has a
3316 copy of the X connection (which we've already closed, for safety.)
3317 If one uses exit() instead of _exit(), then one sometimes gets a
3318 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3320 _exit (1); /* exits fork */
3326 int wait_status = 0;
3328 FILE *f = fdopen (in, "r");
3332 close (out); /* don't need this one */
3335 fgets (buf, sizeof(buf)-1, f);
3338 /* Wait for the child to die. */
3339 waitpid (-1, &wait_status, 0);
3341 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3347 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3353 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3355 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3356 blurb(), av[0], result);
3368 kill_preview_subproc (state *s, Bool reset_p)
3370 s->running_preview_error_p = False;
3373 clear_preview_window (s);
3375 if (s->subproc_check_timer_id)
3377 gtk_timeout_remove (s->subproc_check_timer_id);
3378 s->subproc_check_timer_id = 0;
3379 s->subproc_check_countdown = 0;
3382 if (s->running_preview_pid)
3384 int status = kill (s->running_preview_pid, SIGTERM);
3385 char *ss = subproc_pretty_name (s);
3392 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3393 blurb(), (unsigned long) s->running_preview_pid, ss);
3398 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3399 blurb(), (unsigned long) s->running_preview_pid, ss);
3403 else if (s->debug_p)
3404 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3405 (unsigned long) s->running_preview_pid, ss);
3408 s->running_preview_pid = 0;
3409 if (s->running_preview_cmd) free (s->running_preview_cmd);
3410 s->running_preview_cmd = 0;
3417 reset_preview_window (s);
3418 clear_preview_window (s);
3423 /* Immediately and unconditionally launches the given process,
3424 after appending the -window-id option; sets running_preview_pid.
3427 launch_preview_subproc (state *s)
3429 saver_preferences *p = &s->prefs;
3433 const char *cmd = s->desired_preview_cmd;
3435 GtkWidget *pr = name_to_widget (s, "preview");
3438 reset_preview_window (s);
3440 window = pr->window;
3442 s->running_preview_error_p = False;
3444 if (s->preview_suppressed_p)
3446 kill_preview_subproc (s, False);
3450 new_cmd = malloc (strlen (cmd) + 40);
3452 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3455 /* No window id? No command to run. */
3461 strcpy (new_cmd, cmd);
3462 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3466 kill_preview_subproc (s, False);
3469 s->running_preview_error_p = True;
3470 clear_preview_window (s);
3474 switch ((int) (forked = fork ()))
3479 sprintf (buf, "%s: couldn't fork", blurb());
3481 s->running_preview_error_p = True;
3487 close (ConnectionNumber (GDK_DISPLAY()));
3489 hack_subproc_environment (id, s->debug_p);
3491 usleep (250000); /* pause for 1/4th second before launching, to give
3492 the previous program time to die and flush its X
3493 buffer, so we don't get leftover turds on the
3496 exec_command (p->shell, new_cmd, p->nice_inferior);
3497 /* Don't bother printing an error message when we are unable to
3498 exec subprocesses; we handle that by polling the pid later.
3500 Note that one must use _exit() instead of exit() in procs forked
3501 off of Gtk programs -- Gtk installs an atexit handler that has a
3502 copy of the X connection (which we've already closed, for safety.)
3503 If one uses exit() instead of _exit(), then one sometimes gets a
3504 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3506 _exit (1); /* exits child fork */
3511 if (s->running_preview_cmd) free (s->running_preview_cmd);
3512 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3513 s->running_preview_pid = forked;
3517 char *ss = subproc_pretty_name (s);
3518 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3519 (unsigned long) forked, ss);
3526 schedule_preview_check (s);
3529 if (new_cmd) free (new_cmd);
3534 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3537 hack_environment (state *s)
3539 static const char *def_path =
3540 # ifdef DEFAULT_PATH_PREFIX
3541 DEFAULT_PATH_PREFIX;
3546 Display *dpy = GDK_DISPLAY();
3547 const char *odpy = DisplayString (dpy);
3548 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3549 strcpy (ndpy, "DISPLAY=");
3550 strcat (ndpy, odpy);
3555 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3557 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3558 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3559 So we must leak it (and/or the previous setting). Yay.
3562 if (def_path && *def_path)
3564 const char *opath = getenv("PATH");
3565 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3566 strcpy (npath, "PATH=");
3567 strcat (npath, def_path);
3568 strcat (npath, ":");
3569 strcat (npath, opath);
3573 /* do not free(npath) -- see above */
3576 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3582 hack_subproc_environment (Window preview_window_id, Bool debug_p)
3584 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
3585 necessary yet, but it will make programs work if we had invoked
3586 them with "-root" and not with "-window-id" -- which, of course,
3589 char *nssw = (char *) malloc (40);
3590 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
3592 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
3593 any more, right? It's not Posix, but everyone seems to have it. */
3598 fprintf (stderr, "%s: %s\n", blurb(), nssw);
3600 /* do not free(nssw) -- see above */
3604 /* Called from a timer:
3605 Launches the currently-chosen subprocess, if it's not already running.
3606 If there's a different process running, kills it.
3609 update_subproc_timer (gpointer data)
3611 state *s = (state *) data;
3612 if (! s->desired_preview_cmd)
3613 kill_preview_subproc (s, True);
3614 else if (!s->running_preview_cmd ||
3615 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3616 launch_preview_subproc (s);
3618 s->subproc_timer_id = 0;
3619 return FALSE; /* do not re-execute timer */
3623 /* Call this when you think you might want a preview process running.
3624 It will set a timer that will actually launch that program a second
3625 from now, if you haven't changed your mind (to avoid double-click
3626 spazzing, etc.) `cmd' may be null meaning "no process".
3629 schedule_preview (state *s, const char *cmd)
3631 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3636 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3638 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3641 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3642 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3644 if (s->subproc_timer_id)
3645 gtk_timeout_remove (s->subproc_timer_id);
3646 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3650 /* Called from a timer:
3651 Checks to see if the subproc that should be running, actually is.
3654 check_subproc_timer (gpointer data)
3656 state *s = (state *) data;
3657 Bool again_p = True;
3659 if (s->running_preview_error_p || /* already dead */
3660 s->running_preview_pid <= 0)
3668 status = kill (s->running_preview_pid, 0);
3669 if (status < 0 && errno == ESRCH)
3670 s->running_preview_error_p = True;
3674 char *ss = subproc_pretty_name (s);
3675 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3676 (unsigned long) s->running_preview_pid, ss,
3677 (s->running_preview_error_p ? "dead" : "alive"));
3681 if (s->running_preview_error_p)
3683 clear_preview_window (s);
3688 /* Otherwise, it's currently alive. We might be checking again, or we
3689 might be satisfied. */
3691 if (--s->subproc_check_countdown <= 0)
3695 return TRUE; /* re-execute timer */
3698 s->subproc_check_timer_id = 0;
3699 s->subproc_check_countdown = 0;
3700 return FALSE; /* do not re-execute timer */
3705 /* Call this just after launching a subprocess.
3706 This sets a timer that will, five times a second for two seconds,
3707 check whether the program is still running. The assumption here
3708 is that if the process didn't stay up for more than a couple of
3709 seconds, then either the program doesn't exist, or it doesn't
3710 take a -window-id argument.
3713 schedule_preview_check (state *s)
3719 fprintf (stderr, "%s: scheduling check\n", blurb());
3721 if (s->subproc_check_timer_id)
3722 gtk_timeout_remove (s->subproc_check_timer_id);
3723 s->subproc_check_timer_id =
3724 gtk_timeout_add (1000 / ticks,
3725 check_subproc_timer, (gpointer) s);
3726 s->subproc_check_countdown = ticks * seconds;
3731 screen_blanked_p (void)
3735 unsigned long nitems, bytesafter;
3736 unsigned char *dataP = 0;
3737 Display *dpy = GDK_DISPLAY();
3738 Bool blanked_p = False;
3740 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3741 XA_SCREENSAVER_STATUS,
3742 0, 3, False, XA_INTEGER,
3743 &type, &format, &nitems, &bytesafter,
3746 && type == XA_INTEGER
3750 Atom *data = (Atom *) dataP;
3751 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3754 if (dataP) XFree (dataP);
3759 /* Wake up every now and then and see if the screen is blanked.
3760 If it is, kill off the small-window demo -- no point in wasting
3761 cycles by running two screensavers at once...
3764 check_blanked_timer (gpointer data)
3766 state *s = (state *) data;
3767 Bool blanked_p = screen_blanked_p ();
3768 if (blanked_p && s->running_preview_pid)
3771 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3772 kill_preview_subproc (s, True);
3775 return True; /* re-execute timer */
3779 /* Setting window manager icon
3783 init_icon (GdkWindow *window)
3785 GdkBitmap *mask = 0;
3788 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3789 (gchar **) logo_50_xpm);
3791 gdk_window_set_icon (window, 0, pixmap, mask);
3795 /* The main demo-mode command loop.
3800 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3801 XrmRepresentation *type, XrmValue *value, XPointer closure)
3804 for (i = 0; quarks[i]; i++)
3806 if (bindings[i] == XrmBindTightly)
3807 fprintf (stderr, (i == 0 ? "" : "."));
3808 else if (bindings[i] == XrmBindLoosely)
3809 fprintf (stderr, "*");
3811 fprintf (stderr, " ??? ");
3812 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3815 fprintf (stderr, ": %s\n", (char *) value->addr);
3823 the_network_is_not_the_computer (state *s)
3825 Display *dpy = GDK_DISPLAY();
3826 char *rversion = 0, *ruser = 0, *rhost = 0;
3827 char *luser, *lhost;
3829 struct passwd *p = getpwuid (getuid ());
3830 const char *d = DisplayString (dpy);
3832 # if defined(HAVE_UNAME)
3834 if (uname (&uts) < 0)
3835 lhost = "<UNKNOWN>";
3837 lhost = uts.nodename;
3839 strcpy (lhost, getenv("SYS$NODE"));
3840 # else /* !HAVE_UNAME && !VMS */
3841 strcat (lhost, "<UNKNOWN>");
3842 # endif /* !HAVE_UNAME && !VMS */
3844 if (p && p->pw_name)
3849 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3851 /* Make a buffer that's big enough for a number of copies of all the
3852 strings, plus some. */
3853 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3854 (ruser ? strlen(ruser) : 0) +
3855 (rhost ? strlen(rhost) : 0) +
3862 if (!rversion || !*rversion)
3866 "The XScreenSaver daemon doesn't seem to be running\n"
3867 "on display \"%s\". Launch it now?"),
3870 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3872 /* Warn that the two processes are running as different users.
3876 "%s is running as user \"%s\" on host \"%s\".\n"
3877 "But the xscreensaver managing display \"%s\"\n"
3878 "is running as user \"%s\" on host \"%s\".\n"
3880 "Since they are different users, they won't be reading/writing\n"
3881 "the same ~/.xscreensaver file, so %s isn't\n"
3882 "going to work right.\n"
3884 "You should either re-run %s as \"%s\", or re-run\n"
3885 "xscreensaver as \"%s\".\n"
3887 "Restart the xscreensaver daemon now?\n"),
3888 progname, luser, lhost,
3890 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3892 progname, (ruser ? ruser : "???"),
3895 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3897 /* Warn that the two processes are running on different hosts.
3901 "%s is running as user \"%s\" on host \"%s\".\n"
3902 "But the xscreensaver managing display \"%s\"\n"
3903 "is running as user \"%s\" on host \"%s\".\n"
3905 "If those two machines don't share a file system (that is,\n"
3906 "if they don't see the same ~%s/.xscreensaver file) then\n"
3907 "%s won't work right.\n"
3909 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3910 progname, luser, lhost,
3912 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3917 else if (!!strcmp (rversion, s->short_version))
3919 /* Warn that the version numbers don't match.
3923 "This is %s version %s.\n"
3924 "But the xscreensaver managing display \"%s\"\n"
3925 "is version %s. This could cause problems.\n"
3927 "Restart the xscreensaver daemon now?\n"),
3928 progname, s->short_version,
3935 warning_dialog (s->toplevel_widget, msg, True, 1);
3937 if (rversion) free (rversion);
3938 if (ruser) free (ruser);
3939 if (rhost) free (rhost);
3944 /* We use this error handler so that X errors are preceeded by the name
3945 of the program that generated them.
3948 demo_ehandler (Display *dpy, XErrorEvent *error)
3950 state *s = global_state_kludge; /* I hate C so much... */
3951 fprintf (stderr, "\nX error in %s:\n", blurb());
3952 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3953 kill_preview_subproc (s, False);
3959 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3960 of the program that generated them; and also that we can ignore one
3961 particular bogus error message that Gdk madly spews.
3964 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3965 const gchar *message, gpointer user_data)
3967 /* Ignore the message "Got event for unknown window: 0x...".
3968 Apparently some events are coming in for the xscreensaver window
3969 (presumably reply events related to the ClientMessage) and Gdk
3970 feels the need to complain about them. So, just suppress any
3971 messages that look like that one.
3973 if (strstr (message, "unknown window"))
3976 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3977 (log_domain ? log_domain : progclass),
3978 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3979 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3980 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3981 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3982 log_level == G_LOG_LEVEL_INFO ? "info" :
3983 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3985 ((!*message || message[strlen(message)-1] != '\n')
3991 __extension__ /* shut up about "string length is greater than the length
3992 ISO C89 compilers are required to support" when including
3996 static char *defaults[] = {
3997 #include "XScreenSaver_ad.h"
4002 #ifdef HAVE_CRAPPLET
4003 static struct poptOption crapplet_options[] = {
4004 {NULL, '\0', 0, NULL, 0}
4006 #endif /* HAVE_CRAPPLET */
4009 const char *usage = "[--display dpy] [--prefs]"
4010 # ifdef HAVE_CRAPPLET
4013 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4016 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4018 state *s = (state *) user_data;
4019 Boolean oi = s->initializing_p;
4020 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4021 s->initializing_p = True;
4022 eschew_gtk_lossage (label);
4023 s->initializing_p = oi;
4029 print_widget_tree (GtkWidget *w, int depth)
4032 for (i = 0; i < depth; i++)
4033 fprintf (stderr, " ");
4034 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4036 if (GTK_IS_LIST (w))
4038 for (i = 0; i < depth+1; i++)
4039 fprintf (stderr, " ");
4040 fprintf (stderr, "...list kids...\n");
4042 else if (GTK_IS_CONTAINER (w))
4044 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4047 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4055 delayed_scroll_kludge (gpointer data)
4057 state *s = (state *) data;
4058 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4059 ensure_selected_item_visible (w);
4061 /* Oh, this is just fucking lovely, too. */
4062 w = GTK_WIDGET (name_to_widget (s, "preview"));
4063 gtk_widget_hide (w);
4064 gtk_widget_show (w);
4066 return FALSE; /* do not re-execute timer */
4072 create_xscreensaver_demo (void)
4076 nb = name_to_widget (global_state_kludge, "preview_notebook");
4077 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4079 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4083 create_xscreensaver_settings_dialog (void)
4087 box = name_to_widget (global_state_kludge, "dialog_action_area");
4089 w = name_to_widget (global_state_kludge, "adv_button");
4090 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4092 w = name_to_widget (global_state_kludge, "std_button");
4093 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4095 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4098 #endif /* HAVE_GTK2 */
4101 main (int argc, char **argv)
4105 saver_preferences *p;
4109 Widget toplevel_shell;
4110 char *real_progname = argv[0];
4113 Bool crapplet_p = False;
4117 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4118 textdomain (GETTEXT_PACKAGE);
4121 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4122 # else /* !HAVE_GTK2 */
4123 if (!setlocale (LC_ALL, ""))
4124 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4125 # endif /* !HAVE_GTK2 */
4127 #endif /* ENABLE_NLS */
4129 str = strrchr (real_progname, '/');
4130 if (str) real_progname = str+1;
4133 memset (s, 0, sizeof(*s));
4134 s->initializing_p = True;
4137 global_state_kludge = s; /* I hate C so much... */
4139 progname = real_progname;
4141 s->short_version = (char *) malloc (5);
4142 memcpy (s->short_version, screensaver_id + 17, 4);
4143 s->short_version [4] = 0;
4146 /* Register our error message logger for every ``log domain'' known.
4147 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4148 for all of the domains that seem to be in use.
4151 const char * const domains[] = { 0,
4152 "Gtk", "Gdk", "GLib", "GModule",
4153 "GThread", "Gnome", "GnomeUI" };
4154 for (i = 0; i < countof(domains); i++)
4155 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4158 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4161 const char *dir = DEFAULT_ICONDIR;
4162 if (*dir) add_pixmap_directory (dir);
4164 # endif /* !HAVE_GTK2 */
4165 #endif /* DEFAULT_ICONDIR */
4167 /* This is gross, but Gtk understands --display and not -display...
4169 for (i = 1; i < argc; i++)
4170 if (argv[i][0] && argv[i][1] &&
4171 !strncmp(argv[i], "-display", strlen(argv[i])))
4172 argv[i] = "--display";
4175 /* We need to parse this arg really early... Sigh. */
4176 for (i = 1; i < argc; i++)
4179 (!strcmp(argv[i], "--crapplet") ||
4180 !strcmp(argv[i], "--capplet")))
4182 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4185 for (j = i; j < argc; j++) /* remove it from the list */
4186 argv[j] = argv[j+1];
4188 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4189 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4191 fprintf (stderr, "%s: %s\n", real_progname, usage);
4193 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4196 (!strcmp(argv[i], "--debug") ||
4197 !strcmp(argv[i], "-debug") ||
4198 !strcmp(argv[i], "-d")))
4202 for (j = i; j < argc; j++) /* remove it from the list */
4203 argv[j] = argv[j+1];
4210 (!strcmp(argv[i], "-geometry") ||
4211 !strcmp(argv[i], "-geom") ||
4212 !strcmp(argv[i], "-geo") ||
4213 !strcmp(argv[i], "-g")))
4217 for (j = i; j < argc; j++) /* remove them from the list */
4218 argv[j] = argv[j+2];
4225 (!strcmp(argv[i], "--configdir")))
4229 hack_configuration_path = argv[i+1];
4230 for (j = i; j < argc; j++) /* remove them from the list */
4231 argv[j] = argv[j+2];
4235 if (0 != stat (hack_configuration_path, &st))
4238 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4242 else if (!S_ISDIR (st.st_mode))
4244 fprintf (stderr, "%s: not a directory: %s\n",
4245 blurb(), hack_configuration_path);
4253 fprintf (stderr, "%s: using config directory \"%s\"\n",
4254 progname, hack_configuration_path);
4257 /* Let Gtk open the X connection, then initialize Xt to use that
4258 same connection. Doctor Frankenstein would be proud.
4260 # ifdef HAVE_CRAPPLET
4263 GnomeClient *client;
4264 GnomeClientFlags flags = 0;
4266 int init_results = gnome_capplet_init ("screensaver-properties",
4268 argc, argv, NULL, 0, NULL);
4270 0 upon successful initialization;
4271 1 if --init-session-settings was passed on the cmdline;
4272 2 if --ignore was passed on the cmdline;
4275 So the 1 signifies just to init the settings, and quit, basically.
4276 (Meaning launch the xscreensaver daemon.)
4279 if (init_results < 0)
4282 g_error ("An initialization error occurred while "
4283 "starting xscreensaver-capplet.\n");
4285 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4286 real_progname, init_results);
4291 client = gnome_master_client ();
4294 flags = gnome_client_get_flags (client);
4296 if (flags & GNOME_CLIENT_IS_CONNECTED)
4299 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4300 gnome_client_get_id (client));
4303 char *session_args[20];
4305 session_args[i++] = real_progname;
4306 session_args[i++] = "--capplet";
4307 session_args[i++] = "--init-session-settings";
4308 session_args[i] = 0;
4309 gnome_client_set_priority (client, 20);
4310 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4311 gnome_client_set_restart_command (client, i, session_args);
4315 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4318 gnome_client_flush (client);
4321 if (init_results == 1)
4323 system ("xscreensaver -nosplash &");
4329 # endif /* HAVE_CRAPPLET */
4331 gtk_init (&argc, &argv);
4335 /* We must read exactly the same resources as xscreensaver.
4336 That means we must have both the same progclass *and* progname,
4337 at least as far as the resource database is concerned. So,
4338 put "xscreensaver" in argv[0] while initializing Xt.
4340 argv[0] = "xscreensaver";
4344 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4346 XtToolkitInitialize ();
4347 app = XtCreateApplicationContext ();
4348 dpy = GDK_DISPLAY();
4349 XtAppSetFallbackResources (app, defaults);
4350 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4351 toplevel_shell = XtAppCreateShell (progname, progclass,
4352 applicationShellWidgetClass,
4355 dpy = XtDisplay (toplevel_shell);
4356 db = XtDatabase (dpy);
4357 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4358 XSetErrorHandler (demo_ehandler);
4360 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4361 signal (SIGPIPE, SIG_IGN);
4363 /* After doing Xt-style command-line processing, complain about any
4364 unrecognized command-line arguments.
4366 for (i = 1; i < argc; i++)
4368 char *str = argv[i];
4369 if (str[0] == '-' && str[1] == '-')
4371 if (!strcmp (str, "-prefs"))
4373 else if (crapplet_p)
4374 /* There are lots of random args that we don't care about when we're
4375 started as a crapplet, so just ignore unknown args in that case. */
4379 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4381 fprintf (stderr, "%s: %s\n", real_progname, usage);
4386 /* Load the init file, which may end up consulting the X resource database
4387 and the site-wide app-defaults file. Note that at this point, it's
4388 important that `progname' be "xscreensaver", rather than whatever
4393 hack_environment (s); /* must be before initialize_sort_map() */
4396 initialize_sort_map (s);
4398 /* Now that Xt has been initialized, and the resources have been read,
4399 we can set our `progname' variable to something more in line with
4402 progname = real_progname;
4406 /* Print out all the resources we read. */
4408 XrmName name = { 0 };
4409 XrmClass class = { 0 };
4411 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4417 /* Intern the atoms that xscreensaver_command() needs.
4419 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4420 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4421 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4422 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4423 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4424 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4425 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4426 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4427 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4428 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4429 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4430 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4431 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4434 /* Create the window and all its widgets.
4436 s->base_widget = create_xscreensaver_demo ();
4437 s->popup_widget = create_xscreensaver_settings_dialog ();
4438 s->toplevel_widget = s->base_widget;
4441 /* Set the main window's title. */
4443 char *base_title = _("Screensaver Preferences");
4444 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4445 char *s1, *s2, *s3, *s4;
4446 s1 = (char *) strchr(v, ' '); s1++;
4447 s2 = (char *) strchr(s1, ' ');
4448 s3 = (char *) strchr(v, '('); s3++;
4449 s4 = (char *) strchr(s3, ')');
4453 window_title = (char *) malloc (strlen (base_title) +
4454 strlen (progclass) +
4455 strlen (s1) + strlen (s3) +
4457 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4458 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4459 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4463 /* Adjust the (invisible) notebooks on the popup dialog... */
4465 GtkNotebook *notebook =
4466 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4467 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4471 gtk_widget_hide (std);
4472 # else /* !HAVE_XML */
4473 /* Make the advanced page be the only one available. */
4474 gtk_widget_set_sensitive (std, False);
4475 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4476 gtk_widget_hide (std);
4478 # endif /* !HAVE_XML */
4480 gtk_notebook_set_page (notebook, page);
4481 gtk_notebook_set_show_tabs (notebook, False);
4484 /* Various other widget initializations...
4486 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4487 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4489 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4490 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4493 populate_hack_list (s);
4494 populate_prefs_page (s);
4495 sensitize_demo_widgets (s, False);
4496 fix_text_entry_sizes (s);
4497 scroll_to_current_hack (s);
4499 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4500 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4504 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4505 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4507 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4508 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4510 #endif /* !HAVE_GTK2 */
4512 /* Hook up callbacks to the items on the mode menu. */
4514 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4515 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4516 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4517 for (; kids; kids = kids->next)
4518 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4519 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4524 /* Handle the -prefs command-line argument. */
4527 GtkNotebook *notebook =
4528 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4529 gtk_notebook_set_page (notebook, 1);
4532 # ifdef HAVE_CRAPPLET
4536 GtkWidget *outer_vbox;
4538 gtk_widget_hide (s->toplevel_widget);
4540 capplet = capplet_widget_new ();
4542 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4543 # ifdef HAVE_CRAPPLET_IMMEDIATE
4544 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4545 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4546 /* In crapplet-mode, take off the menubar. */
4547 gtk_widget_hide (name_to_widget (s, "menubar"));
4549 /* Reparent our top-level container to be a child of the capplet
4552 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4553 gtk_widget_ref (outer_vbox);
4554 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4556 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4557 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4559 /* Find the window above us, and set the title and close handler. */
4561 GtkWidget *window = capplet;
4562 while (window && !GTK_IS_WINDOW (window))
4563 window = window->parent;
4566 gtk_window_set_title (GTK_WINDOW (window), window_title);
4567 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4568 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4573 s->toplevel_widget = capplet;
4575 # endif /* HAVE_CRAPPLET */
4578 /* The Gnome folks hate the menubar. I think it's important to have access
4579 to the commands on the File menu (Restart Daemon, etc.) and to the
4580 About and Documentation commands on the Help menu.
4584 gtk_widget_hide (name_to_widget (s, "menubar"));
4588 free (window_title);
4592 /* After picking the default size, allow -geometry to override it. */
4594 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
4597 gtk_widget_show (s->toplevel_widget);
4598 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4599 fix_preview_visual (s);
4601 /* Realize page zero, so that we can diddle the scrollbar when the
4602 user tabs back to it -- otherwise, the current hack isn't scrolled
4603 to the first time they tab back there, when started with "-prefs".
4604 (Though it is if they then tab away, and back again.)
4606 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4607 #### understands this crap, explain to me how to make this work.
4609 gtk_widget_realize (name_to_widget (s, "demos_table"));
4612 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4615 /* Issue any warnings about the running xscreensaver daemon. */
4616 the_network_is_not_the_computer (s);
4619 /* Run the Gtk event loop, and not the Xt event loop. This means that
4620 if there were Xt timers or fds registered, they would never get serviced,
4621 and if there were any Xt widgets, they would never have events delivered.
4622 Fortunately, we're using Gtk for all of the UI, and only initialized
4623 Xt so that we could process the command line and use the X resource
4626 s->initializing_p = False;
4628 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4629 after we start up. Otherwise, it always appears scrolled to the top
4630 when in crapplet-mode. */
4631 gtk_timeout_add (500, delayed_scroll_kludge, s);
4635 /* Load every configurator in turn, to scan them for errors all at once. */
4638 for (i = 0; i < p->screenhacks_count; i++)
4640 screenhack *hack = p->screenhacks[i];
4641 conf_data *d = load_configurator (hack->command, False);
4642 if (d) free_conf_data (d);
4648 # ifdef HAVE_CRAPPLET
4650 capplet_gtk_main ();
4652 # endif /* HAVE_CRAPPLET */
4655 kill_preview_subproc (s, False);
4659 #endif /* HAVE_GTK -- whole file */