http://se.aminet.net/pub/X11/ftp.x.org/contrib/vms/xscreensaver-124.zip
[xscreensaver] / driver / timers.c
1 /* xscreensaver, Copyright (c) 1991-1993 Jamie Zawinski <jwz@mcom.com>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 #include <stdio.h>
13 #include <X11/Xlib.h>
14 #include <X11/Intrinsic.h>
15 #include <X11/Xos.h>
16 #ifndef VMS
17 #include <X11/Xmu/Error.h>
18 #else
19 #include "sys$common:[decw$include.xmu]Error.h"
20 #endif
21
22 #ifdef HAVE_XIDLE
23 #include <X11/extensions/xidle.h>
24 #endif
25
26 #include "xscreensaver.h"
27
28 #if __STDC__
29 # define P(x)x
30 #else
31 #define P(x)()
32 #endif
33
34 extern XtAppContext app;
35
36 Time cycle;
37 Time timeout;
38 Time pointer_timeout;
39 Time notice_events_timeout;
40
41 extern Bool use_xidle;
42 extern Bool dbox_up_p;
43 extern Bool locked_p;
44 extern Window screensaver_window;
45
46 extern Bool handle_clientmessage P((XEvent *, Bool));
47
48 static time_t last_activity_time; /* for non-XIdle mode */
49 static XtIntervalId timer_id = 0;
50 static XtIntervalId check_pointer_timer_id = 0;
51 XtIntervalId cycle_id = 0;
52 XtIntervalId lock_id = 0;
53
54 void
55 idle_timer (junk1, junk2)
56      void *junk1;
57      XtPointer junk2;
58 {
59   /* What an amazingly shitty design.  Not only does Xt execute timeout
60      events from XtAppNextEvent() instead of from XtDispatchEvent(), but
61      there is no way to tell Xt to block until there is an X event OR a
62      timeout happens.  Once your timeout proc is called, XtAppNextEvent()
63      still won't return until a "real" X event comes in.
64
65      So this function pushes a stupid, gratuitous, unnecessary event back
66      on the event queue to force XtAppNextEvent to return Right Fucking Now.
67      When the code in sleep_until_idle() sees an event of type XAnyEvent,
68      which the server never generates, it knows that a timeout has occurred.
69    */
70   XEvent fake_event;
71   fake_event.type = 0;  /* XAnyEvent type, ignored. */
72   fake_event.xany.display = dpy;
73   fake_event.xany.window  = 0;
74   XPutBackEvent (dpy, &fake_event);
75 }
76
77
78 static void
79 #if __STDC__
80 notice_events (Window window, Bool top_p)
81 #else
82 notice_events (window, top_p)
83      Window window;
84      Bool top_p;
85 #endif
86 {
87   XWindowAttributes attrs;
88   unsigned long events;
89   Window root, parent, *kids;
90   unsigned int nkids;
91
92   if (XtWindowToWidget (dpy, window))
93     /* If it's one of ours, don't mess up its event mask. */
94     return;
95
96   if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
97     return;
98   if (window == root)
99     top_p = False;
100
101   XGetWindowAttributes (dpy, window, &attrs);
102   events = ((attrs.all_event_masks | attrs.do_not_propagate_mask)
103             & KeyPressMask);
104
105   /* Select for SubstructureNotify on all windows.
106      Select for KeyPress on all windows that already have it selected.
107      Do we need to select for ButtonRelease?  I don't think so.
108    */
109   XSelectInput (dpy, window, SubstructureNotifyMask | events);
110
111   if (top_p && verbose_p && (events & KeyPressMask))
112     {
113       /* Only mention one window per tree (hack hack). */
114       printf ("%s: selected KeyPress on 0x%X\n", progname, window);
115       top_p = False;
116     }
117
118   if (kids)
119     {
120       while (nkids)
121         notice_events (kids [--nkids], top_p);
122       XFree ((char *) kids);
123     }
124 }
125
126
127 int
128 BadWindow_ehandler (dpy, error)
129      Display *dpy;
130      XErrorEvent *error;
131 {
132   /* When we notice a window being created, we spawn a timer that waits
133      30 seconds or so, and then selects events on that window.  This error
134      handler is used so that we can cope with the fact that the window
135      may have been destroyed <30 seconds after it was created.
136    */
137   if (error->error_code == BadWindow ||
138       error->error_code == BadDrawable)
139     return 0;
140   XmuPrintDefaultErrorMessage (dpy, error, stderr);
141   exit (1);
142 }
143
144 void
145 notice_events_timer (closure, timer)
146      XtPointer closure;
147      void *timer;
148 {
149   Window window = (Window) closure;
150   int (*old_handler) ();
151   old_handler = XSetErrorHandler (BadWindow_ehandler);
152   notice_events (window, True);
153   XSync (dpy, False);
154   XSetErrorHandler (old_handler);
155 }
156
157
158 /* When the screensaver is active, this timer will periodically change
159    the running program.
160  */
161 void
162 cycle_timer (junk1, junk2)
163      void *junk1;
164      XtPointer junk2;
165 {
166   Time how_long = cycle;
167   if (dbox_up_p)
168     {
169       if (verbose_p)
170         printf ("%s: dbox up; delaying hack change.\n", progname);
171       how_long = 30000; /* 30 secs */
172     }
173   else
174     {
175       if (verbose_p)
176         printf ("%s: changing graphics hacks.\n", progname);
177       kill_screenhack ();
178       spawn_screenhack (False);
179     }
180   cycle_id = XtAppAddTimeOut (app, how_long, cycle_timer, 0);
181 }
182
183
184 void
185 activate_lock_timer (junk1, junk2)
186      void *junk1;
187      XtPointer junk2;
188 {
189   if (verbose_p)
190     printf ("%s: timed out; activating lock\n", progname);
191   locked_p = True;
192 }
193
194
195 /* Call this when user activity (or "simulated" activity) has been noticed.
196  */
197 static void
198 reset_timers P((void))
199 {
200 #ifdef DEBUG_TIMERS
201   if (verbose_p)
202     printf ("%s: restarting idle_timer (%d, %d)\n",
203             progname, timeout, timer_id);
204 #endif
205   XtRemoveTimeOut (timer_id);
206   timer_id = XtAppAddTimeOut (app, timeout, idle_timer, 0);
207   if (cycle_id) abort ();
208
209   last_activity_time = time ((time_t *) 0);
210 }
211
212 /* When we aren't using XIdle, this timer is used to periodically wake up
213    and poll the mouse position, which is possibly more reliable than
214    selecting motion events on every window.
215  */
216 static void
217 check_pointer_timer (closure, this_timer)
218      void *closure;
219      XtPointer this_timer;
220 {
221   static int last_root_x = -1;
222   static int last_root_y = -1;
223   static Window last_child = (Window) -1;
224   static unsigned int last_mask = 0;
225   Window root, child;
226   int root_x, root_y, x, y;
227   unsigned int mask;
228   XtIntervalId *timerP = (XtIntervalId *) closure;
229 #ifdef HAVE_XIDLE
230   if (use_xidle)
231     abort ();
232 #endif
233
234   *timerP = XtAppAddTimeOut (app, pointer_timeout, check_pointer_timer,
235                              closure);
236
237   XQueryPointer (dpy, screensaver_window, &root, &child,
238                  &root_x, &root_y, &x, &y, &mask);
239   if (root_x == last_root_x && root_y == last_root_y &&
240       child == last_child && mask == last_mask)
241     return;
242
243 #ifdef DEBUG_TIMERS
244   if (verbose_p && this_timer)
245     if (root_x == last_root_x && root_y == last_root_y && child == last_child)
246       printf ("%s: modifiers changed at %s.\n", progname, timestring ());
247     else
248       printf ("%s: pointer moved at %s.\n", progname, timestring ());
249 #endif
250
251   last_root_x = root_x;
252   last_root_y = root_y;
253   last_child = child;
254   last_mask = mask;
255
256   reset_timers ();
257 }
258
259
260 void
261 sleep_until_idle (until_idle_p)
262      Bool until_idle_p;
263 {
264   XEvent event;
265
266   if (until_idle_p)
267     {
268       timer_id = XtAppAddTimeOut (app, timeout, idle_timer, 0);
269 #ifdef HAVE_XIDLE
270       if (! use_xidle)
271 #endif
272         /* start polling the mouse position */
273         check_pointer_timer (&check_pointer_timer_id, 0);
274     }
275
276   while (1)
277     {
278       XtAppNextEvent (app, &event);
279
280       switch (event.xany.type) {
281       case 0:           /* our synthetic "timeout" event has been signalled */
282         if (until_idle_p)
283           {
284             Time idle;
285 #ifdef HAVE_XIDLE
286             if (use_xidle)
287               {
288                 if (! XGetIdleTime (dpy, &idle))
289                   {
290                     fprintf (stderr, "%s: %sXGetIdleTime() failed.\n",
291                              progname, (verbose_p ? "## " : ""));
292                     exit (1);
293                   }
294               }
295             else
296 #endif /* HAVE_XIDLE */
297               idle = 1000 * (last_activity_time - time ((time_t *) 0));
298             
299             if (idle >= timeout)
300               goto DONE;
301             else
302               timer_id = XtAppAddTimeOut (app, timeout - idle,
303                                           idle_timer, 0);
304           }
305         break;
306
307       case ClientMessage:
308         if (handle_clientmessage (&event, until_idle_p))
309           goto DONE;
310         break;
311
312       case CreateNotify:
313 #ifdef HAVE_XIDLE
314         if (! use_xidle)
315 #endif
316           XtAppAddTimeOut (app, notice_events_timeout, notice_events_timer,
317                            (XtPointer) event.xcreatewindow.window);
318         break;
319
320       case KeyPress:
321       case KeyRelease:
322       case ButtonPress:
323       case ButtonRelease:
324       case MotionNotify:
325
326 #ifdef DEBUG_TIMERS
327         if (verbose_p)
328           {
329             if (event.xany.type == MotionNotify)
330               printf ("%s: MotionNotify at %s\n", progname, timestring ());
331             else if (event.xany.type == KeyPress)
332               printf ("%s: KeyPress seen on 0x%X at %s\n", progname,
333                       event.xkey.window, timestring ());
334           }
335 #endif
336
337         /* We got a user event */
338         if (!until_idle_p)
339           goto DONE;
340         else
341           reset_timers ();
342         break;
343
344       default:
345         XtDispatchEvent (&event);
346       }
347     }
348  DONE:
349
350   if (check_pointer_timer_id)
351     {
352       XtRemoveTimeOut (check_pointer_timer_id);
353       check_pointer_timer_id = 0;
354     }
355   if (timer_id)
356     {
357       XtRemoveTimeOut (timer_id);
358       timer_id = 0;
359     }
360
361   if (until_idle_p && cycle_id)
362     abort ();
363
364   return;
365 }