1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2002 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #ifdef HAVE_GTK /* whole file */
19 #include <xscreensaver-intl.h>
29 #endif /* ENABLE_NLS */
32 # include <pwd.h> /* for getpwuid() */
38 # include <sys/utsname.h> /* for uname() */
39 #endif /* HAVE_UNAME */
49 #ifdef HAVE_SYS_WAIT_H
50 # include <sys/wait.h> /* for waitpid() and associated macros */
54 #include <X11/Xproto.h> /* for CARD32 */
55 #include <X11/Xatom.h> /* for XA_INTEGER */
56 #include <X11/Intrinsic.h>
57 #include <X11/StringDefs.h>
59 /* We don't actually use any widget internals, but these are included
60 so that gdb will have debug info for the widgets... */
61 #include <X11/IntrinsicP.h>
62 #include <X11/ShellP.h>
66 # include <X11/Xmu/Error.h>
68 # include <Xmu/Error.h>
78 # include <capplet-widget.h>
84 #include <glade/glade-xml.h>
85 #endif /* HAVE_GTK2 */
87 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
88 # define GLADE_DIR DEFAULT_ICONDIR
90 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
91 # define DEFAULT_ICONDIR GLADE_DIR
97 #include "resources.h" /* for parse_time() */
98 #include "visual.h" /* for has_writable_cells() */
99 #include "remote.h" /* for xscreensaver_command() */
102 #include "logo-50.xpm"
103 #include "logo-180.xpm"
105 #include "demo-Gtk-widgets.h"
106 #include "demo-Gtk-support.h"
107 #include "demo-Gtk-conf.h"
119 #endif /* HAVE_GTK2 */
122 extern void exec_command (const char *shell, const char *command, int nice);
125 #define countof(x) (sizeof((x))/sizeof((*x)))
129 char *progclass = "XScreenSaver";
132 /* The order of the items in the mode menu. */
133 static int mode_menu_order[] = {
134 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS };
139 char *short_version; /* version number of this xscreensaver build */
141 GtkWidget *toplevel_widget; /* the main window */
142 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
143 GtkWidget *popup_widget; /* the "Settings" dialog */
144 conf_data *cdata; /* private data for per-hack configuration */
147 GladeXML *glade_ui; /* Glade UI file */
148 #endif /* HAVE_GTK2 */
150 Bool debug_p; /* whether to print diagnostics */
151 Bool initializing_p; /* flag for breaking recursion loops */
152 Bool saving_p; /* flag for breaking recursion loops */
154 char *desired_preview_cmd; /* subprocess we intend to run */
155 char *running_preview_cmd; /* subprocess we are currently running */
156 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
157 Bool running_preview_error_p; /* whether the pid died abnormally */
159 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
160 int subproc_timer_id; /* timer to delay subproc launch */
161 int subproc_check_timer_id; /* timer to check whether it started up */
162 int subproc_check_countdown; /* how many more checks left */
164 int *list_elt_to_hack_number; /* table for sorting the hack list */
165 int *hack_number_to_list_elt; /* the inverse table */
167 int _selected_list_element; /* don't use this: call
168 selected_list_element() instead */
170 saver_preferences prefs;
175 /* Total fucking evilness due to the fact that it's rocket science to get
176 a closure object of our own down into the various widget callbacks. */
177 static state *global_state_kludge;
180 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
181 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
182 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
185 static void populate_demo_window (state *, int list_elt);
186 static void populate_prefs_page (state *);
187 static void populate_popup_window (state *);
189 static Bool flush_dialog_changes_and_save (state *);
190 static Bool flush_popup_changes_and_save (state *);
192 static int maybe_reload_init_file (state *);
193 static void await_xscreensaver (state *);
195 static void schedule_preview (state *, const char *cmd);
196 static void kill_preview_subproc (state *);
197 static void schedule_preview_check (state *);
201 /* Some random utility functions
207 time_t now = time ((time_t *) 0);
208 char *ct = (char *) ctime (&now);
209 static char buf[255];
210 int n = strlen(progname);
212 strncpy(buf, progname, n);
215 strncpy(buf+n, ct+11, 8);
216 strcpy(buf+n+9, ": ");
222 name_to_widget (state *s, const char *name)
232 s->glade_ui = glade_xml_new (GLADE_DIR "/xscreensaver-demo.glade2",
237 "%s: could not load glade file"
238 " \"%s/xscreensaver-demo.glade2\"\n",
242 glade_xml_signal_autoconnect (s->glade_ui);
245 w = glade_xml_get_widget (s->glade_ui, name);
247 #else /* !HAVE_GTK2 */
249 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
252 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
254 #endif /* HAVE_GTK2 */
257 fprintf (stderr, "%s: no widget \"%s\"\n", blurb(), name);
262 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
263 Takes a scroller, viewport, or list as an argument.
266 ensure_selected_item_visible (GtkWidget *widget)
270 #else /* !HAVE_GTK2 */
272 GtkScrolledWindow *scroller = 0;
274 GtkList *list_widget = 0;
278 GtkWidget *selected = 0;
281 gint parent_h, child_y, child_h, children_h, ignore;
282 double ratio_t, ratio_b;
284 if (GTK_IS_SCROLLED_WINDOW (widget))
286 scroller = GTK_SCROLLED_WINDOW (widget);
287 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
288 list_widget = GTK_LIST (GTK_BIN(vp)->child);
290 else if (GTK_IS_VIEWPORT (widget))
292 vp = GTK_VIEWPORT (widget);
293 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
294 list_widget = GTK_LIST (GTK_BIN(vp)->child);
296 else if (GTK_IS_LIST (widget))
298 list_widget = GTK_LIST (widget);
299 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
300 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
305 slist = list_widget->selection;
306 selected = (slist ? GTK_WIDGET (slist->data) : 0);
310 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
312 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
313 kids; kids = kids->next)
316 adj = gtk_scrolled_window_get_vadjustment (scroller);
318 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
319 &ignore, &ignore, &ignore, &parent_h, &ignore);
320 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
321 &ignore, &child_y, &ignore, &child_h, &ignore);
322 children_h = nkids * child_h;
324 ratio_t = ((double) child_y) / ((double) children_h);
325 ratio_b = ((double) child_y + child_h) / ((double) children_h);
327 if (adj->upper == 0.0) /* no items in list */
330 if (ratio_t < (adj->value / adj->upper) ||
331 ratio_b > ((adj->value + adj->page_size) / adj->upper))
334 int slop = parent_h * 0.75; /* how much to overshoot by */
336 if (ratio_t < (adj->value / adj->upper))
338 double ratio_w = ((double) parent_h) / ((double) children_h);
339 double ratio_l = (ratio_b - ratio_t);
340 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
343 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
345 target = ratio_t * adj->upper;
349 if (target > adj->upper - adj->page_size)
350 target = adj->upper - adj->page_size;
354 gtk_adjustment_set_value (adj, target);
356 #endif /* !HAVE_GTK2 */
360 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
362 GtkWidget *shell = GTK_WIDGET (user_data);
363 while (shell->parent)
364 shell = shell->parent;
365 gtk_widget_destroy (GTK_WIDGET (shell));
369 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
371 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
373 restart_menu_cb (widget, user_data);
374 warning_dialog_dismiss_cb (widget, user_data);
378 warning_dialog (GtkWidget *parent, const char *message,
379 Boolean restart_button_p, int center)
381 char *msg = strdup (message);
384 GtkWidget *dialog = gtk_dialog_new ();
385 GtkWidget *label = 0;
387 GtkWidget *cancel = 0;
390 while (parent && !parent->window)
391 parent = parent->parent;
393 if (!GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
395 fprintf (stderr, "%s: too early for dialog?\n", progname);
403 char *s = strchr (head, '\n');
406 sprintf (name, "label%d", i++);
409 label = gtk_label_new (head);
411 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
412 #endif /* HAVE_GTK2 */
417 GTK_WIDGET (label)->style =
418 gtk_style_copy (GTK_WIDGET (label)->style);
419 GTK_WIDGET (label)->style->font =
420 gdk_font_load (get_string_resource("warning_dialog.headingFont",
422 gtk_widget_set_style (GTK_WIDGET (label),
423 GTK_WIDGET (label)->style);
425 #endif /* !HAVE_GTK2 */
427 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
428 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
429 label, TRUE, TRUE, 0);
430 gtk_widget_show (label);
441 label = gtk_label_new ("");
442 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
443 label, TRUE, TRUE, 0);
444 gtk_widget_show (label);
446 label = gtk_hbutton_box_new ();
447 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
448 label, TRUE, TRUE, 0);
451 if (restart_button_p)
453 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
454 gtk_container_add (GTK_CONTAINER (label), cancel);
457 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
458 gtk_container_add (GTK_CONTAINER (label), ok);
460 #else /* !HAVE_GTK2 */
462 ok = gtk_button_new_with_label ("OK");
463 gtk_container_add (GTK_CONTAINER (label), ok);
465 if (restart_button_p)
467 cancel = gtk_button_new_with_label ("Cancel");
468 gtk_container_add (GTK_CONTAINER (label), cancel);
471 #endif /* !HAVE_GTK2 */
473 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
474 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
475 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
476 GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
477 gtk_widget_show (ok);
478 gtk_widget_grab_focus (ok);
482 GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
483 gtk_widget_show (cancel);
485 gtk_widget_show (label);
486 gtk_widget_show (dialog);
488 if (restart_button_p)
490 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
491 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
493 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
494 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
499 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
500 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
504 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
505 GTK_WIDGET (parent)->window);
508 gtk_window_present (GTK_WINDOW (dialog));
509 #else /* !HAVE_GTK2 */
510 gdk_window_show (GTK_WIDGET (dialog)->window);
511 gdk_window_raise (GTK_WIDGET (dialog)->window);
512 #endif /* !HAVE_GTK2 */
519 run_cmd (state *s, Atom command, int arg)
524 flush_dialog_changes_and_save (s);
525 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
530 sprintf (buf, "Error:\n\n%s", err);
532 strcpy (buf, "Unknown error!");
533 warning_dialog (s->toplevel_widget, buf, False, 100);
540 run_hack (state *s, int list_elt, Bool report_errors_p)
543 if (list_elt < 0) return;
544 hack_number = s->list_elt_to_hack_number[list_elt];
546 flush_dialog_changes_and_save (s);
547 schedule_preview (s, 0);
549 run_cmd (s, XA_DEMO, hack_number + 1);
553 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
565 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
567 state *s = global_state_kludge; /* I hate C so much... */
568 flush_dialog_changes_and_save (s);
569 kill_preview_subproc (s);
574 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
576 state *s = (state *) data;
577 flush_dialog_changes_and_save (s);
583 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
586 char *vers = strdup (screensaver_id + 4);
589 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
591 s = strchr (vers, ',');
596 sprintf(copy, _("Copyright \xC2\xA9 1991-2002 %s"), s);
597 #else /* !HAVE_GTK2 */
598 sprintf(copy, _("Copyright \251 1991-2002 %s"), s);
599 #endif /* !HAVE_GTK2 */
601 sprintf (msg, "%s\n\n%s", copy, desc);
603 /* I can't make gnome_about_new() work here -- it starts dying in
604 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
605 then this might be the thing to do:
609 const gchar *auth[] = { 0 };
610 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
612 gtk_widget_show (about);
614 #else / * GTK but not GNOME * /
618 GdkColormap *colormap;
619 GdkPixmap *gdkpixmap;
622 GtkWidget *dialog = gtk_dialog_new ();
623 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
624 GtkWidget *parent = GTK_WIDGET (menuitem);
625 while (parent->parent)
626 parent = parent->parent;
628 hbox = gtk_hbox_new (FALSE, 20);
629 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
630 hbox, TRUE, TRUE, 0);
632 colormap = gtk_widget_get_colormap (parent);
634 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
635 (gchar **) logo_180_xpm);
636 icon = gtk_pixmap_new (gdkpixmap, mask);
637 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
639 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
641 vbox = gtk_vbox_new (FALSE, 0);
642 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
644 label1 = gtk_label_new (vers);
645 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
646 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
647 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
650 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
651 GTK_WIDGET (label1)->style->font =
652 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
653 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
654 #endif /* HAVE_GTK2 */
656 label2 = gtk_label_new (msg);
657 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
658 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
659 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
662 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
663 GTK_WIDGET (label2)->style->font =
664 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
665 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
666 #endif /* HAVE_GTK2 */
668 hb = gtk_hbutton_box_new ();
670 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
674 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
675 #else /* !HAVE_GTK2 */
676 ok = gtk_button_new_with_label (_("OK"));
677 #endif /* !HAVE_GTK2 */
678 gtk_container_add (GTK_CONTAINER (hb), ok);
680 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
681 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
682 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
684 gtk_widget_show (hbox);
685 gtk_widget_show (icon);
686 gtk_widget_show (vbox);
687 gtk_widget_show (label1);
688 gtk_widget_show (label2);
689 gtk_widget_show (hb);
690 gtk_widget_show (ok);
691 gtk_widget_show (dialog);
693 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
694 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
696 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
697 GTK_WIDGET (parent)->window);
698 gdk_window_show (GTK_WIDGET (dialog)->window);
699 gdk_window_raise (GTK_WIDGET (dialog)->window);
705 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
707 state *s = global_state_kludge; /* I hate C so much... */
708 saver_preferences *p = &s->prefs;
711 if (!p->help_url || !*p->help_url)
713 warning_dialog (s->toplevel_widget,
715 "No Help URL has been specified.\n"), False, 100);
719 help_command = (char *) malloc (strlen (p->load_url_command) +
720 (strlen (p->help_url) * 2) + 20);
721 strcpy (help_command, "( ");
722 sprintf (help_command + strlen(help_command),
723 p->load_url_command, p->help_url, p->help_url);
724 strcat (help_command, " ) &");
725 system (help_command);
731 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
733 state *s = global_state_kludge; /* I hate C so much... */
734 run_cmd (s, XA_ACTIVATE, 0);
739 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
741 state *s = global_state_kludge; /* I hate C so much... */
742 run_cmd (s, XA_LOCK, 0);
747 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
749 state *s = global_state_kludge; /* I hate C so much... */
750 run_cmd (s, XA_EXIT, 0);
755 restart_menu_cb (GtkWidget *widget, gpointer user_data)
757 state *s = global_state_kludge; /* I hate C so much... */
758 flush_dialog_changes_and_save (s);
759 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
761 system ("xscreensaver -nosplash &");
763 await_xscreensaver (s);
767 await_xscreensaver (state *s)
771 Display *dpy = GDK_DISPLAY();
772 /* GtkWidget *dialog = 0;*/
775 while (!rversion && (--countdown > 0))
777 /* Check for the version of the running xscreensaver... */
778 server_xscreensaver_version (dpy, &rversion, 0, 0);
780 /* If it's not there yet, wait a second... */
785 /* if (dialog) gtk_widget_destroy (dialog);*/
794 /* Timed out, no screensaver running. */
797 Bool root_p = (geteuid () == 0);
801 "The xscreensaver daemon did not start up properly.\n"
806 _("You are running as root. This usually means that xscreensaver\n"
807 "was unable to contact your X server because access control is\n"
808 "turned on. Try running this command:\n"
810 " xhost +localhost\n"
812 "and then selecting `File / Restart Daemon'.\n"
814 "Note that turning off access control will allow anyone logged\n"
815 "on to this machine to access your screen, which might be\n"
816 "considered a security problem. Please read the xscreensaver\n"
817 "manual and FAQ for more information.\n"
819 "You shouldn't run X as root. Instead, you should log in as a\n"
820 "normal user, and `su' as necessary."));
822 strcat (buf, _("Please check your $PATH and permissions."));
824 warning_dialog (s->toplevel_widget, buf, False, 1);
830 selected_list_element (state *s)
832 return s->_selected_list_element;
837 demo_write_init_file (state *s, saver_preferences *p)
841 /* #### try to figure out why shit keeps getting reordered... */
842 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
846 if (!write_init_file (p, s->short_version, False))
849 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
854 const char *f = init_file_name();
856 warning_dialog (s->toplevel_widget,
857 _("Error:\n\nCouldn't determine init file name!\n"),
861 char *b = (char *) malloc (strlen(f) + 1024);
862 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
863 warning_dialog (s->toplevel_widget, b, False, 100);
872 run_this_cb (GtkButton *button, gpointer user_data)
874 state *s = global_state_kludge; /* I hate C so much... */
875 int list_elt = selected_list_element (s);
876 if (list_elt < 0) return;
877 if (!flush_dialog_changes_and_save (s))
878 run_hack (s, list_elt, True);
883 manual_cb (GtkButton *button, gpointer user_data)
885 state *s = global_state_kludge; /* I hate C so much... */
886 saver_preferences *p = &s->prefs;
887 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
888 int list_elt = selected_list_element (s);
890 char *name, *name2, *cmd, *str;
891 if (list_elt < 0) return;
892 hack_number = s->list_elt_to_hack_number[list_elt];
894 flush_dialog_changes_and_save (s);
895 ensure_selected_item_visible (GTK_WIDGET (list_widget));
897 name = strdup (p->screenhacks[hack_number]->command);
899 while (isspace (*name2)) name2++;
901 while (*str && !isspace (*str)) str++;
903 str = strrchr (name2, '/');
904 if (str) name = str+1;
906 cmd = get_string_resource ("manualCommand", "ManualCommand");
909 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
911 sprintf (cmd2 + strlen (cmd2),
913 name2, name2, name2, name2);
914 strcat (cmd2, " ) &");
920 warning_dialog (GTK_WIDGET (button),
921 _("Error:\n\nno `manualCommand' resource set."),
930 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
932 GtkWidget *parent = name_to_widget (s, "scroller");
933 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
937 GtkTreeSelection *selection;
938 #endif /* HAVE_GTK2 */
940 if (!was) gtk_widget_set_sensitive (parent, True);
942 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
944 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
945 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
946 gtk_tree_selection_select_iter (selection, &iter);
947 #else /* !HAVE_GTK2 */
948 gtk_list_select_item (GTK_LIST (list), list_elt);
949 #endif /* !HAVE_GTK2 */
950 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
951 if (!was) gtk_widget_set_sensitive (parent, False);
956 run_next_cb (GtkButton *button, gpointer user_data)
958 state *s = global_state_kludge; /* I hate C so much... */
959 saver_preferences *p = &s->prefs;
960 Bool ops = s->preview_suppressed_p;
962 GtkWidget *list_widget = name_to_widget (s, "list");
963 int list_elt = selected_list_element (s);
970 if (list_elt >= p->screenhacks_count)
973 s->preview_suppressed_p = True;
975 flush_dialog_changes_and_save (s);
976 force_list_select_item (s, list_widget, list_elt, True);
977 populate_demo_window (s, list_elt);
978 run_hack (s, list_elt, False);
980 s->preview_suppressed_p = ops;
985 run_prev_cb (GtkButton *button, gpointer user_data)
987 state *s = global_state_kludge; /* I hate C so much... */
988 saver_preferences *p = &s->prefs;
989 Bool ops = s->preview_suppressed_p;
991 GtkWidget *list_widget = name_to_widget (s, "list");
992 int list_elt = selected_list_element (s);
995 list_elt = p->screenhacks_count - 1;
1000 list_elt = p->screenhacks_count - 1;
1002 s->preview_suppressed_p = True;
1004 flush_dialog_changes_and_save (s);
1005 force_list_select_item (s, list_widget, list_elt, True);
1006 populate_demo_window (s, list_elt);
1007 run_hack (s, list_elt, False);
1009 s->preview_suppressed_p = ops;
1013 /* Writes the given settings into prefs.
1014 Returns true if there was a change, False otherwise.
1015 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1018 flush_changes (state *s,
1021 const char *command,
1024 saver_preferences *p = &s->prefs;
1025 Bool changed = False;
1028 if (list_elt < 0 || list_elt >= p->screenhacks_count)
1031 hack_number = s->list_elt_to_hack_number[list_elt];
1032 hack = p->screenhacks[hack_number];
1034 if (enabled_p != -1 &&
1035 enabled_p != hack->enabled_p)
1037 hack->enabled_p = enabled_p;
1040 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1041 blurb(), hack->name, enabled_p);
1046 if (!hack->command || !!strcmp (command, hack->command))
1048 if (hack->command) free (hack->command);
1049 hack->command = strdup (command);
1052 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1053 blurb(), hack->name, command);
1059 const char *ov = hack->visual;
1060 if (!ov || !*ov) ov = "any";
1061 if (!*visual) visual = "any";
1062 if (!!strcasecmp (visual, ov))
1064 if (hack->visual) free (hack->visual);
1065 hack->visual = strdup (visual);
1068 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1069 blurb(), hack->name, visual);
1077 /* Helper for the text fields that contain time specifications:
1078 this parses the text, and does error checking.
1081 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1086 if (!sec_p || strchr (line, ':'))
1087 value = parse_time ((char *) line, sec_p, True);
1091 if (sscanf (line, "%u%c", &value, &c) != 1)
1097 value *= 1000; /* Time measures in microseconds */
1103 "Unparsable time format: \"%s\"\n"),
1105 warning_dialog (s->toplevel_widget, b, False, 100);
1114 directory_p (const char *path)
1117 if (!path || !*path)
1119 else if (stat (path, &st))
1121 else if (!S_ISDIR (st.st_mode))
1128 normalize_directory (const char *path)
1132 if (!path) return 0;
1134 p2 = (char *) malloc (L + 2);
1136 if (p2[L-1] == '/') /* remove trailing slash */
1139 for (s = p2; s && *s; s++)
1142 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1143 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1146 while (s0 > p2 && s0[-1] != '/')
1156 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1157 strcpy (s, s+2), s--;
1158 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1162 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1163 while (s[0] == '/' && s[1] == '/')
1166 /* and strip trailing whitespace for good measure. */
1168 while (isspace(p2[L-1]))
1181 } FlushForeachClosure;
1184 flush_checkbox (GtkTreeModel *model,
1189 FlushForeachClosure *closure = data;
1192 gtk_tree_model_get (model, iter,
1193 COL_ENABLED, &checked,
1196 if (flush_changes (closure->s, closure->i,
1198 *closure->changed = True;
1202 /* don't remove row */
1206 #endif /* HAVE_GTK2 */
1208 /* Flush out any changes made in the main dialog window (where changes
1209 take place immediately: clicking on a checkbox causes the init file
1210 to be written right away.)
1213 flush_dialog_changes_and_save (state *s)
1215 saver_preferences *p = &s->prefs;
1216 saver_preferences P2, *p2 = &P2;
1218 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1219 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1220 FlushForeachClosure closure;
1221 #else /* !HAVE_GTK2 */
1222 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1223 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1225 #endif /* !HAVE_GTK2 */
1227 Bool changed = False;
1230 if (s->saving_p) return False;
1235 /* Flush any checkbox changes in the list down into the prefs struct.
1239 closure.changed = &changed;
1241 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1243 #else /* !HAVE_GTK2 */
1245 for (i = 0; kids; kids = kids->next, i++)
1247 GtkWidget *line = GTK_WIDGET (kids->data);
1248 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1249 GtkWidget *line_check =
1250 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1252 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1254 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1257 #endif /* ~HAVE_GTK2 */
1259 /* Flush the non-hack-specific settings down into the prefs struct.
1262 # define SECONDS(FIELD,NAME) \
1263 w = name_to_widget (s, (NAME)); \
1264 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1266 # define MINUTES(FIELD,NAME) \
1267 w = name_to_widget (s, (NAME)); \
1268 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1270 # define CHECKBOX(FIELD,NAME) \
1271 w = name_to_widget (s, (NAME)); \
1272 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1274 # define PATHNAME(FIELD,NAME) \
1275 w = name_to_widget (s, (NAME)); \
1276 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1278 MINUTES (&p2->timeout, "timeout_spinbutton");
1279 MINUTES (&p2->cycle, "cycle_spinbutton");
1280 CHECKBOX (p2->lock_p, "lock_button");
1281 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1283 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1284 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1285 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1286 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1288 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1289 CHECKBOX (p2->grab_video_p, "grab_video_button");
1290 CHECKBOX (p2->random_image_p, "grab_image_button");
1291 PATHNAME (p2->image_directory, "image_text");
1293 CHECKBOX (p2->verbose_p, "verbose_button");
1294 CHECKBOX (p2->capture_stderr_p, "capture_button");
1295 CHECKBOX (p2->splash_p, "splash_button");
1297 CHECKBOX (p2->install_cmap_p, "install_button");
1298 CHECKBOX (p2->fade_p, "fade_button");
1299 CHECKBOX (p2->unfade_p, "unfade_button");
1300 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1307 /* Warn if the image directory doesn't exist.
1309 if (p2->image_directory &&
1310 *p2->image_directory &&
1311 !directory_p (p2->image_directory))
1314 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1315 p2->image_directory);
1316 warning_dialog (s->toplevel_widget, b, False, 100);
1320 /* Map the mode menu to `saver_mode' enum values. */
1322 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1323 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1324 GtkWidget *selected = gtk_menu_get_active (menu);
1325 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1326 int menu_elt = g_list_index (kids, (gpointer) selected);
1327 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1328 p2->mode = mode_menu_order[menu_elt];
1331 if (p2->mode == ONE_HACK)
1333 int list_elt = selected_list_element (s);
1334 p2->selected_hack = (list_elt >= 0
1335 ? s->list_elt_to_hack_number[list_elt]
1339 # define COPY(field, name) \
1340 if (p->field != p2->field) { \
1343 fprintf (stderr, "%s: %s => %d\n", blurb(), name, p2->field); \
1345 p->field = p2->field
1348 COPY(selected_hack, "selected_hack");
1350 COPY(timeout, "timeout");
1351 COPY(cycle, "cycle");
1352 COPY(lock_p, "lock_p");
1353 COPY(lock_timeout, "lock_timeout");
1355 COPY(dpms_enabled_p, "dpms_enabled_p");
1356 COPY(dpms_standby, "dpms_standby");
1357 COPY(dpms_suspend, "dpms_suspend");
1358 COPY(dpms_off, "dpms_off");
1360 COPY(verbose_p, "verbose_p");
1361 COPY(capture_stderr_p, "capture_stderr_p");
1362 COPY(splash_p, "splash_p");
1364 COPY(install_cmap_p, "install_cmap_p");
1365 COPY(fade_p, "fade_p");
1366 COPY(unfade_p, "unfade_p");
1367 COPY(fade_seconds, "fade_seconds");
1369 COPY(grab_desktop_p, "grab_desktop_p");
1370 COPY(grab_video_p, "grab_video_p");
1371 COPY(random_image_p, "random_image_p");
1375 if (!p->image_directory ||
1376 !p2->image_directory ||
1377 strcmp(p->image_directory, p2->image_directory))
1381 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1382 blurb(), p2->image_directory);
1384 if (p->image_directory && p->image_directory != p2->image_directory)
1385 free (p->image_directory);
1386 p->image_directory = p2->image_directory;
1387 p2->image_directory = 0;
1389 populate_prefs_page (s);
1393 Display *dpy = GDK_DISPLAY();
1394 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1395 sync_server_dpms_settings (dpy, enabled_p,
1396 p->dpms_standby / 1000,
1397 p->dpms_suspend / 1000,
1401 changed = demo_write_init_file (s, p);
1404 s->saving_p = False;
1409 /* Flush out any changes made in the popup dialog box (where changes
1410 take place only when the OK button is clicked.)
1413 flush_popup_changes_and_save (state *s)
1415 Bool changed = False;
1416 saver_preferences *p = &s->prefs;
1417 int list_elt = selected_list_element (s);
1419 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1420 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1422 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1423 const char *command = gtk_entry_get_text (cmd);
1428 if (s->saving_p) return False;
1434 if (maybe_reload_init_file (s) != 0)
1440 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1442 if (!strcasecmp (visual, "")) visual = "";
1443 else if (!strcasecmp (visual, "any")) visual = "";
1444 else if (!strcasecmp (visual, "default")) visual = "Default";
1445 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1446 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1447 else if (!strcasecmp (visual, "best")) visual = "Best";
1448 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1449 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1450 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1451 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1452 else if (!strcasecmp (visual, "color")) visual = "Color";
1453 else if (!strcasecmp (visual, "gl")) visual = "GL";
1454 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1455 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1456 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1457 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1458 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1459 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1460 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1461 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
1462 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1465 gdk_beep (); /* unparsable */
1467 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1470 changed = flush_changes (s, list_elt, -1, command, visual);
1473 changed = demo_write_init_file (s, p);
1475 /* Do this to re-launch the hack if (and only if) the command line
1477 populate_demo_window (s, selected_list_element (s));
1481 s->saving_p = False;
1487 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1489 state *s = global_state_kludge; /* I hate C so much... */
1490 if (! s->initializing_p)
1492 s->initializing_p = True;
1493 flush_dialog_changes_and_save (s);
1494 s->initializing_p = False;
1499 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1501 pref_changed_cb (widget, user_data);
1505 /* Callback on menu items in the "mode" options menu.
1508 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1510 state *s = (state *) user_data;
1511 saver_preferences *p = &s->prefs;
1512 GtkWidget *list = name_to_widget (s, "list");
1515 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1517 saver_mode new_mode;
1518 int old_selected = p->selected_hack;
1522 if (menu_items->data == widget)
1525 menu_items = menu_items->next;
1527 if (!menu_items) abort();
1529 new_mode = mode_menu_order[menu_index];
1531 /* Keep the same list element displayed as before; except if we're
1532 switching *to* "one screensaver" mode from any other mode, scroll
1533 to and select "the one".
1536 if (new_mode == ONE_HACK)
1537 list_elt = (p->selected_hack >= 0
1538 ? s->hack_number_to_list_elt[p->selected_hack]
1542 list_elt = selected_list_element (s);
1545 saver_mode old_mode = p->mode;
1547 populate_demo_window (s, list_elt);
1548 force_list_select_item (s, list, list_elt, True);
1549 p->mode = old_mode; /* put it back, so the init file gets written */
1552 pref_changed_cb (widget, user_data);
1554 if (old_selected != p->selected_hack)
1555 abort(); /* dammit, not again... */
1560 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1561 gint page_num, gpointer user_data)
1563 state *s = global_state_kludge; /* I hate C so much... */
1564 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1566 /* If we're switching to page 0, schedule the current hack to be run.
1567 Otherwise, schedule it to stop. */
1569 populate_demo_window (s, selected_list_element (s));
1571 schedule_preview (s, 0);
1576 list_activated_cb (GtkTreeView *list,
1578 GtkTreeViewColumn *column,
1585 g_return_if_fail (!gdk_pointer_is_grabbed ());
1587 str = gtk_tree_path_to_string (path);
1588 list_elt = strtol (str, NULL, 10);
1592 run_hack (s, list_elt, True);
1596 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1598 state *s = (state *)data;
1599 GtkTreeModel *model;
1605 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1608 path = gtk_tree_model_get_path (model, &iter);
1609 str = gtk_tree_path_to_string (path);
1610 list_elt = strtol (str, NULL, 10);
1612 gtk_tree_path_free (path);
1615 populate_demo_window (s, list_elt);
1616 flush_dialog_changes_and_save (s);
1619 #else /* !HAVE_GTK2 */
1621 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1622 list_select_cb that comes in
1623 *after* we've double-clicked.
1627 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1630 state *s = (state *) data;
1631 if (event->type == GDK_2BUTTON_PRESS)
1633 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1634 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1636 last_doubleclick_time = time ((time_t *) 0);
1639 run_hack (s, list_elt, True);
1647 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1649 state *s = (state *) data;
1650 time_t now = time ((time_t *) 0);
1652 if (now >= last_doubleclick_time + 2)
1654 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1655 populate_demo_window (s, list_elt);
1656 flush_dialog_changes_and_save (s);
1661 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1663 state *s = (state *) data;
1664 populate_demo_window (s, -1);
1665 flush_dialog_changes_and_save (s);
1668 #endif /* !HAVE_GTK2 */
1671 /* Called when the checkboxes that are in the left column of the
1672 scrolling list are clicked. This both populates the right pane
1673 (just as clicking on the label (really, listitem) does) and
1674 also syncs this checkbox with the right pane Enabled checkbox.
1679 GtkCellRendererToggle *toggle,
1681 #else /* !HAVE_GTK2 */
1683 #endif /* !HAVE_GTK2 */
1686 state *s = (state *) data;
1689 GtkScrolledWindow *scroller =
1690 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1691 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1692 GtkTreeModel *model = gtk_tree_view_get_model (list);
1693 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1696 #else /* !HAVE_GTK2 */
1697 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1698 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1700 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1701 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1702 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1703 #endif /* ~HAVE_GTK2 */
1710 if (!gtk_tree_model_get_iter (model, &iter, path))
1712 g_warning ("bad path: %s", path_string);
1715 gtk_tree_path_free (path);
1717 gtk_tree_model_get (model, &iter,
1718 COL_ENABLED, &active,
1721 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1722 COL_ENABLED, !active,
1725 list_elt = strtol (path_string, NULL, 10);
1726 #else /* !HAVE_GTK2 */
1727 list_elt = gtk_list_child_position (list, line);
1728 #endif /* !HAVE_GTK2 */
1730 /* remember previous scroll position of the top of the list */
1731 adj = gtk_scrolled_window_get_vadjustment (scroller);
1732 scroll_top = adj->value;
1734 flush_dialog_changes_and_save (s);
1735 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1736 populate_demo_window (s, list_elt);
1738 /* restore the previous scroll position of the top of the list.
1739 this is weak, but I don't really know why it's moving... */
1740 gtk_adjustment_set_value (adj, scroll_top);
1746 GtkFileSelection *widget;
1747 } file_selection_data;
1752 store_image_directory (GtkWidget *button, gpointer user_data)
1754 file_selection_data *fsd = (file_selection_data *) user_data;
1755 state *s = fsd->state;
1756 GtkFileSelection *selector = fsd->widget;
1757 GtkWidget *top = s->toplevel_widget;
1758 saver_preferences *p = &s->prefs;
1759 const char *path = gtk_file_selection_get_filename (selector);
1761 if (p->image_directory && !strcmp(p->image_directory, path))
1762 return; /* no change */
1764 if (!directory_p (path))
1767 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1768 warning_dialog (GTK_WIDGET (top), b, False, 100);
1772 if (p->image_directory) free (p->image_directory);
1773 p->image_directory = normalize_directory (path);
1775 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1776 (p->image_directory ? p->image_directory : ""));
1777 demo_write_init_file (s, p);
1782 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1784 file_selection_data *fsd = (file_selection_data *) user_data;
1785 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1789 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1791 browse_image_dir_cancel (button, user_data);
1792 store_image_directory (button, user_data);
1796 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1798 browse_image_dir_cancel (widget, user_data);
1803 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1805 state *s = global_state_kludge; /* I hate C so much... */
1806 saver_preferences *p = &s->prefs;
1807 static file_selection_data *fsd = 0;
1809 GtkFileSelection *selector = GTK_FILE_SELECTION(
1810 gtk_file_selection_new ("Please select the image directory."));
1813 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1815 fsd->widget = selector;
1818 if (p->image_directory && *p->image_directory)
1819 gtk_file_selection_set_filename (selector, p->image_directory);
1821 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1822 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1824 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1825 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1827 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1828 GTK_SIGNAL_FUNC (browse_image_dir_close),
1831 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1833 gtk_window_set_modal (GTK_WINDOW (selector), True);
1834 gtk_widget_show (GTK_WIDGET (selector));
1839 settings_cb (GtkButton *button, gpointer user_data)
1841 state *s = global_state_kludge; /* I hate C so much... */
1842 int list_elt = selected_list_element (s);
1844 populate_demo_window (s, list_elt); /* reset the widget */
1845 populate_popup_window (s); /* create UI on popup window */
1846 gtk_widget_show (s->popup_widget);
1850 settings_sync_cmd_text (state *s)
1853 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1854 char *cmd_line = get_configurator_command_line (s->cdata);
1855 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1856 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1858 # endif /* HAVE_XML */
1862 settings_adv_cb (GtkButton *button, gpointer user_data)
1864 state *s = global_state_kludge; /* I hate C so much... */
1865 GtkNotebook *notebook =
1866 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1868 settings_sync_cmd_text (s);
1869 gtk_notebook_set_page (notebook, 1);
1873 settings_std_cb (GtkButton *button, gpointer user_data)
1875 state *s = global_state_kludge; /* I hate C so much... */
1876 GtkNotebook *notebook =
1877 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1879 /* Re-create UI to reflect the in-progress command-line settings. */
1880 populate_popup_window (s);
1882 gtk_notebook_set_page (notebook, 0);
1886 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1887 gint page_num, gpointer user_data)
1889 state *s = global_state_kludge; /* I hate C so much... */
1890 GtkWidget *adv = name_to_widget (s, "adv_button");
1891 GtkWidget *std = name_to_widget (s, "std_button");
1895 gtk_widget_show (adv);
1896 gtk_widget_hide (std);
1898 else if (page_num == 1)
1900 gtk_widget_hide (adv);
1901 gtk_widget_show (std);
1910 settings_cancel_cb (GtkButton *button, gpointer user_data)
1912 state *s = global_state_kludge; /* I hate C so much... */
1913 gtk_widget_hide (s->popup_widget);
1917 settings_ok_cb (GtkButton *button, gpointer user_data)
1919 state *s = global_state_kludge; /* I hate C so much... */
1920 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1921 int page = gtk_notebook_get_current_page (notebook);
1924 /* Regenerate the command-line from the widget contents before saving.
1925 But don't do this if we're looking at the command-line page already,
1926 or we will blow away what they typed... */
1927 settings_sync_cmd_text (s);
1929 flush_popup_changes_and_save (s);
1930 gtk_widget_hide (s->popup_widget);
1934 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1936 state *s = (state *) data;
1937 settings_cancel_cb (0, (gpointer) s);
1942 /* Populating the various widgets
1946 /* Returns the number of the last hack run by the server.
1949 server_current_hack (void)
1953 unsigned long nitems, bytesafter;
1955 Display *dpy = GDK_DISPLAY();
1956 int hack_number = -1;
1958 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1959 XA_SCREENSAVER_STATUS,
1960 0, 3, False, XA_INTEGER,
1961 &type, &format, &nitems, &bytesafter,
1962 (unsigned char **) &data)
1964 && type == XA_INTEGER
1967 hack_number = (int) data[2] - 1;
1969 if (data) free (data);
1975 /* Finds the number of the last hack to run, and makes that item be
1976 selected by default.
1979 scroll_to_current_hack (state *s)
1981 saver_preferences *p = &s->prefs;
1984 if (p->mode == ONE_HACK)
1985 hack_number = p->selected_hack;
1987 hack_number = server_current_hack ();
1989 if (hack_number >= 0 && hack_number < p->screenhacks_count)
1991 int list_elt = s->hack_number_to_list_elt[hack_number];
1992 GtkWidget *list = name_to_widget (s, "list");
1993 force_list_select_item (s, list, list_elt, True);
1994 populate_demo_window (s, list_elt);
2000 populate_hack_list (state *s)
2003 saver_preferences *p = &s->prefs;
2004 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2005 GtkListStore *model;
2006 GtkTreeSelection *selection;
2007 GtkCellRenderer *ren;
2011 g_object_get (G_OBJECT (list),
2016 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2017 g_object_set (G_OBJECT (list), "model", model, NULL);
2018 g_object_unref (model);
2020 ren = gtk_cell_renderer_toggle_new ();
2021 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2023 "active", COL_ENABLED,
2026 g_signal_connect (ren, "toggled",
2027 G_CALLBACK (list_checkbox_cb),
2030 ren = gtk_cell_renderer_text_new ();
2031 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2032 _("Screen Saver"), ren,
2036 g_signal_connect_after (list, "row_activated",
2037 G_CALLBACK (list_activated_cb),
2040 selection = gtk_tree_view_get_selection (list);
2041 g_signal_connect (selection, "changed",
2042 G_CALLBACK (list_select_changed_cb),
2047 for (i = 0; i < p->screenhacks_count; i++)
2049 screenhack *hack = p->screenhacks[s->list_elt_to_hack_number[i]];
2051 char *pretty_name = (hack->name
2052 ? strdup (hack->name)
2053 : make_hack_name (hack->command));
2055 gtk_list_store_append (model, &iter);
2056 gtk_list_store_set (model, &iter,
2057 COL_ENABLED, hack->enabled_p,
2058 COL_NAME, pretty_name,
2064 #else /* !HAVE_GTK2 */
2066 saver_preferences *p = &s->prefs;
2067 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2069 for (i = 0; i < p->screenhacks_count; i++)
2071 screenhack *hack = p->screenhacks[s->list_elt_to_hack_number[i]];
2073 /* A GtkList must contain only GtkListItems, but those can contain
2074 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2075 and a Label. We handle single and double click events on the
2076 line itself, for clicking on the text, but the interior checkbox
2077 also handles its own events.
2080 GtkWidget *line_hbox;
2081 GtkWidget *line_check;
2082 GtkWidget *line_label;
2084 char *pretty_name = (hack->name
2085 ? strdup (hack->name)
2086 : make_hack_name (hack->command));
2088 line = gtk_list_item_new ();
2089 line_hbox = gtk_hbox_new (FALSE, 0);
2090 line_check = gtk_check_button_new ();
2091 line_label = gtk_label_new (pretty_name);
2093 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2094 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2095 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2097 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2099 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2101 gtk_widget_show (line_check);
2102 gtk_widget_show (line_label);
2103 gtk_widget_show (line_hbox);
2104 gtk_widget_show (line);
2108 gtk_container_add (GTK_CONTAINER (list), line);
2109 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2110 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2113 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2114 GTK_SIGNAL_FUNC (list_checkbox_cb),
2118 GTK_WIDGET (GTK_BIN(line)->child)->style =
2119 gtk_style_copy (GTK_WIDGET (text_line)->style);
2121 gtk_widget_show (line);
2124 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2125 GTK_SIGNAL_FUNC (list_select_cb),
2127 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2128 GTK_SIGNAL_FUNC (list_unselect_cb),
2130 #endif /* !HAVE_GTK2 */
2134 update_list_sensitivity (state *s)
2136 saver_preferences *p = &s->prefs;
2137 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2138 Bool checkable = (p->mode == RANDOM_HACKS);
2139 Bool blankable = (p->mode != DONT_BLANK);
2142 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2143 GtkWidget *use = name_to_widget (s, "use_col_frame");
2144 #endif /* HAVE_GTK2 */
2145 GtkWidget *scroller = name_to_widget (s, "scroller");
2146 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2147 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2150 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2151 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2152 #else /* !HAVE_GTK2 */
2153 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2154 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2156 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2157 #endif /* !HAVE_GTK2 */
2158 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2159 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2161 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2164 gtk_tree_view_column_set_visible (use, checkable);
2165 #else /* !HAVE_GTK2 */
2167 gtk_widget_show (use); /* the "Use" column header */
2169 gtk_widget_hide (use);
2173 GtkBin *line = GTK_BIN (kids->data);
2174 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2175 GtkWidget *line_check =
2176 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2179 gtk_widget_show (line_check);
2181 gtk_widget_hide (line_check);
2185 #endif /* !HAVE_GTK2 */
2190 populate_prefs_page (state *s)
2192 saver_preferences *p = &s->prefs;
2194 /* The file supports timeouts of less than a minute, but the GUI does
2195 not, so throttle the values to be at least one minute (since "0" is
2196 a bad rounding choice...)
2198 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2201 THROTTLE (passwd_timeout);
2204 # define FMT_MINUTES(NAME,N) \
2205 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2207 # define FMT_SECONDS(NAME,N) \
2208 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2210 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2211 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2212 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2213 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2214 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2215 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2216 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2221 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2222 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2225 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2226 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2227 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2228 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2229 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2230 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2231 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2232 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2233 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2234 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2235 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2237 # undef TOGGLE_ACTIVE
2239 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2240 (p->image_directory ? p->image_directory : ""));
2241 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2243 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2246 /* Map the `saver_mode' enum to mode menu to values. */
2248 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2251 for (i = 0; i < countof(mode_menu_order); i++)
2252 if (mode_menu_order[i] == p->mode)
2254 gtk_option_menu_set_history (opt, i);
2255 update_list_sensitivity (s);
2259 Bool found_any_writable_cells = False;
2260 Bool dpms_supported = False;
2262 Display *dpy = GDK_DISPLAY();
2263 int nscreens = ScreenCount(dpy);
2265 for (i = 0; i < nscreens; i++)
2267 Screen *s = ScreenOfDisplay (dpy, i);
2268 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2270 found_any_writable_cells = True;
2275 #ifdef HAVE_XF86VMODE_GAMMA
2276 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2279 #ifdef HAVE_DPMS_EXTENSION
2281 int op = 0, event = 0, error = 0;
2282 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2283 dpms_supported = True;
2285 #endif /* HAVE_DPMS_EXTENSION */
2288 # define SENSITIZE(NAME,SENSITIVEP) \
2289 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2291 /* Blanking and Locking
2293 SENSITIZE ("lock_spinbutton", p->lock_p);
2294 SENSITIZE ("lock_mlabel", p->lock_p);
2298 SENSITIZE ("dpms_frame", dpms_supported);
2299 SENSITIZE ("dpms_button", dpms_supported);
2300 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2301 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2302 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2303 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2304 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2305 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2306 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2307 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2308 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2312 SENSITIZE ("cmap_frame", found_any_writable_cells);
2313 SENSITIZE ("install_button", found_any_writable_cells);
2314 SENSITIZE ("fade_button", found_any_writable_cells);
2315 SENSITIZE ("unfade_button", found_any_writable_cells);
2317 SENSITIZE ("fade_label", (found_any_writable_cells &&
2318 (p->fade_p || p->unfade_p)));
2319 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2320 (p->fade_p || p->unfade_p)));
2328 populate_popup_window (state *s)
2330 saver_preferences *p = &s->prefs;
2331 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2332 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2333 int list_elt = selected_list_element (s);
2334 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2335 ? s->list_elt_to_hack_number[list_elt]
2337 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2338 char *doc_string = 0;
2340 /* #### not in Gtk 1.2
2341 gtk_label_set_selectable (doc);
2347 free_conf_data (s->cdata);
2353 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2354 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2355 s->cdata = load_configurator (cmd_line, s->debug_p);
2356 if (s->cdata && s->cdata->widget)
2357 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget, TRUE, TRUE, 0);
2360 doc_string = (s->cdata
2361 ? s->cdata->description
2363 # else /* !HAVE_XML */
2364 doc_string = _("Descriptions not available: no XML support compiled in.");
2365 # endif /* !HAVE_XML */
2367 gtk_label_set_text (doc, (doc_string
2369 : _("No description available.")));
2374 sensitize_demo_widgets (state *s, Bool sensitive_p)
2376 const char *names1[] = { "demo", "settings" };
2377 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2378 "visual", "visual_combo" };
2380 for (i = 0; i < countof(names1); i++)
2382 GtkWidget *w = name_to_widget (s, names1[i]);
2383 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2385 for (i = 0; i < countof(names2); i++)
2387 GtkWidget *w = name_to_widget (s, names2[i]);
2388 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2393 /* Even though we've given these text fields a maximum number of characters,
2394 their default size is still about 30 characters wide -- so measure out
2395 a string in their font, and resize them to just fit that.
2398 fix_text_entry_sizes (state *s)
2401 const char * const spinbuttons[] = {
2402 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2403 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2404 "dpms_off_spinbutton",
2405 "-fade_spinbutton" };
2410 for (i = 0; i < countof(spinbuttons); i++)
2412 const char *n = spinbuttons[i];
2414 while (*n == '-') n++, cols--;
2415 w = GTK_WIDGET (name_to_widget (s, n));
2416 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2417 gtk_widget_set_usize (w, width, -2);
2420 /* Now fix the width of the combo box.
2422 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2423 w = GTK_COMBO (w)->entry;
2424 width = gdk_string_width (w->style->font, "PseudoColor___");
2425 gtk_widget_set_usize (w, width, -2);
2427 /* Now fix the width of the file entry text.
2429 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2430 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2431 gtk_widget_set_usize (w, width, -2);
2433 /* Now fix the width of the command line text.
2435 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2436 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2437 gtk_widget_set_usize (w, width, -2);
2439 /* Now fix the height of the list.
2444 int leading = 3; /* approximate is ok... */
2446 w = GTK_WIDGET (name_to_widget (s, "list"));
2447 height = w->style->font->ascent + w->style->font->descent;
2450 height += border * 2;
2451 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2452 gtk_widget_set_usize (w, -2, height);
2460 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2463 static char *up_arrow_xpm[] = {
2486 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2487 the end of the array (Gtk 1.2.5.) */
2488 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2489 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2492 static char *down_arrow_xpm[] = {
2515 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2516 the end of the array (Gtk 1.2.5.) */
2517 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2518 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2522 pixmapify_button (state *s, int down_p)
2526 GtkWidget *pixmapwid;
2530 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2531 style = gtk_widget_get_style (w);
2533 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2534 &style->bg[GTK_STATE_NORMAL],
2536 ? (gchar **) down_arrow_xpm
2537 : (gchar **) up_arrow_xpm));
2538 pixmapwid = gtk_pixmap_new (pixmap, mask);
2539 gtk_widget_show (pixmapwid);
2540 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2541 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2545 map_next_button_cb (GtkWidget *w, gpointer user_data)
2547 state *s = (state *) user_data;
2548 pixmapify_button (s, 1);
2552 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2554 state *s = (state *) user_data;
2555 pixmapify_button (s, 0);
2557 #endif /* !HAVE_GTK2 */
2560 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2564 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2565 GtkAllocation *allocation,
2569 GtkWidgetAuxInfo *aux_info;
2571 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2573 aux_info->width = allocation->width;
2574 aux_info->height = -2;
2578 gtk_widget_size_request (label, &req);
2582 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2585 eschew_gtk_lossage (GtkLabel *label)
2587 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2588 aux_info->width = GTK_WIDGET (label)->allocation.width;
2589 aux_info->height = -2;
2593 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2595 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2596 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2599 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2601 gtk_widget_queue_resize (GTK_WIDGET (label));
2606 populate_demo_window (state *s, int list_elt)
2608 saver_preferences *p = &s->prefs;
2611 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2612 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2613 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2614 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2615 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2617 if (p->mode == BLANK_ONLY)
2620 pretty_name = strdup (_("Blank Screen"));
2621 schedule_preview (s, 0);
2623 else if (p->mode == DONT_BLANK)
2626 pretty_name = strdup (_("Screen Saver Disabled"));
2627 schedule_preview (s, 0);
2631 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2632 ? s->list_elt_to_hack_number[list_elt]
2634 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2638 ? strdup (hack->name)
2639 : make_hack_name (hack->command))
2643 schedule_preview (s, hack->command);
2645 schedule_preview (s, 0);
2649 pretty_name = strdup (_("Preview"));
2651 gtk_frame_set_label (frame1, pretty_name);
2652 gtk_frame_set_label (frame2, pretty_name);
2654 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2655 gtk_entry_set_position (cmd, 0);
2659 sprintf (title, "%s: %.100s Settings",
2660 progclass, (pretty_name ? pretty_name : "???"));
2661 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2664 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2666 ? (hack->visual && *hack->visual
2671 sensitize_demo_widgets (s, (hack ? True : False));
2673 if (pretty_name) free (pretty_name);
2675 ensure_selected_item_visible (list);
2677 s->_selected_list_element = list_elt;
2682 widget_deleter (GtkWidget *widget, gpointer data)
2684 /* #### Well, I want to destroy these widgets, but if I do that, they get
2685 referenced again, and eventually I get a SEGV. So instead of
2686 destroying them, I'll just hide them, and leak a bunch of memory
2687 every time the disk file changes. Go go go Gtk!
2689 #### Ok, that's a lie, I get a crash even if I just hide the widget
2690 and don't ever delete it. Fuck!
2693 gtk_widget_destroy (widget);
2695 gtk_widget_hide (widget);
2700 static char **sort_hack_cmp_names_kludge;
2702 sort_hack_cmp (const void *a, const void *b)
2707 return strcmp (sort_hack_cmp_names_kludge[*(int *) a],
2708 sort_hack_cmp_names_kludge[*(int *) b]);
2713 initialize_sort_map (state *s)
2715 saver_preferences *p = &s->prefs;
2718 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2719 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2721 s->list_elt_to_hack_number = (int *)
2722 calloc (sizeof(int), p->screenhacks_count + 1);
2723 s->hack_number_to_list_elt = (int *)
2724 calloc (sizeof(int), p->screenhacks_count + 1);
2726 /* Initialize table to 1:1 mapping */
2727 for (i = 0; i < p->screenhacks_count; i++)
2728 s->list_elt_to_hack_number[i] = i;
2730 /* Generate list of names (once)
2732 sort_hack_cmp_names_kludge = (char **)
2733 calloc (sizeof(char *), p->screenhacks_count);
2734 for (i = 0; i < p->screenhacks_count; i++)
2736 screenhack *hack = p->screenhacks[i];
2737 char *name = (hack->name && *hack->name
2738 ? strdup (hack->name)
2739 : make_hack_name (hack->command));
2741 for (str = name; *str; str++)
2742 *str = tolower(*str);
2743 sort_hack_cmp_names_kludge[i] = name;
2746 /* Sort alphabetically
2748 qsort (s->list_elt_to_hack_number,
2749 p->screenhacks_count,
2750 sizeof(*s->list_elt_to_hack_number),
2755 for (i = 0; i < p->screenhacks_count; i++)
2756 free (sort_hack_cmp_names_kludge[i]);
2757 free (sort_hack_cmp_names_kludge);
2758 sort_hack_cmp_names_kludge = 0;
2760 /* Build inverse table */
2761 for (i = 0; i < p->screenhacks_count; i++)
2762 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2767 maybe_reload_init_file (state *s)
2769 saver_preferences *p = &s->prefs;
2772 static Bool reentrant_lock = False;
2773 if (reentrant_lock) return 0;
2774 reentrant_lock = True;
2776 if (init_file_changed_p (p))
2778 const char *f = init_file_name();
2783 if (!f || !*f) return 0;
2784 b = (char *) malloc (strlen(f) + 1024);
2787 "file \"%s\" has changed, reloading.\n"),
2789 warning_dialog (s->toplevel_widget, b, False, 100);
2793 initialize_sort_map (s);
2795 list_elt = selected_list_element (s);
2796 list = name_to_widget (s, "list");
2797 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
2798 populate_hack_list (s);
2799 force_list_select_item (s, list, list_elt, True);
2800 populate_prefs_page (s);
2801 populate_demo_window (s, list_elt);
2802 ensure_selected_item_visible (list);
2807 reentrant_lock = False;
2813 /* Making the preview window have the right X visual (so that GL works.)
2816 static Visual *get_best_gl_visual (state *);
2819 x_visual_to_gdk_visual (Visual *xv)
2821 GList *gvs = gdk_list_visuals();
2822 if (!xv) return gdk_visual_get_system();
2823 for (; gvs; gvs = gvs->next)
2825 GdkVisual *gv = (GdkVisual *) gvs->data;
2826 if (xv == GDK_VISUAL_XVISUAL (gv))
2829 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
2830 blurb(), (unsigned long) xv->visualid);
2835 clear_preview_window (state *s)
2840 if (!s->toplevel_widget) return; /* very early */
2841 p = name_to_widget (s, "preview");
2844 if (!window) return;
2846 /* Flush the widget background down into the window, in case a subproc
2848 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
2849 gdk_window_clear (window);
2853 GtkWidget *notebook;
2855 notebook = name_to_widget (s, "preview_notebook");
2856 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
2857 s->running_preview_error_p
2860 #else /* !HAVE_GTK2 */
2861 if (s->running_preview_error_p)
2863 const char * const lines[] = { N_("No Preview"), N_("Available") };
2864 int lh = p->style->font->ascent + p->style->font->descent;
2867 gdk_window_get_size (window, &w, &h);
2868 y = (h - (lh * countof(lines))) / 2;
2869 y += p->style->font->ascent;
2870 for (i = 0; i < countof(lines); i++)
2872 int sw = gdk_string_width (p->style->font, _(lines[i]));
2873 int x = (w - sw) / 2;
2874 gdk_draw_string (window, p->style->font,
2875 p->style->fg_gc[GTK_STATE_NORMAL],
2880 #endif /* !HAVE_GTK2 */
2887 fix_preview_visual (state *s)
2889 GtkWidget *widget = name_to_widget (s, "preview");
2890 Visual *xvisual = get_best_gl_visual (s);
2891 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
2892 GdkVisual *dvisual = gdk_visual_get_system();
2893 GdkColormap *cmap = (visual == dvisual
2894 ? gdk_colormap_get_system ()
2895 : gdk_colormap_new (visual, False));
2898 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
2899 (visual == dvisual ? "default" : "non-default"),
2900 (xvisual ? (unsigned long) xvisual->visualid : 0L));
2902 if (!GTK_WIDGET_REALIZED (widget) ||
2903 gtk_widget_get_visual (widget) != visual)
2905 gtk_widget_unrealize (widget);
2906 gtk_widget_set_visual (widget, visual);
2907 gtk_widget_set_colormap (widget, cmap);
2908 gtk_widget_realize (widget);
2911 /* Set the Widget colors to be white-on-black. */
2913 GdkWindow *window = widget->window;
2914 GtkStyle *style = gtk_style_copy (widget->style);
2915 GdkColormap *cmap = gtk_widget_get_colormap (widget);
2916 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
2917 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
2918 GdkGC *fgc = gdk_gc_new(window);
2919 GdkGC *bgc = gdk_gc_new(window);
2920 if (!gdk_color_white (cmap, fg)) abort();
2921 if (!gdk_color_black (cmap, bg)) abort();
2922 gdk_gc_set_foreground (fgc, fg);
2923 gdk_gc_set_background (fgc, bg);
2924 gdk_gc_set_foreground (bgc, bg);
2925 gdk_gc_set_background (bgc, fg);
2926 style->fg_gc[GTK_STATE_NORMAL] = fgc;
2927 style->bg_gc[GTK_STATE_NORMAL] = fgc;
2928 gtk_widget_set_style (widget, style);
2931 gtk_widget_show (widget);
2939 subproc_pretty_name (state *s)
2941 if (s->running_preview_cmd)
2943 char *ps = strdup (s->running_preview_cmd);
2944 char *ss = strchr (ps, ' ');
2946 ss = strrchr (ps, '/');
2952 return strdup ("???");
2957 reap_zombies (state *s)
2959 int wait_status = 0;
2961 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
2965 if (pid == s->running_preview_pid)
2967 char *ss = subproc_pretty_name (s);
2968 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(), pid, ss);
2972 fprintf (stderr, "%s: pid %lu died\n", blurb(), pid);
2978 /* Mostly lifted from driver/subprocs.c */
2980 get_best_gl_visual (state *s)
2982 Display *dpy = GDK_DISPLAY();
2991 av[ac++] = "xscreensaver-gl-helper";
2996 perror ("error creating pipe:");
3003 switch ((int) (forked = fork ()))
3007 sprintf (buf, "%s: couldn't fork", blurb());
3015 close (in); /* don't need this one */
3016 close (ConnectionNumber (dpy)); /* close display fd */
3018 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3020 perror ("could not dup() a new stdout:");
3024 execvp (av[0], av); /* shouldn't return. */
3026 if (errno != ENOENT)
3028 /* Ignore "no such file or directory" errors, unless verbose.
3029 Issue all other exec errors, though. */
3030 sprintf (buf, "%s: running %s", blurb(), av[0]);
3033 exit (1); /* exits fork */
3039 int wait_status = 0;
3041 FILE *f = fdopen (in, "r");
3042 unsigned long v = 0;
3045 close (out); /* don't need this one */
3048 fgets (buf, sizeof(buf)-1, f);
3051 /* Wait for the child to die. */
3052 waitpid (-1, &wait_status, 0);
3054 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3060 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3066 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3068 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3069 blurb(), av[0], result);
3081 kill_preview_subproc (state *s)
3083 s->running_preview_error_p = False;
3086 clear_preview_window (s);
3088 if (s->subproc_check_timer_id)
3090 gtk_timeout_remove (s->subproc_check_timer_id);
3091 s->subproc_check_timer_id = 0;
3092 s->subproc_check_countdown = 0;
3095 if (s->running_preview_pid)
3097 int status = kill (s->running_preview_pid, SIGTERM);
3098 char *ss = subproc_pretty_name (s);
3105 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3106 blurb(), s->running_preview_pid, ss);
3111 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3112 blurb(), s->running_preview_pid, ss);
3116 else if (s->debug_p)
3117 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3118 s->running_preview_pid, ss);
3121 s->running_preview_pid = 0;
3122 if (s->running_preview_cmd) free (s->running_preview_cmd);
3123 s->running_preview_cmd = 0;
3130 /* Immediately and unconditionally launches the given process,
3131 after appending the -window-id option; sets running_preview_pid.
3134 launch_preview_subproc (state *s)
3136 saver_preferences *p = &s->prefs;
3140 const char *cmd = s->desired_preview_cmd;
3142 GtkWidget *pr = name_to_widget (s, "preview");
3143 GdkWindow *window = pr->window;
3145 s->running_preview_error_p = False;
3147 if (s->preview_suppressed_p)
3149 kill_preview_subproc (s);
3153 new_cmd = malloc (strlen (cmd) + 40);
3155 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3158 /* No window id? No command to run. */
3164 strcpy (new_cmd, cmd);
3165 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X", id);
3168 kill_preview_subproc (s);
3171 s->running_preview_error_p = True;
3172 clear_preview_window (s);
3176 switch ((int) (forked = fork ()))
3181 sprintf (buf, "%s: couldn't fork", blurb());
3183 s->running_preview_error_p = True;
3189 close (ConnectionNumber (GDK_DISPLAY()));
3191 usleep (250000); /* pause for 1/4th second before launching, to give
3192 the previous program time to die and flush its X
3193 buffer, so we don't get leftover turds on the
3196 exec_command (p->shell, new_cmd, p->nice_inferior);
3197 /* Don't bother printing an error message when we are unable to
3198 exec subprocesses; we handle that by polling the pid later. */
3199 exit (1); /* exits child fork */
3204 if (s->running_preview_cmd) free (s->running_preview_cmd);
3205 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3206 s->running_preview_pid = forked;
3210 char *ss = subproc_pretty_name (s);
3211 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(), forked, ss);
3218 schedule_preview_check (s);
3221 if (new_cmd) free (new_cmd);
3226 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3229 hack_environment (state *s)
3231 static const char *def_path =
3232 # ifdef DEFAULT_PATH_PREFIX
3233 DEFAULT_PATH_PREFIX;
3238 Display *dpy = GDK_DISPLAY();
3239 const char *odpy = DisplayString (dpy);
3240 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3241 strcpy (ndpy, "DISPLAY=");
3242 strcat (ndpy, odpy);
3247 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3249 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3250 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3251 So we must leak it (and/or the previous setting). Yay.
3254 if (def_path && *def_path)
3256 const char *opath = getenv("PATH");
3257 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3258 strcpy (npath, "PATH=");
3259 strcat (npath, def_path);
3260 strcat (npath, ":");
3261 strcat (npath, opath);
3265 /* do not free(npath) -- see above */
3268 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3273 /* Called from a timer:
3274 Launches the currently-chosen subprocess, if it's not already running.
3275 If there's a different process running, kills it.
3278 update_subproc_timer (gpointer data)
3280 state *s = (state *) data;
3281 if (! s->desired_preview_cmd)
3282 kill_preview_subproc (s);
3283 else if (!s->running_preview_cmd ||
3284 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3285 launch_preview_subproc (s);
3287 s->subproc_timer_id = 0;
3288 return FALSE; /* do not re-execute timer */
3292 /* Call this when you think you might want a preview process running.
3293 It will set a timer that will actually launch that program a second
3294 from now, if you haven't changed your mind (to avoid double-click
3295 spazzing, etc.) `cmd' may be null meaning "no process".
3298 schedule_preview (state *s, const char *cmd)
3300 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3305 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3307 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3310 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3311 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3313 if (s->subproc_timer_id)
3314 gtk_timeout_remove (s->subproc_timer_id);
3315 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3319 /* Called from a timer:
3320 Checks to see if the subproc that should be running, actually is.
3323 check_subproc_timer (gpointer data)
3325 state *s = (state *) data;
3326 Bool again_p = True;
3328 if (s->running_preview_error_p || /* already dead */
3329 s->running_preview_pid <= 0)
3337 status = kill (s->running_preview_pid, 0);
3338 if (status < 0 && errno == ESRCH)
3339 s->running_preview_error_p = True;
3343 char *ss = subproc_pretty_name (s);
3344 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3345 s->running_preview_pid, ss,
3346 (s->running_preview_error_p ? "dead" : "alive"));
3350 if (s->running_preview_error_p)
3352 clear_preview_window (s);
3357 /* Otherwise, it's currently alive. We might be checking again, or we
3358 might be satisfied. */
3360 if (--s->subproc_check_countdown <= 0)
3364 return TRUE; /* re-execute timer */
3367 s->subproc_check_timer_id = 0;
3368 s->subproc_check_countdown = 0;
3369 return FALSE; /* do not re-execute timer */
3374 /* Call this just after launching a subprocess.
3375 This sets a timer that will, five times a second for two seconds,
3376 check whether the program is still running. The assumption here
3377 is that if the process didn't stay up for more than a couple of
3378 seconds, then either the program doesn't exist, or it doesn't
3379 take a -window-id argument.
3382 schedule_preview_check (state *s)
3388 fprintf (stderr, "%s: scheduling check\n", blurb());
3390 if (s->subproc_check_timer_id)
3391 gtk_timeout_remove (s->subproc_check_timer_id);
3392 s->subproc_check_timer_id =
3393 gtk_timeout_add (1000 / ticks,
3394 check_subproc_timer, (gpointer) s);
3395 s->subproc_check_countdown = ticks * seconds;
3400 screen_blanked_p (void)
3404 unsigned long nitems, bytesafter;
3406 Display *dpy = GDK_DISPLAY();
3407 Bool blanked_p = False;
3409 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3410 XA_SCREENSAVER_STATUS,
3411 0, 3, False, XA_INTEGER,
3412 &type, &format, &nitems, &bytesafter,
3413 (unsigned char **) &data)
3415 && type == XA_INTEGER
3418 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3420 if (data) free (data);
3425 /* Wake up every now and then and see if the screen is blanked.
3426 If it is, kill off the small-window demo -- no point in wasting
3427 cycles by running two screensavers at once...
3430 check_blanked_timer (gpointer data)
3432 state *s = (state *) data;
3433 Bool blanked_p = screen_blanked_p ();
3434 if (blanked_p && s->running_preview_pid)
3437 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3438 kill_preview_subproc (s);
3441 return True; /* re-execute timer */
3445 /* Setting window manager icon
3449 init_icon (GdkWindow *window)
3451 GdkBitmap *mask = 0;
3454 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3455 (gchar **) logo_50_xpm);
3457 gdk_window_set_icon (window, 0, pixmap, mask);
3461 /* The main demo-mode command loop.
3466 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3467 XrmRepresentation *type, XrmValue *value, XPointer closure)
3470 for (i = 0; quarks[i]; i++)
3472 if (bindings[i] == XrmBindTightly)
3473 fprintf (stderr, (i == 0 ? "" : "."));
3474 else if (bindings[i] == XrmBindLoosely)
3475 fprintf (stderr, "*");
3477 fprintf (stderr, " ??? ");
3478 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3481 fprintf (stderr, ": %s\n", (char *) value->addr);
3489 the_network_is_not_the_computer (state *s)
3491 Display *dpy = GDK_DISPLAY();
3492 char *rversion = 0, *ruser = 0, *rhost = 0;
3493 char *luser, *lhost;
3495 struct passwd *p = getpwuid (getuid ());
3496 const char *d = DisplayString (dpy);
3498 # if defined(HAVE_UNAME)
3500 if (uname (&uts) < 0)
3501 lhost = "<UNKNOWN>";
3503 lhost = uts.nodename;
3505 strcpy (lhost, getenv("SYS$NODE"));
3506 # else /* !HAVE_UNAME && !VMS */
3507 strcat (lhost, "<UNKNOWN>");
3508 # endif /* !HAVE_UNAME && !VMS */
3510 if (p && p->pw_name)
3515 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3517 /* Make a buffer that's big enough for a number of copies of all the
3518 strings, plus some. */
3519 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3520 (ruser ? strlen(ruser) : 0) +
3521 (rhost ? strlen(rhost) : 0) +
3528 if (!rversion || !*rversion)
3532 "The XScreenSaver daemon doesn't seem to be running\n"
3533 "on display \"%s\". Launch it now?"),
3536 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3538 /* Warn that the two processes are running as different users.
3542 "%s is running as user \"%s\" on host \"%s\".\n"
3543 "But the xscreensaver managing display \"%s\"\n"
3544 "is running as user \"%s\" on host \"%s\".\n"
3546 "Since they are different users, they won't be reading/writing\n"
3547 "the same ~/.xscreensaver file, so %s isn't\n"
3548 "going to work right.\n"
3550 "You should either re-run %s as \"%s\", or re-run\n"
3551 "xscreensaver as \"%s\".\n"
3553 "Restart the xscreensaver daemon now?\n"),
3554 blurb(), luser, lhost,
3556 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3558 blurb(), (ruser ? ruser : "???"),
3561 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3563 /* Warn that the two processes are running on different hosts.
3567 "%s is running as user \"%s\" on host \"%s\".\n"
3568 "But the xscreensaver managing display \"%s\"\n"
3569 "is running as user \"%s\" on host \"%s\".\n"
3571 "If those two machines don't share a file system (that is,\n"
3572 "if they don't see the same ~%s/.xscreensaver file) then\n"
3573 "%s won't work right.\n"
3575 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3576 blurb(), luser, lhost,
3578 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3583 else if (!!strcmp (rversion, s->short_version))
3585 /* Warn that the version numbers don't match.
3589 "This is %s version %s.\n"
3590 "But the xscreensaver managing display \"%s\"\n"
3591 "is version %s. This could cause problems.\n"
3593 "Restart the xscreensaver daemon now?\n"),
3594 progname, s->short_version,
3601 warning_dialog (s->toplevel_widget, msg, True, 1);
3603 if (rversion) free (rversion);
3604 if (ruser) free (ruser);
3605 if (rhost) free (rhost);
3610 /* We use this error handler so that X errors are preceeded by the name
3611 of the program that generated them.
3614 demo_ehandler (Display *dpy, XErrorEvent *error)
3616 state *s = global_state_kludge; /* I hate C so much... */
3617 fprintf (stderr, "\nX error in %s:\n", blurb());
3618 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3619 kill_preview_subproc (s);
3625 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3626 of the program that generated them; and also that we can ignore one
3627 particular bogus error message that Gdk madly spews.
3630 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3631 const gchar *message, gpointer user_data)
3633 /* Ignore the message "Got event for unknown window: 0x...".
3634 Apparently some events are coming in for the xscreensaver window
3635 (presumably reply events related to the ClientMessage) and Gdk
3636 feels the need to complain about them. So, just suppress any
3637 messages that look like that one.
3639 if (strstr (message, "unknown window"))
3642 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3643 (log_domain ? log_domain : progclass),
3644 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3645 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3646 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3647 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3648 log_level == G_LOG_LEVEL_INFO ? "info" :
3649 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3651 ((!*message || message[strlen(message)-1] != '\n')
3656 static char *defaults[] = {
3657 #include "XScreenSaver_ad.h"
3662 #ifdef HAVE_CRAPPLET
3663 static struct poptOption crapplet_options[] = {
3664 {NULL, '\0', 0, NULL, 0}
3666 #endif /* HAVE_CRAPPLET */
3669 const char *usage = "[--display dpy] [--prefs]"
3670 # ifdef HAVE_CRAPPLET
3673 "\n\t\t [--debug] [--sync] [--no-xshm]";
3676 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3678 state *s = (state *) user_data;
3679 Boolean oi = s->initializing_p;
3680 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3681 s->initializing_p = True;
3682 eschew_gtk_lossage (label);
3683 s->initializing_p = oi;
3689 print_widget_tree (GtkWidget *w, int depth)
3692 for (i = 0; i < depth; i++)
3693 fprintf (stderr, " ");
3694 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3696 if (GTK_IS_LIST (w))
3698 for (i = 0; i < depth+1; i++)
3699 fprintf (stderr, " ");
3700 fprintf (stderr, "...list kids...\n");
3702 else if (GTK_IS_CONTAINER (w))
3704 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3707 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3715 delayed_scroll_kludge (gpointer data)
3717 state *s = (state *) data;
3718 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
3719 ensure_selected_item_visible (w);
3721 /* Oh, this is just fucking lovely, too. */
3722 w = GTK_WIDGET (name_to_widget (s, "preview"));
3723 gtk_widget_hide (w);
3724 gtk_widget_show (w);
3726 return FALSE; /* do not re-execute timer */
3732 create_xscreensaver_demo (void)
3736 nb = name_to_widget (global_state_kludge, "preview_notebook");
3737 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
3739 return name_to_widget (global_state_kludge, "xscreensaver_demo");
3743 create_xscreensaver_settings_dialog (void)
3747 box = name_to_widget (global_state_kludge, "dialog_action_area");
3749 w = name_to_widget (global_state_kludge, "adv_button");
3750 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
3752 w = name_to_widget (global_state_kludge, "std_button");
3753 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
3755 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
3758 #endif /* HAVE_GTK2 */
3761 main (int argc, char **argv)
3765 saver_preferences *p;
3769 Widget toplevel_shell;
3770 char *real_progname = argv[0];
3771 char window_title[255];
3772 Bool crapplet_p = False;
3776 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
3777 textdomain (GETTEXT_PACKAGE);
3780 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3781 # else /* !HAVE_GTK2 */
3782 if (!setlocale (LC_ALL, ""))
3783 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
3784 # endif /* !HAVE_GTK2 */
3786 #endif /* ENABLE_NLS */
3788 str = strrchr (real_progname, '/');
3789 if (str) real_progname = str+1;
3792 memset (s, 0, sizeof(*s));
3793 s->initializing_p = True;
3796 global_state_kludge = s; /* I hate C so much... */
3798 progname = real_progname;
3800 s->short_version = (char *) malloc (5);
3801 memcpy (s->short_version, screensaver_id + 17, 4);
3802 s->short_version [4] = 0;
3805 /* Register our error message logger for every ``log domain'' known.
3806 There's no way to do this globally, so I grepped the Gtk/Gdk sources
3807 for all of the domains that seem to be in use.
3810 const char * const domains[] = { 0,
3811 "Gtk", "Gdk", "GLib", "GModule",
3812 "GThread", "Gnome", "GnomeUI" };
3813 for (i = 0; i < countof(domains); i++)
3814 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
3817 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
3820 const char *dir = DEFAULT_ICONDIR;
3821 if (*dir) add_pixmap_directory (dir);
3823 # endif /* !HAVE_GTK2 */
3824 #endif /* DEFAULT_ICONDIR */
3826 /* This is gross, but Gtk understands --display and not -display...
3828 for (i = 1; i < argc; i++)
3829 if (argv[i][0] && argv[i][1] &&
3830 !strncmp(argv[i], "-display", strlen(argv[i])))
3831 argv[i] = "--display";
3834 /* We need to parse this arg really early... Sigh. */
3835 for (i = 1; i < argc; i++)
3837 (!strcmp(argv[i], "--crapplet") ||
3838 !strcmp(argv[i], "--capplet")))
3840 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
3843 for (j = i; j < argc; j++) /* remove it from the list */
3844 argv[j] = argv[j+1];
3846 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
3847 fprintf (stderr, "%s: not compiled with --crapplet support\n",
3849 fprintf (stderr, "%s: %s\n", real_progname, usage);
3851 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
3854 (!strcmp(argv[i], "--debug") ||
3855 !strcmp(argv[i], "-debug") ||
3856 !strcmp(argv[i], "-d")))
3860 for (j = i; j < argc; j++) /* remove it from the list */
3861 argv[j] = argv[j+1];
3865 /* Let Gtk open the X connection, then initialize Xt to use that
3866 same connection. Doctor Frankenstein would be proud.
3868 # ifdef HAVE_CRAPPLET
3871 GnomeClient *client;
3872 GnomeClientFlags flags = 0;
3874 int init_results = gnome_capplet_init ("screensaver-properties",
3876 argc, argv, NULL, 0, NULL);
3878 0 upon successful initialization;
3879 1 if --init-session-settings was passed on the cmdline;
3880 2 if --ignore was passed on the cmdline;
3883 So the 1 signifies just to init the settings, and quit, basically.
3884 (Meaning launch the xscreensaver daemon.)
3887 if (init_results < 0)
3890 g_error ("An initialization error occurred while "
3891 "starting xscreensaver-capplet.\n");
3893 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
3894 real_progname, init_results);
3899 client = gnome_master_client ();
3902 flags = gnome_client_get_flags (client);
3904 if (flags & GNOME_CLIENT_IS_CONNECTED)
3907 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
3908 gnome_client_get_id (client));
3911 char *session_args[20];
3913 session_args[i++] = real_progname;
3914 session_args[i++] = "--capplet";
3915 session_args[i++] = "--init-session-settings";
3916 session_args[i] = 0;
3917 gnome_client_set_priority (client, 20);
3918 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
3919 gnome_client_set_restart_command (client, i, session_args);
3923 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
3926 gnome_client_flush (client);
3929 if (init_results == 1)
3931 system ("xscreensaver -nosplash &");
3937 # endif /* HAVE_CRAPPLET */
3939 gtk_init (&argc, &argv);
3943 /* We must read exactly the same resources as xscreensaver.
3944 That means we must have both the same progclass *and* progname,
3945 at least as far as the resource database is concerned. So,
3946 put "xscreensaver" in argv[0] while initializing Xt.
3948 argv[0] = "xscreensaver";
3952 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
3954 XtToolkitInitialize ();
3955 app = XtCreateApplicationContext ();
3956 dpy = GDK_DISPLAY();
3957 XtAppSetFallbackResources (app, defaults);
3958 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
3959 toplevel_shell = XtAppCreateShell (progname, progclass,
3960 applicationShellWidgetClass,
3963 dpy = XtDisplay (toplevel_shell);
3964 db = XtDatabase (dpy);
3965 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
3966 XSetErrorHandler (demo_ehandler);
3968 /* Let's just ignore these. They seem to confuse Irix Gtk... */
3969 signal (SIGPIPE, SIG_IGN);
3971 /* After doing Xt-style command-line processing, complain about any
3972 unrecognized command-line arguments.
3974 for (i = 1; i < argc; i++)
3976 char *str = argv[i];
3977 if (str[0] == '-' && str[1] == '-')
3979 if (!strcmp (str, "-prefs"))
3981 else if (crapplet_p)
3982 /* There are lots of random args that we don't care about when we're
3983 started as a crapplet, so just ignore unknown args in that case. */
3987 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname, argv[i]);
3988 fprintf (stderr, "%s: %s\n", real_progname, usage);
3993 /* Load the init file, which may end up consulting the X resource database
3994 and the site-wide app-defaults file. Note that at this point, it's
3995 important that `progname' be "xscreensaver", rather than whatever
4000 initialize_sort_map (s);
4002 /* Now that Xt has been initialized, and the resources have been read,
4003 we can set our `progname' variable to something more in line with
4006 progname = real_progname;
4010 /* Print out all the resources we read. */
4012 XrmName name = { 0 };
4013 XrmClass class = { 0 };
4015 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4021 /* Intern the atoms that xscreensaver_command() needs.
4023 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4024 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4025 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4026 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4027 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4028 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4029 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4030 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4031 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4032 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4033 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4034 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4035 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4038 /* Create the window and all its widgets.
4040 s->base_widget = create_xscreensaver_demo ();
4041 s->popup_widget = create_xscreensaver_settings_dialog ();
4042 s->toplevel_widget = s->base_widget;
4045 /* Set the main window's title. */
4047 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4048 char *s1, *s2, *s3, *s4;
4049 s1 = (char *) strchr(v, ' '); s1++;
4050 s2 = (char *) strchr(s1, ' ');
4051 s3 = (char *) strchr(v, '('); s3++;
4052 s4 = (char *) strchr(s3, ')');
4055 sprintf (window_title, "%.50s %.50s, %.50s", progclass, s1, s3);
4056 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4057 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4061 /* Adjust the (invisible) notebooks on the popup dialog... */
4063 GtkNotebook *notebook =
4064 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4065 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4069 gtk_widget_hide (std);
4070 # else /* !HAVE_XML */
4071 /* Make the advanced page be the only one available. */
4072 gtk_widget_set_sensitive (std, False);
4073 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4074 gtk_widget_hide (std);
4076 # endif /* !HAVE_XML */
4078 gtk_notebook_set_page (notebook, page);
4079 gtk_notebook_set_show_tabs (notebook, False);
4082 /* Various other widget initializations...
4084 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4085 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4087 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4088 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4091 populate_hack_list (s);
4092 populate_prefs_page (s);
4093 sensitize_demo_widgets (s, False);
4094 fix_text_entry_sizes (s);
4095 scroll_to_current_hack (s);
4097 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4098 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4102 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4103 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4105 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4106 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4108 #endif /* !HAVE_GTK2 */
4110 /* Hook up callbacks to the items on the mode menu. */
4112 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4113 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4114 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4115 for (; kids; kids = kids->next)
4116 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4117 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4122 /* Handle the -prefs command-line argument. */
4125 GtkNotebook *notebook =
4126 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4127 gtk_notebook_set_page (notebook, 1);
4130 # ifdef HAVE_CRAPPLET
4134 GtkWidget *outer_vbox;
4136 gtk_widget_hide (s->toplevel_widget);
4138 capplet = capplet_widget_new ();
4140 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4141 # ifdef HAVE_CRAPPLET_IMMEDIATE
4142 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4143 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4145 /* In crapplet-mode, take off the menubar. */
4146 gtk_widget_hide (name_to_widget (s, "menubar"));
4148 /* Reparent our top-level container to be a child of the capplet
4151 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4152 gtk_widget_ref (outer_vbox);
4153 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4155 GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4156 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4158 /* Find the window above us, and set the title and close handler. */
4160 GtkWidget *window = capplet;
4161 while (window && !GTK_IS_WINDOW (window))
4162 window = window->parent;
4165 gtk_window_set_title (GTK_WINDOW (window), window_title);
4166 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4167 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4172 s->toplevel_widget = capplet;
4174 # endif /* HAVE_CRAPPLET */
4177 gtk_widget_show (s->toplevel_widget);
4178 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4179 hack_environment (s);
4180 fix_preview_visual (s);
4182 /* Realize page zero, so that we can diddle the scrollbar when the
4183 user tabs back to it -- otherwise, the current hack isn't scrolled
4184 to the first time they tab back there, when started with "-prefs".
4185 (Though it is if they then tab away, and back again.)
4187 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4188 #### understands this crap, explain to me how to make this work.
4190 gtk_widget_realize (name_to_widget (s, "demos_table"));
4193 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4196 /* Issue any warnings about the running xscreensaver daemon. */
4197 the_network_is_not_the_computer (s);
4200 /* Run the Gtk event loop, and not the Xt event loop. This means that
4201 if there were Xt timers or fds registered, they would never get serviced,
4202 and if there were any Xt widgets, they would never have events delivered.
4203 Fortunately, we're using Gtk for all of the UI, and only initialized
4204 Xt so that we could process the command line and use the X resource
4207 s->initializing_p = False;
4209 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4210 after we start up. Otherwise, it always appears scrolled to the top
4211 when in crapplet-mode. */
4212 gtk_timeout_add (500, delayed_scroll_kludge, s);
4216 /* Load every configurator in turn, to scan them for errors all at once. */
4219 for (i = 0; i < p->screenhacks_count; i++)
4221 screenhack *hack = p->screenhacks[s->hack_number_to_list_elt[i]];
4222 conf_data *d = load_configurator (hack->command, False);
4223 if (d) free_conf_data (d);
4229 # ifdef HAVE_CRAPPLET
4231 capplet_gtk_main ();
4233 # endif /* HAVE_CRAPPLET */
4236 kill_preview_subproc (s);
4240 #endif /* HAVE_GTK -- whole file */