1 /*****************************************************************************
3 * Authors: Michel Eyckmans (MCE) & Stefan De Troch (SDT)
5 * Content: This file is part of version 2.x of xautolock. It implements
6 * the program's core functions.
8 * Please send bug reports etc. to eyckmans@imec.be.
10 * --------------------------------------------------------------------------
12 * Copyright 1990,1992-1999,2001-2002 by Stefan De Troch and Michel Eyckmans.
14 * Versions 2.0 and above of xautolock are available under version 2 of the
15 * GNU GPL. Earlier versions are available under other conditions. For more
16 * information, see the License file.
18 *****************************************************************************/
22 #include <config-xautolock.h>
23 #include "xautolock_c.h"
26 * Function for querying the idle time from the server.
27 * Only used if either the Xidle or the Xscreensaver
28 * extension is present.
31 xautolock_queryIdleTime (Display
* d
)
33 Time idleTime
= 0; /* millisecs since last input event */
36 if (xautolock_useXidle
)
38 XGetIdleTime (d
, &idleTime
);
43 #ifdef HAVE_SCREENSAVER
44 if( xautolock_useMit
)
46 static XScreenSaverInfo
* mitInfo
= 0;
47 if (!mitInfo
) mitInfo
= XScreenSaverAllocInfo ();
48 XScreenSaverQueryInfo (d
, DefaultRootWindow (d
), mitInfo
);
49 idleTime
= mitInfo
->idle
;
52 #endif /* HAVE_SCREENSAVER */
59 if (idleTime
< CHECK_INTERVAL
)
61 xautolock_resetTriggers ();
66 * Function for monitoring pointer movements. This implements the
67 * `corners' feature and as a side effect also tracks pointer
68 * related user activity. The latter actually is only needed when
69 * we're using the DIY mode of operations, but it's much simpler
70 * to do it unconditionally.
73 xautolock_queryPointer (Display
* d
)
75 Window dummyWin
; /* as it says */
76 int dummyInt
; /* as it says */
77 unsigned mask
; /* modifier mask */
78 int rootX
; /* as it says */
79 int rootY
; /* as it says */
80 int corner
; /* corner index */
81 time_t now
; /* as it says */
82 time_t newTrigger
; /* temporary storage */
83 int i
; /* loop counter */
84 static Window root
; /* root window the pointer is on */
85 static Screen
* screen
; /* screen the pointer is on */
86 static unsigned prevMask
= 0; /* as it says */
87 static int prevRootX
= -1; /* as it says */
88 static int prevRootY
= -1; /* as it says */
89 static Bool firstCall
= True
; /* as it says */
97 root
= DefaultRootWindow (d
);
98 screen
= ScreenOfDisplay (d
, DefaultScreen (d
));
102 * Find out whether the pointer has moved. Using XQueryPointer for this
103 * is gross, but it also is the only way never to mess up propagation
106 if (!XQueryPointer (d
, root
, &root
, &dummyWin
, &rootX
, &rootY
,
107 &dummyInt
, &dummyInt
, &mask
))
110 * Pointer has moved to another screen, so let's find out which one.
112 for (i
= -1; ++i
< ScreenCount (d
); )
114 if (root
== RootWindow (d
, i
))
116 screen
= ScreenOfDisplay (d
, i
);
122 if ( rootX
== prevRootX
123 && rootY
== prevRootY
126 xautolock_corner_t
* corners
= xautolock_corners
;
128 * If the pointer has not moved since the previous call and
129 * is inside one of the 4 corners, we act according to the
130 * contents of the "corners" array.
132 * If rootX and rootY are less than zero, don't lock even if
133 * ca_forceLock is set in the upper-left corner. Why? 'cause
134 * on initial server startup, if (and only if) the pointer is
135 * never moved, XQueryPointer() can return values less than
136 * zero (only some servers, Openwindows 2.0 and 3.0 in
140 rootX
<= cornerSize
&& rootX
>= 0
141 && rootY
<= cornerSize
&& rootY
>= 0)
143 rootX
>= WidthOfScreen (screen
) - cornerSize
- 1
144 && rootY
<= cornerSize
)
147 && rootY
>= HeightOfScreen (screen
) - cornerSize
- 1)
149 rootX
>= WidthOfScreen (screen
) - cornerSize
- 1
150 && rootY
>= HeightOfScreen (screen
) - cornerSize
- 1))
154 switch (corners
[corner
])
158 newTrigger
= now
+ (useRedelay
? cornerRedelay
: cornerDelay
) - 1;
160 newTrigger
= now
+ 2 - 1;
164 if (newTrigger
< lockTrigger
)
166 setLockTrigger (newTrigger
- now
);
169 xautolock_setTrigger( newTrigger
);
174 xautolock_resetTriggers ();
177 default: ; /* Makes gcc -Wall shut up. */
178 #endif /* __GNUC__ */
191 xautolock_resetTriggers ();
197 * Support for deciding whether to lock or kill.
200 evaluateTriggers (Display
* d
)
202 static time_t prevNotification
= 0;
206 * Obvious things first.
208 * The triggers are being moved all the time while in disabled
209 * mode in order to make absolutely sure we cannot run into
210 * trouble by an enable message coming in at an odd moment.
211 * Otherwise we possibly might lock or kill too soon.
219 * Next, wait for (or kill, if we were so told) the previous
220 * locker (if any). Note that this must also be done while in
221 * disabled mode. Not only to avoid a potential zombie proces
222 * hanging around until we are re-enabled, but also to prevent
223 * us from incorrectly setting a kill trigger at the moment
224 * when we are finally re-enabled.
232 #if !defined (UTEKV) && !defined (SYSV) && !defined (SVR4)
233 union wait status
; /* child's process status */
234 #else /* !UTEKV && !SYSV && !SVR4 */
235 int status
= 0; /* child's process status */
236 #endif /* !UTEKV && !SYSV && !SVR4 */
238 if (unlockNow
&& !disabled
)
240 (void) kill (lockerPid
, SIGTERM
);
243 #if !defined (UTEKV) && !defined (SYSV) && !defined (SVR4)
244 if (wait3 (&status
, WNOHANG
, 0))
245 #else /* !UTEKV && !SYSV && !SVR4 */
246 if (waitpid (-1, &status
, WNOHANG
))
247 #endif /* !UTEKV && !SYSV && !SVR4 */
250 * If the locker exited normally, we disable any pending kill
251 * trigger. Otherwise, we assume that it either has crashed or
252 * was not able to lock the display because of an existing
253 * locker (which may have been started manually). In both of
254 * the later cases, disabling the kill trigger would open a
257 if ( WIFEXITED (status
)
258 && WEXITSTATUS (status
) == EXIT_SUCCESS
)
260 disableKillTrigger ();
268 setLockTrigger (lockTime
);
271 * No return here! The pointer may be sitting in a corner, while
272 * parameter settings may be such that we need to start another
273 * locker without further delay. If you think this cannot happen,
274 * consider the case in which the locker simply crashed.
281 * Note that the above lot needs to be done even when we're in
282 * disabled mode, since we may have entered said mode with an
283 * active locker around.
285 if (disabled
) return;
288 * Is it time to run the killer command?
292 if (killTrigger
&& now
>= killTrigger
)
295 * There is a dirty trick here. On the one hand, we don't want
296 * to block until the killer returns, but on the other one
297 * we don't want to have it interfere with the wait() stuff we
298 * do to keep track of the locker. To obtain both, the killer
299 * command has already been patched by KillerChecker() so that
300 * it gets backgrounded by the shell started by system().
302 * For the time being, VMS users are out of luck: their xautolock
303 * will indeed block until the killer returns.
305 (void) system (killer
);
306 setKillTrigger (killTime
);
310 * Now trigger the notifier if required.
313 && now
+ notifyMargin
>= lockTrigger
314 && prevNotification
< now
- notifyMargin
- 1)
316 if (notifierSpecified
)
319 * Here we use the same dirty trick as for the killer command.
321 (void) system (notifier
);
325 (void) XBell (d
, bellPercent
);
329 prevNotification
= now
;
333 * Finally fire up the locker if time has somehow come.
336 || now
>= lockTrigger
)
344 switch (lockerPid
= vfork ())
351 (void) close (ConnectionNumber (d
));
354 lockerPid
= lib$
spawn ((lockNow
? &nowLockerDescr
: &lockerDescr
),
355 0, 0, &1, 0, 0, &vmsStatus
);
357 if (!(lockerPid
& 1)) exit (lockerPid
);
360 (void) sleep (SLOW_VMS_DELAY
);
361 #endif /* SLOW_VMS */
363 (void) execl ("/bin/sh", "/bin/sh", "-c",
364 (lockNow
? nowLocker
: locker
), 0);
366 _exit (EXIT_FAILURE
);
370 * In general xautolock should keep its fingers off the real
371 * screensaver because no universally acceptable policy can
372 * be defined. In no case should it decide to disable or enable
373 * it all by itself. Setting the screensaver policy is something
374 * the locker should take care of. After all, xautolock is not
375 * supposed to know what the "locker" does and doesn't do.
376 * People might be using xautolock for totally different
377 * purposes (which, by the way, is why it will accept a
378 * different set of X resources after being renamed).
380 * Nevertheless, simply resetting the screensaver is a
381 * convenience action that aids many xlock users, and doesn't
382 * harm anyone (*). The problem with older versions of xlock
383 * is that they can be told to replace (= disable) the real
384 * screensaver, but forget to reset that same screensaver if
385 * it was already active at the time xlock starts. I guess
386 * xlock initially wasn't designed to be run without a user
387 * actually typing the comand ;-).
389 * (*) Well, at least it used not to harm anyone, but with the
390 * advent of DPMS monitors, it now can mess up the power
391 * saving setup. Hence we better make it optional.
393 * Also, some xlock versions also unconditionally call
394 * XResetScreenSaver, yielding the same kind of problems
395 * with DPMS that xautolock did. The latest and greatest
396 * xlocks also have a -resetsaver option for this very
397 * reason. You may want to upgrade.
399 if (resetSaver
) (void) XResetScreenSaver(d
);
401 setLockTrigger (lockTime
);
406 * Once the locker is running, all that needs to be done is to
407 * set the killTrigger if needed. Notice that this must be done
408 * even if we actually failed to start the locker. Otherwise
409 * the error would "propagate" from one feature to another.
411 if (killerSpecified
) setKillTrigger (killTime
);