1 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
2 * xscreensaver, Copyright (c) 1991-1998 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... */
72 #define ALL_POINTER_EVENTS \
73 (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
74 LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
75 Button1MotionMask | Button2MotionMask | Button3MotionMask | \
76 Button4MotionMask | Button5MotionMask | ButtonMotionMask)
80 grab_string(int status)
84 case GrabSuccess: return "GrabSuccess";
85 case AlreadyGrabbed: return "AlreadyGrabbed";
86 case GrabInvalidTime: return "GrabInvalidTime";
87 case GrabNotViewable: return "GrabNotViewable";
88 case GrabFrozen: return "GrabFrozen";
92 sprintf(foo, "unknown status: %d", status);
99 grab_kbd(saver_info *si, Window w)
101 saver_preferences *p = &si->prefs;
102 int status = XGrabKeyboard (si->dpy, w, True,
103 /* I don't really understand Sync vs Async,
104 but these seem to work... */
105 GrabModeSync, GrabModeAsync,
107 if (status == GrabSuccess)
108 si->keyboard_grab_window = w;
111 fprintf(stderr, "%s: grabbing keyboard on 0x%x... %s.\n",
112 blurb(), (unsigned long) w, grab_string(status));
118 grab_mouse (saver_info *si, Window w, Cursor cursor)
120 saver_preferences *p = &si->prefs;
121 int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS,
122 GrabModeAsync, GrabModeAsync, w,
123 cursor, CurrentTime);
124 if (status == GrabSuccess)
125 si->mouse_grab_window = w;
128 fprintf(stderr, "%s: grabbing mouse on 0x%x... %s.\n",
129 blurb(), (unsigned long) w, grab_string(status));
135 ungrab_kbd(saver_info *si)
137 saver_preferences *p = &si->prefs;
138 XUngrabKeyboard(si->dpy, CurrentTime);
140 fprintf(stderr, "%s: ungrabbing keyboard (was 0x%x).\n", blurb(),
141 (unsigned long) si->keyboard_grab_window);
142 si->keyboard_grab_window = 0;
147 ungrab_mouse(saver_info *si)
149 saver_preferences *p = &si->prefs;
150 XUngrabPointer(si->dpy, CurrentTime);
152 fprintf(stderr, "%s: ungrabbing mouse (was 0x%x).\n", blurb(),
153 (unsigned long) si->mouse_grab_window);
154 si->mouse_grab_window = 0;
159 grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
161 Status mstatus, kstatus;
162 XSync (si->dpy, False);
164 kstatus = grab_kbd (si, window);
165 if (kstatus != GrabSuccess)
166 { /* try again in a second */
168 kstatus = grab_kbd (si, window);
169 if (kstatus != GrabSuccess)
170 fprintf (stderr, "%s: couldn't grab keyboard! (%s)\n",
171 blurb(), grab_string(kstatus));
174 mstatus = grab_mouse (si, window, cursor);
175 if (mstatus != GrabSuccess)
176 { /* try again in a second */
178 mstatus = grab_mouse (si, window, cursor);
179 if (mstatus != GrabSuccess)
180 fprintf (stderr, "%s: couldn't grab pointer! (%s)\n",
181 blurb(), grab_string(mstatus));
184 return (kstatus == GrabSuccess ||
185 mstatus == GrabSuccess);
189 ungrab_keyboard_and_mouse (saver_info *si)
197 move_mouse_grab (saver_info *si, Window to, Cursor cursor)
199 Window old = si->mouse_grab_window;
202 return grab_mouse (si, to, cursor);
205 saver_preferences *p = &si->prefs;
208 XSync (si->dpy, False);
209 XGrabServer (si->dpy); /* ############ DANGER! */
210 XSync (si->dpy, False);
213 fprintf(stderr, "%s: grabbing server...\n", blurb());
216 status = grab_mouse (si, to, cursor);
218 if (status != GrabSuccess) /* Augh! */
220 sleep (1); /* Note dramatic evil of sleeping
221 with server grabbed. */
222 XSync (si->dpy, False);
223 status = grab_mouse (si, to, cursor);
226 if (status != GrabSuccess) /* Augh! Try to get the old one back... */
227 grab_mouse (si, to, cursor);
229 XUngrabServer (si->dpy);
230 XSync (si->dpy, False); /* ###### (danger over) */
233 fprintf(stderr, "%s: ungrabbing server.\n", blurb());
240 /* Prints an error message to stderr and returns True if there is another
241 xscreensaver running already. Silently returns False otherwise. */
243 ensure_no_screensaver_running (Display *dpy, Screen *screen)
247 Window root = RootWindowOfScreen (screen);
248 Window root2, parent, *kids;
250 XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
252 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
258 for (i = 0; i < nkids; i++)
262 unsigned long nitems, bytesafter;
265 if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
266 False, XA_STRING, &type, &format, &nitems,
267 &bytesafter, (unsigned char **) &version)
272 if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
273 False, XA_STRING, &type, &format, &nitems,
274 &bytesafter, (unsigned char **) &id)
280 "%s: already running on display %s (window 0x%x)\n from process %s.\n",
281 blurb(), DisplayString (dpy), (int) kids [i], id);
286 if (kids) XFree ((char *) kids);
288 XSetErrorHandler (old_handler);
294 /* Virtual-root hackery */
297 ERROR! You must not include vroot.h in this file.
301 store_vroot_property (Display *dpy, Window win, Window value)
306 "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(),
308 (win == screensaver_window ? "ScreenSaver" :
309 (win == real_vroot ? "VRoot" :
310 (win == real_vroot_value ? "Vroot_value" : "???"))),
312 (value == screensaver_window ? "ScreenSaver" :
313 (value == real_vroot ? "VRoot" :
314 (value == real_vroot_value ? "Vroot_value" : "???"))));
316 XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
317 (unsigned char *) &value, 1);
321 remove_vroot_property (Display *dpy, Window win)
325 fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win,
326 (win == screensaver_window ? "ScreenSaver" :
327 (win == real_vroot ? "VRoot" :
328 (win == real_vroot_value ? "Vroot_value" : "???"))));
330 XDeleteProperty (dpy, win, XA_VROOT);
335 kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
339 unsigned long nitems, bytesafter;
342 /* If the user has been using xv or xsetroot as a screensaver (to display
343 an image on the screensaver window, as a kind of slideshow) then the
344 pixmap and its associated color cells have been put in RetainPermanent
345 CloseDown mode. Since we're not destroying the xscreensaver window,
346 but merely unmapping it, we need to free these resources or those
347 colormap cells will stay allocated while the screensaver is off. (We
348 could just delete the screensaver window and recreate it later, but
349 that could cause other problems.) This code does an atomic read-and-
350 delete of the _XSETROOT_ID property, and if it held a pixmap, then we
351 cause the RetainPermanent resources of the client which created it
352 (and which no longer exists) to be freed.
354 if (XGetWindowProperty (dpy, window, XA_XSETROOT_ID, 0, 1,
355 True, AnyPropertyType, &type, &format, &nitems,
356 &bytesafter, (unsigned char **) &dataP)
360 if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
361 nitems == 1 && bytesafter == 0)
364 fprintf (stderr, "%s: destroying xsetroot data (0x%lX).\n",
366 XKillClient (dpy, *dataP);
369 fprintf (stderr, "%s: deleted unrecognised _XSETROOT_ID property: \n\
370 %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
371 blurb(), (unsigned long) dataP, (dataP ? *dataP : 0), type,
372 format, nitems, bytesafter);
377 static void handle_signals (saver_info *si, Bool on_p);
380 save_real_vroot (saver_screen_info *ssi)
382 saver_info *si = ssi->global;
383 Display *dpy = si->dpy;
384 Screen *screen = ssi->screen;
386 Window root = RootWindowOfScreen (screen);
387 Window root2, parent, *kids;
389 XErrorHandler old_handler;
391 /* It's possible that a window might be deleted between our call to
392 XQueryTree() and our call to XGetWindowProperty(). Don't die if
393 that happens (but just ignore that window, it's not the one we're
394 interested in anyway.)
397 old_handler = XSetErrorHandler (BadWindow_ehandler);
401 ssi->real_vroot_value = 0;
402 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
408 for (i = 0; i < nkids; i++)
412 unsigned long nitems, bytesafter;
415 if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
416 &type, &format, &nitems, &bytesafter,
417 (unsigned char **) &vrootP)
424 if (*vrootP == ssi->screensaver_window) abort ();
426 "%s: more than one virtual root window found (0x%x and 0x%x).\n",
427 blurb(), (int) ssi->real_vroot, (int) kids [i]);
430 ssi->real_vroot = kids [i];
431 ssi->real_vroot_value = *vrootP;
435 XSetErrorHandler (old_handler);
440 handle_signals (si, True);
441 remove_vroot_property (si->dpy, ssi->real_vroot);
445 XFree ((char *) kids);
450 restore_real_vroot_2 (saver_screen_info *ssi)
452 saver_info *si = ssi->global;
453 saver_preferences *p = &si->prefs;
454 if (p->verbose_p && ssi->real_vroot)
456 "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
457 blurb(), (unsigned long) ssi->real_vroot);
458 remove_vroot_property (si->dpy, ssi->screensaver_window);
461 store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value);
463 ssi->real_vroot_value = 0;
464 /* make sure the property change gets there before this process
465 terminates! We might be doing this because we have intercepted
466 SIGTERM or something. */
467 XSync (si->dpy, False);
474 restore_real_vroot_1 (saver_info *si)
477 Bool did_any = False;
478 for (i = 0; i < si->nscreens; i++)
480 saver_screen_info *ssi = &si->screens[i];
481 if (restore_real_vroot_2 (ssi))
488 restore_real_vroot (saver_info *si)
490 if (restore_real_vroot_1 (si))
491 handle_signals (si, False);
495 /* Signal hackery to ensure that the vroot doesn't get left in an
500 signal_name(int signal)
503 case SIGHUP: return "SIGHUP";
504 case SIGINT: return "SIGINT";
505 case SIGQUIT: return "SIGQUIT";
506 case SIGILL: return "SIGILL";
507 case SIGTRAP: return "SIGTRAP";
509 case SIGABRT: return "SIGABRT";
511 case SIGFPE: return "SIGFPE";
512 case SIGKILL: return "SIGKILL";
513 case SIGBUS: return "SIGBUS";
514 case SIGSEGV: return "SIGSEGV";
515 case SIGPIPE: return "SIGPIPE";
516 case SIGALRM: return "SIGALRM";
517 case SIGTERM: return "SIGTERM";
519 case SIGSTOP: return "SIGSTOP";
522 case SIGCONT: return "SIGCONT";
525 case SIGUSR1: return "SIGUSR1";
528 case SIGUSR2: return "SIGUSR2";
531 case SIGEMT: return "SIGEMT";
534 case SIGSYS: return "SIGSYS";
537 case SIGCHLD: return "SIGCHLD";
540 case SIGPWR: return "SIGPWR";
543 case SIGWINCH: return "SIGWINCH";
546 case SIGURG: return "SIGURG";
549 case SIGIO: return "SIGIO";
552 case SIGVTALRM: return "SIGVTALRM";
555 case SIGXCPU: return "SIGXCPU";
558 case SIGXFSZ: return "SIGXFSZ";
561 case SIGDANGER: return "SIGDANGER";
566 sprintf(buf, "signal %d\n", signal);
575 restore_real_vroot_handler (int sig)
577 saver_info *si = global_si_kludge; /* I hate C so much... */
579 signal (sig, SIG_DFL);
580 if (restore_real_vroot_1 (si))
581 fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
582 blurb(), signal_name(sig));
583 kill (getpid (), sig);
587 catch_signal (saver_info *si, int sig, Bool on_p)
590 signal (sig, SIG_DFL);
593 if (((long) signal (sig, restore_real_vroot_handler)) == -1L)
596 sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
598 saver_exit (si, 1, 0);
604 handle_signals (saver_info *si, Bool on_p)
607 if (on_p) fprintf (stderr, "handling signals\n");
608 else fprintf (stderr, "unhandling signals\n");
611 catch_signal (si, SIGHUP, on_p);
612 catch_signal (si, SIGINT, on_p);
613 catch_signal (si, SIGQUIT, on_p);
614 catch_signal (si, SIGILL, on_p);
615 catch_signal (si, SIGTRAP, on_p);
616 catch_signal (si, SIGIOT, on_p);
617 catch_signal (si, SIGABRT, on_p);
619 catch_signal (si, SIGEMT, on_p);
621 catch_signal (si, SIGFPE, on_p);
622 catch_signal (si, SIGBUS, on_p);
623 catch_signal (si, SIGSEGV, on_p);
625 catch_signal (si, SIGSYS, on_p);
627 catch_signal (si, SIGTERM, on_p);
629 catch_signal (si, SIGXCPU, on_p);
632 catch_signal (si, SIGXFSZ, on_p);
635 catch_signal (si, SIGDANGER, on_p);
640 saver_exit (saver_info *si, int status, const char *dump_core_reason)
642 saver_preferences *p = &si->prefs;
643 static Bool exiting = False;
652 vrs = restore_real_vroot_1 (si);
653 emergency_kill_subproc (si);
655 if (p->verbose_p) /* nobody cares about this */
658 fprintf (real_stderr, "%s: vroot restored, exiting.\n", blurb());
659 else if (p->verbose_p)
660 fprintf (real_stderr, "%s: no vroot to restore; exiting.\n", blurb());
665 #ifdef VMS /* on VMS, 1 is the "normal" exit code instead of 0. */
666 if (status == 0) status = 1;
667 else if (status == 1) status = -1;
670 bugp = !!dump_core_reason;
672 if (si->prefs.debug_p && !dump_core_reason)
673 dump_core_reason = "because of -debug";
675 if (dump_core_reason)
677 /* Note that the Linux man page for setuid() says If uid is
678 different from the old effective uid, the process will be
679 forbidden from leaving core dumps.
681 char cwd[4096]; /* should really be PATH_MAX, but who cares. */
683 fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
688 "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
689 "\t\tfor bug reporting information.\n\n",
692 # if defined(HAVE_GETCWD)
693 if (!getcwd (cwd, sizeof(cwd)))
694 # elif defined(HAVE_GETWD)
697 strcpy(cwd, "unknown.");
699 fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
700 describe_uids (si, real_stderr);
702 /* Do this to drop a core file, so that we can get a stack trace. */
710 /* Managing the actual screensaver window */
713 window_exists_p (Display *dpy, Window window)
715 XErrorHandler old_handler;
716 XWindowAttributes xgwa;
718 old_handler = XSetErrorHandler (BadWindow_ehandler);
719 XGetWindowAttributes (dpy, window, &xgwa);
721 XSetErrorHandler (old_handler);
722 return (xgwa.screen != 0);
726 store_saver_id (saver_screen_info *ssi)
728 XClassHint class_hints;
729 saver_info *si = ssi->global;
730 unsigned long pid = (unsigned long) getpid ();
732 struct passwd *p = getpwuid (getuid ());
733 const char *name, *host;
736 /* First store the name and class on the window.
738 class_hints.res_name = progname;
739 class_hints.res_class = progclass;
740 XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
741 XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
743 /* Then store the xscreensaver version number.
745 XChangeProperty (si->dpy, ssi->screensaver_window,
746 XA_SCREENSAVER_VERSION,
747 XA_STRING, 8, PropModeReplace,
748 (unsigned char *) si->version,
749 strlen (si->version));
751 /* Now store the XSCREENSAVER_ID property, that says what user and host
752 xscreensaver is running as.
755 if (p && p->pw_name && *p->pw_name)
759 sprintf (buf, "%lu", (unsigned long) p->pw_uid);
765 # if defined(HAVE_UNAME)
768 if (uname (&uts) < 0)
774 host = getenv("SYS$NODE");
775 # else /* !HAVE_UNAME && !VMS */
777 # endif /* !HAVE_UNAME && !VMS */
779 id = (char *) malloc (strlen(name) + strlen(host) + 50);
780 sprintf (id, "%lu (%s@%s)", pid, name, host);
782 XChangeProperty (si->dpy, ssi->screensaver_window,
783 XA_SCREENSAVER_ID, XA_STRING,
785 (unsigned char *) id, strlen (id));
791 store_saver_status (saver_info *si)
794 int size = si->nscreens + 2;
797 status = (CARD32 *) calloc (size, sizeof(CARD32));
799 status[0] = (CARD32) (si->screen_blanked_p
800 ? (si->locked_p ? XA_LOCK : XA_BLANK)
802 status[1] = (CARD32) si->blank_time;
804 for (i = 0; i < si->nscreens; i++)
806 saver_screen_info *ssi = &si->screens[i];
807 status [2 + i] = ssi->current_hack + 1;
810 XChangeProperty (si->dpy,
811 RootWindow (si->dpy, 0), /* always screen #0 */
812 XA_SCREENSAVER_STATUS,
813 XA_INTEGER, 32, PropModeReplace,
814 (unsigned char *) status, size);
819 /* Returns the area of the screen which the xscreensaver window should cover.
820 Normally this is the whole screen, but if the X server's root window is
821 actually larger than the monitor's displayable area, then we want to
822 operate in the currently-visible portion of the desktop instead.
825 get_screen_viewport (saver_screen_info *ssi,
826 int *x_ret, int *y_ret,
827 int *w_ret, int *h_ret,
830 int w = WidthOfScreen (ssi->screen);
831 int h = HeightOfScreen (ssi->screen);
833 #ifdef HAVE_XF86VMODE
834 saver_info *si = ssi->global;
835 int screen_no = screen_number (ssi->screen);
836 int op, event, error;
838 XF86VidModeModeLine ml;
841 /* Check for Xinerama first, because the VidModeExtension is broken
842 when Xinerama is present. Wheee!
845 if (!XQueryExtension (si->dpy, "XINERAMA", &op, &event, &error) &&
846 XF86VidModeQueryExtension (si->dpy, &event, &error) &&
847 XF86VidModeGetModeLine (si->dpy, screen_no, &dot, &ml) &&
848 XF86VidModeGetViewPort (si->dpy, screen_no, &x, &y))
853 *w_ret = ml.hdisplay;
854 *h_ret = ml.vdisplay;
856 if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
857 /* There is no viewport -- the screen does not scroll. */
861 /* Apparently some versions of XFree86 return nonsense here!
862 I've had reports of 1024x768 viewports at -1936862040, -1953705044.
863 So, sanity-check the values and give up if they are out of range.
865 if (*x_ret < 0 || *x_ret >= w ||
866 *y_ret < 0 || *y_ret >= h ||
867 *w_ret <= 0 || *w_ret > w ||
868 *h_ret <= 0 || *h_ret > h)
870 static int warned_once = 0;
873 fprintf (stderr, "\n"
874 "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
875 "%s: The XVidMode server extension is returning nonsense.\n"
876 "%s: Please report this bug to your X server vendor.\n\n",
877 blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
889 sprintf (msg, "%s: vp is %dx%d+%d+%d",
890 blurb(), *w_ret, *h_ret, *x_ret, *y_ret);
893 /* Apparently, though the server stores the X position in increments of
894 1 pixel, it will only make changes to the *display* in some other
895 increment. With XF86_SVGA on a Thinkpad, the display only updates
896 in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
897 pixels in 16-bit mode. I don't know what it does in 24- and 32-bit
898 mode, because I don't have enough video memory to find out.
900 I consider it a bug that XF86VidModeGetViewPort() is telling me the
901 server's *target* scroll position rather than the server's *actual*
902 scroll position. David Dawes agrees, and says they may fix this in
903 XFree86 4.0, but it's notrivial.
905 He also confirms that this behavior is server-dependent, so the
906 actual scroll position cannot be reliably determined by the client.
907 So... that means the only solution is to provide a ``sandbox''
908 around the blackout window -- we make the window be up to N pixels
909 larger than the viewport on both the left and right sides. That
910 means some part of the outer edges of each hack might not be
911 visible, but screw it.
913 I'm going to guess that 16 pixels is enough, and that the Y dimension
914 doesn't have this problem.
916 The drawback of doing this, of course, is that some of the screenhacks
917 will still look pretty stupid -- for example, "slidescreen" will cut
918 off the left and right edges of the grid, etc.
921 if (x > 0 && x < w - ml.hdisplay) /* not at left edge or right edge */
923 /* Round X position down to next lower multiple of FUDGE.
924 Increase width by 2*FUDGE in case some server rounds up.
926 *x_ret = ((x - 1) / FUDGE) * FUDGE;
927 *w_ret += (FUDGE * 2);
933 *w_ret != ml.hdisplay ||
934 *h_ret != ml.vdisplay)
935 sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
936 *w_ret, *h_ret, *x_ret, *y_ret);
939 fprintf (stderr, "%s.\n", msg);
944 #endif /* HAVE_XF86VMODE */
954 initialize_screensaver_window_1 (saver_screen_info *ssi)
956 saver_info *si = ssi->global;
957 saver_preferences *p = &si->prefs;
958 Bool install_cmap_p = ssi->install_cmap_p; /* not p->install_cmap_p */
960 /* This resets the screensaver window as fully as possible, since there's
961 no way of knowing what some random client may have done to us in the
962 meantime. We could just destroy and recreate the window, but that has
963 its own set of problems...
966 XSetWindowAttributes attrs;
967 unsigned long attrmask;
968 int x, y, width, height;
969 static Bool printed_visual_info = False; /* only print the message once. */
971 get_screen_viewport (ssi, &x, &y, &width, &height,
972 (p->verbose_p && !si->screen_blanked_p));
974 black.red = black.green = black.blue = 0;
976 if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
979 if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
980 /* It's not the default visual, so we have no choice but to install. */
981 install_cmap_p = True;
987 ssi->cmap = XCreateColormap (si->dpy,
988 RootWindowOfScreen (ssi->screen),
989 ssi->current_visual, AllocNone);
990 if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
991 ssi->black_pixel = black.pixel;
996 Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
999 XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
1000 if (ssi->cmap != ssi->demo_cmap &&
1001 ssi->cmap != def_cmap)
1002 XFreeColormap (si->dpy, ssi->cmap);
1004 ssi->cmap = def_cmap;
1005 ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
1008 attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
1009 CWBackPixel | CWBackingPixel | CWBorderPixel);
1010 attrs.override_redirect = True;
1012 /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
1013 actually be reading these events during normal operation; but we still
1014 need to see Button events for demo-mode to work properly.
1016 attrs.event_mask = (KeyPressMask | KeyReleaseMask |
1017 ButtonPressMask | ButtonReleaseMask |
1020 attrs.backing_store = NotUseful;
1021 attrs.colormap = ssi->cmap;
1022 attrs.background_pixel = ssi->black_pixel;
1023 attrs.backing_pixel = ssi->black_pixel;
1024 attrs.border_pixel = ssi->black_pixel;
1026 if (p->debug_p) width = width / 2;
1028 if (!p->verbose_p || printed_visual_info)
1030 else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
1032 fprintf (stderr, "%s: using default visual ", blurb());
1033 describe_visual (stderr, ssi->screen, ssi->current_visual,
1038 fprintf (stderr, "%s: using visual: ", blurb());
1039 describe_visual (stderr, ssi->screen, ssi->current_visual,
1041 fprintf (stderr, "%s: default visual: ", blurb());
1042 describe_visual (stderr, ssi->screen,
1043 DefaultVisualOfScreen (ssi->screen),
1044 ssi->install_cmap_p);
1046 printed_visual_info = True;
1048 #ifdef HAVE_MIT_SAVER_EXTENSION
1049 if (si->using_mit_saver_extension)
1051 XScreenSaverInfo *info;
1052 Window root = RootWindowOfScreen (ssi->screen);
1055 /* This call sets the server screensaver timeouts to what we think
1056 they should be (based on the resources and args xscreensaver was
1057 started with.) It's important that we do this to sync back up
1058 with the server - if we have turned on prematurely, as by an
1059 ACTIVATE ClientMessage, then the server may decide to activate
1060 the screensaver while it's already active. That's ok for us,
1061 since we would know to ignore that ScreenSaverActivate event,
1062 but a side effect of this would be that the server would map its
1063 saver window (which we then hide again right away) meaning that
1064 the bits currently on the screen get blown away. Ugly. */
1066 /* #### Ok, that doesn't work - when we tell the server that the
1067 screensaver is "off" it sends us a Deactivate event, which is
1068 sensible... but causes the saver to never come on. Hmm. */
1069 disable_builtin_screensaver (si, True);
1073 /* #### The MIT-SCREEN-SAVER extension gives us access to the
1074 window that the server itself uses for saving the screen.
1075 However, using this window in any way, in particular, calling
1076 XScreenSaverSetAttributes() as below, tends to make the X server
1077 crash. So fuck it, let's try and get along without using it...
1079 It's also inconvenient to use this window because it doesn't
1080 always exist (though the ID is constant.) So to use this
1081 window, we'd have to reimplement the ACTIVATE ClientMessage to
1082 tell the *server* to tell *us* to turn on, to cause the window
1083 to get created at the right time. Gag. */
1084 XScreenSaverSetAttributes (si->dpy, root,
1085 0, 0, width, height, 0,
1086 current_depth, InputOutput, visual,
1088 XSync (si->dpy, False);
1091 info = XScreenSaverAllocInfo ();
1092 XScreenSaverQueryInfo (si->dpy, root, info);
1093 ssi->server_mit_saver_window = info->window;
1094 if (! ssi->server_mit_saver_window) abort ();
1097 #endif /* HAVE_MIT_SAVER_EXTENSION */
1099 if (ssi->screensaver_window)
1101 XWindowChanges changes;
1102 unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1105 changes.width = width;
1106 changes.height = height;
1107 changes.border_width = 0;
1109 XConfigureWindow (si->dpy, ssi->screensaver_window,
1110 changesmask, &changes);
1111 XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
1116 ssi->screensaver_window =
1117 XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
1118 x, y, width, height,
1119 0, ssi->current_depth, InputOutput,
1120 ssi->current_visual, attrmask, &attrs);
1124 fprintf (stderr, "%s: saver window is 0x%lx.\n",
1125 blurb(), (unsigned long) ssi->screensaver_window);
1128 store_saver_id (ssi); /* store window name and IDs */
1133 bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
1135 BlackPixelOfScreen (ssi->screen),
1136 BlackPixelOfScreen (ssi->screen),
1138 ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
1140 XFreePixmap (si->dpy, bit);
1143 XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
1146 XUndefineCursor (si->dpy, ssi->screensaver_window);
1148 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1152 initialize_screensaver_window (saver_info *si)
1155 for (i = 0; i < si->nscreens; i++)
1156 initialize_screensaver_window_1 (&si->screens[i]);
1161 raise_window (saver_info *si,
1162 Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
1164 saver_preferences *p = &si->prefs;
1168 inhibit_fade = True;
1170 if (si->emergency_lock_p)
1171 inhibit_fade = True;
1174 initialize_screensaver_window (si);
1176 reset_watchdog_timer (si, True);
1178 if (p->fade_p && si->fading_possible_p && !inhibit_fade)
1180 Window *current_windows = (Window *)
1181 calloc(sizeof(Window), si->nscreens);
1182 Colormap *current_maps = (Colormap *)
1183 calloc(sizeof(Colormap), si->nscreens);
1185 for (i = 0; i < si->nscreens; i++)
1187 saver_screen_info *ssi = &si->screens[i];
1188 current_windows[i] = ssi->screensaver_window;
1189 current_maps[i] = (between_hacks_p
1191 : DefaultColormapOfScreen (ssi->screen));
1192 /* Ensure that the default background of the window is really black,
1193 not a pixmap or something. (This does not clear the window.) */
1194 XSetWindowBackground (si->dpy, ssi->screensaver_window,
1198 if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1200 XGrabServer (si->dpy); /* ############ DANGER! */
1202 /* Clear the stderr layer on each screen.
1205 for (i = 0; i < si->nscreens; i++)
1207 saver_screen_info *ssi = &si->screens[i];
1208 if (ssi->stderr_overlay_window)
1209 /* Do this before the fade, since the stderr cmap won't fade
1210 even if we uninstall it (beats me...) */
1214 /* Note! The server is grabbed, and this will take several seconds
1216 fade_screens (si->dpy, current_maps, current_windows,
1217 p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1220 free(current_windows);
1222 current_windows = 0;
1224 if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1226 #ifdef HAVE_MIT_SAVER_EXTENSION
1227 for (i = 0; i < si->nscreens; i++)
1229 saver_screen_info *ssi = &si->screens[i];
1230 if (ssi->server_mit_saver_window &&
1231 window_exists_p (si->dpy, ssi->server_mit_saver_window))
1232 XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1234 #endif /* HAVE_MIT_SAVER_EXTENSION */
1236 XUngrabServer (si->dpy);
1237 XSync (si->dpy, False); /* ###### (danger over) */
1241 for (i = 0; i < si->nscreens; i++)
1243 saver_screen_info *ssi = &si->screens[i];
1245 XClearWindow (si->dpy, ssi->screensaver_window);
1246 if (!dont_clear || ssi->stderr_overlay_window)
1248 XMapRaised (si->dpy, ssi->screensaver_window);
1249 #ifdef HAVE_MIT_SAVER_EXTENSION
1250 if (ssi->server_mit_saver_window &&
1251 window_exists_p (si->dpy, ssi->server_mit_saver_window))
1252 XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1253 #endif /* HAVE_MIT_SAVER_EXTENSION */
1257 for (i = 0; i < si->nscreens; i++)
1259 saver_screen_info *ssi = &si->screens[i];
1261 XInstallColormap (si->dpy, ssi->cmap);
1266 blank_screen (saver_info *si)
1271 /* Note: we do our grabs on the root window, not on the screensaver window.
1272 If we grabbed on the saver window, then the demo mode and lock dialog
1273 boxes wouldn't get any events.
1275 ok = grab_keyboard_and_mouse (si,
1276 /*si->screens[0].screensaver_window,*/
1277 RootWindowOfScreen(si->screens[0].screen),
1280 : si->screens[0].cursor));
1283 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1284 /* If we're using a server extension, then failure to get a grab is
1285 not a big deal -- even without the grab, we will still be able
1286 to un-blank when there is user activity, since the server will
1293 for (i = 0; i < si->nscreens; i++)
1295 saver_screen_info *ssi = &si->screens[i];
1297 save_real_vroot (ssi);
1298 store_vroot_property (si->dpy,
1299 ssi->screensaver_window,
1300 ssi->screensaver_window);
1302 #ifdef HAVE_XF86VMODE
1305 if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
1306 !XF86VidModeGetViewPort (si->dpy, i,
1309 ssi->blank_vp_x = ssi->blank_vp_y = -1;
1311 #endif /* HAVE_XF86VMODE */
1314 raise_window (si, False, False, False);
1316 si->screen_blanked_p = True;
1317 si->blank_time = time ((time_t) 0);
1318 si->last_wall_clock_time = 0;
1320 store_saver_status (si); /* store blank time */
1327 unblank_screen (saver_info *si)
1329 saver_preferences *p = &si->prefs;
1330 Bool unfade_p = (si->fading_possible_p && p->unfade_p);
1333 monitor_power_on (si);
1334 reset_watchdog_timer (si, False);
1341 Window *current_windows = (Window *)
1342 calloc(sizeof(Window), si->nscreens);
1344 for (i = 0; i < si->nscreens; i++)
1346 saver_screen_info *ssi = &si->screens[i];
1347 current_windows[i] = ssi->screensaver_window;
1348 /* Ensure that the default background of the window is really black,
1349 not a pixmap or something. (This does not clear the window.) */
1350 XSetWindowBackground (si->dpy, ssi->screensaver_window,
1354 if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1357 XSync (si->dpy, False);
1358 XGrabServer (si->dpy); /* ############ DANGER! */
1359 XSync (si->dpy, False);
1361 /* Clear the stderr layer on each screen.
1363 for (i = 0; i < si->nscreens; i++)
1365 saver_screen_info *ssi = &si->screens[i];
1369 XUngrabServer (si->dpy);
1370 XSync (si->dpy, False); /* ###### (danger over) */
1373 fade_screens (si->dpy, 0, current_windows,
1374 p->fade_seconds/1000, p->fade_ticks,
1377 free(current_windows);
1378 current_windows = 0;
1380 if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1384 for (i = 0; i < si->nscreens; i++)
1386 saver_screen_info *ssi = &si->screens[i];
1389 Colormap c = DefaultColormapOfScreen (ssi->screen);
1390 /* avoid technicolor */
1391 XClearWindow (si->dpy, ssi->screensaver_window);
1392 if (c) XInstallColormap (si->dpy, c);
1394 XUnmapWindow (si->dpy, ssi->screensaver_window);
1399 /* If the focus window does has a non-default colormap, then install
1400 that colormap as well. (On SGIs, this will cause both the root map
1401 and the focus map to be installed simultaniously. It'd be nice to
1402 pick up the other colormaps that had been installed, too; perhaps
1403 XListInstalledColormaps could be used for that?)
1408 XGetInputFocus (si->dpy, &focus, &revert_to);
1409 if (focus && focus != PointerRoot && focus != None)
1411 XWindowAttributes xgwa;
1413 XGetWindowAttributes (si->dpy, focus, &xgwa);
1414 if (xgwa.colormap &&
1415 xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1416 XInstallColormap (si->dpy, xgwa.colormap);
1421 for (i = 0; i < si->nscreens; i++)
1423 saver_screen_info *ssi = &si->screens[i];
1424 kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1427 store_saver_status (si); /* store unblank time */
1428 ungrab_keyboard_and_mouse (si);
1429 restore_real_vroot (si);
1431 /* Unmap the windows a second time, dammit -- just to avoid a race
1432 with the screen-grabbing hacks. (I'm not sure if this is really
1433 necessary; I'm stabbing in the dark now.)
1435 for (i = 0; i < si->nscreens; i++)
1436 XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1438 si->screen_blanked_p = False;
1439 si->blank_time = time ((time_t) 0);
1440 si->last_wall_clock_time = 0;
1442 store_saver_status (si); /* store unblank time */
1447 select_visual (saver_screen_info *ssi, const char *visual_name)
1449 saver_info *si = ssi->global;
1450 saver_preferences *p = &si->prefs;
1451 Bool install_cmap_p = p->install_cmap_p;
1452 Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1456 if (visual_name && *visual_name)
1458 if (!strcmp(visual_name, "default-i") ||
1459 !strcmp(visual_name, "Default-i") ||
1460 !strcmp(visual_name, "Default-I")
1463 visual_name = "default";
1464 install_cmap_p = True;
1466 else if (!strcmp(visual_name, "default-n") ||
1467 !strcmp(visual_name, "Default-n") ||
1468 !strcmp(visual_name, "Default-N"))
1470 visual_name = "default";
1471 install_cmap_p = False;
1473 #ifdef DAEMON_USE_GL
1474 else if (!strcmp(visual_name, "gl") ||
1475 !strcmp(visual_name, "Gl") ||
1476 !strcmp(visual_name, "GL"))
1478 new_v = get_gl_visual (ssi->screen);
1479 if (!new_v && p->verbose_p)
1480 fprintf (stderr, "%s: no GL visuals.\n", progname);
1482 #endif /* DAEMON_USE_GL */
1485 new_v = get_visual (ssi->screen, visual_name, True, False);
1489 new_v = ssi->default_visual;
1494 if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1495 /* It's not the default visual, so we have no choice but to install. */
1496 install_cmap_p = True;
1498 ssi->install_cmap_p = install_cmap_p;
1501 ((ssi->current_visual != new_v) ||
1502 (install_cmap_p != was_installed_p)))
1504 Colormap old_c = ssi->cmap;
1505 Window old_w = ssi->screensaver_window;
1509 fprintf (stderr, "%s: switching to visual ", blurb());
1510 describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
1512 fprintf (stderr, "%s: from ", blurb());
1513 describe_visual (stderr, ssi->screen, ssi->current_visual,
1519 ssi->current_visual = new_v;
1520 ssi->current_depth = visual_depth(ssi->screen, new_v);
1522 ssi->screensaver_window = 0;
1524 initialize_screensaver_window_1 (ssi);
1526 /* stderr_overlay_window is a child of screensaver_window, so we need
1527 to destroy that as well (actually, we just need to invalidate and
1528 drop our pointers to it, but this will destroy it, which is ok so
1529 long as it happens before old_w itself is destroyed.) */
1532 raise_window (si, True, True, False);
1533 store_vroot_property (si->dpy,
1534 ssi->screensaver_window, ssi->screensaver_window);
1537 /* Transfer the grabs from the old window to the new.
1538 Actually I think none of this is necessary, since we always
1539 hold our grabs on the root window, but I wrote this before
1540 re-discovering that...
1544 /* If we're destroying the window that holds our mouse grab,
1545 transfer the grab to the new window. (Grab the server while
1546 so doing, to avoid a race condition.)
1548 if (old_w == si->mouse_grab_window)
1550 XGrabServer (si->dpy); /* ############ DANGER! */
1552 grab_mouse (si, ssi->screensaver_window,
1556 XUngrabServer (si->dpy);
1557 XSync (si->dpy, False); /* ###### (danger over) */
1560 /* If we're destroying the window that holds our keyboard grab,
1561 transfer the grab to the new window. (Grab the server while
1562 so doing, to avoid a race condition.)
1564 if (old_w == si->keyboard_grab_window)
1566 XGrabServer (si->dpy); /* ############ DANGER! */
1568 grab_kbd(si, ssi->screensaver_window);
1569 XUngrabServer (si->dpy);
1570 XSync (si->dpy, False); /* ###### (danger over) */
1573 /* Now we can destroy this window without horking our grabs. */
1575 XDestroyWindow (si->dpy, old_w);
1578 fprintf (stderr, "%s: destroyed old saver window 0x%lx.\n",
1579 blurb(), (unsigned long) old_w);
1582 old_c != DefaultColormapOfScreen (ssi->screen) &&
1583 old_c != ssi->demo_cmap)
1584 XFreeColormap (si->dpy, old_c);