1 /* xscreensaver, Copyright (c) 1991-1995 Jamie Zawinski <jwz@mcom.com>
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
12 /* #define DEBUG_TIMERS */
16 #include <X11/Intrinsic.h>
18 #include <X11/Xmu/Error.h>
20 #ifdef HAVE_XIDLE_EXTENSION
21 #include <X11/extensions/xidle.h>
22 #endif /* HAVE_XIDLE_EXTENSION */
24 #ifdef HAVE_SAVER_EXTENSION
25 #include <X11/extensions/scrnsaver.h>
26 extern int saver_ext_event_number;
27 extern Window server_saver_window;
28 #endif /* HAVE_SAVER_EXTENSION */
30 #include "xscreensaver.h"
38 extern XtAppContext app;
43 Time notice_events_timeout;
45 extern Bool use_xidle_extension;
46 extern Bool use_saver_extension;
47 extern Bool dbox_up_p;
49 extern Window screensaver_window;
51 extern Bool handle_clientmessage P((XEvent *, Bool));
53 static time_t last_activity_time; /* for when we have no server extensions */
54 static XtIntervalId timer_id = 0;
55 static XtIntervalId check_pointer_timer_id = 0;
56 XtIntervalId cycle_id = 0;
57 XtIntervalId lock_id = 0;
60 idle_timer (junk1, junk2)
64 /* What an amazingly shitty design. Not only does Xt execute timeout
65 events from XtAppNextEvent() instead of from XtDispatchEvent(), but
66 there is no way to tell Xt to block until there is an X event OR a
67 timeout happens. Once your timeout proc is called, XtAppNextEvent()
68 still won't return until a "real" X event comes in.
70 So this function pushes a stupid, gratuitous, unnecessary event back
71 on the event queue to force XtAppNextEvent to return Right Fucking Now.
72 When the code in sleep_until_idle() sees an event of type XAnyEvent,
73 which the server never generates, it knows that a timeout has occurred.
76 fake_event.type = 0; /* XAnyEvent type, ignored. */
77 fake_event.xany.display = dpy;
78 fake_event.xany.window = 0;
79 XPutBackEvent (dpy, &fake_event);
85 notice_events (Window window, Bool top_p)
87 notice_events (window, top_p)
92 XWindowAttributes attrs;
94 Window root, parent, *kids;
97 if (XtWindowToWidget (dpy, window))
98 /* If it's one of ours, don't mess up its event mask. */
101 if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
106 XGetWindowAttributes (dpy, window, &attrs);
107 events = ((attrs.all_event_masks | attrs.do_not_propagate_mask)
110 /* Select for SubstructureNotify on all windows.
111 Select for KeyPress on all windows that already have it selected.
112 Do we need to select for ButtonRelease? I don't think so.
114 XSelectInput (dpy, window, SubstructureNotifyMask | events);
116 if (top_p && verbose_p && (events & KeyPressMask))
118 /* Only mention one window per tree (hack hack). */
119 printf ("%s: selected KeyPress on 0x%lX\n", progname,
120 (unsigned long) window);
127 notice_events (kids [--nkids], top_p);
128 XFree ((char *) kids);
134 BadWindow_ehandler (dpy, error)
138 /* When we notice a window being created, we spawn a timer that waits
139 30 seconds or so, and then selects events on that window. This error
140 handler is used so that we can cope with the fact that the window
141 may have been destroyed <30 seconds after it was created.
143 if (error->error_code == BadWindow ||
144 error->error_code == BadDrawable)
146 XmuPrintDefaultErrorMessage (dpy, error, stderr);
151 notice_events_timer (closure, timer)
155 Window window = (Window) closure;
156 int (*old_handler) ();
157 old_handler = XSetErrorHandler (BadWindow_ehandler);
158 notice_events (window, True);
160 XSetErrorHandler (old_handler);
164 /* When the screensaver is active, this timer will periodically change
168 cycle_timer (junk1, junk2)
172 Time how_long = cycle;
176 printf ("%s: dbox up; delaying hack change.\n", progname);
177 how_long = 30000; /* 30 secs */
182 printf ("%s: changing graphics hacks.\n", progname);
184 spawn_screenhack (False);
186 cycle_id = XtAppAddTimeOut (app, how_long, cycle_timer, 0);
190 printf ("%s: starting cycle_timer (%ld, %ld)\n",
191 progname, how_long, cycle_id);
197 activate_lock_timer (junk1, junk2)
202 printf ("%s: timed out; activating lock\n", progname);
207 /* Call this when user activity (or "simulated" activity) has been noticed.
210 reset_timers P((void))
212 if (use_saver_extension)
217 printf ("%s: restarting idle_timer (%ld, %ld)\n",
218 progname, timeout, timer_id);
220 XtRemoveTimeOut (timer_id);
221 timer_id = XtAppAddTimeOut (app, timeout, idle_timer, 0);
222 if (cycle_id) abort ();
226 printf ("%s: starting idle_timer (%ld, %ld)\n",
227 progname, timeout, timer_id);
230 last_activity_time = time ((time_t *) 0);
233 /* When we aren't using a server extension, this timer is used to periodically
234 wake up and poll the mouse position, which is possibly more reliable than
235 selecting motion events on every window.
238 check_pointer_timer (closure, this_timer)
240 XtPointer this_timer;
242 static int last_root_x = -1;
243 static int last_root_y = -1;
244 static Window last_child = (Window) -1;
245 static unsigned int last_mask = 0;
247 int root_x, root_y, x, y;
249 XtIntervalId *timerP = (XtIntervalId *) closure;
251 if (use_xidle_extension || use_saver_extension)
254 *timerP = XtAppAddTimeOut (app, pointer_timeout, check_pointer_timer,
257 XQueryPointer (dpy, screensaver_window, &root, &child,
258 &root_x, &root_y, &x, &y, &mask);
259 if (root_x == last_root_x && root_y == last_root_y &&
260 child == last_child && mask == last_mask)
264 if (verbose_p && this_timer)
265 if (root_x == last_root_x && root_y == last_root_y && child == last_child)
266 printf ("%s: modifiers changed at %s.\n", progname, timestring ());
268 printf ("%s: pointer moved at %s.\n", progname, timestring ());
271 last_root_x = root_x;
272 last_root_y = root_y;
281 sleep_until_idle (until_idle_p)
288 if (!use_saver_extension)
290 /* Wake up periodically to ask the server if we are idle. */
291 timer_id = XtAppAddTimeOut (app, timeout, idle_timer, 0);
294 printf ("%s: starting idle_timer (%ld, %ld)\n",
295 progname, timeout, timer_id);
299 if (!use_xidle_extension && !use_saver_extension)
300 /* start polling the mouse position */
301 check_pointer_timer (&check_pointer_timer_id, 0);
306 XtAppNextEvent (app, &event);
308 switch (event.xany.type) {
309 case 0: /* our synthetic "timeout" event has been signalled */
313 #ifdef HAVE_XIDLE_EXTENSION
314 if (use_xidle_extension)
316 if (! XGetIdleTime (dpy, &idle))
318 fprintf (stderr, "%s: %sXGetIdleTime() failed.\n",
319 progname, (verbose_p ? "## " : ""));
324 #endif /* HAVE_XIDLE_EXTENSION */
325 #ifdef HAVE_SAVER_EXTENSION
326 if (use_saver_extension)
328 /* We don't need to do anything in this case - the synthetic
329 event isn't necessary, as we get sent specific events
334 #endif /* HAVE_SAVER_EXTENSION */
336 idle = 1000 * (last_activity_time - time ((time_t *) 0));
341 else if (!use_saver_extension)
343 timer_id = XtAppAddTimeOut (app, timeout - idle,
347 printf ("%s: starting idle_timer (%ld, %ld)\n",
348 progname, timeout - idle, timer_id);
349 #endif /* DEBUG_TIMERS */
355 if (handle_clientmessage (&event, until_idle_p))
360 if (!use_xidle_extension && !use_saver_extension)
362 XtAppAddTimeOut (app, notice_events_timeout, notice_events_timer,
363 (XtPointer) event.xcreatewindow.window);
366 printf ("%s: starting notice_events_timer for 0x%X (%lu)\n",
368 (unsigned int) event.xcreatewindow.window,
369 notice_events_timeout);
370 #endif /* DEBUG_TIMERS */
383 if (event.xany.type == MotionNotify)
384 printf ("%s: MotionNotify at %s\n", progname, timestring ());
385 else if (event.xany.type == KeyPress)
386 printf ("%s: KeyPress seen on 0x%X at %s\n", progname,
387 (unsigned int) event.xkey.window, timestring ());
391 /* We got a user event */
400 #ifdef HAVE_SAVER_EXTENSION
401 if (event.type == saver_ext_event_number)
403 XScreenSaverNotifyEvent *sevent =
404 (XScreenSaverNotifyEvent *) &event;
405 if (sevent->state == ScreenSaverOn)
409 printf ("%s: ScreenSaverOn event received at %s\n",
410 progname, timestring ());
411 # endif /* DEBUG_TIMERS */
413 /* Get the "real" server window out of the way as soon
415 if (server_saver_window &&
416 window_exists_p (dpy, server_saver_window))
417 XUnmapWindow (dpy, server_saver_window);
419 if (sevent->kind != ScreenSaverExternal)
423 "%s: ScreenSaverOn event wasn't of type External!\n",
425 # endif /* DEBUG_TIMERS */
431 else if (sevent->state == ScreenSaverOff)
435 printf ("%s: ScreenSaverOff event received at %s\n",
436 progname, timestring ());
437 # endif /* DEBUG_TIMERS */
443 printf ("%s: unknown ScreenSaver event received at %s\n",
444 progname, timestring ());
445 # endif /* DEBUG_TIMERS */
449 #endif /* HAVE_SAVER_EXTENSION */
451 XtDispatchEvent (&event);
456 if (check_pointer_timer_id)
458 XtRemoveTimeOut (check_pointer_timer_id);
459 check_pointer_timer_id = 0;
463 XtRemoveTimeOut (timer_id);
467 if (until_idle_p && cycle_id)