d88737eefd98340fe9412a6ce4f03f4c2179aec8
[xscreensaver] / driver / dpms.c
1 /* dpms.c --- syncing the X Display Power Management values
2  * xscreensaver, Copyright (c) 2001-2011 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,
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 (standby_secs == 0 && suspend_secs == 0 && off_secs == 0)
111     /* all zero implies "DPMS disabled" */
112     enabled_p = False;
113
114   else if ((standby_secs != 0 && standby_secs < 10) ||
115            (suspend_secs != 0 && suspend_secs < 10) ||
116            (off_secs     != 0 && off_secs     < 10))
117     /* any negative, or any positive-and-less-than-10-seconds, is crazy. */
118     bogus_p = True;
119
120   if (bogus_p) enabled_p = False;
121
122   /* X protocol sends these values in a CARD16, so truncate them to 16 bits.
123      This means that the maximum timeout is 18:12:15.
124    */
125   if (standby_secs > 0xFFFF) standby_secs = 0xFFFF;
126   if (suspend_secs > 0xFFFF) suspend_secs = 0xFFFF;
127   if (off_secs     > 0xFFFF) off_secs     = 0xFFFF;
128
129   if (! DPMSQueryExtension (dpy, &event, &error))
130     {
131       if (verbose_p)
132         fprintf (stderr, "%s: XDPMS extension not supported.\n", blurb());
133       return;
134     }
135
136   if (! DPMSCapable (dpy))
137     {
138       if (verbose_p)
139         fprintf (stderr, "%s: DPMS not supported.\n", blurb());
140       return;
141     }
142
143   if (! DPMSInfo (dpy, &o_power, &o_enabled))
144     {
145       if (verbose_p)
146         fprintf (stderr, "%s: unable to get DPMS state.\n", blurb());
147       return;
148     }
149
150   if (o_enabled != enabled_p)
151     {
152       if (! (enabled_p ? DPMSEnable (dpy) : DPMSDisable (dpy)))
153         {
154           if (verbose_p)
155             fprintf (stderr, "%s: unable to set DPMS state.\n", blurb());
156           return;
157         }
158       else if (verbose_p)
159         fprintf (stderr, "%s: turned DPMS %s.\n", blurb(),
160                  enabled_p ? "on" : "off");
161     }
162
163   if (bogus_p)
164     {
165       if (verbose_p)
166         fprintf (stderr, "%s: not setting bogus DPMS timeouts: %d %d %d.\n",
167                  blurb(), standby_secs, suspend_secs, off_secs);
168       return;
169     }
170
171   if (!DPMSGetTimeouts (dpy, &o_standby, &o_suspend, &o_off))
172     {
173       if (verbose_p)
174         fprintf (stderr, "%s: unable to get DPMS timeouts.\n", blurb());
175       return;
176     }
177
178   if (o_standby != standby_secs ||
179       o_suspend != suspend_secs ||
180       o_off != off_secs)
181     {
182       if (!DPMSSetTimeouts (dpy, standby_secs, suspend_secs, off_secs))
183         {
184           if (verbose_p)
185             fprintf (stderr, "%s: unable to set DPMS timeouts.\n", blurb());
186           return;
187         }
188       else if (verbose_p)
189         fprintf (stderr, "%s: set DPMS timeouts: %d %d %d.\n", blurb(),
190                  standby_secs, suspend_secs, off_secs);
191     }
192 }
193
194 Bool
195 monitor_powered_on_p (saver_info *si)
196 {
197   Bool result;
198   int event_number, error_number;
199   BOOL onoff = False;
200   CARD16 state;
201
202   if (!DPMSQueryExtension(si->dpy, &event_number, &error_number))
203     /* Server doesn't know -- assume the monitor is on. */
204     result = True;
205
206   else if (!DPMSCapable(si->dpy))
207     /* Server says the monitor doesn't do power management -- so it's on. */
208     result = True;
209
210   else
211     {
212       DPMSInfo(si->dpy, &state, &onoff);
213       if (!onoff)
214         /* Server says DPMS is disabled -- so the monitor is on. */
215         result = True;
216       else
217         switch (state) {
218         case DPMSModeOn:      result = True;  break;  /* really on */
219         case DPMSModeStandby: result = False; break;  /* kinda off */
220         case DPMSModeSuspend: result = False; break;  /* pretty off */
221         case DPMSModeOff:     result = False; break;  /* really off */
222         default:              result = True;  break;  /* protocol error? */
223         }
224     }
225
226   return result;
227 }
228
229 void
230 monitor_power_on (saver_info *si, Bool on_p)
231 {
232   if ((!!on_p) != monitor_powered_on_p (si))
233     {
234       XErrorHandler old_handler;
235       int event_number, error_number;
236       if (!DPMSQueryExtension(si->dpy, &event_number, &error_number) ||
237           !DPMSCapable(si->dpy))
238         {
239           if (si->prefs.verbose_p)
240             fprintf (stderr,
241                      "%s: unable to power %s monitor: no DPMS extension.\n",
242                      blurb(), (on_p ? "on" : "off"));
243           return;
244         }
245
246       /* The manual for DPMSForceLevel() says that it throws BadMatch if
247          "DPMS is disabled on the specified display."
248
249          The manual for DPMSCapable() says that it "returns True if the X
250          server is capable of DPMS."
251
252          Apparently they consider "capable of DPMS" and "DPMS is enabled"
253          to be different things, and so even if DPMSCapable() returns
254          True, DPMSForceLevel() *might* throw an X Error.  Isn't that
255          just fucking special.
256        */
257       XSync (si->dpy, False);
258       error_handler_hit_p = False;
259       old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
260       XSync (si->dpy, False);
261       DPMSForceLevel(si->dpy, (on_p ? DPMSModeOn : DPMSModeOff));
262       XSync (si->dpy, False);
263       /* Ignore error_handler_hit_p, just probe monitor instead */
264
265       if ((!!on_p) != monitor_powered_on_p (si))  /* double-check */
266         fprintf (stderr,
267        "%s: DPMSForceLevel(dpy, %s) did not change monitor power state.\n",
268                  blurb(),
269                  (on_p ? "DPMSModeOn" : "DPMSModeOff"));
270     }
271 }
272
273 #else  /* !HAVE_DPMS_EXTENSION */
274
275 void
276 sync_server_dpms_settings (Display *dpy, Bool enabled_p,
277                            int standby_secs, int suspend_secs, int off_secs,
278                            Bool verbose_p)
279 {
280   if (verbose_p)
281     fprintf (stderr, "%s: DPMS support not compiled in.\n", blurb());
282 }
283
284 Bool
285 monitor_powered_on_p (saver_info *si) 
286 {
287   return True; 
288 }
289
290 void
291 monitor_power_on (saver_info *si, Bool on_p)
292 {
293   return; 
294 }
295
296 #endif /* !HAVE_DPMS_EXTENSION */