X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Fxscreensaver.c;h=3f0c7409edb20e9a5a062f2731cccb8819282e0d;hb=c494fd2e6b3b25582375d62e40f4f5cc984ca424;hp=5ce1a90917104e3f2dd014989e7c5b9df4655f5e;hpb=3f9592851ce4ed76a9979bfdd6ec7dc5c457e183;p=xscreensaver diff --git a/driver/xscreensaver.c b/driver/xscreensaver.c index 5ce1a909..3f0c7409 100644 --- a/driver/xscreensaver.c +++ b/driver/xscreensaver.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1991-2002 Jamie Zawinski +/* xscreensaver, Copyright (c) 1991-2006 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 @@ -16,11 +16,11 @@ * on (nearly) all windows; or by waiting for the MIT-SCREEN-SAVER extension * to send us a "you are idle" event. * - * Then, we map a full screen black window (or, in the case of the - * MIT-SCREEN-SAVER extension, use the one it gave us.) + * Then, we map a full screen black window. * * We place a __SWM_VROOT property on this window, so that newly-started - * clients will think that this window is a "virtual root" window. + * clients will think that this window is a "virtual root" window (as per + * the logic in the historical "vroot.h" header.) * * If there is an existing "virtual root" window (one that already had * an __SWM_VROOT property) then we remove that property from that window. @@ -38,10 +38,18 @@ * When they are, we kill the inferior process, unmap the window, and restore * the __SWM_VROOT property to the real virtual root window if there was one. * - * While we are waiting, we also set up timers so that, after a certain - * amount of time has passed, we can start a different screenhack. We do - * this by killing the running child process with SIGTERM, and then starting - * a new one in the same way. + * On multi-screen systems, we do the above on each screen, and start + * multiple programs, each with a different value of $DISPLAY. + * + * On Xinerama systems, we do a similar thing, but instead create multiple + * windows on the (only) display, and tell the subprocess which one to use + * via the $XSCREENSAVER_WINDOW environment variable -- this trick requires + * a recent (Aug 2003) revision of vroot.h. + * + * While we are waiting for user activity, we also set up timers so that, + * after a certain amount of time has passed, we can start a different + * screenhack. We do this by killing the running child process with + * SIGTERM, and then starting a new one in the same way. * * If there was a real virtual root, meaning that we removed the __SWM_VROOT * property from it, meaning we must (absolutely must) restore it before we @@ -57,7 +65,7 @@ * can really fuck up the world by killing this process with "kill -9". * * This program accepts ClientMessages of type SCREENSAVER; these messages - * may contain the atom ACTIVATE or DEACTIVATE, meaning to turn the + * may contain the atoms ACTIVATE, DEACTIVATE, etc, meaning to turn the * screensaver on or off now, regardless of the idleness of the user, * and a few other things. The included "xscreensaver-command" program * sends these messsages. @@ -92,9 +100,15 @@ * "client changing event mask" problem that the KeyPress events hack does. * I think polling is more reliable. * - * None of this crap happens if we're using one of the extensions, so install - * one of them if the description above sounds just too flaky to live. It - * is, but those are your choices. + * On systems with /proc/interrupts (Linux) we poll that file and note when + * the interrupt counter numbers on the "keyboard" and "PS/2" lines change. + * (There is no reliable way, using /proc/interrupts, to detect non-PS/2 + * mice, so it doesn't help for serial or USB mice.) + * + * None of this crap happens if we're using one of the extensions. Sadly, + * the XIdle extension hasn't been available for many years; the SGI + * extension only exists on SGIs; and the MIT extension, while widely + * deployed, is garbage in several ways. * * A third idle-detection option could be implemented (but is not): when * running on the console display ($DISPLAY is `localhost`:0) and we're on a @@ -127,6 +141,9 @@ #include #include #include + +#include + #include #include #include @@ -135,6 +152,8 @@ #include #include #include /* for gethostbyname() */ +#include +#include #ifdef HAVE_XMU # ifndef VMS # include @@ -149,12 +168,17 @@ # include #endif /* HAVE_XIDLE_EXTENSION */ +#ifdef HAVE_XINERAMA +# include +#endif /* HAVE_XINERAMA */ + #include "xscreensaver.h" #include "version.h" #include "yarandom.h" #include "resources.h" #include "visual.h" #include "usleep.h" +#include "auth.h" saver_info *global_si_kludge = 0; /* I hate C so much... */ @@ -181,38 +205,14 @@ static XrmOptionDescRec options [] = { /* useful for debugging */ { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" }, - - /* There's really no reason to have these command-line args; they just - lead to confusion when the .xscreensaver file has conflicting values. - */ -#if 0 - { "-splash", ".splash", XrmoptionNoArg, "on" }, - { "-capture-stderr", ".captureStderr", XrmoptionNoArg, "on" }, - { "-timeout", ".timeout", XrmoptionSepArg, 0 }, - { "-cycle", ".cycle", XrmoptionSepArg, 0 }, - { "-lock-mode", ".lock", XrmoptionNoArg, "on" }, - { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" }, - { "-no-lock", ".lock", XrmoptionNoArg, "off" }, - { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 }, - { "-lock-vts", ".lockVTs", XrmoptionNoArg, "on" }, - { "-no-lock-vts", ".lockVTs", XrmoptionNoArg, "off" }, - { "-visual", ".visualID", XrmoptionSepArg, 0 }, - { "-install", ".installColormap", XrmoptionNoArg, "on" }, - { "-no-install", ".installColormap", XrmoptionNoArg, "off" }, - { "-timestamp", ".timestamp", XrmoptionNoArg, "on" }, - { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" }, - { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" }, - { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" }, - { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" }, - { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" }, - { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" }, - { "-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "on" }, - { "-no-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "off" }, - { "-idelay", ".initialDelay", XrmoptionSepArg, 0 }, - { "-nice", ".nice", XrmoptionSepArg, 0 }, -#endif /* 0 */ }; +#ifdef __GNUC__ + __extension__ /* shut up about "string length is greater than the length + ISO C89 compilers are required to support" when including + the .ad file... */ +#endif + static char *defaults[] = { #include "XScreenSaver_ad.h" 0 @@ -228,13 +228,15 @@ do_help (saver_info *si) fflush (stdout); fflush (stderr); fprintf (stdout, "\ -xscreensaver %s, copyright (c) 1991-2002 by Jamie Zawinski \n\ +xscreensaver %s, copyright (c) 1991-2006 by Jamie Zawinski \n\ \n\ All xscreensaver configuration is via the `~/.xscreensaver' file.\n\ Rather than editing that file by hand, just run `xscreensaver-demo':\n\ that program lets you configure the screen saver graphically,\n\ including timeouts, locking, and display modes.\n\ -\n\ +\n", + si->version); + fprintf (stdout, "\ Just getting started? Try this:\n\ \n\ xscreensaver &\n\ @@ -243,8 +245,8 @@ xscreensaver %s, copyright (c) 1991-2002 by Jamie Zawinski \n\ For updates, online manual, and FAQ, please see the web page:\n\ \n\ http://www.jwz.org/xscreensaver/\n\ -\n", - si->version); +\n"); + fflush (stdout); fflush (stderr); exit (1); @@ -300,11 +302,14 @@ saver_ehandler (Display *dpy, XErrorEvent *error) blurb()); for (i = 0; i < si->nscreens; i++) - fprintf (real_stderr, "%s: screen %d: 0x%x, 0x%x, 0x%x\n", - blurb(), i, - RootWindowOfScreen (si->screens[i].screen), - si->screens[i].real_vroot, - si->screens[i].screensaver_window); + { + saver_screen_info *ssi = &si->screens[i]; + fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n", + blurb(), ssi->real_screen_number, ssi->number, + (unsigned int) RootWindowOfScreen (si->screens[i].screen), + (unsigned int) si->screens[i].real_vroot, + (unsigned int) si->screens[i].screensaver_window); + } fprintf (real_stderr, "\n" "#######################################" @@ -338,7 +343,8 @@ saver_ehandler (Display *dpy, XErrorEvent *error) " bug. That will cause xscreensaver to dump a `core' file to the\n" " current directory. Please include the stack trace from that core\n" " file in your bug report. *DO NOT* mail the core file itself!\n" - " That won't work.\n" + " That won't work.\n"); + fprintf (real_stderr, "\n" " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n" " the most useful bug reports, and how to examine core files.\n" @@ -493,6 +499,37 @@ lock_initialization (saver_info *si, int *argc, char **argv) si->locking_disabled_p = True; si->nolock_reason = "running under GDM"; } + + /* If the server is XDarwin (MacOS X) then disable locking. + (X grabs only affect X programs, so you can use Command-Tab + to bring any other Mac program to the front, e.g., Terminal.) + */ + if (!si->locking_disabled_p) + { + int op = 0, event = 0, error = 0; + Bool macos_p = False; + +#ifdef __APPLE__ + /* Disable locking if *running* on Apple hardware, since we have no + reliable way to determine whether the server is running on MacOS. + Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware" + but I'm not really sure about that. + */ + macos_p = True; +#endif + + if (!macos_p) + /* This extension exists on the Apple X11 server, but not + on earlier versions of the XDarwin server. */ + macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error); + + if (macos_p) + { + si->locking_disabled_p = True; + si->nolock_reason = "Cannot lock securely on MacOS X"; + } + } + #endif /* NO_LOCKING */ } @@ -648,14 +685,15 @@ print_banner (saver_info *si) whether to print the banner (and so that the banner gets printed before any resource-database-related error messages.) */ - p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean")); + p->verbose_p = (p->debug_p || + get_boolean_resource (si->dpy, "verbose", "Boolean")); /* Ditto, for the locking_disabled_p message. */ - p->lock_p = get_boolean_resource ("lock", "Boolean"); + p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean"); if (p->verbose_p) fprintf (stderr, - "%s %s, copyright (c) 1991-2002 " + "%s %s, copyright (c) 1991-2006 " "by Jamie Zawinski .\n", progname, si->version); @@ -688,11 +726,17 @@ print_banner (saver_info *si) fprintf (stderr, "%s: in process %lu.\n", blurb(), (unsigned long) getpid()); } +} + +static void +print_lock_failure_banner (saver_info *si) +{ + saver_preferences *p = &si->prefs; /* If locking was not able to be initalized for some reason, explain why. (This has to be done after we've read the lock_p resource.) */ - if (p->lock_p && si->locking_disabled_p) + if (si->locking_disabled_p) { p->lock_p = False; fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(), @@ -707,6 +751,7 @@ print_banner (saver_info *si) "\t See the manual for details.\n", blurb()); } + } @@ -719,18 +764,130 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell) Bool found_any_writable_cells = False; int i; - si->nscreens = ScreenCount(si->dpy); - si->screens = (saver_screen_info *) - calloc(sizeof(saver_screen_info), si->nscreens); +# ifdef HAVE_XINERAMA + { + int event, error; + si->xinerama_p = (XineramaQueryExtension (si->dpy, &event, &error) && + XineramaIsActive (si->dpy)); + } + + if (si->xinerama_p && ScreenCount (si->dpy) != 1) + { + si->xinerama_p = False; + if (si->prefs.verbose_p) + fprintf (stderr, + "%s: Xinerama AND %d screens? Disabling Xinerama support!\n", + blurb(), ScreenCount(si->dpy)); + } + + if (si->xinerama_p) + { + XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &si->nscreens); + if (!xsi) + si->xinerama_p = False; + else + { + si->screens = (saver_screen_info *) + calloc(sizeof(saver_screen_info), si->nscreens); + for (i = 0; i < si->nscreens; i++) + { + si->screens[i].x = xsi[i].x_org; + si->screens[i].y = xsi[i].y_org; + si->screens[i].width = xsi[i].width; + si->screens[i].height = xsi[i].height; + } + XFree (xsi); + } + si->default_screen = &si->screens[0]; + si->default_screen->real_screen_p = True; + } +# endif /* !HAVE_XINERAMA */ + + if (!si->xinerama_p) + { + si->nscreens = ScreenCount(si->dpy); + si->screens = (saver_screen_info *) + calloc(sizeof(saver_screen_info), si->nscreens); + si->default_screen = &si->screens[DefaultScreen(si->dpy)]; - si->default_screen = &si->screens[DefaultScreen(si->dpy)]; + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + ssi->width = DisplayWidth (si->dpy, i); + ssi->height = DisplayHeight (si->dpy, i); + ssi->real_screen_p = True; + ssi->real_screen_number = i; + } + } + +# ifdef QUAD_MODE + /* In "quad mode", we use the Xinerama code to pretend that there are 4 + screens for every physical screen, and run four times as many hacks... + */ + if (si->prefs.quad_p) + { + int ns2 = si->nscreens * 4; + saver_screen_info *ssi2 = (saver_screen_info *) + calloc(sizeof(saver_screen_info), ns2); + + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *old = &si->screens[i]; + + if (si->prefs.debug_p) old->width = old->width / 2; + + ssi2[i*4 ] = *old; + ssi2[i*4+1] = *old; + ssi2[i*4+2] = *old; + ssi2[i*4+3] = *old; + + ssi2[i*4 ].width /= 2; + ssi2[i*4 ].height /= 2; + + ssi2[i*4+1].x += ssi2[i*4 ].width; + ssi2[i*4+1].width -= ssi2[i*4 ].width; + ssi2[i*4+1].height /= 2; + + ssi2[i*4+2].y += ssi2[i*4 ].height; + ssi2[i*4+2].width /= 2; + ssi2[i*4+2].height -= ssi2[i*4 ].height; + + ssi2[i*4+3].x += ssi2[i*4+2].width; + ssi2[i*4+3].y += ssi2[i*4+2].height; + ssi2[i*4+3].width -= ssi2[i*4+2].width; + ssi2[i*4+3].height -= ssi2[i*4+2].height; + + ssi2[i*4+1].real_screen_p = False; + ssi2[i*4+2].real_screen_p = False; + ssi2[i*4+3].real_screen_p = False; + } + + si->nscreens = ns2; + free (si->screens); + si->screens = ssi2; + si->default_screen = &si->screens[DefaultScreen(si->dpy) * 4]; + si->xinerama_p = True; + } +# endif /* QUAD_MODE */ + + /* finish initializing the screens. + */ for (i = 0; i < si->nscreens; i++) { saver_screen_info *ssi = &si->screens[i]; ssi->global = si; - ssi->screen = ScreenOfDisplay (si->dpy, i); + ssi->number = i; + ssi->screen = ScreenOfDisplay (si->dpy, ssi->real_screen_number); + ssi->poll_mouse_last_root_x = -1; + ssi->poll_mouse_last_root_y = -1; + + if (!si->xinerama_p) + { + ssi->width = WidthOfScreen (ssi->screen); + ssi->height = HeightOfScreen (ssi->screen); + } /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */ ssi->default_visual = @@ -754,7 +911,7 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell) XtNvisual, ssi->current_visual, XtNdepth, visual_depth (ssi->screen, ssi->current_visual), - 0); + NULL); if (! found_any_writable_cells) { @@ -846,6 +1003,19 @@ initialize_server_extensions (saver_info *si) blurb()); } + /* These are incompatible (or at least, our support for them is...) */ + if (si->xinerama_p && si->using_mit_saver_extension) + { + si->using_mit_saver_extension = False; + if (p->verbose_p) + fprintf (stderr, "%s: Xinerama in use: disabling MIT-SCREEN-SAVER.\n", + blurb()); + } + +#ifdef HAVE_RANDR + query_randr_extension (si); +#endif + if (!system_has_proc_interrupts_p) { si->using_proc_interrupts = False; @@ -910,8 +1080,12 @@ select_events (saver_info *si) for window creation events, so that new subwindows will be noticed. */ for (i = 0; i < si->nscreens; i++) - start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen), - False); + { + saver_screen_info *ssi = &si->screens[i]; + if (ssi->real_screen_p) + start_notice_events_timer (si, + RootWindowOfScreen (si->screens[i].screen), False); + } if (p->verbose_p) fprintf (stderr, " done.\n"); @@ -928,7 +1102,7 @@ maybe_reload_init_file (saver_info *si) fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n", blurb(), init_file_name()); - load_init_file (p); + load_init_file (si->dpy, p); /* If a server extension is in use, and p->timeout has changed, we need to inform the server of the new timeout. */ @@ -994,10 +1168,33 @@ main_loop (saver_info *si) /* Go around the loop and wait for the next bout of idleness, or for the init file to change, or for a remote command to come in, or something. + + But, if locked_p is true, go ahead. This can only happen + if we're in "disabled" mode but a "lock" clientmessage came + in: in that case, we should go ahead and blank/lock the screen. */ - continue; + if (!si->locked_p) + continue; + } + + /* Since we're about to blank the screen, kill the de-race timer, + if any. It might still be running if we have unblanked and then + re-blanked in a short period (e.g., when using the "next" button + in xscreensaver-demo.) + */ + if (si->de_race_id) + { + if (p->verbose_p) + fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n", + blurb(), si->de_race_ticks); + XtRemoveTimeOut (si->de_race_id); + si->de_race_id = 0; } + + /* Now, try to blank. + */ + if (! blank_screen (si)) { /* We were unable to grab either the keyboard or mouse. @@ -1008,13 +1205,29 @@ main_loop (saver_info *si) see any events, and the display would be wedged. So, just go around the loop again and wait for the - next bout of idleness. + next bout of idleness. (If the user remains idle, we + will next try to blank the screen again in no more than + 60 seconds.) */ + Time retry = 60 * 1000; + if (p->timeout < retry) + retry = p->timeout; - fprintf (stderr, + if (p->debug_p) + { + fprintf (stderr, + "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n", + blurb()); + } + else + { + fprintf (stderr, "%s: unable to grab keyboard or mouse! Blanking aborted.\n", - blurb()); - continue; + blurb()); + + schedule_wakeup_event (si, retry, p->debug_p); + continue; + } } kill_screenhack (si); @@ -1156,56 +1369,15 @@ main_loop (saver_info *si) si->lock_id = 0; } - /* It's possible that a race condition could have led to the saver - window being unexpectedly still mapped. This can happen like so: - - - screen is blanked - - hack is launched - - that hack tries to grab a screen image( it does this by - first unmapping the saver window, then remapping it.) - - hack unmaps window - - hack waits - - user becomes active - - hack re-maps window (*) - - driver kills subprocess - - driver unmaps window (**) - - The race is that (*) might have been sent to the server before - the client process was killed, but, due to scheduling randomness, - might not have been received by the server until after (**). - In other words, (*) and (**) might happen out of order, meaning - the driver will unmap the window, and then after that, the - recently-dead client will re-map it. This leaves the user - locked out (it looks like a desktop, but it's not!) - - To avoid this: after un-blanking the screen, sleep for a second, - and then really make sure the window is unmapped. - */ - { - int i; - XSync (si->dpy, False); - sleep (1); - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - Window w = ssi->screensaver_window; - XWindowAttributes xgwa; - XGetWindowAttributes (si->dpy, w, &xgwa); - if (xgwa.map_state != IsUnmapped) - { - if (p->verbose_p) - fprintf (stderr, - "%s: %d: client race! emergency unmap 0x%lx.\n", - blurb(), i, (unsigned long) w); - XUnmapWindow (si->dpy, w); - } - } - XSync (si->dpy, False); - } + /* Since we're unblanked now, break race conditions and make + sure we stay that way (see comment in timers.c.) */ + if (! si->de_race_id) + de_race_timer ((XtPointer) si, 0); } } static void analyze_display (saver_info *si); +static void fix_fds (void); int main (int argc, char **argv) @@ -1214,11 +1386,14 @@ main (int argc, char **argv) saver_info the_si; saver_info *si = &the_si; saver_preferences *p = &si->prefs; + struct passwd *spasswd; int i; memset(si, 0, sizeof(*si)); global_si_kludge = si; /* I hate C so much... */ + fix_fds(); + # undef ya_rand_init ya_rand_init (0); @@ -1227,15 +1402,30 @@ main (int argc, char **argv) privileged_initialization (si, &argc, argv); hack_environment (si); + spasswd = getpwuid(getuid()); + if (!spasswd) + { + fprintf(stderr, "Could not figure out who the current user is!\n"); + fprintf(stderr, "spasswd is %x\n", (unsigned int) spasswd); + return 1; + } + + si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)"); + +# ifndef NO_LOCKING + si->unlock_cb = gui_auth_conv; + si->auth_finished_cb = auth_finished_cb; +# endif /* !NO_LOCKING */ + shell = connect_to_server (si, &argc, argv); process_command_line (si, &argc, argv); print_banner (si); - load_init_file (p); /* must be before initialize_per_screen_info() */ + load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */ blurb_timestamp_p = p->timestamp_p; /* kludge */ initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */ - /* We can only issue this warnings now. */ + /* We can only issue this warning now. */ if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p)) fprintf (stderr, "%s: there are no PseudoColor or GrayScale visuals.\n" @@ -1243,10 +1433,15 @@ main (int argc, char **argv) blurb(), blurb()); for (i = 0; i < si->nscreens; i++) - if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen)) - exit (1); + { + saver_screen_info *ssi = &si->screens[i]; + if (ssi->real_screen_p) + if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen)) + exit (1); + } lock_initialization (si, &argc, argv); + print_lock_failure_banner (si); if (p->xsync_p) XSynchronize (si->dpy, True); @@ -1277,6 +1472,36 @@ main (int argc, char **argv) return 0; } +static void +fix_fds (void) +{ + /* Bad Things Happen if stdin, stdout, and stderr have been closed + (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do + that, the X connection gets allocated to one of these fds, and + then some random library writes to stderr, and random bits get + stuffed down the X pipe, causing "Xlib: sequence lost" errors. + So, we cause the first three file descriptors to be open to + /dev/null if they aren't open to something else already. This + must be done before any other files are opened (or the closing + of that other file will again free up one of the "magic" first + three FDs.) + + We do this by opening /dev/null three times, and then closing + those fds, *unless* any of them got allocated as #0, #1, or #2, + in which case we leave them open. Gag. + + Really, this crap is technically required of *every* X program, + if you want it to be robust in the face of "2>&-". + */ + int fd0 = open ("/dev/null", O_RDWR); + int fd1 = open ("/dev/null", O_RDWR); + int fd2 = open ("/dev/null", O_RDWR); + if (fd0 > 2) close (fd0); + if (fd1 > 2) close (fd1); + if (fd2 > 2) close (fd2); +} + + /* Processing ClientMessage events. */ @@ -1316,7 +1541,7 @@ XGetAtomName_safe (Display *dpy, Atom atom) else { char buf[100]; - sprintf (buf, "<>", (unsigned long) atom); + sprintf (buf, "<>", (unsigned int) atom); return strdup (buf); } } @@ -1365,10 +1590,12 @@ clientmessage_response (saver_info *si, Window w, Bool error, static void bogus_clientmessage_warning (saver_info *si, XEvent *event) { + saver_preferences *p = &si->prefs; char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type); Window w = event->xclient.window; char wdesc[255]; int screen = 0; + Bool root_p = False; *wdesc = 0; for (screen = 0; screen < si->nscreens; screen++) @@ -1380,9 +1607,19 @@ bogus_clientmessage_warning (saver_info *si, XEvent *event) else if (w == RootWindow (si->dpy, screen)) { strcpy (wdesc, "root"); + root_p = True; break; } + /* If this ClientMessage was sent to the real root window instead of to the + xscreensaver window, then it might be intended for someone else who is + listening on the root window (e.g., the window manager). So only print + the warning if: we are in debug mode; or if the bogus message was + actually sent to one of the xscreensaver-created windows. + */ + if (root_p && !p->debug_p) + return; + if (!*wdesc) { XErrorHandler old_handler; @@ -1437,6 +1674,14 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) { if (until_idle_p) { + if (p->mode == DONT_BLANK) + { + clientmessage_response(si, window, True, + "ACTIVATE ClientMessage received in DONT_BLANK mode.", + "screen blanking is currently disabled."); + return False; + } + clientmessage_response(si, window, False, "ACTIVATE ClientMessage received.", "activating."); @@ -1541,6 +1786,14 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) char buf2 [255]; long which = event->xclient.data.l[1]; + if (p->mode == DONT_BLANK) + { + clientmessage_response(si, window, True, + "SELECT ClientMessage received in DONT_BLANK mode.", + "screen blanking is currently disabled."); + return False; + } + sprintf (buf, "SELECT %ld ClientMessage received.", which); sprintf (buf2, "activating (%ld).", which); clientmessage_response (si, window, False, buf, buf2); @@ -1612,7 +1865,7 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) else if (type == XA_DEMO) { long arg = event->xclient.data.l[1]; - Bool demo_one_hack_p = (arg == 300); + Bool demo_one_hack_p = (arg == 5000); if (demo_one_hack_p) { @@ -1704,6 +1957,12 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) } else if (type == XA_THROTTLE) { + /* The THROTTLE command is deprecated -- it predates the XDPMS + extension. Instead of using -throttle, users should instead + just power off the monitor (e.g., "xset dpms force off".) + In a few minutes, xscreensaver will notice that the monitor + is off, and cease running hacks. + */ if (si->throttled_p) clientmessage_response (si, window, True, "THROTTLE ClientMessage received, but " @@ -1859,14 +2118,28 @@ analyze_display (saver_info *si) False # endif }, { "XINERAMA", "Xinerama", +# ifdef HAVE_XINERAMA + True +# else + False +# endif + }, { "RANDR", "Resize-and-Rotate", +# ifdef HAVE_RANDR + True +# else + False +# endif + }, { "Apple-DRI", "Apple-DRI (XDarwin)", True }, }; - fprintf (stderr, "%s: running on display \"%s\" (%d screen%s).\n", + fprintf (stderr, "%s: running on display \"%s\" (%d %sscreen%s).\n", blurb(), DisplayString(si->dpy), - si->nscreens, (si->nscreens == 1 ? "" : "s")); + si->nscreens, + (si->xinerama_p ? "Xinerama " : ""), + (si->nscreens == 1 ? "" : "s")); fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(), ServerVendor(si->dpy), VendorRelease(si->dpy)); @@ -1892,11 +2165,15 @@ analyze_display (saver_info *si) for (i = 0; i < si->nscreens; i++) { + saver_screen_info *ssi = &si->screens[i]; unsigned long colormapped_depths = 0; unsigned long non_mapped_depths = 0; XVisualInfo vi_in, *vi_out; int out_count; - vi_in.screen = i; + + if (!ssi->real_screen_p) continue; + + vi_in.screen = ssi->real_screen_number; vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count); if (!vi_out) continue; for (j = 0; j < out_count; j++) @@ -1908,7 +2185,8 @@ analyze_display (saver_info *si) if (colormapped_depths) { - fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i); + fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), + ssi->real_screen_number); for (j = 0; j < 32; j++) if (colormapped_depths & (1 << j)) fprintf (stderr, " %d", j); @@ -1917,13 +2195,27 @@ analyze_display (saver_info *si) if (non_mapped_depths) { fprintf (stderr, "%s: screen %d non-colormapped depths:", - blurb(), i); + blurb(), ssi->real_screen_number); for (j = 0; j < 32; j++) if (non_mapped_depths & (1 << j)) fprintf (stderr, " %d", j); fprintf (stderr, ".\n"); } } + + if (si->xinerama_p) + { + fprintf (stderr, "%s: Xinerama layout:\n", blurb()); + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + fprintf (stderr, "%s: %c %d/%d: %dx%d+%d+%d\n", + blurb(), + (ssi->real_screen_p ? '+' : ' '), + ssi->number, ssi->real_screen_number, + ssi->width, ssi->height, ssi->x, ssi->y); + } + } } Bool @@ -1979,7 +2271,7 @@ display_is_on_console_p (saver_info *si) void check_for_leaks (const char *where) { -#ifdef HAVE_SBRK +#if defined(HAVE_SBRK) && defined(LEAK_PARANOIA) static unsigned long last_brk = 0; int b = (unsigned long) sbrk(0); if (last_brk && last_brk < b)