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;
1581 int old_selected = p->selected_hack;
1585 if (menu_items->data == widget)
1588 menu_items = menu_items->next;
1590 if (!menu_items) abort();
1592 new_mode = mode_menu_order[menu_index];
1594 /* Keep the same list element displayed as before; except if we're
1595 switching *to* "one screensaver" mode from any other mode, scroll
1596 to and select "the one".
1599 if (new_mode == ONE_HACK)
1600 list_elt = (p->selected_hack >= 0
1601 ? s->hack_number_to_list_elt[p->selected_hack]
1605 list_elt = selected_list_element (s);
1608 saver_mode old_mode = p->mode;
1610 populate_demo_window (s, list_elt);
1611 force_list_select_item (s, list, list_elt, True);
1612 p->mode = old_mode; /* put it back, so the init file gets written */
1615 pref_changed_cb (widget, user_data);
1617 if (old_selected != p->selected_hack)
1618 abort(); /* dammit, not again... */
1623 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1624 gint page_num, gpointer user_data)
1626 state *s = global_state_kludge; /* I hate C so much... */
1627 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1629 /* If we're switching to page 0, schedule the current hack to be run.
1630 Otherwise, schedule it to stop. */
1632 populate_demo_window (s, selected_list_element (s));
1634 schedule_preview (s, 0);
1639 list_activated_cb (GtkTreeView *list,
1641 GtkTreeViewColumn *column,
1648 STFU g_return_if_fail (!gdk_pointer_is_grabbed ());
1650 str = gtk_tree_path_to_string (path);
1651 list_elt = strtol (str, NULL, 10);
1655 run_hack (s, list_elt, True);
1659 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1661 state *s = (state *)data;
1662 GtkTreeModel *model;
1668 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1671 path = gtk_tree_model_get_path (model, &iter);
1672 str = gtk_tree_path_to_string (path);
1673 list_elt = strtol (str, NULL, 10);
1675 gtk_tree_path_free (path);
1678 populate_demo_window (s, list_elt);
1679 flush_dialog_changes_and_save (s);
1682 #else /* !HAVE_GTK2 */
1684 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1685 list_select_cb that comes in
1686 *after* we've double-clicked.
1690 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1693 state *s = (state *) data;
1694 if (event->type == GDK_2BUTTON_PRESS)
1696 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1697 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1699 last_doubleclick_time = time ((time_t *) 0);
1702 run_hack (s, list_elt, True);
1710 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1712 state *s = (state *) data;
1713 time_t now = time ((time_t *) 0);
1715 if (now >= last_doubleclick_time + 2)
1717 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1718 populate_demo_window (s, list_elt);
1719 flush_dialog_changes_and_save (s);
1724 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1726 state *s = (state *) data;
1727 populate_demo_window (s, -1);
1728 flush_dialog_changes_and_save (s);
1731 #endif /* !HAVE_GTK2 */
1734 /* Called when the checkboxes that are in the left column of the
1735 scrolling list are clicked. This both populates the right pane
1736 (just as clicking on the label (really, listitem) does) and
1737 also syncs this checkbox with the right pane Enabled checkbox.
1742 GtkCellRendererToggle *toggle,
1744 #else /* !HAVE_GTK2 */
1746 #endif /* !HAVE_GTK2 */
1749 state *s = (state *) data;
1752 GtkScrolledWindow *scroller =
1753 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1754 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1755 GtkTreeModel *model = gtk_tree_view_get_model (list);
1756 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1759 #else /* !HAVE_GTK2 */
1760 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1761 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1763 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1764 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1765 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1766 #endif /* !HAVE_GTK2 */
1773 if (!gtk_tree_model_get_iter (model, &iter, path))
1775 g_warning ("bad path: %s", path_string);
1778 gtk_tree_path_free (path);
1780 gtk_tree_model_get (model, &iter,
1781 COL_ENABLED, &active,
1784 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1785 COL_ENABLED, !active,
1788 list_elt = strtol (path_string, NULL, 10);
1789 #else /* !HAVE_GTK2 */
1790 list_elt = gtk_list_child_position (list, line);
1791 #endif /* !HAVE_GTK2 */
1793 /* remember previous scroll position of the top of the list */
1794 adj = gtk_scrolled_window_get_vadjustment (scroller);
1795 scroll_top = adj->value;
1797 flush_dialog_changes_and_save (s);
1798 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1799 populate_demo_window (s, list_elt);
1801 /* restore the previous scroll position of the top of the list.
1802 this is weak, but I don't really know why it's moving... */
1803 gtk_adjustment_set_value (adj, scroll_top);
1809 GtkFileSelection *widget;
1810 } file_selection_data;
1815 store_image_directory (GtkWidget *button, gpointer user_data)
1817 file_selection_data *fsd = (file_selection_data *) user_data;
1818 state *s = fsd->state;
1819 GtkFileSelection *selector = fsd->widget;
1820 GtkWidget *top = s->toplevel_widget;
1821 saver_preferences *p = &s->prefs;
1822 const char *path = gtk_file_selection_get_filename (selector);
1824 if (p->image_directory && !strcmp(p->image_directory, path))
1825 return; /* no change */
1827 if (!directory_p (path))
1830 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1831 warning_dialog (GTK_WIDGET (top), b, False, 100);
1835 if (p->image_directory) free (p->image_directory);
1836 p->image_directory = normalize_directory (path);
1838 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1839 (p->image_directory ? p->image_directory : ""));
1840 demo_write_init_file (s, p);
1845 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1847 file_selection_data *fsd = (file_selection_data *) user_data;
1848 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1852 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1854 browse_image_dir_cancel (button, user_data);
1855 store_image_directory (button, user_data);
1859 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1861 browse_image_dir_cancel (widget, user_data);
1866 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1868 state *s = global_state_kludge; /* I hate C so much... */
1869 saver_preferences *p = &s->prefs;
1870 static file_selection_data *fsd = 0;
1872 GtkFileSelection *selector = GTK_FILE_SELECTION(
1873 gtk_file_selection_new ("Please select the image directory."));
1876 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1878 fsd->widget = selector;
1881 if (p->image_directory && *p->image_directory)
1882 gtk_file_selection_set_filename (selector, p->image_directory);
1884 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1885 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1887 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1888 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1890 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1891 GTK_SIGNAL_FUNC (browse_image_dir_close),
1894 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1896 gtk_window_set_modal (GTK_WINDOW (selector), True);
1897 gtk_widget_show (GTK_WIDGET (selector));
1902 settings_cb (GtkButton *button, gpointer user_data)
1904 state *s = global_state_kludge; /* I hate C so much... */
1905 int list_elt = selected_list_element (s);
1907 populate_demo_window (s, list_elt); /* reset the widget */
1908 populate_popup_window (s); /* create UI on popup window */
1909 gtk_widget_show (s->popup_widget);
1913 settings_sync_cmd_text (state *s)
1916 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1917 char *cmd_line = get_configurator_command_line (s->cdata);
1918 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1919 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1921 # endif /* HAVE_XML */
1925 settings_adv_cb (GtkButton *button, gpointer user_data)
1927 state *s = global_state_kludge; /* I hate C so much... */
1928 GtkNotebook *notebook =
1929 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1931 settings_sync_cmd_text (s);
1932 gtk_notebook_set_page (notebook, 1);
1936 settings_std_cb (GtkButton *button, gpointer user_data)
1938 state *s = global_state_kludge; /* I hate C so much... */
1939 GtkNotebook *notebook =
1940 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1942 /* Re-create UI to reflect the in-progress command-line settings. */
1943 populate_popup_window (s);
1945 gtk_notebook_set_page (notebook, 0);
1949 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1950 gint page_num, gpointer user_data)
1952 state *s = global_state_kludge; /* I hate C so much... */
1953 GtkWidget *adv = name_to_widget (s, "adv_button");
1954 GtkWidget *std = name_to_widget (s, "std_button");
1958 gtk_widget_show (adv);
1959 gtk_widget_hide (std);
1961 else if (page_num == 1)
1963 gtk_widget_hide (adv);
1964 gtk_widget_show (std);
1973 settings_cancel_cb (GtkButton *button, gpointer user_data)
1975 state *s = global_state_kludge; /* I hate C so much... */
1976 gtk_widget_hide (s->popup_widget);
1980 settings_ok_cb (GtkButton *button, gpointer user_data)
1982 state *s = global_state_kludge; /* I hate C so much... */
1983 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1984 int page = gtk_notebook_get_current_page (notebook);
1987 /* Regenerate the command-line from the widget contents before saving.
1988 But don't do this if we're looking at the command-line page already,
1989 or we will blow away what they typed... */
1990 settings_sync_cmd_text (s);
1992 flush_popup_changes_and_save (s);
1993 gtk_widget_hide (s->popup_widget);
1997 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1999 state *s = (state *) data;
2000 settings_cancel_cb (0, (gpointer) s);
2006 /* Populating the various widgets
2010 /* Returns the number of the last hack run by the server.
2013 server_current_hack (void)
2017 unsigned long nitems, bytesafter;
2019 Display *dpy = GDK_DISPLAY();
2020 int hack_number = -1;
2022 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2023 XA_SCREENSAVER_STATUS,
2024 0, 3, False, XA_INTEGER,
2025 &type, &format, &nitems, &bytesafter,
2026 (unsigned char **) &data)
2028 && type == XA_INTEGER
2031 hack_number = (int) data[2] - 1;
2033 if (data) free (data);
2039 /* Finds the number of the last hack to run, and makes that item be
2040 selected by default.
2043 scroll_to_current_hack (state *s)
2045 saver_preferences *p = &s->prefs;
2048 if (p->mode == ONE_HACK)
2049 hack_number = p->selected_hack;
2051 hack_number = server_current_hack ();
2053 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2055 int list_elt = s->hack_number_to_list_elt[hack_number];
2056 GtkWidget *list = name_to_widget (s, "list");
2057 force_list_select_item (s, list, list_elt, True);
2058 populate_demo_window (s, list_elt);
2064 on_path_p (const char *program)
2068 char *cmd = strdup (program);
2069 char *token = strchr (cmd, ' ');
2073 if (token) *token = 0;
2076 if (strchr (cmd, '/'))
2078 result = (0 == stat (cmd, &st));
2082 path = getenv("PATH");
2083 if (!path || !*path)
2087 path = strdup (path);
2088 token = strtok (path, ":");
2092 char *p2 = (char *) malloc (strlen (token) + L + 3);
2096 result = (0 == stat (p2, &st));
2099 token = strtok (0, ":");
2104 if (path) free (path);
2110 populate_hack_list (state *s)
2113 saver_preferences *p = &s->prefs;
2114 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2115 GtkListStore *model;
2116 GtkTreeSelection *selection;
2117 GtkCellRenderer *ren;
2121 g_object_get (G_OBJECT (list),
2126 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2127 g_object_set (G_OBJECT (list), "model", model, NULL);
2128 g_object_unref (model);
2130 ren = gtk_cell_renderer_toggle_new ();
2131 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2133 "active", COL_ENABLED,
2136 g_signal_connect (ren, "toggled",
2137 G_CALLBACK (list_checkbox_cb),
2140 ren = gtk_cell_renderer_text_new ();
2141 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2142 _("Screen Saver"), ren,
2146 g_signal_connect_after (list, "row_activated",
2147 G_CALLBACK (list_activated_cb),
2150 selection = gtk_tree_view_get_selection (list);
2151 g_signal_connect (selection, "changed",
2152 G_CALLBACK (list_select_changed_cb),
2157 for (i = 0; i < s->list_count; i++)
2159 int hack_number = s->list_elt_to_hack_number[i];
2160 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2162 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2164 if (!hack) continue;
2166 /* If we're to suppress uninstalled hacks, check $PATH now. */
2167 if (p->ignore_uninstalled_p && !available_p)
2170 pretty_name = (hack->name
2171 ? strdup (hack->name)
2172 : make_hack_name (hack->command));
2176 /* Make the text foreground be the color of insensitive widgets
2177 (but don't actually make it be insensitive, since we still
2178 want to be able to click on it.)
2180 GtkStyle *style = GTK_WIDGET (list)->style;
2181 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2182 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2183 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2185 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2186 /* " background=\"#%02X%02X%02X\"" */
2188 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2189 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2195 gtk_list_store_append (model, &iter);
2196 gtk_list_store_set (model, &iter,
2197 COL_ENABLED, hack->enabled_p,
2198 COL_NAME, pretty_name,
2203 #else /* !HAVE_GTK2 */
2205 saver_preferences *p = &s->prefs;
2206 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2208 for (i = 0; i < s->list_count; i++)
2210 int hack_number = s->list_elt_to_hack_number[i];
2211 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2213 /* A GtkList must contain only GtkListItems, but those can contain
2214 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2215 and a Label. We handle single and double click events on the
2216 line itself, for clicking on the text, but the interior checkbox
2217 also handles its own events.
2220 GtkWidget *line_hbox;
2221 GtkWidget *line_check;
2222 GtkWidget *line_label;
2224 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2226 if (!hack) continue;
2228 /* If we're to suppress uninstalled hacks, check $PATH now. */
2229 if (p->ignore_uninstalled_p && !available_p)
2232 pretty_name = (hack->name
2233 ? strdup (hack->name)
2234 : make_hack_name (hack->command));
2236 line = gtk_list_item_new ();
2237 line_hbox = gtk_hbox_new (FALSE, 0);
2238 line_check = gtk_check_button_new ();
2239 line_label = gtk_label_new (pretty_name);
2241 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2242 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2243 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2245 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2247 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2249 gtk_widget_show (line_check);
2250 gtk_widget_show (line_label);
2251 gtk_widget_show (line_hbox);
2252 gtk_widget_show (line);
2256 gtk_container_add (GTK_CONTAINER (list), line);
2257 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2258 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2261 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2262 GTK_SIGNAL_FUNC (list_checkbox_cb),
2265 gtk_widget_show (line);
2269 /* Make the widget be colored like insensitive widgets
2270 (but don't actually make it be insensitive, since we
2271 still want to be able to click on it.)
2273 GtkRcStyle *rc_style;
2276 gtk_widget_realize (GTK_WIDGET (line_label));
2278 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2279 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2281 rc_style = gtk_rc_style_new ();
2282 rc_style->fg[GTK_STATE_NORMAL] = fg;
2283 rc_style->bg[GTK_STATE_NORMAL] = bg;
2284 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2286 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2287 gtk_rc_style_unref (rc_style);
2291 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2292 GTK_SIGNAL_FUNC (list_select_cb),
2294 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2295 GTK_SIGNAL_FUNC (list_unselect_cb),
2297 #endif /* !HAVE_GTK2 */
2301 update_list_sensitivity (state *s)
2303 saver_preferences *p = &s->prefs;
2304 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2305 Bool checkable = (p->mode == RANDOM_HACKS);
2306 Bool blankable = (p->mode != DONT_BLANK);
2309 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2310 GtkWidget *use = name_to_widget (s, "use_col_frame");
2311 #endif /* HAVE_GTK2 */
2312 GtkWidget *scroller = name_to_widget (s, "scroller");
2313 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2314 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2317 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2318 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2319 #else /* !HAVE_GTK2 */
2320 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2321 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2323 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2324 #endif /* !HAVE_GTK2 */
2325 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2326 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2328 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2331 gtk_tree_view_column_set_visible (use, checkable);
2332 #else /* !HAVE_GTK2 */
2334 gtk_widget_show (use); /* the "Use" column header */
2336 gtk_widget_hide (use);
2340 GtkBin *line = GTK_BIN (kids->data);
2341 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2342 GtkWidget *line_check =
2343 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2346 gtk_widget_show (line_check);
2348 gtk_widget_hide (line_check);
2352 #endif /* !HAVE_GTK2 */
2357 populate_prefs_page (state *s)
2359 saver_preferences *p = &s->prefs;
2361 Bool can_lock_p = True;
2363 /* Disable all the "lock" controls if locking support was not provided
2364 at compile-time, or if running on MacOS. */
2365 # if defined(NO_LOCKING) || defined(__APPLE__)
2370 /* The file supports timeouts of less than a minute, but the GUI does
2371 not, so throttle the values to be at least one minute (since "0" is
2372 a bad rounding choice...)
2374 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2377 THROTTLE (passwd_timeout);
2380 # define FMT_MINUTES(NAME,N) \
2381 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2383 # define FMT_SECONDS(NAME,N) \
2384 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2386 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2387 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2388 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2389 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2390 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2391 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2392 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2397 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2398 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2401 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2402 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2403 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2404 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2405 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2406 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2407 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2408 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2409 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2410 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2411 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2413 # undef TOGGLE_ACTIVE
2415 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2416 (p->image_directory ? p->image_directory : ""));
2417 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2419 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2422 /* Map the `saver_mode' enum to mode menu to values. */
2424 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2427 for (i = 0; i < countof(mode_menu_order); i++)
2428 if (mode_menu_order[i] == p->mode)
2430 gtk_option_menu_set_history (opt, i);
2431 update_list_sensitivity (s);
2435 Bool found_any_writable_cells = False;
2436 Bool dpms_supported = False;
2438 Display *dpy = GDK_DISPLAY();
2439 int nscreens = ScreenCount(dpy);
2441 for (i = 0; i < nscreens; i++)
2443 Screen *s = ScreenOfDisplay (dpy, i);
2444 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2446 found_any_writable_cells = True;
2451 #ifdef HAVE_XF86VMODE_GAMMA
2452 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2455 #ifdef HAVE_DPMS_EXTENSION
2457 int op = 0, event = 0, error = 0;
2458 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2459 dpms_supported = True;
2461 #endif /* HAVE_DPMS_EXTENSION */
2464 # define SENSITIZE(NAME,SENSITIVEP) \
2465 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2467 /* Blanking and Locking
2469 SENSITIZE ("lock_button", can_lock_p);
2470 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2471 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2475 SENSITIZE ("dpms_frame", dpms_supported);
2476 SENSITIZE ("dpms_button", dpms_supported);
2477 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2478 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2479 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2480 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2481 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2482 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2483 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2484 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2485 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2489 SENSITIZE ("cmap_frame", found_any_writable_cells);
2490 SENSITIZE ("install_button", found_any_writable_cells);
2491 SENSITIZE ("fade_button", found_any_writable_cells);
2492 SENSITIZE ("unfade_button", found_any_writable_cells);
2494 SENSITIZE ("fade_label", (found_any_writable_cells &&
2495 (p->fade_p || p->unfade_p)));
2496 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2497 (p->fade_p || p->unfade_p)));
2505 populate_popup_window (state *s)
2507 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2508 char *doc_string = 0;
2510 /* #### not in Gtk 1.2
2511 gtk_label_set_selectable (doc);
2517 free_conf_data (s->cdata);
2522 saver_preferences *p = &s->prefs;
2523 int list_elt = selected_list_element (s);
2524 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2525 ? s->list_elt_to_hack_number[list_elt]
2527 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2530 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2531 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2532 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2533 s->cdata = load_configurator (cmd_line, s->debug_p);
2534 if (s->cdata && s->cdata->widget)
2535 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2540 doc_string = (s->cdata
2541 ? s->cdata->description
2543 # else /* !HAVE_XML */
2544 doc_string = _("Descriptions not available: no XML support compiled in.");
2545 # endif /* !HAVE_XML */
2547 gtk_label_set_text (doc, (doc_string
2549 : _("No description available.")));
2554 sensitize_demo_widgets (state *s, Bool sensitive_p)
2556 const char *names1[] = { "demo", "settings" };
2557 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2558 "visual", "visual_combo" };
2560 for (i = 0; i < countof(names1); i++)
2562 GtkWidget *w = name_to_widget (s, names1[i]);
2563 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2565 for (i = 0; i < countof(names2); i++)
2567 GtkWidget *w = name_to_widget (s, names2[i]);
2568 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2573 /* Even though we've given these text fields a maximum number of characters,
2574 their default size is still about 30 characters wide -- so measure out
2575 a string in their font, and resize them to just fit that.
2578 fix_text_entry_sizes (state *s)
2582 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2583 const char * const spinbuttons[] = {
2584 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2585 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2586 "dpms_off_spinbutton",
2587 "-fade_spinbutton" };
2591 for (i = 0; i < countof(spinbuttons); i++)
2593 const char *n = spinbuttons[i];
2595 while (*n == '-') n++, cols--;
2596 w = GTK_WIDGET (name_to_widget (s, n));
2597 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2598 gtk_widget_set_usize (w, width, -2);
2601 /* Now fix the width of the combo box.
2603 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2604 w = GTK_COMBO (w)->entry;
2605 width = gdk_string_width (w->style->font, "PseudoColor___");
2606 gtk_widget_set_usize (w, width, -2);
2608 /* Now fix the width of the file entry text.
2610 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2611 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2612 gtk_widget_set_usize (w, width, -2);
2614 /* Now fix the width of the command line text.
2616 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2617 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2618 gtk_widget_set_usize (w, width, -2);
2622 /* Now fix the height of the list widget:
2623 make it default to being around 10 text-lines high instead of 4.
2625 w = GTK_WIDGET (name_to_widget (s, "list"));
2629 int leading = 3; /* approximate is ok... */
2633 PangoFontMetrics *pain =
2634 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2635 w->style->font_desc,
2636 gtk_get_default_language ());
2637 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2638 pango_font_metrics_get_descent (pain));
2639 #else /* !HAVE_GTK2 */
2640 height = w->style->font->ascent + w->style->font->descent;
2641 #endif /* !HAVE_GTK2 */
2645 height += border * 2;
2646 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2647 gtk_widget_set_usize (w, -2, height);
2654 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2657 static char *up_arrow_xpm[] = {
2680 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2681 the end of the array (Gtk 1.2.5.) */
2682 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2683 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2686 static char *down_arrow_xpm[] = {
2709 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2710 the end of the array (Gtk 1.2.5.) */
2711 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2712 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2716 pixmapify_button (state *s, int down_p)
2720 GtkWidget *pixmapwid;
2724 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2725 style = gtk_widget_get_style (w);
2727 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2728 &style->bg[GTK_STATE_NORMAL],
2730 ? (gchar **) down_arrow_xpm
2731 : (gchar **) up_arrow_xpm));
2732 pixmapwid = gtk_pixmap_new (pixmap, mask);
2733 gtk_widget_show (pixmapwid);
2734 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2735 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2739 map_next_button_cb (GtkWidget *w, gpointer user_data)
2741 state *s = (state *) user_data;
2742 pixmapify_button (s, 1);
2746 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2748 state *s = (state *) user_data;
2749 pixmapify_button (s, 0);
2751 #endif /* !HAVE_GTK2 */
2754 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2758 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2759 GtkAllocation *allocation,
2763 GtkWidgetAuxInfo *aux_info;
2765 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2767 aux_info->width = allocation->width;
2768 aux_info->height = -2;
2772 gtk_widget_size_request (label, &req);
2776 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2779 eschew_gtk_lossage (GtkLabel *label)
2781 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2782 aux_info->width = GTK_WIDGET (label)->allocation.width;
2783 aux_info->height = -2;
2787 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2789 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2790 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2793 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2795 gtk_widget_queue_resize (GTK_WIDGET (label));
2800 populate_demo_window (state *s, int list_elt)
2802 saver_preferences *p = &s->prefs;
2805 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2806 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2807 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2808 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2809 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2811 if (p->mode == BLANK_ONLY)
2814 pretty_name = strdup (_("Blank Screen"));
2815 schedule_preview (s, 0);
2817 else if (p->mode == DONT_BLANK)
2820 pretty_name = strdup (_("Screen Saver Disabled"));
2821 schedule_preview (s, 0);
2825 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2826 ? s->list_elt_to_hack_number[list_elt]
2828 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2832 ? strdup (hack->name)
2833 : make_hack_name (hack->command))
2837 schedule_preview (s, hack->command);
2839 schedule_preview (s, 0);
2843 pretty_name = strdup (_("Preview"));
2845 gtk_frame_set_label (frame1, pretty_name);
2846 gtk_frame_set_label (frame2, pretty_name);
2848 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2849 gtk_entry_set_position (cmd, 0);
2853 sprintf (title, "%s: %.100s Settings",
2854 progclass, (pretty_name ? pretty_name : "???"));
2855 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2858 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2860 ? (hack->visual && *hack->visual
2865 sensitize_demo_widgets (s, (hack ? True : False));
2867 if (pretty_name) free (pretty_name);
2869 ensure_selected_item_visible (list);
2871 s->_selected_list_element = list_elt;
2876 widget_deleter (GtkWidget *widget, gpointer data)
2878 /* #### Well, I want to destroy these widgets, but if I do that, they get
2879 referenced again, and eventually I get a SEGV. So instead of
2880 destroying them, I'll just hide them, and leak a bunch of memory
2881 every time the disk file changes. Go go go Gtk!
2883 #### Ok, that's a lie, I get a crash even if I just hide the widget
2884 and don't ever delete it. Fuck!
2887 gtk_widget_destroy (widget);
2889 gtk_widget_hide (widget);
2894 static char **sort_hack_cmp_names_kludge;
2896 sort_hack_cmp (const void *a, const void *b)
2902 int aa = *(int *) a;
2903 int bb = *(int *) b;
2904 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
2905 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
2906 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
2912 initialize_sort_map (state *s)
2914 saver_preferences *p = &s->prefs;
2917 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2918 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2919 if (s->hacks_available_p) free (s->hacks_available_p);
2921 s->list_elt_to_hack_number = (int *)
2922 calloc (sizeof(int), p->screenhacks_count + 1);
2923 s->hack_number_to_list_elt = (int *)
2924 calloc (sizeof(int), p->screenhacks_count + 1);
2925 s->hacks_available_p = (Bool *)
2926 calloc (sizeof(Bool), p->screenhacks_count + 1);
2928 /* Check which hacks actually exist on $PATH
2930 for (i = 0; i < p->screenhacks_count; i++)
2932 screenhack *hack = p->screenhacks[i];
2933 s->hacks_available_p[i] = on_path_p (hack->command);
2936 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
2940 for (i = 0; i < p->screenhacks_count; i++)
2942 if (!p->ignore_uninstalled_p ||
2943 s->hacks_available_p[i])
2944 s->list_elt_to_hack_number[j++] = i;
2948 for (; j < p->screenhacks_count; j++)
2949 s->list_elt_to_hack_number[j] = -1;
2952 /* Generate list of sortable names (once)
2954 sort_hack_cmp_names_kludge = (char **)
2955 calloc (sizeof(char *), p->screenhacks_count);
2956 for (i = 0; i < p->screenhacks_count; i++)
2958 screenhack *hack = p->screenhacks[i];
2959 char *name = (hack->name && *hack->name
2960 ? strdup (hack->name)
2961 : make_hack_name (hack->command));
2963 for (str = name; *str; str++)
2964 *str = tolower(*str);
2965 sort_hack_cmp_names_kludge[i] = name;
2968 /* Sort list->hack map alphabetically
2970 qsort (s->list_elt_to_hack_number,
2971 p->screenhacks_count,
2972 sizeof(*s->list_elt_to_hack_number),
2977 for (i = 0; i < p->screenhacks_count; i++)
2978 free (sort_hack_cmp_names_kludge[i]);
2979 free (sort_hack_cmp_names_kludge);
2980 sort_hack_cmp_names_kludge = 0;
2982 /* Build inverse table */
2983 for (i = 0; i < p->screenhacks_count; i++)
2984 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2989 maybe_reload_init_file (state *s)
2991 saver_preferences *p = &s->prefs;
2994 static Bool reentrant_lock = False;
2995 if (reentrant_lock) return 0;
2996 reentrant_lock = True;
2998 if (init_file_changed_p (p))
3000 const char *f = init_file_name();
3005 if (!f || !*f) return 0;
3006 b = (char *) malloc (strlen(f) + 1024);
3009 "file \"%s\" has changed, reloading.\n"),
3011 warning_dialog (s->toplevel_widget, b, False, 100);
3015 initialize_sort_map (s);
3017 list_elt = selected_list_element (s);
3018 list = name_to_widget (s, "list");
3019 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3020 populate_hack_list (s);
3021 force_list_select_item (s, list, list_elt, True);
3022 populate_prefs_page (s);
3023 populate_demo_window (s, list_elt);
3024 ensure_selected_item_visible (list);
3029 reentrant_lock = False;
3035 /* Making the preview window have the right X visual (so that GL works.)
3038 static Visual *get_best_gl_visual (state *);
3041 x_visual_to_gdk_visual (Visual *xv)
3043 GList *gvs = gdk_list_visuals();
3044 if (!xv) return gdk_visual_get_system();
3045 for (; gvs; gvs = gvs->next)
3047 GdkVisual *gv = (GdkVisual *) gvs->data;
3048 if (xv == GDK_VISUAL_XVISUAL (gv))
3051 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3052 blurb(), (unsigned long) xv->visualid);
3057 clear_preview_window (state *s)
3062 if (!s->toplevel_widget) return; /* very early */
3063 p = name_to_widget (s, "preview");
3066 if (!window) return;
3068 /* Flush the widget background down into the window, in case a subproc
3070 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3071 gdk_window_clear (window);
3074 int list_elt = selected_list_element (s);
3075 int hack_number = (list_elt >= 0
3076 ? s->list_elt_to_hack_number[list_elt]
3078 Bool available_p = (hack_number >= 0
3079 ? s->hacks_available_p [hack_number]
3082 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3083 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3084 (s->running_preview_error_p
3085 ? (available_p ? 1 : 2)
3087 #else /* !HAVE_GTK2 */
3088 if (s->running_preview_error_p)
3090 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3091 const char * const lines2[] = { N_("Not"), N_("Installed") };
3092 int nlines = countof(lines1);
3093 int lh = p->style->font->ascent + p->style->font->descent;
3097 const char * const *lines = (available_p ? lines1 : lines2);
3099 gdk_window_get_size (window, &w, &h);
3100 y = (h - (lh * nlines)) / 2;
3101 y += p->style->font->ascent;
3102 for (i = 0; i < nlines; i++)
3104 int sw = gdk_string_width (p->style->font, _(lines[i]));
3105 int x = (w - sw) / 2;
3106 gdk_draw_string (window, p->style->font,
3107 p->style->fg_gc[GTK_STATE_NORMAL],
3112 #endif /* !HAVE_GTK2 */
3120 fix_preview_visual (state *s)
3122 GtkWidget *widget = name_to_widget (s, "preview");
3123 Visual *xvisual = get_best_gl_visual (s);
3124 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3125 GdkVisual *dvisual = gdk_visual_get_system();
3126 GdkColormap *cmap = (visual == dvisual
3127 ? gdk_colormap_get_system ()
3128 : gdk_colormap_new (visual, False));
3131 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3132 (visual == dvisual ? "default" : "non-default"),
3133 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3135 if (!GTK_WIDGET_REALIZED (widget) ||
3136 gtk_widget_get_visual (widget) != visual)
3138 gtk_widget_unrealize (widget);
3139 gtk_widget_set_visual (widget, visual);
3140 gtk_widget_set_colormap (widget, cmap);
3141 gtk_widget_realize (widget);
3144 /* Set the Widget colors to be white-on-black. */
3146 GdkWindow *window = widget->window;
3147 GtkStyle *style = gtk_style_copy (widget->style);
3148 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3149 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3150 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3151 GdkGC *fgc = gdk_gc_new(window);
3152 GdkGC *bgc = gdk_gc_new(window);
3153 if (!gdk_color_white (cmap, fg)) abort();
3154 if (!gdk_color_black (cmap, bg)) abort();
3155 gdk_gc_set_foreground (fgc, fg);
3156 gdk_gc_set_background (fgc, bg);
3157 gdk_gc_set_foreground (bgc, bg);
3158 gdk_gc_set_background (bgc, fg);
3159 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3160 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3161 gtk_widget_set_style (widget, style);
3163 /* For debugging purposes, put a title on the window (so that
3164 it can be easily found in the output of "xwininfo -tree".)
3166 gdk_window_set_title (window, "Preview");
3169 gtk_widget_show (widget);
3177 subproc_pretty_name (state *s)
3179 if (s->running_preview_cmd)
3181 char *ps = strdup (s->running_preview_cmd);
3182 char *ss = strchr (ps, ' ');
3184 ss = strrchr (ps, '/');
3195 return strdup ("???");
3200 reap_zombies (state *s)
3202 int wait_status = 0;
3204 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3208 if (pid == s->running_preview_pid)
3210 char *ss = subproc_pretty_name (s);
3211 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3212 (unsigned long) pid, ss);
3216 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3217 (unsigned long) pid);
3223 /* Mostly lifted from driver/subprocs.c */
3225 get_best_gl_visual (state *s)
3227 Display *dpy = GDK_DISPLAY();
3236 av[ac++] = "xscreensaver-gl-helper";
3241 perror ("error creating pipe:");
3248 switch ((int) (forked = fork ()))
3252 sprintf (buf, "%s: couldn't fork", blurb());
3260 close (in); /* don't need this one */
3261 close (ConnectionNumber (dpy)); /* close display fd */
3263 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3265 perror ("could not dup() a new stdout:");
3269 execvp (av[0], av); /* shouldn't return. */
3271 if (errno != ENOENT)
3273 /* Ignore "no such file or directory" errors, unless verbose.
3274 Issue all other exec errors, though. */
3275 sprintf (buf, "%s: running %s", blurb(), av[0]);
3279 /* Note that one must use _exit() instead of exit() in procs forked
3280 off of Gtk programs -- Gtk installs an atexit handler that has a
3281 copy of the X connection (which we've already closed, for safety.)
3282 If one uses exit() instead of _exit(), then one sometimes gets a
3283 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3285 _exit (1); /* exits fork */
3291 int wait_status = 0;
3293 FILE *f = fdopen (in, "r");
3297 close (out); /* don't need this one */
3300 fgets (buf, sizeof(buf)-1, f);
3303 /* Wait for the child to die. */
3304 waitpid (-1, &wait_status, 0);
3306 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3312 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3318 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3320 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3321 blurb(), av[0], result);
3333 kill_preview_subproc (state *s)
3335 s->running_preview_error_p = False;
3338 clear_preview_window (s);
3340 if (s->subproc_check_timer_id)
3342 gtk_timeout_remove (s->subproc_check_timer_id);
3343 s->subproc_check_timer_id = 0;
3344 s->subproc_check_countdown = 0;
3347 if (s->running_preview_pid)
3349 int status = kill (s->running_preview_pid, SIGTERM);
3350 char *ss = subproc_pretty_name (s);
3357 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3358 blurb(), (unsigned long) s->running_preview_pid, ss);
3363 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3364 blurb(), (unsigned long) s->running_preview_pid, ss);
3368 else if (s->debug_p)
3369 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3370 (unsigned long) s->running_preview_pid, ss);
3373 s->running_preview_pid = 0;
3374 if (s->running_preview_cmd) free (s->running_preview_cmd);
3375 s->running_preview_cmd = 0;
3382 /* Immediately and unconditionally launches the given process,
3383 after appending the -window-id option; sets running_preview_pid.
3386 launch_preview_subproc (state *s)
3388 saver_preferences *p = &s->prefs;
3392 const char *cmd = s->desired_preview_cmd;
3394 GtkWidget *pr = name_to_widget (s, "preview");
3395 GdkWindow *window = pr->window;
3397 s->running_preview_error_p = False;
3399 if (s->preview_suppressed_p)
3401 kill_preview_subproc (s);
3405 new_cmd = malloc (strlen (cmd) + 40);
3407 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3410 /* No window id? No command to run. */
3416 strcpy (new_cmd, cmd);
3417 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3421 kill_preview_subproc (s);
3424 s->running_preview_error_p = True;
3425 clear_preview_window (s);
3429 switch ((int) (forked = fork ()))
3434 sprintf (buf, "%s: couldn't fork", blurb());
3436 s->running_preview_error_p = True;
3442 close (ConnectionNumber (GDK_DISPLAY()));
3444 hack_subproc_environment (id, s->debug_p);
3446 usleep (250000); /* pause for 1/4th second before launching, to give
3447 the previous program time to die and flush its X
3448 buffer, so we don't get leftover turds on the
3451 exec_command (p->shell, new_cmd, p->nice_inferior);
3452 /* Don't bother printing an error message when we are unable to
3453 exec subprocesses; we handle that by polling the pid later.
3455 Note that one must use _exit() instead of exit() in procs forked
3456 off of Gtk programs -- Gtk installs an atexit handler that has a
3457 copy of the X connection (which we've already closed, for safety.)
3458 If one uses exit() instead of _exit(), then one sometimes gets a
3459 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3461 _exit (1); /* exits child fork */
3466 if (s->running_preview_cmd) free (s->running_preview_cmd);
3467 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3468 s->running_preview_pid = forked;
3472 char *ss = subproc_pretty_name (s);
3473 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3474 (unsigned long) forked, ss);
3481 schedule_preview_check (s);
3484 if (new_cmd) free (new_cmd);
3489 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3492 hack_environment (state *s)
3494 static const char *def_path =
3495 # ifdef DEFAULT_PATH_PREFIX
3496 DEFAULT_PATH_PREFIX;
3501 Display *dpy = GDK_DISPLAY();
3502 const char *odpy = DisplayString (dpy);
3503 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3504 strcpy (ndpy, "DISPLAY=");
3505 strcat (ndpy, odpy);
3510 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3512 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3513 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3514 So we must leak it (and/or the previous setting). Yay.
3517 if (def_path && *def_path)
3519 const char *opath = getenv("PATH");
3520 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3521 strcpy (npath, "PATH=");
3522 strcat (npath, def_path);
3523 strcat (npath, ":");
3524 strcat (npath, opath);
3528 /* do not free(npath) -- see above */
3531 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3537 hack_subproc_environment (Window preview_window_id, Bool debug_p)
3539 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
3540 necessary yet, but it will make programs work if we had invoked
3541 them with "-root" and not with "-window-id" -- which, of course,
3544 char *nssw = (char *) malloc (40);
3545 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
3547 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
3548 any more, right? It's not Posix, but everyone seems to have it. */
3553 fprintf (stderr, "%s: %s\n", blurb(), nssw);
3555 /* do not free(nssw) -- see above */
3559 /* Called from a timer:
3560 Launches the currently-chosen subprocess, if it's not already running.
3561 If there's a different process running, kills it.
3564 update_subproc_timer (gpointer data)
3566 state *s = (state *) data;
3567 if (! s->desired_preview_cmd)
3568 kill_preview_subproc (s);
3569 else if (!s->running_preview_cmd ||
3570 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3571 launch_preview_subproc (s);
3573 s->subproc_timer_id = 0;
3574 return FALSE; /* do not re-execute timer */
3578 /* Call this when you think you might want a preview process running.
3579 It will set a timer that will actually launch that program a second
3580 from now, if you haven't changed your mind (to avoid double-click
3581 spazzing, etc.) `cmd' may be null meaning "no process".
3584 schedule_preview (state *s, const char *cmd)
3586 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3591 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3593 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3596 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3597 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3599 if (s->subproc_timer_id)
3600 gtk_timeout_remove (s->subproc_timer_id);
3601 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3605 /* Called from a timer:
3606 Checks to see if the subproc that should be running, actually is.
3609 check_subproc_timer (gpointer data)
3611 state *s = (state *) data;
3612 Bool again_p = True;
3614 if (s->running_preview_error_p || /* already dead */
3615 s->running_preview_pid <= 0)
3623 status = kill (s->running_preview_pid, 0);
3624 if (status < 0 && errno == ESRCH)
3625 s->running_preview_error_p = True;
3629 char *ss = subproc_pretty_name (s);
3630 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3631 (unsigned long) s->running_preview_pid, ss,
3632 (s->running_preview_error_p ? "dead" : "alive"));
3636 if (s->running_preview_error_p)
3638 clear_preview_window (s);
3643 /* Otherwise, it's currently alive. We might be checking again, or we
3644 might be satisfied. */
3646 if (--s->subproc_check_countdown <= 0)
3650 return TRUE; /* re-execute timer */
3653 s->subproc_check_timer_id = 0;
3654 s->subproc_check_countdown = 0;
3655 return FALSE; /* do not re-execute timer */
3660 /* Call this just after launching a subprocess.
3661 This sets a timer that will, five times a second for two seconds,
3662 check whether the program is still running. The assumption here
3663 is that if the process didn't stay up for more than a couple of
3664 seconds, then either the program doesn't exist, or it doesn't
3665 take a -window-id argument.
3668 schedule_preview_check (state *s)
3674 fprintf (stderr, "%s: scheduling check\n", blurb());
3676 if (s->subproc_check_timer_id)
3677 gtk_timeout_remove (s->subproc_check_timer_id);
3678 s->subproc_check_timer_id =
3679 gtk_timeout_add (1000 / ticks,
3680 check_subproc_timer, (gpointer) s);
3681 s->subproc_check_countdown = ticks * seconds;
3686 screen_blanked_p (void)
3690 unsigned long nitems, bytesafter;
3692 Display *dpy = GDK_DISPLAY();
3693 Bool blanked_p = False;
3695 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3696 XA_SCREENSAVER_STATUS,
3697 0, 3, False, XA_INTEGER,
3698 &type, &format, &nitems, &bytesafter,
3699 (unsigned char **) &data)
3701 && type == XA_INTEGER
3704 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3706 if (data) free (data);
3711 /* Wake up every now and then and see if the screen is blanked.
3712 If it is, kill off the small-window demo -- no point in wasting
3713 cycles by running two screensavers at once...
3716 check_blanked_timer (gpointer data)
3718 state *s = (state *) data;
3719 Bool blanked_p = screen_blanked_p ();
3720 if (blanked_p && s->running_preview_pid)
3723 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3724 kill_preview_subproc (s);
3727 return True; /* re-execute timer */
3731 /* Setting window manager icon
3735 init_icon (GdkWindow *window)
3737 GdkBitmap *mask = 0;
3740 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3741 (gchar **) logo_50_xpm);
3743 gdk_window_set_icon (window, 0, pixmap, mask);
3747 /* The main demo-mode command loop.
3752 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3753 XrmRepresentation *type, XrmValue *value, XPointer closure)
3756 for (i = 0; quarks[i]; i++)
3758 if (bindings[i] == XrmBindTightly)
3759 fprintf (stderr, (i == 0 ? "" : "."));
3760 else if (bindings[i] == XrmBindLoosely)
3761 fprintf (stderr, "*");
3763 fprintf (stderr, " ??? ");
3764 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3767 fprintf (stderr, ": %s\n", (char *) value->addr);
3775 the_network_is_not_the_computer (state *s)
3777 Display *dpy = GDK_DISPLAY();
3778 char *rversion = 0, *ruser = 0, *rhost = 0;
3779 char *luser, *lhost;
3781 struct passwd *p = getpwuid (getuid ());
3782 const char *d = DisplayString (dpy);
3784 # if defined(HAVE_UNAME)
3786 if (uname (&uts) < 0)
3787 lhost = "<UNKNOWN>";
3789 lhost = uts.nodename;
3791 strcpy (lhost, getenv("SYS$NODE"));
3792 # else /* !HAVE_UNAME && !VMS */
3793 strcat (lhost, "<UNKNOWN>");
3794 # endif /* !HAVE_UNAME && !VMS */
3796 if (p && p->pw_name)
3801 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3803 /* Make a buffer that's big enough for a number of copies of all the
3804 strings, plus some. */
3805 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3806 (ruser ? strlen(ruser) : 0) +
3807 (rhost ? strlen(rhost) : 0) +
3814 if (!rversion || !*rversion)
3818 "The XScreenSaver daemon doesn't seem to be running\n"
3819 "on display \"%s\". Launch it now?"),
3822 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3824 /* Warn that the two processes are running as different users.
3828 "%s is running as user \"%s\" on host \"%s\".\n"
3829 "But the xscreensaver managing display \"%s\"\n"
3830 "is running as user \"%s\" on host \"%s\".\n"
3832 "Since they are different users, they won't be reading/writing\n"
3833 "the same ~/.xscreensaver file, so %s isn't\n"
3834 "going to work right.\n"
3836 "You should either re-run %s as \"%s\", or re-run\n"
3837 "xscreensaver as \"%s\".\n"
3839 "Restart the xscreensaver daemon now?\n"),
3840 progname, luser, lhost,
3842 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3844 progname, (ruser ? ruser : "???"),
3847 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3849 /* Warn that the two processes are running on different hosts.
3853 "%s is running as user \"%s\" on host \"%s\".\n"
3854 "But the xscreensaver managing display \"%s\"\n"
3855 "is running as user \"%s\" on host \"%s\".\n"
3857 "If those two machines don't share a file system (that is,\n"
3858 "if they don't see the same ~%s/.xscreensaver file) then\n"
3859 "%s won't work right.\n"
3861 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3862 progname, luser, lhost,
3864 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3869 else if (!!strcmp (rversion, s->short_version))
3871 /* Warn that the version numbers don't match.
3875 "This is %s version %s.\n"
3876 "But the xscreensaver managing display \"%s\"\n"
3877 "is version %s. This could cause problems.\n"
3879 "Restart the xscreensaver daemon now?\n"),
3880 progname, s->short_version,
3887 warning_dialog (s->toplevel_widget, msg, True, 1);
3889 if (rversion) free (rversion);
3890 if (ruser) free (ruser);
3891 if (rhost) free (rhost);
3896 /* We use this error handler so that X errors are preceeded by the name
3897 of the program that generated them.
3900 demo_ehandler (Display *dpy, XErrorEvent *error)
3902 state *s = global_state_kludge; /* I hate C so much... */
3903 fprintf (stderr, "\nX error in %s:\n", blurb());
3904 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3905 kill_preview_subproc (s);
3911 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3912 of the program that generated them; and also that we can ignore one
3913 particular bogus error message that Gdk madly spews.
3916 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3917 const gchar *message, gpointer user_data)
3919 /* Ignore the message "Got event for unknown window: 0x...".
3920 Apparently some events are coming in for the xscreensaver window
3921 (presumably reply events related to the ClientMessage) and Gdk
3922 feels the need to complain about them. So, just suppress any
3923 messages that look like that one.
3925 if (strstr (message, "unknown window"))
3928 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3929 (log_domain ? log_domain : progclass),
3930 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3931 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3932 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3933 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3934 log_level == G_LOG_LEVEL_INFO ? "info" :
3935 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3937 ((!*message || message[strlen(message)-1] != '\n')
3943 __extension__ /* shut up about "string length is greater than the length
3944 ISO C89 compilers are required to support" when including
3948 static char *defaults[] = {
3949 #include "XScreenSaver_ad.h"
3954 #ifdef HAVE_CRAPPLET
3955 static struct poptOption crapplet_options[] = {
3956 {NULL, '\0', 0, NULL, 0}
3958 #endif /* HAVE_CRAPPLET */
3961 const char *usage = "[--display dpy] [--prefs]"
3962 # ifdef HAVE_CRAPPLET
3965 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
3968 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3970 state *s = (state *) user_data;
3971 Boolean oi = s->initializing_p;
3972 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3973 s->initializing_p = True;
3974 eschew_gtk_lossage (label);
3975 s->initializing_p = oi;
3981 print_widget_tree (GtkWidget *w, int depth)
3984 for (i = 0; i < depth; i++)
3985 fprintf (stderr, " ");
3986 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3988 if (GTK_IS_LIST (w))
3990 for (i = 0; i < depth+1; i++)
3991 fprintf (stderr, " ");
3992 fprintf (stderr, "...list kids...\n");
3994 else if (GTK_IS_CONTAINER (w))
3996 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3999 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4007 delayed_scroll_kludge (gpointer data)
4009 state *s = (state *) data;
4010 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4011 ensure_selected_item_visible (w);
4013 /* Oh, this is just fucking lovely, too. */
4014 w = GTK_WIDGET (name_to_widget (s, "preview"));
4015 gtk_widget_hide (w);
4016 gtk_widget_show (w);
4018 return FALSE; /* do not re-execute timer */
4024 create_xscreensaver_demo (void)
4028 nb = name_to_widget (global_state_kludge, "preview_notebook");
4029 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4031 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4035 create_xscreensaver_settings_dialog (void)
4039 box = name_to_widget (global_state_kludge, "dialog_action_area");
4041 w = name_to_widget (global_state_kludge, "adv_button");
4042 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4044 w = name_to_widget (global_state_kludge, "std_button");
4045 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4047 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4050 #endif /* HAVE_GTK2 */
4053 main (int argc, char **argv)
4057 saver_preferences *p;
4061 Widget toplevel_shell;
4062 char *real_progname = argv[0];
4065 Bool crapplet_p = False;
4069 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4070 textdomain (GETTEXT_PACKAGE);
4073 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4074 # else /* !HAVE_GTK2 */
4075 if (!setlocale (LC_ALL, ""))
4076 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4077 # endif /* !HAVE_GTK2 */
4079 #endif /* ENABLE_NLS */
4081 str = strrchr (real_progname, '/');
4082 if (str) real_progname = str+1;
4085 memset (s, 0, sizeof(*s));
4086 s->initializing_p = True;
4089 global_state_kludge = s; /* I hate C so much... */
4091 progname = real_progname;
4093 s->short_version = (char *) malloc (5);
4094 memcpy (s->short_version, screensaver_id + 17, 4);
4095 s->short_version [4] = 0;
4098 /* Register our error message logger for every ``log domain'' known.
4099 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4100 for all of the domains that seem to be in use.
4103 const char * const domains[] = { 0,
4104 "Gtk", "Gdk", "GLib", "GModule",
4105 "GThread", "Gnome", "GnomeUI" };
4106 for (i = 0; i < countof(domains); i++)
4107 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4110 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4113 const char *dir = DEFAULT_ICONDIR;
4114 if (*dir) add_pixmap_directory (dir);
4116 # endif /* !HAVE_GTK2 */
4117 #endif /* DEFAULT_ICONDIR */
4119 /* This is gross, but Gtk understands --display and not -display...
4121 for (i = 1; i < argc; i++)
4122 if (argv[i][0] && argv[i][1] &&
4123 !strncmp(argv[i], "-display", strlen(argv[i])))
4124 argv[i] = "--display";
4127 /* We need to parse this arg really early... Sigh. */
4128 for (i = 1; i < argc; i++)
4131 (!strcmp(argv[i], "--crapplet") ||
4132 !strcmp(argv[i], "--capplet")))
4134 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4137 for (j = i; j < argc; j++) /* remove it from the list */
4138 argv[j] = argv[j+1];
4140 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4141 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4143 fprintf (stderr, "%s: %s\n", real_progname, usage);
4145 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4148 (!strcmp(argv[i], "--debug") ||
4149 !strcmp(argv[i], "-debug") ||
4150 !strcmp(argv[i], "-d")))
4154 for (j = i; j < argc; j++) /* remove it from the list */
4155 argv[j] = argv[j+1];
4162 (!strcmp(argv[i], "-geometry") ||
4163 !strcmp(argv[i], "-geom") ||
4164 !strcmp(argv[i], "-geo") ||
4165 !strcmp(argv[i], "-g")))
4169 for (j = i; j < argc; j++) /* remove them from the list */
4170 argv[j] = argv[j+2];
4177 (!strcmp(argv[i], "--configdir")))
4181 hack_configuration_path = argv[i+1];
4182 for (j = i; j < argc; j++) /* remove them from the list */
4183 argv[j] = argv[j+2];
4187 if (0 != stat (hack_configuration_path, &st))
4190 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4194 else if (!S_ISDIR (st.st_mode))
4196 fprintf (stderr, "%s: not a directory: %s\n",
4197 blurb(), hack_configuration_path);
4205 fprintf (stderr, "%s: using config directory \"%s\"\n",
4206 progname, hack_configuration_path);
4209 /* Let Gtk open the X connection, then initialize Xt to use that
4210 same connection. Doctor Frankenstein would be proud.
4212 # ifdef HAVE_CRAPPLET
4215 GnomeClient *client;
4216 GnomeClientFlags flags = 0;
4218 int init_results = gnome_capplet_init ("screensaver-properties",
4220 argc, argv, NULL, 0, NULL);
4222 0 upon successful initialization;
4223 1 if --init-session-settings was passed on the cmdline;
4224 2 if --ignore was passed on the cmdline;
4227 So the 1 signifies just to init the settings, and quit, basically.
4228 (Meaning launch the xscreensaver daemon.)
4231 if (init_results < 0)
4234 g_error ("An initialization error occurred while "
4235 "starting xscreensaver-capplet.\n");
4237 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4238 real_progname, init_results);
4243 client = gnome_master_client ();
4246 flags = gnome_client_get_flags (client);
4248 if (flags & GNOME_CLIENT_IS_CONNECTED)
4251 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4252 gnome_client_get_id (client));
4255 char *session_args[20];
4257 session_args[i++] = real_progname;
4258 session_args[i++] = "--capplet";
4259 session_args[i++] = "--init-session-settings";
4260 session_args[i] = 0;
4261 gnome_client_set_priority (client, 20);
4262 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4263 gnome_client_set_restart_command (client, i, session_args);
4267 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4270 gnome_client_flush (client);
4273 if (init_results == 1)
4275 system ("xscreensaver -nosplash &");
4281 # endif /* HAVE_CRAPPLET */
4283 gtk_init (&argc, &argv);
4287 /* We must read exactly the same resources as xscreensaver.
4288 That means we must have both the same progclass *and* progname,
4289 at least as far as the resource database is concerned. So,
4290 put "xscreensaver" in argv[0] while initializing Xt.
4292 argv[0] = "xscreensaver";
4296 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4298 XtToolkitInitialize ();
4299 app = XtCreateApplicationContext ();
4300 dpy = GDK_DISPLAY();
4301 XtAppSetFallbackResources (app, defaults);
4302 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4303 toplevel_shell = XtAppCreateShell (progname, progclass,
4304 applicationShellWidgetClass,
4307 dpy = XtDisplay (toplevel_shell);
4308 db = XtDatabase (dpy);
4309 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4310 XSetErrorHandler (demo_ehandler);
4312 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4313 signal (SIGPIPE, SIG_IGN);
4315 /* After doing Xt-style command-line processing, complain about any
4316 unrecognized command-line arguments.
4318 for (i = 1; i < argc; i++)
4320 char *str = argv[i];
4321 if (str[0] == '-' && str[1] == '-')
4323 if (!strcmp (str, "-prefs"))
4325 else if (crapplet_p)
4326 /* There are lots of random args that we don't care about when we're
4327 started as a crapplet, so just ignore unknown args in that case. */
4331 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4333 fprintf (stderr, "%s: %s\n", real_progname, usage);
4338 /* Load the init file, which may end up consulting the X resource database
4339 and the site-wide app-defaults file. Note that at this point, it's
4340 important that `progname' be "xscreensaver", rather than whatever
4345 hack_environment (s); /* must be before initialize_sort_map() */
4348 initialize_sort_map (s);
4350 /* Now that Xt has been initialized, and the resources have been read,
4351 we can set our `progname' variable to something more in line with
4354 progname = real_progname;
4358 /* Print out all the resources we read. */
4360 XrmName name = { 0 };
4361 XrmClass class = { 0 };
4363 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4369 /* Intern the atoms that xscreensaver_command() needs.
4371 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4372 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4373 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4374 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4375 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4376 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4377 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4378 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4379 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4380 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4381 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4382 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4383 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4386 /* Create the window and all its widgets.
4388 s->base_widget = create_xscreensaver_demo ();
4389 s->popup_widget = create_xscreensaver_settings_dialog ();
4390 s->toplevel_widget = s->base_widget;
4393 /* Set the main window's title. */
4395 char *base_title = _("Screensaver Preferences");
4396 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4397 char *s1, *s2, *s3, *s4;
4398 s1 = (char *) strchr(v, ' '); s1++;
4399 s2 = (char *) strchr(s1, ' ');
4400 s3 = (char *) strchr(v, '('); s3++;
4401 s4 = (char *) strchr(s3, ')');
4405 window_title = (char *) malloc (strlen (base_title) +
4406 strlen (progclass) +
4407 strlen (s1) + strlen (s3) +
4409 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4410 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4411 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4415 /* Adjust the (invisible) notebooks on the popup dialog... */
4417 GtkNotebook *notebook =
4418 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4419 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4423 gtk_widget_hide (std);
4424 # else /* !HAVE_XML */
4425 /* Make the advanced page be the only one available. */
4426 gtk_widget_set_sensitive (std, False);
4427 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4428 gtk_widget_hide (std);
4430 # endif /* !HAVE_XML */
4432 gtk_notebook_set_page (notebook, page);
4433 gtk_notebook_set_show_tabs (notebook, False);
4436 /* Various other widget initializations...
4438 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4439 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4441 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4442 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4445 populate_hack_list (s);
4446 populate_prefs_page (s);
4447 sensitize_demo_widgets (s, False);
4448 fix_text_entry_sizes (s);
4449 scroll_to_current_hack (s);
4451 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4452 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4456 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4457 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4459 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4460 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4462 #endif /* !HAVE_GTK2 */
4464 /* Hook up callbacks to the items on the mode menu. */
4466 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4467 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4468 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4469 for (; kids; kids = kids->next)
4470 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4471 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4476 /* Handle the -prefs command-line argument. */
4479 GtkNotebook *notebook =
4480 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4481 gtk_notebook_set_page (notebook, 1);
4484 # ifdef HAVE_CRAPPLET
4488 GtkWidget *outer_vbox;
4490 gtk_widget_hide (s->toplevel_widget);
4492 capplet = capplet_widget_new ();
4494 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4495 # ifdef HAVE_CRAPPLET_IMMEDIATE
4496 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4497 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4498 /* In crapplet-mode, take off the menubar. */
4499 gtk_widget_hide (name_to_widget (s, "menubar"));
4501 /* Reparent our top-level container to be a child of the capplet
4504 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4505 gtk_widget_ref (outer_vbox);
4506 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4508 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4509 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4511 /* Find the window above us, and set the title and close handler. */
4513 GtkWidget *window = capplet;
4514 while (window && !GTK_IS_WINDOW (window))
4515 window = window->parent;
4518 gtk_window_set_title (GTK_WINDOW (window), window_title);
4519 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4520 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4525 s->toplevel_widget = capplet;
4527 # endif /* HAVE_CRAPPLET */
4530 /* The Gnome folks hate the menubar. I think it's important to have access
4531 to the commands on the File menu (Restart Daemon, etc.) and to the
4532 About and Documentation commands on the Help menu.
4536 gtk_widget_hide (name_to_widget (s, "menubar"));
4540 free (window_title);
4543 /* After picking the default size, allow -geometry to override it. */
4545 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
4547 gtk_widget_show (s->toplevel_widget);
4548 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4549 fix_preview_visual (s);
4551 /* Realize page zero, so that we can diddle the scrollbar when the
4552 user tabs back to it -- otherwise, the current hack isn't scrolled
4553 to the first time they tab back there, when started with "-prefs".
4554 (Though it is if they then tab away, and back again.)
4556 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4557 #### understands this crap, explain to me how to make this work.
4559 gtk_widget_realize (name_to_widget (s, "demos_table"));
4562 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4565 /* Issue any warnings about the running xscreensaver daemon. */
4566 the_network_is_not_the_computer (s);
4569 /* Run the Gtk event loop, and not the Xt event loop. This means that
4570 if there were Xt timers or fds registered, they would never get serviced,
4571 and if there were any Xt widgets, they would never have events delivered.
4572 Fortunately, we're using Gtk for all of the UI, and only initialized
4573 Xt so that we could process the command line and use the X resource
4576 s->initializing_p = False;
4578 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4579 after we start up. Otherwise, it always appears scrolled to the top
4580 when in crapplet-mode. */
4581 gtk_timeout_add (500, delayed_scroll_kludge, s);
4585 /* Load every configurator in turn, to scan them for errors all at once. */
4588 for (i = 0; i < p->screenhacks_count; i++)
4590 screenhack *hack = p->screenhacks[i];
4591 conf_data *d = load_configurator (hack->command, False);
4592 if (d) free_conf_data (d);
4598 # ifdef HAVE_CRAPPLET
4600 capplet_gtk_main ();
4602 # endif /* HAVE_CRAPPLET */
4605 kill_preview_subproc (s);
4609 #endif /* HAVE_GTK -- whole file */