1 /* xscreensaver, Copyright (c) 1991-1993 Jamie Zawinski <jwz@lucid.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
14 #include <X11/Intrinsic.h>
16 #include <X11/Xmu/Error.h>
19 #include <X11/extensions/xidle.h>
22 #include "xscreensaver.h"
24 extern XtAppContext app;
29 Time notice_events_timeout;
31 extern Bool use_xidle;
32 extern Bool dbox_up_p;
34 extern Window screensaver_window;
36 extern Bool handle_clientmessage P((/*XEvent *, Bool*/));
38 static time_t last_activity_time; /* for non-XIdle mode */
39 static XtIntervalId timer_id = 0;
40 static XtIntervalId check_pointer_timer_id = 0;
41 XtIntervalId cycle_id = 0;
42 XtIntervalId lock_id = 0;
45 idle_timer (junk1, junk2)
49 /* What an amazingly shitty design. Not only does Xt execute timeout
50 events from XtAppNextEvent() instead of from XtDispatchEvent(), but
51 there is no way to tell Xt to block until there is an X event OR a
52 timeout happens. Once your timeout proc is called, XtAppNextEvent()
53 still won't return until a "real" X event comes in.
55 So this function pushes a stupid, gratuitous, unnecessary event back
56 on the event queue to force XtAppNextEvent to return Right Fucking Now.
57 When the code in sleep_until_idle() sees an event of type XAnyEvent,
58 which the server never generates, it knows that a timeout has occurred.
61 fake_event.type = 0; /* XAnyEvent type, ignored. */
62 fake_event.xany.display = dpy;
63 fake_event.xany.window = 0;
64 XPutBackEvent (dpy, &fake_event);
69 notice_events (window, top_p)
73 XWindowAttributes attrs;
75 Window root, parent, *kids;
78 if (XtWindowToWidget (dpy, window))
79 /* If it's one of ours, don't mess up its event mask. */
82 if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
87 XGetWindowAttributes (dpy, window, &attrs);
88 events = ((attrs.all_event_masks | attrs.do_not_propagate_mask)
91 /* Select for SubstructureNotify on all windows.
92 Select for KeyPress on all windows that already have it selected.
93 Do we need to select for ButtonRelease? I don't think so.
95 XSelectInput (dpy, window, SubstructureNotifyMask | events);
97 if (top_p && verbose_p && (events & KeyPressMask))
99 /* Only mention one window per tree (hack hack). */
100 printf ("%s: selected KeyPress on 0x%X\n", progname, window);
107 notice_events (kids [--nkids], top_p);
108 XFree ((char *) kids);
114 BadWindow_ehandler (dpy, error)
118 /* When we notice a window being created, we spawn a timer that waits
119 30 seconds or so, and then selects events on that window. This error
120 handler is used so that we can cope with the fact that the window
121 may have been destroyed <30 seconds after it was created.
123 if (error->error_code == BadWindow ||
124 error->error_code == BadDrawable)
126 XmuPrintDefaultErrorMessage (dpy, error, stderr);
131 notice_events_timer (closure, timer)
135 Window window = (Window) closure;
136 int (*old_handler) ();
137 old_handler = XSetErrorHandler (BadWindow_ehandler);
138 notice_events (window, True);
140 XSetErrorHandler (old_handler);
144 /* When the screensaver is active, this timer will periodically change
148 cycle_timer (junk1, junk2)
152 Time how_long = cycle;
156 printf ("%s: dbox up; delaying hack change.\n", progname);
157 how_long = 30000; /* 30 secs */
162 printf ("%s: changing graphics hacks.\n", progname);
164 spawn_screenhack (False);
166 cycle_id = XtAppAddTimeOut (app, how_long, cycle_timer, 0);
171 activate_lock_timer (junk1, junk2)
176 printf ("%s: timed out; activating lock\n", progname);
181 /* Call this when user activity (or "simulated" activity) has been noticed.
188 printf ("%s: restarting idle_timer (%d, %d)\n",
189 progname, timeout, timer_id);
191 XtRemoveTimeOut (timer_id);
192 timer_id = XtAppAddTimeOut (app, timeout, idle_timer, 0);
193 if (cycle_id) abort ();
195 last_activity_time = time ((time_t *) 0);
198 /* When we aren't using XIdle, this timer is used to periodically wake up
199 and poll the mouse position, which is possibly more reliable than
200 selecting motion events on every window.
203 check_pointer_timer (closure, this_timer)
205 XtPointer this_timer;
207 static int last_root_x = -1;
208 static int last_root_y = -1;
209 static Window last_child = (Window) -1;
210 static unsigned int last_mask = 0;
212 int root_x, root_y, x, y;
214 XtIntervalId *timerP = (XtIntervalId *) closure;
220 *timerP = XtAppAddTimeOut (app, pointer_timeout, check_pointer_timer,
223 XQueryPointer (dpy, screensaver_window, &root, &child,
224 &root_x, &root_y, &x, &y, &mask);
225 if (root_x == last_root_x && root_y == last_root_y &&
226 child == last_child && mask == last_mask)
230 if (verbose_p && this_timer)
231 if (root_x == last_root_x && root_y == last_root_y && child == last_child)
232 printf ("%s: modifiers changed at %s.\n", progname, timestring ());
234 printf ("%s: pointer moved at %s.\n", progname, timestring ());
237 last_root_x = root_x;
238 last_root_y = root_y;
247 sleep_until_idle (until_idle_p)
254 timer_id = XtAppAddTimeOut (app, timeout, idle_timer, 0);
258 /* start polling the mouse position */
259 check_pointer_timer (&check_pointer_timer_id, 0);
264 XtAppNextEvent (app, &event);
266 switch (event.xany.type) {
267 case 0: /* our synthetic "timeout" event has been signalled */
274 if (! XGetIdleTime (dpy, &idle))
276 fprintf (stderr, "%s: %sXGetIdleTime() failed.\n",
277 progname, (verbose_p ? "## " : ""));
282 #endif /* HAVE_XIDLE */
283 idle = 1000 * (last_activity_time - time ((time_t *) 0));
288 timer_id = XtAppAddTimeOut (app, timeout - idle,
294 if (handle_clientmessage (&event, until_idle_p))
302 XtAppAddTimeOut (app, notice_events_timeout, notice_events_timer,
303 (XtPointer) event.xcreatewindow.window);
315 if (event.xany.type == MotionNotify)
316 printf ("%s: MotionNotify at %s\n", progname, timestring ());
317 else if (event.xany.type == KeyPress)
318 printf ("%s: KeyPress seen on 0x%X at %s\n", progname,
319 event.xkey.window, timestring ());
323 /* We got a user event */
331 XtDispatchEvent (&event);
336 if (check_pointer_timer_id)
338 XtRemoveTimeOut (check_pointer_timer_id);
339 check_pointer_timer_id = 0;
343 XtRemoveTimeOut (timer_id);
347 if (until_idle_p && cycle_id)