Logo Search packages:      
Sourcecode: qps version File versions  Download package

qps.cpp

// qps.cpp
// qps -- Qt-based visual process status monitor
// This program is free software. See the file COPYING for details.
// Author:  Mattias Engdegård, 1997-1999

#define QPS_VERSION "1.9.18.6"

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/utsname.h>    // uname 
#include <signal.h>
#include <errno.h>
#include <sched.h>

#include "./icon/icon.xpm" 

#include "qps.h"
#include "dialogs.h"
#include "scheddlg.h"
#include "lookup.h"
#include "svec.cpp"
#include "trayicon.h"
#include "misc.h"

#include <qpopupmenu.h>
#include <qmenubar.h>
#include <qkeycode.h>
#include <qapplication.h>
#include <qfont.h>
#include <qpainter.h>
#include <qaccel.h>
#include <qtooltip.h>
#include <qmessagebox.h>
#include <qbitmap.h>
#include <qclipboard.h>
#include <qnamespace.h> 
#include <qwidget.h>
#include <qurl.h>
#include <qcheckbox.h>
#include <qstatusbar.h>
#include <qtoolbutton.h>
#include <qpalette.h> 
#include <qsessionmanager.h> 
#include <qlayout.h> 
#include <qtabwidget.h> 


#define ARRAYSIZE(a) ((int)(sizeof(a)/sizeof((a)[0])))

/******************************* Global Variable START **********************/
List<Command*>    commands;
QStringList hidden_process;             //testing

int         default_font_height=0;
bool  flag_show_thread=false;           // to see informations at the thread level
bool  flag_show_thread_prev=false;      // previous state
int   flag_thread_ok=true;              // we presume a kernel 2.6.x using NPTL
bool  flag_session_start=false;
bool  flag_start_mini=false;
int   num_opened_files=0;               // testing
int   wmaker=false;                       // draft
int         kernel_version=0;             

Qps         *qps;
ControlBar  *control_bar=NULL;
SearchBox   *search_box=NULL;
TrayIcon    *trayicon=NULL;
TFrame            *infobox=NULL;              // testing

QString Qps::font_name="null";                //
int   Qps::font_size=0;


// for Non-ASCII Languages (chinese,japanese,korean) 
#include <qtextcodec.h> 
QTextCodec * codec = NULL;
#define UniString(str)   codec->toUnicode(str)


/******************************* END ***************************************/

// default values of settings, overridden by $HOME/.qps-settings if present
bool Qps::flag_show= true ;           //window state of last run : mini(iconic) or normal window 
bool Qps::flag_exit= false ;        
bool Qps::flag_systray= false ;           // DEL 
bool Qps::show_file_path = FALSE;
bool Qps::show_cmd_path = TRUE;
bool Qps::show_infobar = TRUE;
bool Qps::show_ctrlbar = TRUE;
bool Qps::show_mem_bar = TRUE;
bool Qps::show_swap_bar = TRUE;
bool Qps::show_cpu_bar = TRUE;
bool Qps::show_load_graph = TRUE;
bool Qps::load_in_icon = TRUE;
bool Qps::auto_save_options = TRUE;
#ifdef LINUX
bool  Qps::hostname_lookup = TRUE;
bool  Qps::service_lookup = TRUE;
#endif
bool  Qps::pids_to_selection = TRUE;
bool  Qps::cumulative = FALSE;
bool  Qps::vertical_cpu_bar = FALSE; // not used
#ifdef SOLARIS
bool    Qps::normalize_nice = TRUE;
bool    Qps::use_pmap = TRUE;
#endif
bool  Qps::tree_table = true; 
bool  Qps::tree_gadgets = TRUE;
bool  Qps::tree_lines = TRUE;
bool  Qps::comm_is_magic = FALSE;   
int   Qps::swaplimit = 10;    // default: warn when less than 10% swap left
bool  Qps::swaplim_percent = TRUE;

//DEL 
QColor Qps::color_set[NUM_COLORS] = {
      black, darkGray, lightGray, white, // cpu: user, nice, sys, idle
      black, darkGray, lightGray, white, // mem: used, buff, cache, free
      black, white, red,            // swap: used, free, warn
      black, green, darkGreen,      // load: bg, fg, lines
      yellow, black           // selected process: bg, fg
};


const char *Qps::color_name[NUM_COLORS] = {
      "cpu-user",
#ifdef LINUX
      "cpu-nice",
#endif
      "cpu-sys",
#ifdef SOLARIS
      "cpu-wait",
#endif
      "cpu-idle",
      "mem-used", "mem-buff", "mem-cache", "mem-free",
      "swap-used", "swap-free", "swap-warn",
      "load-bg", "load-fg", "load-lines",
      "selection-bg", "selection-fg"
};


void getDefaultFont(QFont f);

Qps::Qps()//: QWidget(0) // I will clean this code later (fasthyun@magicn.com) 
{
      setName("qps");
      // dont modify !!
      field_win = 0;
      prefs_win = 0;
      command_win = 0;
      
        setIconSize(45,45);
      QToolTip::setWakeUpDelay (50) ;
      
      getDefaultFont(font()); 
      QPopupMenu *popup_signals = make_signal_menu();
      connect(popup_signals, SIGNAL(activated(int)), SLOT(signal_menu(int)));
      
      m_popup = new QPopupMenu(this);
      m_popup->insertItem("Renice...",    this, SLOT(menu_renice()));
      m_popup->insertItem("Scheduling...",      this, SLOT(menu_sched()));
#ifdef MOSIX
      Procinfo::check_for_mosix();
      if(Procinfo::mosix_running) {
            QPopupMenu *popup_migrate = make_migrate_menu();
            m_popup->insertSeparator();
            m_popup->insertItem("Migrate", popup_migrate, POPUP_MIGRATE);
            connect(popup_migrate, SIGNAL(activated(int)), SLOT(mig_menu(int)));
      }
#endif      
      m_popup->insertSeparator();
      m_popup->insertItem("Terminate", this, SLOT(sig_term()),Key_Delete ); // better
      m_popup->insertItem("Hangup", this, SLOT(sig_hup()),ALT + Key_H);
      m_popup->insertItem("Stop", this, SLOT(sig_stop()),0,MENU_SIGSTOP);
      m_popup->insertItem("Kill", this, SLOT(sig_kill()),ALT + Key_K);
      m_popup->insertItem("Other Signals", popup_signals);
      m_popup->insertSeparator();
      m_popup->insertItem("View Details",       this, SLOT(menu_info()),0,MENU_DETAILS);
///     m_popup->insertItem("Add to Hidden List",this, SLOT(menu_add_hiddenlist()),0,MENU_ADD_HIDDEN);
///   m_popup->insertItem("Remove from Hidden List ",this,SLOT(menu_remove_hiddenlist()),0,MENU_REMOVE_HIDDEN);
//    m_popup->insertSeparator();
      m_popup->insertItem("Find Parent",  this, SLOT(menu_parent()),0,MENU_PARENT);
      m_popup->insertItem("Find Children",      this, SLOT(menu_children()),0,MENU_CHILD);
      m_popup->insertItem("Find Descendants", this, SLOT(menu_dynasty()),0,MENU_DYNASTY);


      m_fields = new QPopupMenu;
      //m_fields will be filled with all non-displayed fields when used
      connect(m_fields, SIGNAL(activated(int)), SLOT(add_fields_menu(int)));

      m_headpopup = new QPopupMenu;
      m_headpopup->insertItem("Remove Field", this, SLOT(menu_remove_field()));
      m_headpopup->insertItem("Add Field", m_fields);
      //m_headpopup->insertItem("Select Field", this, SLOT(menu_custom()) );

      m_view = new CheckMenu;
      m_view->setCheckable(TRUE);
      m_view->insertItem("Custom Fields", Procview::CUSTOM);
      m_view->insertItem("Basic Fields ", Procview::USER);
      m_view->insertItem("Jobs Fields ", Procview::JOBS);
      m_view->insertItem("Memory Fields ", Procview::MEM);
#ifdef LINUX
      m_view->insertItem("Scheduling Fields ", Procview::SCHED);
#endif
      m_view->insertSeparator();
      m_view->insertItem("Select Custom Fields...", MENU_CUSTOM); //better
      m_view->connectItem(MENU_CUSTOM, this, SLOT(menu_custom()));
      connect(m_view, SIGNAL(activated(int)), SLOT(view_menu(int)));
      
      m_command = new QPopupMenu;   // filled in later
      connect(m_command, SIGNAL(activated(int)), SLOT(run_command(int)));  // *** important
      m_options = new QPopupMenu;
      m_options->insertItem("Update Period...", this, SLOT(menu_update()));
      m_options->insertSeparator();
      m_options->insertItem("", MENU_PATH);     // text will be set later
      m_options->connectItem(MENU_PATH, this, SLOT(menu_toggle_path()));
      m_options->insertItem("", MENU_INFOBAR);  // text will be set later
      m_options->connectItem(MENU_INFOBAR, this, SLOT(menu_toggle_infobar()));
      m_options->insertItem("", MENU_CTRLBAR);  // text will be set later
      m_options->connectItem(MENU_CTRLBAR, this, SLOT(menu_toggle_ctrlbar()));
      m_options->insertItem("", MENU_CUMUL);    // text will be set later
      m_options->connectItem(MENU_CUMUL, this, SLOT(menu_toggle_cumul()));
      m_options->insertSeparator();
      m_options->insertItem("Preferences...", MENU_PREFS);
      m_options->connectItem(MENU_PREFS, this, SLOT(menu_prefs()));

      m_event = new QPopupMenu;

      QPopupMenu *m_help = new QPopupMenu(this);
      m_help->insertItem("FAQ", this, SLOT(license()),0,-1,-1);
      m_help->insertItem("About", this, SLOT(about()),0,-1,-1);
      
      menu = new QMenuBar(this);
      menu->insertItem("&Command", m_command);
      menu->insertItem("&Field", m_view);
      //menu->insertItem("Event", m_event);
      menu->insertItem("&Options", m_options);
      menu->insertItem("&Help", m_help);

      //for OS LOGO
        //menu->insertSeparator (2) ;
      //QLabel *label=new QLabel(this);
      //QPushButton *b1=new QPushButton(this);
      //b1->setText("dddd");
      //menu->insertItem(label, 0);
      //menu->insertItem(b1,3);
            
      ctrlbar = new ControlBar(this);
      control_bar=ctrlbar;
      connect(ctrlbar, SIGNAL(modeChange(bool)), SLOT(set_table_mode(bool)));
      connect(ctrlbar, SIGNAL(update()), SLOT(forced_update()));
      connect(ctrlbar->view, SIGNAL(activated( int ) ), SLOT(view_menu(int)));

      context_col = -1;
      proc = new Proc();      // creates fields etc
      procview = new Procview(proc);
      Procinfo::read_loadavg();

      pstable = new Pstable(this);
      pstable->setSizePolicy(QSizePolicy (QSizePolicy::Minimum,QSizePolicy::Expanding));
      //pstable->setSizePolicy(QSizePolicy (QSizePolicy::Minimum,QSizePolicy::Minimum));
      pstable->setProcview(procview);
      
      
      set_update_period(1000);      // default
      resize(640, 350);       // default initial size
      
      if(!read_settings()) 
      {
      }

      add_default_command();
      make_command_menu();

      pstable->set_sortcol();
      pstable->enableFolding(tree_gadgets);
      pstable->enableLines(tree_lines);
      set_table_mode(tree_table); // ok !!  pstable->refresh()

      default_icon = 0;
      default_icon_set = FALSE;

      infobar = new Infobar(this);
      infobar->configure();   // make settings take effect in status bar
      infobar->update_load();
      infobar->refresh();
      
      statusBar= new StatusBar(this);     
      infobox     =new TFrame(this);
      infobox->hide();

      // misc. accelerators
      QAccel *acc = new QAccel(this);
      acc->connectItem(acc->insertItem(Key_Space), this, SLOT(forced_update()));
      acc->connectItem(acc->insertItem(Key_Return), this, SLOT(forced_update()));
      acc->connectItem(acc->insertItem(Key_Q), this, SLOT(save_quit()));
      acc->connectItem(acc->insertItem(CTRL + Key_Q), this, SLOT(save_quit()));
      acc->connectItem(acc->insertItem(CTRL + Key_X), this, SLOT(save_quit()));
      ///acc->connectItem(acc->insertItem(ALT+Key_T), this, SLOT(sig_term()));
      
      connect(pstable, SIGNAL(doubleClicked(int)), SLOT(open_details(int)));
      connect(pstable, SIGNAL(rightClickedRow(QPoint)), this, SLOT(context_row_menu(QPoint)));
      connect(pstable, SIGNAL(rightClickedHeading(QPoint, int)), this, SLOT(context_heading_menu(QPoint, int)));
      //connect(pstable, SIGNAL(mouseMoveEvent ( QMouseEvent * e )),search_box2, SLOT(event_cursor_moved(QMouseEvent *e)));


      selection_items_enabled = TRUE;
      details.setAutoDelete(TRUE);
      update_load_time = 0;
      update_menu_status();
      bar_visibility(); // need 

      if(update_period != eternity)
            startTimer(update_period);
      
      /*
      QTabWidget *tbar = new QTabWidget(this);
      tbar->setMargin(10);
      tbar->addTab(pstable, "&Process");
      tbar->addTab(0, "&Files");
      tbar->addTab(0, "&Network");
      tbar->addTab(0, "&X Client");
      tbar->addTab(0, "&Users");
      */

      QVBoxLayout *v_layout = new QVBoxLayout( this);
      //v_layout->addWidget( menu);
      v_layout->setMenuBar(menu);
      v_layout->addWidget( infobar);
      v_layout->addWidget( ctrlbar);
      v_layout->addWidget( pstable);
      //v_layout->addWidget( tbar );
      v_layout->addWidget( statusBar);

      //printf("menubar1=%x\n",v_layout->menuBar () );
      //printf("menubar2=%x\n",v_layout->mainWidget () );
      //printf("menubar2=%x\n",menu);
      //printf("menu.layout=%x\n",menu->layout());
      //default_font_height=fontMetrics().height();
      //if((default_font_height>16 and font_size==0)  or font_name=="null")
      if(font_size==0  or font_name=="null")
      {
            menu_prefs();
      }
      
}

// explicit destructor needed for gcc
Qps::~Qps()
{}

// return true if all selected processes are stopped
bool Qps::all_selected_stopped()
{
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected && p->state != 'T')
                  return FALSE;
      }
      return TRUE;
}

// Adjust menu to contain Stop or Continue
void Qps::adjust_popup_menu(QPopupMenu *m, bool cont)
{
      //int idx = m->indexOf(MENU_DETAILS);
      
      if(procview->treeview==true)
      {
            m->setItemVisible (MENU_PARENT,false);
            m->setItemVisible (MENU_CHILD,false);
            m->setItemVisible (MENU_DYNASTY,false);
      }
      else  
      {
            m->setItemVisible (MENU_PARENT,true);
            m->setItemVisible (MENU_CHILD,true);
            m->setItemVisible (MENU_DYNASTY,true);
      }

      if(procview->viewproc == Procview::HIDDEN) 
      {
            m->setItemVisible (MENU_ADD_HIDDEN,false);
            m->setItemVisible (MENU_REMOVE_HIDDEN,true);
      }     
      else 
      {
            m->setItemVisible (MENU_ADD_HIDDEN,true);
            m->setItemVisible (MENU_REMOVE_HIDDEN,false);
      }

      int ix = m->indexOf(MENU_SIGSTOP);  

      if((ix >= 0) != cont)
            return;
      if(ix >= 0) {
            m->removeItemAt(ix);
            m->insertItem("Continue", MENU_SIGCONT, ix);
            m->connectItem(MENU_SIGCONT, this, SLOT(sig_cont()));
      } else {
            ix = m->indexOf(MENU_SIGCONT);
            m->removeItemAt(ix);
            m->insertItem("Stop", MENU_SIGSTOP, ix);
            m->connectItem(MENU_SIGSTOP, this, SLOT(sig_stop()));
      }
}

// build signal menu (used in two places)
QPopupMenu *Qps::make_signal_menu()
{
      QPopupMenu *m = new QPopupMenu;
      m->insertItem("SIGINT (interrupt)", MENU_SIGINT);
      m->insertItem("SIGCONT (continue)", MENU_SIGCONT);
      m->insertItem("SIGSTOP (stop)", MENU_SIGSTOP);
      m->insertItem("SIGQUIT (quit)", MENU_SIGQUIT);
      m->insertItem("SIGILL (illegal instruction)", MENU_SIGILL);
      m->insertItem("SIGABRT (abort)", MENU_SIGABRT);
      m->insertItem("SIGFPE (floating point exception)", MENU_SIGFPE);
      m->insertItem("SIGSEGV (segmentation violation)", MENU_SIGSEGV);
      m->insertItem("SIGPIPE (broken pipe)", MENU_SIGPIPE);
      m->insertItem("SIGALRM (timer signal)", MENU_SIGALRM);
      m->insertItem("SIGUSR1 (user-defined 1)", MENU_SIGUSR1);
      m->insertItem("SIGUSR2 (user-defined 2)", MENU_SIGUSR2);
      m->insertItem("SIGCHLD (child death)", MENU_SIGCHLD);
      m->insertItem("SIGTSTP (stop from tty)", MENU_SIGTSTP);
      m->insertItem("SIGTTIN (tty input)", MENU_SIGTTIN);
      m->insertItem("SIGTTOU (tty output)", MENU_SIGTTOU);
      return m;
}


#ifdef MOSIX

// build migrate menu
static int intcmp(const int *a, const int *b)
{
      return *a - *b;
}

QPopupMenu *Qps::make_migrate_menu()
{
      QString buf;
      QPopupMenu *m = new QPopupMenu;
      Svec<int> lst = Procinfo::mosix_nodes();
      lst.sort(intcmp);
      m->insertItem("Home", 1);
      m->insertItem("Find Best", 0);
      for(int i = 0; i < lst.size(); i++) {
            buf.sprintf("to node %d", lst[i]);
            m->insertItem(buf, lst[i] + 1);
      }
      return m;
}
#endif // MOSIX

// update the visibility of the {info, control, status} bar
void Qps::bar_visibility()
{
      update_menu_status();
      if(show_infobar) 
            infobar->show();
      else 
            infobar->hide();
      
      if(show_ctrlbar) 
            ctrlbar->show();
      else 
            ctrlbar->hide();
      
}


void Qps::timerEvent(QTimerEvent *e)
{
      killTimers();     // avoid accumulation of timer events if slow
      timer_refresh();
      startTimer(update_period);
}

// prevent too fast update !!!
void Qps::forced_update()
{
      killTimers(); 
      startTimer(200); // *** important !!!
      return;
}

void Qps::timer_refresh()
{
      Procinfo::read_loadavg();
      infobar->update_load();
      update_icon();
      statusBar->update(Procinfo::num_process);

      if(isVisible() && !pstable->locked()) {
            pstable->refresh(); //pstable->repaintAll(); //pstable->repaint_changed();
            if(show_infobar)
                  infobar->refresh();     // 2
            update_menu_selection_status();
      }
      refresh_details();
}


bool Qps::event(QEvent *e)
{
      switch(e->type()) {
            case QEvent::Show:
                  //printf("debug:Show event()\n");
                  break;
            case QEvent::Hide:
                  //printf("debug:hide event()\n");
                  break;
            case QEvent::ShowMinimized:
                  //printf("debug:showMinimized event()\n");
                  if(trayicon->hasSysTray()== true)
                        hide();
                  else 
                        showMinimized();
                  break;
            default:
                  return QWidget::event(e);
      }
      return true;
} 

/* void QWidget::showMinimized () 
void Qps::showMinimized()
{            
      if(trayicon->hasSysTray()== true)
            hide();
      else 
            QWidget::showMinimized();
} */

// Don't modify !!!! (by fasthyun@magicn.com) 
// commitData() 
//
// Description :
//    1. only called when Shown() state !!!!
//    2. called when clicked  WINDOW_EXIT_button  
//    3. called when user logout 
void Qps::closeEvent(QCloseEvent *e) 
{
      //printf("debug: closeEvent()\n");
      if(Qps::flag_exit || !trayicon->hasSysTray() )
      {
            if(auto_save_options)   save_settings(); // for session logout & no_systray system
            e->accept(); //important 
            return;
      }
      else  //if(trayicon->hasSysTray())
      {
            // Mean : systray & don't exit !!
            hide();
            return;
      }
}

void Qps::save_quit() // will be removed !!!(by fasthyun@magicn.com) 
{
      if(auto_save_options)
            save_settings();
      qApp->exit();
}

void Qps::update_icon()
{
      if(load_in_icon)
            set_load_icon();
      else
            set_default_icon();
}

// update table due to a configuration change
// col is column that has changed
// called by 
//    1.Qps::menu_toggle_cumul()
//    2.Qps::menu_toggle_path()
void Qps::update_table(int col)
{
      pstable->repaintColumns(col);
}

void Qps::set_default_icon()
{
      if(!default_icon_set) {
            if(!default_icon)
                  default_icon = new QPixmap((const char**)icon_xpm);
            setIcon(*default_icon);
            default_icon_set = TRUE;
      }
}

// avoid a fvwm/Qt 1.30 problem and create a filled mask for the icon
// (without mask, Qt would attempt to use a heuristically created mask)
void Qps::make_filled_mask(QPixmap *pm)
{
      QBitmap bm(pm->size());
      bm.fill(color1);
      pm->setMask(bm);
}

void Qps::set_load_icon()
{
      QPixmap *pm = infobar->load_icon(icon_width, icon_height);
      if(!pm->mask())   make_filled_mask(pm);  //???

      if(trayicon!=NULL) //always not NULL
      {
            if(trayicon->hasSysTray())
                  trayicon->setIcon(*pm); /// by hyun
            else setIcon(*pm);  
      }

      default_icon_set = FALSE;
}


QPixmap *Qps::get_load_icon()
{
      return infobar->load_icon(icon_width, icon_height);
}

void Qps::refresh_details()
{     
      details.first();
      Details *d = 0;
#ifdef LINUX
      Procinfo::invalidate_sockets();
#endif
      while((d = details.current()) != 0) {
            if(d->isVisible())
                  d->refresh();
            details.next();
      }
}


// update the menu status
void Qps::update_menu_status()
{
      update_menu_selection_status();
      ctrlbar->view->setCurrentItem (procview->viewproc);
      for(int i = Procview::USER; i <= Procview::CUSTOM; i++)
            m_view->setItemChecked(i, i == procview->viewfields);

      m_options->changeItem(show_file_path ? "Hide File Path" : "Show File Path",
                  MENU_PATH);
      m_options->changeItem(show_infobar
                  ? "Hide Graph" : "Show Graph",
                  MENU_INFOBAR);
      m_options->changeItem(show_ctrlbar
                  ? "Hide Control bar" : "Show Control Bar",
                  MENU_CTRLBAR);
      m_options->changeItem(cumulative
                  ? "Exclude Child Times" : "Include Child Times",
                  MENU_CUMUL);
}

// event_called when selection changed & update time
void Qps::update_menu_selection_status()
{
      bool enabled = (pstable->numSelected() > 0);

      if(enabled) {
            bool cont = all_selected_stopped();
            adjust_popup_menu(m_popup, cont);
      }
      
      for(int i = 0; i < commands.size(); i++)
      {
            if (commands[i]->IsNeedProc()==false)
                  m_command->setItemEnabled(MENU_FIRST_COMMAND + i, true);
            else  m_command->setItemEnabled(MENU_FIRST_COMMAND + i, enabled);
      
      }
}


void Qps::sig_term()
{
      send_to_selected(SIGTERM);
}

void Qps::sig_hup()
{
      send_to_selected(SIGHUP);
}

// need
void Qps::sig_stop()
{
      send_to_selected(SIGSTOP);
      
      //test
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected)
                  p->test_stop=1;   // who ??
                  //   
                  //sendsig(p, sig);
      }
}

void Qps::sig_cont()
{
      send_to_selected(SIGCONT);

      // test
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected)
                  p->test_stop=0;
      }

}

void Qps::sig_kill()
{
      send_to_selected(SIGKILL);
}

static struct { int id, sig; } sigtab[] = {
      { Qps::MENU_SIGQUIT, SIGQUIT },
      { Qps::MENU_SIGILL, SIGILL },
      { Qps::MENU_SIGABRT, SIGABRT },
      { Qps::MENU_SIGFPE, SIGFPE },
      { Qps::MENU_SIGSEGV, SIGSEGV },
      { Qps::MENU_SIGPIPE, SIGPIPE },
      { Qps::MENU_SIGALRM, SIGALRM },
      { Qps::MENU_SIGUSR1, SIGUSR1 },
      { Qps::MENU_SIGUSR2, SIGUSR2 },
      { Qps::MENU_SIGCHLD, SIGCHLD },
      { Qps::MENU_SIGCONT, SIGCONT },
      { Qps::MENU_SIGSTOP, SIGSTOP },
      { Qps::MENU_SIGTSTP, SIGTSTP },
      { Qps::MENU_SIGTTIN, SIGTTIN },
      { Qps::MENU_SIGTTOU, SIGTTOU },
      { Qps::MENU_SIGTERM, SIGTERM },
      { Qps::MENU_SIGHUP, SIGHUP },
      { Qps::MENU_SIGINT, SIGINT },
      { Qps::MENU_SIGKILL, SIGKILL }
};

void Qps::signal_menu(int id)
{
      for(int i = 0; i < ARRAYSIZE(sigtab); i++)
            if(id == sigtab[i].id) {
                  send_to_selected(sigtab[i].sig);
                  return;
            }
}
void Qps::about()
{
      QUrl url("http://qps.kldp.net");

      QString s("<h2> qps " QPS_VERSION "  -   A Visual Process Manager </h2> "
#ifdef SOLARIS
#ifdef _LP64
                  "64-bit "
#else
                  "32-bit "
#endif
                  "Solaris version "
#endif // SOLARIS
                  "using Qt library "
                  );

      s.append(qVersion());
      //s.append(url);
      s.append(
            "<hr>"
            "<b><u><font color=\"#0000FF\">http://qps.kldp.net</font></u></b><br>"
            "<center>Bug report to</center>"
            "<center><i>fasthyun@magicn.com </i></center>"
            "<center><i>Olivier.Daudel@u-paris10.fr </i></center>"
            "<center><i>jsanchez@todounix.homeip.net </i></center>"
            "<br>"
            "<center>Original qps by<br></center>"
            "<center><i>" 
            "     Mattias Engdegård\n" //"(f91-men@nada.kth.se)\n"
            "</i></center>"
            );

      QMessageBox mb("About qps", s, QMessageBox::NoIcon,
                  QMessageBox::Ok | QMessageBox::Default, 0, 0);
      mb.setIconPixmap(QPixmap((const char **)icon_xpm));
      mb.setTextFormat(Qt::RichText);
      mb.exec();
}


void Qps::menu_custom()
{
      view_menu(Procview::CUSTOM); // should !!

      if(field_win) {
            field_win->show();
            field_win->raise();
      } else {
            field_win = new FieldSelect(procview, proc);
            setWindowGroup(field_win);
            field_win->show();
            connect(field_win, SIGNAL(added_field(int)), this, SLOT(field_added(int)));
            connect(field_win, SIGNAL(removed_field(int)),this, SLOT(field_removed(int)));
      }
}

// Slot:  
// called by 
//    1.click Tree checkbox  
//    2.void Qps::view_menu(int id)
void Qps::set_table_mode(bool treemode)
{
      
      if(treemode) {
            // Tree Mode 
            // If COMMAND isn't the leftmost column, move it there
            for(int i = 0; i < procview->cats.size(); i++)
            {
                  if(procview->cats[i]->index == F_COMM)
                        procview->cats.remove(i);
            }

            // if COMMAND field doesn't exist, insert it
            procview->cats.insert(0,proc->allcats[F_COMM]);
            
            // for convinient PID sort (default)
            for(int i = 0; i < procview->cats.size(); i++)
                  if( procview->cats[i]->index == F_PID ) 
                  {
                        procview->reversed = FALSE;
                        procview->sortcat = procview->cats[i];
                        pstable->setSortedCol(i);   
                  }
      } 
      else  
      {
            //Linear_Mode
            
            //remove  F_COMM
            for(int i = 0; i < procview->cats.size(); i++)
            {
                  if( procview->cats[i]->index == F_COMM ) 
                        procview->remove_cat(i);  //better ! 

                  if( procview->cats[i]->index == F_CPU ) 
                  {
                        procview->reversed = FALSE;
                        procview->sortcat = procview->cats[i];
                        pstable->setSortedCol(i);
                  }
            }

            //pstable->topAndRepaint();
      }
      
      if(field_win)     field_win->update_boxes();
      
      pstable->set_mode(treemode);        
      pstable->refresh();         
      ctrlbar->setMode(treemode);   // toggle checkbox
      
      tree_table = treemode;        // QPS::tree_table  tree/linear mode 
}



// TEST CODE: 
// Don't modify (by fasthyun@magicn.com) 
// called by 
//      1.void Qps::add_fields_menu(int id)
void Qps::field_added(int index)
{
      int where; 
      where=pstable->sortedCol();   //where=pstable->clickedColumn();
        
      if( where ==0 )
      {     
            if (pstable->treeMode())
                where=1;
      }

      if(where> procview->cats.size())
            where= procview->cats.size()-1;

      if (index==F_CMDLINE)
      {
            where=procview->cats.size();  //last column
      }
      
      Category *newcat = proc->allcats[index];
      procview->cats.insert(where,newcat);
      pstable->refresh(); // repaint 
      pstable->update_customfield(); 
        update_menu_status();
      
}

void Qps::field_removed(int index)
{
      for(int i = 0; i < procview->cats.size(); i++) {
            if(procview->cats[i]->index == index) {
                  procview->cats.remove(i);  //procview->remove_cat(i);
                  pstable->refresh();     
                      pstable->update_customfield(); 
                  update_menu_status();
                  if(index == F_COMM)
                  {
                        set_table_mode(false); //should be changed to linear mode !!
                  }
                  context_col=-1;   // DEL *** important **** : right clicked column removed
                  return;
            }
      }
}


// need to change name
void Qps::view_menu(int id)
{
      int state = id;
      if(id >= Procview::ALL && id <= Procview::HIDDEN) {
            if(procview->viewproc != state) {
                  procview->viewproc = state;
                  //pstable->topAndRepaint();
                  pstable->refresh();
                  pstable->repaintAll();
            }
      }
      
      if(id >= Procview::USER && id <= Procview::CUSTOM) {
            if(procview->viewfields != state) {
                  procview->viewfields = state;
                  procview->set_fields();
                  set_table_mode(pstable->treeMode());
                  //pstable->set_sortcol();
            }
      }
      update_menu_status();
}


void Qps::menu_edit_cmd()
{
      FUNC_START;
      if(command_win) {
            command_win->show();
            command_win->raise();
      } else {
            command_win = new CommandDialog();
            setWindowGroup(command_win);
            command_win->show();
            connect(command_win, SIGNAL(command_change()),  SLOT(make_command_menu()));
      }
      FUNC_END;

}

void Qps::make_command_menu()
{
      FUNC_START;
      m_command->clear();
      m_command->connectItem(m_command->insertItem("Edit Commands..."),this, SLOT(menu_edit_cmd()));

      if(commands.size())
            m_command->insertSeparator();

      for(int i = 0; i < commands.size(); i++)
            commands[i]->menu = m_command->insertItem(commands[i]->name, MENU_FIRST_COMMAND + i);

      update_menu_selection_status();
//#ifdef SOLARIS
      /* Solaris CDE don't have a tray, so we need a method to terminate */
//    m_command->insertSeparator();
      m_command->insertItem( "&Quit", this, SLOT(save_quit()), Qt::ALT + Qt::Key_Q);
//#endif

      FUNC_END;
}


// run by MENU_ID ?
void Qps::run_command(int command_id)
{
      FUNC_START
      int i,j,idx=-1;
      
      for(i = 0; i < commands.size(); i++) 
      {
            if(commands[i]->menu==command_id)
            {
                  idx=i;
                  break;
            }
      }
      
      if (idx>=0)
      {
            if (commands[idx]->IsNeedProc()==false)
            {
                  commands[idx]->call(NULL);
                  return;
            }
      
            for(int i = 0; i < procview->procs.size(); i++) {
                  Procinfo *p = procview->procs[i];
                  if(p->selected)
                        commands[idx]->call(p);
            }
      }

      return;
}

void Qps::menu_add_hiddenlist()
{
      QString str;
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected)
                  //hidden_process.add(QString(p->command.ascii()));
                  //hidden_process.append(QString(p->command.ascii()));
                  hidden_process.append(QString(p->command));
      }
}


void Qps::menu_remove_hiddenlist()
{
      int idx;
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected)   
            {
                  for(int j=0;j<hidden_process.size();j++)
                  {
                        if(hidden_process[j]==p->command)
                              //hidden_process.erase(j);
                              hidden_process.remove(p->command);
                  }
            }
      }
}


void Qps::menu_info()
{
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected)   open_details(i);
      }
}

void Qps::open_details(int row)
{
      Procinfo *p = procview->procs[row];
      if(p->detail)
      {
            p->detail->raise();
      }
      else {
            Details *d = new Details(p, this, proc);
            details.append(d);
            setWindowGroup(d);
            d->show();
            connect(d, SIGNAL(closed(Details *)), this, SLOT(details_closed(Details *)));
      }
}

void Qps::details_closed(Details *d)
{
      //printf("details_closed()\n");
      //disconnect 
      
      // This is potentially dangerous, since this is called in response to a
      // signal sent by the widget that is about to be deleted here. Better hope
      // that nobody references the object down the call chain!
      details.removeRef(d);   // deletes window
}

// find parents of selected processes
void Qps::menu_parent()
{
      locate_relatives(&Procinfo::ppid, &Procinfo::pid);
}

void Qps::menu_children()
{
      locate_relatives(&Procinfo::pid, &Procinfo::ppid);
}

// Find processes whose attribute b is equal to the attribute a of
// selected processes. Center around topmost found.
// This is quadratic in worst case (shouldn't be a problem)
void Qps::locate_relatives(int Procinfo::*a, int Procinfo::*b)
{
      Svec<int> relatives;
      const int infinity = 2000000000;
      int topmost = infinity;
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected) {
                  pstable->setSelected(i, FALSE);
                  for(int j = 0; j < procview->procs.size(); j++) {
                        Procinfo *q = procview->procs[j];
                        if(p->*a == q->*b) {
                              relatives.add(j);
                              if(j < topmost)
                                    topmost = j;
                        }
                  }
            }
      }
      for(int i = 0; i < relatives.size(); i++)
            pstable->setSelected(relatives[i], TRUE);
      if(topmost < infinity)
            pstable->centerVertically(topmost);
      pstable->selectionNotify();
}

// select all (direct and indirect) offsprings of currently selected
// processes, without deselecting them
void Qps::menu_dynasty()
{
      Svec<int> family;
      for(int i = 0; i < procview->procs.size(); i++)
            if(pstable->isSelected(i))
                  family.add(i);
      for(int i = 0, j = family.size(); i < j;) {
            for(int k = 0; k < procview->procs.size(); k++) {
                  Procinfo *p = procview->procs[k];
                  for(int m = i; m < j; m++) {
                        Procinfo *q = procview->procs[family[m]];
                        if(q->pid == p->ppid)
                              family.add(k);
                  }
            }
            i = j;
            j = family.size();
      }
      const int infinity = 2000000000;
      int topmost = infinity;
      for(int i = 0; i < family.size(); i++) {
            pstable->setSelected(family[i], TRUE);
            if(family[i] < topmost)
                  topmost = family[i];
      }
      if(topmost < infinity)
            pstable->centerVertically(topmost);
      pstable->selectionNotify();
}

// change the update period, recomputing the averaging factor
void Qps::set_update_period(int milliseconds)
{
      update_period = milliseconds;
      Procview::avg_factor =  exp(-(float)update_period / Procview::cpu_avg_time);
}

// called when right button is clicked in table
void Qps::context_row_menu(QPoint p)
{
#ifdef MOSIX
      bool may_migrate = FALSE;
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected && p->cantmove.isEmpty()) {
                  may_migrate = TRUE;
                  break;
            }
      }
      m_popup->setItemEnabled(POPUP_MIGRATE, may_migrate);
#endif
      m_popup->popup(p);
}

// called when right button is clicked in heading
void Qps::context_heading_menu(QPoint p, int col)
{
      // rebuild the submenu: only include non-displayed fields
      m_fields->clear();
      int ncats = proc->allcats.size();
      QBitArray displayed(ncats);
      displayed.fill(FALSE);
      for(int i = 0; i < procview->cats.size(); i++)
            displayed.setBit(procview->cats[i]->index);
      for(int i = 0; i < ncats; i++)
            if(!displayed.testBit(i))
                  m_fields->insertItem(proc->allcats[i]->name, i);
      m_headpopup->setItemEnabled(1,      procview->cats.size() < proc->allcats.size());

      context_col = col;
      m_headpopup->popup(p);
}

// called when field is added from heading context menu
void Qps::add_fields_menu(int id)
{
      field_added(id);
      context_col = -1;
      if(field_win)
            field_win->update_boxes();
}

void Qps::menu_remove_field()
{
      if(procview->cats.size() >= 3) {
            field_removed(procview->cats[context_col]->index);
            if(field_win)
                  field_win->update_boxes();
      }
}

void Qps::menu_update()
{

      QString txt;
      if(update_period == eternity)
            txt = "1 s";
      else if(update_period % 1000 == 0)
            txt.sprintf("%d s", update_period / 1000);
      else
            txt.sprintf("%d ms", update_period);

      IntervalDialog id(txt, update_period != eternity);
      id.exec();
      save_settings();
      return;

}

void Qps::menu_toggle_path()
{     
      show_file_path = !show_file_path;
      update_menu_status();
      int col = procview->findCol(F_CMDLINE);
      if(col != -1)
            update_table(col);
}

void Qps::menu_prefs()
{
      if(prefs_win) {
            prefs_win->show();
            prefs_win->raise();
      } else {
            prefs_win = new Preferences();
            setWindowGroup(prefs_win);
            prefs_win->show();
            prefs_win->raise();

            connect(prefs_win, SIGNAL(prefs_change()),
                        this, SLOT(config_change()));
            connect(infobar, SIGNAL(config_change()),
                        prefs_win, SLOT(update_boxes()));
      }
}

// if "Preferences" changed
void Qps::config_change()
{
      infobar->configure();
      resizeEvent(0);         // in case it caused geometry change
      pstable->enableFolding(tree_gadgets);
      pstable->enableLines(tree_lines);
      details.first();
      Details *d = 0;
      while((d = details.current()) != 0) {
            d->config_change();
            details.next();
      }
}

void Qps::menu_toggle_infobar()
{     
      show_infobar = !show_infobar;
      bar_visibility();
}

void Qps::menu_toggle_ctrlbar()
{
      show_ctrlbar = !show_ctrlbar;
      bar_visibility();
}

void Qps::menu_toggle_cumul()
{
      cumulative = !cumulative;
      update_menu_status();
      int col = procview->findCol(F_TIME);
      if(col == pstable->sortedCol()) {
            procview->rebuild();
            pstable->transfer_selection();
            pstable->topAndRepaint();
      } else if(col != -1)
            update_table(col);
}

void Qps::menu_renice()
{
      if(pstable->numSelected() == 0)
            return;
      int defnice = -1000;

      // use nice of first selected process as default, and check permission
      bool possible = TRUE;
      int euid = geteuid();
      Procinfo *p = 0;
      for(int i = 0; i < procview->procs.size(); i++) {
            p = procview->procs[i];
            if(p->selected) {
                  if(defnice == -1000)
                        defnice = p->nice;
                  if(euid != 0 && euid != p->uid && euid != p->euid)
                        possible = FALSE;
            }
      }
      if(!possible) {
            QString s;
            s.sprintf("You do not have permission to renice the\n"
                        "selected process%s.\n"
                        "Only the process owner and the super-user\n"
                        "are allowed to do that.",
                        (pstable->numSelected() == 1) ? "" : "es");
            QMessageBox::warning(this, "Permission denied", s);
            return;
      }

      int new_nice;
      for(;;) {
            SliderDialog sd(defnice, -20, 19); // Linux kernel : -20 ~ 19  
            if(!sd.exec())
                  return;
            bool ok;
            new_nice = sd.ed_result.toInt(&ok);
            if(ok && new_nice >= -20 && new_nice <= 19)
                  break;
      }
      int nicecol = procview->findCol(F_NICE);
      int statcol = procview->findCol(F_STAT);
#ifdef LINUX
      int tmscol = procview->findCol(F_TMS);
#endif

      // do the actual renicing
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected) {
                  if(setpriority(PRIO_PROCESS, p->pid, new_nice) < 0) {
                        QString s;
                        switch(errno) {
                              case EPERM:
                                    // this shouldn't happen, but (e)uid could be changed...
                                    s.sprintf("You do not have permission to renice"
                                                " process %d (", p->pid);
                                    s.append(p->command);
                                    s.append(").\n"
                                                "Only the process owner and the super-user are"
                                                " allowed to do that.");
                                    QMessageBox::warning(this, "Permission denied", s);
                                    break;
                              case EACCES:
                                    QMessageBox::warning(this, "Permission denied",
                                                "Only the super-user may lower"
                                                " the nice value of a process.");
                                    return;
                        }
                  } else {
                        p->nice = new_nice; // don't wait for update
#ifdef LINUX
                        p->tms = p->get_tms(); //ditto
#endif
                  }
            }
      }
}



void Qps::menu_sched()
{
      if(pstable->numSelected() == 0)
            return;
      if(geteuid() != 0) {
            QMessageBox::warning(this, "Permission denied",
                        "Only the super-user may change the\n"
                        "scheduling policy and static priority.");
            return;
      }

      // provide reasonable defaults (first selected process)
      Procinfo *p = 0;
      for(int i = 0; i < procview->procs.size(); i++) {
            p = procview->procs[i];
            if(p->selected)
                  break;
      }
      int pol = p->get_policy();
      int pri = p->get_rtprio();
      SchedDialog sd(pol, pri);
      if(!sd.exec())
            return;
      if(sd.out_policy == SCHED_OTHER)
            sd.out_prio = 0;  // only allowed value
      int plcycol = procview->findCol(F_PLCY);
      int rpricol = procview->findCol(F_RPRI);
#ifdef LINUX
      int tmscol = procview->findCol(F_TMS);
#endif
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected) {
                  struct sched_param sp;
                  sp.sched_priority = sd.out_prio;
                  if(sched_setscheduler(p->pid, sd.out_policy, &sp) < 0) {
                        QString s;
                        if(errno == EPERM) {
                              s.sprintf("You do not have permission to change the\n"
                                          "scheduling and/or priority of"
                                          " process %d (", p->pid);
                              s.append(p->command);
                              s.append(").\n"
                                          "Only the super-user may do that.");
                              QMessageBox::warning(this, "Permission denied", s);
                              break;
                        }
                  } else {
                        p->policy = sd.out_policy; // don't wait for update
                        p->rtprio = sd.out_prio;
#ifdef LINUX
                        p->tms = p->get_tms();
#endif

                  }
            }
      }
}


#ifdef MOSIX

void Qps::mig_menu(int id)
{
      migrate_selected(id - 1);
}

void Qps::migrate_selected(int migto)
{
      // User wants to migrate a process somewhere
      // Write destination into /proc/XX/goto
      int warnremote = 0;
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected) {
                  if(p->isremote)
                        ++warnremote;
                  char buf[80];
                  sprintf(buf, "/proc/%d/goto", p->pid);
                  FILE *f = fopen(buf, "w");
                  if(f) {
                        fprintf(f, "%d", migto);
                        fclose(f);
                  }
            }
      }
      if (warnremote)
            QMessageBox::warning(this, "Remote migration attempt",
                        "You can only migrate an immigrated process "
                        "using qps on the home node.");
      earlier_refresh();            
}
#else

// Since this is a slot, at least a stub must be defined even when it isn't
// used (moc ignores preprocessor directives)
void Qps::mig_menu(int) {}

#endif // MOSIX

void Qps::send_to_selected(int sig)
{
      for(int i = 0; i < procview->procs.size(); i++) {
            Procinfo *p = procview->procs[i];
            if(p->selected)
                  sendsig(p, sig);
      }
      earlier_refresh();            // in case we killed one
}

QString supasswd;
void Qps::sendsig(Procinfo *p, int sig)
{
      if(kill(p->pid, sig) < 0) {
            // if the process is gone, do nothing - no need to alert the user
            if(errno == EPERM) {
                  QString s;
                  s.sprintf("You do not have permission to send a signal to"
                              " process %d (", p->pid);
                  s.append(p->command);
                  s.append(").\n"
                              "Only the super-user and the owner of the process"
                              " may send signals to it.");
                  /*
                  s.sprintf("You do not have permission to send a signal to"
                              " process %d (%s).\n"
                              "Only the super-user and the owner of the process"
                              " may send signals to it."
                              , p->pid,p->command);
                              */
                  //QMessageBox::warning(this, "Permission denied", s);
                  PermissionDialog *pd = new PermissionDialog(s,supasswd);
                  pd->exec();
                  printf("ssss\n");
            }
      }
}

// hyun : need changed !!
// make next timer_refresh happen a little earlier to remove processes that
// might have died after a signal
void Qps::earlier_refresh()
{
      const int delay = 500;  // wait no more than this period (ms)
      if(update_period > delay && update_period != eternity) {
            killTimers();
            startTimer(delay);
      }
}

// write geometry, visible fields and other settings to $HOME/.qps-settings
#ifdef LINUX

#ifdef MOSIX
#define SETTINGS_FILE ".qps-linux-mosix"
#else
#define SETTINGS_FILE ".qps-linux"
#endif

#endif // LINUX

#ifdef SOLARIS
#define SETTINGS_FILE ".qps-solaris"
#endif

// If the file format is changed in any way (including adding new
// viewable fields), QPS_FILE_VERSION must be incremented to prevent
// version mismatches and core dumps

#ifdef LINUX
#define QPS_FILE_VERSION 26   // version of .qps-linux file format
#endif

#ifdef SOLARIS
#define QPS_FILE_VERSION 24   // version of .qps-solaris file format
#endif

//DEL 
static FILE *open_settings_file(const char *mode)
{
      char name[512];
      char *home = getenv("HOME");
      if(!home) return 0;
      strcpy(name, home);
      strcat(name, "/" SETTINGS_FILE);
      return fopen(name, mode);
}

// very good ideas (by fasthyun@magicn.com) 
static struct { const char *name; bool *var; } 
flagvars[] = {
      { "systray", &Qps::flag_systray },
      { "cmdpath", &Qps::show_file_path },
      { "infobar", &Qps::show_infobar },
      { "ctrlbar", &Qps::show_ctrlbar },
      { "autosave", &Qps::auto_save_options },
      { "membar", &Qps::show_mem_bar },
      { "swapbar", &Qps::show_swap_bar },
      { "cpubar", &Qps::show_cpu_bar },
      { "loadgraph", &Qps::show_load_graph },
      { "loadicon", &Qps::load_in_icon },
      { "selectpids", &Qps::pids_to_selection },
      { "tree", &Qps::tree_table },
      { "lines", &Qps::tree_lines },
      { "magiccomm", &Qps::comm_is_magic },
      { "cumulative", &Qps::cumulative },
#ifdef LINUX
      { "hostname", &Qps::hostname_lookup },
      { "service", &Qps::service_lookup }    
#endif
#ifdef SOLARIS
      { "normalize", &Qps::normalize_nice },
      { "pmap", &Qps::use_pmap }
#endif
};

//????
static void print_escaped(FILE *f, const char *s)
{
      char c;
      while((c = *s++) != '\0') {
            if(strchr("\n\\,", c)) {
                  if(c == '\n')
                        c = 'n';
                  putc('\\', f);
            }
            putc(c, f);
      }
}

//????? 
static QString unescape(char **s)
{
      QString d;
      bool esc = FALSE;
      while(**s) {
            char c = *(*s)++;
            if(esc) {
                  if(c == 'n')
                        c = '\n';
                  esc = FALSE;
            } else if(c == '\\') {
                  esc = TRUE;
                  continue;
            } else if(c == ',')
                  break;            // unescaped comma as delimiter
            d += c;
      }     
      return d;
}

// delimiter : ,
// return splitted str 
static QString nextAtom(char **s)
{
      QString d;
      bool esc = FALSE;

      while(**s) {
            char c = *(*s)++;
            if(esc) {
                  if(c == 'n')
                        c = '\n';
                  esc = FALSE;
            } else if(c == '\\') {
                  esc = TRUE;
                  continue;
            } else if(c == ',')
                  break;            // unescaped comma as delimiter
            d += c;
      }     
      return d.stripWhiteSpace();
}



// USE
bool Qps::read_settings()
{
      int i;
      FILE *f = open_settings_file("r");
      if(!f) return FALSE;

      int ver;
      if(fscanf(f, "version: %d\n", &ver) != 1 || ver != QPS_FILE_VERSION) {
            fclose(f);
            return FALSE;
      }

      char buf[4096];
      while(fgets(buf, sizeof(buf), f)) {
            char *end = buf + strlen(buf) - 1;
            if(*end == '\n')
                  *end = '\0';
            char *p = strchr(buf, ':');
            if(!p)
                  continue;
            *p++ = '\0';
            if(strcmp(buf, "geom") == 0) {
                  int x, y, w, h;
                  char state[32];
                  if(sscanf(p, "%d %d %d %d %s", &x, &y, &w, &h,state) == 5)
                  {
                        setGeometry(x, y, w, h);
                        if(strcmp("show",state)==0)
                              Qps::flag_show=true;
                  }
                  else if(sscanf(p, "%d %d %d %d", &x, &y, &w, &h) == 4)
                  {
                        setGeometry(x, y, w, h);
                        //printf("xxxxxxxx11111111\n");
                  }


            } else if(strcmp(buf, "viewproc") == 0) {
                  procview->viewproc = (Procview::procstates)atoi(p);
            } else if(strcmp(buf, "fields") == 0) {
                  procview->cats.clear();
                  i=0;
                  int id;
                  for(char *q = strtok(p, " "); q; q = strtok(NULL, " ")) {
                        Category *cat = proc->cat_by_name(q);
                        if(cat)     procview->add_cat(cat);
                        
                        id = proc->field_id_by_name(q);
                        procview->custom_fields[i++]=id;
                  }
                  procview->custom_fields[i]=F_END;
                  procview->viewfields = Procview::CUSTOM;
                  procview->deduce_fields();

            } else if(strcmp(buf, "sort") == 0) {
                  char *q = strtok(p, " ");
                  Category *cat = proc->cat_by_name(q);
                  if(cat) {
                        procview->sortcat = cat;
                        q = strtok(NULL, " ");
                        procview->reversed = (q && strcmp(q, "reversed") == 0);
                  }
            } else if(strcmp(buf, "flags") == 0) {
                  for(int i = 0; i < ARRAYSIZE(flagvars); i++)
                        *flagvars[i].var = FALSE;

                  for(char *q = strtok(p, " "); q; q = strtok(NULL, " ")) {
                        for(int i = 0; i < ARRAYSIZE(flagvars); i++)
                              if(strcmp(q, flagvars[i].name) == 0)
                                    *flagvars[i].var = TRUE;
                  }
            } else if(strcmp(buf, "command") == 0) {
                  while(*p == ' ') p++;
                  bool toolbar;
                  QString cmdname = unescape(&p);
                  QString cmdline = unescape(&p);
                  QString opt = unescape(&p);

                  if(opt=="toolbar")
                        toolbar=true;
                  else
                        toolbar=false;

                  //printf("opt=%s\n",opt.ascii());
                  commands.add(new Command(cmdname, cmdline,toolbar));

                  if(*p) {
                        
                  }
                  
            } else if(strcmp(buf, "swaplim") == 0) {
                  swaplimit = atoi(p);
                  swaplim_percent = FALSE;
                  if(swaplimit < 0) {
                        swaplimit = -swaplimit;
                        swaplim_percent = TRUE;
                  }
            } else if(strcmp(buf, "interval") == 0) {
                  set_update_period(atoi(p));
            } else if(strcmp(buf, "font") == 0) {
                  QString name = unescape(&p);
                  QString size = unescape(&p);
                  font_name=name;
                  font_size=size.toInt();
            }else if(strcmp(buf, "hidden_process") == 0) {
                  QString name;
                  while(*p!=0)
                  {
                        name=nextAtom(&p);
                  ///PAUSED hidden_process.append(QString(name));
                  }
            }

      }

      fclose(f);
      return TRUE;
}


// USING : OLD
// write geometry, visible fields and other settings to $HOME/.qps-settings
void Qps::write_settings() //save setting
{
      FILE *f = open_settings_file("w");        
        pstable->update_customfield(); // important !!

      if(!f) 
      {                       
            QMessageBox::warning(this,"write_settings()", "Can't save !!!");
            return;     
      }
      
      fprintf(f, "version: %d\n",QPS_FILE_VERSION);
      fprintf(f, "geom: %d %d %d %d %s\n",
                  pos().x(), pos().y(), width(), height(),
                  isVisible() ? "show":"hide");
      fprintf(f, "viewproc: %d\n"
                  "fields:",procview->viewproc);


/*
      for(int i = 0; i < procview->cats.size(); i++)
      {
            fprintf(f, " %s", procview->cats[pstable->logCol(i)]->name);
      }
*/

      for(int i = 0; procview->custom_fields[i]!= F_END ; i++)
      {
            //only custom field will be saved !!
            Category *newcat = proc->allcats[procview->custom_fields[i]]; 
            fprintf(f, " %s",newcat->name);
      }
      
      fprintf(f,"\nsort: %s%s\n"
                  "flags:",
                  procview->sortcat->name,
                  procview->reversed ? " reversed" : "");

      for(int i = 0; i < ARRAYSIZE(flagvars); i++)
            if(*flagvars[i].var)
                  fprintf(f, " %s", flagvars[i].name);
      fprintf(f,"\n");



      fprintf(f,  "swaplim: %d\n"
                  "interval: %d\n",
                  swaplim_percent ? -swaplimit : swaplimit,
                  update_period);

      fprintf(f,  "font:%s,%d\n",font_name.ascii(),font_size);
      
      for(int i = 0; i < commands.size(); i++) {
            fprintf(f, "command: %s,%s",(const char *)commands[i]->name,
                        (const char *)commands[i]->cmdline );
            //print_escaped(f, (const char *)commands[i]->name);
            //putc(',', f);
            //print_escaped(f, (const char *)commands[i]->cmdline);
            if(commands[i]->toolbar==true)
                  fprintf(f, ",toolbar");
            fprintf(f, "\n");
      }

      fprintf(f, "hidden_process:");
      for(int i = 0; i < hidden_process.size(); i++) {
            if(i!=0) fprintf(f,",");
            fprintf(f,"%s",(const char *)hidden_process[i]);
      }
      fprintf(f, "\n");
      //printf("Qps: setting saved !\n");
      fclose(f);
}

// NEW Version !
// : write geometry, visible fields and other settings to $HOME/.qpsrc
void Qps::save_settings() 
{
      if(Qps::auto_save_options)
            write_settings();
}


// set the window_group hint to that of the main (qps) window
// DELETE ? -> need function , why?
void Qps::setWindowGroup(QWidget *w)
{
      XWMHints wmh;
      wmh.flags = WindowGroupHint;
      wmh.window_group = handle();
      XSetWMHints(w->x11Display(), w->handle(), &wmh);
}

//DEL
void Qps::setCommand(int argc, char **argv)
{
      // bug: argv[0] should really be frobbed into an absolute path name here
      XSetCommand(x11Display(), handle(), argv, argc);
}


// return host name with domain stripped
QString short_hostname()
{
      //// int gethostname(char *name, size_t len); // hyun?
      struct utsname u;
      uname(&u);
      char *p = strchr(u.nodename, '.');
      if(p) *p = '\0';
      QString s(u.nodename);
      return s;
}

bool opt_eq(const char *arg, const char *opt)
{
      if(arg[0] == '-') 
            arg++;
      if(arg[0] == '-')
            arg++;

      return strncmp(arg, opt,strlen(opt)) == 0;
      //} elsereturn FALSE;
}

// print some help to stdout and exit
void print_help(char *cmdname)
{
      fprintf(stderr, "Usage: qps [options]\n"
                  "Options:\n"
                  "  -version\t\tversion\n"
                  "  -wm     \t\tfor WindowMaker manager\n"
                  "  -mini     \t\tstart Minimized\n");
}


void Qps::clicked_trayicon()
{

      if(!isHidden())
      {
            hide();
      }
      else{
            showNormal(); 
      }
}


// For systray update. 
// this trick very suck, but I can't find a better solution. (by fasthyun@magicn.com)
class QpsApp :public QApplication
{ 
      public:
            QpsApp( int &argc, char ** argv ):QApplication(argc,argv){};
            virtual bool x11EventFilter ( XEvent *xev ){ 
                  // should catch all X11 event for systray_update !!
                  if(trayicon!=NULL) 
                        trayicon->checkNewTrayEvent(xev); // suck !
                  return FALSE;           
            };
            void commitData ( QSessionManager & sm );
};


// this is called  when Session Logout 
// before closeEvent() called
void QpsApp::commitData ( QSessionManager & sm )
{ 
      //DEL sm.release();
      //printf("debug: received Session signal\n");
      //sm.setRestartHint (QSessionManager::RestartIfRunning); 
      qps->flag_exit=true;
      QApplication::commitData(sm);
} 
#include <signal.h>
void signal_handler(int sig)
{
//    if(sig==SIGINT)   printf("DEBUG: catched SIGINT \n");
//    if(sig==SIGTERM)printf("DEBUG: catched SIGTERM \n");
      qps->save_quit();
      //printf("Qps: suiciding.... wait \n");
      printf("Qps: terminating...\n");
}


int main(int argc, char **argv, char **envp)
{
      signal(SIGTERM, signal_handler);
      signal(SIGINT, signal_handler);
      
      Lookup::initproctitle(argv, envp);  

      for(int i = 1; i < argc; i++) {
            if(opt_eq(argv[i], "version")) {
                  fprintf(stderr, "qps version " QPS_VERSION ", using Qt library %s\n", qVersion());
                  exit(1);
            } else if(opt_eq(argv[i], "help") || opt_eq(argv[i], "h")) {
                  print_help(argv[0]);
                  exit(1);
            } else      if(opt_eq(argv[i], "wm")) // for WindowMaker Dock
            {
                  wmaker=TRUE;
            } else if (opt_eq(argv[i], "session")) 
            {
                  flag_session_start=true;
            } else if (opt_eq(argv[i], "min"))  //mini
            {
                  flag_start_mini=true;
            } 
      }
      
      codec = QTextCodec::codecForLocale(); //for Local locale 
      
      QpsApp app(argc, argv);
#ifdef LINUX
      kernel_version=get_kernel_version();
      if(kernel_version< 20600)  // less 2.6 
      {
          printf("Qps: kernel 2.4.x not supported  temporary !!!\n\n");
//        printf("Qps: because of 2.4.x bugs \n\n");
          exit(0);
      }
#endif
      init_xpm();
      setQpsTheme();
      qps=new Qps();
      
      QString caption = "Qps - ";  
      caption.append(getenv("USER"));
      caption.append("@");
      caption.append(short_hostname());   //geteuid()

      qps->setCaption(UniString(caption));
      qps->setIcon(QPixmap( (const char**)icon_xpm));
      
      if(qps->font_name!="null" and qps->font_name!="default" )
      {
            QFont appFont(qps->font_name, qps->font_size);
            app.setFont(appFont,true);
      }

      // systray 
      QPopupMenu *menu=new QPopupMenu(qps);
      menu->insertItem( UniString("About"), qps, SLOT(about()) );
      menu->insertSeparator();
      menu->insertItem( "Show", qps, SLOT(showNormal()) );
      menu->insertItem( "Hide", qps, SLOT(hide()) );
      menu->insertSeparator();
      menu->insertItem( "&Quit", qps, SLOT(save_quit()), Qt::ALT + Qt::Key_Q);


      trayicon=new TrayIcon(QPixmap( (const char**)icon_xpm), "qps", menu);
      QObject::connect(trayicon,SIGNAL(clicked(const QPoint&)),qps,SLOT(clicked_trayicon()));
      QObject::connect(trayicon,SIGNAL(doubleClicked(const QPoint&)),qps,SLOT(clicked_trayicon())); //more covenient 
      
        // if WindowMaker then...
      if(wmaker) {   
            trayicon->setWMDock(true); 
      }

      //****************************************************************
      //  very very suck 
      //  because of  GNOME , KDE , Systray , Windowmaker, Motif....
      //  these all things are different !!! 
      
      trayicon->sysInstall(); 
      if(flag_session_start or flag_start_mini ) 
      {
            qps->showMinimized();
      }
      else {
            qps->show();
      }
            
      app.setMainWidget(qps);
      return app.exec();
}



Generated by  Doxygen 1.6.0   Back to index