X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=driver%2Fxscreensaver.c;h=4460c19df6125b9c358df07e4a3a179bb06f98e4;hp=8942df98c5198ad6708bcf5bf577508147e87cfe;hb=551b3de3f619c04c2dd1971ee9b3f02e270c28c9;hpb=ce3185de9d9705e259f2b60dd4b5509007fa17d4 diff --git a/driver/xscreensaver.c b/driver/xscreensaver.c index 8942df98..4460c19d 100644 --- a/driver/xscreensaver.c +++ b/driver/xscreensaver.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski +/* xscreensaver, Copyright (c) 1991-1999 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 @@ -131,6 +131,7 @@ #include #include #include +#include /* for gethostbyname() */ #ifdef HAVE_XMU # ifndef VMS # include @@ -142,7 +143,7 @@ #endif /* !HAVE_XMU */ #ifdef HAVE_XIDLE_EXTENSION -#include +# include #endif /* HAVE_XIDLE_EXTENSION */ #include "xscreensaver.h" @@ -150,6 +151,7 @@ #include "yarandom.h" #include "resources.h" #include "visual.h" +#include "usleep.h" saver_info *global_si_kludge = 0; /* I hate C so much... */ @@ -158,8 +160,9 @@ char *progclass = 0; 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; +static Atom XA_EXIT, XA_RESTART, XA_LOCK, XA_SELECT; Atom XA_DEMO, XA_PREFS; @@ -168,27 +171,35 @@ static XrmOptionDescRec options [] = { { "-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" }, { "-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" }, { "-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" }, { "-splash", ".splash", XrmoptionNoArg, "on" }, { "-no-splash", ".splash", XrmoptionNoArg, "off" }, { "-nosplash", ".splash", XrmoptionNoArg, "off" }, { "-idelay", ".initialDelay", XrmoptionSepArg, 0 }, { "-nice", ".nice", XrmoptionSepArg, 0 }, - /* Actually this one is built in to Xt, but just to be sure... */ - { "-synchronous", ".synchronous", XrmoptionNoArg, "on" } + /* Actually these are built in to Xt, but just to be sure... */ + { "-synchronous", ".synchronous", XrmoptionNoArg, "on" }, + { "-xrm", NULL, XrmoptionResArg, NULL } }; static char *defaults[] = { @@ -206,7 +217,7 @@ do_help (saver_info *si) fflush (stdout); fflush (stderr); fprintf (stdout, "\ -xscreensaver %s, copyright (c) 1991-1998 by Jamie Zawinski \n\ +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\ @@ -222,7 +233,8 @@ The standard Xt command-line options are accepted; other options include:\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-command' program to manipulate a running xscreensaver.\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\ @@ -230,7 +242,7 @@ the screensaver. See `man xscreensaver' or the web page for more details.\n\ Just getting started? Try this:\n\ \n\ xscreensaver &\n\ - xscreensaver-command -demo\n\ + xscreensaver-demo\n\ \n\ For updates, check http://www.jwz.org/xscreensaver/\n\ \n", @@ -241,265 +253,6 @@ For updates, check http://www.jwz.org/xscreensaver/\n\ } -static char * -reformat_hack(const char *hack) -{ - int i; - const char *in = hack; - int indent = 13; - char *h2 = (char *) malloc(strlen(in) + indent + 2); - char *out = h2; - - while (isspace(*in)) in++; /* skip whitespace */ - while (*in && !isspace(*in) && *in != ':') - *out++ = *in++; /* snarf first token */ - while (isspace(*in)) in++; /* skip whitespace */ - - if (*in == ':') - *out++ = *in++; /* copy colon */ - else - { - in = hack; - out = h2; /* reset to beginning */ - } - - *out = 0; - - while (isspace(*in)) in++; /* skip whitespace */ - for (i = strlen(h2); i < indent; i++) /* indent */ - *out++ = ' '; - - while (*in) *out++ = *in++; /* copy rest of line */ - *out = 0; - - return h2; -} - - -static void -get_screenhacks (saver_info *si) -{ - saver_preferences *p = &si->prefs; - int i = 0; - int hacks_size = 60; - int size; - char *d; - - d = get_string_resource ("monoPrograms", "MonoPrograms"); - if (d && !*d) { free(d); d = 0; } - if (!d) - d = get_string_resource ("colorPrograms", "ColorPrograms"); - if (d && !*d) { free(d); d = 0; } - - if (d) - { - fprintf (stderr, - "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\ - see the manual for details.\n", blurb()); - free(d); - } - - d = get_string_resource ("programs", "Programs"); - - size = d ? strlen (d) : 0; - p->screenhacks = (char **) malloc (sizeof (char *) * hacks_size); - p->screenhacks_count = 0; - - while (i < size) - { - int end, start = i; - if (d[i] == ' ' || d[i] == '\t' || d[i] == '\n' || d[i] == 0) - { - i++; - continue; - } - if (hacks_size <= p->screenhacks_count) - p->screenhacks = (char **) realloc (p->screenhacks, - (hacks_size = hacks_size * 2) * - sizeof (char *)); - p->screenhacks [p->screenhacks_count++] = d + i; - while (d[i] != 0 && d[i] != '\n') - i++; - end = i; - while (i > start && (d[i-1] == ' ' || d[i-1] == '\t')) - i--; - d[i] = 0; - i = end + 1; - } - - /* shrink all whitespace to one space, for the benefit of the "demo" - mode display. We only do this when we can easily tell that the - whitespace is not significant (no shell metachars). - */ - for (i = 0; i < p->screenhacks_count; i++) - { - char *s = p->screenhacks [i]; - char *s2; - int L = strlen (s); - int j, k; - for (j = 0; j < L; j++) - { - switch (s[j]) - { - case '\'': case '"': case '`': case '\\': - goto DONE; - case '\t': - s[j] = ' '; - case ' ': - k = 0; - for (s2 = s+j+1; *s2 == ' ' || *s2 == '\t'; s2++) - k++; - if (k > 0) - { - for (s2 = s+j+1; s2[k]; s2++) - *s2 = s2[k]; - *s2 = 0; - } - break; - } - } - DONE: - p->screenhacks[i] = reformat_hack(s); /* mallocs */ - } - - if (p->screenhacks_count) - { - /* Shrink down the screenhacks array to be only as big as it needs to. - This doesn't really matter at all. */ - p->screenhacks = (char **) - realloc (p->screenhacks, ((p->screenhacks_count + 1) * - sizeof(char *))); - p->screenhacks [p->screenhacks_count] = 0; - } - else - { - free (p->screenhacks); - p->screenhacks = 0; - } -} - - -static Bool blurb_timestamp_p = False; /* kludge */ - - -static void -get_resources (saver_info *si) -{ - char *s; - saver_preferences *p = &si->prefs; - - p->xsync_p = get_boolean_resource ("synchronous", "Synchronous"); - if (p->xsync_p) - XSynchronize(si->dpy, True); - - p->verbose_p = get_boolean_resource ("verbose", "Boolean"); - p->timestamp_p = get_boolean_resource ("timestamp", "Boolean"); - p->lock_p = get_boolean_resource ("lock", "Boolean"); - p->fade_p = get_boolean_resource ("fade", "Boolean"); - p->unfade_p = get_boolean_resource ("unfade", "Boolean"); - p->fade_seconds = get_seconds_resource ("fadeSeconds", "Time"); - p->fade_ticks = get_integer_resource ("fadeTicks", "Integer"); - p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean"); - p->nice_inferior = get_integer_resource ("nice", "Nice"); - - p->initial_delay = get_seconds_resource ("initialDelay", "Time"); - p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time"); - p->timeout = 1000 * get_minutes_resource ("timeout", "Time"); - p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time"); - p->cycle = 1000 * get_minutes_resource ("cycle", "Time"); - -#ifndef NO_LOCKING - p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time"); -#endif - - p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time"); - p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout", - "Time"); - p->shell = get_string_resource ("bourneShell", "BourneShell"); - - p->help_url = get_string_resource("helpURL", "URL"); - p->load_url_command = get_string_resource("loadURL", "LoadURL"); - - if ((s = get_string_resource ("splash", "Boolean"))) - if (!get_boolean_resource("splash", "Boolean")) - p->splash_duration = 0; - if (s) free (s); - - /* don't set use_xidle_extension unless it is explicitly specified */ - if ((s = get_string_resource ("xidleExtension", "Boolean"))) - p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean"); - else -#ifdef HAVE_XIDLE_EXTENSION /* pick a default */ - p->use_xidle_extension = True; /* if we have it, use it */ -#else /* !HAVE_XIDLE_EXTENSION */ - p->use_xidle_extension = False; -#endif /* !HAVE_XIDLE_EXTENSION */ - if (s) free (s); - - /* don't set use_mit_extension unless it is explicitly specified */ - if ((s = get_string_resource ("mitSaverExtension", "Boolean"))) - p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension", - "Boolean"); - else -#ifdef HAVE_MIT_SAVER_EXTENSION /* pick a default */ - p->use_mit_saver_extension = False; /* Default false, because it sucks */ -#else /* !HAVE_MIT_SAVER_EXTENSION */ - p->use_mit_saver_extension = False; -#endif /* !HAVE_MIT_SAVER_EXTENSION */ - if (s) free (s); - - - /* don't set use_mit_extension unless it is explicitly specified */ - if ((s = get_string_resource ("sgiSaverExtension", "Boolean"))) - p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension", - "Boolean"); - else -#ifdef HAVE_SGI_SAVER_EXTENSION /* pick a default */ - p->use_sgi_saver_extension = True; /* if we have it, use it */ -#else /* !HAVE_SGI_SAVER_EXTENSION */ - p->use_sgi_saver_extension = False; -#endif /* !HAVE_SGI_SAVER_EXTENSION */ - if (s) free (s); - - - /* Throttle the various timeouts to reasonable values. - */ -#ifndef NO_LOCKING - if (p->passwd_timeout == 0) p->passwd_timeout = 30000; /* 30 secs */ -#endif - if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */ - if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */ - if (p->pointer_timeout == 0) p->pointer_timeout = 5000; /* 5 secs */ - if (p->notice_events_timeout == 0) - p->notice_events_timeout = 10000; /* 10 secs */ - if (p->fade_seconds == 0 || p->fade_ticks == 0) - p->fade_p = False; - if (! p->fade_p) p->unfade_p = False; - - p->watchdog_timeout = p->cycle; - if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */ - if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */ - -#ifdef NO_LOCKING - si->locking_disabled_p = True; - si->nolock_reason = "not compiled with locking support"; -#endif /* NO_LOCKING */ - - get_screenhacks (si); - - if (p->debug_p) - { - XSynchronize(si->dpy, True); - p->xsync_p = True; - p->verbose_p = True; - p->timestamp_p = True; - p->initial_delay = 0; - } - - blurb_timestamp_p = p->timestamp_p; -} - - char * timestring (void) { @@ -510,20 +263,26 @@ timestring (void) return str; } -static void initialize (saver_info *si, int argc, char **argv); -static void main_loop (saver_info *si); +static Bool blurb_timestamp_p = False; /* kludge */ -int -main (int argc, char **argv) +const char * +blurb (void) { - saver_info si; - memset(&si, 0, sizeof(si)); - global_si_kludge = &si; /* I hate C so much... */ - initialize (&si, argc, argv); - if (!si.demo_mode_p) - pop_splash_dialog (&si); - main_loop (&si); /* doesn't return */ - return 0; + if (!blurb_timestamp_p) + return progname; + else + { + static char buf[255]; + char *ct = timestring(); + int n = strlen(progname); + if (n > 100) n = 99; + strncpy(buf, progname, n); + buf[n++] = ':'; + buf[n++] = ' '; + strncpy(buf+n, ct+11, 8); + strcpy(buf+n+9, ": "); + return buf; + } } @@ -532,6 +291,8 @@ saver_ehandler (Display *dpy, XErrorEvent *error) { saver_info *si = global_si_kludge; /* I hate C so much... */ + if (!real_stderr) real_stderr = stderr; + fprintf (real_stderr, "\n" "#######################################" "#######################################\n\n" @@ -548,9 +309,25 @@ saver_ehandler (Display *dpy, XErrorEvent *error) } else { - fprintf(real_stderr, - "%s: to dump a core file, re-run with `-sync'.\n\n", - blurb()); + fprintf (real_stderr, + "#######################################" + "#######################################\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" + "\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" + "\n" + " The more information you can provide, the better. But please report\n" + " report this bug, regardless!\n" + "\n"); + fprintf (real_stderr, + "#######################################" + "#######################################\n\n"); + saver_exit (si, -1, 0); } } @@ -560,32 +337,67 @@ saver_ehandler (Display *dpy, XErrorEvent *error) } -const char * -blurb (void) +/* This error handler is used only while the X connection is being set up; + after we've got a connection, we don't use this handler again. The only + reason for having this is so that we can present a more idiot-proof error + message than "cannot open display." + */ +static void +startup_ehandler (String name, String type, String class, + String defalt, /* one can't even spel properly + in this joke of a language */ + String *av, Cardinal *ac) { - if (!blurb_timestamp_p) - return progname; - else - { - static char buf[255]; - char *ct = timestring(); - int n = strlen(progname); - if (n > 100) n = 99; - strncpy(buf, progname, n); - buf[n++] = ':'; - buf[n++] = ' '; - strncpy(buf+n, ct+11, 8); - strcpy(buf+n+9, ": "); - return buf; - } + char fmt[512]; + String p[10]; + saver_info *si = global_si_kludge; /* I hate C so much... */ + XrmDatabase *db = XtAppGetErrorDatabase(si->app); + *fmt = 0; + XtAppGetErrorDatabaseText(si->app, name, type, class, defalt, + fmt, sizeof(fmt)-1, *db); + + fprintf (stderr, "%s: ", blurb()); + + memset (p, 0, sizeof(p)); + if (*ac > countof (p)) *ac = countof (p); + memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av)); + fprintf (stderr, fmt, /* Did I mention that I hate C? */ + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]); + 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", + blurb()); + + fflush (stderr); + fflush (stdout); + exit (1); } + +/* The zillions of initializations. + */ + +/* Set progname, version, etc. This is done very early. + */ static void -initialize_connection (saver_info *si, int argc, char **argv) +set_version_string (saver_info *si, int *argc, char **argv) { - int i; - Widget toplevel_shell; - saver_preferences *p = &si->prefs; + progclass = "XScreenSaver"; + + /* progname is reset later, after we connect to X. */ + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + + if (strlen(progname) > 100) /* keep it short. */ + progname[99] = 0; /* The X resource database blows up if argv[0] has a "." in it. */ { @@ -594,68 +406,227 @@ initialize_connection (saver_info *si, int argc, char **argv) *s = '_'; } + si->version = (char *) malloc (5); + memcpy (si->version, screensaver_id + 17, 4); + si->version [4] = 0; +} + + +/* Initializations that potentially take place as a priveleged user: + If the xscreensaver executable is setuid root, then these initializations + are run as root, before discarding privileges. + */ +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 */ + +#ifndef NO_SETUID + hack_uid (si); +#endif /* NO_SETUID */ +} + + +/* 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; + + /* 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"; + } +#endif /* NO_LOCKING */ + +#ifndef NO_SETUID + hack_uid (si); +#endif /* NO_SETUID */ +} + + +/* Open the connection to the X server, and intern our Atoms. + */ +static Widget +connect_to_server (saver_info *si, int *argc, char **argv) +{ + Widget toplevel_shell; + + XSetErrorHandler (saver_ehandler); + + XtAppSetErrorMsgHandler (si->app, startup_ehandler); toplevel_shell = XtAppInitialize (&si->app, progclass, options, XtNumber (options), - &argc, argv, defaults, 0, 0); + argc, argv, defaults, 0, 0); + XtAppSetErrorMsgHandler (si->app, 0); si->dpy = XtDisplay (toplevel_shell); - si->db = XtDatabase (si->dpy); + si->prefs.db = XtDatabase (si->dpy); XtGetApplicationNameAndClass (si->dpy, &progname, &progclass); - if(strlen(progname) > 100) progname [99] = 0; /* keep it short. */ + if(strlen(progname) > 100) /* keep it short. */ + progname [99] = 0; + + db = si->prefs.db; /* resources.c needs this */ - db = si->db; /* resources.c needs this */ + XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False); + 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_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE", + False); + XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False); + XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False); + XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False); + XA_RESTART = XInternAtom (si->dpy, "RESTART", False); + XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False); + XA_NEXT = XInternAtom (si->dpy, "NEXT", False); + XA_PREV = XInternAtom (si->dpy, "PREV", False); + XA_SELECT = XInternAtom (si->dpy, "SELECT", False); + XA_EXIT = XInternAtom (si->dpy, "EXIT", False); + XA_DEMO = XInternAtom (si->dpy, "DEMO", False); + XA_PREFS = XInternAtom (si->dpy, "PREFS", False); + XA_LOCK = XInternAtom (si->dpy, "LOCK", False); - if (argc == 2 && - (!strcmp (argv[1], "-h") || - !strcmp (argv[1], "-help") || - !strcmp (argv[1], "--help"))) - do_help (si); + return toplevel_shell; +} - else if (argc == 2 && !strcmp (argv[1], "-debug")) - si->prefs.debug_p = True; /* no resource for this one, out of paranoia. */ - else if (argc > 1) +/* Handle the command-line arguments that were not handled for us by Xt. + Issue an error message and exit if there are unknown options. + */ +static void +process_command_line (saver_info *si, int *argc, char **argv) +{ + int i; + for (i = 1; i < *argc; i++) { - const char *s = argv[1]; - fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n", - blurb(), s); - - if (s[0] == '-' && s[1] == '-') s++; - if (!strcmp (s, "-activate") || - !strcmp (s, "-deactivate") || - !strcmp (s, "-cycle") || - !strcmp (s, "-next") || - !strcmp (s, "-prev") || - !strcmp (s, "-exit") || - !strcmp (s, "-restart") || - !strcmp (s, "-demo") || - !strcmp (s, "-prefs") || - !strcmp (s, "-preferences") || - !strcmp (s, "-lock") || - !strcmp (s, "-version") || - !strcmp (s, "-time")) + if (!strcmp (argv[i], "-debug")) + /* no resource for this one, out of paranoia. */ + si->prefs.debug_p = True; + + else if (!strcmp (argv[i], "-h") || + !strcmp (argv[i], "-help") || + !strcmp (argv[i], "--help")) + do_help (si); + + else { - fprintf (stderr, "\n\ - However, %s is an option to the `xscreensaver-command' program.\n\ + const char *s = argv[i]; + fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n", + blurb(), s); + + if (s[0] == '-' && s[1] == '-') s++; + if (!strcmp (s, "-activate") || + !strcmp (s, "-deactivate") || + !strcmp (s, "-cycle") || + !strcmp (s, "-next") || + !strcmp (s, "-prev") || + !strcmp (s, "-exit") || + !strcmp (s, "-restart") || + !strcmp (s, "-demo") || + !strcmp (s, "-prefs") || + !strcmp (s, "-preferences") || + !strcmp (s, "-lock") || + !strcmp (s, "-version") || + !strcmp (s, "-time")) + { + + if (!strcmp (s, "-demo") || !strcmp (s, "-prefs")) + fprintf (stderr, "\n\ + Perhaps you meant to run the `xscreensaver-demo' program instead?\n"); + else + fprintf (stderr, "\n\ + However, `%s' is an option to the `xscreensaver-command' program.\n", s); + + fprintf (stderr, "\ The `xscreensaver' program is a daemon that runs in the background.\n\ You control a running xscreensaver process by sending it messages\n\ - with `xscreensaver-command'. See the man pages for details,\n\ - or check the web page: http://www.jwz.org/xscreensaver/\n\n", - s); - - /* Since version 1.21 renamed the "-lock" option to "-lock-mode", - suggest that explicitly. */ - if (!strcmp (s, "-lock")) - fprintf (stderr, "\ + with `xscreensaver-demo' or `xscreensaver-command'.\n\ +. See the man pages for details, or check the web page:\n\ + http://www.jwz.org/xscreensaver/\n\n"); + + /* Since version 1.21 renamed the "-lock" option to "-lock-mode", + suggest that explicitly. */ + if (!strcmp (s, "-lock")) + fprintf (stderr, "\ Or perhaps you meant either the \"-lock-mode\" or the\n\ \"-lock-timeout \" options to xscreensaver?\n\n"); + } + + exit (1); } + } +} - exit (1); + +/* Print out the xscreensaver banner to the tty if applicable; + Issue any other warnings that are called for at this point. + */ +static void +print_banner (saver_info *si) +{ + saver_preferences *p = &si->prefs; + + /* This resource gets set some time before the others, so that we know + 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")); + + /* Ditto, for the locking_disabled_p message. */ + p->lock_p = get_boolean_resource ("lock", "Boolean"); + + if (p->verbose_p) + fprintf (stderr, + "%s %s, copyright (c) 1991-1999 " + "by Jamie Zawinski .\n", + progname, si->version); + + if (p->debug_p) + fprintf (stderr, "\n" + "%s: Warning: running in DEBUG MODE. Be afraid.\n" + "\n" + "\tNote that in debug mode, the xscreensaver window will only\n" + "\tcover the left half of the screen. (The idea is that you\n" + "\tcan still see debugging output in a shell, if you position\n" + "\tit on the right side of the screen.)\n" + "\n" + "\tDebug mode is NOT SECURE. Do not run with -debug in\n" + "\tuntrusted environments.\n" + "\n", + blurb()); + + if (p->verbose_p) + { + if (!si->uid_message || !*si->uid_message) + describe_uids (si, stderr); + else + { + if (si->orig_uid && *si->orig_uid) + fprintf (stderr, "%s: initial effective uid/gid was %s.\n", + blurb(), si->orig_uid); + fprintf (stderr, "%s: %s\n", blurb(), si->uid_message); + } + + fprintf (stderr, "%s: in process %lu.\n", blurb(), + (unsigned long) getpid()); } - get_resources (si); + /* 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) { p->lock_p = False; @@ -664,390 +635,458 @@ initialize_connection (saver_info *si, int argc, char **argv) if (strstr (si->nolock_reason, "passw")) fprintf (stderr, "%s: does xscreensaver need to be setuid? " "consult the manual.\n", blurb()); + else if (strstr (si->nolock_reason, "running as ")) + fprintf (stderr, + "%s: locking only works when xscreensaver is launched\n" + "\t by a normal, non-privileged user (e.g., not \"root\".)\n" + "\t See the manual for details.\n", + blurb()); } +} - /* Defer the printing of this message until after we have loaded the - resources and know whether `verbose' is on. - */ - if (p->verbose_p && si->uid_message) - { - if (si->orig_uid && *si->orig_uid) - fprintf (stderr, "%s: initial effective uid/gid was %s.\n", blurb(), - si->orig_uid); - fprintf (stderr, "%s: %s\n", blurb(), si->uid_message); - } - XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False); - 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_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False); - XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False); - XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False); - XA_RESTART = XInternAtom (si->dpy, "RESTART", False); - XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False); - XA_NEXT = XInternAtom (si->dpy, "NEXT", False); - XA_PREV = XInternAtom (si->dpy, "PREV", False); - XA_EXIT = XInternAtom (si->dpy, "EXIT", False); - XA_DEMO = XInternAtom (si->dpy, "DEMO", False); - XA_PREFS = XInternAtom (si->dpy, "PREFS", False); - XA_LOCK = XInternAtom (si->dpy, "LOCK", False); +/* Examine all of the display's screens, and populate the `saver_screen_info' + structures. + */ +static void +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); - 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->global = si; + ssi->screen = ScreenOfDisplay (si->dpy, i); + + /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */ + ssi->default_visual = + get_visual_resource (ssi->screen, "visualID", "VisualID", False); + + ssi->current_visual = ssi->default_visual; + ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual); + + if (ssi == si->default_screen) + /* Since this is the default screen, use the one already created. */ + ssi->toplevel_shell = toplevel_shell; + else + /* Otherwise, each screen must have its own unmapped root widget. */ + ssi->toplevel_shell = + XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass, + si->dpy, + XtNscreen, ssi->screen, + XtNvisual, ssi->current_visual, + XtNdepth, visual_depth (ssi->screen, + ssi->current_visual), + 0); + + if (! found_any_writable_cells) + { + /* Check to see whether fading is ever possible -- if any of the + screens on the display has a PseudoColor visual, then fading can + work (on at least some screens.) If no screen has a PseudoColor + visual, then don't bother ever trying to fade, because it will + just cause a delay without causing any visible effect. + */ + if (has_writable_cells (ssi->screen, ssi->current_visual) || + get_visual (ssi->screen, "PseudoColor", True, False) || + get_visual (ssi->screen, "GrayScale", True, False)) + found_any_writable_cells = True; + } + } + + si->prefs.fading_possible_p = found_any_writable_cells; +} + + +/* If any server extensions have been requested, try and initialize them. + Issue warnings if requests can't be honored. + */ +static void +initialize_server_extensions (saver_info *si) +{ + saver_preferences *p = &si->prefs; + + Bool server_has_xidle_extension_p = False; + Bool server_has_sgi_saver_extension_p = False; + Bool server_has_mit_saver_extension_p = False; + Bool system_has_proc_interrupts_p = False; + const char *piwhy = 0; + + si->using_xidle_extension = p->use_xidle_extension; + si->using_sgi_saver_extension = p->use_sgi_saver_extension; + si->using_mit_saver_extension = p->use_mit_saver_extension; + si->using_proc_interrupts = p->use_proc_interrupts; + +#ifdef HAVE_XIDLE_EXTENSION + server_has_xidle_extension_p = query_xidle_extension (si); +#endif +#ifdef HAVE_SGI_SAVER_EXTENSION + server_has_sgi_saver_extension_p = query_sgi_saver_extension (si); +#endif +#ifdef HAVE_MIT_SAVER_EXTENSION + server_has_mit_saver_extension_p = query_mit_saver_extension (si); +#endif +#ifdef HAVE_PROC_INTERRUPTS + system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy); +#endif + + if (!server_has_xidle_extension_p) + si->using_xidle_extension = False; + else if (p->verbose_p) + { + if (si->using_xidle_extension) + fprintf (stderr, "%s: using XIDLE extension.\n", blurb()); + else + fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb()); + } + + if (!server_has_sgi_saver_extension_p) + si->using_sgi_saver_extension = False; + else if (p->verbose_p) + { + if (si->using_sgi_saver_extension) + fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb()); + else + fprintf (stderr, + "%s: not using server's SGI SCREEN_SAVER extension.\n", + blurb()); + } + + if (!server_has_mit_saver_extension_p) + si->using_mit_saver_extension = False; + else if (p->verbose_p) + { + if (si->using_mit_saver_extension) + fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n", + blurb()); + else + fprintf (stderr, + "%s: not using server's lame MIT-SCREEN-SAVER extension.\n", + blurb()); + } + + if (!system_has_proc_interrupts_p) + { + si->using_proc_interrupts = False; + if (p->verbose_p && piwhy) + fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(), + piwhy); + } + else if (p->verbose_p) + { + if (si->using_proc_interrupts) + fprintf (stderr, + "%s: consulting /proc/interrupts for keyboard activity.\n", + blurb()); + else + fprintf (stderr, + "%s: not consulting /proc/interrupts for keyboard activity.\n", + blurb()); + } +} + + +/* For the case where we aren't using an server extensions, select user events + on all the existing windows, and launch timers to select events on + newly-created windows as well. + + If a server extension is being used, this does nothing. + */ +static void +select_events (saver_info *si) +{ + saver_preferences *p = &si->prefs; + int i; + + if (si->using_xidle_extension || + si->using_mit_saver_extension || + si->using_sgi_saver_extension) + return; + + if (p->initial_delay) + { + if (p->verbose_p) + { + fprintf (stderr, "%s: waiting for %d second%s...", blurb(), + (int) p->initial_delay/1000, + (p->initial_delay == 1000 ? "" : "s")); + fflush (stderr); + fflush (stdout); + } + usleep (p->initial_delay); + if (p->verbose_p) + fprintf (stderr, " done.\n"); + } - for (i = 0; i < si->nscreens; i++) + if (p->verbose_p) { - saver_screen_info *ssi = &si->screens[i]; - ssi->global = si; - ssi->screen = ScreenOfDisplay (si->dpy, i); + fprintf (stderr, "%s: selecting events on extant windows...", blurb()); + fflush (stderr); + fflush (stdout); + } - /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */ - ssi->default_visual = - get_visual_resource (ssi->screen, "visualID", "VisualID", False); + /* Select events on the root windows of every screen. This also selects + 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); - ssi->current_visual = ssi->default_visual; - ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual); + if (p->verbose_p) + fprintf (stderr, " done.\n"); +} - if (ssi == si->default_screen) - /* Since this is the default screen, use the one already created. */ - ssi->toplevel_shell = toplevel_shell; - else - /* Otherwise, each screen must have its own unmapped root widget. */ - ssi->toplevel_shell = - XtVaAppCreateShell(progname, progclass, applicationShellWidgetClass, - si->dpy, - XtNscreen, ssi->screen, - XtNvisual, ssi->current_visual, - XtNdepth, visual_depth(ssi->screen, - ssi->current_visual), - 0); + +void +maybe_reload_init_file (saver_info *si) +{ + saver_preferences *p = &si->prefs; + if (init_file_changed_p (p)) + { + if (p->verbose_p) + fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n", + blurb(), init_file_name()); + + load_init_file (p); + + /* 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); } } +/* Loop forever: + + - wait until the user is idle; + - blank the screen; + - wait until the user is active; + - unblank the screen; + - repeat. + + */ static void -initialize (saver_info *si, int argc, char **argv) +main_loop (saver_info *si) { - int i; saver_preferences *p = &si->prefs; - Bool initial_demo_mode_p = False; - si->version = (char *) malloc (5); - memcpy (si->version, screensaver_id + 17, 4); - si->version [4] = 0; - progname = argv[0]; /* reset later; this is for the benefit of lock_init() */ + Bool ok_to_unblank; - if(strlen(progname) > 100) progname[99] = 0; /* keep it short. */ + while (1) + { + sleep_until_idle (si, True); -#ifdef NO_LOCKING - si->locking_disabled_p = True; - si->nolock_reason = "not compiled with locking support"; -#else /* !NO_LOCKING */ - si->locking_disabled_p = False; + if (p->verbose_p) + { + if (si->demoing_p) + 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()); + } -# ifdef SCO - set_auth_parameters(argc, argv); -# endif /* SCO */ + maybe_reload_init_file (si); - if (! lock_init (argc, argv)) /* before hack_uid() for proper permissions */ - { - si->locking_disabled_p = True; - si->nolock_reason = "error getting password"; - } -#endif /* !NO_LOCKING */ + 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. -#ifndef NO_SETUID - hack_uid (si); -#endif /* NO_SETUID */ + So, just go around the loop again and wait for the + next bout of idleness. + */ - progclass = "XScreenSaver"; + fprintf (stderr, + "%s: unable to grab keyboard or mouse! Blanking aborted.\n", + blurb()); + continue; + } - /* remove -initial-demo-mode switch before saving argv */ - for (i = 1; i < argc; i++) - while (!strcmp ("-initial-demo-mode", argv [i])) - { - int j; - initial_demo_mode_p = True; - for (j = i; j < argc; j++) - argv [j] = argv [j+1]; - argv [j] = 0; - argc--; - if (argc <= i) break; - } - save_argv (argc, argv); - initialize_connection (si, argc, argv); + kill_screenhack (si); + spawn_screenhack (si, True); - if (p->verbose_p) - fprintf (stderr, "\ -%s %s, copyright (c) 1991-1998 by Jamie Zawinski \n\ - pid = %d.\n", progname, si->version, (int) getpid ()); + /* 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, + (XtPointer) si); - - for (i = 0; i < si->nscreens; i++) - if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen)) - exit (1); - hack_environment (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. + */ + 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); +#endif /* !NO_LOCKING */ - si->demo_mode_p = initial_demo_mode_p; - srandom ((int) time ((time_t *) 0)); - if (p->debug_p) - fprintf (stderr, "\n" - "%s: Warning: running in DEBUG MODE. Be afraid.\n" - "\n" - "\tNote that in debug mode, the xscreensaver window will only\n" - "\tcover the left half of the screen. (The idea is that you\n" - "\tcan still see debugging output in a shell, if you position\n" - "\tit on the right side of the screen.)\n" - "\n" - "\tDebug mode is NOT SECURE. Do not run with -debug in\n" - "\tuntrusted environments.\n" - "\n", - progname); + ok_to_unblank = True; + do { - if (p->use_sgi_saver_extension) - { -#ifdef HAVE_SGI_SAVER_EXTENSION - if (! query_sgi_saver_extension (si)) - { - fprintf (stderr, - "%s: display %s does not support the SGI SCREEN_SAVER extension.\n", - blurb(), DisplayString (si->dpy)); - p->use_sgi_saver_extension = False; - } - else if (p->use_mit_saver_extension) - { - fprintf (stderr, "%s: SGI SCREEN_SAVER extension used instead\ - of MIT-SCREEN-SAVER extension.\n", - blurb()); - p->use_mit_saver_extension = False; - } - else if (p->use_xidle_extension) - { - fprintf (stderr, - "%s: SGI SCREEN_SAVER extension used instead of XIDLE extension.\n", - blurb()); - p->use_xidle_extension = False; - } -#else /* !HAVE_MIT_SAVER_EXTENSION */ - fprintf (stderr, - "%s: not compiled with support for the SGI SCREEN_SAVER extension.\n", - blurb()); - p->use_sgi_saver_extension = False; -#endif /* !HAVE_SGI_SAVER_EXTENSION */ - } + sleep_until_idle (si, False); /* until not idle */ + maybe_reload_init_file (si); - if (p->use_mit_saver_extension) - { -#ifdef HAVE_MIT_SAVER_EXTENSION - if (! query_mit_saver_extension (si)) - { - fprintf (stderr, - "%s: display %s does not support the MIT-SCREEN-SAVER extension.\n", - blurb(), DisplayString (si->dpy)); - p->use_mit_saver_extension = False; - } - else if (p->use_xidle_extension) - { - fprintf (stderr, - "%s: MIT-SCREEN-SAVER extension used instead of XIDLE extension.\n", - blurb()); - p->use_xidle_extension = False; - } -#else /* !HAVE_MIT_SAVER_EXTENSION */ - fprintf (stderr, - "%s: not compiled with support for the MIT-SCREEN-SAVER extension.\n", - blurb()); - p->use_mit_saver_extension = False; -#endif /* !HAVE_MIT_SAVER_EXTENSION */ - } +#ifndef NO_LOCKING + if (si->locked_p) + { + saver_screen_info *ssi = si->default_screen; + if (si->locking_disabled_p) abort (); - if (p->use_xidle_extension) - { -#ifdef HAVE_XIDLE_EXTENSION - int first_event, first_error; - if (! XidleQueryExtension (si->dpy, &first_event, &first_error)) - { - fprintf (stderr, - "%s: display %s does not support the XIdle extension.\n", - blurb(), DisplayString (si->dpy)); - p->use_xidle_extension = False; - } -#else /* !HAVE_XIDLE_EXTENSION */ - fprintf (stderr, "%s: not compiled with support for XIdle.\n", - blurb()); - p->use_xidle_extension = False; -#endif /* !HAVE_XIDLE_EXTENSION */ - } + si->dbox_up_p = True; + suspend_screenhack (si, True); + XUndefineCursor (si->dpy, ssi->screensaver_window); - /* Call this only after having probed for presence of desired extension. */ - initialize_screensaver_window (si); + ok_to_unblank = unlock_p (si); - init_sigchld (); + si->dbox_up_p = False; + XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor); + suspend_screenhack (si, False); /* resume */ + } +#endif /* !NO_LOCKING */ - disable_builtin_screensaver (si, True); + } while (!ok_to_unblank); - if (p->verbose_p && p->use_mit_saver_extension) - fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n", - blurb()); - if (p->verbose_p && p->use_sgi_saver_extension) - fprintf (stderr, "%s: using SGI SCREEN_SAVER server extension.\n", - blurb()); - if (p->verbose_p && p->use_xidle_extension) - fprintf (stderr, "%s: using XIdle server extension.\n", - blurb()); - initialize_stderr (si); - XSetErrorHandler (saver_ehandler); + if (p->verbose_p) + fprintf (stderr, "%s: unblanking screen at %s.\n", + blurb(), timestring ()); - if (initial_demo_mode_p) - /* If the user wants demo mode, don't wait around before doing it. */ - p->initial_delay = 0; + /* Kill before unblanking, to stop drawing as soon as possible. */ + kill_screenhack (si); + unblank_screen (si); - if (!p->use_xidle_extension && - !p->use_mit_saver_extension && - !p->use_sgi_saver_extension) - { - if (p->initial_delay) + si->locked_p = False; + si->demoing_p = 0; + si->selection_mode = 0; + + if (si->cycle_id) { - if (p->verbose_p) - { - fprintf (stderr, "%s: waiting for %d second%s...", blurb(), - (int) p->initial_delay, - (p->initial_delay == 1 ? "" : "s")); - fflush (stderr); - fflush (stdout); - } - sleep (p->initial_delay); - if (p->verbose_p) - fprintf (stderr, " done.\n"); + XtRemoveTimeOut (si->cycle_id); + si->cycle_id = 0; } - if (p->verbose_p) + + if (si->lock_id) { - fprintf (stderr, "%s: selecting events on extant windows...", - blurb()); - fflush (stderr); - fflush (stdout); + XtRemoveTimeOut (si->lock_id); + si->lock_id = 0; } - /* Select events on the root windows of every screen. This also selects - 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)); - if (p->verbose_p) - fprintf (stderr, " done.\n"); + fprintf (stderr, "%s: awaiting idleness.\n", blurb()); } } -static void -main_loop (saver_info *si) +static void analyze_display (saver_info *si); + +int +main (int argc, char **argv) { + Widget shell; + saver_info the_si; + saver_info *si = &the_si; saver_preferences *p = &si->prefs; - while (1) - { - if (! si->demo_mode_p) - sleep_until_idle (si, True); + int i; -#ifndef NO_DEMO_MODE - if (si->demo_mode_p) - demo_mode (si); - else -#endif /* !NO_DEMO_MODE */ - { - if (p->verbose_p) - fprintf (stderr, "%s: user is idle; waking up at %s.\n", blurb(), - timestring()); - blank_screen (si); - spawn_screenhack (si, True); - if (p->cycle) - si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer, - (XtPointer) si); + memset(si, 0, sizeof(*si)); + global_si_kludge = si; /* I hate C so much... */ -#ifndef NO_LOCKING - if (p->lock_p && p->lock_timeout == 0) - si->locked_p = True; - if (p->lock_p && !si->locked_p) - /* locked_p might be true already because of ClientMessage */ - si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout, - activate_lock_timer, - (XtPointer) si); -#endif /* !NO_LOCKING */ + srandom ((int) time ((time_t *) 0)); - PASSWD_INVALID: + save_argv (argc, argv); + set_version_string (si, &argc, argv); + privileged_initialization (si, &argc, argv); + hack_environment (si); - sleep_until_idle (si, False); /* until not idle */ + shell = connect_to_server (si, &argc, argv); + process_command_line (si, &argc, argv); + print_banner (si); -#ifndef NO_LOCKING - if (si->locked_p) - { - Bool val; - if (si->locking_disabled_p) abort (); - si->dbox_up_p = True; - - { - saver_screen_info *ssi = si->default_screen; - suspend_screenhack (si, True); - XUndefineCursor (si->dpy, ssi->screensaver_window); - if (p->verbose_p) - fprintf (stderr, "%s: prompting for password.\n", blurb()); - val = unlock_p (si); - if (p->verbose_p && val == False) - fprintf (stderr, "%s: password incorrect!\n", blurb()); - si->dbox_up_p = False; - XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor); - suspend_screenhack (si, False); - } - - if (! val) - goto PASSWD_INVALID; - si->locked_p = False; - } -#endif /* !NO_LOCKING */ + initialize_per_screen_info (si, shell); /* also sets p->fading_possible_p */ - if (p->verbose_p) - fprintf (stderr, "%s: user is active at %s.\n", - blurb(), timestring ()); + for (i = 0; i < si->nscreens; i++) + if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen)) + exit (1); - /* Let's kill it before unblanking, to get it to stop drawing as - soon as possible... */ - kill_screenhack (si); - unblank_screen (si); + load_init_file (p); + lock_initialization (si, &argc, argv); - if (si->cycle_id) - { - XtRemoveTimeOut (si->cycle_id); - si->cycle_id = 0; - } + if (p->xsync_p) XSynchronize (si->dpy, True); + blurb_timestamp_p = p->timestamp_p; /* kludge */ -#ifndef NO_LOCKING - if (si->lock_id) - { - XtRemoveTimeOut (si->lock_id); - si->lock_id = 0; - } -#endif /* !NO_LOCKING */ + if (p->verbose_p) analyze_display (si); + initialize_server_extensions (si); + initialize_screensaver_window (si); + select_events (si); + init_sigchld (); + disable_builtin_screensaver (si, True); + initialize_stderr (si); - if (p->verbose_p) - fprintf (stderr, "%s: going to sleep.\n", blurb()); - } - } + make_splash_dialog (si); + + main_loop (si); /* doesn't return */ + return 0; } +/* Processing ClientMessage events. + */ + +static void +clientmessage_response (saver_info *si, Window w, Bool error, + const char *stderr_msg, + const char *protocol_msg) +{ + char *proto; + int L; + saver_preferences *p = &si->prefs; + if (error || p->verbose_p) + fprintf (stderr, "%s: %s\n", blurb(), stderr_msg); + + L = strlen(protocol_msg); + proto = (char *) malloc (L + 2); + proto[0] = (error ? '-' : '+'); + strcpy (proto+1, protocol_msg); + L++; + + XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8, + PropModeReplace, (unsigned char *) proto, L); + XSync (si->dpy, False); + free (proto); +} 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) { char *str; @@ -1069,10 +1108,12 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) { if (until_idle_p) { - if (p->verbose_p) - fprintf (stderr, - "%s: ACTIVATE ClientMessage received.\n", blurb()); - if (p->use_mit_saver_extension || p->use_sgi_saver_extension) + clientmessage_response(si, window, False, + "ACTIVATE ClientMessage received.", + "activating."); + si->selection_mode = 0; + si->demoing_p = False; + if (si->using_mit_saver_extension || si->using_sgi_saver_extension) { XForceScreenSaver (si->dpy, ScreenSaverActive); return False; @@ -1082,18 +1123,18 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) return True; } } - fprintf (stderr, - "%s: ClientMessage ACTIVATE received while already active.\n", - blurb()); + clientmessage_response(si, window, True, + "ClientMessage ACTIVATE received while already active.", + "already active."); } else if (type == XA_DEACTIVATE) { if (! until_idle_p) { - if (p->verbose_p) - fprintf (stderr, "%s: DEACTIVATE ClientMessage received.\n", - blurb()); - if (p->use_mit_saver_extension || p->use_sgi_saver_extension) + clientmessage_response(si, window, False, + "DEACTIVATE ClientMessage received.", + "deactivating."); + if (si->using_mit_saver_extension || si->using_sgi_saver_extension) { XForceScreenSaver (si->dpy, ScreenSaverReset); return False; @@ -1103,31 +1144,62 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) return True; } } - fprintf (stderr, - "%s: ClientMessage DEACTIVATE received while inactive.\n", - blurb()); + clientmessage_response(si, window, True, + "ClientMessage DEACTIVATE received while inactive.", + "not active."); } else if (type == XA_CYCLE) { if (! until_idle_p) { - if (p->verbose_p) - fprintf (stderr, "%s: CYCLE ClientMessage received.\n", blurb()); + clientmessage_response(si, window, False, + "CYCLE ClientMessage received.", + "cycling."); + si->selection_mode = 0; /* 0 means randomize when its time. */ + si->demoing_p = False; if (si->cycle_id) XtRemoveTimeOut (si->cycle_id); si->cycle_id = 0; cycle_timer ((XtPointer) si, 0); return False; } - fprintf (stderr, "%s: ClientMessage CYCLE received while inactive.\n", - blurb()); + clientmessage_response(si, window, True, + "ClientMessage CYCLE received while inactive.", + "not active."); } else if (type == XA_NEXT || type == XA_PREV) { - if (p->verbose_p) - fprintf (stderr, "%s: %s ClientMessage received.\n", blurb(), - (type == XA_NEXT ? "NEXT" : "PREV")); - si->next_mode_p = 1 + (type == XA_PREV); + clientmessage_response(si, window, False, + (type == XA_NEXT + ? "NEXT ClientMessage received." + : "PREV ClientMessage received."), + "cycling."); + si->selection_mode = (type == XA_NEXT ? -1 : -2); + si->demoing_p = False; + + if (! until_idle_p) + { + if (si->cycle_id) + XtRemoveTimeOut (si->cycle_id); + si->cycle_id = 0; + cycle_timer ((XtPointer) si, 0); + } + else + return True; + } + else if (type == XA_SELECT) + { + char buf [255]; + char buf2 [255]; + long which = event->xclient.data.l[1]; + + sprintf (buf, "SELECT %ld ClientMessage received.", which); + sprintf (buf2, "activating (%ld).", which); + clientmessage_response (si, window, False, buf, buf2); + + if (which < 0) which = 0; /* 0 == "random" */ + si->selection_mode = which; + si->demoing_p = False; if (! until_idle_p) { @@ -1144,8 +1216,9 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) /* Ignore EXIT message if the screen is locked. */ if (until_idle_p || !si->locked_p) { - if (p->verbose_p) - fprintf (stderr, "%s: EXIT ClientMessage received.\n", blurb()); + clientmessage_response (si, window, False, + "EXIT ClientMessage received.", + "exiting."); if (! until_idle_p) { unblank_screen (si); @@ -1155,8 +1228,9 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) saver_exit (si, 0, 0); } else - fprintf (stderr, "%s: EXIT ClientMessage received while locked.\n", - blurb()); + clientmessage_response (si, window, True, + "EXIT ClientMessage received while locked.", + "screen is locked."); } else if (type == XA_RESTART) { @@ -1165,8 +1239,9 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) */ if (until_idle_p || !si->locked_p) { - if (p->verbose_p) - fprintf (stderr, "%s: RESTART ClientMessage received.\n", blurb()); + clientmessage_response (si, window, False, + "RESTART ClientMessage received.", + "restarting."); if (! until_idle_p) { unblank_screen (si); @@ -1183,63 +1258,76 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) make this command be the same as EXIT. */ } else - fprintf(stderr, "%s: RESTART ClientMessage received while locked.\n", - blurb()); + clientmessage_response (si, window, True, + "RESTART ClientMessage received while locked.", + "screen is locked."); } else if (type == XA_DEMO) { -#ifdef NO_DEMO_MODE - fprintf (stderr, "%s: not compiled with support for DEMO mode\n", - blurb()); -#else - if (until_idle_p) + long arg = event->xclient.data.l[1]; + Bool demo_one_hack_p = (arg == 300); + + if (demo_one_hack_p) + { + if (until_idle_p) + { + long which = event->xclient.data.l[2]; + char buf [255]; + char buf2 [255]; + sprintf (buf, "DEMO %ld ClientMessage received.", which); + sprintf (buf2, "demoing (%ld).", which); + clientmessage_response (si, window, False, buf, buf2); + + if (which < 0) which = 0; /* 0 == "random" */ + si->selection_mode = which; + si->demoing_p = True; + + return True; + } + + clientmessage_response (si, window, True, + "DEMO ClientMessage received while active.", + "already active."); + } + else { - if (p->verbose_p) - fprintf (stderr, "%s: DEMO ClientMessage received.\n", blurb()); - si->demo_mode_p = True; - return True; + clientmessage_response (si, window, True, + "obsolete form of DEMO ClientMessage.", + "obsolete form of DEMO ClientMessage."); } - fprintf (stderr, - "%s: DEMO ClientMessage received while active.\n", blurb()); -#endif } else if (type == XA_PREFS) { -#ifdef NO_DEMO_MODE - fprintf (stderr, "%s: not compiled with support for DEMO mode\n", - blurb()); -#else - if (until_idle_p) - { - if (p->verbose_p) - fprintf (stderr, "%s: PREFS ClientMessage received.\n", blurb()); - si->demo_mode_p = (Bool) 2; /* kludge, so sue me. */ - return True; - } - fprintf (stderr, - "%s: PREFS ClientMessage received while active.\n", blurb()); -#endif + clientmessage_response (si, window, True, + "the PREFS client-message is obsolete.", + "the PREFS client-message is obsolete."); } else if (type == XA_LOCK) { #ifdef NO_LOCKING - fprintf (stderr, "%s: not compiled with support for LOCK mode\n", - blurb()); -#else + clientmessage_response (si, window, True, + "not compiled with support for locking.", + "locking not enabled."); +#else /* !NO_LOCKING */ if (si->locking_disabled_p) - fprintf (stderr, - "%s: LOCK ClientMessage received, but locking is disabled.\n", - blurb()); + clientmessage_response (si, window, True, + "LOCK ClientMessage received, but locking is disabled.", + "locking not enabled."); else if (si->locked_p) - fprintf (stderr, - "%s: LOCK ClientMessage received while already locked.\n", - blurb()); + clientmessage_response (si, window, True, + "LOCK ClientMessage received while already locked.", + "already locked."); else { + char buf [255]; + char *response = (until_idle_p + ? "activating and locking." + : "locking."); si->locked_p = True; - if (p->verbose_p) - fprintf (stderr, "%s: LOCK ClientMessage received;%s locking.\n", - blurb(), until_idle_p ? " activating and" : ""); + si->selection_mode = 0; + si->demoing_p = False; + sprintf (buf, "LOCK ClientMessage received; %s", response); + clientmessage_response (si, window, False, buf, response); if (si->lock_id) /* we're doing it now, so lose the timeout */ { @@ -1249,7 +1337,8 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) if (until_idle_p) { - if (p->use_mit_saver_extension || p->use_sgi_saver_extension) + if (si->using_mit_saver_extension || + si->using_sgi_saver_extension) { XForceScreenSaver (si->dpy, ScreenSaverActive); return False; @@ -1260,21 +1349,146 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) } } } -#endif +#endif /* !NO_LOCKING */ } else { + char buf [1024]; char *str; str = (type ? XGetAtomName(si->dpy, type) : 0); + if (str) - fprintf (stderr, - "%s: unrecognised screensaver ClientMessage %s received\n", - blurb(), str); + { + if (strlen (str) > 80) + strcpy (str+70, "..."); + sprintf (buf, "unrecognised screensaver ClientMessage %s received.", + str); + free (str); + } else - fprintf (stderr, - "%s: unrecognised screensaver ClientMessage 0x%x received\n", - blurb(), (unsigned int) event->xclient.data.l[0]); - if (str) XFree (str); + { + sprintf (buf, + "unrecognised screensaver ClientMessage 0x%x received.", + (unsigned int) event->xclient.data.l[0]); + } + + clientmessage_response (si, window, True, buf, buf); } return False; } + + +/* Some random diagnostics printed in -verbose mode. + */ + +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" } + }; + + fprintf (stderr, "%s: running on display \"%s\"\n", blurb(), + DisplayString(si->dpy)); + 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]); + } + + for (i = 0; i < si->nscreens; i++) + { + unsigned long colormapped_depths = 0; + unsigned long non_mapped_depths = 0; + XVisualInfo vi_in, *vi_out; + int out_count; + vi_in.screen = i; + vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count); + if (!vi_out) continue; + for (j = 0; j < out_count; j++) + if (vi_out[j].class == PseudoColor) + colormapped_depths |= (1 << vi_out[j].depth); + else + non_mapped_depths |= (1 << vi_out[j].depth); + XFree ((char *) vi_out); + + if (colormapped_depths) + { + fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i); + for (j = 0; j < 32; j++) + if (colormapped_depths & (1 << j)) + fprintf (stderr, " %d", j); + fprintf (stderr, "\n"); + } + if (non_mapped_depths) + { + fprintf (stderr, "%s: screen %d non-mapped depths:", blurb(), i); + for (j = 0; j < 32; j++) + if (non_mapped_depths & (1 << j)) + fprintf (stderr, " %d", j); + fprintf (stderr, "\n"); + } + } +} + +Bool +display_is_on_console_p (saver_info *si) +{ + Bool not_on_console = True; + char *dpystr = DisplayString (si->dpy); + char *tail = (char *) strchr (dpystr, ':'); + if (! tail || strncmp (tail, ":0", 2)) + not_on_console = True; + else + { + char dpyname[255], localname[255]; + strncpy (dpyname, dpystr, tail-dpystr); + dpyname [tail-dpystr] = 0; + if (!*dpyname || + !strcmp(dpyname, "unix") || + !strcmp(dpyname, "localhost")) + not_on_console = False; + else if (gethostname (localname, sizeof (localname))) + not_on_console = True; /* can't find hostname? */ + else + { + /* We have to call gethostbyname() on the result of gethostname() + because the two aren't guarenteed to be the same name for the + same host: on some losing systems, one is a FQDN and the other + is not. Here in the wide wonderful world of Unix it's rocket + science to obtain the local hostname in a portable fashion. + + And don't forget, gethostbyname() reuses the structure it + returns, so we have to copy the fucker before calling it again. + Thank you master, may I have another. + */ + struct hostent *h = gethostbyname (dpyname); + if (!h) + not_on_console = True; + else + { + char hn [255]; + struct hostent *l; + strcpy (hn, h->h_name); + l = gethostbyname (localname); + not_on_console = (!l || !!(strcmp (l->h_name, hn))); + } + } + } + return !not_on_console; +}