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;
32 Window screensaver_window = 0;
36 Bool fade_p, unfade_p;
37 int fade_seconds, fade_ticks;
39 static unsigned long black_pixel;
40 static Window real_vroot, real_vroot_value;
42 #define ALL_POINTER_EVENTS \
43 (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
44 LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
45 Button1MotionMask | Button2MotionMask | Button3MotionMask | \
46 Button4MotionMask | Button5MotionMask | ButtonMotionMask)
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)
56 grab_keyboard_and_mouse ()
61 if (demo_mode_p) return;
63 status = grab_kbd (screensaver_window);
64 if (status != GrabSuccess)
65 { /* try again in a second */
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);
72 status = grab_mouse (screensaver_window);
73 if (status != GrabSuccess)
74 { /* try again in a second */
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);
84 ungrab_keyboard_and_mouse ()
86 XUngrabPointer (dpy, CurrentTime);
87 XUngrabKeyboard (dpy, CurrentTime);
92 ensure_no_screensaver_running ()
95 Window root = RootWindowOfScreen (screen);
96 Window root2, parent, *kids;
98 int (*old_handler) ();
100 old_handler = XSetErrorHandler (BadWindow_ehandler);
102 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
108 for (i = 0; i < nkids; i++)
112 unsigned long nitems, bytesafter;
115 if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
116 False, XA_STRING, &type, &format, &nitems,
117 &bytesafter, (unsigned char **) &version)
122 if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
123 False, XA_STRING, &type, &format, &nitems,
124 &bytesafter, (unsigned char **) &id)
130 "%s: %salready running on display %s (window 0x%x)\n from process %s.\n",
131 progname, (verbose_p ? "## " : ""), DisplayString (dpy),
137 if (kids) XFree ((char *) kids);
139 XSetErrorHandler (old_handler);
144 disable_builtin_screensaver ()
146 int timeout, interval, prefer_blank, allow_exp;
147 XForceScreenSaver (dpy, ScreenSaverReset);
148 XGetScreenSaver (dpy, &timeout, &interval, &prefer_blank, &allow_exp);
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"));
159 /* Virtual-root hackery */
162 ERROR! You must not include vroot.h in this file.
166 store_vroot_property (win, value)
170 printf ("%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", progname,
172 (win == screensaver_window ? "ScreenSaver" :
173 (win == real_vroot ? "VRoot" :
174 (win == real_vroot_value ? "Vroot_value" : "???"))),
176 (value == screensaver_window ? "ScreenSaver" :
177 (value == real_vroot ? "VRoot" :
178 (value == real_vroot_value ? "Vroot_value" : "???"))));
180 XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
181 (unsigned char *) &value, 1);
185 remove_vroot_property (win)
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" : "???"))));
194 XDeleteProperty (dpy, win, XA_VROOT);
199 kill_xsetroot_data ()
203 unsigned long nitems, bytesafter;
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.
218 if (XGetWindowProperty (dpy, screensaver_window, XA_XSETROOT_ID, 0, 1,
219 True, AnyPropertyType, &type, &format, &nitems,
220 &bytesafter, (unsigned char **) &dataP)
224 if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
225 nitems == 1 && bytesafter == 0)
228 printf ("%s: destroying xsetroot data (0x%X).\n",
230 XKillClient (dpy, *dataP);
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);
242 static void handle_signals P((Bool on_p));
248 Window root = RootWindowOfScreen (screen);
249 Window root2, parent, *kids;
253 real_vroot_value = 0;
254 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
260 for (i = 0; i < nkids; i++)
264 unsigned long nitems, bytesafter;
267 if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
268 &type, &format, &nitems, &bytesafter,
269 (unsigned char **) &vrootP)
276 if (*vrootP == screensaver_window) abort ();
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]);
283 real_vroot = kids [i];
284 real_vroot_value = *vrootP;
289 handle_signals (True);
290 remove_vroot_property (real_vroot);
294 XFree ((char *) kids);
298 restore_real_vroot_1 ()
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);
306 store_vroot_property (real_vroot, real_vroot_value);
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. */
319 restore_real_vroot ()
321 if (restore_real_vroot_1 ())
322 handle_signals (False);
326 /* Signal hackery to ensure that the vroot doesn't get left in an
330 static const char *sig_names [255] = { 0 };
333 restore_real_vroot_handler (sig)
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"),
343 kill (getpid (), sig);
348 catch_signal (sig, signame, on_p)
354 signal (sig, SIG_DFL);
357 sig_names [sig] = signame;
358 if (((int) signal (sig, restore_real_vroot_handler)) == -1)
361 sprintf (buf, "%s: %scouldn't catch %s (%d)", progname,
362 (verbose_p ? "## " : ""), signame, sig);
364 restore_real_vroot ();
371 handle_signals (on_p)
375 if (on_p) printf ("handling signals\n");
376 else printf ("unhandling signals\n");
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);
387 catch_signal (SIGEMT, "SIGEMT", on_p);
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);
395 catch_signal (SIGXCPU, "SIGXCPU", on_p);
398 catch_signal (SIGXFSZ, "SIGXFSZ", on_p);
401 catch_signal (SIGDANGER, "SIGDANGER", on_p);
406 /* Managing the actual screensaver window */
409 initialize_screensaver_window ()
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...
417 XClassHint class_hints;
418 XSetWindowAttributes attrs;
419 unsigned long attrmask;
420 int width = WidthOfScreen (screen);
421 int height = HeightOfScreen (screen);
424 if (cmap == DefaultColormapOfScreen (screen))
427 if ((install_cmap_p && !demo_mode_p) ||
428 (visual != DefaultVisualOfScreen (screen)))
432 cmap = XCreateColormap (dpy, RootWindowOfScreen (screen),
434 if (! XAllocColor (dpy, cmap, &black)) abort ();
435 black_pixel = black.pixel;
442 XFreeColors (dpy, cmap, &black_pixel, 1, 0);
443 XFreeColormap (dpy, cmap);
445 cmap = DefaultColormapOfScreen (screen);
446 black_pixel = BlackPixelOfScreen (screen);
451 XFreeColormap (dpy, cmap2);
457 cmap2 = copy_colormap (dpy, cmap, 0);
459 fade_p = unfade_p = 0;
462 attrmask = CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap;
463 attrs.override_redirect = True;
464 attrs.event_mask = (KeyPressMask | KeyReleaseMask |
465 ButtonPressMask | ButtonReleaseMask |
467 attrs.backing_store = NotUseful;
468 attrs.colormap = cmap;
470 /* if (demo_mode_p || lock_p) width = width / 2; #### */
472 if (screensaver_window)
474 XWindowChanges changes;
475 unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
478 changes.width = width;
479 changes.height = height;
480 changes.border_width = 0;
482 XConfigureWindow (dpy, screensaver_window, changesmask, &changes);
483 XChangeWindowAttributes (dpy, screensaver_window, attrmask, &attrs);
488 XCreateWindow (dpy, RootWindowOfScreen (screen), 0, 0, width, height,
489 0, visual_depth, InputOutput, visual, attrmask,
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));
502 sprintf (id, "%d on host ", getpid ());
503 if (! XmuGetHostname (id + strlen (id), sizeof (id) - strlen (id) - 1))
505 XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_ID, XA_STRING, 8,
506 PropModeReplace, (unsigned char *) id, strlen (id));
508 black.red = black.green = black.blue = 0;
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);
519 XSetWindowBackground (dpy, screensaver_window, black_pixel);
521 XDefineCursor (dpy, screensaver_window, cursor);
523 XUndefineCursor (dpy, screensaver_window);
528 raise_window (inhibit_fade, between_hacks_p)
529 Bool inhibit_fade, between_hacks_p;
531 initialize_screensaver_window ();
533 if (fade_p && !inhibit_fade && !demo_mode_p)
536 Colormap current_map = (between_hacks_p
538 : DefaultColormapOfScreen (screen));
539 copy_colormap (dpy, current_map, cmap2);
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);
555 XClearWindow (dpy, screensaver_window);
556 XMapRaised (dpy, screensaver_window);
559 if (install_cmap_p && !demo_mode_p)
560 XInstallColormap (dpy, cmap);
567 store_vroot_property (screensaver_window, screensaver_window);
568 raise_window (False, False);
569 grab_keyboard_and_mouse ();
572 XHPDisableReset (dpy); /* turn off C-Sh-Reset */
579 if (unfade_p && !demo_mode_p)
582 Colormap default_map = DefaultColormapOfScreen (screen);
583 blacken_colormap (dpy, cmap2);
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);
597 if (install_cmap_p && !demo_mode_p)
599 XClearWindow (dpy, screensaver_window); /* avoid technicolor */
600 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
602 XUnmapWindow (dpy, screensaver_window);
604 kill_xsetroot_data ();
605 ungrab_keyboard_and_mouse ();
606 restore_real_vroot ();
609 XHPEnableReset (dpy); /* turn C-Sh-Reset back on */