1 /* demo.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-1998 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
20 # define HAVE_ATHENA 1
27 /* Only one, please. */
43 # include <pwd.h> /* for getpwuid() */
49 # include <sys/utsname.h> /* for uname() */
50 #endif /* HAVE_UNAME */
54 #include <X11/Xproto.h> /* for CARD32 */
55 #include <X11/Xatom.h> /* for XA_INTEGER */
56 #include <X11/Intrinsic.h>
57 #include <X11/StringDefs.h>
59 /* We don't actually use any widget internals, but these are included
60 so that gdb will have debug info for the widgets... */
61 #include <X11/IntrinsicP.h>
62 #include <X11/ShellP.h>
66 # include <X11/Xmu/Error.h>
68 # include <Xmu/Error.h>
79 # include <Xm/ToggleB.h>
80 # include <Xm/MessageB.h>
81 # include <Xm/LabelG.h>
82 # include <Xm/RowColumn.h>
84 #elif defined(HAVE_ATHENA)
85 /* Athena demo code contributed by Jon A. Christopher <jac8782@tamu.edu> */
86 /* Copyright 1997, with the same permissions as above. */
87 # include <X11/Shell.h>
88 # include <X11/Xaw/Form.h>
89 # include <X11/Xaw/Box.h>
90 # include <X11/Xaw/List.h>
91 # include <X11/Xaw/Command.h>
92 # include <X11/Xaw/Toggle.h>
93 # include <X11/Xaw/Viewport.h>
94 # include <X11/Xaw/Dialog.h>
95 # include <X11/Xaw/Scrollbar.h>
96 # include <X11/Xaw/Text.h>
98 #endif /* HAVE_ATHENA */
102 #include "resources.h" /* for parse_time() */
103 #include "visual.h" /* for has_writable_cells() */
104 #include "remote.h" /* for xscreensaver_command() */
111 #define WIDGET Widget
112 #define POINTER XtPointer
116 char *progclass = "XScreenSaver";
120 saver_preferences *a, *b;
124 char *blurb (void) { return progname; }
126 static void run_hack (Display *dpy, int n);
129 static saver_preferences *global_prefs_kludge = 0; /* I hate C so much... */
130 #endif /* HAVE_ATHENA */
132 static char *short_version = 0;
135 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
136 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
137 Atom XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
139 extern void create_demo_dialog (Widget, Visual *, Colormap);
140 extern void create_preferences_dialog (Widget, Visual *, Colormap);
142 extern WIDGET demo_dialog;
143 extern WIDGET label1;
144 extern WIDGET text_line;
145 extern WIDGET text_activate;
146 extern WIDGET demo_form;
147 extern WIDGET demo_list;
151 extern WIDGET restart;
154 extern WIDGET preferences_dialog;
155 extern WIDGET preferences_form;
156 extern WIDGET prefs_done;
157 extern WIDGET prefs_cancel;
158 extern WIDGET timeout_text;
159 extern WIDGET cycle_text;
160 extern WIDGET fade_text;
161 extern WIDGET fade_ticks_text;
162 extern WIDGET lock_timeout_text;
163 extern WIDGET passwd_timeout_text;
164 extern WIDGET verbose_toggle;
165 extern WIDGET install_cmap_toggle;
166 extern WIDGET fade_toggle;
167 extern WIDGET unfade_toggle;
168 extern WIDGET lock_toggle;
173 # define set_toggle_button_state(toggle,state) \
174 XmToggleButtonSetState ((toggle), (state), True)
175 # define set_text_string(text_widget,string) \
176 XmTextSetString ((text_widget), (string))
177 # define add_button_callback(button,cb,arg) \
178 XtAddCallback ((button), XmNactivateCallback, (cb), (arg))
179 # define add_toggle_callback(button,cb,arg) \
180 XtAddCallback ((button), XmNvalueChangedCallback, (cb), (arg))
181 # define add_text_callback add_toggle_callback
182 # define disable_widget(widget) \
183 XtVaSetValues((widget), XtNsensitive, False, 0)
184 # define widget_name(widget) XtName(widget)
185 # define widget_display(widget) XtDisplay(widget)
186 # define widget_screen(widget) XtScreen(widget)
187 # define CB_ARGS(a,b,c) (a,b,c)
189 #elif defined(HAVE_ATHENA)
191 # define set_toggle_button_state(toggle,state) \
192 XtVaSetValues((toggle), XtNstate, (state), 0)
193 # define set_text_string(text_widget,string) \
194 XtVaSetValues ((text_widget), XtNvalue, (string), 0)
195 # define add_button_callback(button,cb,arg) \
196 XtAddCallback ((button), XtNcallback, (cb), (arg))
197 # define add_toggle_callback add_button_callback
198 # define add_text_callback(b,c,a) ERROR!
199 # define disable_widget(widget) \
200 XtVaSetValues((widget), XtNsensitive, False, 0)
201 # define widget_name(widget) XtName(widget)
202 # define widget_display(widget) XtDisplay(widget)
203 # define widget_screen(widget) XtScreen(widget)
204 # define CB_ARGS(a,b,c) (a,b,c)
206 #endif /* HAVE_ATHENA */
212 get_text_string (WIDGET text_widget)
215 return XmTextGetString (text_widget);
216 #elif defined(HAVE_ATHENA)
218 if (XtIsSubclass(text_widget, textWidgetClass))
219 XtVaGetValues (text_widget, XtNstring, &string, 0);
220 else if (XtIsSubclass(text_widget, dialogWidgetClass))
221 XtVaGetValues (text_widget, XtNvalue, &string, 0);
226 #endif /* HAVE_ATHENA */
231 get_label_string (WIDGET label_widget)
235 XmString xm_label = 0;
236 XtVaGetValues (label_widget, XmNlabelString, &xm_label, 0);
239 XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
241 #elif defined(HAVE_ATHENA)
243 XtVaGetValues (label_widget, XtNlabel, &label, 0);
244 return (label ? strdup(label) : 0);
245 #endif /* HAVE_ATHENA */
250 set_label_string (WIDGET label_widget, char *string)
253 XmString xm_string = XmStringCreate (string, XmSTRING_DEFAULT_CHARSET);
254 XtVaSetValues (label_widget, XmNlabelString, xm_string, 0);
255 XmStringFree (xm_string);
256 #elif defined(HAVE_ATHENA)
257 XtVaSetValues (label_widget, XtNlabel, string, 0);
258 #endif /* HAVE_ATHENA */
262 /* Given a label widget that has a %s in it, do the printf thing.
263 If the label's string is obviously wrong, complain about resource lossage.
266 format_into_label (WIDGET label, const char *arg)
268 char *text = get_label_string (label);
269 char *buf = (char *) malloc ((text ? strlen(text) : 0) + strlen(arg) + 100);
271 if (!text || !*text || !strcmp (text, widget_name (label)))
272 strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
274 sprintf (buf, text, arg);
276 set_label_string (label, buf);
282 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
285 ensure_selected_item_visible (WIDGET list)
290 if (XmListGetSelectedPos (list, &pos_list, &pos_count) && pos_count > 0)
295 XmNtopItemPosition, &top,
296 XmNvisibleItemCount, &visible,
298 if (pos_list[0] >= top + visible)
300 int pos = pos_list[0] - visible + 1;
301 if (pos < 0) pos = 0;
302 XmListSetPos (list, pos);
304 else if (pos_list[0] < top)
306 XmListSetPos (list, pos_list[0]);
310 XtFree ((char *) pos_list);
312 #elif defined(HAVE_ATHENA)
313 # ifdef HAVE_XawViewportSetCoordinates
315 int margin = 16; /* should be line height or something. */
318 Dimension list_h = 0, vp_h = 0;
319 Dimension top_margin = 4; /* I don't know where this value comes from */
320 Position vp_x = 0, vp_y = 0, current_y;
322 Widget viewport = XtParent(demo_list);
323 Widget sb = (viewport ? XtNameToWidget(viewport, "*vertical") : 0);
324 float sb_top = 0, sb_size = 0;
325 XawListReturnStruct *current = XawListShowCurrent(demo_list);
326 if (!current || !sb) return;
328 XtVaGetValues(demo_list,
329 XtNnumberStrings, &count,
332 if (count < 2 || list_h < 10) return;
334 XtVaGetValues(viewport, XtNheight, &vp_h, XtNx, &vp_x, XtNy, &vp_y, 0);
335 if (vp_h < 10) return;
337 XtVaGetValues(sb, XtNtopOfThumb, &sb_top, XtNshown, &sb_size, 0);
338 if (sb_size <= 0) return;
340 pos = current->list_index;
341 cratio = ((double) pos) / ((double) count);
342 current_y = (cratio * list_h);
344 if (cratio < sb_top ||
345 cratio > sb_top + sb_size)
348 current_y -= (vp_h - margin - margin);
352 if ((long)current_y >= (long) list_h)
353 current_y = (Position) ((long)list_h - (long)vp_h);
355 if ((long)current_y < (long)top_margin)
356 current_y = (Position)top_margin;
358 XawViewportSetCoordinates (viewport, vp_x, current_y);
360 # endif /* HAVE_XawViewportSetCoordinates */
361 #endif /* HAVE_ATHENA */
367 set_hack_list (Widget demo_list, saver_preferences *p)
369 char **strings = (char **) calloc (sizeof (char *), p->screenhacks_count);
371 for (i = 0; i < p->screenhacks_count; i++)
372 strings[i] = format_hack (p->screenhacks[i], False);
373 XtVaSetValues (demo_list,
375 XtNnumberStrings, p->screenhacks_count,
378 for (i = 0; i < p->screenhacks_count; i++)
381 strings[i] = (char *) 0xDEADBEEF;
386 #endif /* HAVE_ATHENA */
390 /* Callback for the text area:
391 - note the text the user has entered;
392 - change the corresponding element in `screenhacks';
393 - write the .xscreensaver file;
394 - tell the xscreensaver daemon to run that hack.
397 text_cb (WIDGET text_widget, POINTER client_data, POINTER call_data)
399 saver_preferences *p = (saver_preferences *) client_data;
400 char *new_text = get_text_string (text_widget);
401 Display *dpy = widget_display (text_widget);
404 int hack_number = -1; /* 0-based */
407 XawListReturnStruct *current = XawListShowCurrent(demo_list);
408 hack_number = current->list_index;
409 #elif defined(HAVE_MOTIF)
412 if (XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
413 hack_number = pos_list[0] - 1;
415 XtFree ((char *) pos_list);
416 #endif /* HAVE_ATHENA */
418 ensure_selected_item_visible (demo_list);
420 if (hack_number < 0 || hack_number >= p->screenhacks_count)
422 set_text_string (text_widget, "");
423 XBell (XtDisplay (text_widget), 0);
427 screenhack *new_hack = parse_screenhack (new_text);
428 if (p->screenhacks [hack_number])
429 free_screenhack (p->screenhacks [hack_number]);
430 p->screenhacks [hack_number] = new_hack;
434 XmListDeselectAllItems (demo_list);
436 XmString xmstr = XmStringCreate (new_text, XmSTRING_DEFAULT_CHARSET);
437 XmListReplaceItemsPos (demo_list, &xmstr, 1, hack_number+1);
438 XmStringFree (xmstr);
440 XmListSelectPos (demo_list, hack_number+1, True);
442 #elif defined(HAVE_ATHENA)
445 Widget vp = XtParent(demo_list);
446 Widget sb = (vp ? XtNameToWidget(vp, "*vertical") : 0);
447 Dimension list_h = 0;
448 Position vp_x = 0, vp_y = 0;
451 XawListUnhighlight (demo_list);
452 XtVaGetValues (vp, XtNx, &vp_x, 0);
453 XtVaGetValues (sb, XtNtopOfThumb, &sb_top, 0);
454 XtVaGetValues (demo_list, XtNheight, &list_h, 0);
455 vp_y = (sb_top * list_h);
456 set_hack_list (demo_list, p);
457 XawViewportSetCoordinates (vp, vp_x, vp_y);
458 XawListHighlight (demo_list, hack_number);
461 #endif /* HAVE_ATHENA */
464 write_init_file (p, short_version);
467 usleep (500000); /* give the disk time to settle down */
469 run_hack (dpy, hack_number+1);
475 /* Bend over backwards to make hitting Return in the text field do the
478 static void text_enter (Widget w, XEvent *event, String *av, Cardinal *ac)
480 text_cb (w, global_prefs_kludge, 0); /* I hate C so much... */
483 static XtActionsRec actions[] = {{"done", text_enter}
485 static char translations[] = ("<Key>Return: done()\n"
486 "<Key>Linefeed: done()\n"
487 "Ctrl<Key>M: done()\n"
488 "Ctrl<Key>J: done()\n");
489 #endif /* HAVE_ATHENA */
492 /* Callback for the Run Next button.
495 next_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
498 XawListReturnStruct *current = XawListShowCurrent(demo_list);
500 XtVaGetValues (demo_list, XtNnumberStrings, &cnt, 0);
501 if (current->list_index == XAW_LIST_NONE ||
502 current->list_index + 1 >= cnt)
503 current->list_index = 0;
505 current->list_index++;
506 XawListHighlight(demo_list, current->list_index);
508 ensure_selected_item_visible (demo_list);
509 current = XawListShowCurrent(demo_list);
510 XtVaSetValues(text_line, XtNstring, current->string, 0);
512 run_hack (XtDisplay (button), current->list_index + 1);
514 #elif defined(HAVE_MOTIF)
516 saver_preferences *p = (saver_preferences *) client_data;
520 if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
523 XmListDeselectAllItems (demo_list); /* LessTif lossage */
524 XmListSelectPos (demo_list, pos, True);
528 pos = pos_list[0] + 1;
529 if (pos > p->screenhacks_count)
531 XmListDeselectAllItems (demo_list); /* LessTif lossage */
532 XmListSelectPos (demo_list, pos, True);
535 ensure_selected_item_visible (demo_list);
536 run_hack (XtDisplay (button), pos);
538 XtFree ((char *) pos_list);
540 #endif /* HAVE_MOTIF */
544 /* Callback for the Run Previous button.
547 prev_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
550 XawListReturnStruct *current = XawListShowCurrent(demo_list);
552 XtVaGetValues (demo_list, XtNnumberStrings, &cnt, 0);
553 if (current->list_index == XAW_LIST_NONE ||
554 current->list_index <= 0)
555 current->list_index = cnt-1;
557 current->list_index--;
558 XawListHighlight(demo_list, current->list_index);
560 ensure_selected_item_visible (demo_list);
561 current = XawListShowCurrent(demo_list);
562 XtVaSetValues(text_line, XtNstring, current->string, 0);
564 run_hack (XtDisplay (button), current->list_index + 1);
566 #elif defined(HAVE_MOTIF)
568 saver_preferences *p = (saver_preferences *) client_data;
572 if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
574 pos = p->screenhacks_count;
575 XmListDeselectAllItems (demo_list); /* LessTif lossage */
576 XmListSelectPos (demo_list, pos, True);
580 pos = pos_list[0] - 1;
582 pos = p->screenhacks_count;
583 XmListDeselectAllItems (demo_list); /* LessTif lossage */
584 XmListSelectPos (demo_list, pos, True);
587 ensure_selected_item_visible (demo_list);
588 run_hack (XtDisplay (button), pos);
590 XtFree ((char *) pos_list);
592 #endif /* HAVE_MOTIF */
596 /* Callback run when a list element is double-clicked.
599 select_cb (WIDGET button, POINTER client_data, POINTER call_data)
601 /* saver_preferences *p = (saver_preferences *) client_data; */
604 XawListReturnStruct *item = (XawListReturnStruct*)call_data;
605 XtVaSetValues(text_line, XtNstring, item->string, 0);
606 run_hack (XtDisplay (button), item->list_index + 1);
608 #elif defined(HAVE_MOTIF)
609 XmListCallbackStruct *lcb = (XmListCallbackStruct *) call_data;
612 XmStringGetLtoR (lcb->item, XmSTRING_DEFAULT_CHARSET, &string);
613 set_text_string (text_line, (string ? string : ""));
615 if (lcb->reason == XmCR_DEFAULT_ACTION && string)
616 run_hack (XtDisplay (button), lcb->item_position);
621 #endif /* HAVE_MOTIF */
625 static void pop_preferences_dialog (prefs_pair *pair);
626 static void make_preferences_dialog (prefs_pair *pair, Widget parent);
628 /* Callback for the Preferences button.
631 preferences_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
633 prefs_pair *pair = (prefs_pair *) client_data;
634 Widget parent = button;
637 parent = XtParent(parent);
638 } while (XtParent(parent));
640 if (! preferences_dialog)
641 make_preferences_dialog (pair, parent);
643 pop_preferences_dialog (pair);
647 /* Callback for the Quit button.
650 quit_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
652 /* Save here? Right now we don't need to, because we save every time
653 the text field is edited, or the Preferences OK button is pressed.
659 /* Callback for the (now unused) Restart button.
662 restart_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
664 xscreensaver_command (widget_display (button), XA_RESTART, 0, False);
668 /* Finds the number of the last hack to run, and makes that item be
672 scroll_to_current_hack (WIDGET dialog)
676 unsigned long nitems, bytesafter;
678 Display *dpy = widget_display (dialog);
681 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
682 XA_SCREENSAVER_STATUS,
683 0, 3, False, XA_INTEGER,
684 &type, &format, &nitems, &bytesafter,
685 (unsigned char **) &data)
687 && type == XA_INTEGER
690 hack = (int) data[2];
692 if (data) free (data);
698 XmListDeselectAllItems (demo_list); /* LessTif lossage */
699 XmListSelectPos (demo_list, hack, False);
700 ensure_selected_item_visible (demo_list);
702 #elif defined(HAVE_ATHENA)
703 XawListUnhighlight (demo_list);
704 XawListHighlight (demo_list, hack - 1);
706 #endif /* HAVE_ATHENA */
711 pop_up_dialog_box (WIDGET dialog, WIDGET form)
714 XtRealizeWidget (dialog);
715 XtPopup (dialog, XtGrabNone);
716 #elif defined(HAVE_MOTIF)
717 XtRealizeWidget (form);
718 XtManageChild (form);
720 /* Motif likes to make the dialog wider than the screen; throttle it. */
722 Dimension w=0, h=0, bw=0;
725 XtVaGetValues (dialog, XtNscreen, &screen, 0);
726 max_w = WidthOfScreen (screen) * 0.8;
727 XtVaGetValues(dialog, XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
729 XtResizeWidget(dialog, max_w, h, bw);
731 #endif /* HAVE_MOTIF */
733 XMapRaised (XtDisplay (dialog), XtWindow (dialog));
738 make_demo_dialog (Widget toplevel_shell, prefs_pair *pair)
740 saver_preferences *p = pair->a;
741 /* saver_preferences *p2 = pair->b; */
742 Widget parent = toplevel_shell;
744 screenhack **hacks = p->screenhacks;
745 #endif /* HAVE_MOTIF */
747 create_demo_dialog (parent,
748 DefaultVisualOfScreen (widget_screen (parent)),
749 DefaultColormapOfScreen (widget_screen (parent)));
751 format_into_label (label1, short_version);
752 add_button_callback (next, next_cb, (POINTER) p);
753 add_button_callback (prev, prev_cb, (POINTER) p);
754 add_button_callback (done, quit_cb, (POINTER) p);
756 add_button_callback(restart,restart_cb, (POINTER) p);
757 add_button_callback (edit, preferences_cb, (POINTER) pair);
760 XtAddCallback (demo_list, XmNbrowseSelectionCallback,
761 select_cb, (POINTER) p);
762 XtAddCallback (demo_list, XmNdefaultActionCallback,
763 select_cb, (POINTER) p);
764 XtAddCallback (text_line, XmNactivateCallback, text_cb, (POINTER) p);
767 for (; *hacks; hacks++)
769 char *hs = format_hack (*hacks, False);
770 XmString xmstr = XmStringCreate (hs, XmSTRING_DEFAULT_CHARSET);
771 XmListAddItem (demo_list, xmstr, 0);
772 XmStringFree (xmstr);
776 #elif defined(HAVE_ATHENA)
778 /* Hook up the text line. */
780 XtAppAddActions(XtWidgetToApplicationContext(text_line),
781 actions, XtNumber(actions));
782 XtOverrideTranslations(text_line, XtParseTranslationTable(translations));
785 /* Must realize the widget before populating the list, or the dialog
786 will be as wide as the longest string.
788 XtRealizeWidget (demo_dialog);
790 set_hack_list (demo_list, p);
791 XtAddCallback (demo_list, XtNcallback, select_cb, p);
793 /* Now that we've populated the list, make sure that the list is as
794 wide as the dialog itself.
797 Widget viewport = XtParent(demo_list);
798 Widget subform = XtParent(viewport);
799 Widget box = XtNameToWidget(demo_dialog, "*box");
800 Widget label1 = XtNameToWidget(demo_dialog, "*label1");
801 Widget label2 = XtNameToWidget(demo_dialog, "*label2");
802 Dimension x=0, y=0, w=0, h=0, bw=0, w2=0;
803 XtVaGetValues(subform,
804 XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
805 XtVaGetValues(box, XtNwidth, &w2, 0);
807 XtResizeWidget(subform, w2, h, bw);
809 /* Why isn't the viewport getting centered? */
810 XtVaGetValues(viewport,
811 XtNx, &x, XtNy, &y, XtNheight, &h, XtNborderWidth, &bw, 0);
812 XtConfigureWidget(viewport, x, y, w2-x-x, h, bw);
814 /* And the text line, too. */
815 XtVaGetValues(text_line,
816 XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
817 XtVaGetValues(viewport, XtNwidth, &w2, 0);
819 XtResizeWidget(text_line, w2, h, bw);
821 /* And the labels too. */
822 XtVaGetValues(label1,
823 XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
825 XtResizeWidget(label1, w2, h, bw);
827 XtVaGetValues(label2,
828 XtNwidth, &w, XtNheight, &h, XtNborderWidth, &bw, 0);
830 XtResizeWidget(label2, w2, h, bw);
834 #endif /* HAVE_ATHENA */
836 scroll_to_current_hack (demo_dialog);
838 pop_up_dialog_box(demo_dialog, demo_form);
840 #if defined(HAVE_ATHENA)
841 /* For Athena and Gtk, have to do this after the dialog is managed. */
842 ensure_selected_item_visible (demo_list);
843 #endif /* HAVE_ATHENA */
847 /* the Preferences dialog
850 /* Helper for the text fields that contain time specifications:
851 this parses the text, and does error checking.
854 hack_time_text (Display *dpy, char *line, Time *store, Bool sec_p)
859 value = parse_time (line, sec_p, True);
860 value *= 1000; /* Time measures in microseconds */
869 /* Callback for text fields that hold a time that default to seconds,
870 when not fully spelled out. client_data is a Time* where the value goes.
873 prefs_sec_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
875 hack_time_text (widget_display (button), get_text_string (button),
876 (Time *) client_data, True);
880 /* Callback for text fields that hold a time that default to minutes,
881 when not fully spelled out. client_data is an Time* where the value goes.
884 prefs_min_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
886 hack_time_text (widget_display (button), get_text_string (button),
887 (Time *) client_data, False);
891 /* Callback for text fields that hold an integer value.
892 client_data is an int* where the value goes.
895 prefs_int_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
897 char *line = get_text_string (button);
898 int *store = (int *) client_data;
903 else if (sscanf (line, "%u%c", &value, &c) != 1)
904 XBell (XtDisplay (button), 0);
910 /* Callback for toggle buttons. client_data is a Bool* where the value goes.
913 prefs_bool_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER call_data)
915 Bool *store = (Bool *) client_data;
917 *store = ((XmToggleButtonCallbackStruct *) call_data)->set;
918 #elif defined(HAVE_ATHENA)
919 Boolean state = FALSE;
920 XtVaGetValues (button, XtNstate, &state, 0);
922 #endif /* HAVE_ATHENA */
926 /* Callback for the Cancel button on the Preferences dialog.
929 prefs_cancel_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER ignored)
931 XtDestroyWidget (preferences_dialog);
932 preferences_dialog = 0;
933 XMapRaised (XtDisplay (demo_dialog), XtWindow (demo_dialog));
937 /* Callback for the OK button on the Preferences dialog.
940 prefs_ok_cb CB_ARGS(WIDGET button, POINTER client_data, POINTER call_data)
942 prefs_pair *pair = (prefs_pair *) client_data;
943 saver_preferences *p = pair->a;
944 saver_preferences *p2 = pair->b;
946 prefs_cancel_cb CB_ARGS(button, client_data, call_data);
949 /* Athena doesn't let us put callbacks on these widgets, so run
950 all the callbacks by hand when OK is pressed. */
951 prefs_min_cb (timeout_text, (POINTER) &p2->timeout, 0);
952 prefs_min_cb (cycle_text, (POINTER) &p2->cycle, 0);
953 prefs_sec_cb (fade_text, (POINTER) &p2->fade_seconds, 0);
954 prefs_int_cb (fade_ticks_text, (POINTER) &p2->fade_ticks, 0);
955 prefs_min_cb (lock_timeout_text, (POINTER) &p2->lock_timeout, 0);
956 prefs_sec_cb (passwd_timeout_text, (POINTER) &p2->passwd_timeout, 0);
957 #endif /* HAVE_ATHENA */
959 p->timeout = p2->timeout;
960 p->cycle = p2->cycle;
961 p->lock_timeout = p2->lock_timeout;
962 p->passwd_timeout = p2->passwd_timeout;
963 p->fade_seconds = p2->fade_seconds;
964 p->fade_ticks = p2->fade_ticks;
965 p->verbose_p = p2->verbose_p;
966 p->install_cmap_p = p2->install_cmap_p;
967 p->fade_p = p2->fade_p;
968 p->unfade_p = p2->unfade_p;
969 p->lock_p = p2->lock_p;
971 write_init_file (p, short_version);
976 make_preferences_dialog (prefs_pair *pair, Widget parent)
978 saver_preferences *p = pair->a;
979 saver_preferences *p2 = pair->b;
981 Screen *screen = widget_screen (parent);
982 Display *dpy = widget_display (parent);
984 *p2 = *p; /* copy all slots of p into p2. */
986 create_preferences_dialog (parent,
987 DefaultVisualOfScreen (screen),
988 DefaultColormapOfScreen (screen));
990 add_button_callback (prefs_done, prefs_ok_cb, (POINTER) pair);
991 add_button_callback (prefs_cancel, prefs_cancel_cb, 0);
993 #define CB(widget,type,slot) \
994 add_text_callback ((widget), (type), (POINTER) (slot))
995 #define CBT(widget,type,slot) \
996 add_toggle_callback ((widget), (type), (POINTER) (slot))
999 /* When using Athena widgets, we can't set callbacks for these,
1000 so in that case, we run them by hand when "OK" is pressed. */
1001 CB (timeout_text, prefs_min_cb, &p2->timeout);
1002 CB (cycle_text, prefs_min_cb, &p2->cycle);
1003 CB (fade_text, prefs_sec_cb, &p2->fade_seconds);
1004 CB (fade_ticks_text, prefs_int_cb, &p2->fade_ticks);
1005 CB (lock_timeout_text, prefs_min_cb, &p2->lock_timeout);
1006 CB (passwd_timeout_text, prefs_sec_cb, &p2->passwd_timeout);
1008 #endif /* !HAVE_ATHENA */
1010 CBT (verbose_toggle, prefs_bool_cb, &p2->verbose_p);
1011 CBT (install_cmap_toggle, prefs_bool_cb, &p2->install_cmap_p);
1012 CBT (fade_toggle, prefs_bool_cb, &p2->fade_p);
1013 CBT (unfade_toggle, prefs_bool_cb, &p2->unfade_p);
1014 CBT (lock_toggle, prefs_bool_cb, &p2->lock_p);
1019 Bool found_any_writable_cells = False;
1020 int nscreens = ScreenCount(dpy);
1022 for (i = 0; i < nscreens; i++)
1024 Screen *s = ScreenOfDisplay (dpy, i);
1025 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1027 found_any_writable_cells = True;
1032 if (! found_any_writable_cells) /* fading isn't possible */
1034 disable_widget (fade_text);
1035 disable_widget (fade_ticks_text);
1036 disable_widget (install_cmap_toggle);
1037 disable_widget (fade_toggle);
1038 disable_widget (unfade_toggle);
1044 /* Formats a `Time' into "H:MM:SS". (Time is microseconds.)
1047 format_time (char *buf, Time time)
1049 int s = time / 1000;
1050 unsigned int h = 0, m = 0;
1061 sprintf (buf, "%u:%02u:%02u", h, m, s);
1066 pop_preferences_dialog (prefs_pair *pair)
1068 /* saver_preferences *p = pair->a; */
1069 saver_preferences *p2 = pair->b;
1072 format_time (s, p2->timeout); set_text_string(timeout_text, s);
1073 format_time (s, p2->cycle); set_text_string(cycle_text, s);
1074 format_time (s, p2->lock_timeout); set_text_string(lock_timeout_text, s);
1075 format_time (s, p2->passwd_timeout); set_text_string(passwd_timeout_text, s);
1076 format_time (s, p2->fade_seconds); set_text_string(fade_text, s);
1077 sprintf (s, "%u", p2->fade_ticks); set_text_string(fade_ticks_text, s);
1079 set_toggle_button_state (verbose_toggle, p2->verbose_p);
1080 set_toggle_button_state (install_cmap_toggle, p2->install_cmap_p);
1081 set_toggle_button_state (fade_toggle, p2->fade_p);
1082 set_toggle_button_state (unfade_toggle, p2->unfade_p);
1083 set_toggle_button_state (lock_toggle, p2->lock_p);
1085 pop_up_dialog_box (preferences_dialog, preferences_form);
1090 run_hack (Display *dpy, int n)
1092 if (n <= 0) abort();
1093 xscreensaver_command (dpy, XA_DEMO, n, False);
1098 warning_dialog_dismiss_cb CB_ARGS(WIDGET button, POINTER client_data,
1101 WIDGET shell = (WIDGET) client_data;
1102 XtDestroyWidget (shell);
1107 warning_dialog (WIDGET parent, const char *message)
1109 char *msg = strdup (message);
1126 dialog = XmCreateWarningDialog (parent, "versionWarning", av, ac);
1128 w = XmMessageBoxGetChild (dialog, XmDIALOG_MESSAGE_LABEL);
1129 if (w) XtUnmanageChild (w);
1130 w = XmMessageBoxGetChild (dialog, XmDIALOG_CANCEL_BUTTON);
1131 if (w) XtUnmanageChild (w);
1132 w = XmMessageBoxGetChild (dialog, XmDIALOG_HELP_BUTTON);
1133 if (w) XtUnmanageChild (w);
1135 ok = XmMessageBoxGetChild (dialog, XmDIALOG_OK_BUTTON);
1138 XtSetArg (av[ac], XmNnumColumns, 1); ac++;
1139 XtSetArg (av[ac], XmNorientation, XmVERTICAL); ac++;
1140 XtSetArg (av[ac], XmNpacking, XmPACK_COLUMN); ac++;
1141 XtSetArg (av[ac], XmNrowColumnType, XmWORK_AREA); ac++;
1142 XtSetArg (av[ac], XmNspacing, 0); ac++;
1143 container = XmCreateRowColumn (dialog, "container", av, ac);
1145 #elif defined(HAVE_ATHENA)
1148 dialog = XtVaCreatePopupShell("warning_dialog", transientShellWidgetClass,
1150 form = XtVaCreateManagedWidget("warning_form", formWidgetClass, dialog, 0);
1151 #endif /* HAVE_ATHENA */
1157 char *s = strchr (head, '\n');
1160 sprintf (name, "label%d", i++);
1163 xmstr = XmStringCreate (head, XmSTRING_DEFAULT_CHARSET);
1165 XtSetArg (av[ac], XmNlabelString, xmstr); ac++;
1166 label = XmCreateLabelGadget (container, name, av, ac);
1167 XtManageChild (label);
1168 XmStringFree (xmstr);
1169 #elif defined(HAVE_ATHENA)
1171 label = XtVaCreateManagedWidget (name, labelWidgetClass,
1173 XtNleft, XtChainLeft,
1174 XtNright, XtChainRight,
1176 (label ? XtNfromVert : XtNtop),
1177 (label ? label : XtChainTop),
1180 #endif /* HAVE_ATHENA */
1190 XtManageChild (container);
1191 XtRealizeWidget (dialog);
1192 XtManageChild (dialog);
1194 #elif defined(HAVE_ATHENA)
1196 ok = XtVaCreateManagedWidget ("ok", commandWidgetClass, form,
1197 XtNleft, XtChainLeft,
1198 XtNbottom, XtChainBottom,
1202 XtRealizeWidget (dialog);
1203 XtPopup (dialog, XtGrabNone);
1204 #endif /* HAVE_ATHENA */
1206 add_button_callback (ok, warning_dialog_dismiss_cb, (POINTER) dialog);
1213 /* The main demo-mode command loop.
1218 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1219 XrmRepresentation *type, XrmValue *value, XPointer closure)
1222 for (i = 0; quarks[i]; i++)
1224 if (bindings[i] == XrmBindTightly)
1225 fprintf (stderr, (i == 0 ? "" : "."));
1226 else if (bindings[i] == XrmBindLoosely)
1227 fprintf (stderr, "*");
1229 fprintf (stderr, " ??? ");
1230 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1233 fprintf (stderr, ": %s\n", (char *) value->addr);
1241 the_network_is_not_the_computer (WIDGET parent)
1243 Display *dpy = widget_display (parent);
1244 char *rversion, *ruser, *rhost;
1245 char *luser, *lhost;
1247 struct passwd *p = getpwuid (getuid ());
1248 const char *d = DisplayString (dpy);
1250 # if defined(HAVE_UNAME)
1252 if (uname (&uts) < 0)
1253 lhost = "<UNKNOWN>";
1255 lhost = uts.nodename;
1257 strcpy (lhost, getenv("SYS$NODE"));
1258 # else /* !HAVE_UNAME && !VMS */
1259 strcat (lhost, "<UNKNOWN>");
1260 # endif /* !HAVE_UNAME && !VMS */
1262 if (p && p->pw_name)
1267 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
1269 /* Make a buffer that's big enough for a number of copies of all the
1270 strings, plus some. */
1271 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
1272 (ruser ? strlen(ruser) : 0) +
1273 (rhost ? strlen(rhost) : 0) +
1280 if (!rversion || !*rversion)
1284 "xscreensaver doesn't seem to be running on display \"%s\".",
1287 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
1289 /* Warn that the two processes are running as different users.
1293 "%s is running as user \"%s\" on host \"%s\".\n"
1294 "But the xscreensaver managing display \"%s\"\n"
1295 "is running as user \"%s\" on host \"%s\".\n"
1297 "Since they are different users, they won't be reading/writing\n"
1298 "the same ~/.xscreensaver file, so %s isn't\n"
1299 "going to work right.\n"
1301 "Either re-run %s as \"%s\", or re-run\n"
1302 "xscreensaver as \"%s\".\n",
1303 progname, luser, lhost,
1305 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1307 progname, (ruser ? ruser : "???"),
1310 else if (rhost && *rhost && !!strcmp (rhost, lhost))
1312 /* Warn that the two processes are running on different hosts.
1316 "%s is running as user \"%s\" on host \"%s\".\n"
1317 "But the xscreensaver managing display \"%s\"\n"
1318 "is running as user \"%s\" on host \"%s\".\n"
1320 "If those two machines don't share a file system (that is,\n"
1321 "if they don't see the same ~%s/.xscreensaver file) then\n"
1322 "%s won't work right.",
1323 progname, luser, lhost,
1325 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1329 else if (!!strcmp (rversion, short_version))
1331 /* Warn that the version numbers don't match.
1335 "This is %s version %s.\n"
1336 "But the xscreensaver managing display \"%s\"\n"
1337 "is version %s. This could cause problems.",
1338 progname, short_version,
1345 warning_dialog (parent, msg);
1351 /* We use this error handler so that X errors are preceeded by the name
1352 of the program that generated them.
1355 demo_ehandler (Display *dpy, XErrorEvent *error)
1357 fprintf (stderr, "\nX error in %s:\n", progname);
1358 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
1361 fprintf (stderr, " (nonfatal.)\n");
1366 static char *defaults[] = {
1367 #include "XScreenSaver_ad.h"
1372 main (int argc, char **argv)
1375 prefs_pair Pair, *pair;
1376 saver_preferences P, P2, *p, *p2;
1380 Widget toplevel_shell;
1381 char *real_progname = argv[0];
1384 s = strrchr (real_progname, '/');
1385 if (s) real_progname = s+1;
1392 memset (p, 0, sizeof (*p));
1393 memset (p2, 0, sizeof (*p2));
1395 progname = real_progname;
1397 /* We must read exactly the same resources as xscreensaver.
1398 That means we must have both the same progclass *and* progname,
1399 at least as far as the resource database is concerned. So,
1400 put "xscreensaver" in argv[0] while initializing Xt.
1402 argv[0] = "xscreensaver";
1406 toplevel_shell = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1409 dpy = XtDisplay (toplevel_shell);
1410 db = XtDatabase (dpy);
1411 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
1412 XSetErrorHandler (demo_ehandler);
1414 /* Complain about unrecognized command-line arguments.
1416 for (i = 1; i < argc; i++)
1419 if (s[0] == '-' && s[1] == '-')
1421 if (!strcmp (s, "-prefs"))
1425 fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n",
1431 short_version = (char *) malloc (5);
1432 memcpy (short_version, screensaver_id + 17, 4);
1433 short_version [4] = 0;
1435 /* Load the init file, which may end up consulting the X resource database
1436 and the site-wide app-defaults file. Note that at this point, it's
1437 important that `progname' be "xscreensaver", rather than whatever
1444 /* Now that Xt has been initialized, and the resources have been read,
1445 we can set our `progname' variable to something more in line with
1448 progname = real_progname;
1452 global_prefs_kludge = p; /* I hate C so much... */
1453 #endif /* HAVE_ATHENA */
1457 XrmName name = { 0 };
1458 XrmClass class = { 0 };
1460 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1466 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
1467 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
1468 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
1469 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
1470 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
1471 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
1472 XA_SELECT = XInternAtom (dpy, "SELECT", False);
1473 XA_DEMO = XInternAtom (dpy, "DEMO", False);
1474 XA_BLANK = XInternAtom (dpy, "BLANK", False);
1475 XA_LOCK = XInternAtom (dpy, "LOCK", False);
1476 XA_EXIT = XInternAtom (dpy, "EXIT", False);
1477 XA_RESTART = XInternAtom (dpy, "RESTART", False);
1479 make_demo_dialog (toplevel_shell, pair);
1483 make_preferences_dialog (pair, toplevel_shell);
1484 pop_preferences_dialog (pair);
1487 the_network_is_not_the_computer (preferences_dialog
1488 ? preferences_dialog
1491 XtAppMainLoop (app);