From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / driver / dpms.c
index 570c2348083f1d11f02ac24413252ae03c55681a..553e476ee0886597ff6bfd4453a0e6c61732d1ad 100644 (file)
@@ -1,5 +1,5 @@
 /* dpms.c --- syncing the X Display Power Management values
- * xscreensaver, Copyright (c) 2001 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 2001-2017 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
  * implied warranty.
  */
 
+/* Display Power Management System (DPMS.)
+
+   On XFree86 systems, "man xset" reports:
+
+       -dpms    The -dpms option disables DPMS (Energy Star) features.
+       +dpms    The +dpms option enables DPMS (Energy Star) features.
+
+       dpms flags...
+                The dpms option allows the DPMS (Energy Star)
+                parameters to be set.  The option can take up to three
+                numerical values, or the `force' flag followed by a
+                DPMS state.  The `force' flags forces the server to
+                immediately switch to the DPMS state specified.  The
+                DPMS state can be one of `standby', `suspend', or
+                `off'.  When numerical values are given, they set the
+                inactivity period before the three modes are activated.
+                The first value given is for the `standby' mode, the
+                second is for the `suspend' mode, and the third is for
+                the `off' mode.  Setting these values implicitly
+                enables the DPMS features.  A value of zero disables a
+                particular mode.
+
+   However, note that the implementation is more than a little bogus,
+   in that there is code in /usr/X11R6/lib/libXdpms.a to implement all
+   the usual server-extension-querying utilities -- but there are no
+   prototypes in any header file!  Thus, the prototypes here.  (The
+   stuff in X11/extensions/dpms.h and X11/extensions/dpmsstr.h define
+   the raw X protcol, they don't define the API to libXdpms.a.)
+
+   Some documentation:
+   Library:  ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMSLib.ms
+   Protocol: ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMS.ms
+ */
+
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
 #include <stdio.h>
 #include <X11/Xlib.h>
 
-#ifdef HAVE_DPMS_EXTENSION
+#ifdef HAVE_DPMS_EXTENSION   /* almost the whole file */
 
 # include <X11/Xproto.h>
 # include <X11/extensions/dpms.h>
-# include <X11/extensions/dpmsstr.h>
+/*# include <X11/extensions/dpmsstr.h>*/
 
   /* Why this crap is not in a header file somewhere, I have no idea.  Losers!
    */
-  extern Bool DPMSQueryExtension (Display *dpy, int *event_ret, int *err_ret);
-  extern Bool DPMSCapable (Display *dpy);
-  extern Status DPMSInfo (Display *dpy, CARD16 *power_level, BOOL *state);
-  extern Status DPMSSetTimeouts (Display *dpy,
-                                 CARD16 standby, CARD16 suspend, CARD16 off);
-  extern Bool DPMSGetTimeouts (Display *dpy,
-                               CARD16 *standby, CARD16 *suspend, CARD16 *off);
+  extern Bool   DPMSQueryExtension (Display *, int *event_ret, int *err_ret);
+  extern Status DPMSGetVersion (Display *, int *major_ret, int *minor_ret);
+  extern Bool   DPMSCapable (Display *);
+  extern Status DPMSInfo (Display *, CARD16 *power_level, BOOL *state);
   extern Status DPMSEnable (Display *dpy);
   extern Status DPMSDisable (Display *dpy);
+  extern Status DPMSForceLevel (Display *, CARD16 level);
+  extern Status DPMSSetTimeouts (Display *, CARD16 standby, CARD16 suspend,
+                                 CARD16 off);
+  extern Bool   DPMSGetTimeouts (Display *, CARD16 *standby,
+                                 CARD16 *suspend, CARD16 *off);
 
 #endif /* HAVE_DPMS_EXTENSION */
 
 
 #include "xscreensaver.h"
 
+#ifdef HAVE_DPMS_EXTENSION
+
+static Bool error_handler_hit_p = False;
+
+static int
+ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
+{
+  error_handler_hit_p = True;
+  return 0;
+}
+
+
 void
-sync_server_dpms_settings (Display *dpy, Bool enabled_p,
+sync_server_dpms_settings (Display *dpy, Bool enabled_p, Bool dpms_quickoff_p,
                            int standby_secs, int suspend_secs, int off_secs,
                            Bool verbose_p)
 {
-# ifdef HAVE_DPMS_EXTENSION
-
   int event = 0, error = 0;
   BOOL o_enabled = False;
   CARD16 o_power = 0;
   CARD16 o_standby = 0, o_suspend = 0, o_off = 0;
+  Bool bogus_p = False;
+
+  if (dpms_quickoff_p && !off_secs)
+  {
+    /* To do this, we might need to temporarily re-enable DPMS first. */
+    off_secs = 0xFFFF;
+  }
 
-  Bool bogus_p = (standby_secs == 0 || suspend_secs == 0 || off_secs == 0);
+  if (standby_secs == 0 && suspend_secs == 0 && off_secs == 0)
+    /* all zero implies "DPMS disabled" */
+    enabled_p = False;
+
+  else if ((standby_secs != 0 && standby_secs < 10) ||
+           (suspend_secs != 0 && suspend_secs < 10) ||
+           (off_secs     != 0 && off_secs     < 10))
+    /* any negative, or any positive-and-less-than-10-seconds, is crazy. */
+    bogus_p = True;
 
   if (bogus_p) enabled_p = False;
 
+  /* X protocol sends these values in a CARD16, so truncate them to 16 bits.
+     This means that the maximum timeout is 18:12:15.
+   */
+  if (standby_secs > 0xFFFF) standby_secs = 0xFFFF;
+  if (suspend_secs > 0xFFFF) suspend_secs = 0xFFFF;
+  if (off_secs     > 0xFFFF) off_secs     = 0xFFFF;
+
   if (! DPMSQueryExtension (dpy, &event, &error))
     {
       if (verbose_p)
@@ -127,11 +195,109 @@ sync_server_dpms_settings (Display *dpy, Bool enabled_p,
         fprintf (stderr, "%s: set DPMS timeouts: %d %d %d.\n", blurb(),
                  standby_secs, suspend_secs, off_secs);
     }
+}
+
+Bool
+monitor_powered_on_p (saver_info *si)
+{
+  Bool result;
+  int event_number, error_number;
+  BOOL onoff = False;
+  CARD16 state;
+
+  if (!DPMSQueryExtension(si->dpy, &event_number, &error_number))
+    /* Server doesn't know -- assume the monitor is on. */
+    result = True;
+
+  else if (!DPMSCapable(si->dpy))
+    /* Server says the monitor doesn't do power management -- so it's on. */
+    result = True;
+
+  else
+    {
+      DPMSInfo(si->dpy, &state, &onoff);
+      if (!onoff)
+       /* Server says DPMS is disabled -- so the monitor is on. */
+       result = True;
+      else
+       switch (state) {
+       case DPMSModeOn:      result = True;  break;  /* really on */
+       case DPMSModeStandby: result = False; break;  /* kinda off */
+       case DPMSModeSuspend: result = False; break;  /* pretty off */
+       case DPMSModeOff:     result = False; break;  /* really off */
+       default:              result = True;  break;  /* protocol error? */
+       }
+    }
+
+  return result;
+}
+
+void
+monitor_power_on (saver_info *si, Bool on_p)
+{
+  if ((!!on_p) != monitor_powered_on_p (si))
+    {
+      XErrorHandler old_handler;
+      int event_number, error_number;
+      if (!DPMSQueryExtension(si->dpy, &event_number, &error_number) ||
+          !DPMSCapable(si->dpy))
+        {
+          if (si->prefs.verbose_p)
+            fprintf (stderr,
+                     "%s: unable to power %s monitor: no DPMS extension.\n",
+                     blurb(), (on_p ? "on" : "off"));
+          return;
+        }
+
+      /* The manual for DPMSForceLevel() says that it throws BadMatch if
+         "DPMS is disabled on the specified display."
+
+         The manual for DPMSCapable() says that it "returns True if the X
+         server is capable of DPMS."
+
+         Apparently they consider "capable of DPMS" and "DPMS is enabled"
+         to be different things, and so even if DPMSCapable() returns
+         True, DPMSForceLevel() *might* throw an X Error.  Isn't that
+         just fucking special.
+       */
+      XSync (si->dpy, False);
+      error_handler_hit_p = False;
+      old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+      XSync (si->dpy, False);
+      DPMSForceLevel(si->dpy, (on_p ? DPMSModeOn : DPMSModeOff));
+      XSync (si->dpy, False);
+      XSetErrorHandler (old_handler);
+      /* Ignore error_handler_hit_p, just probe monitor instead */
+
+      if ((!!on_p) != monitor_powered_on_p (si))  /* double-check */
+       fprintf (stderr,
+       "%s: DPMSForceLevel(dpy, %s) did not change monitor power state.\n",
+                blurb(),
+                 (on_p ? "DPMSModeOn" : "DPMSModeOff"));
+    }
+}
 
-# else  /* !HAVE_DPMS_EXTENSION */
+#else  /* !HAVE_DPMS_EXTENSION */
 
+void
+sync_server_dpms_settings (Display *dpy, Bool enabled_p,
+                           int standby_secs, int suspend_secs, int off_secs,
+                           Bool verbose_p)
+{
   if (verbose_p)
     fprintf (stderr, "%s: DPMS support not compiled in.\n", blurb());
+}
 
-# endif /* HAVE_DPMS_EXTENSION */
+Bool
+monitor_powered_on_p (saver_info *si) 
+{
+  return True; 
 }
+
+void
+monitor_power_on (saver_info *si, Bool on_p)
+{
+  return; 
+}
+
+#endif /* !HAVE_DPMS_EXTENSION */