From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / driver / xscreensaver.c
index 2fb1ecd31727f6d4e2f191f0917c54770d9a17c6..e9ed44e31d5dbf0955a8c7487fe194eb5a28708b 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2013 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
 #include <ctype.h>
 #include <X11/Xlib.h>
 
+#ifdef ENABLE_NLS
+# include <locale.h>
+# include <libintl.h>
+#endif /* ENABLE_NLS */
+
 #include <X11/Xlibint.h>
 
 #include <X11/Xatom.h>
 # include "xmu.h"
 #endif /* !HAVE_XMU */
 
+#ifdef HAVE_MIT_SAVER_EXTENSION
+#include <X11/extensions/scrnsaver.h>
+#endif /* HAVE_MIT_SAVER_EXTENSION */
+
 #ifdef HAVE_XIDLE_EXTENSION
 # include <X11/extensions/xidle.h>
 #endif /* HAVE_XIDLE_EXTENSION */
 
+#ifdef HAVE_SGI_VC_EXTENSION
+# include <X11/extensions/XSGIvc.h>
+#endif /* HAVE_SGI_VC_EXTENSION */
+
+#ifdef HAVE_READ_DISPLAY_EXTENSION
+# include <X11/extensions/readdisplay.h>
+#endif /* HAVE_READ_DISPLAY_EXTENSION */
+
+#ifdef HAVE_XSHM_EXTENSION
+# include <X11/extensions/XShm.h>
+#endif /* HAVE_XSHM_EXTENSION */
+
+#ifdef HAVE_DPMS_EXTENSION
+# include <X11/extensions/dpms.h>
+#endif /* HAVE_DPMS_EXTENSION */
+
+
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+# include <X11/extensions/Xdbe.h>
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+
+#ifdef HAVE_XF86VMODE
+# include <X11/extensions/xf86vmode.h>
+#endif /* HAVE_XF86VMODE */
+
+#ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+# include <X11/extensions/xf86misc.h>
+#endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
+
 #ifdef HAVE_XINERAMA
 # include <X11/extensions/Xinerama.h>
 #endif /* HAVE_XINERAMA */
 
+#ifdef HAVE_RANDR
+# include <X11/extensions/Xrandr.h>
+#endif /* HAVE_RANDR */
+
+
 #include "xscreensaver.h"
 #include "version.h"
 #include "yarandom.h"
@@ -228,17 +271,24 @@ ERROR!  You must not include vroot.h in this file.
 static void
 do_help (saver_info *si)
 {
+  char *s, year[5];
+  s = strchr (screensaver_id, '-');
+  s = strrchr (s, '-');
+  s++;
+  strncpy (year, s, 4);
+  year[4] = 0;
+
   fflush (stdout);
   fflush (stderr);
   fprintf (stdout, "\
-xscreensaver %s, copyright (c) 1991-2008 by Jamie Zawinski <jwz@jwz.org>\n\
+xscreensaver %s, copyright (c) 1991-%s by Jamie Zawinski <jwz@jwz.org>\n\
 \n\
   All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
   Rather than editing that file by hand, just run `xscreensaver-demo':\n\
   that program lets you configure the screen saver graphically,\n\
   including timeouts, locking, and display modes.\n\
 \n",
-         si->version);
+         si->version, year);
   fprintf (stdout, "\
   Just getting started?  Try this:\n\
 \n\
@@ -256,14 +306,28 @@ xscreensaver %s, copyright (c) 1991-2008 by Jamie Zawinski <jwz@jwz.org>\n\
 }
 
 
+Bool in_signal_handler_p = 0;  /* I hate C so much... */
+
 char *
 timestring (void)
 {
-  time_t now = time ((time_t *) 0);
-  char *str = (char *) ctime (&now);
-  char *nl = (char *) strchr (str, '\n');
-  if (nl) *nl = 0; /* take off that dang newline */
-  return str;
+  if (in_signal_handler_p)
+    {
+      /* Turns out that ctime() and even localtime_r() call malloc() on Linux!
+         So we can't call them from inside SIGCHLD.  WTF.
+       */
+      static char buf[30];
+      strcpy (buf, "... ... ..   signal ....");
+      return buf;
+    }
+  else
+    {
+      time_t now = time ((time_t *) 0);
+      char *str = (char *) ctime (&now);
+      char *nl = (char *) strchr (str, '\n');
+      if (nl) *nl = 0; /* take off that dang newline */
+      return str;
+    }
 }
 
 static Bool blurb_timestamp_p = True;   /* kludge */
@@ -536,6 +600,9 @@ lock_initialization (saver_info *si, int *argc, char **argv)
         }
     }
 
+  if (si->prefs.debug_p)    /* But allow locking anyway in debug mode. */
+    si->locking_disabled_p = False;
+
 #endif /* NO_LOCKING */
 }
 
@@ -680,6 +747,13 @@ print_banner (saver_info *si)
 {
   saver_preferences *p = &si->prefs;
 
+  char *s, year[5];
+  s = strchr (screensaver_id, '-');
+  s = strrchr (s, '-');
+  s++;
+  strncpy (year, s, 4);
+  year[4] = 0;
+
   /* This resource gets set some time before the others, so that we know
      whether to print the banner (and so that the banner gets printed before
      any resource-database-related error messages.)
@@ -692,9 +766,9 @@ print_banner (saver_info *si)
 
   if (p->verbose_p)
     fprintf (stderr,
-            "%s %s, copyright (c) 1991-2008 "
+            "%s %s, copyright (c) 1991-%s "
             "by Jamie Zawinski <jwz@jwz.org>.\n",
-            progname, si->version);
+            progname, si->version, year);
 
   if (p->debug_p)
     fprintf (stderr, "\n"
@@ -710,6 +784,17 @@ print_banner (saver_info *si)
             "\n",
             blurb());
 
+  if (p->verbose_p && senescent_p ())
+    fprintf (stderr, "\n"
+             "*************************************"
+             "**************************************\n"
+            "%s: Warning: this version of xscreensaver is VERY OLD!\n"
+            "%s: Please upgrade!  http://www.jwz.org/xscreensaver/\n"
+             "*************************************"
+             "**************************************\n"
+            "\n",
+             blurb(), blurb());
+
   if (p->verbose_p)
     {
       if (!si->uid_message || !*si->uid_message)
@@ -819,26 +904,41 @@ initialize_server_extensions (saver_info *si)
   Bool server_has_sgi_saver_extension_p = False;
   Bool server_has_mit_saver_extension_p = False;
   Bool system_has_proc_interrupts_p = False;
+  Bool server_has_xinput_extension_p = False;
   const char *piwhy = 0;
 
   si->using_xidle_extension = p->use_xidle_extension;
   si->using_sgi_saver_extension = p->use_sgi_saver_extension;
   si->using_mit_saver_extension = p->use_mit_saver_extension;
   si->using_proc_interrupts = p->use_proc_interrupts;
+  si->using_xinput_extension = p->use_xinput_extension;
 
 #ifdef HAVE_XIDLE_EXTENSION
-  server_has_xidle_extension_p = query_xidle_extension (si);
+  {
+    int ev, er;
+    server_has_xidle_extension_p = XidleQueryExtension (si->dpy, &ev, &er);
+  }
 #endif
 #ifdef HAVE_SGI_SAVER_EXTENSION
-  server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
+  server_has_sgi_saver_extension_p =
+    XScreenSaverQueryExtension (si->dpy,
+                                &si->sgi_saver_ext_event_number,
+                                &si->sgi_saver_ext_error_number);
 #endif
 #ifdef HAVE_MIT_SAVER_EXTENSION
-  server_has_mit_saver_extension_p = query_mit_saver_extension (si);
+  server_has_mit_saver_extension_p =
+    XScreenSaverQueryExtension (si->dpy,
+                                &si->mit_saver_ext_event_number,
+                                &si->mit_saver_ext_error_number);
 #endif
 #ifdef HAVE_PROC_INTERRUPTS
   system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
 #endif
 
+#ifdef HAVE_XINPUT
+  server_has_xinput_extension_p = query_xinput_extension (si);
+#endif
+
   if (!server_has_xidle_extension_p)
     si->using_xidle_extension = False;
   else if (p->verbose_p)
@@ -875,7 +975,46 @@ initialize_server_extensions (saver_info *si)
     }
 
 #ifdef HAVE_RANDR
-  query_randr_extension (si);
+  if (XRRQueryExtension (si->dpy,
+                         &si->randr_event_number, &si->randr_error_number))
+    {
+      int nscreens = ScreenCount (si->dpy);  /* number of *real* screens */
+      int i;
+
+      si->using_randr_extension = TRUE;
+
+      if (p->verbose_p)
+       fprintf (stderr, "%s: selecting RANDR events\n", blurb());
+      for (i = 0; i < nscreens; i++)
+#  ifdef RRScreenChangeNotifyMask                 /* randr.h 1.5, 2002/09/29 */
+        XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
+                        RRScreenChangeNotifyMask);
+#  else  /* !RRScreenChangeNotifyMask */          /* Xrandr.h 1.4, 2001/06/07 */
+        XRRScreenChangeSelectInput (si->dpy, RootWindow (si->dpy, i), True);
+#  endif /* !RRScreenChangeNotifyMask */
+    }
+# endif /* HAVE_RANDR */
+
+#ifdef HAVE_XINPUT
+  if (!server_has_xinput_extension_p)
+    si->using_xinput_extension = False;
+  else
+    {
+      if (si->using_xinput_extension)
+        init_xinput_extension(si);
+
+      if (p->verbose_p)
+        {
+          if (si->using_xinput_extension)
+            fprintf (stderr,
+                     "%s: selecting events from %d XInputExtension devices.\n",
+                     blurb(), si->num_xinput_devices);
+          else
+            fprintf (stderr,
+                     "%s: not using XInputExtension.\n",
+                     blurb());
+        }
+    }
 #endif
 
   if (!system_has_proc_interrupts_p)
@@ -1091,6 +1230,10 @@ main_loop (saver_info *si)
              we would never be able to un-blank it!  We would never
              see any events, and the display would be wedged.
 
+             In particular, without that keyboard grab, we will be
+             unable to ever read keypresses on the unlock dialog.
+             You can't unlock if you can't type your password.
+
              So, just go around the loop again and wait for the
              next bout of idleness.  (If the user remains idle, we
              will next try to blank the screen again in no more than
@@ -1127,6 +1270,23 @@ main_loop (saver_info *si)
         for (i = 0; i < si->nscreens; i++)
           spawn_screenhack (&si->screens[i]);
 
+      /* If we are blanking only, optionally power down monitor right now.
+         To do this, we might need to temporarily re-enable DPMS first.
+       */
+      if (p->mode == BLANK_ONLY &&
+          p->dpms_enabled_p && 
+          p->dpms_quickoff_p)
+        {
+          sync_server_dpms_settings (si->dpy, True,
+                                     p->dpms_standby / 1000,
+                                     p->dpms_suspend / 1000,
+                                     (p->dpms_off
+                                      ? (p->dpms_off / 1000)
+                                      : 0xFFFF),
+                                     False);
+          monitor_power_on (si, False);
+        }
+
       /* Don't start the cycle timer in demo mode. */
       if (!si->demoing_p && p->cycle)
        si->cycle_id = XtAppAddTimeOut (si->app,
@@ -1282,6 +1442,33 @@ main (int argc, char **argv)
   struct passwd *spasswd;
   int i;
 
+  /* It turns out that if we do setlocale (LC_ALL, "") here, people
+     running in Japanese locales get font craziness on the password
+     dialog, presumably because it is displaying Japanese characters
+     in a non-Japanese font.  However, if we don't call setlocale()
+     at all, then XLookupString() never returns multi-byte UTF-8
+     characters when people type non-Latin1 characters on the
+     keyboard.
+
+     The current theory (and at this point, I'm really guessing!) is
+     that using LC_CTYPE instead of LC_ALL will make XLookupString()
+     behave usefully, without having the side-effect of screwing up
+     the fonts on the unlock dialog.
+
+     See https://bugs.launchpad.net/ubuntu/+source/xscreensaver/+bug/671923
+     from comment #20 onward.
+
+       -- jwz, 24-Sep-2011
+   */
+#ifdef ENABLE_NLS
+  if (!setlocale (LC_CTYPE, ""))
+    fprintf (stderr, "%s: warning: could not set default locale\n",
+             progname);
+
+  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+  textdomain (GETTEXT_PACKAGE);
+#endif /* ENABLE_NLS */
+
   memset(si, 0, sizeof(*si));
   global_si_kludge = si;       /* I hate C so much... */
 
@@ -1615,29 +1802,40 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
     }
   else if (type == XA_DEACTIVATE)
     {
-      if (! until_idle_p)
-       {
-          if (si->throttled_p && p->verbose_p)
-            fprintf (stderr, "%s: unthrottled.\n", blurb());
-         si->throttled_p = False;
+      if (si->locked_p) 
+        {
+          clientmessage_response(si, window, False,
+              "DEACTIVATE ClientMessage received while locked: ignored.",
+              "screen is locked.");
+        }
+      else
+        {
+          if (! until_idle_p)
+            {
+              if (si->throttled_p && p->verbose_p)
+                fprintf (stderr, "%s: unthrottled.\n", blurb());
+              si->throttled_p = False;
 
-         clientmessage_response(si, window, False,
-                                "DEACTIVATE ClientMessage received.",
-                                "deactivating.");
-         if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
-           {
-             XForceScreenSaver (si->dpy, ScreenSaverReset);
-             return False;
-           }
-         else
-           {
-             return True;
-           }
-       }
-      clientmessage_response(si, window, False,
-     "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
-                            "not active: idle timer reset.");
-      reset_timers (si);
+              clientmessage_response(si, window, False,
+                                     "DEACTIVATE ClientMessage received.",
+                                     "deactivating.");
+              if (si->using_mit_saver_extension ||
+                  si->using_sgi_saver_extension)
+                {
+                  XForceScreenSaver (si->dpy, ScreenSaverReset);
+                  return False;
+                }
+              else
+                {
+                  return True;
+                }
+            }
+          clientmessage_response(si, window, False,
+                          "ClientMessage DEACTIVATE received while inactive: "
+                          "resetting idle timer.",
+                                 "not active: idle timer reset.");
+          reset_timers (si);
+        }
     }
   else if (type == XA_CYCLE)
     {
@@ -1959,91 +2157,117 @@ analyze_display (saver_info *si)
 {
   int i, j;
   static struct {
-    const char *name; const char *desc; Bool useful_p;
+    const char *name; const char *desc; 
+    Bool useful_p;
+    Status (*version_fn) (Display *, int *majP, int *minP);
   } exts[] = {
 
    { "SCREEN_SAVER", /* underscore */           "SGI Screen-Saver",
 #     ifdef HAVE_SGI_SAVER_EXTENSION
-        True
+        True,  0
 #     else
-        False
+        False, 0
 #     endif
    }, { "SCREEN-SAVER", /* dash */              "SGI Screen-Saver",
 #     ifdef HAVE_SGI_SAVER_EXTENSION
-        True
+        True,  0
 #     else
-        False
+        False, 0
 #     endif
    }, { "MIT-SCREEN-SAVER",                     "MIT Screen-Saver",
 #     ifdef HAVE_MIT_SAVER_EXTENSION
-        True
+        True,  XScreenSaverQueryVersion
 #     else
-        False
+        False, 0
 #     endif
    }, { "XIDLE",                                "XIdle",           
 #     ifdef HAVE_XIDLE_EXTENSION
-        True
+        True,  0
 #     else
-        False
+        False, 0
 #     endif
    }, { "SGI-VIDEO-CONTROL",                    "SGI Video-Control",
 #     ifdef HAVE_SGI_VC_EXTENSION
-        True
+        True,  XSGIvcQueryVersion
 #     else
-        False
+        False, 0
 #     endif
    }, { "READDISPLAY",                          "SGI Read-Display",
 #     ifdef HAVE_READ_DISPLAY_EXTENSION
-        True
+        True,  XReadDisplayQueryVersion
 #     else
-        False
+        False, 0
 #     endif
    }, { "MIT-SHM",                              "Shared Memory",   
 #     ifdef HAVE_XSHM_EXTENSION
-        True
+        True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */
 #     else
-        False
+        False, 0
 #     endif
    }, { "DOUBLE-BUFFER",                        "Double-Buffering",
 #     ifdef HAVE_DOUBLE_BUFFER_EXTENSION
-        True
+        True, XdbeQueryExtension
 #     else
-        False
+        False, 0
 #     endif
    }, { "DPMS",                                 "Power Management",
 #     ifdef HAVE_DPMS_EXTENSION
-        True
+        True,  DPMSGetVersion
 #     else
-        False
+        False, 0
 #     endif
    }, { "GLX",                                  "GLX",             
 #     ifdef HAVE_GL
-        True
+        True,  0
 #     else
-        False
+        False, 0
 #     endif
    }, { "XFree86-VidModeExtension",             "XF86 Video-Mode", 
 #     ifdef HAVE_XF86VMODE
-        True
+        True,  XF86VidModeQueryVersion
+#     else
+        False, 0
+#     endif
+   }, { "XC-VidModeExtension",                  "XC Video-Mode", 
+#     ifdef HAVE_XF86VMODE
+        True,  XF86VidModeQueryVersion
+#     else
+        False, 0
+#     endif
+   }, { "XFree86-MISC",                         "XF86 Misc", 
+#     ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+        True,  XF86MiscQueryVersion
+#     else
+        False, 0
+#     endif
+   }, { "XC-MISC",                              "XC Misc", 
+#     ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+        True,  XF86MiscQueryVersion
 #     else
-        False
+        False, 0
 #     endif
    }, { "XINERAMA",                             "Xinerama",
 #     ifdef HAVE_XINERAMA
-        True
+        True,  XineramaQueryVersion
 #     else
-        False
+        False, 0
 #     endif
    }, { "RANDR",                                "Resize-and-Rotate",
 #     ifdef HAVE_RANDR
-        True
+        True,  XRRQueryVersion
 #     else
-        False
+        False, 0
 #     endif
    }, { "DRI",                                 "DRI",
-        True
+        True,  0
+   }, { "NV-CONTROL",                           "NVidia",
+        True,  0
+   }, { "NV-GLX",                               "NVidia GLX",
+        True,  0
    }, { "Apple-DRI",                            "Apple-DRI (XDarwin)",
-        True
+        True,  0
+   }, { "XInputExtension",                      "XInput",
+        True,  0
    },
   };
 
@@ -2057,12 +2281,31 @@ analyze_display (saver_info *si)
     {
       int op = 0, event = 0, error = 0;
       char buf [255];
+      int maj = 0, min = 0;
+      int dummy1, dummy2, dummy3;
       int j;
+
+      /* Most of the extension version functions take 3 args,
+         writing results into args 2 and 3, but some take more.
+         We only ever care about the first two results, but we
+         pass in three extra pointers just in case.
+       */
+      Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) =
+        (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn;
+
       if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
         continue;
       sprintf (buf, "%s:   ", blurb());
       j = strlen (buf);
       strcat (buf, exts[i].desc);
+
+      if (!version_fn_2)
+        ;
+      else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3))
+        sprintf (buf+strlen(buf), " (%d.%d)", maj, min);
+      else
+        strcat (buf, " (unavailable)");
+
       if (!exts[i].useful_p)
         strcat (buf, " (disabled at compile time)");
       fprintf (stderr, "%s\n", buf);