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