X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=driver%2Fwindows.c;h=9dbb5004cfbe5428dabe4486803e038d64c49629;hp=763a941f4842f9cc02e4bcbc099a356b30016edb;hb=6b1c86cf395f59389e4ece4ea8f4bea2c332745b;hpb=65740e2a8dea3d6309ae6e8914a0fb79e993ada8 diff --git a/driver/windows.c b/driver/windows.c old mode 100755 new mode 100644 index 763a941f..9dbb5004 --- a/driver/windows.c +++ b/driver/windows.c @@ -1,4 +1,5 @@ -/* xscreensaver, Copyright (c) 1991-1993 Jamie Zawinski +/* windows.c --- turning the screen black; dealing with visuals, virtual roots. + * xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -9,52 +10,78 @@ * implied warranty. */ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #ifdef VMS -typedef char * caddr_t; -#endif +# include /* for getpid() */ +# include "vms-gtod.h" /* for gettimeofday() */ +#endif /* VMS */ -#include -#include -#include -#include #ifndef VMS -#include -#else -#include "sys$common:[decw$include.xmu]SysUtil.h" -#endif +# include /* for getpwuid() */ +#else /* VMS */ +# include "vms-pwd.h" +#endif /* VMS */ +#ifdef HAVE_UNAME +# include /* for uname() */ +#endif /* HAVE_UNAME */ + +#include +/* #include / * for CARD32 */ +#include +#include /* for XSetClassHint() */ +#include +#include /* for time() */ #include /* for the signal names */ +#include +#include + +/* You might think that to store an array of 32-bit quantities onto a + server-side property, you would pass an array of 32-bit data quantities + into XChangeProperty(). You would be wrong. You have to use an array + of longs, even if long is 64 bits (using 32 of each 64.) + */ +typedef long PROP32; + +#ifdef HAVE_MIT_SAVER_EXTENSION +# include +#endif /* HAVE_MIT_SAVER_EXTENSION */ + +#ifdef HAVE_XF86VMODE +# include +#endif /* HAVE_XF86VMODE */ + +#ifdef HAVE_XINERAMA +# include +#endif /* HAVE_XINERAMA */ + +/* This file doesn't need the Xt headers, so stub these types out... */ +#undef XtPointer +#define XtAppContext void* +#define XrmDatabase void* +#define XtIntervalId void* +#define XtPointer void* +#define Widget void* #include "xscreensaver.h" +#include "visual.h" +#include "fade.h" -#if __STDC__ -#ifndef __DECC -extern int kill (pid_t, int); /* signal() is in sys/signal.h... */ -#else -extern int kill (int, int); -#endif -#endif /* __STDC__ */ -extern Bool lock_p, demo_mode_p; +extern int kill (pid_t, int); /* signal() is in sys/signal.h... */ -Atom XA_VROOT, XA_XSETROOT_ID; -Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID; +Atom XA_VROOT, XA_XSETROOT_ID, XA_ESETROOT_PMAP_ID, XA_XROOTPMAP_ID; +Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID; +Atom XA_SCREENSAVER_STATUS; -#if __STDC__ -extern void describe_visual (FILE *, Display *, Visual *); -#endif -Window screensaver_window = 0; -Cursor cursor; -Colormap cmap, cmap2; -Bool install_cmap_p; -Bool fade_p, unfade_p; -int fade_seconds, fade_ticks; +extern saver_info *global_si_kludge; /* I hate C so much... */ -static unsigned long black_pixel; -static Window real_vroot, real_vroot_value; +static void maybe_transfer_grabs (saver_screen_info *ssi, + Window old_w, Window new_w, int new_screen); #define ALL_POINTER_EVENTS \ (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \ @@ -62,59 +89,276 @@ static Window real_vroot, real_vroot_value; Button1MotionMask | Button2MotionMask | Button3MotionMask | \ Button4MotionMask | Button5MotionMask | ButtonMotionMask) -/* I don't really understand Sync vs Async, but these seem to work... */ -#define grab_kbd(win) \ - XGrabKeyboard (dpy, (win), True, GrabModeSync, GrabModeAsync, CurrentTime) -#define grab_mouse(win) \ - XGrabPointer (dpy, (win), True, ALL_POINTER_EVENTS, \ - GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime) -void -grab_keyboard_and_mouse P((void)) +static const char * +grab_string(int status) { - Status status; - XSync (dpy, False); + switch (status) + { + case GrabSuccess: return "GrabSuccess"; + case AlreadyGrabbed: return "AlreadyGrabbed"; + case GrabInvalidTime: return "GrabInvalidTime"; + case GrabNotViewable: return "GrabNotViewable"; + case GrabFrozen: return "GrabFrozen"; + default: + { + static char foo[255]; + sprintf(foo, "unknown status: %d", status); + return foo; + } + } +} + +static int +grab_kbd(saver_info *si, Window w, int screen_no) +{ + saver_preferences *p = &si->prefs; + int status = XGrabKeyboard (si->dpy, w, True, + /* I don't really understand Sync vs Async, + but these seem to work... */ + GrabModeSync, GrabModeAsync, + CurrentTime); + if (status == GrabSuccess) + { + si->keyboard_grab_window = w; + si->keyboard_grab_screen = screen_no; + } + + if (p->verbose_p) + fprintf(stderr, "%s: %d: grabbing keyboard on 0x%lx... %s.\n", + blurb(), screen_no, (unsigned long) w, grab_string(status)); + return status; +} + + +static int +grab_mouse (saver_info *si, Window w, Cursor cursor, int screen_no) +{ + saver_preferences *p = &si->prefs; + int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS, + GrabModeAsync, GrabModeAsync, w, + cursor, CurrentTime); + if (status == GrabSuccess) + { + si->mouse_grab_window = w; + si->mouse_grab_screen = screen_no; + } + + if (p->verbose_p) + fprintf(stderr, "%s: %d: grabbing mouse on 0x%lx... %s.\n", + blurb(), screen_no, (unsigned long) w, grab_string(status)); + return status; +} + + +static void +ungrab_kbd(saver_info *si) +{ + saver_preferences *p = &si->prefs; + XUngrabKeyboard(si->dpy, CurrentTime); + if (p->verbose_p) + fprintf(stderr, "%s: %d: ungrabbing keyboard (was 0x%lx).\n", + blurb(), si->keyboard_grab_screen, + (unsigned long) si->keyboard_grab_window); + si->keyboard_grab_window = 0; +} + + +static void +ungrab_mouse(saver_info *si) +{ + saver_preferences *p = &si->prefs; + XUngrabPointer(si->dpy, CurrentTime); + if (p->verbose_p) + fprintf(stderr, "%s: %d: ungrabbing mouse (was 0x%lx).\n", + blurb(), si->mouse_grab_screen, + (unsigned long) si->mouse_grab_window); + si->mouse_grab_window = 0; +} + + +/* Apparently there is this program called "rdesktop" which is a windows + terminal server client for Unix. It would seem that this program holds + the keyboard GRABBED the whole time it has focus! This is, of course, + completely idiotic: the whole point of grabbing is to get events when + you do *not* have focus, so grabbing *only when* you have focus is + completely redundant -- unless your goal is to make xscreensaver not + able to ever lock the screen when your program is running. + + If xscreensaver blanks while rdesktop still has a keyboard grab, then + when we try to prompt for the password, we won't get the characters: + they'll be typed into rdesktop. + + Perhaps rdesktop will release its keyboard grab if it loses focus? + What the hell, let's give it a try. If we fail to grab the keyboard + four times in a row, we forcibly set focus to "None" and try four + more times. (We don't touch focus unless we're already having a hard + time getting a grab.) + */ +static void +nuke_focus (saver_info *si, int screen_no) +{ + saver_preferences *p = &si->prefs; + Window focus = 0; + int rev = 0; + + XGetInputFocus (si->dpy, &focus, &rev); + + if (p->verbose_p) + { + char w[255], r[255]; + + if (focus == PointerRoot) strcpy (w, "PointerRoot"); + else if (focus == None) strcpy (w, "None"); + else sprintf (w, "0x%lx", (unsigned long) focus); + + if (rev == RevertToParent) strcpy (r, "RevertToParent"); + else if (rev == RevertToPointerRoot) strcpy (r, "RevertToPointerRoot"); + else if (rev == RevertToNone) strcpy (r, "RevertToNone"); + else sprintf (r, "0x%x", rev); + + fprintf (stderr, "%s: %d: removing focus from %s / %s.\n", + blurb(), screen_no, w, r); + } + + XSetInputFocus (si->dpy, None, RevertToNone, CurrentTime); + XSync (si->dpy, False); +} - if (demo_mode_p) return; - status = grab_kbd (screensaver_window); - if (status != GrabSuccess) - { /* try again in a second */ +static Bool +grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor, + int screen_no) +{ + Status mstatus = 0, kstatus = 0; + int i; + int retries = 4; + Bool focus_fuckus = False; + + AGAIN: + + for (i = 0; i < retries; i++) + { + XSync (si->dpy, False); + kstatus = grab_kbd (si, window, screen_no); + if (kstatus == GrabSuccess) + break; + + /* else, wait a second and try to grab again. */ sleep (1); - status = grab_kbd (screensaver_window); - if (status != GrabSuccess) - fprintf (stderr, "%s: %scouldn't grab keyboard! (%d)\n", - progname, (verbose_p ? "## " : ""), status); - } - status = grab_mouse (screensaver_window); - if (status != GrabSuccess) - { /* try again in a second */ + } + + if (kstatus != GrabSuccess) + { + fprintf (stderr, "%s: couldn't grab keyboard! (%s)\n", + blurb(), grab_string(kstatus)); + + if (! focus_fuckus) + { + focus_fuckus = True; + nuke_focus (si, screen_no); + goto AGAIN; + } + } + + for (i = 0; i < retries; i++) + { + XSync (si->dpy, False); + mstatus = grab_mouse (si, window, cursor, screen_no); + if (mstatus == GrabSuccess) + break; + + /* else, wait a second and try to grab again. */ sleep (1); - status = grab_mouse (screensaver_window); - if (status != GrabSuccess) - fprintf (stderr, "%s: %scouldn't grab pointer! (%d)\n", - progname, (verbose_p ? "## " : ""), status); } + + if (mstatus != GrabSuccess) + fprintf (stderr, "%s: couldn't grab pointer! (%s)\n", + blurb(), grab_string(mstatus)); + + + /* When should we allow blanking to proceed? The current theory + is that a keyboard grab is manditory; a mouse grab is optional. + + - If we don't have a keyboard grab, then we won't be able to + read a password to unlock, so the kbd grab is manditory. + (We can't conditionalize this on locked_p, because someone + might run "xscreensaver-command -lock" at any time.) + + - If we don't have a mouse grab, then we might not see mouse + clicks as a signal to unblank -- but we will still see kbd + activity, so that's not a disaster. + */ + + if (kstatus != GrabSuccess) /* Do not blank without a kbd grab. */ + return False; + + return True; /* Grab is good, go ahead and blank. */ } -void -ungrab_keyboard_and_mouse P((void)) +static void +ungrab_keyboard_and_mouse (saver_info *si) { - XUngrabPointer (dpy, CurrentTime); - XUngrabKeyboard (dpy, CurrentTime); + ungrab_mouse (si); + ungrab_kbd (si); } -void -ensure_no_screensaver_running () +int +move_mouse_grab (saver_info *si, Window to, Cursor cursor, int to_screen_no) +{ + Window old = si->mouse_grab_window; + + if (old == 0) + return grab_mouse (si, to, cursor, to_screen_no); + else + { + saver_preferences *p = &si->prefs; + int status; + + XSync (si->dpy, False); + XGrabServer (si->dpy); /* ############ DANGER! */ + XSync (si->dpy, False); + + if (p->verbose_p) + fprintf(stderr, "%s: grabbing server...\n", blurb()); + + ungrab_mouse (si); + status = grab_mouse (si, to, cursor, to_screen_no); + + if (status != GrabSuccess) /* Augh! */ + { + sleep (1); /* Note dramatic evil of sleeping + with server grabbed. */ + XSync (si->dpy, False); + status = grab_mouse (si, to, cursor, to_screen_no); + } + + if (status != GrabSuccess) /* Augh! Try to get the old one back... */ + grab_mouse (si, old, cursor, to_screen_no); + + XUngrabServer (si->dpy); + XSync (si->dpy, False); /* ###### (danger over) */ + + if (p->verbose_p) + fprintf(stderr, "%s: ungrabbing server.\n", blurb()); + + return status; + } +} + + +/* Prints an error message to stderr and returns True if there is another + xscreensaver running already. Silently returns False otherwise. */ +Bool +ensure_no_screensaver_running (Display *dpy, Screen *screen) { + Bool status = 0; int i; Window root = RootWindowOfScreen (screen); Window root2, parent, *kids; unsigned int nkids; - int (*old_handler) (); - - old_handler = XSetErrorHandler (BadWindow_ehandler); + XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler); if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids)) abort (); @@ -127,51 +371,37 @@ ensure_no_screensaver_running () Atom type; int format; unsigned long nitems, bytesafter; - char *version; + unsigned char *version; if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1, False, XA_STRING, &type, &format, &nitems, - &bytesafter, (unsigned char **) &version) + &bytesafter, &version) == Success && type != None) { - char *id; + unsigned char *id; if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512, False, XA_STRING, &type, &format, &nitems, - &bytesafter, (unsigned char **) &id) + &bytesafter, &id) == Success || type == None) - id = "???"; + id = (unsigned char *) "???"; fprintf (stderr, - "%s: %salready running on display %s (window 0x%x)\n from process %s.\n", - progname, (verbose_p ? "## " : ""), DisplayString (dpy), - (int) kids [i], id); - exit (1); + "%s: already running on display %s (window 0x%x)\n from process %s.\n", + blurb(), DisplayString (dpy), (int) kids [i], + (char *) id); + status = True; } } if (kids) XFree ((char *) kids); XSync (dpy, False); XSetErrorHandler (old_handler); + return status; } -void -disable_builtin_screensaver () -{ - int timeout, interval, prefer_blank, allow_exp; - XForceScreenSaver (dpy, ScreenSaverReset); - XGetScreenSaver (dpy, &timeout, &interval, &prefer_blank, &allow_exp); - if (timeout != 0) - { - XSetScreenSaver (dpy, 0, interval, prefer_blank, allow_exp); - printf ("%s%sisabling server builtin screensaver.\n\ - You can re-enable it with \"xset s on\".\n", - (verbose_p ? "" : progname), (verbose_p ? "\n\tD" : ": d")); - } -} - /* Virtual-root hackery */ @@ -180,53 +410,55 @@ ERROR! You must not include vroot.h in this file. #endif static void -#if __STDC__ -store_vroot_property (Window win, Window value) -#else -store_vroot_property (win, value) - Window win, value; -#endif +store_vroot_property (Display *dpy, Window win, Window value) { #if 0 - printf ("%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", progname, - win, - (win == screensaver_window ? "ScreenSaver" : - (win == real_vroot ? "VRoot" : - (win == real_vroot_value ? "Vroot_value" : "???"))), - value, - (value == screensaver_window ? "ScreenSaver" : - (value == real_vroot ? "VRoot" : - (value == real_vroot_value ? "Vroot_value" : "???")))); + if (p->verbose_p) + fprintf (stderr, + "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(), + win, + (win == screensaver_window ? "ScreenSaver" : + (win == real_vroot ? "VRoot" : + (win == real_vroot_value ? "Vroot_value" : "???"))), + value, + (value == screensaver_window ? "ScreenSaver" : + (value == real_vroot ? "VRoot" : + (value == real_vroot_value ? "Vroot_value" : "???")))); #endif XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace, (unsigned char *) &value, 1); } static void -#if __STDC__ -remove_vroot_property (Window win) -#else -remove_vroot_property (win) - Window win; -#endif +remove_vroot_property (Display *dpy, Window win) { #if 0 - printf ("%s: removing XA_VROOT from 0x%x (%s)\n", progname, win, - (win == screensaver_window ? "ScreenSaver" : - (win == real_vroot ? "VRoot" : - (win == real_vroot_value ? "Vroot_value" : "???")))); + if (p->verbose_p) + fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win, + (win == screensaver_window ? "ScreenSaver" : + (win == real_vroot ? "VRoot" : + (win == real_vroot_value ? "Vroot_value" : "???")))); #endif XDeleteProperty (dpy, win, XA_VROOT); } +static Bool safe_XKillClient (Display *dpy, XID id); + +#ifdef HAVE_XF86VMODE +static Bool safe_XF86VidModeGetViewPort (Display *, int, int *, int *); +#endif /* HAVE_XF86VMODE */ + + static void -kill_xsetroot_data P((void)) +kill_xsetroot_data_1 (Display *dpy, Window window, + Atom prop, const char *atom_name, + Bool verbose_p) { Atom type; int format; unsigned long nitems, bytesafter; - Pixmap *dataP = 0; + unsigned char *dataP = 0; /* If the user has been using xv or xsetroot as a screensaver (to display an image on the screensaver window, as a kind of slideshow) then the @@ -239,43 +471,71 @@ kill_xsetroot_data P((void)) delete of the _XSETROOT_ID property, and if it held a pixmap, then we cause the RetainPermanent resources of the client which created it (and which no longer exists) to be freed. + + Update: it seems that Gnome and KDE do this same trick, but with the + properties "ESETROOT_PMAP_ID" and/or "_XROOTPMAP_ID" instead of + "_XSETROOT_ID". So, we'll kill those too. */ - if (XGetWindowProperty (dpy, screensaver_window, XA_XSETROOT_ID, 0, 1, + if (XGetWindowProperty (dpy, window, prop, 0, 1, True, AnyPropertyType, &type, &format, &nitems, - &bytesafter, (unsigned char **) &dataP) + &bytesafter, &dataP) == Success && type != None) { - if (dataP && *dataP && type == XA_PIXMAP && format == 32 && + Pixmap *pixP = (Pixmap *) dataP; + if (pixP && *pixP && type == XA_PIXMAP && format == 32 && nitems == 1 && bytesafter == 0) { if (verbose_p) - printf ("%s: destroying xsetroot data (0x%X).\n", - progname, *dataP); - XKillClient (dpy, *dataP); + fprintf (stderr, "%s: destroying %s data (0x%lX).\n", + blurb(), atom_name, *pixP); + safe_XKillClient (dpy, *pixP); } else - fprintf (stderr, "%s: %sdeleted unrecognised _XSETROOT_ID property: \n\ - %d, %d; type: %d, format: %d, nitems: %d, bytesafter %d\n", - progname, (verbose_p ? "## " : ""), - dataP, (dataP ? *dataP : 0), type, + fprintf (stderr, + "%s: deleted unrecognised %s property: \n" + "\t%lu, %lu; type: %lu, format: %d, " + "nitems: %lu, bytesafter %ld\n", + blurb(), atom_name, + (unsigned long) pixP, (pixP ? *pixP : 0), type, format, nitems, bytesafter); } } -static void handle_signals P((Bool on_p)); +static void +kill_xsetroot_data (Display *dpy, Window w, Bool verbose_p) +{ + kill_xsetroot_data_1 (dpy, w, XA_XSETROOT_ID, "_XSETROOT_ID", verbose_p); + kill_xsetroot_data_1 (dpy, w, XA_ESETROOT_PMAP_ID, "ESETROOT_PMAP_ID", + verbose_p); + kill_xsetroot_data_1 (dpy, w, XA_XROOTPMAP_ID, "_XROOTPMAP_ID", verbose_p); +} + static void -save_real_vroot P((void)) +save_real_vroot (saver_screen_info *ssi) { + saver_info *si = ssi->global; + Display *dpy = si->dpy; + Screen *screen = ssi->screen; int i; Window root = RootWindowOfScreen (screen); Window root2, parent, *kids; unsigned int nkids; + XErrorHandler old_handler; + + /* It's possible that a window might be deleted between our call to + XQueryTree() and our call to XGetWindowProperty(). Don't die if + that happens (but just ignore that window, it's not the one we're + interested in anyway.) + */ + XSync (dpy, False); + old_handler = XSetErrorHandler (BadWindow_ehandler); + XSync (dpy, False); - real_vroot = 0; - real_vroot_value = 0; + ssi->real_vroot = 0; + ssi->real_vroot_value = 0; if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids)) abort (); if (root != root2) @@ -287,64 +547,93 @@ save_real_vroot P((void)) Atom type; int format; unsigned long nitems, bytesafter; - Window *vrootP = 0; + unsigned char *dataP = 0; + Window *vrootP; + int j; + + /* Skip this window if it is the xscreensaver window of any other + screen (this can happen in the Xinerama case.) + */ + for (j = 0; j < si->nscreens; j++) + { + saver_screen_info *ssi2 = &si->screens[j]; + if (kids[i] == ssi2->screensaver_window) + goto SKIP; + } if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW, &type, &format, &nitems, &bytesafter, - (unsigned char **) &vrootP) + &dataP) != Success) continue; - if (! vrootP) + if (! dataP) continue; - if (real_vroot) + + vrootP = (Window *) dataP; + if (ssi->real_vroot) { - if (*vrootP == screensaver_window) abort (); + if (*vrootP == ssi->screensaver_window) abort (); fprintf (stderr, - "%s: %smore than one virtual root window found (0x%x and 0x%x).\n", - progname, (verbose_p ? "## " : ""), - (int) real_vroot, (int) kids [i]); + "%s: more than one virtual root window found (0x%x and 0x%x).\n", + blurb(), (int) ssi->real_vroot, (int) kids [i]); exit (1); } - real_vroot = kids [i]; - real_vroot_value = *vrootP; + ssi->real_vroot = kids [i]; + ssi->real_vroot_value = *vrootP; + SKIP: + ; } - if (real_vroot) + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + + if (ssi->real_vroot) { - handle_signals (True); - remove_vroot_property (real_vroot); + remove_vroot_property (si->dpy, ssi->real_vroot); XSync (dpy, False); } XFree ((char *) kids); } + static Bool -restore_real_vroot_1 P((void)) +restore_real_vroot_1 (saver_screen_info *ssi) { - if (verbose_p && real_vroot) - printf ("%s: restoring __SWM_VROOT property on the real vroot (0x%x).\n", - progname, real_vroot); - remove_vroot_property (screensaver_window); - if (real_vroot) + saver_info *si = ssi->global; + saver_preferences *p = &si->prefs; + if (p->verbose_p && ssi->real_vroot) + fprintf (stderr, + "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n", + blurb(), (unsigned long) ssi->real_vroot); + remove_vroot_property (si->dpy, ssi->screensaver_window); + if (ssi->real_vroot) { - store_vroot_property (real_vroot, real_vroot_value); - real_vroot = 0; - real_vroot_value = 0; + store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value); + ssi->real_vroot = 0; + ssi->real_vroot_value = 0; /* make sure the property change gets there before this process terminates! We might be doing this because we have intercepted SIGTERM or something. */ - XSync (dpy, False); + XSync (si->dpy, False); return True; } return False; } -void -restore_real_vroot () +Bool +restore_real_vroot (saver_info *si) { - if (restore_real_vroot_1 ()) - handle_signals (False); + int i; + Bool did_any = False; + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (restore_real_vroot_1 (ssi)) + did_any = True; + } + return did_any; } @@ -352,315 +641,1571 @@ restore_real_vroot () inconsistent state */ -static const char *sig_names [255] = { 0 }; +const char * +signal_name(int signal) +{ + switch (signal) { + case SIGHUP: return "SIGHUP"; + case SIGINT: return "SIGINT"; + case SIGQUIT: return "SIGQUIT"; + case SIGILL: return "SIGILL"; + case SIGTRAP: return "SIGTRAP"; +#ifdef SIGABRT + case SIGABRT: return "SIGABRT"; +#endif + case SIGFPE: return "SIGFPE"; + case SIGKILL: return "SIGKILL"; + case SIGBUS: return "SIGBUS"; + case SIGSEGV: return "SIGSEGV"; + case SIGPIPE: return "SIGPIPE"; + case SIGALRM: return "SIGALRM"; + case SIGTERM: return "SIGTERM"; +#ifdef SIGSTOP + case SIGSTOP: return "SIGSTOP"; +#endif +#ifdef SIGCONT + case SIGCONT: return "SIGCONT"; +#endif +#ifdef SIGUSR1 + case SIGUSR1: return "SIGUSR1"; +#endif +#ifdef SIGUSR2 + case SIGUSR2: return "SIGUSR2"; +#endif +#ifdef SIGEMT + case SIGEMT: return "SIGEMT"; +#endif +#ifdef SIGSYS + case SIGSYS: return "SIGSYS"; +#endif +#ifdef SIGCHLD + case SIGCHLD: return "SIGCHLD"; +#endif +#ifdef SIGPWR + case SIGPWR: return "SIGPWR"; +#endif +#ifdef SIGWINCH + case SIGWINCH: return "SIGWINCH"; +#endif +#ifdef SIGURG + case SIGURG: return "SIGURG"; +#endif +#ifdef SIGIO + case SIGIO: return "SIGIO"; +#endif +#ifdef SIGVTALRM + case SIGVTALRM: return "SIGVTALRM"; +#endif +#ifdef SIGXCPU + case SIGXCPU: return "SIGXCPU"; +#endif +#ifdef SIGXFSZ + case SIGXFSZ: return "SIGXFSZ"; +#endif +#ifdef SIGDANGER + case SIGDANGER: return "SIGDANGER"; +#endif + default: + { + static char buf[50]; + sprintf(buf, "signal %d\n", signal); + return buf; + } + } +} + -static void -restore_real_vroot_handler (sig) - int sig; + +static RETSIGTYPE +restore_real_vroot_handler (int sig) { + saver_info *si = global_si_kludge; /* I hate C so much... */ + signal (sig, SIG_DFL); - if (restore_real_vroot_1 ()) - fprintf (stderr, "\n%s: %s%s (%d) intercepted, vroot restored.\n", - progname, (verbose_p ? "## " : ""), - ((sig < sizeof(sig_names) && sig >= 0 && sig_names [sig]) - ? sig_names [sig] : "unknown signal"), - sig); + if (restore_real_vroot (si)) + fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n", + blurb(), signal_name(sig)); kill (getpid (), sig); } - static void -#if __STDC__ -catch_signal (int sig, char *signame, Bool on_p) -#else -catch_signal (sig, signame, on_p) - int sig; - char *signame; - Bool on_p; -#endif +catch_signal (saver_info *si, int sig, RETSIGTYPE (*handler) (int)) { - if (! on_p) - signal (sig, SIG_DFL); - else +# ifdef HAVE_SIGACTION + + struct sigaction a; + a.sa_handler = handler; + sigemptyset (&a.sa_mask); + a.sa_flags = 0; + + /* On Linux 2.4.9 (at least) we need to tell the kernel to not mask delivery + of this signal from inside its handler, or else when we execvp() the + process again, it starts up with SIGHUP blocked, meaning that killing + it with -HUP only works *once*. You'd think that execvp() would reset + all the signal masks, but it doesn't. + */ +# if defined(SA_NOMASK) + a.sa_flags |= SA_NOMASK; +# elif defined(SA_NODEFER) + a.sa_flags |= SA_NODEFER; +# endif + + if (sigaction (sig, &a, 0) < 0) +# else /* !HAVE_SIGACTION */ + if (((long) signal (sig, handler)) == -1L) +# endif /* !HAVE_SIGACTION */ { - sig_names [sig] = signame; - if (((int) signal (sig, restore_real_vroot_handler)) == -1) - { - char buf [255]; - sprintf (buf, "%s: %scouldn't catch %s (%d)", progname, - (verbose_p ? "## " : ""), signame, sig); - perror (buf); - restore_real_vroot (); - exit (1); - } + char buf [255]; + sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig)); + perror (buf); + saver_exit (si, 1, 0); } } -static void -handle_signals (on_p) - Bool on_p; -{ -#if 0 - if (on_p) printf ("handling signals\n"); - else printf ("unhandling signals\n"); -#endif +static RETSIGTYPE saver_sighup_handler (int sig); - catch_signal (SIGHUP, "SIGHUP", on_p); - catch_signal (SIGINT, "SIGINT", on_p); - catch_signal (SIGQUIT, "SIGQUIT", on_p); - catch_signal (SIGILL, "SIGILL", on_p); - catch_signal (SIGTRAP, "SIGTRAP", on_p); - catch_signal (SIGIOT, "SIGIOT", on_p); -#ifndef VMS - catch_signal (SIGABRT, "SIGABRT", on_p); +void +handle_signals (saver_info *si) +{ + catch_signal (si, SIGHUP, saver_sighup_handler); + + catch_signal (si, SIGINT, restore_real_vroot_handler); + catch_signal (si, SIGQUIT, restore_real_vroot_handler); + catch_signal (si, SIGILL, restore_real_vroot_handler); + catch_signal (si, SIGTRAP, restore_real_vroot_handler); +#ifdef SIGIOT + catch_signal (si, SIGIOT, restore_real_vroot_handler); #endif + catch_signal (si, SIGABRT, restore_real_vroot_handler); #ifdef SIGEMT - catch_signal (SIGEMT, "SIGEMT", on_p); + catch_signal (si, SIGEMT, restore_real_vroot_handler); #endif - catch_signal (SIGFPE, "SIGFPE", on_p); - catch_signal (SIGBUS, "SIGBUS", on_p); - catch_signal (SIGSEGV, "SIGSEGV", on_p); + catch_signal (si, SIGFPE, restore_real_vroot_handler); + catch_signal (si, SIGBUS, restore_real_vroot_handler); + catch_signal (si, SIGSEGV, restore_real_vroot_handler); #ifdef SIGSYS - catch_signal (SIGSYS, "SIGSYS", on_p); + catch_signal (si, SIGSYS, restore_real_vroot_handler); #endif - catch_signal (SIGTERM, "SIGTERM", on_p); + catch_signal (si, SIGTERM, restore_real_vroot_handler); #ifdef SIGXCPU - catch_signal (SIGXCPU, "SIGXCPU", on_p); + catch_signal (si, SIGXCPU, restore_real_vroot_handler); #endif #ifdef SIGXFSZ - catch_signal (SIGXFSZ, "SIGXFSZ", on_p); + catch_signal (si, SIGXFSZ, restore_real_vroot_handler); #endif #ifdef SIGDANGER - catch_signal (SIGDANGER, "SIGDANGER", on_p); + catch_signal (si, SIGDANGER, restore_real_vroot_handler); #endif } - -/* Managing the actual screensaver window */ -void -initialize_screensaver_window P((void)) +static RETSIGTYPE +saver_sighup_handler (int sig) { - /* This resets the screensaver window as fully as possible, since there's - no way of knowing what some random client may have done to us in the - meantime. We could just destroy and recreate the window, but that has - its own set of problems... - */ - XColor black; - XClassHint class_hints; - XSetWindowAttributes attrs; - unsigned long attrmask; - int width = WidthOfScreen (screen); - int height = HeightOfScreen (screen); - char id [2048]; - - black.red = black.green = black.blue = 0; + saver_info *si = global_si_kludge; /* I hate C so much... */ - if (cmap == DefaultColormapOfScreen (screen)) - cmap = 0; + /* Re-establish SIGHUP handler */ + catch_signal (si, SIGHUP, saver_sighup_handler); - if ((install_cmap_p && !demo_mode_p) || - (visual != DefaultVisualOfScreen (screen))) - { - if (! cmap) - { - cmap = XCreateColormap (dpy, RootWindowOfScreen (screen), - visual, AllocNone); - if (! XAllocColor (dpy, cmap, &black)) abort (); - black_pixel = black.pixel; - } - } - else - { - if (cmap) - { - XFreeColors (dpy, cmap, &black_pixel, 1, 0); - XFreeColormap (dpy, cmap); - } - cmap = DefaultColormapOfScreen (screen); - black_pixel = BlackPixelOfScreen (screen); - } + fprintf (stderr, "%s: %s received: restarting...\n", + blurb(), signal_name(sig)); - if (cmap2) + if (si->screen_blanked_p) { - XFreeColormap (dpy, cmap2); - cmap2 = 0; + unblank_screen (si); + kill_screenhack (si); + XSync (si->dpy, False); } - if (fade_p) - { - cmap2 = copy_colormap (dpy, cmap, 0); - if (! cmap2) - fade_p = unfade_p = 0; - } + restart_process (si); /* Does not return */ + abort (); +} - attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap | - CWBackPixel | CWBackingPixel | CWBorderPixel); - attrs.override_redirect = True; - attrs.event_mask = (KeyPressMask | KeyReleaseMask | - ButtonPressMask | ButtonReleaseMask | - PointerMotionMask); - attrs.backing_store = NotUseful; - attrs.colormap = cmap; - attrs.background_pixel = black_pixel; - attrs.backing_pixel = black_pixel; - attrs.border_pixel = black_pixel; -/* if (demo_mode_p || lock_p) width = width / 2; #### */ - if (screensaver_window) - { - XWindowChanges changes; - unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth; - changes.x = 0; - changes.y = 0; - changes.width = width; - changes.height = height; - changes.border_width = 0; +void +saver_exit (saver_info *si, int status, const char *dump_core_reason) +{ + saver_preferences *p = &si->prefs; + static Bool exiting = False; + Bool bugp; + Bool vrs; - XConfigureWindow (dpy, screensaver_window, changesmask, &changes); - XChangeWindowAttributes (dpy, screensaver_window, attrmask, &attrs); - } - else - { - if (! verbose_p) - ; - else if (visual == DefaultVisualOfScreen (screen)) - { - fprintf (stderr, "%s: using default visual ", progname); - describe_visual (stderr, dpy, visual); - } - else - { - fprintf (stderr, "%s: using visual: ", progname); - describe_visual (stderr, dpy, visual); - fprintf (stderr, "%s: default visual: ", progname); - describe_visual (stderr, dpy, DefaultVisualOfScreen (screen)); - } + if (exiting) + exit(status); - screensaver_window = - XCreateWindow (dpy, RootWindowOfScreen (screen), 0, 0, width, height, - 0, visual_depth, InputOutput, visual, attrmask, - &attrs); - } + exiting = True; + + vrs = restore_real_vroot (si); + emergency_kill_subproc (si); + shutdown_stderr (si); - class_hints.res_name = progname; - class_hints.res_class = progclass; - XSetClassHint (dpy, screensaver_window, &class_hints); - XStoreName (dpy, screensaver_window, "screensaver"); - XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_VERSION, - XA_STRING, 8, PropModeReplace, - (unsigned char *) screensaver_version, - strlen (screensaver_version)); + if (p->verbose_p && vrs) + fprintf (real_stderr, "%s: old vroot restored.\n", blurb()); - sprintf (id, "%d on host ", getpid ()); - if (! XmuGetHostname (id + strlen (id), sizeof (id) - strlen (id) - 1)) - strcat (id, "???"); - XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_ID, XA_STRING, 8, - PropModeReplace, (unsigned char *) id, strlen (id)); + fflush(real_stdout); - if (!cursor) - { - Pixmap bit; - bit = XCreatePixmapFromBitmapData (dpy, screensaver_window, "\000", 1, 1, - BlackPixelOfScreen (screen), - BlackPixelOfScreen (screen), 1); - cursor = XCreatePixmapCursor (dpy, bit, bit, &black, &black, 0, 0); - XFreePixmap (dpy, bit); - } +#ifdef VMS /* on VMS, 1 is the "normal" exit code instead of 0. */ + if (status == 0) status = 1; + else if (status == 1) status = -1; +#endif - XSetWindowBackground (dpy, screensaver_window, black_pixel); - if (! demo_mode_p) - XDefineCursor (dpy, screensaver_window, cursor); - else - XUndefineCursor (dpy, screensaver_window); -} + bugp = !!dump_core_reason; + if (si->prefs.debug_p && !dump_core_reason) + dump_core_reason = "because of -debug"; -void -raise_window (inhibit_fade, between_hacks_p) - Bool inhibit_fade, between_hacks_p; -{ - initialize_screensaver_window (); - - if (fade_p && !inhibit_fade && !demo_mode_p) - { - int grabbed; - Colormap current_map = (between_hacks_p - ? cmap - : DefaultColormapOfScreen (screen)); - copy_colormap (dpy, current_map, cmap2); - XGrabServer (dpy); - /* grab and blacken mouse on the root window (saver not mapped yet) */ - grabbed = grab_mouse (RootWindowOfScreen (screen)); - /* fade what's on the screen to black */ - XInstallColormap (dpy, cmap2); - fade_colormap (dpy, current_map, cmap2, fade_seconds, fade_ticks, True); - XClearWindow (dpy, screensaver_window); - XMapRaised (dpy, screensaver_window); - /* Once the saver window is up, restore the colormap. - (The "black" pixels of the two colormaps are compatible.) */ - XInstallColormap (dpy, cmap); - if (grabbed == GrabSuccess) - XUngrabPointer (dpy, CurrentTime); - XUngrabServer (dpy); - } - else + if (dump_core_reason) { - XClearWindow (dpy, screensaver_window); - XMapRaised (dpy, screensaver_window); + /* Note that the Linux man page for setuid() says If uid is + different from the old effective uid, the process will be + forbidden from leaving core dumps. + */ + char cwd[4096]; /* should really be PATH_MAX, but who cares. */ + cwd[0] = 0; + fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(), + dump_core_reason); + + if (bugp) + fprintf(real_stderr, + "%s: see http://www.jwz.org/xscreensaver/bugs.html\n" + "\t\t\tfor bug reporting information.\n\n", + blurb()); + +# if defined(HAVE_GETCWD) + if (!getcwd (cwd, sizeof(cwd))) +# elif defined(HAVE_GETWD) + if (!getwd (cwd)) +# endif + strcpy(cwd, "unknown."); + + fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd); + describe_uids (si, real_stderr); + + /* Do this to drop a core file, so that we can get a stack trace. */ + abort(); } - if (install_cmap_p && !demo_mode_p) - XInstallColormap (dpy, cmap); + exit (status); } -void -blank_screen () -{ - save_real_vroot (); - store_vroot_property (screensaver_window, screensaver_window); - raise_window (False, False); - grab_keyboard_and_mouse (); -#ifdef __hpux - if (lock_p) - XHPDisableReset (dpy); /* turn off C-Sh-Reset */ -#endif + +/* Managing the actual screensaver window */ + +Bool +window_exists_p (Display *dpy, Window window) +{ + XErrorHandler old_handler; + XWindowAttributes xgwa; + xgwa.screen = 0; + old_handler = XSetErrorHandler (BadWindow_ehandler); + XGetWindowAttributes (dpy, window, &xgwa); + XSync (dpy, False); + XSetErrorHandler (old_handler); + return (xgwa.screen != 0); } -void -unblank_screen () -{ - if (unfade_p && !demo_mode_p) - { - int grabbed; - Colormap default_map = DefaultColormapOfScreen (screen); - blacken_colormap (dpy, cmap2); - XGrabServer (dpy); - /* grab and blacken mouse on the root window. */ - grabbed = grab_mouse (RootWindowOfScreen (screen)); - XInstallColormap (dpy, cmap2); - XUnmapWindow (dpy, screensaver_window); - fade_colormap (dpy, default_map, cmap2, fade_seconds, fade_ticks, False); - XInstallColormap (dpy, default_map); - if (grabbed == GrabSuccess) - XUngrabPointer (dpy, CurrentTime); - XUngrabServer (dpy); - } +static void +store_saver_id (saver_screen_info *ssi) +{ + XClassHint class_hints; + saver_info *si = ssi->global; + unsigned long pid = (unsigned long) getpid (); + char buf[20]; + struct passwd *p = getpwuid (getuid ()); + const char *name, *host; + char *id; + + /* First store the name and class on the window. + */ + class_hints.res_name = progname; + class_hints.res_class = progclass; + XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints); + XStoreName (si->dpy, ssi->screensaver_window, "screensaver"); + + /* Then store the xscreensaver version number. + */ + XChangeProperty (si->dpy, ssi->screensaver_window, + XA_SCREENSAVER_VERSION, + XA_STRING, 8, PropModeReplace, + (unsigned char *) si->version, + strlen (si->version)); + + /* Now store the XSCREENSAVER_ID property, that says what user and host + xscreensaver is running as. + */ + + if (p && p->pw_name && *p->pw_name) + name = p->pw_name; + else if (p) + { + sprintf (buf, "%lu", (unsigned long) p->pw_uid); + name = buf; + } else + name = "???"; + +# if defined(HAVE_UNAME) + { + struct utsname uts; + if (uname (&uts) < 0) + host = "???"; + else + host = uts.nodename; + } +# elif defined(VMS) + host = getenv("SYS$NODE"); +# else /* !HAVE_UNAME && !VMS */ + host = "???"; +# endif /* !HAVE_UNAME && !VMS */ + + id = (char *) malloc (strlen(name) + strlen(host) + 50); + sprintf (id, "%lu (%s@%s)", pid, name, host); + + XChangeProperty (si->dpy, ssi->screensaver_window, + XA_SCREENSAVER_ID, XA_STRING, + 8, PropModeReplace, + (unsigned char *) id, strlen (id)); + free (id); +} + + +void +store_saver_status (saver_info *si) +{ + PROP32 *status; + int size = si->nscreens + 2; + int i; + + status = (PROP32 *) calloc (size, sizeof(PROP32)); + + status[0] = (PROP32) (si->screen_blanked_p + ? (si->locked_p ? XA_LOCK : XA_BLANK) + : 0); + status[1] = (PROP32) si->blank_time; + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + status [2 + i] = ssi->current_hack + 1; + } + + XChangeProperty (si->dpy, + RootWindow (si->dpy, 0), /* always screen #0 */ + XA_SCREENSAVER_STATUS, + XA_INTEGER, 32, PropModeReplace, + (unsigned char *) status, size); + free (status); +} + + + +/* Returns the area of the screen which the xscreensaver window should cover. + Normally this is the whole screen, but if the X server's root window is + actually larger than the monitor's displayable area, then we want to + operate in the currently-visible portion of the desktop instead. + */ +void +get_screen_viewport (saver_screen_info *ssi, + int *x_ret, int *y_ret, + int *w_ret, int *h_ret, + int target_x, int target_y, + Bool verbose_p) +{ + int w = WidthOfScreen (ssi->screen); + int h = HeightOfScreen (ssi->screen); + +# ifdef HAVE_XF86VMODE + saver_info *si = ssi->global; + saver_preferences *p = &si->prefs; + int event, error; + int dot; + XF86VidModeModeLine ml; + int x, y; + Bool xinerama_p = si->xinerama_p; + +# ifndef HAVE_XINERAMA + /* Even if we don't have the client-side Xinerama lib, check to see if + the server supports Xinerama, so that we know to ignore the VidMode + extension -- otherwise a server crash could result. Yay. */ + xinerama_p = XQueryExtension (si->dpy, "XINERAMA", &error, &event, &error); +# endif /* !HAVE_XINERAMA */ + +# ifdef HAVE_XINERAMA + if (xinerama_p) { - if (install_cmap_p && !demo_mode_p) + int mouse_p = (target_x != -1 && target_y != -1); + int which = -1; + int i; + + /* If a mouse position wasn't passed in, assume we're talking about + this screen. */ + if (!mouse_p) + { + target_x = ssi->x; + target_y = ssi->y; + which = ssi->number; + } + + /* Find the Xinerama rectangle that contains the mouse position. */ + for (i = 0; i < si->nscreens; i++) + { + if (which == -1 && + target_x >= si->screens[i].x && + target_y >= si->screens[i].y && + target_x < si->screens[i].x + si->screens[i].width && + target_y < si->screens[i].y + si->screens[i].height) + which = i; + } + if (which == -1) which = 0; /* didn't find it? Use the first. */ + *x_ret = si->screens[which].x; + *y_ret = si->screens[which].y; + *w_ret = si->screens[which].width; + *h_ret = si->screens[which].height; + + if (verbose_p) + { + fprintf (stderr, "%s: %d: xinerama vp: %dx%d+%d+%d", + blurb(), which, + si->screens[which].width, si->screens[which].height, + si->screens[which].x, si->screens[which].y); + if (mouse_p) + fprintf (stderr, "; mouse at %d,%d", target_x, target_y); + fprintf (stderr, ".\n"); + } + + return; + } +# endif /* HAVE_XINERAMA */ + + if (!xinerama_p && /* Xinerama + VidMode = broken. */ + XF86VidModeQueryExtension (si->dpy, &event, &error) && + safe_XF86VidModeGetViewPort (si->dpy, ssi->number, &x, &y) && + XF86VidModeGetModeLine (si->dpy, ssi->number, &dot, &ml)) + { + char msg[512]; + *x_ret = x; + *y_ret = y; + *w_ret = ml.hdisplay; + *h_ret = ml.vdisplay; + + if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h) + /* There is no viewport -- the screen does not scroll. */ + return; + + + /* Apparently some versions of XFree86 return nonsense here! + I've had reports of 1024x768 viewports at -1936862040, -1953705044. + So, sanity-check the values and give up if they are out of range. + */ + if (*x_ret < 0 || *x_ret >= w || + *y_ret < 0 || *y_ret >= h || + *w_ret <= 0 || *w_ret > w || + *h_ret <= 0 || *h_ret > h) + { + static int warned_once = 0; + if (!warned_once) + { + fprintf (stderr, "\n" + "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n" + "%s: The XVidMode server extension is returning nonsense.\n" + "%s: Please report this bug to your X server vendor.\n\n", + blurb(), *w_ret, *h_ret, *x_ret, *y_ret, + blurb(), blurb()); + warned_once = 1; + } + *x_ret = 0; + *y_ret = 0; + *w_ret = w; + *h_ret = h; + return; + } + + sprintf (msg, "%s: %d: vp is %dx%d+%d+%d", + blurb(), ssi->number, + *w_ret, *h_ret, *x_ret, *y_ret); + + + if (p->getviewport_full_of_lies_p) + { + /* XF86VidModeGetViewPort() tends to be full of lies on laptops + that have a docking station or external monitor that runs in + a different resolution than the laptop's screen: + + http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=81593 + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=208417 + http://bugs.xfree86.org/show_bug.cgi?id=421 + + The XFree86 developers have closed the bug. As far as I can + tell, their reason for this was, "this is an X server bug, + but it's pretty hard to fix. Therefore, we are closing it." + + So, now there's a preference item for those unfortunate users to + tell us not to trust a word that XF86VidModeGetViewPort() says. + */ + static int warned_once = 0; + if (!warned_once && verbose_p) + { + warned_once = 1; + fprintf (stderr, + "%s: %d: XF86VidModeGetViewPort() says vp is %dx%d+%d+%d;\n" + "%s: %d: assuming that is a pack of lies;\n" + "%s: %d: using %dx%d+0+0 instead.\n", + blurb(), ssi->number, + *w_ret, *h_ret, *x_ret, *y_ret, + blurb(), ssi->number, + blurb(), ssi->number, w, h); + } + + *x_ret = 0; + *y_ret = 0; + *w_ret = w; + *h_ret = h; + return; + } + + + /* Apparently, though the server stores the X position in increments of + 1 pixel, it will only make changes to the *display* in some other + increment. With XF86_SVGA on a Thinkpad, the display only updates + in multiples of 8 pixels when in 8-bit mode, and in multiples of 4 + pixels in 16-bit mode. I don't know what it does in 24- and 32-bit + mode, because I don't have enough video memory to find out. + + I consider it a bug that XF86VidModeGetViewPort() is telling me the + server's *target* scroll position rather than the server's *actual* + scroll position. David Dawes agrees, and says they may fix this in + XFree86 4.0, but it's notrivial. + + He also confirms that this behavior is server-dependent, so the + actual scroll position cannot be reliably determined by the client. + So... that means the only solution is to provide a ``sandbox'' + around the blackout window -- we make the window be up to N pixels + larger than the viewport on both the left and right sides. That + means some part of the outer edges of each hack might not be + visible, but screw it. + + I'm going to guess that 16 pixels is enough, and that the Y dimension + doesn't have this problem. + + The drawback of doing this, of course, is that some of the screenhacks + will still look pretty stupid -- for example, "slidescreen" will cut + off the left and right edges of the grid, etc. + */ +# define FUDGE 16 + if (x > 0 && x < w - ml.hdisplay) /* not at left edge or right edge */ + { + /* Round X position down to next lower multiple of FUDGE. + Increase width by 2*FUDGE in case some server rounds up. + */ + *x_ret = ((x - 1) / FUDGE) * FUDGE; + *w_ret += (FUDGE * 2); + } +# undef FUDGE + + if (*x_ret != x || + *y_ret != y || + *w_ret != ml.hdisplay || + *h_ret != ml.vdisplay) + sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d", + *w_ret, *h_ret, *x_ret, *y_ret); + + if (verbose_p) + fprintf (stderr, "%s.\n", msg); + + return; + } + +# endif /* HAVE_XF86VMODE */ + + *x_ret = 0; + *y_ret = 0; + *w_ret = w; + *h_ret = h; +} + + +static Bool error_handler_hit_p = False; + +static int +ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) +{ + error_handler_hit_p = True; + return 0; +} + + +/* Returns True if successful, False if an X error occurred. + We need this because other programs might have done things to + our window that will cause XChangeWindowAttributes() to fail: + if that happens, we give up, destroy the window, and re-create + it. + */ +static Bool +safe_XChangeWindowAttributes (Display *dpy, Window window, + unsigned long mask, + XSetWindowAttributes *attrs) +{ + XErrorHandler old_handler; + XSync (dpy, False); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + + XChangeWindowAttributes (dpy, window, mask, attrs); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + + return (!error_handler_hit_p); +} + + +/* This might not be necessary, but just in case. */ +static Bool +safe_XConfigureWindow (Display *dpy, Window window, + unsigned long mask, XWindowChanges *changes) +{ + XErrorHandler old_handler; + XSync (dpy, False); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + + XConfigureWindow (dpy, window, mask, changes); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + + return (!error_handler_hit_p); +} + +/* This might not be necessary, but just in case. */ +static Bool +safe_XDestroyWindow (Display *dpy, Window window) +{ + XErrorHandler old_handler; + XSync (dpy, False); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + + XDestroyWindow (dpy, window); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + + return (!error_handler_hit_p); +} + + +static Bool +safe_XKillClient (Display *dpy, XID id) +{ + XErrorHandler old_handler; + XSync (dpy, False); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + + XKillClient (dpy, id); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + + return (!error_handler_hit_p); +} + + +#ifdef HAVE_XF86VMODE +static Bool +safe_XF86VidModeGetViewPort (Display *dpy, int screen, int *xP, int *yP) +{ + Bool result; + XErrorHandler old_handler; + XSync (dpy, False); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + + result = XF86VidModeGetViewPort (dpy, screen, xP, yP); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + + return (error_handler_hit_p + ? False + : result); +} + +/* There is no "safe_XF86VidModeGetModeLine" because it fails with an + untrappable I/O error instead of an X error -- so one must call + safe_XF86VidModeGetViewPort first, and assume that both have the + same error condition. Thank you XFree, may I have another. + */ + +#endif /* HAVE_XF86VMODE */ + + +static void +initialize_screensaver_window_1 (saver_screen_info *ssi) +{ + saver_info *si = ssi->global; + saver_preferences *p = &si->prefs; + Bool install_cmap_p = ssi->install_cmap_p; /* not p->install_cmap_p */ + + /* This resets the screensaver window as fully as possible, since there's + no way of knowing what some random client may have done to us in the + meantime. We could just destroy and recreate the window, but that has + its own set of problems... + */ + XColor black; + XSetWindowAttributes attrs; + unsigned long attrmask; + int x, y, width, height; + static Bool printed_visual_info = False; /* only print the message once. */ + Window horked_window = 0; + + get_screen_viewport (ssi, &x, &y, &width, &height, -1, -1, + (p->verbose_p && !si->screen_blanked_p)); + + black.red = black.green = black.blue = 0; + + if (ssi->cmap == DefaultColormapOfScreen (ssi->screen)) + ssi->cmap = 0; + + if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen)) + /* It's not the default visual, so we have no choice but to install. */ + install_cmap_p = True; + + if (install_cmap_p) + { + if (! ssi->cmap) + { + ssi->cmap = XCreateColormap (si->dpy, + RootWindowOfScreen (ssi->screen), + ssi->current_visual, AllocNone); + if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort (); + ssi->black_pixel = black.pixel; + } + } + else + { + Colormap def_cmap = DefaultColormapOfScreen (ssi->screen); + if (ssi->cmap) { - XClearWindow (dpy, screensaver_window); /* avoid technicolor */ - XInstallColormap (dpy, DefaultColormapOfScreen (screen)); + XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0); + if (ssi->cmap != ssi->demo_cmap && + ssi->cmap != def_cmap) + XFreeColormap (si->dpy, ssi->cmap); } - XUnmapWindow (dpy, screensaver_window); - } - kill_xsetroot_data (); - ungrab_keyboard_and_mouse (); - restore_real_vroot (); -#ifdef __hpux - if (lock_p) - XHPEnableReset (dpy); /* turn C-Sh-Reset back on */ + ssi->cmap = def_cmap; + ssi->black_pixel = BlackPixelOfScreen (ssi->screen); + } + + attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap | + CWBackPixel | CWBackingPixel | CWBorderPixel); + attrs.override_redirect = True; + + /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't + actually be reading these events during normal operation; but we still + need to see Button events for demo-mode to work properly. + */ + attrs.event_mask = (KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask); + + attrs.backing_store = NotUseful; + attrs.colormap = ssi->cmap; + attrs.background_pixel = ssi->black_pixel; + attrs.backing_pixel = ssi->black_pixel; + attrs.border_pixel = ssi->black_pixel; + + if (p->debug_p +# ifdef QUAD_MODE + && !p->quad_p +# endif + ) + width = width / 2; + + if (!p->verbose_p || printed_visual_info) + ; + else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen)) + { + fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number); + describe_visual (stderr, ssi->screen, ssi->current_visual, + install_cmap_p); + } + else + { + fprintf (stderr, "%s: using visual: ", blurb()); + describe_visual (stderr, ssi->screen, ssi->current_visual, + install_cmap_p); + fprintf (stderr, "%s: default visual: ", blurb()); + describe_visual (stderr, ssi->screen, + DefaultVisualOfScreen (ssi->screen), + ssi->install_cmap_p); + } + printed_visual_info = True; + +#ifdef HAVE_MIT_SAVER_EXTENSION + if (si->using_mit_saver_extension) + { + XScreenSaverInfo *info; + Window root = RootWindowOfScreen (ssi->screen); + +#if 0 + /* This call sets the server screensaver timeouts to what we think + they should be (based on the resources and args xscreensaver was + started with.) It's important that we do this to sync back up + with the server - if we have turned on prematurely, as by an + ACTIVATE ClientMessage, then the server may decide to activate + the screensaver while it's already active. That's ok for us, + since we would know to ignore that ScreenSaverActivate event, + but a side effect of this would be that the server would map its + saver window (which we then hide again right away) meaning that + the bits currently on the screen get blown away. Ugly. */ + + /* #### Ok, that doesn't work - when we tell the server that the + screensaver is "off" it sends us a Deactivate event, which is + sensible... but causes the saver to never come on. Hmm. */ + disable_builtin_screensaver (si, True); +#endif /* 0 */ + +#if 0 + /* #### The MIT-SCREEN-SAVER extension gives us access to the + window that the server itself uses for saving the screen. + However, using this window in any way, in particular, calling + XScreenSaverSetAttributes() as below, tends to make the X server + crash. So fuck it, let's try and get along without using it... + + It's also inconvenient to use this window because it doesn't + always exist (though the ID is constant.) So to use this + window, we'd have to reimplement the ACTIVATE ClientMessage to + tell the *server* to tell *us* to turn on, to cause the window + to get created at the right time. Gag. */ + XScreenSaverSetAttributes (si->dpy, root, + 0, 0, width, height, 0, + current_depth, InputOutput, visual, + attrmask, &attrs); + XSync (si->dpy, False); +#endif /* 0 */ + + info = XScreenSaverAllocInfo (); + XScreenSaverQueryInfo (si->dpy, root, info); + ssi->server_mit_saver_window = info->window; + if (! ssi->server_mit_saver_window) abort (); + XFree (info); + } +#endif /* HAVE_MIT_SAVER_EXTENSION */ + + if (ssi->screensaver_window) + { + XWindowChanges changes; + unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth; + changes.x = x; + changes.y = y; + changes.width = width; + changes.height = height; + changes.border_width = 0; + + if (! (safe_XConfigureWindow (si->dpy, ssi->screensaver_window, + changesmask, &changes) && + safe_XChangeWindowAttributes (si->dpy, ssi->screensaver_window, + attrmask, &attrs))) + { + horked_window = ssi->screensaver_window; + ssi->screensaver_window = 0; + } + } + + if (!ssi->screensaver_window) + { + ssi->screensaver_window = + XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen), + x, y, width, height, + 0, ssi->current_depth, InputOutput, + ssi->current_visual, attrmask, &attrs); + + reset_stderr (ssi); + + if (horked_window) + { + fprintf (stderr, + "%s: someone horked our saver window (0x%lx)! Recreating it...\n", + blurb(), (unsigned long) horked_window); + maybe_transfer_grabs (ssi, horked_window, ssi->screensaver_window, + ssi->number); + safe_XDestroyWindow (si->dpy, horked_window); + horked_window = 0; + } + + if (p->verbose_p) + fprintf (stderr, "%s: %d: saver window is 0x%lx.\n", + blurb(), ssi->number, + (unsigned long) ssi->screensaver_window); + } + + store_saver_id (ssi); /* store window name and IDs */ + + if (!ssi->cursor) + { + Pixmap bit; + bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window, + "\000", 1, 1, + BlackPixelOfScreen (ssi->screen), + BlackPixelOfScreen (ssi->screen), + 1); + ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black, + 0, 0); + XFreePixmap (si->dpy, bit); + } + + XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel); + + if (si->demoing_p) + XUndefineCursor (si->dpy, ssi->screensaver_window); + else + XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor); +} + +void +initialize_screensaver_window (saver_info *si) +{ + int i; + for (i = 0; i < si->nscreens; i++) + initialize_screensaver_window_1 (&si->screens[i]); +} + + +/* Called when the RANDR (Resize and Rotate) extension tells us that the + size of the screen has changed while the screen was blanked. If we + don't do this, then the screen saver will no longer fully fill the + screen, and some of the underlying desktop may be visible. + */ +void +resize_screensaver_window (saver_info *si) +{ + saver_preferences *p = &si->prefs; + int i; + + /* First update the size info in the saver_screen_info structs. + */ + +# ifdef HAVE_XINERAMA + if (si->xinerama_p) + { + /* As of XFree86 4.3.0, the RANDR and XINERAMA extensions cannot coexist. + However, maybe they will someday, so I'm guessing that the right thing + to do in that case will be to re-query the Xinerama rectangles after + a RANDR size change is received: presumably, if the resolution of one + or more of the monitors has changed, then the Xinerama rectangle + corresponding to that monitor will also have been updated. + */ + int nscreens; + XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &nscreens); + + if (nscreens != si->nscreens) { + /* Apparently some Xinerama implementations let you use a hot-key + to change the number of screens in use! This is, of course, + documented nowhere. Let's try to do something marginally less + bad than crashing. + */ + fprintf (stderr, "%s: bad craziness: xinerama screen count changed " + "from %d to %d!\n", blurb(), si->nscreens, nscreens); + if (nscreens > si->nscreens) + nscreens = si->nscreens; + } + + if (!xsi) abort(); + for (i = 0; i < nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (p->verbose_p && + (ssi->x != xsi[i].x_org || + ssi->y != xsi[i].y_org || + ssi->width != xsi[i].width || + ssi->height != xsi[i].height)) + fprintf (stderr, + "%s: %d: resize xinerama from %dx%d+%d+%d to %dx%d+%d+%d\n", + blurb(), i, + ssi->width, ssi->height, ssi->x, ssi->y, + xsi[i].width, xsi[i].height, xsi[i].x_org, xsi[i].y_org); + + ssi->x = xsi[i].x_org; + ssi->y = xsi[i].y_org; + ssi->width = xsi[i].width; + ssi->height = xsi[i].height; + } + XFree (xsi); + } + else +# endif /* HAVE_XINERAMA */ + { + /* Not Xinerama -- get the real sizes of the root windows. */ + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + XWindowAttributes xgwa; + XGetWindowAttributes (si->dpy, RootWindowOfScreen (ssi->screen), + &xgwa); + + if (p->verbose_p && + (ssi->x != xgwa.x || + ssi->y != xgwa.y || + ssi->width != xgwa.width || + ssi->height != xgwa.height)) + fprintf (stderr, + "%s: %d: resize screen from %dx%d+%d+%d to %dx%d+%d+%d\n", + blurb(), i, + ssi->width, ssi->height, ssi->x, ssi->y, + xgwa.width, xgwa.height, xgwa.x, xgwa.y); + + ssi->x = xgwa.x; + ssi->y = xgwa.y; + ssi->width = xgwa.width; + ssi->height = xgwa.height; + } + } + + /* Next, ensure that the screensaver windows are the right size, taking + into account both the new size of the screen in question's root window, + and any viewport within that. + */ + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + XWindowAttributes xgwa; + XWindowChanges changes; + int x, y, width, height; + unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth; + + XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa); + get_screen_viewport (ssi, &x, &y, &width, &height, -1, -1, + (p->verbose_p && !si->screen_blanked_p)); + if (xgwa.x == x && + xgwa.y == y && + xgwa.width == width && + xgwa.height == height) + continue; /* no change! */ + + changes.x = x; + changes.y = y; + changes.width = width; + changes.height = height; + changes.border_width = 0; + + if (p->debug_p +# ifdef QUAD_MODE + && !p->quad_p +# endif + ) + changes.width = changes.width / 2; + + if (p->verbose_p) + fprintf (stderr, + "%s: %d: resize 0x%lx from %dx%d+%d+%d to %dx%d+%d+%d\n", + blurb(), i, (unsigned long) ssi->screensaver_window, + xgwa.width, xgwa.height, xgwa.x, xgwa.y, + width, height, x, y); + if (! safe_XConfigureWindow (si->dpy, ssi->screensaver_window, + changesmask, &changes)) + { + fprintf (stderr, + "%s: %d: someone horked our saver window (0x%lx)! Unable to resize it!\n", + blurb(), i, (unsigned long) ssi->screensaver_window); + } + } +} + + +void +raise_window (saver_info *si, + Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear) +{ + saver_preferences *p = &si->prefs; + int i; + + if (si->demoing_p) + inhibit_fade = True; + + if (si->emergency_lock_p) + inhibit_fade = True; + + if (!dont_clear) + initialize_screensaver_window (si); + + reset_watchdog_timer (si, True); + + if (p->fade_p && si->fading_possible_p && !inhibit_fade) + { + Window *current_windows = (Window *) + calloc(sizeof(Window), si->nscreens); + Colormap *current_maps = (Colormap *) + calloc(sizeof(Colormap), si->nscreens); + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + current_windows[i] = ssi->screensaver_window; + current_maps[i] = (between_hacks_p + ? ssi->cmap + : DefaultColormapOfScreen (ssi->screen)); + /* Ensure that the default background of the window is really black, + not a pixmap or something. (This does not clear the window.) */ + XSetWindowBackground (si->dpy, ssi->screensaver_window, + ssi->black_pixel); + } + + if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb()); + + XGrabServer (si->dpy); /* ############ DANGER! */ + + /* Clear the stderr layer on each screen. + */ + if (!dont_clear) + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (ssi->stderr_overlay_window) + /* Do this before the fade, since the stderr cmap won't fade + even if we uninstall it (beats me...) */ + clear_stderr (ssi); + } + + /* Note! The server is grabbed, and this will take several seconds + to complete! */ + fade_screens (si->dpy, current_maps, + current_windows, si->nscreens, + p->fade_seconds/1000, p->fade_ticks, True, !dont_clear); + + free(current_maps); + free(current_windows); + current_maps = 0; + current_windows = 0; + + if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb()); + +#ifdef HAVE_MIT_SAVER_EXTENSION + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (ssi->server_mit_saver_window && + window_exists_p (si->dpy, ssi->server_mit_saver_window)) + XUnmapWindow (si->dpy, ssi->server_mit_saver_window); + } +#endif /* HAVE_MIT_SAVER_EXTENSION */ + + XUngrabServer (si->dpy); + XSync (si->dpy, False); /* ###### (danger over) */ + } + else + { + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (!dont_clear) + XClearWindow (si->dpy, ssi->screensaver_window); + if (!dont_clear || ssi->stderr_overlay_window) + clear_stderr (ssi); + XMapRaised (si->dpy, ssi->screensaver_window); +#ifdef HAVE_MIT_SAVER_EXTENSION + if (ssi->server_mit_saver_window && + window_exists_p (si->dpy, ssi->server_mit_saver_window)) + XUnmapWindow (si->dpy, ssi->server_mit_saver_window); +#endif /* HAVE_MIT_SAVER_EXTENSION */ + } + } + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (ssi->cmap) + XInstallColormap (si->dpy, ssi->cmap); + } +} + + +int +mouse_screen (saver_info *si) +{ + saver_preferences *p = &si->prefs; + Window pointer_root, pointer_child; + int root_x, root_y, win_x, win_y; + unsigned int mask; + int i; + + if (si->nscreens == 1) + return 0; + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (XQueryPointer (si->dpy, RootWindowOfScreen (ssi->screen), + &pointer_root, &pointer_child, + &root_x, &root_y, &win_x, &win_y, &mask) && + root_x >= ssi->x && + root_y >= ssi->y && + root_x < ssi->x + ssi->width && + root_y < ssi->y + ssi->height) + { + if (p->verbose_p) + fprintf (stderr, "%s: mouse is on screen %d of %d\n", + blurb(), i, si->nscreens); + return i; + } + } + + /* couldn't figure out where the mouse is? Oh well. */ + return 0; +} + + +Bool +blank_screen (saver_info *si) +{ + int i; + Bool ok; + Window w; + int mscreen; + + /* Note: we do our grabs on the root window, not on the screensaver window. + If we grabbed on the saver window, then the demo mode and lock dialog + boxes wouldn't get any events. + + By "the root window", we mean "the root window that contains the mouse." + We use to always grab the mouse on screen 0, but that has the effect of + moving the mouse to screen 0 from whichever screen it was on, on + multi-head systems. + */ + mscreen = mouse_screen (si); + w = RootWindowOfScreen(si->screens[mscreen].screen); + ok = grab_keyboard_and_mouse (si, w, + (si->demoing_p ? 0 : si->screens[0].cursor), + mscreen); + + +# if 0 + if (si->using_mit_saver_extension || si->using_sgi_saver_extension) + /* If we're using a server extension, then failure to get a grab is + not a big deal -- even without the grab, we will still be able + to un-blank when there is user activity, since the server will + tell us. */ + /* #### No, that's not true: if we don't have a keyboard grab, + then we can't read passwords to unlock. + */ + ok = True; +# endif /* 0 */ + + if (!ok) + return False; + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (ssi->real_screen_p) + save_real_vroot (ssi); + store_vroot_property (si->dpy, + ssi->screensaver_window, + ssi->screensaver_window); + +#ifdef HAVE_XF86VMODE + { + int ev, er; + if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) || + !safe_XF86VidModeGetViewPort (si->dpy, i, + &ssi->blank_vp_x, + &ssi->blank_vp_y)) + ssi->blank_vp_x = ssi->blank_vp_y = -1; + } +#endif /* HAVE_XF86VMODE */ + } + + raise_window (si, False, False, False); + + si->screen_blanked_p = True; + si->blank_time = time ((time_t) 0); + si->last_wall_clock_time = 0; + + store_saver_status (si); /* store blank time */ + + return True; +} + + +void +unblank_screen (saver_info *si) +{ + saver_preferences *p = &si->prefs; + Bool unfade_p = (si->fading_possible_p && p->unfade_p); + int i; + + monitor_power_on (si); + reset_watchdog_timer (si, False); + + if (si->demoing_p) + unfade_p = False; + + if (unfade_p) + { + Window *current_windows = (Window *) + calloc(sizeof(Window), si->nscreens); + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + current_windows[i] = ssi->screensaver_window; + /* Ensure that the default background of the window is really black, + not a pixmap or something. (This does not clear the window.) */ + XSetWindowBackground (si->dpy, ssi->screensaver_window, + ssi->black_pixel); + } + + if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb()); + + + XSync (si->dpy, False); + XGrabServer (si->dpy); /* ############ DANGER! */ + XSync (si->dpy, False); + + /* Clear the stderr layer on each screen. + */ + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + clear_stderr (ssi); + } + + XUngrabServer (si->dpy); + XSync (si->dpy, False); /* ###### (danger over) */ + + fade_screens (si->dpy, 0, + current_windows, si->nscreens, + p->fade_seconds/1000, p->fade_ticks, + False, False); + + free(current_windows); + current_windows = 0; + + if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb()); + } + else + { + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (ssi->cmap) + { + Colormap c = DefaultColormapOfScreen (ssi->screen); + /* avoid technicolor */ + XClearWindow (si->dpy, ssi->screensaver_window); + if (c) XInstallColormap (si->dpy, c); + } + XUnmapWindow (si->dpy, ssi->screensaver_window); + } + } + + + /* If the focus window does has a non-default colormap, then install + that colormap as well. (On SGIs, this will cause both the root map + and the focus map to be installed simultaneously. It'd be nice to + pick up the other colormaps that had been installed, too; perhaps + XListInstalledColormaps could be used for that?) + */ + { + Window focus = 0; + int revert_to; + XGetInputFocus (si->dpy, &focus, &revert_to); + if (focus && focus != PointerRoot && focus != None) + { + XWindowAttributes xgwa; + xgwa.colormap = 0; + XGetWindowAttributes (si->dpy, focus, &xgwa); + if (xgwa.colormap && + xgwa.colormap != DefaultColormapOfScreen (xgwa.screen)) + XInstallColormap (si->dpy, xgwa.colormap); + } + } + + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p); + } + + store_saver_status (si); /* store unblank time */ + ungrab_keyboard_and_mouse (si); + restore_real_vroot (si); + + /* Unmap the windows a second time, dammit -- just to avoid a race + with the screen-grabbing hacks. (I'm not sure if this is really + necessary; I'm stabbing in the dark now.) + */ + for (i = 0; i < si->nscreens; i++) + XUnmapWindow (si->dpy, si->screens[i].screensaver_window); + + si->screen_blanked_p = False; + si->blank_time = time ((time_t) 0); + si->last_wall_clock_time = 0; + + store_saver_status (si); /* store unblank time */ +} + + +/* Transfer any grabs from the old window to the new. + Actually I think none of this is necessary, since we always + hold our grabs on the root window, but I wrote this before + re-discovering that... + */ +static void +maybe_transfer_grabs (saver_screen_info *ssi, + Window old_w, Window new_w, + int new_screen_no) +{ + saver_info *si = ssi->global; + + /* If the old window held our mouse grab, transfer the grab to the new + window. (Grab the server while so doing, to avoid a race condition.) + */ + if (old_w == si->mouse_grab_window) + { + XGrabServer (si->dpy); /* ############ DANGER! */ + ungrab_mouse (si); + grab_mouse (si, ssi->screensaver_window, + (si->demoing_p ? 0 : ssi->cursor), + new_screen_no); + XUngrabServer (si->dpy); + XSync (si->dpy, False); /* ###### (danger over) */ + } + + /* If the old window held our keyboard grab, transfer the grab to the new + window. (Grab the server while so doing, to avoid a race condition.) + */ + if (old_w == si->keyboard_grab_window) + { + XGrabServer (si->dpy); /* ############ DANGER! */ + ungrab_kbd(si); + grab_kbd(si, ssi->screensaver_window, ssi->number); + XUngrabServer (si->dpy); + XSync (si->dpy, False); /* ###### (danger over) */ + } +} + + + +Bool +select_visual (saver_screen_info *ssi, const char *visual_name) +{ + saver_info *si = ssi->global; + saver_preferences *p = &si->prefs; + Bool install_cmap_p = p->install_cmap_p; + Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen)); + Visual *new_v = 0; + Bool got_it; + + /* On some systems (most recently, MacOS X) OpenGL programs get confused + when you kill one and re-start another on the same window. So maybe + it's best to just always destroy and recreate the xscreensaver window + when changing hacks, instead of trying to reuse the old one? + */ + Bool always_recreate_window_p = True; + + if (visual_name && *visual_name) + { + if (!strcmp(visual_name, "default-i") || + !strcmp(visual_name, "Default-i") || + !strcmp(visual_name, "Default-I") + ) + { + visual_name = "default"; + install_cmap_p = True; + } + else if (!strcmp(visual_name, "default-n") || + !strcmp(visual_name, "Default-n") || + !strcmp(visual_name, "Default-N")) + { + visual_name = "default"; + install_cmap_p = False; + } + else if (!strcmp(visual_name, "gl") || + !strcmp(visual_name, "Gl") || + !strcmp(visual_name, "GL")) + { + new_v = ssi->best_gl_visual; + if (!new_v && p->verbose_p) + fprintf (stderr, "%s: no GL visuals.\n", progname); + } + + if (!new_v) + new_v = get_visual (ssi->screen, visual_name, True, False); + } + else + { + new_v = ssi->default_visual; + } + + got_it = !!new_v; + + if (new_v && new_v != DefaultVisualOfScreen(ssi->screen)) + /* It's not the default visual, so we have no choice but to install. */ + install_cmap_p = True; + + ssi->install_cmap_p = install_cmap_p; + + if (new_v && + (always_recreate_window_p || + (ssi->current_visual != new_v) || + (install_cmap_p != was_installed_p))) + { + Colormap old_c = ssi->cmap; + Window old_w = ssi->screensaver_window; + + if (p->verbose_p) + { + fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number); + describe_visual (stderr, ssi->screen, new_v, install_cmap_p); +#if 0 + fprintf (stderr, "%s: from ", blurb()); + describe_visual (stderr, ssi->screen, ssi->current_visual, + was_installed_p); #endif + } + + reset_stderr (ssi); + ssi->current_visual = new_v; + ssi->current_depth = visual_depth(ssi->screen, new_v); + ssi->cmap = 0; + ssi->screensaver_window = 0; + + initialize_screensaver_window_1 (ssi); + + /* stderr_overlay_window is a child of screensaver_window, so we need + to destroy that as well (actually, we just need to invalidate and + drop our pointers to it, but this will destroy it, which is ok so + long as it happens before old_w itself is destroyed.) */ + reset_stderr (ssi); + + raise_window (si, True, True, False); + store_vroot_property (si->dpy, + ssi->screensaver_window, ssi->screensaver_window); + + /* Transfer any grabs from the old window to the new. */ + maybe_transfer_grabs (ssi, old_w, ssi->screensaver_window, ssi->number); + + /* Now we can destroy the old window without horking our grabs. */ + XDestroyWindow (si->dpy, old_w); + + if (p->verbose_p) + fprintf (stderr, "%s: %d: destroyed old saver window 0x%lx.\n", + blurb(), ssi->number, (unsigned long) old_w); + + if (old_c && + old_c != DefaultColormapOfScreen (ssi->screen) && + old_c != ssi->demo_cmap) + XFreeColormap (si->dpy, old_c); + } + + return got_it; }