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