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