/* timers.c --- detecting when the user is idle, and other timer-related tasks.
- * xscreensaver, Copyright (c) 1991-2004 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 1991-2012 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
}
else
{
+ int i;
maybe_reload_init_file (si);
- kill_screenhack (si);
+ for (i = 0; i < si->nscreens; i++)
+ kill_screenhack (&si->screens[i]);
+
+ raise_window (si, True, True, False);
if (!si->throttled_p)
- spawn_screenhack (si, False);
+ for (i = 0; i < si->nscreens; i++)
+ spawn_screenhack (&si->screens[i]);
else
{
- raise_window (si, True, True, False);
if (p->verbose_p)
fprintf (stderr, "%s: not launching new hack (throttled.)\n",
blurb());
DPMS timer. Without this, the -deactivate clientmessage would
prevent xscreensaver from blanking, but would not prevent the
monitor from powering down. */
+#if 0
+ /* #### With some servers, this causes the screen to flicker every
+ time a key is pressed! Ok, I surrender. I give up on ever
+ having DPMS work properly.
+ */
XForceScreenSaver (si->dpy, ScreenSaverReset);
/* And if the monitor is already powered off, turn it on.
You'd think the above would do that, but apparently not? */
- monitor_power_on (si);
+ monitor_power_on (si, True);
+#endif
+
}
-/* Returns true if the mouse has moved since the last time we checked.
+/* Returns true if a mouse has moved since the last time we checked.
Small motions (of less than "hysteresis" pixels/second) are ignored.
*/
static Bool
-pointer_moved_p (saver_screen_info *ssi, Bool mods_p)
+device_pointer_moved_p (saver_info *si, poll_mouse_data *last_poll_mouse,
+ poll_mouse_data *this_poll_mouse, Bool mods_p,
+ const char *debug_type, int debug_id)
{
- saver_info *si = ssi->global;
saver_preferences *p = &si->prefs;
- Window root, child;
- int root_x, root_y, x, y;
- unsigned int mask;
- time_t now = time ((time_t *) 0);
unsigned int distance, dps;
unsigned long seconds = 0;
Bool moved_p = False;
- /* don't check xinerama pseudo-screens. */
- if (!ssi->real_screen_p) return False;
-
- if (!XQueryPointer (si->dpy, ssi->screensaver_window, &root, &child,
- &root_x, &root_y, &x, &y, &mask))
- {
- /* If XQueryPointer() returns false, the mouse is not on this screen.
- */
- x = root_x = -1;
- y = root_y = -1;
- root = child = 0;
- mask = 0;
- }
-
- distance = MAX (ABS (ssi->poll_mouse_last_root_x - root_x),
- ABS (ssi->poll_mouse_last_root_y - root_y));
- seconds = (now - ssi->poll_mouse_last_time);
+ distance = MAX (ABS (last_poll_mouse->root_x - this_poll_mouse->root_x),
+ ABS (last_poll_mouse->root_y - this_poll_mouse->root_y));
+ seconds = (this_poll_mouse->time - last_poll_mouse->time);
/* When the screen is blanked, we get MotionNotify events, but when not
If the mouse was not on this screen, but is now, that's motion.
*/
{
- Bool on_screen_p = (root_x != -1 && root_y != -1);
- Bool was_on_screen_p = (ssi->poll_mouse_last_root_x != -1 &&
- ssi->poll_mouse_last_root_y != -1);
+ Bool on_screen_p = (this_poll_mouse->root_x != -1 &&
+ this_poll_mouse->root_y != -1);
+ Bool was_on_screen_p = (last_poll_mouse->root_x != -1 &&
+ last_poll_mouse->root_y != -1);
if (on_screen_p != was_on_screen_p)
moved_p = True;
if (p->debug_p && (distance != 0 || moved_p))
{
- fprintf (stderr, "%s: %d: pointer %s", blurb(), ssi->number,
+ fprintf (stderr, "%s: %s %d: pointer %s", blurb(), debug_type, debug_id,
(moved_p ? "moved: " : "ignored:"));
- if (ssi->poll_mouse_last_root_x == -1)
+ if (last_poll_mouse->root_x == -1)
fprintf (stderr, "off screen");
else
fprintf (stderr, "%d,%d",
- ssi->poll_mouse_last_root_x,
- ssi->poll_mouse_last_root_y);
+ last_poll_mouse->root_x,
+ last_poll_mouse->root_y);
fprintf (stderr, " -> ");
- if (root_x == -1)
+ if (this_poll_mouse->root_x == -1)
fprintf (stderr, "off screen");
else
- fprintf (stderr, "%d,%d", root_x, root_y);
- if (ssi->poll_mouse_last_root_x != -1 && root_x != -1)
+ fprintf (stderr, "%d,%d", this_poll_mouse->root_x,
+ this_poll_mouse->root_y);
+ if (last_poll_mouse->root_x != -1 && this_poll_mouse->root_x != -1)
fprintf (stderr, " (%d,%d; %d/%lu=%d)",
- ABS(ssi->poll_mouse_last_root_x - root_x),
- ABS(ssi->poll_mouse_last_root_y - root_y),
+ ABS(last_poll_mouse->root_x - this_poll_mouse->root_x),
+ ABS(last_poll_mouse->root_y - this_poll_mouse->root_y),
distance, seconds, dps);
fprintf (stderr, ".\n");
if (!moved_p &&
mods_p &&
- mask != ssi->poll_mouse_last_mask)
+ this_poll_mouse->mask != last_poll_mouse->mask)
{
moved_p = True;
if (p->debug_p)
- fprintf (stderr, "%s: %d: modifiers changed: 0x%04x -> 0x%04x.\n",
- blurb(), ssi->number, ssi->poll_mouse_last_mask, mask);
+ fprintf (stderr, "%s: %s %d: modifiers changed: 0x%04x -> 0x%04x.\n",
+ blurb(), debug_type, debug_id,
+ last_poll_mouse->mask, this_poll_mouse->mask);
}
- si->last_activity_screen = ssi;
- ssi->poll_mouse_last_child = child;
- ssi->poll_mouse_last_mask = mask;
+ last_poll_mouse->child = this_poll_mouse->child;
+ last_poll_mouse->mask = this_poll_mouse->mask;
if (moved_p || seconds > 0)
{
- ssi->poll_mouse_last_time = now;
- ssi->poll_mouse_last_root_x = root_x;
- ssi->poll_mouse_last_root_y = root_y;
+ last_poll_mouse->time = this_poll_mouse->time;
+ last_poll_mouse->root_x = this_poll_mouse->root_x;
+ last_poll_mouse->root_y = this_poll_mouse->root_y;
}
return moved_p;
}
+/* Returns true if core mouse pointer has moved since the last time we checked.
+ */
+static Bool
+pointer_moved_p (saver_screen_info *ssi, Bool mods_p)
+{
+ saver_info *si = ssi->global;
+
+ Window root;
+ poll_mouse_data this_poll_mouse;
+ int x, y;
+
+ /* don't check xinerama pseudo-screens. */
+ if (!ssi->real_screen_p) return False;
+
+ this_poll_mouse.time = time ((time_t *) 0);
+
+ if (!XQueryPointer (si->dpy, ssi->screensaver_window, &root,
+ &this_poll_mouse.child,
+ &this_poll_mouse.root_x, &this_poll_mouse.root_y,
+ &x, &y, &this_poll_mouse.mask))
+ {
+ /* If XQueryPointer() returns false, the mouse is not on this screen.
+ */
+ this_poll_mouse.root_x = -1;
+ this_poll_mouse.root_y = -1;
+ this_poll_mouse.child = 0;
+ this_poll_mouse.mask = 0;
+ }
+ else
+ si->last_activity_screen = ssi;
+
+ return device_pointer_moved_p(si, &(ssi->last_poll_mouse), &this_poll_mouse,
+ mods_p, "screen", ssi->number);
+}
+
/* When we aren't using a server extension, this timer is used to periodically
wake up and poll the mouse position, which is possibly more reliable than
break;
case '\025': case '\030': /* Erase line */
case '\012': case '\015': /* Enter */
+ case '\033': /* ESC */
i = 0;
break;
case '\040': /* Space */
sleep_until_idle (saver_info *si, Bool until_idle_p)
{
saver_preferences *p = &si->prefs;
- XEvent event;
+
+ /* We have to go through this union bullshit because gcc-4.4.0 has
+ stricter struct-aliasing rules. Without this, the optimizer
+ can fuck things up.
+ */
+ union {
+ XEvent x_event;
+# ifdef HAVE_RANDR
+ XRRScreenChangeNotifyEvent xrr_event;
+# endif /* HAVE_RANDR */
+# ifdef HAVE_MIT_SAVER_EXTENSION
+ XScreenSaverNotifyEvent sevent;
+# endif /* HAVE_MIT_SAVER_EXTENSION */
+ } event;
/* We need to select events on all windows if we're not using any extensions.
Otherwise, we don't need to. */
Bool polling_mouse_position = (si->using_proc_interrupts ||
!(si->using_xidle_extension ||
si->using_mit_saver_extension ||
- si->using_sgi_saver_extension));
+ si->using_sgi_saver_extension) ||
+ si->using_xinput_extension);
+
+ const char *why = 0; /* What caused the idle-state to change? */
if (until_idle_p)
{
while (1)
{
- XtAppNextEvent (si->app, &event);
+ XtAppNextEvent (si->app, &event.x_event);
- switch (event.xany.type) {
+ switch (event.x_event.xany.type) {
case 0: /* our synthetic "timeout" event has been signalled */
if (until_idle_p)
{
if (idle >= p->timeout)
{
/* Look, we've been idle long enough. We're done. */
+ why = "timeout";
goto DONE;
}
else if (si->emergency_lock_p)
{
/* Oops, the wall clock has jumped far into the future, so
we need to lock down in a hurry! */
+ why = "large wall clock change";
goto DONE;
}
else
break;
case ClientMessage:
- if (handle_clientmessage (si, &event, until_idle_p))
- goto DONE;
+ if (handle_clientmessage (si, &event.x_event, until_idle_p))
+ {
+ why = "ClientMessage";
+ goto DONE;
+ }
break;
case CreateNotify:
supposed to scan all windows for events, prepare this window. */
if (scanning_all_windows)
{
- Window w = event.xcreatewindow.window;
+ Window w = event.x_event.xcreatewindow.window;
start_notice_events_timer (si, w, p->debug_p);
}
break;
case KeyPress:
- case KeyRelease:
case ButtonPress:
- case ButtonRelease:
+ /* Ignore release events so that hitting ESC at the password dialog
+ doesn't result in the password dialog coming right back again when
+ the fucking release key is seen! */
+ /* case KeyRelease:*/
+ /* case ButtonRelease:*/
case MotionNotify:
if (p->debug_p)
Window root=0, window=0;
int x=-1, y=-1;
const char *type = 0;
- if (event.xany.type == MotionNotify)
+ if (event.x_event.xany.type == MotionNotify)
{
/*type = "MotionNotify";*/
- root = event.xmotion.root;
- window = event.xmotion.window;
- x = event.xmotion.x_root;
- y = event.xmotion.y_root;
+ root = event.x_event.xmotion.root;
+ window = event.x_event.xmotion.window;
+ x = event.x_event.xmotion.x_root;
+ y = event.x_event.xmotion.y_root;
}
- else if (event.xany.type == KeyPress)
+ else if (event.x_event.xany.type == KeyPress)
{
type = "KeyPress";
- root = event.xkey.root;
- window = event.xkey.window;
+ root = event.x_event.xkey.root;
+ window = event.x_event.xkey.window;
x = y = -1;
}
- else if (event.xany.type == ButtonPress)
+ else if (event.x_event.xany.type == ButtonPress)
{
type = "ButtonPress";
- root = event.xkey.root;
- window = event.xkey.window;
- x = event.xmotion.x_root;
- y = event.xmotion.y_root;
+ root = event.x_event.xkey.root;
+ window = event.x_event.xkey.window;
+ x = event.x_event.xmotion.x_root;
+ y = event.x_event.xmotion.y_root;
}
if (type)
/* Be careful never to do this unless in -debug mode, as
this could expose characters from the unlock password. */
- if (p->debug_p && event.xany.type == KeyPress)
+ if (p->debug_p && event.x_event.xany.type == KeyPress)
{
KeySym keysym;
char c = 0;
- XLookupString (&event.xkey, &c, 1, &keysym, 0);
+ XLookupString (&event.x_event.xkey, &c, 1, &keysym, 0);
fprintf (stderr, " (%s%s)",
- (event.xkey.send_event ? "synthetic " : ""),
+ (event.x_event.xkey.send_event ? "synthetic " : ""),
XKeysymToString (keysym));
}
}
/* If any widgets want to handle this event, let them. */
- dispatch_event (si, &event);
+ dispatch_event (si, &event.x_event);
/* If we got a MotionNotify event, figure out what screen it
far enough to count as "real" motion, then ignore this
event.
*/
- if (event.xany.type == MotionNotify)
+ if (event.x_event.xany.type == MotionNotify)
{
int i;
for (i = 0; i < si->nscreens; i++)
- if (event.xmotion.root ==
+ if (event.x_event.xmotion.root ==
RootWindowOfScreen (si->screens[i].screen))
break;
if (i < si->nscreens)
if (!until_idle_p)
{
if (si->demoing_p &&
- (event.xany.type == MotionNotify ||
- event.xany.type == KeyRelease))
+ (event.x_event.xany.type == MotionNotify ||
+ event.x_event.xany.type == KeyRelease))
/* When we're demoing a single hack, mouse motion doesn't
cause deactivation. Only clicks and keypresses do. */
;
else
- /* If we're not demoing, then any activity causes deactivation.
- */
- goto DONE;
+ {
+ /* If we're not demoing, then any activity causes deactivation.
+ */
+ why = (event.x_event.xany.type == MotionNotify ?"mouse motion":
+ event.x_event.xany.type == KeyPress?"keyboard activity":
+ event.x_event.xany.type == ButtonPress ? "mouse click" :
+ "unknown user activity");
+ goto DONE;
+ }
}
else
reset_timers (si);
default:
#ifdef HAVE_MIT_SAVER_EXTENSION
- if (event.type == si->mit_saver_ext_event_number)
+ if (event.x_event.type == si->mit_saver_ext_event_number)
{
/* This event's number is that of the MIT-SCREEN-SAVER server
extension. This extension has one event number, and the event
itself contains sub-codes that say what kind of event it was
(an "idle" or "not-idle" event.)
*/
- XScreenSaverNotifyEvent *sevent =
- (XScreenSaverNotifyEvent *) &event;
- if (sevent->state == ScreenSaverOn)
+ if (event.sevent.state == ScreenSaverOn)
{
int i = 0;
if (p->verbose_p)
XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
}
- if (sevent->kind != ScreenSaverExternal)
+ if (event.sevent.kind != ScreenSaverExternal)
{
fprintf (stderr,
"%s: ScreenSaverOn event wasn't of type External!\n",
}
if (until_idle_p)
- goto DONE;
+ {
+ why = "MIT ScreenSaverOn";
+ goto DONE;
+ }
}
- else if (sevent->state == ScreenSaverOff)
+ else if (event.sevent.state == ScreenSaverOff)
{
if (p->verbose_p)
fprintf (stderr, "%s: MIT ScreenSaverOff event received.\n",
blurb());
if (!until_idle_p)
- goto DONE;
+ {
+ why = "MIT ScreenSaverOff";
+ goto DONE;
+ }
}
else
fprintf (stderr,
"%s: unknown MIT-SCREEN-SAVER event %d received!\n",
- blurb(), sevent->state);
+ blurb(), event.sevent.state);
}
else
#ifdef HAVE_SGI_SAVER_EXTENSION
- if (event.type == (si->sgi_saver_ext_event_number + ScreenSaverStart))
+ if (event.x_event.type == (si->sgi_saver_ext_event_number + ScreenSaverStart))
{
/* The SGI SCREEN_SAVER server extension has two event numbers,
and this event matches the "idle" event. */
blurb());
if (until_idle_p)
- goto DONE;
+ {
+ why = "SGI ScreenSaverStart";
+ goto DONE;
+ }
}
- else if (event.type == (si->sgi_saver_ext_event_number +
+ else if (event.x_event.type == (si->sgi_saver_ext_event_number +
ScreenSaverEnd))
{
/* The SGI SCREEN_SAVER server extension has two event numbers,
fprintf (stderr, "%s: SGI ScreenSaverEnd event received.\n",
blurb());
if (!until_idle_p)
- goto DONE;
+ {
+ why = "SGI ScreenSaverEnd";
+ goto DONE;
+ }
}
else
#endif /* HAVE_SGI_SAVER_EXTENSION */
-#ifdef HAVE_RANDR
- if (event.type == (si->randr_event_number + RRScreenChangeNotify))
+#ifdef HAVE_XINPUT
+ /* If we got a MotionNotify event, check to see if the mouse has
+ moved far enough to count as "real" motion, if not, then ignore
+ this event.
+ */
+ if ((si->num_xinput_devices > 0) &&
+ (event.x_event.type == si->xinput_DeviceMotionNotify))
{
- /* The Resize and Rotate extension sends an event when the
- size, rotation, or refresh rate of the screen has changed. */
+ XDeviceMotionEvent *dme = (XDeviceMotionEvent *) &event;
+ poll_mouse_data *last_poll_mouse = NULL;
+ int d;
+
+ for (d = 0; d < si->num_xinput_devices; d++)
+ {
+ if (si->xinput_devices[d].device->device_id == dme->deviceid)
+ {
+ last_poll_mouse = &(si->xinput_devices[d].last_poll_mouse);
+ break;
+ }
+ }
+
+ if (last_poll_mouse)
+ {
+ poll_mouse_data this_poll_mouse;
+ this_poll_mouse.root_x = dme->x_root;
+ this_poll_mouse.root_y = dme->y_root;
+ this_poll_mouse.child = dme->subwindow;
+ this_poll_mouse.mask = dme->device_state;
+ this_poll_mouse.time = dme->time / 1000; /* milliseconds */
+
+ if (!device_pointer_moved_p (si, last_poll_mouse,
+ &this_poll_mouse, False,
+ "device", dme->deviceid))
+ continue;
+ }
+ else if (p->debug_p)
+ fprintf (stderr,
+ "%s: received MotionNotify from unknown device %d\n",
+ blurb(), (int) dme->deviceid);
+ }
+
+ if ((!until_idle_p) &&
+ (si->num_xinput_devices > 0) &&
+ (event.x_event.type == si->xinput_DeviceMotionNotify ||
+ event.x_event.type == si->xinput_DeviceButtonPress))
+ /* Ignore DeviceButtonRelease, see ButtonRelease comment above. */
+ {
- XRRScreenChangeNotifyEvent *xrr_event =
- (XRRScreenChangeNotifyEvent *) &event;
- /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */
- int screen = XRRRootToScreen (si->dpy, xrr_event->window);
+ dispatch_event (si, &event.x_event);
+ if (si->demoing_p &&
+ event.x_event.type == si->xinput_DeviceMotionNotify)
+ /* When we're demoing a single hack, mouse motion doesn't
+ cause deactivation. Only clicks and keypresses do. */
+ ;
+ else
+ /* If we're not demoing, then any activity causes deactivation.
+ */
+ {
+ why = (event.x_event.type == si->xinput_DeviceMotionNotify
+ ? "XI mouse motion" :
+ event.x_event.type == si->xinput_DeviceButtonPress
+ ? "XI mouse click" : "unknown XINPUT event");
+ goto DONE;
+ }
+ }
+ else
+#endif /* HAVE_XINPUT */
+#ifdef HAVE_RANDR
+ if (si->using_randr_extension &&
+ (event.x_event.type ==
+ (si->randr_event_number + RRScreenChangeNotify)))
+ {
+ /* The Resize and Rotate extension sends an event when the
+ size, rotation, or refresh rate of any screen has changed.
+ */
if (p->verbose_p)
{
- if (si->screens[screen].width == xrr_event->width &&
- si->screens[screen].height == xrr_event->height)
- fprintf (stderr,
- "%s: %d: no-op screen size change event (%dx%d)\n",
- blurb(), screen,
- xrr_event->width, xrr_event->height);
- else
- fprintf (stderr,
- "%s: %d: screen size changed from %dx%d to %dx%d\n",
- blurb(), screen,
- si->screens[screen].width,
- si->screens[screen].height,
- xrr_event->width, xrr_event->height);
+ /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */
+ int screen = XRRRootToScreen (si->dpy, event.xrr_event.window);
+ fprintf (stderr, "%s: %d: screen change event received\n",
+ blurb(), screen);
}
# ifdef RRScreenChangeNotifyMask
/* Inform Xlib that it's ok to update its data structures. */
- XRRUpdateConfiguration (&event); /* Xrandr.h 1.9, 2002/09/29 */
+ XRRUpdateConfiguration (&event.x_event); /* Xrandr.h 1.9, 2002/09/29 */
# endif /* RRScreenChangeNotifyMask */
/* Resize the existing xscreensaver windows and cached ssi data. */
- resize_screensaver_window (si);
+ if (update_screen_layout (si))
+ {
+ if (p->verbose_p)
+ {
+ fprintf (stderr, "%s: new layout:\n", blurb());
+ describe_monitor_layout (si);
+ }
+ resize_screensaver_window (si);
+ }
}
else
#endif /* HAVE_RANDR */
/* Just some random event. Let the Widgets handle it, if desired. */
- dispatch_event (si, &event);
+ dispatch_event (si, &event.x_event);
}
}
DONE:
+ if (p->verbose_p)
+ {
+ if (! why) why = "unknown reason";
+ fprintf (stderr, "%s: %s (%s)\n", blurb(),
+ (until_idle_p ? "user is idle" : "user is active"),
+ why);
+ }
/* If there's a user event on the queue, swallow it.
If we're using a server extension, and the user becomes active, we
there's only one event generated by user activity, not two.)
*/
if (!until_idle_p && si->locked_p)
- swallow_unlock_typeahead_events (si, &event);
+ swallow_unlock_typeahead_events (si, &event.x_event);
else
while (XCheckMaskEvent (si->dpy,
(KeyPressMask|ButtonPressMask|PointerMotionMask),
- &event))
+ &event.x_event))
;
if (until_idle_p && si->cycle_id) /* no cycle timer when inactive */
abort ();
-
- return;
}
f = fopen (PROC_INTERRUPTS, "r");
if (!f)
- return False;
+ {
+ if (why) *why = "does not exist";
+ return False;
+ }
fclose (f);
return True;
perror (buf);
goto FAIL;
}
+
+# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
+ /* Close this fd upon exec instead of inheriting / leaking it. */
+ if (fcntl (fileno (f0), F_SETFD, FD_CLOEXEC) != 0)
+ perror ("fcntl: CLOEXEC:");
+# endif
}
if (f0 == (FILE *) -1) /* means we got an error initializing. */
if (screenhack_running_p (si) &&
!monitor_powered_on_p (si))
{
+ int i;
if (si->prefs.verbose_p)
fprintf (stderr,
"%s: X says monitor has powered down; "
"killing running hacks.\n", blurb());
- kill_screenhack (si);
+ for (i = 0; i < si->nscreens; i++)
+ kill_screenhack (&si->screens[i]);
}
/* Re-schedule this timer. The watchdog timer defaults to a bit less