1
|
1 #if 0
|
|
2 //----------------------------------------------------------------------------
|
|
3 // This is a somewhat modified kscreensaver.
|
|
4 // The original copyright notice follows
|
|
5 //
|
|
6 //----------------------------------------------------------------------------
|
|
7 //
|
|
8 // KDE screensavers
|
|
9 //
|
|
10 // This module is a heavily modified xautolock.
|
|
11 // The orignal copyright notice follows
|
|
12 //
|
|
13
|
|
14 /*****************************************************************************
|
|
15 *
|
|
16 * xautolock
|
|
17 * =========
|
|
18 *
|
|
19 * Authors : S. De Troch (SDT) + M. Eyckmans (MCE)
|
|
20 *
|
|
21 * Date : 22/07/90
|
|
22 *
|
|
23 * ---------------------------------------------------------------------------
|
|
24 *
|
|
25 * Copyright 1990, 1992-1995 by S. De Troch and MCE.
|
|
26 *
|
|
27 * Permission to use, copy, modify and distribute this software and the
|
|
28 * supporting documentation without fee is hereby granted, provided that
|
|
29 *
|
|
30 * 1 : Both the above copyright notice and this permission notice
|
|
31 * appear in all copies of both the software and the supporting
|
|
32 * documentation.
|
|
33 * 2 : No financial profit is made out of it.
|
|
34 *
|
|
35 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
36 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
|
|
37 * EVENT SHALL THEY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
|
|
38 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
|
39 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
40 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
41 * PERFORMANCE OF THIS SOFTWARE.
|
|
42 *
|
|
43 *****************************************************************************/
|
|
44
|
|
45
|
|
46
|
|
47 /*
|
|
48 * Have a guess what this does...
|
|
49 * ==============================
|
|
50 *
|
|
51 * Warning for swm & tvtwm users : xautolock should *not* be compiled
|
|
52 * with vroot.h, because it needs to know the real root window.
|
|
53 */
|
|
54
|
|
55 #ifdef HAVE_CONFIG_H
|
|
56 #include <config.h>
|
|
57 #endif
|
|
58
|
|
59 #if defined(hpux) || defined (__hpux)
|
|
60 #ifndef _HPUX_SOURCE
|
|
61 #define _HPUX_SOURCE
|
|
62 #endif /* _HPUX_SOURCE */
|
|
63 #endif /* hpux || __hpux */
|
|
64
|
|
65 #include <stdio.h>
|
|
66 #include <string.h>
|
|
67 #include <ctype.h>
|
|
68
|
|
69 #ifdef VMS
|
|
70 #include <ssdef.h>
|
|
71 #include <processes.h> /* really needed? */
|
|
72 #endif /* VMS */
|
|
73
|
|
74 #include <X11/Xlib.h>
|
|
75 #include <X11/Xatom.h>
|
|
76 #include <X11/Xresource.h>
|
|
77
|
|
78 #include <time.h>
|
|
79 #include <signal.h>
|
|
80 #include <sys/wait.h>
|
|
81 #include <sys/types.h>
|
|
82
|
|
83 #ifdef HAVE_SYS_M_WAIT_H
|
|
84 #include <sys/m_wait.h>
|
|
85 #endif
|
|
86
|
|
87 #ifdef HAVE_MALLOC_H
|
|
88 #include <malloc.h>
|
|
89 #endif
|
|
90
|
|
91 #ifdef HAVE_UNISTD_H
|
|
92 #include <unistd.h>
|
|
93 #endif
|
|
94 #include <stdlib.h>
|
|
95
|
|
96 #include <gdk/gdkx.h>
|
|
97 #include <gtk/gtk.h>
|
|
98 #include "gaim.h"
|
|
99
|
|
100 void initAutoLock();
|
|
101 void cleanupAutoLock();
|
|
102
|
|
103 /*
|
|
104 * Usefull macros and customization stuff
|
|
105 * ======================================
|
|
106 */
|
|
107 #define PP(x) x
|
|
108
|
|
109 #ifdef VMS
|
|
110 #define ALL_OK 1 /* for use by exit () */
|
|
111 #define PROBLEMS SS$_ABORT
|
|
112 /* for use by exit () */
|
|
113 #else /* VMS */
|
|
114 #define ALL_OK 0 /* for use by exit () */
|
|
115 #define PROBLEMS 1 /* for use by exit () */
|
|
116 #endif /* VMS */
|
|
117
|
|
118
|
|
119 #define CREATION_DELAY 30 /* should be > 10 and
|
|
120 < min (45,(MIN_MINUTES*30)) */
|
|
121 #define TIME_CHANGE_LIMIT 120 /* if the time changes by more
|
|
122 than x secs then we will
|
|
123 assume someone has changed
|
|
124 date or machine has suspended */
|
|
125
|
|
126
|
|
127 #ifndef HasVFork
|
|
128 #define vfork fork
|
|
129 #endif /* HasVFork */
|
|
130
|
|
131 #define Error0(str) fprintf (stderr, str)
|
|
132 #define SetTrigger(delta) trigger = time ((time_t*) NULL) + delta
|
|
133
|
|
134 static caddr_t ch_ptr; /* this is dirty */
|
|
135 #define Skeleton(t,s) (ch_ptr = (Caddrt) malloc ((Unsigned) s), \
|
|
136 (ch_ptr == (Caddrt) NULL) \
|
|
137 ? (Error0 ("Out of memory.\n"), \
|
|
138 exit (PROBLEMS), \
|
|
139 /*NOTREACHED*/ (t*) NULL \
|
|
140 ) \
|
|
141 : (t*) ch_ptr \
|
|
142 ) \
|
|
143
|
|
144 #define New(tp) Skeleton (tp, sizeof (tp))
|
|
145
|
|
146
|
|
147
|
|
148 /*
|
|
149 * New types
|
|
150 * =========
|
|
151 */
|
|
152 #if defined (apollo) || defined (news1800)
|
|
153 typedef int (*XErrorHandler) PP((Display*,
|
|
154 XErrorEvent*));
|
|
155 #endif /* apollo || news1800 */
|
|
156
|
|
157 #if defined (news1800) || defined (sun386)
|
|
158 typedef int pid_t;
|
|
159 #endif /* news1800 || sun386*/
|
|
160
|
|
161 #ifdef VMS
|
|
162 typedef long pid_t;
|
|
163 #endif /* VMS */
|
|
164
|
|
165 #define Void void /* no typedef because of VAX */
|
|
166 typedef int Int;
|
|
167 typedef char Char;
|
|
168 typedef char* String;
|
|
169 typedef int Boolean;
|
|
170 typedef caddr_t Caddrt;
|
|
171 typedef unsigned int Unsigned;
|
|
172 typedef unsigned long Huge;
|
|
173
|
|
174 typedef struct QueueItem_
|
|
175 {
|
|
176 Window window; /* as it says */
|
|
177 time_t creationtime; /* as it says */
|
|
178 struct QueueItem_* next; /* as it says */
|
|
179 struct QueueItem_* prev; /* as it says */
|
|
180 } aQueueItem, *QueueItem;
|
|
181
|
|
182 typedef struct Queue_
|
|
183 {
|
|
184 struct QueueItem_* head; /* as it says */
|
|
185 struct QueueItem_* tail; /* as it says */
|
|
186 } aQueue, *Queue;
|
|
187
|
|
188
|
|
189 /*
|
|
190 * Function declarations
|
|
191 * =====================
|
|
192 */
|
|
193 #if defined(news1800)
|
|
194 extern Void* malloc PP((Unsigned));
|
|
195 #endif /* news1800 */
|
|
196
|
|
197 static int EvaluateCounter PP((Display*));
|
|
198 static int QueryPointer PP((Display*, int));
|
|
199 static int ProcessEvents PP((Display*, Queue, int));
|
|
200 static Queue NewQueue PP((Void));
|
|
201 static Void AddToQueue PP((Queue, Window));
|
|
202 static Void ProcessQueue PP((Queue, Display*, time_t));
|
|
203 static Void SelectEvents PP((Display*, Window, Boolean));
|
|
204
|
|
205
|
|
206 /*
|
|
207 * Global variables
|
|
208 * ================
|
|
209 */
|
|
210 static time_t trigger = 0; /* as it says */
|
|
211 static time_t time_limit = IDLE_REPORT_TIME; /* as it says */
|
|
212
|
|
213 /*
|
|
214 * Functions related to the window queue
|
|
215 * =====================================
|
|
216 *
|
|
217 * Function for creating a new queue
|
|
218 * ---------------------------------
|
|
219 */
|
|
220 static Queue NewQueue ()
|
|
221
|
|
222 {
|
|
223 Queue queue; /* return value */
|
|
224
|
|
225 queue = New (aQueue);
|
|
226 queue->tail = New (aQueueItem);
|
|
227 queue->head = New (aQueueItem);
|
|
228
|
|
229 queue->tail->next = queue->head;
|
|
230 queue->head->prev = queue->tail;
|
|
231 queue->tail->prev = queue->head->next = (QueueItem) NULL;
|
|
232
|
|
233 return queue;
|
|
234 }
|
|
235
|
|
236
|
|
237 /*
|
|
238 * Function for adding an item to a queue
|
|
239 * --------------------------------------
|
|
240 */
|
|
241 static Void AddToQueue (Queue queue, Window window)
|
|
242 {
|
|
243 QueueItem newq; /* new item */
|
|
244
|
|
245 newq = New (aQueueItem);
|
|
246
|
|
247 newq->window = window;
|
|
248 newq->creationtime = time ((time_t*) NULL);
|
|
249 newq->next = queue->tail->next;
|
|
250 newq->prev = queue->tail;
|
|
251 queue->tail->next->prev = newq;
|
|
252 queue->tail->next = newq;
|
|
253 }
|
|
254
|
|
255 /*
|
|
256 * Function for processing those entries that are old enough
|
|
257 * ---------------------------------------------------------
|
|
258 */
|
|
259 static Void ProcessQueue (Queue queue, Display *d, time_t age)
|
|
260 {
|
|
261 QueueItem current; /* as it says */
|
|
262 time_t now; /* as it says */
|
|
263
|
|
264 time (&now);
|
|
265 current = queue->head->prev;
|
|
266
|
|
267 while ( current->prev && current->creationtime + age < now )
|
|
268 {
|
|
269 SelectEvents (d, current->window, False);
|
|
270 current = current->prev;
|
|
271 free (current->next);
|
|
272 }
|
|
273
|
|
274 current->next = queue->head;
|
|
275 queue->head->prev = current;
|
|
276 }
|
|
277
|
|
278
|
|
279 static Void FreeQueue( Queue queue )
|
|
280 {
|
|
281 QueueItem current; /* as it says */
|
|
282
|
|
283 current = queue->head->prev;
|
|
284
|
|
285 while ( current->prev )
|
|
286 {
|
|
287 current = current->prev;
|
|
288 free(current->next);
|
|
289 }
|
|
290
|
|
291 free(current);
|
|
292 free(queue);
|
|
293 }
|
|
294
|
|
295
|
|
296 /*
|
|
297 * Functions related to (the lack of) user activity
|
|
298 * ================================================
|
|
299 *
|
|
300 * Function for processing the event queue
|
|
301 * ---------------------------------------
|
|
302 */
|
|
303 static int ProcessEvents (Display *d, Queue queue, int until_idle)
|
|
304 {
|
|
305 XEvent event; /* as it says */
|
|
306
|
|
307 /*
|
|
308 * Read whatever is available for reading.
|
|
309 */
|
|
310 while (XPending (d))
|
|
311 {
|
|
312 if (XCheckMaskEvent (d, SubstructureNotifyMask, &event))
|
|
313 {
|
|
314 if ((event.type == CreateNotify) && until_idle)
|
|
315 {
|
|
316 AddToQueue (queue, event.xcreatewindow.window);
|
|
317 }
|
|
318 }
|
|
319 else
|
|
320 {
|
|
321 XNextEvent (d, &event);
|
|
322 }
|
|
323
|
|
324
|
|
325 /*
|
|
326 * Reset the counter if and only if the event is a KeyPress
|
|
327 * event *and* was not generated by XSendEvent ().
|
|
328 */
|
|
329 if ( event.type == KeyPress && !event.xany.send_event )
|
|
330 {
|
|
331 if (!until_idle) /* We've become un-idle */
|
|
332 return 1;
|
|
333 SetTrigger (time_limit);
|
|
334 }
|
|
335 }
|
|
336
|
|
337
|
|
338 /*
|
|
339 * Check the window queue for entries that are older than
|
|
340 * CREATION_DELAY seconds.
|
|
341 */
|
|
342 ProcessQueue (queue, d, (time_t) CREATION_DELAY);
|
|
343 return 0;
|
|
344 }
|
|
345
|
|
346
|
|
347 /*
|
|
348 * Function for monitoring pointer movements
|
|
349 * -----------------------------------------
|
|
350 */
|
|
351 static int QueryPointer (Display *d, int until_idle)
|
|
352 {
|
|
353 Window dummy_w; /* as it says */
|
|
354 Int dummy_c; /* as it says */
|
|
355 Unsigned mask; /* modifier mask */
|
|
356 Int root_x; /* as it says */
|
|
357 Int root_y; /* as it says */
|
|
358 Int i; /* loop counter */
|
|
359 static Window root; /* root window the pointer is on */
|
|
360 static Screen* screen; /* screen the pointer is on */
|
|
361 static Unsigned prev_mask = 0; /* as it says */
|
|
362 static Int prev_root_x = -1; /* as it says */
|
|
363 static Int prev_root_y = -1; /* as it says */
|
|
364 static Boolean first_call = TRUE; /* as it says */
|
|
365
|
|
366
|
|
367 /*
|
|
368 * Have a guess...
|
|
369 */
|
|
370 if (first_call)
|
|
371 {
|
|
372 first_call = FALSE;
|
|
373 root = DefaultRootWindow (d);
|
|
374 screen = ScreenOfDisplay (d, DefaultScreen (d));
|
|
375 }
|
|
376
|
|
377
|
|
378 /*
|
|
379 * Find out whether the pointer has moved. Using XQueryPointer for this
|
|
380 * is gross, but it also is the only way never to mess up propagation
|
|
381 * of pointer events.
|
|
382 *
|
|
383 * Remark : Unlike XNextEvent(), XPending () doesn't notice if the
|
|
384 * connection to the server is lost. For this reason, earlier
|
|
385 * versions of xautolock periodically called XNoOp (). But
|
|
386 * why not let XQueryPointer () do the job for us, since
|
|
387 * we now call that periodically anyway?
|
|
388 */
|
|
389 if (!XQueryPointer (d, root, &root, &dummy_w, &root_x, &root_y,
|
|
390 &dummy_c, &dummy_c, &mask))
|
|
391 {
|
|
392 /*
|
|
393 * Pointer has moved to another screen, so let's find out which one.
|
|
394 */
|
|
395 for (i = -1; ++i < ScreenCount (d); )
|
|
396 {
|
|
397 if (root == RootWindow (d, i))
|
|
398 {
|
|
399 screen = ScreenOfDisplay (d, i);
|
|
400 break;
|
|
401 }
|
|
402 }
|
|
403 }
|
|
404
|
|
405 if ( root_x != prev_root_x
|
|
406 || root_y != prev_root_y
|
|
407 || mask != prev_mask
|
|
408 )
|
|
409 {
|
|
410 prev_root_x = root_x;
|
|
411 prev_root_y = root_y;
|
|
412 prev_mask = mask;
|
|
413 SetTrigger (time_limit);
|
|
414 if (!until_idle)
|
|
415 return 1;
|
|
416 }
|
|
417
|
|
418 return 0;
|
|
419
|
|
420 }
|
|
421
|
|
422 /*
|
|
423 * Function for deciding whether to lock
|
|
424 * -------------------------------------
|
|
425 */
|
|
426 static int EvaluateCounter (Display *d)
|
|
427 {
|
|
428 time_t now = 0; /* as it says */
|
|
429
|
|
430 /*
|
|
431 * Now trigger the notifier if required.
|
|
432 */
|
|
433 time (&now);
|
|
434
|
|
435 /*
|
|
436 * Finally fire up the locker if time has come.
|
|
437 */
|
|
438 if (now >= trigger)
|
|
439 {
|
|
440 SetTrigger (time_limit);
|
|
441 return TRUE;
|
|
442 }
|
|
443
|
|
444 return FALSE;
|
|
445 }
|
|
446
|
|
447 /*
|
|
448 * Function for selecting events on a tree of windows
|
|
449 * --------------------------------------------------
|
|
450 */
|
|
451 static Void SelectEvents (Display *d, Window window, Boolean substructure_only)
|
|
452 {
|
|
453 Window root; /* root window of this window */
|
|
454 Window parent; /* parent of this window */
|
|
455 Window* children; /* children of this window */
|
|
456 Unsigned nof_children = 0; /* number of children */
|
|
457 Unsigned i; /* loop counter */
|
|
458 XWindowAttributes attribs; /* attributes of the window */
|
|
459
|
|
460
|
|
461 /*
|
|
462 * Start by querying the server about parent and child windows.
|
|
463 */
|
|
464 if (!XQueryTree (d, window, &root, &parent, &children, &nof_children))
|
|
465 {
|
|
466 return;
|
|
467 }
|
|
468
|
|
469
|
|
470 /*
|
|
471 * Build the appropriate event mask. The basic idea is that we don't
|
|
472 * want to interfere with the normal event propagation mechanism if
|
|
473 * we don't have to.
|
|
474 */
|
|
475 if (substructure_only)
|
|
476 {
|
|
477 XSelectInput (d, window, SubstructureNotifyMask);
|
|
478 }
|
|
479 else
|
|
480 {
|
|
481 if (parent == None) /* the *real* rootwindow */
|
|
482 {
|
|
483 attribs.all_event_masks =
|
|
484 attribs.do_not_propagate_mask = KeyPressMask;
|
|
485 }
|
|
486 else if (XGetWindowAttributes (d, window, &attribs) == 0)
|
|
487 {
|
|
488 return;
|
|
489 }
|
|
490
|
|
491 XSelectInput (d, window, SubstructureNotifyMask
|
|
492 | ( ( attribs.all_event_masks
|
|
493 | attribs.do_not_propagate_mask)
|
|
494 & KeyPressMask));
|
|
495 }
|
|
496
|
|
497
|
|
498 /*
|
|
499 * Now do the same thing for all children.
|
|
500 */
|
|
501 for (i = 0; i < nof_children; ++i)
|
|
502 {
|
|
503 SelectEvents (d, children[i], substructure_only);
|
|
504 }
|
|
505
|
|
506 if (nof_children) XFree ((Char*) children);
|
|
507 }
|
|
508
|
|
509
|
|
510 int catchFalseAlarms( Display *d, XErrorEvent *x )
|
|
511 {
|
|
512 return 0;
|
|
513 }
|
|
514
|
|
515 Queue windowQueue;
|
|
516 Window hiddenWin; /* hidden window */
|
|
517
|
|
518 void initAutoLock()
|
|
519 {
|
|
520 Display* d; /* display pointer */
|
|
521 Window r; /* root window */
|
|
522 Int s; /* screen index */
|
|
523 XSetWindowAttributes attribs; /* for dummy window */
|
|
524 int (*oldHandler)(Display *, XErrorEvent *);
|
|
525
|
|
526 d = GDK_DISPLAY();
|
|
527
|
|
528 oldHandler = XSetErrorHandler( catchFalseAlarms );
|
|
529 XSync (d, 0);
|
|
530
|
|
531 windowQueue = NewQueue ();
|
|
532
|
|
533 for (s = -1; ++s < ScreenCount (d); )
|
|
534 {
|
|
535 AddToQueue (windowQueue, r = RootWindowOfScreen (ScreenOfDisplay (d, s)));
|
|
536 SelectEvents (d, r, True);
|
|
537 }
|
|
538
|
|
539 /*
|
|
540 * Get ourselves a dummy window in order to allow display and/or
|
|
541 * session managers etc. to use XKillClient() on us (e.g. xdm when
|
|
542 * not using XDMCP).
|
|
543 *
|
|
544 * I'm not sure whether the window needs to be mapped for xdm, but
|
|
545 * the default set up Sun uses for OpenWindows and olwm definitely
|
|
546 * requires it to be mapped.
|
|
547 */
|
|
548 attribs.override_redirect = True;
|
|
549 hiddenWin = XCreateWindow (d, DefaultRootWindow (d), -100, -100, 1, 1, 0,
|
|
550 CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect,
|
|
551 &attribs);
|
|
552
|
|
553 XMapWindow (d, hiddenWin );
|
|
554
|
|
555 XSetErrorHandler( oldHandler );
|
|
556 }
|
|
557
|
|
558 /* I don't think this should be needed, but leaving the code here
|
|
559 in case I change my mind. */
|
|
560 /*
|
|
561 void cleanupAutoLock()
|
|
562 {
|
|
563 int (*oldHandler)(Display *, XErrorEvent *);
|
|
564 oldHandler = XSetErrorHandler( catchFalseAlarms );
|
|
565
|
|
566 FreeQueue( windowQueue );
|
|
567 XDestroyWindow( GDK_DISPLAY(), hiddenWin );
|
|
568 XSetErrorHandler( oldHandler );
|
|
569 }
|
|
570 */
|
|
571
|
|
572 /*
|
|
573 * Main function
|
|
574 * -------------
|
|
575 */
|
|
576 void waitIdle( int timeout, int until_idle )
|
|
577 {
|
|
578 Display* d; /* display pointer */
|
|
579 int (*oldHandler)(Display *, XErrorEvent *);
|
|
580 time_t now, prev;
|
|
581
|
|
582 time_limit = timeout;
|
|
583
|
|
584 d = GDK_DISPLAY();
|
|
585
|
|
586 oldHandler = XSetErrorHandler( catchFalseAlarms );
|
|
587
|
|
588 SetTrigger (time_limit);
|
|
589
|
|
590 time(&prev);
|
|
591
|
|
592 /*
|
|
593 * Main event loop.
|
|
594 */
|
|
595 while ( 1 )
|
|
596 {
|
|
597 if (ProcessEvents (d, windowQueue, until_idle))
|
|
598 break;
|
|
599 if (QueryPointer (d, until_idle))
|
|
600 break;
|
|
601
|
|
602 if (until_idle) {
|
|
603 time(&now);
|
|
604
|
|
605 if ((now > prev && now - prev > TIME_CHANGE_LIMIT) ||
|
|
606 (prev > now && prev - now > TIME_CHANGE_LIMIT+1))
|
|
607 {
|
|
608 /* the time has changed in one large jump. This could be because the
|
|
609 date was changed, or the machine was suspended. We'll just
|
|
610 reset the triger. */
|
|
611 SetTrigger (time_limit);
|
|
612 }
|
|
613
|
|
614 prev = now;
|
|
615
|
|
616 if ( EvaluateCounter (d) )
|
|
617 break;
|
|
618 }
|
|
619
|
|
620 /*
|
|
621 * It seems that, on some operating systems (VMS to name just one),
|
|
622 * sleep () can be vastly inaccurate: sometimes 60 calls to sleep (1)
|
|
623 * add up to only 30 seconds or even less of sleeping. Therefore,
|
|
624 * as of patchlevel 9 we no longer rely on it for keeping track of
|
|
625 * time. The only reason why we still call it, is to make xautolock
|
|
626 * (which after all uses a busy-form-of-waiting algorithm), less
|
|
627 * processor hungry.
|
|
628 */
|
|
629 sleep (1);
|
|
630 }
|
|
631
|
|
632 XSetErrorHandler( oldHandler );
|
|
633
|
|
634 }
|
|
635
|
|
636 void idle_main(pid_t gaimpid) {
|
|
637 initAutoLock();
|
|
638 while (1) {
|
|
639 waitIdle(IDLE_REPORT_TIME, 1);
|
|
640 kill(gaimpid, SIGALRM);
|
|
641 sleep(1); /* Just to be safe */
|
|
642 waitIdle(1, 0);
|
|
643 kill(gaimpid, SIGALRM);
|
|
644 }
|
|
645 }
|
|
646
|
|
647
|
|
648 #endif |