1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2017 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 https://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"
1043 _("You are running as root. This usually means that xscreensaver\n"
1044 "was unable to contact your X server because access control is\n"
1045 "turned on. Try running this command:\n"
1047 " xhost +localhost\n"
1049 "and then selecting `File / Restart Daemon'.\n"
1051 "Note that turning off access control will allow anyone logged\n"
1052 "on to this machine to access your screen, which might be\n"
1053 "considered a security problem. Please read the xscreensaver\n"
1054 "manual and FAQ for more information.\n"
1056 "You shouldn't run X as root. Instead, you should log in as a\n"
1057 "normal user, and `su' as necessary."));
1059 strcat (buf, _("Please check your $PATH and permissions."));
1061 warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1064 force_dialog_repaint (s);
1069 selected_list_element (state *s)
1071 return s->_selected_list_element;
1076 demo_write_init_file (state *s, saver_preferences *p)
1078 Display *dpy = GDK_DISPLAY();
1081 /* #### try to figure out why shit keeps getting reordered... */
1082 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1086 if (!write_init_file (dpy, p, s->short_version, False))
1089 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1094 const char *f = init_file_name();
1096 warning_dialog (s->toplevel_widget,
1097 _("Error:\n\nCouldn't determine init file name!\n"),
1101 char *b = (char *) malloc (strlen(f) + 1024);
1102 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1103 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1111 G_MODULE_EXPORT void
1112 run_this_cb (GtkButton *button, gpointer user_data)
1114 state *s = global_state_kludge; /* I hate C so much... */
1115 int list_elt = selected_list_element (s);
1116 if (list_elt < 0) return;
1117 if (!flush_dialog_changes_and_save (s))
1118 run_hack (s, list_elt, True);
1122 G_MODULE_EXPORT void
1123 manual_cb (GtkButton *button, gpointer user_data)
1125 Display *dpy = GDK_DISPLAY();
1126 state *s = global_state_kludge; /* I hate C so much... */
1127 saver_preferences *p = &s->prefs;
1128 GtkWidget *list_widget = name_to_widget (s, "list");
1129 int list_elt = selected_list_element (s);
1131 char *name, *name2, *cmd, *str;
1133 if (list_elt < 0) return;
1134 hack_number = s->list_elt_to_hack_number[list_elt];
1136 flush_dialog_changes_and_save (s);
1137 ensure_selected_item_visible (list_widget);
1139 name = strdup (p->screenhacks[hack_number]->command);
1142 while (isspace (*name2)) name2++;
1144 while (*str && !isspace (*str)) str++;
1146 str = strrchr (name2, '/');
1147 if (str) name2 = str+1;
1149 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1152 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1153 strcpy (cmd2, "( ");
1154 sprintf (cmd2 + strlen (cmd2),
1156 name2, name2, name2, name2);
1157 strcat (cmd2, " ) &");
1158 if (system (cmd2) < 0)
1159 fprintf (stderr, "%s: fork error\n", blurb());
1164 warning_dialog (GTK_WIDGET (button),
1165 _("Error:\n\nno `manualCommand' resource set."),
1174 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1176 GtkWidget *parent = name_to_widget (s, "scroller");
1177 gboolean was = GET_SENSITIVE (parent);
1180 GtkTreeModel *model;
1181 GtkTreeSelection *selection;
1182 #endif /* HAVE_GTK2 */
1184 if (!was) gtk_widget_set_sensitive (parent, True);
1186 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1188 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1190 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1191 gtk_tree_selection_select_iter (selection, &iter);
1193 #else /* !HAVE_GTK2 */
1194 gtk_list_select_item (GTK_LIST (list), list_elt);
1195 #endif /* !HAVE_GTK2 */
1196 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1197 if (!was) gtk_widget_set_sensitive (parent, False);
1201 G_MODULE_EXPORT void
1202 run_next_cb (GtkButton *button, gpointer user_data)
1204 state *s = global_state_kludge; /* I hate C so much... */
1205 /* saver_preferences *p = &s->prefs; */
1206 Bool ops = s->preview_suppressed_p;
1208 GtkWidget *list_widget = name_to_widget (s, "list");
1209 int list_elt = selected_list_element (s);
1216 if (list_elt >= s->list_count)
1219 s->preview_suppressed_p = True;
1221 flush_dialog_changes_and_save (s);
1222 force_list_select_item (s, list_widget, list_elt, True);
1223 populate_demo_window (s, list_elt);
1224 run_hack (s, list_elt, False);
1226 s->preview_suppressed_p = ops;
1230 G_MODULE_EXPORT void
1231 run_prev_cb (GtkButton *button, gpointer user_data)
1233 state *s = global_state_kludge; /* I hate C so much... */
1234 /* saver_preferences *p = &s->prefs; */
1235 Bool ops = s->preview_suppressed_p;
1237 GtkWidget *list_widget = name_to_widget (s, "list");
1238 int list_elt = selected_list_element (s);
1241 list_elt = s->list_count - 1;
1246 list_elt = s->list_count - 1;
1248 s->preview_suppressed_p = True;
1250 flush_dialog_changes_and_save (s);
1251 force_list_select_item (s, list_widget, list_elt, True);
1252 populate_demo_window (s, list_elt);
1253 run_hack (s, list_elt, False);
1255 s->preview_suppressed_p = ops;
1259 /* Writes the given settings into prefs.
1260 Returns true if there was a change, False otherwise.
1261 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1264 flush_changes (state *s,
1267 const char *command,
1270 saver_preferences *p = &s->prefs;
1271 Bool changed = False;
1274 if (list_elt < 0 || list_elt >= s->list_count)
1277 hack_number = s->list_elt_to_hack_number[list_elt];
1278 hack = p->screenhacks[hack_number];
1280 if (enabled_p != -1 &&
1281 enabled_p != hack->enabled_p)
1283 hack->enabled_p = enabled_p;
1286 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1287 blurb(), hack->name, enabled_p);
1292 if (!hack->command || !!strcmp (command, hack->command))
1294 if (hack->command) free (hack->command);
1295 hack->command = strdup (command);
1298 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1299 blurb(), hack->name, command);
1305 const char *ov = hack->visual;
1306 if (!ov || !*ov) ov = "any";
1307 if (!*visual) visual = "any";
1308 if (!!strcasecmp (visual, ov))
1310 if (hack->visual) free (hack->visual);
1311 hack->visual = strdup (visual);
1314 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1315 blurb(), hack->name, visual);
1323 /* Helper for the text fields that contain time specifications:
1324 this parses the text, and does error checking.
1327 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1332 if (!sec_p || strchr (line, ':'))
1333 value = parse_time ((char *) line, sec_p, True);
1337 if (sscanf (line, "%d%c", &value, &c) != 1)
1343 value *= 1000; /* Time measures in microseconds */
1349 "Unparsable time format: \"%s\"\n"),
1351 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1360 directory_p (const char *path)
1363 if (!path || !*path)
1365 else if (stat (path, &st))
1367 else if (!S_ISDIR (st.st_mode))
1374 file_p (const char *path)
1377 if (!path || !*path)
1379 else if (stat (path, &st))
1381 else if (S_ISDIR (st.st_mode))
1388 normalize_directory (const char *path)
1392 if (!path || !*path) return 0;
1394 p2 = (char *) malloc (L + 2);
1396 if (p2[L-1] == '/') /* remove trailing slash */
1399 for (s = p2; s && *s; s++)
1402 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1403 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1406 while (s0 > p2 && s0[-1] != '/')
1412 /* strcpy (s0, s); */
1413 memmove(s0, s, strlen(s) + 1);
1417 else if (*s == '/' && !strncmp (s, "/./", 3)) { /* delete "/./" */
1418 /* strcpy (s, s+2), s--; */
1419 memmove(s, s+2, strlen(s+2) + 1);
1422 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1427 Normalize consecutive slashes.
1428 Ignore doubled slashes after ":" to avoid mangling URLs.
1431 for (s = p2; s && *s; s++){
1432 if (*s == ':') continue;
1433 if (!s[1] || !s[2]) continue;
1434 while (s[1] == '/' && s[2] == '/')
1435 /* strcpy (s+1, s+2); */
1436 memmove (s+1, s+2, strlen(s+2) + 1);
1439 /* and strip trailing whitespace for good measure. */
1441 while (isspace(p2[L-1]))
1454 } FlushForeachClosure;
1457 flush_checkbox (GtkTreeModel *model,
1462 FlushForeachClosure *closure = data;
1465 gtk_tree_model_get (model, iter,
1466 COL_ENABLED, &checked,
1469 if (flush_changes (closure->s, closure->i,
1471 *closure->changed = True;
1475 /* don't remove row */
1479 #endif /* HAVE_GTK2 */
1481 /* Flush out any changes made in the main dialog window (where changes
1482 take place immediately: clicking on a checkbox causes the init file
1483 to be written right away.)
1486 flush_dialog_changes_and_save (state *s)
1488 saver_preferences *p = &s->prefs;
1489 saver_preferences P2, *p2 = &P2;
1491 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1492 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1493 FlushForeachClosure closure;
1494 #else /* !HAVE_GTK2 */
1495 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1496 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1498 #endif /* !HAVE_GTK2 */
1499 static Bool already_warned_about_missing_image_directory = False; /* very long name... */
1501 Bool changed = False;
1504 if (s->saving_p) return False;
1509 /* Flush any checkbox changes in the list down into the prefs struct.
1513 closure.changed = &changed;
1515 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1517 #else /* !HAVE_GTK2 */
1519 for (i = 0; kids; kids = kids->next, i++)
1521 GtkWidget *line = GTK_WIDGET (kids->data);
1522 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1523 GtkWidget *line_check =
1524 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1526 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1528 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1531 #endif /* ~HAVE_GTK2 */
1533 /* Flush the non-hack-specific settings down into the prefs struct.
1536 # define SECONDS(FIELD,NAME) \
1537 w = name_to_widget (s, (NAME)); \
1538 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1540 # define MINUTES(FIELD,NAME) \
1541 w = name_to_widget (s, (NAME)); \
1542 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1544 # define CHECKBOX(FIELD,NAME) \
1545 w = name_to_widget (s, (NAME)); \
1546 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1548 # define PATHNAME(FIELD,NAME) \
1549 w = name_to_widget (s, (NAME)); \
1550 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1552 # define TEXT(FIELD,NAME) \
1553 w = name_to_widget (s, (NAME)); \
1554 (FIELD) = (char *) g_strdup(gtk_entry_get_text (GTK_ENTRY (w)))
1556 MINUTES (&p2->timeout, "timeout_spinbutton");
1557 MINUTES (&p2->cycle, "cycle_spinbutton");
1558 CHECKBOX (p2->lock_p, "lock_button");
1559 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1561 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1562 CHECKBOX (p2->dpms_quickoff_p, "dpms_quickoff_button");
1563 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1564 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1565 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1567 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1568 CHECKBOX (p2->grab_video_p, "grab_video_button");
1569 CHECKBOX (p2->random_image_p, "grab_image_button");
1570 PATHNAME (p2->image_directory, "image_text");
1573 CHECKBOX (p2->verbose_p, "verbose_button");
1574 CHECKBOX (p2->capture_stderr_p, "capture_button");
1575 CHECKBOX (p2->splash_p, "splash_button");
1580 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1581 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1582 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1583 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1584 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1585 TEXT (p2->text_literal, "text_entry");
1586 PATHNAME (p2->text_file, "text_file_entry");
1587 PATHNAME (p2->text_program, "text_program_entry");
1588 PATHNAME (p2->text_program, "text_program_entry");
1589 TEXT (p2->text_url, "text_url_entry");
1592 CHECKBOX (p2->install_cmap_p, "install_button");
1593 CHECKBOX (p2->fade_p, "fade_button");
1594 CHECKBOX (p2->unfade_p, "unfade_button");
1595 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1603 /* Warn if the image directory doesn't exist, when:
1604 - not being warned before
1605 - image directory is changed and the directory doesn't exist
1606 - image directory does not begin with http://
1608 if (p2->image_directory &&
1609 *p2->image_directory &&
1610 !directory_p (p2->image_directory) &&
1611 strncmp(p2->image_directory, "http://", 6) &&
1612 ( !already_warned_about_missing_image_directory ||
1613 ( p->image_directory &&
1614 *p->image_directory &&
1615 strcmp(p->image_directory, p2->image_directory)
1621 sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n",
1622 p2->image_directory);
1623 if (warning_dialog (s->toplevel_widget, b, D_NONE, 100))
1624 already_warned_about_missing_image_directory = True;
1628 /* Map the mode menu to `saver_mode' enum values. */
1630 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1631 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1632 GtkWidget *selected = gtk_menu_get_active (menu);
1633 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1634 int menu_elt = g_list_index (kids, (gpointer) selected);
1635 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1636 p2->mode = mode_menu_order[menu_elt];
1639 if (p2->mode == ONE_HACK)
1641 int list_elt = selected_list_element (s);
1642 p2->selected_hack = (list_elt >= 0
1643 ? s->list_elt_to_hack_number[list_elt]
1647 # define COPY(field, name) \
1648 if (p->field != p2->field) { \
1651 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1653 p->field = p2->field
1656 COPY(selected_hack, "selected_hack");
1658 COPY(timeout, "timeout");
1659 COPY(cycle, "cycle");
1660 COPY(lock_p, "lock_p");
1661 COPY(lock_timeout, "lock_timeout");
1663 COPY(dpms_enabled_p, "dpms_enabled_p");
1664 COPY(dpms_quickoff_p, "dpms_quickoff_enabled_p");
1665 COPY(dpms_standby, "dpms_standby");
1666 COPY(dpms_suspend, "dpms_suspend");
1667 COPY(dpms_off, "dpms_off");
1670 COPY(verbose_p, "verbose_p");
1671 COPY(capture_stderr_p, "capture_stderr_p");
1672 COPY(splash_p, "splash_p");
1675 COPY(tmode, "tmode");
1677 COPY(install_cmap_p, "install_cmap_p");
1678 COPY(fade_p, "fade_p");
1679 COPY(unfade_p, "unfade_p");
1680 COPY(fade_seconds, "fade_seconds");
1682 COPY(grab_desktop_p, "grab_desktop_p");
1683 COPY(grab_video_p, "grab_video_p");
1684 COPY(random_image_p, "random_image_p");
1688 # define COPYSTR(FIELD,NAME) \
1691 strcmp(p->FIELD, p2->FIELD)) \
1695 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1697 if (p->FIELD && p->FIELD != p2->FIELD) \
1699 p->FIELD = p2->FIELD; \
1702 COPYSTR(image_directory, "image_directory");
1703 COPYSTR(text_literal, "text_literal");
1704 COPYSTR(text_file, "text_file");
1705 COPYSTR(text_program, "text_program");
1706 COPYSTR(text_url, "text_url");
1709 populate_prefs_page (s);
1713 Display *dpy = GDK_DISPLAY();
1714 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1715 sync_server_dpms_settings (dpy, enabled_p, p->dpms_quickoff_p,
1716 p->dpms_standby / 1000,
1717 p->dpms_suspend / 1000,
1721 changed = demo_write_init_file (s, p);
1724 s->saving_p = False;
1729 /* Flush out any changes made in the popup dialog box (where changes
1730 take place only when the OK button is clicked.)
1733 flush_popup_changes_and_save (state *s)
1735 Bool changed = False;
1736 saver_preferences *p = &s->prefs;
1737 int list_elt = selected_list_element (s);
1739 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1740 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1742 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1743 const char *command = gtk_entry_get_text (cmd);
1748 if (s->saving_p) return False;
1754 if (maybe_reload_init_file (s) != 0)
1760 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1762 if (!strcasecmp (visual, "")) visual = "";
1763 else if (!strcasecmp (visual, "any")) visual = "";
1764 else if (!strcasecmp (visual, "default")) visual = "Default";
1765 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1766 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1767 else if (!strcasecmp (visual, "best")) visual = "Best";
1768 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1769 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1770 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1771 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1772 else if (!strcasecmp (visual, "color")) visual = "Color";
1773 else if (!strcasecmp (visual, "gl")) visual = "GL";
1774 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1775 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1776 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1777 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1778 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1779 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1780 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1781 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1782 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1785 gdk_beep (); /* unparsable */
1787 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1790 changed = flush_changes (s, list_elt, -1, command, visual);
1793 changed = demo_write_init_file (s, p);
1795 /* Do this to re-launch the hack if (and only if) the command line
1797 populate_demo_window (s, selected_list_element (s));
1801 s->saving_p = False;
1806 G_MODULE_EXPORT void
1807 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1809 state *s = global_state_kludge; /* I hate C so much... */
1810 if (! s->initializing_p)
1812 s->initializing_p = True;
1813 flush_dialog_changes_and_save (s);
1814 s->initializing_p = False;
1818 G_MODULE_EXPORT gboolean
1819 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1821 pref_changed_cb (widget, user_data);
1825 /* Callback on menu items in the "mode" options menu.
1827 G_MODULE_EXPORT void
1828 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1830 state *s = (state *) user_data;
1831 saver_preferences *p = &s->prefs;
1832 GtkWidget *list = name_to_widget (s, "list");
1836 gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
1838 saver_mode new_mode;
1842 if (menu_items->data == widget)
1845 menu_items = menu_items->next;
1847 if (!menu_items) abort();
1849 new_mode = mode_menu_order[menu_index];
1851 /* Keep the same list element displayed as before; except if we're
1852 switching *to* "one screensaver" mode from any other mode, set
1853 "the one" to be that which is currently selected.
1855 list_elt = selected_list_element (s);
1856 if (new_mode == ONE_HACK)
1857 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1860 saver_mode old_mode = p->mode;
1862 populate_demo_window (s, list_elt);
1863 force_list_select_item (s, list, list_elt, True);
1864 p->mode = old_mode; /* put it back, so the init file gets written */
1867 pref_changed_cb (widget, user_data);
1871 G_MODULE_EXPORT void
1872 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1873 gint page_num, gpointer user_data)
1875 state *s = global_state_kludge; /* I hate C so much... */
1876 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1878 /* If we're switching to page 0, schedule the current hack to be run.
1879 Otherwise, schedule it to stop. */
1881 populate_demo_window (s, selected_list_element (s));
1883 schedule_preview (s, 0);
1888 list_activated_cb (GtkTreeView *list,
1890 GtkTreeViewColumn *column,
1897 g_return_if_fail (!gdk_pointer_is_grabbed ());
1899 str = gtk_tree_path_to_string (path);
1900 list_elt = strtol (str, NULL, 10);
1904 run_hack (s, list_elt, True);
1908 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1910 state *s = (state *)data;
1911 GtkTreeModel *model;
1917 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1920 path = gtk_tree_model_get_path (model, &iter);
1921 str = gtk_tree_path_to_string (path);
1922 list_elt = strtol (str, NULL, 10);
1924 gtk_tree_path_free (path);
1927 populate_demo_window (s, list_elt);
1928 flush_dialog_changes_and_save (s);
1930 /* Re-populate the Settings window any time a new item is selected
1931 in the list, in case both windows are currently visible.
1933 populate_popup_window (s);
1936 #else /* !HAVE_GTK2 */
1938 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1939 list_select_cb that comes in
1940 *after* we've double-clicked.
1944 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1947 state *s = (state *) data;
1948 if (event->type == GDK_2BUTTON_PRESS)
1950 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1951 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1953 last_doubleclick_time = time ((time_t *) 0);
1956 run_hack (s, list_elt, True);
1964 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1966 state *s = (state *) data;
1967 time_t now = time ((time_t *) 0);
1969 if (now >= last_doubleclick_time + 2)
1971 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1972 populate_demo_window (s, list_elt);
1973 flush_dialog_changes_and_save (s);
1978 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1980 state *s = (state *) data;
1981 populate_demo_window (s, -1);
1982 flush_dialog_changes_and_save (s);
1985 #endif /* !HAVE_GTK2 */
1988 /* Called when the checkboxes that are in the left column of the
1989 scrolling list are clicked. This both populates the right pane
1990 (just as clicking on the label (really, listitem) does) and
1991 also syncs this checkbox with the right pane Enabled checkbox.
1996 GtkCellRendererToggle *toggle,
1998 #else /* !HAVE_GTK2 */
2000 #endif /* !HAVE_GTK2 */
2003 state *s = (state *) data;
2006 GtkScrolledWindow *scroller =
2007 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
2008 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2009 GtkTreeModel *model = gtk_tree_view_get_model (list);
2010 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
2013 #else /* !HAVE_GTK2 */
2014 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
2015 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
2017 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
2018 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
2019 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
2020 #endif /* !HAVE_GTK2 */
2027 if (!gtk_tree_model_get_iter (model, &iter, path))
2029 g_warning ("bad path: %s", path_string);
2032 gtk_tree_path_free (path);
2034 gtk_tree_model_get (model, &iter,
2035 COL_ENABLED, &active,
2038 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2039 COL_ENABLED, !active,
2042 list_elt = strtol (path_string, NULL, 10);
2043 #else /* !HAVE_GTK2 */
2044 list_elt = gtk_list_child_position (list, line);
2045 #endif /* !HAVE_GTK2 */
2047 /* remember previous scroll position of the top of the list */
2048 adj = gtk_scrolled_window_get_vadjustment (scroller);
2049 scroll_top = GET_ADJ_VALUE (adj);
2051 flush_dialog_changes_and_save (s);
2052 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
2053 populate_demo_window (s, list_elt);
2055 /* restore the previous scroll position of the top of the list.
2056 this is weak, but I don't really know why it's moving... */
2057 gtk_adjustment_set_value (adj, scroll_top);
2063 GtkFileSelection *widget;
2064 } file_selection_data;
2069 store_image_directory (GtkWidget *button, gpointer user_data)
2071 file_selection_data *fsd = (file_selection_data *) user_data;
2072 state *s = fsd->state;
2073 GtkFileSelection *selector = fsd->widget;
2074 GtkWidget *top = s->toplevel_widget;
2075 saver_preferences *p = &s->prefs;
2076 const char *path = gtk_file_selection_get_filename (selector);
2078 if (p->image_directory && !strcmp(p->image_directory, path))
2079 return; /* no change */
2081 /* No warning for URLs. */
2082 if ((!directory_p (path)) && strncmp(path, "http://", 6))
2085 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2086 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2090 if (p->image_directory) free (p->image_directory);
2091 p->image_directory = normalize_directory (path);
2093 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2094 (p->image_directory ? p->image_directory : ""));
2095 demo_write_init_file (s, p);
2100 store_text_file (GtkWidget *button, gpointer user_data)
2102 file_selection_data *fsd = (file_selection_data *) user_data;
2103 state *s = fsd->state;
2104 GtkFileSelection *selector = fsd->widget;
2105 GtkWidget *top = s->toplevel_widget;
2106 saver_preferences *p = &s->prefs;
2107 const char *path = gtk_file_selection_get_filename (selector);
2109 if (p->text_file && !strcmp(p->text_file, path))
2110 return; /* no change */
2115 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2116 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2120 if (p->text_file) free (p->text_file);
2121 p->text_file = normalize_directory (path);
2123 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2124 (p->text_file ? p->text_file : ""));
2125 demo_write_init_file (s, p);
2130 store_text_program (GtkWidget *button, gpointer user_data)
2132 file_selection_data *fsd = (file_selection_data *) user_data;
2133 state *s = fsd->state;
2134 GtkFileSelection *selector = fsd->widget;
2135 /*GtkWidget *top = s->toplevel_widget;*/
2136 saver_preferences *p = &s->prefs;
2137 const char *path = gtk_file_selection_get_filename (selector);
2139 if (p->text_program && !strcmp(p->text_program, path))
2140 return; /* no change */
2146 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2147 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2152 if (p->text_program) free (p->text_program);
2153 p->text_program = normalize_directory (path);
2155 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2156 (p->text_program ? p->text_program : ""));
2157 demo_write_init_file (s, p);
2163 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2165 file_selection_data *fsd = (file_selection_data *) user_data;
2166 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2170 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2172 browse_image_dir_cancel (button, user_data);
2173 store_image_directory (button, user_data);
2177 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2179 browse_image_dir_cancel (button, user_data);
2180 store_text_file (button, user_data);
2184 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2186 browse_image_dir_cancel (button, user_data);
2187 store_text_program (button, user_data);
2191 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2193 browse_image_dir_cancel (widget, user_data);
2197 G_MODULE_EXPORT void
2198 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2200 state *s = global_state_kludge; /* I hate C so much... */
2201 saver_preferences *p = &s->prefs;
2202 static file_selection_data *fsd = 0;
2204 GtkFileSelection *selector = GTK_FILE_SELECTION(
2205 gtk_file_selection_new ("Please select the image directory."));
2208 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2210 fsd->widget = selector;
2213 if (p->image_directory && *p->image_directory)
2214 gtk_file_selection_set_filename (selector, p->image_directory);
2216 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2217 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2219 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2220 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2222 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2223 GTK_SIGNAL_FUNC (browse_image_dir_close),
2226 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2228 gtk_window_set_modal (GTK_WINDOW (selector), True);
2229 gtk_widget_show (GTK_WIDGET (selector));
2233 G_MODULE_EXPORT void
2234 browse_text_file_cb (GtkButton *button, gpointer user_data)
2236 state *s = global_state_kludge; /* I hate C so much... */
2237 saver_preferences *p = &s->prefs;
2238 static file_selection_data *fsd = 0;
2240 GtkFileSelection *selector = GTK_FILE_SELECTION(
2241 gtk_file_selection_new ("Please select a text file."));
2244 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2246 fsd->widget = selector;
2249 if (p->text_file && *p->text_file)
2250 gtk_file_selection_set_filename (selector, p->text_file);
2252 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2253 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2255 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2256 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2258 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2259 GTK_SIGNAL_FUNC (browse_image_dir_close),
2262 gtk_window_set_modal (GTK_WINDOW (selector), True);
2263 gtk_widget_show (GTK_WIDGET (selector));
2267 G_MODULE_EXPORT void
2268 browse_text_program_cb (GtkButton *button, gpointer user_data)
2270 state *s = global_state_kludge; /* I hate C so much... */
2271 saver_preferences *p = &s->prefs;
2272 static file_selection_data *fsd = 0;
2274 GtkFileSelection *selector = GTK_FILE_SELECTION(
2275 gtk_file_selection_new ("Please select a text-generating program."));
2278 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2280 fsd->widget = selector;
2283 if (p->text_program && *p->text_program)
2284 gtk_file_selection_set_filename (selector, p->text_program);
2286 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2287 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2289 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2290 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2292 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2293 GTK_SIGNAL_FUNC (browse_image_dir_close),
2296 gtk_window_set_modal (GTK_WINDOW (selector), True);
2297 gtk_widget_show (GTK_WIDGET (selector));
2304 G_MODULE_EXPORT void
2305 settings_cb (GtkButton *button, gpointer user_data)
2307 state *s = global_state_kludge; /* I hate C so much... */
2308 int list_elt = selected_list_element (s);
2310 populate_demo_window (s, list_elt); /* reset the widget */
2311 populate_popup_window (s); /* create UI on popup window */
2312 gtk_widget_show (s->popup_widget);
2316 settings_sync_cmd_text (state *s)
2319 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2320 char *cmd_line = get_configurator_command_line (s->cdata, False);
2321 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2322 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2324 # endif /* HAVE_XML */
2327 G_MODULE_EXPORT void
2328 settings_adv_cb (GtkButton *button, gpointer user_data)
2330 state *s = global_state_kludge; /* I hate C so much... */
2331 GtkNotebook *notebook =
2332 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2334 settings_sync_cmd_text (s);
2335 gtk_notebook_set_page (notebook, 1);
2338 G_MODULE_EXPORT void
2339 settings_std_cb (GtkButton *button, gpointer user_data)
2341 state *s = global_state_kludge; /* I hate C so much... */
2342 GtkNotebook *notebook =
2343 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2345 /* Re-create UI to reflect the in-progress command-line settings. */
2346 populate_popup_window (s);
2348 gtk_notebook_set_page (notebook, 0);
2351 G_MODULE_EXPORT void
2352 settings_reset_cb (GtkButton *button, gpointer user_data)
2355 state *s = global_state_kludge; /* I hate C so much... */
2356 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2357 char *cmd_line = get_configurator_command_line (s->cdata, True);
2358 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2359 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2361 populate_popup_window (s);
2362 # endif /* HAVE_XML */
2365 G_MODULE_EXPORT void
2366 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2367 gint page_num, gpointer user_data)
2369 state *s = global_state_kludge; /* I hate C so much... */
2370 GtkWidget *adv = name_to_widget (s, "adv_button");
2371 GtkWidget *std = name_to_widget (s, "std_button");
2375 gtk_widget_show (adv);
2376 gtk_widget_hide (std);
2378 else if (page_num == 1)
2380 gtk_widget_hide (adv);
2381 gtk_widget_show (std);
2389 G_MODULE_EXPORT void
2390 settings_cancel_cb (GtkButton *button, gpointer user_data)
2392 state *s = global_state_kludge; /* I hate C so much... */
2393 gtk_widget_hide (s->popup_widget);
2396 G_MODULE_EXPORT void
2397 settings_ok_cb (GtkButton *button, gpointer user_data)
2399 state *s = global_state_kludge; /* I hate C so much... */
2400 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2401 int page = gtk_notebook_get_current_page (notebook);
2404 /* Regenerate the command-line from the widget contents before saving.
2405 But don't do this if we're looking at the command-line page already,
2406 or we will blow away what they typed... */
2407 settings_sync_cmd_text (s);
2409 flush_popup_changes_and_save (s);
2410 gtk_widget_hide (s->popup_widget);
2414 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2416 state *s = (state *) data;
2417 settings_cancel_cb (0, (gpointer) s);
2423 /* Populating the various widgets
2427 /* Returns the number of the last hack run by the server.
2430 server_current_hack (void)
2434 unsigned long nitems, bytesafter;
2435 unsigned char *dataP = 0;
2436 Display *dpy = GDK_DISPLAY();
2437 int hack_number = -1;
2439 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2440 XA_SCREENSAVER_STATUS,
2441 0, 3, False, XA_INTEGER,
2442 &type, &format, &nitems, &bytesafter,
2445 && type == XA_INTEGER
2449 PROP32 *data = (PROP32 *) dataP;
2450 hack_number = (int) data[2] - 1;
2453 if (dataP) XFree (dataP);
2459 /* Finds the number of the last hack that was run, and makes that item be
2460 selected by default.
2463 scroll_to_current_hack (state *s)
2465 saver_preferences *p = &s->prefs;
2466 int hack_number = -1;
2468 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2469 hack_number = p->selected_hack;
2470 if (hack_number < 0) /* otherwise, use the last-run */
2471 hack_number = server_current_hack ();
2472 if (hack_number < 0) /* failing that, last "one mode" */
2473 hack_number = p->selected_hack;
2474 if (hack_number < 0) /* failing that, newest hack. */
2476 /* We should only get here if the user does not have a .xscreensaver
2477 file, and the screen has not been blanked with a hack since X
2478 started up: in other words, this is probably a fresh install.
2480 Instead of just defaulting to hack #0 (in either "programs" or
2481 "alphabetical" order) let's try to default to the last runnable
2482 hack in the "programs" list: this is probably the hack that was
2483 most recently added to the xscreensaver distribution (and so
2484 it's probably the currently-coolest one!)
2486 hack_number = p->screenhacks_count-1;
2487 while (hack_number > 0 &&
2488 ! (s->hacks_available_p[hack_number] &&
2489 p->screenhacks[hack_number]->enabled_p))
2493 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2495 int list_elt = s->hack_number_to_list_elt[hack_number];
2496 GtkWidget *list = name_to_widget (s, "list");
2497 force_list_select_item (s, list, list_elt, True);
2498 populate_demo_window (s, list_elt);
2504 populate_hack_list (state *s)
2506 Display *dpy = GDK_DISPLAY();
2508 saver_preferences *p = &s->prefs;
2509 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2510 GtkListStore *model;
2511 GtkTreeSelection *selection;
2512 GtkCellRenderer *ren;
2516 g_object_get (G_OBJECT (list),
2521 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2522 g_object_set (G_OBJECT (list), "model", model, NULL);
2523 g_object_unref (model);
2525 ren = gtk_cell_renderer_toggle_new ();
2526 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2528 "active", COL_ENABLED,
2531 g_signal_connect (ren, "toggled",
2532 G_CALLBACK (list_checkbox_cb),
2535 ren = gtk_cell_renderer_text_new ();
2536 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2537 _("Screen Saver"), ren,
2541 g_signal_connect_after (list, "row_activated",
2542 G_CALLBACK (list_activated_cb),
2545 selection = gtk_tree_view_get_selection (list);
2546 g_signal_connect (selection, "changed",
2547 G_CALLBACK (list_select_changed_cb),
2552 for (i = 0; i < s->list_count; i++)
2554 int hack_number = s->list_elt_to_hack_number[i];
2555 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2557 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2559 if (!hack) continue;
2561 /* If we're to suppress uninstalled hacks, check $PATH now. */
2562 if (p->ignore_uninstalled_p && !available_p)
2565 pretty_name = (hack->name
2566 ? strdup (hack->name)
2567 : make_hack_name (dpy, hack->command));
2571 /* Make the text foreground be the color of insensitive widgets
2572 (but don't actually make it be insensitive, since we still
2573 want to be able to click on it.)
2575 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
2576 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2577 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2578 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2580 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2581 /* " background=\"#%02X%02X%02X\"" */
2583 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2584 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2590 gtk_list_store_append (model, &iter);
2591 gtk_list_store_set (model, &iter,
2592 COL_ENABLED, hack->enabled_p,
2593 COL_NAME, pretty_name,
2598 #else /* !HAVE_GTK2 */
2600 saver_preferences *p = &s->prefs;
2601 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2603 for (i = 0; i < s->list_count; i++)
2605 int hack_number = s->list_elt_to_hack_number[i];
2606 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2608 /* A GtkList must contain only GtkListItems, but those can contain
2609 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2610 and a Label. We handle single and double click events on the
2611 line itself, for clicking on the text, but the interior checkbox
2612 also handles its own events.
2615 GtkWidget *line_hbox;
2616 GtkWidget *line_check;
2617 GtkWidget *line_label;
2619 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2621 if (!hack) continue;
2623 /* If we're to suppress uninstalled hacks, check $PATH now. */
2624 if (p->ignore_uninstalled_p && !available_p)
2627 pretty_name = (hack->name
2628 ? strdup (hack->name)
2629 : make_hack_name (hack->command));
2631 line = gtk_list_item_new ();
2632 line_hbox = gtk_hbox_new (FALSE, 0);
2633 line_check = gtk_check_button_new ();
2634 line_label = gtk_label_new (pretty_name);
2636 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2637 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2638 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2640 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2642 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2644 gtk_widget_show (line_check);
2645 gtk_widget_show (line_label);
2646 gtk_widget_show (line_hbox);
2647 gtk_widget_show (line);
2651 gtk_container_add (GTK_CONTAINER (list), line);
2652 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2653 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2656 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2657 GTK_SIGNAL_FUNC (list_checkbox_cb),
2660 gtk_widget_show (line);
2664 /* Make the widget be colored like insensitive widgets
2665 (but don't actually make it be insensitive, since we
2666 still want to be able to click on it.)
2668 GtkRcStyle *rc_style;
2671 gtk_widget_realize (GTK_WIDGET (line_label));
2673 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2674 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2676 rc_style = gtk_rc_style_new ();
2677 rc_style->fg[GTK_STATE_NORMAL] = fg;
2678 rc_style->bg[GTK_STATE_NORMAL] = bg;
2679 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2681 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2682 gtk_rc_style_unref (rc_style);
2686 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2687 GTK_SIGNAL_FUNC (list_select_cb),
2689 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2690 GTK_SIGNAL_FUNC (list_unselect_cb),
2692 #endif /* !HAVE_GTK2 */
2696 update_list_sensitivity (state *s)
2698 saver_preferences *p = &s->prefs;
2699 Bool sensitive = (p->mode == RANDOM_HACKS ||
2700 p->mode == RANDOM_HACKS_SAME ||
2701 p->mode == ONE_HACK);
2702 Bool checkable = (p->mode == RANDOM_HACKS ||
2703 p->mode == RANDOM_HACKS_SAME);
2704 Bool blankable = (p->mode != DONT_BLANK);
2707 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2708 GtkWidget *use = name_to_widget (s, "use_col_frame");
2709 #endif /* HAVE_GTK2 */
2710 GtkWidget *scroller = name_to_widget (s, "scroller");
2711 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2712 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2715 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2716 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2717 #else /* !HAVE_GTK2 */
2718 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2719 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2721 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2722 #endif /* !HAVE_GTK2 */
2723 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2724 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2726 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2729 gtk_tree_view_column_set_visible (use, checkable);
2730 #else /* !HAVE_GTK2 */
2732 gtk_widget_show (use); /* the "Use" column header */
2734 gtk_widget_hide (use);
2738 GtkBin *line = GTK_BIN (kids->data);
2739 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2740 GtkWidget *line_check =
2741 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2744 gtk_widget_show (line_check);
2746 gtk_widget_hide (line_check);
2750 #endif /* !HAVE_GTK2 */
2755 populate_prefs_page (state *s)
2757 saver_preferences *p = &s->prefs;
2759 Bool can_lock_p = True;
2761 /* Disable all the "lock" controls if locking support was not provided
2762 at compile-time, or if running on MacOS. */
2763 # if defined(NO_LOCKING) || defined(__APPLE__)
2768 /* If there is only one screen, the mode menu contains
2769 "random" but not "random-same".
2771 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2772 p->mode = RANDOM_HACKS;
2775 /* The file supports timeouts of less than a minute, but the GUI does
2776 not, so throttle the values to be at least one minute (since "0" is
2777 a bad rounding choice...)
2779 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2782 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2785 # define FMT_MINUTES(NAME,N) \
2786 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2788 # define FMT_SECONDS(NAME,N) \
2789 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2791 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2792 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2793 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2794 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2795 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2796 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2797 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2802 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2803 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2806 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2808 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2809 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2810 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2812 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2813 TOGGLE_ACTIVE ("dpms_quickoff_button", p->dpms_quickoff_p);
2814 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2815 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2816 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2817 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2818 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2819 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2823 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2824 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2825 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2826 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2827 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2830 # undef TOGGLE_ACTIVE
2832 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2833 (p->image_directory ? p->image_directory : ""));
2834 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2836 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2839 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2840 (p->text_literal ? p->text_literal : ""));
2841 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2842 (p->text_file ? p->text_file : ""));
2843 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2844 (p->text_program ? p->text_program : ""));
2845 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2846 (p->text_url ? p->text_url : ""));
2848 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2849 p->tmode == TEXT_LITERAL);
2850 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2851 p->tmode == TEXT_FILE);
2852 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2853 p->tmode == TEXT_FILE);
2854 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2855 p->tmode == TEXT_PROGRAM);
2856 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2857 p->tmode == TEXT_PROGRAM);
2858 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2859 p->tmode == TEXT_URL);
2862 /* Map the `saver_mode' enum to mode menu to values. */
2864 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2867 for (i = 0; i < countof(mode_menu_order); i++)
2868 if (mode_menu_order[i] == p->mode)
2870 gtk_option_menu_set_history (opt, i);
2871 update_list_sensitivity (s);
2875 Bool found_any_writable_cells = False;
2876 Bool fading_possible = False;
2877 Bool dpms_supported = False;
2879 Display *dpy = GDK_DISPLAY();
2880 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2882 for (i = 0; i < nscreens; i++)
2884 Screen *s = ScreenOfDisplay (dpy, i);
2885 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2887 found_any_writable_cells = True;
2892 fading_possible = found_any_writable_cells;
2893 #ifdef HAVE_XF86VMODE_GAMMA
2894 fading_possible = True;
2897 #ifdef HAVE_DPMS_EXTENSION
2899 int op = 0, event = 0, error = 0;
2900 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2901 dpms_supported = True;
2903 #endif /* HAVE_DPMS_EXTENSION */
2906 # define SENSITIZE(NAME,SENSITIVEP) \
2907 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2909 /* Blanking and Locking
2911 SENSITIZE ("lock_button", can_lock_p);
2912 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2913 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2917 SENSITIZE ("dpms_frame", dpms_supported);
2918 SENSITIZE ("dpms_button", dpms_supported);
2919 SENSITIZE ("dpms_quickoff_button", dpms_supported);
2921 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2922 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2923 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2924 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2925 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2926 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2927 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2928 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2929 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2933 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2934 SENSITIZE ("install_button", found_any_writable_cells);
2935 SENSITIZE ("fade_button", fading_possible);
2936 SENSITIZE ("unfade_button", fading_possible);
2938 SENSITIZE ("fade_label", (fading_possible &&
2939 (p->fade_p || p->unfade_p)));
2940 SENSITIZE ("fade_spinbutton", (fading_possible &&
2941 (p->fade_p || p->unfade_p)));
2949 populate_popup_window (state *s)
2951 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2952 char *doc_string = 0;
2954 /* #### not in Gtk 1.2
2955 gtk_label_set_selectable (doc);
2961 free_conf_data (s->cdata);
2966 saver_preferences *p = &s->prefs;
2967 int list_elt = selected_list_element (s);
2968 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2969 ? s->list_elt_to_hack_number[list_elt]
2971 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2974 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2975 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2976 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2977 s->cdata = load_configurator (cmd_line, s->debug_p);
2978 if (s->cdata && s->cdata->widget)
2979 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2984 doc_string = (s->cdata
2985 ? s->cdata->description
2987 # else /* !HAVE_XML */
2988 doc_string = _("Descriptions not available: no XML support compiled in.");
2989 # endif /* !HAVE_XML */
2991 gtk_label_set_text (doc, (doc_string
2993 : _("No description available.")));
2998 sensitize_demo_widgets (state *s, Bool sensitive_p)
3000 const char *names[] = { "demo", "settings",
3001 "cmd_label", "cmd_text", "manual",
3002 "visual", "visual_combo" };
3004 for (i = 0; i < countof(names); i++)
3006 GtkWidget *w = name_to_widget (s, names[i]);
3007 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
3013 sensitize_menu_items (state *s, Bool force_p)
3015 static Bool running_p = False;
3016 static time_t last_checked = 0;
3017 time_t now = time ((time_t *) 0);
3018 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
3022 if (force_p || now > last_checked + 10) /* check every 10 seconds */
3024 running_p = xscreensaver_running_p (s);
3025 last_checked = time ((time_t *) 0);
3028 for (i = 0; i < countof(names); i++)
3030 GtkWidget *w = name_to_widget (s, names[i]);
3031 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
3036 /* When the File menu is de-posted after a "Restart Daemon" command,
3037 the window underneath doesn't repaint for some reason. I guess this
3038 is a bug in exposure handling in GTK or GDK. This works around it.
3041 force_dialog_repaint (state *s)
3044 /* Tell GDK to invalidate and repaint the whole window.
3046 GdkWindow *w = GET_WINDOW (s->toplevel_widget);
3047 GdkRegion *region = gdk_region_new ();
3049 rect.x = rect.y = 0;
3050 rect.width = rect.height = 32767;
3051 gdk_region_union_with_rect (region, &rect);
3052 gdk_window_invalidate_region (w, region, True);
3053 gdk_region_destroy (region);
3054 gdk_window_process_updates (w, True);
3056 /* Force the server to send an exposure event by creating and then
3057 destroying a window as a child of the top level shell.
3059 Display *dpy = GDK_DISPLAY();
3060 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
3062 XWindowAttributes xgwa;
3063 XGetWindowAttributes (dpy, parent, &xgwa);
3064 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
3065 XMapRaised (dpy, w);
3066 XDestroyWindow (dpy, w);
3072 /* Even though we've given these text fields a maximum number of characters,
3073 their default size is still about 30 characters wide -- so measure out
3074 a string in their font, and resize them to just fit that.
3077 fix_text_entry_sizes (state *s)
3081 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
3082 const char * const spinbuttons[] = {
3083 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3084 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3085 "dpms_off_spinbutton",
3086 "-fade_spinbutton" };
3090 for (i = 0; i < countof(spinbuttons); i++)
3092 const char *n = spinbuttons[i];
3094 while (*n == '-') n++, cols--;
3095 w = GTK_WIDGET (name_to_widget (s, n));
3096 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3097 gtk_widget_set_usize (w, width, -2);
3100 /* Now fix the width of the combo box.
3102 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3103 w = GTK_COMBO (w)->entry;
3104 width = gdk_string_width (w->style->font, "PseudoColor___");
3105 gtk_widget_set_usize (w, width, -2);
3107 /* Now fix the width of the file entry text.
3109 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3110 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3111 gtk_widget_set_usize (w, width, -2);
3113 /* Now fix the width of the command line text.
3115 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3116 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3117 gtk_widget_set_usize (w, width, -2);
3121 /* Now fix the height of the list widget:
3122 make it default to being around 10 text-lines high instead of 4.
3124 w = GTK_WIDGET (name_to_widget (s, "list"));
3128 int leading = 3; /* approximate is ok... */
3132 PangoFontMetrics *pain =
3133 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3134 gtk_widget_get_style (w)->font_desc,
3135 gtk_get_default_language ());
3136 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3137 pango_font_metrics_get_descent (pain));
3138 #else /* !HAVE_GTK2 */
3139 height = w->style->font->ascent + w->style->font->descent;
3140 #endif /* !HAVE_GTK2 */
3144 height += border * 2;
3145 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3146 gtk_widget_set_usize (w, -2, height);
3153 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3156 static char *up_arrow_xpm[] = {
3179 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3180 the end of the array (Gtk 1.2.5.) */
3181 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3182 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3185 static char *down_arrow_xpm[] = {
3208 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3209 the end of the array (Gtk 1.2.5.) */
3210 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3211 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3215 pixmapify_button (state *s, int down_p)
3219 GtkWidget *pixmapwid;
3223 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3224 style = gtk_widget_get_style (w);
3226 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3227 &style->bg[GTK_STATE_NORMAL],
3229 ? (gchar **) down_arrow_xpm
3230 : (gchar **) up_arrow_xpm));
3231 pixmapwid = gtk_pixmap_new (pixmap, mask);
3232 gtk_widget_show (pixmapwid);
3233 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3234 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3238 map_next_button_cb (GtkWidget *w, gpointer user_data)
3240 state *s = (state *) user_data;
3241 pixmapify_button (s, 1);
3245 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3247 state *s = (state *) user_data;
3248 pixmapify_button (s, 0);
3250 #endif /* !HAVE_GTK2 */
3254 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3258 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3259 GtkAllocation *allocation,
3263 GtkWidgetAuxInfo *aux_info;
3265 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3267 aux_info->width = allocation->width;
3268 aux_info->height = -2;
3272 gtk_widget_size_request (label, &req);
3275 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3278 eschew_gtk_lossage (GtkLabel *label)
3280 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3281 aux_info->width = GTK_WIDGET (label)->allocation.width;
3282 aux_info->height = -2;
3286 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3288 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3289 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3292 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3294 gtk_widget_queue_resize (GTK_WIDGET (label));
3296 #endif /* !HAVE_GTK2 */
3300 populate_demo_window (state *s, int list_elt)
3302 Display *dpy = GDK_DISPLAY();
3303 saver_preferences *p = &s->prefs;
3306 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3307 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3308 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3309 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3310 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3312 if (p->mode == BLANK_ONLY)
3315 pretty_name = strdup (_("Blank Screen"));
3316 schedule_preview (s, 0);
3318 else if (p->mode == DONT_BLANK)
3321 pretty_name = strdup (_("Screen Saver Disabled"));
3322 schedule_preview (s, 0);
3326 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3327 ? s->list_elt_to_hack_number[list_elt]
3329 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3333 ? strdup (hack->name)
3334 : make_hack_name (dpy, hack->command))
3338 schedule_preview (s, hack->command);
3340 schedule_preview (s, 0);
3344 pretty_name = strdup (_("Preview"));
3346 gtk_frame_set_label (frame1, _(pretty_name));
3347 gtk_frame_set_label (frame2, _(pretty_name));
3349 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3350 gtk_entry_set_position (cmd, 0);
3354 sprintf (title, _("%s: %.100s Settings"),
3355 progclass, (pretty_name ? pretty_name : "???"));
3356 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3359 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3361 ? (hack->visual && *hack->visual
3366 sensitize_demo_widgets (s, (hack ? True : False));
3368 if (pretty_name) free (pretty_name);
3370 ensure_selected_item_visible (list);
3372 s->_selected_list_element = list_elt;
3377 widget_deleter (GtkWidget *widget, gpointer data)
3379 /* #### Well, I want to destroy these widgets, but if I do that, they get
3380 referenced again, and eventually I get a SEGV. So instead of
3381 destroying them, I'll just hide them, and leak a bunch of memory
3382 every time the disk file changes. Go go go Gtk!
3384 #### Ok, that's a lie, I get a crash even if I just hide the widget
3385 and don't ever delete it. Fuck!
3388 gtk_widget_destroy (widget);
3390 gtk_widget_hide (widget);
3395 static char **sort_hack_cmp_names_kludge;
3397 sort_hack_cmp (const void *a, const void *b)
3403 int aa = *(int *) a;
3404 int bb = *(int *) b;
3405 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3406 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3407 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3413 initialize_sort_map (state *s)
3415 Display *dpy = GDK_DISPLAY();
3416 saver_preferences *p = &s->prefs;
3419 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3420 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3421 if (s->hacks_available_p) free (s->hacks_available_p);
3423 s->list_elt_to_hack_number = (int *)
3424 calloc (sizeof(int), p->screenhacks_count + 1);
3425 s->hack_number_to_list_elt = (int *)
3426 calloc (sizeof(int), p->screenhacks_count + 1);
3427 s->hacks_available_p = (Bool *)
3428 calloc (sizeof(Bool), p->screenhacks_count + 1);
3429 s->total_available = 0;
3431 /* Check which hacks actually exist on $PATH
3433 for (i = 0; i < p->screenhacks_count; i++)
3435 screenhack *hack = p->screenhacks[i];
3436 int on = on_path_p (hack->command) ? 1 : 0;
3437 s->hacks_available_p[i] = on;
3438 s->total_available += on;
3441 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3445 for (i = 0; i < p->screenhacks_count; i++)
3447 if (!p->ignore_uninstalled_p ||
3448 s->hacks_available_p[i])
3449 s->list_elt_to_hack_number[j++] = i;
3453 for (; j < p->screenhacks_count; j++)
3454 s->list_elt_to_hack_number[j] = -1;
3457 /* Generate list of sortable names (once)
3459 sort_hack_cmp_names_kludge = (char **)
3460 calloc (sizeof(char *), p->screenhacks_count);
3461 for (i = 0; i < p->screenhacks_count; i++)
3463 screenhack *hack = p->screenhacks[i];
3464 char *name = (hack->name && *hack->name
3465 ? strdup (hack->name)
3466 : make_hack_name (dpy, hack->command));
3468 for (str = name; *str; str++)
3469 *str = tolower(*str);
3470 sort_hack_cmp_names_kludge[i] = name;
3473 /* Sort list->hack map alphabetically
3475 qsort (s->list_elt_to_hack_number,
3476 p->screenhacks_count,
3477 sizeof(*s->list_elt_to_hack_number),
3482 for (i = 0; i < p->screenhacks_count; i++)
3483 free (sort_hack_cmp_names_kludge[i]);
3484 free (sort_hack_cmp_names_kludge);
3485 sort_hack_cmp_names_kludge = 0;
3487 /* Build inverse table */
3488 for (i = 0; i < p->screenhacks_count; i++)
3490 int n = s->list_elt_to_hack_number[i];
3492 s->hack_number_to_list_elt[n] = i;
3498 maybe_reload_init_file (state *s)
3500 Display *dpy = GDK_DISPLAY();
3501 saver_preferences *p = &s->prefs;
3504 static Bool reentrant_lock = False;
3505 if (reentrant_lock) return 0;
3506 reentrant_lock = True;
3508 if (init_file_changed_p (p))
3510 const char *f = init_file_name();
3515 if (!f || !*f) return 0;
3516 b = (char *) malloc (strlen(f) + 1024);
3519 "file \"%s\" has changed, reloading.\n"),
3521 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3524 load_init_file (dpy, p);
3525 initialize_sort_map (s);
3527 list_elt = selected_list_element (s);
3528 list = name_to_widget (s, "list");
3529 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3530 populate_hack_list (s);
3531 force_list_select_item (s, list, list_elt, True);
3532 populate_prefs_page (s);
3533 populate_demo_window (s, list_elt);
3534 ensure_selected_item_visible (list);
3539 reentrant_lock = False;
3545 /* Making the preview window have the right X visual (so that GL works.)
3548 static Visual *get_best_gl_visual (state *);
3551 x_visual_to_gdk_visual (Visual *xv)
3553 GList *gvs = gdk_list_visuals();
3554 if (!xv) return gdk_visual_get_system();
3555 for (; gvs; gvs = gvs->next)
3557 GdkVisual *gv = (GdkVisual *) gvs->data;
3558 if (xv == GDK_VISUAL_XVISUAL (gv))
3561 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3562 blurb(), (unsigned long) xv->visualid);
3567 clear_preview_window (state *s)
3573 if (!s->toplevel_widget) return; /* very early */
3574 p = name_to_widget (s, "preview");
3575 window = GET_WINDOW (p);
3577 if (!window) return;
3579 /* Flush the widget background down into the window, in case a subproc
3581 style = gtk_widget_get_style (p);
3582 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
3583 gdk_window_clear (window);
3586 int list_elt = selected_list_element (s);
3587 int hack_number = (list_elt >= 0
3588 ? s->list_elt_to_hack_number[list_elt]
3590 Bool available_p = (hack_number >= 0
3591 ? s->hacks_available_p [hack_number]
3593 Bool nothing_p = (s->total_available < 5);
3596 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3597 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3598 (s->running_preview_error_p
3599 ? (available_p ? 1 :
3602 #else /* !HAVE_GTK2 */
3603 if (s->running_preview_error_p)
3605 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3606 const char * const lines2[] = { N_("Not"), N_("Installed") };
3607 int nlines = countof(lines1);
3608 int lh = p->style->font->ascent + p->style->font->descent;
3612 const char * const *lines = (available_p ? lines1 : lines2);
3614 gdk_window_get_size (window, &w, &h);
3615 y = (h - (lh * nlines)) / 2;
3616 y += p->style->font->ascent;
3617 for (i = 0; i < nlines; i++)
3619 int sw = gdk_string_width (p->style->font, _(lines[i]));
3620 int x = (w - sw) / 2;
3621 gdk_draw_string (window, p->style->font,
3622 p->style->fg_gc[GTK_STATE_NORMAL],
3627 #endif /* !HAVE_GTK2 */
3635 reset_preview_window (state *s)
3637 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3638 when you kill one and re-start another on the same window. So maybe
3639 it's best to just always destroy and recreate the preview window
3640 when changing hacks, instead of always trying to reuse the same one?
3642 GtkWidget *pr = name_to_widget (s, "preview");
3643 if (GET_REALIZED (pr))
3645 GdkWindow *window = GET_WINDOW (pr);
3646 Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3648 gtk_widget_hide (pr);
3649 gtk_widget_unrealize (pr);
3650 gtk_widget_realize (pr);
3651 gtk_widget_show (pr);
3652 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3654 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3662 fix_preview_visual (state *s)
3664 GtkWidget *widget = name_to_widget (s, "preview");
3665 Visual *xvisual = get_best_gl_visual (s);
3666 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3667 GdkVisual *dvisual = gdk_visual_get_system();
3668 GdkColormap *cmap = (visual == dvisual
3669 ? gdk_colormap_get_system ()
3670 : gdk_colormap_new (visual, False));
3673 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3674 (visual == dvisual ? "default" : "non-default"),
3675 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3677 if (!GET_REALIZED (widget) ||
3678 gtk_widget_get_visual (widget) != visual)
3680 gtk_widget_unrealize (widget);
3681 gtk_widget_set_visual (widget, visual);
3682 gtk_widget_set_colormap (widget, cmap);
3683 gtk_widget_realize (widget);
3686 /* Set the Widget colors to be white-on-black. */
3688 GdkWindow *window = GET_WINDOW (widget);
3689 GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
3690 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3691 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3692 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3693 GdkGC *fgc = gdk_gc_new(window);
3694 GdkGC *bgc = gdk_gc_new(window);
3695 if (!gdk_color_white (cmap, fg)) abort();
3696 if (!gdk_color_black (cmap, bg)) abort();
3697 gdk_gc_set_foreground (fgc, fg);
3698 gdk_gc_set_background (fgc, bg);
3699 gdk_gc_set_foreground (bgc, bg);
3700 gdk_gc_set_background (bgc, fg);
3701 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3702 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3703 gtk_widget_set_style (widget, style);
3705 /* For debugging purposes, put a title on the window (so that
3706 it can be easily found in the output of "xwininfo -tree".)
3708 gdk_window_set_title (window, "Preview");
3711 gtk_widget_show (widget);
3719 subproc_pretty_name (state *s)
3721 if (s->running_preview_cmd)
3723 char *ps = strdup (s->running_preview_cmd);
3724 char *ss = strchr (ps, ' ');
3726 ss = strrchr (ps, '/');
3737 return strdup ("???");
3742 reap_zombies (state *s)
3744 int wait_status = 0;
3746 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3750 if (pid == s->running_preview_pid)
3752 char *ss = subproc_pretty_name (s);
3753 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3754 (unsigned long) pid, ss);
3758 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3759 (unsigned long) pid);
3765 /* Mostly lifted from driver/subprocs.c */
3767 get_best_gl_visual (state *s)
3769 Display *dpy = GDK_DISPLAY();
3778 av[ac++] = "xscreensaver-gl-helper";
3783 perror ("error creating pipe:");
3790 switch ((int) (forked = fork ()))
3794 sprintf (buf, "%s: couldn't fork", blurb());
3802 close (in); /* don't need this one */
3803 close (ConnectionNumber (dpy)); /* close display fd */
3805 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3807 perror ("could not dup() a new stdout:");
3811 execvp (av[0], av); /* shouldn't return. */
3813 if (errno != ENOENT)
3815 /* Ignore "no such file or directory" errors, unless verbose.
3816 Issue all other exec errors, though. */
3817 sprintf (buf, "%s: running %s", blurb(), av[0]);
3821 /* Note that one must use _exit() instead of exit() in procs forked
3822 off of Gtk programs -- Gtk installs an atexit handler that has a
3823 copy of the X connection (which we've already closed, for safety.)
3824 If one uses exit() instead of _exit(), then one sometimes gets a
3825 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3827 _exit (1); /* exits fork */
3833 int wait_status = 0;
3835 FILE *f = fdopen (in, "r");
3839 close (out); /* don't need this one */
3842 if (!fgets (buf, sizeof(buf)-1, f))
3846 /* Wait for the child to die. */
3847 waitpid (-1, &wait_status, 0);
3849 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3855 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3861 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3863 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3864 blurb(), av[0], result);
3876 kill_preview_subproc (state *s, Bool reset_p)
3878 s->running_preview_error_p = False;
3881 clear_preview_window (s);
3883 if (s->subproc_check_timer_id)
3885 gtk_timeout_remove (s->subproc_check_timer_id);
3886 s->subproc_check_timer_id = 0;
3887 s->subproc_check_countdown = 0;
3890 if (s->running_preview_pid)
3892 int status = kill (s->running_preview_pid, SIGTERM);
3893 char *ss = subproc_pretty_name (s);
3900 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3901 blurb(), (unsigned long) s->running_preview_pid, ss);
3906 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3907 blurb(), (unsigned long) s->running_preview_pid, ss);
3913 waitpid(s->running_preview_pid, &endstatus, 0);
3915 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3916 (unsigned long) s->running_preview_pid, ss);
3920 s->running_preview_pid = 0;
3921 if (s->running_preview_cmd) free (s->running_preview_cmd);
3922 s->running_preview_cmd = 0;
3929 reset_preview_window (s);
3930 clear_preview_window (s);
3935 /* Immediately and unconditionally launches the given process,
3936 after appending the -window-id option; sets running_preview_pid.
3939 launch_preview_subproc (state *s)
3941 saver_preferences *p = &s->prefs;
3945 const char *cmd = s->desired_preview_cmd;
3947 GtkWidget *pr = name_to_widget (s, "preview");
3950 reset_preview_window (s);
3952 window = GET_WINDOW (pr);
3954 s->running_preview_error_p = False;
3956 if (s->preview_suppressed_p)
3958 kill_preview_subproc (s, False);
3962 new_cmd = malloc (strlen (cmd) + 40);
3964 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3967 /* No window id? No command to run. */
3973 strcpy (new_cmd, cmd);
3974 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3978 kill_preview_subproc (s, False);
3981 s->running_preview_error_p = True;
3982 clear_preview_window (s);
3986 switch ((int) (forked = fork ()))
3991 sprintf (buf, "%s: couldn't fork", blurb());
3993 s->running_preview_error_p = True;
3999 close (ConnectionNumber (GDK_DISPLAY()));
4001 hack_subproc_environment (id, s->debug_p);
4003 usleep (250000); /* pause for 1/4th second before launching, to give
4004 the previous program time to die and flush its X
4005 buffer, so we don't get leftover turds on the
4008 exec_command (p->shell, new_cmd, p->nice_inferior);
4009 /* Don't bother printing an error message when we are unable to
4010 exec subprocesses; we handle that by polling the pid later.
4012 Note that one must use _exit() instead of exit() in procs forked
4013 off of Gtk programs -- Gtk installs an atexit handler that has a
4014 copy of the X connection (which we've already closed, for safety.)
4015 If one uses exit() instead of _exit(), then one sometimes gets a
4016 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
4018 _exit (1); /* exits child fork */
4023 if (s->running_preview_cmd) free (s->running_preview_cmd);
4024 s->running_preview_cmd = strdup (s->desired_preview_cmd);
4025 s->running_preview_pid = forked;
4029 char *ss = subproc_pretty_name (s);
4030 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
4031 (unsigned long) forked, ss);
4038 schedule_preview_check (s);
4041 if (new_cmd) free (new_cmd);
4046 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
4049 hack_environment (state *s)
4051 static const char *def_path =
4052 # ifdef DEFAULT_PATH_PREFIX
4053 DEFAULT_PATH_PREFIX;
4058 Display *dpy = GDK_DISPLAY();
4059 const char *odpy = DisplayString (dpy);
4060 char *ndpy = (char *) malloc(strlen(odpy) + 20);
4061 strcpy (ndpy, "DISPLAY=");
4062 strcat (ndpy, odpy);
4067 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4069 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4070 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4071 So we must leak it (and/or the previous setting). Yay.
4074 if (def_path && *def_path)
4076 const char *opath = getenv("PATH");
4077 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4078 strcpy (npath, "PATH=");
4079 strcat (npath, def_path);
4080 strcat (npath, ":");
4081 strcat (npath, opath);
4085 /* do not free(npath) -- see above */
4088 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4094 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4096 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4097 necessary yet, but it will make programs work if we had invoked
4098 them with "-root" and not with "-window-id" -- which, of course,
4101 char *nssw = (char *) malloc (40);
4102 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4104 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4105 any more, right? It's not Posix, but everyone seems to have it. */
4110 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4112 /* do not free(nssw) -- see above */
4116 /* Called from a timer:
4117 Launches the currently-chosen subprocess, if it's not already running.
4118 If there's a different process running, kills it.
4121 update_subproc_timer (gpointer data)
4123 state *s = (state *) data;
4124 if (! s->desired_preview_cmd)
4125 kill_preview_subproc (s, True);
4126 else if (!s->running_preview_cmd ||
4127 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4128 launch_preview_subproc (s);
4130 s->subproc_timer_id = 0;
4131 return FALSE; /* do not re-execute timer */
4135 settings_timer (gpointer data)
4142 /* Call this when you think you might want a preview process running.
4143 It will set a timer that will actually launch that program a second
4144 from now, if you haven't changed your mind (to avoid double-click
4145 spazzing, etc.) `cmd' may be null meaning "no process".
4148 schedule_preview (state *s, const char *cmd)
4150 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4155 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4157 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4160 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4161 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4163 if (s->subproc_timer_id)
4164 gtk_timeout_remove (s->subproc_timer_id);
4165 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4169 /* Called from a timer:
4170 Checks to see if the subproc that should be running, actually is.
4173 check_subproc_timer (gpointer data)
4175 state *s = (state *) data;
4176 Bool again_p = True;
4178 if (s->running_preview_error_p || /* already dead */
4179 s->running_preview_pid <= 0)
4187 status = kill (s->running_preview_pid, 0);
4188 if (status < 0 && errno == ESRCH)
4189 s->running_preview_error_p = True;
4193 char *ss = subproc_pretty_name (s);
4194 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4195 (unsigned long) s->running_preview_pid, ss,
4196 (s->running_preview_error_p ? "dead" : "alive"));
4200 if (s->running_preview_error_p)
4202 clear_preview_window (s);
4207 /* Otherwise, it's currently alive. We might be checking again, or we
4208 might be satisfied. */
4210 if (--s->subproc_check_countdown <= 0)
4214 return TRUE; /* re-execute timer */
4217 s->subproc_check_timer_id = 0;
4218 s->subproc_check_countdown = 0;
4219 return FALSE; /* do not re-execute timer */
4224 /* Call this just after launching a subprocess.
4225 This sets a timer that will, five times a second for two seconds,
4226 check whether the program is still running. The assumption here
4227 is that if the process didn't stay up for more than a couple of
4228 seconds, then either the program doesn't exist, or it doesn't
4229 take a -window-id argument.
4232 schedule_preview_check (state *s)
4238 fprintf (stderr, "%s: scheduling check\n", blurb());
4240 if (s->subproc_check_timer_id)
4241 gtk_timeout_remove (s->subproc_check_timer_id);
4242 s->subproc_check_timer_id =
4243 gtk_timeout_add (1000 / ticks,
4244 check_subproc_timer, (gpointer) s);
4245 s->subproc_check_countdown = ticks * seconds;
4250 screen_blanked_p (void)
4254 unsigned long nitems, bytesafter;
4255 unsigned char *dataP = 0;
4256 Display *dpy = GDK_DISPLAY();
4257 Bool blanked_p = False;
4259 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4260 XA_SCREENSAVER_STATUS,
4261 0, 3, False, XA_INTEGER,
4262 &type, &format, &nitems, &bytesafter,
4265 && type == XA_INTEGER
4269 Atom *data = (Atom *) dataP;
4270 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4273 if (dataP) XFree (dataP);
4278 /* Wake up every now and then and see if the screen is blanked.
4279 If it is, kill off the small-window demo -- no point in wasting
4280 cycles by running two screensavers at once...
4283 check_blanked_timer (gpointer data)
4285 state *s = (state *) data;
4286 Bool blanked_p = screen_blanked_p ();
4287 if (blanked_p && s->running_preview_pid)
4290 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4291 kill_preview_subproc (s, True);
4294 return True; /* re-execute timer */
4298 /* How many screens are there (including Xinerama.)
4301 screen_count (Display *dpy)
4303 int nscreens = ScreenCount(dpy);
4304 # ifdef HAVE_XINERAMA
4307 int event_number, error_number;
4308 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4309 XineramaIsActive (dpy))
4311 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4312 if (xsi) XFree (xsi);
4315 # endif /* HAVE_XINERAMA */
4321 /* Setting window manager icon
4325 init_icon (GdkWindow *window)
4327 GdkBitmap *mask = 0;
4329 gdk_pixmap_create_from_xpm_d (window, &mask, 0,
4330 (gchar **) logo_50_xpm);
4332 gdk_window_set_icon (window, 0, pixmap, mask);
4336 /* The main demo-mode command loop.
4341 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4342 XrmRepresentation *type, XrmValue *value, XPointer closure)
4345 for (i = 0; quarks[i]; i++)
4347 if (bindings[i] == XrmBindTightly)
4348 fprintf (stderr, (i == 0 ? "" : "."));
4349 else if (bindings[i] == XrmBindLoosely)
4350 fprintf (stderr, "*");
4352 fprintf (stderr, " ??? ");
4353 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4356 fprintf (stderr, ": %s\n", (char *) value->addr);
4364 gnome_screensaver_window (Screen *screen)
4366 Display *dpy = DisplayOfScreen (screen);
4367 Window root = RootWindowOfScreen (screen);
4368 Window parent, *kids;
4370 Window gnome_window = 0;
4373 if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4375 for (i = 0; i < nkids; i++)
4379 unsigned long nitems, bytesafter;
4380 unsigned char *name;
4381 if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4382 False, XA_STRING, &type, &format, &nitems,
4386 && !strcmp ((char *) name, "gnome-screensaver"))
4388 gnome_window = kids[i];
4393 if (kids) XFree ((char *) kids);
4394 return gnome_window;
4398 gnome_screensaver_active_p (void)
4400 Display *dpy = GDK_DISPLAY();
4401 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4402 return (w ? True : False);
4406 kill_gnome_screensaver (void)
4408 Display *dpy = GDK_DISPLAY();
4409 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4410 if (w) XKillClient (dpy, (XID) w);
4414 kde_screensaver_active_p (void)
4416 FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4419 fgets (buf, sizeof(buf)-1, p);
4421 if (!strcmp (buf, "true\n"))
4428 kill_kde_screensaver (void)
4430 system ("dcop kdesktop KScreensaverIface enable false");
4435 the_network_is_not_the_computer (state *s)
4437 Display *dpy = GDK_DISPLAY();
4438 char *rversion = 0, *ruser = 0, *rhost = 0;
4439 char *luser, *lhost;
4441 struct passwd *p = getpwuid (getuid ());
4442 const char *d = DisplayString (dpy);
4444 # if defined(HAVE_UNAME)
4446 if (uname (&uts) < 0)
4447 lhost = "<UNKNOWN>";
4449 lhost = uts.nodename;
4451 strcpy (lhost, getenv("SYS$NODE"));
4452 # else /* !HAVE_UNAME && !VMS */
4453 strcat (lhost, "<UNKNOWN>");
4454 # endif /* !HAVE_UNAME && !VMS */
4456 if (p && p->pw_name)
4461 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4463 /* Make a buffer that's big enough for a number of copies of all the
4464 strings, plus some. */
4465 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4466 (ruser ? strlen(ruser) : 0) +
4467 (rhost ? strlen(rhost) : 0) +
4474 if (!rversion || !*rversion)
4478 "The XScreenSaver daemon doesn't seem to be running\n"
4479 "on display \"%s\". Launch it now?"),
4482 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4484 /* Warn that the two processes are running as different users.
4488 "%s is running as user \"%s\" on host \"%s\".\n"
4489 "But the xscreensaver managing display \"%s\"\n"
4490 "is running as user \"%s\" on host \"%s\".\n"
4492 "Since they are different users, they won't be reading/writing\n"
4493 "the same ~/.xscreensaver file, so %s isn't\n"
4494 "going to work right.\n"
4496 "You should either re-run %s as \"%s\", or re-run\n"
4497 "xscreensaver as \"%s\".\n"
4499 "Restart the xscreensaver daemon now?\n"),
4500 progname, luser, lhost,
4502 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4504 progname, (ruser ? ruser : "???"),
4507 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4509 /* Warn that the two processes are running on different hosts.
4513 "%s is running as user \"%s\" on host \"%s\".\n"
4514 "But the xscreensaver managing display \"%s\"\n"
4515 "is running as user \"%s\" on host \"%s\".\n"
4517 "If those two machines don't share a file system (that is,\n"
4518 "if they don't see the same ~%s/.xscreensaver file) then\n"
4519 "%s won't work right.\n"
4521 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4522 progname, luser, lhost,
4524 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4529 else if (!!strcmp (rversion, s->short_version))
4531 /* Warn that the version numbers don't match.
4535 "This is %s version %s.\n"
4536 "But the xscreensaver managing display \"%s\"\n"
4537 "is version %s. This could cause problems.\n"
4539 "Restart the xscreensaver daemon now?\n"),
4540 progname, s->short_version,
4547 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4549 if (rversion) free (rversion);
4550 if (ruser) free (ruser);
4551 if (rhost) free (rhost);
4555 /* Note: since these dialogs are not modal, they will stack up.
4556 So we do this check *after* popping up the "xscreensaver is not
4557 running" dialog so that these are on top. Good enough.
4560 if (gnome_screensaver_active_p ())
4561 warning_dialog (s->toplevel_widget,
4563 "The GNOME screensaver daemon appears to be running.\n"
4564 "It must be stopped for XScreenSaver to work properly.\n"
4566 "Stop the GNOME screen saver daemon now?\n"),
4569 if (kde_screensaver_active_p ())
4570 warning_dialog (s->toplevel_widget,
4572 "The KDE screen saver daemon appears to be running.\n"
4573 "It must be stopped for XScreenSaver to work properly.\n"
4575 "Stop the KDE screen saver daemon now?\n"),
4580 /* We use this error handler so that X errors are preceeded by the name
4581 of the program that generated them.
4584 demo_ehandler (Display *dpy, XErrorEvent *error)
4586 state *s = global_state_kludge; /* I hate C so much... */
4587 fprintf (stderr, "\nX error in %s:\n", blurb());
4588 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4589 kill_preview_subproc (s, False);
4595 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4596 of the program that generated them; and also that we can ignore one
4597 particular bogus error message that Gdk madly spews.
4600 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4601 const gchar *message, gpointer user_data)
4603 /* Ignore the message "Got event for unknown window: 0x...".
4604 Apparently some events are coming in for the xscreensaver window
4605 (presumably reply events related to the ClientMessage) and Gdk
4606 feels the need to complain about them. So, just suppress any
4607 messages that look like that one.
4609 if (strstr (message, "unknown window"))
4612 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4613 (log_domain ? log_domain : progclass),
4614 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4615 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4616 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4617 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4618 log_level == G_LOG_LEVEL_INFO ? "info" :
4619 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4621 ((!*message || message[strlen(message)-1] != '\n')
4627 static char *defaults[] = {
4628 #include "XScreenSaver_ad.h"
4633 #ifdef HAVE_CRAPPLET
4634 static struct poptOption crapplet_options[] = {
4635 {NULL, '\0', 0, NULL, 0}
4637 #endif /* HAVE_CRAPPLET */
4640 const char *usage = "[--display dpy] [--prefs | --settings]"
4641 # ifdef HAVE_CRAPPLET
4644 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4647 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4649 state *s = (state *) user_data;
4650 Boolean oi = s->initializing_p;
4652 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4654 s->initializing_p = True;
4656 eschew_gtk_lossage (label);
4658 s->initializing_p = oi;
4664 print_widget_tree (GtkWidget *w, int depth)
4667 for (i = 0; i < depth; i++)
4668 fprintf (stderr, " ");
4669 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4671 if (GTK_IS_LIST (w))
4673 for (i = 0; i < depth+1; i++)
4674 fprintf (stderr, " ");
4675 fprintf (stderr, "...list kids...\n");
4677 else if (GTK_IS_CONTAINER (w))
4679 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4682 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4690 delayed_scroll_kludge (gpointer data)
4692 state *s = (state *) data;
4693 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4694 ensure_selected_item_visible (w);
4696 /* Oh, this is just fucking lovely, too. */
4697 w = GTK_WIDGET (name_to_widget (s, "preview"));
4698 gtk_widget_hide (w);
4699 gtk_widget_show (w);
4701 return FALSE; /* do not re-execute timer */
4707 create_xscreensaver_demo (void)
4711 nb = name_to_widget (global_state_kludge, "preview_notebook");
4712 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4714 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4718 create_xscreensaver_settings_dialog (void)
4722 box = name_to_widget (global_state_kludge, "dialog_action_area");
4724 w = name_to_widget (global_state_kludge, "adv_button");
4725 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4727 w = name_to_widget (global_state_kludge, "std_button");
4728 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4730 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4733 #endif /* HAVE_GTK2 */
4736 main (int argc, char **argv)
4740 saver_preferences *p;
4741 Bool prefs_p = False;
4742 Bool settings_p = False;
4745 Widget toplevel_shell;
4746 char *real_progname = argv[0];
4749 Bool crapplet_p = False;
4753 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4754 textdomain (GETTEXT_PACKAGE);
4757 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4758 # else /* !HAVE_GTK2 */
4759 if (!setlocale (LC_ALL, ""))
4760 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4761 # endif /* !HAVE_GTK2 */
4763 #endif /* ENABLE_NLS */
4765 str = strrchr (real_progname, '/');
4766 if (str) real_progname = str+1;
4769 memset (s, 0, sizeof(*s));
4770 s->initializing_p = True;
4773 global_state_kludge = s; /* I hate C so much... */
4775 progname = real_progname;
4777 s->short_version = (char *) malloc (5);
4778 memcpy (s->short_version, screensaver_id + 17, 4);
4779 s->short_version [4] = 0;
4782 /* Register our error message logger for every ``log domain'' known.
4783 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4784 for all of the domains that seem to be in use.
4787 const char * const domains[] = { 0,
4788 "Gtk", "Gdk", "GLib", "GModule",
4789 "GThread", "Gnome", "GnomeUI" };
4790 for (i = 0; i < countof(domains); i++)
4791 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4794 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4797 const char *dir = DEFAULT_ICONDIR;
4798 if (*dir) add_pixmap_directory (dir);
4800 # endif /* !HAVE_GTK2 */
4801 #endif /* DEFAULT_ICONDIR */
4803 /* This is gross, but Gtk understands --display and not -display...
4805 for (i = 1; i < argc; i++)
4806 if (argv[i][0] && argv[i][1] &&
4807 !strncmp(argv[i], "-display", strlen(argv[i])))
4808 argv[i] = "--display";
4811 /* We need to parse this arg really early... Sigh. */
4812 for (i = 1; i < argc; i++)
4815 (!strcmp(argv[i], "--crapplet") ||
4816 !strcmp(argv[i], "--capplet")))
4818 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4821 for (j = i; j < argc; j++) /* remove it from the list */
4822 argv[j] = argv[j+1];
4824 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4825 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4827 fprintf (stderr, "%s: %s\n", real_progname, usage);
4829 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4832 (!strcmp(argv[i], "--debug") ||
4833 !strcmp(argv[i], "-debug") ||
4834 !strcmp(argv[i], "-d")))
4838 for (j = i; j < argc; j++) /* remove it from the list */
4839 argv[j] = argv[j+1];
4846 (!strcmp(argv[i], "-geometry") ||
4847 !strcmp(argv[i], "-geom") ||
4848 !strcmp(argv[i], "-geo") ||
4849 !strcmp(argv[i], "-g")))
4853 for (j = i; j < argc; j++) /* remove them from the list */
4854 argv[j] = argv[j+2];
4861 (!strcmp(argv[i], "--configdir")))
4865 hack_configuration_path = argv[i+1];
4866 for (j = i; j < argc; j++) /* remove them from the list */
4867 argv[j] = argv[j+2];
4871 if (0 != stat (hack_configuration_path, &st))
4874 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4878 else if (!S_ISDIR (st.st_mode))
4880 fprintf (stderr, "%s: not a directory: %s\n",
4881 blurb(), hack_configuration_path);
4889 fprintf (stderr, "%s: using config directory \"%s\"\n",
4890 progname, hack_configuration_path);
4893 /* Let Gtk open the X connection, then initialize Xt to use that
4894 same connection. Doctor Frankenstein would be proud.
4896 # ifdef HAVE_CRAPPLET
4899 GnomeClient *client;
4900 GnomeClientFlags flags = 0;
4902 int init_results = gnome_capplet_init ("screensaver-properties",
4904 argc, argv, NULL, 0, NULL);
4906 0 upon successful initialization;
4907 1 if --init-session-settings was passed on the cmdline;
4908 2 if --ignore was passed on the cmdline;
4911 So the 1 signifies just to init the settings, and quit, basically.
4912 (Meaning launch the xscreensaver daemon.)
4915 if (init_results < 0)
4918 g_error ("An initialization error occurred while "
4919 "starting xscreensaver-capplet.\n");
4921 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4922 real_progname, init_results);
4927 client = gnome_master_client ();
4930 flags = gnome_client_get_flags (client);
4932 if (flags & GNOME_CLIENT_IS_CONNECTED)
4935 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4936 gnome_client_get_id (client));
4939 char *session_args[20];
4941 session_args[i++] = real_progname;
4942 session_args[i++] = "--capplet";
4943 session_args[i++] = "--init-session-settings";
4944 session_args[i] = 0;
4945 gnome_client_set_priority (client, 20);
4946 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4947 gnome_client_set_restart_command (client, i, session_args);
4951 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4954 gnome_client_flush (client);
4957 if (init_results == 1)
4959 system ("xscreensaver -nosplash &");
4965 # endif /* HAVE_CRAPPLET */
4967 gtk_init (&argc, &argv);
4971 /* We must read exactly the same resources as xscreensaver.
4972 That means we must have both the same progclass *and* progname,
4973 at least as far as the resource database is concerned. So,
4974 put "xscreensaver" in argv[0] while initializing Xt.
4976 argv[0] = "xscreensaver";
4980 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4982 XtToolkitInitialize ();
4983 app = XtCreateApplicationContext ();
4984 dpy = GDK_DISPLAY();
4985 XtAppSetFallbackResources (app, defaults);
4986 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4987 toplevel_shell = XtAppCreateShell (progname, progclass,
4988 applicationShellWidgetClass,
4991 dpy = XtDisplay (toplevel_shell);
4992 db = XtDatabase (dpy);
4993 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4994 XSetErrorHandler (demo_ehandler);
4996 /* Let's just ignore these. They seem to confuse Irix Gtk... */
4997 signal (SIGPIPE, SIG_IGN);
4999 /* After doing Xt-style command-line processing, complain about any
5000 unrecognized command-line arguments.
5002 for (i = 1; i < argc; i++)
5004 char *str = argv[i];
5005 if (str[0] == '-' && str[1] == '-')
5007 if (!strcmp (str, "-prefs"))
5009 else if (!strcmp (str, "-settings"))
5011 else if (crapplet_p)
5012 /* There are lots of random args that we don't care about when we're
5013 started as a crapplet, so just ignore unknown args in that case. */
5017 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
5019 fprintf (stderr, "%s: %s\n", real_progname, usage);
5024 /* Load the init file, which may end up consulting the X resource database
5025 and the site-wide app-defaults file. Note that at this point, it's
5026 important that `progname' be "xscreensaver", rather than whatever
5030 s->nscreens = screen_count (dpy);
5032 hack_environment (s); /* must be before initialize_sort_map() */
5034 load_init_file (dpy, p);
5035 initialize_sort_map (s);
5037 /* Now that Xt has been initialized, and the resources have been read,
5038 we can set our `progname' variable to something more in line with
5041 progname = real_progname;
5045 /* Print out all the resources we read. */
5047 XrmName name = { 0 };
5048 XrmClass class = { 0 };
5050 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
5056 /* Intern the atoms that xscreensaver_command() needs.
5058 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
5059 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
5060 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
5061 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5062 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5063 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5064 XA_SELECT = XInternAtom (dpy, "SELECT", False);
5065 XA_DEMO = XInternAtom (dpy, "DEMO", False);
5066 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5067 XA_BLANK = XInternAtom (dpy, "BLANK", False);
5068 XA_LOCK = XInternAtom (dpy, "LOCK", False);
5069 XA_EXIT = XInternAtom (dpy, "EXIT", False);
5070 XA_RESTART = XInternAtom (dpy, "RESTART", False);
5073 /* Create the window and all its widgets.
5075 s->base_widget = create_xscreensaver_demo ();
5076 s->popup_widget = create_xscreensaver_settings_dialog ();
5077 s->toplevel_widget = s->base_widget;
5080 /* Set the main window's title. */
5082 char *base_title = _("Screensaver Preferences");
5083 char *v = (char *) strdup(strchr(screensaver_id, ' '));
5084 char *s1, *s2, *s3, *s4;
5085 s1 = (char *) strchr(v, ' '); s1++;
5086 s2 = (char *) strchr(s1, ' ');
5087 s3 = (char *) strchr(v, '('); s3++;
5088 s4 = (char *) strchr(s3, ')');
5092 window_title = (char *) malloc (strlen (base_title) +
5093 strlen (progclass) +
5094 strlen (s1) + strlen (s3) +
5096 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
5097 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5098 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
5102 /* Adjust the (invisible) notebooks on the popup dialog... */
5104 GtkNotebook *notebook =
5105 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5106 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5110 gtk_widget_hide (std);
5111 # else /* !HAVE_XML */
5112 /* Make the advanced page be the only one available. */
5113 gtk_widget_set_sensitive (std, False);
5114 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5115 gtk_widget_hide (std);
5116 std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5117 gtk_widget_hide (std);
5119 # endif /* !HAVE_XML */
5121 gtk_notebook_set_page (notebook, page);
5122 gtk_notebook_set_show_tabs (notebook, False);
5125 /* Various other widget initializations...
5127 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5128 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5130 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5131 GTK_SIGNAL_FUNC (wm_popup_close_cb),
5134 populate_hack_list (s);
5135 populate_prefs_page (s);
5136 sensitize_demo_widgets (s, False);
5137 fix_text_entry_sizes (s);
5138 scroll_to_current_hack (s);
5140 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5141 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5145 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5146 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5148 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5149 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5151 #endif /* !HAVE_GTK2 */
5153 /* Hook up callbacks to the items on the mode menu. */
5155 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5156 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5157 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5159 for (i = 0; kids; kids = kids->next, i++)
5161 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5162 GTK_SIGNAL_FUNC (mode_menu_item_cb),
5165 /* The "random-same" mode menu item does not appear unless
5166 there are multple screens.
5168 if (s->nscreens <= 1 &&
5169 mode_menu_order[i] == RANDOM_HACKS_SAME)
5170 gtk_widget_hide (GTK_WIDGET (kids->data));
5173 if (s->nscreens <= 1) /* recompute option-menu size */
5175 gtk_widget_unrealize (GTK_WIDGET (menu));
5176 gtk_widget_realize (GTK_WIDGET (menu));
5181 /* Handle the -prefs command-line argument. */
5184 GtkNotebook *notebook =
5185 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5186 gtk_notebook_set_page (notebook, 1);
5189 # ifdef HAVE_CRAPPLET
5193 GtkWidget *outer_vbox;
5195 gtk_widget_hide (s->toplevel_widget);
5197 capplet = capplet_widget_new ();
5199 /* Make there be a "Close" button instead of "OK" and "Cancel" */
5200 # ifdef HAVE_CRAPPLET_IMMEDIATE
5201 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5202 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5203 /* In crapplet-mode, take off the menubar. */
5204 gtk_widget_hide (name_to_widget (s, "menubar"));
5206 /* Reparent our top-level container to be a child of the capplet
5209 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5210 gtk_widget_ref (outer_vbox);
5211 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5213 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5214 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5216 /* Find the window above us, and set the title and close handler. */
5218 GtkWidget *window = capplet;
5219 while (window && !GTK_IS_WINDOW (window))
5220 window = GET_PARENT (window);
5223 gtk_window_set_title (GTK_WINDOW (window), window_title);
5224 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5225 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5230 s->toplevel_widget = capplet;
5232 # endif /* HAVE_CRAPPLET */
5235 /* The Gnome folks hate the menubar. I think it's important to have access
5236 to the commands on the File menu (Restart Daemon, etc.) and to the
5237 About and Documentation commands on the Help menu.
5241 gtk_widget_hide (name_to_widget (s, "menubar"));
5245 free (window_title);
5249 /* After picking the default size, allow -geometry to override it. */
5251 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5254 gtk_widget_show (s->toplevel_widget);
5255 init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */
5256 fix_preview_visual (s);
5258 /* Realize page zero, so that we can diddle the scrollbar when the
5259 user tabs back to it -- otherwise, the current hack isn't scrolled
5260 to the first time they tab back there, when started with "-prefs".
5261 (Though it is if they then tab away, and back again.)
5263 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5264 #### understands this crap, explain to me how to make this work.
5266 gtk_widget_realize (name_to_widget (s, "demos_table"));
5269 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5272 /* Handle the --settings command-line argument. */
5274 gtk_timeout_add (500, settings_timer, 0);
5277 /* Issue any warnings about the running xscreensaver daemon. */
5279 the_network_is_not_the_computer (s);
5283 warning_dialog (s->toplevel_widget,
5285 "This version of xscreensaver is VERY OLD!\n"
5288 "https://www.jwz.org/xscreensaver/\n"
5290 "(If this is the latest version that your distro ships, then\n"
5291 "your distro is doing you a disservice. Build from source.)\n"
5296 /* Run the Gtk event loop, and not the Xt event loop. This means that
5297 if there were Xt timers or fds registered, they would never get serviced,
5298 and if there were any Xt widgets, they would never have events delivered.
5299 Fortunately, we're using Gtk for all of the UI, and only initialized
5300 Xt so that we could process the command line and use the X resource
5303 s->initializing_p = False;
5305 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5306 after we start up. Otherwise, it always appears scrolled to the top
5307 when in crapplet-mode. */
5308 gtk_timeout_add (500, delayed_scroll_kludge, s);
5312 /* Load every configurator in turn, to scan them for errors all at once. */
5316 for (i = 0; i < p->screenhacks_count; i++)
5318 screenhack *hack = p->screenhacks[i];
5319 conf_data *d = load_configurator (hack->command, s->debug_p);
5320 if (d) free_conf_data (d);
5326 # ifdef HAVE_CRAPPLET
5328 capplet_gtk_main ();
5330 # endif /* HAVE_CRAPPLET */
5333 kill_preview_subproc (s, False);
5337 #endif /* HAVE_GTK -- whole file */