X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Fwindows.c;h=29edc82b66b1349fb3a3f6879a83fbd6f69ea4f6;hb=6afd6db0ae9396cd7ff897ade597cd5483f49b0e;hp=a460e6da4937377d9727e951b449c6d4439d60b1;hpb=481b95e2617b69e6fd4444432747d7e1e0c3dc85;p=xscreensaver diff --git a/driver/windows.c b/driver/windows.c index a460e6da..29edc82b 100644 --- a/driver/windows.c +++ b/driver/windows.c @@ -1,5 +1,5 @@ /* windows.c --- turning the screen black; dealing with visuals, virtual roots. - * xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski + * xscreensaver, Copyright (c) 1991-2014 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 @@ -17,38 +17,46 @@ #ifdef VMS # include /* for getpid() */ # include "vms-gtod.h" /* for gettimeofday() */ -# if !defined(HAVE_UNAME) && (__VMS_VER >= 70000000) -# define HAVE_UNAME 1 -# endif /* !HAVE_UNAME */ #endif /* VMS */ -# ifdef HAVE_UNAME -# include /* for uname() */ -# endif /* HAVE_UNAME */ +#ifndef VMS +# 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 / * 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_XHPDISABLERESET -# include - - /* Calls to XHPDisableReset and XHPEnableReset must be balanced, - or BadAccess errors occur. (Ok for this to be global, since it - affects the whole machine, not just the current screen.) */ - Bool hp_locked_p = False; - -#endif /* HAVE_XHPDISABLERESET */ - +#ifdef HAVE_XINERAMA +# include +#endif /* HAVE_XINERAMA */ /* This file doesn't need the Xt headers, so stub these types out... */ #undef XtPointer @@ -62,17 +70,18 @@ #include "visual.h" #include "fade.h" + extern int kill (pid_t, int); /* signal() is in sys/signal.h... */ -Atom XA_VROOT, XA_XSETROOT_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_TIME; +Atom XA_SCREENSAVER_STATUS; extern saver_info *global_si_kludge; /* I hate C so much... */ - -static void store_activate_time (saver_info *si, Bool use_last_p); +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 | \ @@ -81,41 +90,16 @@ static void store_activate_time (saver_info *si, Bool use_last_p); Button4MotionMask | Button5MotionMask | ButtonMotionMask) -static int -grab_kbd(saver_info *si, Window w) -{ - 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; - - if (p->debug_p) - fprintf(stderr, "%s: XGrabKeyboard(... 0x%x ...) ==> %s\n", - blurb(), (unsigned long) w, - (status == GrabSuccess ? "GrabSuccess" : - status == AlreadyGrabbed ? "AlreadyGrabbed" : - status == GrabInvalidTime ? "GrabInvalidTime" : - status == GrabNotViewable ? "GrabNotViewable" : - status == GrabFrozen ? "GrabFrozen" : - "???")); - - return status; -} - static const char * grab_string(int status) { switch (status) { - case GrabSuccess: return "GrabSuccess"; break; - case AlreadyGrabbed: return "AlreadyGrabbed"; break; - case GrabInvalidTime: return "GrabInvalidTime"; break; - case GrabNotViewable: return "GrabNotViewable"; break; - case GrabFrozen: return "GrabFrozen"; break; + 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]; @@ -125,21 +109,44 @@ grab_string(int status) } } +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) +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, None, + GrabModeAsync, GrabModeAsync, w, cursor, CurrentTime); if (status == GrabSuccess) - si->mouse_grab_window = w; + { + si->mouse_grab_window = w; + si->mouse_grab_screen = screen_no; + } - if (p->debug_p) - fprintf(stderr, "%s: XGrabPointer(... 0x%x, 0x%x ...) ==> %s\n", - blurb(), (unsigned long) w, (unsigned long) cursor, - grab_string(status)); + 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; } @@ -149,9 +156,10 @@ ungrab_kbd(saver_info *si) { saver_preferences *p = &si->prefs; XUngrabKeyboard(si->dpy, CurrentTime); - if (p->debug_p) - fprintf(stderr, "%s: XUngrabKeyboard (was 0x%x)\n", blurb(), - (unsigned long) si->keyboard_grab_window); + 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; } @@ -161,45 +169,198 @@ ungrab_mouse(saver_info *si) { saver_preferences *p = &si->prefs; XUngrabPointer(si->dpy, CurrentTime); - if (p->debug_p) - fprintf(stderr, "%s: XUngrabPointer (was 0x%x)\n", blurb(), - (unsigned long) si->mouse_grab_window); + 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; } -void -grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor) +/* 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) { - Status status; + 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); +} + + +static void +ungrab_keyboard_and_mouse (saver_info *si) +{ + ungrab_mouse (si); + ungrab_kbd (si); +} + - status = grab_kbd (si, 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 (si, window); - if (status != GrabSuccess) - fprintf (stderr, "%s: couldn't grab keyboard! (%s)\n", - blurb(), grab_string(status)); } - status = grab_mouse (si, window, cursor); - 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 (si, window, cursor); - if (status != GrabSuccess) - fprintf (stderr, "%s: couldn't grab pointer! (%s)\n", - blurb(), grab_string(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 mandatory; 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 mandatory. + (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. + + It has been suggested that we should allow blanking if locking + is disabled, and we have a mouse grab but no keyboard grab + (that is: kstatus != GrabSuccess && + mstatus == GrabSuccess && + si->locking_disabled_p) + That would allow screen blanking (but not locking) while the gdm + login screen had the keyboard grabbed, but one would have to use + the mouse to unblank. Keyboard characters would go to the gdm + login field without unblanking. I have not made this change + because I'm not completely convinced it is a safe thing to do. + */ + + if (kstatus != GrabSuccess) /* Do not blank without a kbd grab. */ + { + /* If we didn't get both grabs, release the one we did get. */ + ungrab_keyboard_and_mouse (si); + return False; + } + + return True; /* Grab is good, go ahead and blank. */ } -void -ungrab_keyboard_and_mouse (saver_info *si) + +int +move_mouse_grab (saver_info *si, Window to, Cursor cursor, int to_screen_no) { - ungrab_mouse (si); - ungrab_kbd (si); + 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; + } } @@ -226,26 +387,42 @@ ensure_no_screensaver_running (Display *dpy, Screen *screen) 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; - if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512, + unsigned char *id; + if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512, False, XA_STRING, &type, &format, &nitems, - &bytesafter, (unsigned char **) &id) - == Success + &bytesafter, &id) + != Success || type == None) - id = "???"; + id = (unsigned char *) "???"; fprintf (stderr, "%s: already running on display %s (window 0x%x)\n from process %s.\n", - blurb(), DisplayString (dpy), (int) kids [i], id); + blurb(), DisplayString (dpy), (int) kids [i], + (char *) id); + status = True; + } + + else if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128, + False, XA_STRING, &type, &format, &nitems, + &bytesafter, &version) + == Success + && type != None + && !strcmp ((char *) version, "gnome-screensaver")) + { + fprintf (stderr, + "%s: \"%s\" is already running on display %s (window 0x%x)\n", + blurb(), (char *) version, + DisplayString (dpy), (int) kids [i]); status = True; + break; } } @@ -297,13 +474,17 @@ remove_vroot_property (Display *dpy, Window win) } +static Bool safe_XKillClient (Display *dpy, XID id); + static void -kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p) +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 @@ -316,31 +497,47 @@ kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p) 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, 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%lX).\n", - blurb(), *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: deleted unrecognised _XSETROOT_ID property: \n\ - %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n", - blurb(), (unsigned long) 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 (saver_info *si, 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 (saver_screen_info *ssi) @@ -352,6 +549,16 @@ save_real_vroot (saver_screen_info *ssi) 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); ssi->real_vroot = 0; ssi->real_vroot_value = 0; @@ -366,15 +573,29 @@ save_real_vroot (saver_screen_info *ssi) 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; + + vrootP = (Window *) dataP; if (ssi->real_vroot) { if (*vrootP == ssi->screensaver_window) abort (); @@ -385,11 +606,16 @@ save_real_vroot (saver_screen_info *ssi) } ssi->real_vroot = kids [i]; ssi->real_vroot_value = *vrootP; + SKIP: + ; } + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + if (ssi->real_vroot) { - handle_signals (si, True); remove_vroot_property (si->dpy, ssi->real_vroot); XSync (dpy, False); } @@ -399,14 +625,16 @@ save_real_vroot (saver_screen_info *ssi) static Bool -restore_real_vroot_2 (saver_screen_info *ssi) +restore_real_vroot_1 (saver_screen_info *ssi) { saver_info *si = ssi->global; saver_preferences *p = &si->prefs; if (p->verbose_p && ssi->real_vroot) - printf ("%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); + fprintf (stderr, + "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n", + blurb(), (unsigned long) ssi->real_vroot); + if (ssi->screensaver_window) + remove_vroot_property (si->dpy, ssi->screensaver_window); if (ssi->real_vroot) { store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value); @@ -421,27 +649,20 @@ restore_real_vroot_2 (saver_screen_info *ssi) return False; } -static Bool -restore_real_vroot_1 (saver_info *si) +Bool +restore_real_vroot (saver_info *si) { int i; Bool did_any = False; for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; - if (restore_real_vroot_2 (ssi)) + if (restore_real_vroot_1 (ssi)) did_any = True; } return did_any; } -void -restore_real_vroot (saver_info *si) -{ - if (restore_real_vroot_1 (si)) - handle_signals (si, False); -} - /* Signal hackery to ensure that the vroot doesn't get left in an inconsistent state @@ -528,70 +749,115 @@ 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 (si)) + if (restore_real_vroot (si)) fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n", blurb(), signal_name(sig)); kill (getpid (), sig); } static void -catch_signal (saver_info *si, int sig, Bool on_p) +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 */ { - if (((long) signal (sig, restore_real_vroot_handler)) == -1L) - { - char buf [255]; - sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig)); - perror (buf); - saver_exit (si, 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 (saver_info *si, Bool on_p) +static RETSIGTYPE saver_sighup_handler (int sig); + +void +handle_signals (saver_info *si) { -#if 0 - if (on_p) printf ("handling signals\n"); - else printf ("unhandling signals\n"); + 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, SIGHUP, on_p); - catch_signal (si, SIGINT, on_p); - catch_signal (si, SIGQUIT, on_p); - catch_signal (si, SIGILL, on_p); - catch_signal (si, SIGTRAP, on_p); - catch_signal (si, SIGIOT, on_p); - catch_signal (si, SIGABRT, on_p); + catch_signal (si, SIGABRT, restore_real_vroot_handler); #ifdef SIGEMT - catch_signal (si, SIGEMT, on_p); + catch_signal (si, SIGEMT, restore_real_vroot_handler); #endif - catch_signal (si, SIGFPE, on_p); - catch_signal (si, SIGBUS, on_p); - catch_signal (si, 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 (si, SIGSYS, on_p); + catch_signal (si, SIGSYS, restore_real_vroot_handler); #endif - catch_signal (si, SIGTERM, on_p); + catch_signal (si, SIGTERM, restore_real_vroot_handler); #ifdef SIGXCPU - catch_signal (si, SIGXCPU, on_p); + catch_signal (si, SIGXCPU, restore_real_vroot_handler); #endif #ifdef SIGXFSZ - catch_signal (si, SIGXFSZ, on_p); + catch_signal (si, SIGXFSZ, restore_real_vroot_handler); #endif #ifdef SIGDANGER - catch_signal (si, SIGDANGER, on_p); + catch_signal (si, SIGDANGER, restore_real_vroot_handler); #endif } + +static RETSIGTYPE +saver_sighup_handler (int sig) +{ + saver_info *si = global_si_kludge; /* I hate C so much... */ + + /* Re-establish SIGHUP handler */ + catch_signal (si, SIGHUP, saver_sighup_handler); + + fprintf (stderr, "%s: %s received: restarting...\n", + blurb(), signal_name(sig)); + + if (si->screen_blanked_p) + { + int i; + for (i = 0; i < si->nscreens; i++) + kill_screenhack (&si->screens[i]); + unblank_screen (si); + XSync (si->dpy, False); + } + + restart_process (si); /* Does not return */ + abort (); +} + + + void -saver_exit (saver_info *si, int status) +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; if (exiting) @@ -599,13 +865,12 @@ saver_exit (saver_info *si, int status) exiting = True; - vrs = restore_real_vroot_1 (si); + vrs = restore_real_vroot (si); emergency_kill_subproc (si); + shutdown_stderr (si); - if (vrs && (p->verbose_p || status != 0)) - fprintf (real_stderr, "%s: vroot restored, exiting.\n", blurb()); - else if (p->verbose_p) - fprintf (real_stderr, "%s: no vroot to restore; exiting.\n", blurb()); + if (p->verbose_p && vrs) + fprintf (real_stderr, "%s: old vroot restored.\n", blurb()); fflush(real_stdout); @@ -614,9 +879,38 @@ saver_exit (saver_info *si, int status) else if (status == 1) status = -1; #endif - if (si->prefs.debug_p) + bugp = !!dump_core_reason; + + if (si->prefs.debug_p && !dump_core_reason) + dump_core_reason = "because of -debug"; + + if (dump_core_reason) { - fprintf(real_stderr, "%s: dumping core (because of -debug)\n", blurb()); + /* 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(); } @@ -640,12 +934,228 @@ window_exists_p (Display *dpy, Window window) return (xgwa.screen != 0); } +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); +} + + +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 +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 || p->install_cmap_p); + 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 @@ -653,13 +1163,10 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) its own set of problems... */ XColor black; - XClassHint class_hints; XSetWindowAttributes attrs; unsigned long attrmask; - int width = WidthOfScreen (ssi->screen); - int height = HeightOfScreen (ssi->screen); - char id [2048]; static Bool printed_visual_info = False; /* only print the message once. */ + Window horked_window = 0; black.red = black.green = black.blue = 0; @@ -667,13 +1174,15 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) 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->cmap = XCreateColormap (si->dpy, + RootWindowOfScreen (ssi->screen), ssi->current_visual, AllocNone); if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort (); ssi->black_pixel = black.pixel; @@ -711,13 +1220,11 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) attrs.backing_pixel = ssi->black_pixel; attrs.border_pixel = ssi->black_pixel; - if (p->debug_p) width = width / 2; - if (!p->verbose_p || printed_visual_info) ; else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen)) { - fprintf (stderr, "%s: using default visual ", blurb()); + fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number); describe_visual (stderr, ssi->screen, ssi->current_visual, install_cmap_p); } @@ -734,7 +1241,7 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) printed_visual_info = True; #ifdef HAVE_MIT_SAVER_EXTENSION - if (p->use_mit_saver_extension) + if (si->using_mit_saver_extension) { XScreenSaverInfo *info; Window root = RootWindowOfScreen (ssi->screen); @@ -788,92 +1295,69 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) { XWindowChanges changes; unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth; - changes.x = 0; - changes.y = 0; - changes.width = width; - changes.height = height; + changes.x = ssi->x; + changes.y = ssi->y; + changes.width = ssi->width; + changes.height = ssi->height; changes.border_width = 0; - XConfigureWindow (si->dpy, ssi->screensaver_window, - changesmask, &changes); - XChangeWindowAttributes (si->dpy, ssi->screensaver_window, - attrmask, &attrs); + 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; + } } - else + + if (!ssi->screensaver_window) { ssi->screensaver_window = - XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen), 0, 0, - width, height, 0, ssi->current_depth, InputOutput, + XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen), + ssi->x, ssi->y, ssi->width, ssi->height, + 0, ssi->current_depth, InputOutput, ssi->current_visual, attrmask, &attrs); reset_stderr (ssi); - store_activate_time(si, True); + + 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: saver window is 0x%lx.\n", - blurb(), (unsigned long) ssi->screensaver_window); + fprintf (stderr, "%s: %d: saver window is 0x%lx.\n", + blurb(), ssi->number, + (unsigned long) ssi->screensaver_window); } -#ifdef HAVE_MIT_SAVER_EXTENSION - if (!p->use_mit_saver_extension || - window_exists_p (si->dpy, ssi->screensaver_window)) - /* When using the MIT-SCREEN-SAVER extension, the window pointed to - by screensaver_window only exists while the saver is active. - So we must be careful to only try and manipulate it while it - exists... - (#### The above comment would be true if the MIT extension actually - worked, but it's not true today -- see `server_mit_saver_window'.) - */ -#endif /* HAVE_MIT_SAVER_EXTENSION */ - { - 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"); - XChangeProperty (si->dpy, ssi->screensaver_window, - XA_SCREENSAVER_VERSION, - XA_STRING, 8, PropModeReplace, - (unsigned char *) si->version, - strlen (si->version)); - - sprintf (id, "%lu on host ", (unsigned long) getpid ()); - -# if defined(HAVE_UNAME) - { - struct utsname uts; - if (uname (&uts) < 0) - strcat (id, "???"); - else - strcat (id, uts.nodename); - } -# elif defined(VMS) - strcat (id, getenv("SYS$NODE")); -# else /* !HAVE_UNAME && !VMS */ - strcat (id, "???"); -# endif /* !HAVE_UNAME && !VMS */ + store_saver_id (ssi); /* store window name and IDs */ - XChangeProperty (si->dpy, ssi->screensaver_window, - XA_SCREENSAVER_ID, XA_STRING, - 8, PropModeReplace, (unsigned char *) id, strlen (id)); + 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); + } - 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); - XSetWindowBackground (si->dpy, ssi->screensaver_window, - ssi->black_pixel); - if (si->demo_mode_p) - XUndefineCursor (si->dpy, ssi->screensaver_window); - else - XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor); - } + if (si->demoing_p) + XUndefineCursor (si->dpy, ssi->screensaver_window); + else + XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor); } void @@ -885,6 +1369,113 @@ initialize_screensaver_window (saver_info *si) } +/* Called when the RANDR (Resize and Rotate) extension tells us that + the size of the screen has changed while the screen was blanked. + Call update_screen_layout() first, then call this to synchronize + the size of the saver windows to the new sizes of the screens. + */ +void +resize_screensaver_window (saver_info *si) +{ + saver_preferences *p = &si->prefs; + int i; + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + XWindowAttributes xgwa; + + /* Make sure a window exists -- it might not if a monitor was just + added for the first time. + */ + if (! ssi->screensaver_window) + { + initialize_screensaver_window_1 (ssi); + if (p->verbose_p) + fprintf (stderr, + "%s: %d: newly added window 0x%lx %dx%d+%d+%d\n", + blurb(), i, (unsigned long) ssi->screensaver_window, + ssi->width, ssi->height, ssi->x, ssi->y); + } + + /* Make sure the window is the right size -- it might not be if + the monitor changed resolution, or if a badly-behaved hack + screwed with it. + */ + XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa); + if (xgwa.x != ssi->x || + xgwa.y != ssi->y || + xgwa.width != ssi->width || + xgwa.height != ssi->height) + { + XWindowChanges changes; + unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth; + changes.x = ssi->x; + changes.y = ssi->y; + changes.width = ssi->width; + changes.height = ssi->height; + changes.border_width = 0; + + 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, + ssi->width, ssi->height, ssi->x, ssi->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); + } + + /* Now (if blanked) make sure that it's mapped and running a hack -- + it might not be if we just added it. (We also might be re-using + an old window that existed for a previous monitor that was + removed and re-added.) + + Note that spawn_screenhack() calls select_visual() which may destroy + and re-create the window via initialize_screensaver_window_1(). + */ + if (si->screen_blanked_p) + { + if (ssi->cmap) + XInstallColormap (si->dpy, ssi->cmap); + XMapRaised (si->dpy, ssi->screensaver_window); + if (! ssi->pid) + spawn_screenhack (ssi); + + /* Make sure the act of adding a screen doesn't present as + pointer motion (and thus cause an unblank). */ + { + Window root, child; + int x, y; + unsigned int mask; + XQueryPointer (si->dpy, ssi->screensaver_window, &root, &child, + &ssi->last_poll_mouse.root_x, + &ssi->last_poll_mouse.root_y, + &x, &y, &mask); + } + } + } + + /* Kill off any savers running on no-longer-extant monitors. + */ + for (; i < si->ssi_count; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (ssi->pid) + kill_screenhack (ssi); + if (ssi->screensaver_window) + { + XUnmapWindow (si->dpy, ssi->screensaver_window); + restore_real_vroot_1 (ssi); + } + } +} + + void raise_window (saver_info *si, Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear) @@ -892,12 +1483,19 @@ raise_window (saver_info *si, saver_preferences *p = &si->prefs; int i; - initialize_screensaver_window (si); + 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 && !inhibit_fade && !si->demo_mode_p) + if (p->fade_p && si->fading_possible_p && !inhibit_fade) { - int grabbed = -1; Window *current_windows = (Window *) calloc(sizeof(Window), si->nscreens); Colormap *current_maps = (Colormap *) @@ -916,49 +1514,34 @@ raise_window (saver_info *si, ssi->black_pixel); } - if (p->verbose_p) fprintf (stderr, "%s: fading... ", blurb()); + if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb()); XGrabServer (si->dpy); /* ############ DANGER! */ /* Clear the stderr layer on each screen. - Grab the mouse on the first screen on which the mouse is grabbable - (if there are multiple heads, I think you might only be able to - grab the mouse on the screen that currently has the mouse? Anyway, - we only grab the mouse once, and don't try again after the grab - has succeeded.) We grab the mouse on the root window of the screen, - not on the screensaver window, since the screensaver window is not - yet mapped. */ - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - - /* grab and blacken mouse on the root window (saver not mapped yet) - */ - if (grabbed != GrabSuccess) - { - Window root = RootWindowOfScreen(ssi->screen); - grabbed = grab_mouse (si, root, - (si->demo_mode_p ? 0 : ssi->cursor)); - } - - if (!dont_clear || 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); - } + 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, - p->fade_seconds, p->fade_ticks, True, !dont_clear); + 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, "fading done.\n"); + if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb()); #ifdef HAVE_MIT_SAVER_EXTENSION for (i = 0; i < si->nscreens; i++) @@ -970,10 +1553,6 @@ raise_window (saver_info *si, } #endif /* HAVE_MIT_SAVER_EXTENSION */ - /* If we had successfully grabbed the mouse, let it go now. */ - if (grabbed == GrabSuccess) - ungrab_mouse (si); - XUngrabServer (si->dpy); XSync (si->dpy, False); /* ###### (danger over) */ } @@ -1003,56 +1582,129 @@ raise_window (saver_info *si, } } -void -blank_screen (saver_info *si) + +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]; - - save_real_vroot (ssi); - store_vroot_property (si->dpy, - ssi->screensaver_window, - ssi->screensaver_window); + 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; + } } - store_activate_time (si, si->screen_blanked_p); - raise_window (si, False, False, False); + + /* 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. */ - grab_keyboard_and_mouse (si, - /*si->screens[0].screensaver_window,*/ - RootWindowOfScreen(si->screens[0].screen), - (si->demo_mode_p ? 0 : si->screens[0].cursor)); + 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; -#ifdef HAVE_XHPDISABLERESET - if (si->locked_p && !hp_locked_p) + for (i = 0; i < si->nscreens; i++) { - XHPDisableReset (si->dpy); /* turn off C-Sh-Reset */ - hp_locked_p = True; + 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 */ } -#endif + + 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); - - store_activate_time (si, True); + monitor_power_on (si, True); reset_watchdog_timer (si, False); - if (p->unfade_p && !si->demo_mode_p) + if (si->demoing_p) + unfade_p = False; + + if (unfade_p) { - int grabbed = -1; Window *current_windows = (Window *) calloc(sizeof(Window), si->nscreens); @@ -1066,7 +1718,7 @@ unblank_screen (saver_info *si) ssi->black_pixel); } - if (p->verbose_p) fprintf (stderr, "%s: unfading... ", blurb()); + if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb()); XSync (si->dpy, False); @@ -1074,37 +1726,25 @@ unblank_screen (saver_info *si) XSync (si->dpy, False); /* Clear the stderr layer on each screen. - Grab the mouse on the first screen on which the mouse is grabbable - (if there are multiple heads, I think you might only be able to - grab the mouse on the screen that currently has the mouse? Anyway, - we only grab the mouse once, and don't try again after the grab - has succeeded.) */ for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; - if (grabbed != GrabSuccess) - grabbed = grab_mouse (si, RootWindowOfScreen (ssi->screen), - 0); clear_stderr (ssi); } XUngrabServer (si->dpy); XSync (si->dpy, False); /* ###### (danger over) */ - - fade_screens (si->dpy, 0, current_windows, - p->fade_seconds, p->fade_ticks, + 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, "unfading done.\n"); - - /* If we had successfully grabbed the mouse, let it go now. */ - if (grabbed == GrabSuccess) - ungrab_mouse (si); + if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb()); } else { @@ -1125,7 +1765,7 @@ unblank_screen (saver_info *si) /* 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 simultaniously. It'd be nice to + 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?) */ @@ -1151,19 +1791,10 @@ unblank_screen (saver_info *si) kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p); } - store_activate_time(si, False); /* store unblank time */ - + store_saver_status (si); /* store unblank time */ ungrab_keyboard_and_mouse (si); restore_real_vroot (si); -#ifdef HAVE_XHPDISABLERESET - if (hp_locked_p) - { - XHPEnableReset (si->dpy); /* turn C-Sh-Reset back on */ - hp_locked_p = False; - } -#endif - /* 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.) @@ -1172,52 +1803,130 @@ unblank_screen (saver_info *si) 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 -store_activate_time (saver_info *si, Bool use_last_p) +maybe_transfer_grabs (saver_screen_info *ssi, + Window old_w, Window new_w, + int new_screen_no) { - static time_t last_time = 0; - time_t now = ((use_last_p && last_time) ? last_time : time ((time_t) 0)); - CARD32 now32 = (CARD32) now; - int i; - last_time = now; + saver_info *si = ssi->global; - for (i = 0; i < si->nscreens; i++) + /* 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) { - saver_screen_info *ssi = &si->screens[i]; - if (!ssi->screensaver_window) continue; - XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_TIME, - XA_INTEGER, 32, PropModeReplace, - (unsigned char *) &now32, 1); + 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) */ + } +} + + +static Visual * +get_screen_gl_visual (saver_info *si, int real_screen_number) +{ + int i; + int nscreens = ScreenCount (si->dpy); + + if (! si->best_gl_visuals) + si->best_gl_visuals = (Visual **) + calloc (nscreens + 1, sizeof (*si->best_gl_visuals)); + + for (i = 0; i < nscreens; i++) + if (! si->best_gl_visuals[i]) + si->best_gl_visuals[i] = + get_best_gl_visual (si, ScreenOfDisplay (si->dpy, i)); + + if (real_screen_number < 0 || real_screen_number >= nscreens) abort(); + return si->best_gl_visuals[real_screen_number]; } Bool select_visual (saver_screen_info *ssi, const char *visual_name) { + XWindowAttributes xgwa; 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; + 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; + + get_screen_gl_visual (si, 0); /* let's probe all the GL visuals early */ + + /* We make sure the existing window is actually on ssi->screen before + trying to use it, in case things moved around radically when monitors + were added or deleted. If we don't do this we could get a BadMatch + even though the depths match. I think. + */ + memset (&xgwa, 0, sizeof(xgwa)); + if (ssi->screensaver_window) + XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa); + if (visual_name && *visual_name) { - if (!strcmp(visual_name, "default-i")) + 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")) + else if (!strcmp(visual_name, "default-n") || + !strcmp(visual_name, "Default-n") || + !strcmp(visual_name, "Default-N")) { visual_name = "default"; install_cmap_p = False; } - new_v = get_visual (ssi->screen, visual_name, True, False); + else if (!strcmp(visual_name, "gl") || + !strcmp(visual_name, "Gl") || + !strcmp(visual_name, "GL")) + { + new_v = get_screen_gl_visual (si, ssi->real_screen_number); + 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 { @@ -1227,23 +1936,28 @@ select_visual (saver_screen_info *ssi, const char *visual_name) 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 && - ((ssi->current_visual != new_v) || - (install_cmap_p != was_installed_p))) + if ((ssi->screen != xgwa.screen) || + (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 (! new_v) + new_v = ssi->current_visual; if (p->verbose_p) { - fprintf (stderr, "%s: switching to visual ", blurb()); + 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()); + fprintf (stderr, "%s: from ", blurb()); describe_visual (stderr, ssi->screen, ssi->current_visual, was_installed_p); #endif @@ -1256,54 +1970,26 @@ select_visual (saver_screen_info *ssi, const char *visual_name) 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); - store_activate_time (si, True); - - - - /* Transfer the 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... - */ - - - /* If we're destroying the window that holds 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->demo_mode_p ? 0 : ssi->cursor)); - XUngrabServer (si->dpy); - XSync (si->dpy, False); /* ###### (danger over) */ - } - - /* If we're destroying the window that holds 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); - XUngrabServer (si->dpy); - XSync (si->dpy, False); /* ###### (danger over) */ - } - /* Now we can destroy this window without horking our grabs. */ + /* 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: destroyed old saver window 0x%lx.\n", - blurb(), (unsigned long) old_w); + 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) &&