ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-1.27.tar.Z
[xscreensaver] / driver / demo.c
1 /* xscreensaver, Copyright (c) 1993-1996 Jamie Zawinski <jwz@netscape.com>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 #include <X11/Intrinsic.h>
13
14 #if !__STDC__
15 # define _NO_PROTO
16 #endif
17
18 #include <Xm/Xm.h>
19 #include <Xm/Text.h>
20 #include <Xm/List.h>
21 #include <Xm/ToggleB.h>
22
23 #include "xscreensaver.h"
24 #include <stdio.h>
25
26 #ifdef HAVE_MIT_SAVER_EXTENSION
27 extern int mit_saver_ext_event_number;
28 extern Window server_mit_saver_window;
29 #endif /* HAVE_MIT_SAVER_EXTENSION */
30
31 #ifdef HAVE_SGI_SAVER_EXTENSION
32 /* extern int sgi_saver_ext_event_number; */
33 #endif /* HAVE_SGI_SAVER_EXTENSION */
34
35 extern Bool use_mit_saver_extension;
36 extern Bool use_sgi_saver_extension;
37
38 extern Time timeout, cycle, lock_timeout;
39 #ifndef NO_LOCKING
40 extern Time passwd_timeout;
41 #endif
42 extern int fade_seconds, fade_ticks;
43 extern Bool verbose_p, install_cmap_p, fade_p, unfade_p;
44 extern Bool lock_p, locking_disabled_p;
45
46 static void demo_mode_hack P((char *));
47 static void demo_mode_done P((void));
48
49 static void focus_fuckus P((Widget dialog));
50 static void text_cb P((Widget button, XtPointer, XtPointer));
51
52 extern void demo_mode_restart_process ();
53
54 extern Widget demo_dialog;
55 extern Widget label1;
56 extern Widget text_line;
57 extern Widget demo_form;
58 extern Widget demo_list;
59 extern Widget next, prev, done, restart, edit;
60
61 extern Widget resources_dialog;
62 extern Widget resources_form;
63 extern Widget res_done, res_cancel;
64 extern Widget timeout_text, cycle_text, fade_text, ticks_text;
65 extern Widget lock_time_text, passwd_time_text;
66 extern Widget verbose_toggle, cmap_toggle, fade_toggle, unfade_toggle,
67   lock_toggle;
68
69 extern create_demo_dialog ();
70 extern create_resources_dialog ();
71
72 static void
73 focus_fuckus (dialog)
74      Widget dialog;
75 {
76   XSetInputFocus (XtDisplay (dialog), XtWindow (dialog),
77                   RevertToParent, CurrentTime);
78 }
79
80 static void
81 raise_screenhack_dialog ()
82 {
83   XMapRaised (XtDisplay (demo_dialog), XtWindow (demo_dialog));
84   if (resources_dialog)
85     XMapRaised (XtDisplay (resources_dialog), XtWindow (resources_dialog));
86   focus_fuckus (resources_dialog ? resources_dialog : demo_dialog);
87 }
88
89 static void
90 destroy_screenhack_dialogs ()
91 {
92   if (demo_dialog) XtDestroyWidget (demo_dialog);
93   if (resources_dialog) XtDestroyWidget (resources_dialog);
94   demo_dialog = resources_dialog = 0;
95 }
96
97 static void
98 text_cb (button, client_data, call_data)
99      Widget button;
100      XtPointer client_data, call_data;
101 {
102   char *line = XmTextGetString (button);
103   demo_mode_hack (line);
104 }
105
106
107 static void
108 select_cb (button, client_data, call_data)
109      Widget button;
110      XtPointer client_data, call_data;
111 {
112   char **hacks = (char **) client_data;
113   XmListCallbackStruct *lcb = (XmListCallbackStruct *) call_data;
114   XmTextSetString (text_line, hacks [lcb->item_position - 1]);
115   if (lcb->reason == XmCR_DEFAULT_ACTION)
116     text_cb (text_line, 0, 0);
117   focus_fuckus (demo_dialog);
118 }
119
120 static void
121 ensure_selected_item_visible (list)
122      Widget list;
123 {
124   int *pos_list = 0;
125   int pos_count = 0;
126   if (XmListGetSelectedPos (list, &pos_list, &pos_count) && pos_count > 0)
127     {
128       int top = -2;
129       int visible = 0;
130       XtVaGetValues (list,
131                      XmNtopItemPosition, &top,
132                      XmNvisibleItemCount, &visible,
133                      0);
134       if (pos_list[0] >= top + visible)
135         {
136           int pos = pos_list[0] - visible + 1;
137           if (pos < 0) pos = 0;
138           XmListSetPos (list, pos);
139         }
140       else if (pos_list[0] < top)
141         {
142           XmListSetPos (list, pos_list[0]);
143         }
144     }
145   if (pos_list)
146     XtFree ((char *) pos_list);
147 }
148
149 static void
150 next_cb (button, client_data, call_data)
151      Widget button;
152      XtPointer client_data, call_data;
153 {
154   int *pos_list;
155   int pos_count;
156   if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
157     XmListSelectPos (demo_list, 1, True);
158   else
159     {
160       int pos = pos_list [0];
161       XmListSelectPos (demo_list, pos + 1, True);
162       XtFree ((char *) pos_list);
163       if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
164         abort ();
165       if (pos_list [0] == pos)
166         XmListSelectPos (demo_list, 1, True);
167       XtFree ((char *) pos_list);
168     }
169   ensure_selected_item_visible (demo_list);
170   text_cb (text_line, 0, 0);
171 }
172
173 static void
174 prev_cb (button, client_data, call_data)
175      Widget button;
176      XtPointer client_data, call_data;
177 {
178   int *pos_list;
179   int pos_count;
180   if (! XmListGetSelectedPos (demo_list, &pos_list, &pos_count))
181     XmListSelectPos (demo_list, 0, True);
182   else
183     {
184       XmListSelectPos (demo_list, pos_list [0] - 1, True);
185       XtFree ((char *) pos_list);
186     }
187   ensure_selected_item_visible (demo_list);
188   text_cb (text_line, 0, 0);
189 }
190
191
192 static void pop_resources_dialog ();
193 static void make_resources_dialog ();
194
195 static void
196 edit_cb (button, client_data, call_data)
197      Widget button;
198      XtPointer client_data, call_data;
199 {
200   Widget parent = (Widget) client_data;
201   if (! resources_dialog)
202     make_resources_dialog (parent);
203   pop_resources_dialog ();
204 }
205
206 static void
207 done_cb (button, client_data, call_data)
208      Widget button;
209      XtPointer client_data, call_data;
210 {
211   demo_mode_done ();
212 }
213
214
215 static void
216 restart_cb (button, client_data, call_data)
217      Widget button;
218      XtPointer client_data, call_data;
219 {
220   demo_mode_restart_process ();
221 }
222
223 void
224 pop_up_dialog_box (dialog, form, where)
225      Widget dialog, form;
226      int where;
227 {
228   /* I'm sure this is the wrong way to pop up a dialog box, but I can't
229      figure out how else to do it.
230
231      It's important that the screensaver dialogs not get decorated or
232      otherwise reparented by the window manager, because they need to be
233      children of the *real* root window, not the WM's virtual root, in
234      order for us to guarentee that they are visible above the screensaver
235      window itself.
236    */
237   Arg av [100];
238   int ac = 0;
239   Dimension sw, sh, x, y, w, h;
240   XtRealizeWidget (form);
241   sw = WidthOfScreen (XtScreen (dialog));
242   sh = HeightOfScreen (XtScreen (dialog));
243   ac = 0;
244   XtSetArg (av [ac], XmNwidth, &w); ac++;
245   XtSetArg (av [ac], XmNheight, &h); ac++;
246   XtGetValues (form, av, ac);
247   switch (where)
248     {
249     case 0:     /* center it in the top-right quadrant */
250       x = (sw/2 + w) / 2 + (sw/2) - w;
251       y = (sh/2 + h) / 2 - h;
252       break;
253     case 1:     /* center it in the bottom-right quadrant */
254       x = (sw/2 + w) / 2 + (sw/2) - w;
255       y = (sh/2 + h) / 2 + (sh/2) - h;
256       break;
257     case 2:     /* center it on the screen */
258       x = (sw + w) / 2 - w;
259       y = (sh + h) / 2 - h;
260       break;
261     default:
262       abort ();
263     }
264   if (x + w > sw) x = sw - w;
265   if (y + h > sh) y = sh - h;
266   ac = 0;
267   XtSetArg (av [ac], XmNx, x); ac++;
268   XtSetArg (av [ac], XmNy, y); ac++;
269   XtSetArg (av [ac], XtNoverrideRedirect, True); ac++;
270   XtSetArg (av [ac], XmNdefaultPosition, False); ac++;
271   /* I wonder whether this does anything useful? */
272   /*  XtSetArg (av [ac], XmNdialogStyle, XmDIALOG_SYSTEM_MODAL); ac++; */
273   XtSetValues (dialog, av, ac);
274   XtSetValues (form, av, ac);
275   XtManageChild (form);
276
277   focus_fuckus (dialog);
278 }
279
280
281 static void
282 make_screenhack_dialog (parent, hacks)
283      Widget parent;
284      char **hacks;
285 {
286   char buf [255];
287   Arg av[10];
288   int ac;
289   char *label;
290   XmString xm_label = 0;
291   XmString new_xm_label;
292
293   create_demo_dialog (parent);
294   ac = 0;
295   XtSetArg (av [ac], XmNlabelString, &xm_label); ac++;
296   XtGetValues (label1, av, ac);
297   XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
298   if (!strcmp (label, XtName (label1)))
299     strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
300   else
301     sprintf (buf, label, screensaver_version);
302   new_xm_label = XmStringCreate (buf, XmSTRING_DEFAULT_CHARSET);
303   ac = 0;
304   XtSetArg (av [ac], XmNlabelString, new_xm_label); ac++;
305   XtSetValues (label1, av, ac);
306   XmStringFree (new_xm_label);
307   XtFree (label);
308
309   XtAddCallback (demo_list, XmNbrowseSelectionCallback, select_cb,
310                  (XtPointer) hacks);
311   XtAddCallback (demo_list, XmNdefaultActionCallback, select_cb,
312                  (XtPointer) hacks);
313
314   XtAddCallback (text_line, XmNactivateCallback, text_cb, 0);
315   XtAddCallback (next, XmNactivateCallback, next_cb, 0);
316   XtAddCallback (prev, XmNactivateCallback, prev_cb, 0);
317   XtAddCallback (done, XmNactivateCallback, done_cb, 0);
318   XtAddCallback (restart, XmNactivateCallback, restart_cb, 0);
319   XtAddCallback (edit, XmNactivateCallback, edit_cb, (XtPointer) parent);
320
321   for (; *hacks; hacks++)
322     {
323       XmString xmstr = XmStringCreate (*hacks, XmSTRING_DEFAULT_CHARSET);
324       XmListAddItem (demo_list, xmstr, 0);
325       /* XmListSelectPos (widget, i, False); */
326       XmStringFree (xmstr);
327     }
328
329 #if 0
330   /* Dialogs that have scroll-lists don't obey maxWidth!  Fuck!!  Hack it. */
331   ac = 0;
332   XtSetArg (av [ac], XmNmaxWidth, &max_w); ac++;
333   XtGetValues (demo_dialog, av, ac); /* great, this SEGVs */
334 #endif
335
336   pop_up_dialog_box (demo_dialog, demo_form, 0);
337 }
338
339 \f
340 /* the Screensaver Parameters dialog */
341
342 static struct resources {
343   int timeout, cycle, secs, ticks, lock_time, passwd_time;
344   int verb, cmap, fade, unfade, lock_p;
345 } res;
346
347
348 extern int parse_time ();
349
350 static void 
351 hack_time_cb (dpy, line, store, sec_p)
352      Display *dpy;
353      char *line;
354      int *store;
355      Bool sec_p;
356 {
357   if (*line)
358     {
359       int value;
360       value = parse_time (line, sec_p, True);
361       if (value < 0)
362         /*XBell (dpy, 0)*/;
363       else
364         *store = value;
365     }
366 }
367
368 static void
369 res_sec_cb (button, client_data, call_data)
370      Widget button;
371      XtPointer client_data, call_data;
372 {
373   hack_time_cb (XtDisplay (button), XmTextGetString (button),
374                 (int *) client_data, True);
375 }
376
377 static void
378 res_min_cb (button, client_data, call_data)
379      Widget button;
380      XtPointer client_data, call_data;
381 {
382   hack_time_cb (XtDisplay (button), XmTextGetString (button),
383                 (int *) client_data, False);
384 }
385
386 static void
387 res_int_cb (button, client_data, call_data)
388      Widget button;
389      XtPointer client_data, call_data;
390 {
391   char *line = XmTextGetString (button);
392   int *store = (int *) client_data;
393   unsigned int value;
394   char c;
395   if (! *line)
396     ;
397   else if (sscanf (line, "%u%c", &value, &c) != 1)
398     XBell (XtDisplay (button), 0);
399   else
400     *store = value;
401 }
402
403 static void
404 res_bool_cb (button, client_data, call_data)
405      Widget button;
406      XtPointer client_data, call_data;
407 {
408   int *store = (int *) client_data;
409   *store = ((XmToggleButtonCallbackStruct *) call_data)->set;
410 }
411
412 static void
413 res_cancel_cb (button, client_data, call_data)
414      Widget button;
415      XtPointer client_data, call_data;
416 {
417   XtDestroyWidget (resources_dialog);
418   resources_dialog = 0;
419   raise_screenhack_dialog ();
420 }
421
422
423 static void
424 res_done_cb (button, client_data, call_data)
425      Widget button;
426      XtPointer client_data, call_data;
427 {
428   res_cancel_cb (button, client_data, call_data);
429
430   /* Throttle the timeouts to minimum sane values. */
431   if (res.timeout < 5) res.timeout = 5;
432   if (res.cycle < 2) res.cycle = 2;
433   if (res.passwd_time < 10) res.passwd_time = 10;
434
435   timeout = res.timeout * 1000;
436   cycle = res.cycle * 1000;
437   lock_timeout = res.lock_time * 1000;
438 #ifndef NO_LOCKING
439   passwd_timeout = res.passwd_time * 1000;
440 #endif
441   fade_seconds = res.secs;
442   fade_ticks = res.ticks;
443   verbose_p = res.verb;
444   install_cmap_p = res.cmap;
445   fade_p = res.fade;
446   unfade_p = res.unfade;
447   lock_p = res.lock_p;
448
449 #if defined(HAVE_MIT_SAVER_EXTENSION) || defined(HAVE_SGI_SAVER_EXTENSION)
450   if (use_mit_saver_extension || use_sgi_saver_extension)
451     {
452       /* Need to set the server timeout to the new one the user has picked.
453        */
454       int server_timeout, server_interval, prefer_blank, allow_exp;
455       XGetScreenSaver (dpy, &server_timeout, &server_interval,
456                        &prefer_blank, &allow_exp);
457       if (server_timeout != (timeout / 1000))
458         {
459           server_timeout = (timeout / 1000);
460           if (verbose_p)
461             fprintf (stderr,
462                    "%s: configuring server for saver timeout of %d seconds.\n",
463                      progname, server_timeout);
464           /* Leave all other parameters the same. */
465           XSetScreenSaver (dpy, server_timeout, server_interval,
466                            prefer_blank, allow_exp);
467         }
468     }
469 #endif /* HAVE_MIT_SAVER_EXTENSION || HAVE_SGI_SAVER_EXTENSION */
470 }
471
472
473 static void
474 make_resources_dialog (parent)
475      Widget parent;
476 {
477   Arg av[10];
478   int ac;
479
480   create_resources_dialog (parent);
481
482   XtAddCallback (res_done, XmNactivateCallback, res_done_cb, 0);
483   XtAddCallback (res_cancel, XmNactivateCallback, res_cancel_cb, 0);
484
485 #define CB(widget,type,slot) \
486         XtAddCallback ((widget), XmNvalueChangedCallback, (type), \
487                        (XtPointer) (slot))
488   CB (timeout_text,     res_min_cb,  &res.timeout);
489   CB (cycle_text,       res_min_cb,  &res.cycle);
490   CB (fade_text,        res_sec_cb,  &res.secs);
491   CB (ticks_text,       res_int_cb,  &res.ticks);
492   CB (lock_time_text,   res_min_cb,  &res.lock_time);
493   CB (passwd_time_text, res_sec_cb,  &res.passwd_time);
494   CB (verbose_toggle,   res_bool_cb, &res.verb);
495   CB (cmap_toggle,      res_bool_cb, &res.cmap);
496   CB (fade_toggle,      res_bool_cb, &res.fade);
497   CB (unfade_toggle,    res_bool_cb, &res.unfade);
498   CB (lock_toggle,      res_bool_cb, &res.lock_p);
499 #undef CB
500   ac = 0;
501   XtSetArg (av[ac], XmNsensitive, False); ac++;
502
503   if (locking_disabled_p)
504     {
505       XtSetValues (passwd_time_text, av, ac);
506       XtSetValues (lock_time_text, av, ac);
507       XtSetValues (lock_toggle, av, ac);
508     }
509   if (CellsOfScreen (XtScreen (parent)) <= 2)
510     {
511       XtSetValues (fade_text, av, ac);
512       XtSetValues (ticks_text, av, ac);
513       XtSetValues (cmap_toggle, av, ac);
514       XtSetValues (fade_toggle, av, ac);
515       XtSetValues (unfade_toggle, av, ac);
516     }
517 }
518
519
520 static void
521 fmt_time (buf, s, min_p)
522      char *buf;
523      unsigned int s;
524      int min_p;
525 {
526   unsigned int h = 0, m = 0;
527   if (s >= 60)
528     {
529       m += (s / 60);
530       s %= 60;
531     }
532   if (m >= 60)
533     {
534       h += (m / 60);
535       m %= 60;
536     }
537 /*
538   if (min_p && h == 0 && s == 0)
539     sprintf (buf, "%u", m);
540   else if (!min_p && h == 0 && m == 0)
541     sprintf (buf, "%u", s);
542   else
543   if (h == 0)
544     sprintf (buf, "%u:%02u", m, s);
545   else
546 */
547     sprintf (buf, "%u:%02u:%02u", h, m, s);
548 }
549
550 static void
551 pop_resources_dialog ()
552 {
553   char buf [100];
554
555   res.timeout = timeout / 1000;
556   res.cycle = cycle / 1000;
557   res.lock_time = lock_timeout / 1000;
558 #ifndef NO_LOCKING
559   res.passwd_time = passwd_timeout / 1000;
560 #endif
561   res.secs = fade_seconds;
562   res.ticks = fade_ticks;
563   res.verb = verbose_p;
564   res.cmap = install_cmap_p;
565   res.fade = fade_p;
566   res.unfade = unfade_p;
567   res.lock_p = (lock_p && !locking_disabled_p);
568
569   fmt_time (buf, res.timeout, 1);     XmTextSetString (timeout_text, buf);
570   fmt_time (buf, res.cycle, 1);       XmTextSetString (cycle_text, buf);
571   fmt_time (buf, res.lock_time, 1);   XmTextSetString (lock_time_text, buf);
572   fmt_time (buf, res.passwd_time, 0); XmTextSetString (passwd_time_text, buf);
573   fmt_time (buf, res.secs, 0);        XmTextSetString (fade_text, buf);
574   sprintf (buf, "%u", res.ticks);     XmTextSetString (ticks_text, buf);
575
576   XmToggleButtonSetState (verbose_toggle, res.verb, True);
577   XmToggleButtonSetState (cmap_toggle, res.cmap, True);
578   XmToggleButtonSetState (fade_toggle, res.fade, True);
579   XmToggleButtonSetState (unfade_toggle, res.unfade, True);
580   XmToggleButtonSetState (lock_toggle, res.lock_p, True);
581
582   pop_up_dialog_box (resources_dialog, resources_form, 1);
583 }
584
585 \f
586 /* The code on this page isn't actually Motif-specific */
587
588 Bool dbox_up_p = False;
589 Bool demo_mode_p = False;
590
591 extern XtAppContext app;
592 extern Widget toplevel_shell;
593 extern Bool use_xidle_extension;
594 extern Bool use_mit_saver_extension;
595 extern Bool use_sgi_saver_extension;
596 extern Time notice_events_timeout;
597
598 extern char **screenhacks;
599 extern char *demo_hack;
600
601 extern void notice_events_timer P((XtPointer closure, XtIntervalId *timer));
602 extern Bool handle_clientmessage P((/*XEvent *, Bool*/));
603
604 void
605 demo_mode ()
606 {
607   dbox_up_p = True;
608   initialize_screensaver_window ();
609   raise_window (True, False);
610   make_screenhack_dialog (toplevel_shell, screenhacks);
611   while (demo_mode_p)
612     {
613       XEvent event;
614       XtAppNextEvent (app, &event);
615       switch (event.xany.type)
616         {
617         case 0:         /* synthetic "timeout" event */
618           break;
619
620         case ClientMessage:
621           handle_clientmessage (&event, False);
622           break;
623
624         case CreateNotify:
625           if (!use_xidle_extension &&
626               !use_mit_saver_extension &&
627               !use_sgi_saver_extension)
628             {
629               XtAppAddTimeOut (app, notice_events_timeout, notice_events_timer,
630                                (XtPointer) event.xcreatewindow.window);
631 #ifdef DEBUG_TIMERS
632               if (verbose_p)
633                 printf ("%s: starting notice_events_timer for 0x%X (%lu)\n",
634                         progname,
635                         (unsigned int) event.xcreatewindow.window,
636                         notice_events_timeout);
637 #endif /* DEBUG_TIMERS */
638             }
639           break;
640
641         case ButtonPress:
642         case ButtonRelease:
643           if (!XtWindowToWidget (dpy, event.xbutton.window))
644             raise_screenhack_dialog ();
645           /* fall through */
646
647         default:
648 #ifdef HAVE_MIT_SAVER_EXTENSION
649           if (event.type == mit_saver_ext_event_number)
650             {
651               /* Get the "real" server window out of the way as soon
652                  as possible. */
653               if (server_mit_saver_window &&
654                   window_exists_p (dpy, server_mit_saver_window))
655                 XUnmapWindow (dpy, server_mit_saver_window);
656             }
657           else
658 #endif /* HAVE_MIT_SAVER_EXTENSION */
659
660           XtDispatchEvent (&event);
661           break;
662         }
663     }
664   destroy_screenhack_dialogs ();
665   initialize_screensaver_window ();
666   unblank_screen ();
667 }
668
669 static void
670 demo_mode_hack (hack)
671      char *hack;
672 {
673   if (! demo_mode_p) abort ();
674   kill_screenhack ();
675   if (! demo_hack)
676     blank_screen ();
677   demo_hack = hack;
678   spawn_screenhack (False);
679   /* raise_screenhack_dialog(); */
680 }
681
682 static void
683 demo_mode_done ()
684 {
685   kill_screenhack ();
686   if (demo_hack)
687     unblank_screen ();
688   demo_mode_p = False;
689   dbox_up_p = False;
690   demo_hack = 0;
691 }