1 /* demo-Xm.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-1999 Jamie Zawinski <jwz@jwz.org>
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
17 #ifdef HAVE_MOTIF /* whole file */
26 # include <pwd.h> /* for getpwuid() */
32 # include <sys/utsname.h> /* for uname() */
33 #endif /* HAVE_UNAME */
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>
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>
53 # include <X11/Xmu/Error.h>
55 # include <Xmu/Error.h>
66 #include <Xm/LabelG.h>
67 #include <Xm/RowColumn.h>
68 #include <Xm/MessageB.h>
72 #include "resources.h" /* for parse_time() */
73 #include "visual.h" /* for has_writable_cells() */
74 #include "remote.h" /* for xscreensaver_command() */
82 #define countof(x) (sizeof((x))/sizeof((*x)))
86 char *progclass = "XScreenSaver";
90 saver_preferences *a, *b;
93 static void *global_prefs_pair; /* I hate C so much... */
95 char *blurb (void) { return progname; }
97 extern Widget create_xscreensaver_demo (Widget parent);
98 extern const char *visual_menu[];
101 static char *short_version = 0;
104 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
105 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
106 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
109 static void populate_demo_window (Widget toplevel,
110 int which, prefs_pair *pair);
111 static void populate_prefs_page (Widget top, prefs_pair *pair);
112 static int apply_changes_and_save (Widget widget);
113 static int maybe_reload_init_file (Widget widget, prefs_pair *pair);
116 /* Some random utility functions
120 name_to_widget (Widget widget, const char *name)
125 strcpy (name2+1, name);
127 while ((parent = XtParent (widget)))
129 return XtNameToWidget (widget, name2);
134 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
135 Takes a scroller, viewport, or list as an argument.
138 ensure_selected_item_visible (Widget list)
142 if (XmListGetSelectedPos (list, &pos_list, &pos_count) && pos_count > 0)
147 XmNtopItemPosition, &top,
148 XmNvisibleItemCount, &visible,
150 if (pos_list[0] >= top + visible)
152 int pos = pos_list[0] - visible + 1;
153 if (pos < 0) pos = 0;
154 XmListSetPos (list, pos);
156 else if (pos_list[0] < top)
158 XmListSetPos (list, pos_list[0]);
162 XtFree ((char *) pos_list);
167 warning_dialog_dismiss_cb (Widget button, XtPointer client_data,
170 Widget shell = (Widget) client_data;
171 XtDestroyWidget (shell);
176 warning_dialog (Widget parent, const char *message, int center)
178 char *msg = strdup (message);
193 dialog = XmCreateWarningDialog (parent, "warning", av, ac);
195 w = XmMessageBoxGetChild (dialog, XmDIALOG_MESSAGE_LABEL);
196 if (w) XtUnmanageChild (w);
197 w = XmMessageBoxGetChild (dialog, XmDIALOG_CANCEL_BUTTON);
198 if (w) XtUnmanageChild (w);
199 w = XmMessageBoxGetChild (dialog, XmDIALOG_HELP_BUTTON);
200 if (w) XtUnmanageChild (w);
202 ok = XmMessageBoxGetChild (dialog, XmDIALOG_OK_BUTTON);
205 XtSetArg (av[ac], XmNnumColumns, 1); ac++;
206 XtSetArg (av[ac], XmNorientation, XmVERTICAL); ac++;
207 XtSetArg (av[ac], XmNpacking, XmPACK_COLUMN); ac++;
208 XtSetArg (av[ac], XmNrowColumnType, XmWORK_AREA); ac++;
209 XtSetArg (av[ac], XmNspacing, 0); ac++;
210 container = XmCreateRowColumn (dialog, "container", av, ac);
216 char *s = strchr (head, '\n');
219 sprintf (name, "label%d", i++);
221 xmstr = XmStringCreate (head, XmSTRING_DEFAULT_CHARSET);
223 XtSetArg (av[ac], XmNlabelString, xmstr); ac++;
224 XtSetArg (av[ac], XmNmarginHeight, 0); ac++;
225 label = XmCreateLabelGadget (container, name, av, ac);
226 XtManageChild (label);
227 XmStringFree (xmstr);
237 XtManageChild (container);
238 XtRealizeWidget (dialog);
239 XtManageChild (dialog);
241 XtAddCallback (ok, XmNactivateCallback, warning_dialog_dismiss_cb, dialog);
248 run_cmd (Widget widget, Atom command, int arg)
253 apply_changes_and_save (widget);
254 status = xscreensaver_command (XtDisplay (widget),
255 command, arg, False, &err);
260 sprintf (buf, "Error:\n\n%s", err);
262 strcpy (buf, "Unknown error!");
263 warning_dialog (widget, buf, 100);
270 run_hack (Widget widget, int which, Bool report_errors_p)
272 if (which < 0) return;
273 apply_changes_and_save (widget);
275 run_cmd (widget, XA_DEMO, which + 1);
279 xscreensaver_command (XtDisplay (widget), XA_DEMO, which + 1, False, &s);
290 exit_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
292 apply_changes_and_save (XtParent (button));
298 wm_close_cb (Widget widget, GdkEvent *event, XtPointer data)
300 apply_changes_and_save (XtParent (button));
306 cut_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
309 warning_dialog (XtParent (button),
311 "cut unimplemented\n", 1);
316 copy_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
319 warning_dialog (XtParent (button),
321 "copy unimplemented\n", 1);
326 paste_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
329 warning_dialog (XtParent (button),
331 "paste unimplemented\n", 1);
336 about_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
339 char *s = strdup (screensaver_id + 4);
342 s2 = strchr (s, ',');
346 sprintf (buf, "%s\n%s\n\n"
347 "For updates, check http://www.jwz.org/xscreensaver/",
351 warning_dialog (XtParent (button), buf, 100);
356 doc_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
358 prefs_pair *pair = (prefs_pair *) client_data;
360 saver_preferences *p = pair->a;
363 if (!p->help_url || !*p->help_url)
365 warning_dialog (XtParent (button),
367 "No Help URL has been specified.\n", 100);
371 help_command = (char *) malloc (strlen (p->load_url_command) +
372 (strlen (p->help_url) * 2) + 20);
373 strcpy (help_command, "( ");
374 sprintf (help_command + strlen(help_command),
375 p->load_url_command, p->help_url, p->help_url);
376 strcat (help_command, " ) &");
377 system (help_command);
383 activate_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
385 run_cmd (XtParent (button), XA_ACTIVATE, 0);
390 lock_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
392 run_cmd (XtParent (button), XA_LOCK, 0);
397 kill_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
399 run_cmd (XtParent (button), XA_EXIT, 0);
404 restart_menu_cb (Widget button, XtPointer client_data, XtPointer ignored)
407 run_cmd (XtParent (button), XA_RESTART, 0);
409 button = XtParent (button);
410 apply_changes_and_save (button);
411 xscreensaver_command (XtDisplay (button), XA_EXIT, 0, False, NULL);
413 system ("xscreensaver -nosplash &");
418 static int _selected_hack_number = -1;
421 selected_hack_number (Widget toplevel)
423 return _selected_hack_number;
428 demo_write_init_file (Widget widget, saver_preferences *p)
430 if (!write_init_file (p, short_version, False))
434 const char *f = init_file_name();
436 warning_dialog (widget,
437 "Error:\n\nCouldn't determine init file name!\n",
441 char *b = (char *) malloc (strlen(f) + 1024);
442 sprintf (b, "Error:\n\nCouldn't write %s\n", f);
443 warning_dialog (widget, b, 100);
452 apply_changes_and_save (Widget widget)
454 prefs_pair *pair = global_prefs_pair;
455 saver_preferences *p = pair->a;
456 Widget list_widget = name_to_widget (widget, "list");
457 int which = selected_hack_number (widget);
459 Widget cmd = name_to_widget (widget, "cmdText");
460 Widget enabled = name_to_widget (widget, "enabled");
462 Widget vis = name_to_widget (widget, "combo");
463 # ifdef HAVE_XMCOMBOBOX
465 # else /* !HAVE_XMCOMBOBOX */
466 Widget menu = 0, *kids = 0, selected_item = 0;
469 # endif /* !HAVE_XMCOMBOBOX */
471 Bool enabled_p = False;
472 const char *visual = 0;
473 const char *command = 0;
478 if (which < 0) return -1;
480 # ifdef HAVE_XMCOMBOBOX
481 XtVaGetValues (vis, XmNtextField, &text, 0);
482 XtVaGetValues (text, XmNvalue, &visual, 0);
484 # else /* !HAVE_XMCOMBOBOX */
485 XtVaGetValues (vis, XmNsubMenuId, &menu, 0);
486 XtVaGetValues (menu, XmNnumChildren, &nkids, XmNchildren, &kids, 0);
487 XtVaGetValues (menu, XmNmenuHistory, &selected_item, 0);
489 for (i = 0; i < nkids; i++)
490 if (kids[i] == selected_item)
493 visual = visual_menu[i];
494 # endif /* !HAVE_XMCOMBOBOX */
496 XtVaGetValues (enabled, XmNset, &enabled_p, 0);
497 XtVaGetValues (cmd, XtNvalue, &command, 0);
499 if (maybe_reload_init_file (widget, pair) != 0)
502 /* Sanity-check and canonicalize whatever the user typed into the combo box.
504 if (!strcasecmp (visual, "")) visual = "";
505 else if (!strcasecmp (visual, "any")) visual = "";
506 else if (!strcasecmp (visual, "default")) visual = "Default";
507 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
508 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
509 else if (!strcasecmp (visual, "best")) visual = "Best";
510 else if (!strcasecmp (visual, "mono")) visual = "Mono";
511 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
512 else if (!strcasecmp (visual, "gray")) visual = "Gray";
513 else if (!strcasecmp (visual, "grey")) visual = "Gray";
514 else if (!strcasecmp (visual, "color")) visual = "Color";
515 else if (!strcasecmp (visual, "gl")) visual = "GL";
516 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
517 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
518 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
519 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
520 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
521 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
522 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
523 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
524 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
527 XBell (XtDisplay (widget), 0); /* unparsable */
529 /* #### gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");*/
532 ensure_selected_item_visible (list_widget);
534 if (!p->screenhacks[which]->visual)
535 p->screenhacks[which]->visual = strdup ("");
536 if (!p->screenhacks[which]->command)
537 p->screenhacks[which]->command = strdup ("");
539 if (p->screenhacks[which]->enabled_p != enabled_p ||
540 !!strcasecmp (p->screenhacks[which]->visual, visual) ||
541 !!strcasecmp (p->screenhacks[which]->command, command))
543 /* Something was changed -- store results into the struct,
546 free (p->screenhacks[which]->visual);
547 free (p->screenhacks[which]->command);
548 p->screenhacks[which]->visual = strdup (visual);
549 p->screenhacks[which]->command = strdup (command);
550 p->screenhacks[which]->enabled_p = enabled_p;
552 return demo_write_init_file (widget, p);
555 /* No changes made */
560 run_this_cb (Widget button, XtPointer client_data, XtPointer ignored)
562 int which = selected_hack_number (XtParent (button));
563 if (which < 0) return;
564 if (0 == apply_changes_and_save (XtParent (button)))
565 run_hack (XtParent (button), which, True);
570 manual_cb (Widget button, XtPointer client_data, XtPointer ignored)
572 prefs_pair *pair = (prefs_pair *) client_data;
573 saver_preferences *p = pair->a;
574 Widget list_widget = name_to_widget (button, "list");
575 int which = selected_hack_number (button);
576 char *name, *name2, *cmd, *s;
577 if (which < 0) return;
578 apply_changes_and_save (button);
579 ensure_selected_item_visible (list_widget);
581 name = strdup (p->screenhacks[which]->command);
583 while (isspace (*name2)) name2++;
585 while (*s && !isspace (*s)) s++;
587 s = strrchr (name2, '/');
590 cmd = get_string_resource ("manualCommand", "ManualCommand");
593 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
595 sprintf (cmd2 + strlen (cmd2),
597 name2, name2, name2, name2);
598 strcat (cmd2, " ) &");
604 warning_dialog (XtParent (button),
605 "Error:\n\nno `manualCommand' resource set.",
614 run_next_cb (Widget button, XtPointer client_data, XtPointer ignored)
616 prefs_pair *pair = (prefs_pair *) client_data;
617 saver_preferences *p = pair->a;
619 Widget list_widget = name_to_widget (button, "list");
620 int which = selected_hack_number (button);
622 button = XtParent (button);
629 if (which >= p->screenhacks_count)
632 apply_changes_and_save (button);
634 XmListDeselectAllItems (list_widget); /* LessTif lossage */
635 XmListSelectPos (list_widget, which+1, True);
637 ensure_selected_item_visible (list_widget);
638 populate_demo_window (button, which, pair);
639 run_hack (button, which, False);
644 run_prev_cb (Widget button, XtPointer client_data, XtPointer ignored)
646 prefs_pair *pair = (prefs_pair *) client_data;
647 saver_preferences *p = pair->a;
649 Widget list_widget = name_to_widget (button, "list");
650 int which = selected_hack_number (button);
652 button = XtParent (button);
655 which = p->screenhacks_count - 1;
660 which = p->screenhacks_count - 1;
662 apply_changes_and_save (button);
664 XmListDeselectAllItems (list_widget); /* LessTif lossage */
665 XmListSelectPos (list_widget, which+1, True);
667 ensure_selected_item_visible (list_widget);
668 populate_demo_window (button, which, pair);
669 run_hack (button, which, False);
673 /* Helper for the text fields that contain time specifications:
674 this parses the text, and does error checking.
677 hack_time_text (const char *line, Time *store, Bool sec_p)
682 value = parse_time ((char *) line, sec_p, True);
683 value *= 1000; /* Time measures in microseconds */
693 prefs_ok_cb (Widget button, XtPointer client_data, XtPointer ignored)
695 prefs_pair *pair = (prefs_pair *) client_data;
697 saver_preferences *p = pair->a;
698 saver_preferences *p2 = pair->b;
699 Bool changed = False;
702 button = XtParent (button);
704 # define SECONDS(field, name) \
706 XtVaGetValues (name_to_widget (button, (name)), XtNvalue, &v, 0); \
707 hack_time_text (v, (field), True)
709 # define MINUTES(field, name) \
711 XtVaGetValues (name_to_widget (button, (name)), XtNvalue, &v, 0); \
712 hack_time_text (v, (field), False)
714 # define INTEGER(field, name) do { \
715 unsigned int value; \
717 XtVaGetValues (name_to_widget (button, (name)), XtNvalue, &v, 0); \
720 else if (sscanf (v, "%u%c", &value, &c) != 1) \
721 XBell(XtDisplay(button), 0); \
726 # define CHECKBOX(field, name) \
727 XtVaGetValues (name_to_widget (button, (name)), XmNset, &field, 0)
729 MINUTES (&p2->timeout, "timeoutText");
730 MINUTES (&p2->cycle, "cycleText");
731 SECONDS (&p2->fade_seconds, "fadeSecondsText");
732 INTEGER (&p2->fade_ticks, "fadeTicksText");
733 MINUTES (&p2->lock_timeout, "lockText");
734 SECONDS (&p2->passwd_timeout, "passwdText");
735 CHECKBOX (p2->verbose_p, "verboseToggle");
736 CHECKBOX (p2->install_cmap_p, "cmapToggle");
737 CHECKBOX (p2->fade_p, "fadeToggle");
738 CHECKBOX (p2->unfade_p, "unfadeToggle");
739 CHECKBOX (p2->lock_p, "lockToggle");
746 # define COPY(field) \
747 if (p->field != p2->field) changed = True; \
753 COPY(passwd_timeout);
757 COPY(install_cmap_p);
763 populate_prefs_page (button, pair);
766 demo_write_init_file (button, p);
771 prefs_cancel_cb (Widget button, XtPointer client_data, XtPointer ignored)
773 prefs_pair *pair = (prefs_pair *) client_data;
776 populate_prefs_page (XtParent (button), pair);
781 list_select_cb (Widget list, XtPointer client_data, XtPointer call_data)
783 prefs_pair *pair = (prefs_pair *) client_data;
785 XmListCallbackStruct *lcb = (XmListCallbackStruct *) call_data;
786 int which = lcb->item_position - 1;
788 apply_changes_and_save (list);
789 populate_demo_window (list, which, pair);
791 if (lcb->reason == XmCR_DEFAULT_ACTION && which >= 0)
792 run_hack (list, which, True);
796 /* Populating the various widgets
800 /* Formats a `Time' into "H:MM:SS". (Time is microseconds.)
803 format_time (char *buf, Time time)
806 unsigned int h = 0, m = 0;
817 sprintf (buf, "%u:%02u:%02u", h, m, s);
822 make_pretty_name (const char *shell_command)
824 char *s = strdup (shell_command);
828 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
835 s2 = strrchr (s, '/'); /* if pathname, take last component */
843 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
846 sprintf (res_name, "hacks.%s.name", s); /* resource? */
847 s2 = get_string_resource (res_name, res_name);
851 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
852 if (*s2 >= 'A' && *s2 <= 'Z')
855 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
857 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
863 /* Finds the number of the last hack to run, and makes that item be
867 scroll_to_current_hack (Widget toplevel, prefs_pair *pair)
871 unsigned long nitems, bytesafter;
873 Display *dpy = XtDisplay (toplevel);
877 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
878 XA_SCREENSAVER_STATUS,
879 0, 3, False, XA_INTEGER,
880 &type, &format, &nitems, &bytesafter,
881 (unsigned char **) &data)
883 && type == XA_INTEGER
886 which = (int) data[2] - 1;
888 if (data) free (data);
893 list = name_to_widget (toplevel, "list");
894 apply_changes_and_save (toplevel);
896 XmListDeselectAllItems (list); /* LessTif lossage */
897 XmListSelectPos (list, which+1, True);
899 ensure_selected_item_visible (list);
900 populate_demo_window (toplevel, which, pair);
906 populate_hack_list (Widget toplevel, prefs_pair *pair)
908 saver_preferences *p = pair->a;
909 Widget list = name_to_widget (toplevel, "list");
910 screenhack **hacks = p->screenhacks;
913 for (h = hacks; *h; h++)
915 char *pretty_name = (h[0]->name
916 ? strdup (h[0]->name)
917 : make_pretty_name (h[0]->command));
919 XmString xmstr = XmStringCreate (pretty_name, XmSTRING_DEFAULT_CHARSET);
920 XmListAddItem (list, xmstr, 0);
921 XmStringFree (xmstr);
924 XtAddCallback (list, XmNbrowseSelectionCallback, list_select_cb, pair);
925 XtAddCallback (list, XmNdefaultActionCallback, list_select_cb, pair);
930 populate_prefs_page (Widget top, prefs_pair *pair)
932 saver_preferences *p = pair->a;
935 format_time (s, p->timeout);
936 XtVaSetValues (name_to_widget (top, "timeoutText"), XmNvalue, s, 0);
937 format_time (s, p->cycle);
938 XtVaSetValues (name_to_widget (top, "cycleText"), XmNvalue, s, 0);
939 format_time (s, p->lock_timeout);
940 XtVaSetValues (name_to_widget (top, "lockText"), XmNvalue, s, 0);
941 format_time (s, p->passwd_timeout);
942 XtVaSetValues (name_to_widget (top, "passwdText"), XmNvalue, s, 0);
943 format_time (s, p->fade_seconds);
944 XtVaSetValues (name_to_widget (top, "fadeSecondsText"), XmNvalue, s, 0);
945 sprintf (s, "%u", p->fade_ticks);
946 XtVaSetValues (name_to_widget (top, "fadeTicksText"), XmNvalue, s, 0);
948 XtVaSetValues (name_to_widget (top, "verboseToggle"),
949 XmNset, p->verbose_p, 0);
950 XtVaSetValues (name_to_widget (top, "cmapToggle"),
951 XmNset, p->install_cmap_p, 0);
952 XtVaSetValues (name_to_widget (top, "fadeToggle"),
953 XmNset, p->fade_p, 0);
954 XtVaSetValues (name_to_widget (top, "unfadeToggle"),
955 XmNset, p->unfade_p, 0);
956 XtVaSetValues (name_to_widget (top, "lockToggle"),
957 XmNset, p->lock_p, 0);
961 Bool found_any_writable_cells = False;
962 Display *dpy = XtDisplay (top);
963 int nscreens = ScreenCount(dpy);
965 for (i = 0; i < nscreens; i++)
967 Screen *s = ScreenOfDisplay (dpy, i);
968 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
970 found_any_writable_cells = True;
975 XtVaSetValues (name_to_widget (top, "fadeSecondsLabel"), XtNsensitive,
976 found_any_writable_cells, 0);
977 XtVaSetValues (name_to_widget (top, "fadeTicksLabel"), XtNsensitive,
978 found_any_writable_cells, 0);
979 XtVaSetValues (name_to_widget (top, "fadeSecondsText"), XtNsensitive,
980 found_any_writable_cells, 0);
981 XtVaSetValues (name_to_widget (top, "fadeTicksText"), XtNsensitive,
982 found_any_writable_cells, 0);
983 XtVaSetValues (name_to_widget (top, "cmapToggle"), XtNsensitive,
984 found_any_writable_cells, 0);
985 XtVaSetValues (name_to_widget (top, "fadeToggle"), XtNsensitive,
986 found_any_writable_cells, 0);
987 XtVaSetValues (name_to_widget (top, "unfadeToggle"), XtNsensitive,
988 found_any_writable_cells, 0);
994 sensitize_demo_widgets (Widget toplevel, Bool sensitive_p)
996 const char *names[] = { "cmdLabel", "cmdText", "enabled",
997 "visLabel", "combo", "demo", "man" };
999 for (i = 0; i < sizeof(names)/countof(*names); i++)
1001 Widget w = name_to_widget (toplevel, names[i]);
1002 XtVaSetValues (w, XtNsensitive, sensitive_p, 0);
1005 /* I don't know how to handle these yet... */
1007 const char *names2[] = { "cut", "copy", "paste" };
1008 for (i = 0; i < sizeof(names2)/countof(*names2); i++)
1010 Widget w = name_to_widget (toplevel, names2[i]);
1011 XtVaSetValues (w, XtNsensitive, FALSE, 0);
1018 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
1023 static char *up_arrow_xpm[] = {
1025 " c None s background",
1047 static char *down_arrow_xpm[] = {
1049 " c None s background",
1071 #endif /* HAVE_XPM */
1075 pixmapify_buttons (Widget toplevel)
1079 Display *dpy = XtDisplay (toplevel);
1080 Window window = XtWindow (toplevel);
1081 XWindowAttributes xgwa;
1082 XpmAttributes xpmattrs;
1083 Pixmap up_pixmap = 0, down_pixmap = 0;
1085 Widget up = name_to_widget (toplevel, "up");
1086 Widget dn = name_to_widget (toplevel, "down");
1087 # ifdef XpmColorSymbols
1089 XpmColorSymbol symbols[2];
1093 XGetWindowAttributes (dpy, window, &xgwa);
1095 xpmattrs.valuemask = 0;
1097 # ifdef XpmColorSymbols
1098 symbols[0].name = "background";
1099 symbols[0].pixel = 0;
1100 symbols[1].name = 0;
1101 XtVaGetValues (up, XmNbackground, &xc, 0);
1102 XQueryColor (dpy, xgwa.colormap, &xc);
1103 sprintf (color, "#%04X%04X%04X", xc.red, xc.green, xc.blue);
1104 symbols[0].value = color;
1105 symbols[0].pixel = xc.pixel;
1107 xpmattrs.valuemask |= XpmColorSymbols;
1108 xpmattrs.colorsymbols = symbols;
1109 xpmattrs.numsymbols = 1;
1112 # ifdef XpmCloseness
1113 xpmattrs.valuemask |= XpmCloseness;
1114 xpmattrs.closeness = 40000;
1117 xpmattrs.valuemask |= XpmVisual;
1118 xpmattrs.visual = xgwa.visual;
1121 xpmattrs.valuemask |= XpmDepth;
1122 xpmattrs.depth = xgwa.depth;
1125 xpmattrs.valuemask |= XpmColormap;
1126 xpmattrs.colormap = xgwa.colormap;
1129 result = XpmCreatePixmapFromData(dpy, window, up_arrow_xpm,
1130 &up_pixmap, 0 /* mask */, &xpmattrs);
1131 if (!up_pixmap || (result != XpmSuccess && result != XpmColorError))
1133 fprintf (stderr, "%s: Can't load pixmaps\n", progname);
1137 result = XpmCreatePixmapFromData(dpy, window, down_arrow_xpm,
1138 &down_pixmap, 0 /* mask */, &xpmattrs);
1139 if (!down_pixmap || (result != XpmSuccess && result != XpmColorError))
1141 fprintf (stderr, "%s: Can't load pixmaps\n", progname);
1145 XtVaSetValues (up, XmNlabelType, XmPIXMAP, XmNlabelPixmap, up_pixmap, 0);
1146 XtVaSetValues (dn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, down_pixmap, 0);
1148 #endif /* HAVE_XPM */
1154 get_hack_blurb (screenhack *hack)
1157 char *prog_name = strdup (hack->command);
1158 char *pretty_name = (hack->name
1159 ? strdup (hack->name)
1160 : make_pretty_name (hack->command));
1161 char doc_name[255], doc_class[255];
1164 for (s = prog_name; *s && !isspace(*s); s++)
1167 s = strrchr (prog_name, '/');
1168 if (s) strcpy (prog_name, s+1);
1170 sprintf (doc_name, "hacks.%s.documentation", pretty_name);
1171 sprintf (doc_class, "hacks.%s.documentation", prog_name);
1175 doc_string = get_string_resource (doc_name, doc_class);
1178 for (s = doc_string; *s; s++)
1182 /* skip over whitespace at beginning of line */
1184 while (*s && (*s == ' ' || *s == '\t'))
1187 else if (*s == ' ' || *s == '\t')
1189 /* compress all other horizontal whitespace. */
1192 for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++)
1194 if (s2 > s) strcpy (s, s2);
1199 while (*s && isspace (*s)) /* Strip trailing whitespace */
1202 /* Delete whitespace at end of each line. */
1203 for (; s > doc_string; s--)
1204 if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t'))
1207 s2 > doc_string && (*s2 == ' ' || *s2 == '\t');
1211 if (s2 < s) strcpy (s2, s);
1215 /* Delete leading blank lines. */
1216 for (s = doc_string; *s == '\n'; s++)
1218 if (s > doc_string) strcpy (doc_string, s);
1222 static int doc_installed = 0;
1223 if (doc_installed == 0)
1225 if (get_boolean_resource ("hacks.documentation.isInstalled",
1226 "hacks.documentation.isInstalled"))
1232 if (doc_installed < 0)
1234 strdup ("Error:\n\n"
1235 "The documentation strings do not appear to be "
1236 "installed. This is probably because there is "
1237 "an \"XScreenSaver\" app-defaults file installed "
1238 "that is from an older version of the program. "
1239 "To fix this problem, delete that file, or "
1240 "install a current version (either will work.)");
1242 doc_string = strdup ("");
1250 populate_demo_window (Widget toplevel, int which, prefs_pair *pair)
1252 saver_preferences *p = pair->a;
1253 screenhack *hack = (which >= 0 ? p->screenhacks[which] : 0);
1254 Widget frameL = name_to_widget (toplevel, "frameLabel");
1255 Widget doc = name_to_widget (toplevel, "doc");
1256 Widget cmd = name_to_widget (toplevel, "cmdText");
1257 Widget enabled = name_to_widget (toplevel, "enabled");
1258 Widget vis = name_to_widget (toplevel, "combo");
1261 char *pretty_name = (hack
1263 ? strdup (hack->name)
1264 : make_pretty_name (hack->command))
1266 char *doc_string = hack ? get_hack_blurb (hack) : 0;
1270 xmstr = XmStringCreate (pretty_name, XmSTRING_DEFAULT_CHARSET);
1271 XtVaSetValues (frameL, XmNlabelString, xmstr, 0);
1272 XmStringFree (xmstr);
1274 XtVaSetValues (doc, XmNvalue, doc_string, 0);
1275 XtVaSetValues (cmd, XmNvalue, (hack ? hack->command : ""), 0);
1277 XtVaSetValues (enabled, XmNset, (hack ? hack->enabled_p : False), 0);
1280 if (hack && hack->visual && *hack->visual)
1281 for (i = 0; visual_menu[i]; i++)
1282 if (!strcasecmp (hack->visual, visual_menu[i]))
1284 if (!visual_menu[i]) i = -1;
1287 # ifdef HAVE_XMCOMBOBOX
1289 XtVaGetValues (vis, XmNtextField, &text, 0);
1290 XtVaSetValues (vis, XmNselectedPosition, i, 0);
1292 XtVaSetValues (text, XmNvalue, hack->visual, 0);
1293 # else /* !HAVE_XMCOMBOBOX */
1298 XtVaGetValues (vis, XmNsubMenuId, &menu, 0);
1299 if (!menu) abort ();
1300 XtVaGetValues (menu, XmNnumChildren, &nkids, XmNchildren, &kids, 0);
1303 XtVaSetValues (vis, XmNmenuHistory, kids[i], 0);
1304 # endif /* !HAVE_XMCOMBOBOX */
1307 sensitize_demo_widgets (toplevel, (hack ? True : False));
1309 if (pretty_name) free (pretty_name);
1310 if (doc_string) free (doc_string);
1312 _selected_hack_number = which;
1318 maybe_reload_init_file (Widget widget, prefs_pair *pair)
1321 saver_preferences *p = pair->a;
1323 static Bool reentrant_lock = False;
1324 if (reentrant_lock) return 0;
1325 reentrant_lock = True;
1327 if (init_file_changed_p (p))
1329 const char *f = init_file_name();
1334 if (!f || !*f) return 0;
1335 b = (char *) malloc (strlen(f) + 1024);
1338 "file \"%s\" has changed, reloading.\n",
1340 warning_dialog (widget, b, 100);
1345 which = selected_hack_number (widget);
1346 list = name_to_widget (widget, "list");
1348 XtVaSetValues (list, XmNitemCount, 0, 0);
1350 populate_hack_list (widget, pair);
1352 XmListDeselectAllItems (list); /* LessTif lossage */
1353 XmListSelectPos (list, which+1, True);
1355 populate_prefs_page (widget, pair);
1356 populate_demo_window (widget, which, pair);
1357 ensure_selected_item_visible (list);
1362 reentrant_lock = False;
1368 /* Attach all callback functions to widgets
1372 add_callbacks (Widget toplevel, prefs_pair *pair)
1376 # define CB(NAME,FN) \
1377 w = name_to_widget (toplevel, (NAME)); \
1378 XtAddCallback (w, XmNactivateCallback, (FN), pair)
1380 CB ("blank", activate_menu_cb);
1381 CB ("lock", lock_menu_cb);
1382 CB ("kill", kill_menu_cb);
1383 CB ("restart", restart_menu_cb);
1384 CB ("exit", exit_menu_cb);
1386 CB ("cut", cut_menu_cb);
1387 CB ("copy", copy_menu_cb);
1388 CB ("paste", paste_menu_cb);
1390 CB ("about", about_menu_cb);
1391 CB ("docMenu", doc_menu_cb);
1393 CB ("down", run_next_cb);
1394 CB ("up", run_prev_cb);
1395 CB ("demo", run_this_cb);
1396 CB ("man", manual_cb);
1398 CB ("preferencesForm.Cancel", prefs_cancel_cb);
1399 CB ("preferencesForm.OK", prefs_ok_cb);
1406 sanity_check_resources (Widget toplevel)
1408 const char *names[] = { "demoTab", "optionsTab", "cmdLabel", "visLabel",
1409 "enabled", "demo", "man", "timeoutLabel",
1410 "cycleLabel", "fadeSecondsLabel", "fadeTicksLabel",
1411 "lockLabel", "passwdLabel" };
1413 for (i = 0; i < sizeof(names)/countof(*names); i++)
1415 Widget w = name_to_widget (toplevel, names[i]);
1416 const char *name = XtName(w);
1419 XtVaGetValues (w, XmNlabelString, &xm, 0);
1420 if (xm) XmStringGetLtoR (xm, XmSTRING_DEFAULT_CHARSET, &label);
1421 if (w && (!label || !strcmp (name, label)))
1423 xm = XmStringCreate ("ERROR", XmSTRING_DEFAULT_CHARSET);
1424 XtVaSetValues (w, XmNlabelString, xm, 0);
1429 /* Set certain buttons to be the same size (the max of the set.)
1432 hack_button_sizes (Widget toplevel)
1434 Widget demo = name_to_widget (toplevel, "demo");
1435 Widget man = name_to_widget (toplevel, "man");
1436 Widget ok = name_to_widget (toplevel, "OK");
1437 Widget can = name_to_widget (toplevel, "Cancel");
1438 Widget up = name_to_widget (toplevel, "up");
1439 Widget down = name_to_widget (toplevel, "down");
1442 XtVaGetValues (demo, XmNwidth, &w1, 0);
1443 XtVaGetValues (man, XmNwidth, &w2, 0);
1444 XtVaSetValues ((w1 > w2 ? man : demo), XmNwidth, (w1 > w2 ? w1 : w2), 0);
1446 XtVaGetValues (ok, XmNwidth, &w1, 0);
1447 XtVaGetValues (can, XmNwidth, &w2, 0);
1448 XtVaSetValues ((w1 > w2 ? can : ok), XmNwidth, (w1 > w2 ? w1 : w2), 0);
1450 XtVaGetValues (up, XmNwidth, &w1, 0);
1451 XtVaGetValues (down, XmNwidth, &w2, 0);
1452 XtVaSetValues ((w1 > w2 ? down : up), XmNwidth, (w1 > w2 ? w1 : w2), 0);
1458 /* The main demo-mode command loop.
1463 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1464 XrmRepresentation *type, XrmValue *value, XPointer closure)
1467 for (i = 0; quarks[i]; i++)
1469 if (bindings[i] == XrmBindTightly)
1470 fprintf (stderr, (i == 0 ? "" : "."));
1471 else if (bindings[i] == XrmBindLoosely)
1472 fprintf (stderr, "*");
1474 fprintf (stderr, " ??? ");
1475 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1478 fprintf (stderr, ": %s\n", (char *) value->addr);
1486 the_network_is_not_the_computer (Widget parent)
1488 Display *dpy = XtDisplay (parent);
1489 char *rversion, *ruser, *rhost;
1490 char *luser, *lhost;
1492 struct passwd *p = getpwuid (getuid ());
1493 const char *d = DisplayString (dpy);
1495 # if defined(HAVE_UNAME)
1497 if (uname (&uts) < 0)
1498 lhost = "<UNKNOWN>";
1500 lhost = uts.nodename;
1502 strcpy (lhost, getenv("SYS$NODE"));
1503 # else /* !HAVE_UNAME && !VMS */
1504 strcat (lhost, "<UNKNOWN>");
1505 # endif /* !HAVE_UNAME && !VMS */
1507 if (p && p->pw_name)
1512 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
1514 /* Make a buffer that's big enough for a number of copies of all the
1515 strings, plus some. */
1516 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
1517 (ruser ? strlen(ruser) : 0) +
1518 (rhost ? strlen(rhost) : 0) +
1525 if (!rversion || !*rversion)
1529 "The XScreenSaver daemon doesn't seem to be running\n"
1530 "on display \"%s\". You can launch it by selecting\n"
1531 "`Restart Daemon' from the File menu, or by typing\n"
1532 "\"xscreensaver &\" in a shell.",
1535 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
1537 /* Warn that the two processes are running as different users.
1541 "%s is running as user \"%s\" on host \"%s\".\n"
1542 "But the xscreensaver managing display \"%s\"\n"
1543 "is running as user \"%s\" on host \"%s\".\n"
1545 "Since they are different users, they won't be reading/writing\n"
1546 "the same ~/.xscreensaver file, so %s isn't\n"
1547 "going to work right.\n"
1549 "Either re-run %s as \"%s\", or re-run\n"
1550 "xscreensaver as \"%s\" (which you can do by\n"
1551 "selecting `Restart Daemon' from the File menu.)\n",
1552 progname, luser, lhost,
1554 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1556 progname, (ruser ? ruser : "???"),
1559 else if (rhost && *rhost && !!strcmp (rhost, lhost))
1561 /* Warn that the two processes are running on different hosts.
1565 "%s is running as user \"%s\" on host \"%s\".\n"
1566 "But the xscreensaver managing display \"%s\"\n"
1567 "is running as user \"%s\" on host \"%s\".\n"
1569 "If those two machines don't share a file system (that is,\n"
1570 "if they don't see the same ~%s/.xscreensaver file) then\n"
1571 "%s won't work right.\n"
1573 "You can restart the daemon on \"%s\" as \"%s\" by\n"
1574 "selecting `Restart Daemon' from the File menu.)",
1575 progname, luser, lhost,
1577 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1582 else if (!!strcmp (rversion, short_version))
1584 /* Warn that the version numbers don't match.
1588 "This is %s version %s.\n"
1589 "But the xscreensaver managing display \"%s\"\n"
1590 "is version %s. This could cause problems.",
1591 progname, short_version,
1598 warning_dialog (parent, msg, 1);
1604 /* We use this error handler so that X errors are preceeded by the name
1605 of the program that generated them.
1608 demo_ehandler (Display *dpy, XErrorEvent *error)
1610 fprintf (stderr, "\nX error in %s:\n", progname);
1611 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
1614 fprintf (stderr, " (nonfatal.)\n");
1620 static char *defaults[] = {
1621 #include "XScreenSaver_ad.h"
1627 main (int argc, char **argv)
1630 prefs_pair Pair, *pair;
1631 saver_preferences P, P2, *p, *p2;
1635 Widget toplevel_shell, dialog;
1636 char *real_progname = argv[0];
1639 s = strrchr (real_progname, '/');
1640 if (s) real_progname = s+1;
1647 memset (p, 0, sizeof (*p));
1648 memset (p2, 0, sizeof (*p2));
1650 global_prefs_pair = pair;
1652 progname = real_progname;
1654 /* We must read exactly the same resources as xscreensaver.
1655 That means we must have both the same progclass *and* progname,
1656 at least as far as the resource database is concerned. So,
1657 put "xscreensaver" in argv[0] while initializing Xt.
1659 argv[0] = "xscreensaver";
1663 toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1666 dpy = XtDisplay (toplevel_shell);
1667 db = XtDatabase (dpy);
1668 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
1669 XSetErrorHandler (demo_ehandler);
1671 /* Complain about unrecognized command-line arguments.
1673 for (i = 1; i < argc; i++)
1676 if (s[0] == '-' && s[1] == '-')
1678 if (!strcmp (s, "-prefs"))
1682 fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n",
1688 short_version = (char *) malloc (5);
1689 memcpy (short_version, screensaver_id + 17, 4);
1690 short_version [4] = 0;
1692 /* Load the init file, which may end up consulting the X resource database
1693 and the site-wide app-defaults file. Note that at this point, it's
1694 important that `progname' be "xscreensaver", rather than whatever
1701 /* Now that Xt has been initialized, and the resources have been read,
1702 we can set our `progname' variable to something more in line with
1705 progname = real_progname;
1710 XrmName name = { 0 };
1711 XrmClass class = { 0 };
1713 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1719 /* Intern the atoms that xscreensaver_command() needs.
1721 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
1722 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
1723 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
1724 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
1725 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
1726 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
1727 XA_SELECT = XInternAtom (dpy, "SELECT", False);
1728 XA_DEMO = XInternAtom (dpy, "DEMO", False);
1729 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
1730 XA_BLANK = XInternAtom (dpy, "BLANK", False);
1731 XA_LOCK = XInternAtom (dpy, "LOCK", False);
1732 XA_EXIT = XInternAtom (dpy, "EXIT", False);
1733 XA_RESTART = XInternAtom (dpy, "RESTART", False);
1735 /* Create the window and all its widgets.
1737 dialog = create_xscreensaver_demo (toplevel_shell);
1739 /* Set the window's title. */
1742 char *v = (char *) strdup(strchr(screensaver_id, ' '));
1743 char *s1, *s2, *s3, *s4;
1744 s1 = (char *) strchr(v, ' '); s1++;
1745 s2 = (char *) strchr(s1, ' ');
1746 s3 = (char *) strchr(v, '('); s3++;
1747 s4 = (char *) strchr(s3, ')');
1750 sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3);
1751 XtVaSetValues (toplevel_shell, XtNtitle, title, 0);
1755 sanity_check_resources (toplevel_shell);
1756 add_callbacks (toplevel_shell, pair);
1757 populate_hack_list (toplevel_shell, pair);
1758 populate_prefs_page (toplevel_shell, pair);
1759 sensitize_demo_widgets (toplevel_shell, False);
1760 scroll_to_current_hack (toplevel_shell, pair);
1762 XtManageChild (dialog);
1763 XtRealizeWidget(toplevel_shell);
1765 /* The next few calls must come after XtRealizeWidget(). */
1766 pixmapify_buttons (toplevel_shell);
1767 hack_button_sizes (toplevel_shell);
1768 ensure_selected_item_visible (name_to_widget (toplevel_shell, "list"));
1771 XtVaSetValues (toplevel_shell, XmNallowShellResize, False, 0);
1774 /* Handle the -prefs command-line argument. */
1777 Widget tabber = name_to_widget (toplevel_shell, "folder");
1778 Widget this_tab = name_to_widget (toplevel_shell, "optionsTab");
1779 Widget this_page = name_to_widget (toplevel_shell, "preferencesForm");
1782 if (!tabber) abort();
1784 XtVaGetValues (tabber, XmNnumChildren, &nkids, XmNchildren, &kids, 0);
1787 XtUnmanageChildren (kids, nkids);
1789 XtManageChild (this_page);
1791 XmProcessTraversal (this_tab, XmTRAVERSE_CURRENT);
1794 /* Issue any warnings about the running xscreensaver daemon. */
1795 the_network_is_not_the_computer (toplevel_shell);
1798 XtAppMainLoop (app);
1802 #endif /* HAVE_MOTIF -- whole file */