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