1 /* xscreensaver, Copyright (c) 1991-1993 Jamie Zawinski <jwz@mcom.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
15 typedef char * caddr_t;
19 #include <X11/Xutil.h>
20 #include <X11/Xatom.h>
23 #include <X11/Xmu/SysUtil.h>
25 #include "sys$common:[decw$include.xmu]SysUtil.h"
28 #include <signal.h> /* for the signal names */
30 #include "xscreensaver.h"
34 extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
36 extern int kill (int, int);
40 extern Bool lock_p, demo_mode_p;
42 Atom XA_VROOT, XA_XSETROOT_ID;
43 Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
46 extern void describe_visual (FILE *, Display *, Visual *);
49 Window screensaver_window = 0;
53 Bool fade_p, unfade_p;
54 int fade_seconds, fade_ticks;
56 static unsigned long black_pixel;
57 static Window real_vroot, real_vroot_value;
59 #define ALL_POINTER_EVENTS \
60 (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
61 LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
62 Button1MotionMask | Button2MotionMask | Button3MotionMask | \
63 Button4MotionMask | Button5MotionMask | ButtonMotionMask)
65 /* I don't really understand Sync vs Async, but these seem to work... */
66 #define grab_kbd(win) \
67 XGrabKeyboard (dpy, (win), True, GrabModeSync, GrabModeAsync, CurrentTime)
68 #define grab_mouse(win) \
69 XGrabPointer (dpy, (win), True, ALL_POINTER_EVENTS, \
70 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime)
73 grab_keyboard_and_mouse P((void))
78 if (demo_mode_p) return;
80 status = grab_kbd (screensaver_window);
81 if (status != GrabSuccess)
82 { /* try again in a second */
84 status = grab_kbd (screensaver_window);
85 if (status != GrabSuccess)
86 fprintf (stderr, "%s: %scouldn't grab keyboard! (%d)\n",
87 progname, (verbose_p ? "## " : ""), status);
89 status = grab_mouse (screensaver_window);
90 if (status != GrabSuccess)
91 { /* try again in a second */
93 status = grab_mouse (screensaver_window);
94 if (status != GrabSuccess)
95 fprintf (stderr, "%s: %scouldn't grab pointer! (%d)\n",
96 progname, (verbose_p ? "## " : ""), status);
101 ungrab_keyboard_and_mouse P((void))
103 XUngrabPointer (dpy, CurrentTime);
104 XUngrabKeyboard (dpy, CurrentTime);
109 ensure_no_screensaver_running ()
112 Window root = RootWindowOfScreen (screen);
113 Window root2, parent, *kids;
115 int (*old_handler) ();
117 old_handler = XSetErrorHandler (BadWindow_ehandler);
119 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
125 for (i = 0; i < nkids; i++)
129 unsigned long nitems, bytesafter;
132 if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
133 False, XA_STRING, &type, &format, &nitems,
134 &bytesafter, (unsigned char **) &version)
139 if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
140 False, XA_STRING, &type, &format, &nitems,
141 &bytesafter, (unsigned char **) &id)
147 "%s: %salready running on display %s (window 0x%x)\n from process %s.\n",
148 progname, (verbose_p ? "## " : ""), DisplayString (dpy),
154 if (kids) XFree ((char *) kids);
156 XSetErrorHandler (old_handler);
161 disable_builtin_screensaver ()
163 int timeout, interval, prefer_blank, allow_exp;
164 XForceScreenSaver (dpy, ScreenSaverReset);
165 XGetScreenSaver (dpy, &timeout, &interval, &prefer_blank, &allow_exp);
168 XSetScreenSaver (dpy, 0, interval, prefer_blank, allow_exp);
169 printf ("%s%sisabling server builtin screensaver.\n\
170 You can re-enable it with \"xset s on\".\n",
171 (verbose_p ? "" : progname), (verbose_p ? "\n\tD" : ": d"));
176 /* Virtual-root hackery */
179 ERROR! You must not include vroot.h in this file.
184 store_vroot_property (Window win, Window value)
186 store_vroot_property (win, value)
191 printf ("%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", progname,
193 (win == screensaver_window ? "ScreenSaver" :
194 (win == real_vroot ? "VRoot" :
195 (win == real_vroot_value ? "Vroot_value" : "???"))),
197 (value == screensaver_window ? "ScreenSaver" :
198 (value == real_vroot ? "VRoot" :
199 (value == real_vroot_value ? "Vroot_value" : "???"))));
201 XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
202 (unsigned char *) &value, 1);
207 remove_vroot_property (Window win)
209 remove_vroot_property (win)
214 printf ("%s: removing XA_VROOT from 0x%x (%s)\n", progname, win,
215 (win == screensaver_window ? "ScreenSaver" :
216 (win == real_vroot ? "VRoot" :
217 (win == real_vroot_value ? "Vroot_value" : "???"))));
219 XDeleteProperty (dpy, win, XA_VROOT);
224 kill_xsetroot_data P((void))
228 unsigned long nitems, bytesafter;
231 /* If the user has been using xv or xsetroot as a screensaver (to display
232 an image on the screensaver window, as a kind of slideshow) then the
233 pixmap and its associated color cells have been put in RetainPermanent
234 CloseDown mode. Since we're not destroying the xscreensaver window,
235 but merely unmapping it, we need to free these resources or those
236 colormap cells will stay allocated while the screensaver is off. (We
237 could just delete the screensaver window and recreate it later, but
238 that could cause other problems.) This code does an atomic read-and-
239 delete of the _XSETROOT_ID property, and if it held a pixmap, then we
240 cause the RetainPermanent resources of the client which created it
241 (and which no longer exists) to be freed.
243 if (XGetWindowProperty (dpy, screensaver_window, XA_XSETROOT_ID, 0, 1,
244 True, AnyPropertyType, &type, &format, &nitems,
245 &bytesafter, (unsigned char **) &dataP)
249 if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
250 nitems == 1 && bytesafter == 0)
253 printf ("%s: destroying xsetroot data (0x%X).\n",
255 XKillClient (dpy, *dataP);
258 fprintf (stderr, "%s: %sdeleted unrecognised _XSETROOT_ID property: \n\
259 %d, %d; type: %d, format: %d, nitems: %d, bytesafter %d\n",
260 progname, (verbose_p ? "## " : ""),
261 dataP, (dataP ? *dataP : 0), type,
262 format, nitems, bytesafter);
267 static void handle_signals P((Bool on_p));
270 save_real_vroot P((void))
273 Window root = RootWindowOfScreen (screen);
274 Window root2, parent, *kids;
278 real_vroot_value = 0;
279 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
285 for (i = 0; i < nkids; i++)
289 unsigned long nitems, bytesafter;
292 if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
293 &type, &format, &nitems, &bytesafter,
294 (unsigned char **) &vrootP)
301 if (*vrootP == screensaver_window) abort ();
303 "%s: %smore than one virtual root window found (0x%x and 0x%x).\n",
304 progname, (verbose_p ? "## " : ""),
305 (int) real_vroot, (int) kids [i]);
308 real_vroot = kids [i];
309 real_vroot_value = *vrootP;
314 handle_signals (True);
315 remove_vroot_property (real_vroot);
319 XFree ((char *) kids);
323 restore_real_vroot_1 P((void))
325 if (verbose_p && real_vroot)
326 printf ("%s: restoring __SWM_VROOT property on the real vroot (0x%x).\n",
327 progname, real_vroot);
328 remove_vroot_property (screensaver_window);
331 store_vroot_property (real_vroot, real_vroot_value);
333 real_vroot_value = 0;
334 /* make sure the property change gets there before this process
335 terminates! We might be doing this because we have intercepted
336 SIGTERM or something. */
344 restore_real_vroot ()
346 if (restore_real_vroot_1 ())
347 handle_signals (False);
351 /* Signal hackery to ensure that the vroot doesn't get left in an
355 static const char *sig_names [255] = { 0 };
358 restore_real_vroot_handler (sig)
361 signal (sig, SIG_DFL);
362 if (restore_real_vroot_1 ())
363 fprintf (stderr, "\n%s: %s%s (%d) intercepted, vroot restored.\n",
364 progname, (verbose_p ? "## " : ""),
365 ((sig < sizeof(sig_names) && sig >= 0 && sig_names [sig])
366 ? sig_names [sig] : "unknown signal"),
368 kill (getpid (), sig);
374 catch_signal (int sig, char *signame, Bool on_p)
376 catch_signal (sig, signame, on_p)
383 signal (sig, SIG_DFL);
386 sig_names [sig] = signame;
387 if (((int) signal (sig, restore_real_vroot_handler)) == -1)
390 sprintf (buf, "%s: %scouldn't catch %s (%d)", progname,
391 (verbose_p ? "## " : ""), signame, sig);
393 restore_real_vroot ();
400 handle_signals (on_p)
404 if (on_p) printf ("handling signals\n");
405 else printf ("unhandling signals\n");
408 catch_signal (SIGHUP, "SIGHUP", on_p);
409 catch_signal (SIGINT, "SIGINT", on_p);
410 catch_signal (SIGQUIT, "SIGQUIT", on_p);
411 catch_signal (SIGILL, "SIGILL", on_p);
412 catch_signal (SIGTRAP, "SIGTRAP", on_p);
413 catch_signal (SIGIOT, "SIGIOT", on_p);
415 catch_signal (SIGABRT, "SIGABRT", on_p);
418 catch_signal (SIGEMT, "SIGEMT", on_p);
420 catch_signal (SIGFPE, "SIGFPE", on_p);
421 catch_signal (SIGBUS, "SIGBUS", on_p);
422 catch_signal (SIGSEGV, "SIGSEGV", on_p);
424 catch_signal (SIGSYS, "SIGSYS", on_p);
426 catch_signal (SIGTERM, "SIGTERM", on_p);
428 catch_signal (SIGXCPU, "SIGXCPU", on_p);
431 catch_signal (SIGXFSZ, "SIGXFSZ", on_p);
434 catch_signal (SIGDANGER, "SIGDANGER", on_p);
439 /* Managing the actual screensaver window */
442 initialize_screensaver_window P((void))
444 /* This resets the screensaver window as fully as possible, since there's
445 no way of knowing what some random client may have done to us in the
446 meantime. We could just destroy and recreate the window, but that has
447 its own set of problems...
450 XClassHint class_hints;
451 XSetWindowAttributes attrs;
452 unsigned long attrmask;
453 int width = WidthOfScreen (screen);
454 int height = HeightOfScreen (screen);
457 black.red = black.green = black.blue = 0;
459 if (cmap == DefaultColormapOfScreen (screen))
462 if ((install_cmap_p && !demo_mode_p) ||
463 (visual != DefaultVisualOfScreen (screen)))
467 cmap = XCreateColormap (dpy, RootWindowOfScreen (screen),
469 if (! XAllocColor (dpy, cmap, &black)) abort ();
470 black_pixel = black.pixel;
477 XFreeColors (dpy, cmap, &black_pixel, 1, 0);
478 XFreeColormap (dpy, cmap);
480 cmap = DefaultColormapOfScreen (screen);
481 black_pixel = BlackPixelOfScreen (screen);
486 XFreeColormap (dpy, cmap2);
492 cmap2 = copy_colormap (dpy, cmap, 0);
494 fade_p = unfade_p = 0;
497 attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
498 CWBackPixel | CWBackingPixel | CWBorderPixel);
499 attrs.override_redirect = True;
500 attrs.event_mask = (KeyPressMask | KeyReleaseMask |
501 ButtonPressMask | ButtonReleaseMask |
503 attrs.backing_store = NotUseful;
504 attrs.colormap = cmap;
505 attrs.background_pixel = black_pixel;
506 attrs.backing_pixel = black_pixel;
507 attrs.border_pixel = black_pixel;
509 /* if (demo_mode_p || lock_p) width = width / 2; #### */
511 if (screensaver_window)
513 XWindowChanges changes;
514 unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
517 changes.width = width;
518 changes.height = height;
519 changes.border_width = 0;
521 XConfigureWindow (dpy, screensaver_window, changesmask, &changes);
522 XChangeWindowAttributes (dpy, screensaver_window, attrmask, &attrs);
528 else if (visual == DefaultVisualOfScreen (screen))
530 fprintf (stderr, "%s: using default visual ", progname);
531 describe_visual (stderr, dpy, visual);
535 fprintf (stderr, "%s: using visual: ", progname);
536 describe_visual (stderr, dpy, visual);
537 fprintf (stderr, "%s: default visual: ", progname);
538 describe_visual (stderr, dpy, DefaultVisualOfScreen (screen));
542 XCreateWindow (dpy, RootWindowOfScreen (screen), 0, 0, width, height,
543 0, visual_depth, InputOutput, visual, attrmask,
547 class_hints.res_name = progname;
548 class_hints.res_class = progclass;
549 XSetClassHint (dpy, screensaver_window, &class_hints);
550 XStoreName (dpy, screensaver_window, "screensaver");
551 XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_VERSION,
552 XA_STRING, 8, PropModeReplace,
553 (unsigned char *) screensaver_version,
554 strlen (screensaver_version));
556 sprintf (id, "%d on host ", getpid ());
557 if (! XmuGetHostname (id + strlen (id), sizeof (id) - strlen (id) - 1))
559 XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_ID, XA_STRING, 8,
560 PropModeReplace, (unsigned char *) id, strlen (id));
565 bit = XCreatePixmapFromBitmapData (dpy, screensaver_window, "\000", 1, 1,
566 BlackPixelOfScreen (screen),
567 BlackPixelOfScreen (screen), 1);
568 cursor = XCreatePixmapCursor (dpy, bit, bit, &black, &black, 0, 0);
569 XFreePixmap (dpy, bit);
572 XSetWindowBackground (dpy, screensaver_window, black_pixel);
574 XDefineCursor (dpy, screensaver_window, cursor);
576 XUndefineCursor (dpy, screensaver_window);
581 raise_window (inhibit_fade, between_hacks_p)
582 Bool inhibit_fade, between_hacks_p;
584 initialize_screensaver_window ();
586 if (fade_p && !inhibit_fade && !demo_mode_p)
589 Colormap current_map = (between_hacks_p
591 : DefaultColormapOfScreen (screen));
592 copy_colormap (dpy, current_map, cmap2);
594 /* grab and blacken mouse on the root window (saver not mapped yet) */
595 grabbed = grab_mouse (RootWindowOfScreen (screen));
596 /* fade what's on the screen to black */
597 XInstallColormap (dpy, cmap2);
598 fade_colormap (dpy, current_map, cmap2, fade_seconds, fade_ticks, True);
599 XClearWindow (dpy, screensaver_window);
600 XMapRaised (dpy, screensaver_window);
601 /* Once the saver window is up, restore the colormap.
602 (The "black" pixels of the two colormaps are compatible.) */
603 XInstallColormap (dpy, cmap);
604 if (grabbed == GrabSuccess)
605 XUngrabPointer (dpy, CurrentTime);
610 XClearWindow (dpy, screensaver_window);
611 XMapRaised (dpy, screensaver_window);
614 if (install_cmap_p && !demo_mode_p)
615 XInstallColormap (dpy, cmap);
622 store_vroot_property (screensaver_window, screensaver_window);
623 raise_window (False, False);
624 grab_keyboard_and_mouse ();
627 XHPDisableReset (dpy); /* turn off C-Sh-Reset */
634 if (unfade_p && !demo_mode_p)
637 Colormap default_map = DefaultColormapOfScreen (screen);
638 blacken_colormap (dpy, cmap2);
640 /* grab and blacken mouse on the root window. */
641 grabbed = grab_mouse (RootWindowOfScreen (screen));
642 XInstallColormap (dpy, cmap2);
643 XUnmapWindow (dpy, screensaver_window);
644 fade_colormap (dpy, default_map, cmap2, fade_seconds, fade_ticks, False);
645 XInstallColormap (dpy, default_map);
646 if (grabbed == GrabSuccess)
647 XUngrabPointer (dpy, CurrentTime);
652 if (install_cmap_p && !demo_mode_p)
654 XClearWindow (dpy, screensaver_window); /* avoid technicolor */
655 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
657 XUnmapWindow (dpy, screensaver_window);
659 kill_xsetroot_data ();
660 ungrab_keyboard_and_mouse ();
661 restore_real_vroot ();
664 XHPEnableReset (dpy); /* turn C-Sh-Reset back on */