d26ee1b25a08938164e8d2dbfa3429ed42b40616
[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   if (ok)
175     {
176       int nscreens = ScreenCount (si->dpy);  /* number of *real* screens */
177       int i;
178
179       if (p->verbose_p)
180         fprintf (stderr, "%s: selecting RANDR events\n", blurb());
181       for (i = 0; i < nscreens; i++)
182         XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
183                         RRScreenChangeNotifyMask);
184     }
185
186   return ok;
187 }
188
189 #endif /* HAVE_XIDLE_EXTENSION */
190
191
192 \f
193 /* Figuring out what the appropriate XSetScreenSaver() parameters are
194    (one wouldn't expect this to be rocket science.)
195  */
196
197 void
198 disable_builtin_screensaver (saver_info *si, Bool unblank_screen_p)
199 {
200   saver_preferences *p = &si->prefs;
201   int current_server_timeout, current_server_interval;
202   int current_prefer_blank, current_allow_exp;
203   int desired_server_timeout, desired_server_interval;
204   int desired_prefer_blank, desired_allow_exp;
205
206   XGetScreenSaver (si->dpy, &current_server_timeout, &current_server_interval,
207                    &current_prefer_blank, &current_allow_exp);
208
209   desired_server_timeout = current_server_timeout;
210   desired_server_interval = current_server_interval;
211   desired_prefer_blank = current_prefer_blank;
212   desired_allow_exp = current_allow_exp;
213
214   /* On SGIs, if interval is non-zero, it is the number of seconds after
215      screen saving starts at which the monitor should be powered down.
216      Obviously I don't want that, so set it to 0 (meaning "never".)
217
218      Power saving is disabled if DontPreferBlanking, but in that case,
219      we don't get extension events either.  So we can't turn it off that way.
220
221      Note: if you're running Irix 6.3 (O2), you may find that your monitor is
222      powering down anyway, regardless of the xset settings.  This is fixed by
223      installing SGI patches 2447 and 2537.
224    */
225   desired_server_interval = 0;
226
227   /* I suspect (but am not sure) that DontAllowExposures might have
228      something to do with powering off the monitor as well, at least
229      on some systems that don't support XDPMS?  Who know... */
230   desired_allow_exp = AllowExposures;
231
232   if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
233     {
234       desired_server_timeout = (p->timeout / 1000);
235
236       /* The SGI extension won't give us events unless blanking is on.
237          I think (unsure right now) that the MIT extension is the opposite. */
238       if (si->using_sgi_saver_extension)
239         desired_prefer_blank = PreferBlanking;
240       else
241         desired_prefer_blank = DontPreferBlanking;
242     }
243   else
244     {
245       /* When we're not using an extension, set the server-side timeout to 0,
246          so that the server never gets involved with screen blanking, and we
247          do it all ourselves.  (However, when we *are* using an extension,
248          we tell the server when to notify us, and rather than blanking the
249          screen, the server will send us an X event telling us to blank.)
250        */
251       desired_server_timeout = 0;
252     }
253
254   if (desired_server_timeout != current_server_timeout ||
255       desired_server_interval != current_server_interval ||
256       desired_prefer_blank != current_prefer_blank ||
257       desired_allow_exp != current_allow_exp)
258     {
259       if (p->verbose_p)
260         fprintf (stderr,
261                  "%s: disabling server builtin screensaver:\n"
262                  "%s:  (xset s %d %d; xset s %s; xset s %s)\n",
263                  blurb(), blurb(),
264                  desired_server_timeout, desired_server_interval,
265                  (desired_prefer_blank ? "blank" : "noblank"),
266                  (desired_allow_exp ? "expose" : "noexpose"));
267
268       XSetScreenSaver (si->dpy,
269                        desired_server_timeout, desired_server_interval,
270                        desired_prefer_blank, desired_allow_exp);
271       XSync(si->dpy, False);
272     }
273
274
275 #if defined(HAVE_MIT_SAVER_EXTENSION) || defined(HAVE_SGI_SAVER_EXTENSION)
276   {
277     static Bool extension_initted = False;
278     if (!extension_initted)
279       {
280         extension_initted = True;
281 # ifdef HAVE_MIT_SAVER_EXTENSION
282         if (si->using_mit_saver_extension) init_mit_saver_extension(si);
283 # endif
284 # ifdef HAVE_SGI_SAVER_EXTENSION
285         if (si->using_sgi_saver_extension) init_sgi_saver_extension(si);
286 # endif
287       }
288   }
289 #endif /* HAVE_MIT_SAVER_EXTENSION || HAVE_SGI_SAVER_EXTENSION */
290
291   if (unblank_screen_p)
292     /* Turn off the server builtin saver if it is now running. */
293     XForceScreenSaver (si->dpy, ScreenSaverReset);
294 }
295
296 \f
297 /* Display Power Management System (DPMS.)
298
299    On XFree86 systems, "man xset" reports:
300
301        -dpms    The -dpms option disables DPMS (Energy Star) features.
302        +dpms    The +dpms option enables DPMS (Energy Star) features.
303
304        dpms flags...
305                 The dpms option allows the DPMS (Energy Star)
306                 parameters to be set.  The option can take up to three
307                 numerical values, or the `force' flag followed by a
308                 DPMS state.  The `force' flags forces the server to
309                 immediately switch to the DPMS state specified.  The
310                 DPMS state can be one of `standby', `suspend', or
311                 `off'.  When numerical values are given, they set the
312                 inactivity period before the three modes are activated.
313                 The first value given is for the `standby' mode, the
314                 second is for the `suspend' mode, and the third is for
315                 the `off' mode.  Setting these values implicitly
316                 enables the DPMS features.  A value of zero disables a
317                 particular mode.
318
319    However, note that the implementation is more than a little bogus,
320    in that there is code in /usr/X11R6/lib/libXdpms.a to implement all
321    the usual server-extension-querying utilities -- but there are no
322    prototypes in any header file!  Thus, the prototypes here.  (The
323    stuff in X11/extensions/dpms.h and X11/extensions/dpmsstr.h define
324    the raw X protcol, they don't define the API to libXdpms.a.)
325
326    Some documentation:
327    Library:  ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMSLib.ms
328    Protocol: ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/DPMS.ms
329  */
330
331 #ifdef HAVE_DPMS_EXTENSION
332
333 #include <X11/Xproto.h>                 /* for CARD16 */
334 #include <X11/extensions/dpms.h>
335 #include <X11/extensions/dpmsstr.h>
336
337 extern Bool DPMSQueryExtension (Display *dpy, int *event_ret, int *error_ret);
338 extern Bool DPMSCapable (Display *dpy);
339 extern Status DPMSForceLevel (Display *dpy, CARD16 level);
340 extern Status DPMSInfo (Display *dpy, CARD16 *power_level, BOOL *state);
341
342 #if 0 /* others we don't use */
343 extern Status DPMSGetVersion (Display *dpy, int *major_ret, int *minor_ret);
344 extern Status DPMSSetTimeouts (Display *dpy,
345                                CARD16 standby, CARD16 suspend, CARD16 off);
346 extern Bool DPMSGetTimeouts (Display *dpy,
347                              CARD16 *standby, CARD16 *suspend, CARD16 *off);
348 extern Status DPMSEnable (Display *dpy);
349 extern Status DPMSDisable (Display *dpy);
350 #endif /* 0 */
351
352
353 Bool
354 monitor_powered_on_p (saver_info *si)
355 {
356   Bool result;
357   int event_number, error_number;
358   BOOL onoff = False;
359   CARD16 state;
360
361   if (!DPMSQueryExtension(si->dpy, &event_number, &error_number))
362     /* Server doesn't know -- assume the monitor is on. */
363     result = True;
364
365   else if (!DPMSCapable(si->dpy))
366     /* Server says the monitor doesn't do power management -- so it's on. */
367     result = True;
368
369   else
370     {
371       DPMSInfo(si->dpy, &state, &onoff);
372       if (!onoff)
373         /* Server says DPMS is disabled -- so the monitor is on. */
374         result = True;
375       else
376         switch (state) {
377         case DPMSModeOn:      result = True;  break;  /* really on */
378         case DPMSModeStandby: result = False; break;  /* kinda off */
379         case DPMSModeSuspend: result = False; break;  /* pretty off */
380         case DPMSModeOff:     result = False; break;  /* really off */
381         default:              result = True;  break;  /* protocol error? */
382         }
383     }
384
385   return result;
386 }
387
388 void
389 monitor_power_on (saver_info *si)
390 {
391   if (!monitor_powered_on_p (si))
392     {
393       DPMSForceLevel(si->dpy, DPMSModeOn);
394       XSync(si->dpy, False);
395       if (!monitor_powered_on_p (si))
396         fprintf (stderr,
397        "%s: DPMSForceLevel(dpy, DPMSModeOn) did not power the monitor on?\n",
398                  blurb());
399     }
400 }
401
402 #else  /* !HAVE_DPMS_EXTENSION */
403
404 Bool
405 monitor_powered_on_p (saver_info *si) 
406 {
407   return True; 
408 }
409
410 void
411 monitor_power_on (saver_info *si)
412 {
413   return; 
414 }
415
416 #endif /* !HAVE_DPMS_EXTENSION */