1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2003 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>
92 #endif /* HAVE_GTK2 */
94 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
95 # define GLADE_DIR DEFAULT_ICONDIR
97 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
98 # define DEFAULT_ICONDIR GLADE_DIR
102 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
103 It is unused otherwise, so in that case, stub it out. */
104 static const char *hack_configuration_path = 0;
111 #include "resources.h" /* for parse_time() */
112 #include "visual.h" /* for has_writable_cells() */
113 #include "remote.h" /* for xscreensaver_command() */
116 #include "logo-50.xpm"
117 #include "logo-180.xpm"
119 #include "demo-Gtk-widgets.h"
120 #include "demo-Gtk-support.h"
121 #include "demo-Gtk-conf.h"
133 #endif /* HAVE_GTK2 */
136 extern void exec_command (const char *shell, const char *command, int nice);
138 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
141 #define countof(x) (sizeof((x))/sizeof((*x)))
145 char *progclass = "XScreenSaver";
148 /* The order of the items in the mode menu. */
149 static int mode_menu_order[] = {
150 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS };
155 char *short_version; /* version number of this xscreensaver build */
157 GtkWidget *toplevel_widget; /* the main window */
158 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
159 GtkWidget *popup_widget; /* the "Settings" dialog */
160 conf_data *cdata; /* private data for per-hack configuration */
163 GladeXML *glade_ui; /* Glade UI file */
164 #endif /* HAVE_GTK2 */
166 Bool debug_p; /* whether to print diagnostics */
167 Bool initializing_p; /* flag for breaking recursion loops */
168 Bool saving_p; /* flag for breaking recursion loops */
170 char *desired_preview_cmd; /* subprocess we intend to run */
171 char *running_preview_cmd; /* subprocess we are currently running */
172 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
173 Bool running_preview_error_p; /* whether the pid died abnormally */
175 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
176 int subproc_timer_id; /* timer to delay subproc launch */
177 int subproc_check_timer_id; /* timer to check whether it started up */
178 int subproc_check_countdown; /* how many more checks left */
180 int *list_elt_to_hack_number; /* table for sorting the hack list */
181 int *hack_number_to_list_elt; /* the inverse table */
182 Bool *hacks_available_p; /* whether hacks are on $PATH */
183 int list_count; /* how many items are in the list: this may be
184 less than p->screenhacks_count, if some are
187 int _selected_list_element; /* don't use this: call
188 selected_list_element() instead */
190 saver_preferences prefs;
195 /* Total fucking evilness due to the fact that it's rocket science to get
196 a closure object of our own down into the various widget callbacks. */
197 static state *global_state_kludge;
200 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
201 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
202 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
205 static void populate_demo_window (state *, int list_elt);
206 static void populate_prefs_page (state *);
207 static void populate_popup_window (state *);
209 static Bool flush_dialog_changes_and_save (state *);
210 static Bool flush_popup_changes_and_save (state *);
212 static int maybe_reload_init_file (state *);
213 static void await_xscreensaver (state *);
215 static void schedule_preview (state *, const char *cmd);
216 static void kill_preview_subproc (state *);
217 static void schedule_preview_check (state *);
221 /* Some random utility functions
227 time_t now = time ((time_t *) 0);
228 char *ct = (char *) ctime (&now);
229 static char buf[255];
230 int n = strlen(progname);
232 strncpy(buf, progname, n);
235 strncpy(buf+n, ct+11, 8);
236 strcpy(buf+n+9, ": ");
242 name_to_widget (state *s, const char *name)
252 /* First try to load the Glade file from the current directory;
253 if there isn't one there, check the installed directory.
255 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
256 const char * const files[] = { GLADE_FILE_NAME,
257 GLADE_DIR "/" GLADE_FILE_NAME };
259 for (i = 0; i < countof (files); i++)
262 if (!stat (files[i], &st))
264 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
271 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
272 "\tfrom " GLADE_DIR "/ or current directory.\n",
276 # undef GLADE_FILE_NAME
278 glade_xml_signal_autoconnect (s->glade_ui);
281 w = glade_xml_get_widget (s->glade_ui, name);
283 #else /* !HAVE_GTK2 */
285 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
288 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
290 #endif /* HAVE_GTK2 */
293 fprintf (stderr, "%s: no widget \"%s\"\n", blurb(), name);
298 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
299 Takes a scroller, viewport, or list as an argument.
302 ensure_selected_item_visible (GtkWidget *widget)
306 GtkTreeSelection *selection;
310 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
311 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
312 path = gtk_tree_path_new_first ();
314 path = gtk_tree_model_get_path (model, &iter);
316 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
318 gtk_tree_path_free (path);
320 #else /* !HAVE_GTK2 */
322 GtkScrolledWindow *scroller = 0;
324 GtkList *list_widget = 0;
328 GtkWidget *selected = 0;
331 gint parent_h, child_y, child_h, children_h, ignore;
332 double ratio_t, ratio_b;
334 if (GTK_IS_SCROLLED_WINDOW (widget))
336 scroller = GTK_SCROLLED_WINDOW (widget);
337 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
338 list_widget = GTK_LIST (GTK_BIN(vp)->child);
340 else if (GTK_IS_VIEWPORT (widget))
342 vp = GTK_VIEWPORT (widget);
343 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
344 list_widget = GTK_LIST (GTK_BIN(vp)->child);
346 else if (GTK_IS_LIST (widget))
348 list_widget = GTK_LIST (widget);
349 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
350 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
355 slist = list_widget->selection;
356 selected = (slist ? GTK_WIDGET (slist->data) : 0);
360 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
362 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
363 kids; kids = kids->next)
366 adj = gtk_scrolled_window_get_vadjustment (scroller);
368 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
369 &ignore, &ignore, &ignore, &parent_h, &ignore);
370 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
371 &ignore, &child_y, &ignore, &child_h, &ignore);
372 children_h = nkids * child_h;
374 ratio_t = ((double) child_y) / ((double) children_h);
375 ratio_b = ((double) child_y + child_h) / ((double) children_h);
377 if (adj->upper == 0.0) /* no items in list */
380 if (ratio_t < (adj->value / adj->upper) ||
381 ratio_b > ((adj->value + adj->page_size) / adj->upper))
384 int slop = parent_h * 0.75; /* how much to overshoot by */
386 if (ratio_t < (adj->value / adj->upper))
388 double ratio_w = ((double) parent_h) / ((double) children_h);
389 double ratio_l = (ratio_b - ratio_t);
390 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
393 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
395 target = ratio_t * adj->upper;
399 if (target > adj->upper - adj->page_size)
400 target = adj->upper - adj->page_size;
404 gtk_adjustment_set_value (adj, target);
406 #endif /* !HAVE_GTK2 */
410 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
412 GtkWidget *shell = GTK_WIDGET (user_data);
413 while (shell->parent)
414 shell = shell->parent;
415 gtk_widget_destroy (GTK_WIDGET (shell));
419 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
421 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
423 restart_menu_cb (widget, user_data);
424 warning_dialog_dismiss_cb (widget, user_data);
428 warning_dialog (GtkWidget *parent, const char *message,
429 Boolean restart_button_p, int center)
431 char *msg = strdup (message);
434 GtkWidget *dialog = gtk_dialog_new ();
435 GtkWidget *label = 0;
437 GtkWidget *cancel = 0;
440 while (parent && !parent->window)
441 parent = parent->parent;
444 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
446 fprintf (stderr, "%s: too early for dialog?\n", progname);
454 char *s = strchr (head, '\n');
457 sprintf (name, "label%d", i++);
460 label = gtk_label_new (head);
462 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
463 #endif /* HAVE_GTK2 */
468 GTK_WIDGET (label)->style =
469 gtk_style_copy (GTK_WIDGET (label)->style);
470 GTK_WIDGET (label)->style->font =
471 gdk_font_load (get_string_resource("warning_dialog.headingFont",
473 gtk_widget_set_style (GTK_WIDGET (label),
474 GTK_WIDGET (label)->style);
476 #endif /* !HAVE_GTK2 */
478 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
479 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
480 label, TRUE, TRUE, 0);
481 gtk_widget_show (label);
492 label = gtk_label_new ("");
493 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
494 label, TRUE, TRUE, 0);
495 gtk_widget_show (label);
497 label = gtk_hbutton_box_new ();
498 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
499 label, TRUE, TRUE, 0);
502 if (restart_button_p)
504 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
505 gtk_container_add (GTK_CONTAINER (label), cancel);
508 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
509 gtk_container_add (GTK_CONTAINER (label), ok);
511 #else /* !HAVE_GTK2 */
513 ok = gtk_button_new_with_label ("OK");
514 gtk_container_add (GTK_CONTAINER (label), ok);
516 if (restart_button_p)
518 cancel = gtk_button_new_with_label ("Cancel");
519 gtk_container_add (GTK_CONTAINER (label), cancel);
522 #endif /* !HAVE_GTK2 */
524 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
525 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
526 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
527 STFU GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
528 gtk_widget_show (ok);
529 gtk_widget_grab_focus (ok);
533 STFU GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
534 gtk_widget_show (cancel);
536 gtk_widget_show (label);
537 gtk_widget_show (dialog);
539 if (restart_button_p)
541 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
542 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
544 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
545 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
550 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
551 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
555 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
556 GTK_WIDGET (parent)->window);
559 gtk_window_present (GTK_WINDOW (dialog));
560 #else /* !HAVE_GTK2 */
561 gdk_window_show (GTK_WIDGET (dialog)->window);
562 gdk_window_raise (GTK_WIDGET (dialog)->window);
563 #endif /* !HAVE_GTK2 */
570 run_cmd (state *s, Atom command, int arg)
575 flush_dialog_changes_and_save (s);
576 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
581 sprintf (buf, "Error:\n\n%s", err);
583 strcpy (buf, "Unknown error!");
584 warning_dialog (s->toplevel_widget, buf, False, 100);
591 run_hack (state *s, int list_elt, Bool report_errors_p)
594 if (list_elt < 0) return;
595 hack_number = s->list_elt_to_hack_number[list_elt];
597 flush_dialog_changes_and_save (s);
598 schedule_preview (s, 0);
600 run_cmd (s, XA_DEMO, hack_number + 1);
604 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
616 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
618 state *s = global_state_kludge; /* I hate C so much... */
619 flush_dialog_changes_and_save (s);
620 kill_preview_subproc (s);
625 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
627 state *s = (state *) data;
628 flush_dialog_changes_and_save (s);
635 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
638 char *vers = strdup (screensaver_id + 4);
641 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
643 s = strchr (vers, ',');
647 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
648 non-ASCII characters aren't allowed in localizable string keys."
649 (I don't want to just use (c) instead of © because that doesn't
650 look as good in the plain-old default Latin1 "C" locale.)
653 sprintf(copy, ("Copyright \xC2\xA9 1991-2003 %s"), s);
654 #else /* !HAVE_GTK2 */
655 sprintf(copy, ("Copyright \251 1991-2003 %s"), s);
656 #endif /* !HAVE_GTK2 */
658 sprintf (msg, "%s\n\n%s", copy, desc);
660 /* I can't make gnome_about_new() work here -- it starts dying in
661 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
662 then this might be the thing to do:
666 const gchar *auth[] = { 0 };
667 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
669 gtk_widget_show (about);
671 #else / * GTK but not GNOME * /
675 GdkColormap *colormap;
676 GdkPixmap *gdkpixmap;
679 GtkWidget *dialog = gtk_dialog_new ();
680 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
681 GtkWidget *parent = GTK_WIDGET (menuitem);
682 while (parent->parent)
683 parent = parent->parent;
685 hbox = gtk_hbox_new (FALSE, 20);
686 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
687 hbox, TRUE, TRUE, 0);
689 colormap = gtk_widget_get_colormap (parent);
691 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
692 (gchar **) logo_180_xpm);
693 icon = gtk_pixmap_new (gdkpixmap, mask);
694 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
696 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
698 vbox = gtk_vbox_new (FALSE, 0);
699 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
701 label1 = gtk_label_new (vers);
702 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
703 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
704 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
707 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
708 GTK_WIDGET (label1)->style->font =
709 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
710 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
711 #endif /* HAVE_GTK2 */
713 label2 = gtk_label_new (msg);
714 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
715 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
716 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
719 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
720 GTK_WIDGET (label2)->style->font =
721 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
722 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
723 #endif /* HAVE_GTK2 */
725 hb = gtk_hbutton_box_new ();
727 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
731 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
732 #else /* !HAVE_GTK2 */
733 ok = gtk_button_new_with_label (_("OK"));
734 #endif /* !HAVE_GTK2 */
735 gtk_container_add (GTK_CONTAINER (hb), ok);
737 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
738 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
739 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
741 gtk_widget_show (hbox);
742 gtk_widget_show (icon);
743 gtk_widget_show (vbox);
744 gtk_widget_show (label1);
745 gtk_widget_show (label2);
746 gtk_widget_show (hb);
747 gtk_widget_show (ok);
748 gtk_widget_show (dialog);
750 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
751 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
753 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
754 GTK_WIDGET (parent)->window);
755 gdk_window_show (GTK_WIDGET (dialog)->window);
756 gdk_window_raise (GTK_WIDGET (dialog)->window);
762 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
764 state *s = global_state_kludge; /* I hate C so much... */
765 saver_preferences *p = &s->prefs;
768 if (!p->help_url || !*p->help_url)
770 warning_dialog (s->toplevel_widget,
772 "No Help URL has been specified.\n"), False, 100);
776 help_command = (char *) malloc (strlen (p->load_url_command) +
777 (strlen (p->help_url) * 2) + 20);
778 strcpy (help_command, "( ");
779 sprintf (help_command + strlen(help_command),
780 p->load_url_command, p->help_url, p->help_url);
781 strcat (help_command, " ) &");
782 system (help_command);
788 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
790 state *s = global_state_kludge; /* I hate C so much... */
791 run_cmd (s, XA_ACTIVATE, 0);
796 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
798 state *s = global_state_kludge; /* I hate C so much... */
799 run_cmd (s, XA_LOCK, 0);
804 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
806 state *s = global_state_kludge; /* I hate C so much... */
807 run_cmd (s, XA_EXIT, 0);
812 restart_menu_cb (GtkWidget *widget, gpointer user_data)
814 state *s = global_state_kludge; /* I hate C so much... */
815 flush_dialog_changes_and_save (s);
816 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
818 system ("xscreensaver -nosplash &");
820 await_xscreensaver (s);
824 await_xscreensaver (state *s)
828 Display *dpy = GDK_DISPLAY();
829 /* GtkWidget *dialog = 0;*/
832 while (!rversion && (--countdown > 0))
834 /* Check for the version of the running xscreensaver... */
835 server_xscreensaver_version (dpy, &rversion, 0, 0);
837 /* If it's not there yet, wait a second... */
842 /* if (dialog) gtk_widget_destroy (dialog);*/
851 /* Timed out, no screensaver running. */
854 Bool root_p = (geteuid () == 0);
858 "The xscreensaver daemon did not start up properly.\n"
864 __extension__ /* don't warn about "string length is greater than
865 the length ISO C89 compilers are required to
866 support" in the following expression... */
869 _("You are running as root. This usually means that xscreensaver\n"
870 "was unable to contact your X server because access control is\n"
871 "turned on. Try running this command:\n"
873 " xhost +localhost\n"
875 "and then selecting `File / Restart Daemon'.\n"
877 "Note that turning off access control will allow anyone logged\n"
878 "on to this machine to access your screen, which might be\n"
879 "considered a security problem. Please read the xscreensaver\n"
880 "manual and FAQ for more information.\n"
882 "You shouldn't run X as root. Instead, you should log in as a\n"
883 "normal user, and `su' as necessary."));
885 strcat (buf, _("Please check your $PATH and permissions."));
887 warning_dialog (s->toplevel_widget, buf, False, 1);
893 selected_list_element (state *s)
895 return s->_selected_list_element;
900 demo_write_init_file (state *s, saver_preferences *p)
904 /* #### try to figure out why shit keeps getting reordered... */
905 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
909 if (!write_init_file (p, s->short_version, False))
912 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
917 const char *f = init_file_name();
919 warning_dialog (s->toplevel_widget,
920 _("Error:\n\nCouldn't determine init file name!\n"),
924 char *b = (char *) malloc (strlen(f) + 1024);
925 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
926 warning_dialog (s->toplevel_widget, b, False, 100);
935 run_this_cb (GtkButton *button, gpointer user_data)
937 state *s = global_state_kludge; /* I hate C so much... */
938 int list_elt = selected_list_element (s);
939 if (list_elt < 0) return;
940 if (!flush_dialog_changes_and_save (s))
941 run_hack (s, list_elt, True);
946 manual_cb (GtkButton *button, gpointer user_data)
948 state *s = global_state_kludge; /* I hate C so much... */
949 saver_preferences *p = &s->prefs;
950 GtkWidget *list_widget = name_to_widget (s, "list");
951 int list_elt = selected_list_element (s);
953 char *name, *name2, *cmd, *str;
954 if (list_elt < 0) return;
955 hack_number = s->list_elt_to_hack_number[list_elt];
957 flush_dialog_changes_and_save (s);
958 ensure_selected_item_visible (list_widget);
960 name = strdup (p->screenhacks[hack_number]->command);
962 while (isspace (*name2)) name2++;
964 while (*str && !isspace (*str)) str++;
966 str = strrchr (name2, '/');
967 if (str) name = str+1;
969 cmd = get_string_resource ("manualCommand", "ManualCommand");
972 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
974 sprintf (cmd2 + strlen (cmd2),
976 name2, name2, name2, name2);
977 strcat (cmd2, " ) &");
983 warning_dialog (GTK_WIDGET (button),
984 _("Error:\n\nno `manualCommand' resource set."),
993 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
995 GtkWidget *parent = name_to_widget (s, "scroller");
996 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
1000 GtkTreeSelection *selection;
1001 #endif /* HAVE_GTK2 */
1003 if (!was) gtk_widget_set_sensitive (parent, True);
1005 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1006 STFU g_assert (model);
1007 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
1008 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1009 gtk_tree_selection_select_iter (selection, &iter);
1010 #else /* !HAVE_GTK2 */
1011 gtk_list_select_item (GTK_LIST (list), list_elt);
1012 #endif /* !HAVE_GTK2 */
1013 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1014 if (!was) gtk_widget_set_sensitive (parent, False);
1019 run_next_cb (GtkButton *button, gpointer user_data)
1021 state *s = global_state_kludge; /* I hate C so much... */
1022 /* saver_preferences *p = &s->prefs; */
1023 Bool ops = s->preview_suppressed_p;
1025 GtkWidget *list_widget = name_to_widget (s, "list");
1026 int list_elt = selected_list_element (s);
1033 if (list_elt >= s->list_count)
1036 s->preview_suppressed_p = True;
1038 flush_dialog_changes_and_save (s);
1039 force_list_select_item (s, list_widget, list_elt, True);
1040 populate_demo_window (s, list_elt);
1041 run_hack (s, list_elt, False);
1043 s->preview_suppressed_p = ops;
1048 run_prev_cb (GtkButton *button, gpointer user_data)
1050 state *s = global_state_kludge; /* I hate C so much... */
1051 /* saver_preferences *p = &s->prefs; */
1052 Bool ops = s->preview_suppressed_p;
1054 GtkWidget *list_widget = name_to_widget (s, "list");
1055 int list_elt = selected_list_element (s);
1058 list_elt = s->list_count - 1;
1063 list_elt = s->list_count - 1;
1065 s->preview_suppressed_p = True;
1067 flush_dialog_changes_and_save (s);
1068 force_list_select_item (s, list_widget, list_elt, True);
1069 populate_demo_window (s, list_elt);
1070 run_hack (s, list_elt, False);
1072 s->preview_suppressed_p = ops;
1076 /* Writes the given settings into prefs.
1077 Returns true if there was a change, False otherwise.
1078 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1081 flush_changes (state *s,
1084 const char *command,
1087 saver_preferences *p = &s->prefs;
1088 Bool changed = False;
1091 if (list_elt < 0 || list_elt >= s->list_count)
1094 hack_number = s->list_elt_to_hack_number[list_elt];
1095 hack = p->screenhacks[hack_number];
1097 if (enabled_p != -1 &&
1098 enabled_p != hack->enabled_p)
1100 hack->enabled_p = enabled_p;
1103 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1104 blurb(), hack->name, enabled_p);
1109 if (!hack->command || !!strcmp (command, hack->command))
1111 if (hack->command) free (hack->command);
1112 hack->command = strdup (command);
1115 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1116 blurb(), hack->name, command);
1122 const char *ov = hack->visual;
1123 if (!ov || !*ov) ov = "any";
1124 if (!*visual) visual = "any";
1125 if (!!strcasecmp (visual, ov))
1127 if (hack->visual) free (hack->visual);
1128 hack->visual = strdup (visual);
1131 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1132 blurb(), hack->name, visual);
1140 /* Helper for the text fields that contain time specifications:
1141 this parses the text, and does error checking.
1144 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1149 if (!sec_p || strchr (line, ':'))
1150 value = parse_time ((char *) line, sec_p, True);
1154 if (sscanf (line, "%d%c", &value, &c) != 1)
1160 value *= 1000; /* Time measures in microseconds */
1166 "Unparsable time format: \"%s\"\n"),
1168 warning_dialog (s->toplevel_widget, b, False, 100);
1177 directory_p (const char *path)
1180 if (!path || !*path)
1182 else if (stat (path, &st))
1184 else if (!S_ISDIR (st.st_mode))
1191 normalize_directory (const char *path)
1195 if (!path || !*path) return 0;
1197 p2 = (char *) malloc (L + 2);
1199 if (p2[L-1] == '/') /* remove trailing slash */
1202 for (s = p2; s && *s; s++)
1205 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1206 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1209 while (s0 > p2 && s0[-1] != '/')
1219 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1220 strcpy (s, s+2), s--;
1221 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1225 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1226 while (s[0] == '/' && s[1] == '/')
1229 /* and strip trailing whitespace for good measure. */
1231 while (isspace(p2[L-1]))
1244 } FlushForeachClosure;
1247 flush_checkbox (GtkTreeModel *model,
1252 FlushForeachClosure *closure = data;
1255 gtk_tree_model_get (model, iter,
1256 COL_ENABLED, &checked,
1259 if (flush_changes (closure->s, closure->i,
1261 *closure->changed = True;
1265 /* don't remove row */
1269 #endif /* HAVE_GTK2 */
1271 /* Flush out any changes made in the main dialog window (where changes
1272 take place immediately: clicking on a checkbox causes the init file
1273 to be written right away.)
1276 flush_dialog_changes_and_save (state *s)
1278 saver_preferences *p = &s->prefs;
1279 saver_preferences P2, *p2 = &P2;
1281 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1282 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1283 FlushForeachClosure closure;
1284 #else /* !HAVE_GTK2 */
1285 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1286 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1288 #endif /* !HAVE_GTK2 */
1290 Bool changed = False;
1293 if (s->saving_p) return False;
1298 /* Flush any checkbox changes in the list down into the prefs struct.
1302 closure.changed = &changed;
1304 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1306 #else /* !HAVE_GTK2 */
1308 for (i = 0; kids; kids = kids->next, i++)
1310 GtkWidget *line = GTK_WIDGET (kids->data);
1311 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1312 GtkWidget *line_check =
1313 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1315 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1317 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1320 #endif /* ~HAVE_GTK2 */
1322 /* Flush the non-hack-specific settings down into the prefs struct.
1325 # define SECONDS(FIELD,NAME) \
1326 w = name_to_widget (s, (NAME)); \
1327 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1329 # define MINUTES(FIELD,NAME) \
1330 w = name_to_widget (s, (NAME)); \
1331 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1333 # define CHECKBOX(FIELD,NAME) \
1334 w = name_to_widget (s, (NAME)); \
1335 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1337 # define PATHNAME(FIELD,NAME) \
1338 w = name_to_widget (s, (NAME)); \
1339 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1341 MINUTES (&p2->timeout, "timeout_spinbutton");
1342 MINUTES (&p2->cycle, "cycle_spinbutton");
1343 CHECKBOX (p2->lock_p, "lock_button");
1344 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1346 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1347 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1348 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1349 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1351 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1352 CHECKBOX (p2->grab_video_p, "grab_video_button");
1353 CHECKBOX (p2->random_image_p, "grab_image_button");
1354 PATHNAME (p2->image_directory, "image_text");
1356 CHECKBOX (p2->verbose_p, "verbose_button");
1357 CHECKBOX (p2->capture_stderr_p, "capture_button");
1358 CHECKBOX (p2->splash_p, "splash_button");
1360 CHECKBOX (p2->install_cmap_p, "install_button");
1361 CHECKBOX (p2->fade_p, "fade_button");
1362 CHECKBOX (p2->unfade_p, "unfade_button");
1363 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1370 /* Warn if the image directory doesn't exist.
1372 if (p2->image_directory &&
1373 *p2->image_directory &&
1374 !directory_p (p2->image_directory))
1377 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1378 p2->image_directory);
1379 warning_dialog (s->toplevel_widget, b, False, 100);
1383 /* Map the mode menu to `saver_mode' enum values. */
1385 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1386 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1387 GtkWidget *selected = gtk_menu_get_active (menu);
1388 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1389 int menu_elt = g_list_index (kids, (gpointer) selected);
1390 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1391 p2->mode = mode_menu_order[menu_elt];
1394 if (p2->mode == ONE_HACK)
1396 int list_elt = selected_list_element (s);
1397 p2->selected_hack = (list_elt >= 0
1398 ? s->list_elt_to_hack_number[list_elt]
1402 # define COPY(field, name) \
1403 if (p->field != p2->field) { \
1406 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1408 p->field = p2->field
1411 COPY(selected_hack, "selected_hack");
1413 COPY(timeout, "timeout");
1414 COPY(cycle, "cycle");
1415 COPY(lock_p, "lock_p");
1416 COPY(lock_timeout, "lock_timeout");
1418 COPY(dpms_enabled_p, "dpms_enabled_p");
1419 COPY(dpms_standby, "dpms_standby");
1420 COPY(dpms_suspend, "dpms_suspend");
1421 COPY(dpms_off, "dpms_off");
1423 COPY(verbose_p, "verbose_p");
1424 COPY(capture_stderr_p, "capture_stderr_p");
1425 COPY(splash_p, "splash_p");
1427 COPY(install_cmap_p, "install_cmap_p");
1428 COPY(fade_p, "fade_p");
1429 COPY(unfade_p, "unfade_p");
1430 COPY(fade_seconds, "fade_seconds");
1432 COPY(grab_desktop_p, "grab_desktop_p");
1433 COPY(grab_video_p, "grab_video_p");
1434 COPY(random_image_p, "random_image_p");
1438 if (!p->image_directory ||
1439 !p2->image_directory ||
1440 strcmp(p->image_directory, p2->image_directory))
1444 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1445 blurb(), p2->image_directory);
1447 if (p->image_directory && p->image_directory != p2->image_directory)
1448 free (p->image_directory);
1449 p->image_directory = p2->image_directory;
1450 p2->image_directory = 0;
1452 populate_prefs_page (s);
1456 Display *dpy = GDK_DISPLAY();
1457 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1458 sync_server_dpms_settings (dpy, enabled_p,
1459 p->dpms_standby / 1000,
1460 p->dpms_suspend / 1000,
1464 changed = demo_write_init_file (s, p);
1467 s->saving_p = False;
1472 /* Flush out any changes made in the popup dialog box (where changes
1473 take place only when the OK button is clicked.)
1476 flush_popup_changes_and_save (state *s)
1478 Bool changed = False;
1479 saver_preferences *p = &s->prefs;
1480 int list_elt = selected_list_element (s);
1482 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1483 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1485 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1486 const char *command = gtk_entry_get_text (cmd);
1491 if (s->saving_p) return False;
1497 if (maybe_reload_init_file (s) != 0)
1503 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1505 if (!strcasecmp (visual, "")) visual = "";
1506 else if (!strcasecmp (visual, "any")) visual = "";
1507 else if (!strcasecmp (visual, "default")) visual = "Default";
1508 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1509 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1510 else if (!strcasecmp (visual, "best")) visual = "Best";
1511 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1512 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1513 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1514 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1515 else if (!strcasecmp (visual, "color")) visual = "Color";
1516 else if (!strcasecmp (visual, "gl")) visual = "GL";
1517 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1518 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1519 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1520 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1521 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1522 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1523 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1524 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1525 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1528 gdk_beep (); /* unparsable */
1530 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1533 changed = flush_changes (s, list_elt, -1, command, visual);
1536 changed = demo_write_init_file (s, p);
1538 /* Do this to re-launch the hack if (and only if) the command line
1540 populate_demo_window (s, selected_list_element (s));
1544 s->saving_p = False;
1550 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1552 state *s = global_state_kludge; /* I hate C so much... */
1553 if (! s->initializing_p)
1555 s->initializing_p = True;
1556 flush_dialog_changes_and_save (s);
1557 s->initializing_p = False;
1562 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1564 pref_changed_cb (widget, user_data);
1568 /* Callback on menu items in the "mode" options menu.
1571 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1573 state *s = (state *) user_data;
1574 saver_preferences *p = &s->prefs;
1575 GtkWidget *list = name_to_widget (s, "list");
1578 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1580 saver_mode new_mode;
1584 if (menu_items->data == widget)
1587 menu_items = menu_items->next;
1589 if (!menu_items) abort();
1591 new_mode = mode_menu_order[menu_index];
1593 /* Keep the same list element displayed as before; except if we're
1594 switching *to* "one screensaver" mode from any other mode, set
1595 "the one" to be that which is currently selected.
1597 list_elt = selected_list_element (s);
1598 if (new_mode == ONE_HACK)
1599 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1602 saver_mode old_mode = p->mode;
1604 populate_demo_window (s, list_elt);
1605 force_list_select_item (s, list, list_elt, True);
1606 p->mode = old_mode; /* put it back, so the init file gets written */
1609 pref_changed_cb (widget, user_data);
1614 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1615 gint page_num, gpointer user_data)
1617 state *s = global_state_kludge; /* I hate C so much... */
1618 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1620 /* If we're switching to page 0, schedule the current hack to be run.
1621 Otherwise, schedule it to stop. */
1623 populate_demo_window (s, selected_list_element (s));
1625 schedule_preview (s, 0);
1630 list_activated_cb (GtkTreeView *list,
1632 GtkTreeViewColumn *column,
1639 STFU g_return_if_fail (!gdk_pointer_is_grabbed ());
1641 str = gtk_tree_path_to_string (path);
1642 list_elt = strtol (str, NULL, 10);
1646 run_hack (s, list_elt, True);
1650 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1652 state *s = (state *)data;
1653 GtkTreeModel *model;
1659 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1662 path = gtk_tree_model_get_path (model, &iter);
1663 str = gtk_tree_path_to_string (path);
1664 list_elt = strtol (str, NULL, 10);
1666 gtk_tree_path_free (path);
1669 populate_demo_window (s, list_elt);
1670 flush_dialog_changes_and_save (s);
1673 #else /* !HAVE_GTK2 */
1675 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1676 list_select_cb that comes in
1677 *after* we've double-clicked.
1681 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1684 state *s = (state *) data;
1685 if (event->type == GDK_2BUTTON_PRESS)
1687 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1688 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1690 last_doubleclick_time = time ((time_t *) 0);
1693 run_hack (s, list_elt, True);
1701 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1703 state *s = (state *) data;
1704 time_t now = time ((time_t *) 0);
1706 if (now >= last_doubleclick_time + 2)
1708 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1709 populate_demo_window (s, list_elt);
1710 flush_dialog_changes_and_save (s);
1715 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1717 state *s = (state *) data;
1718 populate_demo_window (s, -1);
1719 flush_dialog_changes_and_save (s);
1722 #endif /* !HAVE_GTK2 */
1725 /* Called when the checkboxes that are in the left column of the
1726 scrolling list are clicked. This both populates the right pane
1727 (just as clicking on the label (really, listitem) does) and
1728 also syncs this checkbox with the right pane Enabled checkbox.
1733 GtkCellRendererToggle *toggle,
1735 #else /* !HAVE_GTK2 */
1737 #endif /* !HAVE_GTK2 */
1740 state *s = (state *) data;
1743 GtkScrolledWindow *scroller =
1744 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1745 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1746 GtkTreeModel *model = gtk_tree_view_get_model (list);
1747 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1750 #else /* !HAVE_GTK2 */
1751 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1752 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1754 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1755 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1756 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1757 #endif /* !HAVE_GTK2 */
1764 if (!gtk_tree_model_get_iter (model, &iter, path))
1766 g_warning ("bad path: %s", path_string);
1769 gtk_tree_path_free (path);
1771 gtk_tree_model_get (model, &iter,
1772 COL_ENABLED, &active,
1775 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1776 COL_ENABLED, !active,
1779 list_elt = strtol (path_string, NULL, 10);
1780 #else /* !HAVE_GTK2 */
1781 list_elt = gtk_list_child_position (list, line);
1782 #endif /* !HAVE_GTK2 */
1784 /* remember previous scroll position of the top of the list */
1785 adj = gtk_scrolled_window_get_vadjustment (scroller);
1786 scroll_top = adj->value;
1788 flush_dialog_changes_and_save (s);
1789 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1790 populate_demo_window (s, list_elt);
1792 /* restore the previous scroll position of the top of the list.
1793 this is weak, but I don't really know why it's moving... */
1794 gtk_adjustment_set_value (adj, scroll_top);
1800 GtkFileSelection *widget;
1801 } file_selection_data;
1806 store_image_directory (GtkWidget *button, gpointer user_data)
1808 file_selection_data *fsd = (file_selection_data *) user_data;
1809 state *s = fsd->state;
1810 GtkFileSelection *selector = fsd->widget;
1811 GtkWidget *top = s->toplevel_widget;
1812 saver_preferences *p = &s->prefs;
1813 const char *path = gtk_file_selection_get_filename (selector);
1815 if (p->image_directory && !strcmp(p->image_directory, path))
1816 return; /* no change */
1818 if (!directory_p (path))
1821 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1822 warning_dialog (GTK_WIDGET (top), b, False, 100);
1826 if (p->image_directory) free (p->image_directory);
1827 p->image_directory = normalize_directory (path);
1829 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1830 (p->image_directory ? p->image_directory : ""));
1831 demo_write_init_file (s, p);
1836 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1838 file_selection_data *fsd = (file_selection_data *) user_data;
1839 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1843 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1845 browse_image_dir_cancel (button, user_data);
1846 store_image_directory (button, user_data);
1850 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1852 browse_image_dir_cancel (widget, user_data);
1857 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1859 state *s = global_state_kludge; /* I hate C so much... */
1860 saver_preferences *p = &s->prefs;
1861 static file_selection_data *fsd = 0;
1863 GtkFileSelection *selector = GTK_FILE_SELECTION(
1864 gtk_file_selection_new ("Please select the image directory."));
1867 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1869 fsd->widget = selector;
1872 if (p->image_directory && *p->image_directory)
1873 gtk_file_selection_set_filename (selector, p->image_directory);
1875 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1876 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1878 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1879 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1881 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1882 GTK_SIGNAL_FUNC (browse_image_dir_close),
1885 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1887 gtk_window_set_modal (GTK_WINDOW (selector), True);
1888 gtk_widget_show (GTK_WIDGET (selector));
1893 settings_cb (GtkButton *button, gpointer user_data)
1895 state *s = global_state_kludge; /* I hate C so much... */
1896 int list_elt = selected_list_element (s);
1898 populate_demo_window (s, list_elt); /* reset the widget */
1899 populate_popup_window (s); /* create UI on popup window */
1900 gtk_widget_show (s->popup_widget);
1904 settings_sync_cmd_text (state *s)
1907 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1908 char *cmd_line = get_configurator_command_line (s->cdata);
1909 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1910 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1912 # endif /* HAVE_XML */
1916 settings_adv_cb (GtkButton *button, gpointer user_data)
1918 state *s = global_state_kludge; /* I hate C so much... */
1919 GtkNotebook *notebook =
1920 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1922 settings_sync_cmd_text (s);
1923 gtk_notebook_set_page (notebook, 1);
1927 settings_std_cb (GtkButton *button, gpointer user_data)
1929 state *s = global_state_kludge; /* I hate C so much... */
1930 GtkNotebook *notebook =
1931 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1933 /* Re-create UI to reflect the in-progress command-line settings. */
1934 populate_popup_window (s);
1936 gtk_notebook_set_page (notebook, 0);
1940 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1941 gint page_num, gpointer user_data)
1943 state *s = global_state_kludge; /* I hate C so much... */
1944 GtkWidget *adv = name_to_widget (s, "adv_button");
1945 GtkWidget *std = name_to_widget (s, "std_button");
1949 gtk_widget_show (adv);
1950 gtk_widget_hide (std);
1952 else if (page_num == 1)
1954 gtk_widget_hide (adv);
1955 gtk_widget_show (std);
1964 settings_cancel_cb (GtkButton *button, gpointer user_data)
1966 state *s = global_state_kludge; /* I hate C so much... */
1967 gtk_widget_hide (s->popup_widget);
1971 settings_ok_cb (GtkButton *button, gpointer user_data)
1973 state *s = global_state_kludge; /* I hate C so much... */
1974 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1975 int page = gtk_notebook_get_current_page (notebook);
1978 /* Regenerate the command-line from the widget contents before saving.
1979 But don't do this if we're looking at the command-line page already,
1980 or we will blow away what they typed... */
1981 settings_sync_cmd_text (s);
1983 flush_popup_changes_and_save (s);
1984 gtk_widget_hide (s->popup_widget);
1988 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1990 state *s = (state *) data;
1991 settings_cancel_cb (0, (gpointer) s);
1997 /* Populating the various widgets
2001 /* Returns the number of the last hack run by the server.
2004 server_current_hack (void)
2008 unsigned long nitems, bytesafter;
2010 Display *dpy = GDK_DISPLAY();
2011 int hack_number = -1;
2013 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2014 XA_SCREENSAVER_STATUS,
2015 0, 3, False, XA_INTEGER,
2016 &type, &format, &nitems, &bytesafter,
2017 (unsigned char **) &data)
2019 && type == XA_INTEGER
2022 hack_number = (int) data[2] - 1;
2024 if (data) free (data);
2030 /* Finds the number of the last hack to run, and makes that item be
2031 selected by default.
2034 scroll_to_current_hack (state *s)
2036 saver_preferences *p = &s->prefs;
2039 if (p->mode == ONE_HACK)
2040 hack_number = p->selected_hack;
2042 hack_number = server_current_hack ();
2044 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2046 int list_elt = s->hack_number_to_list_elt[hack_number];
2047 GtkWidget *list = name_to_widget (s, "list");
2048 force_list_select_item (s, list, list_elt, True);
2049 populate_demo_window (s, list_elt);
2055 on_path_p (const char *program)
2059 char *cmd = strdup (program);
2060 char *token = strchr (cmd, ' ');
2064 if (token) *token = 0;
2067 if (strchr (cmd, '/'))
2069 result = (0 == stat (cmd, &st));
2073 path = getenv("PATH");
2074 if (!path || !*path)
2078 path = strdup (path);
2079 token = strtok (path, ":");
2083 char *p2 = (char *) malloc (strlen (token) + L + 3);
2087 result = (0 == stat (p2, &st));
2090 token = strtok (0, ":");
2095 if (path) free (path);
2101 populate_hack_list (state *s)
2104 saver_preferences *p = &s->prefs;
2105 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2106 GtkListStore *model;
2107 GtkTreeSelection *selection;
2108 GtkCellRenderer *ren;
2112 g_object_get (G_OBJECT (list),
2117 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2118 g_object_set (G_OBJECT (list), "model", model, NULL);
2119 g_object_unref (model);
2121 ren = gtk_cell_renderer_toggle_new ();
2122 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2124 "active", COL_ENABLED,
2127 g_signal_connect (ren, "toggled",
2128 G_CALLBACK (list_checkbox_cb),
2131 ren = gtk_cell_renderer_text_new ();
2132 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2133 _("Screen Saver"), ren,
2137 g_signal_connect_after (list, "row_activated",
2138 G_CALLBACK (list_activated_cb),
2141 selection = gtk_tree_view_get_selection (list);
2142 g_signal_connect (selection, "changed",
2143 G_CALLBACK (list_select_changed_cb),
2148 for (i = 0; i < s->list_count; i++)
2150 int hack_number = s->list_elt_to_hack_number[i];
2151 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2153 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2155 if (!hack) continue;
2157 /* If we're to suppress uninstalled hacks, check $PATH now. */
2158 if (p->ignore_uninstalled_p && !available_p)
2161 pretty_name = (hack->name
2162 ? strdup (hack->name)
2163 : make_hack_name (hack->command));
2167 /* Make the text foreground be the color of insensitive widgets
2168 (but don't actually make it be insensitive, since we still
2169 want to be able to click on it.)
2171 GtkStyle *style = GTK_WIDGET (list)->style;
2172 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2173 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2174 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2176 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2177 /* " background=\"#%02X%02X%02X\"" */
2179 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2180 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2186 gtk_list_store_append (model, &iter);
2187 gtk_list_store_set (model, &iter,
2188 COL_ENABLED, hack->enabled_p,
2189 COL_NAME, pretty_name,
2194 #else /* !HAVE_GTK2 */
2196 saver_preferences *p = &s->prefs;
2197 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2199 for (i = 0; i < s->list_count; i++)
2201 int hack_number = s->list_elt_to_hack_number[i];
2202 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2204 /* A GtkList must contain only GtkListItems, but those can contain
2205 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2206 and a Label. We handle single and double click events on the
2207 line itself, for clicking on the text, but the interior checkbox
2208 also handles its own events.
2211 GtkWidget *line_hbox;
2212 GtkWidget *line_check;
2213 GtkWidget *line_label;
2215 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2217 if (!hack) continue;
2219 /* If we're to suppress uninstalled hacks, check $PATH now. */
2220 if (p->ignore_uninstalled_p && !available_p)
2223 pretty_name = (hack->name
2224 ? strdup (hack->name)
2225 : make_hack_name (hack->command));
2227 line = gtk_list_item_new ();
2228 line_hbox = gtk_hbox_new (FALSE, 0);
2229 line_check = gtk_check_button_new ();
2230 line_label = gtk_label_new (pretty_name);
2232 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2233 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2234 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2236 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2238 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2240 gtk_widget_show (line_check);
2241 gtk_widget_show (line_label);
2242 gtk_widget_show (line_hbox);
2243 gtk_widget_show (line);
2247 gtk_container_add (GTK_CONTAINER (list), line);
2248 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2249 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2252 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2253 GTK_SIGNAL_FUNC (list_checkbox_cb),
2256 gtk_widget_show (line);
2260 /* Make the widget be colored like insensitive widgets
2261 (but don't actually make it be insensitive, since we
2262 still want to be able to click on it.)
2264 GtkRcStyle *rc_style;
2267 gtk_widget_realize (GTK_WIDGET (line_label));
2269 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2270 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2272 rc_style = gtk_rc_style_new ();
2273 rc_style->fg[GTK_STATE_NORMAL] = fg;
2274 rc_style->bg[GTK_STATE_NORMAL] = bg;
2275 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2277 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2278 gtk_rc_style_unref (rc_style);
2282 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2283 GTK_SIGNAL_FUNC (list_select_cb),
2285 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2286 GTK_SIGNAL_FUNC (list_unselect_cb),
2288 #endif /* !HAVE_GTK2 */
2292 update_list_sensitivity (state *s)
2294 saver_preferences *p = &s->prefs;
2295 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2296 Bool checkable = (p->mode == RANDOM_HACKS);
2297 Bool blankable = (p->mode != DONT_BLANK);
2300 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2301 GtkWidget *use = name_to_widget (s, "use_col_frame");
2302 #endif /* HAVE_GTK2 */
2303 GtkWidget *scroller = name_to_widget (s, "scroller");
2304 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2305 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2308 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2309 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2310 #else /* !HAVE_GTK2 */
2311 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2312 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2314 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2315 #endif /* !HAVE_GTK2 */
2316 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2317 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2319 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2322 gtk_tree_view_column_set_visible (use, checkable);
2323 #else /* !HAVE_GTK2 */
2325 gtk_widget_show (use); /* the "Use" column header */
2327 gtk_widget_hide (use);
2331 GtkBin *line = GTK_BIN (kids->data);
2332 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2333 GtkWidget *line_check =
2334 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2337 gtk_widget_show (line_check);
2339 gtk_widget_hide (line_check);
2343 #endif /* !HAVE_GTK2 */
2348 populate_prefs_page (state *s)
2350 saver_preferences *p = &s->prefs;
2352 Bool can_lock_p = True;
2354 /* Disable all the "lock" controls if locking support was not provided
2355 at compile-time, or if running on MacOS. */
2356 # if defined(NO_LOCKING) || defined(__APPLE__)
2361 /* The file supports timeouts of less than a minute, but the GUI does
2362 not, so throttle the values to be at least one minute (since "0" is
2363 a bad rounding choice...)
2365 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2368 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2371 # define FMT_MINUTES(NAME,N) \
2372 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2374 # define FMT_SECONDS(NAME,N) \
2375 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2377 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2378 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2379 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2380 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2381 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2382 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2383 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2388 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2389 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2392 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2393 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2394 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2395 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2396 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2397 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2398 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2399 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2400 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2401 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2402 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2404 # undef TOGGLE_ACTIVE
2406 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2407 (p->image_directory ? p->image_directory : ""));
2408 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2410 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2413 /* Map the `saver_mode' enum to mode menu to values. */
2415 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2418 for (i = 0; i < countof(mode_menu_order); i++)
2419 if (mode_menu_order[i] == p->mode)
2421 gtk_option_menu_set_history (opt, i);
2422 update_list_sensitivity (s);
2426 Bool found_any_writable_cells = False;
2427 Bool dpms_supported = False;
2429 Display *dpy = GDK_DISPLAY();
2430 int nscreens = ScreenCount(dpy);
2432 for (i = 0; i < nscreens; i++)
2434 Screen *s = ScreenOfDisplay (dpy, i);
2435 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2437 found_any_writable_cells = True;
2442 #ifdef HAVE_XF86VMODE_GAMMA
2443 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2446 #ifdef HAVE_DPMS_EXTENSION
2448 int op = 0, event = 0, error = 0;
2449 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2450 dpms_supported = True;
2452 #endif /* HAVE_DPMS_EXTENSION */
2455 # define SENSITIZE(NAME,SENSITIVEP) \
2456 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2458 /* Blanking and Locking
2460 SENSITIZE ("lock_button", can_lock_p);
2461 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2462 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2466 SENSITIZE ("dpms_frame", dpms_supported);
2467 SENSITIZE ("dpms_button", dpms_supported);
2468 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2469 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2470 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2471 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2472 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2473 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2474 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2475 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2476 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2480 SENSITIZE ("cmap_frame", found_any_writable_cells);
2481 SENSITIZE ("install_button", found_any_writable_cells);
2482 SENSITIZE ("fade_button", found_any_writable_cells);
2483 SENSITIZE ("unfade_button", found_any_writable_cells);
2485 SENSITIZE ("fade_label", (found_any_writable_cells &&
2486 (p->fade_p || p->unfade_p)));
2487 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2488 (p->fade_p || p->unfade_p)));
2496 populate_popup_window (state *s)
2498 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2499 char *doc_string = 0;
2501 /* #### not in Gtk 1.2
2502 gtk_label_set_selectable (doc);
2508 free_conf_data (s->cdata);
2513 saver_preferences *p = &s->prefs;
2514 int list_elt = selected_list_element (s);
2515 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2516 ? s->list_elt_to_hack_number[list_elt]
2518 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2521 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2522 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2523 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2524 s->cdata = load_configurator (cmd_line, s->debug_p);
2525 if (s->cdata && s->cdata->widget)
2526 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2531 doc_string = (s->cdata
2532 ? s->cdata->description
2534 # else /* !HAVE_XML */
2535 doc_string = _("Descriptions not available: no XML support compiled in.");
2536 # endif /* !HAVE_XML */
2538 gtk_label_set_text (doc, (doc_string
2540 : _("No description available.")));
2545 sensitize_demo_widgets (state *s, Bool sensitive_p)
2547 const char *names1[] = { "demo", "settings" };
2548 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2549 "visual", "visual_combo" };
2551 for (i = 0; i < countof(names1); i++)
2553 GtkWidget *w = name_to_widget (s, names1[i]);
2554 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2556 for (i = 0; i < countof(names2); i++)
2558 GtkWidget *w = name_to_widget (s, names2[i]);
2559 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2564 /* Even though we've given these text fields a maximum number of characters,
2565 their default size is still about 30 characters wide -- so measure out
2566 a string in their font, and resize them to just fit that.
2569 fix_text_entry_sizes (state *s)
2573 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2574 const char * const spinbuttons[] = {
2575 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2576 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2577 "dpms_off_spinbutton",
2578 "-fade_spinbutton" };
2582 for (i = 0; i < countof(spinbuttons); i++)
2584 const char *n = spinbuttons[i];
2586 while (*n == '-') n++, cols--;
2587 w = GTK_WIDGET (name_to_widget (s, n));
2588 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2589 gtk_widget_set_usize (w, width, -2);
2592 /* Now fix the width of the combo box.
2594 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2595 w = GTK_COMBO (w)->entry;
2596 width = gdk_string_width (w->style->font, "PseudoColor___");
2597 gtk_widget_set_usize (w, width, -2);
2599 /* Now fix the width of the file entry text.
2601 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2602 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2603 gtk_widget_set_usize (w, width, -2);
2605 /* Now fix the width of the command line text.
2607 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2608 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2609 gtk_widget_set_usize (w, width, -2);
2613 /* Now fix the height of the list widget:
2614 make it default to being around 10 text-lines high instead of 4.
2616 w = GTK_WIDGET (name_to_widget (s, "list"));
2620 int leading = 3; /* approximate is ok... */
2624 PangoFontMetrics *pain =
2625 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2626 w->style->font_desc,
2627 gtk_get_default_language ());
2628 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2629 pango_font_metrics_get_descent (pain));
2630 #else /* !HAVE_GTK2 */
2631 height = w->style->font->ascent + w->style->font->descent;
2632 #endif /* !HAVE_GTK2 */
2636 height += border * 2;
2637 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2638 gtk_widget_set_usize (w, -2, height);
2645 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2648 static char *up_arrow_xpm[] = {
2671 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2672 the end of the array (Gtk 1.2.5.) */
2673 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2674 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2677 static char *down_arrow_xpm[] = {
2700 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2701 the end of the array (Gtk 1.2.5.) */
2702 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2703 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2707 pixmapify_button (state *s, int down_p)
2711 GtkWidget *pixmapwid;
2715 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2716 style = gtk_widget_get_style (w);
2718 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2719 &style->bg[GTK_STATE_NORMAL],
2721 ? (gchar **) down_arrow_xpm
2722 : (gchar **) up_arrow_xpm));
2723 pixmapwid = gtk_pixmap_new (pixmap, mask);
2724 gtk_widget_show (pixmapwid);
2725 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2726 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2730 map_next_button_cb (GtkWidget *w, gpointer user_data)
2732 state *s = (state *) user_data;
2733 pixmapify_button (s, 1);
2737 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2739 state *s = (state *) user_data;
2740 pixmapify_button (s, 0);
2742 #endif /* !HAVE_GTK2 */
2745 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2749 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2750 GtkAllocation *allocation,
2754 GtkWidgetAuxInfo *aux_info;
2756 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2758 aux_info->width = allocation->width;
2759 aux_info->height = -2;
2763 gtk_widget_size_request (label, &req);
2767 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2770 eschew_gtk_lossage (GtkLabel *label)
2772 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2773 aux_info->width = GTK_WIDGET (label)->allocation.width;
2774 aux_info->height = -2;
2778 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2780 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2781 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2784 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2786 gtk_widget_queue_resize (GTK_WIDGET (label));
2791 populate_demo_window (state *s, int list_elt)
2793 saver_preferences *p = &s->prefs;
2796 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2797 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2798 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2799 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2800 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2802 if (p->mode == BLANK_ONLY)
2805 pretty_name = strdup (_("Blank Screen"));
2806 schedule_preview (s, 0);
2808 else if (p->mode == DONT_BLANK)
2811 pretty_name = strdup (_("Screen Saver Disabled"));
2812 schedule_preview (s, 0);
2816 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2817 ? s->list_elt_to_hack_number[list_elt]
2819 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2823 ? strdup (hack->name)
2824 : make_hack_name (hack->command))
2828 schedule_preview (s, hack->command);
2830 schedule_preview (s, 0);
2834 pretty_name = strdup (_("Preview"));
2836 gtk_frame_set_label (frame1, pretty_name);
2837 gtk_frame_set_label (frame2, pretty_name);
2839 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2840 gtk_entry_set_position (cmd, 0);
2844 sprintf (title, "%s: %.100s Settings",
2845 progclass, (pretty_name ? pretty_name : "???"));
2846 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2849 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2851 ? (hack->visual && *hack->visual
2856 sensitize_demo_widgets (s, (hack ? True : False));
2858 if (pretty_name) free (pretty_name);
2860 ensure_selected_item_visible (list);
2862 s->_selected_list_element = list_elt;
2867 widget_deleter (GtkWidget *widget, gpointer data)
2869 /* #### Well, I want to destroy these widgets, but if I do that, they get
2870 referenced again, and eventually I get a SEGV. So instead of
2871 destroying them, I'll just hide them, and leak a bunch of memory
2872 every time the disk file changes. Go go go Gtk!
2874 #### Ok, that's a lie, I get a crash even if I just hide the widget
2875 and don't ever delete it. Fuck!
2878 gtk_widget_destroy (widget);
2880 gtk_widget_hide (widget);
2885 static char **sort_hack_cmp_names_kludge;
2887 sort_hack_cmp (const void *a, const void *b)
2893 int aa = *(int *) a;
2894 int bb = *(int *) b;
2895 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
2896 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
2897 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
2903 initialize_sort_map (state *s)
2905 saver_preferences *p = &s->prefs;
2908 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2909 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2910 if (s->hacks_available_p) free (s->hacks_available_p);
2912 s->list_elt_to_hack_number = (int *)
2913 calloc (sizeof(int), p->screenhacks_count + 1);
2914 s->hack_number_to_list_elt = (int *)
2915 calloc (sizeof(int), p->screenhacks_count + 1);
2916 s->hacks_available_p = (Bool *)
2917 calloc (sizeof(Bool), p->screenhacks_count + 1);
2919 /* Check which hacks actually exist on $PATH
2921 for (i = 0; i < p->screenhacks_count; i++)
2923 screenhack *hack = p->screenhacks[i];
2924 s->hacks_available_p[i] = on_path_p (hack->command);
2927 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
2931 for (i = 0; i < p->screenhacks_count; i++)
2933 if (!p->ignore_uninstalled_p ||
2934 s->hacks_available_p[i])
2935 s->list_elt_to_hack_number[j++] = i;
2939 for (; j < p->screenhacks_count; j++)
2940 s->list_elt_to_hack_number[j] = -1;
2943 /* Generate list of sortable names (once)
2945 sort_hack_cmp_names_kludge = (char **)
2946 calloc (sizeof(char *), p->screenhacks_count);
2947 for (i = 0; i < p->screenhacks_count; i++)
2949 screenhack *hack = p->screenhacks[i];
2950 char *name = (hack->name && *hack->name
2951 ? strdup (hack->name)
2952 : make_hack_name (hack->command));
2954 for (str = name; *str; str++)
2955 *str = tolower(*str);
2956 sort_hack_cmp_names_kludge[i] = name;
2959 /* Sort list->hack map alphabetically
2961 qsort (s->list_elt_to_hack_number,
2962 p->screenhacks_count,
2963 sizeof(*s->list_elt_to_hack_number),
2968 for (i = 0; i < p->screenhacks_count; i++)
2969 free (sort_hack_cmp_names_kludge[i]);
2970 free (sort_hack_cmp_names_kludge);
2971 sort_hack_cmp_names_kludge = 0;
2973 /* Build inverse table */
2974 for (i = 0; i < p->screenhacks_count; i++)
2975 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2980 maybe_reload_init_file (state *s)
2982 saver_preferences *p = &s->prefs;
2985 static Bool reentrant_lock = False;
2986 if (reentrant_lock) return 0;
2987 reentrant_lock = True;
2989 if (init_file_changed_p (p))
2991 const char *f = init_file_name();
2996 if (!f || !*f) return 0;
2997 b = (char *) malloc (strlen(f) + 1024);
3000 "file \"%s\" has changed, reloading.\n"),
3002 warning_dialog (s->toplevel_widget, b, False, 100);
3006 initialize_sort_map (s);
3008 list_elt = selected_list_element (s);
3009 list = name_to_widget (s, "list");
3010 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3011 populate_hack_list (s);
3012 force_list_select_item (s, list, list_elt, True);
3013 populate_prefs_page (s);
3014 populate_demo_window (s, list_elt);
3015 ensure_selected_item_visible (list);
3020 reentrant_lock = False;
3026 /* Making the preview window have the right X visual (so that GL works.)
3029 static Visual *get_best_gl_visual (state *);
3032 x_visual_to_gdk_visual (Visual *xv)
3034 GList *gvs = gdk_list_visuals();
3035 if (!xv) return gdk_visual_get_system();
3036 for (; gvs; gvs = gvs->next)
3038 GdkVisual *gv = (GdkVisual *) gvs->data;
3039 if (xv == GDK_VISUAL_XVISUAL (gv))
3042 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3043 blurb(), (unsigned long) xv->visualid);
3048 clear_preview_window (state *s)
3053 if (!s->toplevel_widget) return; /* very early */
3054 p = name_to_widget (s, "preview");
3057 if (!window) return;
3059 /* Flush the widget background down into the window, in case a subproc
3061 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3062 gdk_window_clear (window);
3065 int list_elt = selected_list_element (s);
3066 int hack_number = (list_elt >= 0
3067 ? s->list_elt_to_hack_number[list_elt]
3069 Bool available_p = (hack_number >= 0
3070 ? s->hacks_available_p [hack_number]
3073 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3074 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3075 (s->running_preview_error_p
3076 ? (available_p ? 1 : 2)
3078 #else /* !HAVE_GTK2 */
3079 if (s->running_preview_error_p)
3081 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3082 const char * const lines2[] = { N_("Not"), N_("Installed") };
3083 int nlines = countof(lines1);
3084 int lh = p->style->font->ascent + p->style->font->descent;
3088 const char * const *lines = (available_p ? lines1 : lines2);
3090 gdk_window_get_size (window, &w, &h);
3091 y = (h - (lh * nlines)) / 2;
3092 y += p->style->font->ascent;
3093 for (i = 0; i < nlines; i++)
3095 int sw = gdk_string_width (p->style->font, _(lines[i]));
3096 int x = (w - sw) / 2;
3097 gdk_draw_string (window, p->style->font,
3098 p->style->fg_gc[GTK_STATE_NORMAL],
3103 #endif /* !HAVE_GTK2 */
3111 fix_preview_visual (state *s)
3113 GtkWidget *widget = name_to_widget (s, "preview");
3114 Visual *xvisual = get_best_gl_visual (s);
3115 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3116 GdkVisual *dvisual = gdk_visual_get_system();
3117 GdkColormap *cmap = (visual == dvisual
3118 ? gdk_colormap_get_system ()
3119 : gdk_colormap_new (visual, False));
3122 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3123 (visual == dvisual ? "default" : "non-default"),
3124 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3126 if (!GTK_WIDGET_REALIZED (widget) ||
3127 gtk_widget_get_visual (widget) != visual)
3129 gtk_widget_unrealize (widget);
3130 gtk_widget_set_visual (widget, visual);
3131 gtk_widget_set_colormap (widget, cmap);
3132 gtk_widget_realize (widget);
3135 /* Set the Widget colors to be white-on-black. */
3137 GdkWindow *window = widget->window;
3138 GtkStyle *style = gtk_style_copy (widget->style);
3139 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3140 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3141 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3142 GdkGC *fgc = gdk_gc_new(window);
3143 GdkGC *bgc = gdk_gc_new(window);
3144 if (!gdk_color_white (cmap, fg)) abort();
3145 if (!gdk_color_black (cmap, bg)) abort();
3146 gdk_gc_set_foreground (fgc, fg);
3147 gdk_gc_set_background (fgc, bg);
3148 gdk_gc_set_foreground (bgc, bg);
3149 gdk_gc_set_background (bgc, fg);
3150 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3151 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3152 gtk_widget_set_style (widget, style);
3154 /* For debugging purposes, put a title on the window (so that
3155 it can be easily found in the output of "xwininfo -tree".)
3157 gdk_window_set_title (window, "Preview");
3160 gtk_widget_show (widget);
3168 subproc_pretty_name (state *s)
3170 if (s->running_preview_cmd)
3172 char *ps = strdup (s->running_preview_cmd);
3173 char *ss = strchr (ps, ' ');
3175 ss = strrchr (ps, '/');
3186 return strdup ("???");
3191 reap_zombies (state *s)
3193 int wait_status = 0;
3195 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3199 if (pid == s->running_preview_pid)
3201 char *ss = subproc_pretty_name (s);
3202 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3203 (unsigned long) pid, ss);
3207 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3208 (unsigned long) pid);
3214 /* Mostly lifted from driver/subprocs.c */
3216 get_best_gl_visual (state *s)
3218 Display *dpy = GDK_DISPLAY();
3227 av[ac++] = "xscreensaver-gl-helper";
3232 perror ("error creating pipe:");
3239 switch ((int) (forked = fork ()))
3243 sprintf (buf, "%s: couldn't fork", blurb());
3251 close (in); /* don't need this one */
3252 close (ConnectionNumber (dpy)); /* close display fd */
3254 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3256 perror ("could not dup() a new stdout:");
3260 execvp (av[0], av); /* shouldn't return. */
3262 if (errno != ENOENT)
3264 /* Ignore "no such file or directory" errors, unless verbose.
3265 Issue all other exec errors, though. */
3266 sprintf (buf, "%s: running %s", blurb(), av[0]);
3270 /* Note that one must use _exit() instead of exit() in procs forked
3271 off of Gtk programs -- Gtk installs an atexit handler that has a
3272 copy of the X connection (which we've already closed, for safety.)
3273 If one uses exit() instead of _exit(), then one sometimes gets a
3274 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3276 _exit (1); /* exits fork */
3282 int wait_status = 0;
3284 FILE *f = fdopen (in, "r");
3288 close (out); /* don't need this one */
3291 fgets (buf, sizeof(buf)-1, f);
3294 /* Wait for the child to die. */
3295 waitpid (-1, &wait_status, 0);
3297 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3303 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3309 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3311 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3312 blurb(), av[0], result);
3324 kill_preview_subproc (state *s)
3326 s->running_preview_error_p = False;
3329 clear_preview_window (s);
3331 if (s->subproc_check_timer_id)
3333 gtk_timeout_remove (s->subproc_check_timer_id);
3334 s->subproc_check_timer_id = 0;
3335 s->subproc_check_countdown = 0;
3338 if (s->running_preview_pid)
3340 int status = kill (s->running_preview_pid, SIGTERM);
3341 char *ss = subproc_pretty_name (s);
3348 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3349 blurb(), (unsigned long) s->running_preview_pid, ss);
3354 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3355 blurb(), (unsigned long) s->running_preview_pid, ss);
3359 else if (s->debug_p)
3360 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3361 (unsigned long) s->running_preview_pid, ss);
3364 s->running_preview_pid = 0;
3365 if (s->running_preview_cmd) free (s->running_preview_cmd);
3366 s->running_preview_cmd = 0;
3373 /* Immediately and unconditionally launches the given process,
3374 after appending the -window-id option; sets running_preview_pid.
3377 launch_preview_subproc (state *s)
3379 saver_preferences *p = &s->prefs;
3383 const char *cmd = s->desired_preview_cmd;
3385 GtkWidget *pr = name_to_widget (s, "preview");
3386 GdkWindow *window = pr->window;
3388 s->running_preview_error_p = False;
3390 if (s->preview_suppressed_p)
3392 kill_preview_subproc (s);
3396 new_cmd = malloc (strlen (cmd) + 40);
3398 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3401 /* No window id? No command to run. */
3407 strcpy (new_cmd, cmd);
3408 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3412 kill_preview_subproc (s);
3415 s->running_preview_error_p = True;
3416 clear_preview_window (s);
3420 switch ((int) (forked = fork ()))
3425 sprintf (buf, "%s: couldn't fork", blurb());
3427 s->running_preview_error_p = True;
3433 close (ConnectionNumber (GDK_DISPLAY()));
3435 hack_subproc_environment (id, s->debug_p);
3437 usleep (250000); /* pause for 1/4th second before launching, to give
3438 the previous program time to die and flush its X
3439 buffer, so we don't get leftover turds on the
3442 exec_command (p->shell, new_cmd, p->nice_inferior);
3443 /* Don't bother printing an error message when we are unable to
3444 exec subprocesses; we handle that by polling the pid later.
3446 Note that one must use _exit() instead of exit() in procs forked
3447 off of Gtk programs -- Gtk installs an atexit handler that has a
3448 copy of the X connection (which we've already closed, for safety.)
3449 If one uses exit() instead of _exit(), then one sometimes gets a
3450 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3452 _exit (1); /* exits child fork */
3457 if (s->running_preview_cmd) free (s->running_preview_cmd);
3458 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3459 s->running_preview_pid = forked;
3463 char *ss = subproc_pretty_name (s);
3464 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3465 (unsigned long) forked, ss);
3472 schedule_preview_check (s);
3475 if (new_cmd) free (new_cmd);
3480 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3483 hack_environment (state *s)
3485 static const char *def_path =
3486 # ifdef DEFAULT_PATH_PREFIX
3487 DEFAULT_PATH_PREFIX;
3492 Display *dpy = GDK_DISPLAY();
3493 const char *odpy = DisplayString (dpy);
3494 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3495 strcpy (ndpy, "DISPLAY=");
3496 strcat (ndpy, odpy);
3501 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3503 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3504 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3505 So we must leak it (and/or the previous setting). Yay.
3508 if (def_path && *def_path)
3510 const char *opath = getenv("PATH");
3511 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3512 strcpy (npath, "PATH=");
3513 strcat (npath, def_path);
3514 strcat (npath, ":");
3515 strcat (npath, opath);
3519 /* do not free(npath) -- see above */
3522 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3528 hack_subproc_environment (Window preview_window_id, Bool debug_p)
3530 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
3531 necessary yet, but it will make programs work if we had invoked
3532 them with "-root" and not with "-window-id" -- which, of course,
3535 char *nssw = (char *) malloc (40);
3536 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
3538 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
3539 any more, right? It's not Posix, but everyone seems to have it. */
3544 fprintf (stderr, "%s: %s\n", blurb(), nssw);
3546 /* do not free(nssw) -- see above */
3550 /* Called from a timer:
3551 Launches the currently-chosen subprocess, if it's not already running.
3552 If there's a different process running, kills it.
3555 update_subproc_timer (gpointer data)
3557 state *s = (state *) data;
3558 if (! s->desired_preview_cmd)
3559 kill_preview_subproc (s);
3560 else if (!s->running_preview_cmd ||
3561 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3562 launch_preview_subproc (s);
3564 s->subproc_timer_id = 0;
3565 return FALSE; /* do not re-execute timer */
3569 /* Call this when you think you might want a preview process running.
3570 It will set a timer that will actually launch that program a second
3571 from now, if you haven't changed your mind (to avoid double-click
3572 spazzing, etc.) `cmd' may be null meaning "no process".
3575 schedule_preview (state *s, const char *cmd)
3577 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3582 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3584 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3587 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3588 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3590 if (s->subproc_timer_id)
3591 gtk_timeout_remove (s->subproc_timer_id);
3592 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3596 /* Called from a timer:
3597 Checks to see if the subproc that should be running, actually is.
3600 check_subproc_timer (gpointer data)
3602 state *s = (state *) data;
3603 Bool again_p = True;
3605 if (s->running_preview_error_p || /* already dead */
3606 s->running_preview_pid <= 0)
3614 status = kill (s->running_preview_pid, 0);
3615 if (status < 0 && errno == ESRCH)
3616 s->running_preview_error_p = True;
3620 char *ss = subproc_pretty_name (s);
3621 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3622 (unsigned long) s->running_preview_pid, ss,
3623 (s->running_preview_error_p ? "dead" : "alive"));
3627 if (s->running_preview_error_p)
3629 clear_preview_window (s);
3634 /* Otherwise, it's currently alive. We might be checking again, or we
3635 might be satisfied. */
3637 if (--s->subproc_check_countdown <= 0)
3641 return TRUE; /* re-execute timer */
3644 s->subproc_check_timer_id = 0;
3645 s->subproc_check_countdown = 0;
3646 return FALSE; /* do not re-execute timer */
3651 /* Call this just after launching a subprocess.
3652 This sets a timer that will, five times a second for two seconds,
3653 check whether the program is still running. The assumption here
3654 is that if the process didn't stay up for more than a couple of
3655 seconds, then either the program doesn't exist, or it doesn't
3656 take a -window-id argument.
3659 schedule_preview_check (state *s)
3665 fprintf (stderr, "%s: scheduling check\n", blurb());
3667 if (s->subproc_check_timer_id)
3668 gtk_timeout_remove (s->subproc_check_timer_id);
3669 s->subproc_check_timer_id =
3670 gtk_timeout_add (1000 / ticks,
3671 check_subproc_timer, (gpointer) s);
3672 s->subproc_check_countdown = ticks * seconds;
3677 screen_blanked_p (void)
3681 unsigned long nitems, bytesafter;
3683 Display *dpy = GDK_DISPLAY();
3684 Bool blanked_p = False;
3686 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3687 XA_SCREENSAVER_STATUS,
3688 0, 3, False, XA_INTEGER,
3689 &type, &format, &nitems, &bytesafter,
3690 (unsigned char **) &data)
3692 && type == XA_INTEGER
3695 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3697 if (data) free (data);
3702 /* Wake up every now and then and see if the screen is blanked.
3703 If it is, kill off the small-window demo -- no point in wasting
3704 cycles by running two screensavers at once...
3707 check_blanked_timer (gpointer data)
3709 state *s = (state *) data;
3710 Bool blanked_p = screen_blanked_p ();
3711 if (blanked_p && s->running_preview_pid)
3714 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3715 kill_preview_subproc (s);
3718 return True; /* re-execute timer */
3722 /* Setting window manager icon
3726 init_icon (GdkWindow *window)
3728 GdkBitmap *mask = 0;
3731 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3732 (gchar **) logo_50_xpm);
3734 gdk_window_set_icon (window, 0, pixmap, mask);
3738 /* The main demo-mode command loop.
3743 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3744 XrmRepresentation *type, XrmValue *value, XPointer closure)
3747 for (i = 0; quarks[i]; i++)
3749 if (bindings[i] == XrmBindTightly)
3750 fprintf (stderr, (i == 0 ? "" : "."));
3751 else if (bindings[i] == XrmBindLoosely)
3752 fprintf (stderr, "*");
3754 fprintf (stderr, " ??? ");
3755 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3758 fprintf (stderr, ": %s\n", (char *) value->addr);
3766 the_network_is_not_the_computer (state *s)
3768 Display *dpy = GDK_DISPLAY();
3769 char *rversion = 0, *ruser = 0, *rhost = 0;
3770 char *luser, *lhost;
3772 struct passwd *p = getpwuid (getuid ());
3773 const char *d = DisplayString (dpy);
3775 # if defined(HAVE_UNAME)
3777 if (uname (&uts) < 0)
3778 lhost = "<UNKNOWN>";
3780 lhost = uts.nodename;
3782 strcpy (lhost, getenv("SYS$NODE"));
3783 # else /* !HAVE_UNAME && !VMS */
3784 strcat (lhost, "<UNKNOWN>");
3785 # endif /* !HAVE_UNAME && !VMS */
3787 if (p && p->pw_name)
3792 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3794 /* Make a buffer that's big enough for a number of copies of all the
3795 strings, plus some. */
3796 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3797 (ruser ? strlen(ruser) : 0) +
3798 (rhost ? strlen(rhost) : 0) +
3805 if (!rversion || !*rversion)
3809 "The XScreenSaver daemon doesn't seem to be running\n"
3810 "on display \"%s\". Launch it now?"),
3813 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3815 /* Warn that the two processes are running as different users.
3819 "%s is running as user \"%s\" on host \"%s\".\n"
3820 "But the xscreensaver managing display \"%s\"\n"
3821 "is running as user \"%s\" on host \"%s\".\n"
3823 "Since they are different users, they won't be reading/writing\n"
3824 "the same ~/.xscreensaver file, so %s isn't\n"
3825 "going to work right.\n"
3827 "You should either re-run %s as \"%s\", or re-run\n"
3828 "xscreensaver as \"%s\".\n"
3830 "Restart the xscreensaver daemon now?\n"),
3831 progname, luser, lhost,
3833 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3835 progname, (ruser ? ruser : "???"),
3838 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3840 /* Warn that the two processes are running on different hosts.
3844 "%s is running as user \"%s\" on host \"%s\".\n"
3845 "But the xscreensaver managing display \"%s\"\n"
3846 "is running as user \"%s\" on host \"%s\".\n"
3848 "If those two machines don't share a file system (that is,\n"
3849 "if they don't see the same ~%s/.xscreensaver file) then\n"
3850 "%s won't work right.\n"
3852 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3853 progname, luser, lhost,
3855 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3860 else if (!!strcmp (rversion, s->short_version))
3862 /* Warn that the version numbers don't match.
3866 "This is %s version %s.\n"
3867 "But the xscreensaver managing display \"%s\"\n"
3868 "is version %s. This could cause problems.\n"
3870 "Restart the xscreensaver daemon now?\n"),
3871 progname, s->short_version,
3878 warning_dialog (s->toplevel_widget, msg, True, 1);
3880 if (rversion) free (rversion);
3881 if (ruser) free (ruser);
3882 if (rhost) free (rhost);
3887 /* We use this error handler so that X errors are preceeded by the name
3888 of the program that generated them.
3891 demo_ehandler (Display *dpy, XErrorEvent *error)
3893 state *s = global_state_kludge; /* I hate C so much... */
3894 fprintf (stderr, "\nX error in %s:\n", blurb());
3895 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3896 kill_preview_subproc (s);
3902 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3903 of the program that generated them; and also that we can ignore one
3904 particular bogus error message that Gdk madly spews.
3907 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3908 const gchar *message, gpointer user_data)
3910 /* Ignore the message "Got event for unknown window: 0x...".
3911 Apparently some events are coming in for the xscreensaver window
3912 (presumably reply events related to the ClientMessage) and Gdk
3913 feels the need to complain about them. So, just suppress any
3914 messages that look like that one.
3916 if (strstr (message, "unknown window"))
3919 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3920 (log_domain ? log_domain : progclass),
3921 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3922 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3923 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3924 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3925 log_level == G_LOG_LEVEL_INFO ? "info" :
3926 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3928 ((!*message || message[strlen(message)-1] != '\n')
3934 __extension__ /* shut up about "string length is greater than the length
3935 ISO C89 compilers are required to support" when including
3939 static char *defaults[] = {
3940 #include "XScreenSaver_ad.h"
3945 #ifdef HAVE_CRAPPLET
3946 static struct poptOption crapplet_options[] = {
3947 {NULL, '\0', 0, NULL, 0}
3949 #endif /* HAVE_CRAPPLET */
3952 const char *usage = "[--display dpy] [--prefs]"
3953 # ifdef HAVE_CRAPPLET
3956 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
3959 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3961 state *s = (state *) user_data;
3962 Boolean oi = s->initializing_p;
3963 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3964 s->initializing_p = True;
3965 eschew_gtk_lossage (label);
3966 s->initializing_p = oi;
3972 print_widget_tree (GtkWidget *w, int depth)
3975 for (i = 0; i < depth; i++)
3976 fprintf (stderr, " ");
3977 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3979 if (GTK_IS_LIST (w))
3981 for (i = 0; i < depth+1; i++)
3982 fprintf (stderr, " ");
3983 fprintf (stderr, "...list kids...\n");
3985 else if (GTK_IS_CONTAINER (w))
3987 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3990 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3998 delayed_scroll_kludge (gpointer data)
4000 state *s = (state *) data;
4001 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4002 ensure_selected_item_visible (w);
4004 /* Oh, this is just fucking lovely, too. */
4005 w = GTK_WIDGET (name_to_widget (s, "preview"));
4006 gtk_widget_hide (w);
4007 gtk_widget_show (w);
4009 return FALSE; /* do not re-execute timer */
4015 create_xscreensaver_demo (void)
4019 nb = name_to_widget (global_state_kludge, "preview_notebook");
4020 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4022 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4026 create_xscreensaver_settings_dialog (void)
4030 box = name_to_widget (global_state_kludge, "dialog_action_area");
4032 w = name_to_widget (global_state_kludge, "adv_button");
4033 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4035 w = name_to_widget (global_state_kludge, "std_button");
4036 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4038 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4041 #endif /* HAVE_GTK2 */
4044 main (int argc, char **argv)
4048 saver_preferences *p;
4052 Widget toplevel_shell;
4053 char *real_progname = argv[0];
4056 Bool crapplet_p = False;
4060 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4061 textdomain (GETTEXT_PACKAGE);
4064 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4065 # else /* !HAVE_GTK2 */
4066 if (!setlocale (LC_ALL, ""))
4067 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4068 # endif /* !HAVE_GTK2 */
4070 #endif /* ENABLE_NLS */
4072 str = strrchr (real_progname, '/');
4073 if (str) real_progname = str+1;
4076 memset (s, 0, sizeof(*s));
4077 s->initializing_p = True;
4080 global_state_kludge = s; /* I hate C so much... */
4082 progname = real_progname;
4084 s->short_version = (char *) malloc (5);
4085 memcpy (s->short_version, screensaver_id + 17, 4);
4086 s->short_version [4] = 0;
4089 /* Register our error message logger for every ``log domain'' known.
4090 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4091 for all of the domains that seem to be in use.
4094 const char * const domains[] = { 0,
4095 "Gtk", "Gdk", "GLib", "GModule",
4096 "GThread", "Gnome", "GnomeUI" };
4097 for (i = 0; i < countof(domains); i++)
4098 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4101 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4104 const char *dir = DEFAULT_ICONDIR;
4105 if (*dir) add_pixmap_directory (dir);
4107 # endif /* !HAVE_GTK2 */
4108 #endif /* DEFAULT_ICONDIR */
4110 /* This is gross, but Gtk understands --display and not -display...
4112 for (i = 1; i < argc; i++)
4113 if (argv[i][0] && argv[i][1] &&
4114 !strncmp(argv[i], "-display", strlen(argv[i])))
4115 argv[i] = "--display";
4118 /* We need to parse this arg really early... Sigh. */
4119 for (i = 1; i < argc; i++)
4122 (!strcmp(argv[i], "--crapplet") ||
4123 !strcmp(argv[i], "--capplet")))
4125 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4128 for (j = i; j < argc; j++) /* remove it from the list */
4129 argv[j] = argv[j+1];
4131 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4132 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4134 fprintf (stderr, "%s: %s\n", real_progname, usage);
4136 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4139 (!strcmp(argv[i], "--debug") ||
4140 !strcmp(argv[i], "-debug") ||
4141 !strcmp(argv[i], "-d")))
4145 for (j = i; j < argc; j++) /* remove it from the list */
4146 argv[j] = argv[j+1];
4153 (!strcmp(argv[i], "-geometry") ||
4154 !strcmp(argv[i], "-geom") ||
4155 !strcmp(argv[i], "-geo") ||
4156 !strcmp(argv[i], "-g")))
4160 for (j = i; j < argc; j++) /* remove them from the list */
4161 argv[j] = argv[j+2];
4168 (!strcmp(argv[i], "--configdir")))
4172 hack_configuration_path = argv[i+1];
4173 for (j = i; j < argc; j++) /* remove them from the list */
4174 argv[j] = argv[j+2];
4178 if (0 != stat (hack_configuration_path, &st))
4181 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4185 else if (!S_ISDIR (st.st_mode))
4187 fprintf (stderr, "%s: not a directory: %s\n",
4188 blurb(), hack_configuration_path);
4196 fprintf (stderr, "%s: using config directory \"%s\"\n",
4197 progname, hack_configuration_path);
4200 /* Let Gtk open the X connection, then initialize Xt to use that
4201 same connection. Doctor Frankenstein would be proud.
4203 # ifdef HAVE_CRAPPLET
4206 GnomeClient *client;
4207 GnomeClientFlags flags = 0;
4209 int init_results = gnome_capplet_init ("screensaver-properties",
4211 argc, argv, NULL, 0, NULL);
4213 0 upon successful initialization;
4214 1 if --init-session-settings was passed on the cmdline;
4215 2 if --ignore was passed on the cmdline;
4218 So the 1 signifies just to init the settings, and quit, basically.
4219 (Meaning launch the xscreensaver daemon.)
4222 if (init_results < 0)
4225 g_error ("An initialization error occurred while "
4226 "starting xscreensaver-capplet.\n");
4228 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4229 real_progname, init_results);
4234 client = gnome_master_client ();
4237 flags = gnome_client_get_flags (client);
4239 if (flags & GNOME_CLIENT_IS_CONNECTED)
4242 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4243 gnome_client_get_id (client));
4246 char *session_args[20];
4248 session_args[i++] = real_progname;
4249 session_args[i++] = "--capplet";
4250 session_args[i++] = "--init-session-settings";
4251 session_args[i] = 0;
4252 gnome_client_set_priority (client, 20);
4253 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4254 gnome_client_set_restart_command (client, i, session_args);
4258 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4261 gnome_client_flush (client);
4264 if (init_results == 1)
4266 system ("xscreensaver -nosplash &");
4272 # endif /* HAVE_CRAPPLET */
4274 gtk_init (&argc, &argv);
4278 /* We must read exactly the same resources as xscreensaver.
4279 That means we must have both the same progclass *and* progname,
4280 at least as far as the resource database is concerned. So,
4281 put "xscreensaver" in argv[0] while initializing Xt.
4283 argv[0] = "xscreensaver";
4287 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4289 XtToolkitInitialize ();
4290 app = XtCreateApplicationContext ();
4291 dpy = GDK_DISPLAY();
4292 XtAppSetFallbackResources (app, defaults);
4293 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4294 toplevel_shell = XtAppCreateShell (progname, progclass,
4295 applicationShellWidgetClass,
4298 dpy = XtDisplay (toplevel_shell);
4299 db = XtDatabase (dpy);
4300 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4301 XSetErrorHandler (demo_ehandler);
4303 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4304 signal (SIGPIPE, SIG_IGN);
4306 /* After doing Xt-style command-line processing, complain about any
4307 unrecognized command-line arguments.
4309 for (i = 1; i < argc; i++)
4311 char *str = argv[i];
4312 if (str[0] == '-' && str[1] == '-')
4314 if (!strcmp (str, "-prefs"))
4316 else if (crapplet_p)
4317 /* There are lots of random args that we don't care about when we're
4318 started as a crapplet, so just ignore unknown args in that case. */
4322 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4324 fprintf (stderr, "%s: %s\n", real_progname, usage);
4329 /* Load the init file, which may end up consulting the X resource database
4330 and the site-wide app-defaults file. Note that at this point, it's
4331 important that `progname' be "xscreensaver", rather than whatever
4336 hack_environment (s); /* must be before initialize_sort_map() */
4339 initialize_sort_map (s);
4341 /* Now that Xt has been initialized, and the resources have been read,
4342 we can set our `progname' variable to something more in line with
4345 progname = real_progname;
4349 /* Print out all the resources we read. */
4351 XrmName name = { 0 };
4352 XrmClass class = { 0 };
4354 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4360 /* Intern the atoms that xscreensaver_command() needs.
4362 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4363 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4364 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4365 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4366 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4367 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4368 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4369 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4370 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4371 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4372 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4373 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4374 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4377 /* Create the window and all its widgets.
4379 s->base_widget = create_xscreensaver_demo ();
4380 s->popup_widget = create_xscreensaver_settings_dialog ();
4381 s->toplevel_widget = s->base_widget;
4384 /* Set the main window's title. */
4386 char *base_title = _("Screensaver Preferences");
4387 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4388 char *s1, *s2, *s3, *s4;
4389 s1 = (char *) strchr(v, ' '); s1++;
4390 s2 = (char *) strchr(s1, ' ');
4391 s3 = (char *) strchr(v, '('); s3++;
4392 s4 = (char *) strchr(s3, ')');
4396 window_title = (char *) malloc (strlen (base_title) +
4397 strlen (progclass) +
4398 strlen (s1) + strlen (s3) +
4400 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4401 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4402 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4406 /* Adjust the (invisible) notebooks on the popup dialog... */
4408 GtkNotebook *notebook =
4409 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4410 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4414 gtk_widget_hide (std);
4415 # else /* !HAVE_XML */
4416 /* Make the advanced page be the only one available. */
4417 gtk_widget_set_sensitive (std, False);
4418 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4419 gtk_widget_hide (std);
4421 # endif /* !HAVE_XML */
4423 gtk_notebook_set_page (notebook, page);
4424 gtk_notebook_set_show_tabs (notebook, False);
4427 /* Various other widget initializations...
4429 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4430 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4432 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4433 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4436 populate_hack_list (s);
4437 populate_prefs_page (s);
4438 sensitize_demo_widgets (s, False);
4439 fix_text_entry_sizes (s);
4440 scroll_to_current_hack (s);
4442 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4443 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4447 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4448 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4450 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4451 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4453 #endif /* !HAVE_GTK2 */
4455 /* Hook up callbacks to the items on the mode menu. */
4457 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4458 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4459 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4460 for (; kids; kids = kids->next)
4461 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4462 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4467 /* Handle the -prefs command-line argument. */
4470 GtkNotebook *notebook =
4471 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4472 gtk_notebook_set_page (notebook, 1);
4475 # ifdef HAVE_CRAPPLET
4479 GtkWidget *outer_vbox;
4481 gtk_widget_hide (s->toplevel_widget);
4483 capplet = capplet_widget_new ();
4485 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4486 # ifdef HAVE_CRAPPLET_IMMEDIATE
4487 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4488 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4489 /* In crapplet-mode, take off the menubar. */
4490 gtk_widget_hide (name_to_widget (s, "menubar"));
4492 /* Reparent our top-level container to be a child of the capplet
4495 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4496 gtk_widget_ref (outer_vbox);
4497 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4499 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4500 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4502 /* Find the window above us, and set the title and close handler. */
4504 GtkWidget *window = capplet;
4505 while (window && !GTK_IS_WINDOW (window))
4506 window = window->parent;
4509 gtk_window_set_title (GTK_WINDOW (window), window_title);
4510 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4511 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4516 s->toplevel_widget = capplet;
4518 # endif /* HAVE_CRAPPLET */
4521 /* The Gnome folks hate the menubar. I think it's important to have access
4522 to the commands on the File menu (Restart Daemon, etc.) and to the
4523 About and Documentation commands on the Help menu.
4527 gtk_widget_hide (name_to_widget (s, "menubar"));
4531 free (window_title);
4535 /* After picking the default size, allow -geometry to override it. */
4537 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
4540 gtk_widget_show (s->toplevel_widget);
4541 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4542 fix_preview_visual (s);
4544 /* Realize page zero, so that we can diddle the scrollbar when the
4545 user tabs back to it -- otherwise, the current hack isn't scrolled
4546 to the first time they tab back there, when started with "-prefs".
4547 (Though it is if they then tab away, and back again.)
4549 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4550 #### understands this crap, explain to me how to make this work.
4552 gtk_widget_realize (name_to_widget (s, "demos_table"));
4555 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4558 /* Issue any warnings about the running xscreensaver daemon. */
4559 the_network_is_not_the_computer (s);
4562 /* Run the Gtk event loop, and not the Xt event loop. This means that
4563 if there were Xt timers or fds registered, they would never get serviced,
4564 and if there were any Xt widgets, they would never have events delivered.
4565 Fortunately, we're using Gtk for all of the UI, and only initialized
4566 Xt so that we could process the command line and use the X resource
4569 s->initializing_p = False;
4571 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4572 after we start up. Otherwise, it always appears scrolled to the top
4573 when in crapplet-mode. */
4574 gtk_timeout_add (500, delayed_scroll_kludge, s);
4578 /* Load every configurator in turn, to scan them for errors all at once. */
4581 for (i = 0; i < p->screenhacks_count; i++)
4583 screenhack *hack = p->screenhacks[i];
4584 conf_data *d = load_configurator (hack->command, False);
4585 if (d) free_conf_data (d);
4591 # ifdef HAVE_CRAPPLET
4593 capplet_gtk_main ();
4595 # endif /* HAVE_CRAPPLET */
4598 kill_preview_subproc (s);
4602 #endif /* HAVE_GTK -- whole file */