From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / driver / dpms.c
1 /* dpms.c --- syncing the X Display Power Management values
2  * xscreensaver, Copyright (c) 2001-2017 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* Display Power Management System (DPMS.)
14
15    On XFree86 systems, "man xset" reports:
16
17        -dpms    The -dpms option disables DPMS (Energy Star) features.
18        +dpms    The +dpms option enables DPMS (Energy Star) features.
19
20        dpms flags...
21                 The dpms option allows the DPMS (Energy Star)
22                 parameters to be set.  The option can take up to three
23                 numerical values, or the `force' flag followed by a
24                 DPMS state.  The `force' flags forces the server to
25                 immediately switch to the DPMS state specified.  The
26                 DPMS state can be one of `standby', `suspend', or
27                 `off'.  When numerical values are given, they set the
28                 inactivity period before the three modes are activated.
29                 The first value given is for the `standby' mode, the
30                 second is for the `suspend' mode, and the third is for
31                 the `off' mode.  Setting these values implicitly
32                 enables the DPMS features.  A value of zero disables a
33                 particular mode.
34
35    However, note that the implementation is more than a little bogus,
36    in that there is code in /usr/X11R6/lib/libXdpms.a to implement all
37    the usual server-extension-querying utilities -- but there are no
38    prototypes in any header file!  Thus, the prototypes here.  (The
39    stuff in X11/extensions/dpms.h and X11/extensions/dpmsstr.h define
40    the raw X protcol, they don't define the API to libXdpms.a.)
41
42    Some documentation:
43    Library:  ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMSLib.ms
44    Protocol: ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMS.ms
45  */
46
47 #ifdef HAVE_CONFIG_H
48 # include "config.h"
49 #endif
50
51 #include <stdio.h>
52 #include <X11/Xlib.h>
53
54 #ifdef HAVE_DPMS_EXTENSION   /* almost the whole file */
55
56 # include <X11/Xproto.h>
57 # include <X11/extensions/dpms.h>
58 /*# include <X11/extensions/dpmsstr.h>*/
59
60   /* Why this crap is not in a header file somewhere, I have no idea.  Losers!
61    */
62   extern Bool   DPMSQueryExtension (Display *, int *event_ret, int *err_ret);
63   extern Status DPMSGetVersion (Display *, int *major_ret, int *minor_ret);
64   extern Bool   DPMSCapable (Display *);
65   extern Status DPMSInfo (Display *, CARD16 *power_level, BOOL *state);
66   extern Status DPMSEnable (Display *dpy);
67   extern Status DPMSDisable (Display *dpy);
68   extern Status DPMSForceLevel (Display *, CARD16 level);
69   extern Status DPMSSetTimeouts (Display *, CARD16 standby, CARD16 suspend,
70                                  CARD16 off);
71   extern Bool   DPMSGetTimeouts (Display *, CARD16 *standby,
72                                  CARD16 *suspend, CARD16 *off);
73
74 #endif /* HAVE_DPMS_EXTENSION */
75
76
77 /* This file doesn't need the Xt headers, so stub these types out... */
78 #undef XtPointer
79 #define XtAppContext void*
80 #define XrmDatabase  void*
81 #define XtIntervalId void*
82 #define XtPointer    void*
83 #define Widget       void*
84
85 #include "xscreensaver.h"
86
87 #ifdef HAVE_DPMS_EXTENSION
88
89 static Bool error_handler_hit_p = False;
90
91 static int
92 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
93 {
94   error_handler_hit_p = True;
95   return 0;
96 }
97
98
99 void
100 sync_server_dpms_settings (Display *dpy, Bool enabled_p, Bool dpms_quickoff_p,
101                            int standby_secs, int suspend_secs, int off_secs,
102                            Bool verbose_p)
103 {
104   int event = 0, error = 0;
105   BOOL o_enabled = False;
106   CARD16 o_power = 0;
107   CARD16 o_standby = 0, o_suspend = 0, o_off = 0;
108   Bool bogus_p = False;
109
110   if (dpms_quickoff_p && !off_secs)
111   {
112     /* To do this, we might need to temporarily re-enable DPMS first. */
113     off_secs = 0xFFFF;
114   }
115
116   if (standby_secs == 0 && suspend_secs == 0 && off_secs == 0)
117     /* all zero implies "DPMS disabled" */
118     enabled_p = False;
119
120   else if ((standby_secs != 0 && standby_secs < 10) ||
121            (suspend_secs != 0 && suspend_secs < 10) ||
122            (off_secs     != 0 && off_secs     < 10))
123     /* any negative, or any positive-and-less-than-10-seconds, is crazy. */
124     bogus_p = True;
125
126   if (bogus_p) enabled_p = False;
127
128   /* X protocol sends these values in a CARD16, so truncate them to 16 bits.
129      This means that the maximum timeout is 18:12:15.
130    */
131   if (standby_secs > 0xFFFF) standby_secs = 0xFFFF;
132   if (suspend_secs > 0xFFFF) suspend_secs = 0xFFFF;
133   if (off_secs     > 0xFFFF) off_secs     = 0xFFFF;
134
135   if (! DPMSQueryExtension (dpy, &event, &error))
136     {
137       if (verbose_p)
138         fprintf (stderr, "%s: XDPMS extension not supported.\n", blurb());
139       return;
140     }
141
142   if (! DPMSCapable (dpy))
143     {
144       if (verbose_p)
145         fprintf (stderr, "%s: DPMS not supported.\n", blurb());
146       return;
147     }
148
149   if (! DPMSInfo (dpy, &o_power, &o_enabled))
150     {
151       if (verbose_p)
152         fprintf (stderr, "%s: unable to get DPMS state.\n", blurb());
153       return;
154     }
155
156   if (o_enabled != enabled_p)
157     {
158       if (! (enabled_p ? DPMSEnable (dpy) : DPMSDisable (dpy)))
159         {
160           if (verbose_p)
161             fprintf (stderr, "%s: unable to set DPMS state.\n", blurb());
162           return;
163         }
164       else if (verbose_p)
165         fprintf (stderr, "%s: turned DPMS %s.\n", blurb(),
166                  enabled_p ? "on" : "off");
167     }
168
169   if (bogus_p)
170     {
171       if (verbose_p)
172         fprintf (stderr, "%s: not setting bogus DPMS timeouts: %d %d %d.\n",
173                  blurb(), standby_secs, suspend_secs, off_secs);
174       return;
175     }
176
177   if (!DPMSGetTimeouts (dpy, &o_standby, &o_suspend, &o_off))
178     {
179       if (verbose_p)
180         fprintf (stderr, "%s: unable to get DPMS timeouts.\n", blurb());
181       return;
182     }
183
184   if (o_standby != standby_secs ||
185       o_suspend != suspend_secs ||
186       o_off != off_secs)
187     {
188       if (!DPMSSetTimeouts (dpy, standby_secs, suspend_secs, off_secs))
189         {
190           if (verbose_p)
191             fprintf (stderr, "%s: unable to set DPMS timeouts.\n", blurb());
192           return;
193         }
194       else if (verbose_p)
195         fprintf (stderr, "%s: set DPMS timeouts: %d %d %d.\n", blurb(),
196                  standby_secs, suspend_secs, off_secs);
197     }
198 }
199
200 Bool
201 monitor_powered_on_p (saver_info *si)
202 {
203   Bool result;
204   int event_number, error_number;
205   BOOL onoff = False;
206   CARD16 state;
207
208   if (!DPMSQueryExtension(si->dpy, &event_number, &error_number))
209     /* Server doesn't know -- assume the monitor is on. */
210     result = True;
211
212   else if (!DPMSCapable(si->dpy))
213     /* Server says the monitor doesn't do power management -- so it's on. */
214     result = True;
215
216   else
217     {
218       DPMSInfo(si->dpy, &state, &onoff);
219       if (!onoff)
220         /* Server says DPMS is disabled -- so the monitor is on. */
221         result = True;
222       else
223         switch (state) {
224         case DPMSModeOn:      result = True;  break;  /* really on */
225         case DPMSModeStandby: result = False; break;  /* kinda off */
226         case DPMSModeSuspend: result = False; break;  /* pretty off */
227         case DPMSModeOff:     result = False; break;  /* really off */
228         default:              result = True;  break;  /* protocol error? */
229         }
230     }
231
232   return result;
233 }
234
235 void
236 monitor_power_on (saver_info *si, Bool on_p)
237 {
238   if ((!!on_p) != monitor_powered_on_p (si))
239     {
240       XErrorHandler old_handler;
241       int event_number, error_number;
242       if (!DPMSQueryExtension(si->dpy, &event_number, &error_number) ||
243           !DPMSCapable(si->dpy))
244         {
245           if (si->prefs.verbose_p)
246             fprintf (stderr,
247                      "%s: unable to power %s monitor: no DPMS extension.\n",
248                      blurb(), (on_p ? "on" : "off"));
249           return;
250         }
251
252       /* The manual for DPMSForceLevel() says that it throws BadMatch if
253          "DPMS is disabled on the specified display."
254
255          The manual for DPMSCapable() says that it "returns True if the X
256          server is capable of DPMS."
257
258          Apparently they consider "capable of DPMS" and "DPMS is enabled"
259          to be different things, and so even if DPMSCapable() returns
260          True, DPMSForceLevel() *might* throw an X Error.  Isn't that
261          just fucking special.
262        */
263       XSync (si->dpy, False);
264       error_handler_hit_p = False;
265       old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
266       XSync (si->dpy, False);
267       DPMSForceLevel(si->dpy, (on_p ? DPMSModeOn : DPMSModeOff));
268       XSync (si->dpy, False);
269       XSetErrorHandler (old_handler);
270       /* Ignore error_handler_hit_p, just probe monitor instead */
271
272       if ((!!on_p) != monitor_powered_on_p (si))  /* double-check */
273         fprintf (stderr,
274        "%s: DPMSForceLevel(dpy, %s) did not change monitor power state.\n",
275                  blurb(),
276                  (on_p ? "DPMSModeOn" : "DPMSModeOff"));
277     }
278 }
279
280 #else  /* !HAVE_DPMS_EXTENSION */
281
282 void
283 sync_server_dpms_settings (Display *dpy, Bool enabled_p,
284                            int standby_secs, int suspend_secs, int off_secs,
285                            Bool verbose_p)
286 {
287   if (verbose_p)
288     fprintf (stderr, "%s: DPMS support not compiled in.\n", blurb());
289 }
290
291 Bool
292 monitor_powered_on_p (saver_info *si) 
293 {
294   return True; 
295 }
296
297 void
298 monitor_power_on (saver_info *si, Bool on_p)
299 {
300   return; 
301 }
302
303 #endif /* !HAVE_DPMS_EXTENSION */