Logo Search packages:      
Sourcecode: cairo-dock-plugins version File versions  Download package

applet-top.c

/**
* This file is a part of the Cairo-Dock project
*
* Copyright : (C) see the 'copyright' file.
* E-mail    : see the 'copyright' file.
*
* This program 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 3
* of the License, or (at your option) any later version.
*
* This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <fcntl.h>
#include <unistd.h>

#include "applet-struct.h"
#include "applet-top.h"


void cd_sysmonitor_free_process (CDProcess *pProcess)
{
      if (pProcess == NULL)
            return ;
      g_free (pProcess->cName);
      g_free (pProcess);
}

static gboolean _cd_clean_one_old_processes (int *iPid, CDProcess *pProcess, double *fTime)
{
      if (pProcess->fLastCheckTime < *fTime)
            return TRUE;
      return FALSE;
}
void cd_sysmonitor_clean_old_processes (CairoDockModuleInstance *myApplet, double fTime)
{
      g_hash_table_foreach_remove (myData.pProcessTable, (GHRFunc) _cd_clean_one_old_processes, &fTime);
}

void cd_sysmonitor_clean_all_processes (CairoDockModuleInstance *myApplet)
{
      g_hash_table_remove_all (myData.pProcessTable);
}


static inline void _cd_sysmonitor_insert_process_in_top_list (CairoDockModuleInstance *myApplet, CDProcess *pProcess)
{
      int i, j;
      if (myData.bSortTopByRam)
      {
            if (pProcess->iMemAmount > 0)
            {
                  i = myConfig.iNbDisplayedProcesses - 1;
                  while (i >= 0 && (myData.pTopList[i] == NULL || pProcess->iMemAmount > myData.pTopList[i]->iMemAmount))
                        i --;
                  if (i != myConfig.iNbDisplayedProcesses - 1)
                  {
                        i ++;
                        for (j = myConfig.iNbDisplayedProcesses - 2; j >= i; j --)
                              myData.pTopList[j+1] = myData.pTopList[j];
                        myData.pTopList[i] = pProcess;
                  }
            }
      }
      else
      {
            if (pProcess->fCpuPercent > 0)
            {
                  i = myConfig.iNbDisplayedProcesses - 1;
                  while (i >= 0 && (myData.pTopList[i] == NULL || pProcess->fCpuPercent > myData.pTopList[i]->fCpuPercent))
                        i --;
                  if (i != myConfig.iNbDisplayedProcesses - 1)
                  {
                        i ++;
                        //g_print ("  fCpuPercent:%.2f%% => rang %d\n", 100*pProcess->fCpuPercent, i);
                        for (j = myConfig.iNbDisplayedProcesses - 2; j >= i; j --)
                              myData.pTopList[j+1] = myData.pTopList[j];
                        myData.pTopList[i] = pProcess;
                  }
            }
      }
}

#define jump_to_next_value(tmp) \
      while (*tmp != ' ' && *tmp != '\0') \
            tmp ++; \
      if (*tmp == '\0') { \
            cd_warning ("system monitor : problem when reading pipe"); \
            break ; \
      } \
      while (*tmp == ' ') \
            tmp ++; \

static void _cd_sysmonitor_get_process_data (CairoDockModuleInstance *myApplet, double fTime, double fTimeElapsed)
{
      static gchar cFilePathBuffer[20+1];  // /proc/12345/stat + 4octets de marge.
      static gchar cContent[512+1];
      
      cd_debug ("%s (%.2f)", __func__, fTimeElapsed);
      GError *erreur = NULL;
      GDir *dir = g_dir_open (CD_SYSMONITOR_PROC_FS, 0, &erreur);
      if (erreur != NULL)
      {
            cd_warning ("sysmonitor : %s", erreur->message);
            g_error_free (erreur);
            return ;
      }
      
      if (myData.pProcessTable == NULL)
            myData.pProcessTable = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, (GDestroyNotify) cd_sysmonitor_free_process);  // la cle est dans la valeur.
      if (myData.pTopList == NULL)
            myData.pTopList = g_new0 (CDProcess *, myConfig.iNbDisplayedProcesses);
      else
            memset (myData.pTopList, 0, myConfig.iNbDisplayedProcesses * sizeof (CDProcess *));
      if (myData.iMemPageSize == 0)
            myData.iMemPageSize = sysconf(_SC_PAGESIZE);
      
      const gchar *cPid;
      gchar *tmp;
      CDProcess *pProcess;
      int iNewCpuTime;
      unsigned long long iVmSize, iVmRSS, iTotalMemory;  // Quantité de mémoire totale utilisée / (Virtual Memory Resident Stack Size) Taille de la pile en mémoire.
      int i, j;
      while ((cPid = g_dir_read_name (dir)) != NULL)
      {
            if (! g_ascii_isdigit (*cPid))
                  continue;
            
            snprintf (cFilePathBuffer, 20, "/proc/%s/stat", cPid);
            int pipe = open (cFilePathBuffer, O_RDONLY);
            int iPid = atoi (cPid);
            if (pipe <= 0)  // pas de pot le process s'est termine depuis qu'on a ouvert le repertoire.
            {
                  g_hash_table_remove (myData.pProcessTable, &iPid);
                  continue ;
            }
            
            if (read (pipe, cContent, sizeof (cContent)) <= 0)
            {
                  cd_warning ("sysmonitor : can't read %s", cFilePathBuffer);
                  close (pipe);
                  continue;
            }
            close (pipe);
            
            pProcess = g_hash_table_lookup (myData.pProcessTable, &iPid);
            if (pProcess == NULL)
            {
                  pProcess = g_new0 (CDProcess, 1);
                  pProcess->iPid = iPid;
                  g_hash_table_insert (myData.pProcessTable, &pProcess->iPid, pProcess);
            }
            pProcess->fLastCheckTime = fTime;
            
            tmp = cContent;
            jump_to_next_value (tmp);  // on saute le pid.
            if (pProcess->cName == NULL)
            {
                  tmp ++;  // on saute la '('.
                  gchar *str = tmp;
                  while (*str != ')' && *str != '\0')
                        str ++;
                  pProcess->cName = g_strndup (tmp, str - tmp);
            }
            jump_to_next_value (tmp);  // on saute le nom.
            
            jump_to_next_value (tmp);  // on saute l'etat.
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            iNewCpuTime = atoll (tmp);  // user.
            jump_to_next_value (tmp);
            iNewCpuTime += atoll (tmp);  // system.
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);  // on saute le nice.
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            jump_to_next_value (tmp);
            iVmSize = atoll (tmp);
            jump_to_next_value (tmp);
            iVmRSS = atoll (tmp);
            iTotalMemory = iVmRSS * myData.iMemPageSize;
            
            //g_print ("%s : %d -> %d\n", pProcess->cName, pProcess->iCpuTime, iNewCpuTime);
            if (pProcess->iCpuTime != 0 && fTimeElapsed != 0)
                  pProcess->fCpuPercent = (iNewCpuTime - pProcess->iCpuTime) / myConfig.fUserHZ / myData.iNbCPU / fTimeElapsed;
            pProcess->iCpuTime = iNewCpuTime;
            pProcess->iMemAmount = iTotalMemory;
            
            _cd_sysmonitor_insert_process_in_top_list (myApplet, pProcess);
      }
      
      g_dir_close (dir);
}


static void _cd_sysmonitor_get_top_list (CairoDockModuleInstance *myApplet)
{
      // on recupere le delta T.
      g_timer_stop (myData.pTopClock);
      double fTimeElapsed = g_timer_elapsed (myData.pTopClock, NULL);
      g_timer_start (myData.pTopClock);
      GTimeVal time_val;
      g_get_current_time (&time_val);  // on pourrait aussi utiliser un compteur statique a la fonction ...
      double fTime = time_val.tv_sec + time_val.tv_usec * 1e-6;
      // on recupere les donnees de tous les processus.
      _cd_sysmonitor_get_process_data (myApplet, fTime, fTimeElapsed);
      // on nettoie la table des vieux processus.
      cd_sysmonitor_clean_old_processes (myApplet, fTime);
}

static gboolean _cd_sysmonitor_update_top_list (CairoDockModuleInstance *myApplet)
{
      // On ecrit les processus dans l'ordre.
      CD_APPLET_ENTER;
      CDProcess *pProcess;
      int i, iNameLength=0;
      for (i = 0; i < myConfig.iNbDisplayedProcesses; i ++)
      {
            pProcess = myData.pTopList[i];
            if (pProcess == NULL || pProcess->cName == NULL)
                  break;
            iNameLength = MAX (iNameLength, strlen (pProcess->cName));
      }
      
      gchar *cSpaces = g_new0 (gchar, iNameLength+1);
      memset (cSpaces, ' ', iNameLength);
      int iOffset;
      GString *sTopInfo = g_string_new ("");
      for (i = 0; i < myConfig.iNbDisplayedProcesses; i ++)
      {
            pProcess = myData.pTopList[i];
            if (pProcess == NULL || pProcess->cName == NULL)
                  break;
            iOffset = iNameLength-strlen (pProcess->cName);
            if (pProcess->iPid < 1e5)
            {
                  if (pProcess->iPid < 1e4)
                  {
                        if (pProcess->iPid < 1e3)
                        {
                              if (pProcess->iPid < 1e2)
                              {
                                    if (pProcess->iPid < 1e1)
                                          iOffset += 5;
                                    else
                                          iOffset += 4;
                              }
                              else
                                    iOffset += 3;
                        }
                        else
                              iOffset += 2;
                  }
                  else
                        iOffset += 1;
            }
            cSpaces[iOffset] = '\0';
            g_string_append_printf (sTopInfo, "  %s (%d)%s: %.1f%%  %s-  %.1f%s\n",
                  pProcess->cName,
                  pProcess->iPid,
                  cSpaces,
                  100 * pProcess->fCpuPercent,
                  (pProcess->fCpuPercent > .1 ? "" : " "),
                  (double) pProcess->iMemAmount / (myConfig.bTopInPercent && myData.ramTotal ? 10.24 * myData.ramTotal : 1024 * 1024),
                  (myConfig.bTopInPercent && myData.ramTotal ? "%" : D_("Mb")));
            cSpaces[iOffset] = ' ';
      }
      g_free (cSpaces);
      if (i == 0)  // liste vide.
      {
            g_string_free (sTopInfo, TRUE);
            CD_APPLET_LEAVE (TRUE);
            //return TRUE;
      }
      sTopInfo->str[sTopInfo->len-1] = '\0';
      
      // on affiche ca sur le dialogue.
      cairo_dock_render_dialog_with_new_data (myData.pTopDialog, (CairoDialogRendererDataPtr) sTopInfo->str);
      g_string_free (sTopInfo, TRUE);
      
      
      if (myData.iNbProcesses != g_hash_table_size (myData.pProcessTable))
      {
            myData.iNbProcesses = g_hash_table_size (myData.pProcessTable);
            gchar *cTitle = g_strdup_printf ("  [ Top %d / %d ] :", myConfig.iNbDisplayedProcesses, myData.iNbProcesses);
            cairo_dock_set_dialog_message (myData.pTopDialog, cTitle);
            g_free (cTitle);
      }
      CD_APPLET_LEAVE (TRUE);
      //return TRUE;
}



void cd_sysmonitor_stop_top_dialog (CairoDockModuleInstance *myApplet)
{
      if (myData.pTopDialog == NULL)
            return ;
      // on arrete la mesure.
      cairo_dock_stop_task (myData.pTopTask);
      // on detruit le dialogue.
      cairo_dock_dialog_unreference (myData.pTopDialog);
      myData.pTopDialog = NULL;
      cairo_surface_destroy (myData.pTopSurface);
      myData.pTopSurface = NULL;
      g_timer_destroy (myData.pTopClock);
      myData.pTopClock = NULL;
      // on libere la liste des processus.
      cd_sysmonitor_clean_all_processes (myApplet);
}

static void _sort_one_process (int *iPid, CDProcess *pProcess, CairoDockModuleInstance *myApplet)
{
      _cd_sysmonitor_insert_process_in_top_list (myApplet, pProcess);
}
static void _on_change_order (int iClickedButton, GtkWidget *pInteractiveWidget, CairoDockModuleInstance *myApplet, CairoDialog *pDialog)
{
      gboolean bSortByRamNew = (iClickedButton == 1);
      if (bSortByRamNew != myData.bSortTopByRam)  // on peut lire myData.bSortTopByRam car le thread n'y accede qu'en lecture.
      {
            cairo_dock_stop_task (myData.pTopTask);  // le thread se termine.
            myData.bSortTopByRam = bSortByRamNew;
            memset (myData.pTopList, 0, myConfig.iNbDisplayedProcesses * sizeof (CDProcess *));  // on re-trie tout suivant le nouvel ordre.
            g_hash_table_foreach (myData.pProcessTable, (GHFunc) _sort_one_process, myApplet);
            _cd_sysmonitor_update_top_list (myApplet);  // on redessine.
            cairo_dock_launch_task_delayed (myData.pTopTask, 1000. * myConfig.iProcessCheckInterval);  // on relance en gardant un intervalle de temps constant, sinon relancer la mesure tout de suite risquerait de donner des resultats peu precis.
      }
      cairo_dock_dialog_reference (pDialog);
}
void cd_sysmonitor_start_top_dialog (CairoDockModuleInstance *myApplet)
{
      g_return_if_fail (myData.pTopDialog == NULL);
      // on cree le dialogue.
      gchar *cTitle = g_strdup_printf ("  [ Top %d ] :", myConfig.iNbDisplayedProcesses);
      GtkWidget *pInteractiveWidget = gtk_vbox_new (FALSE, 0);
      gtk_widget_set_size_request (pInteractiveWidget,
            myConfig.pTopTextDescription->iSize * 15,
            myConfig.pTopTextDescription->iSize * myConfig.iNbDisplayedProcesses);  // approximatif au depart.
      /*myData.pTopDialog = cairo_dock_show_dialog_full (cTitle,
            myIcon,
            myContainer,
            0,
            MY_APPLET_SHARE_DATA_DIR"/"MY_APPLET_ICON_FILE,
            pInteractiveWidget,
            NULL,
            NULL,
            NULL);*/
      CairoDialogAttribute attr;
      memset (&attr, 0, sizeof (CairoDialogAttribute));
      attr.cText = cTitle;
      attr.cImageFilePath = MY_APPLET_SHARE_DATA_DIR"/"MY_APPLET_ICON_FILE;
      attr.pInteractiveWidget = pInteractiveWidget;
      attr.pActionFunc = (CairoDockActionOnAnswerFunc) _on_change_order;
      attr.pUserData = myApplet;
      gchar *cButtons[3] = {MY_APPLET_SHARE_DATA_DIR"/button-cpu.png", MY_APPLET_SHARE_DATA_DIR"/button-ram.png", NULL};
      attr.cButtonsImage = cButtons;
      myData.pTopDialog = cairo_dock_build_dialog (&attr, myIcon, myContainer);
      
      g_free (cTitle);
      g_return_if_fail (myData.pTopDialog != NULL);
      
      gpointer pConfig[2] = {myConfig.pTopTextDescription, "Loading ..."};
      cairo_dock_set_dialog_renderer_by_name (myData.pTopDialog, "Text", myDrawContext, (CairoDialogRendererConfigPtr) pConfig);
      
      // on lance la mesure.
      myData.pTopClock = g_timer_new ();
      myData.iNbProcesses = 0;
      if (myData.pTopTask == NULL)
            myData.pTopTask = cairo_dock_new_task (myConfig.iProcessCheckInterval,
                  (CairoDockGetDataAsyncFunc) _cd_sysmonitor_get_top_list,
                  (CairoDockUpdateSyncFunc) _cd_sysmonitor_update_top_list,
                  myApplet);
      cairo_dock_launch_task (myData.pTopTask);
}

Generated by  Doxygen 1.6.0   Back to index