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