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