X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Fxscreensaver.c;h=628bcf1d442b4090899e397bed66979fbddf7de6;hb=a94197e76a5dea5cb60542840809d6c20d0abbf3;hp=7b16c898aaab4d95f22aa643084ed3842b2cd772;hpb=59ac4e9a0de290e4275a7bbb890ad16abd09d68f;p=xscreensaver diff --git a/driver/xscreensaver.c b/driver/xscreensaver.c index 7b16c898..628bcf1d 100644 --- a/driver/xscreensaver.c +++ b/driver/xscreensaver.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1991-1999 Jamie Zawinski +/* xscreensaver, Copyright (c) 1991-2002 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 @@ -59,7 +59,7 @@ * This program accepts ClientMessages of type SCREENSAVER; these messages * may contain the atom ACTIVATE or DEACTIVATE, 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 + * and a few other things. The included "xscreensaver-command" program * sends these messsages. * * If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER @@ -70,8 +70,9 @@ * KeyPress on windows which don't select them, because that would * interfere with event propagation. This will break if any program * changes its event mask to contain KeyRelease or PointerMotion more than - * 30 seconds after creating the window, but that's probably pretty rare. - * + * 30 seconds after creating the window, but such programs do not seem to + * occur in nature (I've never seen it happen in all these years.) + * * The reason that we can't select KeyPresses on windows that don't have * them already is that, when dispatching a KeyPress event, X finds the * lowest (leafmost) window in the hierarchy on which *any* client selects @@ -110,13 +111,13 @@ * to keep your emacs window alive even when xscreensaver has grabbed. * - Go read the code related to `debug_p'. * - You probably can't set breakpoints in functions that are called on - * the other side of a call to fork() -- if your clients are dying - * with signal 5, Trace/BPT Trap, you're losing in this way. + * the other side of a call to fork() -- if your subprocesses are + * dying with signal 5, Trace/BPT Trap, you're losing in this way. * - If you aren't using a server extension, don't leave this stopped * under the debugger for very long, or the X input buffer will get * huge because of the keypress events it's selecting for. This can * make your X server wedge with "no more input buffers." - * + * * ======================================================================== */ #ifdef HAVE_CONFIG_H @@ -131,6 +132,8 @@ #include #include #include +#include +#include #include /* for gethostbyname() */ #ifdef HAVE_XMU # ifndef VMS @@ -162,11 +165,29 @@ XrmDatabase db = 0; static Atom XA_SCREENSAVER_RESPONSE; static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV; -static Atom XA_EXIT, XA_RESTART, XA_LOCK, XA_SELECT; -Atom XA_DEMO, XA_PREFS; +static Atom XA_RESTART, XA_SELECT; +static Atom XA_THROTTLE, XA_UNTHROTTLE; +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" }, @@ -178,11 +199,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" }, @@ -191,15 +208,9 @@ 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 */ }; static char *defaults[] = { @@ -217,34 +228,21 @@ do_help (saver_info *si) fflush (stdout); fflush (stderr); fprintf (stdout, "\ -xscreensaver %s, copyright (c) 1991-1999 by Jamie Zawinski \n\ -The standard Xt command-line options are accepted; other options include:\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\ +xscreensaver %s, copyright (c) 1991-2002 by Jamie Zawinski \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\ + 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\ -Just getting started? Try this:\n\ + Just getting started? Try this:\n\ \n\ xscreensaver &\n\ xscreensaver-demo\n\ \n\ -For updates, check http://www.jwz.org/xscreensaver/\n\ + For updates, online manual, and FAQ, please see the web page:\n\ +\n\ + http://www.jwz.org/xscreensaver/\n\ \n", si->version); fflush (stdout); @@ -290,16 +288,27 @@ int saver_ehandler (Display *dpy, XErrorEvent *error) { saver_info *si = global_si_kludge; /* I hate C so much... */ + int i; if (!real_stderr) real_stderr = stderr; fprintf (real_stderr, "\n" "#######################################" "#######################################\n\n" - "%s: X Error! PLEASE REPORT THIS BUG.\n\n" - "#######################################" - "#######################################\n\n", + "%s: X Error! PLEASE REPORT THIS BUG.\n", 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); + + fprintf (real_stderr, "\n" + "#######################################" + "#######################################\n\n"); + if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr)) { fprintf (real_stderr, "\n"); @@ -313,15 +322,17 @@ saver_ehandler (Display *dpy, XErrorEvent *error) "#######################################" "#######################################\n\n"); fprintf (real_stderr, - " If at all possible, please re-run xscreensaver with the command line\n" - " arguments `-sync -verbose', and reproduce this bug. That will cause\n" - " xscreensaver to dump a `core' file to the current directory. Please\n" - " include the stack trace from that core file in your bug report.\n" + " If at all possible, please re-run xscreensaver with the command\n" + " line arguments `-sync -verbose -no-capture', and reproduce this\n" + " 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" "\n" - " http://www.jwz.org/xscreensaver/bugs.html explains how to create the\n" - " most useful bug reports, and how to examine core files.\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" "\n" - " The more information you can provide, the better. But please report\n" + " The more information you can provide, the better. But please\n" " report this bug, regardless!\n" "\n"); fprintf (real_stderr, @@ -366,14 +377,32 @@ startup_ehandler (String name, String type, String class, fprintf (stderr, "\n"); describe_uids (si, stderr); - fprintf (stderr, "\n" - "%s: Errors at startup are usually authorization problems.\n" - " Did you read the manual? Specifically, the parts\n" - " that talk about XAUTH, XDM, and root logins?\n" - "\n" - " http://www.jwz.org/xscreensaver/man.html\n" - "\n", + + if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5)) + { + fprintf (stderr, "\n" + "%s: This is probably because you're logging in as root. You\n" +" shouldn't log in as root: you should log in as a normal user,\n" +" and then `su' as needed. If you insist on logging in as\n" +" root, you will have to turn off X's security features before\n" +" xscreensaver will work.\n" + "\n" +" Please read the manual and FAQ for more information:\n", + blurb()); + } + else + { + fprintf (stderr, "\n" + "%s: Errors at startup are usually authorization problems.\n" +" But you're not logging in as root (good!) so something\n" +" else must be wrong. Did you read the manual and the FAQ?\n", blurb()); + } + + fprintf (stderr, "\n" + " http://www.jwz.org/xscreensaver/faq.html\n" + " http://www.jwz.org/xscreensaver/man.html\n" + "\n"); fflush (stderr); fflush (stdout); @@ -419,22 +448,44 @@ set_version_string (saver_info *si, int *argc, char **argv) static void privileged_initialization (saver_info *si, int *argc, char **argv) { +#ifndef NO_LOCKING + /* before hack_uid() for proper permissions */ + lock_priv_init (*argc, argv, si->prefs.verbose_p); +#endif /* NO_LOCKING */ + + hack_uid (si); +} + + +/* Figure out what locking mechanisms are supported. + */ +static void +lock_initialization (saver_info *si, int *argc, char **argv) +{ #ifdef NO_LOCKING si->locking_disabled_p = True; si->nolock_reason = "not compiled with locking support"; #else /* !NO_LOCKING */ - si->locking_disabled_p = False; - /* before hack_uid() for proper permissions */ + + /* Finish initializing locking, now that we're out of privileged code. */ if (! lock_init (*argc, argv, si->prefs.verbose_p)) { si->locking_disabled_p = True; si->nolock_reason = "error getting password"; } + + /* 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"; + } #endif /* NO_LOCKING */ -#ifndef NO_SETUID hack_uid (si); -#endif /* NO_SETUID */ } @@ -445,6 +496,20 @@ connect_to_server (saver_info *si, int *argc, char **argv) { Widget toplevel_shell; +#ifdef HAVE_PUTENV + char *d = getenv ("DISPLAY"); + if (!d || !*d) + { + char ndpy[] = "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 (); + } +#endif /* HAVE_PUTENV */ + XSetErrorHandler (saver_ehandler); XtAppSetErrorMsgHandler (si->app, startup_ehandler); @@ -466,10 +531,12 @@ connect_to_server (saver_info *si, int *argc, char **argv) XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False); XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False); XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False); - XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False); + XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False); 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); @@ -481,6 +548,9 @@ connect_to_server (saver_info *si, int *argc, char **argv) XA_DEMO = XInternAtom (si->dpy, "DEMO", False); XA_PREFS = XInternAtom (si->dpy, "PREFS", False); XA_LOCK = XInternAtom (si->dpy, "LOCK", False); + XA_BLANK = XInternAtom (si->dpy, "BLANK", False); + XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False); + XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False); return toplevel_shell; } @@ -573,7 +643,7 @@ print_banner (saver_info *si) if (p->verbose_p) fprintf (stderr, - "%s %s, copyright (c) 1991-1999 " + "%s %s, copyright (c) 1991-2002 " "by Jamie Zawinski .\n", progname, si->version); @@ -629,7 +699,7 @@ print_banner (saver_info *si) /* Examine all of the display's screens, and populate the `saver_screen_info' - structures. + structures. Make sure this is called after hack_environment() sets $PATH. */ static void initialize_per_screen_info (saver_info *si, Widget toplevel_shell) @@ -648,6 +718,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 = @@ -656,6 +727,9 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell) ssi->current_visual = ssi->default_visual; ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual); + /* Execute a subprocess to find the GL visual. */ + ssi->best_gl_visual = get_best_gl_visual (ssi); + if (ssi == si->default_screen) /* Since this is the default screen, use the one already created. */ ssi->toplevel_shell = toplevel_shell; @@ -685,7 +759,11 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell) } } - si->prefs.fading_possible_p = found_any_writable_cells; + si->fading_possible_p = found_any_writable_cells; + +#ifdef HAVE_XF86VMODE_GAMMA + si->fading_possible_p = True; /* if we can gamma fade, go for it */ +#endif } @@ -843,6 +921,16 @@ maybe_reload_init_file (saver_info *si) /* If a server extension is in use, and p->timeout has changed, we need to inform the server of the new timeout. */ disable_builtin_screensaver (si, False); + + /* 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 && + p->mode != DONT_BLANK), + p->dpms_standby / 1000, + p->dpms_suspend / 1000, + p->dpms_off / 1000, + False); } } @@ -864,6 +952,7 @@ main_loop (saver_info *si) while (1) { + Bool was_locked = False; sleep_until_idle (si, True); if (p->verbose_p) @@ -872,40 +961,98 @@ 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); - blank_screen (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. + This means we did not (and must not) blank the screen. + If we were to blank the screen while some other program + is holding both the mouse and keyboard grabbed, then + we would never be able to un-blank it! We would never + see any events, and the display would be wedged. + + So, just go around the loop again and wait for the + next bout of idleness. + */ + + fprintf (stderr, + "%s: unable to grab keyboard or mouse! Blanking aborted.\n", + blurb()); + continue; + } + kill_screenhack (si); - spawn_screenhack (si, True); + + if (!si->throttled_p) + spawn_screenhack (si, True); + else if (p->verbose_p) + fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb()); /* Don't start the cycle timer in demo mode. */ if (!si->demoing_p && p->cycle) - si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer, + si->cycle_id = XtAppAddTimeOut (si->app, + (si->selection_mode + /* see comment in cycle_timer() */ + ? 1000 * 60 * 60 + : p->cycle), + cycle_timer, (XtPointer) si); #ifndef NO_LOCKING - if (!si->demoing_p && /* if not going into demo mode */ - p->lock_p && /* and locking is enabled */ - !si->locking_disabled_p && /* and locking is possible */ - p->lock_timeout == 0) /* and locking is not timer-deferred */ - si->locked_p = True; /* then lock right now. */ - - /* locked_p might be true already because of the above, or because of - the LOCK ClientMessage. But if not, and if we're supposed to lock - after some time, set up a timer to do so. + /* Maybe start locking the screen. */ - if (p->lock_p && - !si->locked_p && - p->lock_timeout > 0) - si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout, - activate_lock_timer, - (XtPointer) si); + { + Time lock_timeout = p->lock_timeout; + + if (si->emergency_lock_p && p->lock_p && lock_timeout) + { + int secs = p->lock_timeout / 1000; + if (p->verbose_p) + fprintf (stderr, + "%s: locking now, instead of waiting for %d:%02d:%02d.\n", + blurb(), + (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60)); + lock_timeout = 0; + } + + si->emergency_lock_p = False; + + if (!si->demoing_p && /* if not going into demo mode */ + p->lock_p && /* and locking is enabled */ + !si->locking_disabled_p && /* and locking is possible */ + lock_timeout == 0) /* and locking is not timer-deferred */ + set_locked_p (si, True); /* then lock right now. */ + + /* locked_p might be true already because of the above, or because of + the LOCK ClientMessage. But if not, and if we're supposed to lock + after some time, set up a timer to do so. + */ + if (p->lock_p && + !si->locked_p && + lock_timeout > 0) + si->lock_id = XtAppAddTimeOut (si->app, lock_timeout, + activate_lock_timer, + (XtPointer) si); + } #endif /* !NO_LOCKING */ @@ -916,11 +1063,14 @@ main_loop (saver_info *si) maybe_reload_init_file (si); #ifndef NO_LOCKING + /* Maybe unlock the screen. + */ if (si->locked_p) { saver_screen_info *ssi = si->default_screen; if (si->locking_disabled_p) abort (); + was_locked = True; si->dbox_up_p = True; suspend_screenhack (si, True); XUndefineCursor (si->dpy, ssi->screensaver_window); @@ -930,6 +1080,20 @@ main_loop (saver_info *si) si->dbox_up_p = False; XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor); suspend_screenhack (si, False); /* resume */ + + if (!ok_to_unblank && + !screenhack_running_p (si)) + { + /* If the lock dialog has been dismissed and we're not about to + unlock the screen, and there is currently no hack running, + then launch one. (There might be no hack running if DPMS + had kicked in. But DPMS is off now, so bring back the hack) + */ + if (si->cycle_id) + XtRemoveTimeOut (si->cycle_id); + si->cycle_id = 0; + cycle_timer ((XtPointer) si, 0); + } } #endif /* !NO_LOCKING */ @@ -944,10 +1108,21 @@ main_loop (saver_info *si) kill_screenhack (si); unblank_screen (si); - si->locked_p = False; + set_locked_p (si, False); + si->emergency_lock_p = False; si->demoing_p = 0; si->selection_mode = 0; + /* If we're throttled, and the user has explicitly unlocked the screen, + then unthrottle. If we weren't locked, then don't unthrottle + automatically, because someone might have just bumped the desk... */ + if (was_locked) + { + if (si->throttled_p && p->verbose_p) + fprintf (stderr, "%s: unthrottled.\n", blurb()); + si->throttled_p = False; + } + if (si->cycle_id) { XtRemoveTimeOut (si->cycle_id); @@ -960,6 +1135,53 @@ 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); + } + if (p->verbose_p) fprintf (stderr, "%s: awaiting idleness.\n", blurb()); } @@ -979,7 +1201,8 @@ main (int argc, char **argv) memset(si, 0, sizeof(*si)); global_si_kludge = si; /* I hate C so much... */ - srandom ((int) time ((time_t *) 0)); +# undef ya_rand_init + ya_rand_init (0); save_argv (argc, argv); set_version_string (si, &argc, argv); @@ -990,24 +1213,45 @@ main (int argc, char **argv) process_command_line (si, &argc, argv); print_banner (si); - initialize_per_screen_info (si, shell); /* also sets p->fading_possible_p */ + 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. */ + 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" + "%s: ignoring the request for fading/unfading.\n", + blurb(), blurb()); for (i = 0; i < si->nscreens; i++) if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen)) exit (1); - load_init_file (p); + lock_initialization (si, &argc, argv); 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); + + si->blank_time = time ((time_t) 0); /* must be before ..._window */ initialize_screensaver_window (si); + select_events (si); init_sigchld (); + disable_builtin_screensaver (si, True); + 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); @@ -1019,6 +1263,47 @@ main (int argc, char **argv) /* Processing ClientMessage events. */ + +static Bool error_handler_hit_p = False; + +static int +ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) +{ + error_handler_hit_p = True; + return 0; +} + +/* Sometimes some systems send us ClientMessage events with bogus atoms in + them. We only look up the atom names for printing warning messages, + so don't bomb out when it happens... + */ +static char * +XGetAtomName_safe (Display *dpy, Atom atom) +{ + char *result; + XErrorHandler old_handler; + if (!atom) return 0; + + XSync (dpy, False); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + result = XGetAtomName (dpy, atom); + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + if (error_handler_hit_p) result = 0; + + if (result) + return result; + else + { + char buf[100]; + sprintf (buf, "<>", (unsigned long) atom); + return strdup (buf); + } +} + + static void clientmessage_response (saver_info *si, Window w, Bool error, const char *stderr_msg, @@ -1027,6 +1312,8 @@ clientmessage_response (saver_info *si, Window w, Bool error, char *proto; int L; saver_preferences *p = &si->prefs; + XErrorHandler old_handler; + if (error || p->verbose_p) fprintf (stderr, "%s: %s\n", blurb(), stderr_msg); @@ -1036,34 +1323,94 @@ clientmessage_response (saver_info *si, Window w, Bool error, strcpy (proto+1, protocol_msg); L++; + /* Ignore all X errors while sending a response to a ClientMessage. + Pretty much the only way we could get an error here is if the + window we're trying to send the reply on has been deleted, in + which case, the sender of the ClientMessage won't see our response + anyway. + */ + XSync (si->dpy, False); + error_handler_hit_p = False; + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8, PropModeReplace, (unsigned char *) proto, L); + XSync (si->dpy, False); + XSetErrorHandler (old_handler); + XSync (si->dpy, False); + 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) { + saver_preferences *p = &si->prefs; Atom type = 0; Window window = event->xclient.window; /* 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 (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; } @@ -1077,6 +1424,11 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) "activating."); si->selection_mode = 0; si->demoing_p = False; + + if (si->throttled_p && p->verbose_p) + fprintf (stderr, "%s: unthrottled.\n", blurb()); + si->throttled_p = False; + if (si->using_mit_saver_extension || si->using_sgi_saver_extension) { XForceScreenSaver (si->dpy, ScreenSaverActive); @@ -1095,6 +1447,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) { if (! until_idle_p) { + if (si->throttled_p && p->verbose_p) + fprintf (stderr, "%s: unthrottled.\n", blurb()); + si->throttled_p = False; + clientmessage_response(si, window, False, "DEACTIVATE ClientMessage received.", "deactivating."); @@ -1108,9 +1464,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) { @@ -1121,6 +1478,11 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) "cycling."); si->selection_mode = 0; /* 0 means randomize when its time. */ si->demoing_p = False; + + if (si->throttled_p && p->verbose_p) + fprintf (stderr, "%s: unthrottled.\n", blurb()); + si->throttled_p = False; + if (si->cycle_id) XtRemoveTimeOut (si->cycle_id); si->cycle_id = 0; @@ -1141,6 +1503,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) si->selection_mode = (type == XA_NEXT ? -1 : -2); si->demoing_p = False; + if (si->throttled_p && p->verbose_p) + fprintf (stderr, "%s: unthrottled.\n", blurb()); + si->throttled_p = False; + if (! until_idle_p) { if (si->cycle_id) @@ -1165,6 +1531,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) si->selection_mode = which; si->demoing_p = False; + if (si->throttled_p && p->verbose_p) + fprintf (stderr, "%s: unthrottled.\n", blurb()); + si->throttled_p = False; + if (! until_idle_p) { if (si->cycle_id) @@ -1213,13 +1583,8 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) XSync (si->dpy, False); } - /* 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, @@ -1246,6 +1611,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) si->selection_mode = which; si->demoing_p = True; + if (si->throttled_p && p->verbose_p) + fprintf (stderr, "%s: unthrottled.\n", blurb()); + si->throttled_p = False; + return True; } @@ -1287,11 +1656,11 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) char *response = (until_idle_p ? "activating and locking." : "locking."); - si->locked_p = True; - si->selection_mode = 0; - si->demoing_p = False; sprintf (buf, "LOCK ClientMessage received; %s", response); clientmessage_response (si, window, False, buf, response); + set_locked_p (si, True); + si->selection_mode = 0; + si->demoing_p = False; if (si->lock_id) /* we're doing it now, so lose the timeout */ { @@ -1315,11 +1684,63 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) } #endif /* !NO_LOCKING */ } + else if (type == XA_THROTTLE) + { + if (si->throttled_p) + clientmessage_response (si, window, True, + "THROTTLE ClientMessage received, but " + "already throttled.", + "already throttled."); + else + { + char buf [255]; + char *response = "throttled."; + si->throttled_p = True; + si->selection_mode = 0; + si->demoing_p = False; + sprintf (buf, "THROTTLE ClientMessage received; %s", response); + clientmessage_response (si, window, False, buf, response); + + if (! until_idle_p) + { + if (si->cycle_id) + XtRemoveTimeOut (si->cycle_id); + si->cycle_id = 0; + cycle_timer ((XtPointer) si, 0); + } + } + } + else if (type == XA_UNTHROTTLE) + { + if (! si->throttled_p) + clientmessage_response (si, window, True, + "UNTHROTTLE ClientMessage received, but " + "not throttled.", + "not throttled."); + else + { + char buf [255]; + char *response = "unthrottled."; + si->throttled_p = False; + si->selection_mode = 0; + si->demoing_p = False; + sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response); + clientmessage_response (si, window, False, buf, response); + + if (! until_idle_p) + { + if (si->cycle_id) + XtRemoveTimeOut (si->cycle_id); + si->cycle_id = 0; + cycle_timer ((XtPointer) si, 0); + } + } + } else { char buf [1024]; char *str; - str = (type ? XGetAtomName(si->dpy, type) : 0); + str = XGetAtomName_safe (si->dpy, type); if (str) { @@ -1349,30 +1770,106 @@ static void analyze_display (saver_info *si) { int i, j; - static const char *exts[][2] = { - { "SCREEN_SAVER", "SGI Screen-Saver" }, - { "SCREEN-SAVER", "SGI Screen-Saver" }, - { "MIT-SCREEN-SAVER", "MIT Screen-Saver" }, - { "XIDLE", "XIdle" }, - { "SGI-VIDEO-CONTROL", "SGI Video-Control" }, - { "READDISPLAY", "SGI Read-Display" }, - { "MIT-SHM", "Shared Memory" }, - { "DOUBLE-BUFFER", "Double-Buffering" }, - { "DPMS", "Power Management" }, - { "GLX", "GLX" } + static struct { + const char *name; const char *desc; Bool useful_p; + } exts[] = { + + { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver", +# ifdef HAVE_SGI_SAVER_EXTENSION + True +# else + False +# endif + }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver", +# ifdef HAVE_SGI_SAVER_EXTENSION + True +# else + False +# endif + }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver", +# ifdef HAVE_MIT_SAVER_EXTENSION + True +# else + False +# endif + }, { "XIDLE", "XIdle", +# ifdef HAVE_XIDLE_EXTENSION + True +# else + False +# endif + }, { "SGI-VIDEO-CONTROL", "SGI Video-Control", +# ifdef HAVE_SGI_VC_EXTENSION + True +# else + False +# endif + }, { "READDISPLAY", "SGI Read-Display", +# ifdef HAVE_READ_DISPLAY_EXTENSION + True +# else + False +# endif + }, { "MIT-SHM", "Shared Memory", +# ifdef HAVE_XSHM_EXTENSION + True +# else + False +# endif + }, { "DOUBLE-BUFFER", "Double-Buffering", +# ifdef HAVE_DOUBLE_BUFFER_EXTENSION + True +# else + False +# endif + }, { "DPMS", "Power Management", +# ifdef HAVE_DPMS_EXTENSION + True +# else + False +# endif + }, { "GLX", "GLX", +# ifdef HAVE_GL + True +# else + False +# endif + }, { "XFree86-VidModeExtension", "XF86 Video-Mode", +# ifdef HAVE_XF86VMODE + True +# else + False +# endif + }, { "XINERAMA", "Xinerama", + 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][0], &op, &event, &error)) - fprintf (stderr, "%s: %s\n", blurb(), exts[i][1]); + 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++) @@ -1397,15 +1894,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"); } } }