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