http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[xscreensaver] / driver / xset.c
1 /* xset.c --- interacting with server extensions and the builtin screensaver.
2  * xscreensaver, Copyright (c) 1991-2008 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 static int
46 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
47 {
48   return 0;
49 }
50
51 static void
52 init_mit_saver_extension (saver_info *si)
53 {
54   int i;
55   Pixmap *blank_pix = (Pixmap *) calloc (sizeof(Pixmap), si->nscreens);
56
57   for (i = 0; i < si->nscreens; i++)
58     {
59       saver_screen_info *ssi = &si->screens[i];
60       XID kill_id = 0;
61       Atom kill_type = 0;
62       Window root = RootWindowOfScreen (ssi->screen);
63       blank_pix[i] = XCreatePixmap (si->dpy, root, 1, 1, 1);
64
65       /* Kill off the old MIT-SCREEN-SAVER client if there is one.
66          This tends to generate X errors, though (possibly due to a bug
67          in the server extension itself?) so just ignore errors here. */
68       if (XScreenSaverGetRegistered (si->dpy,
69                                      XScreenNumberOfScreen (ssi->screen),
70                                      &kill_id, &kill_type)
71           && kill_id != blank_pix[i])
72         {
73           XErrorHandler old_handler =
74             XSetErrorHandler (ignore_all_errors_ehandler);
75           XKillClient (si->dpy, kill_id);
76           XSync (si->dpy, False);
77           XSetErrorHandler (old_handler);
78         }
79       XScreenSaverSelectInput (si->dpy, root, ScreenSaverNotifyMask);
80       XScreenSaverRegister (si->dpy,
81                             XScreenNumberOfScreen (ssi->screen),
82                             (XID) blank_pix[i], XA_PIXMAP);
83     }
84   free(blank_pix);
85 }
86 #endif /* HAVE_MIT_SAVER_EXTENSION */
87
88 \f
89 #ifdef HAVE_XINPUT
90 /* XInputExtension device support */
91 #include <X11/extensions/XInput.h>
92
93 struct xinput_dev_info {
94   XDevice       *device;
95   XEventClass   press, release, valuator;
96 };
97
98 Bool
99 query_xinput_extension (saver_info *si)
100 {
101   XExtCodes codes;
102   return XQueryExtension (si->dpy, INAME, &codes.major_opcode,
103                           &codes.first_event, &codes.first_error);
104 }
105
106 void
107 init_xinput_extension (saver_info *si)
108 {
109   int i, ndevices;
110   int class;
111   XDeviceInfo *list;
112   XDevice *dev;
113   XAnyClassPtr pClass;
114   XEventClass *event_list;
115   int nevents = 0;
116
117   /* skip if already initialized */
118   if (si->num_xinput_devices && si->xinput_devices)
119     return;
120
121   si->num_xinput_devices = 0;
122
123   list = XListInputDevices (si->dpy, &ndevices);
124   if (list == NULL)
125     {
126       si->xinput_devices = NULL;
127       return;
128     }
129
130   /* We only care about 3 event types per device (DeviceButtonPress,
131      DeviceButtonRelease, and DeviceMotionNotify), hence the "* 3"
132      for the event count. */
133   event_list = calloc(ndevices * 3, sizeof(XEventClass));
134   if (event_list == NULL)
135     return;
136
137   si->xinput_devices = calloc(ndevices, sizeof(struct xinput_dev_info));
138   if (si->xinput_devices == NULL)
139     {
140       free(event_list);
141       return;
142     }
143
144   for (i = 0; i < ndevices; i++)
145     {
146       if ((list[i].use == IsXExtensionDevice)
147 #ifdef IsXExtensionPointer
148           || (list[i].use == IsXExtensionPointer)
149 #endif
150          )
151         {
152           struct xinput_dev_info *dev_info =
153             &si->xinput_devices[si->num_xinput_devices];
154           Bool device_we_want = False;
155
156           if (si->prefs.debug_p)
157             fprintf(stderr,
158                     "Extension device #%2d: XID=%2d  type=%3d  name=\"%s\"\n",
159                     i, (int) list[i].id, (int) list[i].type, list[i].name);
160
161           dev = XOpenDevice (si->dpy, list[i].id);
162           if (!dev)
163             continue;
164           dev_info->device = dev;
165
166           pClass = list[i].inputclassinfo;
167           for (class = 0; class < list[i].num_classes; class++)
168             {
169               switch (pClass->class)
170                 {
171                 case ButtonClass:
172                   if (((XButtonInfo *) pClass)->num_buttons > 0)
173                     {
174                       /* Macros set values in the second & third arguments */
175                       DeviceButtonPress (dev, si->xinput_DeviceButtonPress,
176                                          dev_info->press);
177                       event_list[nevents++] = dev_info->press;
178
179                       DeviceButtonRelease (dev, si->xinput_DeviceButtonRelease,
180                                            dev_info->release);
181                       event_list[nevents++] = dev_info->release;
182                       device_we_want = True;
183                     }
184                   break;
185
186                 case ValuatorClass:
187                   if (((XValuatorInfo *) pClass)->num_axes > 0)
188                     {
189                       DeviceMotionNotify (dev, si->xinput_DeviceMotionNotify,
190                                           dev_info->valuator);
191                       event_list[nevents++] = dev_info->valuator;
192                       device_we_want = True;
193                     }
194                   break;
195
196                 default:
197                   /* ignore other classes of devices/events */
198                   break;
199                 }
200
201               pClass = (XAnyClassPtr) & ((char *) pClass)[pClass->length];
202             }
203
204           if (device_we_want)
205             si->num_xinput_devices++;
206           else
207             XCloseDevice (si->dpy, dev);
208         }
209     }
210
211   if (list)
212     XFreeDeviceList (list);
213
214   if ((nevents == 0) || (si->num_xinput_devices == 0))
215     {
216       free(event_list);
217       free(si->xinput_devices);
218       si->xinput_devices = NULL;
219       si->num_xinput_devices = 0;
220       return;
221     }
222
223   for (i = 0; i < si->nscreens; i++)
224     {
225       saver_screen_info *ssi = &si->screens[i];
226       Window root = RootWindowOfScreen (ssi->screen);
227       XSelectExtensionEvent (si->dpy, root, event_list, nevents);
228     }
229
230   free(event_list);
231 }
232
233 #if 0
234 /* not used */
235 static void
236 close_xinput_extension (saver_info *si)
237 {
238   int i;
239
240   for (i = 0; i < si->num_xinput_devices; i++)
241     XCloseDevice (si->dpy, si->xinput_devices[i].device);
242
243   free(si->xinput_devices);
244   si->xinput_devices = NULL;
245   si->num_xinput_devices = 0;
246 }
247 #endif
248 #endif /* HAVE_XINPUT */
249
250 \f
251 /* SGI SCREEN_SAVER server extension hackery.
252  */
253
254 #ifdef HAVE_SGI_SAVER_EXTENSION
255
256 # include <X11/extensions/XScreenSaver.h>
257
258 static void
259 init_sgi_saver_extension (saver_info *si)
260 {
261   saver_preferences *p = &si->prefs;
262   int i;
263   if (si->screen_blanked_p)
264     /* If you mess with this while the server thinks it's active,
265        the server crashes. */
266     return;
267
268   for (i = 0; i < si->nscreens; i++)
269     {
270       saver_screen_info *ssi = &si->screens[i];
271       XScreenSaverDisable (si->dpy, XScreenNumberOfScreen(ssi->screen));
272       if (! XScreenSaverEnable (si->dpy, XScreenNumberOfScreen(ssi->screen)))
273         {
274           fprintf (stderr,
275        "%s: SGI SCREEN_SAVER extension exists, but can't be initialized;\n\
276                 perhaps some other screensaver program is already running?\n",
277                    blurb());
278           si->using_sgi_saver_extension = False;
279           return;
280         }
281     }
282 }
283
284 #endif /* HAVE_SGI_SAVER_EXTENSION */
285
286
287 \f
288 /* Figuring out what the appropriate XSetScreenSaver() parameters are
289    (one wouldn't expect this to be rocket science.)
290  */
291
292 void
293 disable_builtin_screensaver (saver_info *si, Bool unblank_screen_p)
294 {
295   saver_preferences *p = &si->prefs;
296   int current_server_timeout, current_server_interval;
297   int current_prefer_blank, current_allow_exp;
298   int desired_server_timeout, desired_server_interval;
299   int desired_prefer_blank, desired_allow_exp;
300
301   XGetScreenSaver (si->dpy, &current_server_timeout, &current_server_interval,
302                    &current_prefer_blank, &current_allow_exp);
303
304   desired_server_timeout = current_server_timeout;
305   desired_server_interval = current_server_interval;
306   desired_prefer_blank = current_prefer_blank;
307   desired_allow_exp = current_allow_exp;
308
309   /* On SGIs, if interval is non-zero, it is the number of seconds after
310      screen saving starts at which the monitor should be powered down.
311      Obviously I don't want that, so set it to 0 (meaning "never".)
312
313      Power saving is disabled if DontPreferBlanking, but in that case,
314      we don't get extension events either.  So we can't turn it off that way.
315
316      Note: if you're running Irix 6.3 (O2), you may find that your monitor is
317      powering down anyway, regardless of the xset settings.  This is fixed by
318      installing SGI patches 2447 and 2537.
319    */
320   desired_server_interval = 0;
321
322   /* I suspect (but am not sure) that DontAllowExposures might have
323      something to do with powering off the monitor as well, at least
324      on some systems that don't support XDPMS?  Who knows... */
325   desired_allow_exp = AllowExposures;
326
327   if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
328     {
329       desired_server_timeout = (p->timeout / 1000);
330
331       /* The SGI extension won't give us events unless blanking is on.
332          I think (unsure right now) that the MIT extension is the opposite. */
333       if (si->using_sgi_saver_extension)
334         desired_prefer_blank = PreferBlanking;
335       else
336         desired_prefer_blank = DontPreferBlanking;
337     }
338   else
339     {
340       /* When we're not using an extension, set the server-side timeout to 0,
341          so that the server never gets involved with screen blanking, and we
342          do it all ourselves.  (However, when we *are* using an extension,
343          we tell the server when to notify us, and rather than blanking the
344          screen, the server will send us an X event telling us to blank.)
345        */
346       desired_server_timeout = 0;
347     }
348
349   /* XSetScreenSaver() generates BadValue if either timeout parameter
350      exceeds 15 bits (signed short.)  That is 09:06:07.
351    */
352   if (desired_server_timeout  > 0x7FFF) desired_server_timeout  = 0x7FFF;
353   if (desired_server_interval > 0x7FFF) desired_server_interval = 0x7FFF;
354
355   if (desired_server_timeout != current_server_timeout ||
356       desired_server_interval != current_server_interval ||
357       desired_prefer_blank != current_prefer_blank ||
358       desired_allow_exp != current_allow_exp)
359     {
360       if (p->verbose_p)
361         fprintf (stderr,
362                  "%s: disabling server builtin screensaver:\n"
363                  "%s:  (xset s %d %d; xset s %s; xset s %s)\n",
364                  blurb(), blurb(),
365                  desired_server_timeout, desired_server_interval,
366                  (desired_prefer_blank ? "blank" : "noblank"),
367                  (desired_allow_exp ? "expose" : "noexpose"));
368
369       XSetScreenSaver (si->dpy,
370                        desired_server_timeout, desired_server_interval,
371                        desired_prefer_blank, desired_allow_exp);
372       XSync(si->dpy, False);
373     }
374
375
376 #if defined(HAVE_MIT_SAVER_EXTENSION) || defined(HAVE_SGI_SAVER_EXTENSION)
377   {
378     static Bool extension_initted = False;
379     if (!extension_initted)
380       {
381         extension_initted = True;
382 # ifdef HAVE_MIT_SAVER_EXTENSION
383         if (si->using_mit_saver_extension) init_mit_saver_extension(si);
384 # endif
385 # ifdef HAVE_SGI_SAVER_EXTENSION
386         if (si->using_sgi_saver_extension) init_sgi_saver_extension(si);
387 # endif
388       }
389   }
390 #endif /* HAVE_MIT_SAVER_EXTENSION || HAVE_SGI_SAVER_EXTENSION */
391
392   if (unblank_screen_p)
393     /* Turn off the server builtin saver if it is now running. */
394     XForceScreenSaver (si->dpy, ScreenSaverReset);
395 }