http://se.aminet.net/pub/X11/ftp.x.org/contrib/vms/xscreensaver-124.zip
[xscreensaver] / driver / windows.c
1 /* xscreensaver, Copyright (c) 1991-1993 Jamie Zawinski <jwz@mcom.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 <stdio.h>
13
14 #ifdef VMS
15 typedef char * caddr_t;
16 #endif
17
18 #include <X11/Xlib.h>
19 #include <X11/Xutil.h>
20 #include <X11/Xatom.h>
21 #include <X11/Xos.h>
22 #ifndef VMS
23 #include <X11/Xmu/SysUtil.h>
24 #else
25 #include "sys$common:[decw$include.xmu]SysUtil.h"
26 #endif
27
28 #include <signal.h>             /* for the signal names */
29
30 #include "xscreensaver.h"
31
32 #if __STDC__
33 #ifndef __DECC
34 extern int kill (pid_t, int);           /* signal() is in sys/signal.h... */
35 #else
36 extern int kill (int, int);
37 #endif
38 #endif /* __STDC__ */
39
40 extern Bool lock_p, demo_mode_p;
41
42 Atom XA_VROOT, XA_XSETROOT_ID;
43 Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
44
45 #if __STDC__
46 extern void describe_visual (FILE *, Display *, Visual *);
47 #endif
48
49 Window screensaver_window = 0;
50 Cursor cursor;
51 Colormap cmap, cmap2;
52 Bool install_cmap_p;
53 Bool fade_p, unfade_p;
54 int fade_seconds, fade_ticks;
55
56 static unsigned long black_pixel;
57 static Window real_vroot, real_vroot_value;
58
59 #define ALL_POINTER_EVENTS \
60         (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
61          LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
62          Button1MotionMask | Button2MotionMask | Button3MotionMask | \
63          Button4MotionMask | Button5MotionMask | ButtonMotionMask)
64
65 /* I don't really understand Sync vs Async, but these seem to work... */
66 #define grab_kbd(win) \
67   XGrabKeyboard (dpy, (win), True, GrabModeSync, GrabModeAsync, CurrentTime)
68 #define grab_mouse(win) \
69   XGrabPointer (dpy, (win), True, ALL_POINTER_EVENTS, \
70                 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime)
71
72 void
73 grab_keyboard_and_mouse P((void))
74 {
75   Status status;
76   XSync (dpy, False);
77
78   if (demo_mode_p) return;
79
80   status = grab_kbd (screensaver_window);
81   if (status != GrabSuccess)
82     {   /* try again in a second */
83       sleep (1);
84       status = grab_kbd (screensaver_window);
85       if (status != GrabSuccess)
86         fprintf (stderr, "%s: %scouldn't grab keyboard!  (%d)\n",
87                  progname, (verbose_p ? "## " : ""), status);
88     }
89   status = grab_mouse (screensaver_window);
90   if (status != GrabSuccess)
91     {   /* try again in a second */
92       sleep (1);
93       status = grab_mouse (screensaver_window);
94       if (status != GrabSuccess)
95         fprintf (stderr, "%s: %scouldn't grab pointer!  (%d)\n",
96                  progname, (verbose_p ? "## " : ""), status);
97     }
98 }
99
100 void
101 ungrab_keyboard_and_mouse P((void))
102 {
103   XUngrabPointer (dpy, CurrentTime);
104   XUngrabKeyboard (dpy, CurrentTime);
105 }
106
107
108 void
109 ensure_no_screensaver_running ()
110 {
111   int i;
112   Window root = RootWindowOfScreen (screen);
113   Window root2, parent, *kids;
114   unsigned int nkids;
115   int (*old_handler) ();
116
117   old_handler = XSetErrorHandler (BadWindow_ehandler);
118
119   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
120     abort ();
121   if (root != root2)
122     abort ();
123   if (parent)
124     abort ();
125   for (i = 0; i < nkids; i++)
126     {
127       Atom type;
128       int format;
129       unsigned long nitems, bytesafter;
130       char *version;
131
132       if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
133                               False, XA_STRING, &type, &format, &nitems,
134                               &bytesafter, (unsigned char **) &version)
135           == Success
136           && type != None)
137         {
138           char *id;
139           if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
140                                    False, XA_STRING, &type, &format, &nitems,
141                                    &bytesafter, (unsigned char **) &id)
142               == Success
143               || type == None)
144             id = "???";
145
146           fprintf (stderr,
147       "%s: %salready running on display %s (window 0x%x)\n from process %s.\n",
148                    progname, (verbose_p ? "## " : ""), DisplayString (dpy),
149                    (int) kids [i], id);
150           exit (1);
151         }
152     }
153
154   if (kids) XFree ((char *) kids);
155   XSync (dpy, False);
156   XSetErrorHandler (old_handler);
157 }
158
159
160 void
161 disable_builtin_screensaver ()
162 {
163   int timeout, interval, prefer_blank, allow_exp;
164   XForceScreenSaver (dpy, ScreenSaverReset);
165   XGetScreenSaver (dpy, &timeout, &interval, &prefer_blank, &allow_exp);
166   if (timeout != 0)
167     {
168       XSetScreenSaver (dpy, 0, interval, prefer_blank, allow_exp);
169       printf ("%s%sisabling server builtin screensaver.\n\
170         You can re-enable it with \"xset s on\".\n",
171               (verbose_p ? "" : progname), (verbose_p ? "\n\tD" : ": d"));
172     }
173 }
174
175 \f
176 /* Virtual-root hackery */
177
178 #ifdef _VROOT_H_
179 ERROR!  You must not include vroot.h in this file.
180 #endif
181
182 static void
183 #if __STDC__
184 store_vroot_property (Window win, Window value)
185 #else
186 store_vroot_property (win, value)
187      Window win, value;
188 #endif
189 {
190 #if 0
191   printf ("%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", progname, 
192           win,
193           (win == screensaver_window ? "ScreenSaver" :
194            (win == real_vroot ? "VRoot" :
195             (win == real_vroot_value ? "Vroot_value" : "???"))),
196           value,
197           (value == screensaver_window ? "ScreenSaver" :
198            (value == real_vroot ? "VRoot" :
199             (value == real_vroot_value ? "Vroot_value" : "???"))));
200 #endif
201   XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
202                    (unsigned char *) &value, 1);
203 }
204
205 static void
206 #if __STDC__
207 remove_vroot_property (Window win)
208 #else
209 remove_vroot_property (win)
210      Window win;
211 #endif
212 {
213 #if 0
214   printf ("%s: removing XA_VROOT from 0x%x (%s)\n", progname, win, 
215           (win == screensaver_window ? "ScreenSaver" :
216            (win == real_vroot ? "VRoot" :
217             (win == real_vroot_value ? "Vroot_value" : "???"))));
218 #endif
219   XDeleteProperty (dpy, win, XA_VROOT);
220 }
221
222
223 static void
224 kill_xsetroot_data P((void))
225 {
226   Atom type;
227   int format;
228   unsigned long nitems, bytesafter;
229   Pixmap *dataP = 0;
230
231   /* If the user has been using xv or xsetroot as a screensaver (to display
232      an image on the screensaver window, as a kind of slideshow) then the
233      pixmap and its associated color cells have been put in RetainPermanent
234      CloseDown mode.  Since we're not destroying the xscreensaver window,
235      but merely unmapping it, we need to free these resources or those
236      colormap cells will stay allocated while the screensaver is off.  (We
237      could just delete the screensaver window and recreate it later, but
238      that could cause other problems.)  This code does an atomic read-and-
239      delete of the _XSETROOT_ID property, and if it held a pixmap, then we
240      cause the RetainPermanent resources of the client which created it
241      (and which no longer exists) to be freed.
242    */
243   if (XGetWindowProperty (dpy, screensaver_window, XA_XSETROOT_ID, 0, 1,
244                           True, AnyPropertyType, &type, &format, &nitems, 
245                           &bytesafter, (unsigned char **) &dataP)
246       == Success
247       && type != None)
248     {
249       if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
250           nitems == 1 && bytesafter == 0)
251         {
252           if (verbose_p)
253             printf ("%s: destroying xsetroot data (0x%X).\n",
254                     progname, *dataP);
255           XKillClient (dpy, *dataP);
256         }
257       else
258         fprintf (stderr, "%s: %sdeleted unrecognised _XSETROOT_ID property: \n\
259         %d, %d; type: %d, format: %d, nitems: %d, bytesafter %d\n",
260                  progname, (verbose_p ? "## " : ""),
261                  dataP, (dataP ? *dataP : 0), type,
262                  format, nitems, bytesafter);
263     }
264 }
265
266
267 static void handle_signals P((Bool on_p));
268
269 static void
270 save_real_vroot P((void))
271 {
272   int i;
273   Window root = RootWindowOfScreen (screen);
274   Window root2, parent, *kids;
275   unsigned int nkids;
276
277   real_vroot = 0;
278   real_vroot_value = 0;
279   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
280     abort ();
281   if (root != root2)
282     abort ();
283   if (parent)
284     abort ();
285   for (i = 0; i < nkids; i++)
286     {
287       Atom type;
288       int format;
289       unsigned long nitems, bytesafter;
290       Window *vrootP = 0;
291
292       if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
293                               &type, &format, &nitems, &bytesafter,
294                               (unsigned char **) &vrootP)
295           != Success)
296         continue;
297       if (! vrootP)
298         continue;
299       if (real_vroot)
300         {
301           if (*vrootP == screensaver_window) abort ();
302           fprintf (stderr,
303             "%s: %smore than one virtual root window found (0x%x and 0x%x).\n",
304                    progname, (verbose_p ? "## " : ""),
305                    (int) real_vroot, (int) kids [i]);
306           exit (1);
307         }
308       real_vroot = kids [i];
309       real_vroot_value = *vrootP;
310     }
311
312   if (real_vroot)
313     {
314       handle_signals (True);
315       remove_vroot_property (real_vroot);
316       XSync (dpy, False);
317     }
318
319   XFree ((char *) kids);
320 }
321
322 static Bool
323 restore_real_vroot_1 P((void))
324 {
325   if (verbose_p && real_vroot)
326     printf ("%s: restoring __SWM_VROOT property on the real vroot (0x%x).\n",
327             progname, real_vroot);
328   remove_vroot_property (screensaver_window);
329   if (real_vroot)
330     {
331       store_vroot_property (real_vroot, real_vroot_value);
332       real_vroot = 0;
333       real_vroot_value = 0;
334       /* make sure the property change gets there before this process
335          terminates!  We might be doing this because we have intercepted
336          SIGTERM or something. */
337       XSync (dpy, False);
338       return True;
339     }
340   return False;
341 }
342
343 void
344 restore_real_vroot ()
345 {
346   if (restore_real_vroot_1 ())
347     handle_signals (False);
348 }
349
350 \f
351 /* Signal hackery to ensure that the vroot doesn't get left in an 
352    inconsistent state
353  */
354
355 static const char *sig_names [255] = { 0 };
356
357 static void
358 restore_real_vroot_handler (sig)
359      int sig;
360 {
361   signal (sig, SIG_DFL);
362   if (restore_real_vroot_1 ())
363     fprintf (stderr, "\n%s: %s%s (%d) intercepted, vroot restored.\n",
364              progname, (verbose_p ? "## " : ""),
365              ((sig < sizeof(sig_names) && sig >= 0 && sig_names [sig])
366               ? sig_names [sig] : "unknown signal"),
367              sig);
368   kill (getpid (), sig);
369 }
370
371
372 static void
373 #if __STDC__
374 catch_signal (int sig, char *signame, Bool on_p)
375 #else
376 catch_signal (sig, signame, on_p)
377      int sig;
378      char *signame;
379      Bool on_p;
380 #endif
381 {
382   if (! on_p)
383     signal (sig, SIG_DFL);
384   else
385     {
386       sig_names [sig] = signame;
387       if (((int) signal (sig, restore_real_vroot_handler)) == -1)
388         {
389           char buf [255];
390           sprintf (buf, "%s: %scouldn't catch %s (%d)", progname,
391                    (verbose_p ? "## " : ""), signame, sig);
392           perror (buf);
393           restore_real_vroot ();
394           exit (1);
395         }
396     }
397 }
398
399 static void
400 handle_signals (on_p)
401      Bool on_p;
402 {
403 #if 0
404   if (on_p) printf ("handling signals\n");
405   else printf ("unhandling signals\n");
406 #endif
407
408   catch_signal (SIGHUP,  "SIGHUP",  on_p);
409   catch_signal (SIGINT,  "SIGINT",  on_p);
410   catch_signal (SIGQUIT, "SIGQUIT", on_p);
411   catch_signal (SIGILL,  "SIGILL",  on_p);
412   catch_signal (SIGTRAP, "SIGTRAP", on_p);
413   catch_signal (SIGIOT,  "SIGIOT",  on_p);
414 #ifndef VMS
415   catch_signal (SIGABRT, "SIGABRT", on_p);
416 #endif
417 #ifdef SIGEMT
418   catch_signal (SIGEMT,  "SIGEMT",  on_p);
419 #endif
420   catch_signal (SIGFPE,  "SIGFPE",  on_p);
421   catch_signal (SIGBUS,  "SIGBUS",  on_p);
422   catch_signal (SIGSEGV, "SIGSEGV", on_p);
423 #ifdef SIGSYS
424   catch_signal (SIGSYS,  "SIGSYS",  on_p);
425 #endif
426   catch_signal (SIGTERM, "SIGTERM", on_p);
427 #ifdef SIGXCPU
428   catch_signal (SIGXCPU, "SIGXCPU", on_p);
429 #endif
430 #ifdef SIGXFSZ
431   catch_signal (SIGXFSZ, "SIGXFSZ", on_p);
432 #endif
433 #ifdef SIGDANGER
434   catch_signal (SIGDANGER, "SIGDANGER", on_p);
435 #endif
436 }
437
438 \f
439 /* Managing the actual screensaver window */
440
441 void
442 initialize_screensaver_window P((void))
443 {
444   /* This resets the screensaver window as fully as possible, since there's
445      no way of knowing what some random client may have done to us in the
446      meantime.  We could just destroy and recreate the window, but that has
447      its own set of problems...
448    */
449   XColor black;
450   XClassHint class_hints;
451   XSetWindowAttributes attrs;
452   unsigned long attrmask;
453   int width = WidthOfScreen (screen);
454   int height = HeightOfScreen (screen);
455   char id [2048];
456
457   black.red = black.green = black.blue = 0;
458
459   if (cmap == DefaultColormapOfScreen (screen))
460     cmap = 0;
461
462   if ((install_cmap_p && !demo_mode_p) ||
463       (visual != DefaultVisualOfScreen (screen)))
464     {
465       if (! cmap)
466         {
467           cmap = XCreateColormap (dpy, RootWindowOfScreen (screen),
468                                   visual, AllocNone);
469           if (! XAllocColor (dpy, cmap, &black)) abort ();
470           black_pixel = black.pixel;
471         }
472     }
473   else
474     {
475       if (cmap)
476         {
477           XFreeColors (dpy, cmap, &black_pixel, 1, 0);
478           XFreeColormap (dpy, cmap);
479         }
480       cmap = DefaultColormapOfScreen (screen);
481       black_pixel = BlackPixelOfScreen (screen);
482     }
483
484   if (cmap2)
485     {
486       XFreeColormap (dpy, cmap2);
487       cmap2 = 0;
488     }
489
490   if (fade_p)
491     {
492       cmap2 = copy_colormap (dpy, cmap, 0);
493       if (! cmap2)
494         fade_p = unfade_p = 0;
495     }
496
497   attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
498               CWBackPixel | CWBackingPixel | CWBorderPixel);
499   attrs.override_redirect = True;
500   attrs.event_mask = (KeyPressMask | KeyReleaseMask |
501                       ButtonPressMask | ButtonReleaseMask |
502                       PointerMotionMask);
503   attrs.backing_store = NotUseful;
504   attrs.colormap = cmap;
505   attrs.background_pixel = black_pixel;
506   attrs.backing_pixel = black_pixel;
507   attrs.border_pixel = black_pixel;
508
509 /*  if (demo_mode_p || lock_p) width = width / 2;   #### */
510
511   if (screensaver_window)
512     {
513       XWindowChanges changes;
514       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
515       changes.x = 0;
516       changes.y = 0;
517       changes.width = width;
518       changes.height = height;
519       changes.border_width = 0;
520
521       XConfigureWindow (dpy, screensaver_window, changesmask, &changes);
522       XChangeWindowAttributes (dpy, screensaver_window, attrmask, &attrs);
523     }
524   else
525     {
526       if (! verbose_p)
527         ;
528       else if (visual == DefaultVisualOfScreen (screen))
529         {
530           fprintf (stderr, "%s: using default visual ", progname);
531           describe_visual (stderr, dpy, visual);
532         }
533       else
534         {
535           fprintf (stderr, "%s: using visual:   ", progname);
536           describe_visual (stderr, dpy, visual);
537           fprintf (stderr, "%s: default visual: ", progname);
538           describe_visual (stderr, dpy, DefaultVisualOfScreen (screen));
539         }
540
541       screensaver_window =
542         XCreateWindow (dpy, RootWindowOfScreen (screen), 0, 0, width, height,
543                        0, visual_depth, InputOutput, visual, attrmask,
544                        &attrs);
545     }
546
547   class_hints.res_name = progname;
548   class_hints.res_class = progclass;
549   XSetClassHint (dpy, screensaver_window, &class_hints);
550   XStoreName (dpy, screensaver_window, "screensaver");
551   XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_VERSION,
552                    XA_STRING, 8, PropModeReplace,
553                    (unsigned char *) screensaver_version,
554                    strlen (screensaver_version));
555
556   sprintf (id, "%d on host ", getpid ());
557   if (! XmuGetHostname (id + strlen (id), sizeof (id) - strlen (id) - 1))
558     strcat (id, "???");
559   XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_ID, XA_STRING, 8,
560                    PropModeReplace, (unsigned char *) id, strlen (id));
561
562   if (!cursor)
563     {
564       Pixmap bit;
565       bit = XCreatePixmapFromBitmapData (dpy, screensaver_window, "\000", 1, 1,
566                                          BlackPixelOfScreen (screen),
567                                          BlackPixelOfScreen (screen), 1);
568       cursor = XCreatePixmapCursor (dpy, bit, bit, &black, &black, 0, 0);
569       XFreePixmap (dpy, bit);
570     }
571
572   XSetWindowBackground (dpy, screensaver_window, black_pixel);
573   if (! demo_mode_p)
574     XDefineCursor (dpy, screensaver_window, cursor);
575   else
576     XUndefineCursor (dpy, screensaver_window);
577 }
578
579
580 void 
581 raise_window (inhibit_fade, between_hacks_p)
582      Bool inhibit_fade, between_hacks_p;
583 {
584   initialize_screensaver_window ();
585
586   if (fade_p && !inhibit_fade && !demo_mode_p)
587     {
588       int grabbed;
589       Colormap current_map = (between_hacks_p
590                               ? cmap
591                               : DefaultColormapOfScreen (screen));
592       copy_colormap (dpy, current_map, cmap2);
593       XGrabServer (dpy);
594       /* grab and blacken mouse on the root window (saver not mapped yet) */
595       grabbed = grab_mouse (RootWindowOfScreen (screen));
596       /* fade what's on the screen to black */
597       XInstallColormap (dpy, cmap2);
598       fade_colormap (dpy, current_map, cmap2, fade_seconds, fade_ticks, True);
599       XClearWindow (dpy, screensaver_window);
600       XMapRaised (dpy, screensaver_window);
601       /* Once the saver window is up, restore the colormap.
602          (The "black" pixels of the two colormaps are compatible.) */
603       XInstallColormap (dpy, cmap);
604       if (grabbed == GrabSuccess)
605         XUngrabPointer (dpy, CurrentTime);
606       XUngrabServer (dpy);
607     }
608   else
609     {
610       XClearWindow (dpy, screensaver_window);
611       XMapRaised (dpy, screensaver_window);
612     }
613
614   if (install_cmap_p && !demo_mode_p)
615     XInstallColormap (dpy, cmap);
616 }
617
618 void
619 blank_screen ()
620 {
621   save_real_vroot ();
622   store_vroot_property (screensaver_window, screensaver_window);
623   raise_window (False, False);
624   grab_keyboard_and_mouse ();
625 #ifdef __hpux
626   if (lock_p)
627     XHPDisableReset (dpy);      /* turn off C-Sh-Reset */
628 #endif
629 }
630
631 void
632 unblank_screen ()
633 {
634   if (unfade_p && !demo_mode_p)
635     {
636       int grabbed;
637       Colormap default_map = DefaultColormapOfScreen (screen);
638       blacken_colormap (dpy, cmap2);
639       XGrabServer (dpy);
640       /* grab and blacken mouse on the root window. */
641       grabbed = grab_mouse (RootWindowOfScreen (screen));
642       XInstallColormap (dpy, cmap2);
643       XUnmapWindow (dpy, screensaver_window);
644       fade_colormap (dpy, default_map, cmap2, fade_seconds, fade_ticks, False);
645       XInstallColormap (dpy, default_map);
646       if (grabbed == GrabSuccess)
647         XUngrabPointer (dpy, CurrentTime);
648       XUngrabServer (dpy);
649     }
650   else
651     {
652       if (install_cmap_p && !demo_mode_p)
653         {
654           XClearWindow (dpy, screensaver_window); /* avoid technicolor */
655           XInstallColormap (dpy, DefaultColormapOfScreen (screen));
656         }
657       XUnmapWindow (dpy, screensaver_window);
658     }
659   kill_xsetroot_data ();
660   ungrab_keyboard_and_mouse ();
661   restore_real_vroot ();
662 #ifdef __hpux
663   if (lock_p)
664     XHPEnableReset (dpy);       /* turn C-Sh-Reset back on */
665 #endif
666 }