Merge branch 'central-widget'
[krunner.git] / xautolock_engine.c
blob76a5e0a8fa46aa6e1c9484a700a1c864d1e7a67d
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 *****************************************************************************/
20 #include <X11/Xlib.h>
21 #include <time.h>
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.
30 void
31 xautolock_queryIdleTime (Display* d)
33 Time idleTime = 0; /* millisecs since last input event */
35 #ifdef HasXidle
36 if (xautolock_useXidle)
38 XGetIdleTime (d, &idleTime);
40 else
41 #endif /* HasXIdle */
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;
51 else
52 #endif /* HAVE_SCREENSAVER */
54 d = d; /* shut up */
55 return; /* DIY */
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.
72 void
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 */
92 * Have a guess...
94 if (firstCall)
96 firstCall = False;
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
104 * of pointer events.
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);
117 break;
122 if ( rootX == prevRootX
123 && rootY == prevRootY
124 && mask == prevMask)
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
137 * particular).
139 if ( (corner = 0,
140 rootX <= cornerSize && rootX >= 0
141 && rootY <= cornerSize && rootY >= 0)
142 || (corner++,
143 rootX >= WidthOfScreen (screen) - cornerSize - 1
144 && rootY <= cornerSize)
145 || (corner++,
146 rootX <= cornerSize
147 && rootY >= HeightOfScreen (screen) - cornerSize - 1)
148 || (corner++,
149 rootX >= WidthOfScreen (screen) - cornerSize - 1
150 && rootY >= HeightOfScreen (screen) - cornerSize - 1))
152 now = time (0);
154 switch (corners[corner])
156 case ca_forceLock:
157 #if 0
158 newTrigger = now + (useRedelay ? cornerRedelay : cornerDelay) - 1;
159 #else
160 newTrigger = now + 2 - 1;
161 #endif
163 #if 0
164 if (newTrigger < lockTrigger)
166 setLockTrigger (newTrigger - now);
168 #else
169 xautolock_setTrigger( newTrigger );
170 #endif
171 break;
173 case ca_dontLock:
174 xautolock_resetTriggers ();
176 #ifdef __GNUC__
177 default: ; /* Makes gcc -Wall shut up. */
178 #endif /* __GNUC__ */
182 else
184 #if 0
185 useRedelay = False;
186 #endif
187 prevRootX = rootX;
188 prevRootY = rootY;
189 prevMask = mask;
191 xautolock_resetTriggers ();
195 #if 0
197 * Support for deciding whether to lock or kill.
199 void
200 evaluateTriggers (Display* d)
202 static time_t prevNotification = 0;
203 time_t now = 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.
213 if (disabled)
215 resetTriggers ();
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.
226 #ifdef VMS
227 if (vmsStatus == 0)
229 #else /* VMS */
230 if (lockerPid)
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
255 * loop hole.
257 if ( WIFEXITED (status)
258 && WEXITSTATUS (status) == EXIT_SUCCESS)
260 disableKillTrigger ();
263 useRedelay = True;
264 lockerPid = 0;
266 #endif /* VMS */
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.
278 unlockNow = False;
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?
290 now = time (0);
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.
312 if ( notifyLock
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);
323 else
325 (void) XBell (d, bellPercent);
326 (void) XSync (d, 0);
329 prevNotification = now;
333 * Finally fire up the locker if time has somehow come.
335 if ( lockNow
336 || now >= lockTrigger)
338 #ifdef VMS
339 if (vmsStatus != 0)
340 #else /* VMS */
341 if (!lockerPid)
342 #endif /* VMS */
344 switch (lockerPid = vfork ())
346 case -1:
347 lockerPid = 0;
348 break;
350 case 0:
351 (void) close (ConnectionNumber (d));
352 #ifdef VMS
353 vmsStatus = 0;
354 lockerPid = lib$spawn ((lockNow ? &nowLockerDescr : &lockerDescr),
355 0, 0, &1, 0, 0, &vmsStatus);
357 if (!(lockerPid & 1)) exit (lockerPid);
359 #ifdef SLOW_VMS
360 (void) sleep (SLOW_VMS_DELAY);
361 #endif /* SLOW_VMS */
362 #else /* VMS */
363 (void) execl ("/bin/sh", "/bin/sh", "-c",
364 (lockNow ? nowLocker : locker), 0);
365 #endif /* VMS */
366 _exit (EXIT_FAILURE);
368 default:
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);
402 (void) XSync (d,0);
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);
413 useRedelay = False;
416 lockNow = False;
419 #endif