http://ftp.x.org/contrib/applications/xscreensaver-3.24.tar.gz
[xscreensaver] / driver / demo-Gtk.c
1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2  * xscreensaver, Copyright (c) 1993-1999 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
37 #include <X11/Xproto.h>         /* for CARD32 */
38 #include <X11/Xatom.h>          /* for XA_INTEGER */
39 #include <X11/Intrinsic.h>
40 #include <X11/StringDefs.h>
41
42 /* We don't actually use any widget internals, but these are included
43    so that gdb will have debug info for the widgets... */
44 #include <X11/IntrinsicP.h>
45 #include <X11/ShellP.h>
46
47 #ifdef HAVE_XMU
48 # ifndef VMS
49 #  include <X11/Xmu/Error.h>
50 # else /* VMS */
51 #  include <Xmu/Error.h>
52 # endif
53 #else
54 # include "xmu.h"
55 #endif
56
57 #include <gtk/gtk.h>
58
59 #ifdef HAVE_CRAPPLET
60 # include <gnome.h>
61 # include <capplet-widget.h>
62 #endif
63
64 extern Display *gdk_display;
65
66 #include "version.h"
67 #include "prefs.h"
68 #include "resources.h"          /* for parse_time() */
69 #include "visual.h"             /* for has_writable_cells() */
70 #include "remote.h"             /* for xscreensaver_command() */
71 #include "usleep.h"
72
73 #include "demo-Gtk-widgets.h"
74
75 #include <stdio.h>
76 #include <string.h>
77 #include <ctype.h>
78
79 #undef countof
80 #define countof(x) (sizeof((x))/sizeof((*x)))
81
82
83 char *progname = 0;
84 char *progclass = "XScreenSaver";
85 XrmDatabase db;
86
87 static Bool crapplet_p = False;
88 static Bool initializing_p;
89 static GtkWidget *toplevel_widget;
90
91 typedef struct {
92   saver_preferences *a, *b;
93 } prefs_pair;
94
95 static void *global_prefs_pair;  /* I hate C so much... */
96
97 char *blurb (void) { return progname; }
98
99 static char *short_version = 0;
100
101 Atom XA_VROOT;
102 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
103 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
104 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
105
106
107 static void populate_demo_window (GtkWidget *toplevel,
108                                   int which, prefs_pair *pair);
109 static void populate_prefs_page (GtkWidget *top, prefs_pair *pair);
110 static int apply_changes_and_save (GtkWidget *widget);
111 static int maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair);
112
113 \f
114 /* Some random utility functions
115  */
116
117 static GtkWidget *
118 name_to_widget (GtkWidget *widget, const char *name)
119 {
120   return (GtkWidget *) gtk_object_get_data (GTK_OBJECT(toplevel_widget), name);
121 }
122
123
124 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
125    Takes a scroller, viewport, or list as an argument.
126  */
127 static void
128 ensure_selected_item_visible (GtkWidget *widget)
129 {
130   GtkScrolledWindow *scroller = 0;
131   GtkViewport *vp = 0;
132   GtkList *list_widget = 0;
133   GList *slist;
134   GList *kids;
135   int nkids = 0;
136   GtkWidget *selected = 0;
137   int which = -1;
138   GtkAdjustment *adj;
139   gint parent_h, child_y, child_h, children_h, ignore;
140   double ratio_t, ratio_b;
141
142   if (GTK_IS_SCROLLED_WINDOW (widget))
143     {
144       scroller = GTK_SCROLLED_WINDOW (widget);
145       vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
146       list_widget = GTK_LIST (GTK_BIN(vp)->child);
147     }
148   else if (GTK_IS_VIEWPORT (widget))
149     {
150       vp = GTK_VIEWPORT (widget);
151       scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
152       list_widget = GTK_LIST (GTK_BIN(vp)->child);
153     }
154   else if (GTK_IS_LIST (widget))
155     {
156       list_widget = GTK_LIST (widget);
157       vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
158       scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
159     }
160   else
161     abort();
162
163   slist = list_widget->selection;
164   selected = (slist ? GTK_WIDGET (slist->data) : 0);
165   if (!selected)
166     return;
167
168   which = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
169
170   for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
171        kids; kids = kids->next)
172     nkids++;
173
174   adj = gtk_scrolled_window_get_vadjustment (scroller);                        
175
176   gdk_window_get_geometry (GTK_WIDGET(vp)->window,
177                            &ignore, &ignore, &ignore, &parent_h, &ignore);
178   gdk_window_get_geometry (GTK_WIDGET(selected)->window,
179                            &ignore, &child_y, &ignore, &child_h, &ignore);
180   children_h = nkids * child_h;
181
182   ratio_t = ((double) child_y) / ((double) children_h);
183   ratio_b = ((double) child_y + child_h) / ((double) children_h);
184
185   if (adj->upper == 0.0)  /* no items in list */
186     return;
187
188   if (ratio_t < (adj->value / adj->upper) ||
189       ratio_b > ((adj->value + adj->page_size) / adj->upper))
190     {
191       double target;
192       int slop = parent_h * 0.75; /* how much to overshoot by */
193
194       if (ratio_t < (adj->value / adj->upper))
195         {
196           double ratio_w = ((double) parent_h) / ((double) children_h);
197           double ratio_l = (ratio_b - ratio_t);
198           target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
199           target += slop;
200         }
201       else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
202         {
203           target = ratio_t * adj->upper;
204           target -= slop;
205         }
206
207       if (target > adj->upper - adj->page_size)
208         target = adj->upper - adj->page_size;
209       if (target < 0)
210         target = 0;
211
212       gtk_adjustment_set_value (adj, target);
213     }
214 }
215
216 static void
217 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
218 {
219   GtkWidget *shell = GTK_WIDGET (user_data);
220   while (shell->parent)
221     shell = shell->parent;
222   gtk_widget_destroy (GTK_WIDGET (shell));
223 }
224
225
226 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
227
228 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
229 {
230   restart_menu_cb (widget, user_data);
231   warning_dialog_dismiss_cb (widget, user_data);
232 }
233
234 static void
235 warning_dialog (GtkWidget *parent, const char *message,
236                 Boolean restart_button_p, int center)
237 {
238   char *msg = strdup (message);
239   char *head;
240
241   GtkWidget *dialog = gtk_dialog_new ();
242   GtkWidget *label = 0;
243   GtkWidget *ok = 0;
244   GtkWidget *cancel = 0;
245   int i = 0;
246
247   while (parent->parent)
248     parent = parent->parent;
249
250   head = msg;
251   while (head)
252     {
253       char name[20];
254       char *s = strchr (head, '\n');
255       if (s) *s = 0;
256
257       sprintf (name, "label%d", i++);
258
259       {
260         char buf[255];
261         label = gtk_label_new (head);
262         sprintf (buf, "warning_dialog.%s.font", name);
263         GTK_WIDGET (label)->style = gtk_style_copy (GTK_WIDGET (label)->style);
264         GTK_WIDGET (label)->style->font =
265           gdk_font_load (get_string_resource (buf, "Dialog.Label.Font"));
266         if (center <= 0)
267           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
268         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
269                             label, TRUE, TRUE, 0);
270         gtk_widget_show (label);
271       }
272
273       if (s)
274         head = s+1;
275       else
276         head = 0;
277
278       center--;
279     }
280
281   label = gtk_label_new ("");
282   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
283                       label, TRUE, TRUE, 0);
284   gtk_widget_show (label);
285
286   label = gtk_hbutton_box_new ();
287   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
288                       label, TRUE, TRUE, 0);
289
290   ok = gtk_button_new_with_label ("OK");
291   gtk_container_add (GTK_CONTAINER (label), ok);
292
293   if (restart_button_p)
294     {
295       cancel = gtk_button_new_with_label ("Cancel");
296       gtk_container_add (GTK_CONTAINER (label), cancel);
297     }
298
299   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
300   gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
301   gtk_window_set_title (GTK_WINDOW (dialog), progclass);
302   gtk_widget_show (ok);
303   if (cancel)
304     gtk_widget_show (cancel);
305   gtk_widget_show (label);
306   gtk_widget_show (dialog);
307 /*  gtk_window_set_default (GTK_WINDOW (dialog), ok);*/
308
309   if (restart_button_p)
310     {
311       gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
312                                  GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
313                                  (gpointer) dialog);
314       gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
315                                  GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
316                                  (gpointer) dialog);
317     }
318   else
319     {
320       gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
321                                  GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
322                                  (gpointer) dialog);
323     }
324   gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
325                                 GTK_WIDGET (parent)->window);
326
327   gdk_window_show (GTK_WIDGET (dialog)->window);
328   gdk_window_raise (GTK_WIDGET (dialog)->window);
329
330   free (msg);
331 }
332
333
334 static void
335 run_cmd (GtkWidget *widget, Atom command, int arg)
336 {
337   char *err = 0;
338   int status;
339
340   apply_changes_and_save (widget);
341   status = xscreensaver_command (gdk_display, command, arg, False, &err);
342   if (status < 0)
343     {
344       char buf [255];
345       if (err)
346         sprintf (buf, "Error:\n\n%s", err);
347       else
348         strcpy (buf, "Unknown error!");
349       warning_dialog (widget, buf, False, 100);
350     }
351   if (err) free (err);
352 }
353
354
355 static void
356 run_hack (GtkWidget *widget, int which, Bool report_errors_p)
357 {
358   if (which < 0) return;
359   apply_changes_and_save (widget);
360   if (report_errors_p)
361     run_cmd (widget, XA_DEMO, which + 1);
362   else
363     {
364       char *s = 0;
365       xscreensaver_command (gdk_display, XA_DEMO, which + 1, False, &s);
366       if (s) free (s);
367     }
368 }
369
370
371 \f
372 /* Button callbacks
373  */
374
375 void
376 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
377 {
378   apply_changes_and_save (GTK_WIDGET (menuitem));
379   gtk_main_quit ();
380 }
381
382 static void
383 wm_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
384 {
385   apply_changes_and_save (widget);
386   gtk_main_quit ();
387 }
388
389
390 void
391 cut_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
392 {
393   /* #### */
394   warning_dialog (GTK_WIDGET (menuitem),
395                   "Error:\n\n"
396                   "cut unimplemented\n", False, 1);
397 }
398
399
400 void
401 copy_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
402 {
403   /* #### */
404   warning_dialog (GTK_WIDGET (menuitem),
405                   "Error:\n\n"
406                   "copy unimplemented\n", False, 1);
407 }
408
409
410 void
411 paste_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
412 {
413   /* #### */
414   warning_dialog (GTK_WIDGET (menuitem),
415                   "Error:\n\n"
416                   "paste unimplemented\n", False, 1);
417 }
418
419
420 void
421 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
422 {
423   char buf [2048];
424   char *s = strdup (screensaver_id + 4);
425   char *s2;
426
427   s2 = strchr (s, ',');
428   *s2 = 0;
429   s2 += 2;
430
431   sprintf (buf, "%s\n%s\n\n"
432            "For updates, check http://www.jwz.org/xscreensaver/",
433            s, s2);
434   free (s);
435
436   warning_dialog (GTK_WIDGET (menuitem), buf, False, 100);
437 }
438
439
440 void
441 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
442 {
443   /* prefs_pair *pair = (prefs_pair *) client_data; */
444   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
445
446   saver_preferences *p =  pair->a;
447   char *help_command;
448
449   if (!p->help_url || !*p->help_url)
450     {
451       warning_dialog (GTK_WIDGET (menuitem),
452                       "Error:\n\n"
453                       "No Help URL has been specified.\n", False, 100);
454       return;
455     }
456
457   help_command = (char *) malloc (strlen (p->load_url_command) +
458                                   (strlen (p->help_url) * 2) + 20);
459   strcpy (help_command, "( ");
460   sprintf (help_command + strlen(help_command),
461            p->load_url_command, p->help_url, p->help_url);
462   strcat (help_command, " ) &");
463   system (help_command);
464   free (help_command);
465 }
466
467
468 void
469 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
470 {
471   run_cmd (GTK_WIDGET (menuitem), XA_ACTIVATE, 0);
472 }
473
474
475 void
476 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
477 {
478   run_cmd (GTK_WIDGET (menuitem), XA_LOCK, 0);
479 }
480
481
482 void
483 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
484 {
485   run_cmd (GTK_WIDGET (menuitem), XA_EXIT, 0);
486 }
487
488
489 void
490 restart_menu_cb (GtkWidget *widget, gpointer user_data)
491 {
492 #if 0
493   run_cmd (GTK_WIDGET (widget), XA_RESTART, 0);
494 #else
495   apply_changes_and_save (GTK_WIDGET (widget));
496   xscreensaver_command (gdk_display, XA_EXIT, 0, False, NULL);
497   sleep (1);
498   system ("xscreensaver -nosplash &");
499 #endif
500 }
501
502
503 static int _selected_hack_number = -1;
504
505 static int
506 selected_hack_number (GtkWidget *toplevel)
507 {
508 #if 0
509   GtkViewport *vp = GTK_VIEWPORT (name_to_widget (toplevel, "viewport"));
510   GtkList *list_widget = GTK_LIST (GTK_BIN(vp)->child);
511   GList *slist = list_widget->selection;
512   GtkWidget *selected = (slist ? GTK_WIDGET (slist->data) : 0);
513   int which = (selected
514                ? gtk_list_child_position (list_widget, GTK_WIDGET (selected))
515                : -1);
516   return which;
517 #else
518   return _selected_hack_number;
519 #endif
520 }
521
522
523 static int
524 demo_write_init_file (GtkWidget *widget, saver_preferences *p)
525 {
526   if (!write_init_file (p, short_version, False))
527     return 0;
528   else
529     {
530       const char *f = init_file_name();
531       if (!f || !*f)
532         warning_dialog (widget,
533                         "Error:\n\nCouldn't determine init file name!\n",
534                         False, 100);
535       else
536         {
537           char *b = (char *) malloc (strlen(f) + 1024);
538           sprintf (b, "Error:\n\nCouldn't write %s\n", f);
539           warning_dialog (widget, b, False, 100);
540           free (b);
541         }
542       return -1;
543     }
544 }
545
546
547 static int
548 apply_changes_and_save_1 (GtkWidget *widget)
549 {
550   /* prefs_pair *pair = (prefs_pair *) client_data; */
551   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
552   saver_preferences *p =  pair->a;
553   GtkList *list_widget =
554     GTK_LIST (name_to_widget (widget, "list"));
555   int which = selected_hack_number (widget);
556
557   GtkEntry *cmd = GTK_ENTRY (name_to_widget (widget, "cmd_text"));
558   GtkToggleButton *enabled =
559     GTK_TOGGLE_BUTTON (name_to_widget (widget, "enabled"));
560   GtkCombo *vis = GTK_COMBO (name_to_widget (widget, "visual_combo"));
561
562   Bool enabled_p = gtk_toggle_button_get_active (enabled);
563   const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
564   const char *command = gtk_entry_get_text (cmd);
565   
566   char c;
567   unsigned long id;
568
569   if (which < 0) return -1;
570
571   if (maybe_reload_init_file (widget, pair) != 0)
572     return 1;
573
574   /* Sanity-check and canonicalize whatever the user typed into the combo box.
575    */
576   if      (!strcasecmp (visual, ""))                   visual = "";
577   else if (!strcasecmp (visual, "any"))                visual = "";
578   else if (!strcasecmp (visual, "default"))            visual = "Default";
579   else if (!strcasecmp (visual, "default-n"))          visual = "Default-N";
580   else if (!strcasecmp (visual, "default-i"))          visual = "Default-I";
581   else if (!strcasecmp (visual, "best"))               visual = "Best";
582   else if (!strcasecmp (visual, "mono"))               visual = "Mono";
583   else if (!strcasecmp (visual, "monochrome"))         visual = "Mono";
584   else if (!strcasecmp (visual, "gray"))               visual = "Gray";
585   else if (!strcasecmp (visual, "grey"))               visual = "Gray";
586   else if (!strcasecmp (visual, "color"))              visual = "Color";
587   else if (!strcasecmp (visual, "gl"))                 visual = "GL";
588   else if (!strcasecmp (visual, "staticgray"))         visual = "StaticGray";
589   else if (!strcasecmp (visual, "staticcolor"))        visual = "StaticColor";
590   else if (!strcasecmp (visual, "truecolor"))          visual = "TrueColor";
591   else if (!strcasecmp (visual, "grayscale"))          visual = "GrayScale";
592   else if (!strcasecmp (visual, "greyscale"))          visual = "GrayScale";
593   else if (!strcasecmp (visual, "pseudocolor"))        visual = "PseudoColor";
594   else if (!strcasecmp (visual, "directcolor"))        visual = "DirectColor";
595   else if (1 == sscanf (visual, " %ld %c", &id, &c))   ;
596   else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
597   else
598     {
599       gdk_beep ();                                /* unparsable */
600       visual = "";
601       gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");
602     }
603
604   ensure_selected_item_visible (GTK_WIDGET (list_widget));
605
606   if (!p->screenhacks[which]->visual)
607     p->screenhacks[which]->visual = strdup ("");
608   if (!p->screenhacks[which]->command)
609     p->screenhacks[which]->command = strdup ("");
610
611   if (p->screenhacks[which]->enabled_p != enabled_p ||
612       !!strcasecmp (p->screenhacks[which]->visual, visual) ||
613       !!strcasecmp (p->screenhacks[which]->command, command))
614     {
615       /* Something was changed -- store results into the struct,
616          and write the file.
617        */
618       free (p->screenhacks[which]->visual);
619       free (p->screenhacks[which]->command);
620       p->screenhacks[which]->visual = strdup (visual);
621       p->screenhacks[which]->command = strdup (command);
622       p->screenhacks[which]->enabled_p = enabled_p;
623
624       return demo_write_init_file (widget, p);
625     }
626
627   /* No changes made */
628   return 0;
629 }
630
631 void prefs_ok_cb (GtkButton *button, gpointer user_data);
632
633 static int
634 apply_changes_and_save (GtkWidget *widget)
635 {
636   prefs_ok_cb ((GtkButton *) widget, 0);
637   return apply_changes_and_save_1 (widget);
638 }
639
640
641 void
642 run_this_cb (GtkButton *button, gpointer user_data)
643 {
644   int which = selected_hack_number (GTK_WIDGET (button));
645   if (which < 0) return;
646   if (0 == apply_changes_and_save (GTK_WIDGET (button)))
647     run_hack (GTK_WIDGET (button), which, True);
648 }
649
650
651 void
652 manual_cb (GtkButton *button, gpointer user_data)
653 {
654   /* prefs_pair *pair = (prefs_pair *) client_data; */
655   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
656   saver_preferences *p =  pair->a;
657   GtkList *list_widget =
658     GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
659   int which = selected_hack_number (GTK_WIDGET (button));
660   char *name, *name2, *cmd, *s;
661   if (which < 0) return;
662   apply_changes_and_save (GTK_WIDGET (button));
663   ensure_selected_item_visible (GTK_WIDGET (list_widget));
664
665   name = strdup (p->screenhacks[which]->command);
666   name2 = name;
667   while (isspace (*name2)) name2++;
668   s = name2;
669   while (*s && !isspace (*s)) s++;
670   *s = 0;
671   s = strrchr (name2, '/');
672   if (s) name = s+1;
673
674   cmd = get_string_resource ("manualCommand", "ManualCommand");
675   if (cmd)
676     {
677       char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
678       strcpy (cmd2, "( ");
679       sprintf (cmd2 + strlen (cmd2),
680                cmd,
681                name2, name2, name2, name2);
682       strcat (cmd2, " ) &");
683       system (cmd2);
684       free (cmd2);
685     }
686   else
687     {
688       warning_dialog (GTK_WIDGET (button),
689                       "Error:\n\nno `manualCommand' resource set.",
690                       False, 100);
691     }
692
693   free (name);
694 }
695
696
697 void
698 run_next_cb (GtkButton *button, gpointer user_data)
699 {
700   /* prefs_pair *pair = (prefs_pair *) client_data; */
701   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
702   saver_preferences *p =  pair->a;
703
704   GtkList *list_widget =
705     GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
706   int which = selected_hack_number (GTK_WIDGET (button));
707
708   if (which < 0)
709     which = 0;
710   else
711     which++;
712
713   if (which >= p->screenhacks_count)
714     which = 0;
715
716   apply_changes_and_save (GTK_WIDGET (button));
717   gtk_list_select_item (GTK_LIST (list_widget), which);
718   ensure_selected_item_visible (GTK_WIDGET (list_widget));
719   populate_demo_window (GTK_WIDGET (button), which, pair);
720   run_hack (GTK_WIDGET (button), which, False);
721 }
722
723
724 void
725 run_prev_cb (GtkButton *button, gpointer user_data)
726 {
727   /* prefs_pair *pair = (prefs_pair *) client_data; */
728   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
729   saver_preferences *p =  pair->a;
730
731   GtkList *list_widget =
732     GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
733   int which = selected_hack_number (GTK_WIDGET (button));
734
735   if (which < 0)
736     which = p->screenhacks_count - 1;
737   else
738     which--;
739
740   if (which < 0)
741     which = p->screenhacks_count - 1;
742
743   apply_changes_and_save (GTK_WIDGET (button));
744   gtk_list_select_item (GTK_LIST (list_widget), which);
745   ensure_selected_item_visible (GTK_WIDGET (list_widget));
746   populate_demo_window (GTK_WIDGET (button), which, pair);
747   run_hack (GTK_WIDGET (button), which, False);
748 }
749
750
751 /* Helper for the text fields that contain time specifications:
752    this parses the text, and does error checking.
753  */
754 static void 
755 hack_time_text (GtkWidget *widget, const char *line, Time *store, Bool sec_p)
756 {
757   if (*line)
758     {
759       int value;
760       value = parse_time ((char *) line, sec_p, True);
761       value *= 1000;    /* Time measures in microseconds */
762       if (value < 0)
763         {
764           char b[255];
765           sprintf (b,
766                    "Error:\n\n"
767                    "Unparsable time format: \"%s\"\n",
768                    line);
769           warning_dialog (widget, b, False, 100);
770         }
771       else
772         *store = value;
773     }
774 }
775
776
777 void
778 prefs_ok_cb (GtkButton *button, gpointer user_data)
779 {
780   /* prefs_pair *pair = (prefs_pair *) client_data; */
781   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
782
783   saver_preferences *p =  pair->a;
784   saver_preferences *p2 = pair->b;
785   Bool changed = False;
786
787 # define SECONDS(field, name) \
788   hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\
789                     GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
790                   (field), \
791                   True)
792
793 # define MINUTES(field, name) \
794   hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\
795                     GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
796                   (field), \
797                   False)
798
799 # define INTEGER(field, name) do { \
800     char *line = gtk_entry_get_text (\
801                     GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))); \
802     unsigned int value; \
803     char c; \
804     if (! *line) \
805       ; \
806     else if (sscanf (line, "%u%c", &value, &c) != 1) \
807       { \
808         char b[255]; \
809         sprintf (b, "Error:\n\n" "Not an integer: \"%s\"\n", line); \
810         warning_dialog (GTK_WIDGET (button), b, False, 100); \
811       } \
812    else \
813      *(field) = value; \
814   } while(0)
815
816 # define CHECKBOX(field, name) \
817   field = gtk_toggle_button_get_active (\
818              GTK_TOGGLE_BUTTON (name_to_widget (GTK_WIDGET(button), (name))))
819
820   MINUTES (&p2->timeout,        "timeout_text");
821   MINUTES (&p2->cycle,          "cycle_text");
822   SECONDS (&p2->fade_seconds,   "fade_text");
823   INTEGER (&p2->fade_ticks,     "ticks_text");
824   MINUTES (&p2->lock_timeout,   "lock_text");
825   SECONDS (&p2->passwd_timeout, "pass_text");
826   CHECKBOX (p2->verbose_p,      "verbose_button");
827   CHECKBOX (p2->install_cmap_p, "install_button");
828   CHECKBOX (p2->fade_p,         "fade_button");
829   CHECKBOX (p2->unfade_p,       "unfade_button");
830   CHECKBOX (p2->lock_p,         "lock_button");
831
832 # undef SECONDS
833 # undef MINUTES
834 # undef INTEGER
835 # undef CHECKBOX
836
837 # define COPY(field) \
838   if (p->field != p2->field) changed = True; \
839   p->field = p2->field
840
841   COPY(timeout);
842   COPY(cycle);
843   COPY(lock_timeout);
844   COPY(passwd_timeout);
845   COPY(fade_seconds);
846   COPY(fade_ticks);
847   COPY(verbose_p);
848   COPY(install_cmap_p);
849   COPY(fade_p);
850   COPY(unfade_p);
851   COPY(lock_p);
852 # undef COPY
853
854   populate_prefs_page (GTK_WIDGET (button), pair);
855
856   if (changed)
857     demo_write_init_file (GTK_WIDGET (button), p);
858 }
859
860
861 void
862 prefs_cancel_cb (GtkButton *button, gpointer user_data)
863 {
864   /* prefs_pair *pair = (prefs_pair *) client_data; */
865   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
866
867   *pair->b = *pair->a;
868   populate_prefs_page (GTK_WIDGET (button), pair);
869 }
870
871
872 void
873 pref_changed_cb (GtkButton *button, gpointer user_data)
874 {
875   if (! initializing_p)
876     apply_changes_and_save (GTK_WIDGET (button));
877 }
878
879
880 static gint
881 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
882                      gpointer client_data)
883 {
884   if (event->type == GDK_2BUTTON_PRESS)
885     {
886       GtkList *list = GTK_LIST (name_to_widget (button, "list"));
887       int which = gtk_list_child_position (list, GTK_WIDGET (button));
888
889       if (which >= 0)
890         run_hack (GTK_WIDGET (button), which, True);
891     }
892
893   return FALSE;
894 }
895
896
897 static void
898 list_select_cb (GtkList *list, GtkWidget *child)
899 {
900   /* prefs_pair *pair = (prefs_pair *) client_data; */
901   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
902
903   int which = gtk_list_child_position (list, GTK_WIDGET (child));
904   apply_changes_and_save (GTK_WIDGET (list));
905   populate_demo_window (GTK_WIDGET (list), which, pair);
906 }
907
908 static void
909 list_unselect_cb (GtkList *list, GtkWidget *child)
910 {
911   /* prefs_pair *pair = (prefs_pair *) client_data; */
912   prefs_pair *pair = global_prefs_pair;  /* I hate C so much... */
913
914   apply_changes_and_save (GTK_WIDGET (list));
915   populate_demo_window (GTK_WIDGET (list), -1, pair);
916 }
917
918 \f
919 /* Populating the various widgets
920  */
921
922
923 /* Formats a `Time' into "H:MM:SS".  (Time is microseconds.)
924  */
925 static void
926 format_time (char *buf, Time time)
927 {
928   int s = time / 1000;
929   unsigned int h = 0, m = 0;
930   if (s >= 60)
931     {
932       m += (s / 60);
933       s %= 60;
934     }
935   if (m >= 60)
936     {
937       h += (m / 60);
938       m %= 60;
939     }
940   sprintf (buf, "%u:%02u:%02u", h, m, s);
941 }
942
943
944 static char *
945 make_pretty_name (const char *shell_command)
946 {
947   char *s = strdup (shell_command);
948   char *s2;
949   char res_name[255];
950
951   for (s2 = s; *s2; s2++)       /* truncate at first whitespace */
952     if (isspace (*s2))
953       {
954         *s2 = 0;
955         break;
956       }
957
958   s2 = strrchr (s, '/');        /* if pathname, take last component */
959   if (s2)
960     {
961       s2 = strdup (s2+1);
962       free (s);
963       s = s2;
964     }
965
966   if (strlen (s) > 50)          /* 51 is hereby defined as "unreasonable" */
967     s[50] = 0;
968
969   sprintf (res_name, "hacks.%s.name", s);               /* resource? */
970   s2 = get_string_resource (res_name, res_name);
971   if (s2)
972     return s2;
973
974   for (s2 = s; *s2; s2++)       /* if it has any capitals, return it */
975     if (*s2 >= 'A' && *s2 <= 'Z')
976       return s;
977
978   if (s[0] >= 'a' && s[0] <= 'z')                       /* else cap it */
979     s[0] -= 'a'-'A';
980   if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z')        /* (magic leading X) */
981     s[1] -= 'a'-'A';
982   return s;
983 }
984
985
986 /* Finds the number of the last hack to run, and makes that item be
987    selected by default.
988  */
989 static void
990 scroll_to_current_hack (GtkWidget *toplevel, prefs_pair *pair)
991 {
992   saver_preferences *p =  pair->a;
993   Atom type;
994   int format;
995   unsigned long nitems, bytesafter;
996   CARD32 *data = 0;
997   Display *dpy = gdk_display;
998   int which = 0;
999   GtkList *list;
1000
1001   if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1002                           XA_SCREENSAVER_STATUS,
1003                           0, 3, False, XA_INTEGER,
1004                           &type, &format, &nitems, &bytesafter,
1005                           (unsigned char **) &data)
1006       == Success
1007       && type == XA_INTEGER
1008       && nitems >= 3
1009       && data)
1010     which = (int) data[2] - 1;
1011
1012   if (data) free (data);
1013
1014   if (which < 0)
1015     return;
1016
1017   list = GTK_LIST (name_to_widget (toplevel, "list"));
1018   apply_changes_and_save (toplevel);
1019   if (which < p->screenhacks_count)
1020     {
1021       gtk_list_select_item (list, which);
1022       ensure_selected_item_visible (GTK_WIDGET (list));
1023       populate_demo_window (toplevel, which, pair);
1024     }
1025 }
1026
1027
1028
1029 static void
1030 populate_hack_list (GtkWidget *toplevel, prefs_pair *pair)
1031 {
1032   saver_preferences *p =  pair->a;
1033   GtkList *list = GTK_LIST (name_to_widget (toplevel, "list"));
1034   screenhack **hacks = p->screenhacks;
1035   screenhack **h;
1036
1037   for (h = hacks; h && *h; h++)
1038     {
1039       GtkWidget *line;
1040       char *pretty_name = (h[0]->name
1041                            ? strdup (h[0]->name)
1042                            : make_pretty_name (h[0]->command));
1043
1044       line = gtk_list_item_new_with_label (pretty_name);
1045       free (pretty_name);
1046
1047       gtk_container_add (GTK_CONTAINER (list), line);
1048       gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
1049                           GTK_SIGNAL_FUNC (list_doubleclick_cb),
1050                           (gpointer) pair);
1051 #if 0 /* #### */
1052       GTK_WIDGET (GTK_BIN(line)->child)->style =
1053         gtk_style_copy (GTK_WIDGET (text_line)->style);
1054 #endif
1055       gtk_widget_show (line);
1056     }
1057
1058   gtk_signal_connect (GTK_OBJECT (list), "select_child",
1059                       GTK_SIGNAL_FUNC (list_select_cb),
1060                       (gpointer) pair);
1061   gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
1062                       GTK_SIGNAL_FUNC (list_unselect_cb),
1063                       (gpointer) pair);
1064 }
1065
1066
1067 static void
1068 populate_prefs_page (GtkWidget *top, prefs_pair *pair)
1069 {
1070   saver_preferences *p =  pair->a;
1071   char s[100];
1072
1073   format_time (s, p->timeout);
1074   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "timeout_text")), s);
1075   format_time (s, p->cycle);
1076   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "cycle_text")), s);
1077   format_time (s, p->lock_timeout);
1078   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "lock_text")), s);
1079   format_time (s, p->passwd_timeout);
1080   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "pass_text")), s);
1081   format_time (s, p->fade_seconds);
1082   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "fade_text")), s);
1083   sprintf (s, "%u", p->fade_ticks);
1084   gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "ticks_text")), s);
1085
1086   gtk_toggle_button_set_active (
1087                    GTK_TOGGLE_BUTTON (name_to_widget (top, "verbose_button")),
1088                    p->verbose_p);
1089   gtk_toggle_button_set_active (
1090                    GTK_TOGGLE_BUTTON (name_to_widget (top, "install_button")),
1091                    p->install_cmap_p);
1092   gtk_toggle_button_set_active (
1093                    GTK_TOGGLE_BUTTON (name_to_widget (top, "fade_button")),
1094                    p->fade_p);
1095   gtk_toggle_button_set_active (
1096                    GTK_TOGGLE_BUTTON (name_to_widget (top, "unfade_button")),
1097                    p->unfade_p);
1098   gtk_toggle_button_set_active (
1099                    GTK_TOGGLE_BUTTON (name_to_widget (top, "lock_button")),
1100                    p->lock_p);
1101
1102
1103   {
1104     Bool found_any_writable_cells = False;
1105     Display *dpy = gdk_display;
1106     int nscreens = ScreenCount(dpy);
1107     int i;
1108     for (i = 0; i < nscreens; i++)
1109       {
1110         Screen *s = ScreenOfDisplay (dpy, i);
1111         if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1112           {
1113             found_any_writable_cells = True;
1114             break;
1115           }
1116       }
1117
1118     gtk_widget_set_sensitive (
1119                            GTK_WIDGET (name_to_widget (top, "fade_label")),
1120                            found_any_writable_cells);
1121     gtk_widget_set_sensitive (
1122                            GTK_WIDGET (name_to_widget (top, "ticks_label")),
1123                            found_any_writable_cells);
1124     gtk_widget_set_sensitive (
1125                            GTK_WIDGET (name_to_widget (top, "fade_text")),
1126                            found_any_writable_cells);
1127     gtk_widget_set_sensitive (
1128                            GTK_WIDGET (name_to_widget (top, "ticks_text")),
1129                            found_any_writable_cells);
1130     gtk_widget_set_sensitive (
1131                            GTK_WIDGET (name_to_widget (top, "install_button")),
1132                            found_any_writable_cells);
1133     gtk_widget_set_sensitive (
1134                            GTK_WIDGET (name_to_widget (top, "fade_button")),
1135                            found_any_writable_cells);
1136     gtk_widget_set_sensitive (
1137                            GTK_WIDGET (name_to_widget (top, "unfade_button")),
1138                            found_any_writable_cells);
1139   }
1140
1141 }
1142
1143
1144 static void
1145 sensitize_demo_widgets (GtkWidget *toplevel, Bool sensitive_p)
1146 {
1147   const char *names[] = { "cmd_label", "cmd_text", "enabled",
1148                           "visual", "visual_combo",
1149                           "demo", "manual" };
1150   int i;
1151   for (i = 0; i < countof(names); i++)
1152     {
1153       GtkWidget *w = name_to_widget (toplevel, names[i]);
1154       gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
1155     }
1156
1157   /* I don't know how to handle these yet... */
1158   {
1159     const char *names2[] = { "cut_menu", "copy_menu", "paste_menu" };
1160     for (i = 0; i < countof(names2); i++)
1161       {
1162         GtkWidget *w = name_to_widget (toplevel, names2[i]);
1163         gtk_widget_set_sensitive (GTK_WIDGET(w), False);
1164       }
1165   }
1166 }
1167
1168
1169 /* Even though we've given these text fields a maximum number of characters,
1170    their default size is still about 30 characters wide -- so measure out
1171    a string in their font, and resize them to just fit that.
1172  */
1173 static void
1174 fix_text_entry_sizes (GtkWidget *toplevel)
1175 {
1176   const char *names[] = { "timeout_text", "cycle_text", "fade_text",
1177                           "ticks_text", "lock_text", "pass_text" };
1178   int i;
1179   int width = 0;
1180   GtkWidget *w;
1181
1182   for (i = 0; i < countof(names); i++)
1183     {
1184       w = GTK_WIDGET (name_to_widget (toplevel, names[i]));
1185       if (width == 0)
1186         width = gdk_text_width (w->style->font, "00:00:00_", 9);
1187       gtk_widget_set_usize (w, width, -2);
1188     }
1189
1190   /* Now fix the size of the combo box.
1191    */
1192   w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "visual_combo"));
1193   w = GTK_COMBO (w)->entry;
1194   width = gdk_text_width (w->style->font, "PseudoColor___", 14);
1195   gtk_widget_set_usize (w, width, -2);
1196
1197 #if 0
1198   /* Now fix the size of the list.
1199    */
1200   w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "list"));
1201   width = gdk_text_width (w->style->font, "nnnnnnnnnnnnnnnnnnnnnn", 22);
1202   gtk_widget_set_usize (w, width, -2);
1203 #endif
1204 }
1205
1206
1207
1208 \f
1209 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
1210  */
1211
1212 static char *up_arrow_xpm[] = {
1213   "15 15 4 1",
1214   "     c None",
1215   "-    c #FFFFFF",
1216   "+    c #D6D6D6",
1217   "@    c #000000",
1218
1219   "       @       ",
1220   "       @       ",
1221   "      -+@      ",
1222   "      -+@      ",
1223   "     -+++@     ",
1224   "     -+++@     ",
1225   "    -+++++@    ",
1226   "    -+++++@    ",
1227   "   -+++++++@   ",
1228   "   -+++++++@   ",
1229   "  -+++++++++@  ",
1230   "  -+++++++++@  ",
1231   " -+++++++++++@ ",
1232   " @@@@@@@@@@@@@ ",
1233   "               ",
1234
1235   /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1236      the end of the array (Gtk 1.2.5.) */
1237   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1238   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1239 };
1240
1241 static char *down_arrow_xpm[] = {
1242   "15 15 4 1",
1243   "     c None",
1244   "-    c #FFFFFF",
1245   "+    c #D6D6D6",
1246   "@    c #000000",
1247
1248   "               ",
1249   " ------------- ",
1250   " -+++++++++++@ ",
1251   "  -+++++++++@  ",
1252   "  -+++++++++@  ",
1253   "   -+++++++@   ",
1254   "   -+++++++@   ",
1255   "    -+++++@    ",
1256   "    -+++++@    ",
1257   "     -+++@     ",
1258   "     -+++@     ",
1259   "      -+@      ",
1260   "      -+@      ",
1261   "       @       ",
1262   "       @       ",
1263
1264   /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1265      the end of the array (Gtk 1.2.5.) */
1266   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1267   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1268 };
1269
1270 static void
1271 pixmapify_buttons (GtkWidget *toplevel)
1272 {
1273   GdkPixmap *pixmap;
1274   GdkBitmap *mask;
1275   GtkWidget *pixmapwid;
1276   GtkStyle *style;
1277   GtkWidget *w;
1278
1279   w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "next"));
1280   style = gtk_widget_get_style (w);
1281   mask = 0;
1282   pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
1283                                          &style->bg[GTK_STATE_NORMAL],
1284                                          (gchar **) down_arrow_xpm);
1285   pixmapwid = gtk_pixmap_new (pixmap, mask);
1286   gtk_widget_show (pixmapwid);
1287   gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
1288   gtk_container_add (GTK_CONTAINER (w), pixmapwid);
1289
1290   w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "prev"));
1291   style = gtk_widget_get_style (w);
1292   mask = 0;
1293   pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
1294                                          &style->bg[GTK_STATE_NORMAL],
1295                                          (gchar **) up_arrow_xpm);
1296   pixmapwid = gtk_pixmap_new (pixmap, mask);
1297   gtk_widget_show (pixmapwid);
1298   gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
1299   gtk_container_add (GTK_CONTAINER (w), pixmapwid);
1300 }
1301
1302
1303 \f
1304 /* Work around a Gtk bug that causes label widgets to wrap text too early.
1305  */
1306
1307 static void
1308 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
1309                                              GtkAllocation *allocation,
1310                                              void *foo)
1311 {
1312   GtkRequisition req;
1313   GtkWidgetAuxInfo *aux_info;
1314
1315   aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
1316
1317   aux_info->width = allocation->width;
1318   aux_info->height = -2;
1319   aux_info->x = -1;
1320   aux_info->y = -1;
1321
1322   gtk_widget_size_request (label, &req);
1323 }
1324
1325
1326 /* Feel the love.  Thanks to Nat Friedman for finding this workaround.
1327  */
1328 static void
1329 eschew_gtk_lossage (GtkWidget *toplevel)
1330 {
1331   GtkWidgetAuxInfo *aux_info;
1332   GtkWidget *label = GTK_WIDGET (name_to_widget (toplevel, "doc"));
1333
1334   aux_info = g_new0 (GtkWidgetAuxInfo, 1);
1335   aux_info->width = label->allocation.width;
1336   aux_info->height = -2;
1337   aux_info->x = -1;
1338   aux_info->y = -1;
1339
1340   gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
1341
1342   gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
1343                       you_are_not_a_unique_or_beautiful_snowflake, NULL);
1344
1345   gtk_widget_queue_resize (label); 
1346 }
1347
1348
1349 char *
1350 get_hack_blurb (screenhack *hack)
1351 {
1352   char *doc_string;
1353   char *prog_name = strdup (hack->command);
1354   char *pretty_name = (hack->name
1355                        ? strdup (hack->name)
1356                        : make_pretty_name (hack->command));
1357   char doc_name[255], doc_class[255];
1358   char *s, *s2;
1359
1360   for (s = prog_name; *s && !isspace(*s); s++)
1361     ;
1362   *s = 0;
1363   s = strrchr (prog_name, '/');
1364   if (s) strcpy (prog_name, s+1);
1365
1366   sprintf (doc_name,  "hacks.%s.documentation", pretty_name);
1367   sprintf (doc_class, "hacks.%s.documentation", prog_name);
1368   free (prog_name);
1369   free (pretty_name);
1370
1371   doc_string = get_string_resource (doc_name, doc_class);
1372   if (doc_string)
1373     {
1374       for (s = doc_string; *s; s++)
1375         {
1376           if (*s == '\n')
1377             {
1378               /* skip over whitespace at beginning of line */
1379               s++;
1380               while (*s && (*s == ' ' || *s == '\t'))
1381                 s++;
1382             }
1383           else if (*s == ' ' || *s == '\t')
1384             {
1385               /* compress all other horizontal whitespace. */
1386               *s = ' ';
1387               s++;
1388               for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++)
1389                 ;
1390               if (s2 > s) strcpy (s, s2);
1391               s--;
1392             }
1393         }
1394
1395       while (*s && isspace (*s))      /* Strip trailing whitespace */
1396         *(--s) = 0;
1397
1398       /* Delete whitespace at end of each line. */
1399       for (; s > doc_string; s--)
1400         if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t'))
1401           {
1402             for (s2 = s-1;
1403                  s2 > doc_string && (*s2 == ' ' || *s2 == '\t');
1404                  s2--)
1405               ;
1406             s2++;
1407             if (s2 < s) strcpy (s2, s);
1408             s = s2;
1409           }
1410       
1411       /* Delete leading blank lines. */
1412       for (s = doc_string; *s == '\n'; s++)
1413         ;
1414       if (s > doc_string) strcpy (doc_string, s);
1415     }
1416   else
1417     {
1418       static int doc_installed = 0;
1419       if (doc_installed == 0)
1420         {
1421           if (get_boolean_resource ("hacks.documentation.isInstalled",
1422                                     "hacks.documentation.isInstalled"))
1423             doc_installed = 1;
1424           else
1425             doc_installed = -1;
1426         }
1427
1428       if (doc_installed < 0)
1429         doc_string =
1430           strdup ("Error:\n\n"
1431                   "The documentation strings do not appear to be "
1432                   "installed.  This is probably because there is "
1433                   "an \"XScreenSaver\" app-defaults file installed "
1434                   "that is from an older version of the program. "
1435                   "To fix this problem, delete that file, or "
1436                   "install a current version (either will work.)");
1437       else
1438         doc_string = strdup ("");
1439     }
1440
1441   return doc_string;
1442 }
1443
1444
1445 static void
1446 populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair)
1447 {
1448   saver_preferences *p = pair->a;
1449   screenhack *hack = (which >= 0 && which < p->screenhacks_count
1450                       ? p->screenhacks[which] : 0);
1451   GtkFrame *frame = GTK_FRAME (name_to_widget (toplevel, "frame"));
1452   GtkLabel *doc = GTK_LABEL (name_to_widget (toplevel, "doc"));
1453   GtkEntry *cmd = GTK_ENTRY (name_to_widget (toplevel, "cmd_text"));
1454   GtkToggleButton *enabled =
1455     GTK_TOGGLE_BUTTON (name_to_widget (toplevel, "enabled"));
1456   GtkCombo *vis = GTK_COMBO (name_to_widget (toplevel, "visual_combo"));
1457
1458   char *pretty_name = (hack
1459                        ? (hack->name
1460                           ? strdup (hack->name)
1461                           : make_pretty_name (hack->command))
1462                        : 0);
1463   char *doc_string = hack ? get_hack_blurb (hack) : 0;
1464
1465   gtk_frame_set_label (frame, (pretty_name ? pretty_name : ""));
1466   gtk_label_set_text (doc, (doc_string ? doc_string : ""));
1467   gtk_entry_set_text (cmd, (hack ? hack->command : ""));
1468   gtk_entry_set_position (cmd, 0);
1469   gtk_toggle_button_set_active (enabled, (hack ? hack->enabled_p : False));
1470   gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
1471                       (hack
1472                        ? (hack->visual && *hack->visual
1473                           ? hack->visual
1474                           : "Any")
1475                        : ""));
1476
1477   gtk_container_resize_children (GTK_CONTAINER (GTK_WIDGET (doc)->parent));
1478
1479   sensitize_demo_widgets (toplevel, (hack ? True : False));
1480
1481   if (pretty_name) free (pretty_name);
1482   if (doc_string) free (doc_string);
1483
1484   _selected_hack_number = which;
1485 }
1486
1487
1488 static void
1489 widget_deleter (GtkWidget *widget, gpointer data)
1490 {
1491   /* #### Well, I want to destroy these widgets, but if I do that, they get
1492      referenced again, and eventually I get a SEGV.  So instead of
1493      destroying them, I'll just hide them, and leak a bunch of memory
1494      every time the disk file changes.  Go go go Gtk!
1495
1496      #### Ok, that's a lie, I get a crash even if I just hide the widget
1497      and don't ever delete it.  Fuck!
1498    */
1499 #if 0
1500   gtk_widget_destroy (widget);
1501 #else
1502   gtk_widget_hide (widget);
1503 #endif
1504 }
1505
1506
1507 static int
1508 maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair)
1509 {
1510   int status = 0;
1511   saver_preferences *p =  pair->a;
1512
1513   static Bool reentrant_lock = False;
1514   if (reentrant_lock) return 0;
1515   reentrant_lock = True;
1516
1517   if (init_file_changed_p (p))
1518     {
1519       const char *f = init_file_name();
1520       char *b;
1521       int which;
1522       GtkList *list;
1523
1524       if (!f || !*f) return 0;
1525       b = (char *) malloc (strlen(f) + 1024);
1526       sprintf (b,
1527                "Warning:\n\n"
1528                "file \"%s\" has changed, reloading.\n",
1529                f);
1530       warning_dialog (widget, b, False, 100);
1531       free (b);
1532
1533       load_init_file (p);
1534
1535       which = selected_hack_number (widget);
1536       list = GTK_LIST (name_to_widget (widget, "list"));
1537       gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
1538       populate_hack_list (widget, pair);
1539       gtk_list_select_item (list, which);
1540       populate_prefs_page (widget, pair);
1541       populate_demo_window (widget, which, pair);
1542       ensure_selected_item_visible (GTK_WIDGET (list));
1543
1544       status = 1;
1545     }
1546
1547   reentrant_lock = False;
1548   return status;
1549 }
1550
1551
1552 \f
1553 /* The main demo-mode command loop.
1554  */
1555
1556 #if 0
1557 static Bool
1558 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1559         XrmRepresentation *type, XrmValue *value, XPointer closure)
1560 {
1561   int i;
1562   for (i = 0; quarks[i]; i++)
1563     {
1564       if (bindings[i] == XrmBindTightly)
1565         fprintf (stderr, (i == 0 ? "" : "."));
1566       else if (bindings[i] == XrmBindLoosely)
1567         fprintf (stderr, "*");
1568       else
1569         fprintf (stderr, " ??? ");
1570       fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1571     }
1572
1573   fprintf (stderr, ": %s\n", (char *) value->addr);
1574
1575   return False;
1576 }
1577 #endif
1578
1579
1580 static void
1581 the_network_is_not_the_computer (GtkWidget *parent)
1582 {
1583   Display *dpy = gdk_display;
1584   char *rversion, *ruser, *rhost;
1585   char *luser, *lhost;
1586   char *msg = 0;
1587   struct passwd *p = getpwuid (getuid ());
1588   const char *d = DisplayString (dpy);
1589
1590 # if defined(HAVE_UNAME)
1591   struct utsname uts;
1592   if (uname (&uts) < 0)
1593     lhost = "<UNKNOWN>";
1594   else
1595     lhost = uts.nodename;
1596 # elif defined(VMS)
1597   strcpy (lhost, getenv("SYS$NODE"));
1598 # else  /* !HAVE_UNAME && !VMS */
1599   strcat (lhost, "<UNKNOWN>");
1600 # endif /* !HAVE_UNAME && !VMS */
1601
1602   if (p && p->pw_name)
1603     luser = p->pw_name;
1604   else
1605     luser = "???";
1606
1607   server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
1608
1609   /* Make a buffer that's big enough for a number of copies of all the
1610      strings, plus some. */
1611   msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
1612                                (ruser ? strlen(ruser) : 0) +
1613                                (rhost ? strlen(rhost) : 0) +
1614                                strlen(lhost) +
1615                                strlen(luser) +
1616                                strlen(d) +
1617                                1024));
1618   *msg = 0;
1619
1620   if (!rversion || !*rversion)
1621     {
1622       sprintf (msg,
1623                "Warning:\n\n"
1624                "The XScreenSaver daemon doesn't seem to be running\n"
1625                "on display \"%s\".  Launch it now?",
1626                d);
1627     }
1628   else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
1629     {
1630       /* Warn that the two processes are running as different users.
1631        */
1632       sprintf(msg,
1633                "Warning:\n\n"
1634               "%s is running as user \"%s\" on host \"%s\".\n"
1635               "But the xscreensaver managing display \"%s\"\n"
1636               "is running as user \"%s\" on host \"%s\".\n"
1637               "\n"
1638               "Since they are different users, they won't be reading/writing\n"
1639               "the same ~/.xscreensaver file, so %s isn't\n"
1640               "going to work right.\n"
1641               "\n"
1642               "You should either re-run %s as \"%s\", or re-run\n"
1643               "xscreensaver as \"%s\".\n"
1644               "\n"
1645               "Restart the xscreensaver daemon now?\n",
1646               progname, luser, lhost,
1647               d,
1648               (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1649               progname,
1650               progname, (ruser ? ruser : "???"),
1651               luser);
1652     }
1653   else if (rhost && *rhost && !!strcmp (rhost, lhost))
1654     {
1655       /* Warn that the two processes are running on different hosts.
1656        */
1657       sprintf (msg,
1658                "Warning:\n\n"
1659                "%s is running as user \"%s\" on host \"%s\".\n"
1660                "But the xscreensaver managing display \"%s\"\n"
1661                "is running as user \"%s\" on host \"%s\".\n"
1662                "\n"
1663                "If those two machines don't share a file system (that is,\n"
1664                "if they don't see the same ~%s/.xscreensaver file) then\n"
1665                "%s won't work right.\n"
1666                "\n"
1667                "Restart the daemon on \"%s\" as \"%s\" now?\n",
1668                progname, luser, lhost,
1669                d,
1670                (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1671                luser,
1672                progname,
1673                lhost, luser);
1674     }
1675   else if (!!strcmp (rversion, short_version))
1676     {
1677       /* Warn that the version numbers don't match.
1678        */
1679       sprintf (msg,
1680                "Warning:\n\n"
1681                "This is %s version %s.\n"
1682                "But the xscreensaver managing display \"%s\"\n"
1683                "is version %s.  This could cause problems.\n"
1684               "\n"
1685               "Restart the xscreensaver daemon now?\n",
1686                progname, short_version,
1687                d,
1688                rversion);
1689     }
1690
1691
1692   if (*msg)
1693     warning_dialog (parent, msg, True, 1);
1694
1695   free (msg);
1696 }
1697
1698
1699 /* We use this error handler so that X errors are preceeded by the name
1700    of the program that generated them.
1701  */
1702 static int
1703 demo_ehandler (Display *dpy, XErrorEvent *error)
1704 {
1705   fprintf (stderr, "\nX error in %s:\n", progname);
1706   if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
1707     exit (-1);
1708   else
1709     fprintf (stderr, " (nonfatal.)\n");
1710   return 0;
1711 }
1712
1713
1714 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
1715    of the program that generated them; and also that we can ignore one
1716    particular bogus error message that Gdk madly spews.
1717  */
1718 static void
1719 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
1720                const gchar *message, gpointer user_data)
1721 {
1722   /* Ignore the message "Got event for unknown window: 0x...".
1723      Apparently some events are coming in for the xscreensaver window
1724      (presumably reply events related to the ClientMessage) and Gdk
1725      feels the need to complain about them.  So, just suppress any
1726      messages that look like that one.
1727    */
1728   if (strstr (message, "unknown window"))
1729     return;
1730
1731   fprintf (stderr, "%s: %s-%s: %s%s", blurb(), log_domain,
1732            (log_level == G_LOG_LEVEL_ERROR    ? "error" :
1733             log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
1734             log_level == G_LOG_LEVEL_WARNING  ? "warning" :
1735             log_level == G_LOG_LEVEL_MESSAGE  ? "message" :
1736             log_level == G_LOG_LEVEL_INFO     ? "info" :
1737             log_level == G_LOG_LEVEL_DEBUG    ? "debug" : "???"),
1738            message,
1739            ((!*message || message[strlen(message)-1] != '\n')
1740             ? "\n" : ""));
1741 }
1742
1743
1744 static char *defaults[] = {
1745 #include "XScreenSaver_ad.h"
1746  0
1747 };
1748
1749 #if 0
1750 #ifdef HAVE_CRAPPLET
1751 static struct poptOption crapplet_options[] = {
1752   {NULL, '\0', 0, NULL, 0}
1753 };
1754 #endif /* HAVE_CRAPPLET */
1755 #endif /* 0 */
1756
1757 #define USAGE() \
1758   fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n", \
1759            real_progname)
1760
1761
1762 static void
1763 map_window_cb (GtkWidget *w, gpointer user_data)
1764 {
1765   Boolean oi = initializing_p;
1766   initializing_p = True;
1767   pixmapify_buttons (w);
1768   eschew_gtk_lossage (w);
1769   ensure_selected_item_visible (GTK_WIDGET(name_to_widget(w, "list")));
1770   initializing_p = oi;
1771 }
1772
1773
1774 int
1775 main (int argc, char **argv)
1776 {
1777   XtAppContext app;
1778 # ifdef HAVE_CRAPPLET
1779   GnomeClient *client;
1780   GnomeClientFlags flags;
1781   int init_results;
1782 # endif /* HAVE_CRAPPLET */
1783   prefs_pair Pair, *pair;
1784   saver_preferences P, P2, *p, *p2;
1785   Bool prefs = False;
1786   int i;
1787   Display *dpy;
1788   Widget toplevel_shell;
1789   GtkWidget *gtk_window;
1790   char *real_progname = argv[0];
1791   char *s;
1792
1793   initializing_p = True;
1794
1795   s = strrchr (real_progname, '/');
1796   if (s) real_progname = s+1;
1797
1798   p = &P;
1799   p2 = &P2;
1800   pair = &Pair;
1801   pair->a = p;
1802   pair->b = p2;
1803   memset (p,  0, sizeof (*p));
1804   memset (p2, 0, sizeof (*p2));
1805
1806   global_prefs_pair = pair;  /* I hate C so much... */
1807
1808   progname = real_progname;
1809
1810   short_version = (char *) malloc (5);
1811   memcpy (short_version, screensaver_id + 17, 4);
1812   short_version [4] = 0;
1813
1814
1815   /* Register our error message logger for every ``log domain'' known.
1816      There's no way to do this globally, so I grepped the Gtk/Gdk sources
1817      for all of the domains that seem to be in use.
1818   */
1819   {
1820     const char * const domains[] = { "Gtk", "Gdk", "GLib", "GModule",
1821                                      "GThread", "Gnome", "GnomeUI", 0 };
1822     for (i = 0; domains[i]; i++)
1823       g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
1824   }
1825
1826   /* This is gross, but Gtk understands --display and not -display...
1827    */
1828   for (i = 1; i < argc; i++)
1829     if (argv[i][0] && argv[i][1] && 
1830         !strncmp(argv[i], "-display", strlen(argv[i])))
1831       argv[i] = "--display";
1832
1833
1834   /* We need to parse this arg really early... Sigh. */
1835   for (i = 1; i < argc; i++)
1836     if (argv[i] &&
1837         (!strcmp(argv[i], "--crapplet") ||
1838          !strcmp(argv[i], "--capplet")))
1839       {
1840 # ifdef HAVE_CRAPPLET
1841         int j;
1842         crapplet_p = True;
1843         for (j = i; j < argc; j++)  /* remove it from the list */
1844           argv[j] = argv[j+1];
1845         argc--;
1846
1847 # else  /* !HAVE_CRAPPLET */
1848         fprintf (stderr, "%s: not compiled with --crapplet support\n",
1849                  real_progname[i]);
1850         USAGE ();
1851         exit (1);
1852 # endif /* !HAVE_CRAPPLET */
1853       }
1854
1855   /* Let Gtk open the X connection, then initialize Xt to use that
1856      same connection.  Doctor Frankenstein would be proud.
1857    */
1858 # ifdef HAVE_CRAPPLET
1859   if (crapplet_p)
1860     {
1861       init_results = gnome_capplet_init ("screensaver-properties",
1862                                          short_version,
1863                                          argc, argv, NULL, 0, NULL);
1864
1865       if (init_results < 0)
1866         {
1867 #  if 0
1868           g_error ("An initialization error occurred while "
1869                    "starting xscreensaver-capplet.\n");
1870 #  else  /* !0 */
1871           fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
1872                    real_progname, init_results);
1873           exit (1);
1874 #  endif /* !0 */
1875         }
1876
1877       client = gnome_master_client ();
1878
1879       if (client)
1880         flags = gnome_client_get_flags (client);
1881       else
1882         flags = 0;
1883
1884       if (flags & GNOME_CLIENT_IS_CONNECTED)
1885         {
1886           gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
1887           gnome_client_flush (client);
1888         }
1889     }
1890   else
1891 # endif /* HAVE_CRAPPLET */
1892     {
1893       gtk_init (&argc, &argv);
1894     }
1895
1896
1897   /* We must read exactly the same resources as xscreensaver.
1898      That means we must have both the same progclass *and* progname,
1899      at least as far as the resource database is concerned.  So,
1900      put "xscreensaver" in argv[0] while initializing Xt.
1901    */
1902   argv[0] = "xscreensaver";
1903   progname = argv[0];
1904
1905
1906   /* Teach Xt to use the Display that Gtk/Gdk have already opened.
1907    */
1908   XtToolkitInitialize ();
1909   app = XtCreateApplicationContext ();
1910   dpy = gdk_display;
1911   XtAppSetFallbackResources (app, defaults);
1912   XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
1913   toplevel_shell = XtAppCreateShell (progname, progclass,
1914                                      applicationShellWidgetClass,
1915                                      dpy, 0, 0);
1916
1917   dpy = XtDisplay (toplevel_shell);
1918   db = XtDatabase (dpy);
1919   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
1920   XSetErrorHandler (demo_ehandler);
1921
1922
1923   /* After doing Xt-style command-line processing, complain about any
1924      unrecognized command-line arguments.
1925    */
1926   for (i = 1; i < argc; i++)
1927     {
1928       char *s = argv[i];
1929       if (s[0] == '-' && s[1] == '-')
1930         s++;
1931       if (!strcmp (s, "-prefs"))
1932         prefs = True;
1933       else if (crapplet_p)
1934         /* There are lots of random args that we don't care about when we're
1935            started as a crapplet, so just ignore unknown args in that case. */
1936         ;
1937       else
1938         {
1939           fprintf (stderr, "%s: unknown option: %s\n", real_progname, argv[i]);
1940           USAGE ();
1941           exit (1);
1942         }
1943     }
1944
1945   /* Load the init file, which may end up consulting the X resource database
1946      and the site-wide app-defaults file.  Note that at this point, it's
1947      important that `progname' be "xscreensaver", rather than whatever
1948      was in argv[0].
1949    */
1950   p->db = db;
1951   load_init_file (p);
1952   *p2 = *p;
1953
1954   /* Now that Xt has been initialized, and the resources have been read,
1955      we can set our `progname' variable to something more in line with
1956      reality.
1957    */
1958   progname = real_progname;
1959
1960
1961 #if 0
1962   /* Print out all the resources we read. */
1963   {
1964     XrmName name = { 0 };
1965     XrmClass class = { 0 };
1966     int count = 0;
1967     XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1968                           (POINTER) &count);
1969   }
1970 #endif
1971
1972
1973   /* Intern the atoms that xscreensaver_command() needs.
1974    */
1975   XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
1976   XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
1977   XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
1978   XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
1979   XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
1980   XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
1981   XA_SELECT = XInternAtom (dpy, "SELECT", False);
1982   XA_DEMO = XInternAtom (dpy, "DEMO", False);
1983   XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
1984   XA_BLANK = XInternAtom (dpy, "BLANK", False);
1985   XA_LOCK = XInternAtom (dpy, "LOCK", False);
1986   XA_EXIT = XInternAtom (dpy, "EXIT", False);
1987   XA_RESTART = XInternAtom (dpy, "RESTART", False);
1988
1989
1990   /* Create the window and all its widgets.
1991    */
1992   gtk_window = create_xscreensaver_demo ();
1993   toplevel_widget = gtk_window;
1994
1995   /* Set the window's title. */
1996   {
1997     char title[255];
1998     char *v = (char *) strdup(strchr(screensaver_id, ' '));
1999     char *s1, *s2, *s3, *s4;
2000     s1 = (char *) strchr(v,  ' '); s1++;
2001     s2 = (char *) strchr(s1, ' ');
2002     s3 = (char *) strchr(v,  '('); s3++;
2003     s4 = (char *) strchr(s3, ')');
2004     *s2 = 0;
2005     *s4 = 0;
2006     sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3);
2007     gtk_window_set_title (GTK_WINDOW (gtk_window), title);
2008     free (v);
2009   }
2010
2011   /* Various other widget initializations...
2012    */
2013   gtk_signal_connect (GTK_OBJECT (gtk_window), "delete_event",
2014                       GTK_SIGNAL_FUNC (wm_close_cb), NULL);
2015
2016   populate_hack_list (gtk_window, pair);
2017   populate_prefs_page (gtk_window, pair);
2018   sensitize_demo_widgets (gtk_window, False);
2019   fix_text_entry_sizes (gtk_window);
2020   scroll_to_current_hack (gtk_window, pair);
2021
2022   gtk_signal_connect (
2023               GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "list")),
2024               "map", GTK_SIGNAL_FUNC(map_window_cb), 0);
2025
2026
2027   /* Handle the -prefs command-line argument. */
2028   if (prefs)
2029     {
2030       GtkNotebook *notebook =
2031         GTK_NOTEBOOK (name_to_widget (gtk_window, "notebook"));
2032       gtk_notebook_set_page (notebook, 1);
2033     }
2034
2035 # ifdef HAVE_CRAPPLET
2036   if (crapplet_p)
2037     {
2038       GtkWidget *capplet;
2039       GtkWidget *top_vbox;
2040
2041       capplet = capplet_widget_new ();
2042
2043       top_vbox = GTK_BIN (gtk_window)->child;
2044
2045       gtk_widget_ref (top_vbox);
2046       gtk_container_remove (GTK_CONTAINER (gtk_window), top_vbox);
2047       GTK_OBJECT_SET_FLAGS (top_vbox, GTK_FLOATING);
2048
2049       /* This is a crock, but otherwise, the Control Center expands to
2050          be as tall as the screen. */
2051       gtk_window_set_default_size (GTK_WINDOW (top_vbox), 600, 400);
2052
2053       /* In crapplet-mode, take off the menubar. */
2054       gtk_widget_hide (name_to_widget (gtk_window, "menubar"));
2055
2056       gtk_container_add (GTK_CONTAINER (capplet), top_vbox);
2057       gtk_widget_show (capplet);
2058       gtk_widget_hide (gtk_window);
2059
2060       /* Hook up the Control Center's redundant Help button, too. */
2061       gtk_signal_connect (GTK_OBJECT (capplet), "help",
2062                           GTK_SIGNAL_FUNC (doc_menu_cb), 0);
2063
2064       /* Issue any warnings about the running xscreensaver daemon. */
2065       the_network_is_not_the_computer (top_vbox);
2066     }
2067   else
2068 # endif /* HAVE_CRAPPLET */
2069     {
2070       gtk_widget_show (gtk_window);
2071
2072       /* Issue any warnings about the running xscreensaver daemon. */
2073       the_network_is_not_the_computer (gtk_window);
2074     }
2075
2076   /* Run the Gtk event loop, and not the Xt event loop.  This means that
2077      if there were Xt timers or fds registered, they would never get serviced,
2078      and if there were any Xt widgets, they would never have events delivered.
2079      Fortunately, we're using Gtk for all of the UI, and only initialized
2080      Xt so that we could process the command line and use the X resource
2081      manager.
2082    */
2083   initializing_p = False;
2084
2085 # ifdef HAVE_CRAPPLET
2086   if (crapplet_p)
2087     capplet_gtk_main ();
2088   else
2089 # endif /* HAVE_CRAPPLET */
2090     gtk_main ();
2091
2092   exit (0);
2093 }
2094
2095 #endif /* HAVE_GTK -- whole file */