1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2002 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>
29 #endif /* ENABLE_NLS */
32 # include <pwd.h> /* for getpwuid() */
38 # include <sys/utsname.h> /* for uname() */
39 #endif /* HAVE_UNAME */
49 #ifdef HAVE_SYS_WAIT_H
50 # include <sys/wait.h> /* for waitpid() and associated macros */
54 #include <X11/Xproto.h> /* for CARD32 */
55 #include <X11/Xatom.h> /* for XA_INTEGER */
56 #include <X11/Intrinsic.h>
57 #include <X11/StringDefs.h>
59 /* We don't actually use any widget internals, but these are included
60 so that gdb will have debug info for the widgets... */
61 #include <X11/IntrinsicP.h>
62 #include <X11/ShellP.h>
66 # include <X11/Xmu/Error.h>
68 # include <Xmu/Error.h>
78 # include <capplet-widget.h>
84 #include <glade/glade-xml.h>
85 #endif /* HAVE_GTK2 */
87 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
88 # define GLADE_DIR DEFAULT_ICONDIR
90 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
91 # define DEFAULT_ICONDIR GLADE_DIR
97 #include "resources.h" /* for parse_time() */
98 #include "visual.h" /* for has_writable_cells() */
99 #include "remote.h" /* for xscreensaver_command() */
102 #include "logo-50.xpm"
103 #include "logo-180.xpm"
105 #include "demo-Gtk-widgets.h"
106 #include "demo-Gtk-support.h"
107 #include "demo-Gtk-conf.h"
119 #endif /* HAVE_GTK2 */
122 extern void exec_command (const char *shell, const char *command, int nice);
125 #define countof(x) (sizeof((x))/sizeof((*x)))
129 char *progclass = "XScreenSaver";
132 /* The order of the items in the mode menu. */
133 static int mode_menu_order[] = {
134 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS };
139 char *short_version; /* version number of this xscreensaver build */
141 GtkWidget *toplevel_widget; /* the main window */
142 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
143 GtkWidget *popup_widget; /* the "Settings" dialog */
144 conf_data *cdata; /* private data for per-hack configuration */
147 GladeXML *glade_ui; /* Glade UI file */
148 #endif /* HAVE_GTK2 */
150 Bool debug_p; /* whether to print diagnostics */
151 Bool initializing_p; /* flag for breaking recursion loops */
152 Bool saving_p; /* flag for breaking recursion loops */
154 char *desired_preview_cmd; /* subprocess we intend to run */
155 char *running_preview_cmd; /* subprocess we are currently running */
156 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
157 Bool running_preview_error_p; /* whether the pid died abnormally */
159 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
160 int subproc_timer_id; /* timer to delay subproc launch */
161 int subproc_check_timer_id; /* timer to check whether it started up */
162 int subproc_check_countdown; /* how many more checks left */
164 int *list_elt_to_hack_number; /* table for sorting the hack list */
165 int *hack_number_to_list_elt; /* the inverse table */
166 Bool *hacks_available_p; /* whether hacks are on $PATH */
167 int list_count; /* how many items are in the list: this may be
168 less than p->screenhacks_count, if some are
171 int _selected_list_element; /* don't use this: call
172 selected_list_element() instead */
174 saver_preferences prefs;
179 /* Total fucking evilness due to the fact that it's rocket science to get
180 a closure object of our own down into the various widget callbacks. */
181 static state *global_state_kludge;
184 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
185 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
186 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
189 static void populate_demo_window (state *, int list_elt);
190 static void populate_prefs_page (state *);
191 static void populate_popup_window (state *);
193 static Bool flush_dialog_changes_and_save (state *);
194 static Bool flush_popup_changes_and_save (state *);
196 static int maybe_reload_init_file (state *);
197 static void await_xscreensaver (state *);
199 static void schedule_preview (state *, const char *cmd);
200 static void kill_preview_subproc (state *);
201 static void schedule_preview_check (state *);
205 /* Some random utility functions
211 time_t now = time ((time_t *) 0);
212 char *ct = (char *) ctime (&now);
213 static char buf[255];
214 int n = strlen(progname);
216 strncpy(buf, progname, n);
219 strncpy(buf+n, ct+11, 8);
220 strcpy(buf+n+9, ": ");
226 name_to_widget (state *s, const char *name)
236 /* First try to load the Glade file from the current directory;
237 if there isn't one there, check the installed directory.
239 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
240 const char * const files[] = { GLADE_FILE_NAME,
241 GLADE_DIR "/" GLADE_FILE_NAME };
243 for (i = 0; i < countof (files); i++)
246 if (!stat (files[i], &st))
248 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
255 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
256 "\tfrom " GLADE_DIR "/ or current directory.\n",
260 # undef GLADE_FILE_NAME
262 glade_xml_signal_autoconnect (s->glade_ui);
265 w = glade_xml_get_widget (s->glade_ui, name);
267 #else /* !HAVE_GTK2 */
269 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
272 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
274 #endif /* HAVE_GTK2 */
277 fprintf (stderr, "%s: no widget \"%s\"\n", blurb(), name);
282 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
283 Takes a scroller, viewport, or list as an argument.
286 ensure_selected_item_visible (GtkWidget *widget)
290 GtkTreeSelection *selection;
294 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
295 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
298 path = gtk_tree_model_get_path (model, &iter);
300 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (widget),
301 path, NULL, FALSE, 0.0, 0.0);
302 #else /* !HAVE_GTK2 */
304 GtkScrolledWindow *scroller = 0;
306 GtkList *list_widget = 0;
310 GtkWidget *selected = 0;
313 gint parent_h, child_y, child_h, children_h, ignore;
314 double ratio_t, ratio_b;
316 if (GTK_IS_SCROLLED_WINDOW (widget))
318 scroller = GTK_SCROLLED_WINDOW (widget);
319 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
320 list_widget = GTK_LIST (GTK_BIN(vp)->child);
322 else if (GTK_IS_VIEWPORT (widget))
324 vp = GTK_VIEWPORT (widget);
325 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
326 list_widget = GTK_LIST (GTK_BIN(vp)->child);
328 else if (GTK_IS_LIST (widget))
330 list_widget = GTK_LIST (widget);
331 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
332 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
337 slist = list_widget->selection;
338 selected = (slist ? GTK_WIDGET (slist->data) : 0);
342 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
344 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
345 kids; kids = kids->next)
348 adj = gtk_scrolled_window_get_vadjustment (scroller);
350 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
351 &ignore, &ignore, &ignore, &parent_h, &ignore);
352 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
353 &ignore, &child_y, &ignore, &child_h, &ignore);
354 children_h = nkids * child_h;
356 ratio_t = ((double) child_y) / ((double) children_h);
357 ratio_b = ((double) child_y + child_h) / ((double) children_h);
359 if (adj->upper == 0.0) /* no items in list */
362 if (ratio_t < (adj->value / adj->upper) ||
363 ratio_b > ((adj->value + adj->page_size) / adj->upper))
366 int slop = parent_h * 0.75; /* how much to overshoot by */
368 if (ratio_t < (adj->value / adj->upper))
370 double ratio_w = ((double) parent_h) / ((double) children_h);
371 double ratio_l = (ratio_b - ratio_t);
372 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
375 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
377 target = ratio_t * adj->upper;
381 if (target > adj->upper - adj->page_size)
382 target = adj->upper - adj->page_size;
386 gtk_adjustment_set_value (adj, target);
388 #endif /* !HAVE_GTK2 */
392 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
394 GtkWidget *shell = GTK_WIDGET (user_data);
395 while (shell->parent)
396 shell = shell->parent;
397 gtk_widget_destroy (GTK_WIDGET (shell));
401 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
403 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
405 restart_menu_cb (widget, user_data);
406 warning_dialog_dismiss_cb (widget, user_data);
410 warning_dialog (GtkWidget *parent, const char *message,
411 Boolean restart_button_p, int center)
413 char *msg = strdup (message);
416 GtkWidget *dialog = gtk_dialog_new ();
417 GtkWidget *label = 0;
419 GtkWidget *cancel = 0;
422 while (parent && !parent->window)
423 parent = parent->parent;
425 if (!GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
427 fprintf (stderr, "%s: too early for dialog?\n", progname);
435 char *s = strchr (head, '\n');
438 sprintf (name, "label%d", i++);
441 label = gtk_label_new (head);
443 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
444 #endif /* HAVE_GTK2 */
449 GTK_WIDGET (label)->style =
450 gtk_style_copy (GTK_WIDGET (label)->style);
451 GTK_WIDGET (label)->style->font =
452 gdk_font_load (get_string_resource("warning_dialog.headingFont",
454 gtk_widget_set_style (GTK_WIDGET (label),
455 GTK_WIDGET (label)->style);
457 #endif /* !HAVE_GTK2 */
459 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
460 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
461 label, TRUE, TRUE, 0);
462 gtk_widget_show (label);
473 label = gtk_label_new ("");
474 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
475 label, TRUE, TRUE, 0);
476 gtk_widget_show (label);
478 label = gtk_hbutton_box_new ();
479 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
480 label, TRUE, TRUE, 0);
483 if (restart_button_p)
485 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
486 gtk_container_add (GTK_CONTAINER (label), cancel);
489 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
490 gtk_container_add (GTK_CONTAINER (label), ok);
492 #else /* !HAVE_GTK2 */
494 ok = gtk_button_new_with_label ("OK");
495 gtk_container_add (GTK_CONTAINER (label), ok);
497 if (restart_button_p)
499 cancel = gtk_button_new_with_label ("Cancel");
500 gtk_container_add (GTK_CONTAINER (label), cancel);
503 #endif /* !HAVE_GTK2 */
505 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
506 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
507 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
508 GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
509 gtk_widget_show (ok);
510 gtk_widget_grab_focus (ok);
514 GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
515 gtk_widget_show (cancel);
517 gtk_widget_show (label);
518 gtk_widget_show (dialog);
520 if (restart_button_p)
522 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
523 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
525 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
526 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
531 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
532 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
536 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
537 GTK_WIDGET (parent)->window);
540 gtk_window_present (GTK_WINDOW (dialog));
541 #else /* !HAVE_GTK2 */
542 gdk_window_show (GTK_WIDGET (dialog)->window);
543 gdk_window_raise (GTK_WIDGET (dialog)->window);
544 #endif /* !HAVE_GTK2 */
551 run_cmd (state *s, Atom command, int arg)
556 flush_dialog_changes_and_save (s);
557 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
562 sprintf (buf, "Error:\n\n%s", err);
564 strcpy (buf, "Unknown error!");
565 warning_dialog (s->toplevel_widget, buf, False, 100);
572 run_hack (state *s, int list_elt, Bool report_errors_p)
575 if (list_elt < 0) return;
576 hack_number = s->list_elt_to_hack_number[list_elt];
578 flush_dialog_changes_and_save (s);
579 schedule_preview (s, 0);
581 run_cmd (s, XA_DEMO, hack_number + 1);
585 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
597 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
599 state *s = global_state_kludge; /* I hate C so much... */
600 flush_dialog_changes_and_save (s);
601 kill_preview_subproc (s);
606 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
608 state *s = (state *) data;
609 flush_dialog_changes_and_save (s);
616 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
619 char *vers = strdup (screensaver_id + 4);
622 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
624 s = strchr (vers, ',');
628 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
629 non-ASCII characters aren't allowed in localizable string keys."
630 (I don't want to just use (c) instead of © because that doesn't
631 look as good in the plain-old default Latin1 "C" locale.)
634 sprintf(copy, ("Copyright \xC2\xA9 1991-2002 %s"), s);
635 #else /* !HAVE_GTK2 */
636 sprintf(copy, ("Copyright \251 1991-2002 %s"), s);
637 #endif /* !HAVE_GTK2 */
639 sprintf (msg, "%s\n\n%s", copy, desc);
641 /* I can't make gnome_about_new() work here -- it starts dying in
642 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
643 then this might be the thing to do:
647 const gchar *auth[] = { 0 };
648 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
650 gtk_widget_show (about);
652 #else / * GTK but not GNOME * /
656 GdkColormap *colormap;
657 GdkPixmap *gdkpixmap;
660 GtkWidget *dialog = gtk_dialog_new ();
661 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
662 GtkWidget *parent = GTK_WIDGET (menuitem);
663 while (parent->parent)
664 parent = parent->parent;
666 hbox = gtk_hbox_new (FALSE, 20);
667 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
668 hbox, TRUE, TRUE, 0);
670 colormap = gtk_widget_get_colormap (parent);
672 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
673 (gchar **) logo_180_xpm);
674 icon = gtk_pixmap_new (gdkpixmap, mask);
675 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
677 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
679 vbox = gtk_vbox_new (FALSE, 0);
680 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
682 label1 = gtk_label_new (vers);
683 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
684 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
685 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
688 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
689 GTK_WIDGET (label1)->style->font =
690 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
691 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
692 #endif /* HAVE_GTK2 */
694 label2 = gtk_label_new (msg);
695 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
696 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
697 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
700 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
701 GTK_WIDGET (label2)->style->font =
702 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
703 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
704 #endif /* HAVE_GTK2 */
706 hb = gtk_hbutton_box_new ();
708 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
712 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
713 #else /* !HAVE_GTK2 */
714 ok = gtk_button_new_with_label (_("OK"));
715 #endif /* !HAVE_GTK2 */
716 gtk_container_add (GTK_CONTAINER (hb), ok);
718 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
719 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
720 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
722 gtk_widget_show (hbox);
723 gtk_widget_show (icon);
724 gtk_widget_show (vbox);
725 gtk_widget_show (label1);
726 gtk_widget_show (label2);
727 gtk_widget_show (hb);
728 gtk_widget_show (ok);
729 gtk_widget_show (dialog);
731 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
732 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
734 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
735 GTK_WIDGET (parent)->window);
736 gdk_window_show (GTK_WIDGET (dialog)->window);
737 gdk_window_raise (GTK_WIDGET (dialog)->window);
743 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
745 state *s = global_state_kludge; /* I hate C so much... */
746 saver_preferences *p = &s->prefs;
749 if (!p->help_url || !*p->help_url)
751 warning_dialog (s->toplevel_widget,
753 "No Help URL has been specified.\n"), False, 100);
757 help_command = (char *) malloc (strlen (p->load_url_command) +
758 (strlen (p->help_url) * 2) + 20);
759 strcpy (help_command, "( ");
760 sprintf (help_command + strlen(help_command),
761 p->load_url_command, p->help_url, p->help_url);
762 strcat (help_command, " ) &");
763 fprintf(stderr, "## %s\n", help_command);
764 system (help_command);
770 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
772 state *s = global_state_kludge; /* I hate C so much... */
773 run_cmd (s, XA_ACTIVATE, 0);
778 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
780 state *s = global_state_kludge; /* I hate C so much... */
781 run_cmd (s, XA_LOCK, 0);
786 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
788 state *s = global_state_kludge; /* I hate C so much... */
789 run_cmd (s, XA_EXIT, 0);
794 restart_menu_cb (GtkWidget *widget, gpointer user_data)
796 state *s = global_state_kludge; /* I hate C so much... */
797 flush_dialog_changes_and_save (s);
798 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
800 system ("xscreensaver -nosplash &");
802 await_xscreensaver (s);
806 await_xscreensaver (state *s)
810 Display *dpy = GDK_DISPLAY();
811 /* GtkWidget *dialog = 0;*/
814 while (!rversion && (--countdown > 0))
816 /* Check for the version of the running xscreensaver... */
817 server_xscreensaver_version (dpy, &rversion, 0, 0);
819 /* If it's not there yet, wait a second... */
824 /* if (dialog) gtk_widget_destroy (dialog);*/
833 /* Timed out, no screensaver running. */
836 Bool root_p = (geteuid () == 0);
840 "The xscreensaver daemon did not start up properly.\n"
845 _("You are running as root. This usually means that xscreensaver\n"
846 "was unable to contact your X server because access control is\n"
847 "turned on. Try running this command:\n"
849 " xhost +localhost\n"
851 "and then selecting `File / Restart Daemon'.\n"
853 "Note that turning off access control will allow anyone logged\n"
854 "on to this machine to access your screen, which might be\n"
855 "considered a security problem. Please read the xscreensaver\n"
856 "manual and FAQ for more information.\n"
858 "You shouldn't run X as root. Instead, you should log in as a\n"
859 "normal user, and `su' as necessary."));
861 strcat (buf, _("Please check your $PATH and permissions."));
863 warning_dialog (s->toplevel_widget, buf, False, 1);
869 selected_list_element (state *s)
871 return s->_selected_list_element;
876 demo_write_init_file (state *s, saver_preferences *p)
880 /* #### try to figure out why shit keeps getting reordered... */
881 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
885 if (!write_init_file (p, s->short_version, False))
888 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
893 const char *f = init_file_name();
895 warning_dialog (s->toplevel_widget,
896 _("Error:\n\nCouldn't determine init file name!\n"),
900 char *b = (char *) malloc (strlen(f) + 1024);
901 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
902 warning_dialog (s->toplevel_widget, b, False, 100);
911 run_this_cb (GtkButton *button, gpointer user_data)
913 state *s = global_state_kludge; /* I hate C so much... */
914 int list_elt = selected_list_element (s);
915 if (list_elt < 0) return;
916 if (!flush_dialog_changes_and_save (s))
917 run_hack (s, list_elt, True);
922 manual_cb (GtkButton *button, gpointer user_data)
924 state *s = global_state_kludge; /* I hate C so much... */
925 saver_preferences *p = &s->prefs;
926 GtkWidget *list_widget = name_to_widget (s, "list");
927 int list_elt = selected_list_element (s);
929 char *name, *name2, *cmd, *str;
930 if (list_elt < 0) return;
931 hack_number = s->list_elt_to_hack_number[list_elt];
933 flush_dialog_changes_and_save (s);
934 ensure_selected_item_visible (list_widget);
936 name = strdup (p->screenhacks[hack_number]->command);
938 while (isspace (*name2)) name2++;
940 while (*str && !isspace (*str)) str++;
942 str = strrchr (name2, '/');
943 if (str) name = str+1;
945 cmd = get_string_resource ("manualCommand", "ManualCommand");
948 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
950 sprintf (cmd2 + strlen (cmd2),
952 name2, name2, name2, name2);
953 strcat (cmd2, " ) &");
959 warning_dialog (GTK_WIDGET (button),
960 _("Error:\n\nno `manualCommand' resource set."),
969 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
971 GtkWidget *parent = name_to_widget (s, "scroller");
972 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
976 GtkTreeSelection *selection;
977 #endif /* HAVE_GTK2 */
979 if (!was) gtk_widget_set_sensitive (parent, True);
981 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
983 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
984 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
985 gtk_tree_selection_select_iter (selection, &iter);
986 #else /* !HAVE_GTK2 */
987 gtk_list_select_item (GTK_LIST (list), list_elt);
988 #endif /* !HAVE_GTK2 */
989 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
990 if (!was) gtk_widget_set_sensitive (parent, False);
995 run_next_cb (GtkButton *button, gpointer user_data)
997 state *s = global_state_kludge; /* I hate C so much... */
998 /* saver_preferences *p = &s->prefs; */
999 Bool ops = s->preview_suppressed_p;
1001 GtkWidget *list_widget = name_to_widget (s, "list");
1002 int list_elt = selected_list_element (s);
1009 if (list_elt >= s->list_count)
1012 s->preview_suppressed_p = True;
1014 flush_dialog_changes_and_save (s);
1015 force_list_select_item (s, list_widget, list_elt, True);
1016 populate_demo_window (s, list_elt);
1017 run_hack (s, list_elt, False);
1019 s->preview_suppressed_p = ops;
1024 run_prev_cb (GtkButton *button, gpointer user_data)
1026 state *s = global_state_kludge; /* I hate C so much... */
1027 /* saver_preferences *p = &s->prefs; */
1028 Bool ops = s->preview_suppressed_p;
1030 GtkWidget *list_widget = name_to_widget (s, "list");
1031 int list_elt = selected_list_element (s);
1034 list_elt = s->list_count - 1;
1039 list_elt = s->list_count - 1;
1041 s->preview_suppressed_p = True;
1043 flush_dialog_changes_and_save (s);
1044 force_list_select_item (s, list_widget, list_elt, True);
1045 populate_demo_window (s, list_elt);
1046 run_hack (s, list_elt, False);
1048 s->preview_suppressed_p = ops;
1052 /* Writes the given settings into prefs.
1053 Returns true if there was a change, False otherwise.
1054 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1057 flush_changes (state *s,
1060 const char *command,
1063 saver_preferences *p = &s->prefs;
1064 Bool changed = False;
1067 if (list_elt < 0 || list_elt >= s->list_count)
1070 hack_number = s->list_elt_to_hack_number[list_elt];
1071 hack = p->screenhacks[hack_number];
1073 if (enabled_p != -1 &&
1074 enabled_p != hack->enabled_p)
1076 hack->enabled_p = enabled_p;
1079 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1080 blurb(), hack->name, enabled_p);
1085 if (!hack->command || !!strcmp (command, hack->command))
1087 if (hack->command) free (hack->command);
1088 hack->command = strdup (command);
1091 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1092 blurb(), hack->name, command);
1098 const char *ov = hack->visual;
1099 if (!ov || !*ov) ov = "any";
1100 if (!*visual) visual = "any";
1101 if (!!strcasecmp (visual, ov))
1103 if (hack->visual) free (hack->visual);
1104 hack->visual = strdup (visual);
1107 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1108 blurb(), hack->name, visual);
1116 /* Helper for the text fields that contain time specifications:
1117 this parses the text, and does error checking.
1120 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1125 if (!sec_p || strchr (line, ':'))
1126 value = parse_time ((char *) line, sec_p, True);
1130 if (sscanf (line, "%u%c", &value, &c) != 1)
1136 value *= 1000; /* Time measures in microseconds */
1142 "Unparsable time format: \"%s\"\n"),
1144 warning_dialog (s->toplevel_widget, b, False, 100);
1153 directory_p (const char *path)
1156 if (!path || !*path)
1158 else if (stat (path, &st))
1160 else if (!S_ISDIR (st.st_mode))
1167 normalize_directory (const char *path)
1171 if (!path || !*path) return 0;
1173 p2 = (char *) malloc (L + 2);
1175 if (p2[L-1] == '/') /* remove trailing slash */
1178 for (s = p2; s && *s; s++)
1181 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1182 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1185 while (s0 > p2 && s0[-1] != '/')
1195 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1196 strcpy (s, s+2), s--;
1197 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1201 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1202 while (s[0] == '/' && s[1] == '/')
1205 /* and strip trailing whitespace for good measure. */
1207 while (isspace(p2[L-1]))
1220 } FlushForeachClosure;
1223 flush_checkbox (GtkTreeModel *model,
1228 FlushForeachClosure *closure = data;
1231 gtk_tree_model_get (model, iter,
1232 COL_ENABLED, &checked,
1235 if (flush_changes (closure->s, closure->i,
1237 *closure->changed = True;
1241 /* don't remove row */
1245 #endif /* HAVE_GTK2 */
1247 /* Flush out any changes made in the main dialog window (where changes
1248 take place immediately: clicking on a checkbox causes the init file
1249 to be written right away.)
1252 flush_dialog_changes_and_save (state *s)
1254 saver_preferences *p = &s->prefs;
1255 saver_preferences P2, *p2 = &P2;
1257 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1258 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1259 FlushForeachClosure closure;
1260 #else /* !HAVE_GTK2 */
1261 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1262 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1264 #endif /* !HAVE_GTK2 */
1266 Bool changed = False;
1269 if (s->saving_p) return False;
1274 /* Flush any checkbox changes in the list down into the prefs struct.
1278 closure.changed = &changed;
1280 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1282 #else /* !HAVE_GTK2 */
1284 for (i = 0; kids; kids = kids->next, i++)
1286 GtkWidget *line = GTK_WIDGET (kids->data);
1287 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1288 GtkWidget *line_check =
1289 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1291 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1293 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1296 #endif /* ~HAVE_GTK2 */
1298 /* Flush the non-hack-specific settings down into the prefs struct.
1301 # define SECONDS(FIELD,NAME) \
1302 w = name_to_widget (s, (NAME)); \
1303 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1305 # define MINUTES(FIELD,NAME) \
1306 w = name_to_widget (s, (NAME)); \
1307 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1309 # define CHECKBOX(FIELD,NAME) \
1310 w = name_to_widget (s, (NAME)); \
1311 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1313 # define PATHNAME(FIELD,NAME) \
1314 w = name_to_widget (s, (NAME)); \
1315 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1317 MINUTES (&p2->timeout, "timeout_spinbutton");
1318 MINUTES (&p2->cycle, "cycle_spinbutton");
1319 CHECKBOX (p2->lock_p, "lock_button");
1320 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1322 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1323 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1324 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1325 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1327 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1328 CHECKBOX (p2->grab_video_p, "grab_video_button");
1329 CHECKBOX (p2->random_image_p, "grab_image_button");
1330 PATHNAME (p2->image_directory, "image_text");
1332 CHECKBOX (p2->verbose_p, "verbose_button");
1333 CHECKBOX (p2->capture_stderr_p, "capture_button");
1334 CHECKBOX (p2->splash_p, "splash_button");
1336 CHECKBOX (p2->install_cmap_p, "install_button");
1337 CHECKBOX (p2->fade_p, "fade_button");
1338 CHECKBOX (p2->unfade_p, "unfade_button");
1339 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1346 /* Warn if the image directory doesn't exist.
1348 if (p2->image_directory &&
1349 *p2->image_directory &&
1350 !directory_p (p2->image_directory))
1353 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1354 p2->image_directory);
1355 warning_dialog (s->toplevel_widget, b, False, 100);
1359 /* Map the mode menu to `saver_mode' enum values. */
1361 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1362 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1363 GtkWidget *selected = gtk_menu_get_active (menu);
1364 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1365 int menu_elt = g_list_index (kids, (gpointer) selected);
1366 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1367 p2->mode = mode_menu_order[menu_elt];
1370 if (p2->mode == ONE_HACK)
1372 int list_elt = selected_list_element (s);
1373 p2->selected_hack = (list_elt >= 0
1374 ? s->list_elt_to_hack_number[list_elt]
1378 # define COPY(field, name) \
1379 if (p->field != p2->field) { \
1382 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1384 p->field = p2->field
1387 COPY(selected_hack, "selected_hack");
1389 COPY(timeout, "timeout");
1390 COPY(cycle, "cycle");
1391 COPY(lock_p, "lock_p");
1392 COPY(lock_timeout, "lock_timeout");
1394 COPY(dpms_enabled_p, "dpms_enabled_p");
1395 COPY(dpms_standby, "dpms_standby");
1396 COPY(dpms_suspend, "dpms_suspend");
1397 COPY(dpms_off, "dpms_off");
1399 COPY(verbose_p, "verbose_p");
1400 COPY(capture_stderr_p, "capture_stderr_p");
1401 COPY(splash_p, "splash_p");
1403 COPY(install_cmap_p, "install_cmap_p");
1404 COPY(fade_p, "fade_p");
1405 COPY(unfade_p, "unfade_p");
1406 COPY(fade_seconds, "fade_seconds");
1408 COPY(grab_desktop_p, "grab_desktop_p");
1409 COPY(grab_video_p, "grab_video_p");
1410 COPY(random_image_p, "random_image_p");
1414 if (!p->image_directory ||
1415 !p2->image_directory ||
1416 strcmp(p->image_directory, p2->image_directory))
1420 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1421 blurb(), p2->image_directory);
1423 if (p->image_directory && p->image_directory != p2->image_directory)
1424 free (p->image_directory);
1425 p->image_directory = p2->image_directory;
1426 p2->image_directory = 0;
1428 populate_prefs_page (s);
1432 Display *dpy = GDK_DISPLAY();
1433 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1434 sync_server_dpms_settings (dpy, enabled_p,
1435 p->dpms_standby / 1000,
1436 p->dpms_suspend / 1000,
1440 changed = demo_write_init_file (s, p);
1443 s->saving_p = False;
1448 /* Flush out any changes made in the popup dialog box (where changes
1449 take place only when the OK button is clicked.)
1452 flush_popup_changes_and_save (state *s)
1454 Bool changed = False;
1455 saver_preferences *p = &s->prefs;
1456 int list_elt = selected_list_element (s);
1458 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1459 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1461 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1462 const char *command = gtk_entry_get_text (cmd);
1467 if (s->saving_p) return False;
1473 if (maybe_reload_init_file (s) != 0)
1479 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1481 if (!strcasecmp (visual, "")) visual = "";
1482 else if (!strcasecmp (visual, "any")) visual = "";
1483 else if (!strcasecmp (visual, "default")) visual = "Default";
1484 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1485 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1486 else if (!strcasecmp (visual, "best")) visual = "Best";
1487 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1488 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1489 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1490 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1491 else if (!strcasecmp (visual, "color")) visual = "Color";
1492 else if (!strcasecmp (visual, "gl")) visual = "GL";
1493 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1494 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1495 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1496 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1497 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1498 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1499 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1500 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
1501 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1504 gdk_beep (); /* unparsable */
1506 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1509 changed = flush_changes (s, list_elt, -1, command, visual);
1512 changed = demo_write_init_file (s, p);
1514 /* Do this to re-launch the hack if (and only if) the command line
1516 populate_demo_window (s, selected_list_element (s));
1520 s->saving_p = False;
1526 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1528 state *s = global_state_kludge; /* I hate C so much... */
1529 if (! s->initializing_p)
1531 s->initializing_p = True;
1532 flush_dialog_changes_and_save (s);
1533 s->initializing_p = False;
1538 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1540 pref_changed_cb (widget, user_data);
1544 /* Callback on menu items in the "mode" options menu.
1547 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1549 state *s = (state *) user_data;
1550 saver_preferences *p = &s->prefs;
1551 GtkWidget *list = name_to_widget (s, "list");
1554 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1556 saver_mode new_mode;
1557 int old_selected = p->selected_hack;
1561 if (menu_items->data == widget)
1564 menu_items = menu_items->next;
1566 if (!menu_items) abort();
1568 new_mode = mode_menu_order[menu_index];
1570 /* Keep the same list element displayed as before; except if we're
1571 switching *to* "one screensaver" mode from any other mode, scroll
1572 to and select "the one".
1575 if (new_mode == ONE_HACK)
1576 list_elt = (p->selected_hack >= 0
1577 ? s->hack_number_to_list_elt[p->selected_hack]
1581 list_elt = selected_list_element (s);
1584 saver_mode old_mode = p->mode;
1586 populate_demo_window (s, list_elt);
1587 force_list_select_item (s, list, list_elt, True);
1588 p->mode = old_mode; /* put it back, so the init file gets written */
1591 pref_changed_cb (widget, user_data);
1593 if (old_selected != p->selected_hack)
1594 abort(); /* dammit, not again... */
1599 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1600 gint page_num, gpointer user_data)
1602 state *s = global_state_kludge; /* I hate C so much... */
1603 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1605 /* If we're switching to page 0, schedule the current hack to be run.
1606 Otherwise, schedule it to stop. */
1608 populate_demo_window (s, selected_list_element (s));
1610 schedule_preview (s, 0);
1615 list_activated_cb (GtkTreeView *list,
1617 GtkTreeViewColumn *column,
1624 g_return_if_fail (!gdk_pointer_is_grabbed ());
1626 str = gtk_tree_path_to_string (path);
1627 list_elt = strtol (str, NULL, 10);
1631 run_hack (s, list_elt, True);
1635 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1637 state *s = (state *)data;
1638 GtkTreeModel *model;
1644 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1647 path = gtk_tree_model_get_path (model, &iter);
1648 str = gtk_tree_path_to_string (path);
1649 list_elt = strtol (str, NULL, 10);
1651 gtk_tree_path_free (path);
1654 populate_demo_window (s, list_elt);
1655 flush_dialog_changes_and_save (s);
1658 #else /* !HAVE_GTK2 */
1660 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1661 list_select_cb that comes in
1662 *after* we've double-clicked.
1666 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1669 state *s = (state *) data;
1670 if (event->type == GDK_2BUTTON_PRESS)
1672 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1673 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1675 last_doubleclick_time = time ((time_t *) 0);
1678 run_hack (s, list_elt, True);
1686 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1688 state *s = (state *) data;
1689 time_t now = time ((time_t *) 0);
1691 if (now >= last_doubleclick_time + 2)
1693 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1694 populate_demo_window (s, list_elt);
1695 flush_dialog_changes_and_save (s);
1700 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1702 state *s = (state *) data;
1703 populate_demo_window (s, -1);
1704 flush_dialog_changes_and_save (s);
1707 #endif /* !HAVE_GTK2 */
1710 /* Called when the checkboxes that are in the left column of the
1711 scrolling list are clicked. This both populates the right pane
1712 (just as clicking on the label (really, listitem) does) and
1713 also syncs this checkbox with the right pane Enabled checkbox.
1718 GtkCellRendererToggle *toggle,
1720 #else /* !HAVE_GTK2 */
1722 #endif /* !HAVE_GTK2 */
1725 state *s = (state *) data;
1728 GtkScrolledWindow *scroller =
1729 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1730 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1731 GtkTreeModel *model = gtk_tree_view_get_model (list);
1732 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1735 #else /* !HAVE_GTK2 */
1736 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1737 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1739 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1740 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1741 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1742 #endif /* !HAVE_GTK2 */
1749 if (!gtk_tree_model_get_iter (model, &iter, path))
1751 g_warning ("bad path: %s", path_string);
1754 gtk_tree_path_free (path);
1756 gtk_tree_model_get (model, &iter,
1757 COL_ENABLED, &active,
1760 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1761 COL_ENABLED, !active,
1764 list_elt = strtol (path_string, NULL, 10);
1765 #else /* !HAVE_GTK2 */
1766 list_elt = gtk_list_child_position (list, line);
1767 #endif /* !HAVE_GTK2 */
1769 /* remember previous scroll position of the top of the list */
1770 adj = gtk_scrolled_window_get_vadjustment (scroller);
1771 scroll_top = adj->value;
1773 flush_dialog_changes_and_save (s);
1774 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1775 populate_demo_window (s, list_elt);
1777 /* restore the previous scroll position of the top of the list.
1778 this is weak, but I don't really know why it's moving... */
1779 gtk_adjustment_set_value (adj, scroll_top);
1785 GtkFileSelection *widget;
1786 } file_selection_data;
1791 store_image_directory (GtkWidget *button, gpointer user_data)
1793 file_selection_data *fsd = (file_selection_data *) user_data;
1794 state *s = fsd->state;
1795 GtkFileSelection *selector = fsd->widget;
1796 GtkWidget *top = s->toplevel_widget;
1797 saver_preferences *p = &s->prefs;
1798 const char *path = gtk_file_selection_get_filename (selector);
1800 if (p->image_directory && !strcmp(p->image_directory, path))
1801 return; /* no change */
1803 if (!directory_p (path))
1806 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1807 warning_dialog (GTK_WIDGET (top), b, False, 100);
1811 if (p->image_directory) free (p->image_directory);
1812 p->image_directory = normalize_directory (path);
1814 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1815 (p->image_directory ? p->image_directory : ""));
1816 demo_write_init_file (s, p);
1821 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1823 file_selection_data *fsd = (file_selection_data *) user_data;
1824 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1828 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1830 browse_image_dir_cancel (button, user_data);
1831 store_image_directory (button, user_data);
1835 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1837 browse_image_dir_cancel (widget, user_data);
1842 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1844 state *s = global_state_kludge; /* I hate C so much... */
1845 saver_preferences *p = &s->prefs;
1846 static file_selection_data *fsd = 0;
1848 GtkFileSelection *selector = GTK_FILE_SELECTION(
1849 gtk_file_selection_new ("Please select the image directory."));
1852 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1854 fsd->widget = selector;
1857 if (p->image_directory && *p->image_directory)
1858 gtk_file_selection_set_filename (selector, p->image_directory);
1860 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1861 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1863 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1864 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1866 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1867 GTK_SIGNAL_FUNC (browse_image_dir_close),
1870 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1872 gtk_window_set_modal (GTK_WINDOW (selector), True);
1873 gtk_widget_show (GTK_WIDGET (selector));
1878 settings_cb (GtkButton *button, gpointer user_data)
1880 state *s = global_state_kludge; /* I hate C so much... */
1881 int list_elt = selected_list_element (s);
1883 populate_demo_window (s, list_elt); /* reset the widget */
1884 populate_popup_window (s); /* create UI on popup window */
1885 gtk_widget_show (s->popup_widget);
1889 settings_sync_cmd_text (state *s)
1892 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1893 char *cmd_line = get_configurator_command_line (s->cdata);
1894 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1895 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1897 # endif /* HAVE_XML */
1901 settings_adv_cb (GtkButton *button, gpointer user_data)
1903 state *s = global_state_kludge; /* I hate C so much... */
1904 GtkNotebook *notebook =
1905 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1907 settings_sync_cmd_text (s);
1908 gtk_notebook_set_page (notebook, 1);
1912 settings_std_cb (GtkButton *button, gpointer user_data)
1914 state *s = global_state_kludge; /* I hate C so much... */
1915 GtkNotebook *notebook =
1916 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1918 /* Re-create UI to reflect the in-progress command-line settings. */
1919 populate_popup_window (s);
1921 gtk_notebook_set_page (notebook, 0);
1925 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1926 gint page_num, gpointer user_data)
1928 state *s = global_state_kludge; /* I hate C so much... */
1929 GtkWidget *adv = name_to_widget (s, "adv_button");
1930 GtkWidget *std = name_to_widget (s, "std_button");
1934 gtk_widget_show (adv);
1935 gtk_widget_hide (std);
1937 else if (page_num == 1)
1939 gtk_widget_hide (adv);
1940 gtk_widget_show (std);
1949 settings_cancel_cb (GtkButton *button, gpointer user_data)
1951 state *s = global_state_kludge; /* I hate C so much... */
1952 gtk_widget_hide (s->popup_widget);
1956 settings_ok_cb (GtkButton *button, gpointer user_data)
1958 state *s = global_state_kludge; /* I hate C so much... */
1959 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1960 int page = gtk_notebook_get_current_page (notebook);
1963 /* Regenerate the command-line from the widget contents before saving.
1964 But don't do this if we're looking at the command-line page already,
1965 or we will blow away what they typed... */
1966 settings_sync_cmd_text (s);
1968 flush_popup_changes_and_save (s);
1969 gtk_widget_hide (s->popup_widget);
1973 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1975 state *s = (state *) data;
1976 settings_cancel_cb (0, (gpointer) s);
1982 /* Populating the various widgets
1986 /* Returns the number of the last hack run by the server.
1989 server_current_hack (void)
1993 unsigned long nitems, bytesafter;
1995 Display *dpy = GDK_DISPLAY();
1996 int hack_number = -1;
1998 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1999 XA_SCREENSAVER_STATUS,
2000 0, 3, False, XA_INTEGER,
2001 &type, &format, &nitems, &bytesafter,
2002 (unsigned char **) &data)
2004 && type == XA_INTEGER
2007 hack_number = (int) data[2] - 1;
2009 if (data) free (data);
2015 /* Finds the number of the last hack to run, and makes that item be
2016 selected by default.
2019 scroll_to_current_hack (state *s)
2021 saver_preferences *p = &s->prefs;
2024 if (p->mode == ONE_HACK)
2025 hack_number = p->selected_hack;
2027 hack_number = server_current_hack ();
2029 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2031 int list_elt = s->hack_number_to_list_elt[hack_number];
2032 GtkWidget *list = name_to_widget (s, "list");
2033 force_list_select_item (s, list, list_elt, True);
2034 populate_demo_window (s, list_elt);
2040 on_path_p (const char *program)
2044 char *cmd = strdup (program);
2045 char *token = strchr (cmd, ' ');
2049 if (token) *token = 0;
2052 if (strchr (cmd, '/'))
2054 result = (0 == stat (cmd, &st));
2058 path = getenv("PATH");
2059 if (!path || !*path)
2063 path = strdup (path);
2064 token = strtok (path, ":");
2068 char *p2 = (char *) malloc (strlen (token) + L + 3);
2072 result = (0 == stat (p2, &st));
2075 token = strtok (0, ":");
2080 if (path) free (path);
2086 populate_hack_list (state *s)
2089 saver_preferences *p = &s->prefs;
2090 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2091 GtkListStore *model;
2092 GtkTreeSelection *selection;
2093 GtkCellRenderer *ren;
2097 g_object_get (G_OBJECT (list),
2102 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2103 g_object_set (G_OBJECT (list), "model", model, NULL);
2104 g_object_unref (model);
2106 ren = gtk_cell_renderer_toggle_new ();
2107 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2109 "active", COL_ENABLED,
2112 g_signal_connect (ren, "toggled",
2113 G_CALLBACK (list_checkbox_cb),
2116 ren = gtk_cell_renderer_text_new ();
2117 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2118 _("Screen Saver"), ren,
2122 g_signal_connect_after (list, "row_activated",
2123 G_CALLBACK (list_activated_cb),
2126 selection = gtk_tree_view_get_selection (list);
2127 g_signal_connect (selection, "changed",
2128 G_CALLBACK (list_select_changed_cb),
2133 for (i = 0; i < s->list_count; i++)
2135 int hack_number = s->list_elt_to_hack_number[i];
2136 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2138 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2140 if (!hack) continue;
2142 /* If we're to suppress uninstalled hacks, check $PATH now. */
2143 if (p->ignore_uninstalled_p && !available_p)
2146 pretty_name = (hack->name
2147 ? strdup (hack->name)
2148 : make_hack_name (hack->command));
2152 /* Make the text foreground be the color of insensitive widgets
2153 (but don't actually make it be insensitive, since we still
2154 want to be able to click on it.)
2156 GtkStyle *style = GTK_WIDGET (list)->style;
2157 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2158 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2159 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2161 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2162 /* " background=\"#%02X%02X%02X\"" */
2164 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2165 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2171 gtk_list_store_append (model, &iter);
2172 gtk_list_store_set (model, &iter,
2173 COL_ENABLED, hack->enabled_p,
2174 COL_NAME, pretty_name,
2179 #else /* !HAVE_GTK2 */
2181 saver_preferences *p = &s->prefs;
2182 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2184 for (i = 0; i < s->list_count; i++)
2186 int hack_number = s->list_elt_to_hack_number[i];
2187 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2189 /* A GtkList must contain only GtkListItems, but those can contain
2190 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2191 and a Label. We handle single and double click events on the
2192 line itself, for clicking on the text, but the interior checkbox
2193 also handles its own events.
2196 GtkWidget *line_hbox;
2197 GtkWidget *line_check;
2198 GtkWidget *line_label;
2200 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2202 if (!hack) continue;
2204 /* If we're to suppress uninstalled hacks, check $PATH now. */
2205 if (p->ignore_uninstalled_p && !available_p)
2208 pretty_name = (hack->name
2209 ? strdup (hack->name)
2210 : make_hack_name (hack->command));
2212 line = gtk_list_item_new ();
2213 line_hbox = gtk_hbox_new (FALSE, 0);
2214 line_check = gtk_check_button_new ();
2215 line_label = gtk_label_new (pretty_name);
2217 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2218 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2219 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2221 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2223 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2225 gtk_widget_show (line_check);
2226 gtk_widget_show (line_label);
2227 gtk_widget_show (line_hbox);
2228 gtk_widget_show (line);
2232 gtk_container_add (GTK_CONTAINER (list), line);
2233 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2234 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2237 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2238 GTK_SIGNAL_FUNC (list_checkbox_cb),
2241 gtk_widget_show (line);
2245 /* Make the widget be colored like insensitive widgets
2246 (but don't actually make it be insensitive, since we
2247 still want to be able to click on it.)
2249 GtkRcStyle *rc_style;
2252 gtk_widget_realize (GTK_WIDGET (line_label));
2254 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2255 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2257 rc_style = gtk_rc_style_new ();
2258 rc_style->fg[GTK_STATE_NORMAL] = fg;
2259 rc_style->bg[GTK_STATE_NORMAL] = bg;
2260 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2262 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2263 gtk_rc_style_unref (rc_style);
2267 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2268 GTK_SIGNAL_FUNC (list_select_cb),
2270 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2271 GTK_SIGNAL_FUNC (list_unselect_cb),
2273 #endif /* !HAVE_GTK2 */
2277 update_list_sensitivity (state *s)
2279 saver_preferences *p = &s->prefs;
2280 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2281 Bool checkable = (p->mode == RANDOM_HACKS);
2282 Bool blankable = (p->mode != DONT_BLANK);
2285 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2286 GtkWidget *use = name_to_widget (s, "use_col_frame");
2287 #endif /* HAVE_GTK2 */
2288 GtkWidget *scroller = name_to_widget (s, "scroller");
2289 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2290 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2293 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2294 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2295 #else /* !HAVE_GTK2 */
2296 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2297 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2299 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2300 #endif /* !HAVE_GTK2 */
2301 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2302 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2304 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2307 gtk_tree_view_column_set_visible (use, checkable);
2308 #else /* !HAVE_GTK2 */
2310 gtk_widget_show (use); /* the "Use" column header */
2312 gtk_widget_hide (use);
2316 GtkBin *line = GTK_BIN (kids->data);
2317 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2318 GtkWidget *line_check =
2319 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2322 gtk_widget_show (line_check);
2324 gtk_widget_hide (line_check);
2328 #endif /* !HAVE_GTK2 */
2333 populate_prefs_page (state *s)
2335 saver_preferences *p = &s->prefs;
2337 /* The file supports timeouts of less than a minute, but the GUI does
2338 not, so throttle the values to be at least one minute (since "0" is
2339 a bad rounding choice...)
2341 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2344 THROTTLE (passwd_timeout);
2347 # define FMT_MINUTES(NAME,N) \
2348 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2350 # define FMT_SECONDS(NAME,N) \
2351 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2353 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2354 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2355 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2356 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2357 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2358 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2359 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2364 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2365 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2368 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2369 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2370 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2371 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2372 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2373 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2374 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2375 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2376 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2377 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2378 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2380 # undef TOGGLE_ACTIVE
2382 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2383 (p->image_directory ? p->image_directory : ""));
2384 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2386 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2389 /* Map the `saver_mode' enum to mode menu to values. */
2391 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2394 for (i = 0; i < countof(mode_menu_order); i++)
2395 if (mode_menu_order[i] == p->mode)
2397 gtk_option_menu_set_history (opt, i);
2398 update_list_sensitivity (s);
2402 Bool found_any_writable_cells = False;
2403 Bool dpms_supported = False;
2405 Display *dpy = GDK_DISPLAY();
2406 int nscreens = ScreenCount(dpy);
2408 for (i = 0; i < nscreens; i++)
2410 Screen *s = ScreenOfDisplay (dpy, i);
2411 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2413 found_any_writable_cells = True;
2418 #ifdef HAVE_XF86VMODE_GAMMA
2419 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2422 #ifdef HAVE_DPMS_EXTENSION
2424 int op = 0, event = 0, error = 0;
2425 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2426 dpms_supported = True;
2428 #endif /* HAVE_DPMS_EXTENSION */
2431 # define SENSITIZE(NAME,SENSITIVEP) \
2432 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2434 /* Blanking and Locking
2436 SENSITIZE ("lock_spinbutton", p->lock_p);
2437 SENSITIZE ("lock_mlabel", p->lock_p);
2441 SENSITIZE ("dpms_frame", dpms_supported);
2442 SENSITIZE ("dpms_button", dpms_supported);
2443 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2444 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2445 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2446 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2447 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2448 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2449 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2450 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2451 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2455 SENSITIZE ("cmap_frame", found_any_writable_cells);
2456 SENSITIZE ("install_button", found_any_writable_cells);
2457 SENSITIZE ("fade_button", found_any_writable_cells);
2458 SENSITIZE ("unfade_button", found_any_writable_cells);
2460 SENSITIZE ("fade_label", (found_any_writable_cells &&
2461 (p->fade_p || p->unfade_p)));
2462 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2463 (p->fade_p || p->unfade_p)));
2471 populate_popup_window (state *s)
2473 saver_preferences *p = &s->prefs;
2474 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2475 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2476 int list_elt = selected_list_element (s);
2477 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2478 ? s->list_elt_to_hack_number[list_elt]
2480 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2481 char *doc_string = 0;
2483 /* #### not in Gtk 1.2
2484 gtk_label_set_selectable (doc);
2490 free_conf_data (s->cdata);
2496 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2497 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2498 s->cdata = load_configurator (cmd_line, s->debug_p);
2499 if (s->cdata && s->cdata->widget)
2500 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget, TRUE, TRUE, 0);
2503 doc_string = (s->cdata
2504 ? s->cdata->description
2506 # else /* !HAVE_XML */
2507 doc_string = _("Descriptions not available: no XML support compiled in.");
2508 # endif /* !HAVE_XML */
2510 gtk_label_set_text (doc, (doc_string
2512 : _("No description available.")));
2517 sensitize_demo_widgets (state *s, Bool sensitive_p)
2519 const char *names1[] = { "demo", "settings" };
2520 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2521 "visual", "visual_combo" };
2523 for (i = 0; i < countof(names1); i++)
2525 GtkWidget *w = name_to_widget (s, names1[i]);
2526 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2528 for (i = 0; i < countof(names2); i++)
2530 GtkWidget *w = name_to_widget (s, names2[i]);
2531 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2536 /* Even though we've given these text fields a maximum number of characters,
2537 their default size is still about 30 characters wide -- so measure out
2538 a string in their font, and resize them to just fit that.
2541 fix_text_entry_sizes (state *s)
2545 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2546 const char * const spinbuttons[] = {
2547 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2548 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2549 "dpms_off_spinbutton",
2550 "-fade_spinbutton" };
2554 for (i = 0; i < countof(spinbuttons); i++)
2556 const char *n = spinbuttons[i];
2558 while (*n == '-') n++, cols--;
2559 w = GTK_WIDGET (name_to_widget (s, n));
2560 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2561 gtk_widget_set_usize (w, width, -2);
2564 /* Now fix the width of the combo box.
2566 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2567 w = GTK_COMBO (w)->entry;
2568 width = gdk_string_width (w->style->font, "PseudoColor___");
2569 gtk_widget_set_usize (w, width, -2);
2571 /* Now fix the width of the file entry text.
2573 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2574 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2575 gtk_widget_set_usize (w, width, -2);
2577 /* Now fix the width of the command line text.
2579 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2580 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2581 gtk_widget_set_usize (w, width, -2);
2585 /* Now fix the height of the list widget:
2586 make it default to being around 10 text-lines high instead of 4.
2588 w = GTK_WIDGET (name_to_widget (s, "list"));
2592 int leading = 3; /* approximate is ok... */
2596 PangoFontMetrics *pain =
2597 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2598 w->style->font_desc,
2599 gtk_get_default_language ());
2600 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2601 pango_font_metrics_get_descent (pain));
2602 #else /* !HAVE_GTK2 */
2603 height = w->style->font->ascent + w->style->font->descent;
2604 #endif /* !HAVE_GTK2 */
2608 height += border * 2;
2609 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2610 gtk_widget_set_usize (w, -2, height);
2617 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2620 static char *up_arrow_xpm[] = {
2643 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2644 the end of the array (Gtk 1.2.5.) */
2645 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2646 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2649 static char *down_arrow_xpm[] = {
2672 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2673 the end of the array (Gtk 1.2.5.) */
2674 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2675 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2679 pixmapify_button (state *s, int down_p)
2683 GtkWidget *pixmapwid;
2687 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2688 style = gtk_widget_get_style (w);
2690 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2691 &style->bg[GTK_STATE_NORMAL],
2693 ? (gchar **) down_arrow_xpm
2694 : (gchar **) up_arrow_xpm));
2695 pixmapwid = gtk_pixmap_new (pixmap, mask);
2696 gtk_widget_show (pixmapwid);
2697 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2698 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2702 map_next_button_cb (GtkWidget *w, gpointer user_data)
2704 state *s = (state *) user_data;
2705 pixmapify_button (s, 1);
2709 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2711 state *s = (state *) user_data;
2712 pixmapify_button (s, 0);
2714 #endif /* !HAVE_GTK2 */
2717 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2721 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2722 GtkAllocation *allocation,
2726 GtkWidgetAuxInfo *aux_info;
2728 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2730 aux_info->width = allocation->width;
2731 aux_info->height = -2;
2735 gtk_widget_size_request (label, &req);
2739 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2742 eschew_gtk_lossage (GtkLabel *label)
2744 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2745 aux_info->width = GTK_WIDGET (label)->allocation.width;
2746 aux_info->height = -2;
2750 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2752 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2753 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2756 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2758 gtk_widget_queue_resize (GTK_WIDGET (label));
2763 populate_demo_window (state *s, int list_elt)
2765 saver_preferences *p = &s->prefs;
2768 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2769 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2770 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2771 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2772 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2774 if (p->mode == BLANK_ONLY)
2777 pretty_name = strdup (_("Blank Screen"));
2778 schedule_preview (s, 0);
2780 else if (p->mode == DONT_BLANK)
2783 pretty_name = strdup (_("Screen Saver Disabled"));
2784 schedule_preview (s, 0);
2788 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2789 ? s->list_elt_to_hack_number[list_elt]
2791 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2795 ? strdup (hack->name)
2796 : make_hack_name (hack->command))
2800 schedule_preview (s, hack->command);
2802 schedule_preview (s, 0);
2806 pretty_name = strdup (_("Preview"));
2808 gtk_frame_set_label (frame1, pretty_name);
2809 gtk_frame_set_label (frame2, pretty_name);
2811 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2812 gtk_entry_set_position (cmd, 0);
2816 sprintf (title, "%s: %.100s Settings",
2817 progclass, (pretty_name ? pretty_name : "???"));
2818 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2821 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2823 ? (hack->visual && *hack->visual
2828 sensitize_demo_widgets (s, (hack ? True : False));
2830 if (pretty_name) free (pretty_name);
2832 ensure_selected_item_visible (list);
2834 s->_selected_list_element = list_elt;
2839 widget_deleter (GtkWidget *widget, gpointer data)
2841 /* #### Well, I want to destroy these widgets, but if I do that, they get
2842 referenced again, and eventually I get a SEGV. So instead of
2843 destroying them, I'll just hide them, and leak a bunch of memory
2844 every time the disk file changes. Go go go Gtk!
2846 #### Ok, that's a lie, I get a crash even if I just hide the widget
2847 and don't ever delete it. Fuck!
2850 gtk_widget_destroy (widget);
2852 gtk_widget_hide (widget);
2857 static char **sort_hack_cmp_names_kludge;
2859 sort_hack_cmp (const void *a, const void *b)
2865 int aa = *(int *) a;
2866 int bb = *(int *) b;
2867 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
2868 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
2869 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
2875 initialize_sort_map (state *s)
2877 saver_preferences *p = &s->prefs;
2880 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2881 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2882 if (s->hacks_available_p) free (s->hacks_available_p);
2884 s->list_elt_to_hack_number = (int *)
2885 calloc (sizeof(int), p->screenhacks_count + 1);
2886 s->hack_number_to_list_elt = (int *)
2887 calloc (sizeof(int), p->screenhacks_count + 1);
2888 s->hacks_available_p = (Bool *)
2889 calloc (sizeof(Bool), p->screenhacks_count + 1);
2891 /* Check which hacks actually exist on $PATH
2893 for (i = 0; i < p->screenhacks_count; i++)
2895 screenhack *hack = p->screenhacks[i];
2896 s->hacks_available_p[i] = on_path_p (hack->command);
2899 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
2903 for (i = 0; i < p->screenhacks_count; i++)
2905 if (!p->ignore_uninstalled_p ||
2906 s->hacks_available_p[i])
2907 s->list_elt_to_hack_number[j++] = i;
2911 for (; j < p->screenhacks_count; j++)
2912 s->list_elt_to_hack_number[j] = -1;
2915 /* Generate list of sortable names (once)
2917 sort_hack_cmp_names_kludge = (char **)
2918 calloc (sizeof(char *), p->screenhacks_count);
2919 for (i = 0; i < p->screenhacks_count; i++)
2921 screenhack *hack = p->screenhacks[i];
2922 char *name = (hack->name && *hack->name
2923 ? strdup (hack->name)
2924 : make_hack_name (hack->command));
2926 for (str = name; *str; str++)
2927 *str = tolower(*str);
2928 sort_hack_cmp_names_kludge[i] = name;
2931 /* Sort list->hack map alphabetically
2933 qsort (s->list_elt_to_hack_number,
2934 p->screenhacks_count,
2935 sizeof(*s->list_elt_to_hack_number),
2940 for (i = 0; i < p->screenhacks_count; i++)
2941 free (sort_hack_cmp_names_kludge[i]);
2942 free (sort_hack_cmp_names_kludge);
2943 sort_hack_cmp_names_kludge = 0;
2945 /* Build inverse table */
2946 for (i = 0; i < p->screenhacks_count; i++)
2947 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2952 maybe_reload_init_file (state *s)
2954 saver_preferences *p = &s->prefs;
2957 static Bool reentrant_lock = False;
2958 if (reentrant_lock) return 0;
2959 reentrant_lock = True;
2961 if (init_file_changed_p (p))
2963 const char *f = init_file_name();
2968 if (!f || !*f) return 0;
2969 b = (char *) malloc (strlen(f) + 1024);
2972 "file \"%s\" has changed, reloading.\n"),
2974 warning_dialog (s->toplevel_widget, b, False, 100);
2978 initialize_sort_map (s);
2980 list_elt = selected_list_element (s);
2981 list = name_to_widget (s, "list");
2982 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
2983 populate_hack_list (s);
2984 force_list_select_item (s, list, list_elt, True);
2985 populate_prefs_page (s);
2986 populate_demo_window (s, list_elt);
2987 ensure_selected_item_visible (list);
2992 reentrant_lock = False;
2998 /* Making the preview window have the right X visual (so that GL works.)
3001 static Visual *get_best_gl_visual (state *);
3004 x_visual_to_gdk_visual (Visual *xv)
3006 GList *gvs = gdk_list_visuals();
3007 if (!xv) return gdk_visual_get_system();
3008 for (; gvs; gvs = gvs->next)
3010 GdkVisual *gv = (GdkVisual *) gvs->data;
3011 if (xv == GDK_VISUAL_XVISUAL (gv))
3014 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3015 blurb(), (unsigned long) xv->visualid);
3020 clear_preview_window (state *s)
3025 if (!s->toplevel_widget) return; /* very early */
3026 p = name_to_widget (s, "preview");
3029 if (!window) return;
3031 /* Flush the widget background down into the window, in case a subproc
3033 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3034 gdk_window_clear (window);
3037 int list_elt = selected_list_element (s);
3038 int hack_number = (list_elt >= 0
3039 ? s->list_elt_to_hack_number[list_elt]
3041 Bool available_p = (hack_number >= 0
3042 ? s->hacks_available_p [hack_number]
3045 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3046 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3047 (s->running_preview_error_p
3048 ? (available_p ? 1 : 2)
3050 #else /* !HAVE_GTK2 */
3051 if (s->running_preview_error_p)
3053 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3054 const char * const lines2[] = { N_("Not"), N_("Installed") };
3055 int nlines = countof(lines1);
3056 int lh = p->style->font->ascent + p->style->font->descent;
3060 const char * const *lines = (available_p ? lines1 : lines2);
3062 gdk_window_get_size (window, &w, &h);
3063 y = (h - (lh * nlines)) / 2;
3064 y += p->style->font->ascent;
3065 for (i = 0; i < nlines; i++)
3067 int sw = gdk_string_width (p->style->font, _(lines[i]));
3068 int x = (w - sw) / 2;
3069 gdk_draw_string (window, p->style->font,
3070 p->style->fg_gc[GTK_STATE_NORMAL],
3075 #endif /* !HAVE_GTK2 */
3083 fix_preview_visual (state *s)
3085 GtkWidget *widget = name_to_widget (s, "preview");
3086 Visual *xvisual = get_best_gl_visual (s);
3087 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3088 GdkVisual *dvisual = gdk_visual_get_system();
3089 GdkColormap *cmap = (visual == dvisual
3090 ? gdk_colormap_get_system ()
3091 : gdk_colormap_new (visual, False));
3094 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3095 (visual == dvisual ? "default" : "non-default"),
3096 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3098 if (!GTK_WIDGET_REALIZED (widget) ||
3099 gtk_widget_get_visual (widget) != visual)
3101 gtk_widget_unrealize (widget);
3102 gtk_widget_set_visual (widget, visual);
3103 gtk_widget_set_colormap (widget, cmap);
3104 gtk_widget_realize (widget);
3107 /* Set the Widget colors to be white-on-black. */
3109 GdkWindow *window = widget->window;
3110 GtkStyle *style = gtk_style_copy (widget->style);
3111 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3112 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3113 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3114 GdkGC *fgc = gdk_gc_new(window);
3115 GdkGC *bgc = gdk_gc_new(window);
3116 if (!gdk_color_white (cmap, fg)) abort();
3117 if (!gdk_color_black (cmap, bg)) abort();
3118 gdk_gc_set_foreground (fgc, fg);
3119 gdk_gc_set_background (fgc, bg);
3120 gdk_gc_set_foreground (bgc, bg);
3121 gdk_gc_set_background (bgc, fg);
3122 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3123 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3124 gtk_widget_set_style (widget, style);
3126 /* For debugging purposes, put a title on the window (so that
3127 it can be easily found in the output of "xwininfo -tree".)
3129 gdk_window_set_title (window, "Preview");
3132 gtk_widget_show (widget);
3140 subproc_pretty_name (state *s)
3142 if (s->running_preview_cmd)
3144 char *ps = strdup (s->running_preview_cmd);
3145 char *ss = strchr (ps, ' ');
3147 ss = strrchr (ps, '/');
3158 return strdup ("???");
3163 reap_zombies (state *s)
3165 int wait_status = 0;
3167 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3171 if (pid == s->running_preview_pid)
3173 char *ss = subproc_pretty_name (s);
3174 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3175 (unsigned long) pid, ss);
3179 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3180 (unsigned long) pid);
3186 /* Mostly lifted from driver/subprocs.c */
3188 get_best_gl_visual (state *s)
3190 Display *dpy = GDK_DISPLAY();
3199 av[ac++] = "xscreensaver-gl-helper";
3204 perror ("error creating pipe:");
3211 switch ((int) (forked = fork ()))
3215 sprintf (buf, "%s: couldn't fork", blurb());
3223 close (in); /* don't need this one */
3224 close (ConnectionNumber (dpy)); /* close display fd */
3226 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3228 perror ("could not dup() a new stdout:");
3232 execvp (av[0], av); /* shouldn't return. */
3234 if (errno != ENOENT)
3236 /* Ignore "no such file or directory" errors, unless verbose.
3237 Issue all other exec errors, though. */
3238 sprintf (buf, "%s: running %s", blurb(), av[0]);
3241 exit (1); /* exits fork */
3247 int wait_status = 0;
3249 FILE *f = fdopen (in, "r");
3253 close (out); /* don't need this one */
3256 fgets (buf, sizeof(buf)-1, f);
3259 /* Wait for the child to die. */
3260 waitpid (-1, &wait_status, 0);
3262 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3268 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3274 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3276 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3277 blurb(), av[0], result);
3289 kill_preview_subproc (state *s)
3291 s->running_preview_error_p = False;
3294 clear_preview_window (s);
3296 if (s->subproc_check_timer_id)
3298 gtk_timeout_remove (s->subproc_check_timer_id);
3299 s->subproc_check_timer_id = 0;
3300 s->subproc_check_countdown = 0;
3303 if (s->running_preview_pid)
3305 int status = kill (s->running_preview_pid, SIGTERM);
3306 char *ss = subproc_pretty_name (s);
3313 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3314 blurb(), (unsigned long) s->running_preview_pid, ss);
3319 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3320 blurb(), (unsigned long) s->running_preview_pid, ss);
3324 else if (s->debug_p)
3325 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3326 (unsigned long) s->running_preview_pid, ss);
3329 s->running_preview_pid = 0;
3330 if (s->running_preview_cmd) free (s->running_preview_cmd);
3331 s->running_preview_cmd = 0;
3338 /* Immediately and unconditionally launches the given process,
3339 after appending the -window-id option; sets running_preview_pid.
3342 launch_preview_subproc (state *s)
3344 saver_preferences *p = &s->prefs;
3348 const char *cmd = s->desired_preview_cmd;
3350 GtkWidget *pr = name_to_widget (s, "preview");
3351 GdkWindow *window = pr->window;
3353 s->running_preview_error_p = False;
3355 if (s->preview_suppressed_p)
3357 kill_preview_subproc (s);
3361 new_cmd = malloc (strlen (cmd) + 40);
3363 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3366 /* No window id? No command to run. */
3372 strcpy (new_cmd, cmd);
3373 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3377 kill_preview_subproc (s);
3380 s->running_preview_error_p = True;
3381 clear_preview_window (s);
3385 switch ((int) (forked = fork ()))
3390 sprintf (buf, "%s: couldn't fork", blurb());
3392 s->running_preview_error_p = True;
3398 close (ConnectionNumber (GDK_DISPLAY()));
3400 usleep (250000); /* pause for 1/4th second before launching, to give
3401 the previous program time to die and flush its X
3402 buffer, so we don't get leftover turds on the
3405 exec_command (p->shell, new_cmd, p->nice_inferior);
3406 /* Don't bother printing an error message when we are unable to
3407 exec subprocesses; we handle that by polling the pid later. */
3408 exit (1); /* exits child fork */
3413 if (s->running_preview_cmd) free (s->running_preview_cmd);
3414 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3415 s->running_preview_pid = forked;
3419 char *ss = subproc_pretty_name (s);
3420 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3421 (unsigned long) forked, ss);
3428 schedule_preview_check (s);
3431 if (new_cmd) free (new_cmd);
3436 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3439 hack_environment (state *s)
3441 static const char *def_path =
3442 # ifdef DEFAULT_PATH_PREFIX
3443 DEFAULT_PATH_PREFIX;
3448 Display *dpy = GDK_DISPLAY();
3449 const char *odpy = DisplayString (dpy);
3450 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3451 strcpy (ndpy, "DISPLAY=");
3452 strcat (ndpy, odpy);
3457 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3459 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3460 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3461 So we must leak it (and/or the previous setting). Yay.
3464 if (def_path && *def_path)
3466 const char *opath = getenv("PATH");
3467 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3468 strcpy (npath, "PATH=");
3469 strcat (npath, def_path);
3470 strcat (npath, ":");
3471 strcat (npath, opath);
3475 /* do not free(npath) -- see above */
3478 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3483 /* Called from a timer:
3484 Launches the currently-chosen subprocess, if it's not already running.
3485 If there's a different process running, kills it.
3488 update_subproc_timer (gpointer data)
3490 state *s = (state *) data;
3491 if (! s->desired_preview_cmd)
3492 kill_preview_subproc (s);
3493 else if (!s->running_preview_cmd ||
3494 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3495 launch_preview_subproc (s);
3497 s->subproc_timer_id = 0;
3498 return FALSE; /* do not re-execute timer */
3502 /* Call this when you think you might want a preview process running.
3503 It will set a timer that will actually launch that program a second
3504 from now, if you haven't changed your mind (to avoid double-click
3505 spazzing, etc.) `cmd' may be null meaning "no process".
3508 schedule_preview (state *s, const char *cmd)
3510 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3515 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3517 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3520 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3521 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3523 if (s->subproc_timer_id)
3524 gtk_timeout_remove (s->subproc_timer_id);
3525 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3529 /* Called from a timer:
3530 Checks to see if the subproc that should be running, actually is.
3533 check_subproc_timer (gpointer data)
3535 state *s = (state *) data;
3536 Bool again_p = True;
3538 if (s->running_preview_error_p || /* already dead */
3539 s->running_preview_pid <= 0)
3547 status = kill (s->running_preview_pid, 0);
3548 if (status < 0 && errno == ESRCH)
3549 s->running_preview_error_p = True;
3553 char *ss = subproc_pretty_name (s);
3554 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3555 (unsigned long) s->running_preview_pid, ss,
3556 (s->running_preview_error_p ? "dead" : "alive"));
3560 if (s->running_preview_error_p)
3562 clear_preview_window (s);
3567 /* Otherwise, it's currently alive. We might be checking again, or we
3568 might be satisfied. */
3570 if (--s->subproc_check_countdown <= 0)
3574 return TRUE; /* re-execute timer */
3577 s->subproc_check_timer_id = 0;
3578 s->subproc_check_countdown = 0;
3579 return FALSE; /* do not re-execute timer */
3584 /* Call this just after launching a subprocess.
3585 This sets a timer that will, five times a second for two seconds,
3586 check whether the program is still running. The assumption here
3587 is that if the process didn't stay up for more than a couple of
3588 seconds, then either the program doesn't exist, or it doesn't
3589 take a -window-id argument.
3592 schedule_preview_check (state *s)
3598 fprintf (stderr, "%s: scheduling check\n", blurb());
3600 if (s->subproc_check_timer_id)
3601 gtk_timeout_remove (s->subproc_check_timer_id);
3602 s->subproc_check_timer_id =
3603 gtk_timeout_add (1000 / ticks,
3604 check_subproc_timer, (gpointer) s);
3605 s->subproc_check_countdown = ticks * seconds;
3610 screen_blanked_p (void)
3614 unsigned long nitems, bytesafter;
3616 Display *dpy = GDK_DISPLAY();
3617 Bool blanked_p = False;
3619 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3620 XA_SCREENSAVER_STATUS,
3621 0, 3, False, XA_INTEGER,
3622 &type, &format, &nitems, &bytesafter,
3623 (unsigned char **) &data)
3625 && type == XA_INTEGER
3628 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3630 if (data) free (data);
3635 /* Wake up every now and then and see if the screen is blanked.
3636 If it is, kill off the small-window demo -- no point in wasting
3637 cycles by running two screensavers at once...
3640 check_blanked_timer (gpointer data)
3642 state *s = (state *) data;
3643 Bool blanked_p = screen_blanked_p ();
3644 if (blanked_p && s->running_preview_pid)
3647 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3648 kill_preview_subproc (s);
3651 return True; /* re-execute timer */
3655 /* Setting window manager icon
3659 init_icon (GdkWindow *window)
3661 GdkBitmap *mask = 0;
3664 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3665 (gchar **) logo_50_xpm);
3667 gdk_window_set_icon (window, 0, pixmap, mask);
3671 /* The main demo-mode command loop.
3676 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3677 XrmRepresentation *type, XrmValue *value, XPointer closure)
3680 for (i = 0; quarks[i]; i++)
3682 if (bindings[i] == XrmBindTightly)
3683 fprintf (stderr, (i == 0 ? "" : "."));
3684 else if (bindings[i] == XrmBindLoosely)
3685 fprintf (stderr, "*");
3687 fprintf (stderr, " ??? ");
3688 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3691 fprintf (stderr, ": %s\n", (char *) value->addr);
3699 the_network_is_not_the_computer (state *s)
3701 Display *dpy = GDK_DISPLAY();
3702 char *rversion = 0, *ruser = 0, *rhost = 0;
3703 char *luser, *lhost;
3705 struct passwd *p = getpwuid (getuid ());
3706 const char *d = DisplayString (dpy);
3708 # if defined(HAVE_UNAME)
3710 if (uname (&uts) < 0)
3711 lhost = "<UNKNOWN>";
3713 lhost = uts.nodename;
3715 strcpy (lhost, getenv("SYS$NODE"));
3716 # else /* !HAVE_UNAME && !VMS */
3717 strcat (lhost, "<UNKNOWN>");
3718 # endif /* !HAVE_UNAME && !VMS */
3720 if (p && p->pw_name)
3725 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3727 /* Make a buffer that's big enough for a number of copies of all the
3728 strings, plus some. */
3729 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3730 (ruser ? strlen(ruser) : 0) +
3731 (rhost ? strlen(rhost) : 0) +
3738 if (!rversion || !*rversion)
3742 "The XScreenSaver daemon doesn't seem to be running\n"
3743 "on display \"%s\". Launch it now?"),
3746 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3748 /* Warn that the two processes are running as different users.
3752 "%s is running as user \"%s\" on host \"%s\".\n"
3753 "But the xscreensaver managing display \"%s\"\n"
3754 "is running as user \"%s\" on host \"%s\".\n"
3756 "Since they are different users, they won't be reading/writing\n"
3757 "the same ~/.xscreensaver file, so %s isn't\n"
3758 "going to work right.\n"
3760 "You should either re-run %s as \"%s\", or re-run\n"
3761 "xscreensaver as \"%s\".\n"
3763 "Restart the xscreensaver daemon now?\n"),
3764 progname, luser, lhost,
3766 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3768 progname, (ruser ? ruser : "???"),
3771 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3773 /* Warn that the two processes are running on different hosts.
3777 "%s is running as user \"%s\" on host \"%s\".\n"
3778 "But the xscreensaver managing display \"%s\"\n"
3779 "is running as user \"%s\" on host \"%s\".\n"
3781 "If those two machines don't share a file system (that is,\n"
3782 "if they don't see the same ~%s/.xscreensaver file) then\n"
3783 "%s won't work right.\n"
3785 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3786 progname, luser, lhost,
3788 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3793 else if (!!strcmp (rversion, s->short_version))
3795 /* Warn that the version numbers don't match.
3799 "This is %s version %s.\n"
3800 "But the xscreensaver managing display \"%s\"\n"
3801 "is version %s. This could cause problems.\n"
3803 "Restart the xscreensaver daemon now?\n"),
3804 progname, s->short_version,
3811 warning_dialog (s->toplevel_widget, msg, True, 1);
3813 if (rversion) free (rversion);
3814 if (ruser) free (ruser);
3815 if (rhost) free (rhost);
3820 /* We use this error handler so that X errors are preceeded by the name
3821 of the program that generated them.
3824 demo_ehandler (Display *dpy, XErrorEvent *error)
3826 state *s = global_state_kludge; /* I hate C so much... */
3827 fprintf (stderr, "\nX error in %s:\n", blurb());
3828 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3829 kill_preview_subproc (s);
3835 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3836 of the program that generated them; and also that we can ignore one
3837 particular bogus error message that Gdk madly spews.
3840 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3841 const gchar *message, gpointer user_data)
3843 /* Ignore the message "Got event for unknown window: 0x...".
3844 Apparently some events are coming in for the xscreensaver window
3845 (presumably reply events related to the ClientMessage) and Gdk
3846 feels the need to complain about them. So, just suppress any
3847 messages that look like that one.
3849 if (strstr (message, "unknown window"))
3852 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3853 (log_domain ? log_domain : progclass),
3854 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3855 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3856 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3857 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3858 log_level == G_LOG_LEVEL_INFO ? "info" :
3859 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3861 ((!*message || message[strlen(message)-1] != '\n')
3866 static char *defaults[] = {
3867 #include "XScreenSaver_ad.h"
3872 #ifdef HAVE_CRAPPLET
3873 static struct poptOption crapplet_options[] = {
3874 {NULL, '\0', 0, NULL, 0}
3876 #endif /* HAVE_CRAPPLET */
3879 const char *usage = "[--display dpy] [--prefs]"
3880 # ifdef HAVE_CRAPPLET
3883 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
3886 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3888 state *s = (state *) user_data;
3889 Boolean oi = s->initializing_p;
3890 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3891 s->initializing_p = True;
3892 eschew_gtk_lossage (label);
3893 s->initializing_p = oi;
3899 print_widget_tree (GtkWidget *w, int depth)
3902 for (i = 0; i < depth; i++)
3903 fprintf (stderr, " ");
3904 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3906 if (GTK_IS_LIST (w))
3908 for (i = 0; i < depth+1; i++)
3909 fprintf (stderr, " ");
3910 fprintf (stderr, "...list kids...\n");
3912 else if (GTK_IS_CONTAINER (w))
3914 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3917 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3925 delayed_scroll_kludge (gpointer data)
3927 state *s = (state *) data;
3928 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
3929 ensure_selected_item_visible (w);
3931 /* Oh, this is just fucking lovely, too. */
3932 w = GTK_WIDGET (name_to_widget (s, "preview"));
3933 gtk_widget_hide (w);
3934 gtk_widget_show (w);
3936 return FALSE; /* do not re-execute timer */
3942 create_xscreensaver_demo (void)
3946 nb = name_to_widget (global_state_kludge, "preview_notebook");
3947 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
3949 return name_to_widget (global_state_kludge, "xscreensaver_demo");
3953 create_xscreensaver_settings_dialog (void)
3957 box = name_to_widget (global_state_kludge, "dialog_action_area");
3959 w = name_to_widget (global_state_kludge, "adv_button");
3960 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
3962 w = name_to_widget (global_state_kludge, "std_button");
3963 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
3965 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
3968 #endif /* HAVE_GTK2 */
3971 main (int argc, char **argv)
3975 saver_preferences *p;
3979 Widget toplevel_shell;
3980 char *real_progname = argv[0];
3982 Bool crapplet_p = False;
3986 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
3987 textdomain (GETTEXT_PACKAGE);
3990 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3991 # else /* !HAVE_GTK2 */
3992 if (!setlocale (LC_ALL, ""))
3993 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
3994 # endif /* !HAVE_GTK2 */
3996 #endif /* ENABLE_NLS */
3998 str = strrchr (real_progname, '/');
3999 if (str) real_progname = str+1;
4002 memset (s, 0, sizeof(*s));
4003 s->initializing_p = True;
4006 global_state_kludge = s; /* I hate C so much... */
4008 progname = real_progname;
4010 s->short_version = (char *) malloc (5);
4011 memcpy (s->short_version, screensaver_id + 17, 4);
4012 s->short_version [4] = 0;
4015 /* Register our error message logger for every ``log domain'' known.
4016 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4017 for all of the domains that seem to be in use.
4020 const char * const domains[] = { 0,
4021 "Gtk", "Gdk", "GLib", "GModule",
4022 "GThread", "Gnome", "GnomeUI" };
4023 for (i = 0; i < countof(domains); i++)
4024 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4027 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4030 const char *dir = DEFAULT_ICONDIR;
4031 if (*dir) add_pixmap_directory (dir);
4033 # endif /* !HAVE_GTK2 */
4034 #endif /* DEFAULT_ICONDIR */
4036 /* This is gross, but Gtk understands --display and not -display...
4038 for (i = 1; i < argc; i++)
4039 if (argv[i][0] && argv[i][1] &&
4040 !strncmp(argv[i], "-display", strlen(argv[i])))
4041 argv[i] = "--display";
4044 /* We need to parse this arg really early... Sigh. */
4045 for (i = 1; i < argc; i++)
4048 (!strcmp(argv[i], "--crapplet") ||
4049 !strcmp(argv[i], "--capplet")))
4051 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4054 for (j = i; j < argc; j++) /* remove it from the list */
4055 argv[j] = argv[j+1];
4057 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4058 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4060 fprintf (stderr, "%s: %s\n", real_progname, usage);
4062 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4065 (!strcmp(argv[i], "--debug") ||
4066 !strcmp(argv[i], "-debug") ||
4067 !strcmp(argv[i], "-d")))
4071 for (j = i; j < argc; j++) /* remove it from the list */
4072 argv[j] = argv[j+1];
4079 (!strcmp(argv[i], "--configdir")))
4083 hack_configuration_path = argv[i+1];
4084 for (j = i; j < argc; j++) /* remove them from the list */
4085 argv[j] = argv[j+2];
4089 if (0 != stat (hack_configuration_path, &st))
4092 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4096 else if (!S_ISDIR (st.st_mode))
4098 fprintf (stderr, "%s: not a directory: %s\n",
4099 blurb(), hack_configuration_path);
4107 fprintf (stderr, "%s: using config directory \"%s\"\n",
4108 progname, hack_configuration_path);
4111 /* Let Gtk open the X connection, then initialize Xt to use that
4112 same connection. Doctor Frankenstein would be proud.
4114 # ifdef HAVE_CRAPPLET
4117 GnomeClient *client;
4118 GnomeClientFlags flags = 0;
4120 int init_results = gnome_capplet_init ("screensaver-properties",
4122 argc, argv, NULL, 0, NULL);
4124 0 upon successful initialization;
4125 1 if --init-session-settings was passed on the cmdline;
4126 2 if --ignore was passed on the cmdline;
4129 So the 1 signifies just to init the settings, and quit, basically.
4130 (Meaning launch the xscreensaver daemon.)
4133 if (init_results < 0)
4136 g_error ("An initialization error occurred while "
4137 "starting xscreensaver-capplet.\n");
4139 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4140 real_progname, init_results);
4145 client = gnome_master_client ();
4148 flags = gnome_client_get_flags (client);
4150 if (flags & GNOME_CLIENT_IS_CONNECTED)
4153 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4154 gnome_client_get_id (client));
4157 char *session_args[20];
4159 session_args[i++] = real_progname;
4160 session_args[i++] = "--capplet";
4161 session_args[i++] = "--init-session-settings";
4162 session_args[i] = 0;
4163 gnome_client_set_priority (client, 20);
4164 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4165 gnome_client_set_restart_command (client, i, session_args);
4169 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4172 gnome_client_flush (client);
4175 if (init_results == 1)
4177 system ("xscreensaver -nosplash &");
4183 # endif /* HAVE_CRAPPLET */
4185 gtk_init (&argc, &argv);
4189 /* We must read exactly the same resources as xscreensaver.
4190 That means we must have both the same progclass *and* progname,
4191 at least as far as the resource database is concerned. So,
4192 put "xscreensaver" in argv[0] while initializing Xt.
4194 argv[0] = "xscreensaver";
4198 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4200 XtToolkitInitialize ();
4201 app = XtCreateApplicationContext ();
4202 dpy = GDK_DISPLAY();
4203 XtAppSetFallbackResources (app, defaults);
4204 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4205 toplevel_shell = XtAppCreateShell (progname, progclass,
4206 applicationShellWidgetClass,
4209 dpy = XtDisplay (toplevel_shell);
4210 db = XtDatabase (dpy);
4211 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4212 XSetErrorHandler (demo_ehandler);
4214 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4215 signal (SIGPIPE, SIG_IGN);
4217 /* After doing Xt-style command-line processing, complain about any
4218 unrecognized command-line arguments.
4220 for (i = 1; i < argc; i++)
4222 char *str = argv[i];
4223 if (str[0] == '-' && str[1] == '-')
4225 if (!strcmp (str, "-prefs"))
4227 else if (crapplet_p)
4228 /* There are lots of random args that we don't care about when we're
4229 started as a crapplet, so just ignore unknown args in that case. */
4233 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname, argv[i]);
4234 fprintf (stderr, "%s: %s\n", real_progname, usage);
4239 /* Load the init file, which may end up consulting the X resource database
4240 and the site-wide app-defaults file. Note that at this point, it's
4241 important that `progname' be "xscreensaver", rather than whatever
4246 hack_environment (s); /* must be before initialize_sort_map() */
4249 initialize_sort_map (s);
4251 /* Now that Xt has been initialized, and the resources have been read,
4252 we can set our `progname' variable to something more in line with
4255 progname = real_progname;
4259 /* Print out all the resources we read. */
4261 XrmName name = { 0 };
4262 XrmClass class = { 0 };
4264 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4270 /* Intern the atoms that xscreensaver_command() needs.
4272 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4273 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4274 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4275 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4276 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4277 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4278 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4279 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4280 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4281 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4282 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4283 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4284 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4287 /* Create the window and all its widgets.
4289 s->base_widget = create_xscreensaver_demo ();
4290 s->popup_widget = create_xscreensaver_settings_dialog ();
4291 s->toplevel_widget = s->base_widget;
4294 /* Set the main window's title. */
4296 char *base_title = _("Screensaver Preferences");
4297 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4298 char *s1, *s2, *s3, *s4;
4299 s1 = (char *) strchr(v, ' '); s1++;
4300 s2 = (char *) strchr(s1, ' ');
4301 s3 = (char *) strchr(v, '('); s3++;
4302 s4 = (char *) strchr(s3, ')');
4306 window_title = (char *) malloc (strlen (base_title) +
4307 strlen (progclass) +
4308 strlen (s1) + strlen (s3) +
4310 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4311 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4312 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4316 /* Adjust the (invisible) notebooks on the popup dialog... */
4318 GtkNotebook *notebook =
4319 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4320 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4324 gtk_widget_hide (std);
4325 # else /* !HAVE_XML */
4326 /* Make the advanced page be the only one available. */
4327 gtk_widget_set_sensitive (std, False);
4328 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4329 gtk_widget_hide (std);
4331 # endif /* !HAVE_XML */
4333 gtk_notebook_set_page (notebook, page);
4334 gtk_notebook_set_show_tabs (notebook, False);
4337 /* Various other widget initializations...
4339 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4340 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4342 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4343 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4346 populate_hack_list (s);
4347 populate_prefs_page (s);
4348 sensitize_demo_widgets (s, False);
4349 fix_text_entry_sizes (s);
4350 scroll_to_current_hack (s);
4352 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4353 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4357 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4358 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4360 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4361 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4363 #endif /* !HAVE_GTK2 */
4365 /* Hook up callbacks to the items on the mode menu. */
4367 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4368 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4369 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4370 for (; kids; kids = kids->next)
4371 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4372 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4377 /* Handle the -prefs command-line argument. */
4380 GtkNotebook *notebook =
4381 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4382 gtk_notebook_set_page (notebook, 1);
4385 # ifdef HAVE_CRAPPLET
4389 GtkWidget *outer_vbox;
4391 gtk_widget_hide (s->toplevel_widget);
4393 capplet = capplet_widget_new ();
4395 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4396 # ifdef HAVE_CRAPPLET_IMMEDIATE
4397 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4398 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4399 /* In crapplet-mode, take off the menubar. */
4400 gtk_widget_hide (name_to_widget (s, "menubar"));
4402 /* Reparent our top-level container to be a child of the capplet
4405 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4406 gtk_widget_ref (outer_vbox);
4407 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4409 GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4410 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4412 /* Find the window above us, and set the title and close handler. */
4414 GtkWidget *window = capplet;
4415 while (window && !GTK_IS_WINDOW (window))
4416 window = window->parent;
4419 gtk_window_set_title (GTK_WINDOW (window), window_title);
4420 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4421 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4426 s->toplevel_widget = capplet;
4428 # endif /* HAVE_CRAPPLET */
4431 /* The Gnome folks hate the menubar. I think it's important to have access
4432 to the commands on the File menu (Restart Daemon, etc.) and to the
4433 About and Documentation commands on the Help menu.
4437 gtk_widget_hide (name_to_widget (s, "menubar"));
4441 free (window_title);
4445 gtk_widget_show (s->toplevel_widget);
4446 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4447 fix_preview_visual (s);
4449 /* Realize page zero, so that we can diddle the scrollbar when the
4450 user tabs back to it -- otherwise, the current hack isn't scrolled
4451 to the first time they tab back there, when started with "-prefs".
4452 (Though it is if they then tab away, and back again.)
4454 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4455 #### understands this crap, explain to me how to make this work.
4457 gtk_widget_realize (name_to_widget (s, "demos_table"));
4460 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4463 /* Issue any warnings about the running xscreensaver daemon. */
4464 the_network_is_not_the_computer (s);
4467 /* Run the Gtk event loop, and not the Xt event loop. This means that
4468 if there were Xt timers or fds registered, they would never get serviced,
4469 and if there were any Xt widgets, they would never have events delivered.
4470 Fortunately, we're using Gtk for all of the UI, and only initialized
4471 Xt so that we could process the command line and use the X resource
4474 s->initializing_p = False;
4476 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4477 after we start up. Otherwise, it always appears scrolled to the top
4478 when in crapplet-mode. */
4479 gtk_timeout_add (500, delayed_scroll_kludge, s);
4483 /* Load every configurator in turn, to scan them for errors all at once. */
4486 for (i = 0; i < p->screenhacks_count; i++)
4488 screenhack *hack = p->screenhacks[i];
4489 conf_data *d = load_configurator (hack->command, False);
4490 if (d) free_conf_data (d);
4496 # ifdef HAVE_CRAPPLET
4498 capplet_gtk_main ();
4500 # endif /* HAVE_CRAPPLET */
4503 kill_preview_subproc (s);
4507 #endif /* HAVE_GTK -- whole file */