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