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