45f0f0c48330afda7192a3b02fc6aee26100f965
[xscreensaver] / driver / xscreensaver.c
1 /* xscreensaver, Copyright (c) 1991-2015 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /*   ========================================================================
13  *   First we wait until the keyboard and mouse become idle for the specified
14  *   amount of time.  We do this in one of three different ways: periodically
15  *   checking with the XIdle server extension; selecting key and mouse events
16  *   on (nearly) all windows; or by waiting for the MIT-SCREEN-SAVER extension
17  *   to send us a "you are idle" event.
18  *
19  *   Then, we map a full screen black window.
20  *
21  *   We place a __SWM_VROOT property on this window, so that newly-started
22  *   clients will think that this window is a "virtual root" window (as per
23  *   the logic in the historical "vroot.h" header.)
24  *
25  *   If there is an existing "virtual root" window (one that already had
26  *   an __SWM_VROOT property) then we remove that property from that window.
27  *   Otherwise, clients would see that window (the real virtual root) instead
28  *   of ours (the impostor.)
29  *
30  *   Then we pick a random program to run, and start it.  Two assumptions 
31  *   are made about this program: that it has been specified with whatever
32  *   command-line options are necessary to make it run on the root window;
33  *   and that it has been compiled with vroot.h, so that it is able to find
34  *   the root window when a virtual-root window manager (or this program) is
35  *   running.
36  *
37  *   Then, we wait for keyboard or mouse events to be generated on the window.
38  *   When they are, we kill the inferior process, unmap the window, and restore
39  *   the __SWM_VROOT property to the real virtual root window if there was one.
40  *
41  *   On multi-screen systems, we do the above on each screen, and start
42  *   multiple programs, each with a different value of $DISPLAY.
43  *
44  *   On Xinerama systems, we do a similar thing, but instead create multiple
45  *   windows on the (only) display, and tell the subprocess which one to use
46  *   via the $XSCREENSAVER_WINDOW environment variable -- this trick requires
47  *   a recent (Aug 2003) revision of vroot.h.
48  *
49  *   (See comments in screens.c for more details about Xinerama/RANDR stuff.)
50  *
51  *   While we are waiting for user activity, we also set up timers so that,
52  *   after a certain amount of time has passed, we can start a different
53  *   screenhack.  We do this by killing the running child process with
54  *   SIGTERM, and then starting a new one in the same way.
55  *
56  *   If there was a real virtual root, meaning that we removed the __SWM_VROOT
57  *   property from it, meaning we must (absolutely must) restore it before we
58  *   exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
59  *   etc.) that do this.  Most Xlib and Xt routines are not reentrant, so it
60  *   is not generally safe to call them from signal handlers; however, this
61  *   program spends most of its time waiting, so the window of opportunity 
62  *   when code could be called reentrantly is fairly small; and also, the worst
63  *   that could happen is that the call would fail.  If we've gotten one of
64  *   these signals, then we're on our way out anyway.  If we didn't restore the
65  *   __SWM_VROOT property, that would be very bad, so it's worth a shot.  Note
66  *   that this means that, if you're using a virtual-root window manager, you
67  *   can really fuck up the world by killing this process with "kill -9".
68  *
69  *   This program accepts ClientMessages of type SCREENSAVER; these messages
70  *   may contain the atoms ACTIVATE, DEACTIVATE, etc, meaning to turn the 
71  *   screensaver on or off now, regardless of the idleness of the user,
72  *   and a few other things.  The included "xscreensaver-command" program
73  *   sends these messsages.
74  *
75  *   If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
76  *   extensions, then we do the XAutoLock trick: notice every window that
77  *   gets created, and wait 30 seconds or so until its creating process has
78  *   settled down, and then select KeyPress events on those windows which
79  *   already select for KeyPress events.  It's important that we not select
80  *   KeyPress on windows which don't select them, because that would
81  *   interfere with event propagation.  This will break if any program
82  *   changes its event mask to contain KeyRelease or PointerMotion more than
83  *   30 seconds after creating the window, but such programs do not seem to
84  *   occur in nature (I've never seen it happen in all these years.)
85  *
86  *   The reason that we can't select KeyPresses on windows that don't have
87  *   them already is that, when dispatching a KeyPress event, X finds the
88  *   lowest (leafmost) window in the hierarchy on which *any* client selects
89  *   for KeyPress, and sends the event to that window.  This means that if a
90  *   client had a window with subwindows, and expected to receive KeyPress
91  *   events on the parent window instead of the subwindows, then that client
92  *   would malfunction if some other client selected KeyPress events on the
93  *   subwindows.  It is an incredible misdesign that one client can make
94  *   another client malfunction in this way.
95  *
96  *   But here's a new kink that started showing up in late 2014: GNOME programs
97  *   don't actually select for or receive KeyPress events! They do it behind
98  *   the scenes through some kind of Input Method magic, even when running in
99  *   an en_US locale.  However, in that case, those applications *do* seem to
100  *   update the _NET_WM_USER_TIME on their own windows every time they have
101  *   received a secret KeyPress, so we *also* monitor that property on every
102  *   window, and treat changes to it as identical to KeyPress.
103  *
104  *   To detect mouse motion, we periodically wake up and poll the mouse
105  *   position and button/modifier state, and notice when something has
106  *   changed.  We make this check every five seconds by default, and since the
107  *   screensaver timeout has a granularity of one minute, this makes the
108  *   chance of a false positive very small.  We could detect mouse motion in
109  *   the same way as keyboard activity, but that would suffer from the same
110  *   "client changing event mask" problem that the KeyPress events hack does.
111  *   I think polling is more reliable.
112  *
113  *   On systems with /proc/interrupts (Linux) we poll that file and note when
114  *   the interrupt counter numbers on the "keyboard" and "PS/2" lines change.
115  *   (There is no reliable way, using /proc/interrupts, to detect non-PS/2
116  *   mice, so it doesn't help for serial or USB mice.)
117  *
118  *   None of this crap happens if we're using one of the extensions.  Sadly,
119  *   the XIdle extension hasn't been available for many years; the SGI
120  *   extension only exists on SGIs; and the MIT extension, while widely
121  *   deployed, is garbage in several ways.
122  *
123  *   A third idle-detection option could be implemented (but is not): when
124  *   running on the console display ($DISPLAY is `localhost`:0) and we're on a
125  *   machine where /dev/tty and /dev/mouse have reasonable last-modification
126  *   times, we could just stat() those.  But the incremental benefit of
127  *   implementing this is really small, so forget I said anything.
128  *
129  *   Debugging hints:
130  *     - Have a second terminal handy.
131  *     - Be careful where you set your breakpoints, you don't want this to
132  *       stop under the debugger with the keyboard grabbed or the blackout
133  *       window exposed.
134  *     - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
135  *       to keep your emacs window alive even when xscreensaver has grabbed.
136  *     - Go read the code related to `debug_p'.
137  *     - You probably can't set breakpoints in functions that are called on
138  *       the other side of a call to fork() -- if your subprocesses are
139  *       dying with signal 5, Trace/BPT Trap, you're losing in this way.
140  *     - If you aren't using a server extension, don't leave this stopped
141  *       under the debugger for very long, or the X input buffer will get
142  *       huge because of the keypress events it's selecting for.  This can
143  *       make your X server wedge with "no more input buffers."
144  *
145  * ======================================================================== */
146
147 #ifdef HAVE_CONFIG_H
148 # include "config.h"
149 #endif
150
151 #include <stdio.h>
152 #include <ctype.h>
153 #include <X11/Xlib.h>
154
155 #ifdef ENABLE_NLS
156 # include <locale.h>
157 # include <libintl.h>
158 #endif /* ENABLE_NLS */
159
160 #include <X11/Xlibint.h>
161
162 #include <X11/Xatom.h>
163 #include <X11/Intrinsic.h>
164 #include <X11/StringDefs.h>
165 #include <X11/Shell.h>
166 #include <X11/Xos.h>
167 #include <time.h>
168 #include <sys/time.h>
169 #include <netdb.h>      /* for gethostbyname() */
170 #include <sys/types.h>
171 #include <pwd.h>
172 #ifdef HAVE_XMU
173 # ifndef VMS
174 #  include <X11/Xmu/Error.h>
175 # else  /* !VMS */
176 #  include <Xmu/Error.h>
177 # endif /* !VMS */
178 #else  /* !HAVE_XMU */
179 # include "xmu.h"
180 #endif /* !HAVE_XMU */
181
182 #ifdef HAVE_MIT_SAVER_EXTENSION
183 #include <X11/extensions/scrnsaver.h>
184 #endif /* HAVE_MIT_SAVER_EXTENSION */
185
186 #ifdef HAVE_XIDLE_EXTENSION
187 # include <X11/extensions/xidle.h>
188 #endif /* HAVE_XIDLE_EXTENSION */
189
190 #ifdef HAVE_SGI_VC_EXTENSION
191 # include <X11/extensions/XSGIvc.h>
192 #endif /* HAVE_SGI_VC_EXTENSION */
193
194 #ifdef HAVE_READ_DISPLAY_EXTENSION
195 # include <X11/extensions/readdisplay.h>
196 #endif /* HAVE_READ_DISPLAY_EXTENSION */
197
198 #ifdef HAVE_XSHM_EXTENSION
199 # include <X11/extensions/XShm.h>
200 #endif /* HAVE_XSHM_EXTENSION */
201
202 #ifdef HAVE_DPMS_EXTENSION
203 # include <X11/extensions/dpms.h>
204 #endif /* HAVE_DPMS_EXTENSION */
205
206
207 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
208 # include <X11/extensions/Xdbe.h>
209 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
210
211 #ifdef HAVE_XF86VMODE
212 # include <X11/extensions/xf86vmode.h>
213 #endif /* HAVE_XF86VMODE */
214
215 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
216 # include <X11/extensions/xf86misc.h>
217 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
218
219 #ifdef HAVE_XINERAMA
220 # include <X11/extensions/Xinerama.h>
221 #endif /* HAVE_XINERAMA */
222
223 #ifdef HAVE_RANDR
224 # include <X11/extensions/Xrandr.h>
225 #endif /* HAVE_RANDR */
226
227
228 #include "xscreensaver.h"
229 #include "version.h"
230 #include "yarandom.h"
231 #include "resources.h"
232 #include "visual.h"
233 #include "usleep.h"
234 #include "auth.h"
235
236 saver_info *global_si_kludge = 0;       /* I hate C so much... */
237
238 char *progname = 0;
239 char *progclass = 0;
240 XrmDatabase db = 0;
241
242
243 static Atom XA_SCREENSAVER_RESPONSE;
244 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
245 static Atom XA_RESTART, XA_SELECT;
246 static Atom XA_THROTTLE, XA_UNTHROTTLE;
247 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
248
249 \f
250 static XrmOptionDescRec options [] = {
251
252   { "-verbose",            ".verbose",          XrmoptionNoArg, "on" },
253   { "-silent",             ".verbose",          XrmoptionNoArg, "off" },
254
255   /* xscreensaver-demo uses this one */
256   { "-nosplash",           ".splash",           XrmoptionNoArg, "off" },
257   { "-no-splash",          ".splash",           XrmoptionNoArg, "off" },
258
259   /* useful for debugging */
260   { "-no-capture-stderr",  ".captureStderr",    XrmoptionNoArg, "off" },
261   { "-log",                ".logFile",          XrmoptionSepArg, 0 },
262 };
263
264 #ifdef __GNUC__
265  __extension__     /* shut up about "string length is greater than the length
266                       ISO C89 compilers are required to support" when including
267                       the .ad file... */
268 #endif
269
270 static char *defaults[] = {
271 #include "XScreenSaver_ad.h"
272  0
273 };
274
275 #ifdef _VROOT_H_
276 ERROR!  You must not include vroot.h in this file.
277 #endif
278
279 static void
280 do_help (saver_info *si)
281 {
282   char *s, year[5];
283   s = strchr (screensaver_id, '-');
284   s = strrchr (s, '-');
285   s++;
286   strncpy (year, s, 4);
287   year[4] = 0;
288
289   fflush (stdout);
290   fflush (stderr);
291   fprintf (stdout, "\
292 xscreensaver %s, copyright (c) 1991-%s by Jamie Zawinski <jwz@jwz.org>\n\
293 \n\
294   All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
295   Rather than editing that file by hand, just run `xscreensaver-demo':\n\
296   that program lets you configure the screen saver graphically,\n\
297   including timeouts, locking, and display modes.\n\
298 \n",
299           si->version, year);
300   fprintf (stdout, "\
301   Just getting started?  Try this:\n\
302 \n\
303         xscreensaver &\n\
304         xscreensaver-demo\n\
305 \n\
306   For updates, online manual, and FAQ, please see the web page:\n\
307 \n\
308        http://www.jwz.org/xscreensaver/\n\
309 \n");
310
311   fflush (stdout);
312   fflush (stderr);
313   exit (1);
314 }
315
316
317 Bool in_signal_handler_p = 0;   /* I hate C so much... */
318
319 char *
320 timestring (void)
321 {
322   if (in_signal_handler_p)
323     {
324       /* Turns out that ctime() and even localtime_r() call malloc() on Linux!
325          So we can't call them from inside SIGCHLD.  WTF.
326        */
327       static char buf[30];
328       strcpy (buf, "... ... ..   signal ....");
329       return buf;
330     }
331   else
332     {
333       time_t now = time ((time_t *) 0);
334       char *str = (char *) ctime (&now);
335       char *nl = (char *) strchr (str, '\n');
336       if (nl) *nl = 0; /* take off that dang newline */
337       return str;
338     }
339 }
340
341 static Bool blurb_timestamp_p = True;   /* kludge */
342
343 const char *
344 blurb (void)
345 {
346   if (!blurb_timestamp_p)
347     return progname;
348   else
349     {
350       static char buf[255];
351       char *ct = timestring();
352       int n = strlen(progname);
353       if (n > 100) n = 99;
354       strncpy(buf, progname, n);
355       buf[n++] = ':';
356       buf[n++] = ' ';
357       strncpy(buf+n, ct+11, 8);
358       strcpy(buf+n+9, ": ");
359       return buf;
360     }
361 }
362
363
364 int
365 saver_ehandler (Display *dpy, XErrorEvent *error)
366 {
367   saver_info *si = global_si_kludge;    /* I hate C so much... */
368   int i;
369   Bool fatal_p;
370
371   if (!real_stderr) real_stderr = stderr;
372
373   fprintf (real_stderr, "\n"
374            "#######################################"
375            "#######################################\n\n"
376            "%s: X Error!  PLEASE REPORT THIS BUG.\n",
377            blurb());
378
379   for (i = 0; i < si->nscreens; i++)
380     {
381       saver_screen_info *ssi = &si->screens[i];
382       fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
383                blurb(), ssi->real_screen_number, ssi->number,
384                (unsigned int) RootWindowOfScreen (si->screens[i].screen),
385                (unsigned int) si->screens[i].real_vroot,
386                (unsigned int) si->screens[i].screensaver_window);
387     }
388
389   fprintf (real_stderr, "\n"
390            "#######################################"
391            "#######################################\n\n");
392
393   fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
394
395   fatal_p = True;  /* The only time I've ever seen a supposedly nonfatal error,
396                       it has been BadImplementation / Xlib sequence lost, which
397                       are in truth pretty damned fatal.
398                     */
399
400   fprintf (real_stderr, "\n");
401
402   if (! fatal_p)
403     fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
404   else
405     {
406       if (si->prefs.xsync_p)
407         {
408           saver_exit (si, -1, "because of synchronous X Error");
409         }
410       else
411         {
412 #ifdef __GNUC__
413   __extension__   /* don't warn about "string length is greater than the
414                      length ISO C89 compilers are required to support". */
415 #endif
416           fprintf (real_stderr,
417    "#######################################################################\n"
418    "\n"
419    "    If at all possible, please re-run xscreensaver with the command\n"
420    "    line arguments `-sync -verbose -log log.txt', and reproduce this\n"
421    "    bug.  That will cause xscreensaver to dump a `core' file to the\n"
422    "    current directory.  Please include the stack trace from that core\n"
423    "    file in your bug report.  *DO NOT* mail the core file itself!  That\n"
424    "    won't work.  A \"log.txt\" file will also be written.  Please *do*\n"
425    "    include the complete \"log.txt\" file with your bug report.\n"
426    "\n"
427    "    http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
428    "    the most useful bug reports, and how to examine core files.\n"
429    "\n"
430    "    The more information you can provide, the better.  But please\n"
431    "    report this bug, regardless!\n"
432    "\n"
433    "#######################################################################\n"
434    "\n"
435    "\n");
436
437           saver_exit (si, -1, 0);
438         }
439     }
440
441   return 0;
442 }
443
444
445 /* This error handler is used only while the X connection is being set up;
446    after we've got a connection, we don't use this handler again.  The only
447    reason for having this is so that we can present a more idiot-proof error
448    message than "cannot open display."
449  */
450 static void 
451 startup_ehandler (String name, String type, String class,
452                   String defalt,  /* one can't even spel properly
453                                      in this joke of a language */
454                   String *av, Cardinal *ac)
455 {
456   char fmt[512];
457   String p[10];
458   saver_info *si = global_si_kludge;    /* I hate C so much... */
459   XrmDatabase *db = XtAppGetErrorDatabase(si->app);
460   *fmt = 0;
461   XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
462                             fmt, sizeof(fmt)-1, *db);
463
464   fprintf (stderr, "%s: ", blurb());
465
466   memset (p, 0, sizeof(p));
467   if (*ac > countof (p)) *ac = countof (p);
468   memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
469   fprintf (stderr, fmt,         /* Did I mention that I hate C? */
470            p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
471   fprintf (stderr, "\n");
472
473   describe_uids (si, stderr);
474
475   if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
476     {
477       fprintf (stderr, "\n"
478           "%s: This is probably because you're logging in as root.  You\n"
479 "              shouldn't log in as root: you should log in as a normal user,\n"
480 "              and then `su' as needed.  If you insist on logging in as\n"
481 "              root, you will have to turn off X's security features before\n"
482 "              xscreensaver will work.\n"
483                "\n"
484 "              Please read the manual and FAQ for more information:\n",
485                blurb());
486     }
487   else
488     {
489       fprintf (stderr, "\n"
490           "%s: Errors at startup are usually authorization problems.\n"
491 "              But you're not logging in as root (good!) so something\n"
492 "              else must be wrong.  Did you read the manual and the FAQ?\n",
493            blurb());
494     }
495
496   fprintf (stderr, "\n"
497           "              http://www.jwz.org/xscreensaver/faq.html\n"
498           "              http://www.jwz.org/xscreensaver/man.html\n"
499           "\n");
500
501   fflush (stderr);
502   fflush (stdout);
503   exit (1);
504 }
505
506 \f
507 /* The zillions of initializations.
508  */
509
510 /* Set progname, version, etc.  This is done very early.
511  */
512 static void
513 set_version_string (saver_info *si, int *argc, char **argv)
514 {
515   progclass = "XScreenSaver";
516
517   /* progname is reset later, after we connect to X. */
518   progname = strrchr(argv[0], '/');
519   if (progname) progname++;
520   else progname = argv[0];
521
522   if (strlen(progname) > 100)   /* keep it short. */
523     progname[99] = 0;
524
525   /* The X resource database blows up if argv[0] has a "." in it. */
526   {
527     char *s = argv[0];
528     while ((s = strchr (s, '.')))
529       *s = '_';
530   }
531
532   si->version = (char *) malloc (5);
533   memcpy (si->version, screensaver_id + 17, 4);
534   si->version [4] = 0;
535 }
536
537
538 /* Initializations that potentially take place as a priveleged user:
539    If the xscreensaver executable is setuid root, then these initializations
540    are run as root, before discarding privileges.
541  */
542 static void
543 privileged_initialization (saver_info *si, int *argc, char **argv)
544 {
545 #ifndef NO_LOCKING
546   /* before hack_uid() for proper permissions */
547   lock_priv_init (*argc, argv, si->prefs.verbose_p);
548 #endif /* NO_LOCKING */
549
550   hack_uid (si);
551 }
552
553
554 /* Figure out what locking mechanisms are supported.
555  */
556 static void
557 lock_initialization (saver_info *si, int *argc, char **argv)
558 {
559 #ifdef NO_LOCKING
560   si->locking_disabled_p = True;
561   si->nolock_reason = "not compiled with locking support";
562 #else /* !NO_LOCKING */
563
564   /* Finish initializing locking, now that we're out of privileged code. */
565   if (! lock_init (*argc, argv, si->prefs.verbose_p))
566     {
567       si->locking_disabled_p = True;
568       si->nolock_reason = "error getting password";
569     }
570
571   /* If locking is currently enabled, but the environment indicates that
572      we have been launched as GDM's "Background" program, then disable
573      locking just in case.
574    */
575   if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
576     {
577       si->locking_disabled_p = True;
578       si->nolock_reason = "running under GDM";
579     }
580
581   /* If the server is XDarwin (MacOS X) then disable locking.
582      (X grabs only affect X programs, so you can use Command-Tab
583      to bring any other Mac program to the front, e.g., Terminal.)
584    */
585   if (!si->locking_disabled_p)
586     {
587       int op = 0, event = 0, error = 0;
588       Bool macos_p = False;
589
590 #ifdef __APPLE__
591       /* Disable locking if *running* on Apple hardware, since we have no
592          reliable way to determine whether the server is running on MacOS.
593          Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
594          but I'm not really sure about that.
595        */
596       macos_p = True;
597 #endif
598
599       if (!macos_p)
600         /* This extension exists on the Apple X11 server, but not
601            on earlier versions of the XDarwin server. */
602         macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
603
604       if (macos_p)
605         {
606           si->locking_disabled_p = True;
607           si->nolock_reason = "Cannot lock securely on MacOS X";
608         }
609     }
610
611   /* Like MacOS, locking under Wayland's embedded X11 server does not work.
612      (X11 grabs don't work because the Wayland window manager lives at a
613      higher level than the X11 emulation layer.)
614    */
615   if (!si->locking_disabled_p && getenv ("WAYLAND_DISPLAY"))
616     {
617       si->locking_disabled_p = True;
618       si->nolock_reason = "Cannot lock securely under Wayland";
619     }
620
621   if (si->prefs.debug_p)    /* But allow locking anyway in debug mode. */
622     si->locking_disabled_p = False;
623
624 #endif /* NO_LOCKING */
625 }
626
627
628 /* Open the connection to the X server, and intern our Atoms.
629  */
630 static Widget
631 connect_to_server (saver_info *si, int *argc, char **argv)
632 {
633   Widget toplevel_shell;
634
635 #ifdef HAVE_PUTENV
636   char *d = getenv ("DISPLAY");
637   if (!d || !*d)
638     {
639       char *ndpy = strdup("DISPLAY=:0.0");
640       /* if (si->prefs.verbose_p) */      /* sigh, too early to test this... */
641         fprintf (stderr,
642                  "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
643                  blurb(), ndpy+8);
644       if (putenv (ndpy))
645         abort ();
646       /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
647          glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
648          do not.  So we must leak it (and/or the previous setting). Yay.
649        */
650     }
651 #endif /* HAVE_PUTENV */
652
653   XSetErrorHandler (saver_ehandler);
654
655   XtAppSetErrorMsgHandler (si->app, startup_ehandler);
656   toplevel_shell = XtAppInitialize (&si->app, progclass,
657                                     options, XtNumber (options),
658                                     argc, argv, defaults, 0, 0);
659   XtAppSetErrorMsgHandler (si->app, 0);
660
661   si->dpy = XtDisplay (toplevel_shell);
662   si->prefs.db = XtDatabase (si->dpy);
663   XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
664
665   if(strlen(progname) > 100)    /* keep it short. */
666     progname [99] = 0;
667
668   db = si->prefs.db;    /* resources.c needs this */
669
670   XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
671   XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
672   XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
673   XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
674   XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
675   XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
676                                          False);
677   XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
678   XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
679   XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
680   XA_NET_WM_USER_TIME = XInternAtom (si->dpy, "_NET_WM_USER_TIME", False);
681   XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
682   XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
683   XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
684   XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
685   XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
686   XA_PREV = XInternAtom (si->dpy, "PREV", False);
687   XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
688   XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
689   XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
690   XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
691   XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
692   XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
693   XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
694   XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
695
696   return toplevel_shell;
697 }
698
699
700 /* Handle the command-line arguments that were not handled for us by Xt.
701    Issue an error message and exit if there are unknown options.
702  */
703 static void
704 process_command_line (saver_info *si, int *argc, char **argv)
705 {
706   int i;
707   for (i = 1; i < *argc; i++)
708     {
709       if (!strcmp (argv[i], "-debug"))
710         /* no resource for this one, out of paranoia. */
711         si->prefs.debug_p = True;
712
713       else if (!strcmp (argv[i], "-h") ||
714                !strcmp (argv[i], "-help") ||
715                !strcmp (argv[i], "--help"))
716         do_help (si);
717
718       else
719         {
720           const char *s = argv[i];
721           fprintf (stderr, "%s: unknown option \"%s\".  Try \"-help\".\n",
722                    blurb(), s);
723
724           if (s[0] == '-' && s[1] == '-') s++;
725           if (!strcmp (s, "-activate") ||
726               !strcmp (s, "-deactivate") ||
727               !strcmp (s, "-cycle") ||
728               !strcmp (s, "-next") ||
729               !strcmp (s, "-prev") ||
730               !strcmp (s, "-exit") ||
731               !strcmp (s, "-restart") ||
732               !strcmp (s, "-demo") ||
733               !strcmp (s, "-prefs") ||
734               !strcmp (s, "-preferences") ||
735               !strcmp (s, "-lock") ||
736               !strcmp (s, "-version") ||
737               !strcmp (s, "-time"))
738             {
739
740               if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
741                 fprintf (stderr, "\n\
742     Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
743               else
744                 fprintf (stderr, "\n\
745     However, `%s' is an option to the `xscreensaver-command' program.\n", s);
746
747               fprintf (stderr, "\
748     The `xscreensaver' program is a daemon that runs in the background.\n\
749     You control a running xscreensaver process by sending it messages\n\
750     with `xscreensaver-demo' or `xscreensaver-command'.\n\
751 .   See the man pages for details, or check the web page:\n\
752     http://www.jwz.org/xscreensaver/\n\n");
753             }
754
755           exit (1);
756         }
757     }
758 }
759
760
761 /* Print out the xscreensaver banner to the tty if applicable;
762    Issue any other warnings that are called for at this point.
763  */
764 static void
765 print_banner (saver_info *si)
766 {
767   saver_preferences *p = &si->prefs;
768
769   char *s, year[5];
770   s = strchr (screensaver_id, '-');
771   s = strrchr (s, '-');
772   s++;
773   strncpy (year, s, 4);
774   year[4] = 0;
775
776   /* This resource gets set some time before the others, so that we know
777      whether to print the banner (and so that the banner gets printed before
778      any resource-database-related error messages.)
779    */
780   p->verbose_p = (p->debug_p || 
781                   get_boolean_resource (si->dpy, "verbose", "Boolean"));
782
783   /* Ditto, for the locking_disabled_p message. */
784   p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
785
786   if (p->verbose_p)
787     fprintf (stderr,
788              "%s %s, copyright (c) 1991-%s "
789              "by Jamie Zawinski <jwz@jwz.org>.\n",
790              progname, si->version, year);
791
792   if (p->debug_p)
793     fprintf (stderr, "\n"
794              "%s: Warning: running in DEBUG MODE.  Be afraid.\n"
795              "\n"
796              "\tNote that in debug mode, the xscreensaver window will only\n"
797              "\tcover the left half of the screen.  (The idea is that you\n"
798              "\tcan still see debugging output in a shell, if you position\n"
799              "\tit on the right side of the screen.)\n"
800              "\n"
801              "\tDebug mode is NOT SECURE.  Do not run with -debug in\n"
802              "\tuntrusted environments.\n"
803              "\n",
804              blurb());
805
806   if (p->verbose_p && senescent_p ())
807     fprintf (stderr, "\n"
808              "*************************************"
809              "**************************************\n"
810              "%s: Warning: this version of xscreensaver is VERY OLD!\n"
811              "%s: Please upgrade!  http://www.jwz.org/xscreensaver/\n"
812              "*************************************"
813              "**************************************\n"
814              "\n",
815              blurb(), blurb());
816
817   if (p->verbose_p)
818     {
819       if (!si->uid_message || !*si->uid_message)
820         describe_uids (si, stderr);
821       else
822         {
823           if (si->orig_uid && *si->orig_uid)
824             fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
825                      blurb(), si->orig_uid);
826           fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
827         }
828
829       fprintf (stderr, "%s: in process %lu.\n", blurb(),
830                (unsigned long) getpid());
831     }
832 }
833
834 static void
835 print_lock_failure_banner (saver_info *si)
836 {
837   saver_preferences *p = &si->prefs;
838
839   /* If locking was not able to be initalized for some reason, explain why.
840      (This has to be done after we've read the lock_p resource.)
841    */
842   if (si->locking_disabled_p)
843     {
844       p->lock_p = False;
845       fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
846                si->nolock_reason);
847       if (strstr (si->nolock_reason, "passw"))
848         fprintf (stderr, "%s: does xscreensaver need to be setuid?  "
849                  "consult the manual.\n", blurb());
850       else if (strstr (si->nolock_reason, "running as "))
851         fprintf (stderr, 
852                  "%s: locking only works when xscreensaver is launched\n"
853                  "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
854                  "\t See the manual for details.\n",
855                  blurb());
856     }
857
858 }
859
860
861 /* called from screens.c so that all the Xt crud is here. */
862 void
863 initialize_screen_root_widget (saver_screen_info *ssi)
864 {
865   saver_info *si = ssi->global;
866   if (ssi->toplevel_shell)
867     XtDestroyWidget (ssi->toplevel_shell);
868   ssi->toplevel_shell =
869     XtVaAppCreateShell (progname, progclass, 
870                         applicationShellWidgetClass,
871                         si->dpy,
872                         XtNscreen, ssi->screen,
873                         XtNvisual, ssi->current_visual,
874                         XtNdepth,  visual_depth (ssi->screen,
875                                                  ssi->current_visual),
876                         NULL);
877 }
878
879
880 /* Examine all of the display's screens, and populate the `saver_screen_info'
881    structures.  Make sure this is called after hack_environment() sets $PATH.
882  */
883 static void
884 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
885 {
886   int i;
887
888   update_screen_layout (si);
889
890   /* Check to see whether fading is ever possible -- if any of the
891      screens on the display has a PseudoColor visual, then fading can
892      work (on at least some screens.)  If no screen has a PseudoColor
893      visual, then don't bother ever trying to fade, because it will
894      just cause a delay without causing any visible effect.
895    */
896   for (i = 0; i < si->nscreens; i++)
897     {
898       saver_screen_info *ssi = &si->screens[i];
899       if (has_writable_cells (ssi->screen, ssi->current_visual) ||
900           get_visual (ssi->screen, "PseudoColor", True, False) ||
901           get_visual (ssi->screen, "GrayScale", True, False))
902         {
903           si->fading_possible_p = True;
904           break;
905         }
906     }
907
908 #ifdef HAVE_XF86VMODE_GAMMA
909   si->fading_possible_p = True;  /* if we can gamma fade, go for it */
910 #endif
911 }
912
913
914 /* If any server extensions have been requested, try and initialize them.
915    Issue warnings if requests can't be honored.
916  */
917 static void
918 initialize_server_extensions (saver_info *si)
919 {
920   saver_preferences *p = &si->prefs;
921
922   Bool server_has_xidle_extension_p = False;
923   Bool server_has_sgi_saver_extension_p = False;
924   Bool server_has_mit_saver_extension_p = False;
925   Bool system_has_proc_interrupts_p = False;
926   Bool server_has_xinput_extension_p = False;
927   const char *piwhy = 0;
928
929   si->using_xidle_extension = p->use_xidle_extension;
930   si->using_sgi_saver_extension = p->use_sgi_saver_extension;
931   si->using_mit_saver_extension = p->use_mit_saver_extension;
932   si->using_proc_interrupts = p->use_proc_interrupts;
933   si->using_xinput_extension = p->use_xinput_extension;
934
935 #ifdef HAVE_XIDLE_EXTENSION
936   {
937     int ev, er;
938     server_has_xidle_extension_p = XidleQueryExtension (si->dpy, &ev, &er);
939   }
940 #endif
941 #ifdef HAVE_SGI_SAVER_EXTENSION
942   server_has_sgi_saver_extension_p =
943     XScreenSaverQueryExtension (si->dpy,
944                                 &si->sgi_saver_ext_event_number,
945                                 &si->sgi_saver_ext_error_number);
946 #endif
947 #ifdef HAVE_MIT_SAVER_EXTENSION
948   server_has_mit_saver_extension_p =
949     XScreenSaverQueryExtension (si->dpy,
950                                 &si->mit_saver_ext_event_number,
951                                 &si->mit_saver_ext_error_number);
952 #endif
953 #ifdef HAVE_PROC_INTERRUPTS
954   system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
955 #endif
956
957 #ifdef HAVE_XINPUT
958   server_has_xinput_extension_p = query_xinput_extension (si);
959 #endif
960
961   if (!server_has_xidle_extension_p)
962     si->using_xidle_extension = False;
963   else if (p->verbose_p)
964     {
965       if (si->using_xidle_extension)
966         fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
967       else
968         fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
969     }
970
971   if (!server_has_sgi_saver_extension_p)
972     si->using_sgi_saver_extension = False;
973   else if (p->verbose_p)
974     {
975       if (si->using_sgi_saver_extension)
976         fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
977       else
978         fprintf (stderr,
979                  "%s: not using server's SGI SCREEN_SAVER extension.\n",
980                  blurb());
981     }
982
983   if (!server_has_mit_saver_extension_p)
984     si->using_mit_saver_extension = False;
985   else if (p->verbose_p)
986     {
987       if (si->using_mit_saver_extension)
988         fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
989                  blurb());
990       else
991         fprintf (stderr,
992                  "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
993                  blurb());
994     }
995
996 #ifdef HAVE_RANDR
997   if (XRRQueryExtension (si->dpy,
998                          &si->randr_event_number, &si->randr_error_number))
999     {
1000       int nscreens = ScreenCount (si->dpy);  /* number of *real* screens */
1001       int i;
1002
1003       si->using_randr_extension = TRUE;
1004
1005       if (p->verbose_p)
1006         fprintf (stderr, "%s: selecting RANDR events\n", blurb());
1007       for (i = 0; i < nscreens; i++)
1008 #  ifdef RRScreenChangeNotifyMask                 /* randr.h 1.5, 2002/09/29 */
1009         XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
1010                         RRScreenChangeNotifyMask);
1011 #  else  /* !RRScreenChangeNotifyMask */          /* Xrandr.h 1.4, 2001/06/07 */
1012         XRRScreenChangeSelectInput (si->dpy, RootWindow (si->dpy, i), True);
1013 #  endif /* !RRScreenChangeNotifyMask */
1014     }
1015 # endif /* HAVE_RANDR */
1016
1017 #ifdef HAVE_XINPUT
1018   if (!server_has_xinput_extension_p)
1019     si->using_xinput_extension = False;
1020   else
1021     {
1022       if (si->using_xinput_extension)
1023         init_xinput_extension(si);
1024
1025       if (p->verbose_p)
1026         {
1027           if (si->using_xinput_extension)
1028             fprintf (stderr,
1029                      "%s: selecting events from %d XInputExtension devices.\n",
1030                      blurb(), si->num_xinput_devices);
1031           else
1032             fprintf (stderr,
1033                      "%s: not using XInputExtension.\n",
1034                      blurb());
1035         }
1036     }
1037 #endif
1038
1039   if (!system_has_proc_interrupts_p)
1040     {
1041       si->using_proc_interrupts = False;
1042       if (p->verbose_p && piwhy)
1043         fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
1044                  piwhy);
1045     }
1046   else if (p->verbose_p)
1047     {
1048       if (si->using_proc_interrupts)
1049         fprintf (stderr,
1050                  "%s: consulting /proc/interrupts for keyboard activity.\n",
1051                  blurb());
1052       else
1053         fprintf (stderr,
1054                 "%s: not consulting /proc/interrupts for keyboard activity.\n",
1055                  blurb());
1056     }
1057 }
1058
1059
1060 #ifdef DEBUG_MULTISCREEN
1061 static void
1062 debug_multiscreen_timer (XtPointer closure, XtIntervalId *id)
1063 {
1064   saver_info *si = (saver_info *) closure;
1065   saver_preferences *p = &si->prefs;
1066   if (update_screen_layout (si))
1067     {
1068       if (p->verbose_p)
1069         {
1070           fprintf (stderr, "%s: new layout:\n", blurb());
1071           describe_monitor_layout (si);
1072         }
1073       resize_screensaver_window (si);
1074     }
1075   XtAppAddTimeOut (si->app, 1000*4, debug_multiscreen_timer, (XtPointer) si);
1076 }
1077 #endif /* DEBUG_MULTISCREEN */
1078
1079
1080 /* For the case where we aren't using an server extensions, select user events
1081    on all the existing windows, and launch timers to select events on
1082    newly-created windows as well.
1083
1084    If a server extension is being used, this does nothing.
1085  */
1086 static void
1087 select_events (saver_info *si)
1088 {
1089   saver_preferences *p = &si->prefs;
1090   int i;
1091
1092   if (si->using_xidle_extension ||
1093       si->using_mit_saver_extension ||
1094       si->using_sgi_saver_extension)
1095     return;
1096
1097   if (p->initial_delay)
1098     {
1099       if (p->verbose_p)
1100         {
1101           fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
1102                    (int) p->initial_delay/1000,
1103                    (p->initial_delay == 1000 ? "" : "s"));
1104           fflush (stderr);
1105           fflush (stdout);
1106         }
1107       usleep (p->initial_delay);
1108       if (p->verbose_p)
1109         fprintf (stderr, " done.\n");
1110     }
1111
1112   if (p->verbose_p)
1113     {
1114       fprintf (stderr, "%s: selecting events on extant windows...", blurb());
1115       fflush (stderr);
1116       fflush (stdout);
1117     }
1118
1119   /* Select events on the root windows of every screen.  This also selects
1120      for window creation events, so that new subwindows will be noticed.
1121    */
1122   for (i = 0; i < si->nscreens; i++)
1123     {
1124       saver_screen_info *ssi = &si->screens[i];
1125       if (ssi->real_screen_p)
1126         start_notice_events_timer (si,
1127            RootWindowOfScreen (si->screens[i].screen), False);
1128     }
1129
1130   if (p->verbose_p)
1131     fprintf (stderr, " done.\n");
1132
1133 # ifdef DEBUG_MULTISCREEN
1134   if (p->debug_p) debug_multiscreen_timer ((XtPointer) si, 0);
1135 # endif
1136 }
1137
1138
1139 void
1140 maybe_reload_init_file (saver_info *si)
1141 {
1142   saver_preferences *p = &si->prefs;
1143   if (init_file_changed_p (p))
1144     {
1145       if (p->verbose_p)
1146         fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
1147                  blurb(), init_file_name());
1148
1149       load_init_file (si->dpy, p);
1150
1151       /* If a server extension is in use, and p->timeout has changed,
1152          we need to inform the server of the new timeout. */
1153       disable_builtin_screensaver (si, False);
1154
1155       /* If the DPMS settings in the init file have changed,
1156          change the settings on the server to match. */
1157       sync_server_dpms_settings (si->dpy,
1158                                  (p->dpms_enabled_p  &&
1159                                   p->mode != DONT_BLANK),
1160                                  p->dpms_quickoff_p,
1161                                  p->dpms_standby / 1000,
1162                                  p->dpms_suspend / 1000,
1163                                  p->dpms_off / 1000,
1164                                  False);
1165     }
1166 }
1167
1168
1169 /* Loop forever:
1170
1171        - wait until the user is idle;
1172        - blank the screen;
1173        - wait until the user is active;
1174        - unblank the screen;
1175        - repeat.
1176
1177  */
1178 static void
1179 main_loop (saver_info *si)
1180 {
1181   saver_preferences *p = &si->prefs;
1182   Bool ok_to_unblank;
1183   int i;
1184
1185   while (1)
1186     {
1187       Bool was_locked = False;
1188
1189       if (p->verbose_p)
1190         fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1191
1192       check_for_leaks ("unblanked A");
1193       sleep_until_idle (si, True);
1194       check_for_leaks ("unblanked B");
1195
1196       if (p->verbose_p)
1197         {
1198           if (si->demoing_p)
1199             fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1200                      si->selection_mode, timestring());
1201           else
1202             fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1203                      timestring());
1204         }
1205
1206       maybe_reload_init_file (si);
1207
1208       if (p->mode == DONT_BLANK)
1209         {
1210           if (p->verbose_p)
1211             fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1212                      blurb(), timestring());
1213
1214           /* Go around the loop and wait for the next bout of idleness,
1215              or for the init file to change, or for a remote command to
1216              come in, or something.
1217
1218              But, if locked_p is true, go ahead.  This can only happen
1219              if we're in "disabled" mode but a "lock" clientmessage came
1220              in: in that case, we should go ahead and blank/lock the screen.
1221            */
1222           if (!si->locked_p)
1223             continue;
1224         }
1225
1226       /* Since we're about to blank the screen, kill the de-race timer,
1227          if any.  It might still be running if we have unblanked and then
1228          re-blanked in a short period (e.g., when using the "next" button
1229          in xscreensaver-demo.)
1230        */
1231       if (si->de_race_id)
1232         {
1233           if (p->verbose_p)
1234             fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
1235                      blurb(), si->de_race_ticks);
1236           XtRemoveTimeOut (si->de_race_id);
1237           si->de_race_id = 0;
1238         }
1239
1240
1241       /* Now, try to blank.
1242        */
1243
1244       if (! blank_screen (si))
1245         {
1246           /* We were unable to grab either the keyboard or mouse.
1247              This means we did not (and must not) blank the screen.
1248              If we were to blank the screen while some other program
1249              is holding both the mouse and keyboard grabbed, then
1250              we would never be able to un-blank it!  We would never
1251              see any events, and the display would be wedged.
1252
1253              In particular, without that keyboard grab, we will be
1254              unable to ever read keypresses on the unlock dialog.
1255              You can't unlock if you can't type your password.
1256
1257              So, just go around the loop again and wait for the
1258              next bout of idleness.  (If the user remains idle, we
1259              will next try to blank the screen again in no more than
1260              60 seconds.)
1261           */
1262           Time retry = 60 * 1000;
1263           if (p->timeout < retry)
1264             retry = p->timeout;
1265
1266           if (p->debug_p)
1267             {
1268               fprintf (stderr,
1269                   "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
1270                        blurb());
1271             }
1272           else
1273             {
1274               fprintf (stderr,
1275                   "%s: unable to grab keyboard or mouse!  Blanking aborted.\n",
1276                        blurb());
1277
1278               /* Since we were unable to blank, clearly we're not locked,
1279                  but we might have been prematurely marked as locked by
1280                  the LOCK ClientMessage. */
1281               if (si->locked_p)
1282                 set_locked_p (si, False);
1283
1284               schedule_wakeup_event (si, retry, p->debug_p);
1285               continue;
1286             }
1287         }
1288
1289       for (i = 0; i < si->nscreens; i++)
1290         kill_screenhack (&si->screens[i]);
1291
1292       raise_window (si, True, True, False);
1293       if (si->throttled_p)
1294         fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1295       else
1296         for (i = 0; i < si->nscreens; i++)
1297           spawn_screenhack (&si->screens[i]);
1298
1299       /* If we are blanking only, optionally power down monitor right now. */
1300       if (p->mode == BLANK_ONLY &&
1301           p->dpms_enabled_p && 
1302           p->dpms_quickoff_p)
1303         {
1304           sync_server_dpms_settings (si->dpy, True,
1305                                      p->dpms_quickoff_p,
1306                                      p->dpms_standby / 1000,
1307                                      p->dpms_suspend / 1000,
1308                                      p->dpms_off / 1000,
1309                                      False);
1310           monitor_power_on (si, False);
1311         }
1312
1313       /* Don't start the cycle timer in demo mode. */
1314       if (!si->demoing_p && p->cycle)
1315         si->cycle_id = XtAppAddTimeOut (si->app,
1316                                         (si->selection_mode
1317                                          /* see comment in cycle_timer() */
1318                                          ? 1000 * 60 * 60
1319                                          : p->cycle),
1320                                         cycle_timer,
1321                                         (XtPointer) si);
1322
1323
1324 #ifndef NO_LOCKING
1325       /* Maybe start locking the screen.
1326        */
1327       {
1328         Time lock_timeout = p->lock_timeout;
1329
1330         if (si->emergency_lock_p && p->lock_p && lock_timeout)
1331           {
1332             int secs = p->lock_timeout / 1000;
1333             if (p->verbose_p)
1334               fprintf (stderr,
1335                      "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1336                        blurb(),
1337                        (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1338             lock_timeout = 0;
1339           }
1340
1341         si->emergency_lock_p = False;
1342
1343         if (!si->demoing_p &&           /* if not going into demo mode */
1344             p->lock_p &&                /* and locking is enabled */
1345             !si->locking_disabled_p &&  /* and locking is possible */
1346             lock_timeout == 0)          /* and locking is not timer-deferred */
1347           set_locked_p (si, True);      /* then lock right now. */
1348
1349         /* locked_p might be true already because of the above, or because of
1350            the LOCK ClientMessage.  But if not, and if we're supposed to lock
1351            after some time, set up a timer to do so.
1352         */
1353         if (p->lock_p &&
1354             !si->locked_p &&
1355             lock_timeout > 0)
1356           si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1357                                          activate_lock_timer,
1358                                          (XtPointer) si);
1359       }
1360 #endif /* !NO_LOCKING */
1361
1362
1363       ok_to_unblank = True;
1364       do {
1365
1366         check_for_leaks ("blanked A");
1367         sleep_until_idle (si, False);           /* until not idle */
1368         check_for_leaks ("blanked B");
1369
1370         maybe_reload_init_file (si);
1371
1372 #ifndef NO_LOCKING
1373         /* Maybe unlock the screen.
1374          */
1375         if (si->locked_p)
1376           {
1377             saver_screen_info *ssi = si->default_screen;
1378             if (si->locking_disabled_p) abort ();
1379
1380             was_locked = True;
1381             si->dbox_up_p = True;
1382             for (i = 0; i < si->nscreens; i++)
1383               suspend_screenhack (&si->screens[i], True);         /* suspend */
1384             XUndefineCursor (si->dpy, ssi->screensaver_window);
1385
1386             ok_to_unblank = unlock_p (si);
1387
1388             si->dbox_up_p = False;
1389             XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1390             for (i = 0; i < si->nscreens; i++)
1391               suspend_screenhack (&si->screens[i], False);         /* resume */
1392
1393             if (!ok_to_unblank &&
1394                 !screenhack_running_p (si))
1395               {
1396                 /* If the lock dialog has been dismissed and we're not about to
1397                    unlock the screen, and there is currently no hack running,
1398                    then launch one.  (There might be no hack running if DPMS
1399                    had kicked in.  But DPMS is off now, so bring back the hack)
1400                  */
1401                 if (si->cycle_id)
1402                   XtRemoveTimeOut (si->cycle_id);
1403                 si->cycle_id = 0;
1404                 cycle_timer ((XtPointer) si, 0);
1405               }
1406           }
1407 #endif /* !NO_LOCKING */
1408
1409         } while (!ok_to_unblank);
1410
1411
1412       if (p->verbose_p)
1413         fprintf (stderr, "%s: unblanking screen at %s.\n",
1414                  blurb(), timestring ());
1415
1416       /* Kill before unblanking, to stop drawing as soon as possible. */
1417       for (i = 0; i < si->nscreens; i++)
1418         kill_screenhack (&si->screens[i]);
1419       unblank_screen (si);
1420
1421       set_locked_p (si, False);
1422       si->emergency_lock_p = False;
1423       si->demoing_p = 0;
1424       si->selection_mode = 0;
1425
1426       /* If we're throttled, and the user has explicitly unlocked the screen,
1427          then unthrottle.  If we weren't locked, then don't unthrottle
1428          automatically, because someone might have just bumped the desk... */
1429       if (was_locked)
1430         {
1431           if (si->throttled_p && p->verbose_p)
1432             fprintf (stderr, "%s: unthrottled.\n", blurb());
1433           si->throttled_p = False;
1434         }
1435
1436       if (si->cycle_id)
1437         {
1438           XtRemoveTimeOut (si->cycle_id);
1439           si->cycle_id = 0;
1440         }
1441
1442       if (si->lock_id)
1443         {
1444           XtRemoveTimeOut (si->lock_id);
1445           si->lock_id = 0;
1446         }
1447
1448       /* Since we're unblanked now, break race conditions and make
1449          sure we stay that way (see comment in timers.c.) */
1450       if (! si->de_race_id)
1451         de_race_timer ((XtPointer) si, 0);
1452     }
1453 }
1454
1455 static void analyze_display (saver_info *si);
1456 static void fix_fds (void);
1457
1458 int
1459 main (int argc, char **argv)
1460 {
1461   Widget shell;
1462   saver_info the_si;
1463   saver_info *si = &the_si;
1464   saver_preferences *p = &si->prefs;
1465   struct passwd *spasswd;
1466   int i;
1467
1468   /* It turns out that if we do setlocale (LC_ALL, "") here, people
1469      running in Japanese locales get font craziness on the password
1470      dialog, presumably because it is displaying Japanese characters
1471      in a non-Japanese font.  However, if we don't call setlocale()
1472      at all, then XLookupString() never returns multi-byte UTF-8
1473      characters when people type non-Latin1 characters on the
1474      keyboard.
1475
1476      The current theory (and at this point, I'm really guessing!) is
1477      that using LC_CTYPE instead of LC_ALL will make XLookupString()
1478      behave usefully, without having the side-effect of screwing up
1479      the fonts on the unlock dialog.
1480
1481      See https://bugs.launchpad.net/ubuntu/+source/xscreensaver/+bug/671923
1482      from comment #20 onward.
1483
1484        -- jwz, 24-Sep-2011
1485    */
1486 #ifdef ENABLE_NLS
1487   if (!setlocale (LC_CTYPE, ""))
1488     fprintf (stderr, "%s: warning: could not set default locale\n",
1489              progname);
1490
1491   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1492   textdomain (GETTEXT_PACKAGE);
1493 #endif /* ENABLE_NLS */
1494
1495   memset(si, 0, sizeof(*si));
1496   global_si_kludge = si;        /* I hate C so much... */
1497
1498   fix_fds();
1499
1500 # undef ya_rand_init
1501   ya_rand_init (0);
1502
1503   save_argv (argc, argv);
1504   set_version_string (si, &argc, argv);
1505   privileged_initialization (si, &argc, argv);
1506   hack_environment (si);
1507
1508   spasswd = getpwuid(getuid());
1509   if (!spasswd)
1510     {
1511       fprintf(stderr, "Could not figure out who the current user is!\n");
1512       return 1;
1513     }
1514
1515   si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1516
1517 # ifndef NO_LOCKING
1518   si->unlock_cb = gui_auth_conv;
1519   si->auth_finished_cb = auth_finished_cb;
1520 # endif /* !NO_LOCKING */
1521
1522   shell = connect_to_server (si, &argc, argv);
1523   process_command_line (si, &argc, argv);
1524   stderr_log_file (si);
1525   print_banner (si);
1526
1527   load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1528   blurb_timestamp_p = p->timestamp_p;  /* kludge */
1529   initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1530
1531   /* We can only issue this warning now. */
1532   if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1533     fprintf (stderr,
1534              "%s: there are no PseudoColor or GrayScale visuals.\n"
1535              "%s: ignoring the request for fading/unfading.\n",
1536              blurb(), blurb());
1537
1538   for (i = 0; i < si->nscreens; i++)
1539     {
1540       saver_screen_info *ssi = &si->screens[i];
1541       if (ssi->real_screen_p)
1542         if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1543           exit (1);
1544     }
1545
1546   lock_initialization (si, &argc, argv);
1547   print_lock_failure_banner (si);
1548
1549   if (p->xsync_p) XSynchronize (si->dpy, True);
1550
1551   if (p->verbose_p) analyze_display (si);
1552   initialize_server_extensions (si);
1553
1554   si->blank_time = time ((time_t *) 0); /* must be before ..._window */
1555   initialize_screensaver_window (si);
1556
1557   select_events (si);
1558   init_sigchld ();
1559
1560   disable_builtin_screensaver (si, True);
1561   sync_server_dpms_settings (si->dpy,
1562                              (p->dpms_enabled_p  &&
1563                               p->mode != DONT_BLANK),
1564                              p->dpms_quickoff_p,
1565                              p->dpms_standby / 1000,
1566                              p->dpms_suspend / 1000,
1567                              p->dpms_off / 1000,
1568                              False);
1569
1570   initialize_stderr (si);
1571   handle_signals (si);
1572
1573   make_splash_dialog (si);
1574
1575   main_loop (si);               /* doesn't return */
1576   return 0;
1577 }
1578
1579 static void
1580 fix_fds (void)
1581 {
1582   /* Bad Things Happen if stdin, stdout, and stderr have been closed
1583      (as by the `sh incantation "xscreensaver >&- 2>&-").  When you do
1584      that, the X connection gets allocated to one of these fds, and
1585      then some random library writes to stderr, and random bits get
1586      stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1587      So, we cause the first three file descriptors to be open to
1588      /dev/null if they aren't open to something else already.  This
1589      must be done before any other files are opened (or the closing
1590      of that other file will again free up one of the "magic" first
1591      three FDs.)
1592
1593      We do this by opening /dev/null three times, and then closing
1594      those fds, *unless* any of them got allocated as #0, #1, or #2,
1595      in which case we leave them open.  Gag.
1596
1597      Really, this crap is technically required of *every* X program,
1598      if you want it to be robust in the face of "2>&-".
1599    */
1600   int fd0 = open ("/dev/null", O_RDWR);
1601   int fd1 = open ("/dev/null", O_RDWR);
1602   int fd2 = open ("/dev/null", O_RDWR);
1603   if (fd0 > 2) close (fd0);
1604   if (fd1 > 2) close (fd1);
1605   if (fd2 > 2) close (fd2);
1606 }
1607
1608
1609 \f
1610 /* Processing ClientMessage events.
1611  */
1612
1613
1614 static Bool error_handler_hit_p = False;
1615
1616 static int
1617 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1618 {
1619   error_handler_hit_p = True;
1620   return 0;
1621 }
1622
1623 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1624    them.  We only look up the atom names for printing warning messages,
1625    so don't bomb out when it happens...
1626  */
1627 static char *
1628 XGetAtomName_safe (Display *dpy, Atom atom)
1629 {
1630   char *result;
1631   XErrorHandler old_handler;
1632   if (!atom) return 0;
1633
1634   XSync (dpy, False);
1635   error_handler_hit_p = False;
1636   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1637   result = XGetAtomName (dpy, atom);
1638   XSync (dpy, False);
1639   XSetErrorHandler (old_handler);
1640   XSync (dpy, False);
1641   if (error_handler_hit_p) result = 0;
1642
1643   if (result)
1644     return result;
1645   else
1646     {
1647       char buf[100];
1648       sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1649       return strdup (buf);
1650     }
1651 }
1652
1653
1654 static void
1655 clientmessage_response (saver_info *si, Window w, Bool error,
1656                         const char *stderr_msg,
1657                         const char *protocol_msg)
1658 {
1659   char *proto;
1660   int L;
1661   saver_preferences *p = &si->prefs;
1662   XErrorHandler old_handler;
1663
1664   if (error || p->verbose_p)
1665     fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1666
1667   L = strlen(protocol_msg);
1668   proto = (char *) malloc (L + 2);
1669   proto[0] = (error ? '-' : '+');
1670   strcpy (proto+1, protocol_msg);
1671   L++;
1672
1673   /* Ignore all X errors while sending a response to a ClientMessage.
1674      Pretty much the only way we could get an error here is if the
1675      window we're trying to send the reply on has been deleted, in
1676      which case, the sender of the ClientMessage won't see our response
1677      anyway.
1678    */
1679   XSync (si->dpy, False);
1680   error_handler_hit_p = False;
1681   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1682
1683   XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1684                    PropModeReplace, (unsigned char *) proto, L);
1685
1686   XSync (si->dpy, False);
1687   XSetErrorHandler (old_handler);
1688   XSync (si->dpy, False);
1689
1690   free (proto);
1691 }
1692
1693
1694 static void
1695 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1696 {
1697 #if 0  /* Oh, fuck it.  GNOME likes to spew random ClientMessages at us
1698           all the time.  This is presumably indicative of an error in
1699           the sender of that ClientMessage: if we're getting it and 
1700           ignoring it, then it's not reaching the intended recipient.
1701           But people complain to me about this all the time ("waaah!
1702           xscreensaver is printing to it's stderr and that gets my
1703           panties all in a bunch!")  And I'm sick of hearing about it.
1704           So we'll just ignore these messages and let GNOME go right
1705           ahead and continue to stumble along in its malfunction.
1706         */
1707
1708   saver_preferences *p = &si->prefs;
1709   char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1710   Window w = event->xclient.window;
1711   char wdesc[255];
1712   int screen = 0;
1713   Bool root_p = False;
1714
1715   *wdesc = 0;
1716   for (screen = 0; screen < si->nscreens; screen++)
1717     if (w == si->screens[screen].screensaver_window)
1718       {
1719         strcpy (wdesc, "xscreensaver");
1720         break;
1721       }
1722     else if (w == RootWindow (si->dpy, screen))
1723       {
1724         strcpy (wdesc, "root");
1725         root_p = True;
1726         break;
1727       }
1728
1729   /* If this ClientMessage was sent to the real root window instead of to the
1730      xscreensaver window, then it might be intended for someone else who is
1731      listening on the root window (e.g., the window manager).  So only print
1732      the warning if: we are in debug mode; or if the bogus message was
1733      actually sent to one of the xscreensaver-created windows.
1734    */
1735   if (root_p && !p->debug_p)
1736     return;
1737
1738   if (!*wdesc)
1739     {
1740       XErrorHandler old_handler;
1741       XClassHint hint;
1742       XWindowAttributes xgwa;
1743       memset (&hint, 0, sizeof(hint));
1744       memset (&xgwa, 0, sizeof(xgwa));
1745
1746       XSync (si->dpy, False);
1747       old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1748       XGetClassHint (si->dpy, w, &hint);
1749       XGetWindowAttributes (si->dpy, w, &xgwa);
1750       XSync (si->dpy, False);
1751       XSetErrorHandler (old_handler);
1752       XSync (si->dpy, False);
1753
1754       screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1755
1756       sprintf (wdesc, "%.20s / %.20s",
1757                (hint.res_name  ? hint.res_name  : "(null)"),
1758                (hint.res_class ? hint.res_class : "(null)"));
1759       if (hint.res_name)  XFree (hint.res_name);
1760       if (hint.res_class) XFree (hint.res_class);
1761     }
1762
1763   fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1764            blurb(), screen, (str ? str : "(null)"));
1765   fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1766            blurb(), screen, (unsigned long) w, wdesc);
1767   if (str) XFree (str);
1768
1769 #endif /* 0 */
1770 }
1771
1772
1773 Bool
1774 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1775 {
1776   saver_preferences *p = &si->prefs;
1777   Atom type = 0;
1778   Window window = event->xclient.window;
1779
1780   /* Preferences might affect our handling of client messages. */
1781   maybe_reload_init_file (si);
1782
1783   if (event->xclient.message_type != XA_SCREENSAVER ||
1784       event->xclient.format != 32)
1785     {
1786       bogus_clientmessage_warning (si, event);
1787       return False;
1788     }
1789
1790   type = event->xclient.data.l[0];
1791   if (type == XA_ACTIVATE)
1792     {
1793       if (until_idle_p)
1794         {
1795           if (p->mode == DONT_BLANK)
1796             {
1797               clientmessage_response(si, window, True,
1798                          "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1799                                      "screen blanking is currently disabled.");
1800               return False;
1801             }
1802
1803           clientmessage_response(si, window, False,
1804                                  "ACTIVATE ClientMessage received.",
1805                                  "activating.");
1806           si->selection_mode = 0;
1807           si->demoing_p = False;
1808
1809           if (si->throttled_p && p->verbose_p)
1810             fprintf (stderr, "%s: unthrottled.\n", blurb());
1811           si->throttled_p = False;
1812
1813           if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1814             {
1815               XForceScreenSaver (si->dpy, ScreenSaverActive);
1816               return False;
1817             }
1818           else
1819             {
1820               return True;
1821             }
1822         }
1823       clientmessage_response(si, window, True,
1824                        "ClientMessage ACTIVATE received while already active.",
1825                              "already active.");
1826     }
1827   else if (type == XA_DEACTIVATE)
1828     {
1829 # if 0
1830       /* When -deactivate is received while locked, pop up the dialog box
1831          instead of just ignoring it.  Some people depend on this behavior
1832          to be able to unlock by using e.g. a fingerprint reader without
1833          also having to click the mouse first.
1834        */
1835       if (si->locked_p) 
1836         {
1837           clientmessage_response(si, window, False,
1838               "DEACTIVATE ClientMessage received while locked: ignored.",
1839               "screen is locked.");
1840         }
1841       else
1842 # endif /* 0 */
1843         {
1844           if (! until_idle_p)
1845             {
1846               if (si->throttled_p && p->verbose_p)
1847                 fprintf (stderr, "%s: unthrottled.\n", blurb());
1848               si->throttled_p = False;
1849
1850               clientmessage_response(si, window, False,
1851                                      "DEACTIVATE ClientMessage received.",
1852                                      "deactivating.");
1853               if (si->using_mit_saver_extension ||
1854                   si->using_sgi_saver_extension)
1855                 {
1856                   XForceScreenSaver (si->dpy, ScreenSaverReset);
1857                   return False;
1858                 }
1859               else
1860                 {
1861                   return True;
1862                 }
1863             }
1864           clientmessage_response(si, window, False,
1865                           "ClientMessage DEACTIVATE received while inactive: "
1866                           "resetting idle timer.",
1867                                  "not active: idle timer reset.");
1868           reset_timers (si);
1869         }
1870     }
1871   else if (type == XA_CYCLE)
1872     {
1873       if (! until_idle_p)
1874         {
1875           clientmessage_response(si, window, False,
1876                                  "CYCLE ClientMessage received.",
1877                                  "cycling.");
1878           si->selection_mode = 0;       /* 0 means randomize when its time. */
1879           si->demoing_p = False;
1880
1881           if (si->throttled_p && p->verbose_p)
1882             fprintf (stderr, "%s: unthrottled.\n", blurb());
1883           si->throttled_p = False;
1884
1885           if (si->cycle_id)
1886             XtRemoveTimeOut (si->cycle_id);
1887           si->cycle_id = 0;
1888           cycle_timer ((XtPointer) si, 0);
1889           return False;
1890         }
1891       clientmessage_response(si, window, True,
1892                              "ClientMessage CYCLE received while inactive.",
1893                              "not active.");
1894     }
1895   else if (type == XA_NEXT || type == XA_PREV)
1896     {
1897       clientmessage_response(si, window, False,
1898                              (type == XA_NEXT
1899                               ? "NEXT ClientMessage received."
1900                               : "PREV ClientMessage received."),
1901                              "cycling.");
1902       si->selection_mode = (type == XA_NEXT ? -1 : -2);
1903       si->demoing_p = False;
1904
1905       if (si->throttled_p && p->verbose_p)
1906         fprintf (stderr, "%s: unthrottled.\n", blurb());
1907       si->throttled_p = False;
1908
1909       if (! until_idle_p)
1910         {
1911           if (si->cycle_id)
1912             XtRemoveTimeOut (si->cycle_id);
1913           si->cycle_id = 0;
1914           cycle_timer ((XtPointer) si, 0);
1915         }
1916       else
1917         return True;
1918     }
1919   else if (type == XA_SELECT)
1920     {
1921       char buf [255];
1922       char buf2 [255];
1923       long which = event->xclient.data.l[1];
1924
1925       if (p->mode == DONT_BLANK)
1926         {
1927           clientmessage_response(si, window, True,
1928                            "SELECT ClientMessage received in DONT_BLANK mode.",
1929                                  "screen blanking is currently disabled.");
1930           return False;
1931         }
1932
1933       sprintf (buf, "SELECT %ld ClientMessage received.", which);
1934       sprintf (buf2, "activating (%ld).", which);
1935       clientmessage_response (si, window, False, buf, buf2);
1936
1937       if (which < 0) which = 0;         /* 0 == "random" */
1938       si->selection_mode = which;
1939       si->demoing_p = False;
1940
1941       if (si->throttled_p && p->verbose_p)
1942         fprintf (stderr, "%s: unthrottled.\n", blurb());
1943       si->throttled_p = False;
1944
1945       if (! until_idle_p)
1946         {
1947           if (si->cycle_id)
1948             XtRemoveTimeOut (si->cycle_id);
1949           si->cycle_id = 0;
1950           cycle_timer ((XtPointer) si, 0);
1951         }
1952       else
1953         return True;
1954     }
1955   else if (type == XA_EXIT)
1956     {
1957       /* Ignore EXIT message if the screen is locked. */
1958       if (until_idle_p || !si->locked_p)
1959         {
1960           clientmessage_response (si, window, False,
1961                                   "EXIT ClientMessage received.",
1962                                   "exiting.");
1963           if (! until_idle_p)
1964             {
1965               int i;
1966               for (i = 0; i < si->nscreens; i++)
1967                 kill_screenhack (&si->screens[i]);
1968               unblank_screen (si);
1969               XSync (si->dpy, False);
1970             }
1971           saver_exit (si, 0, 0);
1972         }
1973       else
1974         clientmessage_response (si, window, True,
1975                                 "EXIT ClientMessage received while locked.",
1976                                 "screen is locked.");
1977     }
1978   else if (type == XA_RESTART)
1979     {
1980       /* The RESTART message works whether the screensaver is active or not,
1981          unless the screen is locked, in which case it doesn't work.
1982        */
1983       if (until_idle_p || !si->locked_p)
1984         {
1985           clientmessage_response (si, window, False,
1986                                   "RESTART ClientMessage received.",
1987                                   "restarting.");
1988           if (! until_idle_p)
1989             {
1990               int i;
1991               for (i = 0; i < si->nscreens; i++)
1992                 kill_screenhack (&si->screens[i]);
1993               unblank_screen (si);
1994               XSync (si->dpy, False);
1995             }
1996
1997           restart_process (si);  /* does not return */
1998           abort();
1999         }
2000       else
2001         clientmessage_response (si, window, True,
2002                                 "RESTART ClientMessage received while locked.",
2003                                 "screen is locked.");
2004     }
2005   else if (type == XA_DEMO)
2006     {
2007       long arg = event->xclient.data.l[1];
2008       Bool demo_one_hack_p = (arg == 5000);
2009
2010       if (demo_one_hack_p)
2011         {
2012           if (until_idle_p)
2013             {
2014               long which = event->xclient.data.l[2];
2015               char buf [255];
2016               char buf2 [255];
2017               sprintf (buf, "DEMO %ld ClientMessage received.", which);
2018               sprintf (buf2, "demoing (%ld).", which);
2019               clientmessage_response (si, window, False, buf, buf2);
2020
2021               if (which < 0) which = 0;         /* 0 == "random" */
2022               si->selection_mode = which;
2023               si->demoing_p = True;
2024
2025               if (si->throttled_p && p->verbose_p)
2026                 fprintf (stderr, "%s: unthrottled.\n", blurb());
2027               si->throttled_p = False;
2028
2029               return True;
2030             }
2031
2032           clientmessage_response (si, window, True,
2033                                   "DEMO ClientMessage received while active.",
2034                                   "already active.");
2035         }
2036       else
2037         {
2038           clientmessage_response (si, window, True,
2039                                   "obsolete form of DEMO ClientMessage.",
2040                                   "obsolete form of DEMO ClientMessage.");
2041         }
2042     }
2043   else if (type == XA_PREFS)
2044     {
2045       clientmessage_response (si, window, True,
2046                               "the PREFS client-message is obsolete.",
2047                               "the PREFS client-message is obsolete.");
2048     }
2049   else if (type == XA_LOCK)
2050     {
2051 #ifdef NO_LOCKING
2052       clientmessage_response (si, window, True,
2053                               "not compiled with support for locking.",
2054                               "locking not enabled.");
2055 #else /* !NO_LOCKING */
2056       if (si->locking_disabled_p)
2057         clientmessage_response (si, window, True,
2058                       "LOCK ClientMessage received, but locking is disabled.",
2059                               "locking not enabled.");
2060       else if (si->locked_p)
2061         clientmessage_response (si, window, True,
2062                            "LOCK ClientMessage received while already locked.",
2063                                 "already locked.");
2064       else
2065         {
2066           char buf [255];
2067           char *response = (until_idle_p
2068                             ? "activating and locking."
2069                             : "locking.");
2070           sprintf (buf, "LOCK ClientMessage received; %s", response);
2071           clientmessage_response (si, window, False, buf, response);
2072
2073           /* Note that this leaves things in a slightly inconsistent state:
2074              we are blanked but not locked.  And blanking might actually
2075              fail if we can't get the grab. */
2076           set_locked_p (si, True);
2077
2078            /* Have to set the time or xscreensaver-command doesn't
2079               report the LOCK state change. */
2080            si->blank_time = time ((time_t *) 0);
2081
2082           si->selection_mode = 0;
2083           si->demoing_p = False;
2084
2085           if (si->lock_id)      /* we're doing it now, so lose the timeout */
2086             {
2087               XtRemoveTimeOut (si->lock_id);
2088               si->lock_id = 0;
2089             }
2090
2091           if (until_idle_p)
2092             {
2093               if (si->using_mit_saver_extension ||
2094                   si->using_sgi_saver_extension)
2095                 {
2096                   XForceScreenSaver (si->dpy, ScreenSaverActive);
2097                   return False;
2098                 }
2099               else
2100                 {
2101                   return True;
2102                 }
2103             }
2104         }
2105 #endif /* !NO_LOCKING */
2106     }
2107   else if (type == XA_THROTTLE)
2108     {
2109       /* The THROTTLE command is deprecated -- it predates the XDPMS
2110          extension.  Instead of using -throttle, users should instead
2111          just power off the monitor (e.g., "xset dpms force off".)
2112          In a few minutes, xscreensaver will notice that the monitor
2113          is off, and cease running hacks.
2114        */
2115       if (si->throttled_p)
2116         clientmessage_response (si, window, True,
2117                                 "THROTTLE ClientMessage received, but "
2118                                 "already throttled.",
2119                                 "already throttled.");
2120       else
2121         {
2122           char buf [255];
2123           char *response = "throttled.";
2124           si->throttled_p = True;
2125           si->selection_mode = 0;
2126           si->demoing_p = False;
2127           sprintf (buf, "THROTTLE ClientMessage received; %s", response);
2128           clientmessage_response (si, window, False, buf, response);
2129
2130           if (! until_idle_p)
2131             {
2132               if (si->cycle_id)
2133                 XtRemoveTimeOut (si->cycle_id);
2134               si->cycle_id = 0;
2135               cycle_timer ((XtPointer) si, 0);
2136             }
2137         }
2138     }
2139   else if (type == XA_UNTHROTTLE)
2140     {
2141       if (! si->throttled_p)
2142         clientmessage_response (si, window, True,
2143                                 "UNTHROTTLE ClientMessage received, but "
2144                                 "not throttled.",
2145                                 "not throttled.");
2146       else
2147         {
2148           char buf [255];
2149           char *response = "unthrottled.";
2150           si->throttled_p = False;
2151           si->selection_mode = 0;
2152           si->demoing_p = False;
2153           sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
2154           clientmessage_response (si, window, False, buf, response);
2155
2156           if (! until_idle_p)
2157             {
2158               if (si->cycle_id)
2159                 XtRemoveTimeOut (si->cycle_id);
2160               si->cycle_id = 0;
2161               cycle_timer ((XtPointer) si, 0);
2162             }
2163         }
2164     }
2165   else
2166     {
2167       char buf [1024];
2168       char *str;
2169       str = XGetAtomName_safe (si->dpy, type);
2170
2171       if (str)
2172         {
2173           if (strlen (str) > 80)
2174             strcpy (str+70, "...");
2175           sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2176                    str);
2177           free (str);
2178         }
2179       else
2180         {
2181           sprintf (buf,
2182                    "unrecognised screensaver ClientMessage 0x%x received.",
2183                    (unsigned int) event->xclient.data.l[0]);
2184         }
2185
2186       clientmessage_response (si, window, True, buf, buf);
2187     }
2188   return False;
2189 }
2190
2191 \f
2192 /* Some random diagnostics printed in -verbose mode.
2193  */
2194
2195 static void
2196 analyze_display (saver_info *si)
2197 {
2198   int i, j;
2199   static struct {
2200     const char *name; const char *desc; 
2201     Bool useful_p;
2202     Status (*version_fn) (Display *, int *majP, int *minP);
2203   } exts[] = {
2204
2205    { "SCREEN_SAVER", /* underscore */           "SGI Screen-Saver",
2206 #     ifdef HAVE_SGI_SAVER_EXTENSION
2207         True,  0
2208 #     else
2209         False, 0
2210 #     endif
2211    }, { "SCREEN-SAVER", /* dash */              "SGI Screen-Saver",
2212 #     ifdef HAVE_SGI_SAVER_EXTENSION
2213         True,  0
2214 #     else
2215         False, 0
2216 #     endif
2217    }, { "MIT-SCREEN-SAVER",                     "MIT Screen-Saver",
2218 #     ifdef HAVE_MIT_SAVER_EXTENSION
2219         True,  XScreenSaverQueryVersion
2220 #     else
2221         False, 0
2222 #     endif
2223    }, { "XIDLE",                                "XIdle",           
2224 #     ifdef HAVE_XIDLE_EXTENSION
2225         True,  0
2226 #     else
2227         False, 0
2228 #     endif
2229    }, { "SGI-VIDEO-CONTROL",                    "SGI Video-Control",
2230 #     ifdef HAVE_SGI_VC_EXTENSION
2231         True,  XSGIvcQueryVersion
2232 #     else
2233         False, 0
2234 #     endif
2235    }, { "READDISPLAY",                          "SGI Read-Display",
2236 #     ifdef HAVE_READ_DISPLAY_EXTENSION
2237         True,  XReadDisplayQueryVersion
2238 #     else
2239         False, 0
2240 #     endif
2241    }, { "MIT-SHM",                              "Shared Memory",   
2242 #     ifdef HAVE_XSHM_EXTENSION
2243         True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */
2244 #     else
2245         False, 0
2246 #     endif
2247    }, { "DOUBLE-BUFFER",                        "Double-Buffering",
2248 #     ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2249         True, XdbeQueryExtension
2250 #     else
2251         False, 0
2252 #     endif
2253    }, { "DPMS",                                 "Power Management",
2254 #     ifdef HAVE_DPMS_EXTENSION
2255         True,  DPMSGetVersion
2256 #     else
2257         False, 0
2258 #     endif
2259    }, { "GLX",                                  "GLX",             
2260 #     ifdef HAVE_GL
2261         True,  0
2262 #     else
2263         False, 0
2264 #     endif
2265    }, { "XFree86-VidModeExtension",             "XF86 Video-Mode", 
2266 #     ifdef HAVE_XF86VMODE
2267         True,  XF86VidModeQueryVersion
2268 #     else
2269         False, 0
2270 #     endif
2271    }, { "XC-VidModeExtension",                  "XC Video-Mode", 
2272 #     ifdef HAVE_XF86VMODE
2273         True,  XF86VidModeQueryVersion
2274 #     else
2275         False, 0
2276 #     endif
2277    }, { "XFree86-MISC",                         "XF86 Misc", 
2278 #     ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2279         True,  XF86MiscQueryVersion
2280 #     else
2281         False, 0
2282 #     endif
2283    }, { "XC-MISC",                              "XC Misc", 
2284 #     ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2285         True,  XF86MiscQueryVersion
2286 #     else
2287         False, 0
2288 #     endif
2289    }, { "XINERAMA",                             "Xinerama",
2290 #     ifdef HAVE_XINERAMA
2291         True,  XineramaQueryVersion
2292 #     else
2293         False, 0
2294 #     endif
2295    }, { "RANDR",                                "Resize-and-Rotate",
2296 #     ifdef HAVE_RANDR
2297         True,  XRRQueryVersion
2298 #     else
2299         False, 0
2300 #     endif
2301    }, { "DRI",                                  "DRI",
2302         True,  0
2303    }, { "NV-CONTROL",                           "NVidia",
2304         True,  0
2305    }, { "NV-GLX",                               "NVidia GLX",
2306         True,  0
2307    }, { "Apple-DRI",                            "Apple-DRI (XDarwin)",
2308         True,  0
2309    }, { "XInputExtension",                      "XInput",
2310         True,  0
2311    },
2312   };
2313
2314   fprintf (stderr, "%s: running on display \"%s\"\n", blurb(), 
2315            DisplayString(si->dpy));
2316   fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2317            ServerVendor(si->dpy), VendorRelease(si->dpy));
2318
2319   fprintf (stderr, "%s: useful extensions:\n", blurb());
2320   for (i = 0; i < countof(exts); i++)
2321     {
2322       int op = 0, event = 0, error = 0;
2323       char buf [255];
2324       int maj = 0, min = 0;
2325       int dummy1, dummy2, dummy3;
2326       int j;
2327
2328       /* Most of the extension version functions take 3 args,
2329          writing results into args 2 and 3, but some take more.
2330          We only ever care about the first two results, but we
2331          pass in three extra pointers just in case.
2332        */
2333       Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) =
2334         (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn;
2335
2336       if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2337         continue;
2338       sprintf (buf, "%s:   ", blurb());
2339       j = strlen (buf);
2340       strcat (buf, exts[i].desc);
2341
2342       if (!version_fn_2)
2343         ;
2344       else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3))
2345         sprintf (buf+strlen(buf), " (%d.%d)", maj, min);
2346       else
2347         strcat (buf, " (unavailable)");
2348
2349       if (!exts[i].useful_p)
2350         strcat (buf, " (disabled at compile time)");
2351       fprintf (stderr, "%s\n", buf);
2352     }
2353
2354   for (i = 0; i < si->nscreens; i++)
2355     {
2356       saver_screen_info *ssi = &si->screens[i];
2357       unsigned long colormapped_depths = 0;
2358       unsigned long non_mapped_depths = 0;
2359       XVisualInfo vi_in, *vi_out;
2360       int out_count;
2361
2362       if (!ssi->real_screen_p) continue;
2363
2364       vi_in.screen = ssi->real_screen_number;
2365       vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2366       if (!vi_out) continue;
2367       for (j = 0; j < out_count; j++) {
2368         if (vi_out[j].depth >= 32) continue;
2369         if (vi_out[j].class == PseudoColor)
2370           colormapped_depths |= (1 << vi_out[j].depth);
2371         else
2372           non_mapped_depths  |= (1 << vi_out[j].depth);
2373         }
2374       XFree ((char *) vi_out);
2375
2376       if (colormapped_depths)
2377         {
2378           fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2379                    ssi->real_screen_number);
2380           for (j = 0; j < 32; j++)
2381             if (colormapped_depths & (1 << j))
2382               fprintf (stderr, " %d", j);
2383           fprintf (stderr, ".\n");
2384         }
2385       if (non_mapped_depths)
2386         {
2387           fprintf (stderr, "%s: screen %d non-colormapped depths:",
2388                    blurb(), ssi->real_screen_number);
2389           for (j = 0; j < 32; j++)
2390             if (non_mapped_depths & (1 << j))
2391               fprintf (stderr, " %d", j);
2392           fprintf (stderr, ".\n");
2393         }
2394     }
2395
2396   describe_monitor_layout (si);
2397 }
2398
2399
2400 Bool
2401 display_is_on_console_p (saver_info *si)
2402 {
2403   Bool not_on_console = True;
2404   char *dpystr = DisplayString (si->dpy);
2405   char *tail = (char *) strchr (dpystr, ':');
2406   if (! tail || strncmp (tail, ":0", 2))
2407     not_on_console = True;
2408   else
2409     {
2410       char dpyname[255], localname[255];
2411       strncpy (dpyname, dpystr, tail-dpystr);
2412       dpyname [tail-dpystr] = 0;
2413       if (!*dpyname ||
2414           !strcmp(dpyname, "unix") ||
2415           !strcmp(dpyname, "localhost"))
2416         not_on_console = False;
2417       else if (gethostname (localname, sizeof (localname)))
2418         not_on_console = True;  /* can't find hostname? */
2419       else if (!strncmp (dpyname, "/tmp/launch-", 12))  /* MacOS X launchd */
2420         not_on_console = False;
2421       else
2422         {
2423           /* We have to call gethostbyname() on the result of gethostname()
2424              because the two aren't guarenteed to be the same name for the
2425              same host: on some losing systems, one is a FQDN and the other
2426              is not.  Here in the wide wonderful world of Unix it's rocket
2427              science to obtain the local hostname in a portable fashion.
2428              
2429              And don't forget, gethostbyname() reuses the structure it
2430              returns, so we have to copy the fucker before calling it again.
2431              Thank you master, may I have another.
2432            */
2433           struct hostent *h = gethostbyname (dpyname);
2434           if (!h)
2435             not_on_console = True;
2436           else
2437             {
2438               char hn [255];
2439               struct hostent *l;
2440               strcpy (hn, h->h_name);
2441               l = gethostbyname (localname);
2442               not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2443             }
2444         }
2445     }
2446   return !not_on_console;
2447 }
2448
2449
2450 /* Do a little bit of heap introspection...
2451  */
2452 void
2453 check_for_leaks (const char *where)
2454 {
2455 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2456   static unsigned long last_brk = 0;
2457   int b = (unsigned long) sbrk(0);
2458   if (last_brk && last_brk < b)
2459     fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2460              blurb(), where,
2461              (((b - last_brk) + 1023) / 1024));
2462   last_brk = b;
2463 #endif /* HAVE_SBRK */
2464 }