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 attrs.override_redirect = True;
470 attrs.event_mask = (KeyPressMask | KeyReleaseMask |
471 ButtonPressMask | ButtonReleaseMask |
473 attrs.backing_store = NotUseful;
474 attrs.colormap = cmap;
476 /* if (demo_mode_p || lock_p) width = width / 2; #### */
478 if (screensaver_window)
480 XWindowChanges changes;
481 unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
484 changes.width = width;
485 changes.height = height;
486 changes.border_width = 0;
488 XConfigureWindow (dpy, screensaver_window, changesmask, &changes);
489 XChangeWindowAttributes (dpy, screensaver_window, attrmask, &attrs);
495 else if (visual == DefaultVisualOfScreen (screen))
497 fprintf (stderr, "%s: using default visual ", progname);
498 describe_visual (stderr, dpy, visual);
502 fprintf (stderr, "%s: using visual: ", progname);
503 describe_visual (stderr, dpy, visual);
504 fprintf (stderr, "%s: default visual: ", progname);
505 describe_visual (stderr, dpy, DefaultVisualOfScreen (screen));
509 XCreateWindow (dpy, RootWindowOfScreen (screen), 0, 0, width, height,
510 0, visual_depth, InputOutput, visual, attrmask,
514 class_hints.res_name = progname;
515 class_hints.res_class = progclass;
516 XSetClassHint (dpy, screensaver_window, &class_hints);
517 XStoreName (dpy, screensaver_window, "screensaver");
518 XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_VERSION,
519 XA_STRING, 8, PropModeReplace,
520 (unsigned char *) screensaver_version,
521 strlen (screensaver_version));
523 sprintf (id, "%d on host ", getpid ());
524 if (! XmuGetHostname (id + strlen (id), sizeof (id) - strlen (id) - 1))
526 XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_ID, XA_STRING, 8,
527 PropModeReplace, (unsigned char *) id, strlen (id));
532 bit = XCreatePixmapFromBitmapData (dpy, screensaver_window, "\000", 1, 1,
533 BlackPixelOfScreen (screen),
534 BlackPixelOfScreen (screen), 1);
535 cursor = XCreatePixmapCursor (dpy, bit, bit, &black, &black, 0, 0);
536 XFreePixmap (dpy, bit);
539 XSetWindowBackground (dpy, screensaver_window, black_pixel);
541 XDefineCursor (dpy, screensaver_window, cursor);
543 XUndefineCursor (dpy, screensaver_window);
548 raise_window (inhibit_fade, between_hacks_p)
549 Bool inhibit_fade, between_hacks_p;
551 initialize_screensaver_window ();
553 if (fade_p && !inhibit_fade && !demo_mode_p)
556 Colormap current_map = (between_hacks_p
558 : DefaultColormapOfScreen (screen));
559 copy_colormap (dpy, current_map, cmap2);
561 /* grab and blacken mouse on the root window (saver not mapped yet) */
562 grabbed = grab_mouse (RootWindowOfScreen (screen));
563 /* fade what's on the screen to black */
564 XInstallColormap (dpy, cmap2);
565 fade_colormap (dpy, current_map, cmap2, fade_seconds, fade_ticks, True);
566 XClearWindow (dpy, screensaver_window);
567 XMapRaised (dpy, screensaver_window);
568 /* Once the saver window is up, restore the colormap. */
569 XInstallColormap (dpy, cmap);
570 if (grabbed == GrabSuccess)
571 XUngrabPointer (dpy, CurrentTime);
576 XClearWindow (dpy, screensaver_window);
577 XMapRaised (dpy, screensaver_window);
580 if (install_cmap_p && !demo_mode_p)
581 XInstallColormap (dpy, cmap);
588 store_vroot_property (screensaver_window, screensaver_window);
589 raise_window (False, False);
590 grab_keyboard_and_mouse ();
593 XHPDisableReset (dpy); /* turn off C-Sh-Reset */
600 if (unfade_p && !demo_mode_p)
603 Colormap default_map = DefaultColormapOfScreen (screen);
604 blacken_colormap (dpy, cmap2);
606 /* grab and blacken mouse on the root window. */
607 grabbed = grab_mouse (RootWindowOfScreen (screen));
608 XInstallColormap (dpy, cmap2);
609 XUnmapWindow (dpy, screensaver_window);
610 fade_colormap (dpy, default_map, cmap2, fade_seconds, fade_ticks, False);
611 XInstallColormap (dpy, default_map);
612 if (grabbed == GrabSuccess)
613 XUngrabPointer (dpy, CurrentTime);
618 if (install_cmap_p && !demo_mode_p)
620 XClearWindow (dpy, screensaver_window); /* avoid technicolor */
621 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
623 XUnmapWindow (dpy, screensaver_window);
625 kill_xsetroot_data ();
626 ungrab_keyboard_and_mouse ();
627 restore_real_vroot ();
630 XHPEnableReset (dpy); /* turn C-Sh-Reset back on */