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 GtkTreeSelection *selection;
274 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
275 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
278 path = gtk_tree_model_get_path (model, &iter);
280 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (widget),
281 path, NULL, FALSE, 0.0, 0.0);
282 #else /* !HAVE_GTK2 */
284 GtkScrolledWindow *scroller = 0;
286 GtkList *list_widget = 0;
290 GtkWidget *selected = 0;
293 gint parent_h, child_y, child_h, children_h, ignore;
294 double ratio_t, ratio_b;
296 if (GTK_IS_SCROLLED_WINDOW (widget))
298 scroller = GTK_SCROLLED_WINDOW (widget);
299 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
300 list_widget = GTK_LIST (GTK_BIN(vp)->child);
302 else if (GTK_IS_VIEWPORT (widget))
304 vp = GTK_VIEWPORT (widget);
305 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
306 list_widget = GTK_LIST (GTK_BIN(vp)->child);
308 else if (GTK_IS_LIST (widget))
310 list_widget = GTK_LIST (widget);
311 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
312 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
317 slist = list_widget->selection;
318 selected = (slist ? GTK_WIDGET (slist->data) : 0);
322 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
324 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
325 kids; kids = kids->next)
328 adj = gtk_scrolled_window_get_vadjustment (scroller);
330 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
331 &ignore, &ignore, &ignore, &parent_h, &ignore);
332 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
333 &ignore, &child_y, &ignore, &child_h, &ignore);
334 children_h = nkids * child_h;
336 ratio_t = ((double) child_y) / ((double) children_h);
337 ratio_b = ((double) child_y + child_h) / ((double) children_h);
339 if (adj->upper == 0.0) /* no items in list */
342 if (ratio_t < (adj->value / adj->upper) ||
343 ratio_b > ((adj->value + adj->page_size) / adj->upper))
346 int slop = parent_h * 0.75; /* how much to overshoot by */
348 if (ratio_t < (adj->value / adj->upper))
350 double ratio_w = ((double) parent_h) / ((double) children_h);
351 double ratio_l = (ratio_b - ratio_t);
352 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
355 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
357 target = ratio_t * adj->upper;
361 if (target > adj->upper - adj->page_size)
362 target = adj->upper - adj->page_size;
366 gtk_adjustment_set_value (adj, target);
368 #endif /* !HAVE_GTK2 */
372 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
374 GtkWidget *shell = GTK_WIDGET (user_data);
375 while (shell->parent)
376 shell = shell->parent;
377 gtk_widget_destroy (GTK_WIDGET (shell));
381 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
383 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
385 restart_menu_cb (widget, user_data);
386 warning_dialog_dismiss_cb (widget, user_data);
390 warning_dialog (GtkWidget *parent, const char *message,
391 Boolean restart_button_p, int center)
393 char *msg = strdup (message);
396 GtkWidget *dialog = gtk_dialog_new ();
397 GtkWidget *label = 0;
399 GtkWidget *cancel = 0;
402 while (parent && !parent->window)
403 parent = parent->parent;
405 if (!GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
407 fprintf (stderr, "%s: too early for dialog?\n", progname);
415 char *s = strchr (head, '\n');
418 sprintf (name, "label%d", i++);
421 label = gtk_label_new (head);
423 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
424 #endif /* HAVE_GTK2 */
429 GTK_WIDGET (label)->style =
430 gtk_style_copy (GTK_WIDGET (label)->style);
431 GTK_WIDGET (label)->style->font =
432 gdk_font_load (get_string_resource("warning_dialog.headingFont",
434 gtk_widget_set_style (GTK_WIDGET (label),
435 GTK_WIDGET (label)->style);
437 #endif /* !HAVE_GTK2 */
439 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
440 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
441 label, TRUE, TRUE, 0);
442 gtk_widget_show (label);
453 label = gtk_label_new ("");
454 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
455 label, TRUE, TRUE, 0);
456 gtk_widget_show (label);
458 label = gtk_hbutton_box_new ();
459 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
460 label, TRUE, TRUE, 0);
463 if (restart_button_p)
465 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
466 gtk_container_add (GTK_CONTAINER (label), cancel);
469 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
470 gtk_container_add (GTK_CONTAINER (label), ok);
472 #else /* !HAVE_GTK2 */
474 ok = gtk_button_new_with_label ("OK");
475 gtk_container_add (GTK_CONTAINER (label), ok);
477 if (restart_button_p)
479 cancel = gtk_button_new_with_label ("Cancel");
480 gtk_container_add (GTK_CONTAINER (label), cancel);
483 #endif /* !HAVE_GTK2 */
485 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
486 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
487 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
488 GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
489 gtk_widget_show (ok);
490 gtk_widget_grab_focus (ok);
494 GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
495 gtk_widget_show (cancel);
497 gtk_widget_show (label);
498 gtk_widget_show (dialog);
500 if (restart_button_p)
502 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
503 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
505 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
506 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
511 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
512 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
516 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
517 GTK_WIDGET (parent)->window);
520 gtk_window_present (GTK_WINDOW (dialog));
521 #else /* !HAVE_GTK2 */
522 gdk_window_show (GTK_WIDGET (dialog)->window);
523 gdk_window_raise (GTK_WIDGET (dialog)->window);
524 #endif /* !HAVE_GTK2 */
531 run_cmd (state *s, Atom command, int arg)
536 flush_dialog_changes_and_save (s);
537 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
542 sprintf (buf, "Error:\n\n%s", err);
544 strcpy (buf, "Unknown error!");
545 warning_dialog (s->toplevel_widget, buf, False, 100);
552 run_hack (state *s, int list_elt, Bool report_errors_p)
555 if (list_elt < 0) return;
556 hack_number = s->list_elt_to_hack_number[list_elt];
558 flush_dialog_changes_and_save (s);
559 schedule_preview (s, 0);
561 run_cmd (s, XA_DEMO, hack_number + 1);
565 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
577 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
579 state *s = global_state_kludge; /* I hate C so much... */
580 flush_dialog_changes_and_save (s);
581 kill_preview_subproc (s);
586 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
588 state *s = (state *) data;
589 flush_dialog_changes_and_save (s);
595 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
598 char *vers = strdup (screensaver_id + 4);
601 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
603 s = strchr (vers, ',');
608 sprintf(copy, _("Copyright \xC2\xA9 1991-2002 %s"), s);
609 #else /* !HAVE_GTK2 */
610 sprintf(copy, _("Copyright \251 1991-2002 %s"), s);
611 #endif /* !HAVE_GTK2 */
613 sprintf (msg, "%s\n\n%s", copy, desc);
615 /* I can't make gnome_about_new() work here -- it starts dying in
616 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
617 then this might be the thing to do:
621 const gchar *auth[] = { 0 };
622 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
624 gtk_widget_show (about);
626 #else / * GTK but not GNOME * /
630 GdkColormap *colormap;
631 GdkPixmap *gdkpixmap;
634 GtkWidget *dialog = gtk_dialog_new ();
635 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
636 GtkWidget *parent = GTK_WIDGET (menuitem);
637 while (parent->parent)
638 parent = parent->parent;
640 hbox = gtk_hbox_new (FALSE, 20);
641 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
642 hbox, TRUE, TRUE, 0);
644 colormap = gtk_widget_get_colormap (parent);
646 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
647 (gchar **) logo_180_xpm);
648 icon = gtk_pixmap_new (gdkpixmap, mask);
649 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
651 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
653 vbox = gtk_vbox_new (FALSE, 0);
654 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
656 label1 = gtk_label_new (vers);
657 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
658 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
659 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
662 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
663 GTK_WIDGET (label1)->style->font =
664 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
665 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
666 #endif /* HAVE_GTK2 */
668 label2 = gtk_label_new (msg);
669 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
670 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
671 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
674 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
675 GTK_WIDGET (label2)->style->font =
676 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
677 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
678 #endif /* HAVE_GTK2 */
680 hb = gtk_hbutton_box_new ();
682 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
686 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
687 #else /* !HAVE_GTK2 */
688 ok = gtk_button_new_with_label (_("OK"));
689 #endif /* !HAVE_GTK2 */
690 gtk_container_add (GTK_CONTAINER (hb), ok);
692 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
693 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
694 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
696 gtk_widget_show (hbox);
697 gtk_widget_show (icon);
698 gtk_widget_show (vbox);
699 gtk_widget_show (label1);
700 gtk_widget_show (label2);
701 gtk_widget_show (hb);
702 gtk_widget_show (ok);
703 gtk_widget_show (dialog);
705 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
706 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
708 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
709 GTK_WIDGET (parent)->window);
710 gdk_window_show (GTK_WIDGET (dialog)->window);
711 gdk_window_raise (GTK_WIDGET (dialog)->window);
717 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
719 state *s = global_state_kludge; /* I hate C so much... */
720 saver_preferences *p = &s->prefs;
723 if (!p->help_url || !*p->help_url)
725 warning_dialog (s->toplevel_widget,
727 "No Help URL has been specified.\n"), False, 100);
731 help_command = (char *) malloc (strlen (p->load_url_command) +
732 (strlen (p->help_url) * 2) + 20);
733 strcpy (help_command, "( ");
734 sprintf (help_command + strlen(help_command),
735 p->load_url_command, p->help_url, p->help_url);
736 strcat (help_command, " ) &");
737 system (help_command);
743 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
745 state *s = global_state_kludge; /* I hate C so much... */
746 run_cmd (s, XA_ACTIVATE, 0);
751 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
753 state *s = global_state_kludge; /* I hate C so much... */
754 run_cmd (s, XA_LOCK, 0);
759 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
761 state *s = global_state_kludge; /* I hate C so much... */
762 run_cmd (s, XA_EXIT, 0);
767 restart_menu_cb (GtkWidget *widget, gpointer user_data)
769 state *s = global_state_kludge; /* I hate C so much... */
770 flush_dialog_changes_and_save (s);
771 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
773 system ("xscreensaver -nosplash &");
775 await_xscreensaver (s);
779 await_xscreensaver (state *s)
783 Display *dpy = GDK_DISPLAY();
784 /* GtkWidget *dialog = 0;*/
787 while (!rversion && (--countdown > 0))
789 /* Check for the version of the running xscreensaver... */
790 server_xscreensaver_version (dpy, &rversion, 0, 0);
792 /* If it's not there yet, wait a second... */
797 /* if (dialog) gtk_widget_destroy (dialog);*/
806 /* Timed out, no screensaver running. */
809 Bool root_p = (geteuid () == 0);
813 "The xscreensaver daemon did not start up properly.\n"
818 _("You are running as root. This usually means that xscreensaver\n"
819 "was unable to contact your X server because access control is\n"
820 "turned on. Try running this command:\n"
822 " xhost +localhost\n"
824 "and then selecting `File / Restart Daemon'.\n"
826 "Note that turning off access control will allow anyone logged\n"
827 "on to this machine to access your screen, which might be\n"
828 "considered a security problem. Please read the xscreensaver\n"
829 "manual and FAQ for more information.\n"
831 "You shouldn't run X as root. Instead, you should log in as a\n"
832 "normal user, and `su' as necessary."));
834 strcat (buf, _("Please check your $PATH and permissions."));
836 warning_dialog (s->toplevel_widget, buf, False, 1);
842 selected_list_element (state *s)
844 return s->_selected_list_element;
849 demo_write_init_file (state *s, saver_preferences *p)
853 /* #### try to figure out why shit keeps getting reordered... */
854 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
858 if (!write_init_file (p, s->short_version, False))
861 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
866 const char *f = init_file_name();
868 warning_dialog (s->toplevel_widget,
869 _("Error:\n\nCouldn't determine init file name!\n"),
873 char *b = (char *) malloc (strlen(f) + 1024);
874 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
875 warning_dialog (s->toplevel_widget, b, False, 100);
884 run_this_cb (GtkButton *button, gpointer user_data)
886 state *s = global_state_kludge; /* I hate C so much... */
887 int list_elt = selected_list_element (s);
888 if (list_elt < 0) return;
889 if (!flush_dialog_changes_and_save (s))
890 run_hack (s, list_elt, True);
895 manual_cb (GtkButton *button, gpointer user_data)
897 state *s = global_state_kludge; /* I hate C so much... */
898 saver_preferences *p = &s->prefs;
899 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
900 int list_elt = selected_list_element (s);
902 char *name, *name2, *cmd, *str;
903 if (list_elt < 0) return;
904 hack_number = s->list_elt_to_hack_number[list_elt];
906 flush_dialog_changes_and_save (s);
907 ensure_selected_item_visible (GTK_WIDGET (list_widget));
909 name = strdup (p->screenhacks[hack_number]->command);
911 while (isspace (*name2)) name2++;
913 while (*str && !isspace (*str)) str++;
915 str = strrchr (name2, '/');
916 if (str) name = str+1;
918 cmd = get_string_resource ("manualCommand", "ManualCommand");
921 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
923 sprintf (cmd2 + strlen (cmd2),
925 name2, name2, name2, name2);
926 strcat (cmd2, " ) &");
932 warning_dialog (GTK_WIDGET (button),
933 _("Error:\n\nno `manualCommand' resource set."),
942 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
944 GtkWidget *parent = name_to_widget (s, "scroller");
945 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
949 GtkTreeSelection *selection;
950 #endif /* HAVE_GTK2 */
952 if (!was) gtk_widget_set_sensitive (parent, True);
954 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
956 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
957 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
958 gtk_tree_selection_select_iter (selection, &iter);
959 #else /* !HAVE_GTK2 */
960 gtk_list_select_item (GTK_LIST (list), list_elt);
961 #endif /* !HAVE_GTK2 */
962 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
963 if (!was) gtk_widget_set_sensitive (parent, False);
968 run_next_cb (GtkButton *button, gpointer user_data)
970 state *s = global_state_kludge; /* I hate C so much... */
971 saver_preferences *p = &s->prefs;
972 Bool ops = s->preview_suppressed_p;
974 GtkWidget *list_widget = name_to_widget (s, "list");
975 int list_elt = selected_list_element (s);
982 if (list_elt >= p->screenhacks_count)
985 s->preview_suppressed_p = True;
987 flush_dialog_changes_and_save (s);
988 force_list_select_item (s, list_widget, list_elt, True);
989 populate_demo_window (s, list_elt);
990 run_hack (s, list_elt, False);
992 s->preview_suppressed_p = ops;
997 run_prev_cb (GtkButton *button, gpointer user_data)
999 state *s = global_state_kludge; /* I hate C so much... */
1000 saver_preferences *p = &s->prefs;
1001 Bool ops = s->preview_suppressed_p;
1003 GtkWidget *list_widget = name_to_widget (s, "list");
1004 int list_elt = selected_list_element (s);
1007 list_elt = p->screenhacks_count - 1;
1012 list_elt = p->screenhacks_count - 1;
1014 s->preview_suppressed_p = True;
1016 flush_dialog_changes_and_save (s);
1017 force_list_select_item (s, list_widget, list_elt, True);
1018 populate_demo_window (s, list_elt);
1019 run_hack (s, list_elt, False);
1021 s->preview_suppressed_p = ops;
1025 /* Writes the given settings into prefs.
1026 Returns true if there was a change, False otherwise.
1027 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1030 flush_changes (state *s,
1033 const char *command,
1036 saver_preferences *p = &s->prefs;
1037 Bool changed = False;
1040 if (list_elt < 0 || list_elt >= p->screenhacks_count)
1043 hack_number = s->list_elt_to_hack_number[list_elt];
1044 hack = p->screenhacks[hack_number];
1046 if (enabled_p != -1 &&
1047 enabled_p != hack->enabled_p)
1049 hack->enabled_p = enabled_p;
1052 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1053 blurb(), hack->name, enabled_p);
1058 if (!hack->command || !!strcmp (command, hack->command))
1060 if (hack->command) free (hack->command);
1061 hack->command = strdup (command);
1064 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1065 blurb(), hack->name, command);
1071 const char *ov = hack->visual;
1072 if (!ov || !*ov) ov = "any";
1073 if (!*visual) visual = "any";
1074 if (!!strcasecmp (visual, ov))
1076 if (hack->visual) free (hack->visual);
1077 hack->visual = strdup (visual);
1080 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1081 blurb(), hack->name, visual);
1089 /* Helper for the text fields that contain time specifications:
1090 this parses the text, and does error checking.
1093 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1098 if (!sec_p || strchr (line, ':'))
1099 value = parse_time ((char *) line, sec_p, True);
1103 if (sscanf (line, "%u%c", &value, &c) != 1)
1109 value *= 1000; /* Time measures in microseconds */
1115 "Unparsable time format: \"%s\"\n"),
1117 warning_dialog (s->toplevel_widget, b, False, 100);
1126 directory_p (const char *path)
1129 if (!path || !*path)
1131 else if (stat (path, &st))
1133 else if (!S_ISDIR (st.st_mode))
1140 normalize_directory (const char *path)
1144 if (!path) return 0;
1146 p2 = (char *) malloc (L + 2);
1148 if (p2[L-1] == '/') /* remove trailing slash */
1151 for (s = p2; s && *s; s++)
1154 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1155 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1158 while (s0 > p2 && s0[-1] != '/')
1168 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1169 strcpy (s, s+2), s--;
1170 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1174 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1175 while (s[0] == '/' && s[1] == '/')
1178 /* and strip trailing whitespace for good measure. */
1180 while (isspace(p2[L-1]))
1193 } FlushForeachClosure;
1196 flush_checkbox (GtkTreeModel *model,
1201 FlushForeachClosure *closure = data;
1204 gtk_tree_model_get (model, iter,
1205 COL_ENABLED, &checked,
1208 if (flush_changes (closure->s, closure->i,
1210 *closure->changed = True;
1214 /* don't remove row */
1218 #endif /* HAVE_GTK2 */
1220 /* Flush out any changes made in the main dialog window (where changes
1221 take place immediately: clicking on a checkbox causes the init file
1222 to be written right away.)
1225 flush_dialog_changes_and_save (state *s)
1227 saver_preferences *p = &s->prefs;
1228 saver_preferences P2, *p2 = &P2;
1230 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1231 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1232 FlushForeachClosure closure;
1233 #else /* !HAVE_GTK2 */
1234 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1235 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1237 #endif /* !HAVE_GTK2 */
1239 Bool changed = False;
1242 if (s->saving_p) return False;
1247 /* Flush any checkbox changes in the list down into the prefs struct.
1251 closure.changed = &changed;
1253 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1255 #else /* !HAVE_GTK2 */
1257 for (i = 0; kids; kids = kids->next, i++)
1259 GtkWidget *line = GTK_WIDGET (kids->data);
1260 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1261 GtkWidget *line_check =
1262 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1264 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1266 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1269 #endif /* ~HAVE_GTK2 */
1271 /* Flush the non-hack-specific settings down into the prefs struct.
1274 # define SECONDS(FIELD,NAME) \
1275 w = name_to_widget (s, (NAME)); \
1276 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1278 # define MINUTES(FIELD,NAME) \
1279 w = name_to_widget (s, (NAME)); \
1280 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1282 # define CHECKBOX(FIELD,NAME) \
1283 w = name_to_widget (s, (NAME)); \
1284 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1286 # define PATHNAME(FIELD,NAME) \
1287 w = name_to_widget (s, (NAME)); \
1288 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1290 MINUTES (&p2->timeout, "timeout_spinbutton");
1291 MINUTES (&p2->cycle, "cycle_spinbutton");
1292 CHECKBOX (p2->lock_p, "lock_button");
1293 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1295 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1296 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1297 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1298 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1300 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1301 CHECKBOX (p2->grab_video_p, "grab_video_button");
1302 CHECKBOX (p2->random_image_p, "grab_image_button");
1303 PATHNAME (p2->image_directory, "image_text");
1305 CHECKBOX (p2->verbose_p, "verbose_button");
1306 CHECKBOX (p2->capture_stderr_p, "capture_button");
1307 CHECKBOX (p2->splash_p, "splash_button");
1309 CHECKBOX (p2->install_cmap_p, "install_button");
1310 CHECKBOX (p2->fade_p, "fade_button");
1311 CHECKBOX (p2->unfade_p, "unfade_button");
1312 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1319 /* Warn if the image directory doesn't exist.
1321 if (p2->image_directory &&
1322 *p2->image_directory &&
1323 !directory_p (p2->image_directory))
1326 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1327 p2->image_directory);
1328 warning_dialog (s->toplevel_widget, b, False, 100);
1332 /* Map the mode menu to `saver_mode' enum values. */
1334 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1335 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1336 GtkWidget *selected = gtk_menu_get_active (menu);
1337 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1338 int menu_elt = g_list_index (kids, (gpointer) selected);
1339 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1340 p2->mode = mode_menu_order[menu_elt];
1343 if (p2->mode == ONE_HACK)
1345 int list_elt = selected_list_element (s);
1346 p2->selected_hack = (list_elt >= 0
1347 ? s->list_elt_to_hack_number[list_elt]
1351 # define COPY(field, name) \
1352 if (p->field != p2->field) { \
1355 fprintf (stderr, "%s: %s => %d\n", blurb(), name, p2->field); \
1357 p->field = p2->field
1360 COPY(selected_hack, "selected_hack");
1362 COPY(timeout, "timeout");
1363 COPY(cycle, "cycle");
1364 COPY(lock_p, "lock_p");
1365 COPY(lock_timeout, "lock_timeout");
1367 COPY(dpms_enabled_p, "dpms_enabled_p");
1368 COPY(dpms_standby, "dpms_standby");
1369 COPY(dpms_suspend, "dpms_suspend");
1370 COPY(dpms_off, "dpms_off");
1372 COPY(verbose_p, "verbose_p");
1373 COPY(capture_stderr_p, "capture_stderr_p");
1374 COPY(splash_p, "splash_p");
1376 COPY(install_cmap_p, "install_cmap_p");
1377 COPY(fade_p, "fade_p");
1378 COPY(unfade_p, "unfade_p");
1379 COPY(fade_seconds, "fade_seconds");
1381 COPY(grab_desktop_p, "grab_desktop_p");
1382 COPY(grab_video_p, "grab_video_p");
1383 COPY(random_image_p, "random_image_p");
1387 if (!p->image_directory ||
1388 !p2->image_directory ||
1389 strcmp(p->image_directory, p2->image_directory))
1393 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1394 blurb(), p2->image_directory);
1396 if (p->image_directory && p->image_directory != p2->image_directory)
1397 free (p->image_directory);
1398 p->image_directory = p2->image_directory;
1399 p2->image_directory = 0;
1401 populate_prefs_page (s);
1405 Display *dpy = GDK_DISPLAY();
1406 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1407 sync_server_dpms_settings (dpy, enabled_p,
1408 p->dpms_standby / 1000,
1409 p->dpms_suspend / 1000,
1413 changed = demo_write_init_file (s, p);
1416 s->saving_p = False;
1421 /* Flush out any changes made in the popup dialog box (where changes
1422 take place only when the OK button is clicked.)
1425 flush_popup_changes_and_save (state *s)
1427 Bool changed = False;
1428 saver_preferences *p = &s->prefs;
1429 int list_elt = selected_list_element (s);
1431 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1432 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1434 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1435 const char *command = gtk_entry_get_text (cmd);
1440 if (s->saving_p) return False;
1446 if (maybe_reload_init_file (s) != 0)
1452 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1454 if (!strcasecmp (visual, "")) visual = "";
1455 else if (!strcasecmp (visual, "any")) visual = "";
1456 else if (!strcasecmp (visual, "default")) visual = "Default";
1457 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1458 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1459 else if (!strcasecmp (visual, "best")) visual = "Best";
1460 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1461 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1462 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1463 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1464 else if (!strcasecmp (visual, "color")) visual = "Color";
1465 else if (!strcasecmp (visual, "gl")) visual = "GL";
1466 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1467 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1468 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1469 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1470 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1471 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1472 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1473 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
1474 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1477 gdk_beep (); /* unparsable */
1479 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1482 changed = flush_changes (s, list_elt, -1, command, visual);
1485 changed = demo_write_init_file (s, p);
1487 /* Do this to re-launch the hack if (and only if) the command line
1489 populate_demo_window (s, selected_list_element (s));
1493 s->saving_p = False;
1499 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1501 state *s = global_state_kludge; /* I hate C so much... */
1502 if (! s->initializing_p)
1504 s->initializing_p = True;
1505 flush_dialog_changes_and_save (s);
1506 s->initializing_p = False;
1511 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1513 pref_changed_cb (widget, user_data);
1517 /* Callback on menu items in the "mode" options menu.
1520 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1522 state *s = (state *) user_data;
1523 saver_preferences *p = &s->prefs;
1524 GtkWidget *list = name_to_widget (s, "list");
1527 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1529 saver_mode new_mode;
1530 int old_selected = p->selected_hack;
1534 if (menu_items->data == widget)
1537 menu_items = menu_items->next;
1539 if (!menu_items) abort();
1541 new_mode = mode_menu_order[menu_index];
1543 /* Keep the same list element displayed as before; except if we're
1544 switching *to* "one screensaver" mode from any other mode, scroll
1545 to and select "the one".
1548 if (new_mode == ONE_HACK)
1549 list_elt = (p->selected_hack >= 0
1550 ? s->hack_number_to_list_elt[p->selected_hack]
1554 list_elt = selected_list_element (s);
1557 saver_mode old_mode = p->mode;
1559 populate_demo_window (s, list_elt);
1560 force_list_select_item (s, list, list_elt, True);
1561 p->mode = old_mode; /* put it back, so the init file gets written */
1564 pref_changed_cb (widget, user_data);
1566 if (old_selected != p->selected_hack)
1567 abort(); /* dammit, not again... */
1572 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1573 gint page_num, gpointer user_data)
1575 state *s = global_state_kludge; /* I hate C so much... */
1576 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1578 /* If we're switching to page 0, schedule the current hack to be run.
1579 Otherwise, schedule it to stop. */
1581 populate_demo_window (s, selected_list_element (s));
1583 schedule_preview (s, 0);
1588 list_activated_cb (GtkTreeView *list,
1590 GtkTreeViewColumn *column,
1597 g_return_if_fail (!gdk_pointer_is_grabbed ());
1599 str = gtk_tree_path_to_string (path);
1600 list_elt = strtol (str, NULL, 10);
1604 run_hack (s, list_elt, True);
1608 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1610 state *s = (state *)data;
1611 GtkTreeModel *model;
1617 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1620 path = gtk_tree_model_get_path (model, &iter);
1621 str = gtk_tree_path_to_string (path);
1622 list_elt = strtol (str, NULL, 10);
1624 gtk_tree_path_free (path);
1627 populate_demo_window (s, list_elt);
1628 flush_dialog_changes_and_save (s);
1631 #else /* !HAVE_GTK2 */
1633 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1634 list_select_cb that comes in
1635 *after* we've double-clicked.
1639 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1642 state *s = (state *) data;
1643 if (event->type == GDK_2BUTTON_PRESS)
1645 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1646 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1648 last_doubleclick_time = time ((time_t *) 0);
1651 run_hack (s, list_elt, True);
1659 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1661 state *s = (state *) data;
1662 time_t now = time ((time_t *) 0);
1664 if (now >= last_doubleclick_time + 2)
1666 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1667 populate_demo_window (s, list_elt);
1668 flush_dialog_changes_and_save (s);
1673 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1675 state *s = (state *) data;
1676 populate_demo_window (s, -1);
1677 flush_dialog_changes_and_save (s);
1680 #endif /* !HAVE_GTK2 */
1683 /* Called when the checkboxes that are in the left column of the
1684 scrolling list are clicked. This both populates the right pane
1685 (just as clicking on the label (really, listitem) does) and
1686 also syncs this checkbox with the right pane Enabled checkbox.
1691 GtkCellRendererToggle *toggle,
1693 #else /* !HAVE_GTK2 */
1695 #endif /* !HAVE_GTK2 */
1698 state *s = (state *) data;
1701 GtkScrolledWindow *scroller =
1702 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1703 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1704 GtkTreeModel *model = gtk_tree_view_get_model (list);
1705 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1708 #else /* !HAVE_GTK2 */
1709 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1710 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1712 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1713 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1714 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1715 #endif /* !HAVE_GTK2 */
1722 if (!gtk_tree_model_get_iter (model, &iter, path))
1724 g_warning ("bad path: %s", path_string);
1727 gtk_tree_path_free (path);
1729 gtk_tree_model_get (model, &iter,
1730 COL_ENABLED, &active,
1733 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1734 COL_ENABLED, !active,
1737 list_elt = strtol (path_string, NULL, 10);
1738 #else /* !HAVE_GTK2 */
1739 list_elt = gtk_list_child_position (list, line);
1740 #endif /* !HAVE_GTK2 */
1742 /* remember previous scroll position of the top of the list */
1743 adj = gtk_scrolled_window_get_vadjustment (scroller);
1744 scroll_top = adj->value;
1746 flush_dialog_changes_and_save (s);
1747 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1748 populate_demo_window (s, list_elt);
1750 /* restore the previous scroll position of the top of the list.
1751 this is weak, but I don't really know why it's moving... */
1752 gtk_adjustment_set_value (adj, scroll_top);
1758 GtkFileSelection *widget;
1759 } file_selection_data;
1764 store_image_directory (GtkWidget *button, gpointer user_data)
1766 file_selection_data *fsd = (file_selection_data *) user_data;
1767 state *s = fsd->state;
1768 GtkFileSelection *selector = fsd->widget;
1769 GtkWidget *top = s->toplevel_widget;
1770 saver_preferences *p = &s->prefs;
1771 const char *path = gtk_file_selection_get_filename (selector);
1773 if (p->image_directory && !strcmp(p->image_directory, path))
1774 return; /* no change */
1776 if (!directory_p (path))
1779 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1780 warning_dialog (GTK_WIDGET (top), b, False, 100);
1784 if (p->image_directory) free (p->image_directory);
1785 p->image_directory = normalize_directory (path);
1787 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1788 (p->image_directory ? p->image_directory : ""));
1789 demo_write_init_file (s, p);
1794 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1796 file_selection_data *fsd = (file_selection_data *) user_data;
1797 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1801 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1803 browse_image_dir_cancel (button, user_data);
1804 store_image_directory (button, user_data);
1808 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1810 browse_image_dir_cancel (widget, user_data);
1815 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1817 state *s = global_state_kludge; /* I hate C so much... */
1818 saver_preferences *p = &s->prefs;
1819 static file_selection_data *fsd = 0;
1821 GtkFileSelection *selector = GTK_FILE_SELECTION(
1822 gtk_file_selection_new ("Please select the image directory."));
1825 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1827 fsd->widget = selector;
1830 if (p->image_directory && *p->image_directory)
1831 gtk_file_selection_set_filename (selector, p->image_directory);
1833 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1834 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1836 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1837 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1839 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1840 GTK_SIGNAL_FUNC (browse_image_dir_close),
1843 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1845 gtk_window_set_modal (GTK_WINDOW (selector), True);
1846 gtk_widget_show (GTK_WIDGET (selector));
1851 settings_cb (GtkButton *button, gpointer user_data)
1853 state *s = global_state_kludge; /* I hate C so much... */
1854 int list_elt = selected_list_element (s);
1856 populate_demo_window (s, list_elt); /* reset the widget */
1857 populate_popup_window (s); /* create UI on popup window */
1858 gtk_widget_show (s->popup_widget);
1862 settings_sync_cmd_text (state *s)
1865 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1866 char *cmd_line = get_configurator_command_line (s->cdata);
1867 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1868 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1870 # endif /* HAVE_XML */
1874 settings_adv_cb (GtkButton *button, gpointer user_data)
1876 state *s = global_state_kludge; /* I hate C so much... */
1877 GtkNotebook *notebook =
1878 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1880 settings_sync_cmd_text (s);
1881 gtk_notebook_set_page (notebook, 1);
1885 settings_std_cb (GtkButton *button, gpointer user_data)
1887 state *s = global_state_kludge; /* I hate C so much... */
1888 GtkNotebook *notebook =
1889 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1891 /* Re-create UI to reflect the in-progress command-line settings. */
1892 populate_popup_window (s);
1894 gtk_notebook_set_page (notebook, 0);
1898 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1899 gint page_num, gpointer user_data)
1901 state *s = global_state_kludge; /* I hate C so much... */
1902 GtkWidget *adv = name_to_widget (s, "adv_button");
1903 GtkWidget *std = name_to_widget (s, "std_button");
1907 gtk_widget_show (adv);
1908 gtk_widget_hide (std);
1910 else if (page_num == 1)
1912 gtk_widget_hide (adv);
1913 gtk_widget_show (std);
1922 settings_cancel_cb (GtkButton *button, gpointer user_data)
1924 state *s = global_state_kludge; /* I hate C so much... */
1925 gtk_widget_hide (s->popup_widget);
1929 settings_ok_cb (GtkButton *button, gpointer user_data)
1931 state *s = global_state_kludge; /* I hate C so much... */
1932 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1933 int page = gtk_notebook_get_current_page (notebook);
1936 /* Regenerate the command-line from the widget contents before saving.
1937 But don't do this if we're looking at the command-line page already,
1938 or we will blow away what they typed... */
1939 settings_sync_cmd_text (s);
1941 flush_popup_changes_and_save (s);
1942 gtk_widget_hide (s->popup_widget);
1946 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1948 state *s = (state *) data;
1949 settings_cancel_cb (0, (gpointer) s);
1954 /* Populating the various widgets
1958 /* Returns the number of the last hack run by the server.
1961 server_current_hack (void)
1965 unsigned long nitems, bytesafter;
1967 Display *dpy = GDK_DISPLAY();
1968 int hack_number = -1;
1970 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1971 XA_SCREENSAVER_STATUS,
1972 0, 3, False, XA_INTEGER,
1973 &type, &format, &nitems, &bytesafter,
1974 (unsigned char **) &data)
1976 && type == XA_INTEGER
1979 hack_number = (int) data[2] - 1;
1981 if (data) free (data);
1987 /* Finds the number of the last hack to run, and makes that item be
1988 selected by default.
1991 scroll_to_current_hack (state *s)
1993 saver_preferences *p = &s->prefs;
1996 if (p->mode == ONE_HACK)
1997 hack_number = p->selected_hack;
1999 hack_number = server_current_hack ();
2001 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2003 int list_elt = s->hack_number_to_list_elt[hack_number];
2004 GtkWidget *list = name_to_widget (s, "list");
2005 force_list_select_item (s, list, list_elt, True);
2006 populate_demo_window (s, list_elt);
2012 populate_hack_list (state *s)
2015 saver_preferences *p = &s->prefs;
2016 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2017 GtkListStore *model;
2018 GtkTreeSelection *selection;
2019 GtkCellRenderer *ren;
2023 g_object_get (G_OBJECT (list),
2028 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2029 g_object_set (G_OBJECT (list), "model", model, NULL);
2030 g_object_unref (model);
2032 ren = gtk_cell_renderer_toggle_new ();
2033 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2035 "active", COL_ENABLED,
2038 g_signal_connect (ren, "toggled",
2039 G_CALLBACK (list_checkbox_cb),
2042 ren = gtk_cell_renderer_text_new ();
2043 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2044 _("Screen Saver"), ren,
2048 g_signal_connect_after (list, "row_activated",
2049 G_CALLBACK (list_activated_cb),
2052 selection = gtk_tree_view_get_selection (list);
2053 g_signal_connect (selection, "changed",
2054 G_CALLBACK (list_select_changed_cb),
2059 for (i = 0; i < p->screenhacks_count; i++)
2061 screenhack *hack = p->screenhacks[s->list_elt_to_hack_number[i]];
2063 char *pretty_name = (hack->name
2064 ? strdup (hack->name)
2065 : make_hack_name (hack->command));
2067 gtk_list_store_append (model, &iter);
2068 gtk_list_store_set (model, &iter,
2069 COL_ENABLED, hack->enabled_p,
2070 COL_NAME, pretty_name,
2076 #else /* !HAVE_GTK2 */
2078 saver_preferences *p = &s->prefs;
2079 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2081 for (i = 0; i < p->screenhacks_count; i++)
2083 screenhack *hack = p->screenhacks[s->list_elt_to_hack_number[i]];
2085 /* A GtkList must contain only GtkListItems, but those can contain
2086 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2087 and a Label. We handle single and double click events on the
2088 line itself, for clicking on the text, but the interior checkbox
2089 also handles its own events.
2092 GtkWidget *line_hbox;
2093 GtkWidget *line_check;
2094 GtkWidget *line_label;
2096 char *pretty_name = (hack->name
2097 ? strdup (hack->name)
2098 : make_hack_name (hack->command));
2100 line = gtk_list_item_new ();
2101 line_hbox = gtk_hbox_new (FALSE, 0);
2102 line_check = gtk_check_button_new ();
2103 line_label = gtk_label_new (pretty_name);
2105 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2106 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2107 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2109 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2111 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2113 gtk_widget_show (line_check);
2114 gtk_widget_show (line_label);
2115 gtk_widget_show (line_hbox);
2116 gtk_widget_show (line);
2120 gtk_container_add (GTK_CONTAINER (list), line);
2121 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2122 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2125 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2126 GTK_SIGNAL_FUNC (list_checkbox_cb),
2130 GTK_WIDGET (GTK_BIN(line)->child)->style =
2131 gtk_style_copy (GTK_WIDGET (text_line)->style);
2133 gtk_widget_show (line);
2136 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2137 GTK_SIGNAL_FUNC (list_select_cb),
2139 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2140 GTK_SIGNAL_FUNC (list_unselect_cb),
2142 #endif /* !HAVE_GTK2 */
2146 update_list_sensitivity (state *s)
2148 saver_preferences *p = &s->prefs;
2149 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
2150 Bool checkable = (p->mode == RANDOM_HACKS);
2151 Bool blankable = (p->mode != DONT_BLANK);
2154 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2155 GtkWidget *use = name_to_widget (s, "use_col_frame");
2156 #endif /* HAVE_GTK2 */
2157 GtkWidget *scroller = name_to_widget (s, "scroller");
2158 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2159 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2162 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2163 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2164 #else /* !HAVE_GTK2 */
2165 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2166 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2168 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2169 #endif /* !HAVE_GTK2 */
2170 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2171 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2173 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2176 gtk_tree_view_column_set_visible (use, checkable);
2177 #else /* !HAVE_GTK2 */
2179 gtk_widget_show (use); /* the "Use" column header */
2181 gtk_widget_hide (use);
2185 GtkBin *line = GTK_BIN (kids->data);
2186 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2187 GtkWidget *line_check =
2188 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2191 gtk_widget_show (line_check);
2193 gtk_widget_hide (line_check);
2197 #endif /* !HAVE_GTK2 */
2202 populate_prefs_page (state *s)
2204 saver_preferences *p = &s->prefs;
2206 /* The file supports timeouts of less than a minute, but the GUI does
2207 not, so throttle the values to be at least one minute (since "0" is
2208 a bad rounding choice...)
2210 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2213 THROTTLE (passwd_timeout);
2216 # define FMT_MINUTES(NAME,N) \
2217 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2219 # define FMT_SECONDS(NAME,N) \
2220 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2222 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2223 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2224 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2225 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2226 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2227 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2228 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2233 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2234 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2237 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2238 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2239 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2240 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2241 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2242 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2243 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2244 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2245 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2246 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2247 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2249 # undef TOGGLE_ACTIVE
2251 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2252 (p->image_directory ? p->image_directory : ""));
2253 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2255 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2258 /* Map the `saver_mode' enum to mode menu to values. */
2260 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2263 for (i = 0; i < countof(mode_menu_order); i++)
2264 if (mode_menu_order[i] == p->mode)
2266 gtk_option_menu_set_history (opt, i);
2267 update_list_sensitivity (s);
2271 Bool found_any_writable_cells = False;
2272 Bool dpms_supported = False;
2274 Display *dpy = GDK_DISPLAY();
2275 int nscreens = ScreenCount(dpy);
2277 for (i = 0; i < nscreens; i++)
2279 Screen *s = ScreenOfDisplay (dpy, i);
2280 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2282 found_any_writable_cells = True;
2287 #ifdef HAVE_XF86VMODE_GAMMA
2288 found_any_writable_cells = True; /* if we can gamma fade, go for it */
2291 #ifdef HAVE_DPMS_EXTENSION
2293 int op = 0, event = 0, error = 0;
2294 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2295 dpms_supported = True;
2297 #endif /* HAVE_DPMS_EXTENSION */
2300 # define SENSITIZE(NAME,SENSITIVEP) \
2301 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2303 /* Blanking and Locking
2305 SENSITIZE ("lock_spinbutton", p->lock_p);
2306 SENSITIZE ("lock_mlabel", p->lock_p);
2310 SENSITIZE ("dpms_frame", dpms_supported);
2311 SENSITIZE ("dpms_button", dpms_supported);
2312 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2313 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2314 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2315 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2316 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2317 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2318 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2319 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2320 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2324 SENSITIZE ("cmap_frame", found_any_writable_cells);
2325 SENSITIZE ("install_button", found_any_writable_cells);
2326 SENSITIZE ("fade_button", found_any_writable_cells);
2327 SENSITIZE ("unfade_button", found_any_writable_cells);
2329 SENSITIZE ("fade_label", (found_any_writable_cells &&
2330 (p->fade_p || p->unfade_p)));
2331 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2332 (p->fade_p || p->unfade_p)));
2340 populate_popup_window (state *s)
2342 saver_preferences *p = &s->prefs;
2343 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2344 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2345 int list_elt = selected_list_element (s);
2346 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2347 ? s->list_elt_to_hack_number[list_elt]
2349 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2350 char *doc_string = 0;
2352 /* #### not in Gtk 1.2
2353 gtk_label_set_selectable (doc);
2359 free_conf_data (s->cdata);
2365 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2366 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2367 s->cdata = load_configurator (cmd_line, s->debug_p);
2368 if (s->cdata && s->cdata->widget)
2369 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget, TRUE, TRUE, 0);
2372 doc_string = (s->cdata
2373 ? s->cdata->description
2375 # else /* !HAVE_XML */
2376 doc_string = _("Descriptions not available: no XML support compiled in.");
2377 # endif /* !HAVE_XML */
2379 gtk_label_set_text (doc, (doc_string
2381 : _("No description available.")));
2386 sensitize_demo_widgets (state *s, Bool sensitive_p)
2388 const char *names1[] = { "demo", "settings" };
2389 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2390 "visual", "visual_combo" };
2392 for (i = 0; i < countof(names1); i++)
2394 GtkWidget *w = name_to_widget (s, names1[i]);
2395 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2397 for (i = 0; i < countof(names2); i++)
2399 GtkWidget *w = name_to_widget (s, names2[i]);
2400 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2405 /* Even though we've given these text fields a maximum number of characters,
2406 their default size is still about 30 characters wide -- so measure out
2407 a string in their font, and resize them to just fit that.
2410 fix_text_entry_sizes (state *s)
2413 const char * const spinbuttons[] = {
2414 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2415 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2416 "dpms_off_spinbutton",
2417 "-fade_spinbutton" };
2422 for (i = 0; i < countof(spinbuttons); i++)
2424 const char *n = spinbuttons[i];
2426 while (*n == '-') n++, cols--;
2427 w = GTK_WIDGET (name_to_widget (s, n));
2428 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2429 gtk_widget_set_usize (w, width, -2);
2432 /* Now fix the width of the combo box.
2434 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2435 w = GTK_COMBO (w)->entry;
2436 width = gdk_string_width (w->style->font, "PseudoColor___");
2437 gtk_widget_set_usize (w, width, -2);
2439 /* Now fix the width of the file entry text.
2441 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2442 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2443 gtk_widget_set_usize (w, width, -2);
2445 /* Now fix the width of the command line text.
2447 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2448 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2449 gtk_widget_set_usize (w, width, -2);
2451 /* Now fix the height of the list.
2456 int leading = 3; /* approximate is ok... */
2458 w = GTK_WIDGET (name_to_widget (s, "list"));
2459 height = w->style->font->ascent + w->style->font->descent;
2462 height += border * 2;
2463 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2464 gtk_widget_set_usize (w, -2, height);
2472 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2475 static char *up_arrow_xpm[] = {
2498 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2499 the end of the array (Gtk 1.2.5.) */
2500 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2501 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2504 static char *down_arrow_xpm[] = {
2527 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2528 the end of the array (Gtk 1.2.5.) */
2529 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2530 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2534 pixmapify_button (state *s, int down_p)
2538 GtkWidget *pixmapwid;
2542 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2543 style = gtk_widget_get_style (w);
2545 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2546 &style->bg[GTK_STATE_NORMAL],
2548 ? (gchar **) down_arrow_xpm
2549 : (gchar **) up_arrow_xpm));
2550 pixmapwid = gtk_pixmap_new (pixmap, mask);
2551 gtk_widget_show (pixmapwid);
2552 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2553 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2557 map_next_button_cb (GtkWidget *w, gpointer user_data)
2559 state *s = (state *) user_data;
2560 pixmapify_button (s, 1);
2564 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2566 state *s = (state *) user_data;
2567 pixmapify_button (s, 0);
2569 #endif /* !HAVE_GTK2 */
2572 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2576 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2577 GtkAllocation *allocation,
2581 GtkWidgetAuxInfo *aux_info;
2583 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2585 aux_info->width = allocation->width;
2586 aux_info->height = -2;
2590 gtk_widget_size_request (label, &req);
2594 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2597 eschew_gtk_lossage (GtkLabel *label)
2599 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2600 aux_info->width = GTK_WIDGET (label)->allocation.width;
2601 aux_info->height = -2;
2605 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2607 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2608 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
2611 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2613 gtk_widget_queue_resize (GTK_WIDGET (label));
2618 populate_demo_window (state *s, int list_elt)
2620 saver_preferences *p = &s->prefs;
2623 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2624 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2625 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2626 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2627 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2629 if (p->mode == BLANK_ONLY)
2632 pretty_name = strdup (_("Blank Screen"));
2633 schedule_preview (s, 0);
2635 else if (p->mode == DONT_BLANK)
2638 pretty_name = strdup (_("Screen Saver Disabled"));
2639 schedule_preview (s, 0);
2643 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2644 ? s->list_elt_to_hack_number[list_elt]
2646 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2650 ? strdup (hack->name)
2651 : make_hack_name (hack->command))
2655 schedule_preview (s, hack->command);
2657 schedule_preview (s, 0);
2661 pretty_name = strdup (_("Preview"));
2663 gtk_frame_set_label (frame1, pretty_name);
2664 gtk_frame_set_label (frame2, pretty_name);
2666 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2667 gtk_entry_set_position (cmd, 0);
2671 sprintf (title, "%s: %.100s Settings",
2672 progclass, (pretty_name ? pretty_name : "???"));
2673 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2676 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2678 ? (hack->visual && *hack->visual
2683 sensitize_demo_widgets (s, (hack ? True : False));
2685 if (pretty_name) free (pretty_name);
2687 ensure_selected_item_visible (list);
2689 s->_selected_list_element = list_elt;
2694 widget_deleter (GtkWidget *widget, gpointer data)
2696 /* #### Well, I want to destroy these widgets, but if I do that, they get
2697 referenced again, and eventually I get a SEGV. So instead of
2698 destroying them, I'll just hide them, and leak a bunch of memory
2699 every time the disk file changes. Go go go Gtk!
2701 #### Ok, that's a lie, I get a crash even if I just hide the widget
2702 and don't ever delete it. Fuck!
2705 gtk_widget_destroy (widget);
2707 gtk_widget_hide (widget);
2712 static char **sort_hack_cmp_names_kludge;
2714 sort_hack_cmp (const void *a, const void *b)
2719 return strcmp (sort_hack_cmp_names_kludge[*(int *) a],
2720 sort_hack_cmp_names_kludge[*(int *) b]);
2725 initialize_sort_map (state *s)
2727 saver_preferences *p = &s->prefs;
2730 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2731 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2733 s->list_elt_to_hack_number = (int *)
2734 calloc (sizeof(int), p->screenhacks_count + 1);
2735 s->hack_number_to_list_elt = (int *)
2736 calloc (sizeof(int), p->screenhacks_count + 1);
2738 /* Initialize table to 1:1 mapping */
2739 for (i = 0; i < p->screenhacks_count; i++)
2740 s->list_elt_to_hack_number[i] = i;
2742 /* Generate list of names (once)
2744 sort_hack_cmp_names_kludge = (char **)
2745 calloc (sizeof(char *), p->screenhacks_count);
2746 for (i = 0; i < p->screenhacks_count; i++)
2748 screenhack *hack = p->screenhacks[i];
2749 char *name = (hack->name && *hack->name
2750 ? strdup (hack->name)
2751 : make_hack_name (hack->command));
2753 for (str = name; *str; str++)
2754 *str = tolower(*str);
2755 sort_hack_cmp_names_kludge[i] = name;
2758 /* Sort alphabetically
2760 qsort (s->list_elt_to_hack_number,
2761 p->screenhacks_count,
2762 sizeof(*s->list_elt_to_hack_number),
2767 for (i = 0; i < p->screenhacks_count; i++)
2768 free (sort_hack_cmp_names_kludge[i]);
2769 free (sort_hack_cmp_names_kludge);
2770 sort_hack_cmp_names_kludge = 0;
2772 /* Build inverse table */
2773 for (i = 0; i < p->screenhacks_count; i++)
2774 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2779 maybe_reload_init_file (state *s)
2781 saver_preferences *p = &s->prefs;
2784 static Bool reentrant_lock = False;
2785 if (reentrant_lock) return 0;
2786 reentrant_lock = True;
2788 if (init_file_changed_p (p))
2790 const char *f = init_file_name();
2795 if (!f || !*f) return 0;
2796 b = (char *) malloc (strlen(f) + 1024);
2799 "file \"%s\" has changed, reloading.\n"),
2801 warning_dialog (s->toplevel_widget, b, False, 100);
2805 initialize_sort_map (s);
2807 list_elt = selected_list_element (s);
2808 list = name_to_widget (s, "list");
2809 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
2810 populate_hack_list (s);
2811 force_list_select_item (s, list, list_elt, True);
2812 populate_prefs_page (s);
2813 populate_demo_window (s, list_elt);
2814 ensure_selected_item_visible (list);
2819 reentrant_lock = False;
2825 /* Making the preview window have the right X visual (so that GL works.)
2828 static Visual *get_best_gl_visual (state *);
2831 x_visual_to_gdk_visual (Visual *xv)
2833 GList *gvs = gdk_list_visuals();
2834 if (!xv) return gdk_visual_get_system();
2835 for (; gvs; gvs = gvs->next)
2837 GdkVisual *gv = (GdkVisual *) gvs->data;
2838 if (xv == GDK_VISUAL_XVISUAL (gv))
2841 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
2842 blurb(), (unsigned long) xv->visualid);
2847 clear_preview_window (state *s)
2852 if (!s->toplevel_widget) return; /* very early */
2853 p = name_to_widget (s, "preview");
2856 if (!window) return;
2858 /* Flush the widget background down into the window, in case a subproc
2860 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
2861 gdk_window_clear (window);
2865 GtkWidget *notebook;
2867 notebook = name_to_widget (s, "preview_notebook");
2868 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
2869 s->running_preview_error_p
2872 #else /* !HAVE_GTK2 */
2873 if (s->running_preview_error_p)
2875 const char * const lines[] = { N_("No Preview"), N_("Available") };
2876 int lh = p->style->font->ascent + p->style->font->descent;
2879 gdk_window_get_size (window, &w, &h);
2880 y = (h - (lh * countof(lines))) / 2;
2881 y += p->style->font->ascent;
2882 for (i = 0; i < countof(lines); i++)
2884 int sw = gdk_string_width (p->style->font, _(lines[i]));
2885 int x = (w - sw) / 2;
2886 gdk_draw_string (window, p->style->font,
2887 p->style->fg_gc[GTK_STATE_NORMAL],
2892 #endif /* !HAVE_GTK2 */
2899 fix_preview_visual (state *s)
2901 GtkWidget *widget = name_to_widget (s, "preview");
2902 Visual *xvisual = get_best_gl_visual (s);
2903 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
2904 GdkVisual *dvisual = gdk_visual_get_system();
2905 GdkColormap *cmap = (visual == dvisual
2906 ? gdk_colormap_get_system ()
2907 : gdk_colormap_new (visual, False));
2910 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
2911 (visual == dvisual ? "default" : "non-default"),
2912 (xvisual ? (unsigned long) xvisual->visualid : 0L));
2914 if (!GTK_WIDGET_REALIZED (widget) ||
2915 gtk_widget_get_visual (widget) != visual)
2917 gtk_widget_unrealize (widget);
2918 gtk_widget_set_visual (widget, visual);
2919 gtk_widget_set_colormap (widget, cmap);
2920 gtk_widget_realize (widget);
2923 /* Set the Widget colors to be white-on-black. */
2925 GdkWindow *window = widget->window;
2926 GtkStyle *style = gtk_style_copy (widget->style);
2927 GdkColormap *cmap = gtk_widget_get_colormap (widget);
2928 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
2929 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
2930 GdkGC *fgc = gdk_gc_new(window);
2931 GdkGC *bgc = gdk_gc_new(window);
2932 if (!gdk_color_white (cmap, fg)) abort();
2933 if (!gdk_color_black (cmap, bg)) abort();
2934 gdk_gc_set_foreground (fgc, fg);
2935 gdk_gc_set_background (fgc, bg);
2936 gdk_gc_set_foreground (bgc, bg);
2937 gdk_gc_set_background (bgc, fg);
2938 style->fg_gc[GTK_STATE_NORMAL] = fgc;
2939 style->bg_gc[GTK_STATE_NORMAL] = fgc;
2940 gtk_widget_set_style (widget, style);
2943 gtk_widget_show (widget);
2951 subproc_pretty_name (state *s)
2953 if (s->running_preview_cmd)
2955 char *ps = strdup (s->running_preview_cmd);
2956 char *ss = strchr (ps, ' ');
2958 ss = strrchr (ps, '/');
2964 return strdup ("???");
2969 reap_zombies (state *s)
2971 int wait_status = 0;
2973 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
2977 if (pid == s->running_preview_pid)
2979 char *ss = subproc_pretty_name (s);
2980 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(), pid, ss);
2984 fprintf (stderr, "%s: pid %lu died\n", blurb(), pid);
2990 /* Mostly lifted from driver/subprocs.c */
2992 get_best_gl_visual (state *s)
2994 Display *dpy = GDK_DISPLAY();
3003 av[ac++] = "xscreensaver-gl-helper";
3008 perror ("error creating pipe:");
3015 switch ((int) (forked = fork ()))
3019 sprintf (buf, "%s: couldn't fork", blurb());
3027 close (in); /* don't need this one */
3028 close (ConnectionNumber (dpy)); /* close display fd */
3030 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3032 perror ("could not dup() a new stdout:");
3036 execvp (av[0], av); /* shouldn't return. */
3038 if (errno != ENOENT)
3040 /* Ignore "no such file or directory" errors, unless verbose.
3041 Issue all other exec errors, though. */
3042 sprintf (buf, "%s: running %s", blurb(), av[0]);
3045 exit (1); /* exits fork */
3051 int wait_status = 0;
3053 FILE *f = fdopen (in, "r");
3054 unsigned long v = 0;
3057 close (out); /* don't need this one */
3060 fgets (buf, sizeof(buf)-1, f);
3063 /* Wait for the child to die. */
3064 waitpid (-1, &wait_status, 0);
3066 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3072 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3078 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3080 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3081 blurb(), av[0], result);
3093 kill_preview_subproc (state *s)
3095 s->running_preview_error_p = False;
3098 clear_preview_window (s);
3100 if (s->subproc_check_timer_id)
3102 gtk_timeout_remove (s->subproc_check_timer_id);
3103 s->subproc_check_timer_id = 0;
3104 s->subproc_check_countdown = 0;
3107 if (s->running_preview_pid)
3109 int status = kill (s->running_preview_pid, SIGTERM);
3110 char *ss = subproc_pretty_name (s);
3117 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3118 blurb(), s->running_preview_pid, ss);
3123 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3124 blurb(), s->running_preview_pid, ss);
3128 else if (s->debug_p)
3129 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3130 s->running_preview_pid, ss);
3133 s->running_preview_pid = 0;
3134 if (s->running_preview_cmd) free (s->running_preview_cmd);
3135 s->running_preview_cmd = 0;
3142 /* Immediately and unconditionally launches the given process,
3143 after appending the -window-id option; sets running_preview_pid.
3146 launch_preview_subproc (state *s)
3148 saver_preferences *p = &s->prefs;
3152 const char *cmd = s->desired_preview_cmd;
3154 GtkWidget *pr = name_to_widget (s, "preview");
3155 GdkWindow *window = pr->window;
3157 s->running_preview_error_p = False;
3159 if (s->preview_suppressed_p)
3161 kill_preview_subproc (s);
3165 new_cmd = malloc (strlen (cmd) + 40);
3167 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3170 /* No window id? No command to run. */
3176 strcpy (new_cmd, cmd);
3177 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X", id);
3180 kill_preview_subproc (s);
3183 s->running_preview_error_p = True;
3184 clear_preview_window (s);
3188 switch ((int) (forked = fork ()))
3193 sprintf (buf, "%s: couldn't fork", blurb());
3195 s->running_preview_error_p = True;
3201 close (ConnectionNumber (GDK_DISPLAY()));
3203 usleep (250000); /* pause for 1/4th second before launching, to give
3204 the previous program time to die and flush its X
3205 buffer, so we don't get leftover turds on the
3208 exec_command (p->shell, new_cmd, p->nice_inferior);
3209 /* Don't bother printing an error message when we are unable to
3210 exec subprocesses; we handle that by polling the pid later. */
3211 exit (1); /* exits child fork */
3216 if (s->running_preview_cmd) free (s->running_preview_cmd);
3217 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3218 s->running_preview_pid = forked;
3222 char *ss = subproc_pretty_name (s);
3223 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(), forked, ss);
3230 schedule_preview_check (s);
3233 if (new_cmd) free (new_cmd);
3238 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3241 hack_environment (state *s)
3243 static const char *def_path =
3244 # ifdef DEFAULT_PATH_PREFIX
3245 DEFAULT_PATH_PREFIX;
3250 Display *dpy = GDK_DISPLAY();
3251 const char *odpy = DisplayString (dpy);
3252 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3253 strcpy (ndpy, "DISPLAY=");
3254 strcat (ndpy, odpy);
3259 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3261 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3262 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3263 So we must leak it (and/or the previous setting). Yay.
3266 if (def_path && *def_path)
3268 const char *opath = getenv("PATH");
3269 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3270 strcpy (npath, "PATH=");
3271 strcat (npath, def_path);
3272 strcat (npath, ":");
3273 strcat (npath, opath);
3277 /* do not free(npath) -- see above */
3280 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3285 /* Called from a timer:
3286 Launches the currently-chosen subprocess, if it's not already running.
3287 If there's a different process running, kills it.
3290 update_subproc_timer (gpointer data)
3292 state *s = (state *) data;
3293 if (! s->desired_preview_cmd)
3294 kill_preview_subproc (s);
3295 else if (!s->running_preview_cmd ||
3296 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
3297 launch_preview_subproc (s);
3299 s->subproc_timer_id = 0;
3300 return FALSE; /* do not re-execute timer */
3304 /* Call this when you think you might want a preview process running.
3305 It will set a timer that will actually launch that program a second
3306 from now, if you haven't changed your mind (to avoid double-click
3307 spazzing, etc.) `cmd' may be null meaning "no process".
3310 schedule_preview (state *s, const char *cmd)
3312 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
3317 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
3319 fprintf (stderr, "%s: scheduling preview death\n", blurb());
3322 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
3323 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
3325 if (s->subproc_timer_id)
3326 gtk_timeout_remove (s->subproc_timer_id);
3327 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3331 /* Called from a timer:
3332 Checks to see if the subproc that should be running, actually is.
3335 check_subproc_timer (gpointer data)
3337 state *s = (state *) data;
3338 Bool again_p = True;
3340 if (s->running_preview_error_p || /* already dead */
3341 s->running_preview_pid <= 0)
3349 status = kill (s->running_preview_pid, 0);
3350 if (status < 0 && errno == ESRCH)
3351 s->running_preview_error_p = True;
3355 char *ss = subproc_pretty_name (s);
3356 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3357 s->running_preview_pid, ss,
3358 (s->running_preview_error_p ? "dead" : "alive"));
3362 if (s->running_preview_error_p)
3364 clear_preview_window (s);
3369 /* Otherwise, it's currently alive. We might be checking again, or we
3370 might be satisfied. */
3372 if (--s->subproc_check_countdown <= 0)
3376 return TRUE; /* re-execute timer */
3379 s->subproc_check_timer_id = 0;
3380 s->subproc_check_countdown = 0;
3381 return FALSE; /* do not re-execute timer */
3386 /* Call this just after launching a subprocess.
3387 This sets a timer that will, five times a second for two seconds,
3388 check whether the program is still running. The assumption here
3389 is that if the process didn't stay up for more than a couple of
3390 seconds, then either the program doesn't exist, or it doesn't
3391 take a -window-id argument.
3394 schedule_preview_check (state *s)
3400 fprintf (stderr, "%s: scheduling check\n", blurb());
3402 if (s->subproc_check_timer_id)
3403 gtk_timeout_remove (s->subproc_check_timer_id);
3404 s->subproc_check_timer_id =
3405 gtk_timeout_add (1000 / ticks,
3406 check_subproc_timer, (gpointer) s);
3407 s->subproc_check_countdown = ticks * seconds;
3412 screen_blanked_p (void)
3416 unsigned long nitems, bytesafter;
3418 Display *dpy = GDK_DISPLAY();
3419 Bool blanked_p = False;
3421 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3422 XA_SCREENSAVER_STATUS,
3423 0, 3, False, XA_INTEGER,
3424 &type, &format, &nitems, &bytesafter,
3425 (unsigned char **) &data)
3427 && type == XA_INTEGER
3430 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3432 if (data) free (data);
3437 /* Wake up every now and then and see if the screen is blanked.
3438 If it is, kill off the small-window demo -- no point in wasting
3439 cycles by running two screensavers at once...
3442 check_blanked_timer (gpointer data)
3444 state *s = (state *) data;
3445 Bool blanked_p = screen_blanked_p ();
3446 if (blanked_p && s->running_preview_pid)
3449 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3450 kill_preview_subproc (s);
3453 return True; /* re-execute timer */
3457 /* Setting window manager icon
3461 init_icon (GdkWindow *window)
3463 GdkBitmap *mask = 0;
3466 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3467 (gchar **) logo_50_xpm);
3469 gdk_window_set_icon (window, 0, pixmap, mask);
3473 /* The main demo-mode command loop.
3478 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3479 XrmRepresentation *type, XrmValue *value, XPointer closure)
3482 for (i = 0; quarks[i]; i++)
3484 if (bindings[i] == XrmBindTightly)
3485 fprintf (stderr, (i == 0 ? "" : "."));
3486 else if (bindings[i] == XrmBindLoosely)
3487 fprintf (stderr, "*");
3489 fprintf (stderr, " ??? ");
3490 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3493 fprintf (stderr, ": %s\n", (char *) value->addr);
3501 the_network_is_not_the_computer (state *s)
3503 Display *dpy = GDK_DISPLAY();
3504 char *rversion = 0, *ruser = 0, *rhost = 0;
3505 char *luser, *lhost;
3507 struct passwd *p = getpwuid (getuid ());
3508 const char *d = DisplayString (dpy);
3510 # if defined(HAVE_UNAME)
3512 if (uname (&uts) < 0)
3513 lhost = "<UNKNOWN>";
3515 lhost = uts.nodename;
3517 strcpy (lhost, getenv("SYS$NODE"));
3518 # else /* !HAVE_UNAME && !VMS */
3519 strcat (lhost, "<UNKNOWN>");
3520 # endif /* !HAVE_UNAME && !VMS */
3522 if (p && p->pw_name)
3527 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3529 /* Make a buffer that's big enough for a number of copies of all the
3530 strings, plus some. */
3531 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3532 (ruser ? strlen(ruser) : 0) +
3533 (rhost ? strlen(rhost) : 0) +
3540 if (!rversion || !*rversion)
3544 "The XScreenSaver daemon doesn't seem to be running\n"
3545 "on display \"%s\". Launch it now?"),
3548 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3550 /* Warn that the two processes are running as different users.
3554 "%s is running as user \"%s\" on host \"%s\".\n"
3555 "But the xscreensaver managing display \"%s\"\n"
3556 "is running as user \"%s\" on host \"%s\".\n"
3558 "Since they are different users, they won't be reading/writing\n"
3559 "the same ~/.xscreensaver file, so %s isn't\n"
3560 "going to work right.\n"
3562 "You should either re-run %s as \"%s\", or re-run\n"
3563 "xscreensaver as \"%s\".\n"
3565 "Restart the xscreensaver daemon now?\n"),
3566 blurb(), luser, lhost,
3568 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3570 blurb(), (ruser ? ruser : "???"),
3573 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3575 /* Warn that the two processes are running on different hosts.
3579 "%s is running as user \"%s\" on host \"%s\".\n"
3580 "But the xscreensaver managing display \"%s\"\n"
3581 "is running as user \"%s\" on host \"%s\".\n"
3583 "If those two machines don't share a file system (that is,\n"
3584 "if they don't see the same ~%s/.xscreensaver file) then\n"
3585 "%s won't work right.\n"
3587 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3588 blurb(), luser, lhost,
3590 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3595 else if (!!strcmp (rversion, s->short_version))
3597 /* Warn that the version numbers don't match.
3601 "This is %s version %s.\n"
3602 "But the xscreensaver managing display \"%s\"\n"
3603 "is version %s. This could cause problems.\n"
3605 "Restart the xscreensaver daemon now?\n"),
3606 progname, s->short_version,
3613 warning_dialog (s->toplevel_widget, msg, True, 1);
3615 if (rversion) free (rversion);
3616 if (ruser) free (ruser);
3617 if (rhost) free (rhost);
3622 /* We use this error handler so that X errors are preceeded by the name
3623 of the program that generated them.
3626 demo_ehandler (Display *dpy, XErrorEvent *error)
3628 state *s = global_state_kludge; /* I hate C so much... */
3629 fprintf (stderr, "\nX error in %s:\n", blurb());
3630 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3631 kill_preview_subproc (s);
3637 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3638 of the program that generated them; and also that we can ignore one
3639 particular bogus error message that Gdk madly spews.
3642 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3643 const gchar *message, gpointer user_data)
3645 /* Ignore the message "Got event for unknown window: 0x...".
3646 Apparently some events are coming in for the xscreensaver window
3647 (presumably reply events related to the ClientMessage) and Gdk
3648 feels the need to complain about them. So, just suppress any
3649 messages that look like that one.
3651 if (strstr (message, "unknown window"))
3654 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3655 (log_domain ? log_domain : progclass),
3656 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3657 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3658 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3659 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3660 log_level == G_LOG_LEVEL_INFO ? "info" :
3661 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3663 ((!*message || message[strlen(message)-1] != '\n')
3668 static char *defaults[] = {
3669 #include "XScreenSaver_ad.h"
3674 #ifdef HAVE_CRAPPLET
3675 static struct poptOption crapplet_options[] = {
3676 {NULL, '\0', 0, NULL, 0}
3678 #endif /* HAVE_CRAPPLET */
3681 const char *usage = "[--display dpy] [--prefs]"
3682 # ifdef HAVE_CRAPPLET
3685 "\n\t\t [--debug] [--sync] [--no-xshm]";
3688 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3690 state *s = (state *) user_data;
3691 Boolean oi = s->initializing_p;
3692 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3693 s->initializing_p = True;
3694 eschew_gtk_lossage (label);
3695 s->initializing_p = oi;
3701 print_widget_tree (GtkWidget *w, int depth)
3704 for (i = 0; i < depth; i++)
3705 fprintf (stderr, " ");
3706 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3708 if (GTK_IS_LIST (w))
3710 for (i = 0; i < depth+1; i++)
3711 fprintf (stderr, " ");
3712 fprintf (stderr, "...list kids...\n");
3714 else if (GTK_IS_CONTAINER (w))
3716 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3719 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3727 delayed_scroll_kludge (gpointer data)
3729 state *s = (state *) data;
3730 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
3731 ensure_selected_item_visible (w);
3733 /* Oh, this is just fucking lovely, too. */
3734 w = GTK_WIDGET (name_to_widget (s, "preview"));
3735 gtk_widget_hide (w);
3736 gtk_widget_show (w);
3738 return FALSE; /* do not re-execute timer */
3744 create_xscreensaver_demo (void)
3748 nb = name_to_widget (global_state_kludge, "preview_notebook");
3749 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
3751 return name_to_widget (global_state_kludge, "xscreensaver_demo");
3755 create_xscreensaver_settings_dialog (void)
3759 box = name_to_widget (global_state_kludge, "dialog_action_area");
3761 w = name_to_widget (global_state_kludge, "adv_button");
3762 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
3764 w = name_to_widget (global_state_kludge, "std_button");
3765 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
3767 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
3770 #endif /* HAVE_GTK2 */
3773 main (int argc, char **argv)
3777 saver_preferences *p;
3781 Widget toplevel_shell;
3782 char *real_progname = argv[0];
3783 char window_title[255];
3784 Bool crapplet_p = False;
3788 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
3789 textdomain (GETTEXT_PACKAGE);
3792 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3793 # else /* !HAVE_GTK2 */
3794 if (!setlocale (LC_ALL, ""))
3795 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
3796 # endif /* !HAVE_GTK2 */
3798 #endif /* ENABLE_NLS */
3800 str = strrchr (real_progname, '/');
3801 if (str) real_progname = str+1;
3804 memset (s, 0, sizeof(*s));
3805 s->initializing_p = True;
3808 global_state_kludge = s; /* I hate C so much... */
3810 progname = real_progname;
3812 s->short_version = (char *) malloc (5);
3813 memcpy (s->short_version, screensaver_id + 17, 4);
3814 s->short_version [4] = 0;
3817 /* Register our error message logger for every ``log domain'' known.
3818 There's no way to do this globally, so I grepped the Gtk/Gdk sources
3819 for all of the domains that seem to be in use.
3822 const char * const domains[] = { 0,
3823 "Gtk", "Gdk", "GLib", "GModule",
3824 "GThread", "Gnome", "GnomeUI" };
3825 for (i = 0; i < countof(domains); i++)
3826 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
3829 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
3832 const char *dir = DEFAULT_ICONDIR;
3833 if (*dir) add_pixmap_directory (dir);
3835 # endif /* !HAVE_GTK2 */
3836 #endif /* DEFAULT_ICONDIR */
3838 /* This is gross, but Gtk understands --display and not -display...
3840 for (i = 1; i < argc; i++)
3841 if (argv[i][0] && argv[i][1] &&
3842 !strncmp(argv[i], "-display", strlen(argv[i])))
3843 argv[i] = "--display";
3846 /* We need to parse this arg really early... Sigh. */
3847 for (i = 1; i < argc; i++)
3849 (!strcmp(argv[i], "--crapplet") ||
3850 !strcmp(argv[i], "--capplet")))
3852 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
3855 for (j = i; j < argc; j++) /* remove it from the list */
3856 argv[j] = argv[j+1];
3858 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
3859 fprintf (stderr, "%s: not compiled with --crapplet support\n",
3861 fprintf (stderr, "%s: %s\n", real_progname, usage);
3863 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
3866 (!strcmp(argv[i], "--debug") ||
3867 !strcmp(argv[i], "-debug") ||
3868 !strcmp(argv[i], "-d")))
3872 for (j = i; j < argc; j++) /* remove it from the list */
3873 argv[j] = argv[j+1];
3877 /* Let Gtk open the X connection, then initialize Xt to use that
3878 same connection. Doctor Frankenstein would be proud.
3880 # ifdef HAVE_CRAPPLET
3883 GnomeClient *client;
3884 GnomeClientFlags flags = 0;
3886 int init_results = gnome_capplet_init ("screensaver-properties",
3888 argc, argv, NULL, 0, NULL);
3890 0 upon successful initialization;
3891 1 if --init-session-settings was passed on the cmdline;
3892 2 if --ignore was passed on the cmdline;
3895 So the 1 signifies just to init the settings, and quit, basically.
3896 (Meaning launch the xscreensaver daemon.)
3899 if (init_results < 0)
3902 g_error ("An initialization error occurred while "
3903 "starting xscreensaver-capplet.\n");
3905 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
3906 real_progname, init_results);
3911 client = gnome_master_client ();
3914 flags = gnome_client_get_flags (client);
3916 if (flags & GNOME_CLIENT_IS_CONNECTED)
3919 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
3920 gnome_client_get_id (client));
3923 char *session_args[20];
3925 session_args[i++] = real_progname;
3926 session_args[i++] = "--capplet";
3927 session_args[i++] = "--init-session-settings";
3928 session_args[i] = 0;
3929 gnome_client_set_priority (client, 20);
3930 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
3931 gnome_client_set_restart_command (client, i, session_args);
3935 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
3938 gnome_client_flush (client);
3941 if (init_results == 1)
3943 system ("xscreensaver -nosplash &");
3949 # endif /* HAVE_CRAPPLET */
3951 gtk_init (&argc, &argv);
3955 /* We must read exactly the same resources as xscreensaver.
3956 That means we must have both the same progclass *and* progname,
3957 at least as far as the resource database is concerned. So,
3958 put "xscreensaver" in argv[0] while initializing Xt.
3960 argv[0] = "xscreensaver";
3964 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
3966 XtToolkitInitialize ();
3967 app = XtCreateApplicationContext ();
3968 dpy = GDK_DISPLAY();
3969 XtAppSetFallbackResources (app, defaults);
3970 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
3971 toplevel_shell = XtAppCreateShell (progname, progclass,
3972 applicationShellWidgetClass,
3975 dpy = XtDisplay (toplevel_shell);
3976 db = XtDatabase (dpy);
3977 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
3978 XSetErrorHandler (demo_ehandler);
3980 /* Let's just ignore these. They seem to confuse Irix Gtk... */
3981 signal (SIGPIPE, SIG_IGN);
3983 /* After doing Xt-style command-line processing, complain about any
3984 unrecognized command-line arguments.
3986 for (i = 1; i < argc; i++)
3988 char *str = argv[i];
3989 if (str[0] == '-' && str[1] == '-')
3991 if (!strcmp (str, "-prefs"))
3993 else if (crapplet_p)
3994 /* There are lots of random args that we don't care about when we're
3995 started as a crapplet, so just ignore unknown args in that case. */
3999 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname, argv[i]);
4000 fprintf (stderr, "%s: %s\n", real_progname, usage);
4005 /* Load the init file, which may end up consulting the X resource database
4006 and the site-wide app-defaults file. Note that at this point, it's
4007 important that `progname' be "xscreensaver", rather than whatever
4012 initialize_sort_map (s);
4014 /* Now that Xt has been initialized, and the resources have been read,
4015 we can set our `progname' variable to something more in line with
4018 progname = real_progname;
4022 /* Print out all the resources we read. */
4024 XrmName name = { 0 };
4025 XrmClass class = { 0 };
4027 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4033 /* Intern the atoms that xscreensaver_command() needs.
4035 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4036 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4037 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4038 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4039 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4040 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4041 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4042 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4043 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4044 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4045 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4046 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4047 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4050 /* Create the window and all its widgets.
4052 s->base_widget = create_xscreensaver_demo ();
4053 s->popup_widget = create_xscreensaver_settings_dialog ();
4054 s->toplevel_widget = s->base_widget;
4057 /* Set the main window's title. */
4059 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4060 char *s1, *s2, *s3, *s4;
4061 s1 = (char *) strchr(v, ' '); s1++;
4062 s2 = (char *) strchr(s1, ' ');
4063 s3 = (char *) strchr(v, '('); s3++;
4064 s4 = (char *) strchr(s3, ')');
4067 sprintf (window_title, "%.50s %.50s, %.50s", progclass, s1, s3);
4068 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4069 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4073 /* Adjust the (invisible) notebooks on the popup dialog... */
4075 GtkNotebook *notebook =
4076 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4077 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4081 gtk_widget_hide (std);
4082 # else /* !HAVE_XML */
4083 /* Make the advanced page be the only one available. */
4084 gtk_widget_set_sensitive (std, False);
4085 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4086 gtk_widget_hide (std);
4088 # endif /* !HAVE_XML */
4090 gtk_notebook_set_page (notebook, page);
4091 gtk_notebook_set_show_tabs (notebook, False);
4094 /* Various other widget initializations...
4096 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4097 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4099 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4100 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4103 populate_hack_list (s);
4104 populate_prefs_page (s);
4105 sensitize_demo_widgets (s, False);
4106 fix_text_entry_sizes (s);
4107 scroll_to_current_hack (s);
4109 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4110 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4114 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4115 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4117 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4118 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4120 #endif /* !HAVE_GTK2 */
4122 /* Hook up callbacks to the items on the mode menu. */
4124 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4125 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4126 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4127 for (; kids; kids = kids->next)
4128 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4129 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4134 /* Handle the -prefs command-line argument. */
4137 GtkNotebook *notebook =
4138 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4139 gtk_notebook_set_page (notebook, 1);
4142 # ifdef HAVE_CRAPPLET
4146 GtkWidget *outer_vbox;
4148 gtk_widget_hide (s->toplevel_widget);
4150 capplet = capplet_widget_new ();
4152 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4153 # ifdef HAVE_CRAPPLET_IMMEDIATE
4154 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4155 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4157 /* In crapplet-mode, take off the menubar. */
4158 gtk_widget_hide (name_to_widget (s, "menubar"));
4160 /* Reparent our top-level container to be a child of the capplet
4163 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4164 gtk_widget_ref (outer_vbox);
4165 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4167 GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4168 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4170 /* Find the window above us, and set the title and close handler. */
4172 GtkWidget *window = capplet;
4173 while (window && !GTK_IS_WINDOW (window))
4174 window = window->parent;
4177 gtk_window_set_title (GTK_WINDOW (window), window_title);
4178 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4179 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4184 s->toplevel_widget = capplet;
4186 # endif /* HAVE_CRAPPLET */
4189 gtk_widget_show (s->toplevel_widget);
4190 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
4191 hack_environment (s);
4192 fix_preview_visual (s);
4194 /* Realize page zero, so that we can diddle the scrollbar when the
4195 user tabs back to it -- otherwise, the current hack isn't scrolled
4196 to the first time they tab back there, when started with "-prefs".
4197 (Though it is if they then tab away, and back again.)
4199 #### Bah! This doesn't work. Gtk eats my ass! Someone who
4200 #### understands this crap, explain to me how to make this work.
4202 gtk_widget_realize (name_to_widget (s, "demos_table"));
4205 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
4208 /* Issue any warnings about the running xscreensaver daemon. */
4209 the_network_is_not_the_computer (s);
4212 /* Run the Gtk event loop, and not the Xt event loop. This means that
4213 if there were Xt timers or fds registered, they would never get serviced,
4214 and if there were any Xt widgets, they would never have events delivered.
4215 Fortunately, we're using Gtk for all of the UI, and only initialized
4216 Xt so that we could process the command line and use the X resource
4219 s->initializing_p = False;
4221 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
4222 after we start up. Otherwise, it always appears scrolled to the top
4223 when in crapplet-mode. */
4224 gtk_timeout_add (500, delayed_scroll_kludge, s);
4228 /* Load every configurator in turn, to scan them for errors all at once. */
4231 for (i = 0; i < p->screenhacks_count; i++)
4233 screenhack *hack = p->screenhacks[s->hack_number_to_list_elt[i]];
4234 conf_data *d = load_configurator (hack->command, False);
4235 if (d) free_conf_data (d);
4241 # ifdef HAVE_CRAPPLET
4243 capplet_gtk_main ();
4245 # endif /* HAVE_CRAPPLET */
4248 kill_preview_subproc (s);
4252 #endif /* HAVE_GTK -- whole file */