1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2005 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #ifdef HAVE_GTK /* whole file */
19 #include <xscreensaver-intl.h>
28 # define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */
36 #endif /* ENABLE_NLS */
39 # include <pwd.h> /* for getpwuid() */
45 # include <sys/utsname.h> /* for uname() */
46 #endif /* HAVE_UNAME */
56 #ifdef HAVE_SYS_WAIT_H
57 # include <sys/wait.h> /* for waitpid() and associated macros */
61 #include <X11/Xproto.h> /* for CARD32 */
62 #include <X11/Xatom.h> /* for XA_INTEGER */
63 #include <X11/Intrinsic.h>
64 #include <X11/StringDefs.h>
66 /* We don't actually use any widget internals, but these are included
67 so that gdb will have debug info for the widgets... */
68 #include <X11/IntrinsicP.h>
69 #include <X11/ShellP.h>
73 # include <X11/Xmu/Error.h>
75 # include <Xmu/Error.h>
82 # include <X11/extensions/Xinerama.h>
83 #endif /* HAVE_XINERAMA */
89 # include <capplet-widget.h>
95 # include <glade/glade-xml.h>
97 #else /* !HAVE_GTK2 */
98 # define G_MODULE_EXPORT /**/
99 #endif /* !HAVE_GTK2 */
101 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
102 # define GLADE_DIR DEFAULT_ICONDIR
104 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
105 # define DEFAULT_ICONDIR GLADE_DIR
109 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
110 It is unused otherwise, so in that case, stub it out. */
111 static const char *hack_configuration_path = 0;
118 #include "resources.h" /* for parse_time() */
119 #include "visual.h" /* for has_writable_cells() */
120 #include "remote.h" /* for xscreensaver_command() */
123 #include "logo-50.xpm"
124 #include "logo-180.xpm"
126 #undef dgettext /* else these are defined twice... */
129 #include "demo-Gtk-widgets.h"
130 #include "demo-Gtk-support.h"
131 #include "demo-Gtk-conf.h"
143 #endif /* HAVE_GTK2 */
146 extern void exec_command (const char *shell, const char *command, int nice);
148 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
151 #define countof(x) (sizeof((x))/sizeof((*x)))
155 char *progclass = "XScreenSaver";
158 /* The order of the items in the mode menu. */
159 static int mode_menu_order[] = {
160 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
165 char *short_version; /* version number of this xscreensaver build */
167 GtkWidget *toplevel_widget; /* the main window */
168 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
169 GtkWidget *popup_widget; /* the "Settings" dialog */
170 conf_data *cdata; /* private data for per-hack configuration */
173 GladeXML *glade_ui; /* Glade UI file */
174 #endif /* HAVE_GTK2 */
176 Bool debug_p; /* whether to print diagnostics */
177 Bool initializing_p; /* flag for breaking recursion loops */
178 Bool saving_p; /* flag for breaking recursion loops */
180 char *desired_preview_cmd; /* subprocess we intend to run */
181 char *running_preview_cmd; /* subprocess we are currently running */
182 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
183 Bool running_preview_error_p; /* whether the pid died abnormally */
185 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
186 int subproc_timer_id; /* timer to delay subproc launch */
187 int subproc_check_timer_id; /* timer to check whether it started up */
188 int subproc_check_countdown; /* how many more checks left */
190 int *list_elt_to_hack_number; /* table for sorting the hack list */
191 int *hack_number_to_list_elt; /* the inverse table */
192 Bool *hacks_available_p; /* whether hacks are on $PATH */
193 int list_count; /* how many items are in the list: this may be
194 less than p->screenhacks_count, if some are
197 int _selected_list_element; /* don't use this: call
198 selected_list_element() instead */
200 int nscreens; /* How many X or Xinerama screens there are */
202 saver_preferences prefs;
207 /* Total fucking evilness due to the fact that it's rocket science to get
208 a closure object of our own down into the various widget callbacks. */
209 static state *global_state_kludge;
212 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
213 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
214 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
217 static void populate_demo_window (state *, int list_elt);
218 static void populate_prefs_page (state *);
219 static void populate_popup_window (state *);
221 static Bool flush_dialog_changes_and_save (state *);
222 static Bool flush_popup_changes_and_save (state *);
224 static int maybe_reload_init_file (state *);
225 static void await_xscreensaver (state *);
226 static Bool xscreensaver_running_p (state *);
227 static void sensitize_menu_items (state *s, Bool force_p);
228 static void force_dialog_repaint (state *s);
230 static void schedule_preview (state *, const char *cmd);
231 static void kill_preview_subproc (state *, Bool reset_p);
232 static void schedule_preview_check (state *);
236 /* Some random utility functions
242 time_t now = time ((time_t *) 0);
243 char *ct = (char *) ctime (&now);
244 static char buf[255];
245 int n = strlen(progname);
247 strncpy(buf, progname, n);
250 strncpy(buf+n, ct+11, 8);
251 strcpy(buf+n+9, ": ");
257 name_to_widget (state *s, const char *name)
267 /* First try to load the Glade file from the current directory;
268 if there isn't one there, check the installed directory.
270 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
271 const char * const files[] = { GLADE_FILE_NAME,
272 GLADE_DIR "/" GLADE_FILE_NAME };
274 for (i = 0; i < countof (files); i++)
277 if (!stat (files[i], &st))
279 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
286 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
287 "\tfrom " GLADE_DIR "/ or current directory.\n",
291 # undef GLADE_FILE_NAME
293 glade_xml_signal_autoconnect (s->glade_ui);
296 w = glade_xml_get_widget (s->glade_ui, name);
298 #else /* !HAVE_GTK2 */
300 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
303 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
305 #endif /* HAVE_GTK2 */
308 fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
314 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
315 Takes a scroller, viewport, or list as an argument.
318 ensure_selected_item_visible (GtkWidget *widget)
322 GtkTreeSelection *selection;
326 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
327 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
328 path = gtk_tree_path_new_first ();
330 path = gtk_tree_model_get_path (model, &iter);
332 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
334 gtk_tree_path_free (path);
336 #else /* !HAVE_GTK2 */
338 GtkScrolledWindow *scroller = 0;
340 GtkList *list_widget = 0;
344 GtkWidget *selected = 0;
347 gint parent_h, child_y, child_h, children_h, ignore;
348 double ratio_t, ratio_b;
350 if (GTK_IS_SCROLLED_WINDOW (widget))
352 scroller = GTK_SCROLLED_WINDOW (widget);
353 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
354 list_widget = GTK_LIST (GTK_BIN(vp)->child);
356 else if (GTK_IS_VIEWPORT (widget))
358 vp = GTK_VIEWPORT (widget);
359 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
360 list_widget = GTK_LIST (GTK_BIN(vp)->child);
362 else if (GTK_IS_LIST (widget))
364 list_widget = GTK_LIST (widget);
365 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
366 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
371 slist = list_widget->selection;
372 selected = (slist ? GTK_WIDGET (slist->data) : 0);
376 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
378 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
379 kids; kids = kids->next)
382 adj = gtk_scrolled_window_get_vadjustment (scroller);
384 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
385 &ignore, &ignore, &ignore, &parent_h, &ignore);
386 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
387 &ignore, &child_y, &ignore, &child_h, &ignore);
388 children_h = nkids * child_h;
390 ratio_t = ((double) child_y) / ((double) children_h);
391 ratio_b = ((double) child_y + child_h) / ((double) children_h);
393 if (adj->upper == 0.0) /* no items in list */
396 if (ratio_t < (adj->value / adj->upper) ||
397 ratio_b > ((adj->value + adj->page_size) / adj->upper))
400 int slop = parent_h * 0.75; /* how much to overshoot by */
402 if (ratio_t < (adj->value / adj->upper))
404 double ratio_w = ((double) parent_h) / ((double) children_h);
405 double ratio_l = (ratio_b - ratio_t);
406 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
409 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
411 target = ratio_t * adj->upper;
415 if (target > adj->upper - adj->page_size)
416 target = adj->upper - adj->page_size;
420 gtk_adjustment_set_value (adj, target);
422 #endif /* !HAVE_GTK2 */
426 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
428 GtkWidget *shell = GTK_WIDGET (user_data);
429 while (shell->parent)
430 shell = shell->parent;
431 gtk_widget_destroy (GTK_WIDGET (shell));
435 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
437 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
439 restart_menu_cb (widget, user_data);
440 warning_dialog_dismiss_cb (widget, user_data);
444 warning_dialog (GtkWidget *parent, const char *message,
445 Boolean restart_button_p, int center)
447 char *msg = strdup (message);
450 GtkWidget *dialog = gtk_dialog_new ();
451 GtkWidget *label = 0;
453 GtkWidget *cancel = 0;
456 while (parent && !parent->window)
457 parent = parent->parent;
460 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
462 fprintf (stderr, "%s: too early for dialog?\n", progname);
470 char *s = strchr (head, '\n');
473 sprintf (name, "label%d", i++);
476 label = gtk_label_new (head);
478 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
479 #endif /* HAVE_GTK2 */
484 GTK_WIDGET (label)->style =
485 gtk_style_copy (GTK_WIDGET (label)->style);
486 GTK_WIDGET (label)->style->font =
487 gdk_font_load (get_string_resource("warning_dialog.headingFont",
489 gtk_widget_set_style (GTK_WIDGET (label),
490 GTK_WIDGET (label)->style);
492 #endif /* !HAVE_GTK2 */
494 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
495 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
496 label, TRUE, TRUE, 0);
497 gtk_widget_show (label);
508 label = gtk_label_new ("");
509 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
510 label, TRUE, TRUE, 0);
511 gtk_widget_show (label);
513 label = gtk_hbutton_box_new ();
514 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
515 label, TRUE, TRUE, 0);
518 if (restart_button_p)
520 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
521 gtk_container_add (GTK_CONTAINER (label), cancel);
524 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
525 gtk_container_add (GTK_CONTAINER (label), ok);
527 #else /* !HAVE_GTK2 */
529 ok = gtk_button_new_with_label ("OK");
530 gtk_container_add (GTK_CONTAINER (label), ok);
532 if (restart_button_p)
534 cancel = gtk_button_new_with_label ("Cancel");
535 gtk_container_add (GTK_CONTAINER (label), cancel);
538 #endif /* !HAVE_GTK2 */
540 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
541 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
542 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
543 STFU GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
544 gtk_widget_show (ok);
545 gtk_widget_grab_focus (ok);
549 STFU GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
550 gtk_widget_show (cancel);
552 gtk_widget_show (label);
553 gtk_widget_show (dialog);
555 if (restart_button_p)
557 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
558 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
560 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
561 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
566 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
567 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
571 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
572 GTK_WIDGET (parent)->window);
575 gtk_window_present (GTK_WINDOW (dialog));
576 #else /* !HAVE_GTK2 */
577 gdk_window_show (GTK_WIDGET (dialog)->window);
578 gdk_window_raise (GTK_WIDGET (dialog)->window);
579 #endif /* !HAVE_GTK2 */
586 run_cmd (state *s, Atom command, int arg)
591 flush_dialog_changes_and_save (s);
592 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
594 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
595 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
602 sprintf (buf, "Error:\n\n%s", err);
604 strcpy (buf, "Unknown error!");
605 warning_dialog (s->toplevel_widget, buf, False, 100);
609 sensitize_menu_items (s, True);
610 force_dialog_repaint (s);
615 run_hack (state *s, int list_elt, Bool report_errors_p)
621 if (list_elt < 0) return;
622 hack_number = s->list_elt_to_hack_number[list_elt];
624 flush_dialog_changes_and_save (s);
625 schedule_preview (s, 0);
627 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
630 if (status < 0 && report_errors_p)
632 if (xscreensaver_running_p (s))
634 /* Kludge: ignore the spurious "window unexpectedly deleted"
636 if (err && strstr (err, "unexpectedly deleted"))
643 sprintf (buf, "Error:\n\n%s", err);
645 strcpy (buf, "Unknown error!");
646 warning_dialog (s->toplevel_widget, buf, False, 100);
652 /* The error is that the daemon isn't running;
655 const char *d = DisplayString (GDK_DISPLAY());
659 "The XScreenSaver daemon doesn't seem to be running\n"
660 "on display \"%s\". Launch it now?"),
662 warning_dialog (s->toplevel_widget, msg, True, 1);
668 sensitize_menu_items (s, False);
675 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
676 libglade work on Cygwin; apparently all Glade callbacks need this magic
677 extra declaration. I do not pretend to understand.
681 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
683 state *s = global_state_kludge; /* I hate C so much... */
684 flush_dialog_changes_and_save (s);
685 kill_preview_subproc (s, False);
690 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
692 state *s = (state *) data;
693 flush_dialog_changes_and_save (s);
700 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
703 char *vers = strdup (screensaver_id + 4);
706 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
708 s = strchr (vers, ',');
712 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
713 non-ASCII characters aren't allowed in localizable string keys."
714 (I don't want to just use (c) instead of © because that doesn't
715 look as good in the plain-old default Latin1 "C" locale.)
718 sprintf(copy, ("Copyright \xC2\xA9 1991-2005 %s"), s);
719 #else /* !HAVE_GTK2 */
720 sprintf(copy, ("Copyright \251 1991-2005 %s"), s);
721 #endif /* !HAVE_GTK2 */
723 sprintf (msg, "%s\n\n%s", copy, desc);
725 /* I can't make gnome_about_new() work here -- it starts dying in
726 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
727 then this might be the thing to do:
731 const gchar *auth[] = { 0 };
732 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
734 gtk_widget_show (about);
736 #else / * GTK but not GNOME * /
740 GdkColormap *colormap;
741 GdkPixmap *gdkpixmap;
744 GtkWidget *dialog = gtk_dialog_new ();
745 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
746 GtkWidget *parent = GTK_WIDGET (menuitem);
747 while (parent->parent)
748 parent = parent->parent;
750 hbox = gtk_hbox_new (FALSE, 20);
751 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
752 hbox, TRUE, TRUE, 0);
754 colormap = gtk_widget_get_colormap (parent);
756 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
757 (gchar **) logo_180_xpm);
758 icon = gtk_pixmap_new (gdkpixmap, mask);
759 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
761 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
763 vbox = gtk_vbox_new (FALSE, 0);
764 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
766 label1 = gtk_label_new (vers);
767 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
768 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
769 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
772 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
773 GTK_WIDGET (label1)->style->font =
774 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
775 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
776 #endif /* HAVE_GTK2 */
778 label2 = gtk_label_new (msg);
779 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
780 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
781 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
784 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
785 GTK_WIDGET (label2)->style->font =
786 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
787 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
788 #endif /* HAVE_GTK2 */
790 hb = gtk_hbutton_box_new ();
792 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
796 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
797 #else /* !HAVE_GTK2 */
798 ok = gtk_button_new_with_label (_("OK"));
799 #endif /* !HAVE_GTK2 */
800 gtk_container_add (GTK_CONTAINER (hb), ok);
802 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
803 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
804 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
806 gtk_widget_show (hbox);
807 gtk_widget_show (icon);
808 gtk_widget_show (vbox);
809 gtk_widget_show (label1);
810 gtk_widget_show (label2);
811 gtk_widget_show (hb);
812 gtk_widget_show (ok);
813 gtk_widget_show (dialog);
815 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
816 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
818 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
819 GTK_WIDGET (parent)->window);
820 gdk_window_show (GTK_WIDGET (dialog)->window);
821 gdk_window_raise (GTK_WIDGET (dialog)->window);
827 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
829 state *s = global_state_kludge; /* I hate C so much... */
830 saver_preferences *p = &s->prefs;
833 if (!p->help_url || !*p->help_url)
835 warning_dialog (s->toplevel_widget,
837 "No Help URL has been specified.\n"), False, 100);
841 help_command = (char *) malloc (strlen (p->load_url_command) +
842 (strlen (p->help_url) * 4) + 20);
843 strcpy (help_command, "( ");
844 sprintf (help_command + strlen(help_command),
846 p->help_url, p->help_url, p->help_url, p->help_url);
847 strcat (help_command, " ) &");
848 system (help_command);
854 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
856 state *s = global_state_kludge; /* I hate C so much... */
857 sensitize_menu_items (s, False);
862 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
864 state *s = global_state_kludge; /* I hate C so much... */
865 run_cmd (s, XA_ACTIVATE, 0);
870 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
872 state *s = global_state_kludge; /* I hate C so much... */
873 run_cmd (s, XA_LOCK, 0);
878 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
880 state *s = global_state_kludge; /* I hate C so much... */
881 run_cmd (s, XA_EXIT, 0);
886 restart_menu_cb (GtkWidget *widget, gpointer user_data)
888 state *s = global_state_kludge; /* I hate C so much... */
889 flush_dialog_changes_and_save (s);
890 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
892 system ("xscreensaver -nosplash &");
894 await_xscreensaver (s);
898 xscreensaver_running_p (state *s)
900 Display *dpy = GDK_DISPLAY();
902 server_xscreensaver_version (dpy, &rversion, 0, 0);
910 await_xscreensaver (state *s)
915 while (!ok && (--countdown > 0))
916 if (xscreensaver_running_p (s))
919 sleep (1); /* If it's not there yet, wait a second... */
921 sensitize_menu_items (s, True);
925 /* Timed out, no screensaver running. */
928 Bool root_p = (geteuid () == 0);
932 "The xscreensaver daemon did not start up properly.\n"
938 __extension__ /* don't warn about "string length is greater than
939 the length ISO C89 compilers are required to
940 support" in the following expression... */
943 _("You are running as root. This usually means that xscreensaver\n"
944 "was unable to contact your X server because access control is\n"
945 "turned on. Try running this command:\n"
947 " xhost +localhost\n"
949 "and then selecting `File / Restart Daemon'.\n"
951 "Note that turning off access control will allow anyone logged\n"
952 "on to this machine to access your screen, which might be\n"
953 "considered a security problem. Please read the xscreensaver\n"
954 "manual and FAQ for more information.\n"
956 "You shouldn't run X as root. Instead, you should log in as a\n"
957 "normal user, and `su' as necessary."));
959 strcat (buf, _("Please check your $PATH and permissions."));
961 warning_dialog (s->toplevel_widget, buf, False, 1);
964 force_dialog_repaint (s);
969 selected_list_element (state *s)
971 return s->_selected_list_element;
976 demo_write_init_file (state *s, saver_preferences *p)
980 /* #### try to figure out why shit keeps getting reordered... */
981 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
985 if (!write_init_file (p, s->short_version, False))
988 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
993 const char *f = init_file_name();
995 warning_dialog (s->toplevel_widget,
996 _("Error:\n\nCouldn't determine init file name!\n"),
1000 char *b = (char *) malloc (strlen(f) + 1024);
1001 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1002 warning_dialog (s->toplevel_widget, b, False, 100);
1010 G_MODULE_EXPORT void
1011 run_this_cb (GtkButton *button, gpointer user_data)
1013 state *s = global_state_kludge; /* I hate C so much... */
1014 int list_elt = selected_list_element (s);
1015 if (list_elt < 0) return;
1016 if (!flush_dialog_changes_and_save (s))
1017 run_hack (s, list_elt, True);
1021 G_MODULE_EXPORT void
1022 manual_cb (GtkButton *button, gpointer user_data)
1024 state *s = global_state_kludge; /* I hate C so much... */
1025 saver_preferences *p = &s->prefs;
1026 GtkWidget *list_widget = name_to_widget (s, "list");
1027 int list_elt = selected_list_element (s);
1029 char *name, *name2, *cmd, *str;
1030 if (list_elt < 0) return;
1031 hack_number = s->list_elt_to_hack_number[list_elt];
1033 flush_dialog_changes_and_save (s);
1034 ensure_selected_item_visible (list_widget);
1036 name = strdup (p->screenhacks[hack_number]->command);
1038 while (isspace (*name2)) name2++;
1040 while (*str && !isspace (*str)) str++;
1042 str = strrchr (name2, '/');
1043 if (str) name = str+1;
1045 cmd = get_string_resource ("manualCommand", "ManualCommand");
1048 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1049 strcpy (cmd2, "( ");
1050 sprintf (cmd2 + strlen (cmd2),
1052 name2, name2, name2, name2);
1053 strcat (cmd2, " ) &");
1059 warning_dialog (GTK_WIDGET (button),
1060 _("Error:\n\nno `manualCommand' resource set."),
1069 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1071 GtkWidget *parent = name_to_widget (s, "scroller");
1072 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
1075 GtkTreeModel *model;
1076 GtkTreeSelection *selection;
1077 #endif /* HAVE_GTK2 */
1079 if (!was) gtk_widget_set_sensitive (parent, True);
1081 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1082 STFU g_assert (model);
1083 gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt);
1084 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1085 gtk_tree_selection_select_iter (selection, &iter);
1086 #else /* !HAVE_GTK2 */
1087 gtk_list_select_item (GTK_LIST (list), list_elt);
1088 #endif /* !HAVE_GTK2 */
1089 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1090 if (!was) gtk_widget_set_sensitive (parent, False);
1094 G_MODULE_EXPORT void
1095 run_next_cb (GtkButton *button, gpointer user_data)
1097 state *s = global_state_kludge; /* I hate C so much... */
1098 /* saver_preferences *p = &s->prefs; */
1099 Bool ops = s->preview_suppressed_p;
1101 GtkWidget *list_widget = name_to_widget (s, "list");
1102 int list_elt = selected_list_element (s);
1109 if (list_elt >= s->list_count)
1112 s->preview_suppressed_p = True;
1114 flush_dialog_changes_and_save (s);
1115 force_list_select_item (s, list_widget, list_elt, True);
1116 populate_demo_window (s, list_elt);
1117 run_hack (s, list_elt, False);
1119 s->preview_suppressed_p = ops;
1123 G_MODULE_EXPORT void
1124 run_prev_cb (GtkButton *button, gpointer user_data)
1126 state *s = global_state_kludge; /* I hate C so much... */
1127 /* saver_preferences *p = &s->prefs; */
1128 Bool ops = s->preview_suppressed_p;
1130 GtkWidget *list_widget = name_to_widget (s, "list");
1131 int list_elt = selected_list_element (s);
1134 list_elt = s->list_count - 1;
1139 list_elt = s->list_count - 1;
1141 s->preview_suppressed_p = True;
1143 flush_dialog_changes_and_save (s);
1144 force_list_select_item (s, list_widget, list_elt, True);
1145 populate_demo_window (s, list_elt);
1146 run_hack (s, list_elt, False);
1148 s->preview_suppressed_p = ops;
1152 /* Writes the given settings into prefs.
1153 Returns true if there was a change, False otherwise.
1154 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1157 flush_changes (state *s,
1160 const char *command,
1163 saver_preferences *p = &s->prefs;
1164 Bool changed = False;
1167 if (list_elt < 0 || list_elt >= s->list_count)
1170 hack_number = s->list_elt_to_hack_number[list_elt];
1171 hack = p->screenhacks[hack_number];
1173 if (enabled_p != -1 &&
1174 enabled_p != hack->enabled_p)
1176 hack->enabled_p = enabled_p;
1179 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1180 blurb(), hack->name, enabled_p);
1185 if (!hack->command || !!strcmp (command, hack->command))
1187 if (hack->command) free (hack->command);
1188 hack->command = strdup (command);
1191 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1192 blurb(), hack->name, command);
1198 const char *ov = hack->visual;
1199 if (!ov || !*ov) ov = "any";
1200 if (!*visual) visual = "any";
1201 if (!!strcasecmp (visual, ov))
1203 if (hack->visual) free (hack->visual);
1204 hack->visual = strdup (visual);
1207 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1208 blurb(), hack->name, visual);
1216 /* Helper for the text fields that contain time specifications:
1217 this parses the text, and does error checking.
1220 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1225 if (!sec_p || strchr (line, ':'))
1226 value = parse_time ((char *) line, sec_p, True);
1230 if (sscanf (line, "%d%c", &value, &c) != 1)
1236 value *= 1000; /* Time measures in microseconds */
1242 "Unparsable time format: \"%s\"\n"),
1244 warning_dialog (s->toplevel_widget, b, False, 100);
1253 directory_p (const char *path)
1256 if (!path || !*path)
1258 else if (stat (path, &st))
1260 else if (!S_ISDIR (st.st_mode))
1267 file_p (const char *path)
1270 if (!path || !*path)
1272 else if (stat (path, &st))
1274 else if (S_ISDIR (st.st_mode))
1281 normalize_directory (const char *path)
1285 if (!path || !*path) return 0;
1287 p2 = (char *) malloc (L + 2);
1289 if (p2[L-1] == '/') /* remove trailing slash */
1292 for (s = p2; s && *s; s++)
1295 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1296 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1299 while (s0 > p2 && s0[-1] != '/')
1309 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1310 strcpy (s, s+2), s--;
1311 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1315 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1316 while (s[0] == '/' && s[1] == '/')
1319 /* and strip trailing whitespace for good measure. */
1321 while (isspace(p2[L-1]))
1334 } FlushForeachClosure;
1337 flush_checkbox (GtkTreeModel *model,
1342 FlushForeachClosure *closure = data;
1345 gtk_tree_model_get (model, iter,
1346 COL_ENABLED, &checked,
1349 if (flush_changes (closure->s, closure->i,
1351 *closure->changed = True;
1355 /* don't remove row */
1359 #endif /* HAVE_GTK2 */
1361 /* Flush out any changes made in the main dialog window (where changes
1362 take place immediately: clicking on a checkbox causes the init file
1363 to be written right away.)
1366 flush_dialog_changes_and_save (state *s)
1368 saver_preferences *p = &s->prefs;
1369 saver_preferences P2, *p2 = &P2;
1371 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1372 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1373 FlushForeachClosure closure;
1374 #else /* !HAVE_GTK2 */
1375 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1376 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1378 #endif /* !HAVE_GTK2 */
1380 Bool changed = False;
1383 if (s->saving_p) return False;
1388 /* Flush any checkbox changes in the list down into the prefs struct.
1392 closure.changed = &changed;
1394 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1396 #else /* !HAVE_GTK2 */
1398 for (i = 0; kids; kids = kids->next, i++)
1400 GtkWidget *line = GTK_WIDGET (kids->data);
1401 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1402 GtkWidget *line_check =
1403 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1405 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1407 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1410 #endif /* ~HAVE_GTK2 */
1412 /* Flush the non-hack-specific settings down into the prefs struct.
1415 # define SECONDS(FIELD,NAME) \
1416 w = name_to_widget (s, (NAME)); \
1417 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1419 # define MINUTES(FIELD,NAME) \
1420 w = name_to_widget (s, (NAME)); \
1421 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1423 # define CHECKBOX(FIELD,NAME) \
1424 w = name_to_widget (s, (NAME)); \
1425 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1427 # define PATHNAME(FIELD,NAME) \
1428 w = name_to_widget (s, (NAME)); \
1429 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1431 # define TEXT(FIELD,NAME) \
1432 w = name_to_widget (s, (NAME)); \
1433 (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1435 MINUTES (&p2->timeout, "timeout_spinbutton");
1436 MINUTES (&p2->cycle, "cycle_spinbutton");
1437 CHECKBOX (p2->lock_p, "lock_button");
1438 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1440 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1441 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1442 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1443 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1445 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1446 CHECKBOX (p2->grab_video_p, "grab_video_button");
1447 CHECKBOX (p2->random_image_p, "grab_image_button");
1448 PATHNAME (p2->image_directory, "image_text");
1451 CHECKBOX (p2->verbose_p, "verbose_button");
1452 CHECKBOX (p2->capture_stderr_p, "capture_button");
1453 CHECKBOX (p2->splash_p, "splash_button");
1458 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1459 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1460 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1461 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1462 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1463 TEXT (p2->text_literal, "text_entry");
1464 PATHNAME (p2->text_file, "text_file_entry");
1465 PATHNAME (p2->text_program, "text_program_entry");
1466 PATHNAME (p2->text_program, "text_program_entry");
1467 TEXT (p2->text_url, "text_url_entry");
1470 CHECKBOX (p2->install_cmap_p, "install_button");
1471 CHECKBOX (p2->fade_p, "fade_button");
1472 CHECKBOX (p2->unfade_p, "unfade_button");
1473 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1481 /* Warn if the image directory doesn't exist.
1483 if (p2->image_directory &&
1484 *p2->image_directory &&
1485 !directory_p (p2->image_directory))
1488 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1489 p2->image_directory);
1490 warning_dialog (s->toplevel_widget, b, False, 100);
1494 /* Map the mode menu to `saver_mode' enum values. */
1496 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1497 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1498 GtkWidget *selected = gtk_menu_get_active (menu);
1499 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1500 int menu_elt = g_list_index (kids, (gpointer) selected);
1501 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1502 p2->mode = mode_menu_order[menu_elt];
1505 if (p2->mode == ONE_HACK)
1507 int list_elt = selected_list_element (s);
1508 p2->selected_hack = (list_elt >= 0
1509 ? s->list_elt_to_hack_number[list_elt]
1513 # define COPY(field, name) \
1514 if (p->field != p2->field) { \
1517 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1519 p->field = p2->field
1522 COPY(selected_hack, "selected_hack");
1524 COPY(timeout, "timeout");
1525 COPY(cycle, "cycle");
1526 COPY(lock_p, "lock_p");
1527 COPY(lock_timeout, "lock_timeout");
1529 COPY(dpms_enabled_p, "dpms_enabled_p");
1530 COPY(dpms_standby, "dpms_standby");
1531 COPY(dpms_suspend, "dpms_suspend");
1532 COPY(dpms_off, "dpms_off");
1535 COPY(verbose_p, "verbose_p");
1536 COPY(capture_stderr_p, "capture_stderr_p");
1537 COPY(splash_p, "splash_p");
1540 COPY(tmode, "tmode");
1542 COPY(install_cmap_p, "install_cmap_p");
1543 COPY(fade_p, "fade_p");
1544 COPY(unfade_p, "unfade_p");
1545 COPY(fade_seconds, "fade_seconds");
1547 COPY(grab_desktop_p, "grab_desktop_p");
1548 COPY(grab_video_p, "grab_video_p");
1549 COPY(random_image_p, "random_image_p");
1553 # define COPYSTR(FIELD,NAME) \
1556 strcmp(p->FIELD, p2->FIELD)) \
1560 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1562 if (p->FIELD && p->FIELD != p2->FIELD) \
1564 p->FIELD = p2->FIELD; \
1567 COPYSTR(image_directory, "image_directory");
1568 COPYSTR(text_literal, "text_literal");
1569 COPYSTR(text_file, "text_file");
1570 COPYSTR(text_program, "text_program");
1571 COPYSTR(text_url, "text_url");
1574 populate_prefs_page (s);
1578 Display *dpy = GDK_DISPLAY();
1579 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1580 sync_server_dpms_settings (dpy, enabled_p,
1581 p->dpms_standby / 1000,
1582 p->dpms_suspend / 1000,
1586 changed = demo_write_init_file (s, p);
1589 s->saving_p = False;
1594 /* Flush out any changes made in the popup dialog box (where changes
1595 take place only when the OK button is clicked.)
1598 flush_popup_changes_and_save (state *s)
1600 Bool changed = False;
1601 saver_preferences *p = &s->prefs;
1602 int list_elt = selected_list_element (s);
1604 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1605 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1607 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1608 const char *command = gtk_entry_get_text (cmd);
1613 if (s->saving_p) return False;
1619 if (maybe_reload_init_file (s) != 0)
1625 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1627 if (!strcasecmp (visual, "")) visual = "";
1628 else if (!strcasecmp (visual, "any")) visual = "";
1629 else if (!strcasecmp (visual, "default")) visual = "Default";
1630 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1631 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1632 else if (!strcasecmp (visual, "best")) visual = "Best";
1633 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1634 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1635 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1636 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1637 else if (!strcasecmp (visual, "color")) visual = "Color";
1638 else if (!strcasecmp (visual, "gl")) visual = "GL";
1639 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1640 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1641 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1642 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1643 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1644 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1645 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1646 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1647 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1650 gdk_beep (); /* unparsable */
1652 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1655 changed = flush_changes (s, list_elt, -1, command, visual);
1658 changed = demo_write_init_file (s, p);
1660 /* Do this to re-launch the hack if (and only if) the command line
1662 populate_demo_window (s, selected_list_element (s));
1666 s->saving_p = False;
1671 G_MODULE_EXPORT void
1672 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1674 state *s = global_state_kludge; /* I hate C so much... */
1675 if (! s->initializing_p)
1677 s->initializing_p = True;
1678 flush_dialog_changes_and_save (s);
1679 s->initializing_p = False;
1683 G_MODULE_EXPORT gboolean
1684 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1686 pref_changed_cb (widget, user_data);
1690 /* Callback on menu items in the "mode" options menu.
1693 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1695 state *s = (state *) user_data;
1696 saver_preferences *p = &s->prefs;
1697 GtkWidget *list = name_to_widget (s, "list");
1700 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1702 saver_mode new_mode;
1706 if (menu_items->data == widget)
1709 menu_items = menu_items->next;
1711 if (!menu_items) abort();
1713 new_mode = mode_menu_order[menu_index];
1715 /* Keep the same list element displayed as before; except if we're
1716 switching *to* "one screensaver" mode from any other mode, set
1717 "the one" to be that which is currently selected.
1719 list_elt = selected_list_element (s);
1720 if (new_mode == ONE_HACK)
1721 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1724 saver_mode old_mode = p->mode;
1726 populate_demo_window (s, list_elt);
1727 force_list_select_item (s, list, list_elt, True);
1728 p->mode = old_mode; /* put it back, so the init file gets written */
1731 pref_changed_cb (widget, user_data);
1735 G_MODULE_EXPORT void
1736 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1737 gint page_num, gpointer user_data)
1739 state *s = global_state_kludge; /* I hate C so much... */
1740 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1742 /* If we're switching to page 0, schedule the current hack to be run.
1743 Otherwise, schedule it to stop. */
1745 populate_demo_window (s, selected_list_element (s));
1747 schedule_preview (s, 0);
1752 list_activated_cb (GtkTreeView *list,
1754 GtkTreeViewColumn *column,
1761 STFU g_return_if_fail (!gdk_pointer_is_grabbed ());
1763 str = gtk_tree_path_to_string (path);
1764 list_elt = strtol (str, NULL, 10);
1768 run_hack (s, list_elt, True);
1772 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1774 state *s = (state *)data;
1775 GtkTreeModel *model;
1781 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1784 path = gtk_tree_model_get_path (model, &iter);
1785 str = gtk_tree_path_to_string (path);
1786 list_elt = strtol (str, NULL, 10);
1788 gtk_tree_path_free (path);
1791 populate_demo_window (s, list_elt);
1792 flush_dialog_changes_and_save (s);
1794 /* Re-populate the Settings window any time a new item is selected
1795 in the list, in case both windows are currently visible.
1797 populate_popup_window (s);
1800 #else /* !HAVE_GTK2 */
1802 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1803 list_select_cb that comes in
1804 *after* we've double-clicked.
1808 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1811 state *s = (state *) data;
1812 if (event->type == GDK_2BUTTON_PRESS)
1814 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1815 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1817 last_doubleclick_time = time ((time_t *) 0);
1820 run_hack (s, list_elt, True);
1828 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1830 state *s = (state *) data;
1831 time_t now = time ((time_t *) 0);
1833 if (now >= last_doubleclick_time + 2)
1835 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1836 populate_demo_window (s, list_elt);
1837 flush_dialog_changes_and_save (s);
1842 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1844 state *s = (state *) data;
1845 populate_demo_window (s, -1);
1846 flush_dialog_changes_and_save (s);
1849 #endif /* !HAVE_GTK2 */
1852 /* Called when the checkboxes that are in the left column of the
1853 scrolling list are clicked. This both populates the right pane
1854 (just as clicking on the label (really, listitem) does) and
1855 also syncs this checkbox with the right pane Enabled checkbox.
1860 GtkCellRendererToggle *toggle,
1862 #else /* !HAVE_GTK2 */
1864 #endif /* !HAVE_GTK2 */
1867 state *s = (state *) data;
1870 GtkScrolledWindow *scroller =
1871 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1872 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1873 GtkTreeModel *model = gtk_tree_view_get_model (list);
1874 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1877 #else /* !HAVE_GTK2 */
1878 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1879 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1881 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1882 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1883 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1884 #endif /* !HAVE_GTK2 */
1891 if (!gtk_tree_model_get_iter (model, &iter, path))
1893 g_warning ("bad path: %s", path_string);
1896 gtk_tree_path_free (path);
1898 gtk_tree_model_get (model, &iter,
1899 COL_ENABLED, &active,
1902 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1903 COL_ENABLED, !active,
1906 list_elt = strtol (path_string, NULL, 10);
1907 #else /* !HAVE_GTK2 */
1908 list_elt = gtk_list_child_position (list, line);
1909 #endif /* !HAVE_GTK2 */
1911 /* remember previous scroll position of the top of the list */
1912 adj = gtk_scrolled_window_get_vadjustment (scroller);
1913 scroll_top = adj->value;
1915 flush_dialog_changes_and_save (s);
1916 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1917 populate_demo_window (s, list_elt);
1919 /* restore the previous scroll position of the top of the list.
1920 this is weak, but I don't really know why it's moving... */
1921 gtk_adjustment_set_value (adj, scroll_top);
1927 GtkFileSelection *widget;
1928 } file_selection_data;
1933 store_image_directory (GtkWidget *button, gpointer user_data)
1935 file_selection_data *fsd = (file_selection_data *) user_data;
1936 state *s = fsd->state;
1937 GtkFileSelection *selector = fsd->widget;
1938 GtkWidget *top = s->toplevel_widget;
1939 saver_preferences *p = &s->prefs;
1940 const char *path = gtk_file_selection_get_filename (selector);
1942 if (p->image_directory && !strcmp(p->image_directory, path))
1943 return; /* no change */
1945 if (!directory_p (path))
1948 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1949 warning_dialog (GTK_WIDGET (top), b, False, 100);
1953 if (p->image_directory) free (p->image_directory);
1954 p->image_directory = normalize_directory (path);
1956 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1957 (p->image_directory ? p->image_directory : ""));
1958 demo_write_init_file (s, p);
1963 store_text_file (GtkWidget *button, gpointer user_data)
1965 file_selection_data *fsd = (file_selection_data *) user_data;
1966 state *s = fsd->state;
1967 GtkFileSelection *selector = fsd->widget;
1968 GtkWidget *top = s->toplevel_widget;
1969 saver_preferences *p = &s->prefs;
1970 const char *path = gtk_file_selection_get_filename (selector);
1972 if (p->text_file && !strcmp(p->text_file, path))
1973 return; /* no change */
1978 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
1979 warning_dialog (GTK_WIDGET (top), b, False, 100);
1983 if (p->text_file) free (p->text_file);
1984 p->text_file = normalize_directory (path);
1986 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
1987 (p->text_file ? p->text_file : ""));
1988 demo_write_init_file (s, p);
1993 store_text_program (GtkWidget *button, gpointer user_data)
1995 file_selection_data *fsd = (file_selection_data *) user_data;
1996 state *s = fsd->state;
1997 GtkFileSelection *selector = fsd->widget;
1998 /*GtkWidget *top = s->toplevel_widget;*/
1999 saver_preferences *p = &s->prefs;
2000 const char *path = gtk_file_selection_get_filename (selector);
2002 if (p->text_program && !strcmp(p->text_program, path))
2003 return; /* no change */
2009 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2010 warning_dialog (GTK_WIDGET (top), b, False, 100);
2015 if (p->text_program) free (p->text_program);
2016 p->text_program = normalize_directory (path);
2018 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2019 (p->text_program ? p->text_program : ""));
2020 demo_write_init_file (s, p);
2026 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2028 file_selection_data *fsd = (file_selection_data *) user_data;
2029 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2033 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2035 browse_image_dir_cancel (button, user_data);
2036 store_image_directory (button, user_data);
2040 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2042 browse_image_dir_cancel (button, user_data);
2043 store_text_file (button, user_data);
2047 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2049 browse_image_dir_cancel (button, user_data);
2050 store_text_program (button, user_data);
2054 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2056 browse_image_dir_cancel (widget, user_data);
2060 G_MODULE_EXPORT void
2061 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2063 state *s = global_state_kludge; /* I hate C so much... */
2064 saver_preferences *p = &s->prefs;
2065 static file_selection_data *fsd = 0;
2067 GtkFileSelection *selector = GTK_FILE_SELECTION(
2068 gtk_file_selection_new ("Please select the image directory."));
2071 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2073 fsd->widget = selector;
2076 if (p->image_directory && *p->image_directory)
2077 gtk_file_selection_set_filename (selector, p->image_directory);
2079 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2080 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2082 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2083 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2085 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2086 GTK_SIGNAL_FUNC (browse_image_dir_close),
2089 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2091 gtk_window_set_modal (GTK_WINDOW (selector), True);
2092 gtk_widget_show (GTK_WIDGET (selector));
2096 G_MODULE_EXPORT void
2097 browse_text_file_cb (GtkButton *button, gpointer user_data)
2099 state *s = global_state_kludge; /* I hate C so much... */
2100 saver_preferences *p = &s->prefs;
2101 static file_selection_data *fsd = 0;
2103 GtkFileSelection *selector = GTK_FILE_SELECTION(
2104 gtk_file_selection_new ("Please select a text file."));
2107 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2109 fsd->widget = selector;
2112 if (p->text_file && *p->text_file)
2113 gtk_file_selection_set_filename (selector, p->text_file);
2115 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2116 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2118 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2119 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2121 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2122 GTK_SIGNAL_FUNC (browse_image_dir_close),
2125 gtk_window_set_modal (GTK_WINDOW (selector), True);
2126 gtk_widget_show (GTK_WIDGET (selector));
2130 G_MODULE_EXPORT void
2131 browse_text_program_cb (GtkButton *button, gpointer user_data)
2133 state *s = global_state_kludge; /* I hate C so much... */
2134 saver_preferences *p = &s->prefs;
2135 static file_selection_data *fsd = 0;
2137 GtkFileSelection *selector = GTK_FILE_SELECTION(
2138 gtk_file_selection_new ("Please select a text-generating program."));
2141 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2143 fsd->widget = selector;
2146 if (p->text_program && *p->text_program)
2147 gtk_file_selection_set_filename (selector, p->text_program);
2149 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2150 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2152 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2153 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2155 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2156 GTK_SIGNAL_FUNC (browse_image_dir_close),
2159 gtk_window_set_modal (GTK_WINDOW (selector), True);
2160 gtk_widget_show (GTK_WIDGET (selector));
2167 G_MODULE_EXPORT void
2168 settings_cb (GtkButton *button, gpointer user_data)
2170 state *s = global_state_kludge; /* I hate C so much... */
2171 int list_elt = selected_list_element (s);
2173 populate_demo_window (s, list_elt); /* reset the widget */
2174 populate_popup_window (s); /* create UI on popup window */
2175 gtk_widget_show (s->popup_widget);
2179 settings_sync_cmd_text (state *s)
2182 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2183 char *cmd_line = get_configurator_command_line (s->cdata);
2184 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2185 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2187 # endif /* HAVE_XML */
2190 G_MODULE_EXPORT void
2191 settings_adv_cb (GtkButton *button, gpointer user_data)
2193 state *s = global_state_kludge; /* I hate C so much... */
2194 GtkNotebook *notebook =
2195 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2197 settings_sync_cmd_text (s);
2198 gtk_notebook_set_page (notebook, 1);
2201 G_MODULE_EXPORT void
2202 settings_std_cb (GtkButton *button, gpointer user_data)
2204 state *s = global_state_kludge; /* I hate C so much... */
2205 GtkNotebook *notebook =
2206 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2208 /* Re-create UI to reflect the in-progress command-line settings. */
2209 populate_popup_window (s);
2211 gtk_notebook_set_page (notebook, 0);
2214 G_MODULE_EXPORT void
2215 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2216 gint page_num, gpointer user_data)
2218 state *s = global_state_kludge; /* I hate C so much... */
2219 GtkWidget *adv = name_to_widget (s, "adv_button");
2220 GtkWidget *std = name_to_widget (s, "std_button");
2224 gtk_widget_show (adv);
2225 gtk_widget_hide (std);
2227 else if (page_num == 1)
2229 gtk_widget_hide (adv);
2230 gtk_widget_show (std);
2238 G_MODULE_EXPORT void
2239 settings_cancel_cb (GtkButton *button, gpointer user_data)
2241 state *s = global_state_kludge; /* I hate C so much... */
2242 gtk_widget_hide (s->popup_widget);
2245 G_MODULE_EXPORT void
2246 settings_ok_cb (GtkButton *button, gpointer user_data)
2248 state *s = global_state_kludge; /* I hate C so much... */
2249 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2250 int page = gtk_notebook_get_current_page (notebook);
2253 /* Regenerate the command-line from the widget contents before saving.
2254 But don't do this if we're looking at the command-line page already,
2255 or we will blow away what they typed... */
2256 settings_sync_cmd_text (s);
2258 flush_popup_changes_and_save (s);
2259 gtk_widget_hide (s->popup_widget);
2263 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2265 state *s = (state *) data;
2266 settings_cancel_cb (0, (gpointer) s);
2272 /* Populating the various widgets
2276 /* Returns the number of the last hack run by the server.
2279 server_current_hack (void)
2283 unsigned long nitems, bytesafter;
2284 unsigned char *dataP = 0;
2285 Display *dpy = GDK_DISPLAY();
2286 int hack_number = -1;
2288 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2289 XA_SCREENSAVER_STATUS,
2290 0, 3, False, XA_INTEGER,
2291 &type, &format, &nitems, &bytesafter,
2294 && type == XA_INTEGER
2298 CARD32 *data = (CARD32 *) dataP;
2299 hack_number = (int) data[2] - 1;
2302 if (dataP) XFree (dataP);
2308 /* Finds the number of the last hack that was run, and makes that item be
2309 selected by default.
2312 scroll_to_current_hack (state *s)
2314 saver_preferences *p = &s->prefs;
2315 int hack_number = -1;
2317 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2318 hack_number = p->selected_hack;
2319 if (hack_number < 0) /* otherwise, use the last-run */
2320 hack_number = server_current_hack ();
2321 if (hack_number < 0) /* failing that, last "one mode" */
2322 hack_number = p->selected_hack;
2323 if (hack_number < 0) /* failing that, newest hack. */
2325 /* We should only get here if the user does not have a .xscreensaver
2326 file, and the screen has not been blanked with a hack since X
2327 started up: in other words, this is probably a fresh install.
2329 Instead of just defaulting to hack #0 (in either "programs" or
2330 "alphabetical" order) let's try to default to the last runnable
2331 hack in the "programs" list: this is probably the hack that was
2332 most recently added to the xscreensaver distribution (and so
2333 it's probably the currently-coolest one!)
2335 hack_number = p->screenhacks_count-1;
2336 while (hack_number > 0 &&
2337 ! (s->hacks_available_p[hack_number] &&
2338 p->screenhacks[hack_number]->enabled_p))
2342 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2344 int list_elt = s->hack_number_to_list_elt[hack_number];
2345 GtkWidget *list = name_to_widget (s, "list");
2346 force_list_select_item (s, list, list_elt, True);
2347 populate_demo_window (s, list_elt);
2353 on_path_p (const char *program)
2357 char *cmd = strdup (program);
2358 char *token = strchr (cmd, ' ');
2362 if (token) *token = 0;
2365 if (strchr (cmd, '/'))
2367 result = (0 == stat (cmd, &st));
2371 path = getenv("PATH");
2372 if (!path || !*path)
2376 path = strdup (path);
2377 token = strtok (path, ":");
2381 char *p2 = (char *) malloc (strlen (token) + L + 3);
2385 result = (0 == stat (p2, &st));
2388 token = strtok (0, ":");
2393 if (path) free (path);
2399 populate_hack_list (state *s)
2402 saver_preferences *p = &s->prefs;
2403 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2404 GtkListStore *model;
2405 GtkTreeSelection *selection;
2406 GtkCellRenderer *ren;
2410 g_object_get (G_OBJECT (list),
2415 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2416 g_object_set (G_OBJECT (list), "model", model, NULL);
2417 g_object_unref (model);
2419 ren = gtk_cell_renderer_toggle_new ();
2420 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2422 "active", COL_ENABLED,
2425 g_signal_connect (ren, "toggled",
2426 G_CALLBACK (list_checkbox_cb),
2429 ren = gtk_cell_renderer_text_new ();
2430 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2431 _("Screen Saver"), ren,
2435 g_signal_connect_after (list, "row_activated",
2436 G_CALLBACK (list_activated_cb),
2439 selection = gtk_tree_view_get_selection (list);
2440 g_signal_connect (selection, "changed",
2441 G_CALLBACK (list_select_changed_cb),
2446 for (i = 0; i < s->list_count; i++)
2448 int hack_number = s->list_elt_to_hack_number[i];
2449 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2451 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2453 if (!hack) continue;
2455 /* If we're to suppress uninstalled hacks, check $PATH now. */
2456 if (p->ignore_uninstalled_p && !available_p)
2459 pretty_name = (hack->name
2460 ? strdup (hack->name)
2461 : make_hack_name (hack->command));
2465 /* Make the text foreground be the color of insensitive widgets
2466 (but don't actually make it be insensitive, since we still
2467 want to be able to click on it.)
2469 GtkStyle *style = GTK_WIDGET (list)->style;
2470 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2471 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2472 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2474 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2475 /* " background=\"#%02X%02X%02X\"" */
2477 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2478 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2484 gtk_list_store_append (model, &iter);
2485 gtk_list_store_set (model, &iter,
2486 COL_ENABLED, hack->enabled_p,
2487 COL_NAME, pretty_name,
2492 #else /* !HAVE_GTK2 */
2494 saver_preferences *p = &s->prefs;
2495 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2497 for (i = 0; i < s->list_count; i++)
2499 int hack_number = s->list_elt_to_hack_number[i];
2500 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2502 /* A GtkList must contain only GtkListItems, but those can contain
2503 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2504 and a Label. We handle single and double click events on the
2505 line itself, for clicking on the text, but the interior checkbox
2506 also handles its own events.
2509 GtkWidget *line_hbox;
2510 GtkWidget *line_check;
2511 GtkWidget *line_label;
2513 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2515 if (!hack) continue;
2517 /* If we're to suppress uninstalled hacks, check $PATH now. */
2518 if (p->ignore_uninstalled_p && !available_p)
2521 pretty_name = (hack->name
2522 ? strdup (hack->name)
2523 : make_hack_name (hack->command));
2525 line = gtk_list_item_new ();
2526 line_hbox = gtk_hbox_new (FALSE, 0);
2527 line_check = gtk_check_button_new ();
2528 line_label = gtk_label_new (pretty_name);
2530 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2531 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2532 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2534 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2536 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2538 gtk_widget_show (line_check);
2539 gtk_widget_show (line_label);
2540 gtk_widget_show (line_hbox);
2541 gtk_widget_show (line);
2545 gtk_container_add (GTK_CONTAINER (list), line);
2546 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2547 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2550 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2551 GTK_SIGNAL_FUNC (list_checkbox_cb),
2554 gtk_widget_show (line);
2558 /* Make the widget be colored like insensitive widgets
2559 (but don't actually make it be insensitive, since we
2560 still want to be able to click on it.)
2562 GtkRcStyle *rc_style;
2565 gtk_widget_realize (GTK_WIDGET (line_label));
2567 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2568 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2570 rc_style = gtk_rc_style_new ();
2571 rc_style->fg[GTK_STATE_NORMAL] = fg;
2572 rc_style->bg[GTK_STATE_NORMAL] = bg;
2573 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2575 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2576 gtk_rc_style_unref (rc_style);
2580 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2581 GTK_SIGNAL_FUNC (list_select_cb),
2583 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2584 GTK_SIGNAL_FUNC (list_unselect_cb),
2586 #endif /* !HAVE_GTK2 */
2590 update_list_sensitivity (state *s)
2592 saver_preferences *p = &s->prefs;
2593 Bool sensitive = (p->mode == RANDOM_HACKS ||
2594 p->mode == RANDOM_HACKS_SAME ||
2595 p->mode == ONE_HACK);
2596 Bool checkable = (p->mode == RANDOM_HACKS ||
2597 p->mode == RANDOM_HACKS_SAME);
2598 Bool blankable = (p->mode != DONT_BLANK);
2601 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2602 GtkWidget *use = name_to_widget (s, "use_col_frame");
2603 #endif /* HAVE_GTK2 */
2604 GtkWidget *scroller = name_to_widget (s, "scroller");
2605 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2606 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2609 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2610 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2611 #else /* !HAVE_GTK2 */
2612 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2613 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2615 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2616 #endif /* !HAVE_GTK2 */
2617 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2618 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2620 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2623 gtk_tree_view_column_set_visible (use, checkable);
2624 #else /* !HAVE_GTK2 */
2626 gtk_widget_show (use); /* the "Use" column header */
2628 gtk_widget_hide (use);
2632 GtkBin *line = GTK_BIN (kids->data);
2633 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2634 GtkWidget *line_check =
2635 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2638 gtk_widget_show (line_check);
2640 gtk_widget_hide (line_check);
2644 #endif /* !HAVE_GTK2 */
2649 populate_prefs_page (state *s)
2651 saver_preferences *p = &s->prefs;
2653 Bool can_lock_p = True;
2655 /* Disable all the "lock" controls if locking support was not provided
2656 at compile-time, or if running on MacOS. */
2657 # if defined(NO_LOCKING) || defined(__APPLE__)
2662 /* If there is only one screen, the mode menu contains
2663 "random" but not "random-same".
2665 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2666 p->mode = RANDOM_HACKS;
2669 /* The file supports timeouts of less than a minute, but the GUI does
2670 not, so throttle the values to be at least one minute (since "0" is
2671 a bad rounding choice...)
2673 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2676 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2679 # define FMT_MINUTES(NAME,N) \
2680 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2682 # define FMT_SECONDS(NAME,N) \
2683 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2685 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2686 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2687 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2688 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2689 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2690 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2691 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2696 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2697 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2700 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2702 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2703 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2704 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2706 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2707 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2708 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2709 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2710 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2711 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2712 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2716 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2717 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2718 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2719 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2720 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2723 # undef TOGGLE_ACTIVE
2725 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2726 (p->image_directory ? p->image_directory : ""));
2727 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2729 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2732 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2733 (p->text_literal ? p->text_literal : ""));
2734 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2735 (p->text_file ? p->text_file : ""));
2736 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2737 (p->text_program ? p->text_program : ""));
2738 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2739 (p->text_url ? p->text_url : ""));
2741 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2742 p->tmode == TEXT_LITERAL);
2743 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2744 p->tmode == TEXT_FILE);
2745 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2746 p->tmode == TEXT_FILE);
2747 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2748 p->tmode == TEXT_PROGRAM);
2749 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2750 p->tmode == TEXT_PROGRAM);
2751 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2752 p->tmode == TEXT_URL);
2755 /* Map the `saver_mode' enum to mode menu to values. */
2757 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2760 for (i = 0; i < countof(mode_menu_order); i++)
2761 if (mode_menu_order[i] == p->mode)
2763 gtk_option_menu_set_history (opt, i);
2764 update_list_sensitivity (s);
2768 Bool found_any_writable_cells = False;
2769 Bool fading_possible = False;
2770 Bool dpms_supported = False;
2772 Display *dpy = GDK_DISPLAY();
2773 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2775 for (i = 0; i < nscreens; i++)
2777 Screen *s = ScreenOfDisplay (dpy, i);
2778 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2780 found_any_writable_cells = True;
2785 fading_possible = found_any_writable_cells;
2786 #ifdef HAVE_XF86VMODE_GAMMA
2787 fading_possible = True;
2790 #ifdef HAVE_DPMS_EXTENSION
2792 int op = 0, event = 0, error = 0;
2793 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2794 dpms_supported = True;
2796 #endif /* HAVE_DPMS_EXTENSION */
2799 # define SENSITIZE(NAME,SENSITIVEP) \
2800 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2802 /* Blanking and Locking
2804 SENSITIZE ("lock_button", can_lock_p);
2805 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2806 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2810 SENSITIZE ("dpms_frame", dpms_supported);
2811 SENSITIZE ("dpms_button", dpms_supported);
2812 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2813 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2814 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2815 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2816 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2817 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2818 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2819 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2820 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2824 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2825 SENSITIZE ("install_button", found_any_writable_cells);
2826 SENSITIZE ("fade_button", fading_possible);
2827 SENSITIZE ("unfade_button", fading_possible);
2829 SENSITIZE ("fade_label", (fading_possible &&
2830 (p->fade_p || p->unfade_p)));
2831 SENSITIZE ("fade_spinbutton", (fading_possible &&
2832 (p->fade_p || p->unfade_p)));
2840 populate_popup_window (state *s)
2842 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2843 char *doc_string = 0;
2845 /* #### not in Gtk 1.2
2846 gtk_label_set_selectable (doc);
2852 free_conf_data (s->cdata);
2857 saver_preferences *p = &s->prefs;
2858 int list_elt = selected_list_element (s);
2859 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2860 ? s->list_elt_to_hack_number[list_elt]
2862 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2865 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2866 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2867 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2868 s->cdata = load_configurator (cmd_line, s->debug_p);
2869 if (s->cdata && s->cdata->widget)
2870 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2875 doc_string = (s->cdata
2876 ? s->cdata->description
2878 # else /* !HAVE_XML */
2879 doc_string = _("Descriptions not available: no XML support compiled in.");
2880 # endif /* !HAVE_XML */
2882 gtk_label_set_text (doc, (doc_string
2884 : _("No description available.")));
2889 sensitize_demo_widgets (state *s, Bool sensitive_p)
2891 const char *names[] = { "demo", "settings",
2892 "cmd_label", "cmd_text", "manual",
2893 "visual", "visual_combo" };
2895 for (i = 0; i < countof(names); i++)
2897 GtkWidget *w = name_to_widget (s, names[i]);
2898 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2904 sensitize_menu_items (state *s, Bool force_p)
2906 static Bool running_p = False;
2907 static time_t last_checked = 0;
2908 time_t now = time ((time_t *) 0);
2909 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
2913 if (force_p || now > last_checked + 10) /* check every 10 seconds */
2915 running_p = xscreensaver_running_p (s);
2916 last_checked = time ((time_t *) 0);
2919 for (i = 0; i < countof(names); i++)
2921 GtkWidget *w = name_to_widget (s, names[i]);
2922 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
2927 /* When the File menu is de-posted after a "Restart Daemon" command,
2928 the window underneath doesn't repaint for some reason. I guess this
2929 is a bug in exposure handling in GTK or GDK. This works around it.
2932 force_dialog_repaint (state *s)
2935 /* Tell GDK to invalidate and repaint the whole window.
2937 GdkWindow *w = s->toplevel_widget->window;
2938 GdkRegion *region = gdk_region_new ();
2940 rect.x = rect.y = 0;
2941 rect.width = rect.height = 32767;
2942 gdk_region_union_with_rect (region, &rect);
2943 gdk_window_invalidate_region (w, region, True);
2944 gdk_region_destroy (region);
2945 gdk_window_process_updates (w, True);
2947 /* Force the server to send an exposure event by creating and then
2948 destroying a window as a child of the top level shell.
2950 Display *dpy = GDK_DISPLAY();
2951 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
2953 XWindowAttributes xgwa;
2954 XGetWindowAttributes (dpy, parent, &xgwa);
2955 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
2956 XMapRaised (dpy, w);
2957 XDestroyWindow (dpy, w);
2963 /* Even though we've given these text fields a maximum number of characters,
2964 their default size is still about 30 characters wide -- so measure out
2965 a string in their font, and resize them to just fit that.
2968 fix_text_entry_sizes (state *s)
2972 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
2973 const char * const spinbuttons[] = {
2974 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2975 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2976 "dpms_off_spinbutton",
2977 "-fade_spinbutton" };
2981 for (i = 0; i < countof(spinbuttons); i++)
2983 const char *n = spinbuttons[i];
2985 while (*n == '-') n++, cols--;
2986 w = GTK_WIDGET (name_to_widget (s, n));
2987 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2988 gtk_widget_set_usize (w, width, -2);
2991 /* Now fix the width of the combo box.
2993 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2994 w = GTK_COMBO (w)->entry;
2995 width = gdk_string_width (w->style->font, "PseudoColor___");
2996 gtk_widget_set_usize (w, width, -2);
2998 /* Now fix the width of the file entry text.
3000 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3001 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3002 gtk_widget_set_usize (w, width, -2);
3004 /* Now fix the width of the command line text.
3006 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3007 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3008 gtk_widget_set_usize (w, width, -2);
3012 /* Now fix the height of the list widget:
3013 make it default to being around 10 text-lines high instead of 4.
3015 w = GTK_WIDGET (name_to_widget (s, "list"));
3019 int leading = 3; /* approximate is ok... */
3023 PangoFontMetrics *pain =
3024 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3025 w->style->font_desc,
3026 gtk_get_default_language ());
3027 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3028 pango_font_metrics_get_descent (pain));
3029 #else /* !HAVE_GTK2 */
3030 height = w->style->font->ascent + w->style->font->descent;
3031 #endif /* !HAVE_GTK2 */
3035 height += border * 2;
3036 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3037 gtk_widget_set_usize (w, -2, height);
3044 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3047 static char *up_arrow_xpm[] = {
3070 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3071 the end of the array (Gtk 1.2.5.) */
3072 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3073 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3076 static char *down_arrow_xpm[] = {
3099 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3100 the end of the array (Gtk 1.2.5.) */
3101 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3102 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3106 pixmapify_button (state *s, int down_p)
3110 GtkWidget *pixmapwid;
3114 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3115 style = gtk_widget_get_style (w);
3117 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3118 &style->bg[GTK_STATE_NORMAL],
3120 ? (gchar **) down_arrow_xpm
3121 : (gchar **) up_arrow_xpm));
3122 pixmapwid = gtk_pixmap_new (pixmap, mask);
3123 gtk_widget_show (pixmapwid);
3124 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3125 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3129 map_next_button_cb (GtkWidget *w, gpointer user_data)
3131 state *s = (state *) user_data;
3132 pixmapify_button (s, 1);
3136 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3138 state *s = (state *) user_data;
3139 pixmapify_button (s, 0);
3141 #endif /* !HAVE_GTK2 */
3145 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3149 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3150 GtkAllocation *allocation,
3154 GtkWidgetAuxInfo *aux_info;
3156 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3158 aux_info->width = allocation->width;
3159 aux_info->height = -2;
3163 gtk_widget_size_request (label, &req);
3166 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3169 eschew_gtk_lossage (GtkLabel *label)
3171 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3172 aux_info->width = GTK_WIDGET (label)->allocation.width;
3173 aux_info->height = -2;
3177 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3179 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3180 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3183 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3185 gtk_widget_queue_resize (GTK_WIDGET (label));
3187 #endif /* !HAVE_GTK2 */
3191 populate_demo_window (state *s, int list_elt)
3193 saver_preferences *p = &s->prefs;
3196 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3197 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
3198 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3199 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3200 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3202 if (p->mode == BLANK_ONLY)
3205 pretty_name = strdup (_("Blank Screen"));
3206 schedule_preview (s, 0);
3208 else if (p->mode == DONT_BLANK)
3211 pretty_name = strdup (_("Screen Saver Disabled"));
3212 schedule_preview (s, 0);
3216 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3217 ? s->list_elt_to_hack_number[list_elt]
3219 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3223 ? strdup (hack->name)
3224 : make_hack_name (hack->command))
3228 schedule_preview (s, hack->command);
3230 schedule_preview (s, 0);
3234 pretty_name = strdup (_("Preview"));
3236 gtk_frame_set_label (frame1, _(pretty_name));
3237 gtk_frame_set_label (frame2, _(pretty_name));
3239 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3240 gtk_entry_set_position (cmd, 0);
3244 sprintf (title, _("%s: %.100s Settings"),
3245 progclass, (pretty_name ? pretty_name : "???"));
3246 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3249 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3251 ? (hack->visual && *hack->visual
3256 sensitize_demo_widgets (s, (hack ? True : False));
3258 if (pretty_name) free (pretty_name);
3260 ensure_selected_item_visible (list);
3262 s->_selected_list_element = list_elt;
3267 widget_deleter (GtkWidget *widget, gpointer data)
3269 /* #### Well, I want to destroy these widgets, but if I do that, they get
3270 referenced again, and eventually I get a SEGV. So instead of
3271 destroying them, I'll just hide them, and leak a bunch of memory
3272 every time the disk file changes. Go go go Gtk!
3274 #### Ok, that's a lie, I get a crash even if I just hide the widget
3275 and don't ever delete it. Fuck!
3278 gtk_widget_destroy (widget);
3280 gtk_widget_hide (widget);
3285 static char **sort_hack_cmp_names_kludge;
3287 sort_hack_cmp (const void *a, const void *b)
3293 int aa = *(int *) a;
3294 int bb = *(int *) b;
3295 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3296 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3297 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3303 initialize_sort_map (state *s)
3305 saver_preferences *p = &s->prefs;
3308 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3309 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3310 if (s->hacks_available_p) free (s->hacks_available_p);
3312 s->list_elt_to_hack_number = (int *)
3313 calloc (sizeof(int), p->screenhacks_count + 1);
3314 s->hack_number_to_list_elt = (int *)
3315 calloc (sizeof(int), p->screenhacks_count + 1);
3316 s->hacks_available_p = (Bool *)
3317 calloc (sizeof(Bool), p->screenhacks_count + 1);
3319 /* Check which hacks actually exist on $PATH
3321 for (i = 0; i < p->screenhacks_count; i++)
3323 screenhack *hack = p->screenhacks[i];
3324 s->hacks_available_p[i] = on_path_p (hack->command);
3327 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3331 for (i = 0; i < p->screenhacks_count; i++)
3333 if (!p->ignore_uninstalled_p ||
3334 s->hacks_available_p[i])
3335 s->list_elt_to_hack_number[j++] = i;
3339 for (; j < p->screenhacks_count; j++)
3340 s->list_elt_to_hack_number[j] = -1;
3343 /* Generate list of sortable names (once)
3345 sort_hack_cmp_names_kludge = (char **)
3346 calloc (sizeof(char *), p->screenhacks_count);
3347 for (i = 0; i < p->screenhacks_count; i++)
3349 screenhack *hack = p->screenhacks[i];
3350 char *name = (hack->name && *hack->name
3351 ? strdup (hack->name)
3352 : make_hack_name (hack->command));
3354 for (str = name; *str; str++)
3355 *str = tolower(*str);
3356 sort_hack_cmp_names_kludge[i] = name;
3359 /* Sort list->hack map alphabetically
3361 qsort (s->list_elt_to_hack_number,
3362 p->screenhacks_count,
3363 sizeof(*s->list_elt_to_hack_number),
3368 for (i = 0; i < p->screenhacks_count; i++)
3369 free (sort_hack_cmp_names_kludge[i]);
3370 free (sort_hack_cmp_names_kludge);
3371 sort_hack_cmp_names_kludge = 0;
3373 /* Build inverse table */
3374 for (i = 0; i < p->screenhacks_count; i++)
3376 int n = s->list_elt_to_hack_number[i];
3378 s->hack_number_to_list_elt[n] = i;
3384 maybe_reload_init_file (state *s)
3386 saver_preferences *p = &s->prefs;
3389 static Bool reentrant_lock = False;
3390 if (reentrant_lock) return 0;
3391 reentrant_lock = True;
3393 if (init_file_changed_p (p))
3395 const char *f = init_file_name();
3400 if (!f || !*f) return 0;
3401 b = (char *) malloc (strlen(f) + 1024);
3404 "file \"%s\" has changed, reloading.\n"),
3406 warning_dialog (s->toplevel_widget, b, False, 100);
3410 initialize_sort_map (s);
3412 list_elt = selected_list_element (s);
3413 list = name_to_widget (s, "list");
3414 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3415 populate_hack_list (s);
3416 force_list_select_item (s, list, list_elt, True);
3417 populate_prefs_page (s);
3418 populate_demo_window (s, list_elt);
3419 ensure_selected_item_visible (list);
3424 reentrant_lock = False;
3430 /* Making the preview window have the right X visual (so that GL works.)
3433 static Visual *get_best_gl_visual (state *);
3436 x_visual_to_gdk_visual (Visual *xv)
3438 GList *gvs = gdk_list_visuals();
3439 if (!xv) return gdk_visual_get_system();
3440 for (; gvs; gvs = gvs->next)
3442 GdkVisual *gv = (GdkVisual *) gvs->data;
3443 if (xv == GDK_VISUAL_XVISUAL (gv))
3446 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3447 blurb(), (unsigned long) xv->visualid);
3452 clear_preview_window (state *s)
3457 if (!s->toplevel_widget) return; /* very early */
3458 p = name_to_widget (s, "preview");
3461 if (!window) return;
3463 /* Flush the widget background down into the window, in case a subproc
3465 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3466 gdk_window_clear (window);
3469 int list_elt = selected_list_element (s);
3470 int hack_number = (list_elt >= 0
3471 ? s->list_elt_to_hack_number[list_elt]
3473 Bool available_p = (hack_number >= 0
3474 ? s->hacks_available_p [hack_number]
3477 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3478 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3479 (s->running_preview_error_p
3480 ? (available_p ? 1 : 2)
3482 #else /* !HAVE_GTK2 */
3483 if (s->running_preview_error_p)
3485 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3486 const char * const lines2[] = { N_("Not"), N_("Installed") };
3487 int nlines = countof(lines1);
3488 int lh = p->style->font->ascent + p->style->font->descent;
3492 const char * const *lines = (available_p ? lines1 : lines2);
3494 gdk_window_get_size (window, &w, &h);
3495 y = (h - (lh * nlines)) / 2;
3496 y += p->style->font->ascent;
3497 for (i = 0; i < nlines; i++)
3499 int sw = gdk_string_width (p->style->font, _(lines[i]));
3500 int x = (w - sw) / 2;
3501 gdk_draw_string (window, p->style->font,
3502 p->style->fg_gc[GTK_STATE_NORMAL],
3507 #endif /* !HAVE_GTK2 */
3515 reset_preview_window (state *s)
3517 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3518 when you kill one and re-start another on the same window. So maybe
3519 it's best to just always destroy and recreate the preview window
3520 when changing hacks, instead of always trying to reuse the same one?
3522 GtkWidget *pr = name_to_widget (s, "preview");
3523 if (GTK_WIDGET_REALIZED (pr))
3525 Window oid = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3527 gtk_widget_hide (pr);
3528 gtk_widget_unrealize (pr);
3529 gtk_widget_realize (pr);
3530 gtk_widget_show (pr);
3531 id = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3533 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3541 fix_preview_visual (state *s)
3543 GtkWidget *widget = name_to_widget (s, "preview");
3544 Visual *xvisual = get_best_gl_visual (s);
3545 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3546 GdkVisual *dvisual = gdk_visual_get_system();
3547 GdkColormap *cmap = (visual == dvisual
3548 ? gdk_colormap_get_system ()
3549 : gdk_colormap_new (visual, False));
3552 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3553 (visual == dvisual ? "default" : "non-default"),
3554 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3556 if (!GTK_WIDGET_REALIZED (widget) ||
3557 gtk_widget_get_visual (widget) != visual)
3559 gtk_widget_unrealize (widget);
3560 gtk_widget_set_visual (widget, visual);
3561 gtk_widget_set_colormap (widget, cmap);
3562 gtk_widget_realize (widget);
3565 /* Set the Widget colors to be white-on-black. */
3567 GdkWindow *window = widget->window;
3568 GtkStyle *style = gtk_style_copy (widget->style);
3569 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3570 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3571 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3572 GdkGC *fgc = gdk_gc_new(window);
3573 GdkGC *bgc = gdk_gc_new(window);
3574 if (!gdk_color_white (cmap, fg)) abort();
3575 if (!gdk_color_black (cmap, bg)) abort();
3576 gdk_gc_set_foreground (fgc, fg);
3577 gdk_gc_set_background (fgc, bg);
3578 gdk_gc_set_foreground (bgc, bg);
3579 gdk_gc_set_background (bgc, fg);
3580 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3581 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3582 gtk_widget_set_style (widget, style);
3584 /* For debugging purposes, put a title on the window (so that
3585 it can be easily found in the output of "xwininfo -tree".)
3587 gdk_window_set_title (window, "Preview");
3590 gtk_widget_show (widget);
3598 subproc_pretty_name (state *s)
3600 if (s->running_preview_cmd)
3602 char *ps = strdup (s->running_preview_cmd);
3603 char *ss = strchr (ps, ' ');
3605 ss = strrchr (ps, '/');
3616 return strdup ("???");
3621 reap_zombies (state *s)
3623 int wait_status = 0;
3625 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3629 if (pid == s->running_preview_pid)
3631 char *ss = subproc_pretty_name (s);
3632 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3633 (unsigned long) pid, ss);
3637 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3638 (unsigned long) pid);
3644 /* Mostly lifted from driver/subprocs.c */
3646 get_best_gl_visual (state *s)
3648 Display *dpy = GDK_DISPLAY();
3657 av[ac++] = "xscreensaver-gl-helper";
3662 perror ("error creating pipe:");
3669 switch ((int) (forked = fork ()))
3673 sprintf (buf, "%s: couldn't fork", blurb());
3681 close (in); /* don't need this one */
3682 close (ConnectionNumber (dpy)); /* close display fd */
3684 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3686 perror ("could not dup() a new stdout:");
3690 execvp (av[0], av); /* shouldn't return. */
3692 if (errno != ENOENT)
3694 /* Ignore "no such file or directory" errors, unless verbose.
3695 Issue all other exec errors, though. */
3696 sprintf (buf, "%s: running %s", blurb(), av[0]);
3700 /* Note that one must use _exit() instead of exit() in procs forked
3701 off of Gtk programs -- Gtk installs an atexit handler that has a
3702 copy of the X connection (which we've already closed, for safety.)
3703 If one uses exit() instead of _exit(), then one sometimes gets a
3704 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3706 _exit (1); /* exits fork */
3712 int wait_status = 0;
3714 FILE *f = fdopen (in, "r");
3718 close (out); /* don't need this one */
3721 fgets (buf, sizeof(buf)-1, f);
3724 /* Wait for the child to die. */
3725 waitpid (-1, &wait_status, 0);
3727 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3733 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3739 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3741 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3742 blurb(), av[0], result);
3754 kill_preview_subproc (state *s, Bool reset_p)
3756 s->running_preview_error_p = False;
3759 clear_preview_window (s);
3761 if (s->subproc_check_timer_id)
3763 gtk_timeout_remove (s->subproc_check_timer_id);
3764 s->subproc_check_timer_id = 0;
3765 s->subproc_check_countdown = 0;
3768 if (s->running_preview_pid)
3770 int status = kill (s->running_preview_pid, SIGTERM);
3771 char *ss = subproc_pretty_name (s);
3778 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3779 blurb(), (unsigned long) s->running_preview_pid, ss);
3784 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3785 blurb(), (unsigned long) s->running_preview_pid, ss);
3789 else if (s->debug_p)
3790 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3791 (unsigned long) s->running_preview_pid, ss);
3794 s->running_preview_pid = 0;
3795 if (s->running_preview_cmd) free (s->running_preview_cmd);
3796 s->running_preview_cmd = 0;
3803 reset_preview_window (s);
3804 clear_preview_window (s);
3809 /* Immediately and unconditionally launches the given process,
3810 after appending the -window-id option; sets running_preview_pid.
3813 launch_preview_subproc (state *s)
3815 saver_preferences *p = &s->prefs;
3819 const char *cmd = s->desired_preview_cmd;
3821 GtkWidget *pr = name_to_widget (s, "preview");
3824 reset_preview_window (s);
3826 window = pr->window;
3828 s->running_preview_error_p = False;
3830 if (s->preview_suppressed_p)
3832 kill_preview_subproc (s, False);
3836 new_cmd = malloc (strlen (cmd) + 40);
3838 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3841 /* No window id? No command to run. */
3847 strcpy (new_cmd, cmd);
3848 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3852 kill_preview_subproc (s, False);
3855 s->running_preview_error_p = True;
3856 clear_preview_window (s);
3860 switch ((int) (forked = fork ()))
3865 sprintf (buf, "%s: couldn't fork", blurb());
3867 s->running_preview_error_p = True;
3873 close (ConnectionNumber (GDK_DISPLAY()));
3875 hack_subproc_environment (id, s->debug_p);
3877 usleep (250000); /* pause for 1/4th second before launching, to give
3878 the previous program time to die and flush its X
3879 buffer, so we don't get leftover turds on the
3882 exec_command (p->shell, new_cmd, p->nice_inferior);
3883 /* Don't bother printing an error message when we are unable to
3884 exec subprocesses; we handle that by polling the pid later.
3886 Note that one must use _exit() instead of exit() in procs forked
3887 off of Gtk programs -- Gtk installs an atexit handler that has a
3888 copy of the X connection (which we've already closed, for safety.)
3889 If one uses exit() instead of _exit(), then one sometimes gets a
3890 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3892 _exit (1); /* exits child fork */
3897 if (s->running_preview_cmd) free (s->running_preview_cmd);
3898 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3899 s->running_preview_pid = forked;
3903 char *ss = subproc_pretty_name (s);
3904 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3905 (unsigned long) forked, ss);
3912 schedule_preview_check (s);
3915 if (new_cmd) free (new_cmd);
3920 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3923 hack_environment (state *s)
3925 static const char *def_path =
3926 # ifdef DEFAULT_PATH_PREFIX
3927 DEFAULT_PATH_PREFIX;
3932 Display *dpy = GDK_DISPLAY();
3933 const char *odpy = DisplayString (dpy);
3934 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3935 strcpy (ndpy, "DISPLAY=");
3936 strcat (ndpy, odpy);
3941 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
3943 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
3944 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
3945 So we must leak it (and/or the previous setting). Yay.
3948 if (def_path && *def_path)
3950 const char *opath = getenv("PATH");
3951 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
3952 strcpy (npath, "PATH=");
3953 strcat (npath, def_path);
3954 strcat (npath, ":");
3955 strcat (npath, opath);
3959 /* do not free(npath) -- see above */
3962 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
3968 hack_subproc_environment (Window preview_window_id, Bool debug_p)
3970 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
3971 necessary yet, but it will make programs work if we had invoked
3972 them with "-root" and not with "-window-id" -- which, of course,
3975 char *nssw = (char *) malloc (40);
3976 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
3978 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
3979 any more, right? It's not Posix, but everyone seems to have it. */
3984 fprintf (stderr, "%s: %s\n", blurb(), nssw);
3986 /* do not free(nssw) -- see above */
3990 /* Called from a timer:
3991 Launches the currently-chosen subprocess, if it's not already running.
3992 If there's a different process running, kills it.
3995 update_subproc_timer (gpointer data)
3997 state *s = (state *) data;
3998 if (! s->desired_preview_cmd)
3999 kill_preview_subproc (s, True);
4000 else if (!s->running_preview_cmd ||
4001 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4002 launch_preview_subproc (s);
4004 s->subproc_timer_id = 0;
4005 return FALSE; /* do not re-execute timer */
4009 /* Call this when you think you might want a preview process running.
4010 It will set a timer that will actually launch that program a second
4011 from now, if you haven't changed your mind (to avoid double-click
4012 spazzing, etc.) `cmd' may be null meaning "no process".
4015 schedule_preview (state *s, const char *cmd)
4017 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4022 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4024 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4027 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4028 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4030 if (s->subproc_timer_id)
4031 gtk_timeout_remove (s->subproc_timer_id);
4032 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4036 /* Called from a timer:
4037 Checks to see if the subproc that should be running, actually is.
4040 check_subproc_timer (gpointer data)
4042 state *s = (state *) data;
4043 Bool again_p = True;
4045 if (s->running_preview_error_p || /* already dead */
4046 s->running_preview_pid <= 0)
4054 status = kill (s->running_preview_pid, 0);
4055 if (status < 0 && errno == ESRCH)
4056 s->running_preview_error_p = True;
4060 char *ss = subproc_pretty_name (s);
4061 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4062 (unsigned long) s->running_preview_pid, ss,
4063 (s->running_preview_error_p ? "dead" : "alive"));
4067 if (s->running_preview_error_p)
4069 clear_preview_window (s);
4074 /* Otherwise, it's currently alive. We might be checking again, or we
4075 might be satisfied. */
4077 if (--s->subproc_check_countdown <= 0)
4081 return TRUE; /* re-execute timer */
4084 s->subproc_check_timer_id = 0;
4085 s->subproc_check_countdown = 0;
4086 return FALSE; /* do not re-execute timer */
4091 /* Call this just after launching a subprocess.
4092 This sets a timer that will, five times a second for two seconds,
4093 check whether the program is still running. The assumption here
4094 is that if the process didn't stay up for more than a couple of
4095 seconds, then either the program doesn't exist, or it doesn't
4096 take a -window-id argument.
4099 schedule_preview_check (state *s)
4105 fprintf (stderr, "%s: scheduling check\n", blurb());
4107 if (s->subproc_check_timer_id)
4108 gtk_timeout_remove (s->subproc_check_timer_id);
4109 s->subproc_check_timer_id =
4110 gtk_timeout_add (1000 / ticks,
4111 check_subproc_timer, (gpointer) s);
4112 s->subproc_check_countdown = ticks * seconds;
4117 screen_blanked_p (void)
4121 unsigned long nitems, bytesafter;
4122 unsigned char *dataP = 0;
4123 Display *dpy = GDK_DISPLAY();
4124 Bool blanked_p = False;
4126 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4127 XA_SCREENSAVER_STATUS,
4128 0, 3, False, XA_INTEGER,
4129 &type, &format, &nitems, &bytesafter,
4132 && type == XA_INTEGER
4136 Atom *data = (Atom *) dataP;
4137 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4140 if (dataP) XFree (dataP);
4145 /* Wake up every now and then and see if the screen is blanked.
4146 If it is, kill off the small-window demo -- no point in wasting
4147 cycles by running two screensavers at once...
4150 check_blanked_timer (gpointer data)
4152 state *s = (state *) data;
4153 Bool blanked_p = screen_blanked_p ();
4154 if (blanked_p && s->running_preview_pid)
4157 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4158 kill_preview_subproc (s, True);
4161 return True; /* re-execute timer */
4165 /* How many screens are there (including Xinerama.)
4168 screen_count (Display *dpy)
4170 int nscreens = ScreenCount(dpy);
4171 # ifdef HAVE_XINERAMA
4174 int event_number, error_number;
4175 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4176 XineramaIsActive (dpy))
4178 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4179 if (xsi) XFree (xsi);
4182 # endif /* HAVE_XINERAMA */
4188 /* Setting window manager icon
4192 init_icon (GdkWindow *window)
4194 GdkBitmap *mask = 0;
4197 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4198 (gchar **) logo_50_xpm);
4200 gdk_window_set_icon (window, 0, pixmap, mask);
4204 /* The main demo-mode command loop.
4209 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4210 XrmRepresentation *type, XrmValue *value, XPointer closure)
4213 for (i = 0; quarks[i]; i++)
4215 if (bindings[i] == XrmBindTightly)
4216 fprintf (stderr, (i == 0 ? "" : "."));
4217 else if (bindings[i] == XrmBindLoosely)
4218 fprintf (stderr, "*");
4220 fprintf (stderr, " ??? ");
4221 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4224 fprintf (stderr, ": %s\n", (char *) value->addr);
4232 the_network_is_not_the_computer (state *s)
4234 Display *dpy = GDK_DISPLAY();
4235 char *rversion = 0, *ruser = 0, *rhost = 0;
4236 char *luser, *lhost;
4238 struct passwd *p = getpwuid (getuid ());
4239 const char *d = DisplayString (dpy);
4241 # if defined(HAVE_UNAME)
4243 if (uname (&uts) < 0)
4244 lhost = "<UNKNOWN>";
4246 lhost = uts.nodename;
4248 strcpy (lhost, getenv("SYS$NODE"));
4249 # else /* !HAVE_UNAME && !VMS */
4250 strcat (lhost, "<UNKNOWN>");
4251 # endif /* !HAVE_UNAME && !VMS */
4253 if (p && p->pw_name)
4258 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4260 /* Make a buffer that's big enough for a number of copies of all the
4261 strings, plus some. */
4262 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4263 (ruser ? strlen(ruser) : 0) +
4264 (rhost ? strlen(rhost) : 0) +
4271 if (!rversion || !*rversion)
4275 "The XScreenSaver daemon doesn't seem to be running\n"
4276 "on display \"%s\". Launch it now?"),
4279 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4281 /* Warn that the two processes are running as different users.
4285 "%s is running as user \"%s\" on host \"%s\".\n"
4286 "But the xscreensaver managing display \"%s\"\n"
4287 "is running as user \"%s\" on host \"%s\".\n"
4289 "Since they are different users, they won't be reading/writing\n"
4290 "the same ~/.xscreensaver file, so %s isn't\n"
4291 "going to work right.\n"
4293 "You should either re-run %s as \"%s\", or re-run\n"
4294 "xscreensaver as \"%s\".\n"
4296 "Restart the xscreensaver daemon now?\n"),
4297 progname, luser, lhost,
4299 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4301 progname, (ruser ? ruser : "???"),
4304 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4306 /* Warn that the two processes are running on different hosts.
4310 "%s is running as user \"%s\" on host \"%s\".\n"
4311 "But the xscreensaver managing display \"%s\"\n"
4312 "is running as user \"%s\" on host \"%s\".\n"
4314 "If those two machines don't share a file system (that is,\n"
4315 "if they don't see the same ~%s/.xscreensaver file) then\n"
4316 "%s won't work right.\n"
4318 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4319 progname, luser, lhost,
4321 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4326 else if (!!strcmp (rversion, s->short_version))
4328 /* Warn that the version numbers don't match.
4332 "This is %s version %s.\n"
4333 "But the xscreensaver managing display \"%s\"\n"
4334 "is version %s. This could cause problems.\n"
4336 "Restart the xscreensaver daemon now?\n"),
4337 progname, s->short_version,
4344 warning_dialog (s->toplevel_widget, msg, True, 1);
4346 if (rversion) free (rversion);
4347 if (ruser) free (ruser);
4348 if (rhost) free (rhost);
4353 /* We use this error handler so that X errors are preceeded by the name
4354 of the program that generated them.
4357 demo_ehandler (Display *dpy, XErrorEvent *error)
4359 state *s = global_state_kludge; /* I hate C so much... */
4360 fprintf (stderr, "\nX error in %s:\n", blurb());
4361 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4362 kill_preview_subproc (s, False);
4368 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4369 of the program that generated them; and also that we can ignore one
4370 particular bogus error message that Gdk madly spews.
4373 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4374 const gchar *message, gpointer user_data)
4376 /* Ignore the message "Got event for unknown window: 0x...".
4377 Apparently some events are coming in for the xscreensaver window
4378 (presumably reply events related to the ClientMessage) and Gdk
4379 feels the need to complain about them. So, just suppress any
4380 messages that look like that one.
4382 if (strstr (message, "unknown window"))
4385 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4386 (log_domain ? log_domain : progclass),
4387 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4388 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4389 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4390 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4391 log_level == G_LOG_LEVEL_INFO ? "info" :
4392 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4394 ((!*message || message[strlen(message)-1] != '\n')
4400 __extension__ /* shut up about "string length is greater than the length
4401 ISO C89 compilers are required to support" when including
4405 static char *defaults[] = {
4406 #include "XScreenSaver_ad.h"
4411 #ifdef HAVE_CRAPPLET
4412 static struct poptOption crapplet_options[] = {
4413 {NULL, '\0', 0, NULL, 0}
4415 #endif /* HAVE_CRAPPLET */
4418 const char *usage = "[--display dpy] [--prefs]"
4419 # ifdef HAVE_CRAPPLET
4422 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4425 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4427 state *s = (state *) user_data;
4428 Boolean oi = s->initializing_p;
4430 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4432 s->initializing_p = True;
4434 eschew_gtk_lossage (label);
4436 s->initializing_p = oi;
4442 print_widget_tree (GtkWidget *w, int depth)
4445 for (i = 0; i < depth; i++)
4446 fprintf (stderr, " ");
4447 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4449 if (GTK_IS_LIST (w))
4451 for (i = 0; i < depth+1; i++)
4452 fprintf (stderr, " ");
4453 fprintf (stderr, "...list kids...\n");
4455 else if (GTK_IS_CONTAINER (w))
4457 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4460 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4468 delayed_scroll_kludge (gpointer data)
4470 state *s = (state *) data;
4471 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4472 ensure_selected_item_visible (w);
4474 /* Oh, this is just fucking lovely, too. */
4475 w = GTK_WIDGET (name_to_widget (s, "preview"));
4476 gtk_widget_hide (w);
4477 gtk_widget_show (w);
4479 return FALSE; /* do not re-execute timer */
4485 create_xscreensaver_demo (void)
4489 nb = name_to_widget (global_state_kludge, "preview_notebook");
4490 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4492 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4496 create_xscreensaver_settings_dialog (void)
4500 box = name_to_widget (global_state_kludge, "dialog_action_area");
4502 w = name_to_widget (global_state_kludge, "adv_button");
4503 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4505 w = name_to_widget (global_state_kludge, "std_button");
4506 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4508 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4511 #endif /* HAVE_GTK2 */
4514 main (int argc, char **argv)
4518 saver_preferences *p;
4522 Widget toplevel_shell;
4523 char *real_progname = argv[0];
4526 Bool crapplet_p = False;
4530 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4531 textdomain (GETTEXT_PACKAGE);
4534 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4535 # else /* !HAVE_GTK2 */
4536 if (!setlocale (LC_ALL, ""))
4537 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4538 # endif /* !HAVE_GTK2 */
4540 #endif /* ENABLE_NLS */
4542 str = strrchr (real_progname, '/');
4543 if (str) real_progname = str+1;
4546 memset (s, 0, sizeof(*s));
4547 s->initializing_p = True;
4550 global_state_kludge = s; /* I hate C so much... */
4552 progname = real_progname;
4554 s->short_version = (char *) malloc (5);
4555 memcpy (s->short_version, screensaver_id + 17, 4);
4556 s->short_version [4] = 0;
4559 /* Register our error message logger for every ``log domain'' known.
4560 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4561 for all of the domains that seem to be in use.
4564 const char * const domains[] = { 0,
4565 "Gtk", "Gdk", "GLib", "GModule",
4566 "GThread", "Gnome", "GnomeUI" };
4567 for (i = 0; i < countof(domains); i++)
4568 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4571 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4574 const char *dir = DEFAULT_ICONDIR;
4575 if (*dir) add_pixmap_directory (dir);
4577 # endif /* !HAVE_GTK2 */
4578 #endif /* DEFAULT_ICONDIR */
4580 /* This is gross, but Gtk understands --display and not -display...
4582 for (i = 1; i < argc; i++)
4583 if (argv[i][0] && argv[i][1] &&
4584 !strncmp(argv[i], "-display", strlen(argv[i])))
4585 argv[i] = "--display";
4588 /* We need to parse this arg really early... Sigh. */
4589 for (i = 1; i < argc; i++)
4592 (!strcmp(argv[i], "--crapplet") ||
4593 !strcmp(argv[i], "--capplet")))
4595 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4598 for (j = i; j < argc; j++) /* remove it from the list */
4599 argv[j] = argv[j+1];
4601 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4602 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4604 fprintf (stderr, "%s: %s\n", real_progname, usage);
4606 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4609 (!strcmp(argv[i], "--debug") ||
4610 !strcmp(argv[i], "-debug") ||
4611 !strcmp(argv[i], "-d")))
4615 for (j = i; j < argc; j++) /* remove it from the list */
4616 argv[j] = argv[j+1];
4623 (!strcmp(argv[i], "-geometry") ||
4624 !strcmp(argv[i], "-geom") ||
4625 !strcmp(argv[i], "-geo") ||
4626 !strcmp(argv[i], "-g")))
4630 for (j = i; j < argc; j++) /* remove them from the list */
4631 argv[j] = argv[j+2];
4638 (!strcmp(argv[i], "--configdir")))
4642 hack_configuration_path = argv[i+1];
4643 for (j = i; j < argc; j++) /* remove them from the list */
4644 argv[j] = argv[j+2];
4648 if (0 != stat (hack_configuration_path, &st))
4651 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4655 else if (!S_ISDIR (st.st_mode))
4657 fprintf (stderr, "%s: not a directory: %s\n",
4658 blurb(), hack_configuration_path);
4666 fprintf (stderr, "%s: using config directory \"%s\"\n",
4667 progname, hack_configuration_path);
4670 /* Let Gtk open the X connection, then initialize Xt to use that
4671 same connection. Doctor Frankenstein would be proud.
4673 # ifdef HAVE_CRAPPLET
4676 GnomeClient *client;
4677 GnomeClientFlags flags = 0;
4679 int init_results = gnome_capplet_init ("screensaver-properties",
4681 argc, argv, NULL, 0, NULL);
4683 0 upon successful initialization;
4684 1 if --init-session-settings was passed on the cmdline;
4685 2 if --ignore was passed on the cmdline;
4688 So the 1 signifies just to init the settings, and quit, basically.
4689 (Meaning launch the xscreensaver daemon.)
4692 if (init_results < 0)
4695 g_error ("An initialization error occurred while "
4696 "starting xscreensaver-capplet.\n");
4698 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4699 real_progname, init_results);
4704 client = gnome_master_client ();
4707 flags = gnome_client_get_flags (client);
4709 if (flags & GNOME_CLIENT_IS_CONNECTED)
4712 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4713 gnome_client_get_id (client));
4716 char *session_args[20];
4718 session_args[i++] = real_progname;
4719 session_args[i++] = "--capplet";
4720 session_args[i++] = "--init-session-settings";
4721 session_args[i] = 0;
4722 gnome_client_set_priority (client, 20);
4723 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4724 gnome_client_set_restart_command (client, i, session_args);
4728 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4731 gnome_client_flush (client);
4734 if (init_results == 1)
4736 system ("xscreensaver -nosplash &");
4742 # endif /* HAVE_CRAPPLET */
4744 gtk_init (&argc, &argv);
4748 /* We must read exactly the same resources as xscreensaver.
4749 That means we must have both the same progclass *and* progname,
4750 at least as far as the resource database is concerned. So,
4751 put "xscreensaver" in argv[0] while initializing Xt.
4753 argv[0] = "xscreensaver";
4757 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4759 XtToolkitInitialize ();
4760 app = XtCreateApplicationContext ();
4761 dpy = GDK_DISPLAY();
4762 XtAppSetFallbackResources (app, defaults);
4763 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4764 toplevel_shell = XtAppCreateShell (progname, progclass,
4765 applicationShellWidgetClass,
4768 dpy = XtDisplay (toplevel_shell);
4769 db = XtDatabase (dpy);
4770 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4771 XSetErrorHandler (demo_ehandler);
4773 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4774 signal (SIGPIPE, SIG_IGN);
4776 /* After doing Xt-style command-line processing, complain about any
4777 unrecognized command-line arguments.
4779 for (i = 1; i < argc; i++)
4781 char *str = argv[i];
4782 if (str[0] == '-' && str[1] == '-')
4784 if (!strcmp (str, "-prefs"))
4786 else if (crapplet_p)
4787 /* There are lots of random args that we don't care about when we're
4788 started as a crapplet, so just ignore unknown args in that case. */
4792 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4794 fprintf (stderr, "%s: %s\n", real_progname, usage);
4799 /* Load the init file, which may end up consulting the X resource database
4800 and the site-wide app-defaults file. Note that at this point, it's
4801 important that `progname' be "xscreensaver", rather than whatever
4805 s->nscreens = screen_count (dpy);
4807 hack_environment (s); /* must be before initialize_sort_map() */
4810 initialize_sort_map (s);
4812 /* Now that Xt has been initialized, and the resources have been read,
4813 we can set our `progname' variable to something more in line with
4816 progname = real_progname;
4820 /* Print out all the resources we read. */
4822 XrmName name = { 0 };
4823 XrmClass class = { 0 };
4825 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4831 /* Intern the atoms that xscreensaver_command() needs.
4833 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4834 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4835 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4836 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
4837 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
4838 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
4839 XA_SELECT = XInternAtom (dpy, "SELECT", False);
4840 XA_DEMO = XInternAtom (dpy, "DEMO", False);
4841 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
4842 XA_BLANK = XInternAtom (dpy, "BLANK", False);
4843 XA_LOCK = XInternAtom (dpy, "LOCK", False);
4844 XA_EXIT = XInternAtom (dpy, "EXIT", False);
4845 XA_RESTART = XInternAtom (dpy, "RESTART", False);
4848 /* Create the window and all its widgets.
4850 s->base_widget = create_xscreensaver_demo ();
4851 s->popup_widget = create_xscreensaver_settings_dialog ();
4852 s->toplevel_widget = s->base_widget;
4855 /* Set the main window's title. */
4857 char *base_title = _("Screensaver Preferences");
4858 char *v = (char *) strdup(strchr(screensaver_id, ' '));
4859 char *s1, *s2, *s3, *s4;
4860 s1 = (char *) strchr(v, ' '); s1++;
4861 s2 = (char *) strchr(s1, ' ');
4862 s3 = (char *) strchr(v, '('); s3++;
4863 s4 = (char *) strchr(s3, ')');
4867 window_title = (char *) malloc (strlen (base_title) +
4868 strlen (progclass) +
4869 strlen (s1) + strlen (s3) +
4871 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
4872 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
4873 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
4877 /* Adjust the (invisible) notebooks on the popup dialog... */
4879 GtkNotebook *notebook =
4880 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
4881 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
4885 gtk_widget_hide (std);
4886 # else /* !HAVE_XML */
4887 /* Make the advanced page be the only one available. */
4888 gtk_widget_set_sensitive (std, False);
4889 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
4890 gtk_widget_hide (std);
4892 # endif /* !HAVE_XML */
4894 gtk_notebook_set_page (notebook, page);
4895 gtk_notebook_set_show_tabs (notebook, False);
4898 /* Various other widget initializations...
4900 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
4901 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
4903 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
4904 GTK_SIGNAL_FUNC (wm_popup_close_cb),
4907 populate_hack_list (s);
4908 populate_prefs_page (s);
4909 sensitize_demo_widgets (s, False);
4910 fix_text_entry_sizes (s);
4911 scroll_to_current_hack (s);
4913 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
4914 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
4918 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
4919 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
4921 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
4922 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
4924 #endif /* !HAVE_GTK2 */
4926 /* Hook up callbacks to the items on the mode menu. */
4928 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
4929 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
4930 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
4932 for (i = 0; kids; kids = kids->next, i++)
4934 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
4935 GTK_SIGNAL_FUNC (mode_menu_item_cb),
4938 /* The "random-same" mode menu item does not appear unless
4939 there are multple screens.
4941 if (s->nscreens <= 1 &&
4942 mode_menu_order[i] == RANDOM_HACKS_SAME)
4943 gtk_widget_hide (GTK_WIDGET (kids->data));
4946 if (s->nscreens <= 1) /* recompute option-menu size */
4948 gtk_widget_unrealize (GTK_WIDGET (menu));
4949 gtk_widget_realize (GTK_WIDGET (menu));
4954 /* Handle the -prefs command-line argument. */
4957 GtkNotebook *notebook =
4958 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
4959 gtk_notebook_set_page (notebook, 1);
4962 # ifdef HAVE_CRAPPLET
4966 GtkWidget *outer_vbox;
4968 gtk_widget_hide (s->toplevel_widget);
4970 capplet = capplet_widget_new ();
4972 /* Make there be a "Close" button instead of "OK" and "Cancel" */
4973 # ifdef HAVE_CRAPPLET_IMMEDIATE
4974 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
4975 # endif /* HAVE_CRAPPLET_IMMEDIATE */
4976 /* In crapplet-mode, take off the menubar. */
4977 gtk_widget_hide (name_to_widget (s, "menubar"));
4979 /* Reparent our top-level container to be a child of the capplet
4982 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
4983 gtk_widget_ref (outer_vbox);
4984 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
4986 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
4987 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
4989 /* Find the window above us, and set the title and close handler. */
4991 GtkWidget *window = capplet;
4992 while (window && !GTK_IS_WINDOW (window))
4993 window = window->parent;
4996 gtk_window_set_title (GTK_WINDOW (window), window_title);
4997 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
4998 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5003 s->toplevel_widget = capplet;
5005 # endif /* HAVE_CRAPPLET */
5008 /* The Gnome folks hate the menubar. I think it's important to have access
5009 to the commands on the File menu (Restart Daemon, etc.) and to the
5010 About and Documentation commands on the Help menu.
5014 gtk_widget_hide (name_to_widget (s, "menubar"));
5018 free (window_title);
5022 /* After picking the default size, allow -geometry to override it. */
5024 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5027 gtk_widget_show (s->toplevel_widget);
5028 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
5029 fix_preview_visual (s);
5031 /* Realize page zero, so that we can diddle the scrollbar when the
5032 user tabs back to it -- otherwise, the current hack isn't scrolled
5033 to the first time they tab back there, when started with "-prefs".
5034 (Though it is if they then tab away, and back again.)
5036 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5037 #### understands this crap, explain to me how to make this work.
5039 gtk_widget_realize (name_to_widget (s, "demos_table"));
5042 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5045 /* Issue any warnings about the running xscreensaver daemon. */
5046 the_network_is_not_the_computer (s);
5049 /* Run the Gtk event loop, and not the Xt event loop. This means that
5050 if there were Xt timers or fds registered, they would never get serviced,
5051 and if there were any Xt widgets, they would never have events delivered.
5052 Fortunately, we're using Gtk for all of the UI, and only initialized
5053 Xt so that we could process the command line and use the X resource
5056 s->initializing_p = False;
5058 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5059 after we start up. Otherwise, it always appears scrolled to the top
5060 when in crapplet-mode. */
5061 gtk_timeout_add (500, delayed_scroll_kludge, s);
5065 /* Load every configurator in turn, to scan them for errors all at once. */
5068 for (i = 0; i < p->screenhacks_count; i++)
5070 screenhack *hack = p->screenhacks[i];
5071 conf_data *d = load_configurator (hack->command, False);
5072 if (d) free_conf_data (d);
5078 # ifdef HAVE_CRAPPLET
5080 capplet_gtk_main ();
5082 # endif /* HAVE_CRAPPLET */
5085 kill_preview_subproc (s, False);
5089 #endif /* HAVE_GTK -- whole file */