view src/idle.c @ 936:311b3f72e7b9

[gaim-migrate @ 946] Sha la la la la la la, mmm, uh huh Was down at the linux expo, starin' at this blue octane, Mr. Flynn strikes up a conversation, with a black haired CS Major. She codes in C while her father hacks, oh, she's suddenly geeky. We all want something geeky, man I wish I was geeky. So come hack in silence down through the morning, sha la la la la la la la, yeah, uhh huh, yeah. Cut up Mark Spencer, show me some of that free software, and pass me a laptop, Mr. Flynn! Believe in me! Help me believe in anything, cuz, I wanna be someone geeky! Mr. Flynn and Me, tell each other linux tales, and we stare at the beautiful penguins, it's lookin' at you, oh no no, it's looking at me. Smiling in the CRT, Mp3's on the stero, when everyone's geeky, you can never be lonely. Well I'm gonna hack a program, syntax highlighting in blue and red and black and grey. All of the beautiful colours are very very meaningingful. Well you know C is my favorite language, I felt so symbolic yesterday. If I knew R. Stallman, I'd buy myself a grey keyboard and hack! Mr. Flynn and me look into the future! We stare at the beautiful Penguins, it's lookin at me, I dont think so, It's looking at me. Hacking in the CRT, I bought myself a grey keyboard, When everbody's geeky, I will never be lonely. I will never be lonely. I'm never gonna be .. loonely. I wanna be a penguin. Eeh -- everybody wanna pass as birds, they all wanna be big big birds, but we got different reasons for that. Believe in me, cuz I don't believe in anything. And I, wanna be someone, geeky, geeky, geeky, yeaaaah! Mr. Flynn and me, storming through the expo, and we stare at the beautiful penguin! It's coming for you, man there's got to be one for me! I wanna be L. Torvalds, Mr. Flynn wishes he was someone just a little more geeky, when everybody loves you, oh son! It's just about as geeky as you can be! Mr. Flynn and me .. starin' at the penguins, when I look at slashdot I wanna see me, staring right back at me. We all wanna be big geeks, but we dont know why and we don't know how .. when everybody loves me I'll be just about as geeky as I can be ... Mr. Flynn and me.. gonna be big geeks. **bows and cheers** Thank You, Thank You. committer: Tailor Script <tailor@pidgin.im>
author Rob Flynn <gaim@robflynn.com>
date Sun, 24 Sep 2000 22:48:54 +0000
parents 2846a03bda67
children 5bad25457843
line wrap: on
line source

#if 0
//----------------------------------------------------------------------------
// This is a somewhat modified kscreensaver.
// The original copyright notice follows
//
//----------------------------------------------------------------------------
//
// KDE screensavers
//
// This module is a heavily modified xautolock.
// The orignal copyright notice follows
//

/*****************************************************************************
 *
 * xautolock
 * =========
 *
 * Authors   :  S. De Troch (SDT) + M. Eyckmans (MCE)
 *
 * Date      :  22/07/90
 *
 * ---------------------------------------------------------------------------
 *
 * Copyright 1990, 1992-1995 by S. De Troch and MCE.
 *
 * Permission to use, copy, modify and distribute this software and the
 * supporting documentation without fee is hereby granted, provided that
 *
 *  1 : Both the above copyright notice and this permission notice
 *      appear in all copies of both the software and the supporting
 *      documentation.
 *  2 : No financial profit is made out of it.
 *
 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
 * EVENT SHALL THEY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 *****************************************************************************/



/*
 *  Have a guess what this does...
 *  ==============================
 *
 *  Warning for swm & tvtwm users : xautolock should *not* be compiled
 *  with vroot.h, because it needs to know the real root window.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if defined(hpux) || defined (__hpux)
#ifndef _HPUX_SOURCE
#define _HPUX_SOURCE
#endif /* _HPUX_SOURCE */
#endif /* hpux || __hpux */

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#ifdef VMS
#include <ssdef.h>    
#include <processes.h>  /* really needed? */
#endif /* VMS */

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xresource.h>

#include <time.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>

#ifdef HAVE_SYS_M_WAIT_H
#include <sys/m_wait.h>
#endif 

#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>

#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#include "gaim.h"

void initAutoLock();
void cleanupAutoLock();

/*
 *  Usefull macros and customization stuff
 *  ======================================
 */
#define PP(x)                      x

#ifdef VMS
#define ALL_OK                     1       /* for use by exit ()           */
#define PROBLEMS                   SS$_ABORT 
                                           /* for use by exit ()           */
#else /* VMS */
#define ALL_OK                     0       /* for use by exit ()           */
#define PROBLEMS                   1       /* for use by exit ()           */
#endif /* VMS */


#define CREATION_DELAY             30      /* should be > 10 and
                                              < min (45,(MIN_MINUTES*30))  */
#define TIME_CHANGE_LIMIT         120      /* if the time changes by more
                                              than x secs then we will
                                              assume someone has changed
                                              date or machine has suspended */


#ifndef HasVFork
#define vfork                      fork
#endif /* HasVFork */

#define Error0(str)                fprintf (stderr, str)
#define SetTrigger(delta)          trigger = time ((time_t*) NULL) + delta

static caddr_t                     ch_ptr;  /* this is dirty */
#define Skeleton(t,s)              (ch_ptr = (Caddrt) malloc ((Unsigned) s), \
                                      (ch_ptr == (Caddrt) NULL)              \
                                    ? (Error0 ("Out of memory.\n"),          \
                                       exit (PROBLEMS),                      \
                                       /*NOTREACHED*/ (t*) NULL              \
                                      )                                      \
                                    : (t*) ch_ptr                            \
                                   )                                         \

#define New(tp)                    Skeleton (tp, sizeof (tp))



/*
 *  New types
 *  =========
 */
#if defined (apollo) || defined (news1800) 
typedef int                        (*XErrorHandler) PP((Display*,
                                                        XErrorEvent*));
#endif /* apollo || news1800 */

#if defined (news1800) || defined (sun386) 
typedef int                        pid_t;
#endif /* news1800  || sun386*/

#ifdef VMS
typedef long                       pid_t;
#endif /* VMS */

#define Void                       void     /* no typedef because of VAX */
typedef int                        Int;
typedef char                       Char;
typedef char*                      String;
typedef int                        Boolean;
typedef caddr_t                    Caddrt;
typedef unsigned int               Unsigned;
typedef unsigned long              Huge;

typedef struct QueueItem_
        {
          Window                   window;        /* as it says          */
          time_t                   creationtime;  /* as it says          */
          struct QueueItem_*       next;          /* as it says          */
          struct QueueItem_*       prev;          /* as it says          */
        } aQueueItem, *QueueItem;

typedef struct Queue_
        {
          struct QueueItem_*       head;          /* as it says          */
          struct QueueItem_*       tail;          /* as it says          */
        } aQueue, *Queue;


/*
 *  Function declarations
 *  =====================
 */
#if defined(news1800) 
extern Void*    malloc                PP((Unsigned));
#endif /* news1800 */
 
static int      EvaluateCounter       PP((Display*));
static int      QueryPointer          PP((Display*, int));
static int      ProcessEvents         PP((Display*, Queue, int));
static Queue    NewQueue              PP((Void));
static Void     AddToQueue            PP((Queue, Window));
static Void     ProcessQueue          PP((Queue, Display*, time_t));
static Void     SelectEvents          PP((Display*, Window, Boolean));


/*
 *  Global variables
 *  ================
 */
static time_t        trigger = 0;            /* as it says                 */
static time_t        time_limit = IDLE_REPORT_TIME;   /* as it says */

/*
 *  Functions related to the window queue
 *  =====================================
 *
 *  Function for creating a new queue
 *  ---------------------------------
 */
static Queue  NewQueue ()

{
  Queue  queue;  /* return value */

  queue = New (aQueue);
  queue->tail = New (aQueueItem);
  queue->head = New (aQueueItem);

  queue->tail->next = queue->head;
  queue->head->prev = queue->tail;
  queue->tail->prev = queue->head->next = (QueueItem) NULL;

  return queue;
}


/*
 *  Function for adding an item to a queue
 *  --------------------------------------
 */
static Void  AddToQueue (Queue queue, Window window)
{
  QueueItem  newq;  /* new item */

  newq = New (aQueueItem);

  newq->window = window;
  newq->creationtime = time ((time_t*) NULL);
  newq->next = queue->tail->next;
  newq->prev = queue->tail;
  queue->tail->next->prev = newq;
  queue->tail->next = newq;
}

/*
 *  Function for processing those entries that are old enough
 *  ---------------------------------------------------------
 */
static Void  ProcessQueue (Queue queue, Display *d, time_t age)
{
  QueueItem  current;  /* as it says */
  time_t     now;      /* as it says */

  time (&now);
  current = queue->head->prev;

  while ( current->prev && current->creationtime + age < now )
  {
    SelectEvents (d, current->window, False);
    current = current->prev;
    free (current->next);
  }

  current->next = queue->head;
  queue->head->prev = current;
}


static Void  FreeQueue( Queue queue )
{
  QueueItem  current;  /* as it says */

  current = queue->head->prev;

  while ( current->prev )
  {
	  current = current->prev;
	  free(current->next);
  }

  free(current);
  free(queue);
}


/*
 *  Functions related to (the lack of) user activity
 *  ================================================
 *
 *  Function for processing the event queue
 *  ---------------------------------------
 */
static int  ProcessEvents (Display *d, Queue queue, int until_idle)
{
  XEvent  event;  /* as it says */

 /*
  *  Read whatever is available for reading.
  */
  while (XPending (d))
  {
    if (XCheckMaskEvent (d, SubstructureNotifyMask, &event))
    {
      if ((event.type == CreateNotify) && until_idle)
      {
        AddToQueue (queue, event.xcreatewindow.window);
      }
    }
    else
    {
      XNextEvent (d, &event);
    }


   /*
    *  Reset the counter if and only if the event is a KeyPress
    *  event *and* was not generated by XSendEvent ().
    */
    if ( event.type == KeyPress && !event.xany.send_event )
    {
      if (!until_idle)    /* We've become un-idle */
	return 1;
      SetTrigger (time_limit);
    }
  }


 /*
  *  Check the window queue for entries that are older than
  *  CREATION_DELAY seconds.
  */
  ProcessQueue (queue, d, (time_t) CREATION_DELAY);
  return 0;
}


/*
 *  Function for monitoring pointer movements
 *  -----------------------------------------
 */
static int  QueryPointer (Display *d, int until_idle)
{
  Window           dummy_w;            /* as it says                    */
  Int              dummy_c;            /* as it says                    */
  Unsigned         mask;               /* modifier mask                 */
  Int              root_x;             /* as it says                    */
  Int              root_y;             /* as it says                    */
  Int              i;                  /* loop counter                  */
  static Window    root;               /* root window the pointer is on */
  static Screen*   screen;             /* screen the pointer is on      */
  static Unsigned  prev_mask = 0;      /* as it says                    */
  static Int       prev_root_x = -1;   /* as it says                    */
  static Int       prev_root_y = -1;   /* as it says                    */
  static Boolean   first_call = TRUE;  /* as it says                    */
  
  
  /*
   *  Have a guess...
   */
  if (first_call)
    {
      first_call = 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.
   *
   *  Remark : Unlike XNextEvent(), XPending () doesn't notice if the
   *           connection to the server is lost. For this reason, earlier
   *           versions of xautolock periodically called XNoOp (). But
   *           why not let XQueryPointer () do the job for us, since
   *           we now call that periodically anyway?
   */
  if (!XQueryPointer (d, root, &root, &dummy_w, &root_x, &root_y,
                      &dummy_c, &dummy_c, &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 (   root_x != prev_root_x
	 || root_y != prev_root_y
	 || mask != prev_mask
	 )
    {
      prev_root_x = root_x;
      prev_root_y = root_y;
      prev_mask = mask;
      SetTrigger (time_limit);
      if (!until_idle)
	return 1;
    }
  
  return 0;
  
}

/*
 *  Function for deciding whether to lock
 *  -------------------------------------
 */
static int  EvaluateCounter (Display *d)
{
  time_t         now = 0;                /* as it says  */

 /*
  *  Now trigger the notifier if required. 
  */
  time (&now);

 /*
  *  Finally fire up the locker if time has come. 
  */
  if (now >= trigger)
  {
      SetTrigger (time_limit);
	  return TRUE;
  }

  return FALSE;
}

/*
 *  Function for selecting events on a tree of windows
 *  --------------------------------------------------
 */
static Void  SelectEvents (Display *d, Window window, Boolean substructure_only)
{
  Window             root;              /* root window of this window */
  Window             parent;            /* parent of this window      */
  Window*            children;          /* children of this window    */
  Unsigned           nof_children = 0;  /* number of children         */
  Unsigned           i;                 /* loop counter               */
  XWindowAttributes  attribs;           /* attributes of the window   */


 /*
  *  Start by querying the server about parent and child windows.
  */
  if (!XQueryTree (d, window, &root, &parent, &children, &nof_children))
  {
    return;
  }


 /*
  *  Build the appropriate event mask. The basic idea is that we don't
  *  want to interfere with the normal event propagation mechanism if
  *  we don't have to.
  */
  if (substructure_only)
  {
    XSelectInput (d, window, SubstructureNotifyMask);
  }
  else
  {
    if (parent == None)  /* the *real* rootwindow */
    {
      attribs.all_event_masks = 
        attribs.do_not_propagate_mask = KeyPressMask;
    }
    else if (XGetWindowAttributes (d, window, &attribs) == 0)
    {
      return;
    }

    XSelectInput (d, window,   SubstructureNotifyMask
                             | (  (  attribs.all_event_masks
                                   | attribs.do_not_propagate_mask)
                                & KeyPressMask));
  }


 /*
  *  Now do the same thing for all children.
  */
  for (i = 0; i < nof_children; ++i)
  {
    SelectEvents (d, children[i], substructure_only);
  }

  if (nof_children) XFree ((Char*) children);
}


int catchFalseAlarms( Display *d, XErrorEvent *x )
{
	return 0;
}

Queue  windowQueue;
Window hiddenWin;        /* hidden window    */

void initAutoLock()
{
  Display*              d;          /* display pointer  */
  Window                r;          /* root window      */
  Int                   s;          /* screen index     */
  XSetWindowAttributes  attribs;    /* for dummy window */
  int (*oldHandler)(Display *, XErrorEvent *);

  d = GDK_DISPLAY();

  oldHandler = XSetErrorHandler( catchFalseAlarms );
  XSync (d, 0);

  windowQueue = NewQueue ();

  for (s = -1; ++s < ScreenCount (d); )
  {
    AddToQueue (windowQueue, r = RootWindowOfScreen (ScreenOfDisplay (d, s)));
    SelectEvents (d, r, True);
  }

 /*
  *  Get ourselves a dummy window in order to allow display and/or
  *  session managers etc. to use XKillClient() on us (e.g. xdm when
  *  not using XDMCP).
  * 
  *  I'm not sure whether the window needs to be mapped for xdm, but
  *  the default set up Sun uses for OpenWindows and olwm definitely
  *  requires it to be mapped.
  */
  attribs.override_redirect = True;
  hiddenWin = XCreateWindow (d, DefaultRootWindow (d), -100, -100, 1, 1, 0,
                CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect,
				&attribs);

  XMapWindow (d, hiddenWin );

  XSetErrorHandler( oldHandler );
}

/*  I don't think this should be needed, but leaving the code here
    in case I change my mind. */
/*
void cleanupAutoLock()
{
  int (*oldHandler)(Display *, XErrorEvent *);
  oldHandler = XSetErrorHandler( catchFalseAlarms );

  FreeQueue( windowQueue );
  XDestroyWindow( GDK_DISPLAY(), hiddenWin );
  XSetErrorHandler( oldHandler );
}
*/

/*
 *  Main function
 *  -------------
 */
void waitIdle( int timeout, int until_idle )
{
  Display*              d;          /* display pointer  */
  int (*oldHandler)(Display *, XErrorEvent *);
  time_t now, prev;

  time_limit = timeout;

  d = GDK_DISPLAY();

  oldHandler = XSetErrorHandler( catchFalseAlarms );

  SetTrigger (time_limit);

  time(&prev);

 /*
  *  Main event loop.
  */
  while ( 1 )
  {
    if (ProcessEvents (d, windowQueue, until_idle))
      break;
    if (QueryPointer (d, until_idle))
      break;
    
    if (until_idle) {
      time(&now);
      
      if ((now > prev && now - prev > TIME_CHANGE_LIMIT) ||
	  (prev > now && prev - now > TIME_CHANGE_LIMIT+1))
	{
	  /* the time has changed in one large jump.  This could be because the
	     date was changed, or the machine was suspended.  We'll just
	     reset the triger. */
	  SetTrigger (time_limit);
	}
      
      prev = now;
      
      if ( EvaluateCounter (d) )
	break;
    }

    /*
     *  It seems that, on some operating systems (VMS to name just one),
     *  sleep () can be vastly inaccurate: sometimes 60 calls to sleep (1)
     *  add up to only 30 seconds or even less of sleeping. Therefore,
     *  as of patchlevel 9 we no longer rely on it for keeping track of
     *  time. The only reason why we still call it, is to make  xautolock
     *  (which after all uses a busy-form-of-waiting algorithm), less
     *  processor hungry.
     */
    sleep (1);
  }

  XSetErrorHandler( oldHandler );

}

void idle_main(pid_t gaimpid) {
  initAutoLock();
  while (1) {
    waitIdle(IDLE_REPORT_TIME, 1);
    kill(gaimpid, SIGALRM);
    sleep(1);                /* Just to be safe */
    waitIdle(1, 0);
    kill(gaimpid, SIGALRM);
  }
}


#endif