1 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
2 * xscreensaver, Copyright (c) 1991-2001 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
18 # include <unixlib.h> /* for getpid() */
19 # include "vms-gtod.h" /* for gettimeofday() */
23 # include <pwd.h> /* for getpwuid() */
29 # include <sys/utsname.h> /* for uname() */
30 #endif /* HAVE_UNAME */
33 #include <X11/Xproto.h> /* for CARD32 */
35 #include <X11/Xutil.h> /* for XSetClassHint() */
36 #include <X11/Xatom.h>
37 #include <X11/Xos.h> /* for time() */
38 #include <signal.h> /* for the signal names */
40 #ifdef HAVE_MIT_SAVER_EXTENSION
41 # include <X11/extensions/scrnsaver.h>
42 #endif /* HAVE_MIT_SAVER_EXTENSION */
45 # include <X11/extensions/xf86vmode.h>
46 #endif /* HAVE_XF86VMODE */
49 /* This file doesn't need the Xt headers, so stub these types out... */
51 #define XtAppContext void*
52 #define XrmDatabase void*
53 #define XtIntervalId void*
54 #define XtPointer void*
57 #include "xscreensaver.h"
62 extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
64 Atom XA_VROOT, XA_XSETROOT_ID;
65 Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
66 Atom XA_SCREENSAVER_STATUS;
69 extern saver_info *global_si_kludge; /* I hate C so much... */
71 static void maybe_transfer_grabs (saver_screen_info *ssi,
72 Window old_w, Window new_w);
74 #define ALL_POINTER_EVENTS \
75 (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
76 LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
77 Button1MotionMask | Button2MotionMask | Button3MotionMask | \
78 Button4MotionMask | Button5MotionMask | ButtonMotionMask)
82 grab_string(int status)
86 case GrabSuccess: return "GrabSuccess";
87 case AlreadyGrabbed: return "AlreadyGrabbed";
88 case GrabInvalidTime: return "GrabInvalidTime";
89 case GrabNotViewable: return "GrabNotViewable";
90 case GrabFrozen: return "GrabFrozen";
94 sprintf(foo, "unknown status: %d", status);
101 grab_kbd(saver_info *si, Window w)
103 saver_preferences *p = &si->prefs;
104 int status = XGrabKeyboard (si->dpy, w, True,
105 /* I don't really understand Sync vs Async,
106 but these seem to work... */
107 GrabModeSync, GrabModeAsync,
109 if (status == GrabSuccess)
110 si->keyboard_grab_window = w;
113 fprintf(stderr, "%s: grabbing keyboard on 0x%x... %s.\n",
114 blurb(), (unsigned long) w, grab_string(status));
120 grab_mouse (saver_info *si, Window w, Cursor cursor)
122 saver_preferences *p = &si->prefs;
123 int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS,
124 GrabModeAsync, GrabModeAsync, w,
125 cursor, CurrentTime);
126 if (status == GrabSuccess)
127 si->mouse_grab_window = w;
130 fprintf(stderr, "%s: grabbing mouse on 0x%x... %s.\n",
131 blurb(), (unsigned long) w, grab_string(status));
137 ungrab_kbd(saver_info *si)
139 saver_preferences *p = &si->prefs;
140 XUngrabKeyboard(si->dpy, CurrentTime);
142 fprintf(stderr, "%s: ungrabbing keyboard (was 0x%x).\n", blurb(),
143 (unsigned long) si->keyboard_grab_window);
144 si->keyboard_grab_window = 0;
149 ungrab_mouse(saver_info *si)
151 saver_preferences *p = &si->prefs;
152 XUngrabPointer(si->dpy, CurrentTime);
154 fprintf(stderr, "%s: ungrabbing mouse (was 0x%x).\n", blurb(),
155 (unsigned long) si->mouse_grab_window);
156 si->mouse_grab_window = 0;
161 grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
163 Status mstatus, kstatus;
167 for (i = 0; i < retries; i++)
169 XSync (si->dpy, False);
170 kstatus = grab_kbd (si, window);
171 if (kstatus == GrabSuccess)
174 /* else, wait a second and try to grab again. */
178 if (kstatus != GrabSuccess)
179 fprintf (stderr, "%s: couldn't grab keyboard! (%s)\n",
180 blurb(), grab_string(kstatus));
182 for (i = 0; i < retries; i++)
184 XSync (si->dpy, False);
185 mstatus = grab_mouse (si, window, cursor);
186 if (mstatus == GrabSuccess)
189 /* else, wait a second and try to grab again. */
193 if (mstatus != GrabSuccess)
194 fprintf (stderr, "%s: couldn't grab pointer! (%s)\n",
195 blurb(), grab_string(mstatus));
197 return (kstatus == GrabSuccess ||
198 mstatus == GrabSuccess);
202 ungrab_keyboard_and_mouse (saver_info *si)
210 move_mouse_grab (saver_info *si, Window to, Cursor cursor)
212 Window old = si->mouse_grab_window;
215 return grab_mouse (si, to, cursor);
218 saver_preferences *p = &si->prefs;
221 XSync (si->dpy, False);
222 XGrabServer (si->dpy); /* ############ DANGER! */
223 XSync (si->dpy, False);
226 fprintf(stderr, "%s: grabbing server...\n", blurb());
229 status = grab_mouse (si, to, cursor);
231 if (status != GrabSuccess) /* Augh! */
233 sleep (1); /* Note dramatic evil of sleeping
234 with server grabbed. */
235 XSync (si->dpy, False);
236 status = grab_mouse (si, to, cursor);
239 if (status != GrabSuccess) /* Augh! Try to get the old one back... */
240 grab_mouse (si, to, cursor);
242 XUngrabServer (si->dpy);
243 XSync (si->dpy, False); /* ###### (danger over) */
246 fprintf(stderr, "%s: ungrabbing server.\n", blurb());
253 /* Prints an error message to stderr and returns True if there is another
254 xscreensaver running already. Silently returns False otherwise. */
256 ensure_no_screensaver_running (Display *dpy, Screen *screen)
260 Window root = RootWindowOfScreen (screen);
261 Window root2, parent, *kids;
263 XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
265 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
271 for (i = 0; i < nkids; i++)
275 unsigned long nitems, bytesafter;
278 if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
279 False, XA_STRING, &type, &format, &nitems,
280 &bytesafter, (unsigned char **) &version)
285 if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
286 False, XA_STRING, &type, &format, &nitems,
287 &bytesafter, (unsigned char **) &id)
293 "%s: already running on display %s (window 0x%x)\n from process %s.\n",
294 blurb(), DisplayString (dpy), (int) kids [i], id);
299 if (kids) XFree ((char *) kids);
301 XSetErrorHandler (old_handler);
307 /* Virtual-root hackery */
310 ERROR! You must not include vroot.h in this file.
314 store_vroot_property (Display *dpy, Window win, Window value)
319 "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(),
321 (win == screensaver_window ? "ScreenSaver" :
322 (win == real_vroot ? "VRoot" :
323 (win == real_vroot_value ? "Vroot_value" : "???"))),
325 (value == screensaver_window ? "ScreenSaver" :
326 (value == real_vroot ? "VRoot" :
327 (value == real_vroot_value ? "Vroot_value" : "???"))));
329 XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
330 (unsigned char *) &value, 1);
334 remove_vroot_property (Display *dpy, Window win)
338 fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win,
339 (win == screensaver_window ? "ScreenSaver" :
340 (win == real_vroot ? "VRoot" :
341 (win == real_vroot_value ? "Vroot_value" : "???"))));
343 XDeleteProperty (dpy, win, XA_VROOT);
348 kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
352 unsigned long nitems, bytesafter;
355 /* If the user has been using xv or xsetroot as a screensaver (to display
356 an image on the screensaver window, as a kind of slideshow) then the
357 pixmap and its associated color cells have been put in RetainPermanent
358 CloseDown mode. Since we're not destroying the xscreensaver window,
359 but merely unmapping it, we need to free these resources or those
360 colormap cells will stay allocated while the screensaver is off. (We
361 could just delete the screensaver window and recreate it later, but
362 that could cause other problems.) This code does an atomic read-and-
363 delete of the _XSETROOT_ID property, and if it held a pixmap, then we
364 cause the RetainPermanent resources of the client which created it
365 (and which no longer exists) to be freed.
367 if (XGetWindowProperty (dpy, window, XA_XSETROOT_ID, 0, 1,
368 True, AnyPropertyType, &type, &format, &nitems,
369 &bytesafter, (unsigned char **) &dataP)
373 if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
374 nitems == 1 && bytesafter == 0)
377 fprintf (stderr, "%s: destroying xsetroot data (0x%lX).\n",
379 XKillClient (dpy, *dataP);
382 fprintf (stderr, "%s: deleted unrecognised _XSETROOT_ID property: \n\
383 %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
384 blurb(), (unsigned long) dataP, (dataP ? *dataP : 0), type,
385 format, nitems, bytesafter);
390 static void handle_signals (saver_info *si, Bool on_p);
393 save_real_vroot (saver_screen_info *ssi)
395 saver_info *si = ssi->global;
396 Display *dpy = si->dpy;
397 Screen *screen = ssi->screen;
399 Window root = RootWindowOfScreen (screen);
400 Window root2, parent, *kids;
402 XErrorHandler old_handler;
404 /* It's possible that a window might be deleted between our call to
405 XQueryTree() and our call to XGetWindowProperty(). Don't die if
406 that happens (but just ignore that window, it's not the one we're
407 interested in anyway.)
410 old_handler = XSetErrorHandler (BadWindow_ehandler);
414 ssi->real_vroot_value = 0;
415 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
421 for (i = 0; i < nkids; i++)
425 unsigned long nitems, bytesafter;
428 if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
429 &type, &format, &nitems, &bytesafter,
430 (unsigned char **) &vrootP)
437 if (*vrootP == ssi->screensaver_window) abort ();
439 "%s: more than one virtual root window found (0x%x and 0x%x).\n",
440 blurb(), (int) ssi->real_vroot, (int) kids [i]);
443 ssi->real_vroot = kids [i];
444 ssi->real_vroot_value = *vrootP;
448 XSetErrorHandler (old_handler);
453 handle_signals (si, True);
454 remove_vroot_property (si->dpy, ssi->real_vroot);
458 XFree ((char *) kids);
463 restore_real_vroot_2 (saver_screen_info *ssi)
465 saver_info *si = ssi->global;
466 saver_preferences *p = &si->prefs;
467 if (p->verbose_p && ssi->real_vroot)
469 "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
470 blurb(), (unsigned long) ssi->real_vroot);
471 remove_vroot_property (si->dpy, ssi->screensaver_window);
474 store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value);
476 ssi->real_vroot_value = 0;
477 /* make sure the property change gets there before this process
478 terminates! We might be doing this because we have intercepted
479 SIGTERM or something. */
480 XSync (si->dpy, False);
487 restore_real_vroot_1 (saver_info *si)
490 Bool did_any = False;
491 for (i = 0; i < si->nscreens; i++)
493 saver_screen_info *ssi = &si->screens[i];
494 if (restore_real_vroot_2 (ssi))
501 restore_real_vroot (saver_info *si)
503 if (restore_real_vroot_1 (si))
504 handle_signals (si, False);
508 /* Signal hackery to ensure that the vroot doesn't get left in an
513 signal_name(int signal)
516 case SIGHUP: return "SIGHUP";
517 case SIGINT: return "SIGINT";
518 case SIGQUIT: return "SIGQUIT";
519 case SIGILL: return "SIGILL";
520 case SIGTRAP: return "SIGTRAP";
522 case SIGABRT: return "SIGABRT";
524 case SIGFPE: return "SIGFPE";
525 case SIGKILL: return "SIGKILL";
526 case SIGBUS: return "SIGBUS";
527 case SIGSEGV: return "SIGSEGV";
528 case SIGPIPE: return "SIGPIPE";
529 case SIGALRM: return "SIGALRM";
530 case SIGTERM: return "SIGTERM";
532 case SIGSTOP: return "SIGSTOP";
535 case SIGCONT: return "SIGCONT";
538 case SIGUSR1: return "SIGUSR1";
541 case SIGUSR2: return "SIGUSR2";
544 case SIGEMT: return "SIGEMT";
547 case SIGSYS: return "SIGSYS";
550 case SIGCHLD: return "SIGCHLD";
553 case SIGPWR: return "SIGPWR";
556 case SIGWINCH: return "SIGWINCH";
559 case SIGURG: return "SIGURG";
562 case SIGIO: return "SIGIO";
565 case SIGVTALRM: return "SIGVTALRM";
568 case SIGXCPU: return "SIGXCPU";
571 case SIGXFSZ: return "SIGXFSZ";
574 case SIGDANGER: return "SIGDANGER";
579 sprintf(buf, "signal %d\n", signal);
588 restore_real_vroot_handler (int sig)
590 saver_info *si = global_si_kludge; /* I hate C so much... */
592 signal (sig, SIG_DFL);
593 if (restore_real_vroot_1 (si))
594 fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
595 blurb(), signal_name(sig));
596 kill (getpid (), sig);
600 catch_signal (saver_info *si, int sig, Bool on_p)
603 signal (sig, SIG_DFL);
606 if (((long) signal (sig, restore_real_vroot_handler)) == -1L)
609 sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
611 saver_exit (si, 1, 0);
617 handle_signals (saver_info *si, Bool on_p)
620 if (on_p) fprintf (stderr, "handling signals\n");
621 else fprintf (stderr, "unhandling signals\n");
624 catch_signal (si, SIGHUP, on_p);
625 catch_signal (si, SIGINT, on_p);
626 catch_signal (si, SIGQUIT, on_p);
627 catch_signal (si, SIGILL, on_p);
628 catch_signal (si, SIGTRAP, on_p);
630 catch_signal (si, SIGIOT, on_p);
632 catch_signal (si, SIGABRT, on_p);
634 catch_signal (si, SIGEMT, on_p);
636 catch_signal (si, SIGFPE, on_p);
637 catch_signal (si, SIGBUS, on_p);
638 catch_signal (si, SIGSEGV, on_p);
640 catch_signal (si, SIGSYS, on_p);
642 catch_signal (si, SIGTERM, on_p);
644 catch_signal (si, SIGXCPU, on_p);
647 catch_signal (si, SIGXFSZ, on_p);
650 catch_signal (si, SIGDANGER, on_p);
655 saver_exit (saver_info *si, int status, const char *dump_core_reason)
657 saver_preferences *p = &si->prefs;
658 static Bool exiting = False;
667 vrs = restore_real_vroot_1 (si);
668 emergency_kill_subproc (si);
669 shutdown_stderr (si);
671 if (p->verbose_p && vrs)
672 fprintf (real_stderr, "%s: old vroot restored.\n", blurb());
676 #ifdef VMS /* on VMS, 1 is the "normal" exit code instead of 0. */
677 if (status == 0) status = 1;
678 else if (status == 1) status = -1;
681 bugp = !!dump_core_reason;
683 if (si->prefs.debug_p && !dump_core_reason)
684 dump_core_reason = "because of -debug";
686 if (dump_core_reason)
688 /* Note that the Linux man page for setuid() says If uid is
689 different from the old effective uid, the process will be
690 forbidden from leaving core dumps.
692 char cwd[4096]; /* should really be PATH_MAX, but who cares. */
694 fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
699 "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
700 "\t\tfor bug reporting information.\n\n",
703 # if defined(HAVE_GETCWD)
704 if (!getcwd (cwd, sizeof(cwd)))
705 # elif defined(HAVE_GETWD)
708 strcpy(cwd, "unknown.");
710 fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
711 describe_uids (si, real_stderr);
713 /* Do this to drop a core file, so that we can get a stack trace. */
721 /* Managing the actual screensaver window */
724 window_exists_p (Display *dpy, Window window)
726 XErrorHandler old_handler;
727 XWindowAttributes xgwa;
729 old_handler = XSetErrorHandler (BadWindow_ehandler);
730 XGetWindowAttributes (dpy, window, &xgwa);
732 XSetErrorHandler (old_handler);
733 return (xgwa.screen != 0);
737 store_saver_id (saver_screen_info *ssi)
739 XClassHint class_hints;
740 saver_info *si = ssi->global;
741 unsigned long pid = (unsigned long) getpid ();
743 struct passwd *p = getpwuid (getuid ());
744 const char *name, *host;
747 /* First store the name and class on the window.
749 class_hints.res_name = progname;
750 class_hints.res_class = progclass;
751 XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
752 XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
754 /* Then store the xscreensaver version number.
756 XChangeProperty (si->dpy, ssi->screensaver_window,
757 XA_SCREENSAVER_VERSION,
758 XA_STRING, 8, PropModeReplace,
759 (unsigned char *) si->version,
760 strlen (si->version));
762 /* Now store the XSCREENSAVER_ID property, that says what user and host
763 xscreensaver is running as.
766 if (p && p->pw_name && *p->pw_name)
770 sprintf (buf, "%lu", (unsigned long) p->pw_uid);
776 # if defined(HAVE_UNAME)
779 if (uname (&uts) < 0)
785 host = getenv("SYS$NODE");
786 # else /* !HAVE_UNAME && !VMS */
788 # endif /* !HAVE_UNAME && !VMS */
790 id = (char *) malloc (strlen(name) + strlen(host) + 50);
791 sprintf (id, "%lu (%s@%s)", pid, name, host);
793 XChangeProperty (si->dpy, ssi->screensaver_window,
794 XA_SCREENSAVER_ID, XA_STRING,
796 (unsigned char *) id, strlen (id));
802 store_saver_status (saver_info *si)
805 int size = si->nscreens + 2;
808 status = (CARD32 *) calloc (size, sizeof(CARD32));
810 status[0] = (CARD32) (si->screen_blanked_p
811 ? (si->locked_p ? XA_LOCK : XA_BLANK)
813 status[1] = (CARD32) si->blank_time;
815 for (i = 0; i < si->nscreens; i++)
817 saver_screen_info *ssi = &si->screens[i];
818 status [2 + i] = ssi->current_hack + 1;
821 XChangeProperty (si->dpy,
822 RootWindow (si->dpy, 0), /* always screen #0 */
823 XA_SCREENSAVER_STATUS,
824 XA_INTEGER, 32, PropModeReplace,
825 (unsigned char *) status, size);
830 /* Returns the area of the screen which the xscreensaver window should cover.
831 Normally this is the whole screen, but if the X server's root window is
832 actually larger than the monitor's displayable area, then we want to
833 operate in the currently-visible portion of the desktop instead.
836 get_screen_viewport (saver_screen_info *ssi,
837 int *x_ret, int *y_ret,
838 int *w_ret, int *h_ret,
841 int w = WidthOfScreen (ssi->screen);
842 int h = HeightOfScreen (ssi->screen);
844 #ifdef HAVE_XF86VMODE
845 saver_info *si = ssi->global;
846 int screen_no = screen_number (ssi->screen);
847 int op, event, error;
849 XF86VidModeModeLine ml;
852 /* Check for Xinerama first, because the VidModeExtension is broken
853 when Xinerama is present. Wheee!
856 if (!XQueryExtension (si->dpy, "XINERAMA", &op, &event, &error) &&
857 XF86VidModeQueryExtension (si->dpy, &event, &error) &&
858 XF86VidModeGetModeLine (si->dpy, screen_no, &dot, &ml) &&
859 XF86VidModeGetViewPort (si->dpy, screen_no, &x, &y))
864 *w_ret = ml.hdisplay;
865 *h_ret = ml.vdisplay;
867 if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
868 /* There is no viewport -- the screen does not scroll. */
872 /* Apparently some versions of XFree86 return nonsense here!
873 I've had reports of 1024x768 viewports at -1936862040, -1953705044.
874 So, sanity-check the values and give up if they are out of range.
876 if (*x_ret < 0 || *x_ret >= w ||
877 *y_ret < 0 || *y_ret >= h ||
878 *w_ret <= 0 || *w_ret > w ||
879 *h_ret <= 0 || *h_ret > h)
881 static int warned_once = 0;
884 fprintf (stderr, "\n"
885 "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
886 "%s: The XVidMode server extension is returning nonsense.\n"
887 "%s: Please report this bug to your X server vendor.\n\n",
888 blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
900 sprintf (msg, "%s: vp is %dx%d+%d+%d",
901 blurb(), *w_ret, *h_ret, *x_ret, *y_ret);
904 /* Apparently, though the server stores the X position in increments of
905 1 pixel, it will only make changes to the *display* in some other
906 increment. With XF86_SVGA on a Thinkpad, the display only updates
907 in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
908 pixels in 16-bit mode. I don't know what it does in 24- and 32-bit
909 mode, because I don't have enough video memory to find out.
911 I consider it a bug that XF86VidModeGetViewPort() is telling me the
912 server's *target* scroll position rather than the server's *actual*
913 scroll position. David Dawes agrees, and says they may fix this in
914 XFree86 4.0, but it's notrivial.
916 He also confirms that this behavior is server-dependent, so the
917 actual scroll position cannot be reliably determined by the client.
918 So... that means the only solution is to provide a ``sandbox''
919 around the blackout window -- we make the window be up to N pixels
920 larger than the viewport on both the left and right sides. That
921 means some part of the outer edges of each hack might not be
922 visible, but screw it.
924 I'm going to guess that 16 pixels is enough, and that the Y dimension
925 doesn't have this problem.
927 The drawback of doing this, of course, is that some of the screenhacks
928 will still look pretty stupid -- for example, "slidescreen" will cut
929 off the left and right edges of the grid, etc.
932 if (x > 0 && x < w - ml.hdisplay) /* not at left edge or right edge */
934 /* Round X position down to next lower multiple of FUDGE.
935 Increase width by 2*FUDGE in case some server rounds up.
937 *x_ret = ((x - 1) / FUDGE) * FUDGE;
938 *w_ret += (FUDGE * 2);
944 *w_ret != ml.hdisplay ||
945 *h_ret != ml.vdisplay)
946 sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
947 *w_ret, *h_ret, *x_ret, *y_ret);
950 fprintf (stderr, "%s.\n", msg);
955 #endif /* HAVE_XF86VMODE */
964 static Bool error_handler_hit_p = False;
967 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
969 error_handler_hit_p = True;
974 /* Returns True if successful, False if an X error occurred.
975 We need this because other programs might have done things to
976 our window that will cause XChangeWindowAttributes() to fail:
977 if that happens, we give up, destroy the window, and re-create
981 safe_XChangeWindowAttributes (Display *dpy, Window window,
983 XSetWindowAttributes *attrs)
985 XErrorHandler old_handler;
987 error_handler_hit_p = False;
988 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
990 XChangeWindowAttributes (dpy, window, mask, attrs);
993 XSetErrorHandler (old_handler);
996 return (!error_handler_hit_p);
1000 /* This might not be necessary, but just in case. */
1002 safe_XConfigureWindow (Display *dpy, Window window,
1003 unsigned long mask, XWindowChanges *changes)
1005 XErrorHandler old_handler;
1007 error_handler_hit_p = False;
1008 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1010 XConfigureWindow (dpy, window, mask, changes);
1013 XSetErrorHandler (old_handler);
1016 return (!error_handler_hit_p);
1019 /* This might not be necessary, but just in case. */
1021 safe_XDestroyWindow (Display *dpy, Window window)
1023 XErrorHandler old_handler;
1025 error_handler_hit_p = False;
1026 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1028 XDestroyWindow (dpy, window);
1031 XSetErrorHandler (old_handler);
1034 return (!error_handler_hit_p);
1039 initialize_screensaver_window_1 (saver_screen_info *ssi)
1041 saver_info *si = ssi->global;
1042 saver_preferences *p = &si->prefs;
1043 Bool install_cmap_p = ssi->install_cmap_p; /* not p->install_cmap_p */
1045 /* This resets the screensaver window as fully as possible, since there's
1046 no way of knowing what some random client may have done to us in the
1047 meantime. We could just destroy and recreate the window, but that has
1048 its own set of problems...
1051 XSetWindowAttributes attrs;
1052 unsigned long attrmask;
1053 int x, y, width, height;
1054 static Bool printed_visual_info = False; /* only print the message once. */
1055 Window horked_window = 0;
1057 get_screen_viewport (ssi, &x, &y, &width, &height,
1058 (p->verbose_p && !si->screen_blanked_p));
1060 black.red = black.green = black.blue = 0;
1062 if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
1065 if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
1066 /* It's not the default visual, so we have no choice but to install. */
1067 install_cmap_p = True;
1073 ssi->cmap = XCreateColormap (si->dpy,
1074 RootWindowOfScreen (ssi->screen),
1075 ssi->current_visual, AllocNone);
1076 if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
1077 ssi->black_pixel = black.pixel;
1082 Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
1085 XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
1086 if (ssi->cmap != ssi->demo_cmap &&
1087 ssi->cmap != def_cmap)
1088 XFreeColormap (si->dpy, ssi->cmap);
1090 ssi->cmap = def_cmap;
1091 ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
1094 attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
1095 CWBackPixel | CWBackingPixel | CWBorderPixel);
1096 attrs.override_redirect = True;
1098 /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
1099 actually be reading these events during normal operation; but we still
1100 need to see Button events for demo-mode to work properly.
1102 attrs.event_mask = (KeyPressMask | KeyReleaseMask |
1103 ButtonPressMask | ButtonReleaseMask |
1106 attrs.backing_store = NotUseful;
1107 attrs.colormap = ssi->cmap;
1108 attrs.background_pixel = ssi->black_pixel;
1109 attrs.backing_pixel = ssi->black_pixel;
1110 attrs.border_pixel = ssi->black_pixel;
1112 if (p->debug_p) width = width / 2;
1114 if (!p->verbose_p || printed_visual_info)
1116 else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
1118 fprintf (stderr, "%s: using default visual ", blurb());
1119 describe_visual (stderr, ssi->screen, ssi->current_visual,
1124 fprintf (stderr, "%s: using visual: ", blurb());
1125 describe_visual (stderr, ssi->screen, ssi->current_visual,
1127 fprintf (stderr, "%s: default visual: ", blurb());
1128 describe_visual (stderr, ssi->screen,
1129 DefaultVisualOfScreen (ssi->screen),
1130 ssi->install_cmap_p);
1132 printed_visual_info = True;
1134 #ifdef HAVE_MIT_SAVER_EXTENSION
1135 if (si->using_mit_saver_extension)
1137 XScreenSaverInfo *info;
1138 Window root = RootWindowOfScreen (ssi->screen);
1141 /* This call sets the server screensaver timeouts to what we think
1142 they should be (based on the resources and args xscreensaver was
1143 started with.) It's important that we do this to sync back up
1144 with the server - if we have turned on prematurely, as by an
1145 ACTIVATE ClientMessage, then the server may decide to activate
1146 the screensaver while it's already active. That's ok for us,
1147 since we would know to ignore that ScreenSaverActivate event,
1148 but a side effect of this would be that the server would map its
1149 saver window (which we then hide again right away) meaning that
1150 the bits currently on the screen get blown away. Ugly. */
1152 /* #### Ok, that doesn't work - when we tell the server that the
1153 screensaver is "off" it sends us a Deactivate event, which is
1154 sensible... but causes the saver to never come on. Hmm. */
1155 disable_builtin_screensaver (si, True);
1159 /* #### The MIT-SCREEN-SAVER extension gives us access to the
1160 window that the server itself uses for saving the screen.
1161 However, using this window in any way, in particular, calling
1162 XScreenSaverSetAttributes() as below, tends to make the X server
1163 crash. So fuck it, let's try and get along without using it...
1165 It's also inconvenient to use this window because it doesn't
1166 always exist (though the ID is constant.) So to use this
1167 window, we'd have to reimplement the ACTIVATE ClientMessage to
1168 tell the *server* to tell *us* to turn on, to cause the window
1169 to get created at the right time. Gag. */
1170 XScreenSaverSetAttributes (si->dpy, root,
1171 0, 0, width, height, 0,
1172 current_depth, InputOutput, visual,
1174 XSync (si->dpy, False);
1177 info = XScreenSaverAllocInfo ();
1178 XScreenSaverQueryInfo (si->dpy, root, info);
1179 ssi->server_mit_saver_window = info->window;
1180 if (! ssi->server_mit_saver_window) abort ();
1183 #endif /* HAVE_MIT_SAVER_EXTENSION */
1185 if (ssi->screensaver_window)
1187 XWindowChanges changes;
1188 unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1191 changes.width = width;
1192 changes.height = height;
1193 changes.border_width = 0;
1195 if (! (safe_XConfigureWindow (si->dpy, ssi->screensaver_window,
1196 changesmask, &changes) &&
1197 safe_XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
1200 horked_window = ssi->screensaver_window;
1201 ssi->screensaver_window = 0;
1205 if (!ssi->screensaver_window)
1207 ssi->screensaver_window =
1208 XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
1209 x, y, width, height,
1210 0, ssi->current_depth, InputOutput,
1211 ssi->current_visual, attrmask, &attrs);
1218 "%s: someone horked our saver window (0x%lx)! Recreating it...\n",
1219 blurb(), (unsigned long) horked_window);
1220 maybe_transfer_grabs (ssi, horked_window, ssi->screensaver_window);
1221 safe_XDestroyWindow (si->dpy, horked_window);
1226 fprintf (stderr, "%s: saver window is 0x%lx.\n",
1227 blurb(), (unsigned long) ssi->screensaver_window);
1230 store_saver_id (ssi); /* store window name and IDs */
1235 bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
1237 BlackPixelOfScreen (ssi->screen),
1238 BlackPixelOfScreen (ssi->screen),
1240 ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
1242 XFreePixmap (si->dpy, bit);
1245 XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
1248 XUndefineCursor (si->dpy, ssi->screensaver_window);
1250 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1254 initialize_screensaver_window (saver_info *si)
1257 for (i = 0; i < si->nscreens; i++)
1258 initialize_screensaver_window_1 (&si->screens[i]);
1263 raise_window (saver_info *si,
1264 Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
1266 saver_preferences *p = &si->prefs;
1270 inhibit_fade = True;
1272 if (si->emergency_lock_p)
1273 inhibit_fade = True;
1276 initialize_screensaver_window (si);
1278 reset_watchdog_timer (si, True);
1280 if (p->fade_p && si->fading_possible_p && !inhibit_fade)
1282 Window *current_windows = (Window *)
1283 calloc(sizeof(Window), si->nscreens);
1284 Colormap *current_maps = (Colormap *)
1285 calloc(sizeof(Colormap), si->nscreens);
1287 for (i = 0; i < si->nscreens; i++)
1289 saver_screen_info *ssi = &si->screens[i];
1290 current_windows[i] = ssi->screensaver_window;
1291 current_maps[i] = (between_hacks_p
1293 : DefaultColormapOfScreen (ssi->screen));
1294 /* Ensure that the default background of the window is really black,
1295 not a pixmap or something. (This does not clear the window.) */
1296 XSetWindowBackground (si->dpy, ssi->screensaver_window,
1300 if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1302 XGrabServer (si->dpy); /* ############ DANGER! */
1304 /* Clear the stderr layer on each screen.
1307 for (i = 0; i < si->nscreens; i++)
1309 saver_screen_info *ssi = &si->screens[i];
1310 if (ssi->stderr_overlay_window)
1311 /* Do this before the fade, since the stderr cmap won't fade
1312 even if we uninstall it (beats me...) */
1316 /* Note! The server is grabbed, and this will take several seconds
1318 fade_screens (si->dpy, current_maps, current_windows,
1319 p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1322 free(current_windows);
1324 current_windows = 0;
1326 if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1328 #ifdef HAVE_MIT_SAVER_EXTENSION
1329 for (i = 0; i < si->nscreens; i++)
1331 saver_screen_info *ssi = &si->screens[i];
1332 if (ssi->server_mit_saver_window &&
1333 window_exists_p (si->dpy, ssi->server_mit_saver_window))
1334 XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1336 #endif /* HAVE_MIT_SAVER_EXTENSION */
1338 XUngrabServer (si->dpy);
1339 XSync (si->dpy, False); /* ###### (danger over) */
1343 for (i = 0; i < si->nscreens; i++)
1345 saver_screen_info *ssi = &si->screens[i];
1347 XClearWindow (si->dpy, ssi->screensaver_window);
1348 if (!dont_clear || ssi->stderr_overlay_window)
1350 XMapRaised (si->dpy, ssi->screensaver_window);
1351 #ifdef HAVE_MIT_SAVER_EXTENSION
1352 if (ssi->server_mit_saver_window &&
1353 window_exists_p (si->dpy, ssi->server_mit_saver_window))
1354 XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1355 #endif /* HAVE_MIT_SAVER_EXTENSION */
1359 for (i = 0; i < si->nscreens; i++)
1361 saver_screen_info *ssi = &si->screens[i];
1363 XInstallColormap (si->dpy, ssi->cmap);
1368 blank_screen (saver_info *si)
1373 /* Note: we do our grabs on the root window, not on the screensaver window.
1374 If we grabbed on the saver window, then the demo mode and lock dialog
1375 boxes wouldn't get any events.
1377 ok = grab_keyboard_and_mouse (si,
1378 /*si->screens[0].screensaver_window,*/
1379 RootWindowOfScreen(si->screens[0].screen),
1382 : si->screens[0].cursor));
1385 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1386 /* If we're using a server extension, then failure to get a grab is
1387 not a big deal -- even without the grab, we will still be able
1388 to un-blank when there is user activity, since the server will
1395 for (i = 0; i < si->nscreens; i++)
1397 saver_screen_info *ssi = &si->screens[i];
1399 save_real_vroot (ssi);
1400 store_vroot_property (si->dpy,
1401 ssi->screensaver_window,
1402 ssi->screensaver_window);
1404 #ifdef HAVE_XF86VMODE
1407 if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
1408 !XF86VidModeGetViewPort (si->dpy, i,
1411 ssi->blank_vp_x = ssi->blank_vp_y = -1;
1413 #endif /* HAVE_XF86VMODE */
1416 raise_window (si, False, False, False);
1418 si->screen_blanked_p = True;
1419 si->blank_time = time ((time_t) 0);
1420 si->last_wall_clock_time = 0;
1422 store_saver_status (si); /* store blank time */
1429 unblank_screen (saver_info *si)
1431 saver_preferences *p = &si->prefs;
1432 Bool unfade_p = (si->fading_possible_p && p->unfade_p);
1435 monitor_power_on (si);
1436 reset_watchdog_timer (si, False);
1443 Window *current_windows = (Window *)
1444 calloc(sizeof(Window), si->nscreens);
1446 for (i = 0; i < si->nscreens; i++)
1448 saver_screen_info *ssi = &si->screens[i];
1449 current_windows[i] = ssi->screensaver_window;
1450 /* Ensure that the default background of the window is really black,
1451 not a pixmap or something. (This does not clear the window.) */
1452 XSetWindowBackground (si->dpy, ssi->screensaver_window,
1456 if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1459 XSync (si->dpy, False);
1460 XGrabServer (si->dpy); /* ############ DANGER! */
1461 XSync (si->dpy, False);
1463 /* Clear the stderr layer on each screen.
1465 for (i = 0; i < si->nscreens; i++)
1467 saver_screen_info *ssi = &si->screens[i];
1471 XUngrabServer (si->dpy);
1472 XSync (si->dpy, False); /* ###### (danger over) */
1475 fade_screens (si->dpy, 0, current_windows,
1476 p->fade_seconds/1000, p->fade_ticks,
1479 free(current_windows);
1480 current_windows = 0;
1482 if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1486 for (i = 0; i < si->nscreens; i++)
1488 saver_screen_info *ssi = &si->screens[i];
1491 Colormap c = DefaultColormapOfScreen (ssi->screen);
1492 /* avoid technicolor */
1493 XClearWindow (si->dpy, ssi->screensaver_window);
1494 if (c) XInstallColormap (si->dpy, c);
1496 XUnmapWindow (si->dpy, ssi->screensaver_window);
1501 /* If the focus window does has a non-default colormap, then install
1502 that colormap as well. (On SGIs, this will cause both the root map
1503 and the focus map to be installed simultaniously. It'd be nice to
1504 pick up the other colormaps that had been installed, too; perhaps
1505 XListInstalledColormaps could be used for that?)
1510 XGetInputFocus (si->dpy, &focus, &revert_to);
1511 if (focus && focus != PointerRoot && focus != None)
1513 XWindowAttributes xgwa;
1515 XGetWindowAttributes (si->dpy, focus, &xgwa);
1516 if (xgwa.colormap &&
1517 xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1518 XInstallColormap (si->dpy, xgwa.colormap);
1523 for (i = 0; i < si->nscreens; i++)
1525 saver_screen_info *ssi = &si->screens[i];
1526 kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1529 store_saver_status (si); /* store unblank time */
1530 ungrab_keyboard_and_mouse (si);
1531 restore_real_vroot (si);
1533 /* Unmap the windows a second time, dammit -- just to avoid a race
1534 with the screen-grabbing hacks. (I'm not sure if this is really
1535 necessary; I'm stabbing in the dark now.)
1537 for (i = 0; i < si->nscreens; i++)
1538 XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1540 si->screen_blanked_p = False;
1541 si->blank_time = time ((time_t) 0);
1542 si->last_wall_clock_time = 0;
1544 store_saver_status (si); /* store unblank time */
1548 /* Transfer any grabs from the old window to the new.
1549 Actually I think none of this is necessary, since we always
1550 hold our grabs on the root window, but I wrote this before
1551 re-discovering that...
1554 maybe_transfer_grabs (saver_screen_info *ssi,
1555 Window old_w, Window new_w)
1557 saver_info *si = ssi->global;
1559 /* If the old window held our mouse grab, transfer the grab to the new
1560 window. (Grab the server while so doing, to avoid a race condition.)
1562 if (old_w == si->mouse_grab_window)
1564 XGrabServer (si->dpy); /* ############ DANGER! */
1566 grab_mouse (si, ssi->screensaver_window,
1570 XUngrabServer (si->dpy);
1571 XSync (si->dpy, False); /* ###### (danger over) */
1574 /* If the old window held our keyboard grab, transfer the grab to the new
1575 window. (Grab the server while so doing, to avoid a race condition.)
1577 if (old_w == si->keyboard_grab_window)
1579 XGrabServer (si->dpy); /* ############ DANGER! */
1581 grab_kbd(si, ssi->screensaver_window);
1582 XUngrabServer (si->dpy);
1583 XSync (si->dpy, False); /* ###### (danger over) */
1590 select_visual (saver_screen_info *ssi, const char *visual_name)
1592 saver_info *si = ssi->global;
1593 saver_preferences *p = &si->prefs;
1594 Bool install_cmap_p = p->install_cmap_p;
1595 Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1599 if (visual_name && *visual_name)
1601 if (!strcmp(visual_name, "default-i") ||
1602 !strcmp(visual_name, "Default-i") ||
1603 !strcmp(visual_name, "Default-I")
1606 visual_name = "default";
1607 install_cmap_p = True;
1609 else if (!strcmp(visual_name, "default-n") ||
1610 !strcmp(visual_name, "Default-n") ||
1611 !strcmp(visual_name, "Default-N"))
1613 visual_name = "default";
1614 install_cmap_p = False;
1616 else if (!strcmp(visual_name, "gl") ||
1617 !strcmp(visual_name, "Gl") ||
1618 !strcmp(visual_name, "GL"))
1620 new_v = ssi->best_gl_visual;
1621 if (!new_v && p->verbose_p)
1622 fprintf (stderr, "%s: no GL visuals.\n", progname);
1626 new_v = get_visual (ssi->screen, visual_name, True, False);
1630 new_v = ssi->default_visual;
1635 if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1636 /* It's not the default visual, so we have no choice but to install. */
1637 install_cmap_p = True;
1639 ssi->install_cmap_p = install_cmap_p;
1642 ((ssi->current_visual != new_v) ||
1643 (install_cmap_p != was_installed_p)))
1645 Colormap old_c = ssi->cmap;
1646 Window old_w = ssi->screensaver_window;
1650 fprintf (stderr, "%s: switching to visual ", blurb());
1651 describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
1653 fprintf (stderr, "%s: from ", blurb());
1654 describe_visual (stderr, ssi->screen, ssi->current_visual,
1660 ssi->current_visual = new_v;
1661 ssi->current_depth = visual_depth(ssi->screen, new_v);
1663 ssi->screensaver_window = 0;
1665 initialize_screensaver_window_1 (ssi);
1667 /* stderr_overlay_window is a child of screensaver_window, so we need
1668 to destroy that as well (actually, we just need to invalidate and
1669 drop our pointers to it, but this will destroy it, which is ok so
1670 long as it happens before old_w itself is destroyed.) */
1673 raise_window (si, True, True, False);
1674 store_vroot_property (si->dpy,
1675 ssi->screensaver_window, ssi->screensaver_window);
1677 /* Transfer any grabs from the old window to the new. */
1678 maybe_transfer_grabs (ssi, old_w, ssi->screensaver_window);
1680 /* Now we can destroy the old window without horking our grabs. */
1681 XDestroyWindow (si->dpy, old_w);
1684 fprintf (stderr, "%s: destroyed old saver window 0x%lx.\n",
1685 blurb(), (unsigned long) old_w);
1688 old_c != DefaultColormapOfScreen (ssi->screen) &&
1689 old_c != ssi->demo_cmap)
1690 XFreeColormap (si->dpy, old_c);