1 /* xscreensaver, Copyright (c) 1991-1993 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
14 #include <X11/Intrinsic.h>
17 #include <X11/Xmu/Error.h>
19 #include "sys$common:[decw$include.xmu]Error.h"
23 #include <X11/extensions/xidle.h>
26 #include "xscreensaver.h"
34 extern XtAppContext app;
39 Time notice_events_timeout;
41 extern Bool use_xidle;
42 extern Bool dbox_up_p;
44 extern Window screensaver_window;
46 extern Bool handle_clientmessage P((XEvent *, Bool));
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;
55 idle_timer (junk1, junk2)
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.
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.
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);
80 notice_events (Window window, Bool top_p)
82 notice_events (window, top_p)
87 XWindowAttributes attrs;
89 Window root, parent, *kids;
92 if (XtWindowToWidget (dpy, window))
93 /* If it's one of ours, don't mess up its event mask. */
96 if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
101 XGetWindowAttributes (dpy, window, &attrs);
102 events = ((attrs.all_event_masks | attrs.do_not_propagate_mask)
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.
109 XSelectInput (dpy, window, SubstructureNotifyMask | events);
111 if (top_p && verbose_p && (events & KeyPressMask))
113 /* Only mention one window per tree (hack hack). */
114 printf ("%s: selected KeyPress on 0x%X\n", progname, window);
121 notice_events (kids [--nkids], top_p);
122 XFree ((char *) kids);
128 BadWindow_ehandler (dpy, error)
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.
137 if (error->error_code == BadWindow ||
138 error->error_code == BadDrawable)
140 XmuPrintDefaultErrorMessage (dpy, error, stderr);
145 notice_events_timer (closure, timer)
149 Window window = (Window) closure;
150 int (*old_handler) ();
151 old_handler = XSetErrorHandler (BadWindow_ehandler);
152 notice_events (window, True);
154 XSetErrorHandler (old_handler);
158 /* When the screensaver is active, this timer will periodically change
162 cycle_timer (junk1, junk2)
166 Time how_long = cycle;
170 printf ("%s: dbox up; delaying hack change.\n", progname);
171 how_long = 30000; /* 30 secs */
176 printf ("%s: changing graphics hacks.\n", progname);
178 spawn_screenhack (False);
180 cycle_id = XtAppAddTimeOut (app, how_long, cycle_timer, 0);
185 activate_lock_timer (junk1, junk2)
190 printf ("%s: timed out; activating lock\n", progname);
195 /* Call this when user activity (or "simulated" activity) has been noticed.
198 reset_timers P((void))
202 printf ("%s: restarting idle_timer (%d, %d)\n",
203 progname, timeout, timer_id);
205 XtRemoveTimeOut (timer_id);
206 timer_id = XtAppAddTimeOut (app, timeout, idle_timer, 0);
207 if (cycle_id) abort ();
209 last_activity_time = time ((time_t *) 0);
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.
217 check_pointer_timer (closure, this_timer)
219 XtPointer this_timer;
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;
226 int root_x, root_y, x, y;
228 XtIntervalId *timerP = (XtIntervalId *) closure;
234 *timerP = XtAppAddTimeOut (app, pointer_timeout, check_pointer_timer,
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)
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 ());
248 printf ("%s: pointer moved at %s.\n", progname, timestring ());
251 last_root_x = root_x;
252 last_root_y = root_y;
261 sleep_until_idle (until_idle_p)
268 timer_id = XtAppAddTimeOut (app, timeout, idle_timer, 0);
272 /* start polling the mouse position */
273 check_pointer_timer (&check_pointer_timer_id, 0);
278 XtAppNextEvent (app, &event);
280 switch (event.xany.type) {
281 case 0: /* our synthetic "timeout" event has been signalled */
288 if (! XGetIdleTime (dpy, &idle))
290 fprintf (stderr, "%s: %sXGetIdleTime() failed.\n",
291 progname, (verbose_p ? "## " : ""));
296 #endif /* HAVE_XIDLE */
297 idle = 1000 * (last_activity_time - time ((time_t *) 0));
302 timer_id = XtAppAddTimeOut (app, timeout - idle,
308 if (handle_clientmessage (&event, until_idle_p))
316 XtAppAddTimeOut (app, notice_events_timeout, notice_events_timer,
317 (XtPointer) event.xcreatewindow.window);
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 ());
337 /* We got a user event */
345 XtDispatchEvent (&event);
350 if (check_pointer_timer_id)
352 XtRemoveTimeOut (check_pointer_timer_id);
353 check_pointer_timer_id = 0;
357 XtRemoveTimeOut (timer_id);
361 if (until_idle_p && cycle_id)