|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Authors: Michel Eyckmans (MCE) & Stefan De Troch (SDT)
|
|
|
|
*
|
|
|
|
* Content: This file is part of version 2.x of xautolock. It implements
|
|
|
|
* the program's core functions.
|
|
|
|
*
|
|
|
|
* Please send bug reports etc. to eyckmans@imec.be.
|
|
|
|
*
|
|
|
|
* --------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Copyright 1990,1992-1999,2001-2002 by Stefan De Troch and Michel Eyckmans.
|
|
|
|
*
|
|
|
|
* Versions 2.0 and above of xautolock are available under version 2 of the
|
|
|
|
* GNU GPL. Earlier versions are available under other conditions. For more
|
|
|
|
* information, see the License file.
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include "xautolock_c.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Function for querying the idle time from the server.
|
|
|
|
* Only used if either the Xidle or the Xscreensaver
|
|
|
|
* extension is present.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
xautolock_queryIdleTime (Display* d)
|
|
|
|
{
|
|
|
|
Time idleTime = 0; /* millisecs since last input event */
|
|
|
|
|
|
|
|
#ifdef HasXidle
|
|
|
|
if (xautolock_useXidle)
|
|
|
|
{
|
|
|
|
XGetIdleTime (d, &idleTime);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif /* HasXIdle */
|
|
|
|
{
|
|
|
|
#ifdef HasScreenSaver
|
|
|
|
if( xautolock_useMit )
|
|
|
|
{
|
|
|
|
static XScreenSaverInfo* mitInfo = 0;
|
|
|
|
if (!mitInfo) mitInfo = XScreenSaverAllocInfo ();
|
|
|
|
XScreenSaverQueryInfo (d, DefaultRootWindow (d), mitInfo);
|
|
|
|
idleTime = mitInfo->idle;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif /* HasScreenSaver */
|
|
|
|
{
|
|
|
|
d = d; /* shut up */
|
|
|
|
return; /* DIY */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idleTime < CHECK_INTERVAL )
|
|
|
|
{
|
|
|
|
xautolock_resetTriggers ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Function for monitoring pointer movements. This implements the
|
|
|
|
* `corners' feature and as a side effect also tracks pointer
|
|
|
|
* related user activity. The latter actually is only needed when
|
|
|
|
* we're using the DIY mode of operations, but it's much simpler
|
|
|
|
* to do it unconditionally.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
xautolock_queryPointer (Display* d)
|
|
|
|
{
|
|
|
|
Window dummyWin; /* as it says */
|
|
|
|
int dummyInt; /* as it says */
|
|
|
|
unsigned mask; /* modifier mask */
|
|
|
|
int rootX; /* as it says */
|
|
|
|
int rootY; /* as it says */
|
|
|
|
int corner; /* corner index */
|
|
|
|
time_t now; /* as it says */
|
|
|
|
time_t newTrigger; /* temporary storage */
|
|
|
|
int i; /* loop counter */
|
|
|
|
static Window root; /* root window the pointer is on */
|
|
|
|
static Screen* screen; /* screen the pointer is on */
|
|
|
|
static unsigned prevMask = 0; /* as it says */
|
|
|
|
static int prevRootX = -1; /* as it says */
|
|
|
|
static int prevRootY = -1; /* as it says */
|
|
|
|
static Bool firstCall = True; /* as it says */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Have a guess...
|
|
|
|
*/
|
|
|
|
if (firstCall)
|
|
|
|
{
|
|
|
|
firstCall = False;
|
|
|
|
root = DefaultRootWindow (d);
|
|
|
|
screen = ScreenOfDisplay (d, DefaultScreen (d));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find out whether the pointer has moved. Using XQueryPointer for this
|
|
|
|
* is gross, but it also is the only way never to mess up propagation
|
|
|
|
* of pointer events.
|
|
|
|
*/
|
|
|
|
if (!XQueryPointer (d, root, &root, &dummyWin, &rootX, &rootY,
|
|
|
|
&dummyInt, &dummyInt, &mask))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Pointer has moved to another screen, so let's find out which one.
|
|
|
|
*/
|
|
|
|
for (i = -1; ++i < ScreenCount (d); )
|
|
|
|
{
|
|
|
|
if (root == RootWindow (d, i))
|
|
|
|
{
|
|
|
|
screen = ScreenOfDisplay (d, i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( rootX == prevRootX
|
|
|
|
&& rootY == prevRootY
|
|
|
|
&& mask == prevMask)
|
|
|
|
{
|
|
|
|
xautolock_corner_t* corners = xautolock_corners;
|
|
|
|
/*
|
|
|
|
* If the pointer has not moved since the previous call and
|
|
|
|
* is inside one of the 4 corners, we act according to the
|
|
|
|
* contents of the "corners" array.
|
|
|
|
*
|
|
|
|
* If rootX and rootY are less than zero, don't lock even if
|
|
|
|
* ca_forceLock is set in the upper-left corner. Why? 'cause
|
|
|
|
* on initial server startup, if (and only if) the pointer is
|
|
|
|
* never moved, XQueryPointer() can return values less than
|
|
|
|
* zero (only some servers, Openwindows 2.0 and 3.0 in
|
|
|
|
* particular).
|
|
|
|
*/
|
|
|
|
if ( (corner = 0,
|
|
|
|
rootX <= cornerSize && rootX >= 0
|
|
|
|
&& rootY <= cornerSize && rootY >= 0)
|
|
|
|
|| (corner++,
|
|
|
|
rootX >= WidthOfScreen (screen) - cornerSize - 1
|
|
|
|
&& rootY <= cornerSize)
|
|
|
|
|| (corner++,
|
|
|
|
rootX <= cornerSize
|
|
|
|
&& rootY >= HeightOfScreen (screen) - cornerSize - 1)
|
|
|
|
|| (corner++,
|
|
|
|
rootX >= WidthOfScreen (screen) - cornerSize - 1
|
|
|
|
&& rootY >= HeightOfScreen (screen) - cornerSize - 1))
|
|
|
|
{
|
|
|
|
now = time (0);
|
|
|
|
|
|
|
|
switch (corners[corner])
|
|
|
|
{
|
|
|
|
case ca_forceLock:
|
|
|
|
#if 0
|
|
|
|
newTrigger = now + (useRedelay ? cornerRedelay : cornerDelay) - 1;
|
|
|
|
#else
|
|
|
|
newTrigger = now + 2 - 1;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (newTrigger < lockTrigger)
|
|
|
|
{
|
|
|
|
setLockTrigger (newTrigger - now);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
xautolock_setTrigger( newTrigger );
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ca_dontLock:
|
|
|
|
xautolock_resetTriggers ();
|
|
|
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
default: ; /* Makes gcc -Wall shut up. */
|
|
|
|
#endif /* __GNUC__ */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
useRedelay = False;
|
|
|
|
#endif
|
|
|
|
prevRootX = rootX;
|
|
|
|
prevRootY = rootY;
|
|
|
|
prevMask = mask;
|
|
|
|
|
|
|
|
xautolock_resetTriggers ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
* Support for deciding whether to lock or kill.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
evaluateTriggers (Display* d)
|
|
|
|
{
|
|
|
|
static time_t prevNotification = 0;
|
|
|
|
time_t now = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Obvious things first.
|
|
|
|
*
|
|
|
|
* The triggers are being moved all the time while in disabled
|
|
|
|
* mode in order to make absolutely sure we cannot run into
|
|
|
|
* trouble by an enable message coming in at an odd moment.
|
|
|
|
* Otherwise we possibly might lock or kill too soon.
|
|
|
|
*/
|
|
|
|
if (disabled)
|
|
|
|
{
|
|
|
|
resetTriggers ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Next, wait for (or kill, if we were so told) the previous
|
|
|
|
* locker (if any). Note that this must also be done while in
|
|
|
|
* disabled mode. Not only to avoid a potential zombie proces
|
|
|
|
* hanging around until we are re-enabled, but also to prevent
|
|
|
|
* us from incorrectly setting a kill trigger at the moment
|
|
|
|
* when we are finally re-enabled.
|
|
|
|
*/
|
|
|
|
#ifdef VMS
|
|
|
|
if (vmsStatus == 0)
|
|
|
|
{
|
|
|
|
#else /* VMS */
|
|
|
|
if (lockerPid)
|
|
|
|
{
|
|
|
|
#if !defined (UTEKV) && !defined (SYSV) && !defined (SVR4)
|
|
|
|
union wait status; /* childs process status */
|
|
|
|
#else /* !UTEKV && !SYSV && !SVR4 */
|
|
|
|
int status = 0; /* childs process status */
|
|
|
|
#endif /* !UTEKV && !SYSV && !SVR4 */
|
|
|
|
|
|
|
|
if (unlockNow && !disabled)
|
|
|
|
{
|
|
|
|
(void) kill (lockerPid, SIGTERM);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if !defined (UTEKV) && !defined (SYSV) && !defined (SVR4)
|
|
|
|
if (wait3 (&status, WNOHANG, 0))
|
|
|
|
#else /* !UTEKV && !SYSV && !SVR4 */
|
|
|
|
if (waitpid (-1, &status, WNOHANG))
|
|
|
|
#endif /* !UTEKV && !SYSV && !SVR4 */
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If the locker exited normally, we disable any pending kill
|
|
|
|
* trigger. Otherwise, we assume that it either has crashed or
|
|
|
|
* was not able to lock the display because of an existing
|
|
|
|
* locker (which may have been started manually). In both of
|
|
|
|
* the later cases, disabling the kill trigger would open a
|
|
|
|
* loop hole.
|
|
|
|
*/
|
|
|
|
if ( WIFEXITED (status)
|
|
|
|
&& WEXITSTATUS (status) == EXIT_SUCCESS)
|
|
|
|
{
|
|
|
|
disableKillTrigger ();
|
|
|
|
}
|
|
|
|
|
|
|
|
useRedelay = True;
|
|
|
|
lockerPid = 0;
|
|
|
|
}
|
|
|
|
#endif /* VMS */
|
|
|
|
|
|
|
|
setLockTrigger (lockTime);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No return here! The pointer may be sitting in a corner, while
|
|
|
|
* parameter settings may be such that we need to start another
|
|
|
|
* locker without further delay. If you think this cannot happen,
|
|
|
|
* consider the case in which the locker simply crashed.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
unlockNow = False;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that the above lot needs to be done even when we're in
|
|
|
|
* disabled mode, since we may have entered said mode with an
|
|
|
|
* active locker around.
|
|
|
|
*/
|
|
|
|
if (disabled) return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is it time to run the killer command?
|
|
|
|
*/
|
|
|
|
now = time (0);
|
|
|
|
|
|
|
|
if (killTrigger && now >= killTrigger)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* There is a dirty trick here. On the one hand, we don't want
|
|
|
|
* to block until the killer returns, but on the other one
|
|
|
|
* we don't want to have it interfere with the wait() stuff we
|
|
|
|
* do to keep track of the locker. To obtain both, the killer
|
|
|
|
* command has already been patched by KillerChecker() so that
|
|
|
|
* it gets backgrounded by the shell started by system().
|
|
|
|
*
|
|
|
|
* For the time being, VMS users are out of luck: their xautolock
|
|
|
|
* will indeed block until the killer returns.
|
|
|
|
*/
|
|
|
|
(void) system (killer);
|
|
|
|
setKillTrigger (killTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now trigger the notifier if required.
|
|
|
|
*/
|
|
|
|
if ( notifyLock
|
|
|
|
&& now + notifyMargin >= lockTrigger
|
|
|
|
&& prevNotification < now - notifyMargin - 1)
|
|
|
|
{
|
|
|
|
if (notifierSpecified)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Here we use the same dirty trick as for the killer command.
|
|
|
|
*/
|
|
|
|
(void) system (notifier);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
(void) XBell (d, bellPercent);
|
|
|
|
(void) XSync (d, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
prevNotification = now;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finally fire up the locker if time has somehow come.
|
|
|
|
*/
|
|
|
|
if ( lockNow
|
|
|
|
|| now >= lockTrigger)
|
|
|
|
{
|
|
|
|
#ifdef VMS
|
|
|
|
if (vmsStatus != 0)
|
|
|
|
#else /* VMS */
|
|
|
|
if (!lockerPid)
|
|
|
|
#endif /* VMS */
|
|
|
|
{
|
|
|
|
switch (lockerPid = vfork ())
|
|
|
|
{
|
|
|
|
case -1:
|
|
|
|
lockerPid = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
(void) close (ConnectionNumber (d));
|
|
|
|
#ifdef VMS
|
|
|
|
vmsStatus = 0;
|
|
|
|
lockerPid = lib$spawn ((lockNow ? &nowLockerDescr : &lockerDescr),
|
|
|
|
0, 0, &1, 0, 0, &vmsStatus);
|
|
|
|
|
|
|
|
if (!(lockerPid & 1)) exit (lockerPid);
|
|
|
|
|
|
|
|
#ifdef SLOW_VMS
|
|
|
|
(void) sleep (SLOW_VMS_DELAY);
|
|
|
|
#endif /* SLOW_VMS */
|
|
|
|
#else /* VMS */
|
|
|
|
(void) execl ("/bin/sh", "/bin/sh", "-c",
|
|
|
|
(lockNow ? nowLocker : locker), 0);
|
|
|
|
#endif /* VMS */
|
|
|
|
_exit (EXIT_FAILURE);
|
|
|
|
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* In general xautolock should keep its fingers off the real
|
|
|
|
* screensaver because no universally acceptable policy can
|
|
|
|
* be defined. In no case should it decide to disable or enable
|
|
|
|
* it all by itself. Setting the screensaver policy is something
|
|
|
|
* the locker should take care of. After all, xautolock is not
|
|
|
|
* supposed to know what the "locker" does and doesn't do.
|
|
|
|
* People might be using xautolock for totally different
|
|
|
|
* purposes (which, by the way, is why it will accept a
|
|
|
|
* different set of X resources after being renamed).
|
|
|
|
*
|
|
|
|
* Nevertheless, simply resetting the screensaver is a
|
|
|
|
* convenience action that aids many xlock users, and doesn't
|
|
|
|
* harm anyone (*). The problem with older versions of xlock
|
|
|
|
* is that they can be told to replace (= disable) the real
|
|
|
|
* screensaver, but forget to reset that same screensaver if
|
|
|
|
* it was already active at the time xlock starts. I guess
|
|
|
|
* xlock initially wasn't designed to be run without a user
|
|
|
|
* actually typing the comand ;-).
|
|
|
|
*
|
|
|
|
* (*) Well, at least it used not to harm anyone, but with the
|
|
|
|
* advent of DPMS monitors, it now can mess up the power
|
|
|
|
* saving setup. Hence we better make it optional.
|
|
|
|
*
|
|
|
|
* Also, some xlock versions also unconditionally call
|
|
|
|
* XResetScreenSaver, yielding the same kind of problems
|
|
|
|
* with DPMS that xautolock did. The latest and greatest
|
|
|
|
* xlocks also have a -resetsaver option for this very
|
|
|
|
* reason. You may want to upgrade.
|
|
|
|
*/
|
|
|
|
if (resetSaver) (void) XResetScreenSaver(d);
|
|
|
|
|
|
|
|
setLockTrigger (lockTime);
|
|
|
|
(void) XSync (d,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Once the locker is running, all that needs to be done is to
|
|
|
|
* set the killTrigger if needed. Notice that this must be done
|
|
|
|
* even if we actually failed to start the locker. Otherwise
|
|
|
|
* the error would "propagate" from one feature to another.
|
|
|
|
*/
|
|
|
|
if (killerSpecified) setKillTrigger (killTime);
|
|
|
|
|
|
|
|
useRedelay = False;
|
|
|
|
}
|
|
|
|
|
|
|
|
lockNow = False;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|