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