X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=driver%2Fwindows.c;h=60de0a92fa2a51dd7069a995caf5c0f4a2618481;hp=694236113b4a1581c6a0300e0084b9e270df9cfc;hb=ffd8c0873576a9e3065696a624dce6b766b77062;hpb=4cecfc89e5e889c7232693897c06168fb378bd5c diff --git a/driver/windows.c b/driver/windows.c index 69423611..60de0a92 100644 --- a/driver/windows.c +++ b/driver/windows.c @@ -1,5 +1,5 @@ /* windows.c --- turning the screen black; dealing with visuals, virtual roots. - * xscreensaver, Copyright (c) 1991-2001 Jamie Zawinski + * xscreensaver, Copyright (c) 1991-2004 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -51,7 +51,6 @@ # include #endif /* HAVE_XINERAMA */ - /* This file doesn't need the Xt headers, so stub these types out... */ #undef XtPointer #define XtAppContext void* @@ -171,6 +170,55 @@ ungrab_mouse(saver_info *si) } +/* Apparently there is this program called "rdesktop" which is a windows + terminal server client for Unix. It would seem that this program holds + the keyboard GRABBED the whole time it has focus! This is, of course, + completely idiotic: the whole point of grabbing is to get events when + you do *not* have focus, so grabbing *only when* you have focus is + completely redundant -- unless your goal is to make xscreensaver not + able to ever lock the screen when your program is running. + + If xscreensaver blanks while rdesktop still has a keyboard grab, then + when we try to prompt for the password, we won't get the characters: + they'll be typed into rdesktop. + + Perhaps rdesktop will release its keyboard grab if it loses focus? + What the hell, let's give it a try. If we fail to grab the keyboard + four times in a row, we forcibly set focus to "None" and try four + more times. (We don't touch focus unless we're already having a hard + time getting a grab.) + */ +static void +nuke_focus (saver_info *si, int screen_no) +{ + saver_preferences *p = &si->prefs; + Window focus = 0; + int rev = 0; + + XGetInputFocus (si->dpy, &focus, &rev); + + if (p->verbose_p) + { + char w[255], r[255]; + + if (focus == PointerRoot) strcpy (w, "PointerRoot"); + else if (focus == None) strcpy (w, "None"); + else sprintf (w, "0x%lx", (unsigned long) focus); + + if (rev == RevertToParent) strcpy (r, "RevertToParent"); + else if (rev == RevertToPointerRoot) strcpy (r, "RevertToPointerRoot"); + else if (rev == RevertToNone) strcpy (r, "RevertToNone"); + else sprintf (r, "0x%x", rev); + + fprintf (stderr, "%s: %d: removing focus from %s / %s.\n", + blurb(), screen_no, w, r); + } + + XSetInputFocus (si->dpy, None, RevertToNone, CurrentTime); + XSync (si->dpy, False); +} + + static Bool grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor, int screen_no) @@ -178,6 +226,9 @@ grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor, Status mstatus = 0, kstatus = 0; int i; int retries = 4; + Bool focus_fuckus = False; + + AGAIN: for (i = 0; i < retries; i++) { @@ -191,8 +242,17 @@ grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor, } if (kstatus != GrabSuccess) - fprintf (stderr, "%s: couldn't grab keyboard! (%s)\n", - blurb(), grab_string(kstatus)); + { + 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++) { @@ -209,8 +269,24 @@ grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor, fprintf (stderr, "%s: couldn't grab pointer! (%s)\n", blurb(), grab_string(mstatus)); - return (kstatus == GrabSuccess || - mstatus == GrabSuccess); + + /* When should we allow blanking to proceed? The current theory + is that a keyboard grab is manditory; a mouse grab is optional. + + - If we don't have a keyboard grab, then we won't be able to + read a password to unlock, so the kbd grab is manditory. + (We can't conditionalize this on locked_p, because someone + might run "xscreensaver-command -lock" at any time.) + + - If we don't have a mouse grab, then we might not see mouse + clicks as a signal to unblank -- but we will still see kbd + activity, so that's not a disaster. + */ + + if (kstatus != GrabSuccess) /* Do not blank without a kbd grab. */ + return False; + + return True; /* Grab is good, go ahead and blank. */ } static void @@ -288,25 +364,26 @@ ensure_no_screensaver_running (Display *dpy, Screen *screen) Atom type; int format; unsigned long nitems, bytesafter; - char *version; + unsigned char *version; if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1, False, XA_STRING, &type, &format, &nitems, - &bytesafter, (unsigned char **) &version) + &bytesafter, &version) == Success && type != None) { - char *id; + unsigned char *id; if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512, False, XA_STRING, &type, &format, &nitems, - &bytesafter, (unsigned char **) &id) + &bytesafter, &id) == Success || type == None) - id = "???"; + id = (unsigned char *) "???"; fprintf (stderr, "%s: 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; } } @@ -374,7 +451,7 @@ kill_xsetroot_data_1 (Display *dpy, Window window, 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 @@ -394,17 +471,18 @@ kill_xsetroot_data_1 (Display *dpy, Window window, */ 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 %s data (0x%lX).\n", - blurb(), atom_name, *dataP); - safe_XKillClient (dpy, *dataP); + blurb(), atom_name, *pixP); + safe_XKillClient (dpy, *pixP); } else fprintf (stderr, @@ -412,7 +490,7 @@ kill_xsetroot_data_1 (Display *dpy, Window window, "\t%lu, %lu; type: %lu, format: %d, " "nitems: %lu, bytesafter %ld\n", blurb(), atom_name, - (unsigned long) dataP, (dataP ? *dataP : 0), type, + (unsigned long) pixP, (pixP ? *pixP : 0), type, format, nitems, bytesafter); } } @@ -462,15 +540,29 @@ save_real_vroot (saver_screen_info *ssi) Atom type; int format; unsigned long nitems, bytesafter; - Window *vrootP = 0; + unsigned char *dataP = 0; + Window *vrootP; + int j; + + /* Skip this window if it is the xscreensaver window of any other + screen (this can happen in the Xinerama case.) + */ + for (j = 0; j < si->nscreens; j++) + { + saver_screen_info *ssi2 = &si->screens[j]; + if (kids[i] == ssi2->screensaver_window) + goto SKIP; + } if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW, &type, &format, &nitems, &bytesafter, - (unsigned char **) &vrootP) + &dataP) != Success) continue; - if (! vrootP) + if (! dataP) continue; + + vrootP = (Window *) dataP; if (ssi->real_vroot) { if (*vrootP == ssi->screensaver_window) abort (); @@ -481,6 +573,8 @@ save_real_vroot (saver_screen_info *ssi) } ssi->real_vroot = kids [i]; ssi->real_vroot_value = *vrootP; + SKIP: + ; } XSync (dpy, False); @@ -708,9 +802,14 @@ saver_sighup_handler (int sig) fprintf (stderr, "%s: %s received: restarting...\n", blurb(), signal_name(sig)); - unblank_screen (si); - kill_screenhack (si); - XSync (si->dpy, False); + + if (si->screen_blanked_p) + { + unblank_screen (si); + kill_screenhack (si); + XSync (si->dpy, False); + } + restart_process (si); /* Does not return */ abort (); } @@ -909,65 +1008,66 @@ get_screen_viewport (saver_screen_info *ssi, int w = WidthOfScreen (ssi->screen); int h = HeightOfScreen (ssi->screen); -#ifdef HAVE_XF86VMODE +# ifdef HAVE_XF86VMODE saver_info *si = ssi->global; + saver_preferences *p = &si->prefs; int event, error; int dot; XF86VidModeModeLine ml; int x, y; - Bool xinerama_p; - Bool placement_only_p = (target_x != -1 && target_y != -1); + Bool xinerama_p = si->xinerama_p; -#ifdef HAVE_XINERAMA - xinerama_p = (XineramaQueryExtension (si->dpy, &event, &error) && - XineramaIsActive (si->dpy)); -#else /* !HAVE_XINERAMA */ +# ifndef HAVE_XINERAMA /* Even if we don't have the client-side Xinerama lib, check to see if the server supports Xinerama, so that we know to ignore the VidMode extension -- otherwise a server crash could result. Yay. */ xinerama_p = XQueryExtension (si->dpy, "XINERAMA", &error, &event, &error); - -#endif /* !HAVE_XINERAMA */ +# endif /* !HAVE_XINERAMA */ -#ifdef HAVE_XINERAMA - if (xinerama_p && placement_only_p) +# ifdef HAVE_XINERAMA + if (xinerama_p) { - int nscreens = 0; - XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &nscreens); - if (xsi) + int mouse_p = (target_x != -1 && target_y != -1); + int which = -1; + int i; + + /* If a mouse position wasn't passed in, assume we're talking about + this screen. */ + if (!mouse_p) { - /* Find the screen that contains the mouse. */ - int which = -1; - int i; - for (i = 0; i < nscreens; i++) - { - if (target_x >= xsi[i].x_org && - target_y >= xsi[i].y_org && - target_x < xsi[i].x_org + xsi[i].width && - target_y < xsi[i].y_org + xsi[i].height) - which = i; - if (verbose_p) - { - fprintf (stderr, "%s: %d: xinerama vp: %dx%d+%d+%d", - blurb(), i, - xsi[which].width, xsi[which].height, - xsi[i].x_org, xsi[i].y_org); - if (which == i) - fprintf (stderr, "; mouse at %d,%d", - target_x, target_y); - fprintf (stderr, ".\n"); - } - } - if (which == -1) which = 0; /* didn't find it? Use the first. */ - *x_ret = xsi[which].x_org; - *y_ret = xsi[which].y_org; - *w_ret = xsi[which].width; - *h_ret = xsi[which].height; - XFree (xsi); - return; + target_x = ssi->x; + target_y = ssi->y; } + + /* Find the Xinerama rectangle that contains the mouse position. */ + for (i = 0; i < si->nscreens; i++) + { + if (target_x >= si->screens[i].x && + target_y >= si->screens[i].y && + target_x < si->screens[i].x + si->screens[i].width && + target_y < si->screens[i].y + si->screens[i].height) + which = i; + } + if (which == -1) which = 0; /* didn't find it? Use the first. */ + *x_ret = si->screens[which].x; + *y_ret = si->screens[which].y; + *w_ret = si->screens[which].width; + *h_ret = si->screens[which].height; + + if (verbose_p) + { + fprintf (stderr, "%s: %d: xinerama vp: %dx%d+%d+%d", + blurb(), which, + si->screens[which].width, si->screens[which].height, + si->screens[which].x, si->screens[which].y); + if (mouse_p) + fprintf (stderr, "; mouse at %d,%d", target_x, target_y); + fprintf (stderr, ".\n"); + } + + return; } -#endif /* HAVE_XINERAMA */ +# endif /* HAVE_XINERAMA */ if (!xinerama_p && /* Xinerama + VidMode = broken. */ XF86VidModeQueryExtension (si->dpy, &event, &error) && @@ -1017,6 +1117,45 @@ get_screen_viewport (saver_screen_info *ssi, *w_ret, *h_ret, *x_ret, *y_ret); + if (p->getviewport_full_of_lies_p) + { + /* XF86VidModeGetViewPort() tends to be full of lies on laptops + that have a docking station or external monitor that runs in + a different resolution than the laptop's screen: + + http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=81593 + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=208417 + http://bugs.xfree86.org/show_bug.cgi?id=421 + + The XFree86 developers have closed the bug. As far as I can + tell, their reason for this was, "this is an X server bug, + but it's pretty hard to fix. Therefore, we are closing it." + + So, now there's a preference item for those unfortunate users to + tell us not to trust a word that XF86VidModeGetViewPort() says. + */ + static int warned_once = 0; + if (!warned_once && verbose_p) + { + warned_once = 1; + fprintf (stderr, + "%s: %d: XF86VidModeGetViewPort() says vp is %dx%d+%d+%d;\n" + "%s: %d: assuming that is a pack of lies;\n" + "%s: %d: using %dx%d+0+0 instead.\n", + blurb(), ssi->number, + *w_ret, *h_ret, *x_ret, *y_ret, + blurb(), ssi->number, + blurb(), ssi->number, w, h); + } + + *x_ret = 0; + *y_ret = 0; + *w_ret = w; + *h_ret = h; + return; + } + + /* Apparently, though the server stores the X position in increments of 1 pixel, it will only make changes to the *display* in some other increment. With XF86_SVGA on a Thinkpad, the display only updates @@ -1044,7 +1183,7 @@ get_screen_viewport (saver_screen_info *ssi, will still look pretty stupid -- for example, "slidescreen" will cut off the left and right edges of the grid, etc. */ -# define FUDGE 16 +# 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. @@ -1053,7 +1192,7 @@ get_screen_viewport (saver_screen_info *ssi, *x_ret = ((x - 1) / FUDGE) * FUDGE; *w_ret += (FUDGE * 2); } -# undef FUDGE +# undef FUDGE if (*x_ret != x || *y_ret != y || @@ -1068,7 +1207,7 @@ get_screen_viewport (saver_screen_info *ssi, return; } -#endif /* HAVE_XF86VMODE */ +# endif /* HAVE_XF86VMODE */ *x_ret = 0; *y_ret = 0; @@ -1273,7 +1412,7 @@ initialize_screensaver_window_1 (saver_screen_info *ssi) attrs.backing_pixel = ssi->black_pixel; attrs.border_pixel = ssi->black_pixel; - if (p->debug_p) width = width / 2; + if (p->debug_p && !p->quad_p) width = width / 2; if (!p->verbose_p || printed_visual_info) ; @@ -1425,6 +1564,131 @@ initialize_screensaver_window (saver_info *si) } +/* Called when the RANDR (Resize and Rotate) extension tells us that the + size of the screen has changed while the screen was blanked. If we + don't do this, then the screen saver will no longer fully fill the + screen, and some of the underlying desktop may be visible. + */ +void +resize_screensaver_window (saver_info *si) +{ + saver_preferences *p = &si->prefs; + int i; + + /* First update the size info in the saver_screen_info structs. + */ + +# ifdef HAVE_XINERAMA + if (si->xinerama_p) + { + /* As of XFree86 4.3.0, the RANDR and XINERAMA extensions cannot coexist. + However, maybe they will someday, so I'm guessing that the right thing + to do in that case will be to re-query the Xinerama rectangles after + a RANDR size change is received: presumably, if the resolution of one + or more of the monitors has changed, then the Xinerama rectangle + corresponding to that monitor will also have been updated. + */ + int nscreens; + XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &nscreens); + if (nscreens != si->nscreens) abort(); + if (!xsi) abort(); + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (p->verbose_p && + (ssi->x != xsi[i].x_org || + ssi->y != xsi[i].y_org || + ssi->width != xsi[i].width || + ssi->height != xsi[i].height)) + fprintf (stderr, + "%s: %d: resize xinerama from %dx%d+%d+%d to %dx%d+%d+%d\n", + blurb(), i, + ssi->width, ssi->height, ssi->x, ssi->y, + xsi[i].width, xsi[i].height, xsi[i].x_org, xsi[i].y_org); + + ssi->x = xsi[i].x_org; + ssi->y = xsi[i].y_org; + ssi->width = xsi[i].width; + ssi->height = xsi[i].height; + } + XFree (xsi); + } + else +# endif /* HAVE_XINERAMA */ + { + /* Not Xinerama -- get the real sizes of the root windows. */ + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + XWindowAttributes xgwa; + XGetWindowAttributes (si->dpy, RootWindowOfScreen (ssi->screen), + &xgwa); + + if (p->verbose_p && + (ssi->x != xgwa.x || + ssi->y != xgwa.y || + ssi->width != xgwa.width || + ssi->height != xgwa.height)) + fprintf (stderr, + "%s: %d: resize screen from %dx%d+%d+%d to %dx%d+%d+%d\n", + blurb(), i, + ssi->width, ssi->height, ssi->x, ssi->y, + xgwa.width, xgwa.height, xgwa.x, xgwa.y); + + ssi->x = xgwa.x; + ssi->y = xgwa.y; + ssi->width = xgwa.width; + ssi->height = xgwa.height; + } + } + + /* Next, ensure that the screensaver windows are the right size, taking + into account both the new size of the screen in question's root window, + and any viewport within that. + */ + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + XWindowAttributes xgwa; + XWindowChanges changes; + int x, y, width, height; + unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth; + + XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa); + get_screen_viewport (ssi, &x, &y, &width, &height, -1, -1, + (p->verbose_p && !si->screen_blanked_p)); + if (xgwa.x == x && + xgwa.y == y && + xgwa.width == width && + xgwa.height == height) + continue; /* no change! */ + + changes.x = x; + changes.y = y; + changes.width = width; + changes.height = height; + changes.border_width = 0; + + if (p->debug_p && !p->quad_p) changes.width = changes.width / 2; + + if (p->verbose_p) + fprintf (stderr, + "%s: %d: resize 0x%lx from %dx%d+%d+%d to %dx%d+%d+%d\n", + blurb(), i, (unsigned long) ssi->screensaver_window, + xgwa.width, xgwa.height, xgwa.x, xgwa.y, + width, height, x, y); + if (! safe_XConfigureWindow (si->dpy, ssi->screensaver_window, + changesmask, &changes)) + { + fprintf (stderr, + "%s: %d: someone horked our saver window (0x%lx)! Unable to resize it!\n", + blurb(), i, (unsigned long) ssi->screensaver_window); + } + } +} + + void raise_window (saver_info *si, Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear) @@ -1481,7 +1745,8 @@ raise_window (saver_info *si, /* 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); @@ -1535,33 +1800,34 @@ 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; - else + + for (i = 0; i < si->nscreens; i++) { - int i; - 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) { - saver_screen_info *ssi = &si->screens[i]; - Window pointer_root, pointer_child; - int root_x, root_y, win_x, win_y; - unsigned int mask; - if (XQueryPointer (si->dpy, - RootWindowOfScreen (ssi->screen), - &pointer_root, &pointer_child, - &root_x, &root_y, &win_x, &win_y, &mask)) - { - if (p->verbose_p) - fprintf (stderr, "%s: mouse is on screen %d of %d\n", - blurb(), i, si->nscreens); - return i; - } + 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; } + + /* couldn't figure out where the mouse is? Oh well. */ + return 0; } @@ -1589,12 +1855,17 @@ blank_screen (saver_info *si) 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; @@ -1602,8 +1873,8 @@ blank_screen (saver_info *si) 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); @@ -1678,8 +1949,8 @@ unblank_screen (saver_info *si) 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); @@ -1803,6 +2074,13 @@ select_visual (saver_screen_info *ssi, const char *visual_name) Visual *new_v = 0; Bool got_it; + /* On some systems (most recently, MacOS X) OpenGL programs get confused + when you kill one and re-start another on the same window. So maybe + it's best to just always destroy and recreate the xscreensaver window + when changing hacks, instead of trying to reuse the old one? + */ + Bool always_recreate_window_p = True; + if (visual_name && *visual_name) { if (!strcmp(visual_name, "default-i") || @@ -1846,7 +2124,8 @@ select_visual (saver_screen_info *ssi, const char *visual_name) ssi->install_cmap_p = install_cmap_p; if (new_v && - ((ssi->current_visual != new_v) || + (always_recreate_window_p || + (ssi->current_visual != new_v) || (install_cmap_p != was_installed_p))) { Colormap old_c = ssi->cmap;