summaryrefslogtreecommitdiffstats
path: root/klaptopdaemon/xautolock_engine.c
blob: 08094e7b252d63438b044d671786c6c31d49a51b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
/*****************************************************************************
 *
 * 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         tqmask;             /* modifier tqmask                 */
  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, &tqmask))
  {
   /*
    *  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
      && tqmask == 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 = tqmask;

    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 (vmstqStatus == 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 (vmstqStatus != 0)
#else /* VMS */
    if (!lockerPid)
#endif /* VMS */
    {
      switch (lockerPid = vfork ())
      {
        case -1:
          lockerPid = 0;
          break;
  
        case 0:
          (void) close (ConnectionNumber (d));
#ifdef VMS
          vmstqStatus = 0;
          lockerPid = lib$spawn ((lockNow ? &nowLockerDescr : &lockerDescr),
	                         0, 0, &1, 0, 0, &vmstqStatus);

	  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 tqreplace (= 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