1 /* xscreensaver, Copyright (c) 1991-1996 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_MIT_SAVER_EXTENSION
25 #include <X11/extensions/scrnsaver.h>
26 extern int mit_saver_ext_event_number;
27 extern Window server_mit_saver_window;
28 #endif /* HAVE_MIT_SAVER_EXTENSION */
30 #ifdef HAVE_SGI_SAVER_EXTENSION
31 #include <X11/extensions/XScreenSaver.h>
32 extern int sgi_saver_ext_event_number;
33 #endif /* HAVE_SGI_SAVER_EXTENSION */
35 #include "xscreensaver.h"
43 extern XtAppContext app;
48 Time notice_events_timeout;
50 extern Bool use_xidle_extension;
51 extern Bool use_mit_saver_extension;
52 extern Bool use_sgi_saver_extension;
53 extern Bool dbox_up_p;
55 extern Window screensaver_window;
57 extern Bool handle_clientmessage P((XEvent *, Bool));
59 static time_t last_activity_time; /* for when we have no server extensions */
60 static XtIntervalId timer_id = 0;
61 static XtIntervalId check_pointer_timer_id = 0;
62 XtIntervalId cycle_id = 0;
63 XtIntervalId lock_id = 0;
66 idle_timer (junk1, junk2)
70 /* What an amazingly shitty design. Not only does Xt execute timeout
71 events from XtAppNextEvent() instead of from XtDispatchEvent(), but
72 there is no way to tell Xt to block until there is an X event OR a
73 timeout happens. Once your timeout proc is called, XtAppNextEvent()
74 still won't return until a "real" X event comes in.
76 So this function pushes a stupid, gratuitous, unnecessary event back
77 on the event queue to force XtAppNextEvent to return Right Fucking Now.
78 When the code in sleep_until_idle() sees an event of type XAnyEvent,
79 which the server never generates, it knows that a timeout has occurred.
82 fake_event.type = 0; /* XAnyEvent type, ignored. */
83 fake_event.xany.display = dpy;
84 fake_event.xany.window = 0;
85 XPutBackEvent (dpy, &fake_event);
91 notice_events (Window window, Bool top_p)
93 notice_events (window, top_p)
98 XWindowAttributes attrs;
100 Window root, parent, *kids;
103 if (XtWindowToWidget (dpy, window))
104 /* If it's one of ours, don't mess up its event mask. */
107 if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
112 XGetWindowAttributes (dpy, window, &attrs);
113 events = ((attrs.all_event_masks | attrs.do_not_propagate_mask)
116 /* Select for SubstructureNotify on all windows.
117 Select for KeyPress on all windows that already have it selected.
118 Do we need to select for ButtonRelease? I don't think so.
120 XSelectInput (dpy, window, SubstructureNotifyMask | events);
122 if (top_p && verbose_p && (events & KeyPressMask))
124 /* Only mention one window per tree (hack hack). */
125 printf ("%s: selected KeyPress on 0x%lX\n", progname,
126 (unsigned long) window);
133 notice_events (kids [--nkids], top_p);
134 XFree ((char *) kids);
140 BadWindow_ehandler (dpy, error)
144 /* When we notice a window being created, we spawn a timer that waits
145 30 seconds or so, and then selects events on that window. This error
146 handler is used so that we can cope with the fact that the window
147 may have been destroyed <30 seconds after it was created.
149 if (error->error_code == BadWindow ||
150 error->error_code == BadMatch ||
151 error->error_code == BadDrawable)
153 XmuPrintDefaultErrorMessage (dpy, error, stderr);
158 notice_events_timer (closure, timer)
162 Window window = (Window) closure;
163 int (*old_handler) ();
164 old_handler = XSetErrorHandler (BadWindow_ehandler);
165 notice_events (window, True);
167 XSetErrorHandler (old_handler);
171 /* When the screensaver is active, this timer will periodically change
175 cycle_timer (junk1, junk2)
179 Time how_long = cycle;
183 printf ("%s: dbox up; delaying hack change.\n", progname);
184 how_long = 30000; /* 30 secs */
189 printf ("%s: changing graphics hacks.\n", progname);
191 spawn_screenhack (False);
193 cycle_id = XtAppAddTimeOut (app, how_long,
194 (XtTimerCallbackProc) cycle_timer, 0);
198 printf ("%s: starting cycle_timer (%ld, %ld)\n",
199 progname, how_long, cycle_id);
205 activate_lock_timer (junk1, junk2)
210 printf ("%s: timed out; activating lock\n", progname);
215 /* Call this when user activity (or "simulated" activity) has been noticed.
218 reset_timers P((void))
220 if (use_mit_saver_extension || use_sgi_saver_extension)
225 printf ("%s: restarting idle_timer (%ld, %ld)\n",
226 progname, timeout, timer_id);
228 XtRemoveTimeOut (timer_id);
229 timer_id = XtAppAddTimeOut (app, timeout,
230 (XtTimerCallbackProc) idle_timer, 0);
231 if (cycle_id) abort ();
235 printf ("%s: starting idle_timer (%ld, %ld)\n",
236 progname, timeout, timer_id);
239 last_activity_time = time ((time_t *) 0);
242 /* When we aren't using a server extension, this timer is used to periodically
243 wake up and poll the mouse position, which is possibly more reliable than
244 selecting motion events on every window.
247 check_pointer_timer (closure, this_timer)
249 XtPointer this_timer;
251 static int last_root_x = -1;
252 static int last_root_y = -1;
253 static Window last_child = (Window) -1;
254 static unsigned int last_mask = 0;
256 int root_x, root_y, x, y;
258 XtIntervalId *timerP = (XtIntervalId *) closure;
260 if (use_xidle_extension ||
261 use_mit_saver_extension ||
262 use_sgi_saver_extension)
265 *timerP = XtAppAddTimeOut (app, pointer_timeout,
266 (XtTimerCallbackProc) check_pointer_timer,
269 XQueryPointer (dpy, screensaver_window, &root, &child,
270 &root_x, &root_y, &x, &y, &mask);
271 if (root_x == last_root_x && root_y == last_root_y &&
272 child == last_child && mask == last_mask)
276 if (verbose_p && this_timer)
277 if (root_x == last_root_x && root_y == last_root_y && child == last_child)
278 printf ("%s: modifiers changed at %s.\n", progname, timestring ());
280 printf ("%s: pointer moved at %s.\n", progname, timestring ());
283 last_root_x = root_x;
284 last_root_y = root_y;
293 sleep_until_idle (until_idle_p)
300 if (!use_mit_saver_extension && !use_sgi_saver_extension)
302 /* Wake up periodically to ask the server if we are idle. */
303 timer_id = XtAppAddTimeOut (app, timeout,
304 (XtTimerCallbackProc) idle_timer, 0);
307 printf ("%s: starting idle_timer (%ld, %ld)\n",
308 progname, timeout, timer_id);
312 if (!use_xidle_extension &&
313 !use_mit_saver_extension &&
314 !use_sgi_saver_extension)
315 /* start polling the mouse position */
316 check_pointer_timer (&check_pointer_timer_id, 0);
321 XtAppNextEvent (app, &event);
323 switch (event.xany.type) {
324 case 0: /* our synthetic "timeout" event has been signalled */
328 #ifdef HAVE_XIDLE_EXTENSION
329 if (use_xidle_extension)
331 if (! XGetIdleTime (dpy, &idle))
333 fprintf (stderr, "%s: %sXGetIdleTime() failed.\n",
334 progname, (verbose_p ? "## " : ""));
339 #endif /* HAVE_XIDLE_EXTENSION */
340 #ifdef HAVE_MIT_SAVER_EXTENSION
341 if (use_mit_saver_extension)
343 /* We don't need to do anything in this case - the synthetic
344 event isn't necessary, as we get sent specific events
349 #endif /* HAVE_MIT_SAVER_EXTENSION */
350 #ifdef HAVE_SGI_SAVER_EXTENSION
351 if (use_sgi_saver_extension)
353 /* We don't need to do anything in this case - the synthetic
354 event isn't necessary, as we get sent specific events
359 #endif /* HAVE_SGI_SAVER_EXTENSION */
361 idle = 1000 * (last_activity_time - time ((time_t *) 0));
366 else if (!use_mit_saver_extension && !use_sgi_saver_extension)
368 timer_id = XtAppAddTimeOut (app, timeout - idle,
369 (XtTimerCallbackProc) idle_timer,
373 printf ("%s: starting idle_timer (%ld, %ld)\n",
374 progname, timeout - idle, timer_id);
375 #endif /* DEBUG_TIMERS */
381 if (handle_clientmessage (&event, until_idle_p))
386 if (!use_xidle_extension &&
387 !use_mit_saver_extension &&
388 !use_sgi_saver_extension)
390 XtAppAddTimeOut (app, notice_events_timeout,
391 (XtTimerCallbackProc) notice_events_timer,
392 (XtPointer) event.xcreatewindow.window);
395 printf ("%s: starting notice_events_timer for 0x%X (%lu)\n",
397 (unsigned int) event.xcreatewindow.window,
398 notice_events_timeout);
399 #endif /* DEBUG_TIMERS */
412 if (event.xany.type == MotionNotify)
413 printf ("%s: MotionNotify at %s\n", progname, timestring ());
414 else if (event.xany.type == KeyPress)
415 printf ("%s: KeyPress seen on 0x%X at %s\n", progname,
416 (unsigned int) event.xkey.window, timestring ());
420 /* We got a user event */
429 #ifdef HAVE_MIT_SAVER_EXTENSION
430 if (event.type == mit_saver_ext_event_number)
432 XScreenSaverNotifyEvent *sevent =
433 (XScreenSaverNotifyEvent *) &event;
434 if (sevent->state == ScreenSaverOn)
438 printf ("%s: ScreenSaverOn event received at %s\n",
439 progname, timestring ());
440 # endif /* DEBUG_TIMERS */
442 /* Get the "real" server window out of the way as soon
444 if (server_mit_saver_window &&
445 window_exists_p (dpy, server_mit_saver_window))
446 XUnmapWindow (dpy, server_mit_saver_window);
448 if (sevent->kind != ScreenSaverExternal)
452 "%s: ScreenSaverOn event wasn't of type External!\n",
454 # endif /* DEBUG_TIMERS */
460 else if (sevent->state == ScreenSaverOff)
464 printf ("%s: ScreenSaverOff event received at %s\n",
465 progname, timestring ());
466 # endif /* DEBUG_TIMERS */
472 printf ("%s: unknown MIT-SCREEN-SAVER event received at %s\n",
473 progname, timestring ());
474 # endif /* DEBUG_TIMERS */
478 #endif /* HAVE_MIT_SAVER_EXTENSION */
481 #ifdef HAVE_SGI_SAVER_EXTENSION
482 if (event.type == (sgi_saver_ext_event_number + ScreenSaverStart))
486 printf ("%s: ScreenSaverStart event received at %s\n",
487 progname, timestring ());
488 # endif /* DEBUG_TIMERS */
493 else if (event.type == (sgi_saver_ext_event_number + ScreenSaverEnd))
497 printf ("%s: ScreenSaverEnd event received at %s\n",
498 progname, timestring ());
499 # endif /* DEBUG_TIMERS */
504 #endif /* HAVE_SGI_SAVER_EXTENSION */
506 XtDispatchEvent (&event);
511 if (check_pointer_timer_id)
513 XtRemoveTimeOut (check_pointer_timer_id);
514 check_pointer_timer_id = 0;
518 XtRemoveTimeOut (timer_id);
522 if (until_idle_p && cycle_id)