/*
 * 	Copyright (C) 1994  Loganville Technical Resources, Incorporated.
 *
 * Xzet is free software; you can redistribute it and/or modify it under the 
 * terms of the GNU General Public License as published by the Free Software 
 * Foundation; either version 2, or (at your option) any later version.  
 *
 * Xzet is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. 
 *
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with 
 * xzet; see the file  COPYING. If  not, write to the Free Software Foundation, 
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 * xzet - Zmans version of xetat and top.  Graphical system activity and 
 *	  process monitoring utility.  Featuring the unknown ztach widget.
 *
 *
 *	This module has basically two functions:
 *
 *		1) Contains the code for the system activity window, 
 *		   which monitors systemwide idle, user, system, and nice	
 *		   time along with free swap and free memory.  Three file
 *		   systems are also optionally monitored here.
 *
 *		2) Contains the main driver code which maintains all of xzets
 *		   "knowledge base".  This is mostly contained in the functions
 *		   do_stats, get_meminfo,  mesure_charge_courante_systeme, 
 *		   and get_elapsed_time.  Note that a "standalone" proc file
 *		   system information reader module is the low-level guts of
 *		   all of this.  This is contained in the module bit-o-snap
 *		   which I mereley edited together, and did no real 
 *		   programming in.  The copyrights in that module say:
 *
 *		   Loosely derived from a simple hack posted to c.o.l
 *		    	 -Michael K. Johnson, johnsonm@stolaf.edu
 *		     
 *		   Some pieces from Branko Lankester's kmem ps, 
 *		     			copyright 1992 Branko Lankester
 *
 *	Please see the head of the System Data and Process Data Fetch Routines
 *      for further related copyright and acknowledgment information. 		       
 *		        
 *     IF YOU WANT TO PORT XZET TO ANOTHER SYSTEM, SIMPLY (HA!) PROVIDING 
 *     THE FUNCTIONALITY OF BIT-O-SNAP WILL GET YOU MOST OF THE WAY THERE.
 *
 *
 *  		This is bi-lingual source code.  Deal with it.
 *
 * The following copyright information is from xetat:
 *
 *
 * Copyright 1991 Lectra Systemes
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Lectra Systemes not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  Lectra Systemes makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * Auteur : Pierre FICHEUX 	Lectra Systemes R & D
 *
 */
static char sccsid[] = "@(#)activity.c	2.0 02/04/94";

/*
 * Visualisation de l'activite
 */

/* 
 * Fichiers standards du toolkit Xt
 */
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Shell.h>
#include <X11/Xaw/StripChart.h>
/*
 * Public include files for widgets used in this file.
 */
#ifdef X11R3
#include <X11/Command.h>
#include <X11/Box.h>
#else /* R4 or later */
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Box.h>
#endif /* X11R3 */

#include <sys/types.h>
#include <nlist.h>

/* for z stuff */
#include <fcntl.h>     /* for meminfo */
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <sys/time.h>
#include "sysinfo.h"
#include "ps.h"
#include "whattime.h"
#include <sys/vfs.h>	/* for file system info */
#include "Xzet.h"	/* new structs */
#include <stdio.h>				/* DEBUG ONLY */

#define  CPU_IDLE 0
#define  CPU_USER 1
#define  CPU_KERNEL 2
#define  CPU_WAIT 3
#define  CPU_SXBRK 4

#define VALEUR_FREEMEM		5

float 		get_elapsed_time();
void 		do_stats(struct ps_proc_head *ph, float elapsed_time,int pass);
unsigned int 	get_meminfo();


/*                        This is in Xzet.h 			*/
/* struct save_hist {
   		int ticks;
   		int pid;
   		int pcpu;
   		int utime;
   		int stime;
   		struct process_widgets *widget_ptr;   */     /* added by zman */
/*  }; */
 
struct save_hist new_save_hist[NR_TASKS];

struct 	z_system_info 	z_info; 
struct 	ps_proc_head 	*ph;
static float 	elapsed_time;
unsigned int 	main_mem;
char	there_are_popups = FALSE;
char 	active_running = FALSE;
extern 	char	process_running;
XtIntervalId	the_big_cahuna_meister;
extern ApplicationData	z_file_systems;
static long fs1_free=0;
static long fs2_free=0;
static long fs3_free=0;
static unsigned int	main_size=0;
static unsigned int	swap_size=0;
struct	statfs	z_fs_info;
Arg	zargs[10];
int	zar;
int	zscale;		
int	zminscale;
int	zupdate;
extern	char	auto_update_mode;

unsigned long availrmem;
static char buf[80];
unsigned int charge_courante_systeme[6];
static char flag_popup_actif = FALSE;


Dimension distance_horizontale, distance_verticale;

extern Widget forme_activite, topLevel, shell_activite;
extern Widget titre_idle, titre_user, titre_kernel, titre_wait, titre_sxbrk; 
extern Widget titre_freemem;
extern Widget indicateur_idle, indicateur_user, indicateur_kernel; 
extern Widget indicateur_wait, indicateur_sxbrk, indicateur_freemem;
extern Widget afficheur_idle, afficheur_user, afficheur_kernel, afficheur_wait;
extern Widget afficheur_sxbrk, afficheur_freemem;

extern 	Widget zman_process_info, zman_main_mem, zman_swap_mem, zman_fs1;
extern  Widget zman_fs1_data, zman_fs2, zman_fs2_data, zman_fs3, zman_fs3_data;
extern  Widget zman_free_scale_up, zman_free_scale_dn, zman_free_scale;
extern  Widget liste_processus;

void	update_zman_process_info();
void 	mise_a_jour_mesure(Widget, char *, unsigned int, int, unsigned int);
void 	Mise_a_jour_graphe_processus(Widget, caddr_t, caddr_t);
void 	mise_a_jour_liste_processus ();
void 	update_process_tachs_etc(int);



/***********************************************************************
 *
 * Lecture de la charge initiale du systeme         Fetch Initial Data
 *
 ***********************************************************************/
   			/* major change-o-rama by ZMAN */
void init_charge ()
{
    register int i;

    			/* get the initial data */
    		
    	for (i=0; i < NR_TASKS; i++)   /* set initial state of all pointers */
    		new_save_hist[i].widgets_ptr = (struct process_widgets *)NULL;	
	ph = take_snapshot(1, 1, 1, 1, 0, 0, 0);
  	elapsed_time = get_elapsed_time();
  	do_stats(ph,elapsed_time,0);
  
	main_mem=get_meminfo();
	availrmem = z_info.main_mem;	/* hope this is what u meant */
}



/***********************************************************************
 *							Main Cahuna
 * Lecture de la charge courante du systeme         Fetch Current Data
 *						    Set in Display Array
 *	 					  Update the zman additions
 ***********************************************************************/

void mesure_charge_courante_systeme ()
{
    register double tdiff, valeur;
    register int i;
    unsigned long freemem;


			/* get the memory data */
			
	main_mem=get_meminfo();		
	availrmem = z_info.main_mem;
	freemem = z_info.free_mem;


			/* get the time data */

	ph = refresh_snapshot(ph, 1, 1, 1, 1, 0, 0, 0);
  	elapsed_time = get_elapsed_time();
  	do_stats(ph,elapsed_time,1);
  	

    if (flag_popup_actif == TRUE)
       {	 	
  	update_zman_process_info();
  	
  	/* These might be better moved to a separate function with its own
  	   timeout, which could be longer than for the process, but to help
	   things out a bit, they won't write to screen if values are same */

		/* ...actually, don't do that now.  The entire program is
		   now controlled by one timeout, which makes it possible to   
		   easily detect if nothing is being monitored and go idle */
  	
  	if (z_info.main_mem != main_size)
  	       {	
  		mise_a_jour_mesure(zman_main_mem, "Main: %dK", 
  						z_info.main_mem / 1024, 0, 0);
		main_size = z_info.main_mem;
	       }	  						
	if (z_info.swap_mem != swap_size) 
	       {	
  		mise_a_jour_mesure(zman_swap_mem, "Swap: %dK", 
  						z_info.swap_mem / 1024, 0, 0);
		swap_size = z_info.swap_mem;
	       }		  						
	if (z_file_systems.mount_point1 != "")
	       {
           	statfs(z_file_systems.mount_point1, &z_fs_info);
           	if (z_fs_info.f_bfree != fs1_free)
           	       {		
           		mise_a_jour_mesure(zman_fs1_data, "%dK/%dK", 
           	     	  z_fs_info.f_bfree * (float)(z_fs_info.f_bsize / 1024), 1,
           	          z_fs_info.f_bavail * (float)(z_fs_info.f_bsize / 1024) );
			fs1_free = z_fs_info.f_bfree;
		       }	            	          
               }		         		
     	if (z_file_systems.mount_point2 != "")
	       {
           	statfs(z_file_systems.mount_point2, &z_fs_info);
           	if (z_fs_info.f_bfree != fs2_free)
           	       {		
           		mise_a_jour_mesure(zman_fs2_data, "%dK/%dK", 
           	          z_fs_info.f_bfree * (float)(z_fs_info.f_bsize / 1024), 1,
           	          z_fs_info.f_bavail * (float)(z_fs_info.f_bsize / 1024) );
           	        fs2_free = z_fs_info.f_bfree;  
           	       }    
               }		         		
        if (z_file_systems.mount_point3 != "")
	       {
           	statfs(z_file_systems.mount_point3, &z_fs_info);
           	if (z_fs_info.f_bfree != fs3_free)
           	       {		
           		mise_a_jour_mesure(zman_fs3_data, "%dK/%dK", 
           	          z_fs_info.f_bfree * (float)(z_fs_info.f_bsize / 1024), 1,
           	          z_fs_info.f_bavail * (float)(z_fs_info.f_bsize / 1024) );
           	        fs3_free = z_fs_info.f_bfree;
           	       }   
               }		         		
       }
     	          
	
	charge_courante_systeme[0] = z_info.idle_time;   /* was idle */
	charge_courante_systeme[1] = z_info.user_time;   /* was user */
	charge_courante_systeme[2] = z_info.system_time; /* was kernel */
	charge_courante_systeme[3] = z_info.nice_time;	 /* was wait */
	charge_courante_systeme[4] = z_info.free_swap;   /* was segbrk */
	charge_courante_systeme[5] = (int)freemem;	/* new? */


#ifdef DEBUG
    printf ("freemem %ld availrmem = %ld\n*****************************\n", 
    							freemem, availrmem);
#endif

    

	if (auto_update_mode == TRUE)   /* update process list ? */
	       {
	       	mise_a_jour_liste_processus ();
		XtUnmapWidget (liste_processus);
		XtMapWidget (liste_processus);
	       }	

    /* Initialise le comptage */
    	if (process_running == TRUE || there_are_popups == TRUE || 
 						active_running == TRUE)
    		the_big_cahuna_meister = XtAddTimeOut ((unsigned long)3000, 
    				mesure_charge_courante_systeme, (caddr_t)NULL);

#ifdef DEBUG
	else
	       {	
		printf("\nMesure_charge_c_s sees no further work to do, going idle");
		fflush(stdout);
	       }
#endif	       		    				

}



/****************************************************************************
 *
 *      	CallBack for Free Memory scaling
 *
 ****************************************************************************/

void Zfs_UpDn(w, closure, call_data)
Widget	w;		
caddr_t	closure;	/* unused */
caddr_t	call_data;	/* unused */
{
	zar = 0;
	XtSetArg(zargs[zar], XtNminScale, &zminscale); zar++;
	XtGetValues(indicateur_freemem, zargs, zar);
	if (w == zman_free_scale_dn)
	       {	
		zminscale *= 10;
		if (zminscale > 1000)
			zminscale = 1000;
	       }		
	else
	       {	
		zminscale /= 10;
		if (zminscale < 1)
			zminscale = 1;
	       }		
	zar = 0;
	XtSetArg(zargs[zar], XtNminScale, zminscale); zar++;	
	XtSetValues(indicateur_freemem, zargs, zar);		
	mise_a_jour_mesure(zman_free_scale, "Mag:%dx", 1000 / zminscale , 0, 0);
	XtUnmapWidget(indicateur_freemem);
	XtMapWidget(indicateur_freemem);		
}	



/****************************************************************************
 *
 *      		Format Global Process Info Label
 *
 ****************************************************************************/

void update_zman_process_info()
{
    	Arg args[10];
    	register int n;

    	sprintf (buf, 
    	   "%d processes, %d sleeping, %d runnning, %d zombie, %d stopped", 
		z_info.processes, z_info.sleeping, z_info.running, 
			z_info.zombie, z_info.stopped);

    	n = 0;
    	XtSetArg (args[n], XtNlabel, buf); n++;
    	XtSetValues (zman_process_info, args, n);
}



/****************************************************************************
 *
 * Fonction periodique de mise a jour des graphes      Format Graph Labels
 *
 ****************************************************************************/

/* Mise a jour de la valeur affichee */

void mise_a_jour_mesure(widget_afficheur, format, valeur_mesure, vm_2_p, vm_2) 
Widget widget_afficheur;
char *format;
unsigned int valeur_mesure;
int	vm_2_p;
unsigned int	vm_2;
{
    Arg args[10];
    register int n;

    /* Formatage de la mesure */
    if (vm_2_p)
    	sprintf (buf, format, valeur_mesure, vm_2);
    else
    	sprintf (buf, format, valeur_mesure);	

    n = 0;
    XtSetArg (args[n], XtNlabel, buf); n++;
    XtSetValues (widget_afficheur, args, n);
}



/***************************************************************************
 *
 * Callbacks utilises par les stripCharts (Idle,User,...)  STRIPCHART CALLBACKS
 *							   ...Set Label Text
 *							   .....Set SC value
 **************************************************************************/

						/* IDLE */
void Mise_a_jour_idle(w, closure, call_data)
     Widget	w;		/* unused */
     caddr_t	closure;	/* unused */
     caddr_t	call_data;	/* pointer to (double) return value */
{
    double *valeur = (double *)call_data;

    mise_a_jour_mesure (afficheur_idle, "%2d.%d%%", 
          			charge_courante_systeme[CPU_IDLE] / 10, 1,
          			charge_courante_systeme[CPU_IDLE] % 10 );

#ifdef DEBUG
    printf ("Mise a jour idle %g\n", charge_courante_systeme[CPU_IDLE]);
#endif
    *valeur = charge_courante_systeme[CPU_IDLE];
}


						/* USER */
void Mise_a_jour_user(w, closure, call_data)
     Widget	w;		/* unused */
     caddr_t	closure;	/* unused */
     caddr_t	call_data;	/* pointer to (double) return value */
{
    double *valeur = (double *)call_data;

    mise_a_jour_mesure (afficheur_user, "%2d.%d%%", 
				charge_courante_systeme[CPU_USER] / 10, 1,
				charge_courante_systeme[CPU_USER] % 10 );

#ifdef DEBUG
    printf ("Mise a jour user %g\n", charge_courante_systeme[CPU_USER]);
#endif
    *valeur = charge_courante_systeme[CPU_USER];
}


						/* SYSTEM */
void Mise_a_jour_kernel(w, closure, call_data)
     Widget	w;		/* unused */
     caddr_t	closure;	/* unused */
     caddr_t	call_data;	/* pointer to (double) return value */
{
    double *valeur = (double *)call_data;

    mise_a_jour_mesure (afficheur_kernel, "%2d.%d%%", 
    				charge_courante_systeme[CPU_KERNEL] / 10, 1, 
    				charge_courante_systeme[CPU_KERNEL] % 10 );

#ifdef DEBUG
    printf ("Mise a jour kernel %g\n", charge_courante_systeme[CPU_KERNEL]);
#endif
    *valeur = charge_courante_systeme[CPU_KERNEL];
}


						/* NICE */
void Mise_a_jour_wait(w, closure, call_data)
     Widget	w;		/* unused */
     caddr_t	closure;	/* unused */
     caddr_t	call_data;	/* pointer to (double) return value */
{
    double *valeur = (double *)call_data;

    mise_a_jour_mesure (afficheur_wait, "%2d.%d%%", 
    				charge_courante_systeme[CPU_WAIT] / 10 , 1,
    				charge_courante_systeme[CPU_WAIT] % 10  );

#ifdef DEBUG
    printf ("Mise a jour wait %g\n", charge_courante_systeme[CPU_WAIT]);
#endif
    *valeur = charge_courante_systeme[CPU_WAIT];
}


						/* FREE SWAP */
void Mise_a_jour_sxbrk(w, closure, call_data)
     Widget	w;		/* unused */
     caddr_t	closure;	/* unused */
     caddr_t	call_data;	/* pointer to (double) return value */
{
    double *valeur = (double *)call_data;

    mise_a_jour_mesure (afficheur_sxbrk, "%4dK ", 
    		charge_courante_systeme[CPU_SXBRK] / 1024, 0, 0  );

#ifdef DEBUG
    printf ("Mise a jour sxbrk %g\n", charge_courante_systeme[CPU_SXBRK]);
#endif
    *valeur = (1000. * charge_courante_systeme[CPU_SXBRK]) / z_info.swap_mem;

#ifdef DEBUG
    /*	printf("Freeswap is %dK or %d%%", 
    		charge_courante_systeme[CPU_SXBRK] / 1024,
    		100 * charge_courante_systeme[CPU_SXBRK] / z_info.swap_mem);
	zar = 0;
	XtSetArg(zargs[zar], XtNscale, &zscale); zar++;
	XtSetArg(zargs[zar], XtNminScale, &zminscale); zar++;
	XtSetArg(zargs[zar], XtNupdate, &zupdate); zar++;
	XtGetValues(w, zargs, zar);
	printf("      Scale: %d, minScale: %d, Update: %d\n", zscale, 
							zminscale, zupdate);*/
#endif							
}


						/* FREE MEM */
void Mise_a_jour_freemem(w, closure, call_data)
     Widget	w;		/* unused */
     caddr_t	closure;	/* unused */
     caddr_t	call_data;	/* pointer to (double) return value */
{
    double *valeur = (double *)call_data;

    mise_a_jour_mesure (afficheur_freemem, "%4dK", 
    		(int)charge_courante_systeme[VALEUR_FREEMEM] / 1024, 0, 0 );

#ifdef DEBUG
    printf ("Mise a jour freemem %g\n", charge_courante_systeme[VALEUR_FREEMEM] / (double)availrmem);
#endif
    *valeur = (1000. * charge_courante_systeme[VALEUR_FREEMEM]) / availrmem;
}



/****************************************************************************
 * 
 * Fonction d'initialisation de l'affichage de l'activite      ACTIVITY BUTTON
 *								START PROCESS
 *****************************************************************************/

void Activite (w, client_data, call_data)
Widget w;
caddr_t client_data, call_data;
{
#ifdef DEBUG
    printf (stderr, "Activite...\n");
#endif
    
    if (flag_popup_actif == FALSE) {

	/* affiche la fenetre */
	XtPopup (shell_activite, XtGrabNone);

	flag_popup_actif = TRUE;
	active_running = TRUE;
	if (process_running == FALSE && there_are_popups == FALSE)
		mesure_charge_courante_systeme(); 	 /* prime the pump */
    }
}



/***************************************************************************
 *
 * Callback du bouton Quitte du shell d'activite            ACTIVITY BUTTON
 *                                                            QUIT PROCESS
 ****************************************************************************/

void Quit_activite (w, client_data, call_data)
Widget w;
caddr_t client_data, call_data;
{
    	flag_popup_actif = FALSE;
    	XtPopdown (shell_activite);
    	active_running = FALSE;
    	if (process_running == FALSE && there_are_popups == FALSE)
              {
#ifdef DEBUG              
               printf("\nNo One Left, Removing timeout >%lx", the_big_cahuna_meister); 
               fflush(stdout);
#endif               
               XtRemoveTimeOut(the_big_cahuna_meister);
              }    	
}



/**************************************************************************
*         
*         System Data and Process Data Fetch Routines
*
************************************************************************** /

	/* The do_stats function (except for the xzet specific parts for
	   maintaining the active pop-ups widget pointers) was shamelessly
	   bogarted in its entirety from 'top', a program which inspired
	   this one (xzet). I do not know exactly who is responsible for
	   it, but the following people had copyrights or other specifiers
	   in 'top' and are therefore acknowledged here:	 
	   
 		Copyright (c) 1992 Branko Lankester
 		Copyright (c) 1992 Roger Binns
 		Michael K. Johnson, johnsonm@stolaf.edu.  
 		Robert J. Nation (nation@rocket.sanders.lockheed.com)
        
	   get_elapsed_time is also entirely to their credit (at least none 
	   to mine) and finally, get_meminfo is also largely to their credit.			
	*/

/********************************************************************
 * Finds all process with pop-ups active, destroys pop-ups for those
 *                    ...processes that have died since last do_stats
 *
 * Calculates the number of tasks in each state (running, sleeping, etc.)
 * Calculates the CPU time in each state (system, user, nice, etc)
 * calculates percent cpu usage for each task 
 *
 * Restore pointers to still active pop-ups, and calls update function
 ********************************************************************/
void do_stats(struct ps_proc_head *ph,float elapsed_time,int pass)
{
  struct ps_proc *this;
  int index,total_time,i;
  int sleeping = 0,stopped = 0,zombie = 0,running = 0;
  int system_ticks = 0,user_ticks = 0,nice_ticks = 0,idle_ticks = 1000;
  static int prev_count=0;
  static struct save_hist save_hist[NR_TASKS];
  int	active_popups_pid[NR_TASKS];
  struct process_widgets *active_popups_wp[NR_TASKS];
  int	ap_count=0;
  int	j;
  int stime, utime;
  /* make sure that there aren't too many tasks */
  if(ph->count >NR_TASKS)
     {
      printf("Abend. Xzet Max %d tasks, System now has %d", NR_TASKS,ph->count);
      exit(0);
     }
	/* get pid and shell pointer of all processes with pop-ups active */
  	for (i=0; i < prev_count; i++)
 		if ( (new_save_hist[i].widgets_ptr != NULL) && 
 		     (new_save_hist[i].widgets_ptr->flag_shell_actif == TRUE))
 		       {
 			active_popups_pid[ap_count] = new_save_hist[i].pid; 
 			active_popups_wp[ap_count++] = new_save_hist[i].widgets_ptr;  			
		       }
	if(ap_count)
		there_are_popups = TRUE;	       
  /* make a pass through the data to get stats */
  index=0;
  this = ph->head;
  while(this != NULL)
    {
      if((this->state == 'S')||(this->state == 'D'))
	sleeping++;
      else if(this->state == 'T')
	stopped++;
      else if(this->state == 'Z')
	zombie++;
      else if(this->state == 'R')
	running++;

      /* calculate time in this process */
      /* time is sum of user time (utime) plus system time (stime) */
      total_time = this->utime + this->stime;
      new_save_hist[index].ticks = total_time;
      new_save_hist[index].pid = this->pid;
      stime = this->stime;
      utime = this->utime;
      new_save_hist[index].stime = stime;
      new_save_hist[index].utime = utime;
      new_save_hist[index].widgets_ptr = (struct process_widgets *)NULL;
      /* find matching entry from previous pass*/
      i=0;
      while(i<prev_count)
	{
	  if(save_hist[i].pid == this->pid)
	    {
	      total_time -= save_hist[i].ticks;
	      stime -= save_hist[i].stime;
	      utime -= save_hist[i].utime;
	      i = NR_TASKS;
	    }
	  i++;
	}
      /* calculate percent cpu time for this task */
      new_save_hist[index].pcpu = (total_time * 10) /elapsed_time;
      if (new_save_hist[index].pcpu > 999)
	new_save_hist[index].pcpu = 999;

      /* calculate time in idle, system, user and niced tasks */
      idle_ticks -= new_save_hist[index].pcpu;
      system_ticks += stime;
      user_ticks += utime;
      if(this->priority < PZERO)
	nice_ticks += new_save_hist[index].pcpu;

      index++;
      this = this->next;
    }

  if(idle_ticks < 0)
    idle_ticks = 0;
  system_ticks = (system_ticks * 10) /elapsed_time;      
  user_ticks = (user_ticks * 10) /elapsed_time;

  z_info.processes = ph->count;
  z_info.sleeping = sleeping;
  z_info.running = running;
  z_info.zombie = zombie;
  z_info.stopped = stopped;
  z_info.user_time = user_ticks;
  z_info.idle_time = idle_ticks;
  z_info.system_time = system_ticks;
  z_info.nice_time = nice_ticks;  

  /* save this frame's information */
  for(i=0;i<ph->count;i++)
    {
      /* copy the relevant info for the next pass */
      save_hist[i].pid = new_save_hist[i].pid;
      save_hist[i].ticks = new_save_hist[i].ticks;
      save_hist[i].stime = new_save_hist[i].stime;
      save_hist[i].utime = new_save_hist[i].utime;
      save_hist[i].widgets_ptr = new_save_hist[i].widgets_ptr;
    }
  prev_count = ph->count;
  
  /* kill any active pop-ups whose process has terminated, and restore the */
  /* the widgets pointer to those still alive */
  	if(ap_count)
  		for (i=0; i < ap_count; i++)
  		       {	
  			for (j = 0; j < ph->count; j++)
  				if(new_save_hist[j].pid == active_popups_pid[i])
  				       {
  				       	new_save_hist[j].widgets_ptr = 
  				       			active_popups_wp[i];
  				       	update_process_tachs_etc(j);			
  					break;
  				       }	
  			if (j >= ph->count)     /* must have terminated */
  			       {
#ifdef DEBUG  			       
  			        printf("\nGoing to kill pid %d with wp >%x and shell >%x",
  			        	active_popups_pid[i],
  			        	active_popups_wp[i],
  			        	active_popups_wp[i]->popup_shell);
  			        fflush(stdout);
#endif  			        
  			       	XtPopdown(active_popups_wp[i]->popup_shell);
 				XtDestroyWidget(active_popups_wp[i]->popup_shell);
 				free(active_popups_wp[i]); 
  			       }				
  		       }
	else
		there_are_popups = FALSE;
}


/********************************************************************
 * Gets the memory info
 * returns the total memory available for use in percent memory 
 * usage calculations.
 ********************************************************************/
unsigned int get_meminfo()
{
  char memory[1024];
  static int fd;
  unsigned int main_mem, used_mem, free_mem, shared_mem, buf_mem;
  unsigned int swap_mem, used_swap, free_swap;

  fd = open("/proc/meminfo", O_RDONLY, 0);
  if (fd == -1) 
    {
      perror("ps.c:/proc/meminfo");
      exit(0);
    }
  read(fd,memory,sizeof(memory)-1);
  close(fd);
  sscanf(memory, "%*s %*s %*s %*s %*s %*s %u %u %u %u %u %*s %u %u %u",
	 &main_mem, &used_mem, &free_mem, &shared_mem, &buf_mem,
	 &swap_mem, &used_swap, &free_swap);
  z_info.main_mem = main_mem;
  z_info.used_mem = used_mem;
  z_info.free_mem = free_mem;  
  z_info.shared_mem = shared_mem;  
  z_info.buf_mem = buf_mem;  
  z_info.swap_mem = swap_mem;  
  z_info.used_swap = used_swap;  
  z_info.free_swap = free_swap;  
  return main_mem;
}



/********************************************************************
 * Finds the current time (in microseconds) and finds the time
 * elapsed since the last update. This is essential for computing
 * percent CPU usage.
 ********************************************************************/
float get_elapsed_time()
{
  struct timeval time;
  static struct timeval oldtime;
  struct timezone timez;
  float elapsed_time;

  gettimeofday(&time,&timez);
  elapsed_time = (time.tv_sec - oldtime.tv_sec) +
    (float)(time.tv_usec - oldtime.tv_usec)/1000000.0;
  oldtime.tv_sec = time.tv_sec;
  oldtime.tv_usec= time.tv_usec;
  return elapsed_time;
}
         







