http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.34.tar.gz
[xscreensaver] / driver / demo-Gtk.c
1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2  * xscreensaver, Copyright (c) 1993-2001 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 <stdlib.h>
20
21 #ifdef HAVE_UNISTD_H
22 # include <unistd.h>
23 #endif
24
25 #ifndef VMS
26 # include <pwd.h>               /* for getpwuid() */
27 #else /* VMS */
28 # include "vms-pwd.h"
29 #endif /* VMS */
30
31 #ifdef HAVE_UNAME
32 # include <sys/utsname.h>       /* for uname() */
33 #endif /* HAVE_UNAME */
34
35 #include <stdio.h>
36 #include <sys/stat.h>
37
38 #include <X11/Xproto.h>         /* for CARD32 */
39 #include <X11/Xatom.h>          /* for XA_INTEGER */
40 #include <X11/Intrinsic.h>
41 #include <X11/StringDefs.h>
42
43 /* We don't actually use any widget internals, but these are included
44    so that gdb will have debug info for the widgets... */
45 #include <X11/IntrinsicP.h>
46 #include <X11/ShellP.h>
47
48 #ifdef HAVE_XMU
49 # ifndef VMS
50 #  include <X11/Xmu/Error.h>
51 # else /* VMS */
52 #  include <Xmu/Error.h>
53 # endif
54 #else
55 # include "xmu.h"
56 #endif
57
58 #include <gtk/gtk.h>
59
60 #ifdef HAVE_CRAPPLET
61 # include <gnome.h>
62 # include <capplet-widget.h>
63 #endif
64
65 #include <gdk/gdkx.h>
66
67 #include "version.h"
68 #include "prefs.h"
69 #include "resources.h"          /* for parse_time() */
70 #include "visual.h"             /* for has_writable_cells() */
71 #include "remote.h"             /* for xscreensaver_command() */
72 #include "usleep.h"
73
74 #include "logo-50.xpm"
75 #include "logo-180.xpm"
76
77 #include "demo-Gtk-widgets.h"
78
79 #include <stdio.h>
80 #include <string.h>
81 #include <ctype.h>
82
83 #undef countof
84 #define countof(x) (sizeof((x))/sizeof((*x)))
85
86
87 char *progname = 0;
88 char *progclass = "XScreenSaver";
89 XrmDatabase db;
90
91 static Bool crapplet_p = False;
92 static Bool initializing_p;
93 static GtkWidget *toplevel_widget;
94
95 typedef struct {
96   saver_preferences *a, *b;
97 } prefs_pair;
98
99 static void *global_prefs_pair;  /* I hate C so much... */
100
101 char *blurb (void) { return progname; }
102
103 static char *short_version = 0;
104
105 Atom XA_VROOT;
106 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
107 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
108 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
109
110
111 static void populate_demo_window (GtkWidget *toplevel,
112                                   int which, prefs_pair *pair);
113 static void populate_prefs_page (GtkWidget *top, prefs_pair *pair);
114 static int apply_changes_and_save (GtkWidget *widget);
115 static int maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair);
116 static void await_xscreensaver (GtkWidget *widget);
117
118 \f
119 /* Some random utility functions
120  */
121
122 static GtkWidget *
123 name_to_widget (GtkWidget *widget, const char *name)
124 {
125   return (GtkWidget *) gtk_object_get_data (GTK_OBJECT(toplevel_widget), name);
126 }
127
128
129 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
130    Takes a scroller, viewport, or list as an argument.
131  */
132 static void
133 ensure_selected_item_visible (GtkWidget *widget)
134 {
135   GtkScrolledWindow *scroller = 0;
136   GtkViewport *vp = 0;
137   GtkList *list_widget = 0;
138   GList *slist;
139   GList *kids;
140   int nkids = 0;
141   GtkWidget *selected = 0;
142   int which = -1;
143   GtkAdjustment *adj;
144   gint parent_h, child_y, child_h, children_h, ignore;
145   double ratio_t, ratio_b;
146
147   if (GTK_IS_SCROLLED_WINDOW (widget))
148     {
149       scroller = GTK_SCROLLED_WINDOW (widget);
150       vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
151       list_widget = GTK_LIST (GTK_BIN(vp)->child);
152     }
153   else if (GTK_IS_VIEWPORT (widget))
154     {
155       vp = GTK_VIEWPORT (widget);
156       scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
157       list_widget = GTK_LIST (GTK_BIN(vp)->child);
158     }
159   else if (GTK_IS_LIST (widget))
160     {
161       list_widget = GTK_LIST (widget);
162       vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
163       scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
164     }
165   else
166     abort();
167
168   slist = list_widget->selection;
169   selected = (slist ? GTK_WIDGET (slist->data) : 0);
170   if (!selected)
171     return;
172
173   which = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
174
175   for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
176        kids; kids = kids->next)
177     nkids++;
178
179   adj = gtk_scrolled_window_get_vadjustment (scroller);
180
181   gdk_window_get_geometry (GTK_WIDGET(vp)->window,
182                            &ignore, &ignore, &ignore, &parent_h, &ignore);
183   gdk_window_get_geometry (GTK_WIDGET(selected)->window,
184                            &ignore, &child_y, &ignore, &child_h, &ignore);
185   children_h = nkids * child_h;
186
187   ratio_t = ((double) child_y) / ((double) children_h);
188   ratio_b = ((double) child_y + child_h) / ((double) children_h);
189
190   if (adj->upper == 0.0)  /* no items in list */
191     return;
192
193   if (ratio_t < (adj->value / adj->upper) ||
194       ratio_b > ((adj->value + adj->page_size) / adj->upper))
195     {
196       double target;
197       int slop = parent_h * 0.75; /* how much to overshoot by */
198
199       if (ratio_t < (adj->value / adj->upper))
200         {
201           double ratio_w = ((double) parent_h) / ((double) children_h);
202           double ratio_l = (ratio_b - ratio_t);
203           target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
204           target += slop;
205         }
206       else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
207         {
208           target = ratio_t * adj->upper;
209           target -= slop;
210         }
211
212       if (target > adj->upper - adj->page_size)
213         target = adj->upper - adj->page_size;
214       if (target < 0)
215         target = 0;
216
217       gtk_adjustment_set_value (adj, target);
218     }
219 }
220
221 static void
222 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
223 {
224   GtkWidget *shell = GTK_WIDGET (user_data);
225   while (shell->parent)
226     shell = shell->parent;
227   gtk_widget_destroy (GTK_WIDGET (shell));
228 }
229
230
231 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
232
233 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
234 {
235   restart_menu_cb (widget, user_data);
236   warning_dialog_dismiss_cb (widget, user_data);
237 }
238
239 static void
240 warning_dialog (GtkWidget *parent, const char *message,
241                 Boolean restart_button_p, int center)
242 {
243   char *msg = strdup (message);
244   char *head;
245
246   GtkWidget *dialog = gtk_dialog_new ();
247   GtkWidget *label = 0;
248   GtkWidget *ok = 0;
249   GtkWidget *cancel = 0;
250   int i = 0;
251
252   while (parent->parent)
253     parent = parent->parent;
254
255   if (!GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
256     return;
257
258   head = msg;
259   while (head)
260     {
261       char name[20];
262       char *s = strchr (head, '\n');
263       if (s) *s = 0;
264
265       sprintf (name, "label%d", i++);
266
267       {
268         label = gtk_label_new (head);
269
270         if (i == 1)
271           {
272             GTK_WIDGET (label)->style =
273               gtk_style_copy (GTK_WIDGET (label)->style);
274             GTK_WIDGET (label)->style->font =
275               gdk_font_load (get_string_resource("warning_dialog.headingFont",
276                                                  "Dialog.Font"));
277             gtk_widget_set_style (GTK_WIDGET (label),
278                                   GTK_WIDGET (label)->style);
279           }
280
281         if (center <= 0)
282           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
283         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
284                             label, TRUE, TRUE, 0);
285         gtk_widget_show (label);
286       }
287
288       if (s)
289         head = s+1;
290       else
291         head = 0;
292
293       center--;
294     }
295
296   label = gtk_label_new ("");
297   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
298                       label, TRUE, TRUE, 0);
299   gtk_widget_show (label);
300
301   label = gtk_hbutton_box_new ();
302   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
303                       label, TRUE, TRUE, 0);
304
305   ok = gtk_button_new_with_label ("OK");
306   gtk_container_add (GTK_CONTAINER (label), ok);
307
308   if (restart_button_p)
309     {
310       cancel = gtk_button_new_with_label ("Cancel");
311       gtk_container_add (GTK_CONTAINER (label), cancel);
312     }
313
314   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
315   gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
316   gtk_window_set_title (GTK_WINDOW (dialog), progclass);
317   gtk_widget_show (ok);
318   if (cancel)
319     gtk_widget_show (cancel);
320   gtk_widget_show (label);
321   gtk_widget_show (dialog);
322 /*  gtk_window_set_default (GTK_WINDOW (dialog), ok);*/
323
324   if (restart_button_p)
325     {
326       gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
327                                  GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
328                                  (gpointer) dialog);
329       gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
330                                  GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
331                                  (gpointer) dialog);
332     }
333   else
334     {
335       gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
336                                  GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
337                                  (gpointer) dialog);
338     }
339
340   gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
341                                 GTK_WIDGET (parent)->window);
342
343   gdk_window_show (GTK_WIDGET (dialog)->window);
344   gdk_window_raise (GTK_WIDGET (dialog)->window);
345
346   free (msg);
347 }
348
349
350 static void
351 run_cmd (GtkWidget *widget, Atom command, int arg)
352 {
353   char *err = 0;
354   int status;
355
356   apply_changes_and_save (widget);
357   status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
358   if (status < 0)
359     {
360       char buf [255];
361       if (err)
362         sprintf (buf, "Error:\n\n%s", err);
363       else
364         strcpy (buf, "Unknown error!");
365       warning_dialog (widget, buf, False, 100);
366     }
367   if (err) free (err);
368 }
369
370
371 static void
372 run_hack (GtkWidget *widget, int which, Bool report_errors_p)
373 {
374   if (which < 0) return;
375   apply_changes_and_save (widget);
376   if (report_errors_p)
377     run_cmd (widget, XA_DEMO, which + 1);
378   else
379     {
380       char *s = 0;
381       xscreensaver_command (GDK_DISPLAY(), XA_DEMO, which + 1, False, &s);
382       if (s) free (s);
383     }
384 }
385
386
387 \f
388 /* Button callbacks
389  */
390
391 void
392 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
393 {
394   apply_changes_and_save (GTK_WIDGET (menuitem));
395   gtk_main_quit ();
396 }
397
398 static void
399 wm_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
400 {
401   apply_changes_and_save (widget);
402   gtk_main_quit ();
403 }
404
405
406 void
407 cut_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
408 {
409   /* #### */
410   warning_dialog (GTK_WIDGET (menuitem),
411                   "Error:\n\n"
412                   "cut unimplemented\n", False, 1);
413 }
414
415
416 void
417 copy_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
418 {
419   /* #### */
420   warning_dialog (GTK_WIDGET (menuitem),
421                   "Error:\n\n"
422                   "copy unimplemented\n", False, 1);
423 }
424
425
426 void
427 paste_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
428 {
429   /* #### */
430   warning_dialog (GTK_WIDGET (menuitem),
431                   "Error:\n\n"
432                   "paste unimplemented\n", False, 1);
433 }
434
435
436 void
437 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
438 {
439   char msg [2048];
440   char *vers = strdup (screensaver_id + 4);
441   char *s;
442   char copy[1024];
443   char *desc = "For updates, check http://www.jwz.org/xscreensaver/";
444
445   s = strchr (vers, ',');
446   *s = 0;
447   s += 2;
448
449   sprintf(copy, "Copyright \251 1991-2001 %s", s);
450
451   sprintf (msg, "%s\n\n%s", copy, desc);
452
453   /* I can't make gnome_about_new() work here -- it starts dying in
454      gdk_imlib_get_visual() under gnome_about_new().  If this worked,
455      then this might be the thing to do:
456
457      #ifdef HAVE_CRAPPLET
458      {
459        const gchar *auth[] = { 0 };
460        GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
461                                            "xscreensaver.xpm");
462        gtk_widget_show (about);
463      }
464      #else / * GTK but not GNOME * /
465       ...
466    */
467   {
468     GdkColormap *colormap;
469     GdkPixmap *gdkpixmap;
470     GdkBitmap *mask;
471
472     GtkWidget *dialog = gtk_dialog_new ();
473     GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
474     GtkWidget *parent = GTK_WIDGET (menuitem);
475     while (parent->parent)
476       parent = parent->parent;
477
478     hbox = gtk_hbox_new (FALSE, 20);
479     gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
480                         hbox, TRUE, TRUE, 0);
481
482     colormap = gtk_widget_get_colormap (parent);
483     gdkpixmap =
484       gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
485                                              (gchar **) logo_180_xpm);
486     icon = gtk_pixmap_new (gdkpixmap, mask);
487     gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
488
489     gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
490
491     vbox = gtk_vbox_new (FALSE, 0);
492     gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
493
494     label1 = gtk_label_new (vers);
495     gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
496     gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
497     gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
498
499     GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
500     GTK_WIDGET (label1)->style->font =
501       gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
502     gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
503
504     label2 = gtk_label_new (msg);
505     gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
506     gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
507     gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
508
509     GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
510     GTK_WIDGET (label2)->style->font =
511       gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
512     gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
513
514     hb = gtk_hbutton_box_new ();
515
516     gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
517                         hb, TRUE, TRUE, 0);
518
519     ok = gtk_button_new_with_label ("OK");
520     gtk_container_add (GTK_CONTAINER (hb), ok);
521
522     gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
523     gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
524     gtk_window_set_title (GTK_WINDOW (dialog), progclass);
525
526     gtk_widget_show (hbox);
527     gtk_widget_show (icon);
528     gtk_widget_show (vbox);
529     gtk_widget_show (label1);
530     gtk_widget_show (label2);
531     gtk_widget_show (hb);
532     gtk_widget_show (ok);
533     gtk_widget_show (dialog);
534
535     gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
536                                GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
537                                (gpointer) dialog);
538     gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
539                                   GTK_WIDGET (parent)->window);
540     gdk_window_show (GTK_WIDGET (dialog)->window);
541     gdk_window_raise (GTK_WIDGET (dialog)->window);
542   }
543 }
544
545
546 void
547 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
548 {
549   /* prefs_pair *pair = (prefs_pair *) client_data; */
550   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
551
552   saver_preferences *p =  pair->a;
553   char *help_command;
554
555   if (!p->help_url || !*p->help_url)
556     {
557       warning_dialog (GTK_WIDGET (menuitem),
558                       "Error:\n\n"
559                       "No Help URL has been specified.\n", False, 100);
560       return;
561     }
562
563   help_command = (char *) malloc (strlen (p->load_url_command) +
564                                   (strlen (p->help_url) * 2) + 20);
565   strcpy (help_command, "( ");
566   sprintf (help_command + strlen(help_command),
567            p->load_url_command, p->help_url, p->help_url);
568   strcat (help_command, " ) &");
569   system (help_command);
570   free (help_command);
571 }
572
573
574 void
575 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
576 {
577   run_cmd (GTK_WIDGET (menuitem), XA_ACTIVATE, 0);
578 }
579
580
581 void
582 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
583 {
584   run_cmd (GTK_WIDGET (menuitem), XA_LOCK, 0);
585 }
586
587
588 void
589 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
590 {
591   run_cmd (GTK_WIDGET (menuitem), XA_EXIT, 0);
592 }
593
594
595 void
596 restart_menu_cb (GtkWidget *widget, gpointer user_data)
597 {
598 #if 0
599   run_cmd (GTK_WIDGET (widget), XA_RESTART, 0);
600 #else
601   apply_changes_and_save (GTK_WIDGET (widget));
602   xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
603   sleep (1);
604   system ("xscreensaver -nosplash &");
605 #endif
606
607   await_xscreensaver (GTK_WIDGET (widget));
608 }
609
610 static void
611 await_xscreensaver (GtkWidget *widget)
612 {
613   int countdown = 5;
614
615   Display *dpy = GDK_DISPLAY();
616   /*  GtkWidget *dialog = 0;*/
617   char *rversion = 0;
618
619   while (!rversion && (--countdown > 0))
620     {
621       /* Check for the version of the running xscreensaver... */
622       server_xscreensaver_version (dpy, &rversion, 0, 0);
623
624       /* If it's not there yet, wait a second... */
625       sleep (1);
626     }
627
628 /*  if (dialog) gtk_widget_destroy (dialog);*/
629
630   if (rversion)
631     {
632       /* Got it. */
633       free (rversion);
634     }
635   else
636     {
637       /* Timed out, no screensaver running. */
638
639       char buf [1024];
640       Bool root_p = (geteuid () == 0);
641       
642       strcpy (buf, 
643               "Error:\n\n"
644               "The xscreensaver daemon did not start up properly.\n"
645               "\n");
646
647       if (root_p)
648         strcat (buf,
649             "You are running as root.  This usually means that xscreensaver\n"
650             "was unable to contact your X server because access control is\n"
651             "turned on.  Try running this command:\n"
652             "\n"
653             "                        xhost +localhost\n"
654             "\n"
655             "and then selecting `File / Restart Daemon'.\n"
656             "\n"
657             "Note that turning off access control will allow anyone logged\n"
658             "on to this machine to access your screen, which might be\n"
659             "considered a security problem.  Please read the xscreensaver\n"
660             "manual and FAQ for more information.\n"
661             "\n"
662             "You shouldn't run X as root. Instead, you should log in as a\n"
663             "normal user, and `su' as necessary.");
664       else
665         strcat (buf, "Please check your $PATH and permissions.");
666
667       warning_dialog (widget, buf, False, 1);
668     }
669 }
670
671
672 static int _selected_hack_number = -1;
673
674 static int
675 selected_hack_number (GtkWidget *toplevel)
676 {
677 #if 0
678   GtkViewport *vp = GTK_VIEWPORT (name_to_widget (toplevel, "viewport"));
679   GtkList *list_widget = GTK_LIST (GTK_BIN(vp)->child);
680   GList *slist = list_widget->selection;
681   GtkWidget *selected = (slist ? GTK_WIDGET (slist->data) : 0);
682   int which = (selected
683                ? gtk_list_child_position (list_widget, GTK_WIDGET (selected))
684                : -1);
685   return which;
686 #else
687   return _selected_hack_number;
688 #endif
689 }
690
691
692 static int
693 demo_write_init_file (GtkWidget *widget, saver_preferences *p)
694 {
695   if (!write_init_file (p, short_version, False))
696     return 0;
697   else
698     {
699       const char *f = init_file_name();
700       if (!f || !*f)
701         warning_dialog (widget,
702                         "Error:\n\nCouldn't determine init file name!\n",
703                         False, 100);
704       else
705         {
706           char *b = (char *) malloc (strlen(f) + 1024);
707           sprintf (b, "Error:\n\nCouldn't write %s\n", f);
708           warning_dialog (widget, b, False, 100);
709           free (b);
710         }
711       return -1;
712     }
713 }
714
715
716 static int
717 apply_changes_and_save_1 (GtkWidget *widget)
718 {
719   /* prefs_pair *pair = (prefs_pair *) client_data; */
720   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
721   saver_preferences *p =  pair->a;
722   GtkList *list_widget =
723     GTK_LIST (name_to_widget (widget, "list"));
724   int which = selected_hack_number (widget);
725
726   GtkEntry *cmd = GTK_ENTRY (name_to_widget (widget, "cmd_text"));
727   GtkToggleButton *enabled =
728     GTK_TOGGLE_BUTTON (name_to_widget (widget, "enabled"));
729   GtkCombo *vis = GTK_COMBO (name_to_widget (widget, "visual_combo"));
730
731   Bool enabled_p = gtk_toggle_button_get_active (enabled);
732   const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
733   const char *command = gtk_entry_get_text (cmd);
734   
735   char c;
736   unsigned long id;
737
738   if (which < 0) return -1;
739
740   if (maybe_reload_init_file (widget, pair) != 0)
741     return 1;
742
743   /* Sanity-check and canonicalize whatever the user typed into the combo box.
744    */
745   if      (!strcasecmp (visual, ""))                   visual = "";
746   else if (!strcasecmp (visual, "any"))                visual = "";
747   else if (!strcasecmp (visual, "default"))            visual = "Default";
748   else if (!strcasecmp (visual, "default-n"))          visual = "Default-N";
749   else if (!strcasecmp (visual, "default-i"))          visual = "Default-I";
750   else if (!strcasecmp (visual, "best"))               visual = "Best";
751   else if (!strcasecmp (visual, "mono"))               visual = "Mono";
752   else if (!strcasecmp (visual, "monochrome"))         visual = "Mono";
753   else if (!strcasecmp (visual, "gray"))               visual = "Gray";
754   else if (!strcasecmp (visual, "grey"))               visual = "Gray";
755   else if (!strcasecmp (visual, "color"))              visual = "Color";
756   else if (!strcasecmp (visual, "gl"))                 visual = "GL";
757   else if (!strcasecmp (visual, "staticgray"))         visual = "StaticGray";
758   else if (!strcasecmp (visual, "staticcolor"))        visual = "StaticColor";
759   else if (!strcasecmp (visual, "truecolor"))          visual = "TrueColor";
760   else if (!strcasecmp (visual, "grayscale"))          visual = "GrayScale";
761   else if (!strcasecmp (visual, "greyscale"))          visual = "GrayScale";
762   else if (!strcasecmp (visual, "pseudocolor"))        visual = "PseudoColor";
763   else if (!strcasecmp (visual, "directcolor"))        visual = "DirectColor";
764   else if (1 == sscanf (visual, " %ld %c", &id, &c))   ;
765   else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
766   else
767     {
768       gdk_beep ();                                /* unparsable */
769       visual = "";
770       gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");
771     }
772
773   ensure_selected_item_visible (GTK_WIDGET (list_widget));
774
775   if (!p->screenhacks[which]->visual)
776     p->screenhacks[which]->visual = strdup ("");
777   if (!p->screenhacks[which]->command)
778     p->screenhacks[which]->command = strdup ("");
779
780   if (p->screenhacks[which]->enabled_p != enabled_p ||
781       !!strcasecmp (p->screenhacks[which]->visual, visual) ||
782       !!strcasecmp (p->screenhacks[which]->command, command))
783     {
784       /* Something was changed -- store results into the struct,
785          and write the file.
786        */
787       free (p->screenhacks[which]->visual);
788       free (p->screenhacks[which]->command);
789       p->screenhacks[which]->visual = strdup (visual);
790       p->screenhacks[which]->command = strdup (command);
791       p->screenhacks[which]->enabled_p = enabled_p;
792
793       return demo_write_init_file (widget, p);
794     }
795
796   /* No changes made */
797   return 0;
798 }
799
800 void prefs_ok_cb (GtkButton *button, gpointer user_data);
801
802 static int
803 apply_changes_and_save (GtkWidget *widget)
804 {
805   prefs_ok_cb ((GtkButton *) widget, 0);
806   return apply_changes_and_save_1 (widget);
807 }
808
809
810 void
811 run_this_cb (GtkButton *button, gpointer user_data)
812 {
813   int which = selected_hack_number (GTK_WIDGET (button));
814   if (which < 0) return;
815   if (0 == apply_changes_and_save (GTK_WIDGET (button)))
816     run_hack (GTK_WIDGET (button), which, True);
817 }
818
819
820 void
821 manual_cb (GtkButton *button, gpointer user_data)
822 {
823   /* prefs_pair *pair = (prefs_pair *) client_data; */
824   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
825   saver_preferences *p =  pair->a;
826   GtkList *list_widget =
827     GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
828   int which = selected_hack_number (GTK_WIDGET (button));
829   char *name, *name2, *cmd, *s;
830   if (which < 0) return;
831   apply_changes_and_save (GTK_WIDGET (button));
832   ensure_selected_item_visible (GTK_WIDGET (list_widget));
833
834   name = strdup (p->screenhacks[which]->command);
835   name2 = name;
836   while (isspace (*name2)) name2++;
837   s = name2;
838   while (*s && !isspace (*s)) s++;
839   *s = 0;
840   s = strrchr (name2, '/');
841   if (s) name = s+1;
842
843   cmd = get_string_resource ("manualCommand", "ManualCommand");
844   if (cmd)
845     {
846       char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
847       strcpy (cmd2, "( ");
848       sprintf (cmd2 + strlen (cmd2),
849                cmd,
850                name2, name2, name2, name2);
851       strcat (cmd2, " ) &");
852       system (cmd2);
853       free (cmd2);
854     }
855   else
856     {
857       warning_dialog (GTK_WIDGET (button),
858                       "Error:\n\nno `manualCommand' resource set.",
859                       False, 100);
860     }
861
862   free (name);
863 }
864
865
866 void
867 run_next_cb (GtkButton *button, gpointer user_data)
868 {
869   /* prefs_pair *pair = (prefs_pair *) client_data; */
870   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
871   saver_preferences *p =  pair->a;
872
873   GtkList *list_widget =
874     GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
875   int which = selected_hack_number (GTK_WIDGET (button));
876
877   if (which < 0)
878     which = 0;
879   else
880     which++;
881
882   if (which >= p->screenhacks_count)
883     which = 0;
884
885   apply_changes_and_save (GTK_WIDGET (button));
886   gtk_list_select_item (GTK_LIST (list_widget), which);
887   ensure_selected_item_visible (GTK_WIDGET (list_widget));
888   populate_demo_window (GTK_WIDGET (button), which, pair);
889   run_hack (GTK_WIDGET (button), which, False);
890 }
891
892
893 void
894 run_prev_cb (GtkButton *button, gpointer user_data)
895 {
896   /* prefs_pair *pair = (prefs_pair *) client_data; */
897   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
898   saver_preferences *p =  pair->a;
899
900   GtkList *list_widget =
901     GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
902   int which = selected_hack_number (GTK_WIDGET (button));
903
904   if (which < 0)
905     which = p->screenhacks_count - 1;
906   else
907     which--;
908
909   if (which < 0)
910     which = p->screenhacks_count - 1;
911
912   apply_changes_and_save (GTK_WIDGET (button));
913   gtk_list_select_item (GTK_LIST (list_widget), which);
914   ensure_selected_item_visible (GTK_WIDGET (list_widget));
915   populate_demo_window (GTK_WIDGET (button), which, pair);
916   run_hack (GTK_WIDGET (button), which, False);
917 }
918
919
920 /* Helper for the text fields that contain time specifications:
921    this parses the text, and does error checking.
922  */
923 static void 
924 hack_time_text (GtkWidget *widget, const char *line, Time *store, Bool sec_p)
925 {
926   if (*line)
927     {
928       int value;
929       value = parse_time ((char *) line, sec_p, True);
930       value *= 1000;    /* Time measures in microseconds */
931       if (value < 0)
932         {
933           char b[255];
934           sprintf (b,
935                    "Error:\n\n"
936                    "Unparsable time format: \"%s\"\n",
937                    line);
938           warning_dialog (widget, b, False, 100);
939         }
940       else
941         *store = value;
942     }
943 }
944
945
946 static Bool
947 directory_p (const char *path)
948 {
949   struct stat st;
950   if (!path || !*path)
951     return False;
952   else if (stat (path, &st))
953     return False;
954   else if (!S_ISDIR (st.st_mode))
955     return False;
956   else
957     return True;
958 }
959
960 static char *
961 normalize_directory (const char *path)
962 {
963   int L;
964   char *p2, *s;
965   if (!path) return 0;
966   L = strlen (path);;
967   p2 = (char *) malloc (L + 2);
968   strcpy (p2, path);
969   if (p2[L-1] == '/')  /* remove trailing slash */
970     p2[--L] = 0;
971
972   for (s = p2; s && *s; s++)
973     {
974       if (*s == '/' &&
975           (!strncmp (s, "/../", 4) ||                   /* delete "XYZ/../" */
976            !strncmp (s, "/..\000", 4)))                 /* delete "XYZ/..$" */
977         {
978           char *s0 = s;
979           while (s0 > p2 && s0[-1] != '/')
980             s0--;
981           if (s0 > p2)
982             {
983               s0--;
984               s += 3;
985               strcpy (s0, s);
986               s = s0-1;
987             }
988         }
989       else if (*s == '/' && !strncmp (s, "/./", 3))     /* delete "/./" */
990         strcpy (s, s+2), s--;
991       else if (*s == '/' && !strncmp (s, "/.\000", 3))  /* delete "/.$" */
992         *s = 0, s--;
993     }
994
995   for (s = p2; s && *s; s++)            /* normalize consecutive slashes */
996     while (s[0] == '/' && s[1] == '/')
997       strcpy (s, s+1);
998
999   return p2;
1000 }
1001
1002
1003 void
1004 prefs_ok_cb (GtkButton *button, gpointer user_data)
1005 {
1006   /* prefs_pair *pair = (prefs_pair *) client_data; */
1007   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
1008
1009   saver_preferences *p =  pair->a;
1010   saver_preferences *p2 = pair->b;
1011   Bool changed = False;
1012
1013 # define SECONDS(field, name) \
1014   hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\
1015                     GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
1016                   (field), \
1017                   True)
1018
1019 # define MINUTES(field, name) \
1020   hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\
1021                     GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
1022                   (field), \
1023                   False)
1024
1025 # define INTEGER(field, name) do { \
1026     char *line = gtk_entry_get_text (\
1027                     GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))); \
1028     unsigned int value; \
1029     char c; \
1030     if (! *line) \
1031       ; \
1032     else if (sscanf (line, "%u%c", &value, &c) != 1) \
1033       { \
1034         char b[255]; \
1035         sprintf (b, "Error:\n\n" "Not an integer: \"%s\"\n", line); \
1036         warning_dialog (GTK_WIDGET (button), b, False, 100); \
1037       } \
1038    else \
1039      *(field) = value; \
1040   } while(0)
1041
1042 # define PATHNAME(field, name) do { \
1043     char *line = gtk_entry_get_text (\
1044                     GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))); \
1045     if (! *line) \
1046       ; \
1047     else if (!directory_p (line)) \
1048       { \
1049         char b[255]; \
1050         sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n", line); \
1051         warning_dialog (GTK_WIDGET (button), b, False, 100); \
1052         if ((field)) free ((field)); \
1053         (field) = strdup(line); \
1054       } \
1055    else { \
1056      if ((field)) free ((field)); \
1057      (field) = strdup(line); \
1058     } \
1059   } while(0)
1060
1061 # define CHECKBOX(field, name) \
1062   field = gtk_toggle_button_get_active (\
1063              GTK_TOGGLE_BUTTON (name_to_widget (GTK_WIDGET(button), (name))))
1064
1065   MINUTES (&p2->timeout,          "timeout_text");
1066   MINUTES (&p2->cycle,            "cycle_text");
1067   CHECKBOX (p2->lock_p,           "lock_button");
1068   MINUTES (&p2->lock_timeout,     "lock_text");
1069
1070   CHECKBOX (p2->dpms_enabled_p,   "dpms_button");
1071   MINUTES (&p2->dpms_standby,     "dpms_standby_text");
1072   MINUTES (&p2->dpms_suspend,     "dpms_suspend_text");
1073   MINUTES (&p2->dpms_off,         "dpms_off_text");
1074
1075   CHECKBOX (p2->grab_desktop_p,   "grab_desk_button");
1076   CHECKBOX (p2->grab_video_p,     "grab_video_button");
1077   CHECKBOX (p2->random_image_p,   "grab_image_button");
1078   PATHNAME (p2->image_directory,  "image_text");
1079
1080   CHECKBOX (p2->verbose_p,        "verbose_button");
1081   CHECKBOX (p2->capture_stderr_p, "capture_button");
1082   CHECKBOX (p2->splash_p,         "splash_button");
1083
1084   CHECKBOX (p2->install_cmap_p,   "install_button");
1085   CHECKBOX (p2->fade_p,           "fade_button");
1086   CHECKBOX (p2->unfade_p,         "unfade_button");
1087   SECONDS (&p2->fade_seconds,     "fade_text");
1088
1089 # undef SECONDS
1090 # undef MINUTES
1091 # undef INTEGER
1092 # undef PATHNAME
1093 # undef CHECKBOX
1094
1095 # define COPY(field) \
1096   if (p->field != p2->field) changed = True; \
1097   p->field = p2->field
1098
1099   COPY(timeout);
1100   COPY(cycle);
1101   COPY(lock_p);
1102   COPY(lock_timeout);
1103
1104   COPY(dpms_enabled_p);
1105   COPY(dpms_standby);
1106   COPY(dpms_suspend);
1107   COPY(dpms_off);
1108
1109   COPY (grab_desktop_p);
1110   COPY (grab_video_p);
1111   COPY (random_image_p);
1112
1113   if (!p->image_directory ||
1114       !p2->image_directory ||
1115       strcmp(p->image_directory, p2->image_directory))
1116     changed = True;
1117   if (p->image_directory && p->image_directory != p2->image_directory)
1118     free (p->image_directory);
1119   p->image_directory = normalize_directory (p2->image_directory);
1120   if (p2->image_directory) free (p2->image_directory);
1121   p2->image_directory = 0;
1122
1123   COPY(verbose_p);
1124   COPY(capture_stderr_p);
1125   COPY(splash_p);
1126
1127   COPY(install_cmap_p);
1128   COPY(fade_p);
1129   COPY(unfade_p);
1130   COPY(fade_seconds);
1131 # undef COPY
1132
1133   populate_prefs_page (GTK_WIDGET (button), pair);
1134
1135   if (changed)
1136     {
1137       Display *dpy = GDK_DISPLAY();
1138       sync_server_dpms_settings (dpy, p->dpms_enabled_p,
1139                                  p->dpms_standby / 1000,
1140                                  p->dpms_suspend / 1000,
1141                                  p->dpms_off / 1000,
1142                                  False);
1143
1144       demo_write_init_file (GTK_WIDGET (button), p);
1145     }
1146 }
1147
1148
1149 void
1150 prefs_cancel_cb (GtkButton *button, gpointer user_data)
1151 {
1152   /* prefs_pair *pair = (prefs_pair *) client_data; */
1153   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
1154
1155   *pair->b = *pair->a;
1156   populate_prefs_page (GTK_WIDGET (button), pair);
1157 }
1158
1159
1160 void
1161 pref_changed_cb (GtkButton *button, gpointer user_data)
1162 {
1163   if (! initializing_p)
1164     apply_changes_and_save (GTK_WIDGET (button));
1165 }
1166
1167
1168 static gint
1169 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1170                      gpointer client_data)
1171 {
1172   if (event->type == GDK_2BUTTON_PRESS)
1173     {
1174       GtkList *list = GTK_LIST (name_to_widget (button, "list"));
1175       int which = gtk_list_child_position (list, GTK_WIDGET (button));
1176
1177       if (which >= 0)
1178         run_hack (GTK_WIDGET (button), which, True);
1179     }
1180
1181   return FALSE;
1182 }
1183
1184
1185 static void
1186 list_select_cb (GtkList *list, GtkWidget *child)
1187 {
1188   /* prefs_pair *pair = (prefs_pair *) client_data; */
1189   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
1190
1191   int which = gtk_list_child_position (list, GTK_WIDGET (child));
1192   apply_changes_and_save (GTK_WIDGET (list));
1193   populate_demo_window (GTK_WIDGET (list), which, pair);
1194 }
1195
1196 static void
1197 list_unselect_cb (GtkList *list, GtkWidget *child)
1198 {
1199   /* prefs_pair *pair = (prefs_pair *) client_data; */
1200   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
1201
1202   apply_changes_and_save (GTK_WIDGET (list));
1203   populate_demo_window (GTK_WIDGET (list), -1, pair);
1204 }
1205
1206
1207 static int updating_enabled_cb = 0;  /* kludge to make sure that enabled_cb
1208                                         is only run by user action, not by
1209                                         program action. */
1210
1211 /* Called when the checkboxes that are in the left column of the
1212    scrolling list are clicked.  This both populates the right pane
1213    (just as clicking on the label (really, listitem) does) and
1214    also syncs this checkbox with  the right pane Enabled checkbox.
1215  */
1216 static void
1217 list_checkbox_cb (GtkWidget *cb, gpointer client_data)
1218 {
1219   prefs_pair *pair = (prefs_pair *) client_data;
1220
1221   GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1222   GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1223
1224   GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1225   GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1226   GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1227   GtkAdjustment *adj;
1228   double scroll_top;
1229
1230   GtkToggleButton *enabled =
1231     GTK_TOGGLE_BUTTON (name_to_widget (cb, "enabled"));
1232
1233   int which = gtk_list_child_position (list, line);
1234
1235   /* remember previous scroll position of the top of the list */
1236   adj = gtk_scrolled_window_get_vadjustment (scroller);
1237   scroll_top = adj->value;
1238
1239   apply_changes_and_save (GTK_WIDGET (list));
1240   gtk_list_select_item (list, which);
1241   /* ensure_selected_item_visible (GTK_WIDGET (list)); */
1242   populate_demo_window (GTK_WIDGET (list), which, pair);
1243   
1244   updating_enabled_cb++;
1245   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (enabled),
1246                                 GTK_TOGGLE_BUTTON (cb)->active);
1247   updating_enabled_cb--;
1248
1249   /* restore the previous scroll position of the top of the list.
1250      this is weak, but I don't really know why it's moving... */
1251   gtk_adjustment_set_value (adj, scroll_top);
1252 }
1253
1254
1255 /* Called when the right pane Enabled checkbox is clicked.  This syncs
1256    the corresponding checkbox inside the scrolling list to the state
1257    of this checkbox.
1258  */
1259 void
1260 enabled_cb (GtkWidget *cb, gpointer client_data)
1261 {
1262   int which = selected_hack_number (cb);
1263   
1264   if (updating_enabled_cb) return;
1265
1266   if (which != -1)
1267     {
1268       GtkList *list = GTK_LIST (name_to_widget (cb, "list"));
1269       GList *kids = GTK_LIST (list)->children;
1270       GtkWidget *line = GTK_WIDGET (g_list_nth_data (kids, which));
1271       GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1272       GtkWidget *line_check =
1273         GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1274
1275       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1276                                     GTK_TOGGLE_BUTTON (cb)->active);
1277     }
1278 }
1279
1280
1281
1282 typedef struct {
1283   prefs_pair *pair;
1284   GtkFileSelection *widget;
1285 } file_selection_data;
1286
1287
1288
1289 static void
1290 store_image_directory (GtkWidget *button, gpointer user_data)
1291 {
1292   file_selection_data *fsd = (file_selection_data *) user_data;
1293   prefs_pair *pair = fsd->pair;
1294   GtkFileSelection *selector = fsd->widget;
1295   GtkWidget *top = toplevel_widget;
1296   saver_preferences *p = pair->a;
1297   char *path = gtk_file_selection_get_filename (selector);
1298
1299   if (p->image_directory && !strcmp(p->image_directory, path))
1300     return;  /* no change */
1301
1302   if (!directory_p (path))
1303     {
1304       char b[255];
1305       sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n", path);
1306       warning_dialog (GTK_WIDGET (top), b, False, 100);
1307       return;
1308     }
1309
1310   if (p->image_directory) free (p->image_directory);
1311   p->image_directory = normalize_directory (path);
1312
1313   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "image_text")),
1314                       (p->image_directory ? p->image_directory : ""));
1315   demo_write_init_file (GTK_WIDGET (top), p);
1316 }
1317
1318
1319 static void
1320 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1321 {
1322   file_selection_data *fsd = (file_selection_data *) user_data;
1323   gtk_widget_hide (GTK_WIDGET (fsd->widget));
1324 }
1325
1326 static void
1327 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1328 {
1329   browse_image_dir_cancel (button, user_data);
1330   store_image_directory (button, user_data);
1331 }
1332
1333 static void
1334 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1335 {
1336   browse_image_dir_cancel (widget, user_data);
1337 }
1338
1339
1340 void
1341 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1342 {
1343   /* prefs_pair *pair = (prefs_pair *) client_data; */
1344   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
1345   saver_preferences *p = pair->a;
1346   static file_selection_data *fsd = 0;
1347
1348   GtkFileSelection *selector = GTK_FILE_SELECTION(
1349     gtk_file_selection_new ("Please select the image directory."));
1350
1351   if (!fsd)
1352     fsd = (file_selection_data *) malloc (sizeof (*fsd));  
1353
1354   fsd->widget = selector;
1355   fsd->pair = pair;
1356
1357   if (p->image_directory && *p->image_directory)
1358     gtk_file_selection_set_filename (selector, p->image_directory);
1359
1360   gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1361                       "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1362                       (gpointer *) fsd);
1363   gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1364                       "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1365                       (gpointer *) fsd);
1366   gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1367                       GTK_SIGNAL_FUNC (browse_image_dir_close),
1368                       (gpointer *) fsd);
1369
1370   gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1371
1372   gtk_window_set_modal (GTK_WINDOW (selector), True);
1373   gtk_widget_show (GTK_WIDGET (selector));
1374 }
1375
1376
1377 \f
1378 /* Populating the various widgets
1379  */
1380
1381
1382 /* Formats a `Time' into "H:MM:SS".  (Time is microseconds.)
1383  */
1384 static void
1385 format_time (char *buf, Time time)
1386 {
1387   int s = time / 1000;
1388   unsigned int h = 0, m = 0;
1389   if (s >= 60)
1390     {
1391       m += (s / 60);
1392       s %= 60;
1393     }
1394   if (m >= 60)
1395     {
1396       h += (m / 60);
1397       m %= 60;
1398     }
1399   sprintf (buf, "%u:%02u:%02u", h, m, s);
1400 }
1401
1402
1403 /* Finds the number of the last hack to run, and makes that item be
1404    selected by default.
1405  */
1406 static void
1407 scroll_to_current_hack (GtkWidget *toplevel, prefs_pair *pair)
1408 {
1409   saver_preferences *p =  pair->a;
1410   Atom type;
1411   int format;
1412   unsigned long nitems, bytesafter;
1413   CARD32 *data = 0;
1414   Display *dpy = GDK_DISPLAY();
1415   int which = 0;
1416   GtkList *list;
1417
1418   if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1419                           XA_SCREENSAVER_STATUS,
1420                           0, 3, False, XA_INTEGER,
1421                           &type, &format, &nitems, &bytesafter,
1422                           (unsigned char **) &data)
1423       == Success
1424       && type == XA_INTEGER
1425       && nitems >= 3
1426       && data)
1427     which = (int) data[2] - 1;
1428
1429   if (data) free (data);
1430
1431   if (which < 0)
1432     return;
1433
1434   list = GTK_LIST (name_to_widget (toplevel, "list"));
1435   apply_changes_and_save (toplevel);
1436   if (which < p->screenhacks_count)
1437     {
1438       gtk_list_select_item (list, which);
1439       ensure_selected_item_visible (GTK_WIDGET (list));
1440       populate_demo_window (toplevel, which, pair);
1441     }
1442 }
1443
1444
1445
1446 static void
1447 populate_hack_list (GtkWidget *toplevel, prefs_pair *pair)
1448 {
1449   saver_preferences *p =  pair->a;
1450   GtkList *list = GTK_LIST (name_to_widget (toplevel, "list"));
1451   screenhack **hacks = p->screenhacks;
1452   screenhack **h;
1453
1454   for (h = hacks; h && *h; h++)
1455     {
1456       /* A GtkList must contain only GtkListItems, but those can contain
1457          an arbitrary widget.  We add an Hbox, and inside that, a Checkbox
1458          and a Label.  We handle single and double click events on the
1459          line itself, for clicking on the text, but the interior checkbox
1460          also handles its own events.
1461        */
1462       GtkWidget *line;
1463       GtkWidget *line_hbox;
1464       GtkWidget *line_check;
1465       GtkWidget *line_label;
1466
1467       char *pretty_name = (h[0]->name
1468                            ? strdup (h[0]->name)
1469                            : make_hack_name (h[0]->command));
1470
1471       line = gtk_list_item_new ();
1472       line_hbox = gtk_hbox_new (FALSE, 0);
1473       line_check = gtk_check_button_new ();
1474       line_label = gtk_label_new (pretty_name);
1475
1476       gtk_container_add (GTK_CONTAINER (line), line_hbox);
1477       gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
1478       gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
1479
1480       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1481                                     h[0]->enabled_p);
1482       gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
1483
1484       gtk_widget_show (line_check);
1485       gtk_widget_show (line_label);
1486       gtk_widget_show (line_hbox);
1487       gtk_widget_show (line);
1488
1489       free (pretty_name);
1490
1491       gtk_container_add (GTK_CONTAINER (list), line);
1492       gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
1493                           GTK_SIGNAL_FUNC (list_doubleclick_cb),
1494                           (gpointer) pair);
1495
1496       gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
1497                           GTK_SIGNAL_FUNC (list_checkbox_cb),
1498                           (gpointer) pair);
1499
1500 #if 0 /* #### */
1501       GTK_WIDGET (GTK_BIN(line)->child)->style =
1502         gtk_style_copy (GTK_WIDGET (text_line)->style);
1503 #endif
1504       gtk_widget_show (line);
1505     }
1506
1507   gtk_signal_connect (GTK_OBJECT (list), "select_child",
1508                       GTK_SIGNAL_FUNC (list_select_cb),
1509                       (gpointer) pair);
1510   gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
1511                       GTK_SIGNAL_FUNC (list_unselect_cb),
1512                       (gpointer) pair);
1513 }
1514
1515
1516 static void
1517 populate_prefs_page (GtkWidget *top, prefs_pair *pair)
1518 {
1519   saver_preferences *p =  pair->a;
1520   char s[100];
1521
1522   format_time (s, p->timeout);
1523   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "timeout_text")), s);
1524   format_time (s, p->cycle);
1525   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "cycle_text")), s);
1526   format_time (s, p->lock_timeout);
1527   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "lock_text")), s);
1528
1529   format_time (s, p->dpms_standby);
1530   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_standby_text")),s);
1531   format_time (s, p->dpms_suspend);
1532   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_suspend_text")),s);
1533   format_time (s, p->dpms_off);
1534   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_off_text")), s);
1535
1536   format_time (s, p->fade_seconds);
1537   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "fade_text")), s);
1538
1539   gtk_toggle_button_set_active (
1540                    GTK_TOGGLE_BUTTON (name_to_widget (top, "lock_button")),
1541                    p->lock_p);
1542   gtk_toggle_button_set_active (
1543                    GTK_TOGGLE_BUTTON (name_to_widget (top, "verbose_button")),
1544                    p->verbose_p);
1545   gtk_toggle_button_set_active (
1546                    GTK_TOGGLE_BUTTON (name_to_widget (top, "capture_button")),
1547                    p->capture_stderr_p);
1548   gtk_toggle_button_set_active (
1549                    GTK_TOGGLE_BUTTON (name_to_widget (top, "splash_button")),
1550                    p->splash_p);
1551
1552   gtk_toggle_button_set_active (
1553                    GTK_TOGGLE_BUTTON (name_to_widget (top, "dpms_button")),
1554                    p->dpms_enabled_p);
1555
1556   gtk_toggle_button_set_active (
1557                    GTK_TOGGLE_BUTTON (name_to_widget (top,"grab_desk_button")),
1558                    p->grab_desktop_p);
1559   gtk_toggle_button_set_active (
1560                    GTK_TOGGLE_BUTTON (name_to_widget(top,"grab_video_button")),
1561                    p->grab_video_p);
1562   gtk_toggle_button_set_active (
1563                    GTK_TOGGLE_BUTTON (name_to_widget(top,"grab_image_button")),
1564                    p->random_image_p);
1565   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "image_text")),
1566                       (p->image_directory ? p->image_directory : ""));
1567   gtk_widget_set_sensitive (GTK_WIDGET (name_to_widget (top, "image_text")),
1568                             p->random_image_p);
1569   gtk_widget_set_sensitive (
1570                        GTK_WIDGET (name_to_widget (top,"image_browse_button")),
1571                             p->random_image_p);
1572
1573   gtk_toggle_button_set_active (
1574                    GTK_TOGGLE_BUTTON (name_to_widget (top, "install_button")),
1575                    p->install_cmap_p);
1576   gtk_toggle_button_set_active (
1577                    GTK_TOGGLE_BUTTON (name_to_widget (top, "fade_button")),
1578                    p->fade_p);
1579   gtk_toggle_button_set_active (
1580                    GTK_TOGGLE_BUTTON (name_to_widget (top, "unfade_button")),
1581                    p->unfade_p);
1582
1583
1584   {
1585     Bool found_any_writable_cells = False;
1586     Bool dpms_supported = False;
1587
1588     Display *dpy = GDK_DISPLAY();
1589     int nscreens = ScreenCount(dpy);
1590     int i;
1591     for (i = 0; i < nscreens; i++)
1592       {
1593         Screen *s = ScreenOfDisplay (dpy, i);
1594         if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1595           {
1596             found_any_writable_cells = True;
1597             break;
1598           }
1599       }
1600
1601 #ifdef HAVE_XF86VMODE_GAMMA
1602     found_any_writable_cells = True;  /* if we can gamma fade, go for it */
1603 #endif
1604
1605 #ifdef HAVE_DPMS_EXTENSION
1606     {
1607       int op = 0, event = 0, error = 0;
1608       if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
1609         dpms_supported = True;
1610     }
1611 #endif /* HAVE_DPMS_EXTENSION */
1612
1613
1614     /* Blanking and Locking
1615      */
1616     gtk_widget_set_sensitive (
1617                            GTK_WIDGET (name_to_widget (top, "lock_label")),
1618                            p->lock_p);
1619     gtk_widget_set_sensitive (
1620                            GTK_WIDGET (name_to_widget (top, "lock_text")),
1621                            p->lock_p);
1622
1623     /* DPMS
1624      */
1625     gtk_widget_set_sensitive (
1626                       GTK_WIDGET (name_to_widget (top, "dpms_frame")),
1627                       dpms_supported);
1628     gtk_widget_set_sensitive (
1629                       GTK_WIDGET (name_to_widget (top, "dpms_button")),
1630                       dpms_supported);
1631     gtk_widget_set_sensitive (
1632                        GTK_WIDGET (name_to_widget (top, "dpms_standby_label")),
1633                        dpms_supported && p->dpms_enabled_p);
1634     gtk_widget_set_sensitive (
1635                        GTK_WIDGET (name_to_widget (top, "dpms_standby_text")),
1636                        dpms_supported && p->dpms_enabled_p);
1637     gtk_widget_set_sensitive (
1638                        GTK_WIDGET (name_to_widget (top, "dpms_suspend_label")),
1639                        dpms_supported && p->dpms_enabled_p);
1640     gtk_widget_set_sensitive (
1641                        GTK_WIDGET (name_to_widget (top, "dpms_suspend_text")),
1642                        dpms_supported && p->dpms_enabled_p);
1643     gtk_widget_set_sensitive (
1644                        GTK_WIDGET (name_to_widget (top, "dpms_off_label")),
1645                        dpms_supported && p->dpms_enabled_p);
1646     gtk_widget_set_sensitive (
1647                        GTK_WIDGET (name_to_widget (top, "dpms_off_text")),
1648                        dpms_supported && p->dpms_enabled_p);
1649
1650     /* Colormaps
1651      */
1652     gtk_widget_set_sensitive (
1653                            GTK_WIDGET (name_to_widget (top, "cmap_frame")),
1654                            found_any_writable_cells);
1655     gtk_widget_set_sensitive (
1656                            GTK_WIDGET (name_to_widget (top, "install_button")),
1657                            found_any_writable_cells);
1658     gtk_widget_set_sensitive (
1659                            GTK_WIDGET (name_to_widget (top, "fade_button")),
1660                            found_any_writable_cells);
1661     gtk_widget_set_sensitive (
1662                            GTK_WIDGET (name_to_widget (top, "unfade_button")),
1663                            found_any_writable_cells);
1664
1665     gtk_widget_set_sensitive (
1666                            GTK_WIDGET (name_to_widget (top, "fade_label")),
1667                            (found_any_writable_cells &&
1668                             (p->fade_p || p->unfade_p)));
1669     gtk_widget_set_sensitive (
1670                            GTK_WIDGET (name_to_widget (top, "fade_text")),
1671                            (found_any_writable_cells &&
1672                             (p->fade_p || p->unfade_p)));
1673   }
1674
1675 }
1676
1677
1678 static void
1679 sensitize_demo_widgets (GtkWidget *toplevel, Bool sensitive_p)
1680 {
1681   const char *names[] = { "cmd_label", "cmd_text", "enabled",
1682                           "visual", "visual_combo",
1683                           "demo", "manual" };
1684   int i;
1685   for (i = 0; i < countof(names); i++)
1686     {
1687       GtkWidget *w = name_to_widget (toplevel, names[i]);
1688       gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
1689     }
1690
1691   /* I don't know how to handle these yet... */
1692   {
1693     const char *names2[] = { "cut_menu", "copy_menu", "paste_menu" };
1694     for (i = 0; i < countof(names2); i++)
1695       {
1696         GtkWidget *w = name_to_widget (toplevel, names2[i]);
1697         gtk_widget_set_sensitive (GTK_WIDGET(w), False);
1698       }
1699   }
1700 }
1701
1702
1703 /* Even though we've given these text fields a maximum number of characters,
1704    their default size is still about 30 characters wide -- so measure out
1705    a string in their font, and resize them to just fit that.
1706  */
1707 static void
1708 fix_text_entry_sizes (GtkWidget *toplevel)
1709 {
1710   const char *names[] = { "timeout_text", "cycle_text", "lock_text",
1711                           "dpms_standby_text", "dpms_suspend_text",
1712                           "dpms_off_text", "fade_text" };
1713   int i;
1714   int width = 0;
1715   GtkWidget *w;
1716
1717   for (i = 0; i < countof(names); i++)
1718     {
1719       w = GTK_WIDGET (name_to_widget (toplevel, names[i]));
1720       if (width == 0)
1721         width = gdk_text_width (w->style->font, "00:00:00_", 9);
1722       gtk_widget_set_usize (w, width, -2);
1723     }
1724
1725   /* Now fix the size of the combo box.
1726    */
1727   w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "visual_combo"));
1728   w = GTK_COMBO (w)->entry;
1729   width = gdk_text_width (w->style->font, "PseudoColor___", 14);
1730   gtk_widget_set_usize (w, width, -2);
1731
1732   /* Now fix the size of the file entry text.
1733    */
1734   w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "image_text"));
1735   width = gdk_text_width (w->style->font, "MMMMMMMMMMMMMM", 14);
1736   gtk_widget_set_usize (w, width, -2);
1737
1738 #if 0
1739   /* Now fix the size of the list.
1740    */
1741   w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "list"));
1742   width = gdk_text_width (w->style->font, "nnnnnnnnnnnnnnnnnnnnnn", 22);
1743   gtk_widget_set_usize (w, width, -2);
1744 #endif
1745 }
1746
1747
1748
1749 \f
1750 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
1751  */
1752
1753 static char *up_arrow_xpm[] = {
1754   "15 15 4 1",
1755   "     c None",
1756   "-    c #FFFFFF",
1757   "+    c #D6D6D6",
1758   "@    c #000000",
1759
1760   "       @       ",
1761   "       @       ",
1762   "      -+@      ",
1763   "      -+@      ",
1764   "     -+++@     ",
1765   "     -+++@     ",
1766   "    -+++++@    ",
1767   "    -+++++@    ",
1768   "   -+++++++@   ",
1769   "   -+++++++@   ",
1770   "  -+++++++++@  ",
1771   "  -+++++++++@  ",
1772   " -+++++++++++@ ",
1773   " @@@@@@@@@@@@@ ",
1774   "               ",
1775
1776   /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1777      the end of the array (Gtk 1.2.5.) */
1778   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1779   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1780 };
1781
1782 static char *down_arrow_xpm[] = {
1783   "15 15 4 1",
1784   "     c None",
1785   "-    c #FFFFFF",
1786   "+    c #D6D6D6",
1787   "@    c #000000",
1788
1789   "               ",
1790   " ------------- ",
1791   " -+++++++++++@ ",
1792   "  -+++++++++@  ",
1793   "  -+++++++++@  ",
1794   "   -+++++++@   ",
1795   "   -+++++++@   ",
1796   "    -+++++@    ",
1797   "    -+++++@    ",
1798   "     -+++@     ",
1799   "     -+++@     ",
1800   "      -+@      ",
1801   "      -+@      ",
1802   "       @       ",
1803   "       @       ",
1804
1805   /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1806      the end of the array (Gtk 1.2.5.) */
1807   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1808   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1809 };
1810
1811 static void
1812 pixmapify_button (GtkWidget *toplevel, int down_p)
1813 {
1814   GdkPixmap *pixmap;
1815   GdkBitmap *mask;
1816   GtkWidget *pixmapwid;
1817   GtkStyle *style;
1818   GtkWidget *w;
1819
1820   w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel),
1821                                   (down_p ? "next" : "prev")));
1822   style = gtk_widget_get_style (w);
1823   mask = 0;
1824   pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
1825                                          &style->bg[GTK_STATE_NORMAL],
1826                                          (down_p
1827                                           ? (gchar **) down_arrow_xpm
1828                                           : (gchar **) up_arrow_xpm));
1829   pixmapwid = gtk_pixmap_new (pixmap, mask);
1830   gtk_widget_show (pixmapwid);
1831   gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
1832   gtk_container_add (GTK_CONTAINER (w), pixmapwid);
1833 }
1834
1835 static void
1836 map_next_button_cb (GtkWidget *w, gpointer user_data)
1837 {
1838   pixmapify_button (w, 1);
1839 }
1840
1841 static void
1842 map_prev_button_cb (GtkWidget *w, gpointer user_data)
1843 {
1844   pixmapify_button (w, 0);
1845 }
1846
1847
1848 \f
1849 /* Work around a Gtk bug that causes label widgets to wrap text too early.
1850  */
1851
1852 static void
1853 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
1854                                              GtkAllocation *allocation,
1855                                              void *foo)
1856 {
1857   GtkRequisition req;
1858   GtkWidgetAuxInfo *aux_info;
1859
1860   aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
1861
1862   aux_info->width = allocation->width;
1863   aux_info->height = -2;
1864   aux_info->x = -1;
1865   aux_info->y = -1;
1866
1867   gtk_widget_size_request (label, &req);
1868 }
1869
1870
1871 /* Feel the love.  Thanks to Nat Friedman for finding this workaround.
1872  */
1873 static void
1874 eschew_gtk_lossage (GtkWidget *toplevel)
1875 {
1876   GtkWidgetAuxInfo *aux_info;
1877   GtkWidget *label = GTK_WIDGET (name_to_widget (toplevel, "doc"));
1878
1879   aux_info = g_new0 (GtkWidgetAuxInfo, 1);
1880   aux_info->width = label->allocation.width;
1881   aux_info->height = -2;
1882   aux_info->x = -1;
1883   aux_info->y = -1;
1884
1885   gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
1886
1887   gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
1888                       you_are_not_a_unique_or_beautiful_snowflake, NULL);
1889
1890   gtk_widget_queue_resize (label); 
1891 }
1892
1893
1894 char *
1895 get_hack_blurb (screenhack *hack)
1896 {
1897   char *doc_string;
1898   char *prog_name = strdup (hack->command);
1899   char *pretty_name = (hack->name
1900                        ? strdup (hack->name)
1901                        : make_hack_name (hack->command));
1902   char doc_name[255], doc_class[255];
1903   char *s, *s2;
1904
1905   for (s = prog_name; *s && !isspace(*s); s++)
1906     ;
1907   *s = 0;
1908   s = strrchr (prog_name, '/');
1909   if (s) strcpy (prog_name, s+1);
1910
1911   sprintf (doc_name,  "hacks.%s.documentation", pretty_name);
1912   sprintf (doc_class, "hacks.%s.documentation", prog_name);
1913   free (prog_name);
1914   free (pretty_name);
1915
1916   doc_string = get_string_resource (doc_name, doc_class);
1917   if (doc_string)
1918     {
1919       for (s = doc_string; *s; s++)
1920         {
1921           if (*s == '\n')
1922             {
1923               /* skip over whitespace at beginning of line */
1924               s++;
1925               while (*s && (*s == ' ' || *s == '\t'))
1926                 s++;
1927             }
1928           else if (*s == ' ' || *s == '\t')
1929             {
1930               /* compress all other horizontal whitespace. */
1931               *s = ' ';
1932               s++;
1933               for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++)
1934                 ;
1935               if (s2 > s) strcpy (s, s2);
1936               s--;
1937             }
1938         }
1939
1940       while (*s && isspace (*s))      /* Strip trailing whitespace */
1941         *(--s) = 0;
1942
1943       /* Delete whitespace at end of each line. */
1944       for (; s > doc_string; s--)
1945         if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t'))
1946           {
1947             for (s2 = s-1;
1948                  s2 > doc_string && (*s2 == ' ' || *s2 == '\t');
1949                  s2--)
1950               ;
1951             s2++;
1952             if (s2 < s) strcpy (s2, s);
1953             s = s2;
1954           }
1955       
1956       /* Delete leading blank lines. */
1957       for (s = doc_string; *s == '\n'; s++)
1958         ;
1959       if (s > doc_string) strcpy (doc_string, s);
1960     }
1961   else
1962     {
1963       static int doc_installed = 0;
1964       if (doc_installed == 0)
1965         {
1966           if (get_boolean_resource ("hacks.documentation.isInstalled",
1967                                     "hacks.documentation.isInstalled"))
1968             doc_installed = 1;
1969           else
1970             doc_installed = -1;
1971         }
1972
1973       if (doc_installed < 0)
1974         doc_string =
1975           strdup ("Error:\n\n"
1976                   "The documentation strings do not appear to be "
1977                   "installed.  This is probably because there is "
1978                   "an \"XScreenSaver\" app-defaults file installed "
1979                   "that is from an older version of the program. "
1980                   "To fix this problem, delete that file, or "
1981                   "install a current version (either will work.)");
1982       else
1983         doc_string = strdup ("");
1984     }
1985
1986   return doc_string;
1987 }
1988
1989
1990 static void
1991 populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair)
1992 {
1993   saver_preferences *p = pair->a;
1994   screenhack *hack = (which >= 0 && which < p->screenhacks_count
1995                       ? p->screenhacks[which] : 0);
1996   GtkFrame *frame = GTK_FRAME (name_to_widget (toplevel, "frame"));
1997   GtkLabel *doc = GTK_LABEL (name_to_widget (toplevel, "doc"));
1998   GtkEntry *cmd = GTK_ENTRY (name_to_widget (toplevel, "cmd_text"));
1999   GtkToggleButton *enabled =
2000     GTK_TOGGLE_BUTTON (name_to_widget (toplevel, "enabled"));
2001   GtkCombo *vis = GTK_COMBO (name_to_widget (toplevel, "visual_combo"));
2002
2003   char *pretty_name = (hack
2004                        ? (hack->name
2005                           ? strdup (hack->name)
2006                           : make_hack_name (hack->command))
2007                        : 0);
2008   char *doc_string = hack ? get_hack_blurb (hack) : 0;
2009
2010   gtk_frame_set_label (frame, (pretty_name ? pretty_name : ""));
2011   gtk_label_set_text (doc, (doc_string ? doc_string : ""));
2012   gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2013   gtk_entry_set_position (cmd, 0);
2014
2015   updating_enabled_cb++;
2016   gtk_toggle_button_set_active (enabled, (hack ? hack->enabled_p : False));
2017   updating_enabled_cb--;
2018
2019   gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2020                       (hack
2021                        ? (hack->visual && *hack->visual
2022                           ? hack->visual
2023                           : "Any")
2024                        : ""));
2025
2026   gtk_container_resize_children (GTK_CONTAINER (GTK_WIDGET (doc)->parent));
2027
2028   sensitize_demo_widgets (toplevel, (hack ? True : False));
2029
2030   if (pretty_name) free (pretty_name);
2031   if (doc_string) free (doc_string);
2032
2033   _selected_hack_number = which;
2034 }
2035
2036
2037 static void
2038 widget_deleter (GtkWidget *widget, gpointer data)
2039 {
2040   /* #### Well, I want to destroy these widgets, but if I do that, they get
2041      referenced again, and eventually I get a SEGV.  So instead of
2042      destroying them, I'll just hide them, and leak a bunch of memory
2043      every time the disk file changes.  Go go go Gtk!
2044
2045      #### Ok, that's a lie, I get a crash even if I just hide the widget
2046      and don't ever delete it.  Fuck!
2047    */
2048 #if 0
2049   gtk_widget_destroy (widget);
2050 #else
2051   gtk_widget_hide (widget);
2052 #endif
2053 }
2054
2055
2056 static int
2057 maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair)
2058 {
2059   int status = 0;
2060   saver_preferences *p =  pair->a;
2061
2062   static Bool reentrant_lock = False;
2063   if (reentrant_lock) return 0;
2064   reentrant_lock = True;
2065
2066   if (init_file_changed_p (p))
2067     {
2068       const char *f = init_file_name();
2069       char *b;
2070       int which;
2071       GtkList *list;
2072
2073       if (!f || !*f) return 0;
2074       b = (char *) malloc (strlen(f) + 1024);
2075       sprintf (b,
2076                "Warning:\n\n"
2077                "file \"%s\" has changed, reloading.\n",
2078                f);
2079       warning_dialog (widget, b, False, 100);
2080       free (b);
2081
2082       load_init_file (p);
2083
2084       which = selected_hack_number (widget);
2085       list = GTK_LIST (name_to_widget (widget, "list"));
2086       gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
2087       populate_hack_list (widget, pair);
2088       gtk_list_select_item (list, which);
2089       populate_prefs_page (widget, pair);
2090       populate_demo_window (widget, which, pair);
2091       ensure_selected_item_visible (GTK_WIDGET (list));
2092
2093       status = 1;
2094     }
2095
2096   reentrant_lock = False;
2097   return status;
2098 }
2099
2100
2101 \f
2102 /* Setting window manager icon
2103  */
2104
2105 static void
2106 init_icon (GdkWindow *window)
2107 {
2108   GdkBitmap *mask = 0;
2109   GdkColor transp;
2110   GdkPixmap *pixmap =
2111     gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
2112                                   (gchar **) logo_50_xpm);
2113   if (pixmap)
2114     gdk_window_set_icon (window, 0, pixmap, mask);
2115 }
2116
2117 \f
2118 /* The main demo-mode command loop.
2119  */
2120
2121 #if 0
2122 static Bool
2123 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
2124         XrmRepresentation *type, XrmValue *value, XPointer closure)
2125 {
2126   int i;
2127   for (i = 0; quarks[i]; i++)
2128     {
2129       if (bindings[i] == XrmBindTightly)
2130         fprintf (stderr, (i == 0 ? "" : "."));
2131       else if (bindings[i] == XrmBindLoosely)
2132         fprintf (stderr, "*");
2133       else
2134         fprintf (stderr, " ??? ");
2135       fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
2136     }
2137
2138   fprintf (stderr, ": %s\n", (char *) value->addr);
2139
2140   return False;
2141 }
2142 #endif
2143
2144
2145 static void
2146 the_network_is_not_the_computer (GtkWidget *parent)
2147 {
2148   Display *dpy = GDK_DISPLAY();
2149   char *rversion, *ruser, *rhost;
2150   char *luser, *lhost;
2151   char *msg = 0;
2152   struct passwd *p = getpwuid (getuid ());
2153   const char *d = DisplayString (dpy);
2154
2155 # if defined(HAVE_UNAME)
2156   struct utsname uts;
2157   if (uname (&uts) < 0)
2158     lhost = "<UNKNOWN>";
2159   else
2160     lhost = uts.nodename;
2161 # elif defined(VMS)
2162   strcpy (lhost, getenv("SYS$NODE"));
2163 # else  /* !HAVE_UNAME && !VMS */
2164   strcat (lhost, "<UNKNOWN>");
2165 # endif /* !HAVE_UNAME && !VMS */
2166
2167   if (p && p->pw_name)
2168     luser = p->pw_name;
2169   else
2170     luser = "???";
2171
2172   server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
2173
2174   /* Make a buffer that's big enough for a number of copies of all the
2175      strings, plus some. */
2176   msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
2177                                (ruser ? strlen(ruser) : 0) +
2178                                (rhost ? strlen(rhost) : 0) +
2179                                strlen(lhost) +
2180                                strlen(luser) +
2181                                strlen(d) +
2182                                1024));
2183   *msg = 0;
2184
2185   if (!rversion || !*rversion)
2186     {
2187       sprintf (msg,
2188                "Warning:\n\n"
2189                "The XScreenSaver daemon doesn't seem to be running\n"
2190                "on display \"%s\".  Launch it now?",
2191                d);
2192     }
2193   else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
2194     {
2195       /* Warn that the two processes are running as different users.
2196        */
2197       sprintf(msg,
2198                "Warning:\n\n"
2199               "%s is running as user \"%s\" on host \"%s\".\n"
2200               "But the xscreensaver managing display \"%s\"\n"
2201               "is running as user \"%s\" on host \"%s\".\n"
2202               "\n"
2203               "Since they are different users, they won't be reading/writing\n"
2204               "the same ~/.xscreensaver file, so %s isn't\n"
2205               "going to work right.\n"
2206               "\n"
2207               "You should either re-run %s as \"%s\", or re-run\n"
2208               "xscreensaver as \"%s\".\n"
2209               "\n"
2210               "Restart the xscreensaver daemon now?\n",
2211               progname, luser, lhost,
2212               d,
2213               (ruser ? ruser : "???"), (rhost ? rhost : "???"),
2214               progname,
2215               progname, (ruser ? ruser : "???"),
2216               luser);
2217     }
2218   else if (rhost && *rhost && !!strcmp (rhost, lhost))
2219     {
2220       /* Warn that the two processes are running on different hosts.
2221        */
2222       sprintf (msg,
2223                "Warning:\n\n"
2224                "%s is running as user \"%s\" on host \"%s\".\n"
2225                "But the xscreensaver managing display \"%s\"\n"
2226                "is running as user \"%s\" on host \"%s\".\n"
2227                "\n"
2228                "If those two machines don't share a file system (that is,\n"
2229                "if they don't see the same ~%s/.xscreensaver file) then\n"
2230                "%s won't work right.\n"
2231                "\n"
2232                "Restart the daemon on \"%s\" as \"%s\" now?\n",
2233                progname, luser, lhost,
2234                d,
2235                (ruser ? ruser : "???"), (rhost ? rhost : "???"),
2236                luser,
2237                progname,
2238                lhost, luser);
2239     }
2240   else if (!!strcmp (rversion, short_version))
2241     {
2242       /* Warn that the version numbers don't match.
2243        */
2244       sprintf (msg,
2245                "Warning:\n\n"
2246                "This is %s version %s.\n"
2247                "But the xscreensaver managing display \"%s\"\n"
2248                "is version %s.  This could cause problems.\n"
2249               "\n"
2250               "Restart the xscreensaver daemon now?\n",
2251                progname, short_version,
2252                d,
2253                rversion);
2254     }
2255
2256
2257   if (*msg)
2258     warning_dialog (parent, msg, True, 1);
2259
2260   free (msg);
2261 }
2262
2263
2264 /* We use this error handler so that X errors are preceeded by the name
2265    of the program that generated them.
2266  */
2267 static int
2268 demo_ehandler (Display *dpy, XErrorEvent *error)
2269 {
2270   fprintf (stderr, "\nX error in %s:\n", progname);
2271   if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
2272     exit (-1);
2273   else
2274     fprintf (stderr, " (nonfatal.)\n");
2275   return 0;
2276 }
2277
2278
2279 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
2280    of the program that generated them; and also that we can ignore one
2281    particular bogus error message that Gdk madly spews.
2282  */
2283 static void
2284 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
2285                const gchar *message, gpointer user_data)
2286 {
2287   /* Ignore the message "Got event for unknown window: 0x...".
2288      Apparently some events are coming in for the xscreensaver window
2289      (presumably reply events related to the ClientMessage) and Gdk
2290      feels the need to complain about them.  So, just suppress any
2291      messages that look like that one.
2292    */
2293   if (strstr (message, "unknown window"))
2294     return;
2295
2296   fprintf (stderr, "%s: %s-%s: %s%s", blurb(), log_domain,
2297            (log_level == G_LOG_LEVEL_ERROR    ? "error" :
2298             log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
2299             log_level == G_LOG_LEVEL_WARNING  ? "warning" :
2300             log_level == G_LOG_LEVEL_MESSAGE  ? "message" :
2301             log_level == G_LOG_LEVEL_INFO     ? "info" :
2302             log_level == G_LOG_LEVEL_DEBUG    ? "debug" : "???"),
2303            message,
2304            ((!*message || message[strlen(message)-1] != '\n')
2305             ? "\n" : ""));
2306 }
2307
2308
2309 static char *defaults[] = {
2310 #include "XScreenSaver_ad.h"
2311  0
2312 };
2313
2314 #if 0
2315 #ifdef HAVE_CRAPPLET
2316 static struct poptOption crapplet_options[] = {
2317   {NULL, '\0', 0, NULL, 0}
2318 };
2319 #endif /* HAVE_CRAPPLET */
2320 #endif /* 0 */
2321
2322 #define USAGE() \
2323   fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n", \
2324            real_progname)
2325
2326
2327 static void
2328 map_window_cb (GtkWidget *w, gpointer user_data)
2329 {
2330   Boolean oi = initializing_p;
2331   initializing_p = True;
2332   eschew_gtk_lossage (w);
2333   ensure_selected_item_visible (GTK_WIDGET(name_to_widget(w, "list")));
2334   initializing_p = oi;
2335 }
2336
2337
2338 int
2339 main (int argc, char **argv)
2340 {
2341   XtAppContext app;
2342   prefs_pair Pair, *pair;
2343   saver_preferences P, P2, *p, *p2;
2344   Bool prefs = False;
2345   int i;
2346   Display *dpy;
2347   Widget toplevel_shell;
2348   GtkWidget *gtk_window;
2349   char *real_progname = argv[0];
2350   char *s;
2351
2352   initializing_p = True;
2353
2354   s = strrchr (real_progname, '/');
2355   if (s) real_progname = s+1;
2356
2357   p = &P;
2358   p2 = &P2;
2359   pair = &Pair;
2360   pair->a = p;
2361   pair->b = p2;
2362   memset (p,  0, sizeof (*p));
2363   memset (p2, 0, sizeof (*p2));
2364
2365   global_prefs_pair = pair;  /* I hate C so much... */
2366
2367   progname = real_progname;
2368
2369   short_version = (char *) malloc (5);
2370   memcpy (short_version, screensaver_id + 17, 4);
2371   short_version [4] = 0;
2372
2373
2374   /* Register our error message logger for every ``log domain'' known.
2375      There's no way to do this globally, so I grepped the Gtk/Gdk sources
2376      for all of the domains that seem to be in use.
2377   */
2378   {
2379     const char * const domains[] = { "Gtk", "Gdk", "GLib", "GModule",
2380                                      "GThread", "Gnome", "GnomeUI", 0 };
2381     for (i = 0; domains[i]; i++)
2382       g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
2383   }
2384
2385   /* This is gross, but Gtk understands --display and not -display...
2386    */
2387   for (i = 1; i < argc; i++)
2388     if (argv[i][0] && argv[i][1] && 
2389         !strncmp(argv[i], "-display", strlen(argv[i])))
2390       argv[i] = "--display";
2391
2392
2393   /* We need to parse this arg really early... Sigh. */
2394   for (i = 1; i < argc; i++)
2395     if (argv[i] &&
2396         (!strcmp(argv[i], "--crapplet") ||
2397          !strcmp(argv[i], "--capplet")))
2398       {
2399 # ifdef HAVE_CRAPPLET
2400         int j;
2401         crapplet_p = True;
2402         for (j = i; j < argc; j++)  /* remove it from the list */
2403           argv[j] = argv[j+1];
2404         argc--;
2405
2406 # else  /* !HAVE_CRAPPLET */
2407         fprintf (stderr, "%s: not compiled with --crapplet support\n",
2408                  real_progname);
2409         USAGE ();
2410         exit (1);
2411 # endif /* !HAVE_CRAPPLET */
2412       }
2413
2414   /* Let Gtk open the X connection, then initialize Xt to use that
2415      same connection.  Doctor Frankenstein would be proud.
2416    */
2417 # ifdef HAVE_CRAPPLET
2418   if (crapplet_p)
2419     {
2420       GnomeClient *client;
2421       GnomeClientFlags flags = 0;
2422
2423       int init_results = gnome_capplet_init ("screensaver-properties",
2424                                              short_version,
2425                                              argc, argv, NULL, 0, NULL);
2426       /* init_results is:
2427          0 upon successful initialization;
2428          1 if --init-session-settings was passed on the cmdline;
2429          2 if --ignore was passed on the cmdline;
2430         -1 on error.
2431
2432          So the 1 signifies just to init the settings, and quit, basically.
2433          (Meaning launch the xscreensaver daemon.)
2434        */
2435
2436       if (init_results < 0)
2437         {
2438 #  if 0
2439           g_error ("An initialization error occurred while "
2440                    "starting xscreensaver-capplet.\n");
2441 #  else  /* !0 */
2442           fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
2443                    real_progname, init_results);
2444           exit (1);
2445 #  endif /* !0 */
2446         }
2447
2448       client = gnome_master_client ();
2449
2450       if (client)
2451         flags = gnome_client_get_flags (client);
2452
2453       if (flags & GNOME_CLIENT_IS_CONNECTED)
2454         {
2455           int token =
2456             gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
2457                                          gnome_client_get_id (client));
2458           if (token)
2459             {
2460               char *session_args[20];
2461               int i = 0;
2462               session_args[i++] = real_progname;
2463               session_args[i++] = "--capplet";
2464               session_args[i++] = "--init-session-settings";
2465               session_args[i] = 0;
2466               gnome_client_set_priority (client, 20);
2467               gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
2468               gnome_client_set_restart_command (client, i, session_args);
2469             }
2470           else
2471             {
2472               gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
2473             }
2474
2475           gnome_client_flush (client);
2476         }
2477
2478       if (init_results == 1)
2479         {
2480           system ("xscreensaver -nosplash &");
2481           return 0;
2482         }
2483
2484     }
2485   else
2486 # endif /* HAVE_CRAPPLET */
2487     {
2488       gtk_init (&argc, &argv);
2489     }
2490
2491
2492   /* We must read exactly the same resources as xscreensaver.
2493      That means we must have both the same progclass *and* progname,
2494      at least as far as the resource database is concerned.  So,
2495      put "xscreensaver" in argv[0] while initializing Xt.
2496    */
2497   argv[0] = "xscreensaver";
2498   progname = argv[0];
2499
2500
2501   /* Teach Xt to use the Display that Gtk/Gdk have already opened.
2502    */
2503   XtToolkitInitialize ();
2504   app = XtCreateApplicationContext ();
2505   dpy = GDK_DISPLAY();
2506   XtAppSetFallbackResources (app, defaults);
2507   XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
2508   toplevel_shell = XtAppCreateShell (progname, progclass,
2509                                      applicationShellWidgetClass,
2510                                      dpy, 0, 0);
2511
2512   dpy = XtDisplay (toplevel_shell);
2513   db = XtDatabase (dpy);
2514   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
2515   XSetErrorHandler (demo_ehandler);
2516
2517
2518   /* After doing Xt-style command-line processing, complain about any
2519      unrecognized command-line arguments.
2520    */
2521   for (i = 1; i < argc; i++)
2522     {
2523       char *s = argv[i];
2524       if (s[0] == '-' && s[1] == '-')
2525         s++;
2526       if (!strcmp (s, "-prefs"))
2527         prefs = True;
2528       else if (crapplet_p)
2529         /* There are lots of random args that we don't care about when we're
2530            started as a crapplet, so just ignore unknown args in that case. */
2531         ;
2532       else
2533         {
2534           fprintf (stderr, "%s: unknown option: %s\n", real_progname, argv[i]);
2535           USAGE ();
2536           exit (1);
2537         }
2538     }
2539
2540   /* Load the init file, which may end up consulting the X resource database
2541      and the site-wide app-defaults file.  Note that at this point, it's
2542      important that `progname' be "xscreensaver", rather than whatever
2543      was in argv[0].
2544    */
2545   p->db = db;
2546   load_init_file (p);
2547   *p2 = *p;
2548
2549   /* Now that Xt has been initialized, and the resources have been read,
2550      we can set our `progname' variable to something more in line with
2551      reality.
2552    */
2553   progname = real_progname;
2554
2555
2556 #if 0
2557   /* Print out all the resources we read. */
2558   {
2559     XrmName name = { 0 };
2560     XrmClass class = { 0 };
2561     int count = 0;
2562     XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
2563                           (POINTER) &count);
2564   }
2565 #endif
2566
2567
2568   /* Intern the atoms that xscreensaver_command() needs.
2569    */
2570   XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
2571   XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
2572   XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
2573   XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
2574   XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
2575   XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
2576   XA_SELECT = XInternAtom (dpy, "SELECT", False);
2577   XA_DEMO = XInternAtom (dpy, "DEMO", False);
2578   XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
2579   XA_BLANK = XInternAtom (dpy, "BLANK", False);
2580   XA_LOCK = XInternAtom (dpy, "LOCK", False);
2581   XA_EXIT = XInternAtom (dpy, "EXIT", False);
2582   XA_RESTART = XInternAtom (dpy, "RESTART", False);
2583
2584
2585   /* Create the window and all its widgets.
2586    */
2587   gtk_window = create_xscreensaver_demo ();
2588   toplevel_widget = gtk_window;
2589
2590   /* Set the window's title. */
2591   {
2592     char title[255];
2593     char *v = (char *) strdup(strchr(screensaver_id, ' '));
2594     char *s1, *s2, *s3, *s4;
2595     s1 = (char *) strchr(v,  ' '); s1++;
2596     s2 = (char *) strchr(s1, ' ');
2597     s3 = (char *) strchr(v,  '('); s3++;
2598     s4 = (char *) strchr(s3, ')');
2599     *s2 = 0;
2600     *s4 = 0;
2601     sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3);
2602     gtk_window_set_title (GTK_WINDOW (gtk_window), title);
2603     free (v);
2604   }
2605
2606   /* Various other widget initializations...
2607    */
2608   gtk_signal_connect (GTK_OBJECT (gtk_window), "delete_event",
2609                       GTK_SIGNAL_FUNC (wm_close_cb), NULL);
2610
2611   populate_hack_list (gtk_window, pair);
2612   populate_prefs_page (gtk_window, pair);
2613   sensitize_demo_widgets (gtk_window, False);
2614   fix_text_entry_sizes (gtk_window);
2615   scroll_to_current_hack (gtk_window, pair);
2616
2617   gtk_signal_connect (
2618               GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "list")),
2619               "map", GTK_SIGNAL_FUNC(map_window_cb), 0);
2620   gtk_signal_connect (
2621               GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "prev")),
2622               "map", GTK_SIGNAL_FUNC(map_prev_button_cb), 0);
2623   gtk_signal_connect (
2624               GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "next")),
2625               "map", GTK_SIGNAL_FUNC(map_next_button_cb), 0);
2626
2627
2628   /* Handle the -prefs command-line argument. */
2629   if (prefs)
2630     {
2631       GtkNotebook *notebook =
2632         GTK_NOTEBOOK (name_to_widget (gtk_window, "notebook"));
2633       gtk_notebook_set_page (notebook, 1);
2634     }
2635
2636 # ifdef HAVE_CRAPPLET
2637   if (crapplet_p)
2638     {
2639       GtkWidget *capplet;
2640       GtkWidget *top_vbox;
2641
2642       capplet = capplet_widget_new ();
2643
2644       top_vbox = GTK_BIN (gtk_window)->child;
2645
2646       gtk_widget_ref (top_vbox);
2647       gtk_container_remove (GTK_CONTAINER (gtk_window), top_vbox);
2648       GTK_OBJECT_SET_FLAGS (top_vbox, GTK_FLOATING);
2649
2650       /* In crapplet-mode, take off the menubar. */
2651       gtk_widget_hide (name_to_widget (gtk_window, "menubar"));
2652
2653       gtk_container_add (GTK_CONTAINER (capplet), top_vbox);
2654       gtk_widget_show (capplet);
2655       gtk_widget_hide (gtk_window);
2656
2657       /* Hook up the Control Center's redundant Help button, too. */
2658       gtk_signal_connect (GTK_OBJECT (capplet), "help",
2659                           GTK_SIGNAL_FUNC (doc_menu_cb), 0);
2660
2661       /* Issue any warnings about the running xscreensaver daemon. */
2662       the_network_is_not_the_computer (top_vbox);
2663     }
2664   else
2665 # endif /* HAVE_CRAPPLET */
2666     {
2667       gtk_widget_show (gtk_window);
2668       init_icon (GTK_WIDGET(gtk_window)->window);
2669
2670       /* Issue any warnings about the running xscreensaver daemon. */
2671       the_network_is_not_the_computer (gtk_window);
2672     }
2673
2674   /* Run the Gtk event loop, and not the Xt event loop.  This means that
2675      if there were Xt timers or fds registered, they would never get serviced,
2676      and if there were any Xt widgets, they would never have events delivered.
2677      Fortunately, we're using Gtk for all of the UI, and only initialized
2678      Xt so that we could process the command line and use the X resource
2679      manager.
2680    */
2681   initializing_p = False;
2682
2683 # ifdef HAVE_CRAPPLET
2684   if (crapplet_p)
2685     capplet_gtk_main ();
2686   else
2687 # endif /* HAVE_CRAPPLET */
2688     gtk_main ();
2689
2690   exit (0);
2691 }
2692
2693 #endif /* HAVE_GTK -- whole file */