1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2013 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 #include "demo-Gtk-conf.h"
138 #endif /* HAVE_GTK2 */
140 /* Deal with deprecation of direct access to struct fields on the way to GTK3
141 See http://live.gnome.org/GnomeGoals/UseGseal
143 #if GTK_CHECK_VERSION(2,14,0)
144 # define GET_PARENT(w) gtk_widget_get_parent (w)
145 # define GET_WINDOW(w) gtk_widget_get_window (w)
146 # define GET_ACTION_AREA(d) gtk_dialog_get_action_area (d)
147 # define GET_CONTENT_AREA(d) gtk_dialog_get_content_area (d)
148 # define GET_ADJ_VALUE(a) gtk_adjustment_get_value (a)
149 # define SET_ADJ_VALUE(a,v) gtk_adjustment_set_value (a, v)
150 # define SET_ADJ_UPPER(a,v) gtk_adjustment_set_upper (a, v)
152 # define GET_PARENT(w) ((w)->parent)
153 # define GET_WINDOW(w) ((w)->window)
154 # define GET_ACTION_AREA(d) ((d)->action_area)
155 # define GET_CONTENT_AREA(d) ((d)->vbox)
156 # define GET_ADJ_VALUE(a) ((a)->value)
157 # define SET_ADJ_VALUE(a,v) (a)->value = v
158 # define SET_ADJ_UPPER(a,v) (a)->upper = v
161 #if GTK_CHECK_VERSION(2,18,0)
162 # define SET_CAN_DEFAULT(w) gtk_widget_set_can_default ((w), TRUE)
163 # define GET_SENSITIVE(w) gtk_widget_get_sensitive (w)
165 # define SET_CAN_DEFAULT(w) GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_DEFAULT)
166 # define GET_SENSITIVE(w) GTK_WIDGET_IS_SENSITIVE (w)
169 #if GTK_CHECK_VERSION(2,20,0)
170 # define GET_REALIZED(w) gtk_widget_get_realized (w)
172 # define GET_REALIZED(w) GTK_WIDGET_REALIZED (w)
176 extern void exec_command (const char *shell, const char *command, int nice);
177 extern int on_path_p (const char *program);
179 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
182 #define countof(x) (sizeof((x))/sizeof((*x)))
185 /* You might think that to read an array of 32-bit quantities out of a
186 server-side property, you would pass an array of 32-bit data quantities
187 into XGetWindowProperty(). You would be wrong. You have to use an array
188 of longs, even if long is 64 bits (using 32 of each 64.)
193 char *progclass = "XScreenSaver";
196 /* The order of the items in the mode menu. */
197 static int mode_menu_order[] = {
198 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
203 char *short_version; /* version number of this xscreensaver build */
205 GtkWidget *toplevel_widget; /* the main window */
206 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
207 GtkWidget *popup_widget; /* the "Settings" dialog */
208 conf_data *cdata; /* private data for per-hack configuration */
211 GladeXML *glade_ui; /* Glade UI file */
212 #endif /* HAVE_GTK2 */
214 Bool debug_p; /* whether to print diagnostics */
215 Bool initializing_p; /* flag for breaking recursion loops */
216 Bool saving_p; /* flag for breaking recursion loops */
218 char *desired_preview_cmd; /* subprocess we intend to run */
219 char *running_preview_cmd; /* subprocess we are currently running */
220 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
221 Bool running_preview_error_p; /* whether the pid died abnormally */
223 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
224 int subproc_timer_id; /* timer to delay subproc launch */
225 int subproc_check_timer_id; /* timer to check whether it started up */
226 int subproc_check_countdown; /* how many more checks left */
228 int *list_elt_to_hack_number; /* table for sorting the hack list */
229 int *hack_number_to_list_elt; /* the inverse table */
230 Bool *hacks_available_p; /* whether hacks are on $PATH */
231 int total_available; /* how many are on $PATH */
232 int list_count; /* how many items are in the list: this may be
233 less than p->screenhacks_count, if some are
236 int _selected_list_element; /* don't use this: call
237 selected_list_element() instead */
239 int nscreens; /* How many X or Xinerama screens there are */
241 saver_preferences prefs;
246 /* Total fucking evilness due to the fact that it's rocket science to get
247 a closure object of our own down into the various widget callbacks. */
248 static state *global_state_kludge;
251 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
252 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
253 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
256 static void populate_demo_window (state *, int list_elt);
257 static void populate_prefs_page (state *);
258 static void populate_popup_window (state *);
260 static Bool flush_dialog_changes_and_save (state *);
261 static Bool flush_popup_changes_and_save (state *);
263 static int maybe_reload_init_file (state *);
264 static void await_xscreensaver (state *);
265 static Bool xscreensaver_running_p (state *);
266 static void sensitize_menu_items (state *s, Bool force_p);
267 static void force_dialog_repaint (state *s);
269 static void schedule_preview (state *, const char *cmd);
270 static void kill_preview_subproc (state *, Bool reset_p);
271 static void schedule_preview_check (state *);
274 /* Prototypes of functions used by the Glade-generated code,
277 void exit_menu_cb (GtkMenuItem *, gpointer user_data);
278 void about_menu_cb (GtkMenuItem *, gpointer user_data);
279 void doc_menu_cb (GtkMenuItem *, gpointer user_data);
280 void file_menu_cb (GtkMenuItem *, gpointer user_data);
281 void activate_menu_cb (GtkMenuItem *, gpointer user_data);
282 void lock_menu_cb (GtkMenuItem *, gpointer user_data);
283 void kill_menu_cb (GtkMenuItem *, gpointer user_data);
284 void restart_menu_cb (GtkWidget *, gpointer user_data);
285 void run_this_cb (GtkButton *, gpointer user_data);
286 void manual_cb (GtkButton *, gpointer user_data);
287 void run_next_cb (GtkButton *, gpointer user_data);
288 void run_prev_cb (GtkButton *, gpointer user_data);
289 void pref_changed_cb (GtkWidget *, gpointer user_data);
290 gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
291 void mode_menu_item_cb (GtkWidget *, gpointer user_data);
292 void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
293 gint page_num, gpointer user_data);
294 void browse_image_dir_cb (GtkButton *, gpointer user_data);
295 void browse_text_file_cb (GtkButton *, gpointer user_data);
296 void browse_text_program_cb (GtkButton *, gpointer user_data);
297 void settings_cb (GtkButton *, gpointer user_data);
298 void settings_adv_cb (GtkButton *, gpointer user_data);
299 void settings_std_cb (GtkButton *, gpointer user_data);
300 void settings_reset_cb (GtkButton *, gpointer user_data);
301 void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
302 gint page_num, gpointer user_data);
303 void settings_cancel_cb (GtkButton *, gpointer user_data);
304 void settings_ok_cb (GtkButton *, gpointer user_data);
306 static void kill_gnome_screensaver (void);
307 static void kill_kde_screensaver (void);
310 /* Some random utility functions
313 const char *blurb (void);
318 time_t now = time ((time_t *) 0);
319 char *ct = (char *) ctime (&now);
320 static char buf[255];
321 int n = strlen(progname);
323 strncpy(buf, progname, n);
326 strncpy(buf+n, ct+11, 8);
327 strcpy(buf+n+9, ": ");
333 name_to_widget (state *s, const char *name)
343 /* First try to load the Glade file from the current directory;
344 if there isn't one there, check the installed directory.
346 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
347 const char * const files[] = { GLADE_FILE_NAME,
348 GLADE_DIR "/" GLADE_FILE_NAME };
350 for (i = 0; i < countof (files); i++)
353 if (!stat (files[i], &st))
355 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
362 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
363 "\tfrom " GLADE_DIR "/ or current directory.\n",
367 # undef GLADE_FILE_NAME
369 glade_xml_signal_autoconnect (s->glade_ui);
372 w = glade_xml_get_widget (s->glade_ui, name);
374 #else /* !HAVE_GTK2 */
376 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
379 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
381 #endif /* HAVE_GTK2 */
384 fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
390 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
391 Takes a scroller, viewport, or list as an argument.
394 ensure_selected_item_visible (GtkWidget *widget)
398 GtkTreeSelection *selection;
402 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
403 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
404 path = gtk_tree_path_new_first ();
406 path = gtk_tree_model_get_path (model, &iter);
408 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
410 gtk_tree_path_free (path);
412 #else /* !HAVE_GTK2 */
414 GtkScrolledWindow *scroller = 0;
416 GtkList *list_widget = 0;
420 GtkWidget *selected = 0;
423 gint parent_h, child_y, child_h, children_h, ignore;
424 double ratio_t, ratio_b;
426 if (GTK_IS_SCROLLED_WINDOW (widget))
428 scroller = GTK_SCROLLED_WINDOW (widget);
429 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
430 list_widget = GTK_LIST (GTK_BIN(vp)->child);
432 else if (GTK_IS_VIEWPORT (widget))
434 vp = GTK_VIEWPORT (widget);
435 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
436 list_widget = GTK_LIST (GTK_BIN(vp)->child);
438 else if (GTK_IS_LIST (widget))
440 list_widget = GTK_LIST (widget);
441 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
442 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
447 slist = list_widget->selection;
448 selected = (slist ? GTK_WIDGET (slist->data) : 0);
452 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
454 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
455 kids; kids = kids->next)
458 adj = gtk_scrolled_window_get_vadjustment (scroller);
460 gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (vp)),
461 &ignore, &ignore, &ignore, &parent_h, &ignore);
462 gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (selected)),
463 &ignore, &child_y, &ignore, &child_h, &ignore);
464 children_h = nkids * child_h;
466 ratio_t = ((double) child_y) / ((double) children_h);
467 ratio_b = ((double) child_y + child_h) / ((double) children_h);
469 if (adj->upper == 0.0) /* no items in list */
472 if (ratio_t < (adj->value / adj->upper) ||
473 ratio_b > ((adj->value + adj->page_size) / adj->upper))
476 int slop = parent_h * 0.75; /* how much to overshoot by */
478 if (ratio_t < (adj->value / adj->upper))
480 double ratio_w = ((double) parent_h) / ((double) children_h);
481 double ratio_l = (ratio_b - ratio_t);
482 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
485 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
487 target = ratio_t * adj->upper;
491 if (target > adj->upper - adj->page_size)
492 target = adj->upper - adj->page_size;
496 gtk_adjustment_set_value (adj, target);
498 #endif /* !HAVE_GTK2 */
502 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
504 GtkWidget *shell = GTK_WIDGET (user_data);
505 while (GET_PARENT (shell))
506 shell = GET_PARENT (shell);
507 gtk_widget_destroy (GTK_WIDGET (shell));
511 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
513 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
515 restart_menu_cb (widget, user_data);
516 warning_dialog_dismiss_cb (widget, user_data);
519 static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data)
521 kill_gnome_screensaver ();
522 warning_dialog_dismiss_cb (widget, user_data);
525 static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data)
527 kill_kde_screensaver ();
528 warning_dialog_dismiss_cb (widget, user_data);
531 typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
534 warning_dialog (GtkWidget *parent, const char *message,
535 dialog_button button_type, int center)
537 char *msg = strdup (message);
540 GtkWidget *dialog = gtk_dialog_new ();
541 GtkWidget *label = 0;
543 GtkWidget *cancel = 0;
546 while (parent && !GET_WINDOW (parent))
547 parent = GET_PARENT (parent);
550 !GET_WINDOW (parent)) /* too early to pop up transient dialogs */
552 fprintf (stderr, "%s: too early for dialog?\n", progname);
561 char *s = strchr (head, '\n');
564 sprintf (name, "label%d", i++);
567 label = gtk_label_new (head);
569 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
570 #endif /* HAVE_GTK2 */
575 GTK_WIDGET (label)->style =
576 gtk_style_copy (GTK_WIDGET (label)->style);
577 GTK_WIDGET (label)->style->font =
578 gdk_font_load (get_string_resource("warning_dialog.headingFont",
580 gtk_widget_set_style (GTK_WIDGET (label),
581 GTK_WIDGET (label)->style);
583 #endif /* !HAVE_GTK2 */
585 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
586 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
587 label, TRUE, TRUE, 0);
588 gtk_widget_show (label);
599 label = gtk_label_new ("");
600 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
601 label, TRUE, TRUE, 0);
602 gtk_widget_show (label);
604 label = gtk_hbutton_box_new ();
605 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
606 label, TRUE, TRUE, 0);
609 if (button_type != D_NONE)
611 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
612 gtk_container_add (GTK_CONTAINER (label), cancel);
615 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
616 gtk_container_add (GTK_CONTAINER (label), ok);
618 #else /* !HAVE_GTK2 */
620 ok = gtk_button_new_with_label ("OK");
621 gtk_container_add (GTK_CONTAINER (label), ok);
623 if (button_type != D_NONE)
625 cancel = gtk_button_new_with_label ("Cancel");
626 gtk_container_add (GTK_CONTAINER (label), cancel);
629 #endif /* !HAVE_GTK2 */
631 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
632 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
633 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
634 SET_CAN_DEFAULT (ok);
635 gtk_widget_show (ok);
636 gtk_widget_grab_focus (ok);
640 SET_CAN_DEFAULT (cancel);
641 gtk_widget_show (cancel);
643 gtk_widget_show (label);
644 gtk_widget_show (dialog);
646 if (button_type != D_NONE)
649 switch (button_type) {
650 case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break;
651 case D_GNOME: fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb); break;
652 case D_KDE: fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb); break;
653 default: abort(); break;
655 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn,
657 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
658 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
663 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
664 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
668 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
669 GET_WINDOW (GTK_WIDGET (parent)));
672 gtk_window_present (GTK_WINDOW (dialog));
673 #else /* !HAVE_GTK2 */
674 gdk_window_show (GTK_WIDGET (dialog)->window);
675 gdk_window_raise (GTK_WIDGET (dialog)->window);
676 #endif /* !HAVE_GTK2 */
684 run_cmd (state *s, Atom command, int arg)
689 flush_dialog_changes_and_save (s);
690 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
692 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
693 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
700 sprintf (buf, "Error:\n\n%s", err);
702 strcpy (buf, "Unknown error!");
703 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
707 sensitize_menu_items (s, True);
708 force_dialog_repaint (s);
713 run_hack (state *s, int list_elt, Bool report_errors_p)
719 if (list_elt < 0) return;
720 hack_number = s->list_elt_to_hack_number[list_elt];
722 flush_dialog_changes_and_save (s);
723 schedule_preview (s, 0);
725 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
728 if (status < 0 && report_errors_p)
730 if (xscreensaver_running_p (s))
732 /* Kludge: ignore the spurious "window unexpectedly deleted"
734 if (err && strstr (err, "unexpectedly deleted"))
741 sprintf (buf, "Error:\n\n%s", err);
743 strcpy (buf, "Unknown error!");
744 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
749 /* The error is that the daemon isn't running;
752 const char *d = DisplayString (GDK_DISPLAY());
756 "The XScreenSaver daemon doesn't seem to be running\n"
757 "on display \"%s\". Launch it now?"),
759 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
765 sensitize_menu_items (s, False);
772 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
773 libglade work on Cygwin; apparently all Glade callbacks need this magic
774 extra declaration. I do not pretend to understand.
778 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
780 state *s = global_state_kludge; /* I hate C so much... */
781 flush_dialog_changes_and_save (s);
782 kill_preview_subproc (s, False);
787 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
789 state *s = (state *) data;
790 flush_dialog_changes_and_save (s);
797 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
800 char *vers = strdup (screensaver_id + 4);
804 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
806 s = strchr (vers, ',');
811 s2 = strrchr (vers, '-');
813 strncpy (year, s2, 4);
816 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
817 non-ASCII characters aren't allowed in localizable string keys."
818 (I don't want to just use (c) instead of © because that doesn't
819 look as good in the plain-old default Latin1 "C" locale.)
822 sprintf(copy, ("Copyright \xC2\xA9 1991-%s %s"), year, s);
823 #else /* !HAVE_GTK2 */
824 sprintf(copy, ("Copyright \251 1991-%s %s"), year, s);
825 #endif /* !HAVE_GTK2 */
827 sprintf (msg, "%s\n\n%s", copy, desc);
829 /* I can't make gnome_about_new() work here -- it starts dying in
830 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
831 then this might be the thing to do:
835 const gchar *auth[] = { 0 };
836 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
838 gtk_widget_show (about);
840 #else / * GTK but not GNOME * /
844 GdkColormap *colormap;
845 GdkPixmap *gdkpixmap;
848 GtkWidget *dialog = gtk_dialog_new ();
849 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
850 GtkWidget *parent = GTK_WIDGET (menuitem);
851 while (GET_PARENT (parent))
852 parent = GET_PARENT (parent);
854 hbox = gtk_hbox_new (FALSE, 20);
855 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
856 hbox, TRUE, TRUE, 0);
858 colormap = gtk_widget_get_colormap (parent);
860 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
861 (gchar **) logo_180_xpm);
862 icon = gtk_pixmap_new (gdkpixmap, mask);
863 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
865 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
867 vbox = gtk_vbox_new (FALSE, 0);
868 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
870 label1 = gtk_label_new (vers);
871 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
872 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
873 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
876 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
877 GTK_WIDGET (label1)->style->font =
878 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
879 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
880 #endif /* HAVE_GTK2 */
882 label2 = gtk_label_new (msg);
883 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
884 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
885 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
888 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
889 GTK_WIDGET (label2)->style->font =
890 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
891 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
892 #endif /* HAVE_GTK2 */
894 hb = gtk_hbutton_box_new ();
896 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
900 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
901 #else /* !HAVE_GTK2 */
902 ok = gtk_button_new_with_label (_("OK"));
903 #endif /* !HAVE_GTK2 */
904 gtk_container_add (GTK_CONTAINER (hb), ok);
906 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
907 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
908 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
910 gtk_widget_show (hbox);
911 gtk_widget_show (icon);
912 gtk_widget_show (vbox);
913 gtk_widget_show (label1);
914 gtk_widget_show (label2);
915 gtk_widget_show (hb);
916 gtk_widget_show (ok);
917 gtk_widget_show (dialog);
919 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
920 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
922 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
923 GET_WINDOW (GTK_WIDGET (parent)));
924 gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog)));
925 gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog)));
931 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
933 state *s = global_state_kludge; /* I hate C so much... */
934 saver_preferences *p = &s->prefs;
937 if (!p->help_url || !*p->help_url)
939 warning_dialog (s->toplevel_widget,
941 "No Help URL has been specified.\n"), D_NONE, 100);
945 help_command = (char *) malloc (strlen (p->load_url_command) +
946 (strlen (p->help_url) * 4) + 20);
947 strcpy (help_command, "( ");
948 sprintf (help_command + strlen(help_command),
950 p->help_url, p->help_url, p->help_url, p->help_url);
951 strcat (help_command, " ) &");
952 if (system (help_command) < 0)
953 fprintf (stderr, "%s: fork error\n", blurb());
959 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
961 state *s = global_state_kludge; /* I hate C so much... */
962 sensitize_menu_items (s, False);
967 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
969 state *s = global_state_kludge; /* I hate C so much... */
970 run_cmd (s, XA_ACTIVATE, 0);
975 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
977 state *s = global_state_kludge; /* I hate C so much... */
978 run_cmd (s, XA_LOCK, 0);
983 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
985 state *s = global_state_kludge; /* I hate C so much... */
986 run_cmd (s, XA_EXIT, 0);
991 restart_menu_cb (GtkWidget *widget, gpointer user_data)
993 state *s = global_state_kludge; /* I hate C so much... */
994 flush_dialog_changes_and_save (s);
995 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
997 if (system ("xscreensaver -nosplash &") < 0)
998 fprintf (stderr, "%s: fork error\n", blurb());
1000 await_xscreensaver (s);
1004 xscreensaver_running_p (state *s)
1006 Display *dpy = GDK_DISPLAY();
1008 server_xscreensaver_version (dpy, &rversion, 0, 0);
1016 await_xscreensaver (state *s)
1021 while (!ok && (--countdown > 0))
1022 if (xscreensaver_running_p (s))
1025 sleep (1); /* If it's not there yet, wait a second... */
1027 sensitize_menu_items (s, True);
1031 /* Timed out, no screensaver running. */
1034 Bool root_p = (geteuid () == 0);
1038 "The xscreensaver daemon did not start up properly.\n"
1044 __extension__ /* don't warn about "string length is greater than
1045 the length ISO C89 compilers are required to
1046 support" in the following expression... */
1049 _("You are running as root. This usually means that xscreensaver\n"
1050 "was unable to contact your X server because access control is\n"
1051 "turned on. Try running this command:\n"
1053 " xhost +localhost\n"
1055 "and then selecting `File / Restart Daemon'.\n"
1057 "Note that turning off access control will allow anyone logged\n"
1058 "on to this machine to access your screen, which might be\n"
1059 "considered a security problem. Please read the xscreensaver\n"
1060 "manual and FAQ for more information.\n"
1062 "You shouldn't run X as root. Instead, you should log in as a\n"
1063 "normal user, and `su' as necessary."));
1065 strcat (buf, _("Please check your $PATH and permissions."));
1067 warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1070 force_dialog_repaint (s);
1075 selected_list_element (state *s)
1077 return s->_selected_list_element;
1082 demo_write_init_file (state *s, saver_preferences *p)
1084 Display *dpy = GDK_DISPLAY();
1087 /* #### try to figure out why shit keeps getting reordered... */
1088 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1092 if (!write_init_file (dpy, p, s->short_version, False))
1095 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1100 const char *f = init_file_name();
1102 warning_dialog (s->toplevel_widget,
1103 _("Error:\n\nCouldn't determine init file name!\n"),
1107 char *b = (char *) malloc (strlen(f) + 1024);
1108 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1109 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1117 G_MODULE_EXPORT void
1118 run_this_cb (GtkButton *button, gpointer user_data)
1120 state *s = global_state_kludge; /* I hate C so much... */
1121 int list_elt = selected_list_element (s);
1122 if (list_elt < 0) return;
1123 if (!flush_dialog_changes_and_save (s))
1124 run_hack (s, list_elt, True);
1128 G_MODULE_EXPORT void
1129 manual_cb (GtkButton *button, gpointer user_data)
1131 Display *dpy = GDK_DISPLAY();
1132 state *s = global_state_kludge; /* I hate C so much... */
1133 saver_preferences *p = &s->prefs;
1134 GtkWidget *list_widget = name_to_widget (s, "list");
1135 int list_elt = selected_list_element (s);
1137 char *name, *name2, *cmd, *str;
1139 if (list_elt < 0) return;
1140 hack_number = s->list_elt_to_hack_number[list_elt];
1142 flush_dialog_changes_and_save (s);
1143 ensure_selected_item_visible (list_widget);
1145 name = strdup (p->screenhacks[hack_number]->command);
1148 while (isspace (*name2)) name2++;
1150 while (*str && !isspace (*str)) str++;
1152 str = strrchr (name2, '/');
1153 if (str) name2 = str+1;
1155 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1158 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1159 strcpy (cmd2, "( ");
1160 sprintf (cmd2 + strlen (cmd2),
1162 name2, name2, name2, name2);
1163 strcat (cmd2, " ) &");
1164 if (system (cmd2) < 0)
1165 fprintf (stderr, "%s: fork error\n", blurb());
1170 warning_dialog (GTK_WIDGET (button),
1171 _("Error:\n\nno `manualCommand' resource set."),
1180 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1182 GtkWidget *parent = name_to_widget (s, "scroller");
1183 gboolean was = GET_SENSITIVE (parent);
1186 GtkTreeModel *model;
1187 GtkTreeSelection *selection;
1188 #endif /* HAVE_GTK2 */
1190 if (!was) gtk_widget_set_sensitive (parent, True);
1192 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1194 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1196 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1197 gtk_tree_selection_select_iter (selection, &iter);
1199 #else /* !HAVE_GTK2 */
1200 gtk_list_select_item (GTK_LIST (list), list_elt);
1201 #endif /* !HAVE_GTK2 */
1202 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1203 if (!was) gtk_widget_set_sensitive (parent, False);
1207 G_MODULE_EXPORT void
1208 run_next_cb (GtkButton *button, gpointer user_data)
1210 state *s = global_state_kludge; /* I hate C so much... */
1211 /* saver_preferences *p = &s->prefs; */
1212 Bool ops = s->preview_suppressed_p;
1214 GtkWidget *list_widget = name_to_widget (s, "list");
1215 int list_elt = selected_list_element (s);
1222 if (list_elt >= s->list_count)
1225 s->preview_suppressed_p = True;
1227 flush_dialog_changes_and_save (s);
1228 force_list_select_item (s, list_widget, list_elt, True);
1229 populate_demo_window (s, list_elt);
1230 run_hack (s, list_elt, False);
1232 s->preview_suppressed_p = ops;
1236 G_MODULE_EXPORT void
1237 run_prev_cb (GtkButton *button, gpointer user_data)
1239 state *s = global_state_kludge; /* I hate C so much... */
1240 /* saver_preferences *p = &s->prefs; */
1241 Bool ops = s->preview_suppressed_p;
1243 GtkWidget *list_widget = name_to_widget (s, "list");
1244 int list_elt = selected_list_element (s);
1247 list_elt = s->list_count - 1;
1252 list_elt = s->list_count - 1;
1254 s->preview_suppressed_p = True;
1256 flush_dialog_changes_and_save (s);
1257 force_list_select_item (s, list_widget, list_elt, True);
1258 populate_demo_window (s, list_elt);
1259 run_hack (s, list_elt, False);
1261 s->preview_suppressed_p = ops;
1265 /* Writes the given settings into prefs.
1266 Returns true if there was a change, False otherwise.
1267 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1270 flush_changes (state *s,
1273 const char *command,
1276 saver_preferences *p = &s->prefs;
1277 Bool changed = False;
1280 if (list_elt < 0 || list_elt >= s->list_count)
1283 hack_number = s->list_elt_to_hack_number[list_elt];
1284 hack = p->screenhacks[hack_number];
1286 if (enabled_p != -1 &&
1287 enabled_p != hack->enabled_p)
1289 hack->enabled_p = enabled_p;
1292 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1293 blurb(), hack->name, enabled_p);
1298 if (!hack->command || !!strcmp (command, hack->command))
1300 if (hack->command) free (hack->command);
1301 hack->command = strdup (command);
1304 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1305 blurb(), hack->name, command);
1311 const char *ov = hack->visual;
1312 if (!ov || !*ov) ov = "any";
1313 if (!*visual) visual = "any";
1314 if (!!strcasecmp (visual, ov))
1316 if (hack->visual) free (hack->visual);
1317 hack->visual = strdup (visual);
1320 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1321 blurb(), hack->name, visual);
1329 /* Helper for the text fields that contain time specifications:
1330 this parses the text, and does error checking.
1333 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1338 if (!sec_p || strchr (line, ':'))
1339 value = parse_time ((char *) line, sec_p, True);
1343 if (sscanf (line, "%d%c", &value, &c) != 1)
1349 value *= 1000; /* Time measures in microseconds */
1355 "Unparsable time format: \"%s\"\n"),
1357 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1366 directory_p (const char *path)
1369 if (!path || !*path)
1371 else if (stat (path, &st))
1373 else if (!S_ISDIR (st.st_mode))
1380 file_p (const char *path)
1383 if (!path || !*path)
1385 else if (stat (path, &st))
1387 else if (S_ISDIR (st.st_mode))
1394 normalize_directory (const char *path)
1398 if (!path || !*path) return 0;
1400 p2 = (char *) malloc (L + 2);
1402 if (p2[L-1] == '/') /* remove trailing slash */
1405 for (s = p2; s && *s; s++)
1408 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1409 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1412 while (s0 > p2 && s0[-1] != '/')
1418 /* strcpy (s0, s); */
1419 memmove(s0, s, strlen(s) + 1);
1423 else if (*s == '/' && !strncmp (s, "/./", 3)) { /* delete "/./" */
1424 /* strcpy (s, s+2), s--; */
1425 memmove(s, s+2, strlen(s+2) + 1);
1428 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1433 Normalize consecutive slashes.
1434 Ignore doubled slashes after ":" to avoid mangling URLs.
1437 for (s = p2; s && *s; s++){
1438 if (*s == ':') continue;
1439 if (!s[1] || !s[2]) continue;
1440 while (s[1] == '/' && s[2] == '/')
1441 /* strcpy (s+1, s+2); */
1442 memmove (s+1, s+2, strlen(s+2) + 1);
1445 /* and strip trailing whitespace for good measure. */
1447 while (isspace(p2[L-1]))
1460 } FlushForeachClosure;
1463 flush_checkbox (GtkTreeModel *model,
1468 FlushForeachClosure *closure = data;
1471 gtk_tree_model_get (model, iter,
1472 COL_ENABLED, &checked,
1475 if (flush_changes (closure->s, closure->i,
1477 *closure->changed = True;
1481 /* don't remove row */
1485 #endif /* HAVE_GTK2 */
1487 /* Flush out any changes made in the main dialog window (where changes
1488 take place immediately: clicking on a checkbox causes the init file
1489 to be written right away.)
1492 flush_dialog_changes_and_save (state *s)
1494 saver_preferences *p = &s->prefs;
1495 saver_preferences P2, *p2 = &P2;
1497 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1498 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1499 FlushForeachClosure closure;
1500 #else /* !HAVE_GTK2 */
1501 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1502 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1504 #endif /* !HAVE_GTK2 */
1505 static Bool already_warned_about_missing_image_directory = False; /* very long name... */
1507 Bool changed = False;
1510 if (s->saving_p) return False;
1515 /* Flush any checkbox changes in the list down into the prefs struct.
1519 closure.changed = &changed;
1521 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1523 #else /* !HAVE_GTK2 */
1525 for (i = 0; kids; kids = kids->next, i++)
1527 GtkWidget *line = GTK_WIDGET (kids->data);
1528 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1529 GtkWidget *line_check =
1530 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1532 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1534 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1537 #endif /* ~HAVE_GTK2 */
1539 /* Flush the non-hack-specific settings down into the prefs struct.
1542 # define SECONDS(FIELD,NAME) \
1543 w = name_to_widget (s, (NAME)); \
1544 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1546 # define MINUTES(FIELD,NAME) \
1547 w = name_to_widget (s, (NAME)); \
1548 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1550 # define CHECKBOX(FIELD,NAME) \
1551 w = name_to_widget (s, (NAME)); \
1552 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1554 # define PATHNAME(FIELD,NAME) \
1555 w = name_to_widget (s, (NAME)); \
1556 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1558 # define TEXT(FIELD,NAME) \
1559 w = name_to_widget (s, (NAME)); \
1560 (FIELD) = (char *) g_strdup(gtk_entry_get_text (GTK_ENTRY (w)))
1562 MINUTES (&p2->timeout, "timeout_spinbutton");
1563 MINUTES (&p2->cycle, "cycle_spinbutton");
1564 CHECKBOX (p2->lock_p, "lock_button");
1565 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1567 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1568 CHECKBOX (p2->dpms_quickoff_p, "dpms_quickoff_button");
1569 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1570 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1571 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1573 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1574 CHECKBOX (p2->grab_video_p, "grab_video_button");
1575 CHECKBOX (p2->random_image_p, "grab_image_button");
1576 PATHNAME (p2->image_directory, "image_text");
1579 CHECKBOX (p2->verbose_p, "verbose_button");
1580 CHECKBOX (p2->capture_stderr_p, "capture_button");
1581 CHECKBOX (p2->splash_p, "splash_button");
1586 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1587 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1588 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1589 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1590 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1591 TEXT (p2->text_literal, "text_entry");
1592 PATHNAME (p2->text_file, "text_file_entry");
1593 PATHNAME (p2->text_program, "text_program_entry");
1594 PATHNAME (p2->text_program, "text_program_entry");
1595 TEXT (p2->text_url, "text_url_entry");
1598 CHECKBOX (p2->install_cmap_p, "install_button");
1599 CHECKBOX (p2->fade_p, "fade_button");
1600 CHECKBOX (p2->unfade_p, "unfade_button");
1601 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1609 /* Warn if the image directory doesn't exist, when:
1610 - not being warned before
1611 - image directory is changed and the directory doesn't exist
1612 - image directory does not begin with http://
1614 if (p2->image_directory &&
1615 *p2->image_directory &&
1616 !directory_p (p2->image_directory) &&
1617 strncmp(p2->image_directory, "http://", 6) &&
1618 ( !already_warned_about_missing_image_directory ||
1619 ( p->image_directory &&
1620 *p->image_directory &&
1621 strcmp(p->image_directory, p2->image_directory)
1627 sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n",
1628 p2->image_directory);
1629 if (warning_dialog (s->toplevel_widget, b, D_NONE, 100))
1630 already_warned_about_missing_image_directory = True;
1634 /* Map the mode menu to `saver_mode' enum values. */
1636 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1637 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1638 GtkWidget *selected = gtk_menu_get_active (menu);
1639 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1640 int menu_elt = g_list_index (kids, (gpointer) selected);
1641 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1642 p2->mode = mode_menu_order[menu_elt];
1645 if (p2->mode == ONE_HACK)
1647 int list_elt = selected_list_element (s);
1648 p2->selected_hack = (list_elt >= 0
1649 ? s->list_elt_to_hack_number[list_elt]
1653 # define COPY(field, name) \
1654 if (p->field != p2->field) { \
1657 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1659 p->field = p2->field
1662 COPY(selected_hack, "selected_hack");
1664 COPY(timeout, "timeout");
1665 COPY(cycle, "cycle");
1666 COPY(lock_p, "lock_p");
1667 COPY(lock_timeout, "lock_timeout");
1669 COPY(dpms_enabled_p, "dpms_enabled_p");
1670 COPY(dpms_quickoff_p, "dpms_quickoff_enabled_p");
1671 COPY(dpms_standby, "dpms_standby");
1672 COPY(dpms_suspend, "dpms_suspend");
1673 COPY(dpms_off, "dpms_off");
1676 COPY(verbose_p, "verbose_p");
1677 COPY(capture_stderr_p, "capture_stderr_p");
1678 COPY(splash_p, "splash_p");
1681 COPY(tmode, "tmode");
1683 COPY(install_cmap_p, "install_cmap_p");
1684 COPY(fade_p, "fade_p");
1685 COPY(unfade_p, "unfade_p");
1686 COPY(fade_seconds, "fade_seconds");
1688 COPY(grab_desktop_p, "grab_desktop_p");
1689 COPY(grab_video_p, "grab_video_p");
1690 COPY(random_image_p, "random_image_p");
1694 # define COPYSTR(FIELD,NAME) \
1697 strcmp(p->FIELD, p2->FIELD)) \
1701 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1703 if (p->FIELD && p->FIELD != p2->FIELD) \
1705 p->FIELD = p2->FIELD; \
1708 COPYSTR(image_directory, "image_directory");
1709 COPYSTR(text_literal, "text_literal");
1710 COPYSTR(text_file, "text_file");
1711 COPYSTR(text_program, "text_program");
1712 COPYSTR(text_url, "text_url");
1715 populate_prefs_page (s);
1719 Display *dpy = GDK_DISPLAY();
1720 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1721 sync_server_dpms_settings (dpy, enabled_p, p->dpms_quickoff_p,
1722 p->dpms_standby / 1000,
1723 p->dpms_suspend / 1000,
1727 changed = demo_write_init_file (s, p);
1730 s->saving_p = False;
1735 /* Flush out any changes made in the popup dialog box (where changes
1736 take place only when the OK button is clicked.)
1739 flush_popup_changes_and_save (state *s)
1741 Bool changed = False;
1742 saver_preferences *p = &s->prefs;
1743 int list_elt = selected_list_element (s);
1745 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1746 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1748 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1749 const char *command = gtk_entry_get_text (cmd);
1754 if (s->saving_p) return False;
1760 if (maybe_reload_init_file (s) != 0)
1766 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1768 if (!strcasecmp (visual, "")) visual = "";
1769 else if (!strcasecmp (visual, "any")) visual = "";
1770 else if (!strcasecmp (visual, "default")) visual = "Default";
1771 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1772 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1773 else if (!strcasecmp (visual, "best")) visual = "Best";
1774 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1775 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1776 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1777 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1778 else if (!strcasecmp (visual, "color")) visual = "Color";
1779 else if (!strcasecmp (visual, "gl")) visual = "GL";
1780 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1781 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1782 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1783 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1784 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1785 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1786 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1787 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1788 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1791 gdk_beep (); /* unparsable */
1793 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1796 changed = flush_changes (s, list_elt, -1, command, visual);
1799 changed = demo_write_init_file (s, p);
1801 /* Do this to re-launch the hack if (and only if) the command line
1803 populate_demo_window (s, selected_list_element (s));
1807 s->saving_p = False;
1812 G_MODULE_EXPORT void
1813 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1815 state *s = global_state_kludge; /* I hate C so much... */
1816 if (! s->initializing_p)
1818 s->initializing_p = True;
1819 flush_dialog_changes_and_save (s);
1820 s->initializing_p = False;
1824 G_MODULE_EXPORT gboolean
1825 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1827 pref_changed_cb (widget, user_data);
1831 /* Callback on menu items in the "mode" options menu.
1833 G_MODULE_EXPORT void
1834 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1836 state *s = (state *) user_data;
1837 saver_preferences *p = &s->prefs;
1838 GtkWidget *list = name_to_widget (s, "list");
1842 gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
1844 saver_mode new_mode;
1848 if (menu_items->data == widget)
1851 menu_items = menu_items->next;
1853 if (!menu_items) abort();
1855 new_mode = mode_menu_order[menu_index];
1857 /* Keep the same list element displayed as before; except if we're
1858 switching *to* "one screensaver" mode from any other mode, set
1859 "the one" to be that which is currently selected.
1861 list_elt = selected_list_element (s);
1862 if (new_mode == ONE_HACK)
1863 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1866 saver_mode old_mode = p->mode;
1868 populate_demo_window (s, list_elt);
1869 force_list_select_item (s, list, list_elt, True);
1870 p->mode = old_mode; /* put it back, so the init file gets written */
1873 pref_changed_cb (widget, user_data);
1877 G_MODULE_EXPORT void
1878 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1879 gint page_num, gpointer user_data)
1881 state *s = global_state_kludge; /* I hate C so much... */
1882 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1884 /* If we're switching to page 0, schedule the current hack to be run.
1885 Otherwise, schedule it to stop. */
1887 populate_demo_window (s, selected_list_element (s));
1889 schedule_preview (s, 0);
1894 list_activated_cb (GtkTreeView *list,
1896 GtkTreeViewColumn *column,
1903 g_return_if_fail (!gdk_pointer_is_grabbed ());
1905 str = gtk_tree_path_to_string (path);
1906 list_elt = strtol (str, NULL, 10);
1910 run_hack (s, list_elt, True);
1914 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1916 state *s = (state *)data;
1917 GtkTreeModel *model;
1923 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1926 path = gtk_tree_model_get_path (model, &iter);
1927 str = gtk_tree_path_to_string (path);
1928 list_elt = strtol (str, NULL, 10);
1930 gtk_tree_path_free (path);
1933 populate_demo_window (s, list_elt);
1934 flush_dialog_changes_and_save (s);
1936 /* Re-populate the Settings window any time a new item is selected
1937 in the list, in case both windows are currently visible.
1939 populate_popup_window (s);
1942 #else /* !HAVE_GTK2 */
1944 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1945 list_select_cb that comes in
1946 *after* we've double-clicked.
1950 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1953 state *s = (state *) data;
1954 if (event->type == GDK_2BUTTON_PRESS)
1956 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1957 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1959 last_doubleclick_time = time ((time_t *) 0);
1962 run_hack (s, list_elt, True);
1970 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1972 state *s = (state *) data;
1973 time_t now = time ((time_t *) 0);
1975 if (now >= last_doubleclick_time + 2)
1977 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1978 populate_demo_window (s, list_elt);
1979 flush_dialog_changes_and_save (s);
1984 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1986 state *s = (state *) data;
1987 populate_demo_window (s, -1);
1988 flush_dialog_changes_and_save (s);
1991 #endif /* !HAVE_GTK2 */
1994 /* Called when the checkboxes that are in the left column of the
1995 scrolling list are clicked. This both populates the right pane
1996 (just as clicking on the label (really, listitem) does) and
1997 also syncs this checkbox with the right pane Enabled checkbox.
2002 GtkCellRendererToggle *toggle,
2004 #else /* !HAVE_GTK2 */
2006 #endif /* !HAVE_GTK2 */
2009 state *s = (state *) data;
2012 GtkScrolledWindow *scroller =
2013 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
2014 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2015 GtkTreeModel *model = gtk_tree_view_get_model (list);
2016 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
2019 #else /* !HAVE_GTK2 */
2020 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
2021 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
2023 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
2024 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
2025 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
2026 #endif /* !HAVE_GTK2 */
2033 if (!gtk_tree_model_get_iter (model, &iter, path))
2035 g_warning ("bad path: %s", path_string);
2038 gtk_tree_path_free (path);
2040 gtk_tree_model_get (model, &iter,
2041 COL_ENABLED, &active,
2044 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2045 COL_ENABLED, !active,
2048 list_elt = strtol (path_string, NULL, 10);
2049 #else /* !HAVE_GTK2 */
2050 list_elt = gtk_list_child_position (list, line);
2051 #endif /* !HAVE_GTK2 */
2053 /* remember previous scroll position of the top of the list */
2054 adj = gtk_scrolled_window_get_vadjustment (scroller);
2055 scroll_top = GET_ADJ_VALUE (adj);
2057 flush_dialog_changes_and_save (s);
2058 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
2059 populate_demo_window (s, list_elt);
2061 /* restore the previous scroll position of the top of the list.
2062 this is weak, but I don't really know why it's moving... */
2063 gtk_adjustment_set_value (adj, scroll_top);
2069 GtkFileSelection *widget;
2070 } file_selection_data;
2075 store_image_directory (GtkWidget *button, gpointer user_data)
2077 file_selection_data *fsd = (file_selection_data *) user_data;
2078 state *s = fsd->state;
2079 GtkFileSelection *selector = fsd->widget;
2080 GtkWidget *top = s->toplevel_widget;
2081 saver_preferences *p = &s->prefs;
2082 const char *path = gtk_file_selection_get_filename (selector);
2084 if (p->image_directory && !strcmp(p->image_directory, path))
2085 return; /* no change */
2087 /* No warning for URLs. */
2088 if ((!directory_p (path)) && strncmp(path, "http://", 6))
2091 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2092 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2096 if (p->image_directory) free (p->image_directory);
2097 p->image_directory = normalize_directory (path);
2099 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2100 (p->image_directory ? p->image_directory : ""));
2101 demo_write_init_file (s, p);
2106 store_text_file (GtkWidget *button, gpointer user_data)
2108 file_selection_data *fsd = (file_selection_data *) user_data;
2109 state *s = fsd->state;
2110 GtkFileSelection *selector = fsd->widget;
2111 GtkWidget *top = s->toplevel_widget;
2112 saver_preferences *p = &s->prefs;
2113 const char *path = gtk_file_selection_get_filename (selector);
2115 if (p->text_file && !strcmp(p->text_file, path))
2116 return; /* no change */
2121 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2122 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2126 if (p->text_file) free (p->text_file);
2127 p->text_file = normalize_directory (path);
2129 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2130 (p->text_file ? p->text_file : ""));
2131 demo_write_init_file (s, p);
2136 store_text_program (GtkWidget *button, gpointer user_data)
2138 file_selection_data *fsd = (file_selection_data *) user_data;
2139 state *s = fsd->state;
2140 GtkFileSelection *selector = fsd->widget;
2141 /*GtkWidget *top = s->toplevel_widget;*/
2142 saver_preferences *p = &s->prefs;
2143 const char *path = gtk_file_selection_get_filename (selector);
2145 if (p->text_program && !strcmp(p->text_program, path))
2146 return; /* no change */
2152 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2153 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2158 if (p->text_program) free (p->text_program);
2159 p->text_program = normalize_directory (path);
2161 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2162 (p->text_program ? p->text_program : ""));
2163 demo_write_init_file (s, p);
2169 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2171 file_selection_data *fsd = (file_selection_data *) user_data;
2172 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2176 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2178 browse_image_dir_cancel (button, user_data);
2179 store_image_directory (button, user_data);
2183 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2185 browse_image_dir_cancel (button, user_data);
2186 store_text_file (button, user_data);
2190 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2192 browse_image_dir_cancel (button, user_data);
2193 store_text_program (button, user_data);
2197 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2199 browse_image_dir_cancel (widget, user_data);
2203 G_MODULE_EXPORT void
2204 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2206 state *s = global_state_kludge; /* I hate C so much... */
2207 saver_preferences *p = &s->prefs;
2208 static file_selection_data *fsd = 0;
2210 GtkFileSelection *selector = GTK_FILE_SELECTION(
2211 gtk_file_selection_new ("Please select the image directory."));
2214 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2216 fsd->widget = selector;
2219 if (p->image_directory && *p->image_directory)
2220 gtk_file_selection_set_filename (selector, p->image_directory);
2222 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2223 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2225 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2226 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2228 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2229 GTK_SIGNAL_FUNC (browse_image_dir_close),
2232 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2234 gtk_window_set_modal (GTK_WINDOW (selector), True);
2235 gtk_widget_show (GTK_WIDGET (selector));
2239 G_MODULE_EXPORT void
2240 browse_text_file_cb (GtkButton *button, gpointer user_data)
2242 state *s = global_state_kludge; /* I hate C so much... */
2243 saver_preferences *p = &s->prefs;
2244 static file_selection_data *fsd = 0;
2246 GtkFileSelection *selector = GTK_FILE_SELECTION(
2247 gtk_file_selection_new ("Please select a text file."));
2250 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2252 fsd->widget = selector;
2255 if (p->text_file && *p->text_file)
2256 gtk_file_selection_set_filename (selector, p->text_file);
2258 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2259 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2261 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2262 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2264 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2265 GTK_SIGNAL_FUNC (browse_image_dir_close),
2268 gtk_window_set_modal (GTK_WINDOW (selector), True);
2269 gtk_widget_show (GTK_WIDGET (selector));
2273 G_MODULE_EXPORT void
2274 browse_text_program_cb (GtkButton *button, gpointer user_data)
2276 state *s = global_state_kludge; /* I hate C so much... */
2277 saver_preferences *p = &s->prefs;
2278 static file_selection_data *fsd = 0;
2280 GtkFileSelection *selector = GTK_FILE_SELECTION(
2281 gtk_file_selection_new ("Please select a text-generating program."));
2284 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2286 fsd->widget = selector;
2289 if (p->text_program && *p->text_program)
2290 gtk_file_selection_set_filename (selector, p->text_program);
2292 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2293 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2295 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2296 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2298 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2299 GTK_SIGNAL_FUNC (browse_image_dir_close),
2302 gtk_window_set_modal (GTK_WINDOW (selector), True);
2303 gtk_widget_show (GTK_WIDGET (selector));
2310 G_MODULE_EXPORT void
2311 settings_cb (GtkButton *button, gpointer user_data)
2313 state *s = global_state_kludge; /* I hate C so much... */
2314 int list_elt = selected_list_element (s);
2316 populate_demo_window (s, list_elt); /* reset the widget */
2317 populate_popup_window (s); /* create UI on popup window */
2318 gtk_widget_show (s->popup_widget);
2322 settings_sync_cmd_text (state *s)
2325 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2326 char *cmd_line = get_configurator_command_line (s->cdata, False);
2327 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2328 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2330 # endif /* HAVE_XML */
2333 G_MODULE_EXPORT void
2334 settings_adv_cb (GtkButton *button, gpointer user_data)
2336 state *s = global_state_kludge; /* I hate C so much... */
2337 GtkNotebook *notebook =
2338 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2340 settings_sync_cmd_text (s);
2341 gtk_notebook_set_page (notebook, 1);
2344 G_MODULE_EXPORT void
2345 settings_std_cb (GtkButton *button, gpointer user_data)
2347 state *s = global_state_kludge; /* I hate C so much... */
2348 GtkNotebook *notebook =
2349 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2351 /* Re-create UI to reflect the in-progress command-line settings. */
2352 populate_popup_window (s);
2354 gtk_notebook_set_page (notebook, 0);
2357 G_MODULE_EXPORT void
2358 settings_reset_cb (GtkButton *button, gpointer user_data)
2361 state *s = global_state_kludge; /* I hate C so much... */
2362 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2363 char *cmd_line = get_configurator_command_line (s->cdata, True);
2364 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2365 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2367 populate_popup_window (s);
2368 # endif /* HAVE_XML */
2371 G_MODULE_EXPORT void
2372 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2373 gint page_num, gpointer user_data)
2375 state *s = global_state_kludge; /* I hate C so much... */
2376 GtkWidget *adv = name_to_widget (s, "adv_button");
2377 GtkWidget *std = name_to_widget (s, "std_button");
2381 gtk_widget_show (adv);
2382 gtk_widget_hide (std);
2384 else if (page_num == 1)
2386 gtk_widget_hide (adv);
2387 gtk_widget_show (std);
2395 G_MODULE_EXPORT void
2396 settings_cancel_cb (GtkButton *button, gpointer user_data)
2398 state *s = global_state_kludge; /* I hate C so much... */
2399 gtk_widget_hide (s->popup_widget);
2402 G_MODULE_EXPORT void
2403 settings_ok_cb (GtkButton *button, gpointer user_data)
2405 state *s = global_state_kludge; /* I hate C so much... */
2406 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2407 int page = gtk_notebook_get_current_page (notebook);
2410 /* Regenerate the command-line from the widget contents before saving.
2411 But don't do this if we're looking at the command-line page already,
2412 or we will blow away what they typed... */
2413 settings_sync_cmd_text (s);
2415 flush_popup_changes_and_save (s);
2416 gtk_widget_hide (s->popup_widget);
2420 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2422 state *s = (state *) data;
2423 settings_cancel_cb (0, (gpointer) s);
2429 /* Populating the various widgets
2433 /* Returns the number of the last hack run by the server.
2436 server_current_hack (void)
2440 unsigned long nitems, bytesafter;
2441 unsigned char *dataP = 0;
2442 Display *dpy = GDK_DISPLAY();
2443 int hack_number = -1;
2445 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2446 XA_SCREENSAVER_STATUS,
2447 0, 3, False, XA_INTEGER,
2448 &type, &format, &nitems, &bytesafter,
2451 && type == XA_INTEGER
2455 PROP32 *data = (PROP32 *) dataP;
2456 hack_number = (int) data[2] - 1;
2459 if (dataP) XFree (dataP);
2465 /* Finds the number of the last hack that was run, and makes that item be
2466 selected by default.
2469 scroll_to_current_hack (state *s)
2471 saver_preferences *p = &s->prefs;
2472 int hack_number = -1;
2474 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2475 hack_number = p->selected_hack;
2476 if (hack_number < 0) /* otherwise, use the last-run */
2477 hack_number = server_current_hack ();
2478 if (hack_number < 0) /* failing that, last "one mode" */
2479 hack_number = p->selected_hack;
2480 if (hack_number < 0) /* failing that, newest hack. */
2482 /* We should only get here if the user does not have a .xscreensaver
2483 file, and the screen has not been blanked with a hack since X
2484 started up: in other words, this is probably a fresh install.
2486 Instead of just defaulting to hack #0 (in either "programs" or
2487 "alphabetical" order) let's try to default to the last runnable
2488 hack in the "programs" list: this is probably the hack that was
2489 most recently added to the xscreensaver distribution (and so
2490 it's probably the currently-coolest one!)
2492 hack_number = p->screenhacks_count-1;
2493 while (hack_number > 0 &&
2494 ! (s->hacks_available_p[hack_number] &&
2495 p->screenhacks[hack_number]->enabled_p))
2499 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2501 int list_elt = s->hack_number_to_list_elt[hack_number];
2502 GtkWidget *list = name_to_widget (s, "list");
2503 force_list_select_item (s, list, list_elt, True);
2504 populate_demo_window (s, list_elt);
2510 populate_hack_list (state *s)
2512 Display *dpy = GDK_DISPLAY();
2514 saver_preferences *p = &s->prefs;
2515 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2516 GtkListStore *model;
2517 GtkTreeSelection *selection;
2518 GtkCellRenderer *ren;
2522 g_object_get (G_OBJECT (list),
2527 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2528 g_object_set (G_OBJECT (list), "model", model, NULL);
2529 g_object_unref (model);
2531 ren = gtk_cell_renderer_toggle_new ();
2532 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2534 "active", COL_ENABLED,
2537 g_signal_connect (ren, "toggled",
2538 G_CALLBACK (list_checkbox_cb),
2541 ren = gtk_cell_renderer_text_new ();
2542 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2543 _("Screen Saver"), ren,
2547 g_signal_connect_after (list, "row_activated",
2548 G_CALLBACK (list_activated_cb),
2551 selection = gtk_tree_view_get_selection (list);
2552 g_signal_connect (selection, "changed",
2553 G_CALLBACK (list_select_changed_cb),
2558 for (i = 0; i < s->list_count; i++)
2560 int hack_number = s->list_elt_to_hack_number[i];
2561 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2563 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2565 if (!hack) continue;
2567 /* If we're to suppress uninstalled hacks, check $PATH now. */
2568 if (p->ignore_uninstalled_p && !available_p)
2571 pretty_name = (hack->name
2572 ? strdup (hack->name)
2573 : make_hack_name (dpy, hack->command));
2577 /* Make the text foreground be the color of insensitive widgets
2578 (but don't actually make it be insensitive, since we still
2579 want to be able to click on it.)
2581 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
2582 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2583 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2584 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2586 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2587 /* " background=\"#%02X%02X%02X\"" */
2589 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2590 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2596 gtk_list_store_append (model, &iter);
2597 gtk_list_store_set (model, &iter,
2598 COL_ENABLED, hack->enabled_p,
2599 COL_NAME, pretty_name,
2604 #else /* !HAVE_GTK2 */
2606 saver_preferences *p = &s->prefs;
2607 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2609 for (i = 0; i < s->list_count; i++)
2611 int hack_number = s->list_elt_to_hack_number[i];
2612 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2614 /* A GtkList must contain only GtkListItems, but those can contain
2615 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2616 and a Label. We handle single and double click events on the
2617 line itself, for clicking on the text, but the interior checkbox
2618 also handles its own events.
2621 GtkWidget *line_hbox;
2622 GtkWidget *line_check;
2623 GtkWidget *line_label;
2625 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2627 if (!hack) continue;
2629 /* If we're to suppress uninstalled hacks, check $PATH now. */
2630 if (p->ignore_uninstalled_p && !available_p)
2633 pretty_name = (hack->name
2634 ? strdup (hack->name)
2635 : make_hack_name (hack->command));
2637 line = gtk_list_item_new ();
2638 line_hbox = gtk_hbox_new (FALSE, 0);
2639 line_check = gtk_check_button_new ();
2640 line_label = gtk_label_new (pretty_name);
2642 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2643 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2644 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2646 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2648 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2650 gtk_widget_show (line_check);
2651 gtk_widget_show (line_label);
2652 gtk_widget_show (line_hbox);
2653 gtk_widget_show (line);
2657 gtk_container_add (GTK_CONTAINER (list), line);
2658 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2659 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2662 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2663 GTK_SIGNAL_FUNC (list_checkbox_cb),
2666 gtk_widget_show (line);
2670 /* Make the widget be colored like insensitive widgets
2671 (but don't actually make it be insensitive, since we
2672 still want to be able to click on it.)
2674 GtkRcStyle *rc_style;
2677 gtk_widget_realize (GTK_WIDGET (line_label));
2679 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2680 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2682 rc_style = gtk_rc_style_new ();
2683 rc_style->fg[GTK_STATE_NORMAL] = fg;
2684 rc_style->bg[GTK_STATE_NORMAL] = bg;
2685 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2687 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2688 gtk_rc_style_unref (rc_style);
2692 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2693 GTK_SIGNAL_FUNC (list_select_cb),
2695 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2696 GTK_SIGNAL_FUNC (list_unselect_cb),
2698 #endif /* !HAVE_GTK2 */
2702 update_list_sensitivity (state *s)
2704 saver_preferences *p = &s->prefs;
2705 Bool sensitive = (p->mode == RANDOM_HACKS ||
2706 p->mode == RANDOM_HACKS_SAME ||
2707 p->mode == ONE_HACK);
2708 Bool checkable = (p->mode == RANDOM_HACKS ||
2709 p->mode == RANDOM_HACKS_SAME);
2710 Bool blankable = (p->mode != DONT_BLANK);
2713 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2714 GtkWidget *use = name_to_widget (s, "use_col_frame");
2715 #endif /* HAVE_GTK2 */
2716 GtkWidget *scroller = name_to_widget (s, "scroller");
2717 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2718 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2721 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2722 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2723 #else /* !HAVE_GTK2 */
2724 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2725 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2727 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2728 #endif /* !HAVE_GTK2 */
2729 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2730 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2732 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2735 gtk_tree_view_column_set_visible (use, checkable);
2736 #else /* !HAVE_GTK2 */
2738 gtk_widget_show (use); /* the "Use" column header */
2740 gtk_widget_hide (use);
2744 GtkBin *line = GTK_BIN (kids->data);
2745 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2746 GtkWidget *line_check =
2747 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2750 gtk_widget_show (line_check);
2752 gtk_widget_hide (line_check);
2756 #endif /* !HAVE_GTK2 */
2761 populate_prefs_page (state *s)
2763 saver_preferences *p = &s->prefs;
2765 Bool can_lock_p = True;
2767 /* Disable all the "lock" controls if locking support was not provided
2768 at compile-time, or if running on MacOS. */
2769 # if defined(NO_LOCKING) || defined(__APPLE__)
2774 /* If there is only one screen, the mode menu contains
2775 "random" but not "random-same".
2777 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2778 p->mode = RANDOM_HACKS;
2781 /* The file supports timeouts of less than a minute, but the GUI does
2782 not, so throttle the values to be at least one minute (since "0" is
2783 a bad rounding choice...)
2785 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2788 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2791 # define FMT_MINUTES(NAME,N) \
2792 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2794 # define FMT_SECONDS(NAME,N) \
2795 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2797 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2798 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2799 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2800 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2801 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2802 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2803 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2808 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2809 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2812 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2814 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2815 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2816 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2818 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2819 TOGGLE_ACTIVE ("dpms_quickoff_button", p->dpms_quickoff_p);
2820 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2821 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2822 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2823 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2824 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2825 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2829 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2830 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2831 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2832 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2833 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2836 # undef TOGGLE_ACTIVE
2838 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2839 (p->image_directory ? p->image_directory : ""));
2840 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2842 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2845 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2846 (p->text_literal ? p->text_literal : ""));
2847 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2848 (p->text_file ? p->text_file : ""));
2849 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2850 (p->text_program ? p->text_program : ""));
2851 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2852 (p->text_url ? p->text_url : ""));
2854 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2855 p->tmode == TEXT_LITERAL);
2856 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2857 p->tmode == TEXT_FILE);
2858 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2859 p->tmode == TEXT_FILE);
2860 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2861 p->tmode == TEXT_PROGRAM);
2862 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2863 p->tmode == TEXT_PROGRAM);
2864 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2865 p->tmode == TEXT_URL);
2868 /* Map the `saver_mode' enum to mode menu to values. */
2870 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2873 for (i = 0; i < countof(mode_menu_order); i++)
2874 if (mode_menu_order[i] == p->mode)
2876 gtk_option_menu_set_history (opt, i);
2877 update_list_sensitivity (s);
2881 Bool found_any_writable_cells = False;
2882 Bool fading_possible = False;
2883 Bool dpms_supported = False;
2885 Display *dpy = GDK_DISPLAY();
2886 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2888 for (i = 0; i < nscreens; i++)
2890 Screen *s = ScreenOfDisplay (dpy, i);
2891 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2893 found_any_writable_cells = True;
2898 fading_possible = found_any_writable_cells;
2899 #ifdef HAVE_XF86VMODE_GAMMA
2900 fading_possible = True;
2903 #ifdef HAVE_DPMS_EXTENSION
2905 int op = 0, event = 0, error = 0;
2906 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2907 dpms_supported = True;
2909 #endif /* HAVE_DPMS_EXTENSION */
2912 # define SENSITIZE(NAME,SENSITIVEP) \
2913 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2915 /* Blanking and Locking
2917 SENSITIZE ("lock_button", can_lock_p);
2918 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2919 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2924 SENSITIZE ("dpms_frame", dpms_supported);
2925 SENSITIZE ("dpms_button", dpms_supported);
2926 SENSITIZE ("dpms_quickoff_button", dpms_supported);
2928 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2929 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2930 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2931 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2932 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2933 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2934 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2935 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2936 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2940 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2941 SENSITIZE ("install_button", found_any_writable_cells);
2942 SENSITIZE ("fade_button", fading_possible);
2943 SENSITIZE ("unfade_button", fading_possible);
2945 SENSITIZE ("fade_label", (fading_possible &&
2946 (p->fade_p || p->unfade_p)));
2947 SENSITIZE ("fade_spinbutton", (fading_possible &&
2948 (p->fade_p || p->unfade_p)));
2956 populate_popup_window (state *s)
2958 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2959 char *doc_string = 0;
2961 /* #### not in Gtk 1.2
2962 gtk_label_set_selectable (doc);
2968 free_conf_data (s->cdata);
2973 saver_preferences *p = &s->prefs;
2974 int list_elt = selected_list_element (s);
2975 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2976 ? s->list_elt_to_hack_number[list_elt]
2978 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2981 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2982 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2983 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2984 s->cdata = load_configurator (cmd_line, s->debug_p);
2985 if (s->cdata && s->cdata->widget)
2986 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2991 doc_string = (s->cdata
2992 ? s->cdata->description
2994 # else /* !HAVE_XML */
2995 doc_string = _("Descriptions not available: no XML support compiled in.");
2996 # endif /* !HAVE_XML */
2998 gtk_label_set_text (doc, (doc_string
3000 : _("No description available.")));
3005 sensitize_demo_widgets (state *s, Bool sensitive_p)
3007 const char *names[] = { "demo", "settings",
3008 "cmd_label", "cmd_text", "manual",
3009 "visual", "visual_combo" };
3011 for (i = 0; i < countof(names); i++)
3013 GtkWidget *w = name_to_widget (s, names[i]);
3014 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
3020 sensitize_menu_items (state *s, Bool force_p)
3022 static Bool running_p = False;
3023 static time_t last_checked = 0;
3024 time_t now = time ((time_t *) 0);
3025 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
3029 if (force_p || now > last_checked + 10) /* check every 10 seconds */
3031 running_p = xscreensaver_running_p (s);
3032 last_checked = time ((time_t *) 0);
3035 for (i = 0; i < countof(names); i++)
3037 GtkWidget *w = name_to_widget (s, names[i]);
3038 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
3043 /* When the File menu is de-posted after a "Restart Daemon" command,
3044 the window underneath doesn't repaint for some reason. I guess this
3045 is a bug in exposure handling in GTK or GDK. This works around it.
3048 force_dialog_repaint (state *s)
3051 /* Tell GDK to invalidate and repaint the whole window.
3053 GdkWindow *w = GET_WINDOW (s->toplevel_widget);
3054 GdkRegion *region = gdk_region_new ();
3056 rect.x = rect.y = 0;
3057 rect.width = rect.height = 32767;
3058 gdk_region_union_with_rect (region, &rect);
3059 gdk_window_invalidate_region (w, region, True);
3060 gdk_region_destroy (region);
3061 gdk_window_process_updates (w, True);
3063 /* Force the server to send an exposure event by creating and then
3064 destroying a window as a child of the top level shell.
3066 Display *dpy = GDK_DISPLAY();
3067 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
3069 XWindowAttributes xgwa;
3070 XGetWindowAttributes (dpy, parent, &xgwa);
3071 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
3072 XMapRaised (dpy, w);
3073 XDestroyWindow (dpy, w);
3079 /* Even though we've given these text fields a maximum number of characters,
3080 their default size is still about 30 characters wide -- so measure out
3081 a string in their font, and resize them to just fit that.
3084 fix_text_entry_sizes (state *s)
3088 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
3089 const char * const spinbuttons[] = {
3090 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3091 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3092 "dpms_off_spinbutton",
3093 "-fade_spinbutton" };
3097 for (i = 0; i < countof(spinbuttons); i++)
3099 const char *n = spinbuttons[i];
3101 while (*n == '-') n++, cols--;
3102 w = GTK_WIDGET (name_to_widget (s, n));
3103 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3104 gtk_widget_set_usize (w, width, -2);
3107 /* Now fix the width of the combo box.
3109 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3110 w = GTK_COMBO (w)->entry;
3111 width = gdk_string_width (w->style->font, "PseudoColor___");
3112 gtk_widget_set_usize (w, width, -2);
3114 /* Now fix the width of the file entry text.
3116 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3117 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3118 gtk_widget_set_usize (w, width, -2);
3120 /* Now fix the width of the command line text.
3122 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3123 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3124 gtk_widget_set_usize (w, width, -2);
3128 /* Now fix the height of the list widget:
3129 make it default to being around 10 text-lines high instead of 4.
3131 w = GTK_WIDGET (name_to_widget (s, "list"));
3135 int leading = 3; /* approximate is ok... */
3139 PangoFontMetrics *pain =
3140 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3141 gtk_widget_get_style (w)->font_desc,
3142 gtk_get_default_language ());
3143 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3144 pango_font_metrics_get_descent (pain));
3145 #else /* !HAVE_GTK2 */
3146 height = w->style->font->ascent + w->style->font->descent;
3147 #endif /* !HAVE_GTK2 */
3151 height += border * 2;
3152 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3153 gtk_widget_set_usize (w, -2, height);
3160 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3163 static char *up_arrow_xpm[] = {
3186 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3187 the end of the array (Gtk 1.2.5.) */
3188 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3189 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3192 static char *down_arrow_xpm[] = {
3215 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3216 the end of the array (Gtk 1.2.5.) */
3217 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3218 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3222 pixmapify_button (state *s, int down_p)
3226 GtkWidget *pixmapwid;
3230 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3231 style = gtk_widget_get_style (w);
3233 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3234 &style->bg[GTK_STATE_NORMAL],
3236 ? (gchar **) down_arrow_xpm
3237 : (gchar **) up_arrow_xpm));
3238 pixmapwid = gtk_pixmap_new (pixmap, mask);
3239 gtk_widget_show (pixmapwid);
3240 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3241 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3245 map_next_button_cb (GtkWidget *w, gpointer user_data)
3247 state *s = (state *) user_data;
3248 pixmapify_button (s, 1);
3252 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3254 state *s = (state *) user_data;
3255 pixmapify_button (s, 0);
3257 #endif /* !HAVE_GTK2 */
3261 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3265 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3266 GtkAllocation *allocation,
3270 GtkWidgetAuxInfo *aux_info;
3272 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3274 aux_info->width = allocation->width;
3275 aux_info->height = -2;
3279 gtk_widget_size_request (label, &req);
3282 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3285 eschew_gtk_lossage (GtkLabel *label)
3287 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3288 aux_info->width = GTK_WIDGET (label)->allocation.width;
3289 aux_info->height = -2;
3293 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3295 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3296 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3299 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3301 gtk_widget_queue_resize (GTK_WIDGET (label));
3303 #endif /* !HAVE_GTK2 */
3307 populate_demo_window (state *s, int list_elt)
3309 Display *dpy = GDK_DISPLAY();
3310 saver_preferences *p = &s->prefs;
3313 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3314 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3315 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3316 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3317 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3319 if (p->mode == BLANK_ONLY)
3322 pretty_name = strdup (_("Blank Screen"));
3323 schedule_preview (s, 0);
3325 else if (p->mode == DONT_BLANK)
3328 pretty_name = strdup (_("Screen Saver Disabled"));
3329 schedule_preview (s, 0);
3333 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3334 ? s->list_elt_to_hack_number[list_elt]
3336 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3340 ? strdup (hack->name)
3341 : make_hack_name (dpy, hack->command))
3345 schedule_preview (s, hack->command);
3347 schedule_preview (s, 0);
3351 pretty_name = strdup (_("Preview"));
3353 gtk_frame_set_label (frame1, _(pretty_name));
3354 gtk_frame_set_label (frame2, _(pretty_name));
3356 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3357 gtk_entry_set_position (cmd, 0);
3361 sprintf (title, _("%s: %.100s Settings"),
3362 progclass, (pretty_name ? pretty_name : "???"));
3363 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3366 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3368 ? (hack->visual && *hack->visual
3373 sensitize_demo_widgets (s, (hack ? True : False));
3375 if (pretty_name) free (pretty_name);
3377 ensure_selected_item_visible (list);
3379 s->_selected_list_element = list_elt;
3384 widget_deleter (GtkWidget *widget, gpointer data)
3386 /* #### Well, I want to destroy these widgets, but if I do that, they get
3387 referenced again, and eventually I get a SEGV. So instead of
3388 destroying them, I'll just hide them, and leak a bunch of memory
3389 every time the disk file changes. Go go go Gtk!
3391 #### Ok, that's a lie, I get a crash even if I just hide the widget
3392 and don't ever delete it. Fuck!
3395 gtk_widget_destroy (widget);
3397 gtk_widget_hide (widget);
3402 static char **sort_hack_cmp_names_kludge;
3404 sort_hack_cmp (const void *a, const void *b)
3410 int aa = *(int *) a;
3411 int bb = *(int *) b;
3412 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3413 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3414 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3420 initialize_sort_map (state *s)
3422 Display *dpy = GDK_DISPLAY();
3423 saver_preferences *p = &s->prefs;
3426 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3427 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3428 if (s->hacks_available_p) free (s->hacks_available_p);
3430 s->list_elt_to_hack_number = (int *)
3431 calloc (sizeof(int), p->screenhacks_count + 1);
3432 s->hack_number_to_list_elt = (int *)
3433 calloc (sizeof(int), p->screenhacks_count + 1);
3434 s->hacks_available_p = (Bool *)
3435 calloc (sizeof(Bool), p->screenhacks_count + 1);
3436 s->total_available = 0;
3438 /* Check which hacks actually exist on $PATH
3440 for (i = 0; i < p->screenhacks_count; i++)
3442 screenhack *hack = p->screenhacks[i];
3443 int on = on_path_p (hack->command) ? 1 : 0;
3444 s->hacks_available_p[i] = on;
3445 s->total_available += on;
3448 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3452 for (i = 0; i < p->screenhacks_count; i++)
3454 if (!p->ignore_uninstalled_p ||
3455 s->hacks_available_p[i])
3456 s->list_elt_to_hack_number[j++] = i;
3460 for (; j < p->screenhacks_count; j++)
3461 s->list_elt_to_hack_number[j] = -1;
3464 /* Generate list of sortable names (once)
3466 sort_hack_cmp_names_kludge = (char **)
3467 calloc (sizeof(char *), p->screenhacks_count);
3468 for (i = 0; i < p->screenhacks_count; i++)
3470 screenhack *hack = p->screenhacks[i];
3471 char *name = (hack->name && *hack->name
3472 ? strdup (hack->name)
3473 : make_hack_name (dpy, hack->command));
3475 for (str = name; *str; str++)
3476 *str = tolower(*str);
3477 sort_hack_cmp_names_kludge[i] = name;
3480 /* Sort list->hack map alphabetically
3482 qsort (s->list_elt_to_hack_number,
3483 p->screenhacks_count,
3484 sizeof(*s->list_elt_to_hack_number),
3489 for (i = 0; i < p->screenhacks_count; i++)
3490 free (sort_hack_cmp_names_kludge[i]);
3491 free (sort_hack_cmp_names_kludge);
3492 sort_hack_cmp_names_kludge = 0;
3494 /* Build inverse table */
3495 for (i = 0; i < p->screenhacks_count; i++)
3497 int n = s->list_elt_to_hack_number[i];
3499 s->hack_number_to_list_elt[n] = i;
3505 maybe_reload_init_file (state *s)
3507 Display *dpy = GDK_DISPLAY();
3508 saver_preferences *p = &s->prefs;
3511 static Bool reentrant_lock = False;
3512 if (reentrant_lock) return 0;
3513 reentrant_lock = True;
3515 if (init_file_changed_p (p))
3517 const char *f = init_file_name();
3522 if (!f || !*f) return 0;
3523 b = (char *) malloc (strlen(f) + 1024);
3526 "file \"%s\" has changed, reloading.\n"),
3528 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3531 load_init_file (dpy, p);
3532 initialize_sort_map (s);
3534 list_elt = selected_list_element (s);
3535 list = name_to_widget (s, "list");
3536 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3537 populate_hack_list (s);
3538 force_list_select_item (s, list, list_elt, True);
3539 populate_prefs_page (s);
3540 populate_demo_window (s, list_elt);
3541 ensure_selected_item_visible (list);
3546 reentrant_lock = False;
3552 /* Making the preview window have the right X visual (so that GL works.)
3555 static Visual *get_best_gl_visual (state *);
3558 x_visual_to_gdk_visual (Visual *xv)
3560 GList *gvs = gdk_list_visuals();
3561 if (!xv) return gdk_visual_get_system();
3562 for (; gvs; gvs = gvs->next)
3564 GdkVisual *gv = (GdkVisual *) gvs->data;
3565 if (xv == GDK_VISUAL_XVISUAL (gv))
3568 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3569 blurb(), (unsigned long) xv->visualid);
3574 clear_preview_window (state *s)
3580 if (!s->toplevel_widget) return; /* very early */
3581 p = name_to_widget (s, "preview");
3582 window = GET_WINDOW (p);
3584 if (!window) return;
3586 /* Flush the widget background down into the window, in case a subproc
3588 style = gtk_widget_get_style (p);
3589 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
3590 gdk_window_clear (window);
3593 int list_elt = selected_list_element (s);
3594 int hack_number = (list_elt >= 0
3595 ? s->list_elt_to_hack_number[list_elt]
3597 Bool available_p = (hack_number >= 0
3598 ? s->hacks_available_p [hack_number]
3600 Bool nothing_p = (s->total_available < 5);
3603 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3604 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3605 (s->running_preview_error_p
3606 ? (available_p ? 1 :
3609 #else /* !HAVE_GTK2 */
3610 if (s->running_preview_error_p)
3612 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3613 const char * const lines2[] = { N_("Not"), N_("Installed") };
3614 int nlines = countof(lines1);
3615 int lh = p->style->font->ascent + p->style->font->descent;
3619 const char * const *lines = (available_p ? lines1 : lines2);
3621 gdk_window_get_size (window, &w, &h);
3622 y = (h - (lh * nlines)) / 2;
3623 y += p->style->font->ascent;
3624 for (i = 0; i < nlines; i++)
3626 int sw = gdk_string_width (p->style->font, _(lines[i]));
3627 int x = (w - sw) / 2;
3628 gdk_draw_string (window, p->style->font,
3629 p->style->fg_gc[GTK_STATE_NORMAL],
3634 #endif /* !HAVE_GTK2 */
3642 reset_preview_window (state *s)
3644 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3645 when you kill one and re-start another on the same window. So maybe
3646 it's best to just always destroy and recreate the preview window
3647 when changing hacks, instead of always trying to reuse the same one?
3649 GtkWidget *pr = name_to_widget (s, "preview");
3650 if (GET_REALIZED (pr))
3652 GdkWindow *window = GET_WINDOW (pr);
3653 Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3655 gtk_widget_hide (pr);
3656 gtk_widget_unrealize (pr);
3657 gtk_widget_realize (pr);
3658 gtk_widget_show (pr);
3659 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3661 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3669 fix_preview_visual (state *s)
3671 GtkWidget *widget = name_to_widget (s, "preview");
3672 Visual *xvisual = get_best_gl_visual (s);
3673 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3674 GdkVisual *dvisual = gdk_visual_get_system();
3675 GdkColormap *cmap = (visual == dvisual
3676 ? gdk_colormap_get_system ()
3677 : gdk_colormap_new (visual, False));
3680 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3681 (visual == dvisual ? "default" : "non-default"),
3682 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3684 if (!GET_REALIZED (widget) ||
3685 gtk_widget_get_visual (widget) != visual)
3687 gtk_widget_unrealize (widget);
3688 gtk_widget_set_visual (widget, visual);
3689 gtk_widget_set_colormap (widget, cmap);
3690 gtk_widget_realize (widget);
3693 /* Set the Widget colors to be white-on-black. */
3695 GdkWindow *window = GET_WINDOW (widget);
3696 GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
3697 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3698 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3699 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3700 GdkGC *fgc = gdk_gc_new(window);
3701 GdkGC *bgc = gdk_gc_new(window);
3702 if (!gdk_color_white (cmap, fg)) abort();
3703 if (!gdk_color_black (cmap, bg)) abort();
3704 gdk_gc_set_foreground (fgc, fg);
3705 gdk_gc_set_background (fgc, bg);
3706 gdk_gc_set_foreground (bgc, bg);
3707 gdk_gc_set_background (bgc, fg);
3708 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3709 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3710 gtk_widget_set_style (widget, style);
3712 /* For debugging purposes, put a title on the window (so that
3713 it can be easily found in the output of "xwininfo -tree".)
3715 gdk_window_set_title (window, "Preview");
3718 gtk_widget_show (widget);
3726 subproc_pretty_name (state *s)
3728 if (s->running_preview_cmd)
3730 char *ps = strdup (s->running_preview_cmd);
3731 char *ss = strchr (ps, ' ');
3733 ss = strrchr (ps, '/');
3744 return strdup ("???");
3749 reap_zombies (state *s)
3751 int wait_status = 0;
3753 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3757 if (pid == s->running_preview_pid)
3759 char *ss = subproc_pretty_name (s);
3760 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3761 (unsigned long) pid, ss);
3765 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3766 (unsigned long) pid);
3772 /* Mostly lifted from driver/subprocs.c */
3774 get_best_gl_visual (state *s)
3776 Display *dpy = GDK_DISPLAY();
3785 av[ac++] = "xscreensaver-gl-helper";
3790 perror ("error creating pipe:");
3797 switch ((int) (forked = fork ()))
3801 sprintf (buf, "%s: couldn't fork", blurb());
3809 close (in); /* don't need this one */
3810 close (ConnectionNumber (dpy)); /* close display fd */
3812 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3814 perror ("could not dup() a new stdout:");
3818 execvp (av[0], av); /* shouldn't return. */
3820 if (errno != ENOENT)
3822 /* Ignore "no such file or directory" errors, unless verbose.
3823 Issue all other exec errors, though. */
3824 sprintf (buf, "%s: running %s", blurb(), av[0]);
3828 /* Note that one must use _exit() instead of exit() in procs forked
3829 off of Gtk programs -- Gtk installs an atexit handler that has a
3830 copy of the X connection (which we've already closed, for safety.)
3831 If one uses exit() instead of _exit(), then one sometimes gets a
3832 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3834 _exit (1); /* exits fork */
3840 int wait_status = 0;
3842 FILE *f = fdopen (in, "r");
3846 close (out); /* don't need this one */
3849 if (!fgets (buf, sizeof(buf)-1, f))
3853 /* Wait for the child to die. */
3854 waitpid (-1, &wait_status, 0);
3856 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3862 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3868 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3870 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3871 blurb(), av[0], result);
3883 kill_preview_subproc (state *s, Bool reset_p)
3885 s->running_preview_error_p = False;
3888 clear_preview_window (s);
3890 if (s->subproc_check_timer_id)
3892 gtk_timeout_remove (s->subproc_check_timer_id);
3893 s->subproc_check_timer_id = 0;
3894 s->subproc_check_countdown = 0;
3897 if (s->running_preview_pid)
3899 int status = kill (s->running_preview_pid, SIGTERM);
3900 char *ss = subproc_pretty_name (s);
3907 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3908 blurb(), (unsigned long) s->running_preview_pid, ss);
3913 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3914 blurb(), (unsigned long) s->running_preview_pid, ss);
3920 waitpid(s->running_preview_pid, &endstatus, 0);
3922 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3923 (unsigned long) s->running_preview_pid, ss);
3927 s->running_preview_pid = 0;
3928 if (s->running_preview_cmd) free (s->running_preview_cmd);
3929 s->running_preview_cmd = 0;
3936 reset_preview_window (s);
3937 clear_preview_window (s);
3942 /* Immediately and unconditionally launches the given process,
3943 after appending the -window-id option; sets running_preview_pid.
3946 launch_preview_subproc (state *s)
3948 saver_preferences *p = &s->prefs;
3952 const char *cmd = s->desired_preview_cmd;
3954 GtkWidget *pr = name_to_widget (s, "preview");
3957 reset_preview_window (s);
3959 window = GET_WINDOW (pr);
3961 s->running_preview_error_p = False;
3963 if (s->preview_suppressed_p)
3965 kill_preview_subproc (s, False);
3969 new_cmd = malloc (strlen (cmd) + 40);
3971 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3974 /* No window id? No command to run. */
3980 strcpy (new_cmd, cmd);
3981 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3985 kill_preview_subproc (s, False);
3988 s->running_preview_error_p = True;
3989 clear_preview_window (s);
3993 switch ((int) (forked = fork ()))
3998 sprintf (buf, "%s: couldn't fork", blurb());
4000 s->running_preview_error_p = True;
4006 close (ConnectionNumber (GDK_DISPLAY()));
4008 hack_subproc_environment (id, s->debug_p);
4010 usleep (250000); /* pause for 1/4th second before launching, to give
4011 the previous program time to die and flush its X
4012 buffer, so we don't get leftover turds on the
4015 exec_command (p->shell, new_cmd, p->nice_inferior);
4016 /* Don't bother printing an error message when we are unable to
4017 exec subprocesses; we handle that by polling the pid later.
4019 Note that one must use _exit() instead of exit() in procs forked
4020 off of Gtk programs -- Gtk installs an atexit handler that has a
4021 copy of the X connection (which we've already closed, for safety.)
4022 If one uses exit() instead of _exit(), then one sometimes gets a
4023 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
4025 _exit (1); /* exits child fork */
4030 if (s->running_preview_cmd) free (s->running_preview_cmd);
4031 s->running_preview_cmd = strdup (s->desired_preview_cmd);
4032 s->running_preview_pid = forked;
4036 char *ss = subproc_pretty_name (s);
4037 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
4038 (unsigned long) forked, ss);
4045 schedule_preview_check (s);
4048 if (new_cmd) free (new_cmd);
4053 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
4056 hack_environment (state *s)
4058 static const char *def_path =
4059 # ifdef DEFAULT_PATH_PREFIX
4060 DEFAULT_PATH_PREFIX;
4065 Display *dpy = GDK_DISPLAY();
4066 const char *odpy = DisplayString (dpy);
4067 char *ndpy = (char *) malloc(strlen(odpy) + 20);
4068 strcpy (ndpy, "DISPLAY=");
4069 strcat (ndpy, odpy);
4074 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4076 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4077 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4078 So we must leak it (and/or the previous setting). Yay.
4081 if (def_path && *def_path)
4083 const char *opath = getenv("PATH");
4084 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4085 strcpy (npath, "PATH=");
4086 strcat (npath, def_path);
4087 strcat (npath, ":");
4088 strcat (npath, opath);
4092 /* do not free(npath) -- see above */
4095 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4101 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4103 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4104 necessary yet, but it will make programs work if we had invoked
4105 them with "-root" and not with "-window-id" -- which, of course,
4108 char *nssw = (char *) malloc (40);
4109 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4111 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4112 any more, right? It's not Posix, but everyone seems to have it. */
4117 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4119 /* do not free(nssw) -- see above */
4123 /* Called from a timer:
4124 Launches the currently-chosen subprocess, if it's not already running.
4125 If there's a different process running, kills it.
4128 update_subproc_timer (gpointer data)
4130 state *s = (state *) data;
4131 if (! s->desired_preview_cmd)
4132 kill_preview_subproc (s, True);
4133 else if (!s->running_preview_cmd ||
4134 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4135 launch_preview_subproc (s);
4137 s->subproc_timer_id = 0;
4138 return FALSE; /* do not re-execute timer */
4142 settings_timer (gpointer data)
4149 /* Call this when you think you might want a preview process running.
4150 It will set a timer that will actually launch that program a second
4151 from now, if you haven't changed your mind (to avoid double-click
4152 spazzing, etc.) `cmd' may be null meaning "no process".
4155 schedule_preview (state *s, const char *cmd)
4157 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4162 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4164 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4167 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4168 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4170 if (s->subproc_timer_id)
4171 gtk_timeout_remove (s->subproc_timer_id);
4172 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4176 /* Called from a timer:
4177 Checks to see if the subproc that should be running, actually is.
4180 check_subproc_timer (gpointer data)
4182 state *s = (state *) data;
4183 Bool again_p = True;
4185 if (s->running_preview_error_p || /* already dead */
4186 s->running_preview_pid <= 0)
4194 status = kill (s->running_preview_pid, 0);
4195 if (status < 0 && errno == ESRCH)
4196 s->running_preview_error_p = True;
4200 char *ss = subproc_pretty_name (s);
4201 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4202 (unsigned long) s->running_preview_pid, ss,
4203 (s->running_preview_error_p ? "dead" : "alive"));
4207 if (s->running_preview_error_p)
4209 clear_preview_window (s);
4214 /* Otherwise, it's currently alive. We might be checking again, or we
4215 might be satisfied. */
4217 if (--s->subproc_check_countdown <= 0)
4221 return TRUE; /* re-execute timer */
4224 s->subproc_check_timer_id = 0;
4225 s->subproc_check_countdown = 0;
4226 return FALSE; /* do not re-execute timer */
4231 /* Call this just after launching a subprocess.
4232 This sets a timer that will, five times a second for two seconds,
4233 check whether the program is still running. The assumption here
4234 is that if the process didn't stay up for more than a couple of
4235 seconds, then either the program doesn't exist, or it doesn't
4236 take a -window-id argument.
4239 schedule_preview_check (state *s)
4245 fprintf (stderr, "%s: scheduling check\n", blurb());
4247 if (s->subproc_check_timer_id)
4248 gtk_timeout_remove (s->subproc_check_timer_id);
4249 s->subproc_check_timer_id =
4250 gtk_timeout_add (1000 / ticks,
4251 check_subproc_timer, (gpointer) s);
4252 s->subproc_check_countdown = ticks * seconds;
4257 screen_blanked_p (void)
4261 unsigned long nitems, bytesafter;
4262 unsigned char *dataP = 0;
4263 Display *dpy = GDK_DISPLAY();
4264 Bool blanked_p = False;
4266 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4267 XA_SCREENSAVER_STATUS,
4268 0, 3, False, XA_INTEGER,
4269 &type, &format, &nitems, &bytesafter,
4272 && type == XA_INTEGER
4276 Atom *data = (Atom *) dataP;
4277 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4280 if (dataP) XFree (dataP);
4285 /* Wake up every now and then and see if the screen is blanked.
4286 If it is, kill off the small-window demo -- no point in wasting
4287 cycles by running two screensavers at once...
4290 check_blanked_timer (gpointer data)
4292 state *s = (state *) data;
4293 Bool blanked_p = screen_blanked_p ();
4294 if (blanked_p && s->running_preview_pid)
4297 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4298 kill_preview_subproc (s, True);
4301 return True; /* re-execute timer */
4305 /* How many screens are there (including Xinerama.)
4308 screen_count (Display *dpy)
4310 int nscreens = ScreenCount(dpy);
4311 # ifdef HAVE_XINERAMA
4314 int event_number, error_number;
4315 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4316 XineramaIsActive (dpy))
4318 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4319 if (xsi) XFree (xsi);
4322 # endif /* HAVE_XINERAMA */
4328 /* Setting window manager icon
4332 init_icon (GdkWindow *window)
4334 GdkBitmap *mask = 0;
4336 gdk_pixmap_create_from_xpm_d (window, &mask, 0,
4337 (gchar **) logo_50_xpm);
4339 gdk_window_set_icon (window, 0, pixmap, mask);
4343 /* The main demo-mode command loop.
4348 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4349 XrmRepresentation *type, XrmValue *value, XPointer closure)
4352 for (i = 0; quarks[i]; i++)
4354 if (bindings[i] == XrmBindTightly)
4355 fprintf (stderr, (i == 0 ? "" : "."));
4356 else if (bindings[i] == XrmBindLoosely)
4357 fprintf (stderr, "*");
4359 fprintf (stderr, " ??? ");
4360 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4363 fprintf (stderr, ": %s\n", (char *) value->addr);
4371 gnome_screensaver_window (Screen *screen)
4373 Display *dpy = DisplayOfScreen (screen);
4374 Window root = RootWindowOfScreen (screen);
4375 Window parent, *kids;
4377 Window gnome_window = 0;
4380 if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4382 for (i = 0; i < nkids; i++)
4386 unsigned long nitems, bytesafter;
4387 unsigned char *name;
4388 if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4389 False, XA_STRING, &type, &format, &nitems,
4393 && !strcmp ((char *) name, "gnome-screensaver"))
4395 gnome_window = kids[i];
4400 if (kids) XFree ((char *) kids);
4401 return gnome_window;
4405 gnome_screensaver_active_p (void)
4407 Display *dpy = GDK_DISPLAY();
4408 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4409 return (w ? True : False);
4413 kill_gnome_screensaver (void)
4415 Display *dpy = GDK_DISPLAY();
4416 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4417 if (w) XKillClient (dpy, (XID) w);
4421 kde_screensaver_active_p (void)
4423 FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4426 fgets (buf, sizeof(buf)-1, p);
4428 if (!strcmp (buf, "true\n"))
4435 kill_kde_screensaver (void)
4437 system ("dcop kdesktop KScreensaverIface enable false");
4442 the_network_is_not_the_computer (state *s)
4444 Display *dpy = GDK_DISPLAY();
4445 char *rversion = 0, *ruser = 0, *rhost = 0;
4446 char *luser, *lhost;
4448 struct passwd *p = getpwuid (getuid ());
4449 const char *d = DisplayString (dpy);
4451 # if defined(HAVE_UNAME)
4453 if (uname (&uts) < 0)
4454 lhost = "<UNKNOWN>";
4456 lhost = uts.nodename;
4458 strcpy (lhost, getenv("SYS$NODE"));
4459 # else /* !HAVE_UNAME && !VMS */
4460 strcat (lhost, "<UNKNOWN>");
4461 # endif /* !HAVE_UNAME && !VMS */
4463 if (p && p->pw_name)
4468 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4470 /* Make a buffer that's big enough for a number of copies of all the
4471 strings, plus some. */
4472 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4473 (ruser ? strlen(ruser) : 0) +
4474 (rhost ? strlen(rhost) : 0) +
4481 if (!rversion || !*rversion)
4485 "The XScreenSaver daemon doesn't seem to be running\n"
4486 "on display \"%s\". Launch it now?"),
4489 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4491 /* Warn that the two processes are running as different users.
4495 "%s is running as user \"%s\" on host \"%s\".\n"
4496 "But the xscreensaver managing display \"%s\"\n"
4497 "is running as user \"%s\" on host \"%s\".\n"
4499 "Since they are different users, they won't be reading/writing\n"
4500 "the same ~/.xscreensaver file, so %s isn't\n"
4501 "going to work right.\n"
4503 "You should either re-run %s as \"%s\", or re-run\n"
4504 "xscreensaver as \"%s\".\n"
4506 "Restart the xscreensaver daemon now?\n"),
4507 progname, luser, lhost,
4509 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4511 progname, (ruser ? ruser : "???"),
4514 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4516 /* Warn that the two processes are running on different hosts.
4520 "%s is running as user \"%s\" on host \"%s\".\n"
4521 "But the xscreensaver managing display \"%s\"\n"
4522 "is running as user \"%s\" on host \"%s\".\n"
4524 "If those two machines don't share a file system (that is,\n"
4525 "if they don't see the same ~%s/.xscreensaver file) then\n"
4526 "%s won't work right.\n"
4528 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4529 progname, luser, lhost,
4531 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4536 else if (!!strcmp (rversion, s->short_version))
4538 /* Warn that the version numbers don't match.
4542 "This is %s version %s.\n"
4543 "But the xscreensaver managing display \"%s\"\n"
4544 "is version %s. This could cause problems.\n"
4546 "Restart the xscreensaver daemon now?\n"),
4547 progname, s->short_version,
4554 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4556 if (rversion) free (rversion);
4557 if (ruser) free (ruser);
4558 if (rhost) free (rhost);
4562 /* Note: since these dialogs are not modal, they will stack up.
4563 So we do this check *after* popping up the "xscreensaver is not
4564 running" dialog so that these are on top. Good enough.
4567 if (gnome_screensaver_active_p ())
4568 warning_dialog (s->toplevel_widget,
4570 "The GNOME screensaver daemon appears to be running.\n"
4571 "It must be stopped for XScreenSaver to work properly.\n"
4573 "Stop the GNOME screen saver daemon now?\n"),
4576 if (kde_screensaver_active_p ())
4577 warning_dialog (s->toplevel_widget,
4579 "The KDE screen saver daemon appears to be running.\n"
4580 "It must be stopped for XScreenSaver to work properly.\n"
4582 "Stop the KDE screen saver daemon now?\n"),
4587 /* We use this error handler so that X errors are preceeded by the name
4588 of the program that generated them.
4591 demo_ehandler (Display *dpy, XErrorEvent *error)
4593 state *s = global_state_kludge; /* I hate C so much... */
4594 fprintf (stderr, "\nX error in %s:\n", blurb());
4595 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4596 kill_preview_subproc (s, False);
4602 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4603 of the program that generated them; and also that we can ignore one
4604 particular bogus error message that Gdk madly spews.
4607 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4608 const gchar *message, gpointer user_data)
4610 /* Ignore the message "Got event for unknown window: 0x...".
4611 Apparently some events are coming in for the xscreensaver window
4612 (presumably reply events related to the ClientMessage) and Gdk
4613 feels the need to complain about them. So, just suppress any
4614 messages that look like that one.
4616 if (strstr (message, "unknown window"))
4619 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4620 (log_domain ? log_domain : progclass),
4621 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4622 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4623 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4624 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4625 log_level == G_LOG_LEVEL_INFO ? "info" :
4626 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4628 ((!*message || message[strlen(message)-1] != '\n')
4634 __extension__ /* shut up about "string length is greater than the length
4635 ISO C89 compilers are required to support" when including
4640 static char *defaults[] = {
4641 #include "XScreenSaver_ad.h"
4646 #ifdef HAVE_CRAPPLET
4647 static struct poptOption crapplet_options[] = {
4648 {NULL, '\0', 0, NULL, 0}
4650 #endif /* HAVE_CRAPPLET */
4653 const char *usage = "[--display dpy] [--prefs | --settings]"
4654 # ifdef HAVE_CRAPPLET
4657 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4660 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4662 state *s = (state *) user_data;
4663 Boolean oi = s->initializing_p;
4665 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4667 s->initializing_p = True;
4669 eschew_gtk_lossage (label);
4671 s->initializing_p = oi;
4677 print_widget_tree (GtkWidget *w, int depth)
4680 for (i = 0; i < depth; i++)
4681 fprintf (stderr, " ");
4682 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4684 if (GTK_IS_LIST (w))
4686 for (i = 0; i < depth+1; i++)
4687 fprintf (stderr, " ");
4688 fprintf (stderr, "...list kids...\n");
4690 else if (GTK_IS_CONTAINER (w))
4692 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4695 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4703 delayed_scroll_kludge (gpointer data)
4705 state *s = (state *) data;
4706 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4707 ensure_selected_item_visible (w);
4709 /* Oh, this is just fucking lovely, too. */
4710 w = GTK_WIDGET (name_to_widget (s, "preview"));
4711 gtk_widget_hide (w);
4712 gtk_widget_show (w);
4714 return FALSE; /* do not re-execute timer */
4720 create_xscreensaver_demo (void)
4724 nb = name_to_widget (global_state_kludge, "preview_notebook");
4725 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4727 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4731 create_xscreensaver_settings_dialog (void)
4735 box = name_to_widget (global_state_kludge, "dialog_action_area");
4737 w = name_to_widget (global_state_kludge, "adv_button");
4738 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4740 w = name_to_widget (global_state_kludge, "std_button");
4741 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4743 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4746 #endif /* HAVE_GTK2 */
4749 main (int argc, char **argv)
4753 saver_preferences *p;
4754 Bool prefs_p = False;
4755 Bool settings_p = False;
4758 Widget toplevel_shell;
4759 char *real_progname = argv[0];
4762 Bool crapplet_p = False;
4766 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4767 textdomain (GETTEXT_PACKAGE);
4770 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4771 # else /* !HAVE_GTK2 */
4772 if (!setlocale (LC_ALL, ""))
4773 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4774 # endif /* !HAVE_GTK2 */
4776 #endif /* ENABLE_NLS */
4778 str = strrchr (real_progname, '/');
4779 if (str) real_progname = str+1;
4782 memset (s, 0, sizeof(*s));
4783 s->initializing_p = True;
4786 global_state_kludge = s; /* I hate C so much... */
4788 progname = real_progname;
4790 s->short_version = (char *) malloc (5);
4791 memcpy (s->short_version, screensaver_id + 17, 4);
4792 s->short_version [4] = 0;
4795 /* Register our error message logger for every ``log domain'' known.
4796 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4797 for all of the domains that seem to be in use.
4800 const char * const domains[] = { 0,
4801 "Gtk", "Gdk", "GLib", "GModule",
4802 "GThread", "Gnome", "GnomeUI" };
4803 for (i = 0; i < countof(domains); i++)
4804 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4807 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4810 const char *dir = DEFAULT_ICONDIR;
4811 if (*dir) add_pixmap_directory (dir);
4813 # endif /* !HAVE_GTK2 */
4814 #endif /* DEFAULT_ICONDIR */
4816 /* This is gross, but Gtk understands --display and not -display...
4818 for (i = 1; i < argc; i++)
4819 if (argv[i][0] && argv[i][1] &&
4820 !strncmp(argv[i], "-display", strlen(argv[i])))
4821 argv[i] = "--display";
4824 /* We need to parse this arg really early... Sigh. */
4825 for (i = 1; i < argc; i++)
4828 (!strcmp(argv[i], "--crapplet") ||
4829 !strcmp(argv[i], "--capplet")))
4831 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4834 for (j = i; j < argc; j++) /* remove it from the list */
4835 argv[j] = argv[j+1];
4837 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4838 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4840 fprintf (stderr, "%s: %s\n", real_progname, usage);
4842 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4845 (!strcmp(argv[i], "--debug") ||
4846 !strcmp(argv[i], "-debug") ||
4847 !strcmp(argv[i], "-d")))
4851 for (j = i; j < argc; j++) /* remove it from the list */
4852 argv[j] = argv[j+1];
4859 (!strcmp(argv[i], "-geometry") ||
4860 !strcmp(argv[i], "-geom") ||
4861 !strcmp(argv[i], "-geo") ||
4862 !strcmp(argv[i], "-g")))
4866 for (j = i; j < argc; j++) /* remove them from the list */
4867 argv[j] = argv[j+2];
4874 (!strcmp(argv[i], "--configdir")))
4878 hack_configuration_path = argv[i+1];
4879 for (j = i; j < argc; j++) /* remove them from the list */
4880 argv[j] = argv[j+2];
4884 if (0 != stat (hack_configuration_path, &st))
4887 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4891 else if (!S_ISDIR (st.st_mode))
4893 fprintf (stderr, "%s: not a directory: %s\n",
4894 blurb(), hack_configuration_path);
4902 fprintf (stderr, "%s: using config directory \"%s\"\n",
4903 progname, hack_configuration_path);
4906 /* Let Gtk open the X connection, then initialize Xt to use that
4907 same connection. Doctor Frankenstein would be proud.
4909 # ifdef HAVE_CRAPPLET
4912 GnomeClient *client;
4913 GnomeClientFlags flags = 0;
4915 int init_results = gnome_capplet_init ("screensaver-properties",
4917 argc, argv, NULL, 0, NULL);
4919 0 upon successful initialization;
4920 1 if --init-session-settings was passed on the cmdline;
4921 2 if --ignore was passed on the cmdline;
4924 So the 1 signifies just to init the settings, and quit, basically.
4925 (Meaning launch the xscreensaver daemon.)
4928 if (init_results < 0)
4931 g_error ("An initialization error occurred while "
4932 "starting xscreensaver-capplet.\n");
4934 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4935 real_progname, init_results);
4940 client = gnome_master_client ();
4943 flags = gnome_client_get_flags (client);
4945 if (flags & GNOME_CLIENT_IS_CONNECTED)
4948 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4949 gnome_client_get_id (client));
4952 char *session_args[20];
4954 session_args[i++] = real_progname;
4955 session_args[i++] = "--capplet";
4956 session_args[i++] = "--init-session-settings";
4957 session_args[i] = 0;
4958 gnome_client_set_priority (client, 20);
4959 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4960 gnome_client_set_restart_command (client, i, session_args);
4964 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4967 gnome_client_flush (client);
4970 if (init_results == 1)
4972 system ("xscreensaver -nosplash &");
4978 # endif /* HAVE_CRAPPLET */
4980 gtk_init (&argc, &argv);
4984 /* We must read exactly the same resources as xscreensaver.
4985 That means we must have both the same progclass *and* progname,
4986 at least as far as the resource database is concerned. So,
4987 put "xscreensaver" in argv[0] while initializing Xt.
4989 argv[0] = "xscreensaver";
4993 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4995 XtToolkitInitialize ();
4996 app = XtCreateApplicationContext ();
4997 dpy = GDK_DISPLAY();
4998 XtAppSetFallbackResources (app, defaults);
4999 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
5000 toplevel_shell = XtAppCreateShell (progname, progclass,
5001 applicationShellWidgetClass,
5004 dpy = XtDisplay (toplevel_shell);
5005 db = XtDatabase (dpy);
5006 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
5007 XSetErrorHandler (demo_ehandler);
5009 /* Let's just ignore these. They seem to confuse Irix Gtk... */
5010 signal (SIGPIPE, SIG_IGN);
5012 /* After doing Xt-style command-line processing, complain about any
5013 unrecognized command-line arguments.
5015 for (i = 1; i < argc; i++)
5017 char *str = argv[i];
5018 if (str[0] == '-' && str[1] == '-')
5020 if (!strcmp (str, "-prefs"))
5022 else if (!strcmp (str, "-settings"))
5024 else if (crapplet_p)
5025 /* There are lots of random args that we don't care about when we're
5026 started as a crapplet, so just ignore unknown args in that case. */
5030 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
5032 fprintf (stderr, "%s: %s\n", real_progname, usage);
5037 /* Load the init file, which may end up consulting the X resource database
5038 and the site-wide app-defaults file. Note that at this point, it's
5039 important that `progname' be "xscreensaver", rather than whatever
5043 s->nscreens = screen_count (dpy);
5045 hack_environment (s); /* must be before initialize_sort_map() */
5047 load_init_file (dpy, p);
5048 initialize_sort_map (s);
5050 /* Now that Xt has been initialized, and the resources have been read,
5051 we can set our `progname' variable to something more in line with
5054 progname = real_progname;
5058 /* Print out all the resources we read. */
5060 XrmName name = { 0 };
5061 XrmClass class = { 0 };
5063 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
5069 /* Intern the atoms that xscreensaver_command() needs.
5071 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
5072 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
5073 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
5074 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5075 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5076 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5077 XA_SELECT = XInternAtom (dpy, "SELECT", False);
5078 XA_DEMO = XInternAtom (dpy, "DEMO", False);
5079 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5080 XA_BLANK = XInternAtom (dpy, "BLANK", False);
5081 XA_LOCK = XInternAtom (dpy, "LOCK", False);
5082 XA_EXIT = XInternAtom (dpy, "EXIT", False);
5083 XA_RESTART = XInternAtom (dpy, "RESTART", False);
5086 /* Create the window and all its widgets.
5088 s->base_widget = create_xscreensaver_demo ();
5089 s->popup_widget = create_xscreensaver_settings_dialog ();
5090 s->toplevel_widget = s->base_widget;
5093 /* Set the main window's title. */
5095 char *base_title = _("Screensaver Preferences");
5096 char *v = (char *) strdup(strchr(screensaver_id, ' '));
5097 char *s1, *s2, *s3, *s4;
5098 s1 = (char *) strchr(v, ' '); s1++;
5099 s2 = (char *) strchr(s1, ' ');
5100 s3 = (char *) strchr(v, '('); s3++;
5101 s4 = (char *) strchr(s3, ')');
5105 window_title = (char *) malloc (strlen (base_title) +
5106 strlen (progclass) +
5107 strlen (s1) + strlen (s3) +
5109 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
5110 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5111 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
5115 /* Adjust the (invisible) notebooks on the popup dialog... */
5117 GtkNotebook *notebook =
5118 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5119 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5123 gtk_widget_hide (std);
5124 # else /* !HAVE_XML */
5125 /* Make the advanced page be the only one available. */
5126 gtk_widget_set_sensitive (std, False);
5127 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5128 gtk_widget_hide (std);
5129 std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5130 gtk_widget_hide (std);
5132 # endif /* !HAVE_XML */
5134 gtk_notebook_set_page (notebook, page);
5135 gtk_notebook_set_show_tabs (notebook, False);
5138 /* Various other widget initializations...
5140 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5141 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5143 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5144 GTK_SIGNAL_FUNC (wm_popup_close_cb),
5147 populate_hack_list (s);
5148 populate_prefs_page (s);
5149 sensitize_demo_widgets (s, False);
5150 fix_text_entry_sizes (s);
5151 scroll_to_current_hack (s);
5153 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5154 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5158 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5159 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5161 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5162 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5164 #endif /* !HAVE_GTK2 */
5166 /* Hook up callbacks to the items on the mode menu. */
5168 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5169 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5170 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5172 for (i = 0; kids; kids = kids->next, i++)
5174 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5175 GTK_SIGNAL_FUNC (mode_menu_item_cb),
5178 /* The "random-same" mode menu item does not appear unless
5179 there are multple screens.
5181 if (s->nscreens <= 1 &&
5182 mode_menu_order[i] == RANDOM_HACKS_SAME)
5183 gtk_widget_hide (GTK_WIDGET (kids->data));
5186 if (s->nscreens <= 1) /* recompute option-menu size */
5188 gtk_widget_unrealize (GTK_WIDGET (menu));
5189 gtk_widget_realize (GTK_WIDGET (menu));
5194 /* Handle the -prefs command-line argument. */
5197 GtkNotebook *notebook =
5198 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5199 gtk_notebook_set_page (notebook, 1);
5202 # ifdef HAVE_CRAPPLET
5206 GtkWidget *outer_vbox;
5208 gtk_widget_hide (s->toplevel_widget);
5210 capplet = capplet_widget_new ();
5212 /* Make there be a "Close" button instead of "OK" and "Cancel" */
5213 # ifdef HAVE_CRAPPLET_IMMEDIATE
5214 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5215 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5216 /* In crapplet-mode, take off the menubar. */
5217 gtk_widget_hide (name_to_widget (s, "menubar"));
5219 /* Reparent our top-level container to be a child of the capplet
5222 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5223 gtk_widget_ref (outer_vbox);
5224 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5226 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5227 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5229 /* Find the window above us, and set the title and close handler. */
5231 GtkWidget *window = capplet;
5232 while (window && !GTK_IS_WINDOW (window))
5233 window = GET_PARENT (window);
5236 gtk_window_set_title (GTK_WINDOW (window), window_title);
5237 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5238 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5243 s->toplevel_widget = capplet;
5245 # endif /* HAVE_CRAPPLET */
5248 /* The Gnome folks hate the menubar. I think it's important to have access
5249 to the commands on the File menu (Restart Daemon, etc.) and to the
5250 About and Documentation commands on the Help menu.
5254 gtk_widget_hide (name_to_widget (s, "menubar"));
5258 free (window_title);
5262 /* After picking the default size, allow -geometry to override it. */
5264 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5267 gtk_widget_show (s->toplevel_widget);
5268 init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */
5269 fix_preview_visual (s);
5271 /* Realize page zero, so that we can diddle the scrollbar when the
5272 user tabs back to it -- otherwise, the current hack isn't scrolled
5273 to the first time they tab back there, when started with "-prefs".
5274 (Though it is if they then tab away, and back again.)
5276 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5277 #### understands this crap, explain to me how to make this work.
5279 gtk_widget_realize (name_to_widget (s, "demos_table"));
5282 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5285 /* Handle the --settings command-line argument. */
5287 gtk_timeout_add (500, settings_timer, 0);
5290 /* Issue any warnings about the running xscreensaver daemon. */
5292 the_network_is_not_the_computer (s);
5296 warning_dialog (s->toplevel_widget,
5298 "This version of xscreensaver is VERY OLD!\n"
5301 "http://www.jwz.org/xscreensaver/\n"
5303 "(If this is the latest version that your distro ships, then\n"
5304 "your distro is doing you a disservice. Build from source.)\n"
5309 /* Run the Gtk event loop, and not the Xt event loop. This means that
5310 if there were Xt timers or fds registered, they would never get serviced,
5311 and if there were any Xt widgets, they would never have events delivered.
5312 Fortunately, we're using Gtk for all of the UI, and only initialized
5313 Xt so that we could process the command line and use the X resource
5316 s->initializing_p = False;
5318 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5319 after we start up. Otherwise, it always appears scrolled to the top
5320 when in crapplet-mode. */
5321 gtk_timeout_add (500, delayed_scroll_kludge, s);
5325 /* Load every configurator in turn, to scan them for errors all at once. */
5329 for (i = 0; i < p->screenhacks_count; i++)
5331 screenhack *hack = p->screenhacks[i];
5332 conf_data *d = load_configurator (hack->command, s->debug_p);
5333 if (d) free_conf_data (d);
5339 # ifdef HAVE_CRAPPLET
5341 capplet_gtk_main ();
5343 # endif /* HAVE_CRAPPLET */
5346 kill_preview_subproc (s, False);
5350 #endif /* HAVE_GTK -- whole file */