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