bec72e0e8a4bf52185bc086063712cff504ae1e8
[xscreensaver] / driver / demo.c
1 /* demo.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
18 #ifdef FORCE_ATHENA
19 # undef HAVE_MOTIF
20 # define HAVE_ATHENA 1
21 #endif
22 #ifdef FORCE_MOTIF
23 # undef HAVE_ATHENA
24 # define HAVE_MOTIF 1
25 #endif
26
27 /* Only one, please. */
28 #ifdef HAVE_MOTIF
29 # undef HAVE_ATHENA
30 #endif
31 #ifdef HAVE_ATHENA
32 # undef HAVE_MOTIF
33 #endif
34
35
36 #include <stdlib.h>
37
38 #ifdef HAVE_UNISTD_H
39 # include <unistd.h>
40 #endif
41
42 #ifndef VMS
43 # include <pwd.h>               /* for getpwuid() */
44 #else /* VMS */
45 # include "vms-pwd.h"
46 #endif /* VMS */
47
48 #ifdef HAVE_UNAME
49 # include <sys/utsname.h>       /* for uname() */
50 #endif /* HAVE_UNAME */
51
52 #include <stdio.h>
53
54 #include <X11/Xproto.h>         /* for CARD32 */
55 #include <X11/Xatom.h>          /* for XA_INTEGER */
56 #include <X11/Intrinsic.h>
57 #include <X11/StringDefs.h>
58
59 /* We don't actually use any widget internals, but these are included
60    so that gdb will have debug info for the widgets... */
61 #include <X11/IntrinsicP.h>
62 #include <X11/ShellP.h>
63
64 #ifdef HAVE_XMU
65 # ifndef VMS
66 #  include <X11/Xmu/Error.h>
67 # else /* VMS */
68 #  include <Xmu/Error.h>
69 # endif
70 #else
71 # include "xmu.h"
72 #endif
73
74
75 #ifdef HAVE_MOTIF
76 # include <Xm/Xm.h>
77 # include <Xm/Text.h>
78 # include <Xm/List.h>
79 # include <Xm/ToggleB.h>
80 # include <Xm/MessageB.h>
81 # include <Xm/LabelG.h>
82 # include <Xm/RowColumn.h>
83
84 #elif defined(HAVE_ATHENA)
85   /* Athena demo code contributed by Jon A. Christopher <jac8782@tamu.edu> */
86   /* Copyright 1997, with the same permissions as above. */
87 # include <X11/Shell.h>
88 # include <X11/Xaw/Form.h>
89 # include <X11/Xaw/Box.h>
90 # include <X11/Xaw/List.h>
91 # include <X11/Xaw/Command.h>
92 # include <X11/Xaw/Toggle.h>
93 # include <X11/Xaw/Viewport.h>
94 # include <X11/Xaw/Dialog.h>
95 # include <X11/Xaw/Scrollbar.h>
96 # include <X11/Xaw/Text.h>
97
98 #endif /* HAVE_ATHENA */
99
100 #include "version.h"
101 #include "prefs.h"
102 #include "resources.h"          /* for parse_time() */
103 #include "visual.h"             /* for has_writable_cells() */
104 #include "remote.h"             /* for xscreensaver_command() */
105 #include "usleep.h"
106
107 #include <stdio.h>
108 #include <string.h>
109 #include <ctype.h>
110
111 #define WIDGET Widget
112 #define POINTER XtPointer
113
114
115 char *progname = 0;
116 char *progclass = "XScreenSaver";
117 XrmDatabase db;
118
119 typedef struct {
120   saver_preferences *a, *b;
121 } prefs_pair;
122
123
124 char *blurb (void) { return progname; }
125
126 static void run_hack (Display *dpy, int n);
127
128 #ifdef HAVE_ATHENA
129 static saver_preferences *global_prefs_kludge = 0;    /* I hate C so much... */
130 #endif /* HAVE_ATHENA */
131
132 static char *short_version = 0;
133
134 Atom XA_VROOT;
135 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
136 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
137 Atom XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
138
139 extern void create_demo_dialog (Widget, Visual *, Colormap);
140 extern void create_preferences_dialog (Widget, Visual *, Colormap);
141
142 extern WIDGET demo_dialog;
143 extern WIDGET label1;
144 extern WIDGET text_line;
145 extern WIDGET text_activate;
146 extern WIDGET demo_form;
147 extern WIDGET demo_list;
148 extern WIDGET next;
149 extern WIDGET prev;
150 extern WIDGET done;
151 extern WIDGET restart;
152 extern WIDGET edit;
153
154 extern WIDGET preferences_dialog;
155 extern WIDGET preferences_form;
156 extern WIDGET prefs_done;
157 extern WIDGET prefs_cancel;
158 extern WIDGET timeout_text;
159 extern WIDGET cycle_text;
160 extern WIDGET fade_text;
161 extern WIDGET fade_ticks_text;
162 extern WIDGET lock_timeout_text;
163 extern WIDGET passwd_timeout_text;
164 extern WIDGET verbose_toggle;
165 extern WIDGET install_cmap_toggle;
166 extern WIDGET fade_toggle;
167 extern WIDGET unfade_toggle;
168 extern WIDGET lock_toggle;
169
170
171 #ifdef HAVE_MOTIF
172
173 # define set_toggle_button_state(toggle,state) \
174   XmToggleButtonSetState ((toggle), (state), True)
175 # define set_text_string(text_widget,string) \
176   XmTextSetString ((text_widget), (string))
177 # define add_button_callback(button,cb,arg) \
178   XtAddCallback ((button), XmNactivateCallback, (cb), (arg))
179 # define add_toggle_callback(button,cb,arg) \
180   XtAddCallback ((button), XmNvalueChangedCallback, (cb), (arg))
181 # define add_text_callback add_toggle_callback
182 # define disable_widget(widget) \
183   XtVaSetValues((widget), XtNsensitive, False, 0)
184 # define widget_name(widget) XtName(widget)
185 # define widget_display(widget) XtDisplay(widget)
186 # define widget_screen(widget) XtScreen(widget)
187 # define CB_ARGS(a,b,c) (a,b,c)
188
189 #elif defined(HAVE_ATHENA)
190
191 # define set_toggle_button_state(toggle,state) \
192   XtVaSetValues((toggle), XtNstate, (state),  0)
193 # define set_text_string(text_widget,string) \
194   XtVaSetValues ((text_widget), XtNvalue, (string), 0)
195 # define add_button_callback(button,cb,arg) \
196   XtAddCallback ((button), XtNcallback, (cb), (arg))
197 # define add_toggle_callback add_button_callback
198 # define add_text_callback(b,c,a) ERROR!
199 # define disable_widget(widget) \
200   XtVaSetValues((widget), XtNsensitive, False, 0)
201 # define widget_name(widget) XtName(widget)
202 # define widget_display(widget) XtDisplay(widget)
203 # define widget_screen(widget) XtScreen(widget)
204 # define CB_ARGS(a,b,c) (a,b,c)
205
206 #endif /* HAVE_ATHENA */
207
208
209
210
211 static char *
212 get_text_string (WIDGET text_widget)
213 {
214 #ifdef HAVE_MOTIF
215   return XmTextGetString (text_widget);
216 #elif defined(HAVE_ATHENA)
217   char *string = 0;
218   if (XtIsSubclass(text_widget, textWidgetClass))
219     XtVaGetValues (text_widget, XtNstring, &string, 0);
220   else if (XtIsSubclass(text_widget, dialogWidgetClass))
221     XtVaGetValues (text_widget, XtNvalue, &string, 0);
222   else
223     string = 0;
224
225   return string;
226 #endif /* HAVE_ATHENA */
227 }
228
229
230 static char *
231 get_label_string (WIDGET label_widget)
232 {
233 #ifdef HAVE_MOTIF
234   char *label = 0;
235   XmString xm_label = 0;
236   XtVaGetValues (label_widget, XmNlabelString, &xm_label, 0);
237   if (!xm_label)
238     return 0;
239   XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
240   return label;
241 #elif defined(HAVE_ATHENA)
242   char *label = 0;
243   XtVaGetValues (label_widget, XtNlabel, &label, 0);
244   return (label ? strdup(label) : 0);
245 #endif /* HAVE_ATHENA */
246 }
247
248
249 static void
250 set_label_string (WIDGET label_widget, char *string)
251 {
252 #ifdef HAVE_MOTIF
253   XmString xm_string = XmStringCreate (string, XmSTRING_DEFAULT_CHARSET);
254   XtVaSetValues (label_widget, XmNlabelString, xm_string, 0);
255   XmStringFree (xm_string);
256 #elif defined(HAVE_ATHENA)
257   XtVaSetValues (label_widget, XtNlabel, string, 0);
258 #endif /* HAVE_ATHENA */
259 }
260
261
262 /* Given a label widget that has a %s in it, do the printf thing.
263    If the label's string is obviously wrong, complain about resource lossage.
264  */
265 static void
266 format_into_label (WIDGET label, const char *arg)
267 {
268   char *text = get_label_string (label);
269   char *buf = (char *) malloc ((text ? strlen(text) : 0) + strlen(arg) + 100);
270
271   if (!text || !*text || !strcmp (text, widget_name (label)))
272       strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
273     else
274       sprintf (buf, text, arg);
275
276     set_label_string (label, buf);
277     free (buf);
278     XtFree (text);
279 }
280
281
282 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
283  */
284 static void
285 ensure_selected_item_visible (WIDGET list)
286 {
287 #ifdef HAVE_MOTIF
288   int *pos_list = 0;
289   int pos_count = 0;
290   if (XmListGetSelectedPos (list, &pos_list, &pos_count) && pos_count > 0)
291     {
292       int top = -2;
293       int visible = 0;
294       XtVaGetValues (list,
295                      XmNtopItemPosition, &top,
296                      XmNvisibleItemCount, &visible,
297                      0);
298       if (pos_list[0] >= top + visible)
299         {
300           int pos = pos_list[0] - visible + 1;
301           if (pos < 0) pos = 0;
302           XmListSetPos (list, pos);
303         }
304       else if (pos_list[0] < top)
305         {
306           XmListSetPos (list, pos_list[0]);
307         }
308     }
309   if (pos_list)
310     XtFree ((char *) pos_list);
311
312 #elif defined(HAVE_ATHENA)
313 # ifdef HAVE_XawViewportSetCoordinates
314
315   int margin = 16;      /* should be line height or something. */
316   int count = 0;
317   int pos;
318   Dimension list_h = 0, vp_h = 0;
319   Dimension top_margin = 4;  /* I don't know where this value comes from */
320   Position vp_x = 0, vp_y = 0, current_y;
321   double cratio;
322   Widget viewport = XtParent(demo_list);
323   Widget sb = (viewport ? XtNameToWidget(viewport, "*vertical") : 0);
324   float sb_top = 0, sb_size = 0;
325   XawListReturnStruct *current = XawListShowCurrent(demo_list);
326   if (!current || !sb) return;
327
328   XtVaGetValues(demo_list,
329                 XtNnumberStrings, &count,
330                 XtNheight, &list_h,
331                 0);
332   if (count < 2 || list_h < 10) return;
333
334   XtVaGetValues(viewport, XtNheight, &vp_h, XtNx, &vp_x, XtNy, &vp_y, 0);
335   if (vp_h < 10) return;
336
337   XtVaGetValues(sb, XtNtopOfThumb, &sb_top, XtNshown, &sb_size, 0);
338   if (sb_size <= 0) return;
339
340   pos = current->list_index;
341   cratio = ((double) pos)  / ((double) count);
342   current_y = (cratio * list_h);
343
344   if (cratio < sb_top ||
345       cratio > sb_top + sb_size)
346     {
347       if (cratio < sb_top)
348         current_y -= (vp_h - margin - margin);
349       else
350         current_y -= margin;
351
352       if ((long)current_y >= (long) list_h)
353         current_y = (Position) ((long)list_h - (long)vp_h);
354
355       if ((long)current_y < (long)top_margin)
356         current_y = (Position)top_margin;
357
358       XawViewportSetCoordinates (viewport, vp_x, current_y);
359     }
360 # endif /* HAVE_XawViewportSetCoordinates */
361 #endif /* HAVE_ATHENA */
362 }
363
364
365 #ifdef HAVE_ATHENA
366 static void
367 set_hack_list (Widget demo_list, saver_preferences *p)
368 {
369   char **strings = (char **) calloc (sizeof (char *), p->screenhacks_count);
370   int i;
371   for (i = 0; i < p->screenhacks_count; i++)
372     strings[i] = format_hack (p->screenhacks[i], False);
373   XtVaSetValues (demo_list,
374                  XtNlist, strings,
375                  XtNnumberStrings, p->screenhacks_count,
376                  0);
377 # if 0
378   for (i = 0; i < p->screenhacks_count; i++)
379     {
380       free (strings[i]);
381       strings[i] = (char *) 0xDEADBEEF;
382     }
383   free (strings);
384 # endif
385 }
386 #endif /* HAVE_ATHENA */
387
388
389
390 /* Callback for the text area:
391    - note the text the user has entered;
392    - change the corresponding element in `screenhacks';
393    - write the .xscreensaver file;
394    - tell the xscreensaver daemon to run that hack.
395  */
396 static void
397 text_cb (WIDGET text_widget, POINTER client_data, POINTER call_data)
398 {
399   saver_preferences *p = (saver_preferences *) client_data;
400   char *new_text = get_text_string (text_widget);
401   Display *dpy = widget_display (text_widget);
402   Bool save = TRUE;
403
404   int hack_number = -1;         /* 0-based */
405
406 #ifdef HAVE_ATHENA
407   XawListReturnStruct *current = XawListShowCurrent(demo_list);
408   hack_number = current->list_index;
409 #elif defined(HAVE_MOTIF)
410   int *pos_list = 0;
411   int pos_count = 0;
412   if (XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
413     hack_number = pos_list[0] - 1;
414   if (pos_list)
415     XtFree ((char *) pos_list);
416 #endif /* HAVE_ATHENA */
417
418   ensure_selected_item_visible (demo_list);
419
420   if (hack_number < 0 || hack_number >= p->screenhacks_count)
421     {
422       set_text_string (text_widget, "");
423       XBell (XtDisplay (text_widget), 0);
424     }
425   else
426     {
427       screenhack *new_hack = parse_screenhack (new_text);
428       if (p->screenhacks [hack_number])
429         free_screenhack (p->screenhacks [hack_number]);
430       p->screenhacks [hack_number] = new_hack;
431
432 #ifdef HAVE_MOTIF
433
434       XmListDeselectAllItems (demo_list);
435       {
436         XmString xmstr = XmStringCreate (new_text, XmSTRING_DEFAULT_CHARSET);
437         XmListReplaceItemsPos (demo_list, &xmstr, 1, hack_number+1);
438         XmStringFree (xmstr);
439       }
440       XmListSelectPos (demo_list, hack_number+1, True);
441
442 #elif defined(HAVE_ATHENA)
443
444       {
445         Widget vp = XtParent(demo_list);
446         Widget sb = (vp ? XtNameToWidget(vp, "*vertical") : 0);
447         Dimension list_h = 0;
448         Position vp_x = 0, vp_y = 0;
449         float sb_top = 0;
450
451         XawListUnhighlight (demo_list);
452         XtVaGetValues (vp, XtNx, &vp_x, 0);
453         XtVaGetValues (sb, XtNtopOfThumb, &sb_top, 0);
454         XtVaGetValues (demo_list, XtNheight, &list_h, 0);
455         vp_y = (sb_top * list_h);
456         set_hack_list (demo_list, p);
457         XawViewportSetCoordinates (vp, vp_x, vp_y);
458         XawListHighlight (demo_list, hack_number);
459       }
460
461 #endif /* HAVE_ATHENA */
462
463       if (save)
464         write_init_file (p, short_version);
465
466       XSync (dpy, False);
467       usleep (500000);          /* give the disk time to settle down */
468
469       run_hack (dpy, hack_number+1);
470     }
471 }
472
473
474 #ifdef HAVE_ATHENA
475 /* Bend over backwards to make hitting Return in the text field do the
476    right thing. 
477    */
478 static void text_enter (Widget w, XEvent *event, String *av, Cardinal *ac)
479 {
480   text_cb (w, global_prefs_kludge, 0);    /* I hate C so much... */
481 }
482
483 static XtActionsRec actions[] = {{"done",      text_enter}
484                                 };
485 static char translations[] = ("<Key>Return:     done()\n"
486                               "<Key>Linefeed:   done()\n"
487                               "Ctrl<Key>M:      done()\n"
488                               "Ctrl<Key>J:      done()\n");
489 #endif /* HAVE_ATHENA */
490
491
492 /* Callback for the Run Next button.
493  */
494 static void
495 next_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
496 {
497 #ifdef HAVE_ATHENA
498   XawListReturnStruct *current = XawListShowCurrent(demo_list);
499   int cnt;
500   XtVaGetValues (demo_list, XtNnumberStrings, &cnt, 0);
501   if (current->list_index == XAW_LIST_NONE ||
502       current->list_index + 1 >= cnt)
503     current->list_index = 0;
504   else
505     current->list_index++;
506   XawListHighlight(demo_list, current->list_index);
507
508   ensure_selected_item_visible (demo_list);
509   current = XawListShowCurrent(demo_list);
510   XtVaSetValues(text_line, XtNstring, current->string, 0);
511
512   run_hack (XtDisplay (button), current->list_index + 1);
513
514 #elif defined(HAVE_MOTIF)
515
516   saver_preferences *p = (saver_preferences *) client_data;
517   int *pos_list = 0;
518   int pos_count = 0;
519   int pos;
520   if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
521     {
522       pos = 1;
523       XmListDeselectAllItems (demo_list);       /* LessTif lossage */
524       XmListSelectPos (demo_list, pos, True);
525     }
526   else
527     {
528       pos = pos_list[0] + 1;
529       if (pos > p->screenhacks_count)
530         pos = 1;
531       XmListDeselectAllItems (demo_list);       /* LessTif lossage */
532       XmListSelectPos (demo_list, pos, True);
533     }
534      
535   ensure_selected_item_visible (demo_list);
536   run_hack (XtDisplay (button), pos);
537   if (pos_list)
538     XtFree ((char *) pos_list);
539
540 #endif /* HAVE_MOTIF */
541 }
542
543
544 /* Callback for the Run Previous button.
545  */
546 static void
547 prev_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
548 {
549 #ifdef HAVE_ATHENA
550   XawListReturnStruct *current = XawListShowCurrent(demo_list);
551   int cnt;
552   XtVaGetValues (demo_list, XtNnumberStrings, &cnt, 0);
553   if (current->list_index == XAW_LIST_NONE ||
554       current->list_index <= 0)
555     current->list_index = cnt-1;
556   else
557     current->list_index--;
558   XawListHighlight(demo_list, current->list_index);
559
560   ensure_selected_item_visible (demo_list);
561   current = XawListShowCurrent(demo_list);
562   XtVaSetValues(text_line, XtNstring, current->string, 0);
563
564   run_hack (XtDisplay (button), current->list_index + 1);
565
566 #elif defined(HAVE_MOTIF)
567
568   saver_preferences *p = (saver_preferences *) client_data;
569   int *pos_list = 0;
570   int pos_count = 0;
571   int pos;
572   if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
573     {
574       pos = p->screenhacks_count;
575       XmListDeselectAllItems (demo_list);       /* LessTif lossage */
576       XmListSelectPos (demo_list, pos, True);
577     }
578   else
579     {
580       pos = pos_list[0] - 1;
581       if (pos == 0)
582         pos = p->screenhacks_count;
583       XmListDeselectAllItems (demo_list);       /* LessTif lossage */
584       XmListSelectPos (demo_list, pos, True);
585     }
586      
587   ensure_selected_item_visible (demo_list);
588   run_hack (XtDisplay (button), pos);
589   if (pos_list)
590     XtFree ((char *) pos_list);
591
592 #endif /* HAVE_MOTIF */
593 }
594
595
596 /* Callback run when a list element is double-clicked.
597  */
598 static void
599 select_cb (WIDGET button, POINTER client_data, POINTER call_data)
600 {
601 /*  saver_preferences *p = (saver_preferences *) client_data; */
602
603 #ifdef HAVE_ATHENA
604   XawListReturnStruct *item = (XawListReturnStruct*)call_data;
605   XtVaSetValues(text_line, XtNstring, item->string, 0);
606   run_hack (XtDisplay (button), item->list_index + 1);
607
608 #elif defined(HAVE_MOTIF)
609   XmListCallbackStruct *lcb = (XmListCallbackStruct *) call_data;
610   char *string = 0;
611   if (lcb->item)
612     XmStringGetLtoR (lcb->item, XmSTRING_DEFAULT_CHARSET, &string);
613   set_text_string (text_line, (string ? string : ""));
614
615   if (lcb->reason == XmCR_DEFAULT_ACTION && string)
616     run_hack (XtDisplay (button), lcb->item_position);
617
618   if (string)
619     XtFree (string);
620
621 #endif /* HAVE_MOTIF */
622 }
623
624
625 static void pop_preferences_dialog (prefs_pair *pair);
626 static void make_preferences_dialog (prefs_pair *pair, Widget parent);
627
628 /* Callback for the Preferences button.
629  */
630 static void
631 preferences_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
632 {
633   prefs_pair *pair = (prefs_pair *) client_data;
634   Widget parent = button;
635
636   do {
637     parent = XtParent(parent);
638   } while (XtParent(parent));
639
640   if (! preferences_dialog)
641     make_preferences_dialog (pair, parent);
642   *pair->b = *pair->a;
643   pop_preferences_dialog (pair);
644 }
645
646
647 /* Callback for the Quit button.
648  */
649 static void
650 quit_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
651 {
652   /* Save here?  Right now we don't need to, because we save every time
653      the text field is edited, or the Preferences OK button is pressed.
654   */
655   exit (0);
656 }
657
658
659 /* Callback for the (now unused) Restart button.
660  */
661 static void
662 restart_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
663 {
664   xscreensaver_command (widget_display (button), XA_RESTART, 0, False);
665 }
666
667
668 /* Finds the number of the last hack to run, and makes that item be
669    selected by default.
670  */
671 static void
672 scroll_to_current_hack (WIDGET dialog)
673 {
674   Atom type;
675   int format;
676   unsigned long nitems, bytesafter;
677   CARD32 *data = 0;
678   Display *dpy = widget_display (dialog);
679   int hack = 0;
680
681   if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
682                           XA_SCREENSAVER_STATUS,
683                           0, 3, False, XA_INTEGER,
684                           &type, &format, &nitems, &bytesafter,
685                           (unsigned char **) &data)
686       == Success
687       && type == XA_INTEGER
688       && nitems >= 3
689       && data)
690     hack = (int) data[2];
691
692   if (data) free (data);
693
694   if (hack <= 0)
695     return;
696
697 #ifdef HAVE_MOTIF
698   XmListDeselectAllItems (demo_list);   /* LessTif lossage */
699   XmListSelectPos (demo_list, hack, False);
700   ensure_selected_item_visible (demo_list);
701
702 #elif defined(HAVE_ATHENA)
703   XawListUnhighlight (demo_list);
704   XawListHighlight (demo_list, hack - 1);
705
706 #endif /* HAVE_ATHENA */
707 }
708
709
710 static void
711 pop_up_dialog_box (WIDGET dialog, WIDGET form)
712 {
713 #ifdef HAVE_ATHENA
714   XtRealizeWidget (dialog);
715   XtPopup (dialog, XtGrabNone);
716 #elif defined(HAVE_MOTIF)
717   XtRealizeWidget (form);
718   XtManageChild (form);
719
720   /* Motif likes to make the dialog wider than the screen; throttle it. */
721   {
722     Dimension w=0, h=0, bw=0;
723     Dimension max_w;
724     Screen *screen = 0;
725     XtVaGetValues (dialog, XtNscreen, &screen, 0);
726     max_w = WidthOfScreen (screen) * 0.8;
727     XtVaGetValues(dialog, XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
728     if (w > max_w)
729       XtResizeWidget(dialog, max_w, h, bw);
730   }
731 #endif /* HAVE_MOTIF */
732
733   XMapRaised (XtDisplay (dialog), XtWindow (dialog));
734 }
735
736
737 static void
738 make_demo_dialog (Widget toplevel_shell, prefs_pair *pair)
739 {
740   saver_preferences *p =  pair->a;
741   /* saver_preferences *p2 = pair->b; */
742   Widget parent = toplevel_shell;
743 #ifdef HAVE_MOTIF
744   screenhack **hacks = p->screenhacks;
745 #endif /* HAVE_MOTIF */
746
747   create_demo_dialog (parent,
748                       DefaultVisualOfScreen (widget_screen (parent)),
749                       DefaultColormapOfScreen (widget_screen (parent)));
750
751   format_into_label (label1, short_version);
752   add_button_callback (next,    next_cb,        (POINTER) p);
753   add_button_callback (prev,    prev_cb,        (POINTER) p);
754   add_button_callback (done,    quit_cb,        (POINTER) p);
755   if (restart)
756     add_button_callback(restart,restart_cb,     (POINTER) p);
757   add_button_callback (edit,    preferences_cb, (POINTER) pair);
758
759 #ifdef HAVE_MOTIF
760   XtAddCallback (demo_list, XmNbrowseSelectionCallback,
761                  select_cb, (POINTER) p);
762   XtAddCallback (demo_list, XmNdefaultActionCallback,
763                  select_cb, (POINTER) p);
764   XtAddCallback (text_line, XmNactivateCallback, text_cb, (POINTER) p);
765
766   if (hacks)
767     for (; *hacks; hacks++)
768       {
769         char *hs = format_hack (*hacks, False);
770         XmString xmstr = XmStringCreate (hs, XmSTRING_DEFAULT_CHARSET);
771         XmListAddItem (demo_list, xmstr, 0);
772         XmStringFree (xmstr);
773         free (hs);
774       }
775
776 #elif defined(HAVE_ATHENA)
777
778   /* Hook up the text line. */
779
780   XtAppAddActions(XtWidgetToApplicationContext(text_line),
781                   actions, XtNumber(actions));
782   XtOverrideTranslations(text_line, XtParseTranslationTable(translations));
783
784
785   /* Must realize the widget before populating the list, or the dialog
786      will be as wide as the longest string.
787   */
788   XtRealizeWidget (demo_dialog);
789
790   set_hack_list (demo_list, p);
791   XtAddCallback (demo_list, XtNcallback, select_cb, p);
792
793   /* Now that we've populated the list, make sure that the list is as
794      wide as the dialog itself.
795   */
796   {
797     Widget viewport = XtParent(demo_list);
798     Widget subform = XtParent(viewport);
799     Widget box = XtNameToWidget(demo_dialog, "*box");
800     Widget label1 = XtNameToWidget(demo_dialog, "*label1");
801     Widget label2 = XtNameToWidget(demo_dialog, "*label2");
802     Dimension x=0, y=0, w=0, h=0, bw=0, w2=0;
803     XtVaGetValues(subform,
804                   XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
805     XtVaGetValues(box, XtNwidth, &w2, 0);
806     if (w2 != w)
807       XtResizeWidget(subform, w2, h, bw);
808
809     /* Why isn't the viewport getting centered? */
810     XtVaGetValues(viewport,
811                   XtNx, &x, XtNy, &y, XtNheight, &h, XtNborderWidth, &bw, 0);
812     XtConfigureWidget(viewport, x, y, w2-x-x, h, bw);
813
814     /* And the text line, too. */
815     XtVaGetValues(text_line,
816                   XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
817     XtVaGetValues(viewport, XtNwidth, &w2, 0);
818     if (w2 != w)
819       XtResizeWidget(text_line, w2, h, bw);
820
821     /* And the labels too. */
822     XtVaGetValues(label1,
823                   XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
824     if (w2 != w)
825       XtResizeWidget(label1, w2, h, bw);
826
827     XtVaGetValues(label2,
828                   XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
829     if (w2 != w)
830       XtResizeWidget(label2, w2, h, bw);
831
832   }
833
834 #endif /* HAVE_ATHENA */
835
836   scroll_to_current_hack (demo_dialog);
837
838   pop_up_dialog_box(demo_dialog, demo_form);
839
840 #if defined(HAVE_ATHENA)
841   /* For Athena and Gtk, have to do this after the dialog is managed. */
842   ensure_selected_item_visible (demo_list);
843 #endif /* HAVE_ATHENA */
844 }
845
846 \f
847 /* the Preferences dialog
848  */
849
850 /* Helper for the text fields that contain time specifications:
851    this parses the text, and does error checking.
852  */
853 static void 
854 hack_time_text (Display *dpy, char *line, Time *store, Bool sec_p)
855 {
856   if (*line)
857     {
858       int value;
859       value = parse_time (line, sec_p, True);
860       value *= 1000;    /* Time measures in microseconds */
861       if (value < 0)
862         /*XBell (dpy, 0)*/;
863       else
864         *store = value;
865     }
866 }
867
868
869 /* Callback for text fields that hold a time that default to seconds,
870    when not fully spelled out.  client_data is a Time* where the value goes.
871  */
872 static void
873 prefs_sec_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
874 {
875   hack_time_text (widget_display (button), get_text_string (button),
876                   (Time *) client_data, True);
877 }
878
879
880 /* Callback for text fields that hold a time that default to minutes,
881    when not fully spelled out.  client_data is an Time* where the value goes.
882  */
883 static void
884 prefs_min_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
885 {
886   hack_time_text (widget_display (button), get_text_string (button),
887                   (Time *) client_data, False);
888 }
889
890
891 /* Callback for text fields that hold an integer value.
892    client_data is an int* where the value goes.
893  */
894 static void
895 prefs_int_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
896 {
897   char *line = get_text_string (button);
898   int *store = (int *) client_data;
899   unsigned int value;
900   char c;
901   if (! *line)
902     ;
903   else if (sscanf (line, "%u%c", &value, &c) != 1)
904     XBell (XtDisplay (button), 0);
905   else
906     *store = value;
907 }
908
909
910 /* Callback for toggle buttons.  client_data is a Bool* where the value goes.
911  */
912 static void
913 prefs_bool_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER call_data)
914 {
915   Bool *store = (Bool *) client_data;
916 #ifdef HAVE_MOTIF
917   *store = ((XmToggleButtonCallbackStruct *) call_data)->set;
918 #elif defined(HAVE_ATHENA)
919   Boolean state = FALSE;
920   XtVaGetValues (button, XtNstate, &state, 0);
921   *store = state;
922 #endif /* HAVE_ATHENA */
923 }
924
925
926 /* Callback for the Cancel button on the Preferences dialog.
927  */
928 static void
929 prefs_cancel_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
930 {
931   XtDestroyWidget (preferences_dialog);
932   preferences_dialog = 0;
933   XMapRaised (XtDisplay (demo_dialog), XtWindow (demo_dialog));
934 }
935
936
937 /* Callback for the OK button on the Preferences dialog.
938  */
939 static void
940 prefs_ok_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER call_data)
941 {
942   prefs_pair *pair = (prefs_pair *) client_data;
943   saver_preferences *p =  pair->a;
944   saver_preferences *p2 = pair->b;
945
946   prefs_cancel_cb CB_ARGS(button, client_data, call_data);
947
948 #ifdef HAVE_ATHENA
949   /* Athena doesn't let us put callbacks on these widgets, so run
950      all the callbacks by hand when OK is pressed. */
951   prefs_min_cb (timeout_text,        (POINTER) &p2->timeout,        0);
952   prefs_min_cb (cycle_text,          (POINTER) &p2->cycle,          0);
953   prefs_sec_cb (fade_text,           (POINTER) &p2->fade_seconds,   0);
954   prefs_int_cb (fade_ticks_text,     (POINTER) &p2->fade_ticks,     0);
955   prefs_min_cb (lock_timeout_text,   (POINTER) &p2->lock_timeout,   0);
956   prefs_sec_cb (passwd_timeout_text, (POINTER) &p2->passwd_timeout, 0);
957 #endif /* HAVE_ATHENA */
958
959   p->timeout        = p2->timeout;
960   p->cycle          = p2->cycle;
961   p->lock_timeout   = p2->lock_timeout;
962   p->passwd_timeout = p2->passwd_timeout;
963   p->fade_seconds   = p2->fade_seconds;
964   p->fade_ticks     = p2->fade_ticks;
965   p->verbose_p      = p2->verbose_p;
966   p->install_cmap_p = p2->install_cmap_p;
967   p->fade_p         = p2->fade_p;
968   p->unfade_p       = p2->unfade_p;
969   p->lock_p         = p2->lock_p;
970
971   write_init_file (p, short_version);
972 }
973
974
975 static void
976 make_preferences_dialog (prefs_pair *pair, Widget parent)
977 {
978   saver_preferences *p =  pair->a;
979   saver_preferences *p2 = pair->b;
980
981   Screen *screen = widget_screen (parent);
982   Display *dpy = widget_display (parent);
983
984   *p2 = *p;     /* copy all slots of p into p2. */
985
986   create_preferences_dialog (parent,
987                              DefaultVisualOfScreen (screen),
988                              DefaultColormapOfScreen (screen));
989
990   add_button_callback (prefs_done,   prefs_ok_cb,     (POINTER) pair);
991   add_button_callback (prefs_cancel, prefs_cancel_cb, 0);
992
993 #define CB(widget,type,slot) \
994         add_text_callback ((widget), (type), (POINTER) (slot))
995 #define CBT(widget,type,slot) \
996         add_toggle_callback ((widget), (type), (POINTER) (slot))
997
998 #ifndef HAVE_ATHENA
999   /* When using Athena widgets, we can't set callbacks for these,
1000      so in that case, we run them by hand when "OK" is pressed. */
1001   CB (timeout_text,             prefs_min_cb,  &p2->timeout);
1002   CB (cycle_text,               prefs_min_cb,  &p2->cycle);
1003   CB (fade_text,                prefs_sec_cb,  &p2->fade_seconds);
1004   CB (fade_ticks_text,          prefs_int_cb,  &p2->fade_ticks);
1005   CB (lock_timeout_text,        prefs_min_cb,  &p2->lock_timeout);
1006   CB (passwd_timeout_text,      prefs_sec_cb,  &p2->passwd_timeout);
1007
1008 #endif /* !HAVE_ATHENA */
1009
1010   CBT (verbose_toggle,          prefs_bool_cb, &p2->verbose_p);
1011   CBT (install_cmap_toggle,     prefs_bool_cb, &p2->install_cmap_p);
1012   CBT (fade_toggle,             prefs_bool_cb, &p2->fade_p);
1013   CBT (unfade_toggle,           prefs_bool_cb, &p2->unfade_p);
1014   CBT (lock_toggle,             prefs_bool_cb, &p2->lock_p);
1015 #undef CB
1016 #undef CBT
1017
1018   {
1019     Bool found_any_writable_cells = False;
1020     int nscreens = ScreenCount(dpy);
1021     int i;
1022     for (i = 0; i < nscreens; i++)
1023       {
1024         Screen *s = ScreenOfDisplay (dpy, i);
1025         if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1026           {
1027             found_any_writable_cells = True;
1028             break;
1029           }
1030       }
1031
1032     if (! found_any_writable_cells)     /* fading isn't possible */
1033       {
1034         disable_widget (fade_text);
1035         disable_widget (fade_ticks_text);
1036         disable_widget (install_cmap_toggle);
1037         disable_widget (fade_toggle);
1038         disable_widget (unfade_toggle);
1039       }
1040   }
1041 }
1042
1043
1044 /* Formats a `Time' into "H:MM:SS".  (Time is microseconds.)
1045  */
1046 static void
1047 format_time (char *buf, Time time)
1048 {
1049   int s = time / 1000;
1050   unsigned int h = 0, m = 0;
1051   if (s >= 60)
1052     {
1053       m += (s / 60);
1054       s %= 60;
1055     }
1056   if (m >= 60)
1057     {
1058       h += (m / 60);
1059       m %= 60;
1060     }
1061   sprintf (buf, "%u:%02u:%02u", h, m, s);
1062 }
1063
1064
1065 static void
1066 pop_preferences_dialog (prefs_pair *pair)
1067 {
1068   /* saver_preferences *p =  pair->a; */
1069   saver_preferences *p2 = pair->b;
1070   char s[100];
1071
1072   format_time (s, p2->timeout);        set_text_string(timeout_text, s);
1073   format_time (s, p2->cycle);          set_text_string(cycle_text, s);
1074   format_time (s, p2->lock_timeout);   set_text_string(lock_timeout_text, s);
1075   format_time (s, p2->passwd_timeout); set_text_string(passwd_timeout_text, s);
1076   format_time (s, p2->fade_seconds);   set_text_string(fade_text, s);
1077   sprintf (s, "%u", p2->fade_ticks);   set_text_string(fade_ticks_text, s);
1078
1079   set_toggle_button_state (verbose_toggle,      p2->verbose_p);
1080   set_toggle_button_state (install_cmap_toggle, p2->install_cmap_p);
1081   set_toggle_button_state (fade_toggle,         p2->fade_p);
1082   set_toggle_button_state (unfade_toggle,       p2->unfade_p);
1083   set_toggle_button_state (lock_toggle,         p2->lock_p);
1084
1085   pop_up_dialog_box (preferences_dialog, preferences_form);
1086 }
1087
1088
1089 static void
1090 run_hack (Display *dpy, int n)
1091 {
1092   if (n <= 0) abort();
1093   xscreensaver_command (dpy, XA_DEMO, n, False);
1094 }
1095
1096
1097 static void
1098 warning_dialog_dismiss_cb CB_ARGS(WIDGET button, POINTER client_data,
1099                                   POINTER ignored)
1100 {
1101   WIDGET shell = (WIDGET) client_data;
1102   XtDestroyWidget (shell);
1103 }
1104
1105
1106 static void
1107 warning_dialog (WIDGET parent, const char *message)
1108 {
1109   char *msg = strdup (message);
1110   char *head;
1111
1112   WIDGET dialog = 0;
1113   WIDGET label = 0;
1114   WIDGET ok = 0;
1115   int i = 0;
1116
1117 #ifdef HAVE_MOTIF
1118
1119   Widget w;
1120   Widget container;
1121   XmString xmstr;
1122   Arg av[10];
1123   int ac = 0;
1124
1125   ac = 0;
1126   dialog = XmCreateWarningDialog (parent, "versionWarning", av, ac);
1127
1128   w = XmMessageBoxGetChild (dialog, XmDIALOG_MESSAGE_LABEL);
1129   if (w) XtUnmanageChild (w);
1130   w = XmMessageBoxGetChild (dialog, XmDIALOG_CANCEL_BUTTON);
1131   if (w) XtUnmanageChild (w);
1132   w = XmMessageBoxGetChild (dialog, XmDIALOG_HELP_BUTTON);
1133   if (w) XtUnmanageChild (w);
1134
1135   ok = XmMessageBoxGetChild (dialog, XmDIALOG_OK_BUTTON);
1136
1137   ac = 0;
1138   XtSetArg (av[ac], XmNnumColumns, 1); ac++;
1139   XtSetArg (av[ac], XmNorientation, XmVERTICAL); ac++;
1140   XtSetArg (av[ac], XmNpacking, XmPACK_COLUMN); ac++;
1141   XtSetArg (av[ac], XmNrowColumnType, XmWORK_AREA); ac++;
1142   XtSetArg (av[ac], XmNspacing, 0); ac++;
1143   container = XmCreateRowColumn (dialog, "container", av, ac);
1144
1145 #elif defined(HAVE_ATHENA)
1146
1147   Widget form;
1148   dialog = XtVaCreatePopupShell("warning_dialog", transientShellWidgetClass,
1149                                 parent, 0);
1150   form = XtVaCreateManagedWidget("warning_form", formWidgetClass, dialog, 0);
1151 #endif /* HAVE_ATHENA */
1152
1153   head = msg;
1154   while (head)
1155     {
1156       char name[20];
1157       char *s = strchr (head, '\n');
1158       if (s) *s = 0;
1159
1160       sprintf (name, "label%d", i++);
1161
1162 #ifdef HAVE_MOTIF
1163       xmstr = XmStringCreate (head, XmSTRING_DEFAULT_CHARSET);
1164       ac = 0;
1165       XtSetArg (av[ac], XmNlabelString, xmstr); ac++;
1166       label = XmCreateLabelGadget (container, name, av, ac);
1167       XtManageChild (label);
1168       XmStringFree (xmstr);
1169 #elif defined(HAVE_ATHENA)
1170       
1171       label = XtVaCreateManagedWidget (name, labelWidgetClass,
1172                                        form,
1173                                        XtNleft, XtChainLeft,
1174                                        XtNright, XtChainRight,
1175                                        XtNlabel, head,
1176                                        (label ? XtNfromVert : XtNtop),
1177                                        (label ? label : XtChainTop),
1178                                        0);
1179
1180 #endif /* HAVE_ATHENA */
1181
1182       if (s)
1183         head = s+1;
1184       else
1185         head = 0;
1186     }
1187
1188 #ifdef HAVE_MOTIF
1189
1190   XtManageChild (container);
1191   XtRealizeWidget (dialog);
1192   XtManageChild (dialog);
1193
1194 #elif defined(HAVE_ATHENA)
1195
1196   ok = XtVaCreateManagedWidget ("ok", commandWidgetClass, form,
1197                                 XtNleft, XtChainLeft,
1198                                 XtNbottom, XtChainBottom,
1199                                 XtNfromVert, label,
1200                                 0);
1201
1202   XtRealizeWidget (dialog);
1203   XtPopup (dialog, XtGrabNone);
1204 #endif /* HAVE_ATHENA */
1205
1206   add_button_callback (ok, warning_dialog_dismiss_cb, (POINTER) dialog);
1207
1208   free (msg);
1209 }
1210
1211
1212 \f
1213 /* The main demo-mode command loop.
1214  */
1215
1216 #if 0
1217 static Bool
1218 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1219         XrmRepresentation *type, XrmValue *value, XPointer closure)
1220 {
1221   int i;
1222   for (i = 0; quarks[i]; i++)
1223     {
1224       if (bindings[i] == XrmBindTightly)
1225         fprintf (stderr, (i == 0 ? "" : "."));
1226       else if (bindings[i] == XrmBindLoosely)
1227         fprintf (stderr, "*");
1228       else
1229         fprintf (stderr, " ??? ");
1230       fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1231     }
1232
1233   fprintf (stderr, ": %s\n", (char *) value->addr);
1234
1235   return False;
1236 }
1237 #endif
1238
1239
1240 static void
1241 the_network_is_not_the_computer (WIDGET parent)
1242 {
1243   Display *dpy = widget_display (parent);
1244   char *rversion, *ruser, *rhost;
1245   char *luser, *lhost;
1246   char *msg = 0;
1247   struct passwd *p = getpwuid (getuid ());
1248   const char *d = DisplayString (dpy);
1249
1250 # if defined(HAVE_UNAME)
1251   struct utsname uts;
1252   if (uname (&uts) < 0)
1253     lhost = "<UNKNOWN>";
1254   else
1255     lhost = uts.nodename;
1256 # elif defined(VMS)
1257   strcpy (lhost, getenv("SYS$NODE"));
1258 # else  /* !HAVE_UNAME && !VMS */
1259   strcat (lhost, "<UNKNOWN>");
1260 # endif /* !HAVE_UNAME && !VMS */
1261
1262   if (p && p->pw_name)
1263     luser = p->pw_name;
1264   else
1265     luser = "???";
1266
1267   server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
1268
1269   /* Make a buffer that's big enough for a number of copies of all the
1270      strings, plus some. */
1271   msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
1272                                (ruser ? strlen(ruser) : 0) +
1273                                (rhost ? strlen(rhost) : 0) +
1274                                strlen(lhost) +
1275                                strlen(luser) +
1276                                strlen(d) +
1277                                30));
1278   *msg = 0;
1279
1280   if (!rversion || !*rversion)
1281     {
1282       sprintf (msg,
1283                "Warning:\n\n"
1284                "xscreensaver doesn't seem to be running on display \"%s\".",
1285                d);
1286     }
1287   else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
1288     {
1289       /* Warn that the two processes are running as different users.
1290        */
1291       sprintf(msg,
1292                "Warning:\n\n"
1293               "%s is running as user \"%s\" on host \"%s\".\n"
1294               "But the xscreensaver managing display \"%s\"\n"
1295               "is running as user \"%s\" on host \"%s\".\n"
1296               "\n"
1297               "Since they are different users, they won't be reading/writing\n"
1298               "the same ~/.xscreensaver file, so %s isn't\n"
1299               "going to work right.\n"
1300               "\n"
1301               "Either re-run %s as \"%s\", or re-run\n"
1302               "xscreensaver as \"%s\".\n",
1303               progname, luser, lhost,
1304               d,
1305               (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1306               progname,
1307               progname, (ruser ? ruser : "???"),
1308               luser);
1309     }
1310   else if (rhost && *rhost && !!strcmp (rhost, lhost))
1311     {
1312       /* Warn that the two processes are running on different hosts.
1313        */
1314       sprintf (msg,
1315                "Warning:\n\n"
1316                "%s is running as user \"%s\" on host \"%s\".\n"
1317                "But the xscreensaver managing display \"%s\"\n"
1318                "is running as user \"%s\" on host \"%s\".\n"
1319                "\n"
1320                "If those two machines don't share a file system (that is,\n"
1321                "if they don't see the same ~%s/.xscreensaver file) then\n"
1322                "%s won't work right.",
1323                progname, luser, lhost,
1324                d,
1325                (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1326                luser,
1327                progname);
1328     }
1329   else if (!!strcmp (rversion, short_version))
1330     {
1331       /* Warn that the version numbers don't match.
1332        */
1333       sprintf (msg,
1334                "Warning:\n\n"
1335                "This is %s version %s.\n"
1336                "But the xscreensaver managing display \"%s\"\n"
1337                "is version %s.  This could cause problems.",
1338                progname, short_version,
1339                d,
1340                rversion);
1341     }
1342
1343
1344   if (*msg)
1345     warning_dialog (parent, msg);
1346
1347   free (msg);
1348 }
1349
1350
1351 /* We use this error handler so that X errors are preceeded by the name
1352    of the program that generated them.
1353  */
1354 static int
1355 demo_ehandler (Display *dpy, XErrorEvent *error)
1356 {
1357   fprintf (stderr, "\nX error in %s:\n", progname);
1358   if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
1359     exit (-1);
1360   else
1361     fprintf (stderr, " (nonfatal.)\n");
1362   return 0;
1363 }
1364
1365
1366 static char *defaults[] = {
1367 #include "XScreenSaver_ad.h"
1368  0
1369 };
1370
1371 int
1372 main (int argc, char **argv)
1373 {
1374   XtAppContext app;
1375   prefs_pair Pair, *pair;
1376   saver_preferences P, P2, *p, *p2;
1377   Bool prefs = False;
1378   int i;
1379   Display *dpy;
1380   Widget toplevel_shell;
1381   char *real_progname = argv[0];
1382   char *s;
1383
1384   s = strrchr (real_progname, '/');
1385   if (s) real_progname = s+1;
1386
1387   p = &P;
1388   p2 = &P2;
1389   pair = &Pair;
1390   pair->a = p;
1391   pair->b = p2;
1392   memset (p,  0, sizeof (*p));
1393   memset (p2, 0, sizeof (*p2));
1394
1395   progname = real_progname;
1396
1397   /* We must read exactly the same resources as xscreensaver.
1398      That means we must have both the same progclass *and* progname,
1399      at least as far as the resource database is concerned.  So,
1400      put "xscreensaver" in argv[0] while initializing Xt.
1401    */
1402   argv[0] = "xscreensaver";
1403   progname = argv[0];
1404
1405
1406   toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1407                                     defaults, 0, 0);
1408
1409   dpy = XtDisplay (toplevel_shell);
1410   db = XtDatabase (dpy);
1411   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
1412   XSetErrorHandler (demo_ehandler);
1413
1414   /* Complain about unrecognized command-line arguments.
1415    */
1416   for (i = 1; i < argc; i++)
1417     {
1418       char *s = argv[i];
1419       if (s[0] == '-' && s[1] == '-')
1420         s++;
1421       if (!strcmp (s, "-prefs"))
1422         prefs = True;
1423       else
1424         {
1425           fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n",
1426                    real_progname);
1427           exit (1);
1428         }
1429     }
1430
1431   short_version = (char *) malloc (5);
1432   memcpy (short_version, screensaver_id + 17, 4);
1433   short_version [4] = 0;
1434
1435   /* Load the init file, which may end up consulting the X resource database
1436      and the site-wide app-defaults file.  Note that at this point, it's
1437      important that `progname' be "xscreensaver", rather than whatever
1438      was in argv[0].
1439    */
1440   p->db = db;
1441   load_init_file (p);
1442   *p2 = *p;
1443
1444   /* Now that Xt has been initialized, and the resources have been read,
1445      we can set our `progname' variable to something more in line with
1446      reality.
1447    */
1448   progname = real_progname;
1449
1450
1451 #ifdef HAVE_ATHENA
1452   global_prefs_kludge = p;      /* I hate C so much... */
1453 #endif /* HAVE_ATHENA */
1454
1455 #if 0
1456   {
1457     XrmName name = { 0 };
1458     XrmClass class = { 0 };
1459     int count = 0;
1460     XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1461                           (POINTER) &count);
1462   }
1463 #endif
1464
1465
1466   XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
1467   XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
1468   XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
1469   XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
1470   XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
1471   XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
1472   XA_SELECT = XInternAtom (dpy, "SELECT", False);
1473   XA_DEMO = XInternAtom (dpy, "DEMO", False);
1474   XA_BLANK = XInternAtom (dpy, "BLANK", False);
1475   XA_LOCK = XInternAtom (dpy, "LOCK", False);
1476   XA_EXIT = XInternAtom (dpy, "EXIT", False);
1477   XA_RESTART = XInternAtom (dpy, "RESTART", False);
1478
1479   make_demo_dialog (toplevel_shell, pair);
1480
1481   if (prefs)
1482     {
1483       make_preferences_dialog (pair, toplevel_shell);
1484       pop_preferences_dialog (pair);
1485     }
1486
1487   the_network_is_not_the_computer (preferences_dialog
1488                                    ? preferences_dialog
1489                                    : demo_dialog);
1490
1491   XtAppMainLoop (app);
1492
1493   exit (0);
1494 }