1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2005 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-2005 %s"), s);
669 #else /* !HAVE_GTK2 */
670 sprintf(copy, ("Copyright \251 1991-2005 %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) * 4) + 20);
793 strcpy (help_command, "( ");
794 sprintf (help_command + strlen(help_command),
796 p->help_url, p->help_url, p->help_url, p->help_url);
797 strcat (help_command, " ) &");
798 system (help_command);
804 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
806 state *s = global_state_kludge; /* I hate C so much... */
807 run_cmd (s, XA_ACTIVATE, 0);
812 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
814 state *s = global_state_kludge; /* I hate C so much... */
815 run_cmd (s, XA_LOCK, 0);
820 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
822 state *s = global_state_kludge; /* I hate C so much... */
823 run_cmd (s, XA_EXIT, 0);
828 restart_menu_cb (GtkWidget *widget, gpointer user_data)
830 state *s = global_state_kludge; /* I hate C so much... */
831 flush_dialog_changes_and_save (s);
832 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
834 system ("xscreensaver -nosplash &");
836 await_xscreensaver (s);
840 await_xscreensaver (state *s)
844 Display *dpy = GDK_DISPLAY();
845 /* GtkWidget *dialog = 0;*/
848 while (!rversion && (--countdown > 0))
850 /* Check for the version of the running xscreensaver... */
851 server_xscreensaver_version (dpy, &rversion, 0, 0);
853 /* If it's not there yet, wait a second... */
858 /* if (dialog) gtk_widget_destroy (dialog);*/
867 /* Timed out, no screensaver running. */
870 Bool root_p = (geteuid () == 0);
874 "The xscreensaver daemon did not start up properly.\n"
880 __extension__ /* don't warn about "string length is greater than
881 the length ISO C89 compilers are required to
882 support" in the following expression... */
885 _("You are running as root. This usually means that xscreensaver\n"
886 "was unable to contact your X server because access control is\n"
887 "turned on. Try running this command:\n"
889 " xhost +localhost\n"
891 "and then selecting `File / Restart Daemon'.\n"
893 "Note that turning off access control will allow anyone logged\n"
894 "on to this machine to access your screen, which might be\n"
895 "considered a security problem. Please read the xscreensaver\n"
896 "manual and FAQ for more information.\n"
898 "You shouldn't run X as root. Instead, you should log in as a\n"
899 "normal user, and `su' as necessary."));
901 strcat (buf, _("Please check your $PATH and permissions."));
903 warning_dialog (s->toplevel_widget, buf, False, 1);
909 selected_list_element (state *s)
911 return s->_selected_list_element;
916 demo_write_init_file (state *s, saver_preferences *p)
920 /* #### try to figure out why shit keeps getting reordered... */
921 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
925 if (!write_init_file (p, s->short_version, False))
928 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
933 const char *f = init_file_name();
935 warning_dialog (s->toplevel_widget,
936 _("Error:\n\nCouldn't determine init file name!\n"),
940 char *b = (char *) malloc (strlen(f) + 1024);
941 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
942 warning_dialog (s->toplevel_widget, b, False, 100);
951 run_this_cb (GtkButton *button, gpointer user_data)
953 state *s = global_state_kludge; /* I hate C so much... */
954 int list_elt = selected_list_element (s);
955 if (list_elt < 0) return;
956 if (!flush_dialog_changes_and_save (s))
957 run_hack (s, list_elt, True);
962 manual_cb (GtkButton *button, gpointer user_data)
964 state *s = global_state_kludge; /* I hate C so much... */
965 saver_preferences *p = &s->prefs;
966 GtkWidget *list_widget = name_to_widget (s, "list");
967 int list_elt = selected_list_element (s);
969 char *name, *name2, *cmd, *str;
970 if (list_elt < 0) return;
971 hack_number = s->list_elt_to_hack_number[list_elt];
973 flush_dialog_changes_and_save (s);
974 ensure_selected_item_visible (list_widget);
976 name = strdup (p->screenhacks[hack_number]->command);
978 while (isspace (*name2)) name2++;
980 while (*str && !isspace (*str)) str++;
982 str = strrchr (name2, '/');
983 if (str) name = str+1;
985 cmd = get_string_resource ("manualCommand", "ManualCommand");
988 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
990 sprintf (cmd2 + strlen (cmd2),
992 name2, name2, name2, name2);
993 strcat (cmd2, " ) &");
999 warning_dialog (GTK_WIDGET (button),
1000 _("Error:\n\nno `manualCommand' resource set."),
1009 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1011 GtkWidget *parent = name_to_widget (s, "scroller");
1012 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
1015 GtkTreeModel *model;
1016 GtkTreeSelection *selection;
1017 #endif /* HAVE_GTK2 */
1019 if (!was) gtk_widget_set_sensitive (parent, True);
1021 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1022 STFU g_assert (model);
1023 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
1024 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1025 gtk_tree_selection_select_iter (selection, &iter);
1026 #else /* !HAVE_GTK2 */
1027 gtk_list_select_item (GTK_LIST (list), list_elt);
1028 #endif /* !HAVE_GTK2 */
1029 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1030 if (!was) gtk_widget_set_sensitive (parent, False);
1034 G_MODULE_EXPORT void
1035 run_next_cb (GtkButton *button, gpointer user_data)
1037 state *s = global_state_kludge; /* I hate C so much... */
1038 /* saver_preferences *p = &s->prefs; */
1039 Bool ops = s->preview_suppressed_p;
1041 GtkWidget *list_widget = name_to_widget (s, "list");
1042 int list_elt = selected_list_element (s);
1049 if (list_elt >= s->list_count)
1052 s->preview_suppressed_p = True;
1054 flush_dialog_changes_and_save (s);
1055 force_list_select_item (s, list_widget, list_elt, True);
1056 populate_demo_window (s, list_elt);
1057 run_hack (s, list_elt, False);
1059 s->preview_suppressed_p = ops;
1063 G_MODULE_EXPORT void
1064 run_prev_cb (GtkButton *button, gpointer user_data)
1066 state *s = global_state_kludge; /* I hate C so much... */
1067 /* saver_preferences *p = &s->prefs; */
1068 Bool ops = s->preview_suppressed_p;
1070 GtkWidget *list_widget = name_to_widget (s, "list");
1071 int list_elt = selected_list_element (s);
1074 list_elt = s->list_count - 1;
1079 list_elt = s->list_count - 1;
1081 s->preview_suppressed_p = True;
1083 flush_dialog_changes_and_save (s);
1084 force_list_select_item (s, list_widget, list_elt, True);
1085 populate_demo_window (s, list_elt);
1086 run_hack (s, list_elt, False);
1088 s->preview_suppressed_p = ops;
1092 /* Writes the given settings into prefs.
1093 Returns true if there was a change, False otherwise.
1094 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1097 flush_changes (state *s,
1100 const char *command,
1103 saver_preferences *p = &s->prefs;
1104 Bool changed = False;
1107 if (list_elt < 0 || list_elt >= s->list_count)
1110 hack_number = s->list_elt_to_hack_number[list_elt];
1111 hack = p->screenhacks[hack_number];
1113 if (enabled_p != -1 &&
1114 enabled_p != hack->enabled_p)
1116 hack->enabled_p = enabled_p;
1119 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1120 blurb(), hack->name, enabled_p);
1125 if (!hack->command || !!strcmp (command, hack->command))
1127 if (hack->command) free (hack->command);
1128 hack->command = strdup (command);
1131 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1132 blurb(), hack->name, command);
1138 const char *ov = hack->visual;
1139 if (!ov || !*ov) ov = "any";
1140 if (!*visual) visual = "any";
1141 if (!!strcasecmp (visual, ov))
1143 if (hack->visual) free (hack->visual);
1144 hack->visual = strdup (visual);
1147 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1148 blurb(), hack->name, visual);
1156 /* Helper for the text fields that contain time specifications:
1157 this parses the text, and does error checking.
1160 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1165 if (!sec_p || strchr (line, ':'))
1166 value = parse_time ((char *) line, sec_p, True);
1170 if (sscanf (line, "%d%c", &value, &c) != 1)
1176 value *= 1000; /* Time measures in microseconds */
1182 "Unparsable time format: \"%s\"\n"),
1184 warning_dialog (s->toplevel_widget, b, False, 100);
1193 directory_p (const char *path)
1196 if (!path || !*path)
1198 else if (stat (path, &st))
1200 else if (!S_ISDIR (st.st_mode))
1207 normalize_directory (const char *path)
1211 if (!path || !*path) return 0;
1213 p2 = (char *) malloc (L + 2);
1215 if (p2[L-1] == '/') /* remove trailing slash */
1218 for (s = p2; s && *s; s++)
1221 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1222 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1225 while (s0 > p2 && s0[-1] != '/')
1235 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1236 strcpy (s, s+2), s--;
1237 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1241 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1242 while (s[0] == '/' && s[1] == '/')
1245 /* and strip trailing whitespace for good measure. */
1247 while (isspace(p2[L-1]))
1260 } FlushForeachClosure;
1263 flush_checkbox (GtkTreeModel *model,
1268 FlushForeachClosure *closure = data;
1271 gtk_tree_model_get (model, iter,
1272 COL_ENABLED, &checked,
1275 if (flush_changes (closure->s, closure->i,
1277 *closure->changed = True;
1281 /* don't remove row */
1285 #endif /* HAVE_GTK2 */
1287 /* Flush out any changes made in the main dialog window (where changes
1288 take place immediately: clicking on a checkbox causes the init file
1289 to be written right away.)
1292 flush_dialog_changes_and_save (state *s)
1294 saver_preferences *p = &s->prefs;
1295 saver_preferences P2, *p2 = &P2;
1297 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1298 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1299 FlushForeachClosure closure;
1300 #else /* !HAVE_GTK2 */
1301 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1302 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1304 #endif /* !HAVE_GTK2 */
1306 Bool changed = False;
1309 if (s->saving_p) return False;
1314 /* Flush any checkbox changes in the list down into the prefs struct.
1318 closure.changed = &changed;
1320 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1322 #else /* !HAVE_GTK2 */
1324 for (i = 0; kids; kids = kids->next, i++)
1326 GtkWidget *line = GTK_WIDGET (kids->data);
1327 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1328 GtkWidget *line_check =
1329 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1331 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1333 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1336 #endif /* ~HAVE_GTK2 */
1338 /* Flush the non-hack-specific settings down into the prefs struct.
1341 # define SECONDS(FIELD,NAME) \
1342 w = name_to_widget (s, (NAME)); \
1343 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1345 # define MINUTES(FIELD,NAME) \
1346 w = name_to_widget (s, (NAME)); \
1347 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1349 # define CHECKBOX(FIELD,NAME) \
1350 w = name_to_widget (s, (NAME)); \
1351 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1353 # define PATHNAME(FIELD,NAME) \
1354 w = name_to_widget (s, (NAME)); \
1355 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1357 MINUTES (&p2->timeout, "timeout_spinbutton");
1358 MINUTES (&p2->cycle, "cycle_spinbutton");
1359 CHECKBOX (p2->lock_p, "lock_button");
1360 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1362 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1363 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1364 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1365 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1367 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1368 CHECKBOX (p2->grab_video_p, "grab_video_button");
1369 CHECKBOX (p2->random_image_p, "grab_image_button");
1370 PATHNAME (p2->image_directory, "image_text");
1372 CHECKBOX (p2->verbose_p, "verbose_button");
1373 CHECKBOX (p2->capture_stderr_p, "capture_button");
1374 CHECKBOX (p2->splash_p, "splash_button");
1376 CHECKBOX (p2->install_cmap_p, "install_button");
1377 CHECKBOX (p2->fade_p, "fade_button");
1378 CHECKBOX (p2->unfade_p, "unfade_button");
1379 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1386 /* Warn if the image directory doesn't exist.
1388 if (p2->image_directory &&
1389 *p2->image_directory &&
1390 !directory_p (p2->image_directory))
1393 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1394 p2->image_directory);
1395 warning_dialog (s->toplevel_widget, b, False, 100);
1399 /* Map the mode menu to `saver_mode' enum values. */
1401 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1402 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1403 GtkWidget *selected = gtk_menu_get_active (menu);
1404 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1405 int menu_elt = g_list_index (kids, (gpointer) selected);
1406 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1407 p2->mode = mode_menu_order[menu_elt];
1410 if (p2->mode == ONE_HACK)
1412 int list_elt = selected_list_element (s);
1413 p2->selected_hack = (list_elt >= 0
1414 ? s->list_elt_to_hack_number[list_elt]
1418 # define COPY(field, name) \
1419 if (p->field != p2->field) { \
1422 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1424 p->field = p2->field
1427 COPY(selected_hack, "selected_hack");
1429 COPY(timeout, "timeout");
1430 COPY(cycle, "cycle");
1431 COPY(lock_p, "lock_p");
1432 COPY(lock_timeout, "lock_timeout");
1434 COPY(dpms_enabled_p, "dpms_enabled_p");
1435 COPY(dpms_standby, "dpms_standby");
1436 COPY(dpms_suspend, "dpms_suspend");
1437 COPY(dpms_off, "dpms_off");
1439 COPY(verbose_p, "verbose_p");
1440 COPY(capture_stderr_p, "capture_stderr_p");
1441 COPY(splash_p, "splash_p");
1443 COPY(install_cmap_p, "install_cmap_p");
1444 COPY(fade_p, "fade_p");
1445 COPY(unfade_p, "unfade_p");
1446 COPY(fade_seconds, "fade_seconds");
1448 COPY(grab_desktop_p, "grab_desktop_p");
1449 COPY(grab_video_p, "grab_video_p");
1450 COPY(random_image_p, "random_image_p");
1454 if (!p->image_directory ||
1455 !p2->image_directory ||
1456 strcmp(p->image_directory, p2->image_directory))
1460 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1461 blurb(), p2->image_directory);
1463 if (p->image_directory && p->image_directory != p2->image_directory)
1464 free (p->image_directory);
1465 p->image_directory = p2->image_directory;
1466 p2->image_directory = 0;
1468 populate_prefs_page (s);
1472 Display *dpy = GDK_DISPLAY();
1473 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1474 sync_server_dpms_settings (dpy, enabled_p,
1475 p->dpms_standby / 1000,
1476 p->dpms_suspend / 1000,
1480 changed = demo_write_init_file (s, p);
1483 s->saving_p = False;
1488 /* Flush out any changes made in the popup dialog box (where changes
1489 take place only when the OK button is clicked.)
1492 flush_popup_changes_and_save (state *s)
1494 Bool changed = False;
1495 saver_preferences *p = &s->prefs;
1496 int list_elt = selected_list_element (s);
1498 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1499 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1501 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1502 const char *command = gtk_entry_get_text (cmd);
1507 if (s->saving_p) return False;
1513 if (maybe_reload_init_file (s) != 0)
1519 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1521 if (!strcasecmp (visual, "")) visual = "";
1522 else if (!strcasecmp (visual, "any")) visual = "";
1523 else if (!strcasecmp (visual, "default")) visual = "Default";
1524 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1525 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1526 else if (!strcasecmp (visual, "best")) visual = "Best";
1527 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1528 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1529 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1530 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1531 else if (!strcasecmp (visual, "color")) visual = "Color";
1532 else if (!strcasecmp (visual, "gl")) visual = "GL";
1533 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1534 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1535 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1536 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1537 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1538 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1539 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1540 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1541 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1544 gdk_beep (); /* unparsable */
1546 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1549 changed = flush_changes (s, list_elt, -1, command, visual);
1552 changed = demo_write_init_file (s, p);
1554 /* Do this to re-launch the hack if (and only if) the command line
1556 populate_demo_window (s, selected_list_element (s));
1560 s->saving_p = False;
1565 G_MODULE_EXPORT void
1566 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1568 state *s = global_state_kludge; /* I hate C so much... */
1569 if (! s->initializing_p)
1571 s->initializing_p = True;
1572 flush_dialog_changes_and_save (s);
1573 s->initializing_p = False;
1577 G_MODULE_EXPORT gboolean
1578 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1580 pref_changed_cb (widget, user_data);
1584 /* Callback on menu items in the "mode" options menu.
1587 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1589 state *s = (state *) user_data;
1590 saver_preferences *p = &s->prefs;
1591 GtkWidget *list = name_to_widget (s, "list");
1594 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1596 saver_mode new_mode;
1600 if (menu_items->data == widget)
1603 menu_items = menu_items->next;
1605 if (!menu_items) abort();
1607 new_mode = mode_menu_order[menu_index];
1609 /* Keep the same list element displayed as before; except if we're
1610 switching *to* "one screensaver" mode from any other mode, set
1611 "the one" to be that which is currently selected.
1613 list_elt = selected_list_element (s);
1614 if (new_mode == ONE_HACK)
1615 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1618 saver_mode old_mode = p->mode;
1620 populate_demo_window (s, list_elt);
1621 force_list_select_item (s, list, list_elt, True);
1622 p->mode = old_mode; /* put it back, so the init file gets written */
1625 pref_changed_cb (widget, user_data);
1629 G_MODULE_EXPORT void
1630 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1631 gint page_num, gpointer user_data)
1633 state *s = global_state_kludge; /* I hate C so much... */
1634 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1636 /* If we're switching to page 0, schedule the current hack to be run.
1637 Otherwise, schedule it to stop. */
1639 populate_demo_window (s, selected_list_element (s));
1641 schedule_preview (s, 0);
1646 list_activated_cb (GtkTreeView *list,
1648 GtkTreeViewColumn *column,
1655 STFU g_return_if_fail (!gdk_pointer_is_grabbed ());
1657 str = gtk_tree_path_to_string (path);
1658 list_elt = strtol (str, NULL, 10);
1662 run_hack (s, list_elt, True);
1666 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1668 state *s = (state *)data;
1669 GtkTreeModel *model;
1675 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1678 path = gtk_tree_model_get_path (model, &iter);
1679 str = gtk_tree_path_to_string (path);
1680 list_elt = strtol (str, NULL, 10);
1682 gtk_tree_path_free (path);
1685 populate_demo_window (s, list_elt);
1686 flush_dialog_changes_and_save (s);
1689 #else /* !HAVE_GTK2 */
1691 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1692 list_select_cb that comes in
1693 *after* we've double-clicked.
1697 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1700 state *s = (state *) data;
1701 if (event->type == GDK_2BUTTON_PRESS)
1703 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1704 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1706 last_doubleclick_time = time ((time_t *) 0);
1709 run_hack (s, list_elt, True);
1717 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1719 state *s = (state *) data;
1720 time_t now = time ((time_t *) 0);
1722 if (now >= last_doubleclick_time + 2)
1724 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1725 populate_demo_window (s, list_elt);
1726 flush_dialog_changes_and_save (s);
1731 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1733 state *s = (state *) data;
1734 populate_demo_window (s, -1);
1735 flush_dialog_changes_and_save (s);
1738 #endif /* !HAVE_GTK2 */
1741 /* Called when the checkboxes that are in the left column of the
1742 scrolling list are clicked. This both populates the right pane
1743 (just as clicking on the label (really, listitem) does) and
1744 also syncs this checkbox with the right pane Enabled checkbox.
1749 GtkCellRendererToggle *toggle,
1751 #else /* !HAVE_GTK2 */
1753 #endif /* !HAVE_GTK2 */
1756 state *s = (state *) data;
1759 GtkScrolledWindow *scroller =
1760 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1761 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1762 GtkTreeModel *model = gtk_tree_view_get_model (list);
1763 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1766 #else /* !HAVE_GTK2 */
1767 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1768 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1770 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1771 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1772 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1773 #endif /* !HAVE_GTK2 */
1780 if (!gtk_tree_model_get_iter (model, &iter, path))
1782 g_warning ("bad path: %s", path_string);
1785 gtk_tree_path_free (path);
1787 gtk_tree_model_get (model, &iter,
1788 COL_ENABLED, &active,
1791 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1792 COL_ENABLED, !active,
1795 list_elt = strtol (path_string, NULL, 10);
1796 #else /* !HAVE_GTK2 */
1797 list_elt = gtk_list_child_position (list, line);
1798 #endif /* !HAVE_GTK2 */
1800 /* remember previous scroll position of the top of the list */
1801 adj = gtk_scrolled_window_get_vadjustment (scroller);
1802 scroll_top = adj->value;
1804 flush_dialog_changes_and_save (s);
1805 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1806 populate_demo_window (s, list_elt);
1808 /* restore the previous scroll position of the top of the list.
1809 this is weak, but I don't really know why it's moving... */
1810 gtk_adjustment_set_value (adj, scroll_top);
1816 GtkFileSelection *widget;
1817 } file_selection_data;
1822 store_image_directory (GtkWidget *button, gpointer user_data)
1824 file_selection_data *fsd = (file_selection_data *) user_data;
1825 state *s = fsd->state;
1826 GtkFileSelection *selector = fsd->widget;
1827 GtkWidget *top = s->toplevel_widget;
1828 saver_preferences *p = &s->prefs;
1829 const char *path = gtk_file_selection_get_filename (selector);
1831 if (p->image_directory && !strcmp(p->image_directory, path))
1832 return; /* no change */
1834 if (!directory_p (path))
1837 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1838 warning_dialog (GTK_WIDGET (top), b, False, 100);
1842 if (p->image_directory) free (p->image_directory);
1843 p->image_directory = normalize_directory (path);
1845 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1846 (p->image_directory ? p->image_directory : ""));
1847 demo_write_init_file (s, p);
1852 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1854 file_selection_data *fsd = (file_selection_data *) user_data;
1855 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1859 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1861 browse_image_dir_cancel (button, user_data);
1862 store_image_directory (button, user_data);
1866 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1868 browse_image_dir_cancel (widget, user_data);
1872 G_MODULE_EXPORT void
1873 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1875 state *s = global_state_kludge; /* I hate C so much... */
1876 saver_preferences *p = &s->prefs;
1877 static file_selection_data *fsd = 0;
1879 GtkFileSelection *selector = GTK_FILE_SELECTION(
1880 gtk_file_selection_new ("Please select the image directory."));
1883 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1885 fsd->widget = selector;
1888 if (p->image_directory && *p->image_directory)
1889 gtk_file_selection_set_filename (selector, p->image_directory);
1891 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1892 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1894 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1895 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1897 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1898 GTK_SIGNAL_FUNC (browse_image_dir_close),
1901 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1903 gtk_window_set_modal (GTK_WINDOW (selector), True);
1904 gtk_widget_show (GTK_WIDGET (selector));
1908 G_MODULE_EXPORT void
1909 settings_cb (GtkButton *button, gpointer user_data)
1911 state *s = global_state_kludge; /* I hate C so much... */
1912 int list_elt = selected_list_element (s);
1914 populate_demo_window (s, list_elt); /* reset the widget */
1915 populate_popup_window (s); /* create UI on popup window */
1916 gtk_widget_show (s->popup_widget);
1920 settings_sync_cmd_text (state *s)
1923 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1924 char *cmd_line = get_configurator_command_line (s->cdata);
1925 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1926 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1928 # endif /* HAVE_XML */
1931 G_MODULE_EXPORT void
1932 settings_adv_cb (GtkButton *button, gpointer user_data)
1934 state *s = global_state_kludge; /* I hate C so much... */
1935 GtkNotebook *notebook =
1936 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1938 settings_sync_cmd_text (s);
1939 gtk_notebook_set_page (notebook, 1);
1942 G_MODULE_EXPORT void
1943 settings_std_cb (GtkButton *button, gpointer user_data)
1945 state *s = global_state_kludge; /* I hate C so much... */
1946 GtkNotebook *notebook =
1947 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1949 /* Re-create UI to reflect the in-progress command-line settings. */
1950 populate_popup_window (s);
1952 gtk_notebook_set_page (notebook, 0);
1955 G_MODULE_EXPORT void
1956 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1957 gint page_num, gpointer user_data)
1959 state *s = global_state_kludge; /* I hate C so much... */
1960 GtkWidget *adv = name_to_widget (s, "adv_button");
1961 GtkWidget *std = name_to_widget (s, "std_button");
1965 gtk_widget_show (adv);
1966 gtk_widget_hide (std);
1968 else if (page_num == 1)
1970 gtk_widget_hide (adv);
1971 gtk_widget_show (std);
1979 G_MODULE_EXPORT void
1980 settings_cancel_cb (GtkButton *button, gpointer user_data)
1982 state *s = global_state_kludge; /* I hate C so much... */
1983 gtk_widget_hide (s->popup_widget);
1986 G_MODULE_EXPORT void
1987 settings_ok_cb (GtkButton *button, gpointer user_data)
1989 state *s = global_state_kludge; /* I hate C so much... */
1990 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1991 int page = gtk_notebook_get_current_page (notebook);
1994 /* Regenerate the command-line from the widget contents before saving.
1995 But don't do this if we're looking at the command-line page already,
1996 or we will blow away what they typed... */
1997 settings_sync_cmd_text (s);
1999 flush_popup_changes_and_save (s);
2000 gtk_widget_hide (s->popup_widget);
2004 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2006 state *s = (state *) data;
2007 settings_cancel_cb (0, (gpointer) s);
2013 /* Populating the various widgets
2017 /* Returns the number of the last hack run by the server.
2020 server_current_hack (void)
2024 unsigned long nitems, bytesafter;
2025 unsigned char *dataP = 0;
2026 Display *dpy = GDK_DISPLAY();
2027 int hack_number = -1;
2029 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2030 XA_SCREENSAVER_STATUS,
2031 0, 3, False, XA_INTEGER,
2032 &type, &format, &nitems, &bytesafter,
2035 && type == XA_INTEGER
2039 CARD32 *data = (CARD32 *) dataP;
2040 hack_number = (int) data[2] - 1;
2043 if (dataP) XFree (dataP);
2049 /* Finds the number of the last hack to run, and makes that item be
2050 selected by default.
2053 scroll_to_current_hack (state *s)
2055 saver_preferences *p = &s->prefs;
2058 if (p->mode == ONE_HACK)
2059 hack_number = p->selected_hack;
2061 hack_number = server_current_hack ();
2063 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2065 int list_elt = s->hack_number_to_list_elt[hack_number];
2066 GtkWidget *list = name_to_widget (s, "list");
2067 force_list_select_item (s, list, list_elt, True);
2068 populate_demo_window (s, list_elt);
2074 on_path_p (const char *program)
2078 char *cmd = strdup (program);
2079 char *token = strchr (cmd, ' ');
2083 if (token) *token = 0;
2086 if (strchr (cmd, '/'))
2088 result = (0 == stat (cmd, &st));
2092 path = getenv("PATH");
2093 if (!path || !*path)
2097 path = strdup (path);
2098 token = strtok (path, ":");
2102 char *p2 = (char *) malloc (strlen (token) + L + 3);
2106 result = (0 == stat (p2, &st));
2109 token = strtok (0, ":");
2114 if (path) free (path);
2120 populate_hack_list (state *s)
2123 saver_preferences *p = &s->prefs;
2124 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2125 GtkListStore *model;
2126 GtkTreeSelection *selection;
2127 GtkCellRenderer *ren;
2131 g_object_get (G_OBJECT (list),
2136 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2137 g_object_set (G_OBJECT (list), "model", model, NULL);
2138 g_object_unref (model);
2140 ren = gtk_cell_renderer_toggle_new ();
2141 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2143 "active", COL_ENABLED,
2146 g_signal_connect (ren, "toggled",
2147 G_CALLBACK (list_checkbox_cb),
2150 ren = gtk_cell_renderer_text_new ();
2151 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2152 _("Screen Saver"), ren,
2156 g_signal_connect_after (list, "row_activated",
2157 G_CALLBACK (list_activated_cb),
2160 selection = gtk_tree_view_get_selection (list);
2161 g_signal_connect (selection, "changed",
2162 G_CALLBACK (list_select_changed_cb),
2167 for (i = 0; i < s->list_count; i++)
2169 int hack_number = s->list_elt_to_hack_number[i];
2170 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2172 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2174 if (!hack) continue;
2176 /* If we're to suppress uninstalled hacks, check $PATH now. */
2177 if (p->ignore_uninstalled_p && !available_p)
2180 pretty_name = (hack->name
2181 ? strdup (hack->name)
2182 : make_hack_name (hack->command));
2186 /* Make the text foreground be the color of insensitive widgets
2187 (but don't actually make it be insensitive, since we still
2188 want to be able to click on it.)
2190 GtkStyle *style = GTK_WIDGET (list)->style;
2191 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2192 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2193 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2195 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2196 /* " background=\"#%02X%02X%02X\"" */
2198 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2199 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2205 gtk_list_store_append (model, &iter);
2206 gtk_list_store_set (model, &iter,
2207 COL_ENABLED, hack->enabled_p,
2208 COL_NAME, pretty_name,
2213 #else /* !HAVE_GTK2 */
2215 saver_preferences *p = &s->prefs;
2216 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2218 for (i = 0; i < s->list_count; i++)
2220 int hack_number = s->list_elt_to_hack_number[i];
2221 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2223 /* A GtkList must contain only GtkListItems, but those can contain
2224 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2225 and a Label. We handle single and double click events on the
2226 line itself, for clicking on the text, but the interior checkbox
2227 also handles its own events.
2230 GtkWidget *line_hbox;
2231 GtkWidget *line_check;
2232 GtkWidget *line_label;
2234 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2236 if (!hack) continue;
2238 /* If we're to suppress uninstalled hacks, check $PATH now. */
2239 if (p->ignore_uninstalled_p && !available_p)
2242 pretty_name = (hack->name
2243 ? strdup (hack->name)
2244 : make_hack_name (hack->command));
2246 line = gtk_list_item_new ();
2247 line_hbox = gtk_hbox_new (FALSE, 0);
2248 line_check = gtk_check_button_new ();
2249 line_label = gtk_label_new (pretty_name);
2251 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2252 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2253 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2255 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2257 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2259 gtk_widget_show (line_check);
2260 gtk_widget_show (line_label);
2261 gtk_widget_show (line_hbox);
2262 gtk_widget_show (line);
2266 gtk_container_add (GTK_CONTAINER (list), line);
2267 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2268 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2271 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2272 GTK_SIGNAL_FUNC (list_checkbox_cb),
2275 gtk_widget_show (line);
2279 /* Make the widget be colored like insensitive widgets
2280 (but don't actually make it be insensitive, since we
2281 still want to be able to click on it.)
2283 GtkRcStyle *rc_style;
2286 gtk_widget_realize (GTK_WIDGET (line_label));
2288 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2289 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2291 rc_style = gtk_rc_style_new ();
2292 rc_style->fg[GTK_STATE_NORMAL] = fg;
2293 rc_style->bg[GTK_STATE_NORMAL] = bg;
2294 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2296 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2297 gtk_rc_style_unref (rc_style);
2301 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2302 GTK_SIGNAL_FUNC (list_select_cb),
2304 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2305 GTK_SIGNAL_FUNC (list_unselect_cb),
2307 #endif /* !HAVE_GTK2 */
2311 update_list_sensitivity (state *s)
2313 saver_preferences *p = &s->prefs;
2314 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2315 Bool checkable = (p->mode == RANDOM_HACKS);
2316 Bool blankable = (p->mode != DONT_BLANK);
2319 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2320 GtkWidget *use = name_to_widget (s, "use_col_frame");
2321 #endif /* HAVE_GTK2 */
2322 GtkWidget *scroller = name_to_widget (s, "scroller");
2323 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2324 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2327 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2328 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2329 #else /* !HAVE_GTK2 */
2330 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2331 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2333 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2334 #endif /* !HAVE_GTK2 */
2335 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2336 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2338 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2341 gtk_tree_view_column_set_visible (use, checkable);
2342 #else /* !HAVE_GTK2 */
2344 gtk_widget_show (use); /* the "Use" column header */
2346 gtk_widget_hide (use);
2350 GtkBin *line = GTK_BIN (kids->data);
2351 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2352 GtkWidget *line_check =
2353 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2356 gtk_widget_show (line_check);
2358 gtk_widget_hide (line_check);
2362 #endif /* !HAVE_GTK2 */
2367 populate_prefs_page (state *s)
2369 saver_preferences *p = &s->prefs;
2371 Bool can_lock_p = True;
2373 /* Disable all the "lock" controls if locking support was not provided
2374 at compile-time, or if running on MacOS. */
2375 # if defined(NO_LOCKING) || defined(__APPLE__)
2380 /* The file supports timeouts of less than a minute, but the GUI does
2381 not, so throttle the values to be at least one minute (since "0" is
2382 a bad rounding choice...)
2384 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2387 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2390 # define FMT_MINUTES(NAME,N) \
2391 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2393 # define FMT_SECONDS(NAME,N) \
2394 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2396 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2397 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2398 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2399 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2400 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2401 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2402 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2407 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2408 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2411 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2412 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2413 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2414 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2415 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2416 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2417 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2418 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2419 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2420 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2421 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2423 # undef TOGGLE_ACTIVE
2425 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2426 (p->image_directory ? p->image_directory : ""));
2427 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2429 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2432 /* Map the `saver_mode' enum to mode menu to values. */
2434 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2437 for (i = 0; i < countof(mode_menu_order); i++)
2438 if (mode_menu_order[i] == p->mode)
2440 gtk_option_menu_set_history (opt, i);
2441 update_list_sensitivity (s);
2445 Bool found_any_writable_cells = False;
2446 Bool fading_possible = False;
2447 Bool dpms_supported = False;
2449 Display *dpy = GDK_DISPLAY();
2450 int nscreens = ScreenCount(dpy);
2452 for (i = 0; i < nscreens; i++)
2454 Screen *s = ScreenOfDisplay (dpy, i);
2455 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2457 found_any_writable_cells = True;
2462 fading_possible = found_any_writable_cells;
2463 #ifdef HAVE_XF86VMODE_GAMMA
2464 fading_possible = True;
2467 #ifdef HAVE_DPMS_EXTENSION
2469 int op = 0, event = 0, error = 0;
2470 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2471 dpms_supported = True;
2473 #endif /* HAVE_DPMS_EXTENSION */
2476 # define SENSITIZE(NAME,SENSITIVEP) \
2477 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2479 /* Blanking and Locking
2481 SENSITIZE ("lock_button", can_lock_p);
2482 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2483 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2487 SENSITIZE ("dpms_frame", dpms_supported);
2488 SENSITIZE ("dpms_button", dpms_supported);
2489 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2490 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2491 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2492 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2493 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2494 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2495 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2496 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2497 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2501 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2502 SENSITIZE ("install_button", found_any_writable_cells);
2503 SENSITIZE ("fade_button", fading_possible);
2504 SENSITIZE ("unfade_button", fading_possible);
2506 SENSITIZE ("fade_label", (fading_possible &&
2507 (p->fade_p || p->unfade_p)));
2508 SENSITIZE ("fade_spinbutton", (fading_possible &&
2509 (p->fade_p || p->unfade_p)));
2517 populate_popup_window (state *s)
2519 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2520 char *doc_string = 0;
2522 /* #### not in Gtk 1.2
2523 gtk_label_set_selectable (doc);
2529 free_conf_data (s->cdata);
2534 saver_preferences *p = &s->prefs;
2535 int list_elt = selected_list_element (s);
2536 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2537 ? s->list_elt_to_hack_number[list_elt]
2539 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2542 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2543 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2544 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2545 s->cdata = load_configurator (cmd_line, s->debug_p);
2546 if (s->cdata && s->cdata->widget)
2547 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2552 doc_string = (s->cdata
2553 ? s->cdata->description
2555 # else /* !HAVE_XML */
2556 doc_string = _("Descriptions not available: no XML support compiled in.");
2557 # endif /* !HAVE_XML */
2559 gtk_label_set_text (doc, (doc_string
2561 : _("No description available.")));
2566 sensitize_demo_widgets (state *s, Bool sensitive_p)
2568 const char *names1[] = { "demo", "settings" };
2569 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2570 "visual", "visual_combo" };
2572 for (i = 0; i < countof(names1); i++)
2574 GtkWidget *w = name_to_widget (s, names1[i]);
2575 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2577 for (i = 0; i < countof(names2); i++)
2579 GtkWidget *w = name_to_widget (s, names2[i]);
2580 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2585 /* Even though we've given these text fields a maximum number of characters,
2586 their default size is still about 30 characters wide -- so measure out
2587 a string in their font, and resize them to just fit that.
2590 fix_text_entry_sizes (state *s)
2594 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2595 const char * const spinbuttons[] = {
2596 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2597 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2598 "dpms_off_spinbutton",
2599 "-fade_spinbutton" };
2603 for (i = 0; i < countof(spinbuttons); i++)
2605 const char *n = spinbuttons[i];
2607 while (*n == '-') n++, cols--;
2608 w = GTK_WIDGET (name_to_widget (s, n));
2609 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2610 gtk_widget_set_usize (w, width, -2);
2613 /* Now fix the width of the combo box.
2615 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2616 w = GTK_COMBO (w)->entry;
2617 width = gdk_string_width (w->style->font, "PseudoColor___");
2618 gtk_widget_set_usize (w, width, -2);
2620 /* Now fix the width of the file entry text.
2622 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2623 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2624 gtk_widget_set_usize (w, width, -2);
2626 /* Now fix the width of the command line text.
2628 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2629 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2630 gtk_widget_set_usize (w, width, -2);
2634 /* Now fix the height of the list widget:
2635 make it default to being around 10 text-lines high instead of 4.
2637 w = GTK_WIDGET (name_to_widget (s, "list"));
2641 int leading = 3; /* approximate is ok... */
2645 PangoFontMetrics *pain =
2646 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2647 w->style->font_desc,
2648 gtk_get_default_language ());
2649 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2650 pango_font_metrics_get_descent (pain));
2651 #else /* !HAVE_GTK2 */
2652 height = w->style->font->ascent + w->style->font->descent;
2653 #endif /* !HAVE_GTK2 */
2657 height += border * 2;
2658 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2659 gtk_widget_set_usize (w, -2, height);
2666 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2669 static char *up_arrow_xpm[] = {
2692 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2693 the end of the array (Gtk 1.2.5.) */
2694 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2695 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2698 static char *down_arrow_xpm[] = {
2721 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2722 the end of the array (Gtk 1.2.5.) */
2723 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2724 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2728 pixmapify_button (state *s, int down_p)
2732 GtkWidget *pixmapwid;
2736 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2737 style = gtk_widget_get_style (w);
2739 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2740 &style->bg[GTK_STATE_NORMAL],
2742 ? (gchar **) down_arrow_xpm
2743 : (gchar **) up_arrow_xpm));
2744 pixmapwid = gtk_pixmap_new (pixmap, mask);
2745 gtk_widget_show (pixmapwid);
2746 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2747 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2751 map_next_button_cb (GtkWidget *w, gpointer user_data)
2753 state *s = (state *) user_data;
2754 pixmapify_button (s, 1);
2758 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2760 state *s = (state *) user_data;
2761 pixmapify_button (s, 0);
2763 #endif /* !HAVE_GTK2 */
2766 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2770 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2771 GtkAllocation *allocation,
2775 GtkWidgetAuxInfo *aux_info;
2777 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2779 aux_info->width = allocation->width;
2780 aux_info->height = -2;
2784 gtk_widget_size_request (label, &req);
2788 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2791 eschew_gtk_lossage (GtkLabel *label)
2793 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2794 aux_info->width = GTK_WIDGET (label)->allocation.width;
2795 aux_info->height = -2;
2799 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2801 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2802 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2805 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2807 gtk_widget_queue_resize (GTK_WIDGET (label));
2812 populate_demo_window (state *s, int list_elt)
2814 saver_preferences *p = &s->prefs;
2817 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2818 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2819 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2820 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2821 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2823 if (p->mode == BLANK_ONLY)
2826 pretty_name = strdup (_("Blank Screen"));
2827 schedule_preview (s, 0);
2829 else if (p->mode == DONT_BLANK)
2832 pretty_name = strdup (_("Screen Saver Disabled"));
2833 schedule_preview (s, 0);
2837 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2838 ? s->list_elt_to_hack_number[list_elt]
2840 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2844 ? strdup (hack->name)
2845 : make_hack_name (hack->command))
2849 schedule_preview (s, hack->command);
2851 schedule_preview (s, 0);
2855 pretty_name = strdup (_("Preview"));
2857 gtk_frame_set_label (frame1, _(pretty_name));
2858 gtk_frame_set_label (frame2, _(pretty_name));
2860 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2861 gtk_entry_set_position (cmd, 0);
2865 sprintf (title, _("%s: %.100s Settings"),
2866 progclass, (pretty_name ? pretty_name : "???"));
2867 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2870 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2872 ? (hack->visual && *hack->visual
2877 sensitize_demo_widgets (s, (hack ? True : False));
2879 if (pretty_name) free (pretty_name);
2881 ensure_selected_item_visible (list);
2883 s->_selected_list_element = list_elt;
2888 widget_deleter (GtkWidget *widget, gpointer data)
2890 /* #### Well, I want to destroy these widgets, but if I do that, they get
2891 referenced again, and eventually I get a SEGV. So instead of
2892 destroying them, I'll just hide them, and leak a bunch of memory
2893 every time the disk file changes. Go go go Gtk!
2895 #### Ok, that's a lie, I get a crash even if I just hide the widget
2896 and don't ever delete it. Fuck!
2899 gtk_widget_destroy (widget);
2901 gtk_widget_hide (widget);
2906 static char **sort_hack_cmp_names_kludge;
2908 sort_hack_cmp (const void *a, const void *b)
2914 int aa = *(int *) a;
2915 int bb = *(int *) b;
2916 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
2917 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
2918 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
2924 initialize_sort_map (state *s)
2926 saver_preferences *p = &s->prefs;
2929 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2930 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2931 if (s->hacks_available_p) free (s->hacks_available_p);
2933 s->list_elt_to_hack_number = (int *)
2934 calloc (sizeof(int), p->screenhacks_count + 1);
2935 s->hack_number_to_list_elt = (int *)
2936 calloc (sizeof(int), p->screenhacks_count + 1);
2937 s->hacks_available_p = (Bool *)
2938 calloc (sizeof(Bool), p->screenhacks_count + 1);
2940 /* Check which hacks actually exist on $PATH
2942 for (i = 0; i < p->screenhacks_count; i++)
2944 screenhack *hack = p->screenhacks[i];
2945 s->hacks_available_p[i] = on_path_p (hack->command);
2948 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
2952 for (i = 0; i < p->screenhacks_count; i++)
2954 if (!p->ignore_uninstalled_p ||
2955 s->hacks_available_p[i])
2956 s->list_elt_to_hack_number[j++] = i;
2960 for (; j < p->screenhacks_count; j++)
2961 s->list_elt_to_hack_number[j] = -1;
2964 /* Generate list of sortable names (once)
2966 sort_hack_cmp_names_kludge = (char **)
2967 calloc (sizeof(char *), p->screenhacks_count);
2968 for (i = 0; i < p->screenhacks_count; i++)
2970 screenhack *hack = p->screenhacks[i];
2971 char *name = (hack->name && *hack->name
2972 ? strdup (hack->name)
2973 : make_hack_name (hack->command));
2975 for (str = name; *str; str++)
2976 *str = tolower(*str);
2977 sort_hack_cmp_names_kludge[i] = name;
2980 /* Sort list->hack map alphabetically
2982 qsort (s->list_elt_to_hack_number,
2983 p->screenhacks_count,
2984 sizeof(*s->list_elt_to_hack_number),
2989 for (i = 0; i < p->screenhacks_count; i++)
2990 free (sort_hack_cmp_names_kludge[i]);
2991 free (sort_hack_cmp_names_kludge);
2992 sort_hack_cmp_names_kludge = 0;
2994 /* Build inverse table */
2995 for (i = 0; i < p->screenhacks_count; i++)
2996 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
3001 maybe_reload_init_file (state *s)
3003 saver_preferences *p = &s->prefs;
3006 static Bool reentrant_lock = False;
3007 if (reentrant_lock) return 0;
3008 reentrant_lock = True;
3010 if (init_file_changed_p (p))
3012 const char *f = init_file_name();
3017 if (!f || !*f) return 0;
3018 b = (char *) malloc (strlen(f) + 1024);
3021 "file \"%s\" has changed, reloading.\n"),
3023 warning_dialog (s->toplevel_widget, b, False, 100);
3027 initialize_sort_map (s);
3029 list_elt = selected_list_element (s);
3030 list = name_to_widget (s, "list");
3031 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3032 populate_hack_list (s);
3033 force_list_select_item (s, list, list_elt, True);
3034 populate_prefs_page (s);
3035 populate_demo_window (s, list_elt);
3036 ensure_selected_item_visible (list);
3041 reentrant_lock = False;
3047 /* Making the preview window have the right X visual (so that GL works.)
3050 static Visual *get_best_gl_visual (state *);
3053 x_visual_to_gdk_visual (Visual *xv)
3055 GList *gvs = gdk_list_visuals();
3056 if (!xv) return gdk_visual_get_system();
3057 for (; gvs; gvs = gvs->next)
3059 GdkVisual *gv = (GdkVisual *) gvs->data;
3060 if (xv == GDK_VISUAL_XVISUAL (gv))
3063 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3064 blurb(), (unsigned long) xv->visualid);
3069 clear_preview_window (state *s)
3074 if (!s->toplevel_widget) return; /* very early */
3075 p = name_to_widget (s, "preview");
3078 if (!window) return;
3080 /* Flush the widget background down into the window, in case a subproc
3082 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3083 gdk_window_clear (window);
3086 int list_elt = selected_list_element (s);
3087 int hack_number = (list_elt >= 0
3088 ? s->list_elt_to_hack_number[list_elt]
3090 Bool available_p = (hack_number >= 0
3091 ? s->hacks_available_p [hack_number]
3094 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3095 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3096 (s->running_preview_error_p
3097 ? (available_p ? 1 : 2)
3099 #else /* !HAVE_GTK2 */
3100 if (s->running_preview_error_p)
3102 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3103 const char * const lines2[] = { N_("Not"), N_("Installed") };
3104 int nlines = countof(lines1);
3105 int lh = p->style->font->ascent + p->style->font->descent;
3109 const char * const *lines = (available_p ? lines1 : lines2);
3111 gdk_window_get_size (window, &w, &h);
3112 y = (h - (lh * nlines)) / 2;
3113 y += p->style->font->ascent;
3114 for (i = 0; i < nlines; i++)
3116 int sw = gdk_string_width (p->style->font, _(lines[i]));
3117 int x = (w - sw) / 2;
3118 gdk_draw_string (window, p->style->font,
3119 p->style->fg_gc[GTK_STATE_NORMAL],
3124 #endif /* !HAVE_GTK2 */
3132 reset_preview_window (state *s)
3134 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3135 when you kill one and re-start another on the same window. So maybe
3136 it's best to just always destroy and recreate the preview window
3137 when changing hacks, instead of always trying to reuse the same one?
3139 GtkWidget *pr = name_to_widget (s, "preview");
3140 if (GTK_WIDGET_REALIZED (pr))
3142 Window oid = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3144 gtk_widget_hide (pr);
3145 gtk_widget_unrealize (pr);
3146 gtk_widget_realize (pr);
3147 gtk_widget_show (pr);
3148 id = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3150 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3158 fix_preview_visual (state *s)
3160 GtkWidget *widget = name_to_widget (s, "preview");
3161 Visual *xvisual = get_best_gl_visual (s);
3162 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3163 GdkVisual *dvisual = gdk_visual_get_system();
3164 GdkColormap *cmap = (visual == dvisual
3165 ? gdk_colormap_get_system ()
3166 : gdk_colormap_new (visual, False));
3169 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3170 (visual == dvisual ? "default" : "non-default"),
3171 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3173 if (!GTK_WIDGET_REALIZED (widget) ||
3174 gtk_widget_get_visual (widget) != visual)
3176 gtk_widget_unrealize (widget);
3177 gtk_widget_set_visual (widget, visual);
3178 gtk_widget_set_colormap (widget, cmap);
3179 gtk_widget_realize (widget);
3182 /* Set the Widget colors to be white-on-black. */
3184 GdkWindow *window = widget->window;
3185 GtkStyle *style = gtk_style_copy (widget->style);
3186 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3187 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3188 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3189 GdkGC *fgc = gdk_gc_new(window);
3190 GdkGC *bgc = gdk_gc_new(window);
3191 if (!gdk_color_white (cmap, fg)) abort();
3192 if (!gdk_color_black (cmap, bg)) abort();
3193 gdk_gc_set_foreground (fgc, fg);
3194 gdk_gc_set_background (fgc, bg);
3195 gdk_gc_set_foreground (bgc, bg);
3196 gdk_gc_set_background (bgc, fg);
3197 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3198 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3199 gtk_widget_set_style (widget, style);
3201 /* For debugging purposes, put a title on the window (so that
3202 it can be easily found in the output of "xwininfo -tree".)
3204 gdk_window_set_title (window, "Preview");
3207 gtk_widget_show (widget);
3215 subproc_pretty_name (state *s)
3217 if (s->running_preview_cmd)
3219 char *ps = strdup (s->running_preview_cmd);
3220 char *ss = strchr (ps, ' ');
3222 ss = strrchr (ps, '/');
3233 return strdup ("???");
3238 reap_zombies (state *s)
3240 int wait_status = 0;
3242 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3246 if (pid == s->running_preview_pid)
3248 char *ss = subproc_pretty_name (s);
3249 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3250 (unsigned long) pid, ss);
3254 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3255 (unsigned long) pid);
3261 /* Mostly lifted from driver/subprocs.c */
3263 get_best_gl_visual (state *s)
3265 Display *dpy = GDK_DISPLAY();
3274 av[ac++] = "xscreensaver-gl-helper";
3279 perror ("error creating pipe:");
3286 switch ((int) (forked = fork ()))
3290 sprintf (buf, "%s: couldn't fork", blurb());
3298 close (in); /* don't need this one */
3299 close (ConnectionNumber (dpy)); /* close display fd */
3301 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3303 perror ("could not dup() a new stdout:");
3307 execvp (av[0], av); /* shouldn't return. */
3309 if (errno != ENOENT)
3311 /* Ignore "no such file or directory" errors, unless verbose.
3312 Issue all other exec errors, though. */
3313 sprintf (buf, "%s: running %s", blurb(), av[0]);
3317 /* Note that one must use _exit() instead of exit() in procs forked
3318 off of Gtk programs -- Gtk installs an atexit handler that has a
3319 copy of the X connection (which we've already closed, for safety.)
3320 If one uses exit() instead of _exit(), then one sometimes gets a
3321 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3323 _exit (1); /* exits fork */
3329 int wait_status = 0;
3331 FILE *f = fdopen (in, "r");
3335 close (out); /* don't need this one */
3338 fgets (buf, sizeof(buf)-1, f);
3341 /* Wait for the child to die. */
3342 waitpid (-1, &wait_status, 0);
3344 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3350 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3356 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3358 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3359 blurb(), av[0], result);
3371 kill_preview_subproc (state *s, Bool reset_p)
3373 s->running_preview_error_p = False;
3376 clear_preview_window (s);
3378 if (s->subproc_check_timer_id)
3380 gtk_timeout_remove (s->subproc_check_timer_id);
3381 s->subproc_check_timer_id = 0;
3382 s->subproc_check_countdown = 0;
3385 if (s->running_preview_pid)
3387 int status = kill (s->running_preview_pid, SIGTERM);
3388 char *ss = subproc_pretty_name (s);
3395 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3396 blurb(), (unsigned long) s->running_preview_pid, ss);
3401 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3402 blurb(), (unsigned long) s->running_preview_pid, ss);
3406 else if (s->debug_p)
3407 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3408 (unsigned long) s->running_preview_pid, ss);
3411 s->running_preview_pid = 0;
3412 if (s->running_preview_cmd) free (s->running_preview_cmd);
3413 s->running_preview_cmd = 0;
3420 reset_preview_window (s);
3421 clear_preview_window (s);
3426 /* Immediately and unconditionally launches the given process,
3427 after appending the -window-id option; sets running_preview_pid.
3430 launch_preview_subproc (state *s)
3432 saver_preferences *p = &s->prefs;
3436 const char *cmd = s->desired_preview_cmd;
3438 GtkWidget *pr = name_to_widget (s, "preview");
3441 reset_preview_window (s);
3443 window = pr->window;
3445 s->running_preview_error_p = False;
3447 if (s->preview_suppressed_p)
3449 kill_preview_subproc (s, False);
3453 new_cmd = malloc (strlen (cmd) + 40);
3455 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3458 /* No window id? No command to run. */
3464 strcpy (new_cmd, cmd);
3465 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3469 kill_preview_subproc (s, False);
3472 s->running_preview_error_p = True;
3473 clear_preview_window (s);
3477 switch ((int) (forked = fork ()))
3482 sprintf (buf, "%s: couldn't fork", blurb());
3484 s->running_preview_error_p = True;
3490 close (ConnectionNumber (GDK_DISPLAY()));
3492 hack_subproc_environment (id, s->debug_p);
3494 usleep (250000); /* pause for 1/4th second before launching, to give
3495 the previous program time to die and flush its X
3496 buffer, so we don't get leftover turds on the
3499 exec_command (p->shell, new_cmd, p->nice_inferior);
3500 /* Don't bother printing an error message when we are unable to
3501 exec subprocesses; we handle that by polling the pid later.
3503 Note that one must use _exit() instead of exit() in procs forked
3504 off of Gtk programs -- Gtk installs an atexit handler that has a
3505 copy of the X connection (which we've already closed, for safety.)
3506 If one uses exit() instead of _exit(), then one sometimes gets a
3507 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3509 _exit (1); /* exits child fork */
3514 if (s->running_preview_cmd) free (s->running_preview_cmd);
3515 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3516 s->running_preview_pid = forked;
3520 char *ss = subproc_pretty_name (s);
3521 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3522 (unsigned long) forked, ss);
3529 schedule_preview_check (s);
3532 if (new_cmd) free (new_cmd);
3537 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3540 hack_environment (state *s)
3542 static const char *def_path =
3543 # ifdef DEFAULT_PATH_PREFIX
3544 DEFAULT_PATH_PREFIX;
3549 Display *dpy = GDK_DISPLAY();
3550 const char *odpy = DisplayString (dpy);
3551 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3552 strcpy (ndpy, "DISPLAY=");
3553 strcat (ndpy, odpy);
3558 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3560 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3561 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3562 So we must leak it (and/or the previous setting). Yay.
3565 if (def_path && *def_path)
3567 const char *opath = getenv("PATH");
3568 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3569 strcpy (npath, "PATH=");
3570 strcat (npath, def_path);
3571 strcat (npath, ":");
3572 strcat (npath, opath);
3576 /* do not free(npath) -- see above */
3579 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3585 hack_subproc_environment (Window preview_window_id, Bool debug_p)
3587 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
3588 necessary yet, but it will make programs work if we had invoked
3589 them with "-root" and not with "-window-id" -- which, of course,
3592 char *nssw = (char *) malloc (40);
3593 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
3595 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
3596 any more, right? It's not Posix, but everyone seems to have it. */
3601 fprintf (stderr, "%s: %s\n", blurb(), nssw);
3603 /* do not free(nssw) -- see above */
3607 /* Called from a timer:
3608 Launches the currently-chosen subprocess, if it's not already running.
3609 If there's a different process running, kills it.
3612 update_subproc_timer (gpointer data)
3614 state *s = (state *) data;
3615 if (! s->desired_preview_cmd)
3616 kill_preview_subproc (s, True);
3617 else if (!s->running_preview_cmd ||
3618 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3619 launch_preview_subproc (s);
3621 s->subproc_timer_id = 0;
3622 return FALSE; /* do not re-execute timer */
3626 /* Call this when you think you might want a preview process running.
3627 It will set a timer that will actually launch that program a second
3628 from now, if you haven't changed your mind (to avoid double-click
3629 spazzing, etc.) `cmd' may be null meaning "no process".
3632 schedule_preview (state *s, const char *cmd)
3634 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3639 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3641 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3644 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3645 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3647 if (s->subproc_timer_id)
3648 gtk_timeout_remove (s->subproc_timer_id);
3649 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3653 /* Called from a timer:
3654 Checks to see if the subproc that should be running, actually is.
3657 check_subproc_timer (gpointer data)
3659 state *s = (state *) data;
3660 Bool again_p = True;
3662 if (s->running_preview_error_p || /* already dead */
3663 s->running_preview_pid <= 0)
3671 status = kill (s->running_preview_pid, 0);
3672 if (status < 0 && errno == ESRCH)
3673 s->running_preview_error_p = True;
3677 char *ss = subproc_pretty_name (s);
3678 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3679 (unsigned long) s->running_preview_pid, ss,
3680 (s->running_preview_error_p ? "dead" : "alive"));
3684 if (s->running_preview_error_p)
3686 clear_preview_window (s);
3691 /* Otherwise, it's currently alive. We might be checking again, or we
3692 might be satisfied. */
3694 if (--s->subproc_check_countdown <= 0)
3698 return TRUE; /* re-execute timer */
3701 s->subproc_check_timer_id = 0;
3702 s->subproc_check_countdown = 0;
3703 return FALSE; /* do not re-execute timer */
3708 /* Call this just after launching a subprocess.
3709 This sets a timer that will, five times a second for two seconds,
3710 check whether the program is still running. The assumption here
3711 is that if the process didn't stay up for more than a couple of
3712 seconds, then either the program doesn't exist, or it doesn't
3713 take a -window-id argument.
3716 schedule_preview_check (state *s)
3722 fprintf (stderr, "%s: scheduling check\n", blurb());
3724 if (s->subproc_check_timer_id)
3725 gtk_timeout_remove (s->subproc_check_timer_id);
3726 s->subproc_check_timer_id =
3727 gtk_timeout_add (1000 / ticks,
3728 check_subproc_timer, (gpointer) s);
3729 s->subproc_check_countdown = ticks * seconds;
3734 screen_blanked_p (void)
3738 unsigned long nitems, bytesafter;
3739 unsigned char *dataP = 0;
3740 Display *dpy = GDK_DISPLAY();
3741 Bool blanked_p = False;
3743 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3744 XA_SCREENSAVER_STATUS,
3745 0, 3, False, XA_INTEGER,
3746 &type, &format, &nitems, &bytesafter,
3749 && type == XA_INTEGER
3753 Atom *data = (Atom *) dataP;
3754 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3757 if (dataP) XFree (dataP);
3762 /* Wake up every now and then and see if the screen is blanked.
3763 If it is, kill off the small-window demo -- no point in wasting
3764 cycles by running two screensavers at once...
3767 check_blanked_timer (gpointer data)
3769 state *s = (state *) data;
3770 Bool blanked_p = screen_blanked_p ();
3771 if (blanked_p && s->running_preview_pid)
3774 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3775 kill_preview_subproc (s, True);
3778 return True; /* re-execute timer */
3782 /* Setting window manager icon
3786 init_icon (GdkWindow *window)
3788 GdkBitmap *mask = 0;
3791 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3792 (gchar **) logo_50_xpm);
3794 gdk_window_set_icon (window, 0, pixmap, mask);
3798 /* The main demo-mode command loop.
3803 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3804 XrmRepresentation *type, XrmValue *value, XPointer closure)
3807 for (i = 0; quarks[i]; i++)
3809 if (bindings[i] == XrmBindTightly)
3810 fprintf (stderr, (i == 0 ? "" : "."));
3811 else if (bindings[i] == XrmBindLoosely)
3812 fprintf (stderr, "*");
3814 fprintf (stderr, " ??? ");
3815 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3818 fprintf (stderr, ": %s\n", (char *) value->addr);
3826 the_network_is_not_the_computer (state *s)
3828 Display *dpy = GDK_DISPLAY();
3829 char *rversion = 0, *ruser = 0, *rhost = 0;
3830 char *luser, *lhost;
3832 struct passwd *p = getpwuid (getuid ());
3833 const char *d = DisplayString (dpy);
3835 # if defined(HAVE_UNAME)
3837 if (uname (&uts) < 0)
3838 lhost = "<UNKNOWN>";
3840 lhost = uts.nodename;
3842 strcpy (lhost, getenv("SYS$NODE"));
3843 # else /* !HAVE_UNAME && !VMS */
3844 strcat (lhost, "<UNKNOWN>");
3845 # endif /* !HAVE_UNAME && !VMS */
3847 if (p && p->pw_name)
3852 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3854 /* Make a buffer that's big enough for a number of copies of all the
3855 strings, plus some. */
3856 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3857 (ruser ? strlen(ruser) : 0) +
3858 (rhost ? strlen(rhost) : 0) +
3865 if (!rversion || !*rversion)
3869 "The XScreenSaver daemon doesn't seem to be running\n"
3870 "on display \"%s\". Launch it now?"),
3873 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3875 /* Warn that the two processes are running as different users.
3879 "%s is running as user \"%s\" on host \"%s\".\n"
3880 "But the xscreensaver managing display \"%s\"\n"
3881 "is running as user \"%s\" on host \"%s\".\n"
3883 "Since they are different users, they won't be reading/writing\n"
3884 "the same ~/.xscreensaver file, so %s isn't\n"
3885 "going to work right.\n"
3887 "You should either re-run %s as \"%s\", or re-run\n"
3888 "xscreensaver as \"%s\".\n"
3890 "Restart the xscreensaver daemon now?\n"),
3891 progname, luser, lhost,
3893 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3895 progname, (ruser ? ruser : "???"),
3898 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3900 /* Warn that the two processes are running on different hosts.
3904 "%s is running as user \"%s\" on host \"%s\".\n"
3905 "But the xscreensaver managing display \"%s\"\n"
3906 "is running as user \"%s\" on host \"%s\".\n"
3908 "If those two machines don't share a file system (that is,\n"
3909 "if they don't see the same ~%s/.xscreensaver file) then\n"
3910 "%s won't work right.\n"
3912 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3913 progname, luser, lhost,
3915 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3920 else if (!!strcmp (rversion, s->short_version))
3922 /* Warn that the version numbers don't match.
3926 "This is %s version %s.\n"
3927 "But the xscreensaver managing display \"%s\"\n"
3928 "is version %s. This could cause problems.\n"
3930 "Restart the xscreensaver daemon now?\n"),
3931 progname, s->short_version,
3938 warning_dialog (s->toplevel_widget, msg, True, 1);
3940 if (rversion) free (rversion);
3941 if (ruser) free (ruser);
3942 if (rhost) free (rhost);
3947 /* We use this error handler so that X errors are preceeded by the name
3948 of the program that generated them.
3951 demo_ehandler (Display *dpy, XErrorEvent *error)
3953 state *s = global_state_kludge; /* I hate C so much... */
3954 fprintf (stderr, "\nX error in %s:\n", blurb());
3955 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3956 kill_preview_subproc (s, False);
3962 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3963 of the program that generated them; and also that we can ignore one
3964 particular bogus error message that Gdk madly spews.
3967 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3968 const gchar *message, gpointer user_data)
3970 /* Ignore the message "Got event for unknown window: 0x...".
3971 Apparently some events are coming in for the xscreensaver window
3972 (presumably reply events related to the ClientMessage) and Gdk
3973 feels the need to complain about them. So, just suppress any
3974 messages that look like that one.
3976 if (strstr (message, "unknown window"))
3979 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3980 (log_domain ? log_domain : progclass),
3981 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3982 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3983 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3984 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3985 log_level == G_LOG_LEVEL_INFO ? "info" :
3986 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3988 ((!*message || message[strlen(message)-1] != '\n')
3994 __extension__ /* shut up about "string length is greater than the length
3995 ISO C89 compilers are required to support" when including
3999 static char *defaults[] = {
4000 #include "XScreenSaver_ad.h"
4005 #ifdef HAVE_CRAPPLET
4006 static struct poptOption crapplet_options[] = {
4007 {NULL, '\0', 0, NULL, 0}
4009 #endif /* HAVE_CRAPPLET */
4012 const char *usage = "[--display dpy] [--prefs]"
4013 # ifdef HAVE_CRAPPLET
4016 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4019 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4021 state *s = (state *) user_data;
4022 Boolean oi = s->initializing_p;
4023 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4024 s->initializing_p = True;
4025 eschew_gtk_lossage (label);
4026 s->initializing_p = oi;
4032 print_widget_tree (GtkWidget *w, int depth)
4035 for (i = 0; i < depth; i++)
4036 fprintf (stderr, " ");
4037 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4039 if (GTK_IS_LIST (w))
4041 for (i = 0; i < depth+1; i++)
4042 fprintf (stderr, " ");
4043 fprintf (stderr, "...list kids...\n");
4045 else if (GTK_IS_CONTAINER (w))
4047 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4050 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4058 delayed_scroll_kludge (gpointer data)
4060 state *s = (state *) data;
4061 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4062 ensure_selected_item_visible (w);
4064 /* Oh, this is just fucking lovely, too. */
4065 w = GTK_WIDGET (name_to_widget (s, "preview"));
4066 gtk_widget_hide (w);
4067 gtk_widget_show (w);
4069 return FALSE; /* do not re-execute timer */
4075 create_xscreensaver_demo (void)
4079 nb = name_to_widget (global_state_kludge, "preview_notebook");
4080 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4082 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4086 create_xscreensaver_settings_dialog (void)
4090 box = name_to_widget (global_state_kludge, "dialog_action_area");
4092 w = name_to_widget (global_state_kludge, "adv_button");
4093 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4095 w = name_to_widget (global_state_kludge, "std_button");
4096 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4098 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4101 #endif /* HAVE_GTK2 */
4104 main (int argc, char **argv)
4108 saver_preferences *p;
4112 Widget toplevel_shell;
4113 char *real_progname = argv[0];
4116 Bool crapplet_p = False;
4120 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4121 textdomain (GETTEXT_PACKAGE);
4124 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4125 # else /* !HAVE_GTK2 */
4126 if (!setlocale (LC_ALL, ""))
4127 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4128 # endif /* !HAVE_GTK2 */
4130 #endif /* ENABLE_NLS */
4132 str = strrchr (real_progname, '/');
4133 if (str) real_progname = str+1;
4136 memset (s, 0, sizeof(*s));
4137 s->initializing_p = True;
4140 global_state_kludge = s; /* I hate C so much... */
4142 progname = real_progname;
4144 s->short_version = (char *) malloc (5);
4145 memcpy (s->short_version, screensaver_id + 17, 4);
4146 s->short_version [4] = 0;
4149 /* Register our error message logger for every ``log domain'' known.
4150 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4151 for all of the domains that seem to be in use.
4154 const char * const domains[] = { 0,
4155 "Gtk", "Gdk", "GLib", "GModule",
4156 "GThread", "Gnome", "GnomeUI" };
4157 for (i = 0; i < countof(domains); i++)
4158 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4161 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4164 const char *dir = DEFAULT_ICONDIR;
4165 if (*dir) add_pixmap_directory (dir);
4167 # endif /* !HAVE_GTK2 */
4168 #endif /* DEFAULT_ICONDIR */
4170 /* This is gross, but Gtk understands --display and not -display...
4172 for (i = 1; i < argc; i++)
4173 if (argv[i][0] && argv[i][1] &&
4174 !strncmp(argv[i], "-display", strlen(argv[i])))
4175 argv[i] = "--display";
4178 /* We need to parse this arg really early... Sigh. */
4179 for (i = 1; i < argc; i++)
4182 (!strcmp(argv[i], "--crapplet") ||
4183 !strcmp(argv[i], "--capplet")))
4185 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4188 for (j = i; j < argc; j++) /* remove it from the list */
4189 argv[j] = argv[j+1];
4191 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4192 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4194 fprintf (stderr, "%s: %s\n", real_progname, usage);
4196 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4199 (!strcmp(argv[i], "--debug") ||
4200 !strcmp(argv[i], "-debug") ||
4201 !strcmp(argv[i], "-d")))
4205 for (j = i; j < argc; j++) /* remove it from the list */
4206 argv[j] = argv[j+1];
4213 (!strcmp(argv[i], "-geometry") ||
4214 !strcmp(argv[i], "-geom") ||
4215 !strcmp(argv[i], "-geo") ||
4216 !strcmp(argv[i], "-g")))
4220 for (j = i; j < argc; j++) /* remove them from the list */
4221 argv[j] = argv[j+2];
4228 (!strcmp(argv[i], "--configdir")))
4232 hack_configuration_path = argv[i+1];
4233 for (j = i; j < argc; j++) /* remove them from the list */
4234 argv[j] = argv[j+2];
4238 if (0 != stat (hack_configuration_path, &st))
4241 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4245 else if (!S_ISDIR (st.st_mode))
4247 fprintf (stderr, "%s: not a directory: %s\n",
4248 blurb(), hack_configuration_path);
4256 fprintf (stderr, "%s: using config directory \"%s\"\n",
4257 progname, hack_configuration_path);
4260 /* Let Gtk open the X connection, then initialize Xt to use that
4261 same connection. Doctor Frankenstein would be proud.
4263 # ifdef HAVE_CRAPPLET
4266 GnomeClient *client;
4267 GnomeClientFlags flags = 0;
4269 int init_results = gnome_capplet_init ("screensaver-properties",
4271 argc, argv, NULL, 0, NULL);
4273 0 upon successful initialization;
4274 1 if --init-session-settings was passed on the cmdline;
4275 2 if --ignore was passed on the cmdline;
4278 So the 1 signifies just to init the settings, and quit, basically.
4279 (Meaning launch the xscreensaver daemon.)
4282 if (init_results < 0)
4285 g_error ("An initialization error occurred while "
4286 "starting xscreensaver-capplet.\n");
4288 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4289 real_progname, init_results);
4294 client = gnome_master_client ();
4297 flags = gnome_client_get_flags (client);
4299 if (flags & GNOME_CLIENT_IS_CONNECTED)
4302 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4303 gnome_client_get_id (client));
4306 char *session_args[20];
4308 session_args[i++] = real_progname;
4309 session_args[i++] = "--capplet";
4310 session_args[i++] = "--init-session-settings";
4311 session_args[i] = 0;
4312 gnome_client_set_priority (client, 20);
4313 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4314 gnome_client_set_restart_command (client, i, session_args);
4318 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4321 gnome_client_flush (client);
4324 if (init_results == 1)
4326 system ("xscreensaver -nosplash &");
4332 # endif /* HAVE_CRAPPLET */
4334 gtk_init (&argc, &argv);
4338 /* We must read exactly the same resources as xscreensaver.
4339 That means we must have both the same progclass *and* progname,
4340 at least as far as the resource database is concerned. So,
4341 put "xscreensaver" in argv[0] while initializing Xt.
4343 argv[0] = "xscreensaver";
4347 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4349 XtToolkitInitialize ();
4350 app = XtCreateApplicationContext ();
4351 dpy = GDK_DISPLAY();
4352 XtAppSetFallbackResources (app, defaults);
4353 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4354 toplevel_shell = XtAppCreateShell (progname, progclass,
4355 applicationShellWidgetClass,
4358 dpy = XtDisplay (toplevel_shell);
4359 db = XtDatabase (dpy);
4360 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4361 XSetErrorHandler (demo_ehandler);
4363 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4364 signal (SIGPIPE, SIG_IGN);
4366 /* After doing Xt-style command-line processing, complain about any
4367 unrecognized command-line arguments.
4369 for (i = 1; i < argc; i++)
4371 char *str = argv[i];
4372 if (str[0] == '-' && str[1] == '-')
4374 if (!strcmp (str, "-prefs"))
4376 else if (crapplet_p)
4377 /* There are lots of random args that we don't care about when we're
4378 started as a crapplet, so just ignore unknown args in that case. */
4382 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4384 fprintf (stderr, "%s: %s\n", real_progname, usage);
4389 /* Load the init file, which may end up consulting the X resource database
4390 and the site-wide app-defaults file. Note that at this point, it's
4391 important that `progname' be "xscreensaver", rather than whatever
4396 hack_environment (s); /* must be before initialize_sort_map() */
4399 initialize_sort_map (s);
4401 /* Now that Xt has been initialized, and the resources have been read,
4402 we can set our `progname' variable to something more in line with
4405 progname = real_progname;
4409 /* Print out all the resources we read. */
4411 XrmName name = { 0 };
4412 XrmClass class = { 0 };
4414 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4420 /* Intern the atoms that xscreensaver_command() needs.
4422 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4423 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4424 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4425 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4426 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4427 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4428 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4429 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4430 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4431 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4432 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4433 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4434 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4437 /* Create the window and all its widgets.
4439 s->base_widget = create_xscreensaver_demo ();
4440 s->popup_widget = create_xscreensaver_settings_dialog ();
4441 s->toplevel_widget = s->base_widget;
4444 /* Set the main window's title. */
4446 char *base_title = _("Screensaver Preferences");
4447 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4448 char *s1, *s2, *s3, *s4;
4449 s1 = (char *) strchr(v, ' '); s1++;
4450 s2 = (char *) strchr(s1, ' ');
4451 s3 = (char *) strchr(v, '('); s3++;
4452 s4 = (char *) strchr(s3, ')');
4456 window_title = (char *) malloc (strlen (base_title) +
4457 strlen (progclass) +
4458 strlen (s1) + strlen (s3) +
4460 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4461 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4462 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4466 /* Adjust the (invisible) notebooks on the popup dialog... */
4468 GtkNotebook *notebook =
4469 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4470 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4474 gtk_widget_hide (std);
4475 # else /* !HAVE_XML */
4476 /* Make the advanced page be the only one available. */
4477 gtk_widget_set_sensitive (std, False);
4478 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4479 gtk_widget_hide (std);
4481 # endif /* !HAVE_XML */
4483 gtk_notebook_set_page (notebook, page);
4484 gtk_notebook_set_show_tabs (notebook, False);
4487 /* Various other widget initializations...
4489 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4490 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4492 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4493 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4496 populate_hack_list (s);
4497 populate_prefs_page (s);
4498 sensitize_demo_widgets (s, False);
4499 fix_text_entry_sizes (s);
4500 scroll_to_current_hack (s);
4502 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4503 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4507 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4508 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4510 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4511 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4513 #endif /* !HAVE_GTK2 */
4515 /* Hook up callbacks to the items on the mode menu. */
4517 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4518 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4519 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4520 for (; kids; kids = kids->next)
4521 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4522 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4527 /* Handle the -prefs command-line argument. */
4530 GtkNotebook *notebook =
4531 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4532 gtk_notebook_set_page (notebook, 1);
4535 # ifdef HAVE_CRAPPLET
4539 GtkWidget *outer_vbox;
4541 gtk_widget_hide (s->toplevel_widget);
4543 capplet = capplet_widget_new ();
4545 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4546 # ifdef HAVE_CRAPPLET_IMMEDIATE
4547 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4548 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4549 /* In crapplet-mode, take off the menubar. */
4550 gtk_widget_hide (name_to_widget (s, "menubar"));
4552 /* Reparent our top-level container to be a child of the capplet
4555 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4556 gtk_widget_ref (outer_vbox);
4557 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4559 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4560 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4562 /* Find the window above us, and set the title and close handler. */
4564 GtkWidget *window = capplet;
4565 while (window && !GTK_IS_WINDOW (window))
4566 window = window->parent;
4569 gtk_window_set_title (GTK_WINDOW (window), window_title);
4570 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4571 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4576 s->toplevel_widget = capplet;
4578 # endif /* HAVE_CRAPPLET */
4581 /* The Gnome folks hate the menubar. I think it's important to have access
4582 to the commands on the File menu (Restart Daemon, etc.) and to the
4583 About and Documentation commands on the Help menu.
4587 gtk_widget_hide (name_to_widget (s, "menubar"));
4591 free (window_title);
4595 /* After picking the default size, allow -geometry to override it. */
4597 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
4600 gtk_widget_show (s->toplevel_widget);
4601 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4602 fix_preview_visual (s);
4604 /* Realize page zero, so that we can diddle the scrollbar when the
4605 user tabs back to it -- otherwise, the current hack isn't scrolled
4606 to the first time they tab back there, when started with "-prefs".
4607 (Though it is if they then tab away, and back again.)
4609 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4610 #### understands this crap, explain to me how to make this work.
4612 gtk_widget_realize (name_to_widget (s, "demos_table"));
4615 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4618 /* Issue any warnings about the running xscreensaver daemon. */
4619 the_network_is_not_the_computer (s);
4622 /* Run the Gtk event loop, and not the Xt event loop. This means that
4623 if there were Xt timers or fds registered, they would never get serviced,
4624 and if there were any Xt widgets, they would never have events delivered.
4625 Fortunately, we're using Gtk for all of the UI, and only initialized
4626 Xt so that we could process the command line and use the X resource
4629 s->initializing_p = False;
4631 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4632 after we start up. Otherwise, it always appears scrolled to the top
4633 when in crapplet-mode. */
4634 gtk_timeout_add (500, delayed_scroll_kludge, s);
4638 /* Load every configurator in turn, to scan them for errors all at once. */
4641 for (i = 0; i < p->screenhacks_count; i++)
4643 screenhack *hack = p->screenhacks[i];
4644 conf_data *d = load_configurator (hack->command, False);
4645 if (d) free_conf_data (d);
4651 # ifdef HAVE_CRAPPLET
4653 capplet_gtk_main ();
4655 # endif /* HAVE_CRAPPLET */
4658 kill_preview_subproc (s, False);
4662 #endif /* HAVE_GTK -- whole file */