/* windows.c --- turning the screen black; dealing with visuals, virtual roots.
- * xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@jwz.org>
+ * 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
#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>
# include <X11/extensions/xf86vmode.h>
#endif /* HAVE_XF86VMODE */
+#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
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 | \
}
static int
-grab_kbd(saver_info *si, Window w)
+grab_kbd(saver_info *si, Window w, int screen_no)
{
saver_preferences *p = &si->prefs;
int status = XGrabKeyboard (si->dpy, w, True,
GrabModeSync, GrabModeAsync,
CurrentTime);
if (status == GrabSuccess)
- si->keyboard_grab_window = w;
+ {
+ si->keyboard_grab_window = w;
+ si->keyboard_grab_screen = screen_no;
+ }
if (p->verbose_p)
- fprintf(stderr, "%s: grabbing keyboard on 0x%x... %s.\n",
- blurb(), (unsigned long) w, grab_string(status));
+ 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, w,
cursor, CurrentTime);
if (status == GrabSuccess)
- si->mouse_grab_window = w;
+ {
+ si->mouse_grab_window = w;
+ si->mouse_grab_screen = screen_no;
+ }
if (p->verbose_p)
- fprintf(stderr, "%s: grabbing mouse on 0x%x... %s.\n",
- blurb(), (unsigned long) w, grab_string(status));
+ 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->verbose_p)
- fprintf(stderr, "%s: ungrabbing keyboard (was 0x%x).\n", blurb(),
- (unsigned long) si->keyboard_grab_window);
+ 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->verbose_p)
- fprintf(stderr, "%s: ungrabbing mouse (was 0x%x).\n", blurb(),
- (unsigned long) si->mouse_grab_window);
+ 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;
}
-static Bool
-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 mstatus, kstatus;
- XSync (si->dpy, False);
+ saver_preferences *p = &si->prefs;
+ Window focus = 0;
+ int rev = 0;
- kstatus = grab_kbd (si, window);
- if (kstatus != GrabSuccess)
- { /* try again in a second */
- sleep (1);
- kstatus = grab_kbd (si, window);
- if (kstatus != GrabSuccess)
- fprintf (stderr, "%s: couldn't grab keyboard! (%s)\n",
- blurb(), grab_string(kstatus));
- }
+ XGetInputFocus (si->dpy, &focus, &rev);
- mstatus = grab_mouse (si, window, cursor);
- if (mstatus != GrabSuccess)
- { /* try again in a second */
- sleep (1);
- mstatus = grab_mouse (si, window, cursor);
- if (mstatus != GrabSuccess)
- fprintf (stderr, "%s: couldn't grab pointer! (%s)\n",
- blurb(), grab_string(mstatus));
+ 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);
}
- return (kstatus == GrabSuccess ||
- mstatus == GrabSuccess);
+ XSetInputFocus (si->dpy, None, RevertToNone, CurrentTime);
+ XSync (si->dpy, False);
}
+
static void
ungrab_keyboard_and_mouse (saver_info *si)
{
}
+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);
+ }
+
+ 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);
+ }
+
+ 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. */
+}
+
+
int
-move_mouse_grab (saver_info *si, Window to, Cursor cursor)
+move_mouse_grab (saver_info *si, Window to, Cursor cursor, int to_screen_no)
{
Window old = si->mouse_grab_window;
if (old == 0)
- return grab_mouse (si, to, cursor);
+ return grab_mouse (si, to, cursor, to_screen_no);
else
{
saver_preferences *p = &si->prefs;
fprintf(stderr, "%s: grabbing server...\n", blurb());
ungrab_mouse (si);
- status = grab_mouse (si, to, cursor);
+ 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);
+ status = grab_mouse (si, to, cursor, to_screen_no);
}
if (status != GrabSuccess) /* Augh! Try to get the old one back... */
- grab_mouse (si, to, cursor);
+ grab_mouse (si, old, cursor, to_screen_no);
XUngrabServer (si->dpy);
XSync (si->dpy, False); /* ###### (danger over) */
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;
}
}
}
+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)
- fprintf (stderr, "%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)
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 ();
}
ssi->real_vroot = kids [i];
ssi->real_vroot_value = *vrootP;
+ SKIP:
+ ;
}
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;
fprintf (stderr,
"%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
blurb(), (unsigned long) ssi->real_vroot);
- remove_vroot_property (si->dpy, ssi->screensaver_window);
+ if (ssi->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",
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, 0);
- }
+ 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) fprintf (stderr, "handling signals\n");
- else fprintf (stderr, "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, const char *dump_core_reason)
{
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);
if (bugp)
fprintf(real_stderr,
"%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
- "\t\tfor bug reporting information.\n\n",
+ "\t\t\tfor bug reporting information.\n\n",
blurb());
# if defined(HAVE_GETCWD)
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;
}
-/* Returns the area of the screen which the xscreensaver window should cover.
- Normally this is the whole screen, but if the X server's root window is
- actually larger than the monitor's displayable area, then we want to
- operate in the currently-visible portion of the desktop instead.
- */
void
-get_screen_viewport (saver_screen_info *ssi,
- int *x_ret, int *y_ret,
- int *w_ret, int *h_ret,
- Bool verbose_p)
+store_saver_status (saver_info *si)
{
- int w = WidthOfScreen (ssi->screen);
- int h = HeightOfScreen (ssi->screen);
+ PROP32 *status;
+ int size = si->nscreens + 2;
+ int i;
-#ifdef HAVE_XF86VMODE
- saver_info *si = ssi->global;
- int screen_no = screen_number (ssi->screen);
- int op, event, error;
- int dot;
- XF86VidModeModeLine ml;
- int x, y;
-
- /* Check for Xinerama first, because the VidModeExtension is broken
- when Xinerama is present. Wheee!
- */
+ status = (PROP32 *) calloc (size, sizeof(PROP32));
- if (!XQueryExtension (si->dpy, "XINERAMA", &op, &event, &error) &&
- XF86VidModeQueryExtension (si->dpy, &event, &error) &&
- XF86VidModeGetModeLine (si->dpy, screen_no, &dot, &ml) &&
- XF86VidModeGetViewPort (si->dpy, screen_no, &x, &y))
+ 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++)
{
- char msg[512];
- *x_ret = x;
- *y_ret = y;
- *w_ret = ml.hdisplay;
- *h_ret = ml.vdisplay;
+ saver_screen_info *ssi = &si->screens[i];
+ status [2 + i] = ssi->current_hack + 1;
+ }
- if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
- /* There is no viewport -- the screen does not scroll. */
- return;
+ XChangeProperty (si->dpy,
+ RootWindow (si->dpy, 0), /* always screen #0 */
+ XA_SCREENSAVER_STATUS,
+ XA_INTEGER, 32, PropModeReplace,
+ (unsigned char *) status, size);
+ free (status);
+}
- /* Apparently some versions of XFree86 return nonsense here!
- I've had reports of 1024x768 viewports at -1936862040, -1953705044.
- So, sanity-check the values and give up if they are out of range.
- */
- if (*x_ret < 0 || *x_ret >= w ||
- *y_ret < 0 || *y_ret >= h ||
- *w_ret <= 0 || *w_ret > w ||
- *h_ret <= 0 || *h_ret > h)
- {
- static int warned_once = 0;
- if (!warned_once)
- {
- fprintf (stderr, "\n"
- "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
- "%s: The XVidMode server extension is returning nonsense.\n"
- "%s: Please report this bug to your X server vendor.\n\n",
- blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
- blurb(), blurb());
- warned_once = 1;
- }
- *x_ret = 0;
- *y_ret = 0;
- *w_ret = w;
- *h_ret = h;
- return;
- }
-
-
- sprintf (msg, "%s: vp is %dx%d+%d+%d",
- blurb(), *w_ret, *h_ret, *x_ret, *y_ret);
-
-
- /* Apparently, though the server stores the X position in increments of
- 1 pixel, it will only make changes to the *display* in some other
- increment. With XF86_SVGA on a Thinkpad, the display only updates
- in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
- pixels in 16-bit mode. I don't know what it does in 24- and 32-bit
- mode, because I don't have enough video memory to find out.
-
- I consider it a bug that XF86VidModeGetViewPort() is telling me the
- server's *target* scroll position rather than the server's *actual*
- scroll position. David Dawes agrees, and says they may fix this in
- XFree86 4.0, but it's notrivial.
-
- He also confirms that this behavior is server-dependent, so the
- actual scroll position cannot be reliably determined by the client.
- So... that means the only solution is to provide a ``sandbox''
- around the blackout window -- we make the window be up to N pixels
- larger than the viewport on both the left and right sides. That
- means some part of the outer edges of each hack might not be
- visible, but screw it.
-
- I'm going to guess that 16 pixels is enough, and that the Y dimension
- doesn't have this problem.
-
- The drawback of doing this, of course, is that some of the screenhacks
- will still look pretty stupid -- for example, "slidescreen" will cut
- off the left and right edges of the grid, etc.
- */
-# define FUDGE 16
- if (x > 0 && x < w - ml.hdisplay) /* not at left edge or right edge */
- {
- /* Round X position down to next lower multiple of FUDGE.
- Increase width by 2*FUDGE in case some server rounds up.
- */
- *x_ret = ((x - 1) / FUDGE) * FUDGE;
- *w_ret += (FUDGE * 2);
- }
-# undef FUDGE
+static Bool error_handler_hit_p = False;
- if (*x_ret != x ||
- *y_ret != y ||
- *w_ret != ml.hdisplay ||
- *h_ret != ml.vdisplay)
- sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
- *w_ret, *h_ret, *x_ret, *y_ret);
+static int
+ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
+{
+ error_handler_hit_p = True;
+ return 0;
+}
- if (verbose_p)
- fprintf (stderr, "%s.\n", msg);
- return;
- }
+/* 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);
-#endif /* HAVE_XF86VMODE */
+ 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);
- *x_ret = 0;
- *y_ret = 0;
- *w_ret = w;
- *h_ret = h;
+ 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)
XColor black;
XSetWindowAttributes attrs;
unsigned long attrmask;
- int x, y, width, height;
static Bool printed_visual_info = False; /* only print the message once. */
-
- get_screen_viewport (ssi, &x, &y, &width, &height,
- (p->verbose_p && !si->screen_blanked_p));
+ Window horked_window = 0;
black.red = black.green = black.blue = 0;
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);
}
{
XWindowChanges changes;
unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
- changes.x = x;
- changes.y = y;
- 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),
- x, y, width, height,
+ 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);
}
-
- store_saver_id (ssi);
+ store_saver_id (ssi); /* store window name and IDs */
if (!ssi->cursor)
{
}
+/* 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)
/* Note! The server is grabbed, and this will take several seconds
to complete! */
- fade_screens (si->dpy, current_maps, current_windows,
+ fade_screens (si->dpy, current_maps,
+ current_windows, si->nscreens,
p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
free(current_maps);
}
}
+
+int
+mouse_screen (saver_info *si)
+{
+ saver_preferences *p = &si->prefs;
+ Window pointer_root, pointer_child;
+ int root_x, root_y, win_x, win_y;
+ unsigned int mask;
+ int i;
+
+ if (si->nscreens == 1)
+ return 0;
+
+ for (i = 0; i < si->nscreens; i++)
+ {
+ saver_screen_info *ssi = &si->screens[i];
+ if (XQueryPointer (si->dpy, RootWindowOfScreen (ssi->screen),
+ &pointer_root, &pointer_child,
+ &root_x, &root_y, &win_x, &win_y, &mask) &&
+ root_x >= ssi->x &&
+ root_y >= ssi->y &&
+ root_x < ssi->x + ssi->width &&
+ root_y < ssi->y + ssi->height)
+ {
+ if (p->verbose_p)
+ fprintf (stderr, "%s: mouse is on screen %d of %d\n",
+ blurb(), i, si->nscreens);
+ return i;
+ }
+ }
+
+ /* couldn't figure out where the mouse is? Oh well. */
+ return 0;
+}
+
+
Bool
blank_screen (saver_info *si)
{
int i;
Bool ok;
+ Window w;
+ int mscreen;
/* Note: we do our grabs on the root window, not on the screensaver window.
If we grabbed on the saver window, then the demo mode and lock dialog
boxes wouldn't get any events.
+
+ By "the root window", we mean "the root window that contains the mouse."
+ We use to always grab the mouse on screen 0, but that has the effect of
+ moving the mouse to screen 0 from whichever screen it was on, on
+ multi-head systems.
*/
- ok = grab_keyboard_and_mouse (si,
- /*si->screens[0].screensaver_window,*/
- RootWindowOfScreen(si->screens[0].screen),
- (si->demoing_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;
for (i = 0; i < si->nscreens; i++)
{
saver_screen_info *ssi = &si->screens[i];
-
- save_real_vroot (ssi);
+ if (ssi->real_screen_p)
+ save_real_vroot (ssi);
store_vroot_property (si->dpy,
ssi->screensaver_window,
ssi->screensaver_window);
{
int ev, er;
if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
- !XF86VidModeGetViewPort (si->dpy, i,
- &ssi->blank_vp_x,
- &ssi->blank_vp_y))
+ !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 */
}
- store_activate_time (si, si->screen_blanked_p);
+
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)
{
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 (si->demoing_p)
XUngrabServer (si->dpy);
XSync (si->dpy, False); /* ###### (danger over) */
-
- fade_screens (si->dpy, 0, current_windows,
+ fade_screens (si->dpy, 0,
+ current_windows, si->nscreens,
p->fade_seconds/1000, p->fade_ticks,
False, False);
/* 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);
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;
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;
}
-#ifdef DAEMON_USE_GL
else if (!strcmp(visual_name, "gl") ||
+ !strcmp(visual_name, "Gl") ||
!strcmp(visual_name, "GL"))
{
- new_v = get_gl_visual (ssi->screen);
+ 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);
}
-#endif /* DAEMON_USE_GL */
if (!new_v)
new_v = get_visual (ssi->screen, visual_name, True, False);
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
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->demoing_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) &&