http://www.jwz.org/xscreensaver/xscreensaver-5.12.tar.gz
[xscreensaver] / driver / demo-Gtk.c
1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2  * xscreensaver, Copyright (c) 1993-2008 Jamie Zawinski <jwz@jwz.org>
3  *
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 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #ifdef HAVE_GTK /* whole file */
18
19 #include <xscreensaver-intl.h>
20
21 #include <stdlib.h>
22
23 #ifdef HAVE_UNISTD_H
24 # include <unistd.h>
25 #endif
26
27 # ifdef __GNUC__
28 #  define STFU __extension__  /* ignore gcc -pendantic warnings in next sexp */
29 # else
30 #  define STFU /* */
31 # endif
32
33
34 #ifdef ENABLE_NLS
35 # include <locale.h>
36 #endif /* ENABLE_NLS */
37
38 #ifndef VMS
39 # include <pwd.h>               /* for getpwuid() */
40 #else /* VMS */
41 # include "vms-pwd.h"
42 #endif /* VMS */
43
44 #ifdef HAVE_UNAME
45 # include <sys/utsname.h>       /* for uname() */
46 #endif /* HAVE_UNAME */
47
48 #include <stdio.h>
49 #include <time.h>
50 #include <sys/stat.h>
51 #include <sys/time.h>
52
53
54 #include <signal.h>
55 #include <errno.h>
56 #ifdef HAVE_SYS_WAIT_H
57 # include <sys/wait.h>          /* for waitpid() and associated macros */
58 #endif
59
60
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>
65
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>
70
71 #ifdef HAVE_XMU
72 # ifndef VMS
73 #  include <X11/Xmu/Error.h>
74 # else /* VMS */
75 #  include <Xmu/Error.h>
76 # endif
77 #else
78 # include "xmu.h"
79 #endif
80
81 #ifdef HAVE_XINERAMA
82 # include <X11/extensions/Xinerama.h>
83 #endif /* HAVE_XINERAMA */
84
85 #include <gtk/gtk.h>
86
87 #ifdef HAVE_CRAPPLET
88 # include <gnome.h>
89 # include <capplet-widget.h>
90 #endif
91
92 #include <gdk/gdkx.h>
93
94 #ifdef HAVE_GTK2
95 # include <glade/glade-xml.h>
96 # include <gmodule.h>
97 #else  /* !HAVE_GTK2 */
98 # define G_MODULE_EXPORT /**/
99 #endif /* !HAVE_GTK2 */
100
101 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
102 # define GLADE_DIR DEFAULT_ICONDIR
103 #endif
104 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
105 # define DEFAULT_ICONDIR GLADE_DIR
106 #endif
107
108 #ifndef HAVE_XML
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;
112 #endif
113
114
115
116 #include "version.h"
117 #include "prefs.h"
118 #include "resources.h"          /* for parse_time() */
119 #include "visual.h"             /* for has_writable_cells() */
120 #include "remote.h"             /* for xscreensaver_command() */
121 #include "usleep.h"
122
123 #include "logo-50.xpm"
124 #include "logo-180.xpm"
125
126 #undef dgettext  /* else these are defined twice... */
127 #undef dcgettext
128
129 #include "demo-Gtk-widgets.h"
130 #include "demo-Gtk-support.h"
131 #include "demo-Gtk-conf.h"
132
133 #include <stdio.h>
134 #include <string.h>
135 #include <ctype.h>
136
137 #ifdef HAVE_GTK2
138 enum {
139   COL_ENABLED,
140   COL_NAME,
141   COL_LAST
142 };
143 #endif /* HAVE_GTK2 */
144
145 /* Deal with deprecation of direct access to struct fields on the way to GTK3
146    See http://live.gnome.org/GnomeGoals/UseGseal
147  */
148 #if GTK_CHECK_VERSION(2,14,0)
149 # define GET_PARENT(w)          gtk_widget_get_parent (w)
150 # define GET_WINDOW(w)          gtk_widget_get_window (w)
151 # define GET_ACTION_AREA(d)     gtk_dialog_get_action_area (d)
152 # define GET_CONTENT_AREA(d)    gtk_dialog_get_content_area (d)
153 # define GET_ADJ_VALUE(a)       gtk_adjustment_get_value (a)
154 # define SET_ADJ_VALUE(a,v)     gtk_adjustment_set_value (a, v)
155 # define SET_ADJ_UPPER(a,v)     gtk_adjustment_set_upper (a, v)
156 #else
157 # define GET_PARENT(w)          ((w)->parent)
158 # define GET_WINDOW(w)          ((w)->window)
159 # define GET_ACTION_AREA(d)     ((d)->action_area)
160 # define GET_CONTENT_AREA(d)    ((d)->vbox)
161 # define GET_ADJ_VALUE(a)       ((a)->value)
162 # define SET_ADJ_VALUE(a,v)     (a)->value = v
163 # define SET_ADJ_UPPER(a,v)     (a)->upper = v
164 #endif
165
166 #if GTK_CHECK_VERSION(2,18,0)
167 # define SET_CAN_DEFAULT(w)     gtk_widget_set_can_default ((w), TRUE)
168 # define GET_SENSITIVE(w)       gtk_widget_get_sensitive (w)
169 #else
170 # define SET_CAN_DEFAULT(w)     GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_DEFAULT)
171 # define GET_SENSITIVE(w)       GTK_WIDGET_IS_SENSITIVE (w)
172 #endif
173
174 #if GTK_CHECK_VERSION(2,20,0)
175 # define GET_REALIZED(w)        gtk_widget_get_realized (w)
176 #else
177 # define GET_REALIZED(w)        GTK_WIDGET_REALIZED (w)
178 #endif
179
180 /* from exec.c */
181 extern void exec_command (const char *shell, const char *command, int nice);
182 extern int on_path_p (const char *program);
183
184 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
185
186 #undef countof
187 #define countof(x) (sizeof((x))/sizeof((*x)))
188
189
190 /* You might think that to read an array of 32-bit quantities out of a
191    server-side property, you would pass an array of 32-bit data quantities
192    into XGetWindowProperty().  You would be wrong.  You have to use an array
193    of longs, even if long is 64 bits (using 32 of each 64.)
194  */
195 typedef long PROP32;
196
197 char *progname = 0;
198 char *progclass = "XScreenSaver";
199 XrmDatabase db;
200
201 /* The order of the items in the mode menu. */
202 static int mode_menu_order[] = {
203   DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
204
205
206 typedef struct {
207
208   char *short_version;          /* version number of this xscreensaver build */
209
210   GtkWidget *toplevel_widget;   /* the main window */
211   GtkWidget *base_widget;       /* root of our hierarchy (for name lookups) */
212   GtkWidget *popup_widget;      /* the "Settings" dialog */
213   conf_data *cdata;             /* private data for per-hack configuration */
214
215 #ifdef HAVE_GTK2
216   GladeXML *glade_ui;           /* Glade UI file */
217 #endif /* HAVE_GTK2 */
218
219   Bool debug_p;                 /* whether to print diagnostics */
220   Bool initializing_p;          /* flag for breaking recursion loops */
221   Bool saving_p;                /* flag for breaking recursion loops */
222
223   char *desired_preview_cmd;    /* subprocess we intend to run */
224   char *running_preview_cmd;    /* subprocess we are currently running */
225   pid_t running_preview_pid;    /* pid of forked subproc (might be dead) */
226   Bool running_preview_error_p; /* whether the pid died abnormally */
227
228   Bool preview_suppressed_p;    /* flag meaning "don't launch subproc" */
229   int subproc_timer_id;         /* timer to delay subproc launch */
230   int subproc_check_timer_id;   /* timer to check whether it started up */
231   int subproc_check_countdown;  /* how many more checks left */
232
233   int *list_elt_to_hack_number; /* table for sorting the hack list */
234   int *hack_number_to_list_elt; /* the inverse table */
235   Bool *hacks_available_p;      /* whether hacks are on $PATH */
236   int total_available;          /* how many are on $PATH */
237   int list_count;               /* how many items are in the list: this may be
238                                    less than p->screenhacks_count, if some are
239                                    suppressed. */
240
241   int _selected_list_element;   /* don't use this: call
242                                    selected_list_element() instead */
243
244   int nscreens;                 /* How many X or Xinerama screens there are */
245
246   saver_preferences prefs;
247
248 } state;
249
250
251 /* Total fucking evilness due to the fact that it's rocket science to get
252    a closure object of our own down into the various widget callbacks. */
253 static state *global_state_kludge;
254
255 Atom XA_VROOT;
256 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
257 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
258 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
259
260
261 static void populate_demo_window (state *, int list_elt);
262 static void populate_prefs_page (state *);
263 static void populate_popup_window (state *);
264
265 static Bool flush_dialog_changes_and_save (state *);
266 static Bool flush_popup_changes_and_save (state *);
267
268 static int maybe_reload_init_file (state *);
269 static void await_xscreensaver (state *);
270 static Bool xscreensaver_running_p (state *);
271 static void sensitize_menu_items (state *s, Bool force_p);
272 static void force_dialog_repaint (state *s);
273
274 static void schedule_preview (state *, const char *cmd);
275 static void kill_preview_subproc (state *, Bool reset_p);
276 static void schedule_preview_check (state *);
277
278 \f
279 /* Prototypes of functions used by the Glade-generated code,
280    to avoid warnings.
281  */
282 void exit_menu_cb (GtkMenuItem *, gpointer user_data);
283 void about_menu_cb (GtkMenuItem *, gpointer user_data);
284 void doc_menu_cb (GtkMenuItem *, gpointer user_data);
285 void file_menu_cb (GtkMenuItem *, gpointer user_data);
286 void activate_menu_cb (GtkMenuItem *, gpointer user_data);
287 void lock_menu_cb (GtkMenuItem *, gpointer user_data);
288 void kill_menu_cb (GtkMenuItem *, gpointer user_data);
289 void restart_menu_cb (GtkWidget *, gpointer user_data);
290 void run_this_cb (GtkButton *, gpointer user_data);
291 void manual_cb (GtkButton *, gpointer user_data);
292 void run_next_cb (GtkButton *, gpointer user_data);
293 void run_prev_cb (GtkButton *, gpointer user_data);
294 void pref_changed_cb (GtkWidget *, gpointer user_data);
295 gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
296 void mode_menu_item_cb (GtkWidget *, gpointer user_data);
297 void switch_page_cb (GtkNotebook *, GtkNotebookPage *, 
298                      gint page_num, gpointer user_data);
299 void browse_image_dir_cb (GtkButton *, gpointer user_data);
300 void browse_text_file_cb (GtkButton *, gpointer user_data);
301 void browse_text_program_cb (GtkButton *, gpointer user_data);
302 void settings_cb (GtkButton *, gpointer user_data);
303 void settings_adv_cb (GtkButton *, gpointer user_data);
304 void settings_std_cb (GtkButton *, gpointer user_data);
305 void settings_reset_cb (GtkButton *, gpointer user_data);
306 void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
307                               gint page_num, gpointer user_data);
308 void settings_cancel_cb (GtkButton *, gpointer user_data);
309 void settings_ok_cb (GtkButton *, gpointer user_data);
310
311 static void kill_gnome_screensaver (void);
312 static void kill_kde_screensaver (void);
313
314 \f
315 /* Some random utility functions
316  */
317
318 const char *blurb (void);
319
320 const char *
321 blurb (void)
322 {
323   time_t now = time ((time_t *) 0);
324   char *ct = (char *) ctime (&now);
325   static char buf[255];
326   int n = strlen(progname);
327   if (n > 100) n = 99;
328   strncpy(buf, progname, n);
329   buf[n++] = ':';
330   buf[n++] = ' ';
331   strncpy(buf+n, ct+11, 8);
332   strcpy(buf+n+9, ": ");
333   return buf;
334 }
335
336
337 static GtkWidget *
338 name_to_widget (state *s, const char *name)
339 {
340   GtkWidget *w;
341   if (!s) abort();
342   if (!name) abort();
343   if (!*name) abort();
344
345 #ifdef HAVE_GTK2
346   if (!s->glade_ui)
347     {
348       /* First try to load the Glade file from the current directory;
349          if there isn't one there, check the installed directory.
350        */
351 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
352       const char * const files[] = { GLADE_FILE_NAME,
353                                      GLADE_DIR "/" GLADE_FILE_NAME };
354       int i;
355       for (i = 0; i < countof (files); i++)
356         {
357           struct stat st;
358           if (!stat (files[i], &st))
359             {
360               s->glade_ui = glade_xml_new (files[i], NULL, NULL);
361               break;
362             }
363         }
364       if (!s->glade_ui)
365         {
366           fprintf (stderr,
367                    "%s: could not load \"" GLADE_FILE_NAME "\"\n"
368                    "\tfrom " GLADE_DIR "/ or current directory.\n",
369                    blurb());
370           exit (-1);
371         }
372 # undef GLADE_FILE_NAME
373
374       glade_xml_signal_autoconnect (s->glade_ui);
375     }
376
377   w = glade_xml_get_widget (s->glade_ui, name);
378
379 #else /* !HAVE_GTK2 */
380
381   w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
382                                          name);
383   if (w) return w;
384   w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
385                                          name);
386 #endif /* HAVE_GTK2 */
387   if (w) return w;
388
389   fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
390            blurb(), name);
391   abort();
392 }
393
394
395 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
396    Takes a scroller, viewport, or list as an argument.
397  */
398 static void
399 ensure_selected_item_visible (GtkWidget *widget)
400 {
401 #ifdef HAVE_GTK2
402   GtkTreePath *path;
403   GtkTreeSelection *selection;
404   GtkTreeIter iter;
405   GtkTreeModel *model;
406   
407   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
408   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
409         path = gtk_tree_path_new_first ();
410   else
411         path = gtk_tree_model_get_path (model, &iter);
412   
413   gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
414
415   gtk_tree_path_free (path);
416
417 #else /* !HAVE_GTK2 */
418
419   GtkScrolledWindow *scroller = 0;
420   GtkViewport *vp = 0;
421   GtkList *list_widget = 0;
422   GList *slist;
423   GList *kids;
424   int nkids = 0;
425   GtkWidget *selected = 0;
426   int list_elt = -1;
427   GtkAdjustment *adj;
428   gint parent_h, child_y, child_h, children_h, ignore;
429   double ratio_t, ratio_b;
430
431   if (GTK_IS_SCROLLED_WINDOW (widget))
432     {
433       scroller = GTK_SCROLLED_WINDOW (widget);
434       vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
435       list_widget = GTK_LIST (GTK_BIN(vp)->child);
436     }
437   else if (GTK_IS_VIEWPORT (widget))
438     {
439       vp = GTK_VIEWPORT (widget);
440       scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
441       list_widget = GTK_LIST (GTK_BIN(vp)->child);
442     }
443   else if (GTK_IS_LIST (widget))
444     {
445       list_widget = GTK_LIST (widget);
446       vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
447       scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
448     }
449   else
450     abort();
451
452   slist = list_widget->selection;
453   selected = (slist ? GTK_WIDGET (slist->data) : 0);
454   if (!selected)
455     return;
456
457   list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
458
459   for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
460        kids; kids = kids->next)
461     nkids++;
462
463   adj = gtk_scrolled_window_get_vadjustment (scroller);
464
465   gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (vp)),
466                            &ignore, &ignore, &ignore, &parent_h, &ignore);
467   gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (selected)),
468                            &ignore, &child_y, &ignore, &child_h, &ignore);
469   children_h = nkids * child_h;
470
471   ratio_t = ((double) child_y) / ((double) children_h);
472   ratio_b = ((double) child_y + child_h) / ((double) children_h);
473
474   if (adj->upper == 0.0)  /* no items in list */
475     return;
476
477   if (ratio_t < (adj->value / adj->upper) ||
478       ratio_b > ((adj->value + adj->page_size) / adj->upper))
479     {
480       double target;
481       int slop = parent_h * 0.75; /* how much to overshoot by */
482
483       if (ratio_t < (adj->value / adj->upper))
484         {
485           double ratio_w = ((double) parent_h) / ((double) children_h);
486           double ratio_l = (ratio_b - ratio_t);
487           target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
488           target += slop;
489         }
490       else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
491         {
492           target = ratio_t * adj->upper;
493           target -= slop;
494         }
495
496       if (target > adj->upper - adj->page_size)
497         target = adj->upper - adj->page_size;
498       if (target < 0)
499         target = 0;
500
501       gtk_adjustment_set_value (adj, target);
502     }
503 #endif /* !HAVE_GTK2 */
504 }
505
506 static void
507 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
508 {
509   GtkWidget *shell = GTK_WIDGET (user_data);
510   while (GET_PARENT (shell))
511     shell = GET_PARENT (shell);
512   gtk_widget_destroy (GTK_WIDGET (shell));
513 }
514
515
516 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
517
518 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
519 {
520   restart_menu_cb (widget, user_data);
521   warning_dialog_dismiss_cb (widget, user_data);
522 }
523
524 static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data)
525 {
526   kill_gnome_screensaver ();
527   warning_dialog_dismiss_cb (widget, user_data);
528 }
529
530 static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data)
531 {
532   kill_kde_screensaver ();
533   warning_dialog_dismiss_cb (widget, user_data);
534 }
535
536 typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
537
538 static void
539 warning_dialog (GtkWidget *parent, const char *message,
540                 dialog_button button_type, int center)
541 {
542   char *msg = strdup (message);
543   char *head;
544
545   GtkWidget *dialog = gtk_dialog_new ();
546   GtkWidget *label = 0;
547   GtkWidget *ok = 0;
548   GtkWidget *cancel = 0;
549   int i = 0;
550
551   while (parent && !GET_WINDOW (parent))
552     parent = GET_PARENT (parent);
553
554   if (!parent ||
555       !GET_WINDOW (parent)) /* too early to pop up transient dialogs */
556     {
557       fprintf (stderr, "%s: too early for dialog?\n", progname);
558       return;
559     }
560
561   head = msg;
562   while (head)
563     {
564       char name[20];
565       char *s = strchr (head, '\n');
566       if (s) *s = 0;
567
568       sprintf (name, "label%d", i++);
569
570       {
571         label = gtk_label_new (head);
572 #ifdef HAVE_GTK2
573         gtk_label_set_selectable (GTK_LABEL (label), TRUE);
574 #endif /* HAVE_GTK2 */
575
576 #ifndef HAVE_GTK2
577         if (i == 1)
578           {
579             GTK_WIDGET (label)->style =
580               gtk_style_copy (GTK_WIDGET (label)->style);
581             GTK_WIDGET (label)->style->font =
582               gdk_font_load (get_string_resource("warning_dialog.headingFont",
583                                                  "Dialog.Font"));
584             gtk_widget_set_style (GTK_WIDGET (label),
585                                   GTK_WIDGET (label)->style);
586           }
587 #endif /* !HAVE_GTK2 */
588         if (center <= 0)
589           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
590         gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
591                             label, TRUE, TRUE, 0);
592         gtk_widget_show (label);
593       }
594
595       if (s)
596         head = s+1;
597       else
598         head = 0;
599
600       center--;
601     }
602
603   label = gtk_label_new ("");
604   gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
605                       label, TRUE, TRUE, 0);
606   gtk_widget_show (label);
607
608   label = gtk_hbutton_box_new ();
609   gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
610                       label, TRUE, TRUE, 0);
611
612 #ifdef HAVE_GTK2
613   if (button_type != D_NONE)
614     {
615       cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
616       gtk_container_add (GTK_CONTAINER (label), cancel);
617     }
618
619   ok = gtk_button_new_from_stock (GTK_STOCK_OK);
620   gtk_container_add (GTK_CONTAINER (label), ok);
621
622 #else /* !HAVE_GTK2 */
623
624   ok = gtk_button_new_with_label ("OK");
625   gtk_container_add (GTK_CONTAINER (label), ok);
626
627   if (button_type != D_NONE)
628     {
629       cancel = gtk_button_new_with_label ("Cancel");
630       gtk_container_add (GTK_CONTAINER (label), cancel);
631     }
632
633 #endif /* !HAVE_GTK2 */
634
635   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
636   gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
637   gtk_window_set_title (GTK_WINDOW (dialog), progclass);
638   SET_CAN_DEFAULT (ok);
639   gtk_widget_show (ok);
640   gtk_widget_grab_focus (ok);
641
642   if (cancel)
643     {
644       SET_CAN_DEFAULT (cancel);
645       gtk_widget_show (cancel);
646     }
647   gtk_widget_show (label);
648   gtk_widget_show (dialog);
649
650   if (button_type != D_NONE)
651     {
652       GtkSignalFunc fn;
653       switch (button_type) {
654       case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break;
655       case D_GNOME:  fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb);   break;
656       case D_KDE:    fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb);   break;
657       default: abort(); break;
658       }
659       gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn, 
660                                  (gpointer) dialog);
661       gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
662                                  GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
663                                  (gpointer) dialog);
664     }
665   else
666     {
667       gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
668                                  GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
669                                  (gpointer) dialog);
670     }
671
672   gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
673                                 GET_WINDOW (GTK_WIDGET (parent)));
674
675 #ifdef HAVE_GTK2
676   gtk_window_present (GTK_WINDOW (dialog));
677 #else  /* !HAVE_GTK2 */
678   gdk_window_show (GTK_WIDGET (dialog)->window);
679   gdk_window_raise (GTK_WIDGET (dialog)->window);
680 #endif /* !HAVE_GTK2 */
681
682   free (msg);
683 }
684
685
686 static void
687 run_cmd (state *s, Atom command, int arg)
688 {
689   char *err = 0;
690   int status;
691
692   flush_dialog_changes_and_save (s);
693   status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
694
695   /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
696   if (status < 0 && err && strstr (err, "unexpectedly deleted"))
697     status = 0;
698
699   if (status < 0)
700     {
701       char buf [255];
702       if (err)
703         sprintf (buf, "Error:\n\n%s", err);
704       else
705         strcpy (buf, "Unknown error!");
706       warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
707     }
708   if (err) free (err);
709
710   sensitize_menu_items (s, True);
711   force_dialog_repaint (s);
712 }
713
714
715 static void
716 run_hack (state *s, int list_elt, Bool report_errors_p)
717 {
718   int hack_number;
719   char *err = 0;
720   int status;
721
722   if (list_elt < 0) return;
723   hack_number = s->list_elt_to_hack_number[list_elt];
724
725   flush_dialog_changes_and_save (s);
726   schedule_preview (s, 0);
727
728   status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
729                                  False, &err);
730
731   if (status < 0 && report_errors_p)
732     {
733       if (xscreensaver_running_p (s))
734         {
735           /* Kludge: ignore the spurious "window unexpectedly deleted"
736              errors... */
737           if (err && strstr (err, "unexpectedly deleted"))
738             status = 0;
739
740           if (status < 0)
741             {
742               char buf [255];
743               if (err)
744                 sprintf (buf, "Error:\n\n%s", err);
745               else
746                 strcpy (buf, "Unknown error!");
747               warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
748             }
749         }
750       else
751         {
752           /* The error is that the daemon isn't running;
753              offer to restart it.
754            */
755           const char *d = DisplayString (GDK_DISPLAY());
756           char msg [1024];
757           sprintf (msg,
758                    _("Warning:\n\n"
759                      "The XScreenSaver daemon doesn't seem to be running\n"
760                      "on display \"%s\".  Launch it now?"),
761                    d);
762           warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
763         }
764     }
765
766   if (err) free (err);
767
768   sensitize_menu_items (s, False);
769 }
770
771
772 \f
773 /* Button callbacks
774
775    According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
776    libglade work on Cygwin; apparently all Glade callbacks need this magic
777    extra declaration.  I do not pretend to understand.
778  */
779
780 G_MODULE_EXPORT void
781 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
782 {
783   state *s = global_state_kludge;  /* I hate C so much... */
784   flush_dialog_changes_and_save (s);
785   kill_preview_subproc (s, False);
786   gtk_main_quit ();
787 }
788
789 static gboolean
790 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
791 {
792   state *s = (state *) data;
793   flush_dialog_changes_and_save (s);
794   gtk_main_quit ();
795   return TRUE;
796 }
797
798
799 G_MODULE_EXPORT void
800 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
801 {
802   char msg [2048];
803   char *vers = strdup (screensaver_id + 4);
804   char *s;
805   char copy[1024];
806   char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
807
808   s = strchr (vers, ',');
809   *s = 0;
810   s += 2;
811
812   /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
813      non-ASCII characters aren't allowed in localizable string keys."
814      (I don't want to just use (c) instead of Â© because that doesn't
815      look as good in the plain-old default Latin1 "C" locale.)
816    */
817 #ifdef HAVE_GTK2
818   sprintf(copy, ("Copyright \xC2\xA9 1991-2008 %s"), s);
819 #else  /* !HAVE_GTK2 */
820   sprintf(copy, ("Copyright \251 1991-2008 %s"), s);
821 #endif /* !HAVE_GTK2 */
822
823   sprintf (msg, "%s\n\n%s", copy, desc);
824
825   /* I can't make gnome_about_new() work here -- it starts dying in
826      gdk_imlib_get_visual() under gnome_about_new().  If this worked,
827      then this might be the thing to do:
828
829      #ifdef HAVE_CRAPPLET
830      {
831        const gchar *auth[] = { 0 };
832        GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
833                                            "xscreensaver.xpm");
834        gtk_widget_show (about);
835      }
836      #else / * GTK but not GNOME * /
837       ...
838    */
839   {
840     GdkColormap *colormap;
841     GdkPixmap *gdkpixmap;
842     GdkBitmap *mask;
843
844     GtkWidget *dialog = gtk_dialog_new ();
845     GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
846     GtkWidget *parent = GTK_WIDGET (menuitem);
847     while (GET_PARENT (parent))
848       parent = GET_PARENT (parent);
849
850     hbox = gtk_hbox_new (FALSE, 20);
851     gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
852                         hbox, TRUE, TRUE, 0);
853
854     colormap = gtk_widget_get_colormap (parent);
855     gdkpixmap =
856       gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
857                                              (gchar **) logo_180_xpm);
858     icon = gtk_pixmap_new (gdkpixmap, mask);
859     gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
860
861     gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
862
863     vbox = gtk_vbox_new (FALSE, 0);
864     gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
865
866     label1 = gtk_label_new (vers);
867     gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
868     gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
869     gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
870
871 #ifndef HAVE_GTK2
872     GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
873     GTK_WIDGET (label1)->style->font =
874       gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
875     gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
876 #endif /* HAVE_GTK2 */
877
878     label2 = gtk_label_new (msg);
879     gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
880     gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
881     gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
882
883 #ifndef HAVE_GTK2
884     GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
885     GTK_WIDGET (label2)->style->font =
886       gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
887     gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
888 #endif /* HAVE_GTK2 */
889
890     hb = gtk_hbutton_box_new ();
891
892     gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
893                         hb, TRUE, TRUE, 0);
894
895 #ifdef HAVE_GTK2
896     ok = gtk_button_new_from_stock (GTK_STOCK_OK);
897 #else /* !HAVE_GTK2 */
898     ok = gtk_button_new_with_label (_("OK"));
899 #endif /* !HAVE_GTK2 */
900     gtk_container_add (GTK_CONTAINER (hb), ok);
901
902     gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
903     gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
904     gtk_window_set_title (GTK_WINDOW (dialog), progclass);
905
906     gtk_widget_show (hbox);
907     gtk_widget_show (icon);
908     gtk_widget_show (vbox);
909     gtk_widget_show (label1);
910     gtk_widget_show (label2);
911     gtk_widget_show (hb);
912     gtk_widget_show (ok);
913     gtk_widget_show (dialog);
914
915     gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
916                                GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
917                                (gpointer) dialog);
918     gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
919                                   GET_WINDOW (GTK_WIDGET (parent)));
920     gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog)));
921     gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog)));
922   }
923 }
924
925
926 G_MODULE_EXPORT void
927 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
928 {
929   state *s = global_state_kludge;  /* I hate C so much... */
930   saver_preferences *p = &s->prefs;
931   char *help_command;
932
933   if (!p->help_url || !*p->help_url)
934     {
935       warning_dialog (s->toplevel_widget,
936                       _("Error:\n\n"
937                         "No Help URL has been specified.\n"), D_NONE, 100);
938       return;
939     }
940
941   help_command = (char *) malloc (strlen (p->load_url_command) +
942                                   (strlen (p->help_url) * 4) + 20);
943   strcpy (help_command, "( ");
944   sprintf (help_command + strlen(help_command),
945            p->load_url_command,
946            p->help_url, p->help_url, p->help_url, p->help_url);
947   strcat (help_command, " ) &");
948   if (system (help_command) < 0)
949     fprintf (stderr, "%s: fork error\n", blurb());
950   free (help_command);
951 }
952
953
954 G_MODULE_EXPORT void
955 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
956 {
957   state *s = global_state_kludge;  /* I hate C so much... */
958   sensitize_menu_items (s, False);
959 }
960
961
962 G_MODULE_EXPORT void
963 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
964 {
965   state *s = global_state_kludge;  /* I hate C so much... */
966   run_cmd (s, XA_ACTIVATE, 0);
967 }
968
969
970 G_MODULE_EXPORT void
971 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
972 {
973   state *s = global_state_kludge;  /* I hate C so much... */
974   run_cmd (s, XA_LOCK, 0);
975 }
976
977
978 G_MODULE_EXPORT void
979 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
980 {
981   state *s = global_state_kludge;  /* I hate C so much... */
982   run_cmd (s, XA_EXIT, 0);
983 }
984
985
986 G_MODULE_EXPORT void
987 restart_menu_cb (GtkWidget *widget, gpointer user_data)
988 {
989   state *s = global_state_kludge;  /* I hate C so much... */
990   flush_dialog_changes_and_save (s);
991   xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
992   sleep (1);
993   if (system ("xscreensaver -nosplash &") < 0)
994     fprintf (stderr, "%s: fork error\n", blurb());
995
996   await_xscreensaver (s);
997 }
998
999 static Bool
1000 xscreensaver_running_p (state *s)
1001 {
1002   Display *dpy = GDK_DISPLAY();
1003   char *rversion = 0;
1004   server_xscreensaver_version (dpy, &rversion, 0, 0);
1005   if (!rversion)
1006     return False;
1007   free (rversion);
1008   return True;
1009 }
1010
1011 static void
1012 await_xscreensaver (state *s)
1013 {
1014   int countdown = 5;
1015   Bool ok = False;
1016
1017   while (!ok && (--countdown > 0))
1018     if (xscreensaver_running_p (s))
1019       ok = True;
1020     else
1021       sleep (1);    /* If it's not there yet, wait a second... */
1022
1023   sensitize_menu_items (s, True);
1024
1025   if (! ok)
1026     {
1027       /* Timed out, no screensaver running. */
1028
1029       char buf [1024];
1030       Bool root_p = (geteuid () == 0);
1031       
1032       strcpy (buf, 
1033               _("Error:\n\n"
1034                 "The xscreensaver daemon did not start up properly.\n"
1035                 "\n"));
1036
1037       if (root_p)
1038
1039 # ifdef __GNUC__
1040         __extension__     /* don't warn about "string length is greater than
1041                              the length ISO C89 compilers are required to
1042                              support" in the following expression... */
1043 # endif
1044         strcat (buf, STFU
1045           _("You are running as root.  This usually means that xscreensaver\n"
1046             "was unable to contact your X server because access control is\n"
1047             "turned on.  Try running this command:\n"
1048             "\n"
1049             "                        xhost +localhost\n"
1050             "\n"
1051             "and then selecting `File / Restart Daemon'.\n"
1052             "\n"
1053             "Note that turning off access control will allow anyone logged\n"
1054             "on to this machine to access your screen, which might be\n"
1055             "considered a security problem.  Please read the xscreensaver\n"
1056             "manual and FAQ for more information.\n"
1057             "\n"
1058             "You shouldn't run X as root. Instead, you should log in as a\n"
1059             "normal user, and `su' as necessary."));
1060       else
1061         strcat (buf, _("Please check your $PATH and permissions."));
1062
1063       warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1064     }
1065
1066   force_dialog_repaint (s);
1067 }
1068
1069
1070 static int
1071 selected_list_element (state *s)
1072 {
1073   return s->_selected_list_element;
1074 }
1075
1076
1077 static int
1078 demo_write_init_file (state *s, saver_preferences *p)
1079 {
1080   Display *dpy = GDK_DISPLAY();
1081
1082 #if 0
1083   /* #### try to figure out why shit keeps getting reordered... */
1084   if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1085     abort();
1086 #endif
1087
1088   if (!write_init_file (dpy, p, s->short_version, False))
1089     {
1090       if (s->debug_p)
1091         fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1092       return 0;
1093     }
1094   else
1095     {
1096       const char *f = init_file_name();
1097       if (!f || !*f)
1098         warning_dialog (s->toplevel_widget,
1099                         _("Error:\n\nCouldn't determine init file name!\n"),
1100                         D_NONE, 100);
1101       else
1102         {
1103           char *b = (char *) malloc (strlen(f) + 1024);
1104           sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1105           warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1106           free (b);
1107         }
1108       return -1;
1109     }
1110 }
1111
1112
1113 G_MODULE_EXPORT void
1114 run_this_cb (GtkButton *button, gpointer user_data)
1115 {
1116   state *s = global_state_kludge;  /* I hate C so much... */
1117   int list_elt = selected_list_element (s);
1118   if (list_elt < 0) return;
1119   if (!flush_dialog_changes_and_save (s))
1120     run_hack (s, list_elt, True);
1121 }
1122
1123
1124 G_MODULE_EXPORT void
1125 manual_cb (GtkButton *button, gpointer user_data)
1126 {
1127   Display *dpy = GDK_DISPLAY();
1128   state *s = global_state_kludge;  /* I hate C so much... */
1129   saver_preferences *p = &s->prefs;
1130   GtkWidget *list_widget = name_to_widget (s, "list");
1131   int list_elt = selected_list_element (s);
1132   int hack_number;
1133   char *name, *name2, *cmd, *str;
1134   char *oname = 0;
1135   if (list_elt < 0) return;
1136   hack_number = s->list_elt_to_hack_number[list_elt];
1137
1138   flush_dialog_changes_and_save (s);
1139   ensure_selected_item_visible (list_widget);
1140
1141   name = strdup (p->screenhacks[hack_number]->command);
1142   name2 = name;
1143   oname = name;
1144   while (isspace (*name2)) name2++;
1145   str = name2;
1146   while (*str && !isspace (*str)) str++;
1147   *str = 0;
1148   str = strrchr (name2, '/');
1149   if (str) name2 = str+1;
1150
1151   cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1152   if (cmd)
1153     {
1154       char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1155       strcpy (cmd2, "( ");
1156       sprintf (cmd2 + strlen (cmd2),
1157                cmd,
1158                name2, name2, name2, name2);
1159       strcat (cmd2, " ) &");
1160       if (system (cmd2) < 0)
1161         fprintf (stderr, "%s: fork error\n", blurb());
1162       free (cmd2);
1163     }
1164   else
1165     {
1166       warning_dialog (GTK_WIDGET (button),
1167                       _("Error:\n\nno `manualCommand' resource set."),
1168                       D_NONE, 100);
1169     }
1170
1171   free (oname);
1172 }
1173
1174
1175 static void
1176 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1177 {
1178   GtkWidget *parent = name_to_widget (s, "scroller");
1179   gboolean was = GET_SENSITIVE (parent);
1180 #ifdef HAVE_GTK2
1181   GtkTreeIter iter;
1182   GtkTreeModel *model;
1183   GtkTreeSelection *selection;
1184 #endif /* HAVE_GTK2 */
1185
1186   if (!was) gtk_widget_set_sensitive (parent, True);
1187 #ifdef HAVE_GTK2
1188   model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1189   g_assert (model);
1190   if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1191     {
1192       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1193       gtk_tree_selection_select_iter (selection, &iter);
1194     }
1195 #else  /* !HAVE_GTK2 */
1196   gtk_list_select_item (GTK_LIST (list), list_elt);
1197 #endif /* !HAVE_GTK2 */
1198   if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1199   if (!was) gtk_widget_set_sensitive (parent, False);
1200 }
1201
1202
1203 G_MODULE_EXPORT void
1204 run_next_cb (GtkButton *button, gpointer user_data)
1205 {
1206   state *s = global_state_kludge;  /* I hate C so much... */
1207   /* saver_preferences *p = &s->prefs; */
1208   Bool ops = s->preview_suppressed_p;
1209
1210   GtkWidget *list_widget = name_to_widget (s, "list");
1211   int list_elt = selected_list_element (s);
1212
1213   if (list_elt < 0)
1214     list_elt = 0;
1215   else
1216     list_elt++;
1217
1218   if (list_elt >= s->list_count)
1219     list_elt = 0;
1220
1221   s->preview_suppressed_p = True;
1222
1223   flush_dialog_changes_and_save (s);
1224   force_list_select_item (s, list_widget, list_elt, True);
1225   populate_demo_window (s, list_elt);
1226   run_hack (s, list_elt, False);
1227
1228   s->preview_suppressed_p = ops;
1229 }
1230
1231
1232 G_MODULE_EXPORT void
1233 run_prev_cb (GtkButton *button, gpointer user_data)
1234 {
1235   state *s = global_state_kludge;  /* I hate C so much... */
1236   /* saver_preferences *p = &s->prefs; */
1237   Bool ops = s->preview_suppressed_p;
1238
1239   GtkWidget *list_widget = name_to_widget (s, "list");
1240   int list_elt = selected_list_element (s);
1241
1242   if (list_elt < 0)
1243     list_elt = s->list_count - 1;
1244   else
1245     list_elt--;
1246
1247   if (list_elt < 0)
1248     list_elt = s->list_count - 1;
1249
1250   s->preview_suppressed_p = True;
1251
1252   flush_dialog_changes_and_save (s);
1253   force_list_select_item (s, list_widget, list_elt, True);
1254   populate_demo_window (s, list_elt);
1255   run_hack (s, list_elt, False);
1256
1257   s->preview_suppressed_p = ops;
1258 }
1259
1260
1261 /* Writes the given settings into prefs.
1262    Returns true if there was a change, False otherwise.
1263    command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1264  */
1265 static Bool
1266 flush_changes (state *s,
1267                int list_elt,
1268                int enabled_p,
1269                const char *command,
1270                const char *visual)
1271 {
1272   saver_preferences *p = &s->prefs;
1273   Bool changed = False;
1274   screenhack *hack;
1275   int hack_number;
1276   if (list_elt < 0 || list_elt >= s->list_count)
1277     abort();
1278
1279   hack_number = s->list_elt_to_hack_number[list_elt];
1280   hack = p->screenhacks[hack_number];
1281
1282   if (enabled_p != -1 &&
1283       enabled_p != hack->enabled_p)
1284     {
1285       hack->enabled_p = enabled_p;
1286       changed = True;
1287       if (s->debug_p)
1288         fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1289                  blurb(), hack->name, enabled_p);
1290     }
1291
1292   if (command)
1293     {
1294       if (!hack->command || !!strcmp (command, hack->command))
1295         {
1296           if (hack->command) free (hack->command);
1297           hack->command = strdup (command);
1298           changed = True;
1299           if (s->debug_p)
1300             fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1301                      blurb(), hack->name, command);
1302         }
1303     }
1304
1305   if (visual)
1306     {
1307       const char *ov = hack->visual;
1308       if (!ov || !*ov) ov = "any";
1309       if (!*visual) visual = "any";
1310       if (!!strcasecmp (visual, ov))
1311         {
1312           if (hack->visual) free (hack->visual);
1313           hack->visual = strdup (visual);
1314           changed = True;
1315           if (s->debug_p)
1316             fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1317                      blurb(), hack->name, visual);
1318         }
1319     }
1320
1321   return changed;
1322 }
1323
1324
1325 /* Helper for the text fields that contain time specifications:
1326    this parses the text, and does error checking.
1327  */
1328 static void 
1329 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1330 {
1331   if (*line)
1332     {
1333       int value;
1334       if (!sec_p || strchr (line, ':'))
1335         value = parse_time ((char *) line, sec_p, True);
1336       else
1337         {
1338           char c;
1339           if (sscanf (line, "%d%c", &value, &c) != 1)
1340             value = -1;
1341           if (!sec_p)
1342             value *= 60;
1343         }
1344
1345       value *= 1000;    /* Time measures in microseconds */
1346       if (value < 0)
1347         {
1348           char b[255];
1349           sprintf (b,
1350                    _("Error:\n\n"
1351                      "Unparsable time format: \"%s\"\n"),
1352                    line);
1353           warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1354         }
1355       else
1356         *store = value;
1357     }
1358 }
1359
1360
1361 static Bool
1362 directory_p (const char *path)
1363 {
1364   struct stat st;
1365   if (!path || !*path)
1366     return False;
1367   else if (stat (path, &st))
1368     return False;
1369   else if (!S_ISDIR (st.st_mode))
1370     return False;
1371   else
1372     return True;
1373 }
1374
1375 static Bool
1376 file_p (const char *path)
1377 {
1378   struct stat st;
1379   if (!path || !*path)
1380     return False;
1381   else if (stat (path, &st))
1382     return False;
1383   else if (S_ISDIR (st.st_mode))
1384     return False;
1385   else
1386     return True;
1387 }
1388
1389 static char *
1390 normalize_directory (const char *path)
1391 {
1392   int L;
1393   char *p2, *s;
1394   if (!path || !*path) return 0;
1395   L = strlen (path);
1396   p2 = (char *) malloc (L + 2);
1397   strcpy (p2, path);
1398   if (p2[L-1] == '/')  /* remove trailing slash */
1399     p2[--L] = 0;
1400
1401   for (s = p2; s && *s; s++)
1402     {
1403       if (*s == '/' &&
1404           (!strncmp (s, "/../", 4) ||                   /* delete "XYZ/../" */
1405            !strncmp (s, "/..\000", 4)))                 /* delete "XYZ/..$" */
1406         {
1407           char *s0 = s;
1408           while (s0 > p2 && s0[-1] != '/')
1409             s0--;
1410           if (s0 > p2)
1411             {
1412               s0--;
1413               s += 3;
1414               strcpy (s0, s);
1415               s = s0-1;
1416             }
1417         }
1418       else if (*s == '/' && !strncmp (s, "/./", 3))     /* delete "/./" */
1419         strcpy (s, s+2), s--;
1420       else if (*s == '/' && !strncmp (s, "/.\000", 3))  /* delete "/.$" */
1421         *s = 0, s--;
1422     }
1423
1424   for (s = p2; s && *s; s++)            /* normalize consecutive slashes */
1425     while (s[0] == '/' && s[1] == '/')
1426       strcpy (s, s+1);
1427
1428   /* and strip trailing whitespace for good measure. */
1429   L = strlen(p2);
1430   while (isspace(p2[L-1]))
1431     p2[--L] = 0;
1432
1433   return p2;
1434 }
1435
1436
1437 #ifdef HAVE_GTK2
1438
1439 typedef struct {
1440   state *s;
1441   int i;
1442   Bool *changed;
1443 } FlushForeachClosure;
1444
1445 static gboolean
1446 flush_checkbox  (GtkTreeModel *model,
1447                  GtkTreePath *path,
1448                  GtkTreeIter *iter,
1449                  gpointer data)
1450 {
1451   FlushForeachClosure *closure = data;
1452   gboolean checked;
1453
1454   gtk_tree_model_get (model, iter,
1455                       COL_ENABLED, &checked,
1456                       -1);
1457
1458   if (flush_changes (closure->s, closure->i,
1459                      checked, 0, 0))
1460     *closure->changed = True;
1461   
1462   closure->i++;
1463
1464   /* don't remove row */
1465   return FALSE;
1466 }
1467
1468 #endif /* HAVE_GTK2 */
1469
1470 /* Flush out any changes made in the main dialog window (where changes
1471    take place immediately: clicking on a checkbox causes the init file
1472    to be written right away.)
1473  */
1474 static Bool
1475 flush_dialog_changes_and_save (state *s)
1476 {
1477   saver_preferences *p = &s->prefs;
1478   saver_preferences P2, *p2 = &P2;
1479 #ifdef HAVE_GTK2
1480   GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1481   GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1482   FlushForeachClosure closure;
1483 #else /* !HAVE_GTK2 */
1484   GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1485   GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1486   int i;
1487 #endif /* !HAVE_GTK2 */
1488
1489   Bool changed = False;
1490   GtkWidget *w;
1491
1492   if (s->saving_p) return False;
1493   s->saving_p = True;
1494
1495   *p2 = *p;
1496
1497   /* Flush any checkbox changes in the list down into the prefs struct.
1498    */
1499 #ifdef HAVE_GTK2
1500   closure.s = s;
1501   closure.changed = &changed;
1502   closure.i = 0;
1503   gtk_tree_model_foreach (model, flush_checkbox, &closure);
1504
1505 #else /* !HAVE_GTK2 */
1506
1507   for (i = 0; kids; kids = kids->next, i++)
1508     {
1509       GtkWidget *line = GTK_WIDGET (kids->data);
1510       GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1511       GtkWidget *line_check =
1512         GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1513       Bool checked =
1514         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1515
1516       if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1517         changed = True;
1518     }
1519 #endif /* ~HAVE_GTK2 */
1520
1521   /* Flush the non-hack-specific settings down into the prefs struct.
1522    */
1523
1524 # define SECONDS(FIELD,NAME) \
1525     w = name_to_widget (s, (NAME)); \
1526     hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1527
1528 # define MINUTES(FIELD,NAME) \
1529     w = name_to_widget (s, (NAME)); \
1530     hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1531
1532 # define CHECKBOX(FIELD,NAME) \
1533     w = name_to_widget (s, (NAME)); \
1534     (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1535
1536 # define PATHNAME(FIELD,NAME) \
1537     w = name_to_widget (s, (NAME)); \
1538     (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1539
1540 # define TEXT(FIELD,NAME) \
1541     w = name_to_widget (s, (NAME)); \
1542     (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
1543
1544   MINUTES  (&p2->timeout,         "timeout_spinbutton");
1545   MINUTES  (&p2->cycle,           "cycle_spinbutton");
1546   CHECKBOX (p2->lock_p,           "lock_button");
1547   MINUTES  (&p2->lock_timeout,    "lock_spinbutton");
1548
1549   CHECKBOX (p2->dpms_enabled_p,  "dpms_button");
1550   MINUTES  (&p2->dpms_standby,    "dpms_standby_spinbutton");
1551   MINUTES  (&p2->dpms_suspend,    "dpms_suspend_spinbutton");
1552   MINUTES  (&p2->dpms_off,        "dpms_off_spinbutton");
1553
1554   CHECKBOX (p2->grab_desktop_p,   "grab_desk_button");
1555   CHECKBOX (p2->grab_video_p,     "grab_video_button");
1556   CHECKBOX (p2->random_image_p,   "grab_image_button");
1557   PATHNAME (p2->image_directory,  "image_text");
1558
1559 #if 0
1560   CHECKBOX (p2->verbose_p,        "verbose_button");
1561   CHECKBOX (p2->capture_stderr_p, "capture_button");
1562   CHECKBOX (p2->splash_p,         "splash_button");
1563 #endif
1564
1565   {
1566     Bool v = False;
1567     CHECKBOX (v, "text_host_radio");     if (v) p2->tmode = TEXT_DATE;
1568     CHECKBOX (v, "text_radio");          if (v) p2->tmode = TEXT_LITERAL;
1569     CHECKBOX (v, "text_file_radio");     if (v) p2->tmode = TEXT_FILE;
1570     CHECKBOX (v, "text_program_radio");  if (v) p2->tmode = TEXT_PROGRAM;
1571     CHECKBOX (v, "text_url_radio");      if (v) p2->tmode = TEXT_URL;
1572     TEXT     (p2->text_literal, "text_entry");
1573     PATHNAME (p2->text_file,    "text_file_entry");
1574     PATHNAME (p2->text_program, "text_program_entry");
1575     PATHNAME (p2->text_program, "text_program_entry");
1576     TEXT     (p2->text_url,     "text_url_entry");
1577   }
1578
1579   CHECKBOX (p2->install_cmap_p,   "install_button");
1580   CHECKBOX (p2->fade_p,           "fade_button");
1581   CHECKBOX (p2->unfade_p,         "unfade_button");
1582   SECONDS  (&p2->fade_seconds,    "fade_spinbutton");
1583
1584 # undef SECONDS
1585 # undef MINUTES
1586 # undef CHECKBOX
1587 # undef PATHNAME
1588 # undef TEXT
1589
1590   /* Warn if the image directory doesn't exist.
1591    */
1592   if (p2->image_directory &&
1593       *p2->image_directory &&
1594       !directory_p (p2->image_directory))
1595     {
1596       char b[255];
1597       sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1598                p2->image_directory);
1599       warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1600     }
1601
1602
1603   /* Map the mode menu to `saver_mode' enum values. */
1604   {
1605     GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1606     GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1607     GtkWidget *selected = gtk_menu_get_active (menu);
1608     GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1609     int menu_elt = g_list_index (kids, (gpointer) selected);
1610     if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1611     p2->mode = mode_menu_order[menu_elt];
1612   }
1613
1614   if (p2->mode == ONE_HACK)
1615     {
1616       int list_elt = selected_list_element (s);
1617       p2->selected_hack = (list_elt >= 0
1618                            ? s->list_elt_to_hack_number[list_elt]
1619                            : -1);
1620     }
1621
1622 # define COPY(field, name) \
1623   if (p->field != p2->field) { \
1624     changed = True; \
1625     if (s->debug_p) \
1626       fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1627   } \
1628   p->field = p2->field
1629
1630   COPY(mode,             "mode");
1631   COPY(selected_hack,    "selected_hack");
1632
1633   COPY(timeout,        "timeout");
1634   COPY(cycle,          "cycle");
1635   COPY(lock_p,         "lock_p");
1636   COPY(lock_timeout,   "lock_timeout");
1637
1638   COPY(dpms_enabled_p, "dpms_enabled_p");
1639   COPY(dpms_standby,   "dpms_standby");
1640   COPY(dpms_suspend,   "dpms_suspend");
1641   COPY(dpms_off,       "dpms_off");
1642
1643 #if 0
1644   COPY(verbose_p,        "verbose_p");
1645   COPY(capture_stderr_p, "capture_stderr_p");
1646   COPY(splash_p,         "splash_p");
1647 #endif
1648
1649   COPY(tmode,            "tmode");
1650
1651   COPY(install_cmap_p,   "install_cmap_p");
1652   COPY(fade_p,           "fade_p");
1653   COPY(unfade_p,         "unfade_p");
1654   COPY(fade_seconds,     "fade_seconds");
1655
1656   COPY(grab_desktop_p, "grab_desktop_p");
1657   COPY(grab_video_p,   "grab_video_p");
1658   COPY(random_image_p, "random_image_p");
1659
1660 # undef COPY
1661
1662 # define COPYSTR(FIELD,NAME) \
1663   if (!p->FIELD || \
1664       !p2->FIELD || \
1665       strcmp(p->FIELD, p2->FIELD)) \
1666     { \
1667       changed = True; \
1668       if (s->debug_p) \
1669         fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1670     } \
1671   if (p->FIELD && p->FIELD != p2->FIELD) \
1672     free (p->FIELD); \
1673   p->FIELD = p2->FIELD; \
1674   p2->FIELD = 0
1675
1676   COPYSTR(image_directory, "image_directory");
1677   COPYSTR(text_literal,    "text_literal");
1678   COPYSTR(text_file,       "text_file");
1679   COPYSTR(text_program,    "text_program");
1680   COPYSTR(text_url,        "text_url");
1681 # undef COPYSTR
1682
1683   populate_prefs_page (s);
1684
1685   if (changed)
1686     {
1687       Display *dpy = GDK_DISPLAY();
1688       Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1689       sync_server_dpms_settings (dpy, enabled_p,
1690                                  p->dpms_standby / 1000,
1691                                  p->dpms_suspend / 1000,
1692                                  p->dpms_off / 1000,
1693                                  False);
1694
1695       changed = demo_write_init_file (s, p);
1696     }
1697
1698   s->saving_p = False;
1699   return changed;
1700 }
1701
1702
1703 /* Flush out any changes made in the popup dialog box (where changes
1704    take place only when the OK button is clicked.)
1705  */
1706 static Bool
1707 flush_popup_changes_and_save (state *s)
1708 {
1709   Bool changed = False;
1710   saver_preferences *p = &s->prefs;
1711   int list_elt = selected_list_element (s);
1712
1713   GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1714   GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1715
1716   const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1717   const char *command = gtk_entry_get_text (cmd);
1718
1719   char c;
1720   unsigned long id;
1721
1722   if (s->saving_p) return False;
1723   s->saving_p = True;
1724
1725   if (list_elt < 0)
1726     goto DONE;
1727
1728   if (maybe_reload_init_file (s) != 0)
1729     {
1730       changed = True;
1731       goto DONE;
1732     }
1733
1734   /* Sanity-check and canonicalize whatever the user typed into the combo box.
1735    */
1736   if      (!strcasecmp (visual, ""))                   visual = "";
1737   else if (!strcasecmp (visual, "any"))                visual = "";
1738   else if (!strcasecmp (visual, "default"))            visual = "Default";
1739   else if (!strcasecmp (visual, "default-n"))          visual = "Default-N";
1740   else if (!strcasecmp (visual, "default-i"))          visual = "Default-I";
1741   else if (!strcasecmp (visual, "best"))               visual = "Best";
1742   else if (!strcasecmp (visual, "mono"))               visual = "Mono";
1743   else if (!strcasecmp (visual, "monochrome"))         visual = "Mono";
1744   else if (!strcasecmp (visual, "gray"))               visual = "Gray";
1745   else if (!strcasecmp (visual, "grey"))               visual = "Gray";
1746   else if (!strcasecmp (visual, "color"))              visual = "Color";
1747   else if (!strcasecmp (visual, "gl"))                 visual = "GL";
1748   else if (!strcasecmp (visual, "staticgray"))         visual = "StaticGray";
1749   else if (!strcasecmp (visual, "staticcolor"))        visual = "StaticColor";
1750   else if (!strcasecmp (visual, "truecolor"))          visual = "TrueColor";
1751   else if (!strcasecmp (visual, "grayscale"))          visual = "GrayScale";
1752   else if (!strcasecmp (visual, "greyscale"))          visual = "GrayScale";
1753   else if (!strcasecmp (visual, "pseudocolor"))        visual = "PseudoColor";
1754   else if (!strcasecmp (visual, "directcolor"))        visual = "DirectColor";
1755   else if (1 == sscanf (visual, " %lu %c", &id, &c))   ;
1756   else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1757   else
1758     {
1759       gdk_beep ();                                /* unparsable */
1760       visual = "";
1761       gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1762     }
1763
1764   changed = flush_changes (s, list_elt, -1, command, visual);
1765   if (changed)
1766     {
1767       changed = demo_write_init_file (s, p);
1768
1769       /* Do this to re-launch the hack if (and only if) the command line
1770          has changed. */
1771       populate_demo_window (s, selected_list_element (s));
1772     }
1773
1774  DONE:
1775   s->saving_p = False;
1776   return changed;
1777 }
1778
1779
1780 G_MODULE_EXPORT void
1781 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1782 {
1783   state *s = global_state_kludge;  /* I hate C so much... */
1784   if (! s->initializing_p)
1785     {
1786       s->initializing_p = True;
1787       flush_dialog_changes_and_save (s);
1788       s->initializing_p = False;
1789     }
1790 }
1791
1792 G_MODULE_EXPORT gboolean
1793 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1794 {
1795   pref_changed_cb (widget, user_data);
1796   return FALSE;
1797 }
1798
1799 /* Callback on menu items in the "mode" options menu.
1800  */
1801 G_MODULE_EXPORT void
1802 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1803 {
1804   state *s = (state *) user_data;
1805   saver_preferences *p = &s->prefs;
1806   GtkWidget *list = name_to_widget (s, "list");
1807   int list_elt;
1808
1809   GList *menu_items =
1810     gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
1811   int menu_index = 0;
1812   saver_mode new_mode;
1813
1814   while (menu_items)
1815     {
1816       if (menu_items->data == widget)
1817         break;
1818       menu_index++;
1819       menu_items = menu_items->next;
1820     }
1821   if (!menu_items) abort();
1822
1823   new_mode = mode_menu_order[menu_index];
1824
1825   /* Keep the same list element displayed as before; except if we're
1826      switching *to* "one screensaver" mode from any other mode, set
1827      "the one" to be that which is currently selected.
1828    */
1829   list_elt = selected_list_element (s);
1830   if (new_mode == ONE_HACK)
1831     p->selected_hack = s->list_elt_to_hack_number[list_elt];
1832
1833   {
1834     saver_mode old_mode = p->mode;
1835     p->mode = new_mode;
1836     populate_demo_window (s, list_elt);
1837     force_list_select_item (s, list, list_elt, True);
1838     p->mode = old_mode;  /* put it back, so the init file gets written */
1839   }
1840
1841   pref_changed_cb (widget, user_data);
1842 }
1843
1844
1845 G_MODULE_EXPORT void
1846 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1847                 gint page_num, gpointer user_data)
1848 {
1849   state *s = global_state_kludge;  /* I hate C so much... */
1850   pref_changed_cb (GTK_WIDGET (notebook), user_data);
1851
1852   /* If we're switching to page 0, schedule the current hack to be run.
1853      Otherwise, schedule it to stop. */
1854   if (page_num == 0)
1855     populate_demo_window (s, selected_list_element (s));
1856   else
1857     schedule_preview (s, 0);
1858 }
1859
1860 #ifdef HAVE_GTK2
1861 static void
1862 list_activated_cb (GtkTreeView       *list,
1863                    GtkTreePath       *path,
1864                    GtkTreeViewColumn *column,
1865                    gpointer           data)
1866 {
1867   state *s = data;
1868   char *str;
1869   int list_elt;
1870
1871   g_return_if_fail (!gdk_pointer_is_grabbed ());
1872
1873   str = gtk_tree_path_to_string (path);
1874   list_elt = strtol (str, NULL, 10);
1875   g_free (str);
1876
1877   if (list_elt >= 0)
1878     run_hack (s, list_elt, True);
1879 }
1880
1881 static void
1882 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1883 {
1884   state *s = (state *)data;
1885   GtkTreeModel *model;
1886   GtkTreeIter iter;
1887   GtkTreePath *path;
1888   char *str;
1889   int list_elt;
1890  
1891   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1892     return;
1893
1894   path = gtk_tree_model_get_path (model, &iter);
1895   str = gtk_tree_path_to_string (path);
1896   list_elt = strtol (str, NULL, 10);
1897
1898   gtk_tree_path_free (path);
1899   g_free (str);
1900
1901   populate_demo_window (s, list_elt);
1902   flush_dialog_changes_and_save (s);
1903
1904   /* Re-populate the Settings window any time a new item is selected
1905      in the list, in case both windows are currently visible.
1906    */
1907   populate_popup_window (s);
1908 }
1909
1910 #else /* !HAVE_GTK2 */
1911
1912 static time_t last_doubleclick_time = 0;   /* FMH!  This is to suppress the
1913                                               list_select_cb that comes in
1914                                               *after* we've double-clicked.
1915                                             */
1916
1917 static gint
1918 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1919                      gpointer data)
1920 {
1921   state *s = (state *) data;
1922   if (event->type == GDK_2BUTTON_PRESS)
1923     {
1924       GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1925       int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1926
1927       last_doubleclick_time = time ((time_t *) 0);
1928
1929       if (list_elt >= 0)
1930         run_hack (s, list_elt, True);
1931     }
1932
1933   return FALSE;
1934 }
1935
1936
1937 static void
1938 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1939 {
1940   state *s = (state *) data;
1941   time_t now = time ((time_t *) 0);
1942
1943   if (now >= last_doubleclick_time + 2)
1944     {
1945       int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1946       populate_demo_window (s, list_elt);
1947       flush_dialog_changes_and_save (s);
1948     }
1949 }
1950
1951 static void
1952 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1953 {
1954   state *s = (state *) data;
1955   populate_demo_window (s, -1);
1956   flush_dialog_changes_and_save (s);
1957 }
1958
1959 #endif /* !HAVE_GTK2 */
1960
1961
1962 /* Called when the checkboxes that are in the left column of the
1963    scrolling list are clicked.  This both populates the right pane
1964    (just as clicking on the label (really, listitem) does) and
1965    also syncs this checkbox with  the right pane Enabled checkbox.
1966  */
1967 static void
1968 list_checkbox_cb (
1969 #ifdef HAVE_GTK2
1970                   GtkCellRendererToggle *toggle,
1971                   gchar                 *path_string,
1972 #else  /* !HAVE_GTK2 */
1973                   GtkWidget *cb,
1974 #endif /* !HAVE_GTK2 */
1975                   gpointer               data)
1976 {
1977   state *s = (state *) data;
1978
1979 #ifdef HAVE_GTK2
1980   GtkScrolledWindow *scroller =
1981     GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
1982   GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
1983   GtkTreeModel *model = gtk_tree_view_get_model (list);
1984   GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
1985   GtkTreeIter iter;
1986   gboolean active;
1987 #else /* !HAVE_GTK2 */
1988   GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1989   GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1990
1991   GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1992   GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1993   GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1994 #endif /* !HAVE_GTK2 */
1995   GtkAdjustment *adj;
1996   double scroll_top;
1997
1998   int list_elt;
1999
2000 #ifdef HAVE_GTK2
2001   if (!gtk_tree_model_get_iter (model, &iter, path))
2002     {
2003       g_warning ("bad path: %s", path_string);
2004       return;
2005     }
2006   gtk_tree_path_free (path);
2007
2008   gtk_tree_model_get (model, &iter,
2009                       COL_ENABLED, &active,
2010                       -1);
2011
2012   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2013                       COL_ENABLED, !active,
2014                       -1);
2015
2016   list_elt = strtol (path_string, NULL, 10);  
2017 #else  /* !HAVE_GTK2 */
2018   list_elt = gtk_list_child_position (list, line);
2019 #endif /* !HAVE_GTK2 */
2020
2021   /* remember previous scroll position of the top of the list */
2022   adj = gtk_scrolled_window_get_vadjustment (scroller);
2023   scroll_top = GET_ADJ_VALUE (adj);
2024
2025   flush_dialog_changes_and_save (s);
2026   force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
2027   populate_demo_window (s, list_elt);
2028   
2029   /* restore the previous scroll position of the top of the list.
2030      this is weak, but I don't really know why it's moving... */
2031   gtk_adjustment_set_value (adj, scroll_top);
2032 }
2033
2034
2035 typedef struct {
2036   state *state;
2037   GtkFileSelection *widget;
2038 } file_selection_data;
2039
2040
2041
2042 static void
2043 store_image_directory (GtkWidget *button, gpointer user_data)
2044 {
2045   file_selection_data *fsd = (file_selection_data *) user_data;
2046   state *s = fsd->state;
2047   GtkFileSelection *selector = fsd->widget;
2048   GtkWidget *top = s->toplevel_widget;
2049   saver_preferences *p = &s->prefs;
2050   const char *path = gtk_file_selection_get_filename (selector);
2051
2052   if (p->image_directory && !strcmp(p->image_directory, path))
2053     return;  /* no change */
2054
2055   if (!directory_p (path))
2056     {
2057       char b[255];
2058       sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2059       warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2060       return;
2061     }
2062
2063   if (p->image_directory) free (p->image_directory);
2064   p->image_directory = normalize_directory (path);
2065
2066   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2067                       (p->image_directory ? p->image_directory : ""));
2068   demo_write_init_file (s, p);
2069 }
2070
2071
2072 static void
2073 store_text_file (GtkWidget *button, gpointer user_data)
2074 {
2075   file_selection_data *fsd = (file_selection_data *) user_data;
2076   state *s = fsd->state;
2077   GtkFileSelection *selector = fsd->widget;
2078   GtkWidget *top = s->toplevel_widget;
2079   saver_preferences *p = &s->prefs;
2080   const char *path = gtk_file_selection_get_filename (selector);
2081
2082   if (p->text_file && !strcmp(p->text_file, path))
2083     return;  /* no change */
2084
2085   if (!file_p (path))
2086     {
2087       char b[255];
2088       sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2089       warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2090       return;
2091     }
2092
2093   if (p->text_file) free (p->text_file);
2094   p->text_file = normalize_directory (path);
2095
2096   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2097                       (p->text_file ? p->text_file : ""));
2098   demo_write_init_file (s, p);
2099 }
2100
2101
2102 static void
2103 store_text_program (GtkWidget *button, gpointer user_data)
2104 {
2105   file_selection_data *fsd = (file_selection_data *) user_data;
2106   state *s = fsd->state;
2107   GtkFileSelection *selector = fsd->widget;
2108   /*GtkWidget *top = s->toplevel_widget;*/
2109   saver_preferences *p = &s->prefs;
2110   const char *path = gtk_file_selection_get_filename (selector);
2111
2112   if (p->text_program && !strcmp(p->text_program, path))
2113     return;  /* no change */
2114
2115 # if 0
2116   if (!file_p (path))
2117     {
2118       char b[255];
2119       sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2120       warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2121       return;
2122     }
2123 # endif
2124
2125   if (p->text_program) free (p->text_program);
2126   p->text_program = normalize_directory (path);
2127
2128   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2129                       (p->text_program ? p->text_program : ""));
2130   demo_write_init_file (s, p);
2131 }
2132
2133
2134
2135 static void
2136 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2137 {
2138   file_selection_data *fsd = (file_selection_data *) user_data;
2139   gtk_widget_hide (GTK_WIDGET (fsd->widget));
2140 }
2141
2142 static void
2143 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2144 {
2145   browse_image_dir_cancel (button, user_data);
2146   store_image_directory (button, user_data);
2147 }
2148
2149 static void
2150 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2151 {
2152   browse_image_dir_cancel (button, user_data);
2153   store_text_file (button, user_data);
2154 }
2155
2156 static void
2157 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2158 {
2159   browse_image_dir_cancel (button, user_data);
2160   store_text_program (button, user_data);
2161 }
2162
2163 static void
2164 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2165 {
2166   browse_image_dir_cancel (widget, user_data);
2167 }
2168
2169
2170 G_MODULE_EXPORT void
2171 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2172 {
2173   state *s = global_state_kludge;  /* I hate C so much... */
2174   saver_preferences *p = &s->prefs;
2175   static file_selection_data *fsd = 0;
2176
2177   GtkFileSelection *selector = GTK_FILE_SELECTION(
2178     gtk_file_selection_new ("Please select the image directory."));
2179
2180   if (!fsd)
2181     fsd = (file_selection_data *) malloc (sizeof (*fsd));  
2182
2183   fsd->widget = selector;
2184   fsd->state = s;
2185
2186   if (p->image_directory && *p->image_directory)
2187     gtk_file_selection_set_filename (selector, p->image_directory);
2188
2189   gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2190                       "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2191                       (gpointer *) fsd);
2192   gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2193                       "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2194                       (gpointer *) fsd);
2195   gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2196                       GTK_SIGNAL_FUNC (browse_image_dir_close),
2197                       (gpointer *) fsd);
2198
2199   gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2200
2201   gtk_window_set_modal (GTK_WINDOW (selector), True);
2202   gtk_widget_show (GTK_WIDGET (selector));
2203 }
2204
2205
2206 G_MODULE_EXPORT void
2207 browse_text_file_cb (GtkButton *button, gpointer user_data)
2208 {
2209   state *s = global_state_kludge;  /* I hate C so much... */
2210   saver_preferences *p = &s->prefs;
2211   static file_selection_data *fsd = 0;
2212
2213   GtkFileSelection *selector = GTK_FILE_SELECTION(
2214     gtk_file_selection_new ("Please select a text file."));
2215
2216   if (!fsd)
2217     fsd = (file_selection_data *) malloc (sizeof (*fsd));  
2218
2219   fsd->widget = selector;
2220   fsd->state = s;
2221
2222   if (p->text_file && *p->text_file)
2223     gtk_file_selection_set_filename (selector, p->text_file);
2224
2225   gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2226                       "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2227                       (gpointer *) fsd);
2228   gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2229                       "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2230                       (gpointer *) fsd);
2231   gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2232                       GTK_SIGNAL_FUNC (browse_image_dir_close),
2233                       (gpointer *) fsd);
2234
2235   gtk_window_set_modal (GTK_WINDOW (selector), True);
2236   gtk_widget_show (GTK_WIDGET (selector));
2237 }
2238
2239
2240 G_MODULE_EXPORT void
2241 browse_text_program_cb (GtkButton *button, gpointer user_data)
2242 {
2243   state *s = global_state_kludge;  /* I hate C so much... */
2244   saver_preferences *p = &s->prefs;
2245   static file_selection_data *fsd = 0;
2246
2247   GtkFileSelection *selector = GTK_FILE_SELECTION(
2248     gtk_file_selection_new ("Please select a text-generating program."));
2249
2250   if (!fsd)
2251     fsd = (file_selection_data *) malloc (sizeof (*fsd));  
2252
2253   fsd->widget = selector;
2254   fsd->state = s;
2255
2256   if (p->text_program && *p->text_program)
2257     gtk_file_selection_set_filename (selector, p->text_program);
2258
2259   gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2260                       "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2261                       (gpointer *) fsd);
2262   gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2263                       "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2264                       (gpointer *) fsd);
2265   gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2266                       GTK_SIGNAL_FUNC (browse_image_dir_close),
2267                       (gpointer *) fsd);
2268
2269   gtk_window_set_modal (GTK_WINDOW (selector), True);
2270   gtk_widget_show (GTK_WIDGET (selector));
2271 }
2272
2273
2274
2275
2276
2277 G_MODULE_EXPORT void
2278 settings_cb (GtkButton *button, gpointer user_data)
2279 {
2280   state *s = global_state_kludge;  /* I hate C so much... */
2281   int list_elt = selected_list_element (s);
2282
2283   populate_demo_window (s, list_elt);   /* reset the widget */
2284   populate_popup_window (s);            /* create UI on popup window */
2285   gtk_widget_show (s->popup_widget);
2286 }
2287
2288 static void
2289 settings_sync_cmd_text (state *s)
2290 {
2291 # ifdef HAVE_XML
2292   GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2293   char *cmd_line = get_configurator_command_line (s->cdata, False);
2294   gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2295   gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2296   free (cmd_line);
2297 # endif /* HAVE_XML */
2298 }
2299
2300 G_MODULE_EXPORT void
2301 settings_adv_cb (GtkButton *button, gpointer user_data)
2302 {
2303   state *s = global_state_kludge;  /* I hate C so much... */
2304   GtkNotebook *notebook =
2305     GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2306
2307   settings_sync_cmd_text (s);
2308   gtk_notebook_set_page (notebook, 1);
2309 }
2310
2311 G_MODULE_EXPORT void
2312 settings_std_cb (GtkButton *button, gpointer user_data)
2313 {
2314   state *s = global_state_kludge;  /* I hate C so much... */
2315   GtkNotebook *notebook =
2316     GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2317
2318   /* Re-create UI to reflect the in-progress command-line settings. */
2319   populate_popup_window (s);
2320
2321   gtk_notebook_set_page (notebook, 0);
2322 }
2323
2324 G_MODULE_EXPORT void
2325 settings_reset_cb (GtkButton *button, gpointer user_data)
2326 {
2327 # ifdef HAVE_XML
2328   state *s = global_state_kludge;  /* I hate C so much... */
2329   GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2330   char *cmd_line = get_configurator_command_line (s->cdata, True);
2331   gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2332   gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2333   free (cmd_line);
2334   populate_popup_window (s);
2335 # endif /* HAVE_XML */
2336 }
2337
2338 G_MODULE_EXPORT void
2339 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2340                          gint page_num, gpointer user_data)
2341 {
2342   state *s = global_state_kludge;  /* I hate C so much... */
2343   GtkWidget *adv = name_to_widget (s, "adv_button");
2344   GtkWidget *std = name_to_widget (s, "std_button");
2345
2346   if (page_num == 0)
2347     {
2348       gtk_widget_show (adv);
2349       gtk_widget_hide (std);
2350     }
2351   else if (page_num == 1)
2352     {
2353       gtk_widget_hide (adv);
2354       gtk_widget_show (std);
2355     }
2356   else
2357     abort();
2358 }
2359
2360
2361
2362 G_MODULE_EXPORT void
2363 settings_cancel_cb (GtkButton *button, gpointer user_data)
2364 {
2365   state *s = global_state_kludge;  /* I hate C so much... */
2366   gtk_widget_hide (s->popup_widget);
2367 }
2368
2369 G_MODULE_EXPORT void
2370 settings_ok_cb (GtkButton *button, gpointer user_data)
2371 {
2372   state *s = global_state_kludge;  /* I hate C so much... */
2373   GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2374   int page = gtk_notebook_get_current_page (notebook);
2375
2376   if (page == 0)
2377     /* Regenerate the command-line from the widget contents before saving.
2378        But don't do this if we're looking at the command-line page already,
2379        or we will blow away what they typed... */
2380     settings_sync_cmd_text (s);
2381
2382   flush_popup_changes_and_save (s);
2383   gtk_widget_hide (s->popup_widget);
2384 }
2385
2386 static gboolean
2387 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2388 {
2389   state *s = (state *) data;
2390   settings_cancel_cb (0, (gpointer) s);
2391   return TRUE;
2392 }
2393
2394
2395 \f
2396 /* Populating the various widgets
2397  */
2398
2399
2400 /* Returns the number of the last hack run by the server.
2401  */
2402 static int
2403 server_current_hack (void)
2404 {
2405   Atom type;
2406   int format;
2407   unsigned long nitems, bytesafter;
2408   unsigned char *dataP = 0;
2409   Display *dpy = GDK_DISPLAY();
2410   int hack_number = -1;
2411
2412   if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2413                           XA_SCREENSAVER_STATUS,
2414                           0, 3, False, XA_INTEGER,
2415                           &type, &format, &nitems, &bytesafter,
2416                           &dataP)
2417       == Success
2418       && type == XA_INTEGER
2419       && nitems >= 3
2420       && dataP)
2421     {
2422       PROP32 *data = (PROP32 *) dataP;
2423       hack_number = (int) data[2] - 1;
2424     }
2425
2426   if (dataP) XFree (dataP);
2427
2428   return hack_number;
2429 }
2430
2431
2432 /* Finds the number of the last hack that was run, and makes that item be
2433    selected by default.
2434  */
2435 static void
2436 scroll_to_current_hack (state *s)
2437 {
2438   saver_preferences *p = &s->prefs;
2439   int hack_number = -1;
2440
2441   if (p->mode == ONE_HACK)                 /* in "one" mode, use the one */
2442     hack_number = p->selected_hack;
2443   if (hack_number < 0)                     /* otherwise, use the last-run */
2444     hack_number = server_current_hack ();
2445   if (hack_number < 0)                     /* failing that, last "one mode" */
2446     hack_number = p->selected_hack;
2447   if (hack_number < 0)                     /* failing that, newest hack. */
2448     {
2449       /* We should only get here if the user does not have a .xscreensaver
2450          file, and the screen has not been blanked with a hack since X
2451          started up: in other words, this is probably a fresh install.
2452
2453          Instead of just defaulting to hack #0 (in either "programs" or
2454          "alphabetical" order) let's try to default to the last runnable
2455          hack in the "programs" list: this is probably the hack that was
2456          most recently added to the xscreensaver distribution (and so
2457          it's probably the currently-coolest one!)
2458        */
2459       hack_number = p->screenhacks_count-1;
2460       while (hack_number > 0 &&
2461              ! (s->hacks_available_p[hack_number] &&
2462                 p->screenhacks[hack_number]->enabled_p))
2463         hack_number--;
2464     }
2465
2466   if (hack_number >= 0 && hack_number < p->screenhacks_count)
2467     {
2468       int list_elt = s->hack_number_to_list_elt[hack_number];
2469       GtkWidget *list = name_to_widget (s, "list");
2470       force_list_select_item (s, list, list_elt, True);
2471       populate_demo_window (s, list_elt);
2472     }
2473 }
2474
2475
2476 static void
2477 populate_hack_list (state *s)
2478 {
2479   Display *dpy = GDK_DISPLAY();
2480 #ifdef HAVE_GTK2
2481   saver_preferences *p = &s->prefs;
2482   GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2483   GtkListStore *model;
2484   GtkTreeSelection *selection;
2485   GtkCellRenderer *ren;
2486   GtkTreeIter iter;
2487   int i;
2488
2489   g_object_get (G_OBJECT (list),
2490                 "model", &model,
2491                 NULL);
2492   if (!model)
2493     {
2494       model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2495       g_object_set (G_OBJECT (list), "model", model, NULL);
2496       g_object_unref (model);
2497
2498       ren = gtk_cell_renderer_toggle_new ();
2499       gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2500                                                    _("Use"), ren,
2501                                                    "active", COL_ENABLED,
2502                                                    NULL);
2503
2504       g_signal_connect (ren, "toggled",
2505                         G_CALLBACK (list_checkbox_cb),
2506                         s);
2507
2508       ren = gtk_cell_renderer_text_new ();
2509       gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2510                                                    _("Screen Saver"), ren,
2511                                                    "markup", COL_NAME,
2512                                                    NULL);
2513
2514       g_signal_connect_after (list, "row_activated",
2515                               G_CALLBACK (list_activated_cb),
2516                               s);
2517
2518       selection = gtk_tree_view_get_selection (list);
2519       g_signal_connect (selection, "changed",
2520                         G_CALLBACK (list_select_changed_cb),
2521                         s);
2522
2523     }
2524
2525   for (i = 0; i < s->list_count; i++)
2526     {
2527       int hack_number = s->list_elt_to_hack_number[i];
2528       screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2529       char *pretty_name;
2530       Bool available_p = (hack && s->hacks_available_p [hack_number]);
2531
2532       if (!hack) continue;
2533
2534       /* If we're to suppress uninstalled hacks, check $PATH now. */
2535       if (p->ignore_uninstalled_p && !available_p)
2536         continue;
2537
2538       pretty_name = (hack->name
2539                      ? strdup (hack->name)
2540                      : make_hack_name (dpy, hack->command));
2541
2542       if (!available_p)
2543         {
2544           /* Make the text foreground be the color of insensitive widgets
2545              (but don't actually make it be insensitive, since we still
2546              want to be able to click on it.)
2547            */
2548           GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
2549           GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2550        /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2551           char *buf = (char *) malloc (strlen (pretty_name) + 100);
2552
2553           sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2554                       /*     " background=\"#%02X%02X%02X\""  */
2555                         ">%s</span>",
2556                    fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2557                 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2558                    pretty_name);
2559           free (pretty_name);
2560           pretty_name = buf;
2561         }
2562
2563       gtk_list_store_append (model, &iter);
2564       gtk_list_store_set (model, &iter,
2565                           COL_ENABLED, hack->enabled_p,
2566                           COL_NAME, pretty_name,
2567                           -1);
2568       free (pretty_name);
2569     }
2570
2571 #else /* !HAVE_GTK2 */
2572
2573   saver_preferences *p = &s->prefs;
2574   GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2575   int i;
2576   for (i = 0; i < s->list_count; i++)
2577     {
2578       int hack_number = s->list_elt_to_hack_number[i];
2579       screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2580
2581       /* A GtkList must contain only GtkListItems, but those can contain
2582          an arbitrary widget.  We add an Hbox, and inside that, a Checkbox
2583          and a Label.  We handle single and double click events on the
2584          line itself, for clicking on the text, but the interior checkbox
2585          also handles its own events.
2586        */
2587       GtkWidget *line;
2588       GtkWidget *line_hbox;
2589       GtkWidget *line_check;
2590       GtkWidget *line_label;
2591       char *pretty_name;
2592       Bool available_p = (hack && s->hacks_available_p [hack_number]);
2593
2594       if (!hack) continue;
2595
2596       /* If we're to suppress uninstalled hacks, check $PATH now. */
2597       if (p->ignore_uninstalled_p && !available_p)
2598         continue;
2599
2600       pretty_name = (hack->name
2601                      ? strdup (hack->name)
2602                      : make_hack_name (hack->command));
2603
2604       line = gtk_list_item_new ();
2605       line_hbox = gtk_hbox_new (FALSE, 0);
2606       line_check = gtk_check_button_new ();
2607       line_label = gtk_label_new (pretty_name);
2608
2609       gtk_container_add (GTK_CONTAINER (line), line_hbox);
2610       gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2611       gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2612
2613       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2614                                     hack->enabled_p);
2615       gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2616
2617       gtk_widget_show (line_check);
2618       gtk_widget_show (line_label);
2619       gtk_widget_show (line_hbox);
2620       gtk_widget_show (line);
2621
2622       free (pretty_name);
2623
2624       gtk_container_add (GTK_CONTAINER (list), line);
2625       gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2626                           GTK_SIGNAL_FUNC (list_doubleclick_cb),
2627                           (gpointer) s);
2628
2629       gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2630                           GTK_SIGNAL_FUNC (list_checkbox_cb),
2631                           (gpointer) s);
2632
2633       gtk_widget_show (line);
2634
2635       if (!available_p)
2636         {
2637           /* Make the widget be colored like insensitive widgets
2638              (but don't actually make it be insensitive, since we
2639              still want to be able to click on it.)
2640            */
2641           GtkRcStyle *rc_style;
2642           GdkColor fg, bg;
2643
2644           gtk_widget_realize (GTK_WIDGET (line_label));
2645
2646           fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2647           bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2648
2649           rc_style = gtk_rc_style_new ();
2650           rc_style->fg[GTK_STATE_NORMAL] = fg;
2651           rc_style->bg[GTK_STATE_NORMAL] = bg;
2652           rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2653
2654           gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2655           gtk_rc_style_unref (rc_style);
2656         }
2657     }
2658
2659   gtk_signal_connect (GTK_OBJECT (list), "select_child",
2660                       GTK_SIGNAL_FUNC (list_select_cb),
2661                       (gpointer) s);
2662   gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2663                       GTK_SIGNAL_FUNC (list_unselect_cb),
2664                       (gpointer) s);
2665 #endif /* !HAVE_GTK2 */
2666 }
2667
2668 static void
2669 update_list_sensitivity (state *s)
2670 {
2671   saver_preferences *p = &s->prefs;
2672   Bool sensitive = (p->mode == RANDOM_HACKS ||
2673                     p->mode == RANDOM_HACKS_SAME ||
2674                     p->mode == ONE_HACK);
2675   Bool checkable = (p->mode == RANDOM_HACKS ||
2676                     p->mode == RANDOM_HACKS_SAME);
2677   Bool blankable = (p->mode != DONT_BLANK);
2678
2679 #ifndef HAVE_GTK2
2680   GtkWidget *head     = name_to_widget (s, "col_head_hbox");
2681   GtkWidget *use      = name_to_widget (s, "use_col_frame");
2682 #endif /* HAVE_GTK2 */
2683   GtkWidget *scroller = name_to_widget (s, "scroller");
2684   GtkWidget *buttons  = name_to_widget (s, "next_prev_hbox");
2685   GtkWidget *blanker  = name_to_widget (s, "blanking_table");
2686
2687 #ifdef HAVE_GTK2
2688   GtkTreeView *list      = GTK_TREE_VIEW (name_to_widget (s, "list"));
2689   GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2690 #else /* !HAVE_GTK2 */
2691   GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2692   GList *kids   = gtk_container_children (GTK_CONTAINER (list));
2693
2694   gtk_widget_set_sensitive (GTK_WIDGET (head),     sensitive);
2695 #endif /* !HAVE_GTK2 */
2696   gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2697   gtk_widget_set_sensitive (GTK_WIDGET (buttons),  sensitive);
2698
2699   gtk_widget_set_sensitive (GTK_WIDGET (blanker),  blankable);
2700
2701 #ifdef HAVE_GTK2
2702   gtk_tree_view_column_set_visible (use, checkable);
2703 #else  /* !HAVE_GTK2 */
2704   if (checkable)
2705     gtk_widget_show (use);   /* the "Use" column header */
2706   else
2707     gtk_widget_hide (use);
2708
2709   while (kids)
2710     {
2711       GtkBin *line = GTK_BIN (kids->data);
2712       GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2713       GtkWidget *line_check =
2714         GTK_WIDGET (gtk_container_children (line_hbox)->data);
2715       
2716       if (checkable)
2717         gtk_widget_show (line_check);
2718       else
2719         gtk_widget_hide (line_check);
2720
2721       kids = kids->next;
2722     }
2723 #endif /* !HAVE_GTK2 */
2724 }
2725
2726
2727 static void
2728 populate_prefs_page (state *s)
2729 {
2730   saver_preferences *p = &s->prefs;
2731
2732   Bool can_lock_p = True;
2733
2734   /* Disable all the "lock" controls if locking support was not provided
2735      at compile-time, or if running on MacOS. */
2736 # if defined(NO_LOCKING) || defined(__APPLE__)
2737   can_lock_p = False;
2738 # endif
2739
2740
2741   /* If there is only one screen, the mode menu contains
2742      "random" but not "random-same".
2743    */
2744   if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2745     p->mode = RANDOM_HACKS;
2746
2747
2748   /* The file supports timeouts of less than a minute, but the GUI does
2749      not, so throttle the values to be at least one minute (since "0" is
2750      a bad rounding choice...)
2751    */
2752 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2753   THROTTLE (timeout);
2754   THROTTLE (cycle);
2755   /* THROTTLE (passwd_timeout); */  /* GUI doesn't set this; leave it alone */
2756 # undef THROTTLE
2757
2758 # define FMT_MINUTES(NAME,N) \
2759     gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2760
2761 # define FMT_SECONDS(NAME,N) \
2762     gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2763
2764   FMT_MINUTES ("timeout_spinbutton",      p->timeout);
2765   FMT_MINUTES ("cycle_spinbutton",        p->cycle);
2766   FMT_MINUTES ("lock_spinbutton",         p->lock_timeout);
2767   FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2768   FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2769   FMT_MINUTES ("dpms_off_spinbutton",     p->dpms_off);
2770   FMT_SECONDS ("fade_spinbutton",         p->fade_seconds);
2771
2772 # undef FMT_MINUTES
2773 # undef FMT_SECONDS
2774
2775 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2776   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2777                                 (ACTIVEP))
2778
2779   TOGGLE_ACTIVE ("lock_button",       p->lock_p);
2780 #if 0
2781   TOGGLE_ACTIVE ("verbose_button",    p->verbose_p);
2782   TOGGLE_ACTIVE ("capture_button",    p->capture_stderr_p);
2783   TOGGLE_ACTIVE ("splash_button",     p->splash_p);
2784 #endif
2785   TOGGLE_ACTIVE ("dpms_button",       p->dpms_enabled_p);
2786   TOGGLE_ACTIVE ("grab_desk_button",  p->grab_desktop_p);
2787   TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2788   TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2789   TOGGLE_ACTIVE ("install_button",    p->install_cmap_p);
2790   TOGGLE_ACTIVE ("fade_button",       p->fade_p);
2791   TOGGLE_ACTIVE ("unfade_button",     p->unfade_p);
2792
2793   switch (p->tmode)
2794     {
2795     case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio",         True); break;
2796     case TEXT_FILE:    TOGGLE_ACTIVE ("text_file_radio",    True); break;
2797     case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2798     case TEXT_URL:     TOGGLE_ACTIVE ("text_url_radio",     True); break;
2799     default:           TOGGLE_ACTIVE ("text_host_radio",    True); break;
2800     }
2801
2802 # undef TOGGLE_ACTIVE
2803
2804   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2805                       (p->image_directory ? p->image_directory : ""));
2806   gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2807                             p->random_image_p);
2808   gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2809                             p->random_image_p);
2810
2811   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2812                       (p->text_literal ? p->text_literal : ""));
2813   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2814                       (p->text_file ? p->text_file : ""));
2815   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2816                       (p->text_program ? p->text_program : ""));
2817   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2818                       (p->text_url ? p->text_url : ""));
2819
2820   gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2821                             p->tmode == TEXT_LITERAL);
2822   gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2823                             p->tmode == TEXT_FILE);
2824   gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2825                             p->tmode == TEXT_FILE);
2826   gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2827                             p->tmode == TEXT_PROGRAM);
2828   gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2829                             p->tmode == TEXT_PROGRAM);
2830   gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2831                             p->tmode == TEXT_URL);
2832
2833
2834   /* Map the `saver_mode' enum to mode menu to values. */
2835   {
2836     GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2837
2838     int i;
2839     for (i = 0; i < countof(mode_menu_order); i++)
2840       if (mode_menu_order[i] == p->mode)
2841         break;
2842     gtk_option_menu_set_history (opt, i);
2843     update_list_sensitivity (s);
2844   }
2845
2846   {
2847     Bool found_any_writable_cells = False;
2848     Bool fading_possible = False;
2849     Bool dpms_supported = False;
2850
2851     Display *dpy = GDK_DISPLAY();
2852     int nscreens = ScreenCount(dpy);  /* real screens, not Xinerama */
2853     int i;
2854     for (i = 0; i < nscreens; i++)
2855       {
2856         Screen *s = ScreenOfDisplay (dpy, i);
2857         if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2858           {
2859             found_any_writable_cells = True;
2860             break;
2861           }
2862       }
2863
2864     fading_possible = found_any_writable_cells;
2865 #ifdef HAVE_XF86VMODE_GAMMA
2866     fading_possible = True;
2867 #endif
2868
2869 #ifdef HAVE_DPMS_EXTENSION
2870     {
2871       int op = 0, event = 0, error = 0;
2872       if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2873         dpms_supported = True;
2874     }
2875 #endif /* HAVE_DPMS_EXTENSION */
2876
2877
2878 # define SENSITIZE(NAME,SENSITIVEP) \
2879     gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2880
2881     /* Blanking and Locking
2882      */
2883     SENSITIZE ("lock_button",     can_lock_p);
2884     SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2885     SENSITIZE ("lock_mlabel",     can_lock_p && p->lock_p);
2886
2887     /* DPMS
2888      */
2889     SENSITIZE ("dpms_frame",              dpms_supported);
2890     SENSITIZE ("dpms_button",             dpms_supported);
2891     SENSITIZE ("dpms_standby_label",      dpms_supported && p->dpms_enabled_p);
2892     SENSITIZE ("dpms_standby_mlabel",     dpms_supported && p->dpms_enabled_p);
2893     SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2894     SENSITIZE ("dpms_suspend_label",      dpms_supported && p->dpms_enabled_p);
2895     SENSITIZE ("dpms_suspend_mlabel",     dpms_supported && p->dpms_enabled_p);
2896     SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2897     SENSITIZE ("dpms_off_label",          dpms_supported && p->dpms_enabled_p);
2898     SENSITIZE ("dpms_off_mlabel",         dpms_supported && p->dpms_enabled_p);
2899     SENSITIZE ("dpms_off_spinbutton",     dpms_supported && p->dpms_enabled_p);
2900
2901     /* Colormaps
2902      */
2903     SENSITIZE ("cmap_frame",      found_any_writable_cells || fading_possible);
2904     SENSITIZE ("install_button",  found_any_writable_cells);
2905     SENSITIZE ("fade_button",     fading_possible);
2906     SENSITIZE ("unfade_button",   fading_possible);
2907
2908     SENSITIZE ("fade_label",      (fading_possible &&
2909                                    (p->fade_p || p->unfade_p)));
2910     SENSITIZE ("fade_spinbutton", (fading_possible &&
2911                                    (p->fade_p || p->unfade_p)));
2912
2913 # undef SENSITIZE
2914   }
2915 }
2916
2917
2918 static void
2919 populate_popup_window (state *s)
2920 {
2921   GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2922   char *doc_string = 0;
2923
2924   /* #### not in Gtk 1.2
2925   gtk_label_set_selectable (doc);
2926    */
2927
2928 # ifdef HAVE_XML
2929   if (s->cdata)
2930     {
2931       free_conf_data (s->cdata);
2932       s->cdata = 0;
2933     }
2934
2935   {
2936     saver_preferences *p = &s->prefs;
2937     int list_elt = selected_list_element (s);
2938     int hack_number = (list_elt >= 0 && list_elt < s->list_count
2939                        ? s->list_elt_to_hack_number[list_elt]
2940                        : -1);
2941     screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2942     if (hack)
2943       {
2944         GtkWidget *parent = name_to_widget (s, "settings_vbox");
2945         GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2946         const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2947         s->cdata = load_configurator (cmd_line, s->debug_p);
2948         if (s->cdata && s->cdata->widget)
2949           gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2950                               TRUE, TRUE, 0);
2951       }
2952   }
2953
2954   doc_string = (s->cdata
2955                 ? s->cdata->description
2956                 : 0);
2957 # else  /* !HAVE_XML */
2958   doc_string = _("Descriptions not available: no XML support compiled in.");
2959 # endif /* !HAVE_XML */
2960
2961   gtk_label_set_text (doc, (doc_string
2962                             ? _(doc_string)
2963                             : _("No description available.")));
2964 }
2965
2966
2967 static void
2968 sensitize_demo_widgets (state *s, Bool sensitive_p)
2969 {
2970   const char *names[] = { "demo", "settings",
2971                           "cmd_label", "cmd_text", "manual",
2972                           "visual", "visual_combo" };
2973   int i;
2974   for (i = 0; i < countof(names); i++)
2975     {
2976       GtkWidget *w = name_to_widget (s, names[i]);
2977       gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2978     }
2979 }
2980
2981
2982 static void
2983 sensitize_menu_items (state *s, Bool force_p)
2984 {
2985   static Bool running_p = False;
2986   static time_t last_checked = 0;
2987   time_t now = time ((time_t *) 0);
2988   const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
2989                           /* "demo" */ };
2990   int i;
2991
2992   if (force_p || now > last_checked + 10)   /* check every 10 seconds */
2993     {
2994       running_p = xscreensaver_running_p (s);
2995       last_checked = time ((time_t *) 0);
2996     }
2997
2998   for (i = 0; i < countof(names); i++)
2999     {
3000       GtkWidget *w = name_to_widget (s, names[i]);
3001       gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
3002     }
3003 }
3004
3005
3006 /* When the File menu is de-posted after a "Restart Daemon" command,
3007    the window underneath doesn't repaint for some reason.  I guess this
3008    is a bug in exposure handling in GTK or GDK.  This works around it.
3009  */
3010 static void
3011 force_dialog_repaint (state *s)
3012 {
3013 #if 1
3014   /* Tell GDK to invalidate and repaint the whole window.
3015    */
3016   GdkWindow *w = GET_WINDOW (s->toplevel_widget);
3017   GdkRegion *region = gdk_region_new ();
3018   GdkRectangle rect;
3019   rect.x = rect.y = 0;
3020   rect.width = rect.height = 32767;
3021   gdk_region_union_with_rect (region, &rect);
3022   gdk_window_invalidate_region (w, region, True);
3023   gdk_region_destroy (region);
3024   gdk_window_process_updates (w, True);
3025 #else
3026   /* Force the server to send an exposure event by creating and then
3027      destroying a window as a child of the top level shell.
3028    */
3029   Display *dpy = GDK_DISPLAY();
3030   Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
3031   Window w;
3032   XWindowAttributes xgwa;
3033   XGetWindowAttributes (dpy, parent, &xgwa);
3034   w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
3035   XMapRaised (dpy, w);
3036   XDestroyWindow (dpy, w);
3037   XSync (dpy, False);
3038 #endif
3039 }
3040
3041
3042 /* Even though we've given these text fields a maximum number of characters,
3043    their default size is still about 30 characters wide -- so measure out
3044    a string in their font, and resize them to just fit that.
3045  */
3046 static void
3047 fix_text_entry_sizes (state *s)
3048 {
3049   GtkWidget *w;
3050
3051 # if 0   /* appears no longer necessary with Gtk 1.2.10 */
3052   const char * const spinbuttons[] = {
3053     "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3054     "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3055     "dpms_off_spinbutton",
3056     "-fade_spinbutton" };
3057   int i;
3058   int width = 0;
3059
3060   for (i = 0; i < countof(spinbuttons); i++)
3061     {
3062       const char *n = spinbuttons[i];
3063       int cols = 4;
3064       while (*n == '-') n++, cols--;
3065       w = GTK_WIDGET (name_to_widget (s, n));
3066       width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3067       gtk_widget_set_usize (w, width, -2);
3068     }
3069
3070   /* Now fix the width of the combo box.
3071    */
3072   w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3073   w = GTK_COMBO (w)->entry;
3074   width = gdk_string_width (w->style->font, "PseudoColor___");
3075   gtk_widget_set_usize (w, width, -2);
3076
3077   /* Now fix the width of the file entry text.
3078    */
3079   w = GTK_WIDGET (name_to_widget (s, "image_text"));
3080   width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3081   gtk_widget_set_usize (w, width, -2);
3082
3083   /* Now fix the width of the command line text.
3084    */
3085   w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3086   width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3087   gtk_widget_set_usize (w, width, -2);
3088
3089 # endif /* 0 */
3090
3091   /* Now fix the height of the list widget:
3092      make it default to being around 10 text-lines high instead of 4.
3093    */
3094   w = GTK_WIDGET (name_to_widget (s, "list"));
3095   {
3096     int lines = 10;
3097     int height;
3098     int leading = 3;  /* approximate is ok... */
3099     int border = 2;
3100
3101 #ifdef HAVE_GTK2
3102     PangoFontMetrics *pain =
3103       pango_context_get_metrics (gtk_widget_get_pango_context (w),
3104                                  gtk_widget_get_style (w)->font_desc,
3105                                  gtk_get_default_language ());
3106     height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3107                            pango_font_metrics_get_descent (pain));
3108 #else  /* !HAVE_GTK2 */
3109     height = w->style->font->ascent + w->style->font->descent;
3110 #endif /* !HAVE_GTK2 */
3111
3112     height += leading;
3113     height *= lines;
3114     height += border * 2;
3115     w = GTK_WIDGET (name_to_widget (s, "scroller"));
3116     gtk_widget_set_usize (w, -2, height);
3117   }
3118 }
3119
3120
3121 #ifndef HAVE_GTK2
3122 \f
3123 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3124  */
3125
3126 static char *up_arrow_xpm[] = {
3127   "15 15 4 1",
3128   "     c None",
3129   "-    c #FFFFFF",
3130   "+    c #D6D6D6",
3131   "@    c #000000",
3132
3133   "       @       ",
3134   "       @       ",
3135   "      -+@      ",
3136   "      -+@      ",
3137   "     -+++@     ",
3138   "     -+++@     ",
3139   "    -+++++@    ",
3140   "    -+++++@    ",
3141   "   -+++++++@   ",
3142   "   -+++++++@   ",
3143   "  -+++++++++@  ",
3144   "  -+++++++++@  ",
3145   " -+++++++++++@ ",
3146   " @@@@@@@@@@@@@ ",
3147   "               ",
3148
3149   /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3150      the end of the array (Gtk 1.2.5.) */
3151   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3152   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3153 };
3154
3155 static char *down_arrow_xpm[] = {
3156   "15 15 4 1",
3157   "     c None",
3158   "-    c #FFFFFF",
3159   "+    c #D6D6D6",
3160   "@    c #000000",
3161
3162   "               ",
3163   " ------------- ",
3164   " -+++++++++++@ ",
3165   "  -+++++++++@  ",
3166   "  -+++++++++@  ",
3167   "   -+++++++@   ",
3168   "   -+++++++@   ",
3169   "    -+++++@    ",
3170   "    -+++++@    ",
3171   "     -+++@     ",
3172   "     -+++@     ",
3173   "      -+@      ",
3174   "      -+@      ",
3175   "       @       ",
3176   "       @       ",
3177
3178   /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3179      the end of the array (Gtk 1.2.5.) */
3180   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3181   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3182 };
3183
3184 static void
3185 pixmapify_button (state *s, int down_p)
3186 {
3187   GdkPixmap *pixmap;
3188   GdkBitmap *mask;
3189   GtkWidget *pixmapwid;
3190   GtkStyle *style;
3191   GtkWidget *w;
3192
3193   w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3194   style = gtk_widget_get_style (w);
3195   mask = 0;
3196   pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3197                                          &style->bg[GTK_STATE_NORMAL],
3198                                          (down_p
3199                                           ? (gchar **) down_arrow_xpm
3200                                           : (gchar **) up_arrow_xpm));
3201   pixmapwid = gtk_pixmap_new (pixmap, mask);
3202   gtk_widget_show (pixmapwid);
3203   gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3204   gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3205 }
3206
3207 static void
3208 map_next_button_cb (GtkWidget *w, gpointer user_data)
3209 {
3210   state *s = (state *) user_data;
3211   pixmapify_button (s, 1);
3212 }
3213
3214 static void
3215 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3216 {
3217   state *s = (state *) user_data;
3218   pixmapify_button (s, 0);
3219 }
3220 #endif /* !HAVE_GTK2 */
3221
3222 \f
3223 #ifndef HAVE_GTK2
3224 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3225  */
3226
3227 static void
3228 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3229                                              GtkAllocation *allocation,
3230                                              void *foo)
3231 {
3232   GtkRequisition req;
3233   GtkWidgetAuxInfo *aux_info;
3234
3235   aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3236
3237   aux_info->width = allocation->width;
3238   aux_info->height = -2;
3239   aux_info->x = -1;
3240   aux_info->y = -1;
3241
3242   gtk_widget_size_request (label, &req);
3243 }
3244
3245 /* Feel the love.  Thanks to Nat Friedman for finding this workaround.
3246  */
3247 static void
3248 eschew_gtk_lossage (GtkLabel *label)
3249 {
3250   GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3251   aux_info->width = GTK_WIDGET (label)->allocation.width;
3252   aux_info->height = -2;
3253   aux_info->x = -1;
3254   aux_info->y = -1;
3255
3256   gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3257
3258   gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3259                       GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3260                       0);
3261
3262   gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3263
3264   gtk_widget_queue_resize (GTK_WIDGET (label));
3265 }
3266 #endif /* !HAVE_GTK2 */
3267
3268
3269 static void
3270 populate_demo_window (state *s, int list_elt)
3271 {
3272   Display *dpy = GDK_DISPLAY();
3273   saver_preferences *p = &s->prefs;
3274   screenhack *hack;
3275   char *pretty_name;
3276   GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3277   GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3278   GtkEntry *cmd    = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3279   GtkCombo *vis    = GTK_COMBO (name_to_widget (s, "visual_combo"));
3280   GtkWidget *list  = GTK_WIDGET (name_to_widget (s, "list"));
3281
3282   if (p->mode == BLANK_ONLY)
3283     {
3284       hack = 0;
3285       pretty_name = strdup (_("Blank Screen"));
3286       schedule_preview (s, 0);
3287     }
3288   else if (p->mode == DONT_BLANK)
3289     {
3290       hack = 0;
3291       pretty_name = strdup (_("Screen Saver Disabled"));
3292       schedule_preview (s, 0);
3293     }
3294   else
3295     {
3296       int hack_number = (list_elt >= 0 && list_elt < s->list_count
3297                          ? s->list_elt_to_hack_number[list_elt]
3298                          : -1);
3299       hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3300
3301       pretty_name = (hack
3302                      ? (hack->name
3303                         ? strdup (hack->name)
3304                         : make_hack_name (dpy, hack->command))
3305                      : 0);
3306
3307       if (hack)
3308         schedule_preview (s, hack->command);
3309       else
3310         schedule_preview (s, 0);
3311     }
3312
3313   if (!pretty_name)
3314     pretty_name = strdup (_("Preview"));
3315
3316   gtk_frame_set_label (frame1, _(pretty_name));
3317   gtk_frame_set_label (frame2, _(pretty_name));
3318
3319   gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3320   gtk_entry_set_position (cmd, 0);
3321
3322   {
3323     char title[255];
3324     sprintf (title, _("%s: %.100s Settings"),
3325              progclass, (pretty_name ? pretty_name : "???"));
3326     gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3327   }
3328
3329   gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3330                       (hack
3331                        ? (hack->visual && *hack->visual
3332                           ? hack->visual
3333                           : _("Any"))
3334                        : ""));
3335
3336   sensitize_demo_widgets (s, (hack ? True : False));
3337
3338   if (pretty_name) free (pretty_name);
3339
3340   ensure_selected_item_visible (list);
3341
3342   s->_selected_list_element = list_elt;
3343 }
3344
3345
3346 static void
3347 widget_deleter (GtkWidget *widget, gpointer data)
3348 {
3349   /* #### Well, I want to destroy these widgets, but if I do that, they get
3350      referenced again, and eventually I get a SEGV.  So instead of
3351      destroying them, I'll just hide them, and leak a bunch of memory
3352      every time the disk file changes.  Go go go Gtk!
3353
3354      #### Ok, that's a lie, I get a crash even if I just hide the widget
3355      and don't ever delete it.  Fuck!
3356    */
3357 #if 0
3358   gtk_widget_destroy (widget);
3359 #else
3360   gtk_widget_hide (widget);
3361 #endif
3362 }
3363
3364
3365 static char **sort_hack_cmp_names_kludge;
3366 static int
3367 sort_hack_cmp (const void *a, const void *b)
3368 {
3369   if (a == b)
3370     return 0;
3371   else
3372     {
3373       int aa = *(int *) a;
3374       int bb = *(int *) b;
3375       const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3376       return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3377                      (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3378     }
3379 }
3380
3381
3382 static void
3383 initialize_sort_map (state *s)
3384 {
3385   Display *dpy = GDK_DISPLAY();
3386   saver_preferences *p = &s->prefs;
3387   int i, j;
3388
3389   if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3390   if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3391   if (s->hacks_available_p) free (s->hacks_available_p);
3392
3393   s->list_elt_to_hack_number = (int *)
3394     calloc (sizeof(int), p->screenhacks_count + 1);
3395   s->hack_number_to_list_elt = (int *)
3396     calloc (sizeof(int), p->screenhacks_count + 1);
3397   s->hacks_available_p = (Bool *)
3398     calloc (sizeof(Bool), p->screenhacks_count + 1);
3399   s->total_available = 0;
3400
3401   /* Check which hacks actually exist on $PATH
3402    */
3403   for (i = 0; i < p->screenhacks_count; i++)
3404     {
3405       screenhack *hack = p->screenhacks[i];
3406       int on = on_path_p (hack->command) ? 1 : 0;
3407       s->hacks_available_p[i] = on;
3408       s->total_available += on;
3409     }
3410
3411   /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3412      hacks, if desired.
3413    */
3414   j = 0;
3415   for (i = 0; i < p->screenhacks_count; i++)
3416     {
3417       if (!p->ignore_uninstalled_p ||
3418           s->hacks_available_p[i])
3419         s->list_elt_to_hack_number[j++] = i;
3420     }
3421   s->list_count = j;
3422
3423   for (; j < p->screenhacks_count; j++)
3424     s->list_elt_to_hack_number[j] = -1;
3425
3426
3427   /* Generate list of sortable names (once)
3428    */
3429   sort_hack_cmp_names_kludge = (char **)
3430     calloc (sizeof(char *), p->screenhacks_count);
3431   for (i = 0; i < p->screenhacks_count; i++)
3432     {
3433       screenhack *hack = p->screenhacks[i];
3434       char *name = (hack->name && *hack->name
3435                     ? strdup (hack->name)
3436                     : make_hack_name (dpy, hack->command));
3437       char *str;
3438       for (str = name; *str; str++)
3439         *str = tolower(*str);
3440       sort_hack_cmp_names_kludge[i] = name;
3441     }
3442
3443   /* Sort list->hack map alphabetically
3444    */
3445   qsort (s->list_elt_to_hack_number,
3446          p->screenhacks_count,
3447          sizeof(*s->list_elt_to_hack_number),
3448          sort_hack_cmp);
3449
3450   /* Free names
3451    */
3452   for (i = 0; i < p->screenhacks_count; i++)
3453     free (sort_hack_cmp_names_kludge[i]);
3454   free (sort_hack_cmp_names_kludge);
3455   sort_hack_cmp_names_kludge = 0;
3456
3457   /* Build inverse table */
3458   for (i = 0; i < p->screenhacks_count; i++)
3459     {
3460       int n = s->list_elt_to_hack_number[i];
3461       if (n != -1)
3462         s->hack_number_to_list_elt[n] = i;
3463     }
3464 }
3465
3466
3467 static int
3468 maybe_reload_init_file (state *s)
3469 {
3470   Display *dpy = GDK_DISPLAY();
3471   saver_preferences *p = &s->prefs;
3472   int status = 0;
3473
3474   static Bool reentrant_lock = False;
3475   if (reentrant_lock) return 0;
3476   reentrant_lock = True;
3477
3478   if (init_file_changed_p (p))
3479     {
3480       const char *f = init_file_name();
3481       char *b;
3482       int list_elt;
3483       GtkWidget *list;
3484
3485       if (!f || !*f) return 0;
3486       b = (char *) malloc (strlen(f) + 1024);
3487       sprintf (b,
3488                _("Warning:\n\n"
3489                  "file \"%s\" has changed, reloading.\n"),
3490                f);
3491       warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3492       free (b);
3493
3494       load_init_file (dpy, p);
3495       initialize_sort_map (s);
3496
3497       list_elt = selected_list_element (s);
3498       list = name_to_widget (s, "list");
3499       gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3500       populate_hack_list (s);
3501       force_list_select_item (s, list, list_elt, True);
3502       populate_prefs_page (s);
3503       populate_demo_window (s, list_elt);
3504       ensure_selected_item_visible (list);
3505
3506       status = 1;
3507     }
3508
3509   reentrant_lock = False;
3510   return status;
3511 }
3512
3513
3514 \f
3515 /* Making the preview window have the right X visual (so that GL works.)
3516  */
3517
3518 static Visual *get_best_gl_visual (state *);
3519
3520 static GdkVisual *
3521 x_visual_to_gdk_visual (Visual *xv)
3522 {
3523   GList *gvs = gdk_list_visuals();
3524   if (!xv) return gdk_visual_get_system();
3525   for (; gvs; gvs = gvs->next)
3526     {
3527       GdkVisual *gv = (GdkVisual *) gvs->data;
3528       if (xv == GDK_VISUAL_XVISUAL (gv))
3529         return gv;
3530     }
3531   fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3532            blurb(), (unsigned long) xv->visualid);
3533   abort();
3534 }
3535
3536 static void
3537 clear_preview_window (state *s)
3538 {
3539   GtkWidget *p;
3540   GdkWindow *window;
3541   GtkStyle  *style;
3542
3543   if (!s->toplevel_widget) return;  /* very early */
3544   p = name_to_widget (s, "preview");
3545   window = GET_WINDOW (p);
3546
3547   if (!window) return;
3548
3549   /* Flush the widget background down into the window, in case a subproc
3550      has changed it. */
3551   style = gtk_widget_get_style (p);
3552   gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
3553   gdk_window_clear (window);
3554
3555   {
3556     int list_elt = selected_list_element (s);
3557     int hack_number = (list_elt >= 0
3558                        ? s->list_elt_to_hack_number[list_elt]
3559                        : -1);
3560     Bool available_p = (hack_number >= 0
3561                         ? s->hacks_available_p [hack_number]
3562                         : True);
3563     Bool nothing_p = (s->total_available < 5);
3564
3565 #ifdef HAVE_GTK2
3566     GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3567     gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3568                            (s->running_preview_error_p
3569                             ? (available_p ? 1 :
3570                                nothing_p ? 3 : 2)
3571                             : 0));
3572 #else /* !HAVE_GTK2 */
3573     if (s->running_preview_error_p)
3574       {
3575         const char * const lines1[] = { N_("No Preview"), N_("Available") };
3576         const char * const lines2[] = { N_("Not"), N_("Installed") };
3577         int nlines = countof(lines1);
3578         int lh = p->style->font->ascent + p->style->font->descent;
3579         int y, i;
3580         gint w, h;
3581
3582         const char * const *lines = (available_p ? lines1 : lines2);
3583
3584         gdk_window_get_size (window, &w, &h);
3585         y = (h - (lh * nlines)) / 2;
3586         y += p->style->font->ascent;
3587         for (i = 0; i < nlines; i++)
3588           {
3589             int sw = gdk_string_width (p->style->font, _(lines[i]));
3590             int x = (w - sw) / 2;
3591             gdk_draw_string (window, p->style->font,
3592                              p->style->fg_gc[GTK_STATE_NORMAL],
3593                              x, y, _(lines[i]));
3594             y += lh;
3595           }
3596       }
3597 #endif /* !HAVE_GTK2 */
3598   }
3599
3600   gdk_flush ();
3601 }
3602
3603
3604 static void
3605 reset_preview_window (state *s)
3606 {
3607   /* On some systems (most recently, MacOS X) OpenGL programs get confused
3608      when you kill one and re-start another on the same window.  So maybe
3609      it's best to just always destroy and recreate the preview window
3610      when changing hacks, instead of always trying to reuse the same one?
3611    */
3612   GtkWidget *pr = name_to_widget (s, "preview");
3613   if (GET_REALIZED (pr))
3614     {
3615       GdkWindow *window = GET_WINDOW (pr);
3616       Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3617       Window id;
3618       gtk_widget_hide (pr);
3619       gtk_widget_unrealize (pr);
3620       gtk_widget_realize (pr);
3621       gtk_widget_show (pr);
3622       id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3623       if (s->debug_p)
3624         fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3625                  (unsigned int) oid,
3626                  (unsigned int) id);
3627     }
3628 }
3629
3630
3631 static void
3632 fix_preview_visual (state *s)
3633 {
3634   GtkWidget *widget = name_to_widget (s, "preview");
3635   Visual *xvisual = get_best_gl_visual (s);
3636   GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3637   GdkVisual *dvisual = gdk_visual_get_system();
3638   GdkColormap *cmap = (visual == dvisual
3639                        ? gdk_colormap_get_system ()
3640                        : gdk_colormap_new (visual, False));
3641
3642   if (s->debug_p)
3643     fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3644              (visual == dvisual ? "default" : "non-default"),
3645              (xvisual ? (unsigned long) xvisual->visualid : 0L));
3646
3647   if (!GET_REALIZED (widget) ||
3648       gtk_widget_get_visual (widget) != visual)
3649     {
3650       gtk_widget_unrealize (widget);
3651       gtk_widget_set_visual (widget, visual);
3652       gtk_widget_set_colormap (widget, cmap);
3653       gtk_widget_realize (widget);
3654     }
3655
3656   /* Set the Widget colors to be white-on-black. */
3657   {
3658     GdkWindow *window = GET_WINDOW (widget);
3659     GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
3660     GdkColormap *cmap = gtk_widget_get_colormap (widget);
3661     GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3662     GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3663     GdkGC *fgc = gdk_gc_new(window);
3664     GdkGC *bgc = gdk_gc_new(window);
3665     if (!gdk_color_white (cmap, fg)) abort();
3666     if (!gdk_color_black (cmap, bg)) abort();
3667     gdk_gc_set_foreground (fgc, fg);
3668     gdk_gc_set_background (fgc, bg);
3669     gdk_gc_set_foreground (bgc, bg);
3670     gdk_gc_set_background (bgc, fg);
3671     style->fg_gc[GTK_STATE_NORMAL] = fgc;
3672     style->bg_gc[GTK_STATE_NORMAL] = fgc;
3673     gtk_widget_set_style (widget, style);
3674
3675     /* For debugging purposes, put a title on the window (so that
3676        it can be easily found in the output of "xwininfo -tree".)
3677      */
3678     gdk_window_set_title (window, "Preview");
3679   }
3680
3681   gtk_widget_show (widget);
3682 }
3683
3684 \f
3685 /* Subprocesses
3686  */
3687
3688 static char *
3689 subproc_pretty_name (state *s)
3690 {
3691   if (s->running_preview_cmd)
3692     {
3693       char *ps = strdup (s->running_preview_cmd);
3694       char *ss = strchr (ps, ' ');
3695       if (ss) *ss = 0;
3696       ss = strrchr (ps, '/');
3697       if (!ss)
3698         ss = ps;
3699       else
3700         {
3701           ss = strdup (ss+1);
3702           free (ps);
3703         }
3704       return ss;
3705     }
3706   else
3707     return strdup ("???");
3708 }
3709
3710
3711 static void
3712 reap_zombies (state *s)
3713 {
3714   int wait_status = 0;
3715   pid_t pid;
3716   while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3717     {
3718       if (s->debug_p)
3719         {
3720           if (pid == s->running_preview_pid)
3721             {
3722               char *ss = subproc_pretty_name (s);
3723               fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3724                        (unsigned long) pid, ss);
3725               free (ss);
3726             }
3727           else
3728             fprintf (stderr, "%s: pid %lu died\n", blurb(),
3729                      (unsigned long) pid);
3730         }
3731     }
3732 }
3733
3734
3735 /* Mostly lifted from driver/subprocs.c */
3736 static Visual *
3737 get_best_gl_visual (state *s)
3738 {
3739   Display *dpy = GDK_DISPLAY();
3740   pid_t forked;
3741   int fds [2];
3742   int in, out;
3743   char buf[1024];
3744
3745   char *av[10];
3746   int ac = 0;
3747
3748   av[ac++] = "xscreensaver-gl-helper";
3749   av[ac] = 0;
3750
3751   if (pipe (fds))
3752     {
3753       perror ("error creating pipe:");
3754       return 0;
3755     }
3756
3757   in = fds [0];
3758   out = fds [1];
3759
3760   switch ((int) (forked = fork ()))
3761     {
3762     case -1:
3763       {
3764         sprintf (buf, "%s: couldn't fork", blurb());
3765         perror (buf);
3766         exit (1);
3767       }
3768     case 0:
3769       {
3770         int stdout_fd = 1;
3771
3772         close (in);  /* don't need this one */
3773         close (ConnectionNumber (dpy));         /* close display fd */
3774
3775         if (dup2 (out, stdout_fd) < 0)          /* pipe stdout */
3776           {
3777             perror ("could not dup() a new stdout:");
3778             return 0;
3779           }
3780
3781         execvp (av[0], av);                     /* shouldn't return. */
3782
3783         if (errno != ENOENT)
3784           {
3785             /* Ignore "no such file or directory" errors, unless verbose.
3786                Issue all other exec errors, though. */
3787             sprintf (buf, "%s: running %s", blurb(), av[0]);
3788             perror (buf);
3789           }
3790
3791         /* Note that one must use _exit() instead of exit() in procs forked
3792            off of Gtk programs -- Gtk installs an atexit handler that has a
3793            copy of the X connection (which we've already closed, for safety.)
3794            If one uses exit() instead of _exit(), then one sometimes gets a
3795            spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3796         */
3797         _exit (1);                              /* exits fork */
3798         break;
3799       }
3800     default:
3801       {
3802         int result = 0;
3803         int wait_status = 0;
3804
3805         FILE *f = fdopen (in, "r");
3806         unsigned int v = 0;
3807         char c;
3808
3809         close (out);  /* don't need this one */
3810
3811         *buf = 0;
3812         if (!fgets (buf, sizeof(buf)-1, f))
3813           *buf = 0;
3814         fclose (f);
3815
3816         /* Wait for the child to die. */
3817         waitpid (-1, &wait_status, 0);
3818
3819         if (1 == sscanf (buf, "0x%x %c", &v, &c))
3820           result = (int) v;
3821
3822         if (result == 0)
3823           {
3824             if (s->debug_p)
3825               fprintf (stderr, "%s: %s did not report a GL visual!\n",
3826                        blurb(), av[0]);
3827             return 0;
3828           }
3829         else
3830           {
3831             Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3832             if (s->debug_p)
3833               fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3834                        blurb(), av[0], result);
3835             if (!v) abort();
3836             return v;
3837           }
3838       }
3839     }
3840
3841   abort();
3842 }
3843
3844
3845 static void
3846 kill_preview_subproc (state *s, Bool reset_p)
3847 {
3848   s->running_preview_error_p = False;
3849
3850   reap_zombies (s);
3851   clear_preview_window (s);
3852
3853   if (s->subproc_check_timer_id)
3854     {
3855       gtk_timeout_remove (s->subproc_check_timer_id);
3856       s->subproc_check_timer_id = 0;
3857       s->subproc_check_countdown = 0;
3858     }
3859
3860   if (s->running_preview_pid)
3861     {
3862       int status = kill (s->running_preview_pid, SIGTERM);
3863       char *ss = subproc_pretty_name (s);
3864
3865       if (status < 0)
3866         {
3867           if (errno == ESRCH)
3868             {
3869               if (s->debug_p)
3870                 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3871                          blurb(), (unsigned long) s->running_preview_pid, ss);
3872             }
3873           else
3874             {
3875               char buf [1024];
3876               sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3877                        blurb(), (unsigned long) s->running_preview_pid, ss);
3878               perror (buf);
3879             }
3880         }
3881       else {
3882         int endstatus;
3883         waitpid(s->running_preview_pid, &endstatus, 0);
3884         if (s->debug_p)
3885           fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3886                    (unsigned long) s->running_preview_pid, ss);
3887       }
3888
3889       free (ss);
3890       s->running_preview_pid = 0;
3891       if (s->running_preview_cmd) free (s->running_preview_cmd);
3892       s->running_preview_cmd = 0;
3893     }
3894
3895   reap_zombies (s);
3896
3897   if (reset_p)
3898     {
3899       reset_preview_window (s);
3900       clear_preview_window (s);
3901     }
3902 }
3903
3904
3905 /* Immediately and unconditionally launches the given process,
3906    after appending the -window-id option; sets running_preview_pid.
3907  */
3908 static void
3909 launch_preview_subproc (state *s)
3910 {
3911   saver_preferences *p = &s->prefs;
3912   Window id;
3913   char *new_cmd = 0;
3914   pid_t forked;
3915   const char *cmd = s->desired_preview_cmd;
3916
3917   GtkWidget *pr = name_to_widget (s, "preview");
3918   GdkWindow *window;
3919
3920   reset_preview_window (s);
3921
3922   window = GET_WINDOW (pr);
3923
3924   s->running_preview_error_p = False;
3925
3926   if (s->preview_suppressed_p)
3927     {
3928       kill_preview_subproc (s, False);
3929       goto DONE;
3930     }
3931
3932   new_cmd = malloc (strlen (cmd) + 40);
3933
3934   id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3935   if (id == 0)
3936     {
3937       /* No window id?  No command to run. */
3938       free (new_cmd);
3939       new_cmd = 0;
3940     }
3941   else
3942     {
3943       strcpy (new_cmd, cmd);
3944       sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3945                (unsigned int) id);
3946     }
3947
3948   kill_preview_subproc (s, False);
3949   if (! new_cmd)
3950     {
3951       s->running_preview_error_p = True;
3952       clear_preview_window (s);
3953       goto DONE;
3954     }
3955
3956   switch ((int) (forked = fork ()))
3957     {
3958     case -1:
3959       {
3960         char buf[255];
3961         sprintf (buf, "%s: couldn't fork", blurb());
3962         perror (buf);
3963         s->running_preview_error_p = True;
3964         goto DONE;
3965         break;
3966       }
3967     case 0:
3968       {
3969         close (ConnectionNumber (GDK_DISPLAY()));
3970
3971         hack_subproc_environment (id, s->debug_p);
3972
3973         usleep (250000);  /* pause for 1/4th second before launching, to give
3974                              the previous program time to die and flush its X
3975                              buffer, so we don't get leftover turds on the
3976                              window. */
3977
3978         exec_command (p->shell, new_cmd, p->nice_inferior);
3979         /* Don't bother printing an error message when we are unable to
3980            exec subprocesses; we handle that by polling the pid later.
3981
3982            Note that one must use _exit() instead of exit() in procs forked
3983            off of Gtk programs -- Gtk installs an atexit handler that has a
3984            copy of the X connection (which we've already closed, for safety.)
3985            If one uses exit() instead of _exit(), then one sometimes gets a
3986            spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3987         */
3988         _exit (1);  /* exits child fork */
3989         break;
3990
3991       default:
3992
3993         if (s->running_preview_cmd) free (s->running_preview_cmd);
3994         s->running_preview_cmd = strdup (s->desired_preview_cmd);
3995         s->running_preview_pid = forked;
3996
3997         if (s->debug_p)
3998           {
3999             char *ss = subproc_pretty_name (s);
4000             fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
4001                      (unsigned long) forked, ss);
4002             free (ss);
4003           }
4004         break;
4005       }
4006     }
4007
4008   schedule_preview_check (s);
4009
4010  DONE:
4011   if (new_cmd) free (new_cmd);
4012   new_cmd = 0;
4013 }
4014
4015
4016 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
4017  */
4018 static void
4019 hack_environment (state *s)
4020 {
4021   static const char *def_path =
4022 # ifdef DEFAULT_PATH_PREFIX
4023     DEFAULT_PATH_PREFIX;
4024 # else
4025     "";
4026 # endif
4027
4028   Display *dpy = GDK_DISPLAY();
4029   const char *odpy = DisplayString (dpy);
4030   char *ndpy = (char *) malloc(strlen(odpy) + 20);
4031   strcpy (ndpy, "DISPLAY=");
4032   strcat (ndpy, odpy);
4033   if (putenv (ndpy))
4034     abort ();
4035
4036   if (s->debug_p)
4037     fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4038
4039   /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4040      2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4041      So we must leak it (and/or the previous setting).  Yay.
4042    */
4043
4044   if (def_path && *def_path)
4045     {
4046       const char *opath = getenv("PATH");
4047       char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4048       strcpy (npath, "PATH=");
4049       strcat (npath, def_path);
4050       strcat (npath, ":");
4051       strcat (npath, opath);
4052
4053       if (putenv (npath))
4054         abort ();
4055       /* do not free(npath) -- see above */
4056
4057       if (s->debug_p)
4058         fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4059     }
4060 }
4061
4062
4063 static void
4064 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4065 {
4066   /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4067      necessary yet, but it will make programs work if we had invoked
4068      them with "-root" and not with "-window-id" -- which, of course,
4069      doesn't happen.
4070    */
4071   char *nssw = (char *) malloc (40);
4072   sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4073
4074   /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4075      any more, right?  It's not Posix, but everyone seems to have it. */
4076   if (putenv (nssw))
4077     abort ();
4078
4079   if (debug_p)
4080     fprintf (stderr, "%s: %s\n", blurb(), nssw);
4081
4082   /* do not free(nssw) -- see above */
4083 }
4084
4085
4086 /* Called from a timer:
4087    Launches the currently-chosen subprocess, if it's not already running.
4088    If there's a different process running, kills it.
4089  */
4090 static int
4091 update_subproc_timer (gpointer data)
4092 {
4093   state *s = (state *) data;
4094   if (! s->desired_preview_cmd)
4095     kill_preview_subproc (s, True);
4096   else if (!s->running_preview_cmd ||
4097            !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4098     launch_preview_subproc (s);
4099
4100   s->subproc_timer_id = 0;
4101   return FALSE;  /* do not re-execute timer */
4102 }
4103
4104 static int
4105 settings_timer (gpointer data)
4106 {
4107   settings_cb (0, 0);
4108   return FALSE;
4109 }
4110
4111
4112 /* Call this when you think you might want a preview process running.
4113    It will set a timer that will actually launch that program a second
4114    from now, if you haven't changed your mind (to avoid double-click
4115    spazzing, etc.)  `cmd' may be null meaning "no process".
4116  */
4117 static void
4118 schedule_preview (state *s, const char *cmd)
4119 {
4120   int delay = 1000 * 0.5;   /* 1/2 second hysteresis */
4121
4122   if (s->debug_p)
4123     {
4124       if (cmd)
4125         fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4126       else
4127         fprintf (stderr, "%s: scheduling preview death\n", blurb());
4128     }
4129
4130   if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4131   s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4132
4133   if (s->subproc_timer_id)
4134     gtk_timeout_remove (s->subproc_timer_id);
4135   s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4136 }
4137
4138
4139 /* Called from a timer:
4140    Checks to see if the subproc that should be running, actually is.
4141  */
4142 static int
4143 check_subproc_timer (gpointer data)
4144 {
4145   state *s = (state *) data;
4146   Bool again_p = True;
4147
4148   if (s->running_preview_error_p ||   /* already dead */
4149       s->running_preview_pid <= 0)
4150     {
4151       again_p = False;
4152     }
4153   else
4154     {
4155       int status;
4156       reap_zombies (s);
4157       status = kill (s->running_preview_pid, 0);
4158       if (status < 0 && errno == ESRCH)
4159         s->running_preview_error_p = True;
4160
4161       if (s->debug_p)
4162         {
4163           char *ss = subproc_pretty_name (s);
4164           fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4165                    (unsigned long) s->running_preview_pid, ss,
4166                    (s->running_preview_error_p ? "dead" : "alive"));
4167           free (ss);
4168         }
4169
4170       if (s->running_preview_error_p)
4171         {
4172           clear_preview_window (s);
4173           again_p = False;
4174         }
4175     }
4176
4177   /* Otherwise, it's currently alive.  We might be checking again, or we
4178      might be satisfied. */
4179
4180   if (--s->subproc_check_countdown <= 0)
4181     again_p = False;
4182
4183   if (again_p)
4184     return TRUE;     /* re-execute timer */
4185   else
4186     {
4187       s->subproc_check_timer_id = 0;
4188       s->subproc_check_countdown = 0;
4189       return FALSE;  /* do not re-execute timer */
4190     }
4191 }
4192
4193
4194 /* Call this just after launching a subprocess.
4195    This sets a timer that will, five times a second for two seconds,
4196    check whether the program is still running.  The assumption here
4197    is that if the process didn't stay up for more than a couple of
4198    seconds, then either the program doesn't exist, or it doesn't
4199    take a -window-id argument.
4200  */
4201 static void
4202 schedule_preview_check (state *s)
4203 {
4204   int seconds = 2;
4205   int ticks = 5;
4206
4207   if (s->debug_p)
4208     fprintf (stderr, "%s: scheduling check\n", blurb());
4209
4210   if (s->subproc_check_timer_id)
4211     gtk_timeout_remove (s->subproc_check_timer_id);
4212   s->subproc_check_timer_id =
4213     gtk_timeout_add (1000 / ticks,
4214                      check_subproc_timer, (gpointer) s);
4215   s->subproc_check_countdown = ticks * seconds;
4216 }
4217
4218
4219 static Bool
4220 screen_blanked_p (void)
4221 {
4222   Atom type;
4223   int format;
4224   unsigned long nitems, bytesafter;
4225   unsigned char *dataP = 0;
4226   Display *dpy = GDK_DISPLAY();
4227   Bool blanked_p = False;
4228
4229   if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4230                           XA_SCREENSAVER_STATUS,
4231                           0, 3, False, XA_INTEGER,
4232                           &type, &format, &nitems, &bytesafter,
4233                           &dataP)
4234       == Success
4235       && type == XA_INTEGER
4236       && nitems >= 3
4237       && dataP)
4238     {
4239       Atom *data = (Atom *) dataP;
4240       blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4241     }
4242
4243   if (dataP) XFree (dataP);
4244
4245   return blanked_p;
4246 }
4247
4248 /* Wake up every now and then and see if the screen is blanked.
4249    If it is, kill off the small-window demo -- no point in wasting
4250    cycles by running two screensavers at once...
4251  */
4252 static int
4253 check_blanked_timer (gpointer data)
4254 {
4255   state *s = (state *) data;
4256   Bool blanked_p = screen_blanked_p ();
4257   if (blanked_p && s->running_preview_pid)
4258     {
4259       if (s->debug_p)
4260         fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4261       kill_preview_subproc (s, True);
4262     }
4263
4264   return True;  /* re-execute timer */
4265 }
4266
4267
4268 /* How many screens are there (including Xinerama.)
4269  */
4270 static int
4271 screen_count (Display *dpy)
4272 {
4273   int nscreens = ScreenCount(dpy);
4274 # ifdef HAVE_XINERAMA
4275   if (nscreens <= 1)
4276     {
4277       int event_number, error_number;
4278       if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4279           XineramaIsActive (dpy))
4280         {
4281           XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4282           if (xsi) XFree (xsi);
4283         }
4284     }
4285 # endif /* HAVE_XINERAMA */
4286
4287   return nscreens;
4288 }
4289
4290 \f
4291 /* Setting window manager icon
4292  */
4293
4294 static void
4295 init_icon (GdkWindow *window)
4296 {
4297   GdkBitmap *mask = 0;
4298   GdkColor transp;
4299   GdkPixmap *pixmap =
4300     gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
4301                                   (gchar **) logo_50_xpm);
4302   if (pixmap)
4303     gdk_window_set_icon (window, 0, pixmap, mask);
4304 }
4305
4306 \f
4307 /* The main demo-mode command loop.
4308  */
4309
4310 #if 0
4311 static Bool
4312 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4313         XrmRepresentation *type, XrmValue *value, XPointer closure)
4314 {
4315   int i;
4316   for (i = 0; quarks[i]; i++)
4317     {
4318       if (bindings[i] == XrmBindTightly)
4319         fprintf (stderr, (i == 0 ? "" : "."));
4320       else if (bindings[i] == XrmBindLoosely)
4321         fprintf (stderr, "*");
4322       else
4323         fprintf (stderr, " ??? ");
4324       fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4325     }
4326
4327   fprintf (stderr, ": %s\n", (char *) value->addr);
4328
4329   return False;
4330 }
4331 #endif
4332
4333
4334 static Window
4335 gnome_screensaver_window (Screen *screen)
4336 {
4337   Display *dpy = DisplayOfScreen (screen);
4338   Window root = RootWindowOfScreen (screen);
4339   Window parent, *kids;
4340   unsigned int nkids;
4341   Window gnome_window = 0;
4342   int i;
4343
4344   if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4345     abort ();
4346   for (i = 0; i < nkids; i++)
4347     {
4348       Atom type;
4349       int format;
4350       unsigned long nitems, bytesafter;
4351       unsigned char *name;
4352       if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4353                               False, XA_STRING, &type, &format, &nitems,
4354                               &bytesafter, &name)
4355           == Success
4356           && type != None
4357           && !strcmp ((char *) name, "gnome-screensaver"))
4358         {
4359           gnome_window = kids[i];
4360           break;
4361         }
4362     }
4363
4364   if (kids) XFree ((char *) kids);
4365   return gnome_window;
4366 }
4367
4368 static Bool
4369 gnome_screensaver_active_p (void)
4370 {
4371   Display *dpy = GDK_DISPLAY();
4372   Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4373   return (w ? True : False);
4374 }
4375
4376 static void
4377 kill_gnome_screensaver (void)
4378 {
4379   Display *dpy = GDK_DISPLAY();
4380   Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4381   if (w) XKillClient (dpy, (XID) w);
4382 }
4383
4384 static Bool
4385 kde_screensaver_active_p (void)
4386 {
4387   FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4388                    "r");
4389   char buf[255];
4390   fgets (buf, sizeof(buf)-1, p);
4391   pclose (p);
4392   if (!strcmp (buf, "true\n"))
4393     return True;
4394   else
4395     return False;
4396 }
4397
4398 static void
4399 kill_kde_screensaver (void)
4400 {
4401   system ("dcop kdesktop KScreensaverIface enable false");
4402 }
4403
4404
4405 static void
4406 the_network_is_not_the_computer (state *s)
4407 {
4408   Display *dpy = GDK_DISPLAY();
4409   char *rversion = 0, *ruser = 0, *rhost = 0;
4410   char *luser, *lhost;
4411   char *msg = 0;
4412   struct passwd *p = getpwuid (getuid ());
4413   const char *d = DisplayString (dpy);
4414
4415 # if defined(HAVE_UNAME)
4416   struct utsname uts;
4417   if (uname (&uts) < 0)
4418     lhost = "<UNKNOWN>";
4419   else
4420     lhost = uts.nodename;
4421 # elif defined(VMS)
4422   strcpy (lhost, getenv("SYS$NODE"));
4423 # else  /* !HAVE_UNAME && !VMS */
4424   strcat (lhost, "<UNKNOWN>");
4425 # endif /* !HAVE_UNAME && !VMS */
4426
4427   if (p && p->pw_name)
4428     luser = p->pw_name;
4429   else
4430     luser = "???";
4431
4432   server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4433
4434   /* Make a buffer that's big enough for a number of copies of all the
4435      strings, plus some. */
4436   msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4437                                (ruser ? strlen(ruser) : 0) +
4438                                (rhost ? strlen(rhost) : 0) +
4439                                strlen(lhost) +
4440                                strlen(luser) +
4441                                strlen(d) +
4442                                1024));
4443   *msg = 0;
4444
4445   if (!rversion || !*rversion)
4446     {
4447       sprintf (msg,
4448                _("Warning:\n\n"
4449                  "The XScreenSaver daemon doesn't seem to be running\n"
4450                  "on display \"%s\".  Launch it now?"),
4451                d);
4452     }
4453   else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4454     {
4455       /* Warn that the two processes are running as different users.
4456        */
4457       sprintf(msg,
4458             _("Warning:\n\n"
4459               "%s is running as user \"%s\" on host \"%s\".\n"
4460               "But the xscreensaver managing display \"%s\"\n"
4461               "is running as user \"%s\" on host \"%s\".\n"
4462               "\n"
4463               "Since they are different users, they won't be reading/writing\n"
4464               "the same ~/.xscreensaver file, so %s isn't\n"
4465               "going to work right.\n"
4466               "\n"
4467               "You should either re-run %s as \"%s\", or re-run\n"
4468               "xscreensaver as \"%s\".\n"
4469               "\n"
4470               "Restart the xscreensaver daemon now?\n"),
4471               progname, luser, lhost,
4472               d,
4473               (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4474               progname,
4475               progname, (ruser ? ruser : "???"),
4476               luser);
4477     }
4478   else if (rhost && *rhost && !!strcmp (rhost, lhost))
4479     {
4480       /* Warn that the two processes are running on different hosts.
4481        */
4482       sprintf (msg,
4483               _("Warning:\n\n"
4484                "%s is running as user \"%s\" on host \"%s\".\n"
4485                "But the xscreensaver managing display \"%s\"\n"
4486                "is running as user \"%s\" on host \"%s\".\n"
4487                "\n"
4488                "If those two machines don't share a file system (that is,\n"
4489                "if they don't see the same ~%s/.xscreensaver file) then\n"
4490                "%s won't work right.\n"
4491                "\n"
4492                "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4493                progname, luser, lhost,
4494                d,
4495                (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4496                luser,
4497                progname,
4498                lhost, luser);
4499     }
4500   else if (!!strcmp (rversion, s->short_version))
4501     {
4502       /* Warn that the version numbers don't match.
4503        */
4504       sprintf (msg,
4505              _("Warning:\n\n"
4506                "This is %s version %s.\n"
4507                "But the xscreensaver managing display \"%s\"\n"
4508                "is version %s.  This could cause problems.\n"
4509                "\n"
4510                "Restart the xscreensaver daemon now?\n"),
4511                progname, s->short_version,
4512                d,
4513                rversion);
4514     }
4515
4516
4517   if (*msg)
4518     warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4519
4520   if (rversion) free (rversion);
4521   if (ruser) free (ruser);
4522   if (rhost) free (rhost);
4523   free (msg);
4524   msg = 0;
4525
4526   /* Note: since these dialogs are not modal, they will stack up.
4527      So we do this check *after* popping up the "xscreensaver is not
4528      running" dialog so that these are on top.  Good enough.
4529    */
4530
4531   if (gnome_screensaver_active_p ())
4532     warning_dialog (s->toplevel_widget,
4533                     _("Warning:\n\n"
4534                       "The GNOME screensaver daemon appears to be running.\n"
4535                       "It must be stopped for XScreenSaver to work properly.\n"
4536                       "\n"
4537                       "Stop the GNOME screen saver daemon now?\n"),
4538                     D_GNOME, 1);
4539
4540   if (kde_screensaver_active_p ())
4541     warning_dialog (s->toplevel_widget,
4542                     _("Warning:\n\n"
4543                       "The KDE screen saver daemon appears to be running.\n"
4544                       "It must be stopped for XScreenSaver to work properly.\n"
4545                       "\n"
4546                       "Stop the KDE screen saver daemon now?\n"),
4547                     D_KDE, 1);
4548 }
4549
4550
4551 /* We use this error handler so that X errors are preceeded by the name
4552    of the program that generated them.
4553  */
4554 static int
4555 demo_ehandler (Display *dpy, XErrorEvent *error)
4556 {
4557   state *s = global_state_kludge;  /* I hate C so much... */
4558   fprintf (stderr, "\nX error in %s:\n", blurb());
4559   XmuPrintDefaultErrorMessage (dpy, error, stderr);
4560   kill_preview_subproc (s, False);
4561   exit (-1);
4562   return 0;
4563 }
4564
4565
4566 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4567    of the program that generated them; and also that we can ignore one
4568    particular bogus error message that Gdk madly spews.
4569  */
4570 static void
4571 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4572                const gchar *message, gpointer user_data)
4573 {
4574   /* Ignore the message "Got event for unknown window: 0x...".
4575      Apparently some events are coming in for the xscreensaver window
4576      (presumably reply events related to the ClientMessage) and Gdk
4577      feels the need to complain about them.  So, just suppress any
4578      messages that look like that one.
4579    */
4580   if (strstr (message, "unknown window"))
4581     return;
4582
4583   fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4584            (log_domain ? log_domain : progclass),
4585            (log_level == G_LOG_LEVEL_ERROR    ? "error" :
4586             log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4587             log_level == G_LOG_LEVEL_WARNING  ? "warning" :
4588             log_level == G_LOG_LEVEL_MESSAGE  ? "message" :
4589             log_level == G_LOG_LEVEL_INFO     ? "info" :
4590             log_level == G_LOG_LEVEL_DEBUG    ? "debug" : "???"),
4591            message,
4592            ((!*message || message[strlen(message)-1] != '\n')
4593             ? "\n" : ""));
4594 }
4595
4596
4597 #ifdef __GNUC__
4598  __extension__     /* shut up about "string length is greater than the length
4599                       ISO C89 compilers are required to support" when including
4600                       the .ad file... */
4601 #endif
4602
4603 STFU
4604 static char *defaults[] = {
4605 #include "XScreenSaver_ad.h"
4606  0
4607 };
4608
4609 #if 0
4610 #ifdef HAVE_CRAPPLET
4611 static struct poptOption crapplet_options[] = {
4612   {NULL, '\0', 0, NULL, 0}
4613 };
4614 #endif /* HAVE_CRAPPLET */
4615 #endif /* 0 */
4616
4617 const char *usage = "[--display dpy] [--prefs | --settings]"
4618 # ifdef HAVE_CRAPPLET
4619                     " [--crapplet]"
4620 # endif
4621             "\n\t\t   [--debug] [--sync] [--no-xshm] [--configdir dir]";
4622
4623 static void
4624 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4625 {
4626   state *s = (state *) user_data;
4627   Boolean oi = s->initializing_p;
4628 #ifndef HAVE_GTK2
4629   GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4630 #endif
4631   s->initializing_p = True;
4632 #ifndef HAVE_GTK2
4633   eschew_gtk_lossage (label);
4634 #endif
4635   s->initializing_p = oi;
4636 }
4637
4638
4639 #if 0
4640 static void
4641 print_widget_tree (GtkWidget *w, int depth)
4642 {
4643   int i;
4644   for (i = 0; i < depth; i++)
4645     fprintf (stderr, "  ");
4646   fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4647
4648   if (GTK_IS_LIST (w))
4649     {
4650       for (i = 0; i < depth+1; i++)
4651         fprintf (stderr, "  ");
4652       fprintf (stderr, "...list kids...\n");
4653     }
4654   else if (GTK_IS_CONTAINER (w))
4655     {
4656       GList *kids = gtk_container_children (GTK_CONTAINER (w));
4657       while (kids)
4658         {
4659           print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4660           kids = kids->next;
4661         }
4662     }
4663 }
4664 #endif /* 0 */
4665
4666 static int
4667 delayed_scroll_kludge (gpointer data)
4668 {
4669   state *s = (state *) data;
4670   GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4671   ensure_selected_item_visible (w);
4672
4673   /* Oh, this is just fucking lovely, too. */
4674   w = GTK_WIDGET (name_to_widget (s, "preview"));
4675   gtk_widget_hide (w);
4676   gtk_widget_show (w);
4677
4678   return FALSE;  /* do not re-execute timer */
4679 }
4680
4681 #ifdef HAVE_GTK2
4682
4683 GtkWidget *
4684 create_xscreensaver_demo (void)
4685 {
4686   GtkWidget *nb;
4687
4688   nb = name_to_widget (global_state_kludge, "preview_notebook");
4689   gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4690
4691   return name_to_widget (global_state_kludge, "xscreensaver_demo");
4692 }
4693
4694 GtkWidget *
4695 create_xscreensaver_settings_dialog (void)
4696 {
4697   GtkWidget *w, *box;
4698
4699   box = name_to_widget (global_state_kludge, "dialog_action_area");
4700
4701   w = name_to_widget (global_state_kludge, "adv_button");
4702   gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4703
4704   w = name_to_widget (global_state_kludge, "std_button");
4705   gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4706
4707   return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4708 }
4709
4710 #endif /* HAVE_GTK2 */
4711
4712 int
4713 main (int argc, char **argv)
4714 {
4715   XtAppContext app;
4716   state S, *s;
4717   saver_preferences *p;
4718   Bool prefs_p = False;
4719   Bool settings_p = False;
4720   int i;
4721   Display *dpy;
4722   Widget toplevel_shell;
4723   char *real_progname = argv[0];
4724   char *window_title;
4725   char *geom = 0;
4726   Bool crapplet_p = False;
4727   char *str;
4728
4729 #ifdef ENABLE_NLS
4730   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4731   textdomain (GETTEXT_PACKAGE);
4732
4733 # ifdef HAVE_GTK2
4734   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4735 # else  /* !HAVE_GTK2 */
4736   if (!setlocale (LC_ALL, ""))
4737     fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4738 # endif /* !HAVE_GTK2 */
4739
4740 #endif /* ENABLE_NLS */
4741
4742   str = strrchr (real_progname, '/');
4743   if (str) real_progname = str+1;
4744
4745   s = &S;
4746   memset (s, 0, sizeof(*s));
4747   s->initializing_p = True;
4748   p = &s->prefs;
4749
4750   global_state_kludge = s;  /* I hate C so much... */
4751
4752   progname = real_progname;
4753
4754   s->short_version = (char *) malloc (5);
4755   memcpy (s->short_version, screensaver_id + 17, 4);
4756   s->short_version [4] = 0;
4757
4758
4759   /* Register our error message logger for every ``log domain'' known.
4760      There's no way to do this globally, so I grepped the Gtk/Gdk sources
4761      for all of the domains that seem to be in use.
4762   */
4763   {
4764     const char * const domains[] = { 0,
4765                                      "Gtk", "Gdk", "GLib", "GModule",
4766                                      "GThread", "Gnome", "GnomeUI" };
4767     for (i = 0; i < countof(domains); i++)
4768       g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4769   }
4770
4771 #ifdef DEFAULT_ICONDIR  /* from -D on compile line */
4772 # ifndef HAVE_GTK2
4773   {
4774     const char *dir = DEFAULT_ICONDIR;
4775     if (*dir) add_pixmap_directory (dir);
4776   }
4777 # endif /* !HAVE_GTK2 */
4778 #endif /* DEFAULT_ICONDIR */
4779
4780   /* This is gross, but Gtk understands --display and not -display...
4781    */
4782   for (i = 1; i < argc; i++)
4783     if (argv[i][0] && argv[i][1] && 
4784         !strncmp(argv[i], "-display", strlen(argv[i])))
4785       argv[i] = "--display";
4786
4787
4788   /* We need to parse this arg really early... Sigh. */
4789   for (i = 1; i < argc; i++)
4790     {
4791       if (argv[i] &&
4792           (!strcmp(argv[i], "--crapplet") ||
4793            !strcmp(argv[i], "--capplet")))
4794         {
4795 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4796           int j;
4797           crapplet_p = True;
4798           for (j = i; j < argc; j++)  /* remove it from the list */
4799             argv[j] = argv[j+1];
4800           argc--;
4801 # else  /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4802           fprintf (stderr, "%s: not compiled with --crapplet support\n",
4803                    real_progname);
4804           fprintf (stderr, "%s: %s\n", real_progname, usage);
4805           exit (1);
4806 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4807         }
4808       else if (argv[i] &&
4809                (!strcmp(argv[i], "--debug") ||
4810                 !strcmp(argv[i], "-debug") ||
4811                 !strcmp(argv[i], "-d")))
4812         {
4813           int j;
4814           s->debug_p = True;
4815           for (j = i; j < argc; j++)  /* remove it from the list */
4816             argv[j] = argv[j+1];
4817           argc--;
4818           i--;
4819         }
4820       else if (argv[i] &&
4821                argc > i+1 &&
4822                *argv[i+1] &&
4823                (!strcmp(argv[i], "-geometry") ||
4824                 !strcmp(argv[i], "-geom") ||
4825                 !strcmp(argv[i], "-geo") ||
4826                 !strcmp(argv[i], "-g")))
4827         {
4828           int j;
4829           geom = argv[i+1];
4830           for (j = i; j < argc; j++)  /* remove them from the list */
4831             argv[j] = argv[j+2];
4832           argc -= 2;
4833           i -= 2;
4834         }
4835       else if (argv[i] &&
4836                argc > i+1 &&
4837                *argv[i+1] &&
4838                (!strcmp(argv[i], "--configdir")))
4839         {
4840           int j;
4841           struct stat st;
4842           hack_configuration_path = argv[i+1];
4843           for (j = i; j < argc; j++)  /* remove them from the list */
4844             argv[j] = argv[j+2];
4845           argc -= 2;
4846           i -= 2;
4847
4848           if (0 != stat (hack_configuration_path, &st))
4849             {
4850               char buf[255];
4851               sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4852               perror (buf);
4853               exit (1);
4854             }
4855           else if (!S_ISDIR (st.st_mode))
4856             {
4857               fprintf (stderr, "%s: not a directory: %s\n",
4858                        blurb(), hack_configuration_path);
4859               exit (1);
4860             }
4861         }
4862     }
4863
4864
4865   if (s->debug_p)
4866     fprintf (stderr, "%s: using config directory \"%s\"\n",
4867              progname, hack_configuration_path);
4868
4869
4870   /* Let Gtk open the X connection, then initialize Xt to use that
4871      same connection.  Doctor Frankenstein would be proud.
4872    */
4873 # ifdef HAVE_CRAPPLET
4874   if (crapplet_p)
4875     {
4876       GnomeClient *client;
4877       GnomeClientFlags flags = 0;
4878
4879       int init_results = gnome_capplet_init ("screensaver-properties",
4880                                              s->short_version,
4881                                              argc, argv, NULL, 0, NULL);
4882       /* init_results is:
4883          0 upon successful initialization;
4884          1 if --init-session-settings was passed on the cmdline;
4885          2 if --ignore was passed on the cmdline;
4886         -1 on error.
4887
4888          So the 1 signifies just to init the settings, and quit, basically.
4889          (Meaning launch the xscreensaver daemon.)
4890        */
4891
4892       if (init_results < 0)
4893         {
4894 #  if 0
4895           g_error ("An initialization error occurred while "
4896                    "starting xscreensaver-capplet.\n");
4897 #  else  /* !0 */
4898           fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4899                    real_progname, init_results);
4900           exit (1);
4901 #  endif /* !0 */
4902         }
4903
4904       client = gnome_master_client ();
4905
4906       if (client)
4907         flags = gnome_client_get_flags (client);
4908
4909       if (flags & GNOME_CLIENT_IS_CONNECTED)
4910         {
4911           int token =
4912             gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4913                                          gnome_client_get_id (client));
4914           if (token)
4915             {
4916               char *session_args[20];
4917               int i = 0;
4918               session_args[i++] = real_progname;
4919               session_args[i++] = "--capplet";
4920               session_args[i++] = "--init-session-settings";
4921               session_args[i] = 0;
4922               gnome_client_set_priority (client, 20);
4923               gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4924               gnome_client_set_restart_command (client, i, session_args);
4925             }
4926           else
4927             {
4928               gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4929             }
4930
4931           gnome_client_flush (client);
4932         }
4933
4934       if (init_results == 1)
4935         {
4936           system ("xscreensaver -nosplash &");
4937           return 0;
4938         }
4939
4940     }
4941   else
4942 # endif /* HAVE_CRAPPLET */
4943     {
4944       gtk_init (&argc, &argv);
4945     }
4946
4947
4948   /* We must read exactly the same resources as xscreensaver.
4949      That means we must have both the same progclass *and* progname,
4950      at least as far as the resource database is concerned.  So,
4951      put "xscreensaver" in argv[0] while initializing Xt.
4952    */
4953   argv[0] = "xscreensaver";
4954   progname = argv[0];
4955
4956
4957   /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4958    */
4959   XtToolkitInitialize ();
4960   app = XtCreateApplicationContext ();
4961   dpy = GDK_DISPLAY();
4962   XtAppSetFallbackResources (app, defaults);
4963   XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
4964   toplevel_shell = XtAppCreateShell (progname, progclass,
4965                                      applicationShellWidgetClass,
4966                                      dpy, 0, 0);
4967
4968   dpy = XtDisplay (toplevel_shell);
4969   db = XtDatabase (dpy);
4970   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
4971   XSetErrorHandler (demo_ehandler);
4972
4973   /* Let's just ignore these.  They seem to confuse Irix Gtk... */
4974   signal (SIGPIPE, SIG_IGN);
4975
4976   /* After doing Xt-style command-line processing, complain about any
4977      unrecognized command-line arguments.
4978    */
4979   for (i = 1; i < argc; i++)
4980     {
4981       char *str = argv[i];
4982       if (str[0] == '-' && str[1] == '-')
4983         str++;
4984       if (!strcmp (str, "-prefs"))
4985         prefs_p = True;
4986       else if (!strcmp (str, "-settings"))
4987         settings_p = True;
4988       else if (crapplet_p)
4989         /* There are lots of random args that we don't care about when we're
4990            started as a crapplet, so just ignore unknown args in that case. */
4991         ;
4992       else
4993         {
4994           fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
4995                    argv[i]);
4996           fprintf (stderr, "%s: %s\n", real_progname, usage);
4997           exit (1);
4998         }
4999     }
5000
5001   /* Load the init file, which may end up consulting the X resource database
5002      and the site-wide app-defaults file.  Note that at this point, it's
5003      important that `progname' be "xscreensaver", rather than whatever
5004      was in argv[0].
5005    */
5006   p->db = db;
5007   s->nscreens = screen_count (dpy);
5008
5009   hack_environment (s);  /* must be before initialize_sort_map() */
5010
5011   load_init_file (dpy, p);
5012   initialize_sort_map (s);
5013
5014   /* Now that Xt has been initialized, and the resources have been read,
5015      we can set our `progname' variable to something more in line with
5016      reality.
5017    */
5018   progname = real_progname;
5019
5020
5021 #if 0
5022   /* Print out all the resources we read. */
5023   {
5024     XrmName name = { 0 };
5025     XrmClass class = { 0 };
5026     int count = 0;
5027     XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
5028                           (POINTER) &count);
5029   }
5030 #endif
5031
5032
5033   /* Intern the atoms that xscreensaver_command() needs.
5034    */
5035   XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
5036   XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
5037   XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
5038   XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5039   XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5040   XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5041   XA_SELECT = XInternAtom (dpy, "SELECT", False);
5042   XA_DEMO = XInternAtom (dpy, "DEMO", False);
5043   XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5044   XA_BLANK = XInternAtom (dpy, "BLANK", False);
5045   XA_LOCK = XInternAtom (dpy, "LOCK", False);
5046   XA_EXIT = XInternAtom (dpy, "EXIT", False);
5047   XA_RESTART = XInternAtom (dpy, "RESTART", False);
5048
5049
5050   /* Create the window and all its widgets.
5051    */
5052   s->base_widget     = create_xscreensaver_demo ();
5053   s->popup_widget    = create_xscreensaver_settings_dialog ();
5054   s->toplevel_widget = s->base_widget;
5055
5056
5057   /* Set the main window's title. */
5058   {
5059     char *base_title = _("Screensaver Preferences");
5060     char *v = (char *) strdup(strchr(screensaver_id, ' '));
5061     char *s1, *s2, *s3, *s4;
5062     s1 = (char *) strchr(v,  ' '); s1++;
5063     s2 = (char *) strchr(s1, ' ');
5064     s3 = (char *) strchr(v,  '('); s3++;
5065     s4 = (char *) strchr(s3, ')');
5066     *s2 = 0;
5067     *s4 = 0;
5068
5069     window_title = (char *) malloc (strlen (base_title) +
5070                                     strlen (progclass) +
5071                                     strlen (s1) + strlen (s3) +
5072                                     100);
5073     sprintf (window_title, "%s  (%s %s, %s)", base_title, progclass, s1, s3);
5074     gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5075     gtk_window_set_title (GTK_WINDOW (s->popup_widget),    window_title);
5076     free (v);
5077   }
5078
5079   /* Adjust the (invisible) notebooks on the popup dialog... */
5080   {
5081     GtkNotebook *notebook =
5082       GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5083     GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5084     int page = 0;
5085
5086 # ifdef HAVE_XML
5087     gtk_widget_hide (std);
5088 # else  /* !HAVE_XML */
5089     /* Make the advanced page be the only one available. */
5090     gtk_widget_set_sensitive (std, False);
5091     std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5092     gtk_widget_hide (std);
5093     std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5094     gtk_widget_hide (std);
5095     page = 1;
5096 # endif /* !HAVE_XML */
5097
5098     gtk_notebook_set_page (notebook, page);
5099     gtk_notebook_set_show_tabs (notebook, False);
5100   }
5101
5102   /* Various other widget initializations...
5103    */
5104   gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5105                       GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5106                       (gpointer) s);
5107   gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5108                       GTK_SIGNAL_FUNC (wm_popup_close_cb),
5109                       (gpointer) s);
5110
5111   populate_hack_list (s);
5112   populate_prefs_page (s);
5113   sensitize_demo_widgets (s, False);
5114   fix_text_entry_sizes (s);
5115   scroll_to_current_hack (s);
5116
5117   gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5118                       "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5119                       (gpointer) s);
5120
5121 #ifndef HAVE_GTK2
5122   gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5123                       "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5124                       (gpointer) s);
5125   gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5126                       "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5127                       (gpointer) s);
5128 #endif /* !HAVE_GTK2 */
5129
5130   /* Hook up callbacks to the items on the mode menu. */
5131   {
5132     GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5133     GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5134     GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5135     int i;
5136     for (i = 0; kids; kids = kids->next, i++)
5137       {
5138         gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5139                             GTK_SIGNAL_FUNC (mode_menu_item_cb),
5140                             (gpointer) s);
5141
5142         /* The "random-same" mode menu item does not appear unless
5143            there are multple screens.
5144          */
5145         if (s->nscreens <= 1 &&
5146             mode_menu_order[i] == RANDOM_HACKS_SAME)
5147           gtk_widget_hide (GTK_WIDGET (kids->data));
5148       }
5149
5150     if (s->nscreens <= 1)   /* recompute option-menu size */
5151       {
5152         gtk_widget_unrealize (GTK_WIDGET (menu));
5153         gtk_widget_realize (GTK_WIDGET (menu));
5154       }
5155   }
5156
5157
5158   /* Handle the -prefs command-line argument. */
5159   if (prefs_p)
5160     {
5161       GtkNotebook *notebook =
5162         GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5163       gtk_notebook_set_page (notebook, 1);
5164     }
5165
5166 # ifdef HAVE_CRAPPLET
5167   if (crapplet_p)
5168     {
5169       GtkWidget *capplet;
5170       GtkWidget *outer_vbox;
5171
5172       gtk_widget_hide (s->toplevel_widget);
5173
5174       capplet = capplet_widget_new ();
5175
5176       /* Make there be a "Close" button instead of "OK" and "Cancel" */
5177 # ifdef HAVE_CRAPPLET_IMMEDIATE
5178       capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5179 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5180       /* In crapplet-mode, take off the menubar. */
5181       gtk_widget_hide (name_to_widget (s, "menubar"));
5182
5183       /* Reparent our top-level container to be a child of the capplet
5184          window.
5185        */
5186       outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5187       gtk_widget_ref (outer_vbox);
5188       gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5189                             outer_vbox);
5190       STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5191       gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5192
5193       /* Find the window above us, and set the title and close handler. */
5194       {
5195         GtkWidget *window = capplet;
5196         while (window && !GTK_IS_WINDOW (window))
5197           window = GET_PARENT (window);
5198         if (window)
5199           {
5200             gtk_window_set_title (GTK_WINDOW (window), window_title);
5201             gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5202                                 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5203                                 (gpointer) s);
5204           }
5205       }
5206
5207       s->toplevel_widget = capplet;
5208     }
5209 # endif /* HAVE_CRAPPLET */
5210
5211
5212   /* The Gnome folks hate the menubar.  I think it's important to have access
5213      to the commands on the File menu (Restart Daemon, etc.) and to the
5214      About and Documentation commands on the Help menu.
5215    */
5216 #if 0
5217 #ifdef HAVE_GTK2
5218   gtk_widget_hide (name_to_widget (s, "menubar"));
5219 #endif
5220 #endif
5221
5222   free (window_title);
5223   window_title = 0;
5224
5225 #ifdef HAVE_GTK2
5226   /* After picking the default size, allow -geometry to override it. */
5227   if (geom)
5228     gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5229 #endif
5230
5231   gtk_widget_show (s->toplevel_widget);
5232   init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget)));  /* after `show' */
5233   fix_preview_visual (s);
5234
5235   /* Realize page zero, so that we can diddle the scrollbar when the
5236      user tabs back to it -- otherwise, the current hack isn't scrolled
5237      to the first time they tab back there, when started with "-prefs".
5238      (Though it is if they then tab away, and back again.)
5239
5240      #### Bah!  This doesn't work.  Gtk eats my ass!  Someone who
5241      #### understands this crap, explain to me how to make this work.
5242   */
5243   gtk_widget_realize (name_to_widget (s, "demos_table"));
5244
5245
5246   gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5247
5248
5249   /* Handle the --settings command-line argument. */
5250   if (settings_p)
5251     gtk_timeout_add (500, settings_timer, 0);
5252
5253
5254   /* Issue any warnings about the running xscreensaver daemon. */
5255   if (! s->debug_p)
5256     the_network_is_not_the_computer (s);
5257
5258
5259   /* Run the Gtk event loop, and not the Xt event loop.  This means that
5260      if there were Xt timers or fds registered, they would never get serviced,
5261      and if there were any Xt widgets, they would never have events delivered.
5262      Fortunately, we're using Gtk for all of the UI, and only initialized
5263      Xt so that we could process the command line and use the X resource
5264      manager.
5265    */
5266   s->initializing_p = False;
5267
5268   /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5269      after we start up.  Otherwise, it always appears scrolled to the top
5270      when in crapplet-mode. */
5271   gtk_timeout_add (500, delayed_scroll_kludge, s);
5272
5273
5274 #if 1
5275   /* Load every configurator in turn, to scan them for errors all at once. */
5276   if (s->debug_p)
5277     {
5278       int i;
5279       for (i = 0; i < p->screenhacks_count; i++)
5280         {
5281           screenhack *hack = p->screenhacks[i];
5282           conf_data *d = load_configurator (hack->command, s->debug_p);
5283           if (d) free_conf_data (d);
5284         }
5285     }
5286 #endif
5287
5288
5289 # ifdef HAVE_CRAPPLET
5290   if (crapplet_p)
5291     capplet_gtk_main ();
5292   else
5293 # endif /* HAVE_CRAPPLET */
5294     gtk_main ();
5295
5296   kill_preview_subproc (s, False);
5297   exit (0);
5298 }
5299
5300 #endif /* HAVE_GTK -- whole file */