677a8c0a2dc5b3f8082bf84513f11932746035b9
[xscreensaver] / driver / xset.c
1 /* xset.c --- interacting with server extensions and the builtin screensaver.
2  * xscreensaver, Copyright (c) 1991-2004 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 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <stdio.h>
18 #include <X11/Xlib.h>
19 #include <X11/Xutil.h>
20 #include <X11/Xatom.h>
21 #include <X11/Xos.h>
22
23 /* This file doesn't need the Xt headers, so stub these types out... */
24 #undef XtPointer
25 #define XtAppContext void*
26 #define XrmDatabase  void*
27 #define XtIntervalId void*
28 #define XtPointer    void*
29 #define Widget       void*
30
31 #include "xscreensaver.h"
32
33 #ifdef _VROOT_H_
34 ERROR!  You must not include vroot.h in this file.
35 #endif
36
37 \f
38 /* MIT SCREEN-SAVER server extension hackery.
39  */
40
41 #ifdef HAVE_MIT_SAVER_EXTENSION
42
43 # include <X11/extensions/scrnsaver.h>
44
45 Bool
46 query_mit_saver_extension (saver_info *si)
47 {
48   return XScreenSaverQueryExtension (si->dpy,
49                                      &si->mit_saver_ext_event_number,
50                                      &si->mit_saver_ext_error_number);
51 }
52
53 static int
54 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
55 {
56   return 0;
57 }
58
59 static void
60 init_mit_saver_extension (saver_info *si)
61 {
62   int i;
63   Pixmap *blank_pix = (Pixmap *) calloc (sizeof(Pixmap), si->nscreens);
64
65   for (i = 0; i < si->nscreens; i++)
66     {
67       saver_screen_info *ssi = &si->screens[i];
68       XID kill_id = 0;
69       Atom kill_type = 0;
70       Window root = RootWindowOfScreen (ssi->screen);
71       blank_pix[i] = XCreatePixmap (si->dpy, root, 1, 1, 1);
72
73       /* Kill off the old MIT-SCREEN-SAVER client if there is one.
74          This tends to generate X errors, though (possibly due to a bug
75          in the server extension itself?) so just ignore errors here. */
76       if (XScreenSaverGetRegistered (si->dpy,
77                                      XScreenNumberOfScreen (ssi->screen),
78                                      &kill_id, &kill_type)
79           && kill_id != blank_pix[i])
80         {
81           XErrorHandler old_handler =
82             XSetErrorHandler (ignore_all_errors_ehandler);
83           XKillClient (si->dpy, kill_id);
84           XSync (si->dpy, False);
85           XSetErrorHandler (old_handler);
86         }
87       XScreenSaverSelectInput (si->dpy, root, ScreenSaverNotifyMask);
88       XScreenSaverRegister (si->dpy,
89                             XScreenNumberOfScreen (ssi->screen),
90                             (XID) blank_pix[i], XA_PIXMAP);
91     }
92   free(blank_pix);
93 }
94 #endif /* HAVE_MIT_SAVER_EXTENSION */
95
96 \f
97 /* SGI SCREEN_SAVER server extension hackery.
98  */
99
100 #ifdef HAVE_SGI_SAVER_EXTENSION
101
102 # include <X11/extensions/XScreenSaver.h>
103
104 Bool
105 query_sgi_saver_extension (saver_info *si)
106 {
107   return XScreenSaverQueryExtension (si->dpy,
108                                      &si->sgi_saver_ext_event_number,
109                                      &si->sgi_saver_ext_error_number);
110 }
111
112 static void
113 init_sgi_saver_extension (saver_info *si)
114 {
115   saver_preferences *p = &si->prefs;
116   int i;
117   if (si->screen_blanked_p)
118     /* If you mess with this while the server thinks it's active,
119        the server crashes. */
120     return;
121
122   for (i = 0; i < si->nscreens; i++)
123     {
124       saver_screen_info *ssi = &si->screens[i];
125       XScreenSaverDisable (si->dpy, XScreenNumberOfScreen(ssi->screen));
126       if (! XScreenSaverEnable (si->dpy, XScreenNumberOfScreen(ssi->screen)))
127         {
128           fprintf (stderr,
129        "%s: SGI SCREEN_SAVER extension exists, but can't be initialized;\n\
130                 perhaps some other screensaver program is already running?\n",
131                    blurb());
132           si->using_sgi_saver_extension = False;
133           return;
134         }
135     }
136 }
137
138 #endif /* HAVE_SGI_SAVER_EXTENSION */
139
140 \f
141 /* XIDLE server extension hackery.
142  */
143
144 #ifdef HAVE_XIDLE_EXTENSION
145
146 # include <X11/extensions/xidle.h>
147
148 Bool
149 query_xidle_extension (saver_info *si)
150 {
151   int event_number;
152   int error_number;
153   return XidleQueryExtension (si->dpy, &event_number, &error_number);
154 }
155
156 #endif /* HAVE_XIDLE_EXTENSION */
157
158
159 \f
160 /* Resize and Rotate server extension hackery.
161  */
162
163 #ifdef HAVE_RANDR
164
165 # include <X11/extensions/Xrandr.h>
166
167 Bool
168 query_randr_extension (saver_info *si)
169 {
170   saver_preferences *p = &si->prefs;
171   Bool ok = XRRQueryExtension (si->dpy,
172                                &si->randr_event_number,
173                                &si->randr_error_number);
174
175   if (ok)
176     {
177       int nscreens = ScreenCount (si->dpy);  /* number of *real* screens */
178       int i;
179
180       if (p->verbose_p)
181         fprintf (stderr, "%s: selecting RANDR events\n", blurb());
182       for (i = 0; i < nscreens; i++)
183 # ifdef RRScreenChangeNotifyMask                 /* randr.h 1.5, 2002/09/29 */
184         XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
185                         RRScreenChangeNotifyMask);
186 # else  /* !RRScreenChangeNotifyMask */          /* Xrandr.h 1.4, 2001/06/07 */
187         XRRScreenChangeSelectInput (si->dpy, RootWindow (si->dpy, i), True);
188 # endif /* !RRScreenChangeNotifyMask */
189     }
190
191   return ok;
192 }
193
194 #endif /* HAVE_RANDR */
195
196
197 \f
198 /* Figuring out what the appropriate XSetScreenSaver() parameters are
199    (one wouldn't expect this to be rocket science.)
200  */
201
202 void
203 disable_builtin_screensaver (saver_info *si, Bool unblank_screen_p)
204 {
205   saver_preferences *p = &si->prefs;
206   int current_server_timeout, current_server_interval;
207   int current_prefer_blank, current_allow_exp;
208   int desired_server_timeout, desired_server_interval;
209   int desired_prefer_blank, desired_allow_exp;
210
211   XGetScreenSaver (si->dpy, &current_server_timeout, &current_server_interval,
212                    &current_prefer_blank, &current_allow_exp);
213
214   desired_server_timeout = current_server_timeout;
215   desired_server_interval = current_server_interval;
216   desired_prefer_blank = current_prefer_blank;
217   desired_allow_exp = current_allow_exp;
218
219   /* On SGIs, if interval is non-zero, it is the number of seconds after
220      screen saving starts at which the monitor should be powered down.
221      Obviously I don't want that, so set it to 0 (meaning "never".)
222
223      Power saving is disabled if DontPreferBlanking, but in that case,
224      we don't get extension events either.  So we can't turn it off that way.
225
226      Note: if you're running Irix 6.3 (O2), you may find that your monitor is
227      powering down anyway, regardless of the xset settings.  This is fixed by
228      installing SGI patches 2447 and 2537.
229    */
230   desired_server_interval = 0;
231
232   /* I suspect (but am not sure) that DontAllowExposures might have
233      something to do with powering off the monitor as well, at least
234      on some systems that don't support XDPMS?  Who knows... */
235   desired_allow_exp = AllowExposures;
236
237   if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
238     {
239       desired_server_timeout = (p->timeout / 1000);
240
241       /* The SGI extension won't give us events unless blanking is on.
242          I think (unsure right now) that the MIT extension is the opposite. */
243       if (si->using_sgi_saver_extension)
244         desired_prefer_blank = PreferBlanking;
245       else
246         desired_prefer_blank = DontPreferBlanking;
247     }
248   else
249     {
250       /* When we're not using an extension, set the server-side timeout to 0,
251          so that the server never gets involved with screen blanking, and we
252          do it all ourselves.  (However, when we *are* using an extension,
253          we tell the server when to notify us, and rather than blanking the
254          screen, the server will send us an X event telling us to blank.)
255        */
256       desired_server_timeout = 0;
257     }
258
259   /* XSetScreenSaver() generates BadValue if either timeout parameter
260      exceeds 15 bits (signed short.)  That is 09:06:07.
261    */
262   if (desired_server_timeout  > 0x7FFF) desired_server_timeout  = 0x7FFF;
263   if (desired_server_interval > 0x7FFF) desired_server_interval = 0x7FFF;
264
265   if (desired_server_timeout != current_server_timeout ||
266       desired_server_interval != current_server_interval ||
267       desired_prefer_blank != current_prefer_blank ||
268       desired_allow_exp != current_allow_exp)
269     {
270       if (p->verbose_p)
271         fprintf (stderr,
272                  "%s: disabling server builtin screensaver:\n"
273                  "%s:  (xset s %d %d; xset s %s; xset s %s)\n",
274                  blurb(), blurb(),
275                  desired_server_timeout, desired_server_interval,
276                  (desired_prefer_blank ? "blank" : "noblank"),
277                  (desired_allow_exp ? "expose" : "noexpose"));
278
279       XSetScreenSaver (si->dpy,
280                        desired_server_timeout, desired_server_interval,
281                        desired_prefer_blank, desired_allow_exp);
282       XSync(si->dpy, False);
283     }
284
285
286 #if defined(HAVE_MIT_SAVER_EXTENSION) || defined(HAVE_SGI_SAVER_EXTENSION)
287   {
288     static Bool extension_initted = False;
289     if (!extension_initted)
290       {
291         extension_initted = True;
292 # ifdef HAVE_MIT_SAVER_EXTENSION
293         if (si->using_mit_saver_extension) init_mit_saver_extension(si);
294 # endif
295 # ifdef HAVE_SGI_SAVER_EXTENSION
296         if (si->using_sgi_saver_extension) init_sgi_saver_extension(si);
297 # endif
298       }
299   }
300 #endif /* HAVE_MIT_SAVER_EXTENSION || HAVE_SGI_SAVER_EXTENSION */
301
302   if (unblank_screen_p)
303     /* Turn off the server builtin saver if it is now running. */
304     XForceScreenSaver (si->dpy, ScreenSaverReset);
305 }
306
307 \f
308 /* Display Power Management System (DPMS.)
309
310    On XFree86 systems, "man xset" reports:
311
312        -dpms    The -dpms option disables DPMS (Energy Star) features.
313        +dpms    The +dpms option enables DPMS (Energy Star) features.
314
315        dpms flags...
316                 The dpms option allows the DPMS (Energy Star)
317                 parameters to be set.  The option can take up to three
318                 numerical values, or the `force' flag followed by a
319                 DPMS state.  The `force' flags forces the server to
320                 immediately switch to the DPMS state specified.  The
321                 DPMS state can be one of `standby', `suspend', or
322                 `off'.  When numerical values are given, they set the
323                 inactivity period before the three modes are activated.
324                 The first value given is for the `standby' mode, the
325                 second is for the `suspend' mode, and the third is for
326                 the `off' mode.  Setting these values implicitly
327                 enables the DPMS features.  A value of zero disables a
328                 particular mode.
329
330    However, note that the implementation is more than a little bogus,
331    in that there is code in /usr/X11R6/lib/libXdpms.a to implement all
332    the usual server-extension-querying utilities -- but there are no
333    prototypes in any header file!  Thus, the prototypes here.  (The
334    stuff in X11/extensions/dpms.h and X11/extensions/dpmsstr.h define
335    the raw X protcol, they don't define the API to libXdpms.a.)
336
337    Some documentation:
338    Library:  ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMSLib.ms
339    Protocol: ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMS.ms
340  */
341
342 #ifdef HAVE_DPMS_EXTENSION
343
344 #include <X11/Xproto.h>                 /* for CARD16 */
345 #include <X11/extensions/dpms.h>
346 #include <X11/extensions/dpmsstr.h>
347
348 extern Bool DPMSQueryExtension (Display *dpy, int *event_ret, int *error_ret);
349 extern Bool DPMSCapable (Display *dpy);
350 extern Status DPMSForceLevel (Display *dpy, CARD16 level);
351 extern Status DPMSInfo (Display *dpy, CARD16 *power_level, BOOL *state);
352
353 #if 0 /* others we don't use */
354 extern Status DPMSGetVersion (Display *dpy, int *major_ret, int *minor_ret);
355 extern Status DPMSSetTimeouts (Display *dpy,
356                                CARD16 standby, CARD16 suspend, CARD16 off);
357 extern Bool DPMSGetTimeouts (Display *dpy,
358                              CARD16 *standby, CARD16 *suspend, CARD16 *off);
359 extern Status DPMSEnable (Display *dpy);
360 extern Status DPMSDisable (Display *dpy);
361 #endif /* 0 */
362
363
364 Bool
365 monitor_powered_on_p (saver_info *si)
366 {
367   Bool result;
368   int event_number, error_number;
369   BOOL onoff = False;
370   CARD16 state;
371
372   if (!DPMSQueryExtension(si->dpy, &event_number, &error_number))
373     /* Server doesn't know -- assume the monitor is on. */
374     result = True;
375
376   else if (!DPMSCapable(si->dpy))
377     /* Server says the monitor doesn't do power management -- so it's on. */
378     result = True;
379
380   else
381     {
382       DPMSInfo(si->dpy, &state, &onoff);
383       if (!onoff)
384         /* Server says DPMS is disabled -- so the monitor is on. */
385         result = True;
386       else
387         switch (state) {
388         case DPMSModeOn:      result = True;  break;  /* really on */
389         case DPMSModeStandby: result = False; break;  /* kinda off */
390         case DPMSModeSuspend: result = False; break;  /* pretty off */
391         case DPMSModeOff:     result = False; break;  /* really off */
392         default:              result = True;  break;  /* protocol error? */
393         }
394     }
395
396   return result;
397 }
398
399 void
400 monitor_power_on (saver_info *si)
401 {
402   if (!monitor_powered_on_p (si))
403     {
404       DPMSForceLevel(si->dpy, DPMSModeOn);
405       XSync(si->dpy, False);
406       if (!monitor_powered_on_p (si))
407         fprintf (stderr,
408        "%s: DPMSForceLevel(dpy, DPMSModeOn) did not power the monitor on?\n",
409                  blurb());
410     }
411 }
412
413 #else  /* !HAVE_DPMS_EXTENSION */
414
415 Bool
416 monitor_powered_on_p (saver_info *si) 
417 {
418   return True; 
419 }
420
421 void
422 monitor_power_on (saver_info *si)
423 {
424   return; 
425 }
426
427 #endif /* !HAVE_DPMS_EXTENSION */