Refactored key handlers.

Made the logic more modular, hopefully easier to follow,
and removed repeated code.
Plus, some optimization in RichString code.
This commit is contained in:
Hisham Muhammad 2014-11-19 23:17:52 -02:00
parent 300af4b829
commit 9faf4938b8
7 changed files with 462 additions and 371 deletions

View File

@ -1047,16 +1047,12 @@ void ProcessList_expandTree(ProcessList* this) {
} }
} }
void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, bool userOnly, uid_t userId, const char* incFilter) { void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, const char* incFilter) {
if (!flags) { if (!flags) {
following = this->following; following = this->following;
userOnly = this->userOnly;
userId = this->userId;
incFilter = this->incFilter; incFilter = this->incFilter;
} else { } else {
this->following = following; this->following = following;
this->userOnly = userOnly;
this->userId = userId;
this->incFilter = incFilter; this->incFilter = incFilter;
} }
@ -1072,7 +1068,7 @@ void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, bool
Process* p = ProcessList_get(this, i); Process* p = ProcessList_get(this, i);
if ( (!p->show) if ( (!p->show)
|| (userOnly && (p->st_uid != userId)) || (this->userOnly && (p->st_uid != this->userId))
|| (incFilter && !(String_contains_i(p->comm, incFilter))) || (incFilter && !(String_contains_i(p->comm, incFilter)))
|| (this->pidWhiteList && !Hashtable_get(this->pidWhiteList, p->pid)) ) || (this->pidWhiteList && !Hashtable_get(this->pidWhiteList, p->pid)) )
hidden = true; hidden = true;

View File

@ -183,6 +183,6 @@ ProcessField ProcessList_keyAt(ProcessList* this, int at);
void ProcessList_expandTree(ProcessList* this); void ProcessList_expandTree(ProcessList* this);
void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, bool userOnly, uid_t userId, const char* incFilter); void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, const char* incFilter);
#endif #endif

View File

@ -10,7 +10,7 @@ in the source distribution for its full text.
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define RICHSTRING_MAXLEN 300 #define RICHSTRING_MAXLEN 350
/*{ /*{
#include "config.h" #include "config.h"
@ -52,8 +52,8 @@ in the source distribution for its full text.
typedef struct RichString_ { typedef struct RichString_ {
int chlen; int chlen;
CharType chstr[RICHSTRING_MAXLEN+1];
CharType* chptr; CharType* chptr;
CharType chstr[RICHSTRING_MAXLEN+1];
} RichString; } RichString;
}*/ }*/
@ -64,7 +64,7 @@ typedef struct RichString_ {
#define charBytes(n) (sizeof(CharType) * (n)) #define charBytes(n) (sizeof(CharType) * (n))
static inline void RichString_setLen(RichString* this, int len) { static void RichString_extendLen(RichString* this, int len) {
if (this->chlen <= RICHSTRING_MAXLEN) { if (this->chlen <= RICHSTRING_MAXLEN) {
if (len > RICHSTRING_MAXLEN) { if (len > RICHSTRING_MAXLEN) {
this->chptr = malloc(charBytes(len+1)); this->chptr = malloc(charBytes(len+1));
@ -83,6 +83,8 @@ static inline void RichString_setLen(RichString* this, int len) {
this->chlen = len; this->chlen = len;
} }
#define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0)
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) { static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) {
@ -92,10 +94,11 @@ static inline void RichString_writeFrom(RichString* this, int attrs, const char*
return; return;
int newLen = from + len; int newLen = from + len;
RichString_setLen(this, newLen); RichString_setLen(this, newLen);
memset(&this->chptr[from], 0, sizeof(CharType) * (newLen - from));
for (int i = from, j = 0; i < newLen; i++, j++) { for (int i = from, j = 0; i < newLen; i++, j++) {
this->chptr[i].chars[0] = data[j]; CharType* c = &(this->chptr[i]);
this->chptr[i].attr = attrs; c->attr = attrs;
c->chars[0] = data[j];
c->chars[1] = 0;
} }
this->chptr[newLen].chars[0] = 0; this->chptr[newLen].chars[0] = 0;
} }
@ -171,4 +174,4 @@ void RichString_appendn(RichString* this, int attrs, const char* data, int len)
void RichString_write(RichString* this, int attrs, const char* data) { void RichString_write(RichString* this, int attrs, const char* data) {
RichString_writeFrom(this, attrs, data, 0, strlen(data)); RichString_writeFrom(this, attrs, data, 0, strlen(data));
} }

View File

@ -9,7 +9,7 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#define RICHSTRING_MAXLEN 300 #define RICHSTRING_MAXLEN 350
#include "config.h" #include "config.h"
#include <ctype.h> #include <ctype.h>
@ -50,8 +50,8 @@ in the source distribution for its full text.
typedef struct RichString_ { typedef struct RichString_ {
int chlen; int chlen;
CharType chstr[RICHSTRING_MAXLEN+1];
CharType* chptr; CharType* chptr;
CharType chstr[RICHSTRING_MAXLEN+1];
} RichString; } RichString;
@ -61,6 +61,8 @@ typedef struct RichString_ {
#define charBytes(n) (sizeof(CharType) * (n)) #define charBytes(n) (sizeof(CharType) * (n))
#define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0)
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish); extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);

View File

@ -144,7 +144,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
this->lastScan = now; this->lastScan = now;
} }
Header_draw(this->header); Header_draw(this->header);
ProcessList_rebuildPanel(this->header->pl, false, false, false, false, NULL); ProcessList_rebuildPanel(this->header->pl, false, false, NULL);
} }
for (int i = 0; i < panels; i++) { for (int i = 0; i < panels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i); Panel* panel = (Panel*) Vector_get(this->panels, i);

769
htop.c
View File

@ -24,6 +24,7 @@ in the source distribution for its full text.
#include "AffinityPanel.h" #include "AffinityPanel.h"
#include "IOPriorityPanel.h" #include "IOPriorityPanel.h"
#include "IncSet.h" #include "IncSet.h"
#include "htop.h"
#include <unistd.h> #include <unistd.h>
#include <math.h> #include <math.h>
@ -40,7 +41,32 @@ in the source distribution for its full text.
//#link m //#link m
#define COPYRIGHT "(C) 2004-2012 Hisham Muhammad" #define COPYRIGHT "(C) 2004-2014 Hisham Muhammad"
/*{
typedef enum {
HTOP_OK = 0x00,
HTOP_REFRESH = 0x01,
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH
HTOP_SAVE_SETTINGS = 0x04,
HTOP_KEEP_FOLLOWING = 0x08,
HTOP_QUIT = 0x10,
HTOP_REDRAW_BAR = 0x20,
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
} Htop_Reaction;
typedef Htop_Reaction (*Htop_Action)();
typedef struct State_ {
IncSet* inc;
Settings* settings;
UsersTable* ut;
} State;
typedef bool(*ForeachProcessFn)(Process*, size_t);
}*/
static void printVersionFlag() { static void printVersionFlag() {
fputs("htop " VERSION " - " COPYRIGHT "\n" fputs("htop " VERSION " - " COPYRIGHT "\n"
@ -185,8 +211,6 @@ static void Setup_run(Settings* settings, const Header* header) {
ScreenManager_delete(scr); ScreenManager_delete(scr);
} }
typedef bool(*ForeachProcessFn)(Process*, size_t);
static bool foreachProcess(Panel* panel, ForeachProcessFn fn, int arg, bool* wasAnyTagged) { static bool foreachProcess(Panel* panel, ForeachProcessFn fn, int arg, bool* wasAnyTagged) {
bool ok = true; bool ok = true;
bool anyTagged = false; bool anyTagged = false;
@ -222,7 +246,8 @@ static int selectedPid(Panel* panel) {
return -1; return -1;
} }
static Object* pickFromVector(Panel* panel, Panel* list, int x, int y, const char** keyLabels, FunctionBar* prevBar, Header* header) { static Object* pickFromVector(Panel* panel, Panel* list, int x, const char** keyLabels, Header* header) {
int y = panel->y;
const char* fuKeys[] = {"Enter", "Esc", NULL}; const char* fuKeys[] = {"Enter", "Esc", NULL};
int fuEvents[] = {13, 27}; int fuEvents[] = {13, 27};
ScreenManager* scr = ScreenManager_new(0, y, 0, -1, HORIZONTAL, header, false); ScreenManager* scr = ScreenManager_new(0, y, 0, -1, HORIZONTAL, header, false);
@ -244,7 +269,6 @@ static Object* pickFromVector(Panel* panel, Panel* list, int x, int y, const cha
ScreenManager_delete(scr); ScreenManager_delete(scr);
Panel_move(panel, 0, y); Panel_move(panel, 0, y);
Panel_resize(panel, COLS, LINES-y-1); Panel_resize(panel, COLS, LINES-y-1);
FunctionBar_draw(prevBar, NULL);
if (panelFocus == list && ch == 13) { if (panelFocus == list && ch == 13) {
Process* selected = (Process*)Panel_getSelected(panel); Process* selected = (Process*)Panel_getSelected(panel);
if (selected && selected->pid == pid) if (selected && selected->pid == pid)
@ -271,28 +295,6 @@ static bool setUserOnly(const char* userName, bool* userOnly, uid_t* userId) {
return false; return false;
} }
static void setTreeView(ProcessList* pl, FunctionBar* fuBar, bool mode) {
if (mode) {
FunctionBar_setLabel(fuBar, KEY_F(5), "Sorted");
FunctionBar_setLabel(fuBar, KEY_F(6), "Collap");
} else {
FunctionBar_setLabel(fuBar, KEY_F(5), "Tree ");
FunctionBar_setLabel(fuBar, KEY_F(6), "SortBy");
}
if (mode != pl->treeView) {
FunctionBar_draw(fuBar, NULL);
}
pl->treeView = mode;
}
static inline void setSortKey(ProcessList* pl, FunctionBar* fuBar, ProcessField sortKey, Panel* panel, Settings* settings) {
pl->sortKey = sortKey;
pl->direction = 1;
setTreeView(pl, fuBar, false);
settings->changed = true;
ProcessList_printHeader(pl, Panel_getHeader(panel));
}
static const char* getMainPanelValue(Panel* panel, int i) { static const char* getMainPanelValue(Panel* panel, int i) {
Process* p = (Process*) Panel_get(panel, i); Process* p = (Process*) Panel_get(panel, i);
if (p) if (p)
@ -318,7 +320,15 @@ static bool expandCollapse(Panel* panel) {
return true; return true;
} }
void sortBy(Panel* panel, ProcessList* pl, Settings* settings, int headerHeight, FunctionBar* defaultBar, Header* header) { static inline Htop_Reaction setSortKey(ProcessList* pl, ProcessField sortKey) {
pl->sortKey = sortKey;
pl->direction = 1;
pl->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction sortBy(Panel* panel, ProcessList* pl, Header* header) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem)); Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
Panel_setHeader(sortPanel, "Sort by"); Panel_setHeader(sortPanel, "Sort by");
const char* fuFunctions[] = {"Sort ", "Cancel ", NULL}; const char* fuFunctions[] = {"Sort ", "Cancel ", NULL};
@ -330,14 +340,12 @@ void sortBy(Panel* panel, ProcessList* pl, Settings* settings, int headerHeight,
Panel_setSelected(sortPanel, i); Panel_setSelected(sortPanel, i);
free(name); free(name);
} }
ListItem* field = (ListItem*) pickFromVector(panel, sortPanel, 15, headerHeight, fuFunctions, defaultBar, header); ListItem* field = (ListItem*) pickFromVector(panel, sortPanel, 15, fuFunctions, header);
if (field) { if (field) {
settings->changed = true; reaction |= setSortKey(pl, field->key);
setSortKey(pl, defaultBar, field->key, panel, settings);
} else {
ProcessList_printHeader(pl, Panel_getHeader(panel));
} }
Object_delete(sortPanel); Object_delete(sortPanel);
return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
} }
static void millisleep(unsigned long millisec) { static void millisleep(unsigned long millisec) {
@ -350,6 +358,307 @@ static void millisleep(unsigned long millisec) {
} }
} }
// ----------------------------------------
static Htop_Reaction actionResize(Panel* panel) {
Panel_resize(panel, COLS, LINES-(panel->y)-1);
return HTOP_REDRAW_BAR;
}
static Htop_Reaction actionSortByMemory(Panel* panel, ProcessList* pl) {
(void) panel;
return setSortKey(pl, PERCENT_MEM);
}
static Htop_Reaction actionSortByCPU(Panel* panel, ProcessList* pl) {
(void) panel;
return setSortKey(pl, PERCENT_CPU);
}
static Htop_Reaction actionSortByTime(Panel* panel, ProcessList* pl) {
(void) panel;
return setSortKey(pl, TIME);
}
static Htop_Reaction actionToggleKernelThreads(Panel* panel, ProcessList* pl) {
(void) panel;
pl->hideKernelThreads = !pl->hideKernelThreads;
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionToggleUserlandThreads(Panel* panel, ProcessList* pl) {
(void) panel;
pl->hideUserlandThreads = !pl->hideUserlandThreads;
pl->hideThreads = pl->hideUserlandThreads;
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionToggleTreeView(Panel* panel, ProcessList* pl) {
(void) panel;
pl->treeView = !pl->treeView;
if (pl->treeView) pl->direction = 1;
ProcessList_expandTree(pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionIncFilter(Panel* panel, ProcessList* pl, Header* header, State* state) {
(void) panel, (void) pl, (void) header;
IncSet_activate(state->inc, INC_FILTER);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionIncSearch(Panel* panel, ProcessList* pl, Header* header, State* state) {
(void) panel, (void) pl, (void) header;
IncSet_activate(state->inc, INC_SEARCH);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionHigherPriority(Panel* panel) {
bool changed = changePriority(panel, -1);
return changed ? HTOP_REFRESH : HTOP_OK;
}
static Htop_Reaction actionLowerPriority(Panel* panel) {
bool changed = changePriority(panel, 1);
return changed ? HTOP_REFRESH : HTOP_OK;
}
static Htop_Reaction actionInvertSortOrder(Panel* panel, ProcessList* pl) {
(void) panel;
ProcessList_invertSortOrder(pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionSetIOPriority(Panel* panel, ProcessList* pl, Header* header) {
(void) panel, (void) pl;
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK;
IOPriority ioprio = p->ioPriority;
Panel* ioprioPanel = IOPriorityPanel_new(ioprio);
const char* fuFunctions[] = {"Set ", "Cancel ", NULL};
void* set = pickFromVector(panel, ioprioPanel, 21, fuFunctions, header);
if (set) {
IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel);
bool ok = foreachProcess(panel, (ForeachProcessFn) Process_setIOPriority, (size_t) ioprio, NULL);
if (!ok)
beep();
}
Panel_delete((Object*)ioprioPanel);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionSetSortColumn(Panel* panel, ProcessList* pl, Header* header) {
return sortBy(panel, pl, header);
}
static Htop_Reaction actionExpandOrCollapse(Panel* panel) {
bool changed = expandCollapse(panel);
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionExpandCollapseOrSortColumn(Panel* panel, ProcessList* pl, Header* header) {
return pl->treeView ? actionExpandOrCollapse(panel) : actionSetSortColumn(panel, pl, header);
}
static Htop_Reaction actionQuit() {
return HTOP_QUIT;
}
static Htop_Reaction actionSetAffinity(Panel* panel, ProcessList* pl, Header* header) {
if (pl->cpuCount == 1)
return HTOP_OK;
#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY)
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK;
Affinity* affinity = Process_getAffinity(p);
if (!affinity) return HTOP_OK;
Panel* affinityPanel = AffinityPanel_new(pl, affinity);
Affinity_delete(affinity);
const char* fuFunctions[] = {"Set ", "Cancel ", NULL};
void* set = pickFromVector(panel, affinityPanel, 15, fuFunctions, header);
if (set) {
Affinity* affinity = AffinityPanel_getAffinity(affinityPanel);
bool ok = foreachProcess(panel, (ForeachProcessFn) Process_setAffinity, (size_t) affinity, NULL);
if (!ok) beep();
Affinity_delete(affinity);
}
Panel_delete((Object*)affinityPanel);
#endif
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionKill(Panel* panel, ProcessList* pl, Header* header) {
(void) pl;
Panel* signalsPanel = (Panel*) SignalsPanel_new();
const char* fuFunctions[] = {"Send ", "Cancel ", NULL};
ListItem* sgn = (ListItem*) pickFromVector(panel, signalsPanel, 15, fuFunctions, header);
if (sgn) {
if (sgn->key != 0) {
Panel_setHeader(panel, "Sending...");
Panel_draw(panel, true);
refresh();
foreachProcess(panel, (ForeachProcessFn) Process_sendSignal, (size_t) sgn->key, NULL);
napms(500);
}
}
Panel_delete((Object*)signalsPanel);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionFilterByUser(Panel* panel, ProcessList* pl, Header* header, State* state) {
Panel* usersPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
Panel_setHeader(usersPanel, "Show processes of:");
UsersTable_foreach(state->ut, addUserToVector, usersPanel);
Vector_insertionSort(usersPanel->items);
ListItem* allUsers = ListItem_new("All users", -1);
Panel_insert(usersPanel, 0, (Object*) allUsers);
const char* fuFunctions[] = {"Show ", "Cancel ", NULL};
ListItem* picked = (ListItem*) pickFromVector(panel, usersPanel, 20, fuFunctions, header);
if (picked) {
if (picked == allUsers) {
pl->userOnly = false;
} else {
setUserOnly(ListItem_getRef(picked), &(pl->userOnly), &(pl->userId));
}
}
Panel_delete((Object*)usersPanel);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionFollow() {
return HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionSetup(Panel* panel, ProcessList* pl, Header* header, State* state) {
(void) pl;
Setup_run(state->settings, header);
// TODO: shouldn't need this, colors should be dynamic
int headerHeight = Header_calculateHeight(header);
Panel_move(panel, 0, headerHeight);
Panel_resize(panel, COLS, LINES-headerHeight-1);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionLsof(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK;
OpenFilesScreen* ts = OpenFilesScreen_new(p);
OpenFilesScreen_run(ts);
OpenFilesScreen_delete(ts);
clear();
CRT_enableDelay();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static Htop_Reaction actionStrace(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK;
TraceScreen* ts = TraceScreen_new(p);
TraceScreen_run(ts);
TraceScreen_delete(ts);
clear();
CRT_enableDelay();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static Htop_Reaction actionTag(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK;
Process_toggleTag(p);
Panel_onKey(panel, KEY_DOWN);
return HTOP_OK;
}
static Htop_Reaction actionRedraw() {
clear();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static Htop_Reaction actionHelp(Panel* panel, ProcessList* pl) {
(void) panel;
showHelp(pl);
return HTOP_RECALCULATE | HTOP_REDRAW_BAR;
}
static Htop_Reaction actionUntagAll(Panel* panel) {
for (int i = 0; i < Panel_size(panel); i++) {
Process* p = (Process*) Panel_get(panel, i);
p->tag = false;
}
return HTOP_REFRESH;
}
static Htop_Reaction actionTagAllChildren(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK;
tagAllChildren(panel, p);
return HTOP_OK;
}
void setBindings(Htop_Action* keys) {
keys[KEY_RESIZE] = actionResize;
keys['M'] = actionSortByMemory;
keys['T'] = actionSortByTime;
keys['P'] = actionSortByCPU;
keys['H'] = actionToggleUserlandThreads;
keys['K'] = actionToggleKernelThreads;
keys['t'] = actionToggleTreeView;
keys[KEY_F(5)] = actionToggleTreeView;
keys[KEY_F(4)] = actionIncFilter;
keys['\\'] = actionIncFilter;
keys[KEY_F(3)] = actionIncSearch;
keys['/'] = actionIncSearch;
keys[']'] = actionHigherPriority;
keys[KEY_F(7)] = actionHigherPriority;
keys['['] = actionLowerPriority;
keys[KEY_F(8)] = actionLowerPriority;
keys['I'] = actionInvertSortOrder;
keys['i'] = actionSetIOPriority;
keys[KEY_F(6)] = actionExpandCollapseOrSortColumn;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys['<'] = actionSetSortColumn;
keys[','] = actionSetSortColumn;
keys['>'] = actionSetSortColumn;
keys['.'] = actionSetSortColumn;
keys[KEY_F(10)] = actionQuit;
keys['q'] = actionQuit;
keys['a'] = actionSetAffinity;
keys[KEY_F(9)] = actionKill;
keys['k'] = actionKill;
keys['+'] = actionExpandOrCollapse;
keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse;
keys['u'] = actionFilterByUser;
keys['F'] = actionFollow;
keys['S'] = actionSetup;
keys['C'] = actionSetup;
keys[KEY_F(2)] = actionSetup;
keys['l'] = actionLsof;
keys['s'] = actionStrace;
keys[' '] = actionTag;
keys['\014'] = actionRedraw; // Ctrl+L
keys[KEY_F(1)] = actionHelp;
keys['h'] = actionHelp;
keys['?'] = actionHelp;
keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren;
}
// ----------------------------------------
static void updateTreeFunctions(FunctionBar* fuBar, bool mode) {
if (mode) {
FunctionBar_setLabel(fuBar, KEY_F(5), "Sorted");
FunctionBar_setLabel(fuBar, KEY_F(6), "Collap");
} else {
FunctionBar_setLabel(fuBar, KEY_F(5), "Tree ");
FunctionBar_setLabel(fuBar, KEY_F(6), "SortBy");
}
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
int delay = -1; int delay = -1;
@ -453,11 +762,11 @@ int main(int argc, char** argv) {
exit(1); exit(1);
} }
int quit = 0; bool quit = false;
int refreshTimeout = 0; int sortTimeout = 0;
int resetRefreshTimeout = 5; int resetSortTimeout = 5;
bool doRefresh = true; bool doRefresh = true;
bool doRecalculate = false; bool forceRecalculate = false;
Settings* settings; Settings* settings;
ProcessList* pl = NULL; ProcessList* pl = NULL;
@ -478,6 +787,8 @@ int main(int argc, char** argv) {
#endif #endif
pl = ProcessList_new(ut, pidWhiteList); pl = ProcessList_new(ut, pidWhiteList);
pl->userOnly = userOnly;
pl->userId = userId;
Process_getMaxPid(); Process_getMaxPid();
Header* header = Header_new(pl); Header* header = Header_new(pl);
@ -496,18 +807,17 @@ int main(int argc, char** argv) {
ProcessList_setPanel(pl, panel); ProcessList_setPanel(pl, panel);
FunctionBar* defaultBar = FunctionBar_new(defaultFunctions, NULL, NULL); FunctionBar* defaultBar = FunctionBar_new(defaultFunctions, NULL, NULL);
setTreeView(pl, defaultBar, pl->treeView); updateTreeFunctions(defaultBar, pl->treeView);
if (sortKey > 0) { if (sortKey > 0) {
pl->sortKey = sortKey; pl->sortKey = sortKey;
setTreeView(pl, defaultBar, false); pl->treeView = false;
pl->direction = 1; pl->direction = 1;
} }
ProcessList_printHeader(pl, Panel_getHeader(panel)); ProcessList_printHeader(pl, Panel_getHeader(panel));
IncSet* inc = IncSet_new(defaultBar); IncSet* inc = IncSet_new(defaultBar);
ProcessList_scan(pl); ProcessList_scan(pl);
millisleep(75); millisleep(75);
ProcessList_scan(pl); ProcessList_scan(pl);
@ -523,31 +833,40 @@ int main(int argc, char** argv) {
int ch = ERR; int ch = ERR;
int closeTimeout = 0; int closeTimeout = 0;
bool idle = false; bool drawPanel = true;
bool collapsed = false; bool collapsed = false;
Htop_Action keys[KEY_MAX] = { NULL };
setBindings(keys);
State state = {
.inc = inc,
.settings = settings,
.ut = ut,
};
while (!quit) { while (!quit) {
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000); double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
bool recalculate = (newTime - oldTime > settings->delay); bool timeToRecalculate = (newTime - oldTime > settings->delay);
if (newTime < oldTime) recalculate = true; // clock was adjusted? if (newTime < oldTime) timeToRecalculate = true; // clock was adjusted?
int following = follow ? selectedPid(panel) : -1; int following = follow ? selectedPid(panel) : -1;
if (recalculate) { if (timeToRecalculate) {
Header_draw(header); Header_draw(header);
oldTime = newTime; oldTime = newTime;
} }
if (doRefresh) { if (doRefresh) {
if (recalculate || doRecalculate) { if (timeToRecalculate || forceRecalculate) {
ProcessList_scan(pl); ProcessList_scan(pl);
doRecalculate = false; forceRecalculate = false;
} }
if (refreshTimeout == 0 || pl->treeView) { if (sortTimeout == 0 || pl->treeView) {
ProcessList_sort(pl); ProcessList_sort(pl);
refreshTimeout = 1; sortTimeout = 1;
} }
ProcessList_rebuildPanel(pl, true, following, userOnly, userId, IncSet_filter(inc)); ProcessList_rebuildPanel(pl, true, following, IncSet_filter(inc));
idle = false; drawPanel = true;
} }
doRefresh = true; doRefresh = true;
@ -563,9 +882,11 @@ int main(int argc, char** argv) {
} }
collapsed = !p->showChildren; collapsed = !p->showChildren;
} }
} else {
collapsed = false;
} }
if (!idle) { if (drawPanel) {
Panel_draw(panel, true); Panel_draw(panel, true);
} }
@ -576,18 +897,20 @@ int main(int argc, char** argv) {
if (ch == ERR) { if (ch == ERR) {
if (!inc->active) if (!inc->active)
refreshTimeout--; sortTimeout--;
if (prev == ch && !recalculate) { if (prev == ch && !timeToRecalculate) {
closeTimeout++; closeTimeout++;
if (closeTimeout == 100) { if (closeTimeout == 100) {
break; break;
} }
} else } else
closeTimeout = 0; closeTimeout = 0;
idle = true; drawPanel = false;
continue; continue;
} }
idle = false; drawPanel = true;
Htop_Reaction reaction = HTOP_OK;
if (ch == KEY_MOUSE) { if (ch == KEY_MOUSE) {
MEVENT mevent; MEVENT mevent;
@ -599,18 +922,17 @@ int main(int argc, char** argv) {
ProcessField field = ProcessList_keyAt(pl, x); ProcessField field = ProcessList_keyAt(pl, x);
if (field == pl->sortKey) { if (field == pl->sortKey) {
ProcessList_invertSortOrder(pl); ProcessList_invertSortOrder(pl);
setTreeView(pl, defaultBar, false); pl->treeView = false;
reaction |= HTOP_REDRAW_BAR;
} else { } else {
setSortKey(pl, defaultBar, field, panel, settings); reaction |= setSortKey(pl, field);
} }
refreshTimeout = 0; sortTimeout = 0;
continue; ch = ERR;
} else if (mevent.y >= panel->y + 1 && mevent.y < LINES - 1) { } else if (mevent.y >= panel->y + 1 && mevent.y < LINES - 1) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1); Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
doRefresh = false;
refreshTimeout = resetRefreshTimeout;
follow = true; follow = true;
continue; ch = ERR;
} if (mevent.y == LINES - 1) { } if (mevent.y == LINES - 1) {
ch = FunctionBar_synthesizeEvent(inc->bar, mevent.x); ch = FunctionBar_synthesizeEvent(inc->bar, mevent.x);
} }
@ -650,298 +972,38 @@ int main(int argc, char** argv) {
acc = 0; acc = 0;
} }
switch (ch) { if(ch != ERR && keys[ch]) {
case KEY_RESIZE: reaction |= (keys[ch])(panel, pl, header, &state);
Panel_resize(panel, COLS, LINES-headerHeight-1); } else {
IncSet_drawBar(inc);
break;
case 'M':
{
refreshTimeout = 0;
setSortKey(pl, defaultBar, PERCENT_MEM, panel, settings);
break;
}
case 'T':
{
refreshTimeout = 0;
setSortKey(pl, defaultBar, TIME, panel, settings);
break;
}
case 'c':
{
Process* p = (Process*) Panel_getSelected(panel);
if (!p) break;
tagAllChildren(panel, p);
break;
}
case 'U':
{
for (int i = 0; i < Panel_size(panel); i++) {
Process* p = (Process*) Panel_get(panel, i);
p->tag = false;
}
doRefresh = true;
break;
}
case 'P':
{
refreshTimeout = 0;
setSortKey(pl, defaultBar, PERCENT_CPU, panel, settings);
break;
}
case KEY_F(1):
case 'h':
case '?':
{
showHelp(pl);
FunctionBar_draw(defaultBar, NULL);
recalculate = true;
refreshTimeout = 0;
break;
}
case '\014': // Ctrl+L
{
clear();
FunctionBar_draw(defaultBar, NULL);
refreshTimeout = 0;
break;
}
case ' ':
{
Process* p = (Process*) Panel_getSelected(panel);
if (!p) break;
Process_toggleTag(p);
Panel_onKey(panel, KEY_DOWN);
break;
}
case 's':
{
Process* p = (Process*) Panel_getSelected(panel);
if (!p) break;
TraceScreen* ts = TraceScreen_new(p);
TraceScreen_run(ts);
TraceScreen_delete(ts);
clear();
FunctionBar_draw(defaultBar, NULL);
refreshTimeout = 0;
CRT_enableDelay();
break;
}
case 'l':
{
Process* p = (Process*) Panel_getSelected(panel);
if (!p) break;
OpenFilesScreen* ts = OpenFilesScreen_new(p);
OpenFilesScreen_run(ts);
OpenFilesScreen_delete(ts);
clear();
FunctionBar_draw(defaultBar, NULL);
refreshTimeout = 0;
CRT_enableDelay();
break;
}
case 'S':
case 'C':
case KEY_F(2):
{
Setup_run(settings, header);
// TODO: shouldn't need this, colors should be dynamic
ProcessList_printHeader(pl, Panel_getHeader(panel));
headerHeight = Header_calculateHeight(header);
Panel_move(panel, 0, headerHeight);
Panel_resize(panel, COLS, LINES-headerHeight-1);
FunctionBar_draw(defaultBar, NULL);
refreshTimeout = 0;
break;
}
case 'F':
{
follow = true;
continue;
}
case 'u':
{
Panel* usersPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
Panel_setHeader(usersPanel, "Show processes of:");
UsersTable_foreach(ut, addUserToVector, usersPanel);
Vector_insertionSort(usersPanel->items);
ListItem* allUsers = ListItem_new("All users", -1);
Panel_insert(usersPanel, 0, (Object*) allUsers);
const char* fuFunctions[] = {"Show ", "Cancel ", NULL};
ListItem* picked = (ListItem*) pickFromVector(panel, usersPanel, 20, headerHeight, fuFunctions, defaultBar, header);
if (picked) {
if (picked == allUsers) {
userOnly = false;
} else {
setUserOnly(ListItem_getRef(picked), &userOnly, &userId);
}
}
Panel_delete((Object*)usersPanel);
break;
}
case '+':
case '=':
case '-':
{
if (expandCollapse(panel)) {
doRecalculate = true;
refreshTimeout = 0;
}
break;
}
case KEY_F(9):
case 'k':
{
Panel* signalsPanel = (Panel*) SignalsPanel_new();
const char* fuFunctions[] = {"Send ", "Cancel ", NULL};
ListItem* sgn = (ListItem*) pickFromVector(panel, signalsPanel, 15, headerHeight, fuFunctions, defaultBar, header);
if (sgn) {
if (sgn->key != 0) {
Panel_setHeader(panel, "Sending...");
Panel_draw(panel, true);
refresh();
foreachProcess(panel, (ForeachProcessFn) Process_sendSignal, (size_t) sgn->key, NULL);
napms(500);
}
}
ProcessList_printHeader(pl, Panel_getHeader(panel));
Panel_delete((Object*)signalsPanel);
refreshTimeout = 0;
break;
}
#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY)
case 'a':
{
if (pl->cpuCount == 1)
break;
Process* p = (Process*) Panel_getSelected(panel);
if (!p) break;
Affinity* affinity = Process_getAffinity(p);
if (!affinity) break;
Panel* affinityPanel = AffinityPanel_new(pl, affinity);
Affinity_delete(affinity);
const char* fuFunctions[] = {"Set ", "Cancel ", NULL};
void* set = pickFromVector(panel, affinityPanel, 15, headerHeight, fuFunctions, defaultBar, header);
if (set) {
Affinity* affinity = AffinityPanel_getAffinity(affinityPanel);
bool ok = foreachProcess(panel, (ForeachProcessFn) Process_setAffinity, (size_t) affinity, NULL);
if (!ok) beep();
Affinity_delete(affinity);
}
Panel_delete((Object*)affinityPanel);
ProcessList_printHeader(pl, Panel_getHeader(panel));
refreshTimeout = 0;
break;
}
#endif
case KEY_F(10):
case 'q':
quit = 1;
break;
case '<':
case ',':
case '>':
case '.':
{
sortBy(panel, pl, settings, headerHeight, defaultBar, header);
refreshTimeout = 0;
break;
}
case KEY_F(18):
case KEY_F(6):
{
if (pl->treeView) {
if (expandCollapse(panel)) {
doRecalculate = true;
refreshTimeout = 0;
}
} else {
sortBy(panel, pl, settings, headerHeight, defaultBar, header);
refreshTimeout = 0;
}
break;
}
case 'i':
{
Process* p = (Process*) Panel_getSelected(panel);
if (!p) break;
IOPriority ioprio = p->ioPriority;
Panel* ioprioPanel = IOPriorityPanel_new(ioprio);
const char* fuFunctions[] = {"Set ", "Cancel ", NULL};
void* set = pickFromVector(panel, ioprioPanel, 21, headerHeight, fuFunctions, defaultBar, header);
if (set) {
IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel);
bool ok = foreachProcess(panel, (ForeachProcessFn) Process_setIOPriority, (size_t) ioprio, NULL);
if (!ok)
beep();
}
Panel_delete((Object*)ioprioPanel);
ProcessList_printHeader(pl, Panel_getHeader(panel));
refreshTimeout = 0;
break;
}
case 'I':
{
refreshTimeout = 0;
settings->changed = true;
ProcessList_invertSortOrder(pl);
break;
}
case KEY_F(8):
case '[':
{
doRefresh = changePriority(panel, 1);
break;
}
case KEY_F(7):
case ']':
{
doRefresh = changePriority(panel, -1);
break;
}
case KEY_F(3):
case '/':
IncSet_activate(inc, INC_SEARCH);
break;
case KEY_F(4):
case '\\':
IncSet_activate(inc, INC_FILTER);
refreshTimeout = 0;
doRefresh = true;
continue;
case 't':
case KEY_F(5):
refreshTimeout = 0;
collapsed = false;
setTreeView(pl, defaultBar, !pl->treeView);
if (pl->treeView) pl->direction = 1;
ProcessList_printHeader(pl, Panel_getHeader(panel));
ProcessList_expandTree(pl);
settings->changed = true;
if (following != -1) continue;
break;
case 'H':
doRecalculate = true;
refreshTimeout = 0;
pl->hideUserlandThreads = !pl->hideUserlandThreads;
pl->hideThreads = pl->hideUserlandThreads;
settings->changed = true;
break;
case 'K':
doRecalculate = true;
refreshTimeout = 0;
pl->hideKernelThreads = !pl->hideKernelThreads;
settings->changed = true;
break;
default:
doRefresh = false; doRefresh = false;
refreshTimeout = resetRefreshTimeout; sortTimeout = resetSortTimeout;
Panel_onKey(panel, ch); Panel_onKey(panel, ch);
break;
} }
follow = false;
// Reaction handlers:
if (reaction & HTOP_REDRAW_BAR) {
updateTreeFunctions(defaultBar, pl->treeView);
IncSet_drawBar(inc);
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(pl, Panel_getHeader(panel));
}
if (reaction & HTOP_REFRESH) {
doRefresh = true;
sortTimeout = 0;
}
if (reaction & HTOP_RECALCULATE) {
forceRecalculate = true;
sortTimeout = 0;
}
if (reaction & HTOP_SAVE_SETTINGS) {
settings->changed = true;
}
if (reaction & HTOP_QUIT) {
quit = true;
}
follow = (reaction & HTOP_KEEP_FOLLOWING);
} }
attron(CRT_colors[RESET_COLOR]); attron(CRT_colors[RESET_COLOR]);
mvhline(LINES-1, 0, ' ', COLS); mvhline(LINES-1, 0, ' ', COLS);
@ -953,11 +1015,14 @@ int main(int argc, char** argv) {
Settings_write(settings); Settings_write(settings);
Header_delete(header); Header_delete(header);
ProcessList_delete(pl); ProcessList_delete(pl);
IncSet_delete(inc);
FunctionBar_delete((Object*)defaultBar); FunctionBar_delete((Object*)defaultBar);
Panel_delete((Object*)panel); Panel_delete((Object*)panel);
IncSet_delete(inc);
UsersTable_delete(ut); UsersTable_delete(ut);
Settings_delete(settings); Settings_delete(settings);
if(pidWhiteList) { if(pidWhiteList) {
Hashtable_delete(pidWhiteList); Hashtable_delete(pidWhiteList);
} }

29
htop.h
View File

@ -11,11 +11,36 @@ in the source distribution for its full text.
//#link m //#link m
#define COPYRIGHT "(C) 2004-2012 Hisham Muhammad" #define COPYRIGHT "(C) 2004-2014 Hisham Muhammad"
typedef enum {
HTOP_OK = 0x00,
HTOP_REFRESH = 0x01,
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH
HTOP_SAVE_SETTINGS = 0x04,
HTOP_KEEP_FOLLOWING = 0x08,
HTOP_QUIT = 0x10,
HTOP_REDRAW_BAR = 0x20,
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
} Htop_Reaction;
typedef Htop_Reaction (*Htop_Action)();
typedef struct State_ {
IncSet* inc;
Settings* settings;
UsersTable* ut;
} State;
typedef bool(*ForeachProcessFn)(Process*, size_t); typedef bool(*ForeachProcessFn)(Process*, size_t);
void sortBy(Panel* panel, ProcessList* pl, Settings* settings, int headerHeight, FunctionBar* defaultBar, Header* header);
// ----------------------------------------
void setBindings(Htop_Action* keys);
// ----------------------------------------
int main(int argc, char** argv); int main(int argc, char** argv);