1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2008 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);
147 extern int on_path_p (const char *program);
149 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
152 #define countof(x) (sizeof((x))/sizeof((*x)))
155 /* You might think that to read an array of 32-bit quantities out of a
156 server-side property, you would pass an array of 32-bit data quantities
157 into XGetWindowProperty(). You would be wrong. You have to use an array
158 of longs, even if long is 64 bits (using 32 of each 64.)
163 char *progclass = "XScreenSaver";
166 /* The order of the items in the mode menu. */
167 static int mode_menu_order[] = {
168 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
173 char *short_version; /* version number of this xscreensaver build */
175 GtkWidget *toplevel_widget; /* the main window */
176 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
177 GtkWidget *popup_widget; /* the "Settings" dialog */
178 conf_data *cdata; /* private data for per-hack configuration */
181 GladeXML *glade_ui; /* Glade UI file */
182 #endif /* HAVE_GTK2 */
184 Bool debug_p; /* whether to print diagnostics */
185 Bool initializing_p; /* flag for breaking recursion loops */
186 Bool saving_p; /* flag for breaking recursion loops */
188 char *desired_preview_cmd; /* subprocess we intend to run */
189 char *running_preview_cmd; /* subprocess we are currently running */
190 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
191 Bool running_preview_error_p; /* whether the pid died abnormally */
193 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
194 int subproc_timer_id; /* timer to delay subproc launch */
195 int subproc_check_timer_id; /* timer to check whether it started up */
196 int subproc_check_countdown; /* how many more checks left */
198 int *list_elt_to_hack_number; /* table for sorting the hack list */
199 int *hack_number_to_list_elt; /* the inverse table */
200 Bool *hacks_available_p; /* whether hacks are on $PATH */
201 int total_available; /* how many are on $PATH */
202 int list_count; /* how many items are in the list: this may be
203 less than p->screenhacks_count, if some are
206 int _selected_list_element; /* don't use this: call
207 selected_list_element() instead */
209 int nscreens; /* How many X or Xinerama screens there are */
211 saver_preferences prefs;
216 /* Total fucking evilness due to the fact that it's rocket science to get
217 a closure object of our own down into the various widget callbacks. */
218 static state *global_state_kludge;
221 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
222 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
223 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
226 static void populate_demo_window (state *, int list_elt);
227 static void populate_prefs_page (state *);
228 static void populate_popup_window (state *);
230 static Bool flush_dialog_changes_and_save (state *);
231 static Bool flush_popup_changes_and_save (state *);
233 static int maybe_reload_init_file (state *);
234 static void await_xscreensaver (state *);
235 static Bool xscreensaver_running_p (state *);
236 static void sensitize_menu_items (state *s, Bool force_p);
237 static void force_dialog_repaint (state *s);
239 static void schedule_preview (state *, const char *cmd);
240 static void kill_preview_subproc (state *, Bool reset_p);
241 static void schedule_preview_check (state *);
244 /* Prototypes of functions used by the Glade-generated code,
247 void exit_menu_cb (GtkMenuItem *, gpointer user_data);
248 void about_menu_cb (GtkMenuItem *, gpointer user_data);
249 void doc_menu_cb (GtkMenuItem *, gpointer user_data);
250 void file_menu_cb (GtkMenuItem *, gpointer user_data);
251 void activate_menu_cb (GtkMenuItem *, gpointer user_data);
252 void lock_menu_cb (GtkMenuItem *, gpointer user_data);
253 void kill_menu_cb (GtkMenuItem *, gpointer user_data);
254 void restart_menu_cb (GtkWidget *, gpointer user_data);
255 void run_this_cb (GtkButton *, gpointer user_data);
256 void manual_cb (GtkButton *, gpointer user_data);
257 void run_next_cb (GtkButton *, gpointer user_data);
258 void run_prev_cb (GtkButton *, gpointer user_data);
259 void pref_changed_cb (GtkWidget *, gpointer user_data);
260 gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
261 void mode_menu_item_cb (GtkWidget *, gpointer user_data);
262 void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
263 gint page_num, gpointer user_data);
264 void browse_image_dir_cb (GtkButton *, gpointer user_data);
265 void browse_text_file_cb (GtkButton *, gpointer user_data);
266 void browse_text_program_cb (GtkButton *, gpointer user_data);
267 void settings_cb (GtkButton *, gpointer user_data);
268 void settings_adv_cb (GtkButton *, gpointer user_data);
269 void settings_std_cb (GtkButton *, gpointer user_data);
270 void settings_reset_cb (GtkButton *, gpointer user_data);
271 void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
272 gint page_num, gpointer user_data);
273 void settings_cancel_cb (GtkButton *, gpointer user_data);
274 void settings_ok_cb (GtkButton *, gpointer user_data);
276 static void kill_gnome_screensaver (void);
277 static void kill_kde_screensaver (void);
280 /* Some random utility functions
283 const char *blurb (void);
288 time_t now = time ((time_t *) 0);
289 char *ct = (char *) ctime (&now);
290 static char buf[255];
291 int n = strlen(progname);
293 strncpy(buf, progname, n);
296 strncpy(buf+n, ct+11, 8);
297 strcpy(buf+n+9, ": ");
303 name_to_widget (state *s, const char *name)
313 /* First try to load the Glade file from the current directory;
314 if there isn't one there, check the installed directory.
316 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
317 const char * const files[] = { GLADE_FILE_NAME,
318 GLADE_DIR "/" GLADE_FILE_NAME };
320 for (i = 0; i < countof (files); i++)
323 if (!stat (files[i], &st))
325 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
332 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
333 "\tfrom " GLADE_DIR "/ or current directory.\n",
337 # undef GLADE_FILE_NAME
339 glade_xml_signal_autoconnect (s->glade_ui);
342 w = glade_xml_get_widget (s->glade_ui, name);
344 #else /* !HAVE_GTK2 */
346 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
349 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
351 #endif /* HAVE_GTK2 */
354 fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
360 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
361 Takes a scroller, viewport, or list as an argument.
364 ensure_selected_item_visible (GtkWidget *widget)
368 GtkTreeSelection *selection;
372 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
373 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
374 path = gtk_tree_path_new_first ();
376 path = gtk_tree_model_get_path (model, &iter);
378 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
380 gtk_tree_path_free (path);
382 #else /* !HAVE_GTK2 */
384 GtkScrolledWindow *scroller = 0;
386 GtkList *list_widget = 0;
390 GtkWidget *selected = 0;
393 gint parent_h, child_y, child_h, children_h, ignore;
394 double ratio_t, ratio_b;
396 if (GTK_IS_SCROLLED_WINDOW (widget))
398 scroller = GTK_SCROLLED_WINDOW (widget);
399 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
400 list_widget = GTK_LIST (GTK_BIN(vp)->child);
402 else if (GTK_IS_VIEWPORT (widget))
404 vp = GTK_VIEWPORT (widget);
405 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
406 list_widget = GTK_LIST (GTK_BIN(vp)->child);
408 else if (GTK_IS_LIST (widget))
410 list_widget = GTK_LIST (widget);
411 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
412 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
417 slist = list_widget->selection;
418 selected = (slist ? GTK_WIDGET (slist->data) : 0);
422 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
424 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
425 kids; kids = kids->next)
428 adj = gtk_scrolled_window_get_vadjustment (scroller);
430 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
431 &ignore, &ignore, &ignore, &parent_h, &ignore);
432 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
433 &ignore, &child_y, &ignore, &child_h, &ignore);
434 children_h = nkids * child_h;
436 ratio_t = ((double) child_y) / ((double) children_h);
437 ratio_b = ((double) child_y + child_h) / ((double) children_h);
439 if (adj->upper == 0.0) /* no items in list */
442 if (ratio_t < (adj->value / adj->upper) ||
443 ratio_b > ((adj->value + adj->page_size) / adj->upper))
446 int slop = parent_h * 0.75; /* how much to overshoot by */
448 if (ratio_t < (adj->value / adj->upper))
450 double ratio_w = ((double) parent_h) / ((double) children_h);
451 double ratio_l = (ratio_b - ratio_t);
452 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
455 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
457 target = ratio_t * adj->upper;
461 if (target > adj->upper - adj->page_size)
462 target = adj->upper - adj->page_size;
466 gtk_adjustment_set_value (adj, target);
468 #endif /* !HAVE_GTK2 */
472 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
474 GtkWidget *shell = GTK_WIDGET (user_data);
475 while (shell->parent)
476 shell = shell->parent;
477 gtk_widget_destroy (GTK_WIDGET (shell));
481 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
483 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
485 restart_menu_cb (widget, user_data);
486 warning_dialog_dismiss_cb (widget, user_data);
489 static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data)
491 kill_gnome_screensaver ();
492 warning_dialog_dismiss_cb (widget, user_data);
495 static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data)
497 kill_kde_screensaver ();
498 warning_dialog_dismiss_cb (widget, user_data);
501 typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
504 warning_dialog (GtkWidget *parent, const char *message,
505 dialog_button button_type, int center)
507 char *msg = strdup (message);
510 GtkWidget *dialog = gtk_dialog_new ();
511 GtkWidget *label = 0;
513 GtkWidget *cancel = 0;
516 while (parent && !parent->window)
517 parent = parent->parent;
520 !GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
522 fprintf (stderr, "%s: too early for dialog?\n", progname);
530 char *s = strchr (head, '\n');
533 sprintf (name, "label%d", i++);
536 label = gtk_label_new (head);
538 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
539 #endif /* HAVE_GTK2 */
544 GTK_WIDGET (label)->style =
545 gtk_style_copy (GTK_WIDGET (label)->style);
546 GTK_WIDGET (label)->style->font =
547 gdk_font_load (get_string_resource("warning_dialog.headingFont",
549 gtk_widget_set_style (GTK_WIDGET (label),
550 GTK_WIDGET (label)->style);
552 #endif /* !HAVE_GTK2 */
554 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
555 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
556 label, TRUE, TRUE, 0);
557 gtk_widget_show (label);
568 label = gtk_label_new ("");
569 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
570 label, TRUE, TRUE, 0);
571 gtk_widget_show (label);
573 label = gtk_hbutton_box_new ();
574 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
575 label, TRUE, TRUE, 0);
578 if (button_type != D_NONE)
580 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
581 gtk_container_add (GTK_CONTAINER (label), cancel);
584 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
585 gtk_container_add (GTK_CONTAINER (label), ok);
587 #else /* !HAVE_GTK2 */
589 ok = gtk_button_new_with_label ("OK");
590 gtk_container_add (GTK_CONTAINER (label), ok);
592 if (button_type != D_NONE)
594 cancel = gtk_button_new_with_label ("Cancel");
595 gtk_container_add (GTK_CONTAINER (label), cancel);
598 #endif /* !HAVE_GTK2 */
600 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
601 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
602 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
603 GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
604 gtk_widget_show (ok);
605 gtk_widget_grab_focus (ok);
609 GTK_WIDGET_SET_FLAGS (cancel, GTK_CAN_DEFAULT);
610 gtk_widget_show (cancel);
612 gtk_widget_show (label);
613 gtk_widget_show (dialog);
615 if (button_type != D_NONE)
618 switch (button_type) {
619 case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break;
620 case D_GNOME: fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb); break;
621 case D_KDE: fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb); break;
622 default: abort(); break;
624 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn,
626 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
627 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
632 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
633 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
637 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
638 GTK_WIDGET (parent)->window);
641 gtk_window_present (GTK_WINDOW (dialog));
642 #else /* !HAVE_GTK2 */
643 gdk_window_show (GTK_WIDGET (dialog)->window);
644 gdk_window_raise (GTK_WIDGET (dialog)->window);
645 #endif /* !HAVE_GTK2 */
652 run_cmd (state *s, Atom command, int arg)
657 flush_dialog_changes_and_save (s);
658 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
660 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
661 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
668 sprintf (buf, "Error:\n\n%s", err);
670 strcpy (buf, "Unknown error!");
671 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
675 sensitize_menu_items (s, True);
676 force_dialog_repaint (s);
681 run_hack (state *s, int list_elt, Bool report_errors_p)
687 if (list_elt < 0) return;
688 hack_number = s->list_elt_to_hack_number[list_elt];
690 flush_dialog_changes_and_save (s);
691 schedule_preview (s, 0);
693 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
696 if (status < 0 && report_errors_p)
698 if (xscreensaver_running_p (s))
700 /* Kludge: ignore the spurious "window unexpectedly deleted"
702 if (err && strstr (err, "unexpectedly deleted"))
709 sprintf (buf, "Error:\n\n%s", err);
711 strcpy (buf, "Unknown error!");
712 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
717 /* The error is that the daemon isn't running;
720 const char *d = DisplayString (GDK_DISPLAY());
724 "The XScreenSaver daemon doesn't seem to be running\n"
725 "on display \"%s\". Launch it now?"),
727 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
733 sensitize_menu_items (s, False);
740 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
741 libglade work on Cygwin; apparently all Glade callbacks need this magic
742 extra declaration. I do not pretend to understand.
746 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
748 state *s = global_state_kludge; /* I hate C so much... */
749 flush_dialog_changes_and_save (s);
750 kill_preview_subproc (s, False);
755 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
757 state *s = (state *) data;
758 flush_dialog_changes_and_save (s);
765 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
768 char *vers = strdup (screensaver_id + 4);
771 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
773 s = strchr (vers, ',');
777 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
778 non-ASCII characters aren't allowed in localizable string keys."
779 (I don't want to just use (c) instead of © because that doesn't
780 look as good in the plain-old default Latin1 "C" locale.)
783 sprintf(copy, ("Copyright \xC2\xA9 1991-2008 %s"), s);
784 #else /* !HAVE_GTK2 */
785 sprintf(copy, ("Copyright \251 1991-2008 %s"), s);
786 #endif /* !HAVE_GTK2 */
788 sprintf (msg, "%s\n\n%s", copy, desc);
790 /* I can't make gnome_about_new() work here -- it starts dying in
791 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
792 then this might be the thing to do:
796 const gchar *auth[] = { 0 };
797 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
799 gtk_widget_show (about);
801 #else / * GTK but not GNOME * /
805 GdkColormap *colormap;
806 GdkPixmap *gdkpixmap;
809 GtkWidget *dialog = gtk_dialog_new ();
810 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
811 GtkWidget *parent = GTK_WIDGET (menuitem);
812 while (parent->parent)
813 parent = parent->parent;
815 hbox = gtk_hbox_new (FALSE, 20);
816 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
817 hbox, TRUE, TRUE, 0);
819 colormap = gtk_widget_get_colormap (parent);
821 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
822 (gchar **) logo_180_xpm);
823 icon = gtk_pixmap_new (gdkpixmap, mask);
824 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
826 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
828 vbox = gtk_vbox_new (FALSE, 0);
829 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
831 label1 = gtk_label_new (vers);
832 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
833 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
834 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
837 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
838 GTK_WIDGET (label1)->style->font =
839 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
840 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
841 #endif /* HAVE_GTK2 */
843 label2 = gtk_label_new (msg);
844 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
845 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
846 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
849 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
850 GTK_WIDGET (label2)->style->font =
851 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
852 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
853 #endif /* HAVE_GTK2 */
855 hb = gtk_hbutton_box_new ();
857 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
861 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
862 #else /* !HAVE_GTK2 */
863 ok = gtk_button_new_with_label (_("OK"));
864 #endif /* !HAVE_GTK2 */
865 gtk_container_add (GTK_CONTAINER (hb), ok);
867 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
868 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
869 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
871 gtk_widget_show (hbox);
872 gtk_widget_show (icon);
873 gtk_widget_show (vbox);
874 gtk_widget_show (label1);
875 gtk_widget_show (label2);
876 gtk_widget_show (hb);
877 gtk_widget_show (ok);
878 gtk_widget_show (dialog);
880 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
881 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
883 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
884 GTK_WIDGET (parent)->window);
885 gdk_window_show (GTK_WIDGET (dialog)->window);
886 gdk_window_raise (GTK_WIDGET (dialog)->window);
892 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
894 state *s = global_state_kludge; /* I hate C so much... */
895 saver_preferences *p = &s->prefs;
898 if (!p->help_url || !*p->help_url)
900 warning_dialog (s->toplevel_widget,
902 "No Help URL has been specified.\n"), D_NONE, 100);
906 help_command = (char *) malloc (strlen (p->load_url_command) +
907 (strlen (p->help_url) * 4) + 20);
908 strcpy (help_command, "( ");
909 sprintf (help_command + strlen(help_command),
911 p->help_url, p->help_url, p->help_url, p->help_url);
912 strcat (help_command, " ) &");
913 if (system (help_command) < 0)
914 fprintf (stderr, "%s: fork error\n", blurb());
920 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
922 state *s = global_state_kludge; /* I hate C so much... */
923 sensitize_menu_items (s, False);
928 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
930 state *s = global_state_kludge; /* I hate C so much... */
931 run_cmd (s, XA_ACTIVATE, 0);
936 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
938 state *s = global_state_kludge; /* I hate C so much... */
939 run_cmd (s, XA_LOCK, 0);
944 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
946 state *s = global_state_kludge; /* I hate C so much... */
947 run_cmd (s, XA_EXIT, 0);
952 restart_menu_cb (GtkWidget *widget, gpointer user_data)
954 state *s = global_state_kludge; /* I hate C so much... */
955 flush_dialog_changes_and_save (s);
956 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
958 if (system ("xscreensaver -nosplash &") < 0)
959 fprintf (stderr, "%s: fork error\n", blurb());
961 await_xscreensaver (s);
965 xscreensaver_running_p (state *s)
967 Display *dpy = GDK_DISPLAY();
969 server_xscreensaver_version (dpy, &rversion, 0, 0);
977 await_xscreensaver (state *s)
982 while (!ok && (--countdown > 0))
983 if (xscreensaver_running_p (s))
986 sleep (1); /* If it's not there yet, wait a second... */
988 sensitize_menu_items (s, True);
992 /* Timed out, no screensaver running. */
995 Bool root_p = (geteuid () == 0);
999 "The xscreensaver daemon did not start up properly.\n"
1005 __extension__ /* don't warn about "string length is greater than
1006 the length ISO C89 compilers are required to
1007 support" in the following expression... */
1010 _("You are running as root. This usually means that xscreensaver\n"
1011 "was unable to contact your X server because access control is\n"
1012 "turned on. Try running this command:\n"
1014 " xhost +localhost\n"
1016 "and then selecting `File / Restart Daemon'.\n"
1018 "Note that turning off access control will allow anyone logged\n"
1019 "on to this machine to access your screen, which might be\n"
1020 "considered a security problem. Please read the xscreensaver\n"
1021 "manual and FAQ for more information.\n"
1023 "You shouldn't run X as root. Instead, you should log in as a\n"
1024 "normal user, and `su' as necessary."));
1026 strcat (buf, _("Please check your $PATH and permissions."));
1028 warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1031 force_dialog_repaint (s);
1036 selected_list_element (state *s)
1038 return s->_selected_list_element;
1043 demo_write_init_file (state *s, saver_preferences *p)
1045 Display *dpy = GDK_DISPLAY();
1048 /* #### try to figure out why shit keeps getting reordered... */
1049 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1053 if (!write_init_file (dpy, p, s->short_version, False))
1056 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1061 const char *f = init_file_name();
1063 warning_dialog (s->toplevel_widget,
1064 _("Error:\n\nCouldn't determine init file name!\n"),
1068 char *b = (char *) malloc (strlen(f) + 1024);
1069 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1070 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1078 G_MODULE_EXPORT void
1079 run_this_cb (GtkButton *button, gpointer user_data)
1081 state *s = global_state_kludge; /* I hate C so much... */
1082 int list_elt = selected_list_element (s);
1083 if (list_elt < 0) return;
1084 if (!flush_dialog_changes_and_save (s))
1085 run_hack (s, list_elt, True);
1089 G_MODULE_EXPORT void
1090 manual_cb (GtkButton *button, gpointer user_data)
1092 Display *dpy = GDK_DISPLAY();
1093 state *s = global_state_kludge; /* I hate C so much... */
1094 saver_preferences *p = &s->prefs;
1095 GtkWidget *list_widget = name_to_widget (s, "list");
1096 int list_elt = selected_list_element (s);
1098 char *name, *name2, *cmd, *str;
1100 if (list_elt < 0) return;
1101 hack_number = s->list_elt_to_hack_number[list_elt];
1103 flush_dialog_changes_and_save (s);
1104 ensure_selected_item_visible (list_widget);
1106 name = strdup (p->screenhacks[hack_number]->command);
1109 while (isspace (*name2)) name2++;
1111 while (*str && !isspace (*str)) str++;
1113 str = strrchr (name2, '/');
1114 if (str) name2 = str+1;
1116 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1119 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1120 strcpy (cmd2, "( ");
1121 sprintf (cmd2 + strlen (cmd2),
1123 name2, name2, name2, name2);
1124 strcat (cmd2, " ) &");
1125 if (system (cmd2) < 0)
1126 fprintf (stderr, "%s: fork error\n", blurb());
1131 warning_dialog (GTK_WIDGET (button),
1132 _("Error:\n\nno `manualCommand' resource set."),
1141 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1143 GtkWidget *parent = name_to_widget (s, "scroller");
1144 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
1147 GtkTreeModel *model;
1148 GtkTreeSelection *selection;
1149 #endif /* HAVE_GTK2 */
1151 if (!was) gtk_widget_set_sensitive (parent, True);
1153 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1155 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1157 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1158 gtk_tree_selection_select_iter (selection, &iter);
1160 #else /* !HAVE_GTK2 */
1161 gtk_list_select_item (GTK_LIST (list), list_elt);
1162 #endif /* !HAVE_GTK2 */
1163 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1164 if (!was) gtk_widget_set_sensitive (parent, False);
1168 G_MODULE_EXPORT void
1169 run_next_cb (GtkButton *button, gpointer user_data)
1171 state *s = global_state_kludge; /* I hate C so much... */
1172 /* saver_preferences *p = &s->prefs; */
1173 Bool ops = s->preview_suppressed_p;
1175 GtkWidget *list_widget = name_to_widget (s, "list");
1176 int list_elt = selected_list_element (s);
1183 if (list_elt >= s->list_count)
1186 s->preview_suppressed_p = True;
1188 flush_dialog_changes_and_save (s);
1189 force_list_select_item (s, list_widget, list_elt, True);
1190 populate_demo_window (s, list_elt);
1191 run_hack (s, list_elt, False);
1193 s->preview_suppressed_p = ops;
1197 G_MODULE_EXPORT void
1198 run_prev_cb (GtkButton *button, gpointer user_data)
1200 state *s = global_state_kludge; /* I hate C so much... */
1201 /* saver_preferences *p = &s->prefs; */
1202 Bool ops = s->preview_suppressed_p;
1204 GtkWidget *list_widget = name_to_widget (s, "list");
1205 int list_elt = selected_list_element (s);
1208 list_elt = s->list_count - 1;
1213 list_elt = s->list_count - 1;
1215 s->preview_suppressed_p = True;
1217 flush_dialog_changes_and_save (s);
1218 force_list_select_item (s, list_widget, list_elt, True);
1219 populate_demo_window (s, list_elt);
1220 run_hack (s, list_elt, False);
1222 s->preview_suppressed_p = ops;
1226 /* Writes the given settings into prefs.
1227 Returns true if there was a change, False otherwise.
1228 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1231 flush_changes (state *s,
1234 const char *command,
1237 saver_preferences *p = &s->prefs;
1238 Bool changed = False;
1241 if (list_elt < 0 || list_elt >= s->list_count)
1244 hack_number = s->list_elt_to_hack_number[list_elt];
1245 hack = p->screenhacks[hack_number];
1247 if (enabled_p != -1 &&
1248 enabled_p != hack->enabled_p)
1250 hack->enabled_p = enabled_p;
1253 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1254 blurb(), hack->name, enabled_p);
1259 if (!hack->command || !!strcmp (command, hack->command))
1261 if (hack->command) free (hack->command);
1262 hack->command = strdup (command);
1265 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1266 blurb(), hack->name, command);
1272 const char *ov = hack->visual;
1273 if (!ov || !*ov) ov = "any";
1274 if (!*visual) visual = "any";
1275 if (!!strcasecmp (visual, ov))
1277 if (hack->visual) free (hack->visual);
1278 hack->visual = strdup (visual);
1281 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1282 blurb(), hack->name, visual);
1290 /* Helper for the text fields that contain time specifications:
1291 this parses the text, and does error checking.
1294 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1299 if (!sec_p || strchr (line, ':'))
1300 value = parse_time ((char *) line, sec_p, True);
1304 if (sscanf (line, "%d%c", &value, &c) != 1)
1310 value *= 1000; /* Time measures in microseconds */
1316 "Unparsable time format: \"%s\"\n"),
1318 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1327 directory_p (const char *path)
1330 if (!path || !*path)
1332 else if (stat (path, &st))
1334 else if (!S_ISDIR (st.st_mode))
1341 file_p (const char *path)
1344 if (!path || !*path)
1346 else if (stat (path, &st))
1348 else if (S_ISDIR (st.st_mode))
1355 normalize_directory (const char *path)
1359 if (!path || !*path) return 0;
1361 p2 = (char *) malloc (L + 2);
1363 if (p2[L-1] == '/') /* remove trailing slash */
1366 for (s = p2; s && *s; s++)
1369 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1370 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1373 while (s0 > p2 && s0[-1] != '/')
1383 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1384 strcpy (s, s+2), s--;
1385 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1389 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1390 while (s[0] == '/' && s[1] == '/')
1393 /* and strip trailing whitespace for good measure. */
1395 while (isspace(p2[L-1]))
1408 } FlushForeachClosure;
1411 flush_checkbox (GtkTreeModel *model,
1416 FlushForeachClosure *closure = data;
1419 gtk_tree_model_get (model, iter,
1420 COL_ENABLED, &checked,
1423 if (flush_changes (closure->s, closure->i,
1425 *closure->changed = True;
1429 /* don't remove row */
1433 #endif /* HAVE_GTK2 */
1435 /* Flush out any changes made in the main dialog window (where changes
1436 take place immediately: clicking on a checkbox causes the init file
1437 to be written right away.)
1440 flush_dialog_changes_and_save (state *s)
1442 saver_preferences *p = &s->prefs;
1443 saver_preferences P2, *p2 = &P2;
1445 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1446 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1447 FlushForeachClosure closure;
1448 #else /* !HAVE_GTK2 */
1449 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1450 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1452 #endif /* !HAVE_GTK2 */
1454 Bool changed = False;
1457 if (s->saving_p) return False;
1462 /* Flush any checkbox changes in the list down into the prefs struct.
1466 closure.changed = &changed;
1468 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1470 #else /* !HAVE_GTK2 */
1472 for (i = 0; kids; kids = kids->next, i++)
1474 GtkWidget *line = GTK_WIDGET (kids->data);
1475 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1476 GtkWidget *line_check =
1477 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1479 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1481 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1484 #endif /* ~HAVE_GTK2 */
1486 /* Flush the non-hack-specific settings down into the prefs struct.
1489 # define SECONDS(FIELD,NAME) \
1490 w = name_to_widget (s, (NAME)); \
1491 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1493 # define MINUTES(FIELD,NAME) \
1494 w = name_to_widget (s, (NAME)); \
1495 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1497 # define CHECKBOX(FIELD,NAME) \
1498 w = name_to_widget (s, (NAME)); \
1499 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1501 # define PATHNAME(FIELD,NAME) \
1502 w = name_to_widget (s, (NAME)); \
1503 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1505 # define TEXT(FIELD,NAME) \
1506 w = name_to_widget (s, (NAME)); \
1507 (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1509 MINUTES (&p2->timeout, "timeout_spinbutton");
1510 MINUTES (&p2->cycle, "cycle_spinbutton");
1511 CHECKBOX (p2->lock_p, "lock_button");
1512 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1514 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1515 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1516 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1517 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1519 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1520 CHECKBOX (p2->grab_video_p, "grab_video_button");
1521 CHECKBOX (p2->random_image_p, "grab_image_button");
1522 PATHNAME (p2->image_directory, "image_text");
1525 CHECKBOX (p2->verbose_p, "verbose_button");
1526 CHECKBOX (p2->capture_stderr_p, "capture_button");
1527 CHECKBOX (p2->splash_p, "splash_button");
1532 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1533 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1534 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1535 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1536 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1537 TEXT (p2->text_literal, "text_entry");
1538 PATHNAME (p2->text_file, "text_file_entry");
1539 PATHNAME (p2->text_program, "text_program_entry");
1540 PATHNAME (p2->text_program, "text_program_entry");
1541 TEXT (p2->text_url, "text_url_entry");
1544 CHECKBOX (p2->install_cmap_p, "install_button");
1545 CHECKBOX (p2->fade_p, "fade_button");
1546 CHECKBOX (p2->unfade_p, "unfade_button");
1547 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1555 /* Warn if the image directory doesn't exist.
1557 if (p2->image_directory &&
1558 *p2->image_directory &&
1559 !directory_p (p2->image_directory))
1562 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1563 p2->image_directory);
1564 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1568 /* Map the mode menu to `saver_mode' enum values. */
1570 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1571 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1572 GtkWidget *selected = gtk_menu_get_active (menu);
1573 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1574 int menu_elt = g_list_index (kids, (gpointer) selected);
1575 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1576 p2->mode = mode_menu_order[menu_elt];
1579 if (p2->mode == ONE_HACK)
1581 int list_elt = selected_list_element (s);
1582 p2->selected_hack = (list_elt >= 0
1583 ? s->list_elt_to_hack_number[list_elt]
1587 # define COPY(field, name) \
1588 if (p->field != p2->field) { \
1591 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1593 p->field = p2->field
1596 COPY(selected_hack, "selected_hack");
1598 COPY(timeout, "timeout");
1599 COPY(cycle, "cycle");
1600 COPY(lock_p, "lock_p");
1601 COPY(lock_timeout, "lock_timeout");
1603 COPY(dpms_enabled_p, "dpms_enabled_p");
1604 COPY(dpms_standby, "dpms_standby");
1605 COPY(dpms_suspend, "dpms_suspend");
1606 COPY(dpms_off, "dpms_off");
1609 COPY(verbose_p, "verbose_p");
1610 COPY(capture_stderr_p, "capture_stderr_p");
1611 COPY(splash_p, "splash_p");
1614 COPY(tmode, "tmode");
1616 COPY(install_cmap_p, "install_cmap_p");
1617 COPY(fade_p, "fade_p");
1618 COPY(unfade_p, "unfade_p");
1619 COPY(fade_seconds, "fade_seconds");
1621 COPY(grab_desktop_p, "grab_desktop_p");
1622 COPY(grab_video_p, "grab_video_p");
1623 COPY(random_image_p, "random_image_p");
1627 # define COPYSTR(FIELD,NAME) \
1630 strcmp(p->FIELD, p2->FIELD)) \
1634 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1636 if (p->FIELD && p->FIELD != p2->FIELD) \
1638 p->FIELD = p2->FIELD; \
1641 COPYSTR(image_directory, "image_directory");
1642 COPYSTR(text_literal, "text_literal");
1643 COPYSTR(text_file, "text_file");
1644 COPYSTR(text_program, "text_program");
1645 COPYSTR(text_url, "text_url");
1648 populate_prefs_page (s);
1652 Display *dpy = GDK_DISPLAY();
1653 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1654 sync_server_dpms_settings (dpy, enabled_p,
1655 p->dpms_standby / 1000,
1656 p->dpms_suspend / 1000,
1660 changed = demo_write_init_file (s, p);
1663 s->saving_p = False;
1668 /* Flush out any changes made in the popup dialog box (where changes
1669 take place only when the OK button is clicked.)
1672 flush_popup_changes_and_save (state *s)
1674 Bool changed = False;
1675 saver_preferences *p = &s->prefs;
1676 int list_elt = selected_list_element (s);
1678 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1679 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1681 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1682 const char *command = gtk_entry_get_text (cmd);
1687 if (s->saving_p) return False;
1693 if (maybe_reload_init_file (s) != 0)
1699 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1701 if (!strcasecmp (visual, "")) visual = "";
1702 else if (!strcasecmp (visual, "any")) visual = "";
1703 else if (!strcasecmp (visual, "default")) visual = "Default";
1704 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1705 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1706 else if (!strcasecmp (visual, "best")) visual = "Best";
1707 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1708 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1709 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1710 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1711 else if (!strcasecmp (visual, "color")) visual = "Color";
1712 else if (!strcasecmp (visual, "gl")) visual = "GL";
1713 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1714 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1715 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1716 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1717 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1718 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1719 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1720 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1721 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1724 gdk_beep (); /* unparsable */
1726 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1729 changed = flush_changes (s, list_elt, -1, command, visual);
1732 changed = demo_write_init_file (s, p);
1734 /* Do this to re-launch the hack if (and only if) the command line
1736 populate_demo_window (s, selected_list_element (s));
1740 s->saving_p = False;
1745 G_MODULE_EXPORT void
1746 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1748 state *s = global_state_kludge; /* I hate C so much... */
1749 if (! s->initializing_p)
1751 s->initializing_p = True;
1752 flush_dialog_changes_and_save (s);
1753 s->initializing_p = False;
1757 G_MODULE_EXPORT gboolean
1758 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1760 pref_changed_cb (widget, user_data);
1764 /* Callback on menu items in the "mode" options menu.
1766 G_MODULE_EXPORT void
1767 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1769 state *s = (state *) user_data;
1770 saver_preferences *p = &s->prefs;
1771 GtkWidget *list = name_to_widget (s, "list");
1774 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1776 saver_mode new_mode;
1780 if (menu_items->data == widget)
1783 menu_items = menu_items->next;
1785 if (!menu_items) abort();
1787 new_mode = mode_menu_order[menu_index];
1789 /* Keep the same list element displayed as before; except if we're
1790 switching *to* "one screensaver" mode from any other mode, set
1791 "the one" to be that which is currently selected.
1793 list_elt = selected_list_element (s);
1794 if (new_mode == ONE_HACK)
1795 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1798 saver_mode old_mode = p->mode;
1800 populate_demo_window (s, list_elt);
1801 force_list_select_item (s, list, list_elt, True);
1802 p->mode = old_mode; /* put it back, so the init file gets written */
1805 pref_changed_cb (widget, user_data);
1809 G_MODULE_EXPORT void
1810 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1811 gint page_num, gpointer user_data)
1813 state *s = global_state_kludge; /* I hate C so much... */
1814 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1816 /* If we're switching to page 0, schedule the current hack to be run.
1817 Otherwise, schedule it to stop. */
1819 populate_demo_window (s, selected_list_element (s));
1821 schedule_preview (s, 0);
1826 list_activated_cb (GtkTreeView *list,
1828 GtkTreeViewColumn *column,
1835 g_return_if_fail (!gdk_pointer_is_grabbed ());
1837 str = gtk_tree_path_to_string (path);
1838 list_elt = strtol (str, NULL, 10);
1842 run_hack (s, list_elt, True);
1846 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1848 state *s = (state *)data;
1849 GtkTreeModel *model;
1855 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1858 path = gtk_tree_model_get_path (model, &iter);
1859 str = gtk_tree_path_to_string (path);
1860 list_elt = strtol (str, NULL, 10);
1862 gtk_tree_path_free (path);
1865 populate_demo_window (s, list_elt);
1866 flush_dialog_changes_and_save (s);
1868 /* Re-populate the Settings window any time a new item is selected
1869 in the list, in case both windows are currently visible.
1871 populate_popup_window (s);
1874 #else /* !HAVE_GTK2 */
1876 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1877 list_select_cb that comes in
1878 *after* we've double-clicked.
1882 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1885 state *s = (state *) data;
1886 if (event->type == GDK_2BUTTON_PRESS)
1888 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1889 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1891 last_doubleclick_time = time ((time_t *) 0);
1894 run_hack (s, list_elt, True);
1902 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1904 state *s = (state *) data;
1905 time_t now = time ((time_t *) 0);
1907 if (now >= last_doubleclick_time + 2)
1909 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1910 populate_demo_window (s, list_elt);
1911 flush_dialog_changes_and_save (s);
1916 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1918 state *s = (state *) data;
1919 populate_demo_window (s, -1);
1920 flush_dialog_changes_and_save (s);
1923 #endif /* !HAVE_GTK2 */
1926 /* Called when the checkboxes that are in the left column of the
1927 scrolling list are clicked. This both populates the right pane
1928 (just as clicking on the label (really, listitem) does) and
1929 also syncs this checkbox with the right pane Enabled checkbox.
1934 GtkCellRendererToggle *toggle,
1936 #else /* !HAVE_GTK2 */
1938 #endif /* !HAVE_GTK2 */
1941 state *s = (state *) data;
1944 GtkScrolledWindow *scroller =
1945 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1946 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1947 GtkTreeModel *model = gtk_tree_view_get_model (list);
1948 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1951 #else /* !HAVE_GTK2 */
1952 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1953 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1955 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1956 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1957 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1958 #endif /* !HAVE_GTK2 */
1965 if (!gtk_tree_model_get_iter (model, &iter, path))
1967 g_warning ("bad path: %s", path_string);
1970 gtk_tree_path_free (path);
1972 gtk_tree_model_get (model, &iter,
1973 COL_ENABLED, &active,
1976 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1977 COL_ENABLED, !active,
1980 list_elt = strtol (path_string, NULL, 10);
1981 #else /* !HAVE_GTK2 */
1982 list_elt = gtk_list_child_position (list, line);
1983 #endif /* !HAVE_GTK2 */
1985 /* remember previous scroll position of the top of the list */
1986 adj = gtk_scrolled_window_get_vadjustment (scroller);
1987 scroll_top = adj->value;
1989 flush_dialog_changes_and_save (s);
1990 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
1991 populate_demo_window (s, list_elt);
1993 /* restore the previous scroll position of the top of the list.
1994 this is weak, but I don't really know why it's moving... */
1995 gtk_adjustment_set_value (adj, scroll_top);
2001 GtkFileSelection *widget;
2002 } file_selection_data;
2007 store_image_directory (GtkWidget *button, gpointer user_data)
2009 file_selection_data *fsd = (file_selection_data *) user_data;
2010 state *s = fsd->state;
2011 GtkFileSelection *selector = fsd->widget;
2012 GtkWidget *top = s->toplevel_widget;
2013 saver_preferences *p = &s->prefs;
2014 const char *path = gtk_file_selection_get_filename (selector);
2016 if (p->image_directory && !strcmp(p->image_directory, path))
2017 return; /* no change */
2019 if (!directory_p (path))
2022 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2023 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2027 if (p->image_directory) free (p->image_directory);
2028 p->image_directory = normalize_directory (path);
2030 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2031 (p->image_directory ? p->image_directory : ""));
2032 demo_write_init_file (s, p);
2037 store_text_file (GtkWidget *button, gpointer user_data)
2039 file_selection_data *fsd = (file_selection_data *) user_data;
2040 state *s = fsd->state;
2041 GtkFileSelection *selector = fsd->widget;
2042 GtkWidget *top = s->toplevel_widget;
2043 saver_preferences *p = &s->prefs;
2044 const char *path = gtk_file_selection_get_filename (selector);
2046 if (p->text_file && !strcmp(p->text_file, path))
2047 return; /* no change */
2052 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2053 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2057 if (p->text_file) free (p->text_file);
2058 p->text_file = normalize_directory (path);
2060 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2061 (p->text_file ? p->text_file : ""));
2062 demo_write_init_file (s, p);
2067 store_text_program (GtkWidget *button, gpointer user_data)
2069 file_selection_data *fsd = (file_selection_data *) user_data;
2070 state *s = fsd->state;
2071 GtkFileSelection *selector = fsd->widget;
2072 /*GtkWidget *top = s->toplevel_widget;*/
2073 saver_preferences *p = &s->prefs;
2074 const char *path = gtk_file_selection_get_filename (selector);
2076 if (p->text_program && !strcmp(p->text_program, path))
2077 return; /* no change */
2083 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2084 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2089 if (p->text_program) free (p->text_program);
2090 p->text_program = normalize_directory (path);
2092 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2093 (p->text_program ? p->text_program : ""));
2094 demo_write_init_file (s, p);
2100 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2102 file_selection_data *fsd = (file_selection_data *) user_data;
2103 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2107 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2109 browse_image_dir_cancel (button, user_data);
2110 store_image_directory (button, user_data);
2114 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2116 browse_image_dir_cancel (button, user_data);
2117 store_text_file (button, user_data);
2121 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2123 browse_image_dir_cancel (button, user_data);
2124 store_text_program (button, user_data);
2128 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2130 browse_image_dir_cancel (widget, user_data);
2134 G_MODULE_EXPORT void
2135 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2137 state *s = global_state_kludge; /* I hate C so much... */
2138 saver_preferences *p = &s->prefs;
2139 static file_selection_data *fsd = 0;
2141 GtkFileSelection *selector = GTK_FILE_SELECTION(
2142 gtk_file_selection_new ("Please select the image directory."));
2145 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2147 fsd->widget = selector;
2150 if (p->image_directory && *p->image_directory)
2151 gtk_file_selection_set_filename (selector, p->image_directory);
2153 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2154 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2156 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2157 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2159 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2160 GTK_SIGNAL_FUNC (browse_image_dir_close),
2163 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2165 gtk_window_set_modal (GTK_WINDOW (selector), True);
2166 gtk_widget_show (GTK_WIDGET (selector));
2170 G_MODULE_EXPORT void
2171 browse_text_file_cb (GtkButton *button, gpointer user_data)
2173 state *s = global_state_kludge; /* I hate C so much... */
2174 saver_preferences *p = &s->prefs;
2175 static file_selection_data *fsd = 0;
2177 GtkFileSelection *selector = GTK_FILE_SELECTION(
2178 gtk_file_selection_new ("Please select a text file."));
2181 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2183 fsd->widget = selector;
2186 if (p->text_file && *p->text_file)
2187 gtk_file_selection_set_filename (selector, p->text_file);
2189 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2190 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2192 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2193 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2195 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2196 GTK_SIGNAL_FUNC (browse_image_dir_close),
2199 gtk_window_set_modal (GTK_WINDOW (selector), True);
2200 gtk_widget_show (GTK_WIDGET (selector));
2204 G_MODULE_EXPORT void
2205 browse_text_program_cb (GtkButton *button, gpointer user_data)
2207 state *s = global_state_kludge; /* I hate C so much... */
2208 saver_preferences *p = &s->prefs;
2209 static file_selection_data *fsd = 0;
2211 GtkFileSelection *selector = GTK_FILE_SELECTION(
2212 gtk_file_selection_new ("Please select a text-generating program."));
2215 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2217 fsd->widget = selector;
2220 if (p->text_program && *p->text_program)
2221 gtk_file_selection_set_filename (selector, p->text_program);
2223 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2224 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2226 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2227 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2229 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2230 GTK_SIGNAL_FUNC (browse_image_dir_close),
2233 gtk_window_set_modal (GTK_WINDOW (selector), True);
2234 gtk_widget_show (GTK_WIDGET (selector));
2241 G_MODULE_EXPORT void
2242 settings_cb (GtkButton *button, gpointer user_data)
2244 state *s = global_state_kludge; /* I hate C so much... */
2245 int list_elt = selected_list_element (s);
2247 populate_demo_window (s, list_elt); /* reset the widget */
2248 populate_popup_window (s); /* create UI on popup window */
2249 gtk_widget_show (s->popup_widget);
2253 settings_sync_cmd_text (state *s)
2256 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2257 char *cmd_line = get_configurator_command_line (s->cdata, False);
2258 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2259 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2261 # endif /* HAVE_XML */
2264 G_MODULE_EXPORT void
2265 settings_adv_cb (GtkButton *button, gpointer user_data)
2267 state *s = global_state_kludge; /* I hate C so much... */
2268 GtkNotebook *notebook =
2269 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2271 settings_sync_cmd_text (s);
2272 gtk_notebook_set_page (notebook, 1);
2275 G_MODULE_EXPORT void
2276 settings_std_cb (GtkButton *button, gpointer user_data)
2278 state *s = global_state_kludge; /* I hate C so much... */
2279 GtkNotebook *notebook =
2280 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2282 /* Re-create UI to reflect the in-progress command-line settings. */
2283 populate_popup_window (s);
2285 gtk_notebook_set_page (notebook, 0);
2288 G_MODULE_EXPORT void
2289 settings_reset_cb (GtkButton *button, gpointer user_data)
2292 state *s = global_state_kludge; /* I hate C so much... */
2293 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2294 char *cmd_line = get_configurator_command_line (s->cdata, True);
2295 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2296 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2298 populate_popup_window (s);
2299 # endif /* HAVE_XML */
2302 G_MODULE_EXPORT void
2303 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2304 gint page_num, gpointer user_data)
2306 state *s = global_state_kludge; /* I hate C so much... */
2307 GtkWidget *adv = name_to_widget (s, "adv_button");
2308 GtkWidget *std = name_to_widget (s, "std_button");
2312 gtk_widget_show (adv);
2313 gtk_widget_hide (std);
2315 else if (page_num == 1)
2317 gtk_widget_hide (adv);
2318 gtk_widget_show (std);
2326 G_MODULE_EXPORT void
2327 settings_cancel_cb (GtkButton *button, gpointer user_data)
2329 state *s = global_state_kludge; /* I hate C so much... */
2330 gtk_widget_hide (s->popup_widget);
2333 G_MODULE_EXPORT void
2334 settings_ok_cb (GtkButton *button, gpointer user_data)
2336 state *s = global_state_kludge; /* I hate C so much... */
2337 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2338 int page = gtk_notebook_get_current_page (notebook);
2341 /* Regenerate the command-line from the widget contents before saving.
2342 But don't do this if we're looking at the command-line page already,
2343 or we will blow away what they typed... */
2344 settings_sync_cmd_text (s);
2346 flush_popup_changes_and_save (s);
2347 gtk_widget_hide (s->popup_widget);
2351 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2353 state *s = (state *) data;
2354 settings_cancel_cb (0, (gpointer) s);
2360 /* Populating the various widgets
2364 /* Returns the number of the last hack run by the server.
2367 server_current_hack (void)
2371 unsigned long nitems, bytesafter;
2372 unsigned char *dataP = 0;
2373 Display *dpy = GDK_DISPLAY();
2374 int hack_number = -1;
2376 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2377 XA_SCREENSAVER_STATUS,
2378 0, 3, False, XA_INTEGER,
2379 &type, &format, &nitems, &bytesafter,
2382 && type == XA_INTEGER
2386 PROP32 *data = (PROP32 *) dataP;
2387 hack_number = (int) data[2] - 1;
2390 if (dataP) XFree (dataP);
2396 /* Finds the number of the last hack that was run, and makes that item be
2397 selected by default.
2400 scroll_to_current_hack (state *s)
2402 saver_preferences *p = &s->prefs;
2403 int hack_number = -1;
2405 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2406 hack_number = p->selected_hack;
2407 if (hack_number < 0) /* otherwise, use the last-run */
2408 hack_number = server_current_hack ();
2409 if (hack_number < 0) /* failing that, last "one mode" */
2410 hack_number = p->selected_hack;
2411 if (hack_number < 0) /* failing that, newest hack. */
2413 /* We should only get here if the user does not have a .xscreensaver
2414 file, and the screen has not been blanked with a hack since X
2415 started up: in other words, this is probably a fresh install.
2417 Instead of just defaulting to hack #0 (in either "programs" or
2418 "alphabetical" order) let's try to default to the last runnable
2419 hack in the "programs" list: this is probably the hack that was
2420 most recently added to the xscreensaver distribution (and so
2421 it's probably the currently-coolest one!)
2423 hack_number = p->screenhacks_count-1;
2424 while (hack_number > 0 &&
2425 ! (s->hacks_available_p[hack_number] &&
2426 p->screenhacks[hack_number]->enabled_p))
2430 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2432 int list_elt = s->hack_number_to_list_elt[hack_number];
2433 GtkWidget *list = name_to_widget (s, "list");
2434 force_list_select_item (s, list, list_elt, True);
2435 populate_demo_window (s, list_elt);
2441 populate_hack_list (state *s)
2443 Display *dpy = GDK_DISPLAY();
2445 saver_preferences *p = &s->prefs;
2446 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2447 GtkListStore *model;
2448 GtkTreeSelection *selection;
2449 GtkCellRenderer *ren;
2453 g_object_get (G_OBJECT (list),
2458 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2459 g_object_set (G_OBJECT (list), "model", model, NULL);
2460 g_object_unref (model);
2462 ren = gtk_cell_renderer_toggle_new ();
2463 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2465 "active", COL_ENABLED,
2468 g_signal_connect (ren, "toggled",
2469 G_CALLBACK (list_checkbox_cb),
2472 ren = gtk_cell_renderer_text_new ();
2473 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2474 _("Screen Saver"), ren,
2478 g_signal_connect_after (list, "row_activated",
2479 G_CALLBACK (list_activated_cb),
2482 selection = gtk_tree_view_get_selection (list);
2483 g_signal_connect (selection, "changed",
2484 G_CALLBACK (list_select_changed_cb),
2489 for (i = 0; i < s->list_count; i++)
2491 int hack_number = s->list_elt_to_hack_number[i];
2492 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2494 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2496 if (!hack) continue;
2498 /* If we're to suppress uninstalled hacks, check $PATH now. */
2499 if (p->ignore_uninstalled_p && !available_p)
2502 pretty_name = (hack->name
2503 ? strdup (hack->name)
2504 : make_hack_name (dpy, hack->command));
2508 /* Make the text foreground be the color of insensitive widgets
2509 (but don't actually make it be insensitive, since we still
2510 want to be able to click on it.)
2512 GtkStyle *style = GTK_WIDGET (list)->style;
2513 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2514 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2515 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2517 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2518 /* " background=\"#%02X%02X%02X\"" */
2520 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2521 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2527 gtk_list_store_append (model, &iter);
2528 gtk_list_store_set (model, &iter,
2529 COL_ENABLED, hack->enabled_p,
2530 COL_NAME, pretty_name,
2535 #else /* !HAVE_GTK2 */
2537 saver_preferences *p = &s->prefs;
2538 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2540 for (i = 0; i < s->list_count; i++)
2542 int hack_number = s->list_elt_to_hack_number[i];
2543 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2545 /* A GtkList must contain only GtkListItems, but those can contain
2546 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2547 and a Label. We handle single and double click events on the
2548 line itself, for clicking on the text, but the interior checkbox
2549 also handles its own events.
2552 GtkWidget *line_hbox;
2553 GtkWidget *line_check;
2554 GtkWidget *line_label;
2556 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2558 if (!hack) continue;
2560 /* If we're to suppress uninstalled hacks, check $PATH now. */
2561 if (p->ignore_uninstalled_p && !available_p)
2564 pretty_name = (hack->name
2565 ? strdup (hack->name)
2566 : make_hack_name (hack->command));
2568 line = gtk_list_item_new ();
2569 line_hbox = gtk_hbox_new (FALSE, 0);
2570 line_check = gtk_check_button_new ();
2571 line_label = gtk_label_new (pretty_name);
2573 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2574 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2575 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2577 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2579 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2581 gtk_widget_show (line_check);
2582 gtk_widget_show (line_label);
2583 gtk_widget_show (line_hbox);
2584 gtk_widget_show (line);
2588 gtk_container_add (GTK_CONTAINER (list), line);
2589 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2590 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2593 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2594 GTK_SIGNAL_FUNC (list_checkbox_cb),
2597 gtk_widget_show (line);
2601 /* Make the widget be colored like insensitive widgets
2602 (but don't actually make it be insensitive, since we
2603 still want to be able to click on it.)
2605 GtkRcStyle *rc_style;
2608 gtk_widget_realize (GTK_WIDGET (line_label));
2610 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2611 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2613 rc_style = gtk_rc_style_new ();
2614 rc_style->fg[GTK_STATE_NORMAL] = fg;
2615 rc_style->bg[GTK_STATE_NORMAL] = bg;
2616 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2618 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2619 gtk_rc_style_unref (rc_style);
2623 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2624 GTK_SIGNAL_FUNC (list_select_cb),
2626 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2627 GTK_SIGNAL_FUNC (list_unselect_cb),
2629 #endif /* !HAVE_GTK2 */
2633 update_list_sensitivity (state *s)
2635 saver_preferences *p = &s->prefs;
2636 Bool sensitive = (p->mode == RANDOM_HACKS ||
2637 p->mode == RANDOM_HACKS_SAME ||
2638 p->mode == ONE_HACK);
2639 Bool checkable = (p->mode == RANDOM_HACKS ||
2640 p->mode == RANDOM_HACKS_SAME);
2641 Bool blankable = (p->mode != DONT_BLANK);
2644 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2645 GtkWidget *use = name_to_widget (s, "use_col_frame");
2646 #endif /* HAVE_GTK2 */
2647 GtkWidget *scroller = name_to_widget (s, "scroller");
2648 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2649 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2652 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2653 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2654 #else /* !HAVE_GTK2 */
2655 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2656 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2658 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2659 #endif /* !HAVE_GTK2 */
2660 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2661 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2663 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2666 gtk_tree_view_column_set_visible (use, checkable);
2667 #else /* !HAVE_GTK2 */
2669 gtk_widget_show (use); /* the "Use" column header */
2671 gtk_widget_hide (use);
2675 GtkBin *line = GTK_BIN (kids->data);
2676 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2677 GtkWidget *line_check =
2678 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2681 gtk_widget_show (line_check);
2683 gtk_widget_hide (line_check);
2687 #endif /* !HAVE_GTK2 */
2692 populate_prefs_page (state *s)
2694 saver_preferences *p = &s->prefs;
2696 Bool can_lock_p = True;
2698 /* Disable all the "lock" controls if locking support was not provided
2699 at compile-time, or if running on MacOS. */
2700 # if defined(NO_LOCKING) || defined(__APPLE__)
2705 /* If there is only one screen, the mode menu contains
2706 "random" but not "random-same".
2708 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2709 p->mode = RANDOM_HACKS;
2712 /* The file supports timeouts of less than a minute, but the GUI does
2713 not, so throttle the values to be at least one minute (since "0" is
2714 a bad rounding choice...)
2716 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2719 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2722 # define FMT_MINUTES(NAME,N) \
2723 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2725 # define FMT_SECONDS(NAME,N) \
2726 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2728 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2729 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2730 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2731 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2732 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2733 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2734 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2739 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2740 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2743 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2745 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2746 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2747 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2749 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2750 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2751 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2752 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2753 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2754 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2755 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2759 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2760 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2761 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2762 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2763 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2766 # undef TOGGLE_ACTIVE
2768 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2769 (p->image_directory ? p->image_directory : ""));
2770 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2772 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2775 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2776 (p->text_literal ? p->text_literal : ""));
2777 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2778 (p->text_file ? p->text_file : ""));
2779 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2780 (p->text_program ? p->text_program : ""));
2781 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2782 (p->text_url ? p->text_url : ""));
2784 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2785 p->tmode == TEXT_LITERAL);
2786 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2787 p->tmode == TEXT_FILE);
2788 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2789 p->tmode == TEXT_FILE);
2790 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2791 p->tmode == TEXT_PROGRAM);
2792 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2793 p->tmode == TEXT_PROGRAM);
2794 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2795 p->tmode == TEXT_URL);
2798 /* Map the `saver_mode' enum to mode menu to values. */
2800 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2803 for (i = 0; i < countof(mode_menu_order); i++)
2804 if (mode_menu_order[i] == p->mode)
2806 gtk_option_menu_set_history (opt, i);
2807 update_list_sensitivity (s);
2811 Bool found_any_writable_cells = False;
2812 Bool fading_possible = False;
2813 Bool dpms_supported = False;
2815 Display *dpy = GDK_DISPLAY();
2816 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2818 for (i = 0; i < nscreens; i++)
2820 Screen *s = ScreenOfDisplay (dpy, i);
2821 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2823 found_any_writable_cells = True;
2828 fading_possible = found_any_writable_cells;
2829 #ifdef HAVE_XF86VMODE_GAMMA
2830 fading_possible = True;
2833 #ifdef HAVE_DPMS_EXTENSION
2835 int op = 0, event = 0, error = 0;
2836 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2837 dpms_supported = True;
2839 #endif /* HAVE_DPMS_EXTENSION */
2842 # define SENSITIZE(NAME,SENSITIVEP) \
2843 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2845 /* Blanking and Locking
2847 SENSITIZE ("lock_button", can_lock_p);
2848 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2849 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2853 SENSITIZE ("dpms_frame", dpms_supported);
2854 SENSITIZE ("dpms_button", dpms_supported);
2855 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2856 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2857 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2858 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2859 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2860 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2861 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2862 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2863 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2867 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2868 SENSITIZE ("install_button", found_any_writable_cells);
2869 SENSITIZE ("fade_button", fading_possible);
2870 SENSITIZE ("unfade_button", fading_possible);
2872 SENSITIZE ("fade_label", (fading_possible &&
2873 (p->fade_p || p->unfade_p)));
2874 SENSITIZE ("fade_spinbutton", (fading_possible &&
2875 (p->fade_p || p->unfade_p)));
2883 populate_popup_window (state *s)
2885 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2886 char *doc_string = 0;
2888 /* #### not in Gtk 1.2
2889 gtk_label_set_selectable (doc);
2895 free_conf_data (s->cdata);
2900 saver_preferences *p = &s->prefs;
2901 int list_elt = selected_list_element (s);
2902 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2903 ? s->list_elt_to_hack_number[list_elt]
2905 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2908 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2909 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2910 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2911 s->cdata = load_configurator (cmd_line, s->debug_p);
2912 if (s->cdata && s->cdata->widget)
2913 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2918 doc_string = (s->cdata
2919 ? s->cdata->description
2921 # else /* !HAVE_XML */
2922 doc_string = _("Descriptions not available: no XML support compiled in.");
2923 # endif /* !HAVE_XML */
2925 gtk_label_set_text (doc, (doc_string
2927 : _("No description available.")));
2932 sensitize_demo_widgets (state *s, Bool sensitive_p)
2934 const char *names[] = { "demo", "settings",
2935 "cmd_label", "cmd_text", "manual",
2936 "visual", "visual_combo" };
2938 for (i = 0; i < countof(names); i++)
2940 GtkWidget *w = name_to_widget (s, names[i]);
2941 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2947 sensitize_menu_items (state *s, Bool force_p)
2949 static Bool running_p = False;
2950 static time_t last_checked = 0;
2951 time_t now = time ((time_t *) 0);
2952 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
2956 if (force_p || now > last_checked + 10) /* check every 10 seconds */
2958 running_p = xscreensaver_running_p (s);
2959 last_checked = time ((time_t *) 0);
2962 for (i = 0; i < countof(names); i++)
2964 GtkWidget *w = name_to_widget (s, names[i]);
2965 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
2970 /* When the File menu is de-posted after a "Restart Daemon" command,
2971 the window underneath doesn't repaint for some reason. I guess this
2972 is a bug in exposure handling in GTK or GDK. This works around it.
2975 force_dialog_repaint (state *s)
2978 /* Tell GDK to invalidate and repaint the whole window.
2980 GdkWindow *w = s->toplevel_widget->window;
2981 GdkRegion *region = gdk_region_new ();
2983 rect.x = rect.y = 0;
2984 rect.width = rect.height = 32767;
2985 gdk_region_union_with_rect (region, &rect);
2986 gdk_window_invalidate_region (w, region, True);
2987 gdk_region_destroy (region);
2988 gdk_window_process_updates (w, True);
2990 /* Force the server to send an exposure event by creating and then
2991 destroying a window as a child of the top level shell.
2993 Display *dpy = GDK_DISPLAY();
2994 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
2996 XWindowAttributes xgwa;
2997 XGetWindowAttributes (dpy, parent, &xgwa);
2998 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
2999 XMapRaised (dpy, w);
3000 XDestroyWindow (dpy, w);
3006 /* Even though we've given these text fields a maximum number of characters,
3007 their default size is still about 30 characters wide -- so measure out
3008 a string in their font, and resize them to just fit that.
3011 fix_text_entry_sizes (state *s)
3015 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
3016 const char * const spinbuttons[] = {
3017 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3018 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3019 "dpms_off_spinbutton",
3020 "-fade_spinbutton" };
3024 for (i = 0; i < countof(spinbuttons); i++)
3026 const char *n = spinbuttons[i];
3028 while (*n == '-') n++, cols--;
3029 w = GTK_WIDGET (name_to_widget (s, n));
3030 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3031 gtk_widget_set_usize (w, width, -2);
3034 /* Now fix the width of the combo box.
3036 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3037 w = GTK_COMBO (w)->entry;
3038 width = gdk_string_width (w->style->font, "PseudoColor___");
3039 gtk_widget_set_usize (w, width, -2);
3041 /* Now fix the width of the file entry text.
3043 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3044 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3045 gtk_widget_set_usize (w, width, -2);
3047 /* Now fix the width of the command line text.
3049 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3050 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3051 gtk_widget_set_usize (w, width, -2);
3055 /* Now fix the height of the list widget:
3056 make it default to being around 10 text-lines high instead of 4.
3058 w = GTK_WIDGET (name_to_widget (s, "list"));
3062 int leading = 3; /* approximate is ok... */
3066 PangoFontMetrics *pain =
3067 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3068 w->style->font_desc,
3069 gtk_get_default_language ());
3070 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3071 pango_font_metrics_get_descent (pain));
3072 #else /* !HAVE_GTK2 */
3073 height = w->style->font->ascent + w->style->font->descent;
3074 #endif /* !HAVE_GTK2 */
3078 height += border * 2;
3079 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3080 gtk_widget_set_usize (w, -2, height);
3087 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3090 static char *up_arrow_xpm[] = {
3113 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3114 the end of the array (Gtk 1.2.5.) */
3115 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3116 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3119 static char *down_arrow_xpm[] = {
3142 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3143 the end of the array (Gtk 1.2.5.) */
3144 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3145 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3149 pixmapify_button (state *s, int down_p)
3153 GtkWidget *pixmapwid;
3157 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3158 style = gtk_widget_get_style (w);
3160 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3161 &style->bg[GTK_STATE_NORMAL],
3163 ? (gchar **) down_arrow_xpm
3164 : (gchar **) up_arrow_xpm));
3165 pixmapwid = gtk_pixmap_new (pixmap, mask);
3166 gtk_widget_show (pixmapwid);
3167 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3168 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3172 map_next_button_cb (GtkWidget *w, gpointer user_data)
3174 state *s = (state *) user_data;
3175 pixmapify_button (s, 1);
3179 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3181 state *s = (state *) user_data;
3182 pixmapify_button (s, 0);
3184 #endif /* !HAVE_GTK2 */
3188 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3192 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3193 GtkAllocation *allocation,
3197 GtkWidgetAuxInfo *aux_info;
3199 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3201 aux_info->width = allocation->width;
3202 aux_info->height = -2;
3206 gtk_widget_size_request (label, &req);
3209 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3212 eschew_gtk_lossage (GtkLabel *label)
3214 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3215 aux_info->width = GTK_WIDGET (label)->allocation.width;
3216 aux_info->height = -2;
3220 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3222 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3223 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3226 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3228 gtk_widget_queue_resize (GTK_WIDGET (label));
3230 #endif /* !HAVE_GTK2 */
3234 populate_demo_window (state *s, int list_elt)
3236 Display *dpy = GDK_DISPLAY();
3237 saver_preferences *p = &s->prefs;
3240 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3241 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3242 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3243 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3244 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3246 if (p->mode == BLANK_ONLY)
3249 pretty_name = strdup (_("Blank Screen"));
3250 schedule_preview (s, 0);
3252 else if (p->mode == DONT_BLANK)
3255 pretty_name = strdup (_("Screen Saver Disabled"));
3256 schedule_preview (s, 0);
3260 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3261 ? s->list_elt_to_hack_number[list_elt]
3263 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3267 ? strdup (hack->name)
3268 : make_hack_name (dpy, hack->command))
3272 schedule_preview (s, hack->command);
3274 schedule_preview (s, 0);
3278 pretty_name = strdup (_("Preview"));
3280 gtk_frame_set_label (frame1, _(pretty_name));
3281 gtk_frame_set_label (frame2, _(pretty_name));
3283 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3284 gtk_entry_set_position (cmd, 0);
3288 sprintf (title, _("%s: %.100s Settings"),
3289 progclass, (pretty_name ? pretty_name : "???"));
3290 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3293 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3295 ? (hack->visual && *hack->visual
3300 sensitize_demo_widgets (s, (hack ? True : False));
3302 if (pretty_name) free (pretty_name);
3304 ensure_selected_item_visible (list);
3306 s->_selected_list_element = list_elt;
3311 widget_deleter (GtkWidget *widget, gpointer data)
3313 /* #### Well, I want to destroy these widgets, but if I do that, they get
3314 referenced again, and eventually I get a SEGV. So instead of
3315 destroying them, I'll just hide them, and leak a bunch of memory
3316 every time the disk file changes. Go go go Gtk!
3318 #### Ok, that's a lie, I get a crash even if I just hide the widget
3319 and don't ever delete it. Fuck!
3322 gtk_widget_destroy (widget);
3324 gtk_widget_hide (widget);
3329 static char **sort_hack_cmp_names_kludge;
3331 sort_hack_cmp (const void *a, const void *b)
3337 int aa = *(int *) a;
3338 int bb = *(int *) b;
3339 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3340 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3341 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3347 initialize_sort_map (state *s)
3349 Display *dpy = GDK_DISPLAY();
3350 saver_preferences *p = &s->prefs;
3353 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3354 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3355 if (s->hacks_available_p) free (s->hacks_available_p);
3357 s->list_elt_to_hack_number = (int *)
3358 calloc (sizeof(int), p->screenhacks_count + 1);
3359 s->hack_number_to_list_elt = (int *)
3360 calloc (sizeof(int), p->screenhacks_count + 1);
3361 s->hacks_available_p = (Bool *)
3362 calloc (sizeof(Bool), p->screenhacks_count + 1);
3363 s->total_available = 0;
3365 /* Check which hacks actually exist on $PATH
3367 for (i = 0; i < p->screenhacks_count; i++)
3369 screenhack *hack = p->screenhacks[i];
3370 int on = on_path_p (hack->command) ? 1 : 0;
3371 s->hacks_available_p[i] = on;
3372 s->total_available += on;
3375 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3379 for (i = 0; i < p->screenhacks_count; i++)
3381 if (!p->ignore_uninstalled_p ||
3382 s->hacks_available_p[i])
3383 s->list_elt_to_hack_number[j++] = i;
3387 for (; j < p->screenhacks_count; j++)
3388 s->list_elt_to_hack_number[j] = -1;
3391 /* Generate list of sortable names (once)
3393 sort_hack_cmp_names_kludge = (char **)
3394 calloc (sizeof(char *), p->screenhacks_count);
3395 for (i = 0; i < p->screenhacks_count; i++)
3397 screenhack *hack = p->screenhacks[i];
3398 char *name = (hack->name && *hack->name
3399 ? strdup (hack->name)
3400 : make_hack_name (dpy, hack->command));
3402 for (str = name; *str; str++)
3403 *str = tolower(*str);
3404 sort_hack_cmp_names_kludge[i] = name;
3407 /* Sort list->hack map alphabetically
3409 qsort (s->list_elt_to_hack_number,
3410 p->screenhacks_count,
3411 sizeof(*s->list_elt_to_hack_number),
3416 for (i = 0; i < p->screenhacks_count; i++)
3417 free (sort_hack_cmp_names_kludge[i]);
3418 free (sort_hack_cmp_names_kludge);
3419 sort_hack_cmp_names_kludge = 0;
3421 /* Build inverse table */
3422 for (i = 0; i < p->screenhacks_count; i++)
3424 int n = s->list_elt_to_hack_number[i];
3426 s->hack_number_to_list_elt[n] = i;
3432 maybe_reload_init_file (state *s)
3434 Display *dpy = GDK_DISPLAY();
3435 saver_preferences *p = &s->prefs;
3438 static Bool reentrant_lock = False;
3439 if (reentrant_lock) return 0;
3440 reentrant_lock = True;
3442 if (init_file_changed_p (p))
3444 const char *f = init_file_name();
3449 if (!f || !*f) return 0;
3450 b = (char *) malloc (strlen(f) + 1024);
3453 "file \"%s\" has changed, reloading.\n"),
3455 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3458 load_init_file (dpy, p);
3459 initialize_sort_map (s);
3461 list_elt = selected_list_element (s);
3462 list = name_to_widget (s, "list");
3463 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3464 populate_hack_list (s);
3465 force_list_select_item (s, list, list_elt, True);
3466 populate_prefs_page (s);
3467 populate_demo_window (s, list_elt);
3468 ensure_selected_item_visible (list);
3473 reentrant_lock = False;
3479 /* Making the preview window have the right X visual (so that GL works.)
3482 static Visual *get_best_gl_visual (state *);
3485 x_visual_to_gdk_visual (Visual *xv)
3487 GList *gvs = gdk_list_visuals();
3488 if (!xv) return gdk_visual_get_system();
3489 for (; gvs; gvs = gvs->next)
3491 GdkVisual *gv = (GdkVisual *) gvs->data;
3492 if (xv == GDK_VISUAL_XVISUAL (gv))
3495 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3496 blurb(), (unsigned long) xv->visualid);
3501 clear_preview_window (state *s)
3506 if (!s->toplevel_widget) return; /* very early */
3507 p = name_to_widget (s, "preview");
3510 if (!window) return;
3512 /* Flush the widget background down into the window, in case a subproc
3514 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
3515 gdk_window_clear (window);
3518 int list_elt = selected_list_element (s);
3519 int hack_number = (list_elt >= 0
3520 ? s->list_elt_to_hack_number[list_elt]
3522 Bool available_p = (hack_number >= 0
3523 ? s->hacks_available_p [hack_number]
3525 Bool nothing_p = (s->total_available < 5);
3528 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3529 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3530 (s->running_preview_error_p
3531 ? (available_p ? 1 :
3534 #else /* !HAVE_GTK2 */
3535 if (s->running_preview_error_p)
3537 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3538 const char * const lines2[] = { N_("Not"), N_("Installed") };
3539 int nlines = countof(lines1);
3540 int lh = p->style->font->ascent + p->style->font->descent;
3544 const char * const *lines = (available_p ? lines1 : lines2);
3546 gdk_window_get_size (window, &w, &h);
3547 y = (h - (lh * nlines)) / 2;
3548 y += p->style->font->ascent;
3549 for (i = 0; i < nlines; i++)
3551 int sw = gdk_string_width (p->style->font, _(lines[i]));
3552 int x = (w - sw) / 2;
3553 gdk_draw_string (window, p->style->font,
3554 p->style->fg_gc[GTK_STATE_NORMAL],
3559 #endif /* !HAVE_GTK2 */
3567 reset_preview_window (state *s)
3569 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3570 when you kill one and re-start another on the same window. So maybe
3571 it's best to just always destroy and recreate the preview window
3572 when changing hacks, instead of always trying to reuse the same one?
3574 GtkWidget *pr = name_to_widget (s, "preview");
3575 if (GTK_WIDGET_REALIZED (pr))
3577 Window oid = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3579 gtk_widget_hide (pr);
3580 gtk_widget_unrealize (pr);
3581 gtk_widget_realize (pr);
3582 gtk_widget_show (pr);
3583 id = (pr->window ? GDK_WINDOW_XWINDOW (pr->window) : 0);
3585 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3593 fix_preview_visual (state *s)
3595 GtkWidget *widget = name_to_widget (s, "preview");
3596 Visual *xvisual = get_best_gl_visual (s);
3597 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3598 GdkVisual *dvisual = gdk_visual_get_system();
3599 GdkColormap *cmap = (visual == dvisual
3600 ? gdk_colormap_get_system ()
3601 : gdk_colormap_new (visual, False));
3604 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3605 (visual == dvisual ? "default" : "non-default"),
3606 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3608 if (!GTK_WIDGET_REALIZED (widget) ||
3609 gtk_widget_get_visual (widget) != visual)
3611 gtk_widget_unrealize (widget);
3612 gtk_widget_set_visual (widget, visual);
3613 gtk_widget_set_colormap (widget, cmap);
3614 gtk_widget_realize (widget);
3617 /* Set the Widget colors to be white-on-black. */
3619 GdkWindow *window = widget->window;
3620 GtkStyle *style = gtk_style_copy (widget->style);
3621 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3622 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3623 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3624 GdkGC *fgc = gdk_gc_new(window);
3625 GdkGC *bgc = gdk_gc_new(window);
3626 if (!gdk_color_white (cmap, fg)) abort();
3627 if (!gdk_color_black (cmap, bg)) abort();
3628 gdk_gc_set_foreground (fgc, fg);
3629 gdk_gc_set_background (fgc, bg);
3630 gdk_gc_set_foreground (bgc, bg);
3631 gdk_gc_set_background (bgc, fg);
3632 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3633 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3634 gtk_widget_set_style (widget, style);
3636 /* For debugging purposes, put a title on the window (so that
3637 it can be easily found in the output of "xwininfo -tree".)
3639 gdk_window_set_title (window, "Preview");
3642 gtk_widget_show (widget);
3650 subproc_pretty_name (state *s)
3652 if (s->running_preview_cmd)
3654 char *ps = strdup (s->running_preview_cmd);
3655 char *ss = strchr (ps, ' ');
3657 ss = strrchr (ps, '/');
3668 return strdup ("???");
3673 reap_zombies (state *s)
3675 int wait_status = 0;
3677 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3681 if (pid == s->running_preview_pid)
3683 char *ss = subproc_pretty_name (s);
3684 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3685 (unsigned long) pid, ss);
3689 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3690 (unsigned long) pid);
3696 /* Mostly lifted from driver/subprocs.c */
3698 get_best_gl_visual (state *s)
3700 Display *dpy = GDK_DISPLAY();
3709 av[ac++] = "xscreensaver-gl-helper";
3714 perror ("error creating pipe:");
3721 switch ((int) (forked = fork ()))
3725 sprintf (buf, "%s: couldn't fork", blurb());
3733 close (in); /* don't need this one */
3734 close (ConnectionNumber (dpy)); /* close display fd */
3736 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3738 perror ("could not dup() a new stdout:");
3742 execvp (av[0], av); /* shouldn't return. */
3744 if (errno != ENOENT)
3746 /* Ignore "no such file or directory" errors, unless verbose.
3747 Issue all other exec errors, though. */
3748 sprintf (buf, "%s: running %s", blurb(), av[0]);
3752 /* Note that one must use _exit() instead of exit() in procs forked
3753 off of Gtk programs -- Gtk installs an atexit handler that has a
3754 copy of the X connection (which we've already closed, for safety.)
3755 If one uses exit() instead of _exit(), then one sometimes gets a
3756 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3758 _exit (1); /* exits fork */
3764 int wait_status = 0;
3766 FILE *f = fdopen (in, "r");
3770 close (out); /* don't need this one */
3773 if (!fgets (buf, sizeof(buf)-1, f))
3777 /* Wait for the child to die. */
3778 waitpid (-1, &wait_status, 0);
3780 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3786 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3792 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3794 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3795 blurb(), av[0], result);
3807 kill_preview_subproc (state *s, Bool reset_p)
3809 s->running_preview_error_p = False;
3812 clear_preview_window (s);
3814 if (s->subproc_check_timer_id)
3816 gtk_timeout_remove (s->subproc_check_timer_id);
3817 s->subproc_check_timer_id = 0;
3818 s->subproc_check_countdown = 0;
3821 if (s->running_preview_pid)
3823 int status = kill (s->running_preview_pid, SIGTERM);
3824 char *ss = subproc_pretty_name (s);
3831 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3832 blurb(), (unsigned long) s->running_preview_pid, ss);
3837 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3838 blurb(), (unsigned long) s->running_preview_pid, ss);
3844 waitpid(s->running_preview_pid, &endstatus, 0);
3846 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3847 (unsigned long) s->running_preview_pid, ss);
3851 s->running_preview_pid = 0;
3852 if (s->running_preview_cmd) free (s->running_preview_cmd);
3853 s->running_preview_cmd = 0;
3860 reset_preview_window (s);
3861 clear_preview_window (s);
3866 /* Immediately and unconditionally launches the given process,
3867 after appending the -window-id option; sets running_preview_pid.
3870 launch_preview_subproc (state *s)
3872 saver_preferences *p = &s->prefs;
3876 const char *cmd = s->desired_preview_cmd;
3878 GtkWidget *pr = name_to_widget (s, "preview");
3881 reset_preview_window (s);
3883 window = pr->window;
3885 s->running_preview_error_p = False;
3887 if (s->preview_suppressed_p)
3889 kill_preview_subproc (s, False);
3893 new_cmd = malloc (strlen (cmd) + 40);
3895 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3898 /* No window id? No command to run. */
3904 strcpy (new_cmd, cmd);
3905 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3909 kill_preview_subproc (s, False);
3912 s->running_preview_error_p = True;
3913 clear_preview_window (s);
3917 switch ((int) (forked = fork ()))
3922 sprintf (buf, "%s: couldn't fork", blurb());
3924 s->running_preview_error_p = True;
3930 close (ConnectionNumber (GDK_DISPLAY()));
3932 hack_subproc_environment (id, s->debug_p);
3934 usleep (250000); /* pause for 1/4th second before launching, to give
3935 the previous program time to die and flush its X
3936 buffer, so we don't get leftover turds on the
3939 exec_command (p->shell, new_cmd, p->nice_inferior);
3940 /* Don't bother printing an error message when we are unable to
3941 exec subprocesses; we handle that by polling the pid later.
3943 Note that one must use _exit() instead of exit() in procs forked
3944 off of Gtk programs -- Gtk installs an atexit handler that has a
3945 copy of the X connection (which we've already closed, for safety.)
3946 If one uses exit() instead of _exit(), then one sometimes gets a
3947 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3949 _exit (1); /* exits child fork */
3954 if (s->running_preview_cmd) free (s->running_preview_cmd);
3955 s->running_preview_cmd = strdup (s->desired_preview_cmd);
3956 s->running_preview_pid = forked;
3960 char *ss = subproc_pretty_name (s);
3961 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
3962 (unsigned long) forked, ss);
3969 schedule_preview_check (s);
3972 if (new_cmd) free (new_cmd);
3977 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
3980 hack_environment (state *s)
3982 static const char *def_path =
3983 # ifdef DEFAULT_PATH_PREFIX
3984 DEFAULT_PATH_PREFIX;
3989 Display *dpy = GDK_DISPLAY();
3990 const char *odpy = DisplayString (dpy);
3991 char *ndpy = (char *) malloc(strlen(odpy) + 20);
3992 strcpy (ndpy, "DISPLAY=");
3993 strcat (ndpy, odpy);
3998 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4000 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4001 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4002 So we must leak it (and/or the previous setting). Yay.
4005 if (def_path && *def_path)
4007 const char *opath = getenv("PATH");
4008 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4009 strcpy (npath, "PATH=");
4010 strcat (npath, def_path);
4011 strcat (npath, ":");
4012 strcat (npath, opath);
4016 /* do not free(npath) -- see above */
4019 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4025 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4027 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4028 necessary yet, but it will make programs work if we had invoked
4029 them with "-root" and not with "-window-id" -- which, of course,
4032 char *nssw = (char *) malloc (40);
4033 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4035 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4036 any more, right? It's not Posix, but everyone seems to have it. */
4041 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4043 /* do not free(nssw) -- see above */
4047 /* Called from a timer:
4048 Launches the currently-chosen subprocess, if it's not already running.
4049 If there's a different process running, kills it.
4052 update_subproc_timer (gpointer data)
4054 state *s = (state *) data;
4055 if (! s->desired_preview_cmd)
4056 kill_preview_subproc (s, True);
4057 else if (!s->running_preview_cmd ||
4058 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4059 launch_preview_subproc (s);
4061 s->subproc_timer_id = 0;
4062 return FALSE; /* do not re-execute timer */
4066 settings_timer (gpointer data)
4073 /* Call this when you think you might want a preview process running.
4074 It will set a timer that will actually launch that program a second
4075 from now, if you haven't changed your mind (to avoid double-click
4076 spazzing, etc.) `cmd' may be null meaning "no process".
4079 schedule_preview (state *s, const char *cmd)
4081 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4086 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4088 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4091 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4092 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4094 if (s->subproc_timer_id)
4095 gtk_timeout_remove (s->subproc_timer_id);
4096 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4100 /* Called from a timer:
4101 Checks to see if the subproc that should be running, actually is.
4104 check_subproc_timer (gpointer data)
4106 state *s = (state *) data;
4107 Bool again_p = True;
4109 if (s->running_preview_error_p || /* already dead */
4110 s->running_preview_pid <= 0)
4118 status = kill (s->running_preview_pid, 0);
4119 if (status < 0 && errno == ESRCH)
4120 s->running_preview_error_p = True;
4124 char *ss = subproc_pretty_name (s);
4125 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4126 (unsigned long) s->running_preview_pid, ss,
4127 (s->running_preview_error_p ? "dead" : "alive"));
4131 if (s->running_preview_error_p)
4133 clear_preview_window (s);
4138 /* Otherwise, it's currently alive. We might be checking again, or we
4139 might be satisfied. */
4141 if (--s->subproc_check_countdown <= 0)
4145 return TRUE; /* re-execute timer */
4148 s->subproc_check_timer_id = 0;
4149 s->subproc_check_countdown = 0;
4150 return FALSE; /* do not re-execute timer */
4155 /* Call this just after launching a subprocess.
4156 This sets a timer that will, five times a second for two seconds,
4157 check whether the program is still running. The assumption here
4158 is that if the process didn't stay up for more than a couple of
4159 seconds, then either the program doesn't exist, or it doesn't
4160 take a -window-id argument.
4163 schedule_preview_check (state *s)
4169 fprintf (stderr, "%s: scheduling check\n", blurb());
4171 if (s->subproc_check_timer_id)
4172 gtk_timeout_remove (s->subproc_check_timer_id);
4173 s->subproc_check_timer_id =
4174 gtk_timeout_add (1000 / ticks,
4175 check_subproc_timer, (gpointer) s);
4176 s->subproc_check_countdown = ticks * seconds;
4181 screen_blanked_p (void)
4185 unsigned long nitems, bytesafter;
4186 unsigned char *dataP = 0;
4187 Display *dpy = GDK_DISPLAY();
4188 Bool blanked_p = False;
4190 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4191 XA_SCREENSAVER_STATUS,
4192 0, 3, False, XA_INTEGER,
4193 &type, &format, &nitems, &bytesafter,
4196 && type == XA_INTEGER
4200 Atom *data = (Atom *) dataP;
4201 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4204 if (dataP) XFree (dataP);
4209 /* Wake up every now and then and see if the screen is blanked.
4210 If it is, kill off the small-window demo -- no point in wasting
4211 cycles by running two screensavers at once...
4214 check_blanked_timer (gpointer data)
4216 state *s = (state *) data;
4217 Bool blanked_p = screen_blanked_p ();
4218 if (blanked_p && s->running_preview_pid)
4221 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4222 kill_preview_subproc (s, True);
4225 return True; /* re-execute timer */
4229 /* How many screens are there (including Xinerama.)
4232 screen_count (Display *dpy)
4234 int nscreens = ScreenCount(dpy);
4235 # ifdef HAVE_XINERAMA
4238 int event_number, error_number;
4239 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4240 XineramaIsActive (dpy))
4242 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4243 if (xsi) XFree (xsi);
4246 # endif /* HAVE_XINERAMA */
4252 /* Setting window manager icon
4256 init_icon (GdkWindow *window)
4258 GdkBitmap *mask = 0;
4261 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4262 (gchar **) logo_50_xpm);
4264 gdk_window_set_icon (window, 0, pixmap, mask);
4268 /* The main demo-mode command loop.
4273 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4274 XrmRepresentation *type, XrmValue *value, XPointer closure)
4277 for (i = 0; quarks[i]; i++)
4279 if (bindings[i] == XrmBindTightly)
4280 fprintf (stderr, (i == 0 ? "" : "."));
4281 else if (bindings[i] == XrmBindLoosely)
4282 fprintf (stderr, "*");
4284 fprintf (stderr, " ??? ");
4285 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4288 fprintf (stderr, ": %s\n", (char *) value->addr);
4296 gnome_screensaver_window (Screen *screen)
4298 Display *dpy = DisplayOfScreen (screen);
4299 Window root = RootWindowOfScreen (screen);
4300 Window parent, *kids;
4302 Window gnome_window = 0;
4305 if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4307 for (i = 0; i < nkids; i++)
4311 unsigned long nitems, bytesafter;
4312 unsigned char *name;
4313 if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4314 False, XA_STRING, &type, &format, &nitems,
4318 && !strcmp ((char *) name, "gnome-screensaver"))
4320 gnome_window = kids[i];
4325 if (kids) XFree ((char *) kids);
4326 return gnome_window;
4330 gnome_screensaver_active_p (void)
4332 Display *dpy = GDK_DISPLAY();
4333 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4334 return (w ? True : False);
4338 kill_gnome_screensaver (void)
4340 Display *dpy = GDK_DISPLAY();
4341 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4342 if (w) XKillClient (dpy, (XID) w);
4346 kde_screensaver_active_p (void)
4348 FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4351 fgets (buf, sizeof(buf)-1, p);
4353 if (!strcmp (buf, "true\n"))
4360 kill_kde_screensaver (void)
4362 system ("dcop kdesktop KScreensaverIface enable false");
4367 the_network_is_not_the_computer (state *s)
4369 Display *dpy = GDK_DISPLAY();
4370 char *rversion = 0, *ruser = 0, *rhost = 0;
4371 char *luser, *lhost;
4373 struct passwd *p = getpwuid (getuid ());
4374 const char *d = DisplayString (dpy);
4376 # if defined(HAVE_UNAME)
4378 if (uname (&uts) < 0)
4379 lhost = "<UNKNOWN>";
4381 lhost = uts.nodename;
4383 strcpy (lhost, getenv("SYS$NODE"));
4384 # else /* !HAVE_UNAME && !VMS */
4385 strcat (lhost, "<UNKNOWN>");
4386 # endif /* !HAVE_UNAME && !VMS */
4388 if (p && p->pw_name)
4393 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4395 /* Make a buffer that's big enough for a number of copies of all the
4396 strings, plus some. */
4397 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4398 (ruser ? strlen(ruser) : 0) +
4399 (rhost ? strlen(rhost) : 0) +
4406 if (!rversion || !*rversion)
4410 "The XScreenSaver daemon doesn't seem to be running\n"
4411 "on display \"%s\". Launch it now?"),
4414 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4416 /* Warn that the two processes are running as different users.
4420 "%s is running as user \"%s\" on host \"%s\".\n"
4421 "But the xscreensaver managing display \"%s\"\n"
4422 "is running as user \"%s\" on host \"%s\".\n"
4424 "Since they are different users, they won't be reading/writing\n"
4425 "the same ~/.xscreensaver file, so %s isn't\n"
4426 "going to work right.\n"
4428 "You should either re-run %s as \"%s\", or re-run\n"
4429 "xscreensaver as \"%s\".\n"
4431 "Restart the xscreensaver daemon now?\n"),
4432 progname, luser, lhost,
4434 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4436 progname, (ruser ? ruser : "???"),
4439 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4441 /* Warn that the two processes are running on different hosts.
4445 "%s is running as user \"%s\" on host \"%s\".\n"
4446 "But the xscreensaver managing display \"%s\"\n"
4447 "is running as user \"%s\" on host \"%s\".\n"
4449 "If those two machines don't share a file system (that is,\n"
4450 "if they don't see the same ~%s/.xscreensaver file) then\n"
4451 "%s won't work right.\n"
4453 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4454 progname, luser, lhost,
4456 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4461 else if (!!strcmp (rversion, s->short_version))
4463 /* Warn that the version numbers don't match.
4467 "This is %s version %s.\n"
4468 "But the xscreensaver managing display \"%s\"\n"
4469 "is version %s. This could cause problems.\n"
4471 "Restart the xscreensaver daemon now?\n"),
4472 progname, s->short_version,
4479 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4481 if (rversion) free (rversion);
4482 if (ruser) free (ruser);
4483 if (rhost) free (rhost);
4487 /* Note: since these dialogs are not modal, they will stack up.
4488 So we do this check *after* popping up the "xscreensaver is not
4489 running" dialog so that these are on top. Good enough.
4492 if (gnome_screensaver_active_p ())
4493 warning_dialog (s->toplevel_widget,
4495 "The GNOME screensaver daemon appears to be running.\n"
4496 "It must be stopped for XScreenSaver to work properly.\n"
4498 "Stop the GNOME screen saver daemon now?\n"),
4501 if (kde_screensaver_active_p ())
4502 warning_dialog (s->toplevel_widget,
4504 "The KDE screen saver daemon appears to be running.\n"
4505 "It must be stopped for XScreenSaver to work properly.\n"
4507 "Stop the KDE screen saver daemon now?\n"),
4512 /* We use this error handler so that X errors are preceeded by the name
4513 of the program that generated them.
4516 demo_ehandler (Display *dpy, XErrorEvent *error)
4518 state *s = global_state_kludge; /* I hate C so much... */
4519 fprintf (stderr, "\nX error in %s:\n", blurb());
4520 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4521 kill_preview_subproc (s, False);
4527 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4528 of the program that generated them; and also that we can ignore one
4529 particular bogus error message that Gdk madly spews.
4532 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4533 const gchar *message, gpointer user_data)
4535 /* Ignore the message "Got event for unknown window: 0x...".
4536 Apparently some events are coming in for the xscreensaver window
4537 (presumably reply events related to the ClientMessage) and Gdk
4538 feels the need to complain about them. So, just suppress any
4539 messages that look like that one.
4541 if (strstr (message, "unknown window"))
4544 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4545 (log_domain ? log_domain : progclass),
4546 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4547 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4548 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4549 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4550 log_level == G_LOG_LEVEL_INFO ? "info" :
4551 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4553 ((!*message || message[strlen(message)-1] != '\n')
4559 __extension__ /* shut up about "string length is greater than the length
4560 ISO C89 compilers are required to support" when including
4565 static char *defaults[] = {
4566 #include "XScreenSaver_ad.h"
4571 #ifdef HAVE_CRAPPLET
4572 static struct poptOption crapplet_options[] = {
4573 {NULL, '\0', 0, NULL, 0}
4575 #endif /* HAVE_CRAPPLET */
4578 const char *usage = "[--display dpy] [--prefs | --settings]"
4579 # ifdef HAVE_CRAPPLET
4582 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4585 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4587 state *s = (state *) user_data;
4588 Boolean oi = s->initializing_p;
4590 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4592 s->initializing_p = True;
4594 eschew_gtk_lossage (label);
4596 s->initializing_p = oi;
4602 print_widget_tree (GtkWidget *w, int depth)
4605 for (i = 0; i < depth; i++)
4606 fprintf (stderr, " ");
4607 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4609 if (GTK_IS_LIST (w))
4611 for (i = 0; i < depth+1; i++)
4612 fprintf (stderr, " ");
4613 fprintf (stderr, "...list kids...\n");
4615 else if (GTK_IS_CONTAINER (w))
4617 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4620 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4628 delayed_scroll_kludge (gpointer data)
4630 state *s = (state *) data;
4631 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4632 ensure_selected_item_visible (w);
4634 /* Oh, this is just fucking lovely, too. */
4635 w = GTK_WIDGET (name_to_widget (s, "preview"));
4636 gtk_widget_hide (w);
4637 gtk_widget_show (w);
4639 return FALSE; /* do not re-execute timer */
4645 create_xscreensaver_demo (void)
4649 nb = name_to_widget (global_state_kludge, "preview_notebook");
4650 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4652 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4656 create_xscreensaver_settings_dialog (void)
4660 box = name_to_widget (global_state_kludge, "dialog_action_area");
4662 w = name_to_widget (global_state_kludge, "adv_button");
4663 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4665 w = name_to_widget (global_state_kludge, "std_button");
4666 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4668 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4671 #endif /* HAVE_GTK2 */
4674 main (int argc, char **argv)
4678 saver_preferences *p;
4679 Bool prefs_p = False;
4680 Bool settings_p = False;
4683 Widget toplevel_shell;
4684 char *real_progname = argv[0];
4687 Bool crapplet_p = False;
4691 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4692 textdomain (GETTEXT_PACKAGE);
4695 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4696 # else /* !HAVE_GTK2 */
4697 if (!setlocale (LC_ALL, ""))
4698 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4699 # endif /* !HAVE_GTK2 */
4701 #endif /* ENABLE_NLS */
4703 str = strrchr (real_progname, '/');
4704 if (str) real_progname = str+1;
4707 memset (s, 0, sizeof(*s));
4708 s->initializing_p = True;
4711 global_state_kludge = s; /* I hate C so much... */
4713 progname = real_progname;
4715 s->short_version = (char *) malloc (5);
4716 memcpy (s->short_version, screensaver_id + 17, 4);
4717 s->short_version [4] = 0;
4720 /* Register our error message logger for every ``log domain'' known.
4721 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4722 for all of the domains that seem to be in use.
4725 const char * const domains[] = { 0,
4726 "Gtk", "Gdk", "GLib", "GModule",
4727 "GThread", "Gnome", "GnomeUI" };
4728 for (i = 0; i < countof(domains); i++)
4729 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4732 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4735 const char *dir = DEFAULT_ICONDIR;
4736 if (*dir) add_pixmap_directory (dir);
4738 # endif /* !HAVE_GTK2 */
4739 #endif /* DEFAULT_ICONDIR */
4741 /* This is gross, but Gtk understands --display and not -display...
4743 for (i = 1; i < argc; i++)
4744 if (argv[i][0] && argv[i][1] &&
4745 !strncmp(argv[i], "-display", strlen(argv[i])))
4746 argv[i] = "--display";
4749 /* We need to parse this arg really early... Sigh. */
4750 for (i = 1; i < argc; i++)
4753 (!strcmp(argv[i], "--crapplet") ||
4754 !strcmp(argv[i], "--capplet")))
4756 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4759 for (j = i; j < argc; j++) /* remove it from the list */
4760 argv[j] = argv[j+1];
4762 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4763 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4765 fprintf (stderr, "%s: %s\n", real_progname, usage);
4767 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4770 (!strcmp(argv[i], "--debug") ||
4771 !strcmp(argv[i], "-debug") ||
4772 !strcmp(argv[i], "-d")))
4776 for (j = i; j < argc; j++) /* remove it from the list */
4777 argv[j] = argv[j+1];
4784 (!strcmp(argv[i], "-geometry") ||
4785 !strcmp(argv[i], "-geom") ||
4786 !strcmp(argv[i], "-geo") ||
4787 !strcmp(argv[i], "-g")))
4791 for (j = i; j < argc; j++) /* remove them from the list */
4792 argv[j] = argv[j+2];
4799 (!strcmp(argv[i], "--configdir")))
4803 hack_configuration_path = argv[i+1];
4804 for (j = i; j < argc; j++) /* remove them from the list */
4805 argv[j] = argv[j+2];
4809 if (0 != stat (hack_configuration_path, &st))
4812 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4816 else if (!S_ISDIR (st.st_mode))
4818 fprintf (stderr, "%s: not a directory: %s\n",
4819 blurb(), hack_configuration_path);
4827 fprintf (stderr, "%s: using config directory \"%s\"\n",
4828 progname, hack_configuration_path);
4831 /* Let Gtk open the X connection, then initialize Xt to use that
4832 same connection. Doctor Frankenstein would be proud.
4834 # ifdef HAVE_CRAPPLET
4837 GnomeClient *client;
4838 GnomeClientFlags flags = 0;
4840 int init_results = gnome_capplet_init ("screensaver-properties",
4842 argc, argv, NULL, 0, NULL);
4844 0 upon successful initialization;
4845 1 if --init-session-settings was passed on the cmdline;
4846 2 if --ignore was passed on the cmdline;
4849 So the 1 signifies just to init the settings, and quit, basically.
4850 (Meaning launch the xscreensaver daemon.)
4853 if (init_results < 0)
4856 g_error ("An initialization error occurred while "
4857 "starting xscreensaver-capplet.\n");
4859 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4860 real_progname, init_results);
4865 client = gnome_master_client ();
4868 flags = gnome_client_get_flags (client);
4870 if (flags & GNOME_CLIENT_IS_CONNECTED)
4873 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4874 gnome_client_get_id (client));
4877 char *session_args[20];
4879 session_args[i++] = real_progname;
4880 session_args[i++] = "--capplet";
4881 session_args[i++] = "--init-session-settings";
4882 session_args[i] = 0;
4883 gnome_client_set_priority (client, 20);
4884 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4885 gnome_client_set_restart_command (client, i, session_args);
4889 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4892 gnome_client_flush (client);
4895 if (init_results == 1)
4897 system ("xscreensaver -nosplash &");
4903 # endif /* HAVE_CRAPPLET */
4905 gtk_init (&argc, &argv);
4909 /* We must read exactly the same resources as xscreensaver.
4910 That means we must have both the same progclass *and* progname,
4911 at least as far as the resource database is concerned. So,
4912 put "xscreensaver" in argv[0] while initializing Xt.
4914 argv[0] = "xscreensaver";
4918 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4920 XtToolkitInitialize ();
4921 app = XtCreateApplicationContext ();
4922 dpy = GDK_DISPLAY();
4923 XtAppSetFallbackResources (app, defaults);
4924 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4925 toplevel_shell = XtAppCreateShell (progname, progclass,
4926 applicationShellWidgetClass,
4929 dpy = XtDisplay (toplevel_shell);
4930 db = XtDatabase (dpy);
4931 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4932 XSetErrorHandler (demo_ehandler);
4934 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4935 signal (SIGPIPE, SIG_IGN);
4937 /* After doing Xt-style command-line processing, complain about any
4938 unrecognized command-line arguments.
4940 for (i = 1; i < argc; i++)
4942 char *str = argv[i];
4943 if (str[0] == '-' && str[1] == '-')
4945 if (!strcmp (str, "-prefs"))
4947 else if (!strcmp (str, "-settings"))
4949 else if (crapplet_p)
4950 /* There are lots of random args that we don't care about when we're
4951 started as a crapplet, so just ignore unknown args in that case. */
4955 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4957 fprintf (stderr, "%s: %s\n", real_progname, usage);
4962 /* Load the init file, which may end up consulting the X resource database
4963 and the site-wide app-defaults file. Note that at this point, it's
4964 important that `progname' be "xscreensaver", rather than whatever
4968 s->nscreens = screen_count (dpy);
4970 hack_environment (s); /* must be before initialize_sort_map() */
4972 load_init_file (dpy, p);
4973 initialize_sort_map (s);
4975 /* Now that Xt has been initialized, and the resources have been read,
4976 we can set our `progname' variable to something more in line with
4979 progname = real_progname;
4983 /* Print out all the resources we read. */
4985 XrmName name = { 0 };
4986 XrmClass class = { 0 };
4988 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
4994 /* Intern the atoms that xscreensaver_command() needs.
4996 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
4997 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
4998 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
4999 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5000 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5001 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5002 XA_SELECT = XInternAtom (dpy, "SELECT", False);
5003 XA_DEMO = XInternAtom (dpy, "DEMO", False);
5004 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5005 XA_BLANK = XInternAtom (dpy, "BLANK", False);
5006 XA_LOCK = XInternAtom (dpy, "LOCK", False);
5007 XA_EXIT = XInternAtom (dpy, "EXIT", False);
5008 XA_RESTART = XInternAtom (dpy, "RESTART", False);
5011 /* Create the window and all its widgets.
5013 s->base_widget = create_xscreensaver_demo ();
5014 s->popup_widget = create_xscreensaver_settings_dialog ();
5015 s->toplevel_widget = s->base_widget;
5018 /* Set the main window's title. */
5020 char *base_title = _("Screensaver Preferences");
5021 char *v = (char *) strdup(strchr(screensaver_id, ' '));
5022 char *s1, *s2, *s3, *s4;
5023 s1 = (char *) strchr(v, ' '); s1++;
5024 s2 = (char *) strchr(s1, ' ');
5025 s3 = (char *) strchr(v, '('); s3++;
5026 s4 = (char *) strchr(s3, ')');
5030 window_title = (char *) malloc (strlen (base_title) +
5031 strlen (progclass) +
5032 strlen (s1) + strlen (s3) +
5034 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
5035 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5036 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
5040 /* Adjust the (invisible) notebooks on the popup dialog... */
5042 GtkNotebook *notebook =
5043 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5044 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5048 gtk_widget_hide (std);
5049 # else /* !HAVE_XML */
5050 /* Make the advanced page be the only one available. */
5051 gtk_widget_set_sensitive (std, False);
5052 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5053 gtk_widget_hide (std);
5054 std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5055 gtk_widget_hide (std);
5057 # endif /* !HAVE_XML */
5059 gtk_notebook_set_page (notebook, page);
5060 gtk_notebook_set_show_tabs (notebook, False);
5063 /* Various other widget initializations...
5065 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5066 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5068 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5069 GTK_SIGNAL_FUNC (wm_popup_close_cb),
5072 populate_hack_list (s);
5073 populate_prefs_page (s);
5074 sensitize_demo_widgets (s, False);
5075 fix_text_entry_sizes (s);
5076 scroll_to_current_hack (s);
5078 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5079 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5083 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5084 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5086 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5087 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5089 #endif /* !HAVE_GTK2 */
5091 /* Hook up callbacks to the items on the mode menu. */
5093 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5094 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5095 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5097 for (i = 0; kids; kids = kids->next, i++)
5099 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5100 GTK_SIGNAL_FUNC (mode_menu_item_cb),
5103 /* The "random-same" mode menu item does not appear unless
5104 there are multple screens.
5106 if (s->nscreens <= 1 &&
5107 mode_menu_order[i] == RANDOM_HACKS_SAME)
5108 gtk_widget_hide (GTK_WIDGET (kids->data));
5111 if (s->nscreens <= 1) /* recompute option-menu size */
5113 gtk_widget_unrealize (GTK_WIDGET (menu));
5114 gtk_widget_realize (GTK_WIDGET (menu));
5119 /* Handle the -prefs command-line argument. */
5122 GtkNotebook *notebook =
5123 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5124 gtk_notebook_set_page (notebook, 1);
5127 # ifdef HAVE_CRAPPLET
5131 GtkWidget *outer_vbox;
5133 gtk_widget_hide (s->toplevel_widget);
5135 capplet = capplet_widget_new ();
5137 /* Make there be a "Close" button instead of "OK" and "Cancel" */
5138 # ifdef HAVE_CRAPPLET_IMMEDIATE
5139 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5140 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5141 /* In crapplet-mode, take off the menubar. */
5142 gtk_widget_hide (name_to_widget (s, "menubar"));
5144 /* Reparent our top-level container to be a child of the capplet
5147 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5148 gtk_widget_ref (outer_vbox);
5149 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5151 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5152 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5154 /* Find the window above us, and set the title and close handler. */
5156 GtkWidget *window = capplet;
5157 while (window && !GTK_IS_WINDOW (window))
5158 window = window->parent;
5161 gtk_window_set_title (GTK_WINDOW (window), window_title);
5162 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5163 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5168 s->toplevel_widget = capplet;
5170 # endif /* HAVE_CRAPPLET */
5173 /* The Gnome folks hate the menubar. I think it's important to have access
5174 to the commands on the File menu (Restart Daemon, etc.) and to the
5175 About and Documentation commands on the Help menu.
5179 gtk_widget_hide (name_to_widget (s, "menubar"));
5183 free (window_title);
5187 /* After picking the default size, allow -geometry to override it. */
5189 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5192 gtk_widget_show (s->toplevel_widget);
5193 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
5194 fix_preview_visual (s);
5196 /* Realize page zero, so that we can diddle the scrollbar when the
5197 user tabs back to it -- otherwise, the current hack isn't scrolled
5198 to the first time they tab back there, when started with "-prefs".
5199 (Though it is if they then tab away, and back again.)
5201 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5202 #### understands this crap, explain to me how to make this work.
5204 gtk_widget_realize (name_to_widget (s, "demos_table"));
5207 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5210 /* Handle the --settings command-line argument. */
5212 gtk_timeout_add (500, settings_timer, 0);
5215 /* Issue any warnings about the running xscreensaver daemon. */
5217 the_network_is_not_the_computer (s);
5220 /* Run the Gtk event loop, and not the Xt event loop. This means that
5221 if there were Xt timers or fds registered, they would never get serviced,
5222 and if there were any Xt widgets, they would never have events delivered.
5223 Fortunately, we're using Gtk for all of the UI, and only initialized
5224 Xt so that we could process the command line and use the X resource
5227 s->initializing_p = False;
5229 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5230 after we start up. Otherwise, it always appears scrolled to the top
5231 when in crapplet-mode. */
5232 gtk_timeout_add (500, delayed_scroll_kludge, s);
5236 /* Load every configurator in turn, to scan them for errors all at once. */
5240 for (i = 0; i < p->screenhacks_count; i++)
5242 screenhack *hack = p->screenhacks[i];
5243 conf_data *d = load_configurator (hack->command, s->debug_p);
5244 if (d) free_conf_data (d);
5250 # ifdef HAVE_CRAPPLET
5252 capplet_gtk_main ();
5254 # endif /* HAVE_CRAPPLET */
5257 kill_preview_subproc (s, False);
5261 #endif /* HAVE_GTK -- whole file */