1 /* xscreensaver, Copyright (c) 1991-1995 Jamie Zawinski <jwz@netscape.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 == BadMatch ||
145 error->error_code == BadDrawable)
147 XmuPrintDefaultErrorMessage (dpy, error, stderr);
152 notice_events_timer (closure, timer)
156 Window window = (Window) closure;
157 int (*old_handler) ();
158 old_handler = XSetErrorHandler (BadWindow_ehandler);
159 notice_events (window, True);
161 XSetErrorHandler (old_handler);
165 /* When the screensaver is active, this timer will periodically change
169 cycle_timer (junk1, junk2)
173 Time how_long = cycle;
177 printf ("%s: dbox up; delaying hack change.\n", progname);
178 how_long = 30000; /* 30 secs */
183 printf ("%s: changing graphics hacks.\n", progname);
185 spawn_screenhack (False);
187 cycle_id = XtAppAddTimeOut (app, how_long, cycle_timer, 0);
191 printf ("%s: starting cycle_timer (%ld, %ld)\n",
192 progname, how_long, cycle_id);
198 activate_lock_timer (junk1, junk2)
203 printf ("%s: timed out; activating lock\n", progname);
208 /* Call this when user activity (or "simulated" activity) has been noticed.
211 reset_timers P((void))
213 if (use_saver_extension)
218 printf ("%s: restarting idle_timer (%ld, %ld)\n",
219 progname, timeout, timer_id);
221 XtRemoveTimeOut (timer_id);
222 timer_id = XtAppAddTimeOut (app, timeout, idle_timer, 0);
223 if (cycle_id) abort ();
227 printf ("%s: starting idle_timer (%ld, %ld)\n",
228 progname, timeout, timer_id);
231 last_activity_time = time ((time_t *) 0);
234 /* When we aren't using a server extension, this timer is used to periodically
235 wake up and poll the mouse position, which is possibly more reliable than
236 selecting motion events on every window.
239 check_pointer_timer (closure, this_timer)
241 XtPointer this_timer;
243 static int last_root_x = -1;
244 static int last_root_y = -1;
245 static Window last_child = (Window) -1;
246 static unsigned int last_mask = 0;
248 int root_x, root_y, x, y;
250 XtIntervalId *timerP = (XtIntervalId *) closure;
252 if (use_xidle_extension || use_saver_extension)
255 *timerP = XtAppAddTimeOut (app, pointer_timeout, check_pointer_timer,
258 XQueryPointer (dpy, screensaver_window, &root, &child,
259 &root_x, &root_y, &x, &y, &mask);
260 if (root_x == last_root_x && root_y == last_root_y &&
261 child == last_child && mask == last_mask)
265 if (verbose_p && this_timer)
266 if (root_x == last_root_x && root_y == last_root_y && child == last_child)
267 printf ("%s: modifiers changed at %s.\n", progname, timestring ());
269 printf ("%s: pointer moved at %s.\n", progname, timestring ());
272 last_root_x = root_x;
273 last_root_y = root_y;
282 sleep_until_idle (until_idle_p)
289 if (!use_saver_extension)
291 /* Wake up periodically to ask the server if we are idle. */
292 timer_id = XtAppAddTimeOut (app, timeout, idle_timer, 0);
295 printf ("%s: starting idle_timer (%ld, %ld)\n",
296 progname, timeout, timer_id);
300 if (!use_xidle_extension && !use_saver_extension)
301 /* start polling the mouse position */
302 check_pointer_timer (&check_pointer_timer_id, 0);
307 XtAppNextEvent (app, &event);
309 switch (event.xany.type) {
310 case 0: /* our synthetic "timeout" event has been signalled */
314 #ifdef HAVE_XIDLE_EXTENSION
315 if (use_xidle_extension)
317 if (! XGetIdleTime (dpy, &idle))
319 fprintf (stderr, "%s: %sXGetIdleTime() failed.\n",
320 progname, (verbose_p ? "## " : ""));
325 #endif /* HAVE_XIDLE_EXTENSION */
326 #ifdef HAVE_SAVER_EXTENSION
327 if (use_saver_extension)
329 /* We don't need to do anything in this case - the synthetic
330 event isn't necessary, as we get sent specific events
335 #endif /* HAVE_SAVER_EXTENSION */
337 idle = 1000 * (last_activity_time - time ((time_t *) 0));
342 else if (!use_saver_extension)
344 timer_id = XtAppAddTimeOut (app, timeout - idle,
348 printf ("%s: starting idle_timer (%ld, %ld)\n",
349 progname, timeout - idle, timer_id);
350 #endif /* DEBUG_TIMERS */
356 if (handle_clientmessage (&event, until_idle_p))
361 if (!use_xidle_extension && !use_saver_extension)
363 XtAppAddTimeOut (app, notice_events_timeout, notice_events_timer,
364 (XtPointer) event.xcreatewindow.window);
367 printf ("%s: starting notice_events_timer for 0x%X (%lu)\n",
369 (unsigned int) event.xcreatewindow.window,
370 notice_events_timeout);
371 #endif /* DEBUG_TIMERS */
384 if (event.xany.type == MotionNotify)
385 printf ("%s: MotionNotify at %s\n", progname, timestring ());
386 else if (event.xany.type == KeyPress)
387 printf ("%s: KeyPress seen on 0x%X at %s\n", progname,
388 (unsigned int) event.xkey.window, timestring ());
392 /* We got a user event */
401 #ifdef HAVE_SAVER_EXTENSION
402 if (event.type == saver_ext_event_number)
404 XScreenSaverNotifyEvent *sevent =
405 (XScreenSaverNotifyEvent *) &event;
406 if (sevent->state == ScreenSaverOn)
410 printf ("%s: ScreenSaverOn event received at %s\n",
411 progname, timestring ());
412 # endif /* DEBUG_TIMERS */
414 /* Get the "real" server window out of the way as soon
416 if (server_saver_window &&
417 window_exists_p (dpy, server_saver_window))
418 XUnmapWindow (dpy, server_saver_window);
420 if (sevent->kind != ScreenSaverExternal)
424 "%s: ScreenSaverOn event wasn't of type External!\n",
426 # endif /* DEBUG_TIMERS */
432 else if (sevent->state == ScreenSaverOff)
436 printf ("%s: ScreenSaverOff event received at %s\n",
437 progname, timestring ());
438 # endif /* DEBUG_TIMERS */
444 printf ("%s: unknown ScreenSaver event received at %s\n",
445 progname, timestring ());
446 # endif /* DEBUG_TIMERS */
450 #endif /* HAVE_SAVER_EXTENSION */
452 XtDispatchEvent (&event);
457 if (check_pointer_timer_id)
459 XtRemoveTimeOut (check_pointer_timer_id);
460 check_pointer_timer_id = 0;
464 XtRemoveTimeOut (timer_id);
468 if (until_idle_p && cycle_id)