X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Fxscreensaver.c;h=a70de0e4043a2eba2e497f7b36210824cd85c530;hb=6a1da724858673ac40aa13a9612340d8bed8c7b9;hp=63a63c83da2360cdc70ff06e59bb905210abeb57;hpb=3c58fb6311db49c46f1670922933b27c6ea0c065;p=xscreensaver diff --git a/driver/xscreensaver.c b/driver/xscreensaver.c index 63a63c83..a70de0e4 100644 --- a/driver/xscreensaver.c +++ b/driver/xscreensaver.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1991-2001 Jamie Zawinski +/* xscreensaver, Copyright (c) 1991-2003 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 @@ -127,11 +127,16 @@ #include #include #include + +#include + #include #include #include #include #include +#include +#include #include /* for gethostbyname() */ #ifdef HAVE_XMU # ifndef VMS @@ -169,6 +174,23 @@ Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK; static XrmOptionDescRec options [] = { + + { "-verbose", ".verbose", XrmoptionNoArg, "on" }, + { "-silent", ".verbose", XrmoptionNoArg, "off" }, + + /* xscreensaver-demo uses this one */ + { "-nosplash", ".splash", XrmoptionNoArg, "off" }, + { "-no-splash", ".splash", XrmoptionNoArg, "off" }, + + /* 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" }, @@ -180,11 +202,7 @@ static XrmOptionDescRec options [] = { { "-visual", ".visualID", XrmoptionSepArg, 0 }, { "-install", ".installColormap", XrmoptionNoArg, "on" }, { "-no-install", ".installColormap", XrmoptionNoArg, "off" }, - { "-verbose", ".verbose", XrmoptionNoArg, "on" }, - { "-silent", ".verbose", XrmoptionNoArg, "off" }, { "-timestamp", ".timestamp", XrmoptionNoArg, "on" }, - { "-capture-stderr", ".captureStderr", XrmoptionNoArg, "on" }, - { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" }, { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" }, { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" }, { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" }, @@ -193,17 +211,17 @@ static XrmOptionDescRec options [] = { { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" }, { "-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "on" }, { "-no-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "off" }, - { "-splash", ".splash", XrmoptionNoArg, "on" }, - { "-no-splash", ".splash", XrmoptionNoArg, "off" }, - { "-nosplash", ".splash", XrmoptionNoArg, "off" }, { "-idelay", ".initialDelay", XrmoptionSepArg, 0 }, { "-nice", ".nice", XrmoptionSepArg, 0 }, - - /* Actually these are built in to Xt, but just to be sure... */ - { "-synchronous", ".synchronous", XrmoptionNoArg, "on" }, - { "-xrm", NULL, XrmoptionResArg, NULL } +#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 @@ -219,36 +237,25 @@ do_help (saver_info *si) fflush (stdout); fflush (stderr); fprintf (stdout, "\ -xscreensaver %s, copyright (c) 1991-2001 by Jamie Zawinski \n\ -The standard Xt command-line options are accepted; other options include:\n\ +xscreensaver %s, copyright (c) 1991-2003 by Jamie Zawinski \n\ \n\ - -timeout When the screensaver should activate.\n\ - -cycle How long to let each hack run before switching.\n\ - -lock-mode Require a password before deactivating.\n\ - -lock-timeout Grace period before locking; default 0.\n\ - -visual Which X visual to run on.\n\ - -install Install a private colormap.\n\ - -verbose Be loud.\n\ - -no-splash Don't display a splash-screen at startup.\n\ - -help This message.\n\ -\n\ -See the manual for other options and X resources.\n\ -\n\ -The `xscreensaver' program should be left running in the background.\n\ -Use the `xscreensaver-demo' and `xscreensaver-command' programs to\n\ -manipulate a running xscreensaver.\n\ -\n\ -The `*programs' resource controls which graphics demos will be launched by\n\ -the screensaver. See `man xscreensaver' or the web page for more details.\n\ -\n\ -Just getting started? Try this:\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", + si->version); + fprintf (stdout, "\ + Just getting started? Try this:\n\ \n\ xscreensaver &\n\ xscreensaver-demo\n\ \n\ -For updates, check http://www.jwz.org/xscreensaver/\n\ -\n", - si->version); + For updates, online manual, and FAQ, please see the web page:\n\ +\n\ + http://www.jwz.org/xscreensaver/\n\ +\n"); + fflush (stdout); fflush (stderr); exit (1); @@ -293,6 +300,7 @@ saver_ehandler (Display *dpy, XErrorEvent *error) { saver_info *si = global_si_kludge; /* I hate C so much... */ int i; + Bool fatal_p; if (!real_stderr) real_stderr = stderr; @@ -305,17 +313,27 @@ saver_ehandler (Display *dpy, XErrorEvent *error) 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); + (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" "#######################################" "#######################################\n\n"); - if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr)) + fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr); + + fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error, + it has been BadImplementation / Xlib sequence lost, which + are in truth pretty damned fatal. + */ + + fprintf (real_stderr, "\n"); + + if (! fatal_p) + fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb()); + else { - fprintf (real_stderr, "\n"); if (si->prefs.xsync_p) { saver_exit (si, -1, "because of synchronous X Error"); @@ -331,7 +349,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" @@ -346,8 +365,7 @@ saver_ehandler (Display *dpy, XErrorEvent *error) saver_exit (si, -1, 0); } } - else - fprintf (real_stderr, " (nonfatal.)\n"); + return 0; } @@ -477,9 +495,48 @@ lock_initialization (saver_info *si, int *argc, char **argv) si->locking_disabled_p = True; si->nolock_reason = "error getting password"; } -#endif /* NO_LOCKING */ - hack_uid (si); + /* If locking is currently enabled, but the environment indicates that + we have been launched as GDM's "Background" program, then disable + locking just in case. + */ + if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM")) + { + 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 */ } @@ -494,13 +551,17 @@ connect_to_server (saver_info *si, int *argc, char **argv) char *d = getenv ("DISPLAY"); if (!d || !*d) { - char ndpy[] = "DISPLAY=:0.0"; + char *ndpy = strdup("DISPLAY=:0.0"); /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */ fprintf (stderr, "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n", blurb(), ndpy+8); if (putenv (ndpy)) abort (); + /* don't free (ndpy) -- some implementations of putenv (BSD 4.4, + glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) + do not. So we must leak it (and/or the previous setting). Yay. + */ } #endif /* HAVE_PUTENV */ @@ -529,6 +590,8 @@ connect_to_server (saver_info *si, int *argc, char **argv) XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE", False); XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False); + XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False); + XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False); XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False); XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False); XA_RESTART = XInternAtom (si->dpy, "RESTART", False); @@ -635,7 +698,7 @@ print_banner (saver_info *si) if (p->verbose_p) fprintf (stderr, - "%s %s, copyright (c) 1991-2001 " + "%s %s, copyright (c) 1991-2003 " "by Jamie Zawinski .\n", progname, si->version); @@ -668,11 +731,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(), @@ -687,6 +756,7 @@ print_banner (saver_info *si) "\t See the manual for details.\n", blurb()); } + } @@ -710,6 +780,7 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell) saver_screen_info *ssi = &si->screens[i]; ssi->global = si; ssi->screen = ScreenOfDisplay (si->dpy, i); + ssi->number = i; /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */ ssi->default_visual = @@ -733,7 +804,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) { @@ -915,7 +986,9 @@ maybe_reload_init_file (saver_info *si) /* If the DPMS settings in the init file have changed, change the settings on the server to match. */ - sync_server_dpms_settings (si->dpy, p->dpms_enabled_p, + sync_server_dpms_settings (si->dpy, + (p->dpms_enabled_p && + p->mode != DONT_BLANK), p->dpms_standby / 1000, p->dpms_suspend / 1000, p->dpms_off / 1000, @@ -942,7 +1015,13 @@ main_loop (saver_info *si) while (1) { Bool was_locked = False; + + if (p->verbose_p) + fprintf (stderr, "%s: awaiting idleness.\n", blurb()); + + check_for_leaks ("unblanked A"); sleep_until_idle (si, True); + check_for_leaks ("unblanked B"); if (p->verbose_p) { @@ -950,13 +1029,25 @@ main_loop (saver_info *si) fprintf (stderr, "%s: demoing %d at %s.\n", blurb(), si->selection_mode, timestring()); else - if (p->verbose_p) - fprintf (stderr, "%s: blanking screen at %s.\n", blurb(), - timestring()); + fprintf (stderr, "%s: blanking screen at %s.\n", blurb(), + timestring()); } maybe_reload_init_file (si); + if (p->mode == DONT_BLANK) + { + if (p->verbose_p) + fprintf (stderr, "%s: idle with blanking disabled at %s.\n", + blurb(), timestring()); + + /* 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. + */ + continue; + } + if (! blank_screen (si)) { /* We were unable to grab either the keyboard or mouse. @@ -995,6 +1086,8 @@ main_loop (saver_info *si) #ifndef NO_LOCKING + /* Maybe start locking the screen. + */ { Time lock_timeout = p->lock_timeout; @@ -1034,10 +1127,15 @@ main_loop (saver_info *si) ok_to_unblank = True; do { + check_for_leaks ("blanked A"); sleep_until_idle (si, False); /* until not idle */ + check_for_leaks ("blanked B"); + maybe_reload_init_file (si); #ifndef NO_LOCKING + /* Maybe unlock the screen. + */ if (si->locked_p) { saver_screen_info *ssi = si->default_screen; @@ -1108,12 +1206,57 @@ main_loop (saver_info *si) si->lock_id = 0; } - if (p->verbose_p) - fprintf (stderr, "%s: awaiting idleness.\n", blurb()); + /* 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); + } } } static void analyze_display (saver_info *si); +static void fix_fds (void); int main (int argc, char **argv) @@ -1127,6 +1270,8 @@ main (int argc, char **argv) memset(si, 0, sizeof(*si)); global_si_kludge = si; /* I hate C so much... */ + fix_fds(); + # undef ya_rand_init ya_rand_init (0); @@ -1140,6 +1285,7 @@ main (int argc, char **argv) print_banner (si); load_init_file (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. */ @@ -1154,9 +1300,9 @@ main (int argc, char **argv) exit (1); lock_initialization (si, &argc, argv); + print_lock_failure_banner (si); if (p->xsync_p) XSynchronize (si->dpy, True); - blurb_timestamp_p = p->timestamp_p; /* kludge */ if (p->verbose_p) analyze_display (si); initialize_server_extensions (si); @@ -1168,13 +1314,16 @@ main (int argc, char **argv) init_sigchld (); disable_builtin_screensaver (si, True); - sync_server_dpms_settings (si->dpy, p->dpms_enabled_p, + sync_server_dpms_settings (si->dpy, + (p->dpms_enabled_p && + p->mode != DONT_BLANK), p->dpms_standby / 1000, p->dpms_suspend / 1000, p->dpms_off / 1000, False); initialize_stderr (si); + handle_signals (si); make_splash_dialog (si); @@ -1182,6 +1331,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. */ @@ -1221,7 +1400,7 @@ XGetAtomName_safe (Display *dpy, Atom atom) else { char buf[100]; - sprintf (buf, "<>", (unsigned long) atom); + sprintf (buf, "<>", (unsigned int) atom); return strdup (buf); } } @@ -1266,6 +1445,60 @@ clientmessage_response (saver_info *si, Window w, Bool error, free (proto); } + +static void +bogus_clientmessage_warning (saver_info *si, XEvent *event) +{ + char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type); + Window w = event->xclient.window; + char wdesc[255]; + int screen = 0; + + *wdesc = 0; + for (screen = 0; screen < si->nscreens; screen++) + if (w == si->screens[screen].screensaver_window) + { + strcpy (wdesc, "xscreensaver"); + break; + } + else if (w == RootWindow (si->dpy, screen)) + { + strcpy (wdesc, "root"); + break; + } + + if (!*wdesc) + { + XErrorHandler old_handler; + XClassHint hint; + XWindowAttributes xgwa; + memset (&hint, 0, sizeof(hint)); + memset (&xgwa, 0, sizeof(xgwa)); + + XSync (si->dpy, False); + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + XGetClassHint (si->dpy, w, &hint); + XGetWindowAttributes (si->dpy, w, &xgwa); + XSync (si->dpy, False); + XSetErrorHandler (old_handler); + XSync (si->dpy, False); + + screen = (xgwa.screen ? screen_number (xgwa.screen) : -1); + + sprintf (wdesc, "%.20s / %.20s", + (hint.res_name ? hint.res_name : "(null)"), + (hint.res_class ? hint.res_class : "(null)")); + if (hint.res_name) XFree (hint.res_name); + if (hint.res_class) XFree (hint.res_class); + } + + fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n", + blurb(), screen, (str ? str : "(null)")); + fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n", + blurb(), screen, (unsigned long) w, wdesc); + if (str) XFree (str); +} + Bool handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) { @@ -1276,19 +1509,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) /* Preferences might affect our handling of client messages. */ maybe_reload_init_file (si); - if (event->xclient.message_type != XA_SCREENSAVER) + if (event->xclient.message_type != XA_SCREENSAVER || + event->xclient.format != 32) { - char *str; - str = XGetAtomName_safe (si->dpy, event->xclient.message_type); - fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n", - blurb(), (str ? str : "(null)")); - if (str) XFree (str); - return False; - } - if (event->xclient.format != 32) - { - fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n", - blurb(), event->xclient.format); + bogus_clientmessage_warning (si, event); return False; } @@ -1342,9 +1566,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) return True; } } - clientmessage_response(si, window, True, - "ClientMessage DEACTIVATE received while inactive.", - "not active."); + clientmessage_response(si, window, False, + "ClientMessage DEACTIVATE received while inactive: resetting idle timer.", + "not active: idle timer reset."); + reset_timers (si); } else if (type == XA_CYCLE) { @@ -1460,17 +1685,8 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) XSync (si->dpy, False); } - fflush (stdout); - fflush (stderr); - if (real_stdout) fflush (real_stdout); - if (real_stderr) fflush (real_stderr); - /* make sure error message shows up before exit. */ - if (real_stderr && stderr != real_stderr) - dup2 (fileno(real_stderr), fileno(stderr)); - - restart_process (si); - exit (1); /* shouldn't get here; but if restarting didn't work, - make this command be the same as EXIT. */ + restart_process (si); /* does not return */ + abort(); } else clientmessage_response (si, window, True, @@ -1660,13 +1876,13 @@ analyze_display (saver_info *si) const char *name; const char *desc; Bool useful_p; } exts[] = { - { "SCREEN_SAVER", "SGI Screen-Saver", + { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver", # ifdef HAVE_SGI_SAVER_EXTENSION True # else False # endif - }, { "SCREEN-SAVER", "SGI Screen-Saver", + }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver", # ifdef HAVE_SGI_SAVER_EXTENSION True # else @@ -1728,23 +1944,36 @@ analyze_display (saver_info *si) # endif }, { "XINERAMA", "Xinerama", True + }, { "Apple-DRI", "Apple-DRI (XDarwin)", + True }, }; - fprintf (stderr, "%s: running on display \"%s\"\n", blurb(), - DisplayString(si->dpy)); - fprintf (stderr, "%s: vendor is %s, %d\n", blurb(), + fprintf (stderr, "%s: running on display \"%s\" (%d screen%s).\n", + blurb(), + DisplayString(si->dpy), + si->nscreens, (si->nscreens == 1 ? "" : "s")); + fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(), ServerVendor(si->dpy), VendorRelease(si->dpy)); fprintf (stderr, "%s: useful extensions:\n", blurb()); for (i = 0; i < countof(exts); i++) { int op = 0, event = 0, error = 0; - if (XQueryExtension (si->dpy, exts[i].name, &op, &event, &error)) - fprintf (stderr, "%s: %s%s\n", blurb(), - exts[i].desc, - (exts[i].useful_p ? "" : - " \t<== unsupported at compile-time!")); + char buf [255]; + int j; + if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error)) + continue; + sprintf (buf, "%s: ", blurb()); + j = strlen (buf); + strcat (buf, exts[i].desc); + if (!exts[i].useful_p) + { + int k = j + 18; + while (strlen (buf) < k) strcat (buf, " "); + strcat (buf, "<-- not supported at compile time!"); + } + fprintf (stderr, "%s\n", buf); } for (i = 0; i < si->nscreens; i++) @@ -1769,15 +1998,16 @@ analyze_display (saver_info *si) for (j = 0; j < 32; j++) if (colormapped_depths & (1 << j)) fprintf (stderr, " %d", j); - fprintf (stderr, "\n"); + fprintf (stderr, ".\n"); } if (non_mapped_depths) { - fprintf (stderr, "%s: screen %d non-mapped depths:", blurb(), i); + fprintf (stderr, "%s: screen %d non-colormapped depths:", + blurb(), i); for (j = 0; j < 32; j++) if (non_mapped_depths & (1 << j)) fprintf (stderr, " %d", j); - fprintf (stderr, "\n"); + fprintf (stderr, ".\n"); } } } @@ -1828,3 +2058,20 @@ display_is_on_console_p (saver_info *si) } return !not_on_console; } + + +/* Do a little bit of heap introspection... + */ +void +check_for_leaks (const char *where) +{ +#ifdef HAVE_SBRK + static unsigned long last_brk = 0; + int b = (unsigned long) sbrk(0); + if (last_brk && last_brk < b) + fprintf (stderr, "%s: %s: brk grew by %luK.\n", + blurb(), where, + (((b - last_brk) + 1023) / 1024)); + last_brk = b; +#endif /* HAVE_SBRK */ +}