1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2004 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #ifdef HAVE_GTK /* whole file */
19 #include <xscreensaver-intl.h>
28 # define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */
36 #endif /* ENABLE_NLS */
39 # include <pwd.h> /* for getpwuid() */
45 # include <sys/utsname.h> /* for uname() */
46 #endif /* HAVE_UNAME */
56 #ifdef HAVE_SYS_WAIT_H
57 # include <sys/wait.h> /* for waitpid() and associated macros */
61 #include <X11/Xproto.h> /* for CARD32 */
62 #include <X11/Xatom.h> /* for XA_INTEGER */
63 #include <X11/Intrinsic.h>
64 #include <X11/StringDefs.h>
66 /* We don't actually use any widget internals, but these are included
67 so that gdb will have debug info for the widgets... */
68 #include <X11/IntrinsicP.h>
69 #include <X11/ShellP.h>
73 # include <X11/Xmu/Error.h>
75 # include <Xmu/Error.h>
85 # include <capplet-widget.h>
91 #include <glade/glade-xml.h>
92 #endif /* HAVE_GTK2 */
94 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
95 # define GLADE_DIR DEFAULT_ICONDIR
97 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
98 # define DEFAULT_ICONDIR GLADE_DIR
102 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
103 It is unused otherwise, so in that case, stub it out. */
104 static const char *hack_configuration_path = 0;
111 #include "resources.h" /* for parse_time() */
112 #include "visual.h" /* for has_writable_cells() */
113 #include "remote.h" /* for xscreensaver_command() */
116 #include "logo-50.xpm"
117 #include "logo-180.xpm"
119 #undef dgettext /* else these are defined twice... */
122 #include "demo-Gtk-widgets.h"
123 #include "demo-Gtk-support.h"
124 #include "demo-Gtk-conf.h"
136 #endif /* HAVE_GTK2 */
139 extern void exec_command (const char *shell, const char *command, int nice);
141 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
144 #define countof(x) (sizeof((x))/sizeof((*x)))
148 char *progclass = "XScreenSaver";
151 /* The order of the items in the mode menu. */
152 static int mode_menu_order[] = {
153 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS };
158 char *short_version; /* version number of this xscreensaver build */
160 GtkWidget *toplevel_widget; /* the main window */
161 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
162 GtkWidget *popup_widget; /* the "Settings" dialog */
163 conf_data *cdata; /* private data for per-hack configuration */
166 GladeXML *glade_ui; /* Glade UI file */
167 #endif /* HAVE_GTK2 */
169 Bool debug_p; /* whether to print diagnostics */
170 Bool initializing_p; /* flag for breaking recursion loops */
171 Bool saving_p; /* flag for breaking recursion loops */
173 char *desired_preview_cmd; /* subprocess we intend to run */
174 char *running_preview_cmd; /* subprocess we are currently running */
175 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
176 Bool running_preview_error_p; /* whether the pid died abnormally */
178 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
179 int subproc_timer_id; /* timer to delay subproc launch */
180 int subproc_check_timer_id; /* timer to check whether it started up */
181 int subproc_check_countdown; /* how many more checks left */
183 int *list_elt_to_hack_number; /* table for sorting the hack list */
184 int *hack_number_to_list_elt; /* the inverse table */
185 Bool *hacks_available_p; /* whether hacks are on $PATH */
186 int list_count; /* how many items are in the list: this may be
187 less than p->screenhacks_count, if some are
190 int _selected_list_element; /* don't use this: call
191 selected_list_element() instead */
193 saver_preferences prefs;
198 /* Total fucking evilness due to the fact that it's rocket science to get
199 a closure object of our own down into the various widget callbacks. */
200 static state *global_state_kludge;
203 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
204 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
205 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
208 static void populate_demo_window (state *, int list_elt);
209 static void populate_prefs_page (state *);
210 static void populate_popup_window (state *);
212 static Bool flush_dialog_changes_and_save (state *);
213 static Bool flush_popup_changes_and_save (state *);
215 static int maybe_reload_init_file (state *);
216 static void await_xscreensaver (state *);
218 static void schedule_preview (state *, const char *cmd);
219 static void kill_preview_subproc (state *, Bool reset_p);
220 static void schedule_preview_check (state *);
224 /* Some random utility functions
230 time_t now = time ((time_t *) 0);
231 char *ct = (char *) ctime (&now);
232 static char buf[255];
233 int n = strlen(progname);
235 strncpy(buf, progname, n);
238 strncpy(buf+n, ct+11, 8);
239 strcpy(buf+n+9, ": ");
245 name_to_widget (state *s, const char *name)
255 /* First try to load the Glade file from the current directory;
256 if there isn't one there, check the installed directory.
258 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
259 const char * const files[] = { GLADE_FILE_NAME,
260 GLADE_DIR "/" GLADE_FILE_NAME };
262 for (i = 0; i < countof (files); i++)
265 if (!stat (files[i], &st))
267 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
274 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
275 "\tfrom " GLADE_DIR "/ or current directory.\n",
279 # undef GLADE_FILE_NAME
281 glade_xml_signal_autoconnect (s->glade_ui);
284 w = glade_xml_get_widget (s->glade_ui, name);
286 #else /* !HAVE_GTK2 */
288 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
291 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
293 #endif /* HAVE_GTK2 */
296 fprintf (stderr, "%s: no widget \"%s\"\n", blurb(), name);
301 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
302 Takes a scroller, viewport, or list as an argument.
305 ensure_selected_item_visible (GtkWidget *widget)
309 GtkTreeSelection *selection;
313 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
314 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
315 path = gtk_tree_path_new_first ();
317 path = gtk_tree_model_get_path (model, &iter);
319 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
321 gtk_tree_path_free (path);
323 #else /* !HAVE_GTK2 */
325 GtkScrolledWindow *scroller = 0;
327 GtkList *list_widget = 0;
331 GtkWidget *selected = 0;
334 gint parent_h, child_y, child_h, children_h, ignore;
335 double ratio_t, ratio_b;
337 if (GTK_IS_SCROLLED_WINDOW (widget))
339 scroller = GTK_SCROLLED_WINDOW (widget);
340 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
341 list_widget = GTK_LIST (GTK_BIN(vp)->child);
343 else if (GTK_IS_VIEWPORT (widget))
345 vp = GTK_VIEWPORT (widget);
346 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
347 list_widget = GTK_LIST (GTK_BIN(vp)->child);
349 else if (GTK_IS_LIST (widget))
351 list_widget = GTK_LIST (widget);
352 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
353 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
358 slist = list_widget->selection;
359 selected = (slist ? GTK_WIDGET (slist->data) : 0);
363 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
365 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
366 kids; kids = kids->next)
369 adj = gtk_scrolled_window_get_vadjustment (scroller);
371 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
372 &ignore, &ignore, &ignore, &parent_h, &ignore);
373 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
374 &ignore, &child_y, &ignore, &child_h, &ignore);
375 children_h = nkids * child_h;
377 ratio_t = ((double) child_y) / ((double) children_h);
378 ratio_b = ((double) child_y + child_h) / ((double) children_h);
380 if (adj->upper == 0.0) /* no items in list */
383 if (ratio_t < (adj->value / adj->upper) ||
384 ratio_b > ((adj->value + adj->page_size) / adj->upper))
387 int slop = parent_h * 0.75; /* how much to overshoot by */
389 if (ratio_t < (adj->value / adj->upper))
391 double ratio_w = ((double) parent_h) / ((double) children_h);
392 double ratio_l = (ratio_b - ratio_t);
393 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
396 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
398 target = ratio_t * adj->upper;
402 if (target > adj->upper - adj->page_size)
403 target = adj->upper - adj->page_size;
407 gtk_adjustment_set_value (adj, target);
409 #endif /* !HAVE_GTK2 */
413 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
415 GtkWidget *shell = GTK_WIDGET (user_data);
416 while (shell->parent)
417 shell = shell->parent;
418 gtk_widget_destroy (GTK_WIDGET (shell));
422 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
424 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
426 restart_menu_cb (widget, user_data);
427 warning_dialog_dismiss_cb (widget, user_data);
431 warning_dialog (GtkWidget *parent, const char *message,
432 Boolean restart_button_p, int center)
434 char *msg = strdup (message);
437 GtkWidget *dialog = gtk_dialog_new ();
438 GtkWidget *label = 0;
440 GtkWidget *cancel = 0;
443 while (parent && !parent->window)
444 parent = parent->parent;
447 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
449 fprintf (stderr, "%s: too early for dialog?\n", progname);
457 char *s = strchr (head, '\n');
460 sprintf (name, "label%d", i++);
463 label = gtk_label_new (head);
465 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
466 #endif /* HAVE_GTK2 */
471 GTK_WIDGET (label)->style =
472 gtk_style_copy (GTK_WIDGET (label)->style);
473 GTK_WIDGET (label)->style->font =
474 gdk_font_load (get_string_resource("warning_dialog.headingFont",
476 gtk_widget_set_style (GTK_WIDGET (label),
477 GTK_WIDGET (label)->style);
479 #endif /* !HAVE_GTK2 */
481 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
482 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
483 label, TRUE, TRUE, 0);
484 gtk_widget_show (label);
495 label = gtk_label_new ("");
496 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
497 label, TRUE, TRUE, 0);
498 gtk_widget_show (label);
500 label = gtk_hbutton_box_new ();
501 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
502 label, TRUE, TRUE, 0);
505 if (restart_button_p)
507 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
508 gtk_container_add (GTK_CONTAINER (label), cancel);
511 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
512 gtk_container_add (GTK_CONTAINER (label), ok);
514 #else /* !HAVE_GTK2 */
516 ok = gtk_button_new_with_label ("OK");
517 gtk_container_add (GTK_CONTAINER (label), ok);
519 if (restart_button_p)
521 cancel = gtk_button_new_with_label ("Cancel");
522 gtk_container_add (GTK_CONTAINER (label), cancel);
525 #endif /* !HAVE_GTK2 */
527 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
528 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
529 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
530 STFU GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
531 gtk_widget_show (ok);
532 gtk_widget_grab_focus (ok);
536 STFU GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
537 gtk_widget_show (cancel);
539 gtk_widget_show (label);
540 gtk_widget_show (dialog);
542 if (restart_button_p)
544 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
545 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
547 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
548 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
553 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
554 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
558 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
559 GTK_WIDGET (parent)->window);
562 gtk_window_present (GTK_WINDOW (dialog));
563 #else /* !HAVE_GTK2 */
564 gdk_window_show (GTK_WIDGET (dialog)->window);
565 gdk_window_raise (GTK_WIDGET (dialog)->window);
566 #endif /* !HAVE_GTK2 */
573 run_cmd (state *s, Atom command, int arg)
578 flush_dialog_changes_and_save (s);
579 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
581 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
582 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
589 sprintf (buf, "Error:\n\n%s", err);
591 strcpy (buf, "Unknown error!");
592 warning_dialog (s->toplevel_widget, buf, False, 100);
599 run_hack (state *s, int list_elt, Bool report_errors_p)
602 if (list_elt < 0) return;
603 hack_number = s->list_elt_to_hack_number[list_elt];
605 flush_dialog_changes_and_save (s);
606 schedule_preview (s, 0);
608 run_cmd (s, XA_DEMO, hack_number + 1);
612 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
624 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
626 state *s = global_state_kludge; /* I hate C so much... */
627 flush_dialog_changes_and_save (s);
628 kill_preview_subproc (s, False);
633 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
635 state *s = (state *) data;
636 flush_dialog_changes_and_save (s);
643 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
646 char *vers = strdup (screensaver_id + 4);
649 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
651 s = strchr (vers, ',');
655 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
656 non-ASCII characters aren't allowed in localizable string keys."
657 (I don't want to just use (c) instead of © because that doesn't
658 look as good in the plain-old default Latin1 "C" locale.)
661 sprintf(copy, ("Copyright \xC2\xA9 1991-2004 %s"), s);
662 #else /* !HAVE_GTK2 */
663 sprintf(copy, ("Copyright \251 1991-2004 %s"), s);
664 #endif /* !HAVE_GTK2 */
666 sprintf (msg, "%s\n\n%s", copy, desc);
668 /* I can't make gnome_about_new() work here -- it starts dying in
669 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
670 then this might be the thing to do:
674 const gchar *auth[] = { 0 };
675 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
677 gtk_widget_show (about);
679 #else / * GTK but not GNOME * /
683 GdkColormap *colormap;
684 GdkPixmap *gdkpixmap;
687 GtkWidget *dialog = gtk_dialog_new ();
688 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
689 GtkWidget *parent = GTK_WIDGET (menuitem);
690 while (parent->parent)
691 parent = parent->parent;
693 hbox = gtk_hbox_new (FALSE, 20);
694 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
695 hbox, TRUE, TRUE, 0);
697 colormap = gtk_widget_get_colormap (parent);
699 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
700 (gchar **) logo_180_xpm);
701 icon = gtk_pixmap_new (gdkpixmap, mask);
702 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
704 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
706 vbox = gtk_vbox_new (FALSE, 0);
707 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
709 label1 = gtk_label_new (vers);
710 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
711 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
712 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
715 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
716 GTK_WIDGET (label1)->style->font =
717 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
718 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
719 #endif /* HAVE_GTK2 */
721 label2 = gtk_label_new (msg);
722 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
723 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
724 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
727 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
728 GTK_WIDGET (label2)->style->font =
729 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
730 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
731 #endif /* HAVE_GTK2 */
733 hb = gtk_hbutton_box_new ();
735 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
739 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
740 #else /* !HAVE_GTK2 */
741 ok = gtk_button_new_with_label (_("OK"));
742 #endif /* !HAVE_GTK2 */
743 gtk_container_add (GTK_CONTAINER (hb), ok);
745 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
746 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
747 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
749 gtk_widget_show (hbox);
750 gtk_widget_show (icon);
751 gtk_widget_show (vbox);
752 gtk_widget_show (label1);
753 gtk_widget_show (label2);
754 gtk_widget_show (hb);
755 gtk_widget_show (ok);
756 gtk_widget_show (dialog);
758 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
759 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
761 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
762 GTK_WIDGET (parent)->window);
763 gdk_window_show (GTK_WIDGET (dialog)->window);
764 gdk_window_raise (GTK_WIDGET (dialog)->window);
770 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
772 state *s = global_state_kludge; /* I hate C so much... */
773 saver_preferences *p = &s->prefs;
776 if (!p->help_url || !*p->help_url)
778 warning_dialog (s->toplevel_widget,
780 "No Help URL has been specified.\n"), False, 100);
784 help_command = (char *) malloc (strlen (p->load_url_command) +
785 (strlen (p->help_url) * 2) + 20);
786 strcpy (help_command, "( ");
787 sprintf (help_command + strlen(help_command),
788 p->load_url_command, p->help_url, p->help_url);
789 strcat (help_command, " ) &");
790 system (help_command);
796 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
798 state *s = global_state_kludge; /* I hate C so much... */
799 run_cmd (s, XA_ACTIVATE, 0);
804 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
806 state *s = global_state_kludge; /* I hate C so much... */
807 run_cmd (s, XA_LOCK, 0);
812 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
814 state *s = global_state_kludge; /* I hate C so much... */
815 run_cmd (s, XA_EXIT, 0);
820 restart_menu_cb (GtkWidget *widget, gpointer user_data)
822 state *s = global_state_kludge; /* I hate C so much... */
823 flush_dialog_changes_and_save (s);
824 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
826 system ("xscreensaver -nosplash &");
828 await_xscreensaver (s);
832 await_xscreensaver (state *s)
836 Display *dpy = GDK_DISPLAY();
837 /* GtkWidget *dialog = 0;*/
840 while (!rversion && (--countdown > 0))
842 /* Check for the version of the running xscreensaver... */
843 server_xscreensaver_version (dpy, &rversion, 0, 0);
845 /* If it's not there yet, wait a second... */
850 /* if (dialog) gtk_widget_destroy (dialog);*/
859 /* Timed out, no screensaver running. */
862 Bool root_p = (geteuid () == 0);
866 "The xscreensaver daemon did not start up properly.\n"
872 __extension__ /* don't warn about "string length is greater than
873 the length ISO C89 compilers are required to
874 support" in the following expression... */
877 _("You are running as root. This usually means that xscreensaver\n"
878 "was unable to contact your X server because access control is\n"
879 "turned on. Try running this command:\n"
881 " xhost +localhost\n"
883 "and then selecting `File / Restart Daemon'.\n"
885 "Note that turning off access control will allow anyone logged\n"
886 "on to this machine to access your screen, which might be\n"
887 "considered a security problem. Please read the xscreensaver\n"
888 "manual and FAQ for more information.\n"
890 "You shouldn't run X as root. Instead, you should log in as a\n"
891 "normal user, and `su' as necessary."));
893 strcat (buf, _("Please check your $PATH and permissions."));
895 warning_dialog (s->toplevel_widget, buf, False, 1);
901 selected_list_element (state *s)
903 return s->_selected_list_element;
908 demo_write_init_file (state *s, saver_preferences *p)
912 /* #### try to figure out why shit keeps getting reordered... */
913 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
917 if (!write_init_file (p, s->short_version, False))
920 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
925 const char *f = init_file_name();
927 warning_dialog (s->toplevel_widget,
928 _("Error:\n\nCouldn't determine init file name!\n"),
932 char *b = (char *) malloc (strlen(f) + 1024);
933 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
934 warning_dialog (s->toplevel_widget, b, False, 100);
943 run_this_cb (GtkButton *button, gpointer user_data)
945 state *s = global_state_kludge; /* I hate C so much... */
946 int list_elt = selected_list_element (s);
947 if (list_elt < 0) return;
948 if (!flush_dialog_changes_and_save (s))
949 run_hack (s, list_elt, True);
954 manual_cb (GtkButton *button, gpointer user_data)
956 state *s = global_state_kludge; /* I hate C so much... */
957 saver_preferences *p = &s->prefs;
958 GtkWidget *list_widget = name_to_widget (s, "list");
959 int list_elt = selected_list_element (s);
961 char *name, *name2, *cmd, *str;
962 if (list_elt < 0) return;
963 hack_number = s->list_elt_to_hack_number[list_elt];
965 flush_dialog_changes_and_save (s);
966 ensure_selected_item_visible (list_widget);
968 name = strdup (p->screenhacks[hack_number]->command);
970 while (isspace (*name2)) name2++;
972 while (*str && !isspace (*str)) str++;
974 str = strrchr (name2, '/');
975 if (str) name = str+1;
977 cmd = get_string_resource ("manualCommand", "ManualCommand");
980 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
982 sprintf (cmd2 + strlen (cmd2),
984 name2, name2, name2, name2);
985 strcat (cmd2, " ) &");
991 warning_dialog (GTK_WIDGET (button),
992 _("Error:\n\nno `manualCommand' resource set."),
1001 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1003 GtkWidget *parent = name_to_widget (s, "scroller");
1004 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
1007 GtkTreeModel *model;
1008 GtkTreeSelection *selection;
1009 #endif /* HAVE_GTK2 */
1011 if (!was) gtk_widget_set_sensitive (parent, True);
1013 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1014 STFU g_assert (model);
1015 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
1016 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1017 gtk_tree_selection_select_iter (selection, &iter);
1018 #else /* !HAVE_GTK2 */
1019 gtk_list_select_item (GTK_LIST (list), list_elt);
1020 #endif /* !HAVE_GTK2 */
1021 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1022 if (!was) gtk_widget_set_sensitive (parent, False);
1027 run_next_cb (GtkButton *button, gpointer user_data)
1029 state *s = global_state_kludge; /* I hate C so much... */
1030 /* saver_preferences *p = &s->prefs; */
1031 Bool ops = s->preview_suppressed_p;
1033 GtkWidget *list_widget = name_to_widget (s, "list");
1034 int list_elt = selected_list_element (s);
1041 if (list_elt >= s->list_count)
1044 s->preview_suppressed_p = True;
1046 flush_dialog_changes_and_save (s);
1047 force_list_select_item (s, list_widget, list_elt, True);
1048 populate_demo_window (s, list_elt);
1049 run_hack (s, list_elt, False);
1051 s->preview_suppressed_p = ops;
1056 run_prev_cb (GtkButton *button, gpointer user_data)
1058 state *s = global_state_kludge; /* I hate C so much... */
1059 /* saver_preferences *p = &s->prefs; */
1060 Bool ops = s->preview_suppressed_p;
1062 GtkWidget *list_widget = name_to_widget (s, "list");
1063 int list_elt = selected_list_element (s);
1066 list_elt = s->list_count - 1;
1071 list_elt = s->list_count - 1;
1073 s->preview_suppressed_p = True;
1075 flush_dialog_changes_and_save (s);
1076 force_list_select_item (s, list_widget, list_elt, True);
1077 populate_demo_window (s, list_elt);
1078 run_hack (s, list_elt, False);
1080 s->preview_suppressed_p = ops;
1084 /* Writes the given settings into prefs.
1085 Returns true if there was a change, False otherwise.
1086 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1089 flush_changes (state *s,
1092 const char *command,
1095 saver_preferences *p = &s->prefs;
1096 Bool changed = False;
1099 if (list_elt < 0 || list_elt >= s->list_count)
1102 hack_number = s->list_elt_to_hack_number[list_elt];
1103 hack = p->screenhacks[hack_number];
1105 if (enabled_p != -1 &&
1106 enabled_p != hack->enabled_p)
1108 hack->enabled_p = enabled_p;
1111 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1112 blurb(), hack->name, enabled_p);
1117 if (!hack->command || !!strcmp (command, hack->command))
1119 if (hack->command) free (hack->command);
1120 hack->command = strdup (command);
1123 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1124 blurb(), hack->name, command);
1130 const char *ov = hack->visual;
1131 if (!ov || !*ov) ov = "any";
1132 if (!*visual) visual = "any";
1133 if (!!strcasecmp (visual, ov))
1135 if (hack->visual) free (hack->visual);
1136 hack->visual = strdup (visual);
1139 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1140 blurb(), hack->name, visual);
1148 /* Helper for the text fields that contain time specifications:
1149 this parses the text, and does error checking.
1152 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1157 if (!sec_p || strchr (line, ':'))
1158 value = parse_time ((char *) line, sec_p, True);
1162 if (sscanf (line, "%d%c", &value, &c) != 1)
1168 value *= 1000; /* Time measures in microseconds */
1174 "Unparsable time format: \"%s\"\n"),
1176 warning_dialog (s->toplevel_widget, b, False, 100);
1185 directory_p (const char *path)
1188 if (!path || !*path)
1190 else if (stat (path, &st))
1192 else if (!S_ISDIR (st.st_mode))
1199 normalize_directory (const char *path)
1203 if (!path || !*path) return 0;
1205 p2 = (char *) malloc (L + 2);
1207 if (p2[L-1] == '/') /* remove trailing slash */
1210 for (s = p2; s && *s; s++)
1213 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1214 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1217 while (s0 > p2 && s0[-1] != '/')
1227 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1228 strcpy (s, s+2), s--;
1229 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1233 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1234 while (s[0] == '/' && s[1] == '/')
1237 /* and strip trailing whitespace for good measure. */
1239 while (isspace(p2[L-1]))
1252 } FlushForeachClosure;
1255 flush_checkbox (GtkTreeModel *model,
1260 FlushForeachClosure *closure = data;
1263 gtk_tree_model_get (model, iter,
1264 COL_ENABLED, &checked,
1267 if (flush_changes (closure->s, closure->i,
1269 *closure->changed = True;
1273 /* don't remove row */
1277 #endif /* HAVE_GTK2 */
1279 /* Flush out any changes made in the main dialog window (where changes
1280 take place immediately: clicking on a checkbox causes the init file
1281 to be written right away.)
1284 flush_dialog_changes_and_save (state *s)
1286 saver_preferences *p = &s->prefs;
1287 saver_preferences P2, *p2 = &P2;
1289 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1290 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1291 FlushForeachClosure closure;
1292 #else /* !HAVE_GTK2 */
1293 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1294 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1296 #endif /* !HAVE_GTK2 */
1298 Bool changed = False;
1301 if (s->saving_p) return False;
1306 /* Flush any checkbox changes in the list down into the prefs struct.
1310 closure.changed = &changed;
1312 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1314 #else /* !HAVE_GTK2 */
1316 for (i = 0; kids; kids = kids->next, i++)
1318 GtkWidget *line = GTK_WIDGET (kids->data);
1319 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1320 GtkWidget *line_check =
1321 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1323 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1325 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1328 #endif /* ~HAVE_GTK2 */
1330 /* Flush the non-hack-specific settings down into the prefs struct.
1333 # define SECONDS(FIELD,NAME) \
1334 w = name_to_widget (s, (NAME)); \
1335 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1337 # define MINUTES(FIELD,NAME) \
1338 w = name_to_widget (s, (NAME)); \
1339 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1341 # define CHECKBOX(FIELD,NAME) \
1342 w = name_to_widget (s, (NAME)); \
1343 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1345 # define PATHNAME(FIELD,NAME) \
1346 w = name_to_widget (s, (NAME)); \
1347 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1349 MINUTES (&p2->timeout, "timeout_spinbutton");
1350 MINUTES (&p2->cycle, "cycle_spinbutton");
1351 CHECKBOX (p2->lock_p, "lock_button");
1352 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1354 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1355 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1356 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1357 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1359 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1360 CHECKBOX (p2->grab_video_p, "grab_video_button");
1361 CHECKBOX (p2->random_image_p, "grab_image_button");
1362 PATHNAME (p2->image_directory, "image_text");
1364 CHECKBOX (p2->verbose_p, "verbose_button");
1365 CHECKBOX (p2->capture_stderr_p, "capture_button");
1366 CHECKBOX (p2->splash_p, "splash_button");
1368 CHECKBOX (p2->install_cmap_p, "install_button");
1369 CHECKBOX (p2->fade_p, "fade_button");
1370 CHECKBOX (p2->unfade_p, "unfade_button");
1371 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1378 /* Warn if the image directory doesn't exist.
1380 if (p2->image_directory &&
1381 *p2->image_directory &&
1382 !directory_p (p2->image_directory))
1385 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1386 p2->image_directory);
1387 warning_dialog (s->toplevel_widget, b, False, 100);
1391 /* Map the mode menu to `saver_mode' enum values. */
1393 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1394 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1395 GtkWidget *selected = gtk_menu_get_active (menu);
1396 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1397 int menu_elt = g_list_index (kids, (gpointer) selected);
1398 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1399 p2->mode = mode_menu_order[menu_elt];
1402 if (p2->mode == ONE_HACK)
1404 int list_elt = selected_list_element (s);
1405 p2->selected_hack = (list_elt >= 0
1406 ? s->list_elt_to_hack_number[list_elt]
1410 # define COPY(field, name) \
1411 if (p->field != p2->field) { \
1414 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1416 p->field = p2->field
1419 COPY(selected_hack, "selected_hack");
1421 COPY(timeout, "timeout");
1422 COPY(cycle, "cycle");
1423 COPY(lock_p, "lock_p");
1424 COPY(lock_timeout, "lock_timeout");
1426 COPY(dpms_enabled_p, "dpms_enabled_p");
1427 COPY(dpms_standby, "dpms_standby");
1428 COPY(dpms_suspend, "dpms_suspend");
1429 COPY(dpms_off, "dpms_off");
1431 COPY(verbose_p, "verbose_p");
1432 COPY(capture_stderr_p, "capture_stderr_p");
1433 COPY(splash_p, "splash_p");
1435 COPY(install_cmap_p, "install_cmap_p");
1436 COPY(fade_p, "fade_p");
1437 COPY(unfade_p, "unfade_p");
1438 COPY(fade_seconds, "fade_seconds");
1440 COPY(grab_desktop_p, "grab_desktop_p");
1441 COPY(grab_video_p, "grab_video_p");
1442 COPY(random_image_p, "random_image_p");
1446 if (!p->image_directory ||
1447 !p2->image_directory ||
1448 strcmp(p->image_directory, p2->image_directory))
1452 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1453 blurb(), p2->image_directory);
1455 if (p->image_directory && p->image_directory != p2->image_directory)
1456 free (p->image_directory);
1457 p->image_directory = p2->image_directory;
1458 p2->image_directory = 0;
1460 populate_prefs_page (s);
1464 Display *dpy = GDK_DISPLAY();
1465 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1466 sync_server_dpms_settings (dpy, enabled_p,
1467 p->dpms_standby / 1000,
1468 p->dpms_suspend / 1000,
1472 changed = demo_write_init_file (s, p);
1475 s->saving_p = False;
1480 /* Flush out any changes made in the popup dialog box (where changes
1481 take place only when the OK button is clicked.)
1484 flush_popup_changes_and_save (state *s)
1486 Bool changed = False;
1487 saver_preferences *p = &s->prefs;
1488 int list_elt = selected_list_element (s);
1490 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1491 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1493 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1494 const char *command = gtk_entry_get_text (cmd);
1499 if (s->saving_p) return False;
1505 if (maybe_reload_init_file (s) != 0)
1511 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1513 if (!strcasecmp (visual, "")) visual = "";
1514 else if (!strcasecmp (visual, "any")) visual = "";
1515 else if (!strcasecmp (visual, "default")) visual = "Default";
1516 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1517 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1518 else if (!strcasecmp (visual, "best")) visual = "Best";
1519 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1520 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1521 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1522 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1523 else if (!strcasecmp (visual, "color")) visual = "Color";
1524 else if (!strcasecmp (visual, "gl")) visual = "GL";
1525 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1526 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1527 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1528 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1529 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1530 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1531 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1532 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1533 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1536 gdk_beep (); /* unparsable */
1538 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1541 changed = flush_changes (s, list_elt, -1, command, visual);
1544 changed = demo_write_init_file (s, p);
1546 /* Do this to re-launch the hack if (and only if) the command line
1548 populate_demo_window (s, selected_list_element (s));
1552 s->saving_p = False;
1558 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1560 state *s = global_state_kludge; /* I hate C so much... */
1561 if (! s->initializing_p)
1563 s->initializing_p = True;
1564 flush_dialog_changes_and_save (s);
1565 s->initializing_p = False;
1570 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1572 pref_changed_cb (widget, user_data);
1576 /* Callback on menu items in the "mode" options menu.
1579 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1581 state *s = (state *) user_data;
1582 saver_preferences *p = &s->prefs;
1583 GtkWidget *list = name_to_widget (s, "list");
1586 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1588 saver_mode new_mode;
1592 if (menu_items->data == widget)
1595 menu_items = menu_items->next;
1597 if (!menu_items) abort();
1599 new_mode = mode_menu_order[menu_index];
1601 /* Keep the same list element displayed as before; except if we're
1602 switching *to* "one screensaver" mode from any other mode, set
1603 "the one" to be that which is currently selected.
1605 list_elt = selected_list_element (s);
1606 if (new_mode == ONE_HACK)
1607 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1610 saver_mode old_mode = p->mode;
1612 populate_demo_window (s, list_elt);
1613 force_list_select_item (s, list, list_elt, True);
1614 p->mode = old_mode; /* put it back, so the init file gets written */
1617 pref_changed_cb (widget, user_data);
1622 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1623 gint page_num, gpointer user_data)
1625 state *s = global_state_kludge; /* I hate C so much... */
1626 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1628 /* If we're switching to page 0, schedule the current hack to be run.
1629 Otherwise, schedule it to stop. */
1631 populate_demo_window (s, selected_list_element (s));
1633 schedule_preview (s, 0);
1638 list_activated_cb (GtkTreeView *list,
1640 GtkTreeViewColumn *column,
1647 STFU g_return_if_fail (!gdk_pointer_is_grabbed ());
1649 str = gtk_tree_path_to_string (path);
1650 list_elt = strtol (str, NULL, 10);
1654 run_hack (s, list_elt, True);
1658 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1660 state *s = (state *)data;
1661 GtkTreeModel *model;
1667 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1670 path = gtk_tree_model_get_path (model, &iter);
1671 str = gtk_tree_path_to_string (path);
1672 list_elt = strtol (str, NULL, 10);
1674 gtk_tree_path_free (path);
1677 populate_demo_window (s, list_elt);
1678 flush_dialog_changes_and_save (s);
1681 #else /* !HAVE_GTK2 */
1683 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1684 list_select_cb that comes in
1685 *after* we've double-clicked.
1689 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1692 state *s = (state *) data;
1693 if (event->type == GDK_2BUTTON_PRESS)
1695 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1696 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1698 last_doubleclick_time = time ((time_t *) 0);
1701 run_hack (s, list_elt, True);
1709 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1711 state *s = (state *) data;
1712 time_t now = time ((time_t *) 0);
1714 if (now >= last_doubleclick_time + 2)
1716 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1717 populate_demo_window (s, list_elt);
1718 flush_dialog_changes_and_save (s);
1723 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1725 state *s = (state *) data;
1726 populate_demo_window (s, -1);
1727 flush_dialog_changes_and_save (s);
1730 #endif /* !HAVE_GTK2 */
1733 /* Called when the checkboxes that are in the left column of the
1734 scrolling list are clicked. This both populates the right pane
1735 (just as clicking on the label (really, listitem) does) and
1736 also syncs this checkbox with the right pane Enabled checkbox.
1741 GtkCellRendererToggle *toggle,
1743 #else /* !HAVE_GTK2 */
1745 #endif /* !HAVE_GTK2 */
1748 state *s = (state *) data;
1751 GtkScrolledWindow *scroller =
1752 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1753 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1754 GtkTreeModel *model = gtk_tree_view_get_model (list);
1755 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1758 #else /* !HAVE_GTK2 */
1759 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1760 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1762 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1763 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1764 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1765 #endif /* !HAVE_GTK2 */
1772 if (!gtk_tree_model_get_iter (model, &iter, path))
1774 g_warning ("bad path: %s", path_string);
1777 gtk_tree_path_free (path);
1779 gtk_tree_model_get (model, &iter,
1780 COL_ENABLED, &active,
1783 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1784 COL_ENABLED, !active,
1787 list_elt = strtol (path_string, NULL, 10);
1788 #else /* !HAVE_GTK2 */
1789 list_elt = gtk_list_child_position (list, line);
1790 #endif /* !HAVE_GTK2 */
1792 /* remember previous scroll position of the top of the list */
1793 adj = gtk_scrolled_window_get_vadjustment (scroller);
1794 scroll_top = adj->value;
1796 flush_dialog_changes_and_save (s);
1797 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1798 populate_demo_window (s, list_elt);
1800 /* restore the previous scroll position of the top of the list.
1801 this is weak, but I don't really know why it's moving... */
1802 gtk_adjustment_set_value (adj, scroll_top);
1808 GtkFileSelection *widget;
1809 } file_selection_data;
1814 store_image_directory (GtkWidget *button, gpointer user_data)
1816 file_selection_data *fsd = (file_selection_data *) user_data;
1817 state *s = fsd->state;
1818 GtkFileSelection *selector = fsd->widget;
1819 GtkWidget *top = s->toplevel_widget;
1820 saver_preferences *p = &s->prefs;
1821 const char *path = gtk_file_selection_get_filename (selector);
1823 if (p->image_directory && !strcmp(p->image_directory, path))
1824 return; /* no change */
1826 if (!directory_p (path))
1829 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1830 warning_dialog (GTK_WIDGET (top), b, False, 100);
1834 if (p->image_directory) free (p->image_directory);
1835 p->image_directory = normalize_directory (path);
1837 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1838 (p->image_directory ? p->image_directory : ""));
1839 demo_write_init_file (s, p);
1844 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1846 file_selection_data *fsd = (file_selection_data *) user_data;
1847 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1851 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1853 browse_image_dir_cancel (button, user_data);
1854 store_image_directory (button, user_data);
1858 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1860 browse_image_dir_cancel (widget, user_data);
1865 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1867 state *s = global_state_kludge; /* I hate C so much... */
1868 saver_preferences *p = &s->prefs;
1869 static file_selection_data *fsd = 0;
1871 GtkFileSelection *selector = GTK_FILE_SELECTION(
1872 gtk_file_selection_new ("Please select the image directory."));
1875 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1877 fsd->widget = selector;
1880 if (p->image_directory && *p->image_directory)
1881 gtk_file_selection_set_filename (selector, p->image_directory);
1883 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1884 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1886 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1887 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1889 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1890 GTK_SIGNAL_FUNC (browse_image_dir_close),
1893 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1895 gtk_window_set_modal (GTK_WINDOW (selector), True);
1896 gtk_widget_show (GTK_WIDGET (selector));
1901 settings_cb (GtkButton *button, gpointer user_data)
1903 state *s = global_state_kludge; /* I hate C so much... */
1904 int list_elt = selected_list_element (s);
1906 populate_demo_window (s, list_elt); /* reset the widget */
1907 populate_popup_window (s); /* create UI on popup window */
1908 gtk_widget_show (s->popup_widget);
1912 settings_sync_cmd_text (state *s)
1915 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1916 char *cmd_line = get_configurator_command_line (s->cdata);
1917 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1918 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1920 # endif /* HAVE_XML */
1924 settings_adv_cb (GtkButton *button, gpointer user_data)
1926 state *s = global_state_kludge; /* I hate C so much... */
1927 GtkNotebook *notebook =
1928 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1930 settings_sync_cmd_text (s);
1931 gtk_notebook_set_page (notebook, 1);
1935 settings_std_cb (GtkButton *button, gpointer user_data)
1937 state *s = global_state_kludge; /* I hate C so much... */
1938 GtkNotebook *notebook =
1939 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1941 /* Re-create UI to reflect the in-progress command-line settings. */
1942 populate_popup_window (s);
1944 gtk_notebook_set_page (notebook, 0);
1948 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1949 gint page_num, gpointer user_data)
1951 state *s = global_state_kludge; /* I hate C so much... */
1952 GtkWidget *adv = name_to_widget (s, "adv_button");
1953 GtkWidget *std = name_to_widget (s, "std_button");
1957 gtk_widget_show (adv);
1958 gtk_widget_hide (std);
1960 else if (page_num == 1)
1962 gtk_widget_hide (adv);
1963 gtk_widget_show (std);
1972 settings_cancel_cb (GtkButton *button, gpointer user_data)
1974 state *s = global_state_kludge; /* I hate C so much... */
1975 gtk_widget_hide (s->popup_widget);
1979 settings_ok_cb (GtkButton *button, gpointer user_data)
1981 state *s = global_state_kludge; /* I hate C so much... */
1982 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1983 int page = gtk_notebook_get_current_page (notebook);
1986 /* Regenerate the command-line from the widget contents before saving.
1987 But don't do this if we're looking at the command-line page already,
1988 or we will blow away what they typed... */
1989 settings_sync_cmd_text (s);
1991 flush_popup_changes_and_save (s);
1992 gtk_widget_hide (s->popup_widget);
1996 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1998 state *s = (state *) data;
1999 settings_cancel_cb (0, (gpointer) s);
2005 /* Populating the various widgets
2009 /* Returns the number of the last hack run by the server.
2012 server_current_hack (void)
2016 unsigned long nitems, bytesafter;
2017 unsigned char *dataP = 0;
2018 Display *dpy = GDK_DISPLAY();
2019 int hack_number = -1;
2021 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2022 XA_SCREENSAVER_STATUS,
2023 0, 3, False, XA_INTEGER,
2024 &type, &format, &nitems, &bytesafter,
2027 && type == XA_INTEGER
2031 CARD32 *data = (CARD32 *) dataP;
2032 hack_number = (int) data[2] - 1;
2035 if (dataP) XFree (dataP);
2041 /* Finds the number of the last hack to run, and makes that item be
2042 selected by default.
2045 scroll_to_current_hack (state *s)
2047 saver_preferences *p = &s->prefs;
2050 if (p->mode == ONE_HACK)
2051 hack_number = p->selected_hack;
2053 hack_number = server_current_hack ();
2055 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2057 int list_elt = s->hack_number_to_list_elt[hack_number];
2058 GtkWidget *list = name_to_widget (s, "list");
2059 force_list_select_item (s, list, list_elt, True);
2060 populate_demo_window (s, list_elt);
2066 on_path_p (const char *program)
2070 char *cmd = strdup (program);
2071 char *token = strchr (cmd, ' ');
2075 if (token) *token = 0;
2078 if (strchr (cmd, '/'))
2080 result = (0 == stat (cmd, &st));
2084 path = getenv("PATH");
2085 if (!path || !*path)
2089 path = strdup (path);
2090 token = strtok (path, ":");
2094 char *p2 = (char *) malloc (strlen (token) + L + 3);
2098 result = (0 == stat (p2, &st));
2101 token = strtok (0, ":");
2106 if (path) free (path);
2112 populate_hack_list (state *s)
2115 saver_preferences *p = &s->prefs;
2116 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2117 GtkListStore *model;
2118 GtkTreeSelection *selection;
2119 GtkCellRenderer *ren;
2123 g_object_get (G_OBJECT (list),
2128 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2129 g_object_set (G_OBJECT (list), "model", model, NULL);
2130 g_object_unref (model);
2132 ren = gtk_cell_renderer_toggle_new ();
2133 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2135 "active", COL_ENABLED,
2138 g_signal_connect (ren, "toggled",
2139 G_CALLBACK (list_checkbox_cb),
2142 ren = gtk_cell_renderer_text_new ();
2143 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2144 _("Screen Saver"), ren,
2148 g_signal_connect_after (list, "row_activated",
2149 G_CALLBACK (list_activated_cb),
2152 selection = gtk_tree_view_get_selection (list);
2153 g_signal_connect (selection, "changed",
2154 G_CALLBACK (list_select_changed_cb),
2159 for (i = 0; i < s->list_count; i++)
2161 int hack_number = s->list_elt_to_hack_number[i];
2162 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2164 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2166 if (!hack) continue;
2168 /* If we're to suppress uninstalled hacks, check $PATH now. */
2169 if (p->ignore_uninstalled_p && !available_p)
2172 pretty_name = (hack->name
2173 ? strdup (hack->name)
2174 : make_hack_name (hack->command));
2178 /* Make the text foreground be the color of insensitive widgets
2179 (but don't actually make it be insensitive, since we still
2180 want to be able to click on it.)
2182 GtkStyle *style = GTK_WIDGET (list)->style;
2183 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2184 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2185 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2187 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2188 /* " background=\"#%02X%02X%02X\"" */
2190 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2191 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2197 gtk_list_store_append (model, &iter);
2198 gtk_list_store_set (model, &iter,
2199 COL_ENABLED, hack->enabled_p,
2200 COL_NAME, pretty_name,
2205 #else /* !HAVE_GTK2 */
2207 saver_preferences *p = &s->prefs;
2208 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2210 for (i = 0; i < s->list_count; i++)
2212 int hack_number = s->list_elt_to_hack_number[i];
2213 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2215 /* A GtkList must contain only GtkListItems, but those can contain
2216 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2217 and a Label. We handle single and double click events on the
2218 line itself, for clicking on the text, but the interior checkbox
2219 also handles its own events.
2222 GtkWidget *line_hbox;
2223 GtkWidget *line_check;
2224 GtkWidget *line_label;
2226 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2228 if (!hack) continue;
2230 /* If we're to suppress uninstalled hacks, check $PATH now. */
2231 if (p->ignore_uninstalled_p && !available_p)
2234 pretty_name = (hack->name
2235 ? strdup (hack->name)
2236 : make_hack_name (hack->command));
2238 line = gtk_list_item_new ();
2239 line_hbox = gtk_hbox_new (FALSE, 0);
2240 line_check = gtk_check_button_new ();
2241 line_label = gtk_label_new (pretty_name);
2243 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2244 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2245 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2247 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2249 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2251 gtk_widget_show (line_check);
2252 gtk_widget_show (line_label);
2253 gtk_widget_show (line_hbox);
2254 gtk_widget_show (line);
2258 gtk_container_add (GTK_CONTAINER (list), line);
2259 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2260 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2263 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2264 GTK_SIGNAL_FUNC (list_checkbox_cb),
2267 gtk_widget_show (line);
2271 /* Make the widget be colored like insensitive widgets
2272 (but don't actually make it be insensitive, since we
2273 still want to be able to click on it.)
2275 GtkRcStyle *rc_style;
2278 gtk_widget_realize (GTK_WIDGET (line_label));
2280 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2281 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2283 rc_style = gtk_rc_style_new ();
2284 rc_style->fg[GTK_STATE_NORMAL] = fg;
2285 rc_style->bg[GTK_STATE_NORMAL] = bg;
2286 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2288 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2289 gtk_rc_style_unref (rc_style);
2293 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2294 GTK_SIGNAL_FUNC (list_select_cb),
2296 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2297 GTK_SIGNAL_FUNC (list_unselect_cb),
2299 #endif /* !HAVE_GTK2 */
2303 update_list_sensitivity (state *s)
2305 saver_preferences *p = &s->prefs;
2306 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2307 Bool checkable = (p->mode == RANDOM_HACKS);
2308 Bool blankable = (p->mode != DONT_BLANK);
2311 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2312 GtkWidget *use = name_to_widget (s, "use_col_frame");
2313 #endif /* HAVE_GTK2 */
2314 GtkWidget *scroller = name_to_widget (s, "scroller");
2315 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2316 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2319 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2320 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2321 #else /* !HAVE_GTK2 */
2322 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2323 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2325 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2326 #endif /* !HAVE_GTK2 */
2327 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2328 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2330 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2333 gtk_tree_view_column_set_visible (use, checkable);
2334 #else /* !HAVE_GTK2 */
2336 gtk_widget_show (use); /* the "Use" column header */
2338 gtk_widget_hide (use);
2342 GtkBin *line = GTK_BIN (kids->data);
2343 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2344 GtkWidget *line_check =
2345 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2348 gtk_widget_show (line_check);
2350 gtk_widget_hide (line_check);
2354 #endif /* !HAVE_GTK2 */
2359 populate_prefs_page (state *s)
2361 saver_preferences *p = &s->prefs;
2363 Bool can_lock_p = True;
2365 /* Disable all the "lock" controls if locking support was not provided
2366 at compile-time, or if running on MacOS. */
2367 # if defined(NO_LOCKING) || defined(__APPLE__)
2372 /* The file supports timeouts of less than a minute, but the GUI does
2373 not, so throttle the values to be at least one minute (since "0" is
2374 a bad rounding choice...)
2376 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2379 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2382 # define FMT_MINUTES(NAME,N) \
2383 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2385 # define FMT_SECONDS(NAME,N) \
2386 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2388 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2389 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2390 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2391 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2392 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2393 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2394 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2399 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2400 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2403 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2404 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2405 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2406 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2407 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2408 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2409 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2410 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2411 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2412 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2413 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2415 # undef TOGGLE_ACTIVE
2417 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2418 (p->image_directory ? p->image_directory : ""));
2419 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2421 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2424 /* Map the `saver_mode' enum to mode menu to values. */
2426 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2429 for (i = 0; i < countof(mode_menu_order); i++)
2430 if (mode_menu_order[i] == p->mode)
2432 gtk_option_menu_set_history (opt, i);
2433 update_list_sensitivity (s);
2437 Bool found_any_writable_cells = False;
2438 Bool dpms_supported = False;
2440 Display *dpy = GDK_DISPLAY();
2441 int nscreens = ScreenCount(dpy);
2443 for (i = 0; i < nscreens; i++)
2445 Screen *s = ScreenOfDisplay (dpy, i);
2446 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2448 found_any_writable_cells = True;
2453 #ifdef HAVE_XF86VMODE_GAMMA
2454 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2457 #ifdef HAVE_DPMS_EXTENSION
2459 int op = 0, event = 0, error = 0;
2460 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2461 dpms_supported = True;
2463 #endif /* HAVE_DPMS_EXTENSION */
2466 # define SENSITIZE(NAME,SENSITIVEP) \
2467 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2469 /* Blanking and Locking
2471 SENSITIZE ("lock_button", can_lock_p);
2472 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2473 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2477 SENSITIZE ("dpms_frame", dpms_supported);
2478 SENSITIZE ("dpms_button", dpms_supported);
2479 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2480 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2481 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2482 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2483 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2484 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2485 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2486 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2487 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2491 SENSITIZE ("cmap_frame", found_any_writable_cells);
2492 SENSITIZE ("install_button", found_any_writable_cells);
2493 SENSITIZE ("fade_button", found_any_writable_cells);
2494 SENSITIZE ("unfade_button", found_any_writable_cells);
2496 SENSITIZE ("fade_label", (found_any_writable_cells &&
2497 (p->fade_p || p->unfade_p)));
2498 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2499 (p->fade_p || p->unfade_p)));
2507 populate_popup_window (state *s)
2509 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2510 char *doc_string = 0;
2512 /* #### not in Gtk 1.2
2513 gtk_label_set_selectable (doc);
2519 free_conf_data (s->cdata);
2524 saver_preferences *p = &s->prefs;
2525 int list_elt = selected_list_element (s);
2526 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2527 ? s->list_elt_to_hack_number[list_elt]
2529 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2532 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2533 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2534 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2535 s->cdata = load_configurator (cmd_line, s->debug_p);
2536 if (s->cdata && s->cdata->widget)
2537 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2542 doc_string = (s->cdata
2543 ? s->cdata->description
2545 # else /* !HAVE_XML */
2546 doc_string = _("Descriptions not available: no XML support compiled in.");
2547 # endif /* !HAVE_XML */
2549 gtk_label_set_text (doc, (doc_string
2551 : _("No description available.")));
2556 sensitize_demo_widgets (state *s, Bool sensitive_p)
2558 const char *names1[] = { "demo", "settings" };
2559 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2560 "visual", "visual_combo" };
2562 for (i = 0; i < countof(names1); i++)
2564 GtkWidget *w = name_to_widget (s, names1[i]);
2565 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2567 for (i = 0; i < countof(names2); i++)
2569 GtkWidget *w = name_to_widget (s, names2[i]);
2570 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2575 /* Even though we've given these text fields a maximum number of characters,
2576 their default size is still about 30 characters wide -- so measure out
2577 a string in their font, and resize them to just fit that.
2580 fix_text_entry_sizes (state *s)
2584 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2585 const char * const spinbuttons[] = {
2586 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2587 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2588 "dpms_off_spinbutton",
2589 "-fade_spinbutton" };
2593 for (i = 0; i < countof(spinbuttons); i++)
2595 const char *n = spinbuttons[i];
2597 while (*n == '-') n++, cols--;
2598 w = GTK_WIDGET (name_to_widget (s, n));
2599 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2600 gtk_widget_set_usize (w, width, -2);
2603 /* Now fix the width of the combo box.
2605 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2606 w = GTK_COMBO (w)->entry;
2607 width = gdk_string_width (w->style->font, "PseudoColor___");
2608 gtk_widget_set_usize (w, width, -2);
2610 /* Now fix the width of the file entry text.
2612 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2613 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2614 gtk_widget_set_usize (w, width, -2);
2616 /* Now fix the width of the command line text.
2618 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2619 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2620 gtk_widget_set_usize (w, width, -2);
2624 /* Now fix the height of the list widget:
2625 make it default to being around 10 text-lines high instead of 4.
2627 w = GTK_WIDGET (name_to_widget (s, "list"));
2631 int leading = 3; /* approximate is ok... */
2635 PangoFontMetrics *pain =
2636 pango_context_get_metrics (gtk_widget_get_pango_context (w),
2637 w->style->font_desc,
2638 gtk_get_default_language ());
2639 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
2640 pango_font_metrics_get_descent (pain));
2641 #else /* !HAVE_GTK2 */
2642 height = w->style->font->ascent + w->style->font->descent;
2643 #endif /* !HAVE_GTK2 */
2647 height += border * 2;
2648 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2649 gtk_widget_set_usize (w, -2, height);
2656 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2659 static char *up_arrow_xpm[] = {
2682 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2683 the end of the array (Gtk 1.2.5.) */
2684 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2685 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2688 static char *down_arrow_xpm[] = {
2711 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2712 the end of the array (Gtk 1.2.5.) */
2713 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2714 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2718 pixmapify_button (state *s, int down_p)
2722 GtkWidget *pixmapwid;
2726 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2727 style = gtk_widget_get_style (w);
2729 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2730 &style->bg[GTK_STATE_NORMAL],
2732 ? (gchar **) down_arrow_xpm
2733 : (gchar **) up_arrow_xpm));
2734 pixmapwid = gtk_pixmap_new (pixmap, mask);
2735 gtk_widget_show (pixmapwid);
2736 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2737 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2741 map_next_button_cb (GtkWidget *w, gpointer user_data)
2743 state *s = (state *) user_data;
2744 pixmapify_button (s, 1);
2748 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2750 state *s = (state *) user_data;
2751 pixmapify_button (s, 0);
2753 #endif /* !HAVE_GTK2 */
2756 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2760 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2761 GtkAllocation *allocation,
2765 GtkWidgetAuxInfo *aux_info;
2767 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2769 aux_info->width = allocation->width;
2770 aux_info->height = -2;
2774 gtk_widget_size_request (label, &req);
2778 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2781 eschew_gtk_lossage (GtkLabel *label)
2783 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2784 aux_info->width = GTK_WIDGET (label)->allocation.width;
2785 aux_info->height = -2;
2789 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2791 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2792 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2795 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2797 gtk_widget_queue_resize (GTK_WIDGET (label));
2802 populate_demo_window (state *s, int list_elt)
2804 saver_preferences *p = &s->prefs;
2807 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2808 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2809 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2810 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2811 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2813 if (p->mode == BLANK_ONLY)
2816 pretty_name = strdup (_("Blank Screen"));
2817 schedule_preview (s, 0);
2819 else if (p->mode == DONT_BLANK)
2822 pretty_name = strdup (_("Screen Saver Disabled"));
2823 schedule_preview (s, 0);
2827 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2828 ? s->list_elt_to_hack_number[list_elt]
2830 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2834 ? strdup (hack->name)
2835 : make_hack_name (hack->command))
2839 schedule_preview (s, hack->command);
2841 schedule_preview (s, 0);
2845 pretty_name = strdup (_("Preview"));
2847 gtk_frame_set_label (frame1, _(pretty_name));
2848 gtk_frame_set_label (frame2, _(pretty_name));
2850 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2851 gtk_entry_set_position (cmd, 0);
2855 sprintf (title, _("%s: %.100s Settings"),
2856 progclass, (pretty_name ? pretty_name : "???"));
2857 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2860 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2862 ? (hack->visual && *hack->visual
2867 sensitize_demo_widgets (s, (hack ? True : False));
2869 if (pretty_name) free (pretty_name);
2871 ensure_selected_item_visible (list);
2873 s->_selected_list_element = list_elt;
2878 widget_deleter (GtkWidget *widget, gpointer data)
2880 /* #### Well, I want to destroy these widgets, but if I do that, they get
2881 referenced again, and eventually I get a SEGV. So instead of
2882 destroying them, I'll just hide them, and leak a bunch of memory
2883 every time the disk file changes. Go go go Gtk!
2885 #### Ok, that's a lie, I get a crash even if I just hide the widget
2886 and don't ever delete it. Fuck!
2889 gtk_widget_destroy (widget);
2891 gtk_widget_hide (widget);
2896 static char **sort_hack_cmp_names_kludge;
2898 sort_hack_cmp (const void *a, const void *b)
2904 int aa = *(int *) a;
2905 int bb = *(int *) b;
2906 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
2907 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
2908 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
2914 initialize_sort_map (state *s)
2916 saver_preferences *p = &s->prefs;
2919 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2920 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2921 if (s->hacks_available_p) free (s->hacks_available_p);
2923 s->list_elt_to_hack_number = (int *)
2924 calloc (sizeof(int), p->screenhacks_count + 1);
2925 s->hack_number_to_list_elt = (int *)
2926 calloc (sizeof(int), p->screenhacks_count + 1);
2927 s->hacks_available_p = (Bool *)
2928 calloc (sizeof(Bool), p->screenhacks_count + 1);
2930 /* Check which hacks actually exist on $PATH
2932 for (i = 0; i < p->screenhacks_count; i++)
2934 screenhack *hack = p->screenhacks[i];
2935 s->hacks_available_p[i] = on_path_p (hack->command);
2938 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
2942 for (i = 0; i < p->screenhacks_count; i++)
2944 if (!p->ignore_uninstalled_p ||
2945 s->hacks_available_p[i])
2946 s->list_elt_to_hack_number[j++] = i;
2950 for (; j < p->screenhacks_count; j++)
2951 s->list_elt_to_hack_number[j] = -1;
2954 /* Generate list of sortable names (once)
2956 sort_hack_cmp_names_kludge = (char **)
2957 calloc (sizeof(char *), p->screenhacks_count);
2958 for (i = 0; i < p->screenhacks_count; i++)
2960 screenhack *hack = p->screenhacks[i];
2961 char *name = (hack->name && *hack->name
2962 ? strdup (hack->name)
2963 : make_hack_name (hack->command));
2965 for (str = name; *str; str++)
2966 *str = tolower(*str);
2967 sort_hack_cmp_names_kludge[i] = name;
2970 /* Sort list->hack map alphabetically
2972 qsort (s->list_elt_to_hack_number,
2973 p->screenhacks_count,
2974 sizeof(*s->list_elt_to_hack_number),
2979 for (i = 0; i < p->screenhacks_count; i++)
2980 free (sort_hack_cmp_names_kludge[i]);
2981 free (sort_hack_cmp_names_kludge);
2982 sort_hack_cmp_names_kludge = 0;
2984 /* Build inverse table */
2985 for (i = 0; i < p->screenhacks_count; i++)
2986 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2991 maybe_reload_init_file (state *s)
2993 saver_preferences *p = &s->prefs;
2996 static Bool reentrant_lock = False;
2997 if (reentrant_lock) return 0;
2998 reentrant_lock = True;
3000 if (init_file_changed_p (p))
3002 const char *f = init_file_name();
3007 if (!f || !*f) return 0;
3008 b = (char *) malloc (strlen(f) + 1024);
3011 "file \"%s\" has changed, reloading.\n"),
3013 warning_dialog (s->toplevel_widget, b, False, 100);
3017 initialize_sort_map (s);
3019 list_elt = selected_list_element (s);
3020 list = name_to_widget (s, "list");
3021 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3022 populate_hack_list (s);
3023 force_list_select_item (s, list, list_elt, True);
3024 populate_prefs_page (s);
3025 populate_demo_window (s, list_elt);
3026 ensure_selected_item_visible (list);
3031 reentrant_lock = False;
3037 /* Making the preview window have the right X visual (so that GL works.)
3040 static Visual *get_best_gl_visual (state *);
3043 x_visual_to_gdk_visual (Visual *xv)
3045 GList *gvs = gdk_list_visuals();
3046 if (!xv) return gdk_visual_get_system();
3047 for (; gvs; gvs = gvs->next)
3049 GdkVisual *gv = (GdkVisual *) gvs->data;
3050 if (xv == GDK_VISUAL_XVISUAL (gv))
3053 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3054 blurb(), (unsigned long) xv->visualid);
3059 clear_preview_window (state *s)
3064 if (!s->toplevel_widget) return; /* very early */
3065 p = name_to_widget (s, "preview");
3068 if (!window) return;
3070 /* Flush the widget background down into the window, in case a subproc
3072 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3073 gdk_window_clear (window);
3076 int list_elt = selected_list_element (s);
3077 int hack_number = (list_elt >= 0
3078 ? s->list_elt_to_hack_number[list_elt]
3080 Bool available_p = (hack_number >= 0
3081 ? s->hacks_available_p [hack_number]
3084 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3085 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3086 (s->running_preview_error_p
3087 ? (available_p ? 1 : 2)
3089 #else /* !HAVE_GTK2 */
3090 if (s->running_preview_error_p)
3092 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3093 const char * const lines2[] = { N_("Not"), N_("Installed") };
3094 int nlines = countof(lines1);
3095 int lh = p->style->font->ascent + p->style->font->descent;
3099 const char * const *lines = (available_p ? lines1 : lines2);
3101 gdk_window_get_size (window, &w, &h);
3102 y = (h - (lh * nlines)) / 2;
3103 y += p->style->font->ascent;
3104 for (i = 0; i < nlines; i++)
3106 int sw = gdk_string_width (p->style->font, _(lines[i]));
3107 int x = (w - sw) / 2;
3108 gdk_draw_string (window, p->style->font,
3109 p->style->fg_gc[GTK_STATE_NORMAL],
3114 #endif /* !HAVE_GTK2 */
3122 reset_preview_window (state *s)
3124 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3125 when you kill one and re-start another on the same window. So maybe
3126 it's best to just always destroy and recreate the preview window
3127 when changing hacks, instead of always trying to reuse the same one?
3129 GtkWidget *pr = name_to_widget (s, "preview");
3130 if (GTK_WIDGET_REALIZED (pr))
3132 Window oid = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3134 gtk_widget_hide (pr);
3135 gtk_widget_unrealize (pr);
3136 gtk_widget_realize (pr);
3137 gtk_widget_show (pr);
3138 id = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3140 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3148 fix_preview_visual (state *s)
3150 GtkWidget *widget = name_to_widget (s, "preview");
3151 Visual *xvisual = get_best_gl_visual (s);
3152 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3153 GdkVisual *dvisual = gdk_visual_get_system();
3154 GdkColormap *cmap = (visual == dvisual
3155 ? gdk_colormap_get_system ()
3156 : gdk_colormap_new (visual, False));
3159 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3160 (visual == dvisual ? "default" : "non-default"),
3161 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3163 if (!GTK_WIDGET_REALIZED (widget) ||
3164 gtk_widget_get_visual (widget) != visual)
3166 gtk_widget_unrealize (widget);
3167 gtk_widget_set_visual (widget, visual);
3168 gtk_widget_set_colormap (widget, cmap);
3169 gtk_widget_realize (widget);
3172 /* Set the Widget colors to be white-on-black. */
3174 GdkWindow *window = widget->window;
3175 GtkStyle *style = gtk_style_copy (widget->style);
3176 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3177 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3178 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3179 GdkGC *fgc = gdk_gc_new(window);
3180 GdkGC *bgc = gdk_gc_new(window);
3181 if (!gdk_color_white (cmap, fg)) abort();
3182 if (!gdk_color_black (cmap, bg)) abort();
3183 gdk_gc_set_foreground (fgc, fg);
3184 gdk_gc_set_background (fgc, bg);
3185 gdk_gc_set_foreground (bgc, bg);
3186 gdk_gc_set_background (bgc, fg);
3187 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3188 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3189 gtk_widget_set_style (widget, style);
3191 /* For debugging purposes, put a title on the window (so that
3192 it can be easily found in the output of "xwininfo -tree".)
3194 gdk_window_set_title (window, "Preview");
3197 gtk_widget_show (widget);
3205 subproc_pretty_name (state *s)
3207 if (s->running_preview_cmd)
3209 char *ps = strdup (s->running_preview_cmd);
3210 char *ss = strchr (ps, ' ');
3212 ss = strrchr (ps, '/');
3223 return strdup ("???");
3228 reap_zombies (state *s)
3230 int wait_status = 0;
3232 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3236 if (pid == s->running_preview_pid)
3238 char *ss = subproc_pretty_name (s);
3239 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3240 (unsigned long) pid, ss);
3244 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3245 (unsigned long) pid);
3251 /* Mostly lifted from driver/subprocs.c */
3253 get_best_gl_visual (state *s)
3255 Display *dpy = GDK_DISPLAY();
3264 av[ac++] = "xscreensaver-gl-helper";
3269 perror ("error creating pipe:");
3276 switch ((int) (forked = fork ()))
3280 sprintf (buf, "%s: couldn't fork", blurb());
3288 close (in); /* don't need this one */
3289 close (ConnectionNumber (dpy)); /* close display fd */
3291 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3293 perror ("could not dup() a new stdout:");
3297 execvp (av[0], av); /* shouldn't return. */
3299 if (errno != ENOENT)
3301 /* Ignore "no such file or directory" errors, unless verbose.
3302 Issue all other exec errors, though. */
3303 sprintf (buf, "%s: running %s", blurb(), av[0]);
3307 /* Note that one must use _exit() instead of exit() in procs forked
3308 off of Gtk programs -- Gtk installs an atexit handler that has a
3309 copy of the X connection (which we've already closed, for safety.)
3310 If one uses exit() instead of _exit(), then one sometimes gets a
3311 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3313 _exit (1); /* exits fork */
3319 int wait_status = 0;
3321 FILE *f = fdopen (in, "r");
3325 close (out); /* don't need this one */
3328 fgets (buf, sizeof(buf)-1, f);
3331 /* Wait for the child to die. */
3332 waitpid (-1, &wait_status, 0);
3334 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3340 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3346 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3348 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3349 blurb(), av[0], result);
3361 kill_preview_subproc (state *s, Bool reset_p)
3363 s->running_preview_error_p = False;
3366 clear_preview_window (s);
3368 if (s->subproc_check_timer_id)
3370 gtk_timeout_remove (s->subproc_check_timer_id);
3371 s->subproc_check_timer_id = 0;
3372 s->subproc_check_countdown = 0;
3375 if (s->running_preview_pid)
3377 int status = kill (s->running_preview_pid, SIGTERM);
3378 char *ss = subproc_pretty_name (s);
3385 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3386 blurb(), (unsigned long) s->running_preview_pid, ss);
3391 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3392 blurb(), (unsigned long) s->running_preview_pid, ss);
3396 else if (s->debug_p)
3397 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3398 (unsigned long) s->running_preview_pid, ss);
3401 s->running_preview_pid = 0;
3402 if (s->running_preview_cmd) free (s->running_preview_cmd);
3403 s->running_preview_cmd = 0;
3410 reset_preview_window (s);
3411 clear_preview_window (s);
3416 /* Immediately and unconditionally launches the given process,
3417 after appending the -window-id option; sets running_preview_pid.
3420 launch_preview_subproc (state *s)
3422 saver_preferences *p = &s->prefs;
3426 const char *cmd = s->desired_preview_cmd;
3428 GtkWidget *pr = name_to_widget (s, "preview");
3431 reset_preview_window (s);
3433 window = pr->window;
3435 s->running_preview_error_p = False;
3437 if (s->preview_suppressed_p)
3439 kill_preview_subproc (s, False);
3443 new_cmd = malloc (strlen (cmd) + 40);
3445 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3448 /* No window id? No command to run. */
3454 strcpy (new_cmd, cmd);
3455 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3459 kill_preview_subproc (s, False);
3462 s->running_preview_error_p = True;
3463 clear_preview_window (s);
3467 switch ((int) (forked = fork ()))
3472 sprintf (buf, "%s: couldn't fork", blurb());
3474 s->running_preview_error_p = True;
3480 close (ConnectionNumber (GDK_DISPLAY()));
3482 hack_subproc_environment (id, s->debug_p);
3484 usleep (250000); /* pause for 1/4th second before launching, to give
3485 the previous program time to die and flush its X
3486 buffer, so we don't get leftover turds on the
3489 exec_command (p->shell, new_cmd, p->nice_inferior);
3490 /* Don't bother printing an error message when we are unable to
3491 exec subprocesses; we handle that by polling the pid later.
3493 Note that one must use _exit() instead of exit() in procs forked
3494 off of Gtk programs -- Gtk installs an atexit handler that has a
3495 copy of the X connection (which we've already closed, for safety.)
3496 If one uses exit() instead of _exit(), then one sometimes gets a
3497 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3499 _exit (1); /* exits child fork */
3504 if (s->running_preview_cmd) free (s->running_preview_cmd);
3505 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3506 s->running_preview_pid = forked;
3510 char *ss = subproc_pretty_name (s);
3511 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3512 (unsigned long) forked, ss);
3519 schedule_preview_check (s);
3522 if (new_cmd) free (new_cmd);
3527 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3530 hack_environment (state *s)
3532 static const char *def_path =
3533 # ifdef DEFAULT_PATH_PREFIX
3534 DEFAULT_PATH_PREFIX;
3539 Display *dpy = GDK_DISPLAY();
3540 const char *odpy = DisplayString (dpy);
3541 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3542 strcpy (ndpy, "DISPLAY=");
3543 strcat (ndpy, odpy);
3548 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3550 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3551 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3552 So we must leak it (and/or the previous setting). Yay.
3555 if (def_path && *def_path)
3557 const char *opath = getenv("PATH");
3558 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3559 strcpy (npath, "PATH=");
3560 strcat (npath, def_path);
3561 strcat (npath, ":");
3562 strcat (npath, opath);
3566 /* do not free(npath) -- see above */
3569 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3575 hack_subproc_environment (Window preview_window_id, Bool debug_p)
3577 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
3578 necessary yet, but it will make programs work if we had invoked
3579 them with "-root" and not with "-window-id" -- which, of course,
3582 char *nssw = (char *) malloc (40);
3583 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
3585 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
3586 any more, right? It's not Posix, but everyone seems to have it. */
3591 fprintf (stderr, "%s: %s\n", blurb(), nssw);
3593 /* do not free(nssw) -- see above */
3597 /* Called from a timer:
3598 Launches the currently-chosen subprocess, if it's not already running.
3599 If there's a different process running, kills it.
3602 update_subproc_timer (gpointer data)
3604 state *s = (state *) data;
3605 if (! s->desired_preview_cmd)
3606 kill_preview_subproc (s, True);
3607 else if (!s->running_preview_cmd ||
3608 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3609 launch_preview_subproc (s);
3611 s->subproc_timer_id = 0;
3612 return FALSE; /* do not re-execute timer */
3616 /* Call this when you think you might want a preview process running.
3617 It will set a timer that will actually launch that program a second
3618 from now, if you haven't changed your mind (to avoid double-click
3619 spazzing, etc.) `cmd' may be null meaning "no process".
3622 schedule_preview (state *s, const char *cmd)
3624 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3629 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3631 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3634 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3635 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3637 if (s->subproc_timer_id)
3638 gtk_timeout_remove (s->subproc_timer_id);
3639 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3643 /* Called from a timer:
3644 Checks to see if the subproc that should be running, actually is.
3647 check_subproc_timer (gpointer data)
3649 state *s = (state *) data;
3650 Bool again_p = True;
3652 if (s->running_preview_error_p || /* already dead */
3653 s->running_preview_pid <= 0)
3661 status = kill (s->running_preview_pid, 0);
3662 if (status < 0 && errno == ESRCH)
3663 s->running_preview_error_p = True;
3667 char *ss = subproc_pretty_name (s);
3668 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3669 (unsigned long) s->running_preview_pid, ss,
3670 (s->running_preview_error_p ? "dead" : "alive"));
3674 if (s->running_preview_error_p)
3676 clear_preview_window (s);
3681 /* Otherwise, it's currently alive. We might be checking again, or we
3682 might be satisfied. */
3684 if (--s->subproc_check_countdown <= 0)
3688 return TRUE; /* re-execute timer */
3691 s->subproc_check_timer_id = 0;
3692 s->subproc_check_countdown = 0;
3693 return FALSE; /* do not re-execute timer */
3698 /* Call this just after launching a subprocess.
3699 This sets a timer that will, five times a second for two seconds,
3700 check whether the program is still running. The assumption here
3701 is that if the process didn't stay up for more than a couple of
3702 seconds, then either the program doesn't exist, or it doesn't
3703 take a -window-id argument.
3706 schedule_preview_check (state *s)
3712 fprintf (stderr, "%s: scheduling check\n", blurb());
3714 if (s->subproc_check_timer_id)
3715 gtk_timeout_remove (s->subproc_check_timer_id);
3716 s->subproc_check_timer_id =
3717 gtk_timeout_add (1000 / ticks,
3718 check_subproc_timer, (gpointer) s);
3719 s->subproc_check_countdown = ticks * seconds;
3724 screen_blanked_p (void)
3728 unsigned long nitems, bytesafter;
3729 unsigned char *dataP = 0;
3730 Display *dpy = GDK_DISPLAY();
3731 Bool blanked_p = False;
3733 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3734 XA_SCREENSAVER_STATUS,
3735 0, 3, False, XA_INTEGER,
3736 &type, &format, &nitems, &bytesafter,
3739 && type == XA_INTEGER
3743 Atom *data = (Atom *) dataP;
3744 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3747 if (dataP) XFree (dataP);
3752 /* Wake up every now and then and see if the screen is blanked.
3753 If it is, kill off the small-window demo -- no point in wasting
3754 cycles by running two screensavers at once...
3757 check_blanked_timer (gpointer data)
3759 state *s = (state *) data;
3760 Bool blanked_p = screen_blanked_p ();
3761 if (blanked_p && s->running_preview_pid)
3764 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3765 kill_preview_subproc (s, True);
3768 return True; /* re-execute timer */
3772 /* Setting window manager icon
3776 init_icon (GdkWindow *window)
3778 GdkBitmap *mask = 0;
3781 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3782 (gchar **) logo_50_xpm);
3784 gdk_window_set_icon (window, 0, pixmap, mask);
3788 /* The main demo-mode command loop.
3793 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3794 XrmRepresentation *type, XrmValue *value, XPointer closure)
3797 for (i = 0; quarks[i]; i++)
3799 if (bindings[i] == XrmBindTightly)
3800 fprintf (stderr, (i == 0 ? "" : "."));
3801 else if (bindings[i] == XrmBindLoosely)
3802 fprintf (stderr, "*");
3804 fprintf (stderr, " ??? ");
3805 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3808 fprintf (stderr, ": %s\n", (char *) value->addr);
3816 the_network_is_not_the_computer (state *s)
3818 Display *dpy = GDK_DISPLAY();
3819 char *rversion = 0, *ruser = 0, *rhost = 0;
3820 char *luser, *lhost;
3822 struct passwd *p = getpwuid (getuid ());
3823 const char *d = DisplayString (dpy);
3825 # if defined(HAVE_UNAME)
3827 if (uname (&uts) < 0)
3828 lhost = "<UNKNOWN>";
3830 lhost = uts.nodename;
3832 strcpy (lhost, getenv("SYS$NODE"));
3833 # else /* !HAVE_UNAME && !VMS */
3834 strcat (lhost, "<UNKNOWN>");
3835 # endif /* !HAVE_UNAME && !VMS */
3837 if (p && p->pw_name)
3842 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3844 /* Make a buffer that's big enough for a number of copies of all the
3845 strings, plus some. */
3846 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3847 (ruser ? strlen(ruser) : 0) +
3848 (rhost ? strlen(rhost) : 0) +
3855 if (!rversion || !*rversion)
3859 "The XScreenSaver daemon doesn't seem to be running\n"
3860 "on display \"%s\". Launch it now?"),
3863 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3865 /* Warn that the two processes are running as different users.
3869 "%s is running as user \"%s\" on host \"%s\".\n"
3870 "But the xscreensaver managing display \"%s\"\n"
3871 "is running as user \"%s\" on host \"%s\".\n"
3873 "Since they are different users, they won't be reading/writing\n"
3874 "the same ~/.xscreensaver file, so %s isn't\n"
3875 "going to work right.\n"
3877 "You should either re-run %s as \"%s\", or re-run\n"
3878 "xscreensaver as \"%s\".\n"
3880 "Restart the xscreensaver daemon now?\n"),
3881 progname, luser, lhost,
3883 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3885 progname, (ruser ? ruser : "???"),
3888 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3890 /* Warn that the two processes are running on different hosts.
3894 "%s is running as user \"%s\" on host \"%s\".\n"
3895 "But the xscreensaver managing display \"%s\"\n"
3896 "is running as user \"%s\" on host \"%s\".\n"
3898 "If those two machines don't share a file system (that is,\n"
3899 "if they don't see the same ~%s/.xscreensaver file) then\n"
3900 "%s won't work right.\n"
3902 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3903 progname, luser, lhost,
3905 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3910 else if (!!strcmp (rversion, s->short_version))
3912 /* Warn that the version numbers don't match.
3916 "This is %s version %s.\n"
3917 "But the xscreensaver managing display \"%s\"\n"
3918 "is version %s. This could cause problems.\n"
3920 "Restart the xscreensaver daemon now?\n"),
3921 progname, s->short_version,
3928 warning_dialog (s->toplevel_widget, msg, True, 1);
3930 if (rversion) free (rversion);
3931 if (ruser) free (ruser);
3932 if (rhost) free (rhost);
3937 /* We use this error handler so that X errors are preceeded by the name
3938 of the program that generated them.
3941 demo_ehandler (Display *dpy, XErrorEvent *error)
3943 state *s = global_state_kludge; /* I hate C so much... */
3944 fprintf (stderr, "\nX error in %s:\n", blurb());
3945 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3946 kill_preview_subproc (s, False);
3952 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3953 of the program that generated them; and also that we can ignore one
3954 particular bogus error message that Gdk madly spews.
3957 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3958 const gchar *message, gpointer user_data)
3960 /* Ignore the message "Got event for unknown window: 0x...".
3961 Apparently some events are coming in for the xscreensaver window
3962 (presumably reply events related to the ClientMessage) and Gdk
3963 feels the need to complain about them. So, just suppress any
3964 messages that look like that one.
3966 if (strstr (message, "unknown window"))
3969 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3970 (log_domain ? log_domain : progclass),
3971 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3972 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3973 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3974 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3975 log_level == G_LOG_LEVEL_INFO ? "info" :
3976 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3978 ((!*message || message[strlen(message)-1] != '\n')
3984 __extension__ /* shut up about "string length is greater than the length
3985 ISO C89 compilers are required to support" when including
3989 static char *defaults[] = {
3990 #include "XScreenSaver_ad.h"
3995 #ifdef HAVE_CRAPPLET
3996 static struct poptOption crapplet_options[] = {
3997 {NULL, '\0', 0, NULL, 0}
3999 #endif /* HAVE_CRAPPLET */
4002 const char *usage = "[--display dpy] [--prefs]"
4003 # ifdef HAVE_CRAPPLET
4006 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4009 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4011 state *s = (state *) user_data;
4012 Boolean oi = s->initializing_p;
4013 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4014 s->initializing_p = True;
4015 eschew_gtk_lossage (label);
4016 s->initializing_p = oi;
4022 print_widget_tree (GtkWidget *w, int depth)
4025 for (i = 0; i < depth; i++)
4026 fprintf (stderr, " ");
4027 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4029 if (GTK_IS_LIST (w))
4031 for (i = 0; i < depth+1; i++)
4032 fprintf (stderr, " ");
4033 fprintf (stderr, "...list kids...\n");
4035 else if (GTK_IS_CONTAINER (w))
4037 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4040 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4048 delayed_scroll_kludge (gpointer data)
4050 state *s = (state *) data;
4051 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4052 ensure_selected_item_visible (w);
4054 /* Oh, this is just fucking lovely, too. */
4055 w = GTK_WIDGET (name_to_widget (s, "preview"));
4056 gtk_widget_hide (w);
4057 gtk_widget_show (w);
4059 return FALSE; /* do not re-execute timer */
4065 create_xscreensaver_demo (void)
4069 nb = name_to_widget (global_state_kludge, "preview_notebook");
4070 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4072 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4076 create_xscreensaver_settings_dialog (void)
4080 box = name_to_widget (global_state_kludge, "dialog_action_area");
4082 w = name_to_widget (global_state_kludge, "adv_button");
4083 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4085 w = name_to_widget (global_state_kludge, "std_button");
4086 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4088 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4091 #endif /* HAVE_GTK2 */
4094 main (int argc, char **argv)
4098 saver_preferences *p;
4102 Widget toplevel_shell;
4103 char *real_progname = argv[0];
4106 Bool crapplet_p = False;
4110 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4111 textdomain (GETTEXT_PACKAGE);
4114 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4115 # else /* !HAVE_GTK2 */
4116 if (!setlocale (LC_ALL, ""))
4117 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4118 # endif /* !HAVE_GTK2 */
4120 #endif /* ENABLE_NLS */
4122 str = strrchr (real_progname, '/');
4123 if (str) real_progname = str+1;
4126 memset (s, 0, sizeof(*s));
4127 s->initializing_p = True;
4130 global_state_kludge = s; /* I hate C so much... */
4132 progname = real_progname;
4134 s->short_version = (char *) malloc (5);
4135 memcpy (s->short_version, screensaver_id + 17, 4);
4136 s->short_version [4] = 0;
4139 /* Register our error message logger for every ``log domain'' known.
4140 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4141 for all of the domains that seem to be in use.
4144 const char * const domains[] = { 0,
4145 "Gtk", "Gdk", "GLib", "GModule",
4146 "GThread", "Gnome", "GnomeUI" };
4147 for (i = 0; i < countof(domains); i++)
4148 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4151 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4154 const char *dir = DEFAULT_ICONDIR;
4155 if (*dir) add_pixmap_directory (dir);
4157 # endif /* !HAVE_GTK2 */
4158 #endif /* DEFAULT_ICONDIR */
4160 /* This is gross, but Gtk understands --display and not -display...
4162 for (i = 1; i < argc; i++)
4163 if (argv[i][0] && argv[i][1] &&
4164 !strncmp(argv[i], "-display", strlen(argv[i])))
4165 argv[i] = "--display";
4168 /* We need to parse this arg really early... Sigh. */
4169 for (i = 1; i < argc; i++)
4172 (!strcmp(argv[i], "--crapplet") ||
4173 !strcmp(argv[i], "--capplet")))
4175 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4178 for (j = i; j < argc; j++) /* remove it from the list */
4179 argv[j] = argv[j+1];
4181 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4182 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4184 fprintf (stderr, "%s: %s\n", real_progname, usage);
4186 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4189 (!strcmp(argv[i], "--debug") ||
4190 !strcmp(argv[i], "-debug") ||
4191 !strcmp(argv[i], "-d")))
4195 for (j = i; j < argc; j++) /* remove it from the list */
4196 argv[j] = argv[j+1];
4203 (!strcmp(argv[i], "-geometry") ||
4204 !strcmp(argv[i], "-geom") ||
4205 !strcmp(argv[i], "-geo") ||
4206 !strcmp(argv[i], "-g")))
4210 for (j = i; j < argc; j++) /* remove them from the list */
4211 argv[j] = argv[j+2];
4218 (!strcmp(argv[i], "--configdir")))
4222 hack_configuration_path = argv[i+1];
4223 for (j = i; j < argc; j++) /* remove them from the list */
4224 argv[j] = argv[j+2];
4228 if (0 != stat (hack_configuration_path, &st))
4231 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4235 else if (!S_ISDIR (st.st_mode))
4237 fprintf (stderr, "%s: not a directory: %s\n",
4238 blurb(), hack_configuration_path);
4246 fprintf (stderr, "%s: using config directory \"%s\"\n",
4247 progname, hack_configuration_path);
4250 /* Let Gtk open the X connection, then initialize Xt to use that
4251 same connection. Doctor Frankenstein would be proud.
4253 # ifdef HAVE_CRAPPLET
4256 GnomeClient *client;
4257 GnomeClientFlags flags = 0;
4259 int init_results = gnome_capplet_init ("screensaver-properties",
4261 argc, argv, NULL, 0, NULL);
4263 0 upon successful initialization;
4264 1 if --init-session-settings was passed on the cmdline;
4265 2 if --ignore was passed on the cmdline;
4268 So the 1 signifies just to init the settings, and quit, basically.
4269 (Meaning launch the xscreensaver daemon.)
4272 if (init_results < 0)
4275 g_error ("An initialization error occurred while "
4276 "starting xscreensaver-capplet.\n");
4278 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4279 real_progname, init_results);
4284 client = gnome_master_client ();
4287 flags = gnome_client_get_flags (client);
4289 if (flags & GNOME_CLIENT_IS_CONNECTED)
4292 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4293 gnome_client_get_id (client));
4296 char *session_args[20];
4298 session_args[i++] = real_progname;
4299 session_args[i++] = "--capplet";
4300 session_args[i++] = "--init-session-settings";
4301 session_args[i] = 0;
4302 gnome_client_set_priority (client, 20);
4303 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4304 gnome_client_set_restart_command (client, i, session_args);
4308 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4311 gnome_client_flush (client);
4314 if (init_results == 1)
4316 system ("xscreensaver -nosplash &");
4322 # endif /* HAVE_CRAPPLET */
4324 gtk_init (&argc, &argv);
4328 /* We must read exactly the same resources as xscreensaver.
4329 That means we must have both the same progclass *and* progname,
4330 at least as far as the resource database is concerned. So,
4331 put "xscreensaver" in argv[0] while initializing Xt.
4333 argv[0] = "xscreensaver";
4337 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4339 XtToolkitInitialize ();
4340 app = XtCreateApplicationContext ();
4341 dpy = GDK_DISPLAY();
4342 XtAppSetFallbackResources (app, defaults);
4343 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4344 toplevel_shell = XtAppCreateShell (progname, progclass,
4345 applicationShellWidgetClass,
4348 dpy = XtDisplay (toplevel_shell);
4349 db = XtDatabase (dpy);
4350 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4351 XSetErrorHandler (demo_ehandler);
4353 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4354 signal (SIGPIPE, SIG_IGN);
4356 /* After doing Xt-style command-line processing, complain about any
4357 unrecognized command-line arguments.
4359 for (i = 1; i < argc; i++)
4361 char *str = argv[i];
4362 if (str[0] == '-' && str[1] == '-')
4364 if (!strcmp (str, "-prefs"))
4366 else if (crapplet_p)
4367 /* There are lots of random args that we don't care about when we're
4368 started as a crapplet, so just ignore unknown args in that case. */
4372 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4374 fprintf (stderr, "%s: %s\n", real_progname, usage);
4379 /* Load the init file, which may end up consulting the X resource database
4380 and the site-wide app-defaults file. Note that at this point, it's
4381 important that `progname' be "xscreensaver", rather than whatever
4386 hack_environment (s); /* must be before initialize_sort_map() */
4389 initialize_sort_map (s);
4391 /* Now that Xt has been initialized, and the resources have been read,
4392 we can set our `progname' variable to something more in line with
4395 progname = real_progname;
4399 /* Print out all the resources we read. */
4401 XrmName name = { 0 };
4402 XrmClass class = { 0 };
4404 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4410 /* Intern the atoms that xscreensaver_command() needs.
4412 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4413 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4414 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4415 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4416 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4417 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4418 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4419 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4420 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4421 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4422 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4423 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4424 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4427 /* Create the window and all its widgets.
4429 s->base_widget = create_xscreensaver_demo ();
4430 s->popup_widget = create_xscreensaver_settings_dialog ();
4431 s->toplevel_widget = s->base_widget;
4434 /* Set the main window's title. */
4436 char *base_title = _("Screensaver Preferences");
4437 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4438 char *s1, *s2, *s3, *s4;
4439 s1 = (char *) strchr(v, ' '); s1++;
4440 s2 = (char *) strchr(s1, ' ');
4441 s3 = (char *) strchr(v, '('); s3++;
4442 s4 = (char *) strchr(s3, ')');
4446 window_title = (char *) malloc (strlen (base_title) +
4447 strlen (progclass) +
4448 strlen (s1) + strlen (s3) +
4450 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4451 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4452 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4456 /* Adjust the (invisible) notebooks on the popup dialog... */
4458 GtkNotebook *notebook =
4459 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4460 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4464 gtk_widget_hide (std);
4465 # else /* !HAVE_XML */
4466 /* Make the advanced page be the only one available. */
4467 gtk_widget_set_sensitive (std, False);
4468 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4469 gtk_widget_hide (std);
4471 # endif /* !HAVE_XML */
4473 gtk_notebook_set_page (notebook, page);
4474 gtk_notebook_set_show_tabs (notebook, False);
4477 /* Various other widget initializations...
4479 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4480 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4482 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4483 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4486 populate_hack_list (s);
4487 populate_prefs_page (s);
4488 sensitize_demo_widgets (s, False);
4489 fix_text_entry_sizes (s);
4490 scroll_to_current_hack (s);
4492 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4493 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4497 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4498 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4500 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4501 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4503 #endif /* !HAVE_GTK2 */
4505 /* Hook up callbacks to the items on the mode menu. */
4507 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4508 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4509 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4510 for (; kids; kids = kids->next)
4511 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4512 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4517 /* Handle the -prefs command-line argument. */
4520 GtkNotebook *notebook =
4521 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4522 gtk_notebook_set_page (notebook, 1);
4525 # ifdef HAVE_CRAPPLET
4529 GtkWidget *outer_vbox;
4531 gtk_widget_hide (s->toplevel_widget);
4533 capplet = capplet_widget_new ();
4535 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4536 # ifdef HAVE_CRAPPLET_IMMEDIATE
4537 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4538 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4539 /* In crapplet-mode, take off the menubar. */
4540 gtk_widget_hide (name_to_widget (s, "menubar"));
4542 /* Reparent our top-level container to be a child of the capplet
4545 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4546 gtk_widget_ref (outer_vbox);
4547 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4549 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4550 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4552 /* Find the window above us, and set the title and close handler. */
4554 GtkWidget *window = capplet;
4555 while (window && !GTK_IS_WINDOW (window))
4556 window = window->parent;
4559 gtk_window_set_title (GTK_WINDOW (window), window_title);
4560 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4561 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4566 s->toplevel_widget = capplet;
4568 # endif /* HAVE_CRAPPLET */
4571 /* The Gnome folks hate the menubar. I think it's important to have access
4572 to the commands on the File menu (Restart Daemon, etc.) and to the
4573 About and Documentation commands on the Help menu.
4577 gtk_widget_hide (name_to_widget (s, "menubar"));
4581 free (window_title);
4585 /* After picking the default size, allow -geometry to override it. */
4587 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
4590 gtk_widget_show (s->toplevel_widget);
4591 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4592 fix_preview_visual (s);
4594 /* Realize page zero, so that we can diddle the scrollbar when the
4595 user tabs back to it -- otherwise, the current hack isn't scrolled
4596 to the first time they tab back there, when started with "-prefs".
4597 (Though it is if they then tab away, and back again.)
4599 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4600 #### understands this crap, explain to me how to make this work.
4602 gtk_widget_realize (name_to_widget (s, "demos_table"));
4605 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4608 /* Issue any warnings about the running xscreensaver daemon. */
4609 the_network_is_not_the_computer (s);
4612 /* Run the Gtk event loop, and not the Xt event loop. This means that
4613 if there were Xt timers or fds registered, they would never get serviced,
4614 and if there were any Xt widgets, they would never have events delivered.
4615 Fortunately, we're using Gtk for all of the UI, and only initialized
4616 Xt so that we could process the command line and use the X resource
4619 s->initializing_p = False;
4621 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4622 after we start up. Otherwise, it always appears scrolled to the top
4623 when in crapplet-mode. */
4624 gtk_timeout_add (500, delayed_scroll_kludge, s);
4628 /* Load every configurator in turn, to scan them for errors all at once. */
4631 for (i = 0; i < p->screenhacks_count; i++)
4633 screenhack *hack = p->screenhacks[i];
4634 conf_data *d = load_configurator (hack->command, False);
4635 if (d) free_conf_data (d);
4641 # ifdef HAVE_CRAPPLET
4643 capplet_gtk_main ();
4645 # endif /* HAVE_CRAPPLET */
4648 kill_preview_subproc (s, False);
4652 #endif /* HAVE_GTK -- whole file */