/* windows.c --- turning the screen black; dealing with visuals, virtual roots.
- * xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@netscape.com>
+ * xscreensaver, Copyright (c) 1991-2014 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#ifdef VMS
# include <unixlib.h> /* 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 <sys/utsname.h> /* for uname() */
-# endif /* HAVE_UNAME */
+#ifndef VMS
+# include <pwd.h> /* for getpwuid() */
+#else /* VMS */
+# include "vms-pwd.h"
+#endif /* VMS */
+
+#ifdef HAVE_UNAME
+# include <sys/utsname.h> /* for uname() */
+#endif /* HAVE_UNAME */
#include <stdio.h>
-#include <X11/Xproto.h> /* for CARD32 */
+/* #include <X11/Xproto.h> / * for CARD32 */
#include <X11/Xlib.h>
#include <X11/Xutil.h> /* for XSetClassHint() */
#include <X11/Xatom.h>
#include <X11/Xos.h> /* for time() */
#include <signal.h> /* for the signal names */
+#include <time.h>
+#include <sys/time.h>
+
+/* 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 <X11/extensions/scrnsaver.h>
#endif /* HAVE_MIT_SAVER_EXTENSION */
+#ifdef HAVE_XF86VMODE
+# include <X11/extensions/xf86vmode.h>
+#endif /* HAVE_XF86VMODE */
-#ifdef HAVE_XHPDISABLERESET
-# include <X11/XHPlib.h>
-
- /* 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 <X11/extensions/Xinerama.h>
+#endif /* HAVE_XINERAMA */
/* This file doesn't need the Xt headers, so stub these types out... */
#undef XtPointer
#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_NET_WM_USER_TIME;
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 | \
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",
- progname, (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];
}
}
+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",
- progname, (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;
}
{
saver_preferences *p = &si->prefs;
XUngrabKeyboard(si->dpy, CurrentTime);
- if (p->debug_p)
- fprintf(stderr, "%s: XUngrabKeyboard (was 0x%x)\n", progname,
- (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;
}
{
saver_preferences *p = &si->prefs;
XUngrabPointer(si->dpy, CurrentTime);
- if (p->debug_p)
- fprintf(stderr, "%s: XUngrabPointer (was 0x%x)\n", progname,
- (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",
- progname, 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",
- progname, 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;
+ }
}
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",
- progname, 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;
}
}
#if 0
if (p->verbose_p)
fprintf (stderr,
- "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", progname,
+ "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(),
win,
(win == screensaver_window ? "ScreenSaver" :
(win == real_vroot ? "VRoot" :
{
#if 0
if (p->verbose_p)
- fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", progname, win,
+ 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" : "???"))));
}
+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
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",
- 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: deleted unrecognised _XSETROOT_ID property: \n\
- %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
- progname, (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)
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;
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 ();
fprintf (stderr,
"%s: more than one virtual root window found (0x%x and 0x%x).\n",
- progname, (int) ssi->real_vroot, (int) kids [i]);
+ blurb(), (int) ssi->real_vroot, (int) kids [i]);
exit (1);
}
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);
}
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",
- progname, (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);
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);
-}
-
\f
/* Signal hackery to ensure that the vroot doesn't get left in an
inconsistent state
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",
- progname, signal_name(sig));
+ 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", progname, 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)
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", progname);
- else if (p->verbose_p)
- fprintf (real_stderr, "%s: no vroot to restore; exiting.\n", progname);
+ if (p->verbose_p && vrs)
+ fprintf (real_stderr, "%s: old vroot restored.\n", blurb());
fflush(real_stdout);
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", progname);
+ /* 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();
}
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
+ ? (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;
+ 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
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;
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;
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 ", progname);
- describe_visual (stderr, ssi->screen, ssi->current_visual);
+ 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: ", progname);
- describe_visual (stderr, ssi->screen, ssi->current_visual);
- fprintf (stderr, "%s: default visual: ", progname);
+ 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));
+ DefaultVisualOfScreen (ssi->screen),
+ ssi->install_cmap_p);
}
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);
{
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",
- progname, (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
}
+/* 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)
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 *)
ssi->black_pixel);
}
- if (p->verbose_p) fprintf (stderr, "%s: fading... ", progname);
+ 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++)
}
#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) */
}
}
}
-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 */
-#ifdef HAVE_XHPDISABLERESET
- if (si->locked_p && !hp_locked_p)
+ if (!ok)
+ return False;
+
+ 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;
- 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);
ssi->black_pixel);
}
- if (p->verbose_p) fprintf (stderr, "%s: unfading... ", progname);
+ if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
XSync (si->dpy, False);
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
{
/* 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?)
*/
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.)
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
{
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: %d: visual ", blurb(), ssi->number);
+ describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
#if 0
- fprintf (stderr, "%s: switching visuals\tfrom: ", progname);
- describe_visual (stderr, ssi->screen, ssi->current_visual);
- fprintf (stderr, "\t\t\t\tto: ");
- describe_visual (stderr, ssi->screen, new_v);
- fprintf (stderr, "\t\t\t\t install cmap: %s\n",
- (install_cmap_p ? "yes" : "no"));
-#else
- fprintf (stderr, "%s: switching to visual ", progname);
- describe_visual (stderr, ssi->screen, new_v);
+ fprintf (stderr, "%s: from ", blurb());
+ describe_visual (stderr, ssi->screen, ssi->current_visual,
+ was_installed_p);
#endif
}
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",
- progname, (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) &&