1 /* xscreensaver, Copyright (c) 1991-1993 Jamie Zawinski <jwz@lucid.com>
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
14 #include <X11/Xutil.h>
15 #include <X11/Xatom.h>
17 #include <X11/Xmu/SysUtil.h>
19 #include <signal.h> /* for the signal names */
21 #include "xscreensaver.h"
24 extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
27 extern Bool lock_p, demo_mode_p;
29 Atom XA_VROOT, XA_XSETROOT_ID;
30 Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
33 extern void describe_visual (FILE *, Display *, Visual *);
36 Window screensaver_window = 0;
40 Bool fade_p, unfade_p;
41 int fade_seconds, fade_ticks;
43 static unsigned long black_pixel;
44 static Window real_vroot, real_vroot_value;
46 #define ALL_POINTER_EVENTS \
47 (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
48 LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
49 Button1MotionMask | Button2MotionMask | Button3MotionMask | \
50 Button4MotionMask | Button5MotionMask | ButtonMotionMask)
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)
60 grab_keyboard_and_mouse ()
65 if (demo_mode_p) return;
67 status = grab_kbd (screensaver_window);
68 if (status != GrabSuccess)
69 { /* try again in a second */
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);
76 status = grab_mouse (screensaver_window);
77 if (status != GrabSuccess)
78 { /* try again in a second */
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);
88 ungrab_keyboard_and_mouse ()
90 XUngrabPointer (dpy, CurrentTime);
91 XUngrabKeyboard (dpy, CurrentTime);
96 ensure_no_screensaver_running ()
99 Window root = RootWindowOfScreen (screen);
100 Window root2, parent, *kids;
102 int (*old_handler) ();
104 old_handler = XSetErrorHandler (BadWindow_ehandler);
106 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
112 for (i = 0; i < nkids; i++)
116 unsigned long nitems, bytesafter;
119 if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
120 False, XA_STRING, &type, &format, &nitems,
121 &bytesafter, (unsigned char **) &version)
126 if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
127 False, XA_STRING, &type, &format, &nitems,
128 &bytesafter, (unsigned char **) &id)
134 "%s: %salready running on display %s (window 0x%x)\n from process %s.\n",
135 progname, (verbose_p ? "## " : ""), DisplayString (dpy),
141 if (kids) XFree ((char *) kids);
143 XSetErrorHandler (old_handler);
148 disable_builtin_screensaver ()
150 int timeout, interval, prefer_blank, allow_exp;
151 XForceScreenSaver (dpy, ScreenSaverReset);
152 XGetScreenSaver (dpy, &timeout, &interval, &prefer_blank, &allow_exp);
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"));
163 /* Virtual-root hackery */
166 ERROR! You must not include vroot.h in this file.
170 store_vroot_property (win, value)
174 printf ("%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", progname,
176 (win == screensaver_window ? "ScreenSaver" :
177 (win == real_vroot ? "VRoot" :
178 (win == real_vroot_value ? "Vroot_value" : "???"))),
180 (value == screensaver_window ? "ScreenSaver" :
181 (value == real_vroot ? "VRoot" :
182 (value == real_vroot_value ? "Vroot_value" : "???"))));
184 XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
185 (unsigned char *) &value, 1);
189 remove_vroot_property (win)
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" : "???"))));
198 XDeleteProperty (dpy, win, XA_VROOT);
203 kill_xsetroot_data ()
207 unsigned long nitems, bytesafter;
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.
222 if (XGetWindowProperty (dpy, screensaver_window, XA_XSETROOT_ID, 0, 1,
223 True, AnyPropertyType, &type, &format, &nitems,
224 &bytesafter, (unsigned char **) &dataP)
228 if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
229 nitems == 1 && bytesafter == 0)
232 printf ("%s: destroying xsetroot data (0x%X).\n",
234 XKillClient (dpy, *dataP);
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);
246 static void handle_signals P((Bool on_p));
252 Window root = RootWindowOfScreen (screen);
253 Window root2, parent, *kids;
257 real_vroot_value = 0;
258 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
264 for (i = 0; i < nkids; i++)
268 unsigned long nitems, bytesafter;
271 if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
272 &type, &format, &nitems, &bytesafter,
273 (unsigned char **) &vrootP)
280 if (*vrootP == screensaver_window) abort ();
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]);
287 real_vroot = kids [i];
288 real_vroot_value = *vrootP;
293 handle_signals (True);
294 remove_vroot_property (real_vroot);
298 XFree ((char *) kids);
302 restore_real_vroot_1 ()
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);
310 store_vroot_property (real_vroot, real_vroot_value);
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. */
323 restore_real_vroot ()
325 if (restore_real_vroot_1 ())
326 handle_signals (False);
330 /* Signal hackery to ensure that the vroot doesn't get left in an
334 static const char *sig_names [255] = { 0 };
337 restore_real_vroot_handler (sig)
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"),
347 kill (getpid (), sig);
352 catch_signal (sig, signame, on_p)
358 signal (sig, SIG_DFL);
361 sig_names [sig] = signame;
362 if (((int) signal (sig, restore_real_vroot_handler)) == -1)
365 sprintf (buf, "%s: %scouldn't catch %s (%d)", progname,
366 (verbose_p ? "## " : ""), signame, sig);
368 restore_real_vroot ();
375 handle_signals (on_p)
379 if (on_p) printf ("handling signals\n");
380 else printf ("unhandling signals\n");
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);
391 catch_signal (SIGEMT, "SIGEMT", on_p);
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);
399 catch_signal (SIGXCPU, "SIGXCPU", on_p);
402 catch_signal (SIGXFSZ, "SIGXFSZ", on_p);
405 catch_signal (SIGDANGER, "SIGDANGER", on_p);
410 /* Managing the actual screensaver window */
413 initialize_screensaver_window ()
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...
421 XClassHint class_hints;
422 XSetWindowAttributes attrs;
423 unsigned long attrmask;
424 int width = WidthOfScreen (screen);
425 int height = HeightOfScreen (screen);
428 black.red = black.green = black.blue = 0;
430 if (cmap == DefaultColormapOfScreen (screen))
433 if ((install_cmap_p && !demo_mode_p) ||
434 (visual != DefaultVisualOfScreen (screen)))
438 cmap = XCreateColormap (dpy, RootWindowOfScreen (screen),
440 if (! XAllocColor (dpy, cmap, &black)) abort ();
441 black_pixel = black.pixel;
448 XFreeColors (dpy, cmap, &black_pixel, 1, 0);
449 XFreeColormap (dpy, cmap);
451 cmap = DefaultColormapOfScreen (screen);
452 black_pixel = BlackPixelOfScreen (screen);
457 XFreeColormap (dpy, cmap2);
463 cmap2 = copy_colormap (dpy, cmap, 0);
465 fade_p = unfade_p = 0;
468 attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
469 CWBackPixel | CWBackingPixel | CWBorderPixel);
470 attrs.override_redirect = True;
471 attrs.event_mask = (KeyPressMask | KeyReleaseMask |
472 ButtonPressMask | ButtonReleaseMask |
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;
480 /* if (demo_mode_p || lock_p) width = width / 2; #### */
482 if (screensaver_window)
484 XWindowChanges changes;
485 unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
488 changes.width = width;
489 changes.height = height;
490 changes.border_width = 0;
492 XConfigureWindow (dpy, screensaver_window, changesmask, &changes);
493 XChangeWindowAttributes (dpy, screensaver_window, attrmask, &attrs);
499 else if (visual == DefaultVisualOfScreen (screen))
501 fprintf (stderr, "%s: using default visual ", progname);
502 describe_visual (stderr, dpy, visual);
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));
513 XCreateWindow (dpy, RootWindowOfScreen (screen), 0, 0, width, height,
514 0, visual_depth, InputOutput, visual, attrmask,
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));
527 sprintf (id, "%d on host ", getpid ());
528 if (! XmuGetHostname (id + strlen (id), sizeof (id) - strlen (id) - 1))
530 XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_ID, XA_STRING, 8,
531 PropModeReplace, (unsigned char *) id, strlen (id));
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);
543 XSetWindowBackground (dpy, screensaver_window, black_pixel);
545 XDefineCursor (dpy, screensaver_window, cursor);
547 XUndefineCursor (dpy, screensaver_window);
552 raise_window (inhibit_fade, between_hacks_p)
553 Bool inhibit_fade, between_hacks_p;
555 initialize_screensaver_window ();
557 if (fade_p && !inhibit_fade && !demo_mode_p)
560 Colormap current_map = (between_hacks_p
562 : DefaultColormapOfScreen (screen));
563 copy_colormap (dpy, current_map, cmap2);
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);
581 XClearWindow (dpy, screensaver_window);
582 XMapRaised (dpy, screensaver_window);
585 if (install_cmap_p && !demo_mode_p)
586 XInstallColormap (dpy, cmap);
593 store_vroot_property (screensaver_window, screensaver_window);
594 raise_window (False, False);
595 grab_keyboard_and_mouse ();
598 XHPDisableReset (dpy); /* turn off C-Sh-Reset */
605 if (unfade_p && !demo_mode_p)
608 Colormap default_map = DefaultColormapOfScreen (screen);
609 blacken_colormap (dpy, cmap2);
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);
623 if (install_cmap_p && !demo_mode_p)
625 XClearWindow (dpy, screensaver_window); /* avoid technicolor */
626 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
628 XUnmapWindow (dpy, screensaver_window);
630 kill_xsetroot_data ();
631 ungrab_keyboard_and_mouse ();
632 restore_real_vroot ();
635 XHPEnableReset (dpy); /* turn C-Sh-Reset back on */