http://packetstorm.tacticalflex.com/UNIX/admin/xscreensaver-3.27.tar.gz
[xscreensaver] / driver / demo-Xm.c
1 /* demo-Xm.c --- implements the interactive demo-mode and options dialogs.
2  * xscreensaver, Copyright (c) 1993-1999 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #ifdef HAVE_MOTIF /* 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_XPM
48 # include <X11/xpm.h>
49 #endif /* HAVE_XPM */
50
51 #ifdef HAVE_XMU
52 # ifndef VMS
53 #  include <X11/Xmu/Error.h>
54 # else /* VMS */
55 #  include <Xmu/Error.h>
56 # endif
57 #else
58 # include "xmu.h"
59 #endif
60
61
62
63 #include <Xm/Xm.h>
64 #include <Xm/List.h>
65 #include <Xm/PushB.h>
66 #include <Xm/LabelG.h>
67 #include <Xm/RowColumn.h>
68 #include <Xm/MessageB.h>
69
70 #ifdef HAVE_XMCOMBOBOX          /* a Motif 2.0 widget */
71 # include <Xm/ComboBox.h>
72 # ifndef XmNtextField           /* Lesstif 0.89.4 bug */
73 #  undef HAVE_XMCOMBOBOX
74 # endif
75 #endif /* HAVE_XMCOMBOBOX */
76
77 #include "version.h"
78 #include "prefs.h"
79 #include "resources.h"          /* for parse_time() */
80 #include "visual.h"             /* for has_writable_cells() */
81 #include "remote.h"             /* for xscreensaver_command() */
82 #include "usleep.h"
83
84 #include <stdio.h>
85 #include <string.h>
86 #include <ctype.h>
87
88 #undef countof
89 #define countof(x) (sizeof((x))/sizeof((*x)))
90
91
92 char *progname = 0;
93 char *progclass = "XScreenSaver";
94 XrmDatabase db;
95
96 typedef struct {
97   saver_preferences *a, *b;
98 } prefs_pair;
99
100 static void *global_prefs_pair;  /* I hate C so much... */
101
102 char *blurb (void) { return progname; }
103
104 extern Widget create_xscreensaver_demo (Widget parent);
105 extern const char *visual_menu[];
106
107
108 static char *short_version = 0;
109
110 Atom XA_VROOT;
111 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
112 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
113 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
114
115
116 static void populate_demo_window (Widget toplevel,
117                                   int which, prefs_pair *pair);
118 static void populate_prefs_page (Widget top, prefs_pair *pair);
119 static int apply_changes_and_save (Widget widget);
120 static int maybe_reload_init_file (Widget widget, prefs_pair *pair);
121 static void await_xscreensaver (Widget widget);
122
123 \f
124 /* Some random utility functions
125  */
126
127 static Widget 
128 name_to_widget (Widget widget, const char *name)
129 {
130   Widget parent;
131   char name2[255];
132   name2[0] = '*';
133   strcpy (name2+1, name);
134   
135   while ((parent = XtParent (widget)))
136     widget = parent;
137   return XtNameToWidget (widget, name2);
138 }
139
140
141
142 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
143    Takes a scroller, viewport, or list as an argument.
144  */
145 static void
146 ensure_selected_item_visible (Widget list)
147 {
148   int *pos_list = 0;
149   int pos_count = 0;
150   if (XmListGetSelectedPos (list, &pos_list, &pos_count) && pos_count > 0)
151     {
152       int top = -2;
153       int visible = 0;
154       XtVaGetValues (list,
155                      XmNtopItemPosition, &top,
156                      XmNvisibleItemCount, &visible,
157                      0);
158       if (pos_list[0] >= top + visible)
159         {
160           int pos = pos_list[0] - visible + 1;
161           if (pos < 0) pos = 0;
162           XmListSetPos (list, pos);
163         }
164       else if (pos_list[0] < top)
165         {
166           XmListSetPos (list, pos_list[0]);
167         }
168     }
169   if (pos_list)
170     XtFree ((char *) pos_list);
171 }
172
173
174 static void
175 warning_dialog_dismiss_cb  (Widget button, XtPointer client_data,
176                             XtPointer user_data)
177 {
178   Widget shell = (Widget) client_data;
179   XtDestroyWidget (shell);
180 }
181
182
183 static void
184 warning_dialog (Widget parent, const char *message, int center)
185 {
186   char *msg = strdup (message);
187   char *head;
188
189   Widget dialog = 0;
190   Widget label = 0;
191   Widget ok = 0;
192   int i = 0;
193
194   Widget w;
195   Widget container;
196   XmString xmstr;
197   Arg av[10];
198   int ac = 0;
199
200   ac = 0;
201   dialog = XmCreateWarningDialog (parent, "warning", av, ac);
202
203   w = XmMessageBoxGetChild (dialog, XmDIALOG_MESSAGE_LABEL);
204   if (w) XtUnmanageChild (w);
205   w = XmMessageBoxGetChild (dialog, XmDIALOG_CANCEL_BUTTON);
206   if (w) XtUnmanageChild (w);
207   w = XmMessageBoxGetChild (dialog, XmDIALOG_HELP_BUTTON);
208   if (w) XtUnmanageChild (w);
209
210   ok = XmMessageBoxGetChild (dialog, XmDIALOG_OK_BUTTON);
211
212   ac = 0;
213   XtSetArg (av[ac], XmNnumColumns, 1); ac++;
214   XtSetArg (av[ac], XmNorientation, XmVERTICAL); ac++;
215   XtSetArg (av[ac], XmNpacking, XmPACK_COLUMN); ac++;
216   XtSetArg (av[ac], XmNrowColumnType, XmWORK_AREA); ac++;
217   XtSetArg (av[ac], XmNspacing, 0); ac++;
218   container = XmCreateRowColumn (dialog, "container", av, ac);
219
220   head = msg;
221   while (head)
222     {
223       char name[20];
224       char *s = strchr (head, '\n');
225       if (s) *s = 0;
226
227       sprintf (name, "label%d", i++);
228
229       xmstr = XmStringCreate (head, XmSTRING_DEFAULT_CHARSET);
230       ac = 0;
231       XtSetArg (av[ac], XmNlabelString, xmstr); ac++;
232       XtSetArg (av[ac], XmNmarginHeight, 0); ac++;
233       label = XmCreateLabelGadget (container, name, av, ac);
234       XtManageChild (label);
235       XmStringFree (xmstr);
236
237       if (s)
238         head = s+1;
239       else
240         head = 0;
241
242       center--;
243     }
244
245   XtManageChild (container);
246   XtRealizeWidget (dialog);
247   XtManageChild (dialog);
248
249   XtAddCallback (ok, XmNactivateCallback, warning_dialog_dismiss_cb, dialog);
250
251   free (msg);
252 }
253
254
255 static void
256 run_cmd (Widget widget, Atom command, int arg)
257 {
258   char *err = 0;
259   int status;
260
261   apply_changes_and_save (widget);
262   status = xscreensaver_command (XtDisplay (widget),
263                                  command, arg, False, &err);
264   if (status < 0)
265     {
266       char buf [255];
267       if (err)
268         sprintf (buf, "Error:\n\n%s", err);
269       else
270         strcpy (buf, "Unknown error!");
271       warning_dialog (widget, buf, 100);
272     }
273   if (err) free (err);
274 }
275
276
277 static void
278 run_hack (Widget widget, int which, Bool report_errors_p)
279 {
280   if (which < 0) return;
281   apply_changes_and_save (widget);
282   if (report_errors_p)
283     run_cmd (widget, XA_DEMO, which + 1);
284   else
285     {
286       char *s = 0;
287       xscreensaver_command (XtDisplay (widget), XA_DEMO, which + 1, False, &s);
288       if (s) free (s);
289     }
290 }
291
292
293 \f
294 /* Button callbacks
295  */
296
297 void
298 exit_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
299 {
300   apply_changes_and_save (XtParent (button));
301   exit (0);
302 }
303
304 #if 0
305 static void
306 wm_close_cb (Widget widget, GdkEvent *event, XtPointer data)
307 {
308   apply_changes_and_save (XtParent (button));
309   exit (0);
310 }
311 #endif
312
313 void
314 cut_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
315 {
316   /* #### */
317   warning_dialog (XtParent (button),
318                   "Error:\n\n"
319                   "cut unimplemented\n", 1);
320 }
321
322
323 void
324 copy_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
325 {
326   /* #### */
327   warning_dialog (XtParent (button),
328                   "Error:\n\n"
329                   "copy unimplemented\n", 1);
330 }
331
332
333 void
334 paste_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
335 {
336   /* #### */
337   warning_dialog (XtParent (button),
338                   "Error:\n\n"
339                   "paste unimplemented\n", 1);
340 }
341
342
343 void
344 about_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
345 {
346   char buf [2048];
347   char *s = strdup (screensaver_id + 4);
348   char *s2;
349
350   s2 = strchr (s, ',');
351   *s2 = 0;
352   s2 += 2;
353
354   sprintf (buf, "%s\n%s\n\n"
355            "For updates, check http://www.jwz.org/xscreensaver/",
356            s, s2);
357   free (s);
358
359   warning_dialog (XtParent (button), buf, 100);
360 }
361
362
363 void
364 doc_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
365 {
366   prefs_pair *pair = (prefs_pair *) client_data;
367
368   saver_preferences *p =  pair->a;
369   char *help_command;
370
371   if (!p->help_url || !*p->help_url)
372     {
373       warning_dialog (XtParent (button),
374                       "Error:\n\n"
375                       "No Help URL has been specified.\n", 100);
376       return;
377     }
378
379   help_command = (char *) malloc (strlen (p->load_url_command) +
380                                   (strlen (p->help_url) * 2) + 20);
381   strcpy (help_command, "( ");
382   sprintf (help_command + strlen(help_command),
383            p->load_url_command, p->help_url, p->help_url);
384   strcat (help_command, " ) &");
385   system (help_command);
386   free (help_command);
387 }
388
389
390 void
391 activate_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
392 {
393   run_cmd (XtParent (button), XA_ACTIVATE, 0);
394 }
395
396
397 void
398 lock_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
399 {
400   run_cmd (XtParent (button), XA_LOCK, 0);
401 }
402
403
404 void
405 kill_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
406 {
407   run_cmd (XtParent (button), XA_EXIT, 0);
408 }
409
410
411 void
412 restart_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
413 {
414 #if 0
415   run_cmd (XtParent (button), XA_RESTART, 0);
416 #else
417   button = XtParent (button);
418   apply_changes_and_save (button);
419   xscreensaver_command (XtDisplay (button), XA_EXIT, 0, False, NULL);
420   sleep (1);
421   system ("xscreensaver -nosplash &");
422 #endif
423
424   await_xscreensaver (button);
425 }
426
427 static void
428 await_xscreensaver (Widget widget)
429 {
430   int countdown = 5;
431
432   Display *dpy = XtDisplay (widget);
433   char *rversion = 0;
434
435   while (!rversion && (--countdown > 0))
436     {
437       /* Check for the version of the running xscreensaver... */
438       server_xscreensaver_version (dpy, &rversion, 0, 0);
439
440       /* If it's not there yet, wait a second... */
441       sleep (1);
442     }
443
444   if (rversion)
445     {
446       /* Got it. */
447       free (rversion);
448     }
449   else
450     {
451       /* Timed out, no screensaver running. */
452
453       char buf [1024];
454       Bool root_p = (geteuid () == 0);
455       
456       strcpy (buf, 
457               "Error:\n\n"
458               "The xscreensaver daemon did not start up properly.\n"
459               "\n");
460
461       if (root_p)
462         strcat (buf,
463             "You are running as root.  This usually means that xscreensaver\n"
464             "was unable to contact your X server because access control is\n"
465             "turned on.  Try running this command:\n"
466             "\n"
467             "                        xhost +localhost\n"
468             "\n"
469             "and then selecting `File / Restart Daemon'.\n"
470             "\n"
471             "Note that turning off access control will allow anyone logged\n"
472             "on to this machine to access your screen, which might be\n"
473             "considered a security problem.  Please read the xscreensaver\n"
474             "manual and FAQ for more information.\n"
475             "\n"
476             "You shouldn't run X as root. Instead, you should log in as a\n"
477             "normal user, and `su' as necessary.");
478       else
479         strcat (buf, "Please check your $PATH and permissions.");
480
481       warning_dialog (XtParent (widget), buf, 1);
482     }
483 }
484
485
486 static int _selected_hack_number = -1;
487
488 static int
489 selected_hack_number (Widget toplevel)
490 {
491   return _selected_hack_number;
492 }
493
494
495 static int
496 demo_write_init_file (Widget widget, saver_preferences *p)
497 {
498   if (!write_init_file (p, short_version, False))
499     return 0;
500   else
501     {
502       const char *f = init_file_name();
503       if (!f || !*f)
504         warning_dialog (widget,
505                         "Error:\n\nCouldn't determine init file name!\n",
506                         100);
507       else
508         {
509           char *b = (char *) malloc (strlen(f) + 1024);
510           sprintf (b, "Error:\n\nCouldn't write %s\n", f);
511           warning_dialog (widget, b, 100);
512           free (b);
513         }
514       return -1;
515     }
516 }
517
518
519 static int
520 apply_changes_and_save (Widget widget)
521 {
522   prefs_pair *pair = global_prefs_pair;
523   saver_preferences *p =  pair->a;
524   Widget list_widget = name_to_widget (widget, "list");
525   int which = selected_hack_number (widget);
526
527   Widget cmd = name_to_widget (widget, "cmdText");
528   Widget enabled = name_to_widget (widget, "enabled");
529
530   Widget vis = name_to_widget (widget, "combo");
531 # ifdef HAVE_XMCOMBOBOX
532   Widget text = 0;
533 # else /* !HAVE_XMCOMBOBOX */
534   Widget menu = 0, *kids = 0, selected_item = 0;
535   Cardinal nkids = 0;
536   int i = 0;
537 # endif /* !HAVE_XMCOMBOBOX */
538
539   Bool enabled_p = False;
540   const char *visual = 0;
541   const char *command = 0;
542   
543   char c;
544   unsigned long id;
545
546   if (which < 0) return -1;
547
548 # ifdef HAVE_XMCOMBOBOX
549   XtVaGetValues (vis, XmNtextField, &text, 0);
550   if (!text)
551     /* If we can't get at the text field of this combo box, we're screwed. */
552     abort();
553   XtVaGetValues (text, XmNvalue, &visual, 0);
554
555 # else /* !HAVE_XMCOMBOBOX */
556   XtVaGetValues (vis, XmNsubMenuId, &menu, 0);
557   XtVaGetValues (menu, XmNnumChildren, &nkids, XmNchildren, &kids, 0);
558   XtVaGetValues (menu, XmNmenuHistory, &selected_item, 0);
559   if (selected_item)
560     for (i = 0; i < nkids; i++)
561       if (kids[i] == selected_item)
562         break;
563
564   visual = visual_menu[i];
565 # endif /* !HAVE_XMCOMBOBOX */
566
567   XtVaGetValues (enabled, XmNset, &enabled_p, 0);
568   XtVaGetValues (cmd, XtNvalue, &command, 0);
569
570   if (maybe_reload_init_file (widget, pair) != 0)
571     return 1;
572
573   /* Sanity-check and canonicalize whatever the user typed into the combo box.
574    */
575   if      (!strcasecmp (visual, ""))                   visual = "";
576   else if (!strcasecmp (visual, "any"))                visual = "";
577   else if (!strcasecmp (visual, "default"))            visual = "Default";
578   else if (!strcasecmp (visual, "default-n"))          visual = "Default-N";
579   else if (!strcasecmp (visual, "default-i"))          visual = "Default-I";
580   else if (!strcasecmp (visual, "best"))               visual = "Best";
581   else if (!strcasecmp (visual, "mono"))               visual = "Mono";
582   else if (!strcasecmp (visual, "monochrome"))         visual = "Mono";
583   else if (!strcasecmp (visual, "gray"))               visual = "Gray";
584   else if (!strcasecmp (visual, "grey"))               visual = "Gray";
585   else if (!strcasecmp (visual, "color"))              visual = "Color";
586   else if (!strcasecmp (visual, "gl"))                 visual = "GL";
587   else if (!strcasecmp (visual, "staticgray"))         visual = "StaticGray";
588   else if (!strcasecmp (visual, "staticcolor"))        visual = "StaticColor";
589   else if (!strcasecmp (visual, "truecolor"))          visual = "TrueColor";
590   else if (!strcasecmp (visual, "grayscale"))          visual = "GrayScale";
591   else if (!strcasecmp (visual, "greyscale"))          visual = "GrayScale";
592   else if (!strcasecmp (visual, "pseudocolor"))        visual = "PseudoColor";
593   else if (!strcasecmp (visual, "directcolor"))        visual = "DirectColor";
594   else if (1 == sscanf (visual, " %ld %c", &id, &c))   ;
595   else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
596   else
597     {
598       XBell (XtDisplay (widget), 0);                      /* unparsable */
599       visual = "";
600       /* #### gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");*/
601     }
602
603   ensure_selected_item_visible (list_widget);
604
605   if (!p->screenhacks[which]->visual)
606     p->screenhacks[which]->visual = strdup ("");
607   if (!p->screenhacks[which]->command)
608     p->screenhacks[which]->command = strdup ("");
609
610   if (p->screenhacks[which]->enabled_p != enabled_p ||
611       !!strcasecmp (p->screenhacks[which]->visual, visual) ||
612       !!strcasecmp (p->screenhacks[which]->command, command))
613     {
614       /* Something was changed -- store results into the struct,
615          and write the file.
616        */
617       free (p->screenhacks[which]->visual);
618       free (p->screenhacks[which]->command);
619       p->screenhacks[which]->visual = strdup (visual);
620       p->screenhacks[which]->command = strdup (command);
621       p->screenhacks[which]->enabled_p = enabled_p;
622
623       return demo_write_init_file (widget, p);
624     }
625
626   /* No changes made */
627   return 0;
628 }
629
630 void
631 run_this_cb (Widget button, XtPointer client_data, XtPointer ignored)
632 {
633   int which = selected_hack_number (XtParent (button));
634   if (which < 0) return;
635   if (0 == apply_changes_and_save (XtParent (button)))
636     run_hack (XtParent (button), which, True);
637 }
638
639
640 void
641 manual_cb (Widget button, XtPointer client_data, XtPointer ignored)
642 {
643   prefs_pair *pair = (prefs_pair *) client_data;
644   saver_preferences *p =  pair->a;
645   Widget list_widget = name_to_widget (button, "list");
646   int which = selected_hack_number (button);
647   char *name, *name2, *cmd, *s;
648   if (which < 0) return;
649   apply_changes_and_save (button);
650   ensure_selected_item_visible (list_widget);
651
652   name = strdup (p->screenhacks[which]->command);
653   name2 = name;
654   while (isspace (*name2)) name2++;
655   s = name2;
656   while (*s && !isspace (*s)) s++;
657   *s = 0;
658   s = strrchr (name2, '/');
659   if (s) name = s+1;
660
661   cmd = get_string_resource ("manualCommand", "ManualCommand");
662   if (cmd)
663     {
664       char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
665       strcpy (cmd2, "( ");
666       sprintf (cmd2 + strlen (cmd2),
667                cmd,
668                name2, name2, name2, name2);
669       strcat (cmd2, " ) &");
670       system (cmd2);
671       free (cmd2);
672     }
673   else
674     {
675       warning_dialog (XtParent (button),
676                       "Error:\n\nno `manualCommand' resource set.",
677                       100);
678     }
679
680   free (name);
681 }
682
683
684 void
685 run_next_cb (Widget button, XtPointer client_data, XtPointer ignored)
686 {
687   prefs_pair *pair = (prefs_pair *) client_data;
688   saver_preferences *p =  pair->a;
689
690   Widget list_widget = name_to_widget (button, "list");
691   int which = selected_hack_number (button);
692
693   button = XtParent (button);
694
695   if (which < 0)
696     which = 0;
697   else
698     which++;
699
700   if (which >= p->screenhacks_count)
701     which = 0;
702
703   apply_changes_and_save (button);
704
705   XmListDeselectAllItems (list_widget); /* LessTif lossage */
706   XmListSelectPos (list_widget, which+1, True);
707
708   ensure_selected_item_visible (list_widget);
709   populate_demo_window (button, which, pair);
710   run_hack (button, which, False);
711 }
712
713
714 void
715 run_prev_cb (Widget button, XtPointer client_data, XtPointer ignored)
716 {
717   prefs_pair *pair = (prefs_pair *) client_data;
718   saver_preferences *p =  pair->a;
719
720   Widget list_widget = name_to_widget (button, "list");
721   int which = selected_hack_number (button);
722
723   button = XtParent (button);
724
725   if (which < 0)
726     which = p->screenhacks_count - 1;
727   else
728     which--;
729
730   if (which < 0)
731     which = p->screenhacks_count - 1;
732
733   apply_changes_and_save (button);
734
735   XmListDeselectAllItems (list_widget); /* LessTif lossage */
736   XmListSelectPos (list_widget, which+1, True);
737
738   ensure_selected_item_visible (list_widget);
739   populate_demo_window (button, which, pair);
740   run_hack (button, which, False);
741 }
742
743
744 /* Helper for the text fields that contain time specifications:
745    this parses the text, and does error checking.
746  */
747 static void 
748 hack_time_text (Widget button, const char *line, Time *store, Bool sec_p)
749 {
750   if (*line)
751     {
752       int value;
753       value = parse_time ((char *) line, sec_p, True);
754       value *= 1000;    /* Time measures in microseconds */
755       if (value < 0)
756         {
757           char b[255];
758           sprintf (b,
759                    "Error:\n\n"
760                    "Unparsable time format: \"%s\"\n",
761                    line);
762           warning_dialog (XtParent (button), b, 100);
763         }
764       else
765         *store = value;
766     }
767 }
768
769
770 void
771 prefs_ok_cb (Widget button, XtPointer client_data, XtPointer ignored)
772 {
773   prefs_pair *pair = (prefs_pair *) client_data;
774
775   saver_preferences *p =  pair->a;
776   saver_preferences *p2 = pair->b;
777   Bool changed = False;
778   char *v = 0;
779
780   button = XtParent (button);
781
782 # define SECONDS(field, name) \
783   v = 0; \
784   XtVaGetValues (name_to_widget (button, (name)), XtNvalue, &v, 0); \
785   hack_time_text (button, v, (field), True)
786
787 # define MINUTES(field, name) \
788   v = 0; \
789   XtVaGetValues (name_to_widget (button, (name)), XtNvalue, &v, 0); \
790   hack_time_text (button, v, (field), False)
791
792 # define INTEGER(field, name) do { \
793     unsigned int value; \
794     char c; \
795     XtVaGetValues (name_to_widget (button, (name)), XtNvalue, &v, 0); \
796     if (! *v) \
797       ; \
798     else if (sscanf (v, "%u%c", &value, &c) != 1) \
799       { \
800         char b[255]; \
801         sprintf (b, "Error:\n\n" "Not an integer: \"%s\"\n", v); \
802         warning_dialog (XtParent (button), b, 100); \
803       } \
804    else \
805      *(field) = value; \
806   } while(0)
807
808 # define CHECKBOX(field, name) \
809   XtVaGetValues (name_to_widget (button, (name)), XmNset, &field, 0)
810
811   MINUTES (&p2->timeout,        "timeoutText");
812   MINUTES (&p2->cycle,          "cycleText");
813   SECONDS (&p2->fade_seconds,   "fadeSecondsText");
814   INTEGER (&p2->fade_ticks,     "fadeTicksText");
815   MINUTES (&p2->lock_timeout,   "lockText");
816   SECONDS (&p2->passwd_timeout, "passwdText");
817   CHECKBOX (p2->verbose_p,      "verboseToggle");
818   CHECKBOX (p2->install_cmap_p, "cmapToggle");
819   CHECKBOX (p2->fade_p,         "fadeToggle");
820   CHECKBOX (p2->unfade_p,       "unfadeToggle");
821   CHECKBOX (p2->lock_p,         "lockToggle");
822
823 # undef SECONDS
824 # undef MINUTES
825 # undef INTEGER
826 # undef CHECKBOX
827
828 # define COPY(field) \
829   if (p->field != p2->field) changed = True; \
830   p->field = p2->field
831
832   COPY(timeout);
833   COPY(cycle);
834   COPY(lock_timeout);
835   COPY(passwd_timeout);
836   COPY(fade_seconds);
837   COPY(fade_ticks);
838   COPY(verbose_p);
839   COPY(install_cmap_p);
840   COPY(fade_p);
841   COPY(unfade_p);
842   COPY(lock_p);
843 # undef COPY
844
845   populate_prefs_page (button, pair);
846
847   if (changed)
848     demo_write_init_file (button, p);
849 }
850
851
852 void
853 prefs_cancel_cb (Widget button, XtPointer client_data, XtPointer ignored)
854 {
855   prefs_pair *pair = (prefs_pair *) client_data;
856
857   *pair->b = *pair->a;
858   populate_prefs_page (XtParent (button), pair);
859 }
860
861
862 static void
863 list_select_cb (Widget list, XtPointer client_data, XtPointer call_data)
864 {
865   prefs_pair *pair = (prefs_pair *) client_data;
866
867   XmListCallbackStruct *lcb = (XmListCallbackStruct *) call_data;
868   int which = lcb->item_position - 1;
869
870   apply_changes_and_save (list);
871   populate_demo_window (list, which, pair);
872
873   if (lcb->reason == XmCR_DEFAULT_ACTION && which >= 0)
874     run_hack (list, which, True);
875 }
876
877 \f
878 /* Populating the various widgets
879  */
880
881
882 /* Formats a `Time' into "H:MM:SS".  (Time is microseconds.)
883  */
884 static void
885 format_time (char *buf, Time time)
886 {
887   int s = time / 1000;
888   unsigned int h = 0, m = 0;
889   if (s >= 60)
890     {
891       m += (s / 60);
892       s %= 60;
893     }
894   if (m >= 60)
895     {
896       h += (m / 60);
897       m %= 60;
898     }
899   sprintf (buf, "%u:%02u:%02u", h, m, s);
900 }
901
902
903 static char *
904 make_pretty_name (const char *shell_command)
905 {
906   char *s = strdup (shell_command);
907   char *s2;
908   char res_name[255];
909
910   for (s2 = s; *s2; s2++)       /* truncate at first whitespace */
911     if (isspace (*s2))
912       {
913         *s2 = 0;
914         break;
915       }
916
917   s2 = strrchr (s, '/');        /* if pathname, take last component */
918   if (s2)
919     {
920       s2 = strdup (s2+1);
921       free (s);
922       s = s2;
923     }
924
925   if (strlen (s) > 50)          /* 51 is hereby defined as "unreasonable" */
926     s[50] = 0;
927
928   sprintf (res_name, "hacks.%s.name", s);               /* resource? */
929   s2 = get_string_resource (res_name, res_name);
930   if (s2)
931     return s2;
932
933   for (s2 = s; *s2; s2++)       /* if it has any capitals, return it */
934     if (*s2 >= 'A' && *s2 <= 'Z')
935       return s;
936
937   if (s[0] >= 'a' && s[0] <= 'z')                       /* else cap it */
938     s[0] -= 'a'-'A';
939   if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z')        /* (magic leading X) */
940     s[1] -= 'a'-'A';
941   return s;
942 }
943
944
945 /* Finds the number of the last hack to run, and makes that item be
946    selected by default.
947  */
948 static void
949 scroll_to_current_hack (Widget toplevel, prefs_pair *pair)
950 {
951   Atom type;
952   int format;
953   unsigned long nitems, bytesafter;
954   CARD32 *data = 0;
955   Display *dpy = XtDisplay (toplevel);
956   int which = 0;
957   Widget list;
958
959   if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
960                           XA_SCREENSAVER_STATUS,
961                           0, 3, False, XA_INTEGER,
962                           &type, &format, &nitems, &bytesafter,
963                           (unsigned char **) &data)
964       == Success
965       && type == XA_INTEGER
966       && nitems >= 3
967       && data)
968     which = (int) data[2] - 1;
969
970   if (data) free (data);
971
972   if (which < 0)
973     return;
974
975   list = name_to_widget (toplevel, "list");
976   apply_changes_and_save (toplevel);
977
978   XmListDeselectAllItems (list);        /* LessTif lossage */
979   XmListSelectPos (list, which+1, True);
980
981   ensure_selected_item_visible (list);
982   populate_demo_window (toplevel, which, pair);
983 }
984
985
986
987 static void
988 populate_hack_list (Widget toplevel, prefs_pair *pair)
989 {
990   saver_preferences *p =  pair->a;
991   Widget list = name_to_widget (toplevel, "list");
992   screenhack **hacks = p->screenhacks;
993   screenhack **h;
994
995   for (h = hacks; *h; h++)
996     {
997       char *pretty_name = (h[0]->name
998                            ? strdup (h[0]->name)
999                            : make_pretty_name (h[0]->command));
1000
1001       XmString xmstr = XmStringCreate (pretty_name, XmSTRING_DEFAULT_CHARSET);
1002       XmListAddItem (list, xmstr, 0);
1003       XmStringFree (xmstr);
1004     }
1005
1006   XtAddCallback (list, XmNbrowseSelectionCallback, list_select_cb, pair);
1007   XtAddCallback (list, XmNdefaultActionCallback,   list_select_cb, pair);
1008 }
1009
1010
1011 static void
1012 populate_prefs_page (Widget top, prefs_pair *pair)
1013 {
1014   saver_preferences *p =  pair->a;
1015   char s[100];
1016
1017   format_time (s, p->timeout);
1018   XtVaSetValues (name_to_widget (top, "timeoutText"),     XmNvalue, s, 0);
1019   format_time (s, p->cycle);
1020   XtVaSetValues (name_to_widget (top, "cycleText"),       XmNvalue, s, 0);
1021   format_time (s, p->lock_timeout);
1022   XtVaSetValues (name_to_widget (top, "lockText"),        XmNvalue, s, 0);
1023   format_time (s, p->passwd_timeout);
1024   XtVaSetValues (name_to_widget (top, "passwdText"),      XmNvalue, s, 0);
1025   format_time (s, p->fade_seconds);
1026   XtVaSetValues (name_to_widget (top, "fadeSecondsText"), XmNvalue, s, 0);
1027   sprintf (s, "%u", p->fade_ticks);
1028   XtVaSetValues (name_to_widget (top, "fadeTicksText"),   XmNvalue, s, 0);
1029
1030   XtVaSetValues (name_to_widget (top, "verboseToggle"),
1031                  XmNset, p->verbose_p, 0);
1032   XtVaSetValues (name_to_widget (top, "cmapToggle"),
1033                  XmNset, p->install_cmap_p, 0);
1034   XtVaSetValues (name_to_widget (top, "fadeToggle"),
1035                  XmNset, p->fade_p, 0);
1036   XtVaSetValues (name_to_widget (top, "unfadeToggle"),
1037                  XmNset, p->unfade_p, 0);
1038   XtVaSetValues (name_to_widget (top, "lockToggle"),
1039                  XmNset, p->lock_p, 0);
1040
1041
1042   {
1043     Bool found_any_writable_cells = False;
1044     Display *dpy = XtDisplay (top);
1045     int nscreens = ScreenCount(dpy);
1046     int i;
1047     for (i = 0; i < nscreens; i++)
1048       {
1049         Screen *s = ScreenOfDisplay (dpy, i);
1050         if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1051           {
1052             found_any_writable_cells = True;
1053             break;
1054           }
1055       }
1056
1057     XtVaSetValues (name_to_widget (top, "fadeSecondsLabel"), XtNsensitive,
1058                            found_any_writable_cells, 0);
1059     XtVaSetValues (name_to_widget (top, "fadeTicksLabel"), XtNsensitive,
1060                            found_any_writable_cells, 0);
1061     XtVaSetValues (name_to_widget (top, "fadeSecondsText"), XtNsensitive,
1062                            found_any_writable_cells, 0);
1063     XtVaSetValues (name_to_widget (top, "fadeTicksText"), XtNsensitive,
1064                            found_any_writable_cells, 0);
1065     XtVaSetValues (name_to_widget (top, "cmapToggle"), XtNsensitive,
1066                            found_any_writable_cells, 0);
1067     XtVaSetValues (name_to_widget (top, "fadeToggle"), XtNsensitive,
1068                            found_any_writable_cells, 0);
1069     XtVaSetValues (name_to_widget (top, "unfadeToggle"), XtNsensitive,
1070                            found_any_writable_cells, 0);
1071   }
1072 }
1073
1074
1075 static void
1076 sensitize_demo_widgets (Widget toplevel, Bool sensitive_p)
1077 {
1078   const char *names[] = { "cmdLabel", "cmdText", "enabled",
1079                           "visLabel", "combo", "demo", "man" };
1080   int i;
1081   for (i = 0; i < sizeof(names)/countof(*names); i++)
1082     {
1083       Widget w = name_to_widget (toplevel, names[i]);
1084       XtVaSetValues (w, XtNsensitive, sensitive_p, 0);
1085     }
1086
1087   /* I don't know how to handle these yet... */
1088   {
1089     const char *names2[] = { "cut", "copy", "paste" };
1090     for (i = 0; i < sizeof(names2)/countof(*names2); i++)
1091       {
1092         Widget w = name_to_widget (toplevel, names2[i]);
1093         XtVaSetValues (w, XtNsensitive, FALSE, 0);
1094       }
1095   }
1096 }
1097
1098
1099 \f
1100 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
1101  */
1102
1103 #ifdef HAVE_XPM
1104
1105 static char *up_arrow_xpm[] = {
1106   "15 15 4 1",
1107   "     c None s background",
1108   "-    c #FFFFFF",
1109   "+    c #D6D6D6",
1110   "@    c #000000",
1111
1112   "       @       ",
1113   "       @       ",
1114   "      -+@      ",
1115   "      -+@      ",
1116   "     -+++@     ",
1117   "     -+++@     ",
1118   "    -+++++@    ",
1119   "    -+++++@    ",
1120   "   -+++++++@   ",
1121   "   -+++++++@   ",
1122   "  -+++++++++@  ",
1123   "  -+++++++++@  ",
1124   " -+++++++++++@ ",
1125   " @@@@@@@@@@@@@ ",
1126   "               "
1127 };
1128
1129 static char *down_arrow_xpm[] = {
1130   "15 15 4 1",
1131   "     c None s background",
1132   "-    c #FFFFFF",
1133   "+    c #D6D6D6",
1134   "@    c #000000",
1135
1136   "               ",
1137   " ------------- ",
1138   " -+++++++++++@ ",
1139   "  -+++++++++@  ",
1140   "  -+++++++++@  ",
1141   "   -+++++++@   ",
1142   "   -+++++++@   ",
1143   "    -+++++@    ",
1144   "    -+++++@    ",
1145   "     -+++@     ",
1146   "     -+++@     ",
1147   "      -+@      ",
1148   "      -+@      ",
1149   "       @       ",
1150   "       @       "
1151 };
1152
1153 #endif /* HAVE_XPM */
1154
1155
1156 static void
1157 pixmapify_buttons (Widget toplevel)
1158 {
1159 #ifdef HAVE_XPM
1160
1161   Display *dpy = XtDisplay (toplevel);
1162   Window window = XtWindow (toplevel);
1163   XWindowAttributes xgwa;
1164   XpmAttributes xpmattrs;
1165   Pixmap up_pixmap = 0, down_pixmap = 0;
1166   int result;
1167   Widget up = name_to_widget (toplevel, "up");
1168   Widget dn = name_to_widget (toplevel, "down");
1169 # ifdef XpmColorSymbols
1170   XColor xc;
1171   XpmColorSymbol symbols[2];
1172   char color[20];
1173 # endif
1174
1175   XGetWindowAttributes (dpy, window, &xgwa);
1176
1177   xpmattrs.valuemask = 0;
1178
1179 # ifdef XpmColorSymbols
1180   symbols[0].name = "background";
1181   symbols[0].pixel = 0;
1182   symbols[1].name = 0;
1183   XtVaGetValues (up, XmNbackground, &xc, 0);
1184   XQueryColor (dpy, xgwa.colormap, &xc);
1185   sprintf (color, "#%04X%04X%04X", xc.red, xc.green, xc.blue);
1186   symbols[0].value = color;
1187   symbols[0].pixel = xc.pixel;
1188
1189   xpmattrs.valuemask |= XpmColorSymbols;
1190   xpmattrs.colorsymbols = symbols;
1191   xpmattrs.numsymbols = 1;
1192 # endif
1193
1194 # ifdef XpmCloseness
1195   xpmattrs.valuemask |= XpmCloseness;
1196   xpmattrs.closeness = 40000;
1197 # endif
1198 # ifdef XpmVisual
1199   xpmattrs.valuemask |= XpmVisual;
1200   xpmattrs.visual = xgwa.visual;
1201 # endif
1202 # ifdef XpmDepth
1203   xpmattrs.valuemask |= XpmDepth;
1204   xpmattrs.depth = xgwa.depth;
1205 # endif
1206 # ifdef XpmColormap
1207   xpmattrs.valuemask |= XpmColormap;
1208   xpmattrs.colormap = xgwa.colormap;
1209 # endif
1210
1211   result = XpmCreatePixmapFromData(dpy, window, up_arrow_xpm,
1212                                    &up_pixmap, 0 /* mask */, &xpmattrs);
1213   if (!up_pixmap || (result != XpmSuccess && result != XpmColorError))
1214     {
1215       fprintf (stderr, "%s: Can't load pixmaps\n", progname);
1216       return;
1217     }
1218
1219   result = XpmCreatePixmapFromData(dpy, window, down_arrow_xpm,
1220                                    &down_pixmap, 0 /* mask */, &xpmattrs);
1221   if (!down_pixmap || (result != XpmSuccess && result != XpmColorError))
1222     {
1223       fprintf (stderr, "%s: Can't load pixmaps\n", progname);
1224       return;
1225     }
1226
1227   XtVaSetValues (up, XmNlabelType, XmPIXMAP, XmNlabelPixmap, up_pixmap, 0);
1228   XtVaSetValues (dn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, down_pixmap, 0);
1229
1230 #endif /* HAVE_XPM */
1231 }
1232
1233
1234
1235 char *
1236 get_hack_blurb (screenhack *hack)
1237 {
1238   char *doc_string;
1239   char *prog_name = strdup (hack->command);
1240   char *pretty_name = (hack->name
1241                        ? strdup (hack->name)
1242                        : make_pretty_name (hack->command));
1243   char doc_name[255], doc_class[255];
1244   char *s, *s2;
1245
1246   for (s = prog_name; *s && !isspace(*s); s++)
1247     ;
1248   *s = 0;
1249   s = strrchr (prog_name, '/');
1250   if (s) strcpy (prog_name, s+1);
1251
1252   sprintf (doc_name,  "hacks.%s.documentation", pretty_name);
1253   sprintf (doc_class, "hacks.%s.documentation", prog_name);
1254   free (prog_name);
1255   free (pretty_name);
1256
1257   doc_string = get_string_resource (doc_name, doc_class);
1258   if (doc_string)
1259     {
1260       for (s = doc_string; *s; s++)
1261         {
1262           if (*s == '\n')
1263             {
1264               /* skip over whitespace at beginning of line */
1265               s++;
1266               while (*s && (*s == ' ' || *s == '\t'))
1267                 s++;
1268             }
1269           else if (*s == ' ' || *s == '\t')
1270             {
1271               /* compress all other horizontal whitespace. */
1272               *s = ' ';
1273               s++;
1274               for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++)
1275                 ;
1276               if (s2 > s) strcpy (s, s2);
1277               s--;
1278             }
1279         }
1280
1281       while (*s && isspace (*s))      /* Strip trailing whitespace */
1282         *(--s) = 0;
1283
1284       /* Delete whitespace at end of each line. */
1285       for (; s > doc_string; s--)
1286         if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t'))
1287           {
1288             for (s2 = s-1;
1289                  s2 > doc_string && (*s2 == ' ' || *s2 == '\t');
1290                  s2--)
1291               ;
1292             s2++;
1293             if (s2 < s) strcpy (s2, s);
1294             s = s2;
1295           }
1296       
1297       /* Delete leading blank lines. */
1298       for (s = doc_string; *s == '\n'; s++)
1299         ;
1300       if (s > doc_string) strcpy (doc_string, s);
1301     }
1302   else
1303     {
1304       static int doc_installed = 0;
1305       if (doc_installed == 0)
1306         {
1307           if (get_boolean_resource ("hacks.documentation.isInstalled",
1308                                     "hacks.documentation.isInstalled"))
1309             doc_installed = 1;
1310           else
1311             doc_installed = -1;
1312         }
1313
1314       if (doc_installed < 0)
1315         doc_string =
1316           strdup ("Error:\n\n"
1317                   "The documentation strings do not appear to be "
1318                   "installed.  This is probably because there is "
1319                   "an \"XScreenSaver\" app-defaults file installed "
1320                   "that is from an older version of the program. "
1321                   "To fix this problem, delete that file, or "
1322                   "install a current version (either will work.)");
1323       else
1324         doc_string = strdup ("");
1325     }
1326
1327   return doc_string;
1328 }
1329
1330
1331 static void
1332 populate_demo_window (Widget toplevel, int which, prefs_pair *pair)
1333 {
1334   saver_preferences *p = pair->a;
1335   screenhack *hack = (which >= 0 ? p->screenhacks[which] : 0);
1336   Widget frameL = name_to_widget (toplevel, "frameLabel");
1337   Widget doc = name_to_widget (toplevel, "doc");
1338   Widget cmd = name_to_widget (toplevel, "cmdText");
1339   Widget enabled = name_to_widget (toplevel, "enabled");
1340   Widget vis = name_to_widget (toplevel, "combo");
1341   int i = 0;
1342
1343   char *pretty_name = (hack
1344                        ? (hack->name
1345                           ? strdup (hack->name)
1346                           : make_pretty_name (hack->command))
1347                        : 0);
1348   char *doc_string = hack ? get_hack_blurb (hack) : 0;
1349
1350   XmString xmstr;
1351
1352   xmstr = XmStringCreate (pretty_name, XmSTRING_DEFAULT_CHARSET);
1353   XtVaSetValues (frameL, XmNlabelString, xmstr, 0);
1354   XmStringFree (xmstr);
1355
1356   XtVaSetValues (doc, XmNvalue, doc_string, 0);
1357   XtVaSetValues (cmd, XmNvalue, (hack ? hack->command : ""), 0);
1358
1359   XtVaSetValues (enabled, XmNset, (hack ? hack->enabled_p : False), 0);
1360
1361   i = 0;
1362   if (hack && hack->visual && *hack->visual)
1363     for (i = 0; visual_menu[i]; i++)
1364       if (!strcasecmp (hack->visual, visual_menu[i]))
1365         break;
1366   if (!visual_menu[i]) i = -1;
1367
1368   {
1369 # ifdef HAVE_XMCOMBOBOX
1370     Widget text = 0;
1371     XtVaGetValues (vis, XmNtextField, &text, 0);
1372     XtVaSetValues (vis, XmNselectedPosition, i, 0);
1373     if (i < 0)
1374       XtVaSetValues (text, XmNvalue, hack->visual, 0);
1375 # else /* !HAVE_XMCOMBOBOX */
1376     Cardinal nkids;
1377     Widget *kids;
1378     Widget menu;
1379
1380     XtVaGetValues (vis, XmNsubMenuId, &menu, 0);
1381     if (!menu) abort ();
1382     XtVaGetValues (menu, XmNnumChildren, &nkids, XmNchildren, &kids, 0);
1383     if (!kids) abort();
1384     if (i < nkids)
1385       XtVaSetValues (vis, XmNmenuHistory, kids[i], 0);
1386 # endif /* !HAVE_XMCOMBOBOX */
1387   }
1388
1389   sensitize_demo_widgets (toplevel, (hack ? True : False));
1390
1391   if (pretty_name) free (pretty_name);
1392   if (doc_string) free (doc_string);
1393
1394   _selected_hack_number = which;
1395 }
1396
1397
1398
1399 static int
1400 maybe_reload_init_file (Widget widget, prefs_pair *pair)
1401 {
1402   int status = 0;
1403   saver_preferences *p =  pair->a;
1404
1405   static Bool reentrant_lock = False;
1406   if (reentrant_lock) return 0;
1407   reentrant_lock = True;
1408
1409   if (init_file_changed_p (p))
1410     {
1411       const char *f = init_file_name();
1412       char *b;
1413       int which;
1414       Widget list;
1415
1416       if (!f || !*f) return 0;
1417       b = (char *) malloc (strlen(f) + 1024);
1418       sprintf (b,
1419                "Warning:\n\n"
1420                "file \"%s\" has changed, reloading.\n",
1421                f);
1422       warning_dialog (widget, b, 100);
1423       free (b);
1424
1425       load_init_file (p);
1426
1427       which = selected_hack_number (widget);
1428       list = name_to_widget (widget, "list");
1429
1430       XtVaSetValues (list, XmNitemCount, 0, 0);
1431
1432       populate_hack_list (widget, pair);
1433
1434       XmListDeselectAllItems (list);    /* LessTif lossage */
1435       XmListSelectPos (list, which+1, True);
1436
1437       populate_prefs_page (widget, pair);
1438       populate_demo_window (widget, which, pair);
1439       ensure_selected_item_visible (list);
1440
1441       status = 1;
1442     }
1443
1444   reentrant_lock = False;
1445   return status;
1446 }
1447
1448
1449 \f
1450 /* Attach all callback functions to widgets
1451  */
1452
1453 static void
1454 add_callbacks (Widget toplevel, prefs_pair *pair)
1455 {
1456   Widget w;
1457
1458 # define CB(NAME,FN) \
1459   w = name_to_widget (toplevel, (NAME)); \
1460   XtAddCallback (w, XmNactivateCallback, (FN), pair)
1461
1462   CB ("blank",   activate_menu_cb);
1463   CB ("lock",    lock_menu_cb);
1464   CB ("kill",    kill_menu_cb);
1465   CB ("restart", restart_menu_cb);
1466   CB ("exit",    exit_menu_cb);
1467
1468   CB ("cut",     cut_menu_cb);
1469   CB ("copy",    copy_menu_cb);
1470   CB ("paste",   paste_menu_cb);
1471
1472   CB ("about",   about_menu_cb);
1473   CB ("docMenu", doc_menu_cb);
1474
1475   CB ("down",    run_next_cb);
1476   CB ("up",      run_prev_cb);
1477   CB ("demo",    run_this_cb);
1478   CB ("man",     manual_cb);
1479
1480   CB ("preferencesForm.Cancel",  prefs_cancel_cb);
1481   CB ("preferencesForm.OK",      prefs_ok_cb);
1482
1483 # undef CB
1484 }
1485
1486
1487 static void
1488 sanity_check_resources (Widget toplevel)
1489 {
1490   const char *names[] = { "demoTab", "optionsTab", "cmdLabel", "visLabel",
1491                           "enabled", "demo", "man", "timeoutLabel",
1492                           "cycleLabel", "fadeSecondsLabel", "fadeTicksLabel",
1493                           "lockLabel", "passwdLabel" };
1494   int i;
1495   for (i = 0; i < sizeof(names)/countof(*names); i++)
1496     {
1497       Widget w = name_to_widget (toplevel, names[i]);
1498       const char *name = XtName(w);
1499       XmString xm = 0;
1500       char *label = 0;
1501       XtVaGetValues (w, XmNlabelString, &xm, 0);
1502       if (xm) XmStringGetLtoR (xm, XmSTRING_DEFAULT_CHARSET, &label);
1503       if (w && (!label || !strcmp (name, label)))
1504         {
1505           xm = XmStringCreate ("ERROR", XmSTRING_DEFAULT_CHARSET);
1506           XtVaSetValues (w, XmNlabelString, xm, 0);
1507         }
1508     }
1509 }
1510
1511 /* Set certain buttons to be the same size (the max of the set.)
1512  */
1513 static void
1514 hack_button_sizes (Widget toplevel)
1515 {
1516   Widget demo = name_to_widget (toplevel, "demo");
1517   Widget man  = name_to_widget (toplevel, "man");
1518   Widget ok   = name_to_widget (toplevel, "OK");
1519   Widget can  = name_to_widget (toplevel, "Cancel");
1520   Widget up   = name_to_widget (toplevel, "up");
1521   Widget down = name_to_widget (toplevel, "down");
1522   Dimension w1, w2;
1523
1524   XtVaGetValues (demo, XmNwidth, &w1, 0);
1525   XtVaGetValues (man,  XmNwidth, &w2, 0);
1526   XtVaSetValues ((w1 > w2 ? man : demo), XmNwidth, (w1 > w2 ? w1 : w2), 0);
1527
1528   XtVaGetValues (ok,   XmNwidth, &w1, 0);
1529   XtVaGetValues (can,  XmNwidth, &w2, 0);
1530   XtVaSetValues ((w1 > w2 ? can : ok), XmNwidth, (w1 > w2 ? w1 : w2), 0);
1531
1532   XtVaGetValues (up,   XmNwidth, &w1, 0);
1533   XtVaGetValues (down, XmNwidth, &w2, 0);
1534   XtVaSetValues ((w1 > w2 ? down : up), XmNwidth, (w1 > w2 ? w1 : w2), 0);
1535 }
1536
1537
1538
1539 \f
1540 /* The main demo-mode command loop.
1541  */
1542
1543 #if 0
1544 static Bool
1545 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1546         XrmRepresentation *type, XrmValue *value, XPointer closure)
1547 {
1548   int i;
1549   for (i = 0; quarks[i]; i++)
1550     {
1551       if (bindings[i] == XrmBindTightly)
1552         fprintf (stderr, (i == 0 ? "" : "."));
1553       else if (bindings[i] == XrmBindLoosely)
1554         fprintf (stderr, "*");
1555       else
1556         fprintf (stderr, " ??? ");
1557       fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1558     }
1559
1560   fprintf (stderr, ": %s\n", (char *) value->addr);
1561
1562   return False;
1563 }
1564 #endif
1565
1566
1567 static void
1568 the_network_is_not_the_computer (Widget parent)
1569 {
1570   Display *dpy = XtDisplay (parent);
1571   char *rversion, *ruser, *rhost;
1572   char *luser, *lhost;
1573   char *msg = 0;
1574   struct passwd *p = getpwuid (getuid ());
1575   const char *d = DisplayString (dpy);
1576
1577 # if defined(HAVE_UNAME)
1578   struct utsname uts;
1579   if (uname (&uts) < 0)
1580     lhost = "<UNKNOWN>";
1581   else
1582     lhost = uts.nodename;
1583 # elif defined(VMS)
1584   strcpy (lhost, getenv("SYS$NODE"));
1585 # else  /* !HAVE_UNAME && !VMS */
1586   strcat (lhost, "<UNKNOWN>");
1587 # endif /* !HAVE_UNAME && !VMS */
1588
1589   if (p && p->pw_name)
1590     luser = p->pw_name;
1591   else
1592     luser = "???";
1593
1594   server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
1595
1596   /* Make a buffer that's big enough for a number of copies of all the
1597      strings, plus some. */
1598   msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
1599                                (ruser ? strlen(ruser) : 0) +
1600                                (rhost ? strlen(rhost) : 0) +
1601                                strlen(lhost) +
1602                                strlen(luser) +
1603                                strlen(d) +
1604                                1024));
1605   *msg = 0;
1606
1607   if (!rversion || !*rversion)
1608     {
1609       sprintf (msg,
1610                "Warning:\n\n"
1611                "The XScreenSaver daemon doesn't seem to be running\n"
1612                "on display \"%s\".  You can launch it by selecting\n"
1613                "`Restart Daemon' from the File menu, or by typing\n"
1614                "\"xscreensaver &\" in a shell.",
1615                d);
1616     }
1617   else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
1618     {
1619       /* Warn that the two processes are running as different users.
1620        */
1621       sprintf(msg,
1622                "Warning:\n\n"
1623               "%s is running as user \"%s\" on host \"%s\".\n"
1624               "But the xscreensaver managing display \"%s\"\n"
1625               "is running as user \"%s\" on host \"%s\".\n"
1626               "\n"
1627               "Since they are different users, they won't be reading/writing\n"
1628               "the same ~/.xscreensaver file, so %s isn't\n"
1629               "going to work right.\n"
1630               "\n"
1631               "Either re-run %s as \"%s\", or re-run\n"
1632               "xscreensaver as \"%s\" (which you can do by\n"
1633               "selecting `Restart Daemon' from the File menu.)\n",
1634               progname, luser, lhost,
1635               d,
1636               (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1637               progname,
1638               progname, (ruser ? ruser : "???"),
1639               luser);
1640     }
1641   else if (rhost && *rhost && !!strcmp (rhost, lhost))
1642     {
1643       /* Warn that the two processes are running on different hosts.
1644        */
1645       sprintf (msg,
1646                "Warning:\n\n"
1647                "%s is running as user \"%s\" on host \"%s\".\n"
1648                "But the xscreensaver managing display \"%s\"\n"
1649                "is running as user \"%s\" on host \"%s\".\n"
1650                "\n"
1651                "If those two machines don't share a file system (that is,\n"
1652                "if they don't see the same ~%s/.xscreensaver file) then\n"
1653                "%s won't work right.\n"
1654                "\n"
1655                "You can restart the daemon on \"%s\" as \"%s\" by\n"
1656                "selecting `Restart Daemon' from the File menu.)",
1657                progname, luser, lhost,
1658                d,
1659                (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1660                luser,
1661                progname,
1662                lhost, luser);
1663     }
1664   else if (!!strcmp (rversion, short_version))
1665     {
1666       /* Warn that the version numbers don't match.
1667        */
1668       sprintf (msg,
1669                "Warning:\n\n"
1670                "This is %s version %s.\n"
1671                "But the xscreensaver managing display \"%s\"\n"
1672                "is version %s.  This could cause problems.",
1673                progname, short_version,
1674                d,
1675                rversion);
1676     }
1677
1678
1679   if (*msg)
1680     warning_dialog (parent, msg, 1);
1681
1682   free (msg);
1683 }
1684
1685
1686 /* We use this error handler so that X errors are preceeded by the name
1687    of the program that generated them.
1688  */
1689 static int
1690 demo_ehandler (Display *dpy, XErrorEvent *error)
1691 {
1692   fprintf (stderr, "\nX error in %s:\n", progname);
1693   if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
1694     exit (-1);
1695   else
1696     fprintf (stderr, " (nonfatal.)\n");
1697   return 0;
1698 }
1699
1700
1701
1702 static char *defaults[] = {
1703 #include "XScreenSaver_ad.h"
1704  0
1705 };
1706
1707
1708 int
1709 main (int argc, char **argv)
1710 {
1711   XtAppContext app;
1712   prefs_pair Pair, *pair;
1713   saver_preferences P, P2, *p, *p2;
1714   Bool prefs = False;
1715   int i;
1716   Display *dpy;
1717   Widget toplevel_shell, dialog;
1718   char *real_progname = argv[0];
1719   char *s;
1720
1721   s = strrchr (real_progname, '/');
1722   if (s) real_progname = s+1;
1723
1724   p = &P;
1725   p2 = &P2;
1726   pair = &Pair;
1727   pair->a = p;
1728   pair->b = p2;
1729   memset (p,  0, sizeof (*p));
1730   memset (p2, 0, sizeof (*p2));
1731
1732   global_prefs_pair = pair;
1733
1734   progname = real_progname;
1735
1736   /* We must read exactly the same resources as xscreensaver.
1737      That means we must have both the same progclass *and* progname,
1738      at least as far as the resource database is concerned.  So,
1739      put "xscreensaver" in argv[0] while initializing Xt.
1740    */
1741   argv[0] = "xscreensaver";
1742   progname = argv[0];
1743
1744
1745   toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1746                                     defaults, 0, 0);
1747
1748   dpy = XtDisplay (toplevel_shell);
1749   db = XtDatabase (dpy);
1750   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
1751   XSetErrorHandler (demo_ehandler);
1752
1753   /* Complain about unrecognized command-line arguments.
1754    */
1755   for (i = 1; i < argc; i++)
1756     {
1757       char *s = argv[i];
1758       if (s[0] == '-' && s[1] == '-')
1759         s++;
1760       if (!strcmp (s, "-prefs"))
1761         prefs = True;
1762       else
1763         {
1764           fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n",
1765                    real_progname);
1766           exit (1);
1767         }
1768     }
1769
1770   short_version = (char *) malloc (5);
1771   memcpy (short_version, screensaver_id + 17, 4);
1772   short_version [4] = 0;
1773
1774   /* Load the init file, which may end up consulting the X resource database
1775      and the site-wide app-defaults file.  Note that at this point, it's
1776      important that `progname' be "xscreensaver", rather than whatever
1777      was in argv[0].
1778    */
1779   p->db = db;
1780   load_init_file (p);
1781   *p2 = *p;
1782
1783   /* Now that Xt has been initialized, and the resources have been read,
1784      we can set our `progname' variable to something more in line with
1785      reality.
1786    */
1787   progname = real_progname;
1788
1789
1790 #if 0
1791   {
1792     XrmName name = { 0 };
1793     XrmClass class = { 0 };
1794     int count = 0;
1795     XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1796                           (POINTER) &count);
1797   }
1798 #endif
1799
1800
1801   /* Intern the atoms that xscreensaver_command() needs.
1802    */
1803   XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
1804   XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
1805   XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
1806   XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
1807   XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
1808   XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
1809   XA_SELECT = XInternAtom (dpy, "SELECT", False);
1810   XA_DEMO = XInternAtom (dpy, "DEMO", False);
1811   XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
1812   XA_BLANK = XInternAtom (dpy, "BLANK", False);
1813   XA_LOCK = XInternAtom (dpy, "LOCK", False);
1814   XA_EXIT = XInternAtom (dpy, "EXIT", False);
1815   XA_RESTART = XInternAtom (dpy, "RESTART", False);
1816
1817   /* Create the window and all its widgets.
1818    */
1819   dialog = create_xscreensaver_demo (toplevel_shell);
1820
1821   /* Set the window's title. */
1822   {
1823     char title[255];
1824     char *v = (char *) strdup(strchr(screensaver_id, ' '));
1825     char *s1, *s2, *s3, *s4;
1826     s1 = (char *) strchr(v,  ' '); s1++;
1827     s2 = (char *) strchr(s1, ' ');
1828     s3 = (char *) strchr(v,  '('); s3++;
1829     s4 = (char *) strchr(s3, ')');
1830     *s2 = 0;
1831     *s4 = 0;
1832     sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3);
1833     XtVaSetValues (toplevel_shell, XtNtitle, title, 0);
1834     free (v);
1835   }
1836
1837   sanity_check_resources (toplevel_shell);
1838   add_callbacks (toplevel_shell, pair);
1839   populate_hack_list (toplevel_shell, pair);
1840   populate_prefs_page (toplevel_shell, pair);
1841   sensitize_demo_widgets (toplevel_shell, False);
1842   scroll_to_current_hack (toplevel_shell, pair);
1843
1844   XtManageChild (dialog);
1845   XtRealizeWidget(toplevel_shell);
1846
1847   /* The next few calls must come after XtRealizeWidget(). */
1848   pixmapify_buttons (toplevel_shell);
1849   hack_button_sizes (toplevel_shell);
1850   ensure_selected_item_visible (name_to_widget (toplevel_shell, "list"));
1851
1852   XSync (dpy, False);
1853   XtVaSetValues (toplevel_shell, XmNallowShellResize, False, 0);
1854
1855
1856   /* Handle the -prefs command-line argument. */
1857   if (prefs)
1858     {
1859       Widget tabber = name_to_widget (toplevel_shell, "folder");
1860       Widget this_tab = name_to_widget (toplevel_shell, "optionsTab");
1861       Widget this_page = name_to_widget (toplevel_shell, "preferencesForm");
1862       Widget *kids = 0;
1863       Cardinal nkids = 0;
1864       if (!tabber) abort();
1865   
1866       XtVaGetValues (tabber, XmNnumChildren, &nkids, XmNchildren, &kids, 0);
1867       if (!kids) abort();
1868       if (nkids > 0)
1869         XtUnmanageChildren (kids, nkids);
1870
1871       XtManageChild (this_page);
1872
1873       XmProcessTraversal (this_tab, XmTRAVERSE_CURRENT);
1874     }
1875
1876   /* Issue any warnings about the running xscreensaver daemon. */
1877   the_network_is_not_the_computer (toplevel_shell);
1878
1879
1880   XtAppMainLoop (app);
1881   exit (0);
1882 }
1883
1884 #endif /* HAVE_MOTIF -- whole file */