From 3383d8e5561dfc6fb2b65e0a194df94ccb5e08af Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Wed, 21 Jan 2015 23:27:31 -0200 Subject: [PATCH] Sorry about the mega-patch. This is a work-in-progress, code is currently broken. (Some actions, and notably, the header, are missing.) --- Action.c | 490 +++++++++++++++++++++++- Action.h | 17 +- Affinity.c | 71 +++- Affinity.h | 22 +- AffinityPanel.c | 6 +- AffinityPanel.h | 2 +- AvailableColumnsPanel.c | 22 +- AvailableColumnsPanel.h | 7 +- AvailableMetersPanel.c | 19 +- AvailableMetersPanel.h | 6 +- CPUMeter.c | 36 +- CRT.c | 133 +++---- CRT.h | 28 +- CategoriesPanel.c | 21 +- CategoriesPanel.h | 7 +- ColorsPanel.c | 2 +- ColumnsPanel.c | 60 ++- ColumnsPanel.h | 5 +- DisplayOptionsPanel.c | 32 +- Header.c | 133 ++++--- Header.h | 31 +- ListItem.c | 11 +- ListItem.h | 1 + Makefile.am | 4 +- MemoryMeter.c | 13 +- Meter.c | 99 +++-- Meter.h | 11 +- MetersPanel.c | 25 +- MetersPanel.h | 1 + Panel.c | 179 +++++---- Panel.h | 10 +- Process.c | 337 +++++++---------- Process.h | 44 +-- ProcessList.c | 192 ++-------- ProcessList.h | 96 +---- ScreenManager.c | 105 ++++-- ScreenManager.h | 6 +- Settings.c | 333 +++++++++++------ Settings.h | 45 ++- SwapMeter.c | 9 +- TasksMeter.c | 28 +- configure.ac | 4 + htop.c | 789 ++++++++------------------------------- htop.h | 3 - linux/LinuxProcess.c | 26 +- linux/LinuxProcess.h | 4 +- linux/LinuxProcessList.c | 129 +++++-- linux/LinuxProcessList.h | 56 ++- linux/Platform.c | 68 +++- linux/Platform.h | 8 + scripts/MakeHeader.py | 2 + 51 files changed, 2011 insertions(+), 1777 deletions(-) diff --git a/Action.c b/Action.c index 499422e0..b56f195a 100644 --- a/Action.c +++ b/Action.c @@ -5,16 +5,36 @@ Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ -#include "Process.h" -#include "Panel.h" +#include "config.h" + #include "Action.h" +#include "Affinity.h" +#include "AffinityPanel.h" +#include "CategoriesPanel.h" +#include "CRT.h" +#include "OpenFilesScreen.h" +#include "Process.h" #include "ScreenManager.h" +#include "SignalsPanel.h" +#include "String.h" +#include "TraceScreen.h" + +#include +#include +#include +#include +#include +#include +#include /*{ #include "IncSet.h" #include "Settings.h" +#include "Header.h" #include "UsersTable.h" +#include "ProcessList.h" +#include "Panel.h" typedef enum { HTOP_OK = 0x00, @@ -33,6 +53,9 @@ typedef struct State_ { IncSet* inc; Settings* settings; UsersTable* ut; + ProcessList* pl; + Panel* panel; + Header* header; } State; typedef bool(*Action_ForeachProcessFn)(Process*, size_t); @@ -66,11 +89,15 @@ bool Action_foreachProcess(Panel* panel, Action_ForeachProcessFn fn, int arg, bo return ok; } -Object* Action_pickFromVector(Panel* panel, Panel* list, int x, const char** keyLabels, Header* header) { +Object* Action_pickFromVector(State* st, Panel* list, int x, const char** keyLabels) { + Panel* panel = st->panel; + Header* header = st->header; + Settings* settings = st->settings; + int y = panel->y; const char* fuKeys[] = {"Enter", "Esc", NULL}; 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, settings, false); scr->allowFocusChange = false; ScreenManager_add(scr, list, FunctionBar_new(keyLabels, fuKeys, fuEvents), x - 1); ScreenManager_add(scr, panel, NULL, -1); @@ -98,3 +125,458 @@ Object* Action_pickFromVector(Panel* panel, Panel* list, int x, const char** key } return NULL; } + +// ---------------------------------------- + +static const char* CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; + +static void Setup_run(Settings* settings, const Header* header, ProcessList* pl) { + ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, true); + CategoriesPanel* panelCategories = CategoriesPanel_new(scr, settings, (Header*) header, pl); + ScreenManager_add(scr, (Panel*) panelCategories, FunctionBar_new(CategoriesFunctions, NULL, NULL), 16); + CategoriesPanel_makeMetersPage(panelCategories); + Panel* panelFocus; + int ch; + ScreenManager_run(scr, &panelFocus, &ch); + ScreenManager_delete(scr); +} + +static bool changePriority(Panel* panel, int delta) { + bool anyTagged; + bool ok = Action_foreachProcess(panel, (Action_ForeachProcessFn) Process_changePriorityBy, delta, &anyTagged); + if (!ok) + beep(); + return anyTagged; +} + +static void addUserToVector(int key, void* userCast, void* panelCast) { + char* user = (char*) userCast; + Panel* panel = (Panel*) panelCast; + Panel_add(panel, (Object*) ListItem_new(user, key)); +} + +bool Action_setUserOnly(const char* userName, uid_t* userId) { + struct passwd* user = getpwnam(userName); + if (user) { + *userId = user->pw_uid; + return true; + } + *userId = -1; + return false; +} + +static const char* getMainPanelValue(Panel* panel, int i) { + Process* p = (Process*) Panel_get(panel, i); + if (p) + return p->comm; + return ""; +} + +static void tagAllChildren(Panel* panel, Process* parent) { + parent->tag = true; + pid_t ppid = parent->pid; + for (int i = 0; i < Panel_size(panel); i++) { + Process* p = (Process*) Panel_get(panel, i); + if (!p->tag && p->ppid == ppid) { + tagAllChildren(panel, p); + } + } +} + +static bool expandCollapse(Panel* panel) { + Process* p = (Process*) Panel_getSelected(panel); + if (!p) return false; + p->showChildren = !p->showChildren; + return true; +} + +static inline Htop_Reaction setSortKey(Settings* settings, ProcessField sortKey) { + settings->sortKey = sortKey; + settings->direction = 1; + settings->treeView = false; + return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR; +} + +static Htop_Reaction sortBy(State* st) { + Htop_Reaction reaction = HTOP_OK; + Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem)); + Panel_setHeader(sortPanel, "Sort by"); + const char* fuFunctions[] = {"Sort ", "Cancel ", NULL}; + ProcessField* fields = st->settings->fields; + for (int i = 0; fields[i]; i++) { + char* name = String_trim(Process_fields[fields[i]].name); + Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); + if (fields[i] == st->settings->sortKey) + Panel_setSelected(sortPanel, i); + free(name); + } + ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, fuFunctions); + if (field) { + reaction |= setSortKey(st->settings, field->key); + } + Object_delete(sortPanel); + return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; +} + +// ---------------------------------------- + +static Htop_Reaction actionResize(State* st) { + Panel_resize(st->panel, COLS, LINES-(st->panel->y)-1); + return HTOP_REDRAW_BAR; +} + +static Htop_Reaction actionSortByMemory(State* st) { + return setSortKey(st->settings, PERCENT_MEM); +} + +static Htop_Reaction actionSortByCPU(State* st) { + return setSortKey(st->settings, PERCENT_CPU); +} + +static Htop_Reaction actionSortByTime(State* st) { + return setSortKey(st->settings, TIME); +} + +static Htop_Reaction actionToggleKernelThreads(State* st) { + st->settings->hideKernelThreads = !st->settings->hideKernelThreads; + return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS; +} + +static Htop_Reaction actionToggleUserlandThreads(State* st) { + st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads; + st->settings->hideThreads = st->settings->hideUserlandThreads; + return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS; +} + +static Htop_Reaction actionToggleTreeView(State* st) { + st->settings->treeView = !st->settings->treeView; + if (st->settings->treeView) st->settings->direction = 1; + ProcessList_expandTree(st->pl); + return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; +} + +static Htop_Reaction actionIncFilter(State* st) { + IncSet_activate(st->inc, INC_FILTER); + return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; +} + +static Htop_Reaction actionIncSearch(State* st) { + IncSet_activate(st->inc, INC_SEARCH); + return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; +} + +static Htop_Reaction actionHigherPriority(State* st) { + bool changed = changePriority(st->panel, -1); + return changed ? HTOP_REFRESH : HTOP_OK; +} + +static Htop_Reaction actionLowerPriority(State* st) { + bool changed = changePriority(st->panel, 1); + return changed ? HTOP_REFRESH : HTOP_OK; +} + +static Htop_Reaction actionInvertSortOrder(State* st) { + Settings_invertSortOrder(st->settings); + return HTOP_REFRESH | HTOP_SAVE_SETTINGS; +} + +static Htop_Reaction actionSetSortColumn(State* st) { + return sortBy(st); +} + +static Htop_Reaction actionExpandOrCollapse(State* st) { + bool changed = expandCollapse(st->panel); + return changed ? HTOP_RECALCULATE : HTOP_OK; +} + +static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) { + return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st); +} + +static Htop_Reaction actionQuit() { + return HTOP_QUIT; +} + +static Htop_Reaction actionSetAffinity(State* st) { + if (st->pl->cpuCount == 1) + return HTOP_OK; +#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY) + Panel* panel = st->panel; + + Process* p = (Process*) Panel_getSelected(panel); + if (!p) return HTOP_OK; + Affinity* affinity = Affinity_get(p, st->pl); + if (!affinity) return HTOP_OK; + Panel* affinityPanel = AffinityPanel_new(st->pl, affinity); + Affinity_delete(affinity); + + const char* fuFunctions[] = {"Set ", "Cancel ", NULL}; + void* set = Action_pickFromVector(st, affinityPanel, 15, fuFunctions); + if (set) { + Affinity* affinity = AffinityPanel_getAffinity(affinityPanel, st->pl); + bool ok = Action_foreachProcess(panel, (Action_ForeachProcessFn) Affinity_set, (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(State* st) { + Panel* signalsPanel = (Panel*) SignalsPanel_new(); + const char* fuFunctions[] = {"Send ", "Cancel ", NULL}; + ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, fuFunctions); + if (sgn) { + if (sgn->key != 0) { + Panel_setHeader(st->panel, "Sending..."); + Panel_draw(st->panel, true); + refresh(); + Action_foreachProcess(st->panel, (Action_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(State* st) { + Panel* usersPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem)); + Panel_setHeader(usersPanel, "Show processes of:"); + UsersTable_foreach(st->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*) Action_pickFromVector(st, usersPanel, 20, fuFunctions); + if (picked) { + if (picked == allUsers) { + st->pl->userId = -1; + } else { + Action_setUserOnly(ListItem_getRef(picked), &(st->pl->userId)); + } + } + Panel_delete((Object*)usersPanel); + return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; +} + +static Htop_Reaction actionFollow(State* st) { + st->pl->following = Action_selectedPid(st->panel); + return HTOP_KEEP_FOLLOWING; +} + +static Htop_Reaction actionSetup(State* st) { + Setup_run(st->settings, st->header, st->pl); + // TODO: shouldn't need this, colors should be dynamic + int headerHeight = Header_calculateHeight(st->header); + Panel_move(st->panel, 0, headerHeight); + Panel_resize(st->panel, COLS, LINES-headerHeight-1); + return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; +} + +static Htop_Reaction actionLsof(State* st) { + Process* p = (Process*) Panel_getSelected(st->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(State* st) { + Process* p = (Process*) Panel_getSelected(st->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(State* st) { + Process* p = (Process*) Panel_getSelected(st->panel); + if (!p) return HTOP_OK; + Process_toggleTag(p); + Panel_onKey(st->panel, KEY_DOWN); + return HTOP_OK; +} + +static Htop_Reaction actionRedraw() { + clear(); + return HTOP_REFRESH | HTOP_REDRAW_BAR; +} + +static struct { const char* key; const char* info; } helpLeft[] = { + { .key = " Arrows: ", .info = "scroll process list" }, + { .key = " Digits: ", .info = "incremental PID search" }, + { .key = " F3 /: ", .info = "incremental name search" }, + { .key = " F4 \\: ",.info = "incremental name filtering" }, + { .key = " F5 t: ", .info = "tree view" }, + { .key = " u: ", .info = "show processes of a single user" }, + { .key = " H: ", .info = "hide/show user threads" }, + { .key = " K: ", .info = "hide/show kernel threads" }, + { .key = " F: ", .info = "cursor follows process" }, + { .key = " F6 + -: ", .info = "expand/collapse tree" }, + { .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" }, + { .key = " I: ", .info = "invert sort order" }, + { .key = " F6 >: ", .info = "select sort column" }, + { .key = NULL, .info = NULL } +}; + +static struct { const char* key; const char* info; } helpRight[] = { + { .key = " Space: ", .info = "tag process" }, + { .key = " c: ", .info = "tag process and its children" }, + { .key = " U: ", .info = "untag all processes" }, + { .key = " F9 k: ", .info = "kill process/tagged processes" }, + { .key = " F7 ]: ", .info = "higher priority (root only)" }, + { .key = " F8 [: ", .info = "lower priority (+ nice)" }, +#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY) + { .key = " a: ", .info = "set CPU affinity" }, +#endif + { .key = " i: ", .info = "set IO prority" }, + { .key = " l: ", .info = "list open files with lsof" }, + { .key = " s: ", .info = "trace syscalls with strace" }, + { .key = " ", .info = "" }, + { .key = " F2 S: ", .info = "setup" }, + { .key = " F1 h: ", .info = "show this help screen" }, + { .key = " F10 q: ", .info = "quit" }, + { .key = NULL, .info = NULL } +}; + +static Htop_Reaction actionHelp(State* st) { + Settings* settings = st->settings; + + clear(); + attrset(CRT_colors[HELP_BOLD]); + + for (int i = 0; i < LINES-1; i++) + mvhline(i, 0, ' ', COLS); + + mvaddstr(0, 0, "htop " VERSION " - " COPYRIGHT); + mvaddstr(1, 0, "Released under the GNU GPL. See 'man' page for more info."); + + attrset(CRT_colors[DEFAULT_COLOR]); + mvaddstr(3, 0, "CPU usage bar: "); + #define addattrstr(a,s) attrset(a);addstr(s) + addattrstr(CRT_colors[BAR_BORDER], "["); + if (settings->detailedCPUTime) { + addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/"); + addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); + addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/"); + addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/"); + addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/"); + addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/"); + addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/"); + addattrstr(CRT_colors[CPU_IOWAIT], "io-wait"); + addattrstr(CRT_colors[BAR_SHADOW], " used%"); + } else { + addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/"); + addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); + addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/"); + addattrstr(CRT_colors[CPU_STEAL], "virtualiz"); + addattrstr(CRT_colors[BAR_SHADOW], " used%"); + } + addattrstr(CRT_colors[BAR_BORDER], "]"); + attrset(CRT_colors[DEFAULT_COLOR]); + mvaddstr(4, 0, "Memory bar: "); + addattrstr(CRT_colors[BAR_BORDER], "["); + addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/"); + addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/"); + addattrstr(CRT_colors[MEMORY_CACHE], "cache"); + addattrstr(CRT_colors[BAR_SHADOW], " used/total"); + addattrstr(CRT_colors[BAR_BORDER], "]"); + attrset(CRT_colors[DEFAULT_COLOR]); + mvaddstr(5, 0, "Swap bar: "); + addattrstr(CRT_colors[BAR_BORDER], "["); + addattrstr(CRT_colors[SWAP], "used"); + addattrstr(CRT_colors[BAR_SHADOW], " used/total"); + addattrstr(CRT_colors[BAR_BORDER], "]"); + attrset(CRT_colors[DEFAULT_COLOR]); + mvaddstr(6,0, "Type and layout of header meters are configurable in the setup screen."); + if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { + mvaddstr(7, 0, "In monochrome, meters are displayed through different chars, in order: |#*@$%&"); + } + mvaddstr( 8, 0, " Status: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep"); + for (int i = 0; helpLeft[i].info; i++) { mvaddstr(9+i, 9, helpLeft[i].info); } + for (int i = 0; helpRight[i].info; i++) { mvaddstr(9+i, 49, helpRight[i].info); } + attrset(CRT_colors[HELP_BOLD]); + for (int i = 0; helpLeft[i].key; i++) { mvaddstr(9+i, 0, helpLeft[i].key); } + for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); } + + attrset(CRT_colors[HELP_BOLD]); + mvaddstr(23,0, "Press any key to return."); + attrset(CRT_colors[DEFAULT_COLOR]); + refresh(); + CRT_readKey(); + clear(); + + return HTOP_RECALCULATE | HTOP_REDRAW_BAR; +} + +static Htop_Reaction actionUntagAll(State* st) { + for (int i = 0; i < Panel_size(st->panel); i++) { + Process* p = (Process*) Panel_get(st->panel, i); + p->tag = false; + } + return HTOP_REFRESH; +} + +static Htop_Reaction actionTagAllChildren(State* st) { + Process* p = (Process*) Panel_getSelected(st->panel); + if (!p) return HTOP_OK; + tagAllChildren(st->panel, p); + return HTOP_OK; +} + +void Action_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[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; +} + diff --git a/Action.h b/Action.h index 7571ba41..0a1bf024 100644 --- a/Action.h +++ b/Action.h @@ -12,7 +12,10 @@ in the source distribution for its full text. #include "IncSet.h" #include "Settings.h" +#include "Header.h" #include "UsersTable.h" +#include "ProcessList.h" +#include "Panel.h" typedef enum { HTOP_OK = 0x00, @@ -31,6 +34,9 @@ typedef struct State_ { IncSet* inc; Settings* settings; UsersTable* ut; + ProcessList* pl; + Panel* panel; + Header* header; } State; typedef bool(*Action_ForeachProcessFn)(Process*, size_t); @@ -40,6 +46,15 @@ int Action_selectedPid(Panel* panel); bool Action_foreachProcess(Panel* panel, Action_ForeachProcessFn fn, int arg, bool* wasAnyTagged); -Object* Action_pickFromVector(Panel* panel, Panel* list, int x, const char** keyLabels, Header* header); +Object* Action_pickFromVector(State* st, Panel* list, int x, const char** keyLabels); + +// ---------------------------------------- + +bool Action_setUserOnly(const char* userName, uid_t* userId); + +// ---------------------------------------- + +void Action_setBindings(Htop_Action* keys); + #endif diff --git a/Affinity.c b/Affinity.c index 3b1e311a..8e8651be 100644 --- a/Affinity.c +++ b/Affinity.c @@ -9,9 +9,18 @@ in the source distribution for its full text. #include +#ifdef HAVE_LIBHWLOC +#include +#elif HAVE_NATIVE_AFFINITY +#include +#endif + /*{ +#include "Process.h" +#include "ProcessList.h" typedef struct Affinity_ { + ProcessList* pl; int size; int used; int* cpus; @@ -19,10 +28,11 @@ typedef struct Affinity_ { }*/ -Affinity* Affinity_new() { +Affinity* Affinity_new(ProcessList* pl) { Affinity* this = calloc(1, sizeof(Affinity)); this->size = 8; this->cpus = calloc(this->size, sizeof(int)); + this->pl = pl; return this; } @@ -40,3 +50,62 @@ void Affinity_add(Affinity* this, int id) { this->used++; } + +#ifdef HAVE_LIBHWLOC + +Affinity* Affinity_get(Process* proc, ProcessList* pl) { + hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); + bool ok = (hwloc_linux_get_tid_cpubind(pl->topology, proc->pid, cpuset) == 0); + Affinity* affinity = NULL; + if (ok) { + affinity = Affinity_new(pl); + if (hwloc_bitmap_last(cpuset) == -1) { + for (int i = 0; i < pl->cpuCount; i++) { + Affinity_add(affinity, i); + } + } else { + unsigned int id; + hwloc_bitmap_foreach_begin(id, cpuset); + Affinity_add(affinity, id); + hwloc_bitmap_foreach_end(); + } + } + hwloc_bitmap_free(cpuset); + return affinity; +} + +bool Affinity_set(Process* proc, Affinity* this) { + hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); + for (int i = 0; i < affinity->used; i++) { + hwloc_bitmap_set(cpuset, affinity->cpus[i]); + } + bool ok = (hwloc_linux_set_tid_cpubind(this->pl->topology, proc->pid, cpuset) == 0); + hwloc_bitmap_free(cpuset); + return ok; +} + +#elif HAVE_NATIVE_AFFINITY + +Affinity* Affinity_get(Process* proc, ProcessList* pl) { + cpu_set_t cpuset; + bool ok = (sched_getaffinity(proc->pid, sizeof(cpu_set_t), &cpuset) == 0); + if (!ok) return NULL; + Affinity* affinity = Affinity_new(pl); + for (int i = 0; i < pl->cpuCount; i++) { + if (CPU_ISSET(i, &cpuset)) + Affinity_add(affinity, i); + } + return affinity; +} + +bool Affinity_set(Process* proc, Affinity* this) { + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + for (int i = 0; i < this->used; i++) { + CPU_SET(this->cpus[i], &cpuset); + } + bool ok = (sched_setaffinity(proc->pid, sizeof(unsigned long), &cpuset) == 0); + return ok; +} + +#endif diff --git a/Affinity.h b/Affinity.h index 3a19ea49..8f8dde9c 100644 --- a/Affinity.h +++ b/Affinity.h @@ -9,19 +9,39 @@ Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ +#ifdef HAVE_LIBHWLOC +#elif HAVE_NATIVE_AFFINITY +#endif + +#include "Process.h" +#include "ProcessList.h" typedef struct Affinity_ { + ProcessList* pl; int size; int used; int* cpus; } Affinity; -Affinity* Affinity_new(); +Affinity* Affinity_new(ProcessList* pl); void Affinity_delete(Affinity* this); void Affinity_add(Affinity* this, int id); +#ifdef HAVE_LIBHWLOC + +Affinity* Affinity_get(Process* proc, ProcessList* pl); + +bool Affinity_set(Process* proc, Affinity* this); + +#elif HAVE_NATIVE_AFFINITY + +Affinity* Affinity_get(Process* proc, ProcessList* pl); + +bool Affinity_set(Process* proc, Affinity* this); + +#endif #endif diff --git a/AffinityPanel.c b/AffinityPanel.c index 094a0104..fb642e06 100644 --- a/AffinityPanel.c +++ b/AffinityPanel.c @@ -50,7 +50,7 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity) { int curCpu = 0; for (int i = 0; i < pl->cpuCount; i++) { char number[10]; - snprintf(number, 9, "%d", ProcessList_cpuId(pl, i)); + snprintf(number, 9, "%d", Settings_cpuId(pl->settings, i)); bool mode; if (curCpu < affinity->used && affinity->cpus[curCpu] == i) { mode = true; @@ -63,8 +63,8 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity) { return this; } -Affinity* AffinityPanel_getAffinity(Panel* this) { - Affinity* affinity = Affinity_new(); +Affinity* AffinityPanel_getAffinity(Panel* this, ProcessList* pl) { + Affinity* affinity = Affinity_new(pl); int size = Panel_size(this); for (int i = 0; i < size; i++) { if (CheckItem_get((CheckItem*)Panel_get(this, i))) diff --git a/AffinityPanel.h b/AffinityPanel.h index 63283c51..2b6059b0 100644 --- a/AffinityPanel.h +++ b/AffinityPanel.h @@ -18,6 +18,6 @@ extern PanelClass AffinityPanel_class; Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity); -Affinity* AffinityPanel_getAffinity(Panel* this); +Affinity* AffinityPanel_getAffinity(Panel* this, ProcessList* pl); #endif diff --git a/AvailableColumnsPanel.c b/AvailableColumnsPanel.c index d954da8c..c0c1f2dd 100644 --- a/AvailableColumnsPanel.c +++ b/AvailableColumnsPanel.c @@ -13,18 +13,14 @@ in the source distribution for its full text. #include #include #include +#include /*{ #include "Panel.h" -#include "Settings.h" -#include "ScreenManager.h" typedef struct AvailableColumnsPanel_ { Panel super; Panel* columns; - - Settings* settings; - ScreenManager* scr; } AvailableColumnsPanel; }*/ @@ -38,7 +34,7 @@ static void AvailableColumnsPanel_delete(Object* object) { static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) { AvailableColumnsPanel* this = (AvailableColumnsPanel*) super; - char* text = ((ListItem*) Panel_getSelected(super))->value; + int key = ((ListItem*) Panel_getSelected(super))->key; HandlerResult result = IGNORED; switch(ch) { @@ -47,7 +43,7 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) { case KEY_F(5): { int at = Panel_getSelectedIndex(this->columns); - Panel_insert(this->columns, at, (Object*) ListItem_new(text, 0)); + Panel_insert(this->columns, at, (Object*) ListItem_new(Process_fields[key].name, key)); Panel_setSelected(this->columns, at+1); ColumnsPanel_update(this->columns); result = HANDLED; @@ -71,19 +67,19 @@ PanelClass AvailableColumnsPanel_class = { .eventHandler = AvailableColumnsPanel_eventHandler }; -AvailableColumnsPanel* AvailableColumnsPanel_new(Settings* settings, Panel* columns, ScreenManager* scr) { +AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) { AvailableColumnsPanel* this = AllocThis(AvailableColumnsPanel); Panel* super = (Panel*) this; Panel_init(super, 1, 1, 1, 1, Class(ListItem), true); - - this->settings = settings; - this->scr = scr; Panel_setHeader(super, "Available Columns"); for (int i = 1; i < LAST_PROCESSFIELD; i++) { - if (i != COMM) - Panel_add(super, (Object*) ListItem_new(Process_fieldNames[i], 0)); + if (i != COMM && Process_fields[i].description) { + char description[256]; + snprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description); + Panel_add(super, (Object*) ListItem_new(description, i)); + } } this->columns = columns; return this; diff --git a/AvailableColumnsPanel.h b/AvailableColumnsPanel.h index 0a29e6a4..5a8371dd 100644 --- a/AvailableColumnsPanel.h +++ b/AvailableColumnsPanel.h @@ -10,20 +10,15 @@ in the source distribution for its full text. */ #include "Panel.h" -#include "Settings.h" -#include "ScreenManager.h" typedef struct AvailableColumnsPanel_ { Panel super; Panel* columns; - - Settings* settings; - ScreenManager* scr; } AvailableColumnsPanel; extern PanelClass AvailableColumnsPanel_class; -AvailableColumnsPanel* AvailableColumnsPanel_new(Settings* settings, Panel* columns, ScreenManager* scr); +AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns); #endif diff --git a/AvailableMetersPanel.c b/AvailableMetersPanel.c index 80b2ebee..9cd2b0a3 100644 --- a/AvailableMetersPanel.c +++ b/AvailableMetersPanel.c @@ -19,14 +19,16 @@ in the source distribution for its full text. #include "Settings.h" #include "Panel.h" #include "ScreenManager.h" +#include "ProcessList.h" typedef struct AvailableMetersPanel_ { Panel super; + ScreenManager* scr; Settings* settings; + Header* header; Panel* leftPanel; Panel* rightPanel; - ScreenManager* scr; } AvailableMetersPanel; }*/ @@ -38,14 +40,14 @@ static void AvailableMetersPanel_delete(Object* object) { free(this); } -static inline void AvailableMetersPanel_addHeader(Header* header, Panel* panel, MeterClass* type, int param, HeaderSide side) { - Meter* meter = (Meter*) Header_addMeter(header, type, param, side); +static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, MeterClass* type, int param, int column) { + Meter* meter = (Meter*) Header_addMeterByClass(header, type, param, column); Panel_add(panel, (Object*) Meter_toListItem(meter)); } static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { AvailableMetersPanel* this = (AvailableMetersPanel*) super; - Header* header = this->settings->header; + Header* header = this->header; ListItem* selected = (ListItem*) Panel_getSelected(super); int param = selected->key & 0xff; @@ -57,7 +59,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { case 'l': case 'L': { - AvailableMetersPanel_addHeader(header, this->leftPanel, Platform_meterTypes[type], param, LEFT_HEADER); + AvailableMetersPanel_addMeter(header, this->leftPanel, Platform_meterTypes[type], param, 0); result = HANDLED; break; } @@ -65,7 +67,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { case 'r': case 'R': { - AvailableMetersPanel_addHeader(header, this->rightPanel, Platform_meterTypes[type], param, RIGHT_HEADER); + AvailableMetersPanel_addMeter(header, this->rightPanel, Platform_meterTypes[type], param, 1); result = HANDLED; break; } @@ -87,12 +89,13 @@ PanelClass AvailableMetersPanel_class = { .eventHandler = AvailableMetersPanel_eventHandler }; -AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr) { +AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl) { AvailableMetersPanel* this = AllocThis(AvailableMetersPanel); Panel* super = (Panel*) this; Panel_init(super, 1, 1, 1, 1, Class(ListItem), true); this->settings = settings; + this->header = header; this->leftPanel = leftMeters; this->rightPanel = rightMeters; this->scr = scr; @@ -105,7 +108,7 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Panel* leftMe } } MeterClass* type = &CPUMeter_class; - int cpus = settings->pl->cpuCount; + int cpus = pl->cpuCount; if (cpus > 1) { Panel_add(super, (Object*) ListItem_new("CPU average", 0)); for (int i = 1; i <= cpus; i++) { diff --git a/AvailableMetersPanel.h b/AvailableMetersPanel.h index 281e2857..e9b949b0 100644 --- a/AvailableMetersPanel.h +++ b/AvailableMetersPanel.h @@ -12,19 +12,21 @@ in the source distribution for its full text. #include "Settings.h" #include "Panel.h" #include "ScreenManager.h" +#include "ProcessList.h" typedef struct AvailableMetersPanel_ { Panel super; + ScreenManager* scr; Settings* settings; + Header* header; Panel* leftPanel; Panel* rightPanel; - ScreenManager* scr; } AvailableMetersPanel; extern PanelClass AvailableMetersPanel_class; -AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr); +AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl); #endif diff --git a/CPUMeter.c b/CPUMeter.c index 95b397f6..511af8e9 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -8,7 +8,8 @@ in the source distribution for its full text. #include "CPUMeter.h" #include "CRT.h" -#include "ProcessList.h" +#include "Settings.h" +#include "Platform.h" #include #include @@ -34,7 +35,7 @@ static void CPUMeter_init(Meter* this) { int cpu = this->param; if (this->pl->cpuCount > 1) { char caption[10]; - sprintf(caption, "%-3d", ProcessList_cpuId(this->pl, cpu - 1)); + sprintf(caption, "%-3d", Settings_cpuId(this->pl->settings, cpu - 1)); Meter_setCaption(this, caption); } if (this->param == 0) @@ -42,39 +43,12 @@ static void CPUMeter_init(Meter* this) { } static void CPUMeter_setValues(Meter* this, char* buffer, int size) { - ProcessList* pl = this->pl; int cpu = this->param; if (cpu > this->pl->cpuCount) { snprintf(buffer, size, "absent"); return; } - CPUData* cpuData = &(pl->cpus[cpu]); - double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod); - double percent; - double* v = this->values; - v[0] = cpuData->nicePeriod / total * 100.0; - v[1] = cpuData->userPeriod / total * 100.0; - if (pl->detailedCPUTime) { - v[2] = cpuData->systemPeriod / total * 100.0; - v[3] = cpuData->irqPeriod / total * 100.0; - v[4] = cpuData->softIrqPeriod / total * 100.0; - v[5] = cpuData->stealPeriod / total * 100.0; - v[6] = cpuData->guestPeriod / total * 100.0; - v[7] = cpuData->ioWaitPeriod / total * 100.0; - Meter_setItems(this, 8); - if (pl->accountGuestInCPUMeter) { - percent = v[0]+v[1]+v[2]+v[3]+v[4]+v[5]+v[6]; - } else { - percent = v[0]+v[1]+v[2]+v[3]+v[4]; - } - } else { - v[2] = cpuData->systemAllPeriod / total * 100.0; - v[3] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0; - Meter_setItems(this, 4); - percent = v[0]+v[1]+v[2]+v[3]; - } - percent = MIN(100.0, MAX(0.0, percent)); - if (isnan(percent)) percent = 0.0; + double percent = Platform_setCPUValues(this, cpu); snprintf(buffer, size, "%5.1f%%", percent); } @@ -89,7 +63,7 @@ static void CPUMeter_display(Object* cast, RichString* out) { sprintf(buffer, "%5.1f%% ", this->values[1]); RichString_append(out, CRT_colors[METER_TEXT], ":"); RichString_append(out, CRT_colors[CPU_NORMAL], buffer); - if (this->pl->detailedCPUTime) { + if (this->pl->settings->detailedCPUTime) { sprintf(buffer, "%5.1f%% ", this->values[2]); RichString_append(out, CRT_colors[METER_TEXT], "sy:"); RichString_append(out, CRT_colors[CPU_KERNEL], buffer); diff --git a/CRT.c b/CRT.c index c06c3caf..8b8abff1 100644 --- a/CRT.c +++ b/CRT.c @@ -15,6 +15,7 @@ in the source distribution for its full text. #include #include #include +#include #define ColorPair(i,j) COLOR_PAIR((7-i)*8+j) @@ -39,6 +40,17 @@ in the source distribution for its full text. /*{ #include +typedef enum TreeStr_ { + TREE_STR_HORZ, + TREE_STR_VERT, + TREE_STR_RTEE, + TREE_STR_BEND, + TREE_STR_TEND, + TREE_STR_OPEN, + TREE_STR_SHUT, + TREE_STR_COUNT +} TreeStr; + typedef enum ColorElements_ { RESET_COLOR, DEFAULT_COLOR, @@ -73,13 +85,6 @@ typedef enum ColorElements_ { BAR_SHADOW, GRAPH_1, GRAPH_2, - GRAPH_3, - GRAPH_4, - GRAPH_5, - GRAPH_6, - GRAPH_7, - GRAPH_8, - GRAPH_9, MEMORY_USED, MEMORY_BUFFERS, MEMORY_BUFFERS_TEXT, @@ -112,14 +117,32 @@ void CRT_handleSIGSEGV(int sgn); }*/ -// TODO: centralize these in Settings. +const char *CRT_treeStrAscii[TREE_STR_COUNT] = { + "-", // TREE_STR_HORZ + "|", // TREE_STR_VERT + "`", // TREE_STR_RTEE + "`", // TREE_STR_BEND + ",", // TREE_STR_TEND + "+", // TREE_STR_OPEN + "-", // TREE_STR_SHUT +}; + +const char *CRT_treeStrUtf8[TREE_STR_COUNT] = { + "\xe2\x94\x80", // TREE_STR_HORZ ─ + "\xe2\x94\x82", // TREE_STR_VERT │ + "\xe2\x94\x9c", // TREE_STR_RTEE ├ + "\xe2\x94\x94", // TREE_STR_BEND └ + "\xe2\x94\x8c", // TREE_STR_TEND ┌ + "+", // TREE_STR_OPEN + + "\xe2\x94\x80", // TREE_STR_SHUT ─ +}; + +const char **CRT_treeStr = CRT_treeStrAscii; static bool CRT_hasColors; static int CRT_delay = 0; -int CRT_colorScheme = 0; - bool CRT_utf8 = false; int CRT_colors[LAST_COLORELEMENT] = { 0 }; @@ -130,6 +153,10 @@ int CRT_scrollHAmount = 5; char* CRT_termType; +// TODO move color scheme to Settings, perhaps? + +int CRT_colorScheme = 0; + void *backtraceArray[128]; static void CRT_handleSIGTERM(int sgn) { @@ -188,6 +215,22 @@ void CRT_init(int delay, int colorScheme) { CRT_colorScheme = 1; CRT_setColors(CRT_colorScheme); +#ifdef HAVE_LIBNCURSESW + char *locale = setlocale(LC_ALL, NULL); + if (locale == NULL || locale[0] == '\0') + locale = setlocale(LC_CTYPE, NULL); + if (locale != NULL && + (strstr(locale, "UTF-8") || + strstr(locale, "utf-8") || + strstr(locale, "UTF8") || + strstr(locale, "utf8"))) + CRT_utf8 = true; + else + CRT_utf8 = false; +#endif + + CRT_treeStr = CRT_utf8 ? CRT_treeStrUtf8 : CRT_treeStrAscii; + mousemask(BUTTON1_CLICKED, NULL); } @@ -267,14 +310,7 @@ void CRT_setColors(int colorScheme) { CRT_colors[BAR_SHADOW] = A_DIM; CRT_colors[SWAP] = A_BOLD; CRT_colors[GRAPH_1] = A_BOLD; - CRT_colors[GRAPH_2] = A_BOLD; - CRT_colors[GRAPH_3] = A_BOLD; - CRT_colors[GRAPH_4] = A_NORMAL; - CRT_colors[GRAPH_5] = A_NORMAL; - CRT_colors[GRAPH_6] = A_NORMAL; - CRT_colors[GRAPH_7] = A_DIM; - CRT_colors[GRAPH_8] = A_DIM; - CRT_colors[GRAPH_9] = A_DIM; + CRT_colors[GRAPH_2] = A_NORMAL; CRT_colors[MEMORY_USED] = A_BOLD; CRT_colors[MEMORY_BUFFERS] = A_NORMAL; CRT_colors[MEMORY_BUFFERS_TEXT] = A_NORMAL; @@ -330,15 +366,8 @@ void CRT_setColors(int colorScheme) { CRT_colors[BAR_BORDER] = ColorPair(Blue,White); CRT_colors[BAR_SHADOW] = ColorPair(Black,White); CRT_colors[SWAP] = ColorPair(Red,White); - CRT_colors[GRAPH_1] = ColorPair(Yellow,White); - CRT_colors[GRAPH_2] = ColorPair(Yellow,White); - CRT_colors[GRAPH_3] = ColorPair(Yellow,White); - CRT_colors[GRAPH_4] = ColorPair(Yellow,White); - CRT_colors[GRAPH_5] = ColorPair(Yellow,White); - CRT_colors[GRAPH_6] = ColorPair(Yellow,White); - CRT_colors[GRAPH_7] = ColorPair(Yellow,White); - CRT_colors[GRAPH_8] = ColorPair(Yellow,White); - CRT_colors[GRAPH_9] = ColorPair(Yellow,White); + CRT_colors[GRAPH_1] = A_BOLD | ColorPair(Blue,White); + CRT_colors[GRAPH_2] = ColorPair(Blue,White); CRT_colors[MEMORY_USED] = ColorPair(Green,White); CRT_colors[MEMORY_BUFFERS] = ColorPair(Cyan,White); CRT_colors[MEMORY_BUFFERS_TEXT] = ColorPair(Cyan,White); @@ -394,15 +423,8 @@ void CRT_setColors(int colorScheme) { CRT_colors[BAR_BORDER] = ColorPair(Blue,Black); CRT_colors[BAR_SHADOW] = ColorPair(Black,Black); CRT_colors[SWAP] = ColorPair(Red,Black); - CRT_colors[GRAPH_1] = ColorPair(Yellow,Black); - CRT_colors[GRAPH_2] = ColorPair(Yellow,Black); - CRT_colors[GRAPH_3] = ColorPair(Yellow,Black); - CRT_colors[GRAPH_4] = ColorPair(Yellow,Black); - CRT_colors[GRAPH_5] = ColorPair(Yellow,Black); - CRT_colors[GRAPH_6] = ColorPair(Yellow,Black); - CRT_colors[GRAPH_7] = ColorPair(Yellow,Black); - CRT_colors[GRAPH_8] = ColorPair(Yellow,Black); - CRT_colors[GRAPH_9] = ColorPair(Yellow,Black); + CRT_colors[GRAPH_1] = A_BOLD | ColorPair(Cyan,Black); + CRT_colors[GRAPH_2] = ColorPair(Cyan,Black); CRT_colors[MEMORY_USED] = ColorPair(Green,Black); CRT_colors[MEMORY_BUFFERS] = ColorPair(Cyan,Black); CRT_colors[MEMORY_BUFFERS_TEXT] = ColorPair(Cyan,Black); @@ -458,15 +480,8 @@ void CRT_setColors(int colorScheme) { CRT_colors[BAR_BORDER] = A_BOLD | ColorPair(Yellow,Blue); CRT_colors[BAR_SHADOW] = ColorPair(Cyan,Blue); CRT_colors[SWAP] = ColorPair(Red,Blue); - CRT_colors[GRAPH_1] = A_BOLD | ColorPair(Yellow,Blue); - CRT_colors[GRAPH_2] = A_BOLD | ColorPair(Yellow,Blue); - CRT_colors[GRAPH_3] = A_BOLD | ColorPair(Yellow,Blue); - CRT_colors[GRAPH_4] = A_BOLD | ColorPair(Yellow,Blue); - CRT_colors[GRAPH_5] = A_BOLD | ColorPair(Yellow,Blue); - CRT_colors[GRAPH_6] = A_BOLD | ColorPair(Yellow,Blue); - CRT_colors[GRAPH_7] = A_BOLD | ColorPair(Yellow,Blue); - CRT_colors[GRAPH_8] = A_BOLD | ColorPair(Yellow,Blue); - CRT_colors[GRAPH_9] = A_BOLD | ColorPair(Yellow,Blue); + CRT_colors[GRAPH_1] = A_BOLD | ColorPair(Cyan,Blue); + CRT_colors[GRAPH_2] = ColorPair(Cyan,Blue); CRT_colors[MEMORY_USED] = A_BOLD | ColorPair(Green,Blue); CRT_colors[MEMORY_BUFFERS] = A_BOLD | ColorPair(Cyan,Blue); CRT_colors[MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Cyan,Blue); @@ -522,15 +537,8 @@ void CRT_setColors(int colorScheme) { CRT_colors[BAR_BORDER] = A_BOLD | ColorPair(Green,Black); CRT_colors[BAR_SHADOW] = ColorPair(Cyan,Black); CRT_colors[SWAP] = ColorPair(Red,Black); - CRT_colors[GRAPH_1] = A_BOLD | ColorPair(Red,Black); - CRT_colors[GRAPH_2] = ColorPair(Red,Black); - CRT_colors[GRAPH_3] = A_BOLD | ColorPair(Yellow,Black); - CRT_colors[GRAPH_4] = A_BOLD | ColorPair(Green,Black); - CRT_colors[GRAPH_5] = ColorPair(Green,Black); - CRT_colors[GRAPH_6] = ColorPair(Cyan,Black); - CRT_colors[GRAPH_7] = A_BOLD | ColorPair(Blue,Black); - CRT_colors[GRAPH_8] = ColorPair(Blue,Black); - CRT_colors[GRAPH_9] = A_BOLD | ColorPair(Black,Black); + CRT_colors[GRAPH_1] = A_BOLD | ColorPair(Green,Black); + CRT_colors[GRAPH_2] = ColorPair(Green,Black); CRT_colors[MEMORY_USED] = ColorPair(Green,Black); CRT_colors[MEMORY_BUFFERS] = ColorPair(Blue,Black); CRT_colors[MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue,Black); @@ -587,22 +595,15 @@ void CRT_setColors(int colorScheme) { CRT_colors[BAR_BORDER] = A_BOLD; CRT_colors[BAR_SHADOW] = A_BOLD | ColorPair(Black,Black); CRT_colors[SWAP] = ColorPair(Red,Black); - CRT_colors[GRAPH_1] = A_BOLD | ColorPair(Red,Black); - CRT_colors[GRAPH_2] = ColorPair(Red,Black); - CRT_colors[GRAPH_3] = A_BOLD | ColorPair(Yellow,Black); - CRT_colors[GRAPH_4] = A_BOLD | ColorPair(Green,Black); - CRT_colors[GRAPH_5] = ColorPair(Green,Black); - CRT_colors[GRAPH_6] = ColorPair(Cyan,Black); - CRT_colors[GRAPH_7] = A_BOLD | ColorPair(Blue,Black); - CRT_colors[GRAPH_8] = ColorPair(Blue,Black); - CRT_colors[GRAPH_9] = A_BOLD | ColorPair(Black,Black); + CRT_colors[GRAPH_1] = A_BOLD | ColorPair(Cyan,Black); + CRT_colors[GRAPH_2] = ColorPair(Cyan,Black); CRT_colors[MEMORY_USED] = ColorPair(Green,Black); CRT_colors[MEMORY_BUFFERS] = ColorPair(Blue,Black); CRT_colors[MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue,Black); CRT_colors[MEMORY_CACHE] = ColorPair(Yellow,Black); - CRT_colors[LOAD_AVERAGE_FIFTEEN] = A_BOLD | ColorPair(Black,Black); - CRT_colors[LOAD_AVERAGE_FIVE] = A_NORMAL; - CRT_colors[LOAD_AVERAGE_ONE] = A_BOLD; + CRT_colors[LOAD_AVERAGE_FIFTEEN] = A_NORMAL; + CRT_colors[LOAD_AVERAGE_FIVE] = ColorPair(Cyan,Black); + CRT_colors[LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(Cyan,Black); CRT_colors[LOAD] = A_BOLD; CRT_colors[HELP_BOLD] = A_BOLD | ColorPair(Cyan,Black); CRT_colors[CLOCK] = A_BOLD; diff --git a/CRT.h b/CRT.h index b77a83a7..1160f175 100644 --- a/CRT.h +++ b/CRT.h @@ -31,6 +31,17 @@ in the source distribution for its full text. #include +typedef enum TreeStr_ { + TREE_STR_HORZ, + TREE_STR_VERT, + TREE_STR_RTEE, + TREE_STR_BEND, + TREE_STR_TEND, + TREE_STR_OPEN, + TREE_STR_SHUT, + TREE_STR_COUNT +} TreeStr; + typedef enum ColorElements_ { RESET_COLOR, DEFAULT_COLOR, @@ -65,13 +76,6 @@ typedef enum ColorElements_ { BAR_SHADOW, GRAPH_1, GRAPH_2, - GRAPH_3, - GRAPH_4, - GRAPH_5, - GRAPH_6, - GRAPH_7, - GRAPH_8, - GRAPH_9, MEMORY_USED, MEMORY_BUFFERS, MEMORY_BUFFERS_TEXT, @@ -103,9 +107,11 @@ void CRT_fatalError(const char* note) __attribute__ ((noreturn)); void CRT_handleSIGSEGV(int sgn); -// TODO: centralize these in Settings. +extern const char *CRT_treeStrAscii[TREE_STR_COUNT]; -extern int CRT_colorScheme; +extern const char *CRT_treeStrUtf8[TREE_STR_COUNT]; + +extern const char **CRT_treeStr; extern bool CRT_utf8; @@ -117,6 +123,10 @@ extern int CRT_scrollHAmount; char* CRT_termType; +// TODO move color scheme to Settings, perhaps? + +extern int CRT_colorScheme; + void *backtraceArray[128]; // TODO: pass an instance of Settings instead. diff --git a/CategoriesPanel.c b/CategoriesPanel.c index bf6ee543..cbad064d 100644 --- a/CategoriesPanel.c +++ b/CategoriesPanel.c @@ -21,12 +21,15 @@ in the source distribution for its full text. #include "Panel.h" #include "Settings.h" #include "ScreenManager.h" +#include "ProcessList.h" typedef struct CategoriesPanel_ { Panel super; + ScreenManager* scr; Settings* settings; - ScreenManager* scr; + Header* header; + ProcessList* pl; } CategoriesPanel; }*/ @@ -51,9 +54,9 @@ static void CategoriesPanel_delete(Object* object) { } void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { - Panel* leftMeters = (Panel*) MetersPanel_new(this->settings, "Left column", this->settings->header->leftMeters, this->scr); - Panel* rightMeters = (Panel*) MetersPanel_new(this->settings, "Right column", this->settings->header->rightMeters, this->scr); - Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, leftMeters, rightMeters, this->scr); + Panel* leftMeters = (Panel*) MetersPanel_new(this->settings, "Left column", this->header->columns[0], this->scr); + Panel* rightMeters = (Panel*) MetersPanel_new(this->settings, "Right column", this->header->columns[1], this->scr); + Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, leftMeters, rightMeters, this->scr, this->pl); ScreenManager_add(this->scr, leftMeters, FunctionBar_new(MetersFunctions, NULL, NULL), 20); ScreenManager_add(this->scr, rightMeters, FunctionBar_new(MetersFunctions, NULL, NULL), 20); ScreenManager_add(this->scr, availableMeters, FunctionBar_new(AvailableMetersFunctions, NULL, NULL), -1); @@ -70,8 +73,8 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) { } static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) { - Panel* columns = (Panel*) ColumnsPanel_new(this->settings, this->scr); - Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(this->settings, columns, this->scr); + Panel* columns = (Panel*) ColumnsPanel_new(this->settings); + Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns); ScreenManager_add(this->scr, columns, FunctionBar_new(ColumnsFunctions, NULL, NULL), 20); ScreenManager_add(this->scr, availableColumns, FunctionBar_new(AvailableColumnsFunctions, NULL, NULL), -1); } @@ -140,13 +143,15 @@ PanelClass CategoriesPanel_class = { .eventHandler = CategoriesPanel_eventHandler }; -CategoriesPanel* CategoriesPanel_new(Settings* settings, ScreenManager* scr) { +CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl) { CategoriesPanel* this = AllocThis(CategoriesPanel); Panel* super = (Panel*) this; Panel_init(super, 1, 1, 1, 1, Class(ListItem), true); - this->settings = settings; this->scr = scr; + this->settings = settings; + this->header = header; + this->pl = pl; Panel_setHeader(super, "Setup"); Panel_add(super, (Object*) ListItem_new("Meters", 0)); Panel_add(super, (Object*) ListItem_new("Display options", 0)); diff --git a/CategoriesPanel.h b/CategoriesPanel.h index 9d30330f..ccef0fae 100644 --- a/CategoriesPanel.h +++ b/CategoriesPanel.h @@ -12,12 +12,15 @@ in the source distribution for its full text. #include "Panel.h" #include "Settings.h" #include "ScreenManager.h" +#include "ProcessList.h" typedef struct CategoriesPanel_ { Panel super; + ScreenManager* scr; Settings* settings; - ScreenManager* scr; + Header* header; + ProcessList* pl; } CategoriesPanel; @@ -25,6 +28,6 @@ void CategoriesPanel_makeMetersPage(CategoriesPanel* this); extern PanelClass CategoriesPanel_class; -CategoriesPanel* CategoriesPanel_new(Settings* settings, ScreenManager* scr); +CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl); #endif diff --git a/ColorsPanel.c b/ColorsPanel.c index 4e28d017..81144950 100644 --- a/ColorsPanel.c +++ b/ColorsPanel.c @@ -72,7 +72,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) { if (result == HANDLED) { this->settings->changed = true; - Header* header = this->settings->header; + const Header* header = this->scr->header; CRT_setColors(mark); Panel* menu = (Panel*) Vector_get(this->scr->panels, 0); Header_draw(header); diff --git a/ColumnsPanel.c b/ColumnsPanel.c index f4eed99e..6a2dc084 100644 --- a/ColumnsPanel.c +++ b/ColumnsPanel.c @@ -8,6 +8,7 @@ in the source distribution for its full text. #include "ColumnsPanel.h" #include "String.h" +#include "ListItem.h" #include #include @@ -16,13 +17,12 @@ in the source distribution for its full text. /*{ #include "Panel.h" #include "Settings.h" -#include "ScreenManager.h" typedef struct ColumnsPanel_ { Panel super; Settings* settings; - ScreenManager* scr; + bool moving; } ColumnsPanel; }*/ @@ -35,12 +35,32 @@ static void ColumnsPanel_delete(Object* object) { } static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) { + ColumnsPanel* const this = (ColumnsPanel*) super; int selected = Panel_getSelectedIndex(super); HandlerResult result = IGNORED; int size = Panel_size(super); switch(ch) { + case 0x0a: + case 0x0d: + case KEY_ENTER: + case KEY_MOUSE: + { + if (selected < size - 1) { + this->moving = !(this->moving); + ((ListItem*)Panel_getSelected(super))->moving = this->moving; + result = HANDLED; + } + break; + } + case KEY_UP: + { + if (!this->moving) { + break; + } + /* else fallthrough */ + } case KEY_F(7): case '[': case '-': @@ -50,6 +70,13 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) { result = HANDLED; break; } + case KEY_DOWN: + { + if (!this->moving) { + break; + } + /* else fallthrough */ + } case KEY_F(8): case ']': case '+': @@ -90,47 +117,42 @@ PanelClass ColumnsPanel_class = { .eventHandler = ColumnsPanel_eventHandler }; -ColumnsPanel* ColumnsPanel_new(Settings* settings, ScreenManager* scr) { +ColumnsPanel* ColumnsPanel_new(Settings* settings) { ColumnsPanel* this = AllocThis(ColumnsPanel); Panel* super = (Panel*) this; Panel_init(super, 1, 1, 1, 1, Class(ListItem), true); this->settings = settings; - this->scr = scr; + this->moving = false; Panel_setHeader(super, "Active Columns"); - ProcessField* fields = this->settings->pl->fields; + ProcessField* fields = this->settings->fields; for (; *fields; fields++) { - Panel_add(super, (Object*) ListItem_new(Process_fieldNames[*fields], 0)); + Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields)); } return this; } int ColumnsPanel_fieldNameToIndex(const char* name) { for (int j = 1; j <= LAST_PROCESSFIELD; j++) { - if (String_eq(name, Process_fieldNames[j])) { + if (String_eq(name, Process_fields[j].name)) { return j; } } - return 0; + return -1; } void ColumnsPanel_update(Panel* super) { ColumnsPanel* this = (ColumnsPanel*) super; int size = Panel_size(super); this->settings->changed = true; - // FIXME: this is crappily inefficient - free(this->settings->pl->fields); - this->settings->pl->fields = (ProcessField*) malloc(sizeof(ProcessField) * (size+1)); - this->settings->pl->flags = 0; + this->settings->fields = realloc(this->settings->fields, sizeof(ProcessField) * (size+1)); + this->settings->flags = 0; for (int i = 0; i < size; i++) { - char* text = ((ListItem*) Panel_get(super, i))->value; - int j = ColumnsPanel_fieldNameToIndex(text); - if (j > 0) { - this->settings->pl->fields[i] = j; - this->settings->pl->flags |= Process_fieldFlags[j]; - } + int key = ((ListItem*) Panel_get(super, i))->key; + this->settings->fields[i] = key; + this->settings->flags |= Process_fields[key].flags; } - this->settings->pl->fields[size] = 0; + this->settings->fields[size] = 0; } diff --git a/ColumnsPanel.h b/ColumnsPanel.h index 122d9f99..0da674a8 100644 --- a/ColumnsPanel.h +++ b/ColumnsPanel.h @@ -11,19 +11,18 @@ in the source distribution for its full text. #include "Panel.h" #include "Settings.h" -#include "ScreenManager.h" typedef struct ColumnsPanel_ { Panel super; Settings* settings; - ScreenManager* scr; + bool moving; } ColumnsPanel; extern PanelClass ColumnsPanel_class; -ColumnsPanel* ColumnsPanel_new(Settings* settings, ScreenManager* scr); +ColumnsPanel* ColumnsPanel_new(Settings* settings); int ColumnsPanel_fieldNameToIndex(const char* name); diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c index 61533132..8fa81964 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -52,9 +52,9 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) { if (result == HANDLED) { this->settings->changed = true; - Header* header = this->settings->header; - Header_calculateHeight(header); - Header_reinit(header); + const Header* header = this->scr->header; + Header_calculateHeight((Header*) header); + Header_reinit((Header*) header); Header_draw(header); ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2); } @@ -78,18 +78,18 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* this->scr = scr; Panel_setHeader(super, "Display options"); - Panel_add(super, (Object*) CheckItem_new(strdup("Tree view"), &(settings->pl->treeView), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Shadow other users' processes"), &(settings->pl->shadowOtherUsers), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Hide kernel threads"), &(settings->pl->hideKernelThreads), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Hide userland threads"), &(settings->pl->hideUserlandThreads), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Display threads in a different color"), &(settings->pl->highlightThreads), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Show custom thread names"), &(settings->pl->showThreadNames), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Highlight program \"basename\""), &(settings->pl->highlightBaseName), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Highlight large numbers in memory counters"), &(settings->pl->highlightMegabytes), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Leave a margin around header"), &(settings->header->margin), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)"), &(settings->pl->detailedCPUTime), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Count CPUs from 0 instead of 1"), &(settings->pl->countCPUsFromZero), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Update process names on every refresh"), &(settings->pl->updateProcessNames), false)); - Panel_add(super, (Object*) CheckItem_new(strdup("Add guest time in CPU meter percentage"), &(settings->pl->accountGuestInCPUMeter), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Tree view"), &(settings->treeView), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Shadow other users' processes"), &(settings->shadowOtherUsers), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Hide kernel threads"), &(settings->hideKernelThreads), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Hide userland threads"), &(settings->hideUserlandThreads), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Display threads in a different color"), &(settings->highlightThreads), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Show custom thread names"), &(settings->showThreadNames), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Highlight program \"basename\""), &(settings->highlightBaseName), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Highlight large numbers in memory counters"), &(settings->highlightMegabytes), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Leave a margin around header"), &(settings->headerMargin), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)"), &(settings->detailedCPUTime), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Update process names on every refresh"), &(settings->updateProcessNames), false)); + Panel_add(super, (Object*) CheckItem_new(strdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter), false)); return this; } diff --git a/Header.c b/Header.c index e3ab7f29..8b7cef6d 100644 --- a/Header.c +++ b/Header.c @@ -17,20 +17,15 @@ in the source distribution for its full text. #include /*{ -#include "ProcessList.h" #include "Meter.h" - -typedef enum HeaderSide_ { - LEFT_HEADER, - RIGHT_HEADER -} HeaderSide; +#include "Vector.h" typedef struct Header_ { - Vector* leftMeters; - Vector* rightMeters; - ProcessList* pl; + Vector** columns; + struct ProcessList_* pl; int height; int pad; + int nrColumns; bool margin; } Header; @@ -40,25 +35,32 @@ typedef struct Header_ { #define MAX(a,b) ((a)>(b)?(a):(b)) #endif -Header* Header_new(ProcessList* pl) { +#ifndef Header_forEachColumn +#define Header_forEachColumn(this_, i_) for (int i_=0; i_ < this->nrColumns; i_++) +#endif + +Header* Header_new(struct ProcessList_* pl, int nrColumns) { Header* this = calloc(1, sizeof(Header)); - this->leftMeters = Vector_new(Class(Meter), true, DEFAULT_SIZE); - this->rightMeters = Vector_new(Class(Meter), true, DEFAULT_SIZE); + this->columns = calloc(nrColumns, sizeof(Vector*)); + this->nrColumns = nrColumns; + Header_forEachColumn(this, i) { + this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE); + } this->margin = true; this->pl = pl; return this; } void Header_delete(Header* this) { - Vector_delete(this->leftMeters); - Vector_delete(this->rightMeters); + Header_forEachColumn(this, i) { + Vector_delete(this->columns[i]); + } + free(this->columns); free(this); } -void Header_createMeter(Header* this, char* name, HeaderSide side) { - Vector* meters = side == LEFT_HEADER - ? this->leftMeters - : this->rightMeters; +MeterModeId Header_addMeterByName(Header* this, char* name, int column) { + Vector* meters = this->columns[column]; char* paren = strchr(name, '('); int param = 0; @@ -67,18 +69,20 @@ void Header_createMeter(Header* this, char* name, HeaderSide side) { if (!ok) param = 0; *paren = '\0'; } + MeterModeId mode = TEXT_METERMODE; for (MeterClass** type = Platform_meterTypes; *type; type++) { if (String_eq(name, (*type)->name)) { - Vector_add(meters, Meter_new(this->pl, param, *type)); + Meter* meter = Meter_new(this->pl, param, *type); + Vector_add(meters, meter); + mode = meter->mode; break; } } + return mode; } -void Header_setMode(Header* this, int i, MeterModeId mode, HeaderSide side) { - Vector* meters = side == LEFT_HEADER - ? this->leftMeters - : this->rightMeters; +void Header_setMode(Header* this, int i, MeterModeId mode, int column) { + Vector* meters = this->columns[column]; if (i >= Vector_size(meters)) return; @@ -86,28 +90,21 @@ void Header_setMode(Header* this, int i, MeterModeId mode, HeaderSide side) { Meter_setMode(meter, mode); } -Meter* Header_addMeter(Header* this, MeterClass* type, int param, HeaderSide side) { - Vector* meters = side == LEFT_HEADER - ? this->leftMeters - : this->rightMeters; +Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column) { + Vector* meters = this->columns[column]; Meter* meter = Meter_new(this->pl, param, type); Vector_add(meters, meter); return meter; } -int Header_size(Header* this, HeaderSide side) { - Vector* meters = side == LEFT_HEADER - ? this->leftMeters - : this->rightMeters; - +int Header_size(Header* this, int column) { + Vector* meters = this->columns[column]; return Vector_size(meters); } -char* Header_readMeterName(Header* this, int i, HeaderSide side) { - Vector* meters = side == LEFT_HEADER - ? this->leftMeters - : this->rightMeters; +char* Header_readMeterName(Header* this, int i, int column) { + Vector* meters = this->columns[column]; Meter* meter = (Meter*) Vector_get(meters, i); int nameLen = strlen(Meter_name(meter)); @@ -121,25 +118,20 @@ char* Header_readMeterName(Header* this, int i, HeaderSide side) { return name; } -MeterModeId Header_readMeterMode(Header* this, int i, HeaderSide side) { - Vector* meters = side == LEFT_HEADER - ? this->leftMeters - : this->rightMeters; +MeterModeId Header_readMeterMode(Header* this, int i, int column) { + Vector* meters = this->columns[column]; Meter* meter = (Meter*) Vector_get(meters, i); return meter->mode; } void Header_reinit(Header* this) { - for (int i = 0; i < Vector_size(this->leftMeters); i++) { - Meter* meter = (Meter*) Vector_get(this->leftMeters, i); - if (Meter_initFn(meter)) - Meter_init(meter); - } - for (int i = 0; i < Vector_size(this->rightMeters); i++) { - Meter* meter = (Meter*) Vector_get(this->rightMeters, i); - if (Meter_initFn(meter)) - Meter_init(meter); + Header_forEachColumn(this, col) { + for (int i = 0; i < Vector_size(this->columns[col]); i++) { + Meter* meter = (Meter*) Vector_get(this->columns[col], i); + if (Meter_initFn(meter)) + Meter_init(meter); + } } } @@ -150,32 +142,33 @@ void Header_draw(const Header* this) { for (int y = 0; y < height; y++) { mvhline(y, 0, ' ', COLS); } - for (int y = (pad / 2), i = 0; i < Vector_size(this->leftMeters); i++) { - Meter* meter = (Meter*) Vector_get(this->leftMeters, i); - meter->draw(meter, pad, y, COLS / 2 - (pad * 2 - 1) - 1); - y += meter->h; - } - for (int y = (pad / 2), i = 0; i < Vector_size(this->rightMeters); i++) { - Meter* meter = (Meter*) Vector_get(this->rightMeters, i); - meter->draw(meter, COLS / 2 + pad, y, COLS / 2 - (pad * 2 - 1) - 1); - y += meter->h; + int width = COLS / this->nrColumns - (pad * this->nrColumns - 1) - 1; + int x = pad; + + Header_forEachColumn(this, col) { + Vector* meters = this->columns[col]; + for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) { + Meter* meter = (Meter*) Vector_get(meters, i); + meter->draw(meter, x, y, width); + y += meter->h; + } + x += width + pad; } } int Header_calculateHeight(Header* this) { int pad = this->margin ? 2 : 0; - int leftHeight = pad; - int rightHeight = pad; + int maxHeight = pad; - for (int i = 0; i < Vector_size(this->leftMeters); i++) { - Meter* meter = (Meter*) Vector_get(this->leftMeters, i); - leftHeight += meter->h; + Header_forEachColumn(this, col) { + Vector* meters = this->columns[col]; + int height = pad; + for (int i = 0; i < Vector_size(meters); i++) { + Meter* meter = (Meter*) Vector_get(meters, i); + height += meter->h; + } + maxHeight = MAX(maxHeight, height); } - for (int i = 0; i < Vector_size(this->rightMeters); i++) { - Meter* meter = (Meter*) Vector_get(this->rightMeters, i); - rightHeight += meter->h; - } - this->pad = pad; - this->height = MAX(leftHeight, rightHeight); - return this->height; + this->height = maxHeight; + return maxHeight; } diff --git a/Header.h b/Header.h index 84774391..11c04960 100644 --- a/Header.h +++ b/Header.h @@ -9,20 +9,15 @@ Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ -#include "ProcessList.h" #include "Meter.h" - -typedef enum HeaderSide_ { - LEFT_HEADER, - RIGHT_HEADER -} HeaderSide; +#include "Vector.h" typedef struct Header_ { - Vector* leftMeters; - Vector* rightMeters; - ProcessList* pl; + Vector** columns; + struct ProcessList_* pl; int height; int pad; + int nrColumns; bool margin; } Header; @@ -31,21 +26,25 @@ typedef struct Header_ { #define MAX(a,b) ((a)>(b)?(a):(b)) #endif -Header* Header_new(ProcessList* pl); +#ifndef Header_forEachColumn +#define Header_forEachColumn(this_, i_) for (int i_=0; i_ < this->nrColumns; i_++) +#endif + +Header* Header_new(struct ProcessList_* pl, int nrColumns); void Header_delete(Header* this); -void Header_createMeter(Header* this, char* name, HeaderSide side); +MeterModeId Header_addMeterByName(Header* this, char* name, int column); -void Header_setMode(Header* this, int i, MeterModeId mode, HeaderSide side); +void Header_setMode(Header* this, int i, MeterModeId mode, int column); -Meter* Header_addMeter(Header* this, MeterClass* type, int param, HeaderSide side); +Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column); -int Header_size(Header* this, HeaderSide side); +int Header_size(Header* this, int column); -char* Header_readMeterName(Header* this, int i, HeaderSide side); +char* Header_readMeterName(Header* this, int i, int column); -MeterModeId Header_readMeterMode(Header* this, int i, HeaderSide side); +MeterModeId Header_readMeterMode(Header* this, int i, int column); void Header_reinit(Header* this); diff --git a/ListItem.c b/ListItem.c index 05f5e960..dbedfe12 100644 --- a/ListItem.c +++ b/ListItem.c @@ -22,6 +22,7 @@ typedef struct ListItem_ { Object super; char* value; int key; + bool moving; } ListItem; }*/ @@ -33,14 +34,19 @@ static void ListItem_delete(Object* cast) { } static void ListItem_display(Object* cast, RichString* out) { - ListItem* this = (ListItem*)cast; + ListItem* const this = (ListItem*)cast; assert (this != NULL); /* int len = strlen(this->value)+1; char buffer[len+1]; snprintf(buffer, len, "%s", this->value); */ - RichString_write(out, CRT_colors[DEFAULT_COLOR], this->value/*buffer*/); + if (this->moving) { + RichString_write(out, CRT_colors[DEFAULT_COLOR], "↕ "); + } else { + RichString_prune(out); + } + RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value/*buffer*/); } ObjectClass ListItem_class = { @@ -53,6 +59,7 @@ ListItem* ListItem_new(const char* value, int key) { ListItem* this = AllocThis(ListItem); this->value = strdup(value); this->key = key; + this->moving = false; return this; } diff --git a/ListItem.h b/ListItem.h index 46d2d653..b48f0acd 100644 --- a/ListItem.h +++ b/ListItem.h @@ -15,6 +15,7 @@ typedef struct ListItem_ { Object super; char* value; int key; + bool moving; } ListItem; diff --git a/Makefile.am b/Makefile.am index 0c6eaca3..f2540590 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,7 +16,7 @@ htop_CFLAGS = -pedantic -Wall -Wextra -std=c99 -rdynamic -D_XOPEN_SOURCE_EXTENDE AM_CPPFLAGS = -DNDEBUG myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \ -ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c \ +ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \ DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \ LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \ BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \ @@ -26,7 +26,7 @@ HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c myhtopheaders = AvailableColumnsPanel.h AvailableMetersPanel.h \ CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \ -CPUMeter.h CRT.h DisplayOptionsPanel.h FunctionBar.h \ +CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \ Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \ BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \ ScreenManager.h Settings.h SignalsPanel.h String.h SwapMeter.h TasksMeter.h \ diff --git a/MemoryMeter.c b/MemoryMeter.c index 01516259..b2d45743 100644 --- a/MemoryMeter.c +++ b/MemoryMeter.c @@ -8,7 +8,7 @@ in the source distribution for its full text. #include "MemoryMeter.h" #include "CRT.h" -#include "ProcessList.h" +#include "Platform.h" #include #include @@ -25,15 +25,8 @@ int MemoryMeter_attributes[] = { }; static void MemoryMeter_setValues(Meter* this, char* buffer, int size) { - long int usedMem = this->pl->usedMem; - long int buffersMem = this->pl->buffersMem; - long int cachedMem = this->pl->cachedMem; - usedMem -= buffersMem + cachedMem; - this->total = this->pl->totalMem; - this->values[0] = usedMem; - this->values[1] = buffersMem; - this->values[2] = cachedMem; - snprintf(buffer, size, "%ld/%ldMB", (long int) usedMem / 1024, (long int) this->total / 1024); + Platform_setMemoryValues(this); + snprintf(buffer, size, "%ld/%ldMB", (long int) this->values[0] / 1024, (long int) this->total / 1024); } static void MemoryMeter_display(Object* cast, RichString* out) { diff --git a/Meter.c b/Meter.c index 77458949..1d4e6e3b 100644 --- a/Meter.c +++ b/Meter.c @@ -21,11 +21,12 @@ in the source distribution for its full text. #include #include -#define METER_BUFFER_LEN 128 +#define METER_BUFFER_LEN 256 /*{ #include "ListItem.h" -#include "ProcessList.h" + +#include typedef struct Meter_ Meter; @@ -77,7 +78,7 @@ struct Meter_ { int param; void* drawData; int h; - ProcessList* pl; + struct ProcessList_* pl; double* values; double total; }; @@ -117,7 +118,7 @@ MeterClass Meter_class = { } }; -Meter* Meter_new(ProcessList* pl, int param, MeterClass* type) { +Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type) { Meter* this = calloc(1, sizeof(Meter)); Object_setClass(this, type); this->h = 1; @@ -302,25 +303,41 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) { /* ---------- GraphMeterMode ---------- */ -#define DrawDot(a,y,c) do { attrset(a); mvaddch(y, x+k, c); } while(0) - -static int GraphMeterMode_colors[21] = { - GRAPH_1, GRAPH_1, GRAPH_1, - GRAPH_2, GRAPH_2, GRAPH_2, - GRAPH_3, GRAPH_3, GRAPH_3, - GRAPH_4, GRAPH_4, GRAPH_4, - GRAPH_5, GRAPH_5, GRAPH_6, - GRAPH_7, GRAPH_7, GRAPH_7, - GRAPH_8, GRAPH_8, GRAPH_9 +static const char* GraphMeterMode_dotsUtf8[5][5] = { + { /*00*/"⠀", /*01*/"⢀", /*02*/"⢠", /*03*/"⢰", /*04*/ "⢸" }, + { /*10*/"⡀", /*11*/"⣀", /*12*/"⣠", /*13*/"⣰", /*14*/ "⣸" }, + { /*20*/"⡄", /*21*/"⣄", /*22*/"⣤", /*23*/"⣴", /*24*/ "⣼" }, + { /*30*/"⡆", /*31*/"⣆", /*32*/"⣦", /*33*/"⣶", /*34*/ "⣾" }, + { /*40*/"⡇", /*41*/"⣇", /*42*/"⣧", /*43*/"⣷", /*44*/ "⣿" }, }; -static const char* GraphMeterMode_characters = "^`'-.,_~'`-.,_~'`-.,_"; +static const char* GraphMeterMode_dotsAscii[5][5] = { + { /*00*/" ", /*01*/".", /*02*/".", /*03*/":", /*04*/ ":" }, + { /*10*/"⡀", /*11*/".", /*12*/".", /*13*/":", /*14*/ ":" }, + { /*20*/".", /*21*/".", /*22*/".", /*23*/":", /*24*/ ":" }, + { /*30*/":", /*31*/":", /*32*/":", /*33*/":", /*34*/ ":" }, + { /*40*/":", /*41*/":", /*42*/":", /*43*/":", /*44*/ ":" }, +}; + +static const char* (*GraphMeterMode_dots)[5]; static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { if (!this->drawData) this->drawData = calloc(1, sizeof(GraphData)); - GraphData* data = (GraphData*) this->drawData; + GraphData* data = (GraphData*) this->drawData; const int nValues = METER_BUFFER_LEN; + + if (CRT_utf8) { + GraphMeterMode_dots = GraphMeterMode_dotsUtf8; + } else { + GraphMeterMode_dots = GraphMeterMode_dotsAscii; + } + + attrset(CRT_colors[METER_TEXT]); + int captionLen = 3; + mvaddnstr(y, x, this->caption, captionLen); + x += captionLen; + w -= captionLen; struct timeval now; gettimeofday(&now, NULL); @@ -342,18 +359,26 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { data->values[nValues - 1] = value; } - for (int i = nValues - w, k = 0; i < nValues; i++, k++) { - double value = data->values[i]; - DrawDot( CRT_colors[DEFAULT_COLOR], y, ' ' ); - DrawDot( CRT_colors[DEFAULT_COLOR], y+1, ' ' ); - DrawDot( CRT_colors[DEFAULT_COLOR], y+2, ' ' ); + for (int i = nValues - (w*2) + 2, k = 0; i < nValues; i+=2, k++) { + const double dot = (1.0 / 16); + int v1 = data->values[i] / dot; + int v2 = data->values[i+1] / dot; - double threshold = 1.00; - for (int j = 0; j < 21; j++, threshold -= 0.05) - if (value >= threshold) { - DrawDot(CRT_colors[GraphMeterMode_colors[j]], y+(j/7.0), GraphMeterMode_characters[j]); - break; - } + if (v1 == 0) v1 = 1; + if (v2 == 0) v2 = 1; + + int level = 12; + int colorIdx = GRAPH_1; + for (int line = 0; line < 4; line++) { + + int line1 = MIN(4, MAX(0, v1 - level)); + int line2 = MIN(4, MAX(0, v2 - level)); + + attrset(CRT_colors[colorIdx]); + mvaddstr(y+line, x+k, GraphMeterMode_dots[line1][line2]); + colorIdx = GRAPH_2; + level -= 4; + } } attrset(CRT_colors[RESET_COLOR]); } @@ -372,18 +397,22 @@ static const char* LEDMeterMode_digitsUtf8[3][10] = { { "└──┘"," ╵ ","└──╴","╶──┘"," ╵","╶──┘","└──┘"," ╵","└──┘"," ──┘"}, }; +static const char* (*LEDMeterMode_digits)[10]; + static void LEDMeterMode_drawDigit(int x, int y, int n) { - if (CRT_utf8) { - for (int i = 0; i < 3; i++) - mvaddstr(y+i, x, LEDMeterMode_digitsUtf8[i][n]); - } else { - for (int i = 0; i < 3; i++) - mvaddstr(y+i, x, LEDMeterMode_digitsAscii[i][n]); - } + for (int i = 0; i < 3; i++) + mvaddstr(y+i, x, LEDMeterMode_digits[i][n]); } static void LEDMeterMode_draw(Meter* this, int x, int y, int w) { (void) w; + + if (CRT_utf8) { + LEDMeterMode_digits = LEDMeterMode_digitsUtf8; + } else { + LEDMeterMode_digits = LEDMeterMode_digitsAscii; + } + char buffer[METER_BUFFER_LEN]; Meter_setValues(this, buffer, METER_BUFFER_LEN - 1); @@ -423,7 +452,7 @@ static MeterMode TextMeterMode = { static MeterMode GraphMeterMode = { .uiName = "Graph", - .h = 3, + .h = 4, .draw = GraphMeterMode_draw, }; diff --git a/Meter.h b/Meter.h index bfd6ebbb..97efc1ad 100644 --- a/Meter.h +++ b/Meter.h @@ -9,10 +9,11 @@ Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ -#define METER_BUFFER_LEN 128 +#define METER_BUFFER_LEN 256 #include "ListItem.h" -#include "ProcessList.h" + +#include typedef struct Meter_ Meter; @@ -64,7 +65,7 @@ struct Meter_ { int param; void* drawData; int h; - ProcessList* pl; + struct ProcessList_* pl; double* values; double total; }; @@ -99,7 +100,7 @@ typedef struct GraphData_ { extern MeterClass Meter_class; -Meter* Meter_new(ProcessList* pl, int param, MeterClass* type); +Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type); void Meter_delete(Object* cast); @@ -115,8 +116,6 @@ ListItem* Meter_toListItem(Meter* this); /* ---------- GraphMeterMode ---------- */ -#define DrawDot(a,y,c) do { attrset(a); mvaddch(y, x+k, c); } while(0) - /* ---------- LEDMeterMode ---------- */ extern MeterMode* Meter_modes[]; diff --git a/MetersPanel.c b/MetersPanel.c index 0e755f52..ee25b0d1 100644 --- a/MetersPanel.c +++ b/MetersPanel.c @@ -21,6 +21,7 @@ typedef struct MetersPanel_ { Settings* settings; Vector* meters; ScreenManager* scr; + bool moving; } MetersPanel; }*/ @@ -42,6 +43,13 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { case 0x0a: case 0x0d: case KEY_ENTER: + { + this->moving = !(this->moving); + ((ListItem*)Panel_getSelected(super))->moving = this->moving; + result = HANDLED; + break; + } + case ' ': case KEY_F(4): case 't': { @@ -53,6 +61,13 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { result = HANDLED; break; } + case KEY_UP: + { + if (!this->moving) { + break; + } + /* else fallthrough */ + } case KEY_F(7): case '[': case '-': @@ -62,6 +77,13 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { result = HANDLED; break; } + case KEY_DOWN: + { + if (!this->moving) { + break; + } + /* else fallthrough */ + } case KEY_F(8): case ']': case '+': @@ -83,7 +105,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { } } if (result == HANDLED) { - Header* header = this->settings->header; + Header* header = (Header*) this->scr->header; this->settings->changed = true; Header_calculateHeight(header); Header_draw(header); @@ -108,6 +130,7 @@ MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* met this->settings = settings; this->meters = meters; this->scr = scr; + this->moving = false; Panel_setHeader(super, header); for (int i = 0; i < Vector_size(meters); i++) { Meter* meter = (Meter*) Vector_get(meters, i); diff --git a/MetersPanel.h b/MetersPanel.h index 56c83e77..06b78da6 100644 --- a/MetersPanel.h +++ b/MetersPanel.h @@ -19,6 +19,7 @@ typedef struct MetersPanel_ { Settings* settings; Vector* meters; ScreenManager* scr; + bool moving; } MetersPanel; diff --git a/Panel.c b/Panel.c index 4355b1e2..8982cc2e 100644 --- a/Panel.c +++ b/Panel.c @@ -28,9 +28,11 @@ in the source distribution for its full text. typedef struct Panel_ Panel; typedef enum HandlerResult_ { - HANDLED, - IGNORED, - BREAK_LOOP + HANDLED = 0x00, + IGNORED = 0x01, + BREAK_LOOP = 0x02, + REFRESH = 0x04, + RECALCULATE = 0x08, } HandlerResult; #define EVENT_SETSELECTED -1 @@ -54,7 +56,7 @@ struct Panel_ { Vector* items; int selected; int oldSelected; - char* eventHandlerBuffer; + void* eventHandlerState; int scrollV; short scrollH; bool needsRedraw; @@ -102,7 +104,7 @@ void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool this->y = y; this->w = w; this->h = h; - this->eventHandlerBuffer = NULL; + this->eventHandlerState = NULL; this->items = Vector_new(type, owner, DEFAULT_SIZE); this->scrollV = 0; this->scrollH = 0; @@ -114,7 +116,7 @@ void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool void Panel_done(Panel* this) { assert (this != NULL); - free(this->eventHandlerBuffer); + free(this->eventHandlerState); Vector_delete(this->items); RichString_end(this->header); } @@ -246,30 +248,11 @@ void Panel_setSelected(Panel* this, int selected) { void Panel_draw(Panel* this, bool focus) { assert (this != NULL); - int itemCount = Vector_size(this->items); + int size = Vector_size(this->items); int scrollH = this->scrollH; - int y = this->y; int x = this->x; - int first = this->scrollV; - if (itemCount > this->h && first > itemCount - this->h) { - first = itemCount - this->h; - this->scrollV = first; - } - int last = MIN(itemCount, first + MIN(itemCount, this->h)); - if (this->selected < first) { - first = this->selected; - this->scrollV = first; - this->needsRedraw = true; - } - if (this->selected >= last) { - last = MIN(itemCount, this->selected + 1); - first = last - this->h; - this->scrollV = first; - this->needsRedraw = true; - } - if (first < 0) - first = 0; - if (last > itemCount) - last = itemCount; + int y = this->y; + int x = this->x; + int h = this->h; int headerLen = RichString_sizeVal(this->header); if (headerLen > 0) { @@ -285,14 +268,34 @@ void Panel_draw(Panel* this, bool focus) { attrset(CRT_colors[RESET_COLOR]); y++; } - + + // ensure scroll area is on screen + if (this->scrollV < 0) { + this->scrollV = 0; + this->needsRedraw = true; + } else if (this->scrollV >= size) { + this->scrollV = MAX(size - 1, 0); + this->needsRedraw = true; + } + // ensure selection is on screen + if (this->selected < this->scrollV) { + this->scrollV = this->selected; + this->needsRedraw = true; + } else if (this->selected >= this->scrollV + h) { + this->scrollV = this->selected - h + 1; + this->needsRedraw = true; + } + + int first = this->scrollV; + int upTo = MIN(first + h, size); + int highlight = focus ? CRT_colors[PANEL_HIGHLIGHT_FOCUS] : CRT_colors[PANEL_HIGHLIGHT_UNFOCUS]; if (this->needsRedraw) { - - for(int i = first, j = 0; j < this->h && i < last; i++, j++) { + int line = 0; + for(int i = first; line < h && i < upTo; i++) { Object* itemObj = Vector_get(this->items, i); assert(itemObj); if(!itemObj) continue; RichString_begin(item); @@ -304,15 +307,18 @@ void Panel_draw(Panel* this, bool focus) { attrset(highlight); RichString_setAttr(&item, highlight); } - mvhline(y + j, x, ' ', this->w); + mvhline(y + line, x, ' ', this->w); if (amt > 0) - RichString_printoffnVal(item, y+j, x, scrollH, amt); + RichString_printoffnVal(item, y + line, x, scrollH, amt); if (selected) attrset(CRT_colors[RESET_COLOR]); RichString_end(item); + line++; + } + while (line < h) { + mvhline(y + line, x, ' ', this->w); + line++; } - for (int i = y + (last - first); i < y + this->h; i++) - mvhline(i, x+0, ' ', this->w); this->needsRedraw = false; } else { @@ -325,15 +331,15 @@ void Panel_draw(Panel* this, bool focus) { RichString_begin(new); Object_display(newObj, &new); int newLen = RichString_sizeVal(new); - mvhline(y+ this->oldSelected - this->scrollV, x+0, ' ', this->w); + mvhline(y+ this->oldSelected - first, x+0, ' ', this->w); if (scrollH < oldLen) - RichString_printoffnVal(old, y+this->oldSelected - this->scrollV, x, + RichString_printoffnVal(old, y+this->oldSelected - first, x, scrollH, MIN(oldLen - scrollH, this->w)); attrset(highlight); - mvhline(y+this->selected - this->scrollV, x+0, ' ', this->w); + mvhline(y+this->selected - first, x+0, ' ', this->w); RichString_setAttr(&new, highlight); if (scrollH < newLen) - RichString_printoffnVal(new, y+this->selected - this->scrollV, x, + RichString_printoffnVal(new, y+this->selected - first, x, scrollH, MIN(newLen - scrollH, this->w)); attrset(CRT_colors[RESET_COLOR]); RichString_end(new); @@ -345,38 +351,26 @@ void Panel_draw(Panel* this, bool focus) { bool Panel_onKey(Panel* this, int key) { assert (this != NULL); + + int size = Vector_size(this->items); switch (key) { case KEY_DOWN: case KEY_CTRLN: - if (this->selected + 1 < Vector_size(this->items)) - this->selected++; - return true; + this->selected++; + break; case KEY_UP: case KEY_CTRLP: - if (this->selected > 0) - this->selected--; - return true; + this->selected--; + break; #ifdef KEY_C_DOWN case KEY_C_DOWN: - if (this->selected + 1 < Vector_size(this->items)) { - this->selected++; - if (this->scrollV < Vector_size(this->items) - this->h) { - this->scrollV++; - this->needsRedraw = true; - } - } - return true; + this->selected++; + break; #endif #ifdef KEY_C_UP case KEY_C_UP: - if (this->selected > 0) { - this->selected--; - if (this->scrollV > 0) { - this->scrollV--; - this->needsRedraw = true; - } - } - return true; + this->selected--; + break; #endif case KEY_LEFT: case KEY_CTRLB: @@ -384,71 +378,74 @@ bool Panel_onKey(Panel* this, int key) { this->scrollH -= CRT_scrollHAmount; this->needsRedraw = true; } - return true; + break; case KEY_RIGHT: case KEY_CTRLF: this->scrollH += CRT_scrollHAmount; this->needsRedraw = true; - return true; + break; case KEY_PPAGE: this->selected -= (this->h - 1); this->scrollV -= (this->h - 1); - if (this->selected < 0) - this->selected = 0; - if (this->scrollV < 0) - this->scrollV = 0; this->needsRedraw = true; - return true; + break; case KEY_NPAGE: this->selected += (this->h - 1); - int size = Vector_size(this->items); - if (this->selected < 0) - this->selected = 0; - if (this->selected >= size) - this->selected = size - 1; this->scrollV += (this->h - 1); - if (this->scrollV >= MAX(0, size - this->h)) - this->scrollV = MAX(0, size - this->h - 1); this->needsRedraw = true; - return true; + break; case KEY_HOME: this->selected = 0; - return true; + break; case KEY_END: - this->selected = Vector_size(this->items) - 1; - return true; + this->selected = size - 1; + break; + default: + return false; } - return false; + + // ensure selection within bounds + if (this->selected < 0) { + this->selected = 0; + this->needsRedraw = true; + } else if (this->selected >= size) { + this->selected = size - 1; + this->needsRedraw = true; + } + return true; } HandlerResult Panel_selectByTyping(Panel* this, int ch) { int size = Panel_size(this); - if (!this->eventHandlerBuffer) - this->eventHandlerBuffer = calloc(100, 1); + if (!this->eventHandlerState) + this->eventHandlerState = calloc(100, 1); + char* buffer = this->eventHandlerState; if (isalnum(ch)) { - int len = strlen(this->eventHandlerBuffer); + int len = strlen(buffer); if (len < 99) { - this->eventHandlerBuffer[len] = ch; - this->eventHandlerBuffer[len+1] = '\0'; + buffer[len] = ch; + buffer[len+1] = '\0'; } for (int try = 0; try < 2; try++) { - len = strlen(this->eventHandlerBuffer); + len = strlen(buffer); for (int i = 0; i < size; i++) { char* cur = ((ListItem*) Panel_get(this, i))->value; while (*cur == ' ') cur++; - if (strncasecmp(cur, this->eventHandlerBuffer, len) == 0) { + if (strncasecmp(cur, buffer, len) == 0) { Panel_setSelected(this, i); return HANDLED; } } - this->eventHandlerBuffer[0] = ch; - this->eventHandlerBuffer[1] = '\0'; + // if current word did not match, + // retry considering the character the start of a new word. + buffer[0] = ch; + buffer[1] = '\0'; } return HANDLED; } else if (ch != ERR) { - this->eventHandlerBuffer[0] = '\0'; + buffer[0] = '\0'; } if (ch == 13) { return BREAK_LOOP; diff --git a/Panel.h b/Panel.h index 91c0a40c..717f6dde 100644 --- a/Panel.h +++ b/Panel.h @@ -17,9 +17,11 @@ in the source distribution for its full text. typedef struct Panel_ Panel; typedef enum HandlerResult_ { - HANDLED, - IGNORED, - BREAK_LOOP + HANDLED = 0x00, + IGNORED = 0x01, + BREAK_LOOP = 0x02, + REFRESH = 0x04, + RECALCULATE = 0x08, } HandlerResult; #define EVENT_SETSELECTED -1 @@ -43,7 +45,7 @@ struct Panel_ { Vector* items; int selected; int oldSelected; - char* eventHandlerBuffer; + void* eventHandlerState; int scrollV; short scrollH; bool needsRedraw; diff --git a/Process.c b/Process.c index de25cbbb..31dd9659 100644 --- a/Process.c +++ b/Process.c @@ -7,7 +7,7 @@ in the source distribution for its full text. #include "Process.h" -#include "ProcessList.h" +#include "Settings.h" #include "CRT.h" #include "String.h" #include "RichString.h" @@ -24,14 +24,9 @@ in the source distribution for its full text. #include #include #include -#include #include #include -#ifdef HAVE_LIBHWLOC -#include -#endif - // On Linux, this works only with glibc 2.1+. On earlier versions // the behavior is similar to have a hardcoded page size. #ifndef PAGE_SIZE @@ -41,7 +36,6 @@ in the source distribution for its full text. /*{ #include "Object.h" -#include "Affinity.h" #include @@ -88,12 +82,10 @@ typedef enum ProcessField_ { LAST_PROCESSFIELD } ProcessField; -struct ProcessList_; - typedef struct Process_ { Object super; - struct ProcessList_ *pl; + struct Settings_* settings; pid_t pid; char* comm; @@ -133,10 +125,10 @@ typedef struct Process_ { unsigned long long io_read_bytes; unsigned long long io_write_bytes; unsigned long long io_cancelled_write_bytes; - double io_rate_read_bps; unsigned long long io_rate_read_time; - double io_rate_write_bps; unsigned long long io_rate_write_time; + double io_rate_read_bps; + double io_rate_write_bps; #endif int processor; @@ -192,92 +184,99 @@ typedef struct Process_ { } Process; +typedef struct ProcessFieldData_ { + const char* name; + const char* title; + const char* description; + int flags; +} ProcessFieldData; + +void Process_writeField(Process* this, RichString* str, ProcessField field); +long Process_compare(const void* v1, const void* v2); + }*/ -const char *Process_fieldNames[] = { - "", "PID", "Command", "STATE", "PPID", "PGRP", "SESSION", - "TTY_NR", "TPGID", "FLAGS", "MINFLT", "CMINFLT", "MAJFLT", "CMAJFLT", - "UTIME", "STIME", "CUTIME", "CSTIME", "PRIORITY", "NICE", "ITREALVALUE", - "STARTTIME", "VSIZE", "RSS", "RLIM", "STARTCODE", "ENDCODE", "STARTSTACK", - "KSTKESP", "KSTKEIP", "SIGNAL", "BLOCKED", "SIGIGNORE", "SIGCATCH", "WCHAN", - "NSWAP", "CNSWAP", "EXIT_SIGNAL", "PROCESSOR", "M_SIZE", "M_RESIDENT", "M_SHARE", - "M_TRS", "M_DRS", "M_LRS", "M_DT", "ST_UID", "PERCENT_CPU", "PERCENT_MEM", - "USER", "TIME", "NLWP", "TGID", +ProcessFieldData Process_fields[] = { + { .name = "", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, }, + { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, + { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, }, + { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, }, + { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, }, + { .name = "SESSION", .title = " SESN ", .description = "Process's session ID", .flags = 0, }, + { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, }, + { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, }, + { .name = "FLAGS", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, + { .name = "CMINFLT", .title = " CMINFLT ", .description = "Children processes' minor faults", .flags = 0, }, + { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, + { .name = "CMAJFLT", .title = " CMAJFLT ", .description = "Children processes' major faults", .flags = 0, }, + { .name = "UTIME", .title = " UTIME+ ", .description = "User CPU time - time the process spent executing in user mode", .flags = 0, }, + { .name = "STIME", .title = " STIME+ ", .description = "System CPU time - time the kernel spent running system calls for this process", .flags = 0, }, + { .name = "CUTIME", .title = " CUTIME+ ", .description = "Children processes' user CPU time", .flags = 0, }, + { .name = "CSTIME", .title = " CSTIME+ ", .description = "Children processes' system CPU time", .flags = 0, }, + { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, + { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, }, + { .name = "ITREALVALUE", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, }, + { .name = "VSIZE", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "RSS", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "RLIM", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "STARTCODE", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "ENDCODE", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "STARTSTACK", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "KSTKESP", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "KSTKEIP", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "SIGNAL", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "BLOCKED", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "SIGIGNORE", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "SIGCATCH", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "WCHAN", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "NSWAP", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "CNSWAP", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "EXIT_SIGNAL", .title = NULL, .description = NULL, .flags = 0, }, + { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, + { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, + { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, + { .name = "M_SHARE", .title = " SHR ", .description = "Size of the process's shared pages", .flags = 0, }, + { .name = "M_TRS", .title = " CODE ", .description = "Size of the text segment of the process", .flags = 0, }, + { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, }, + { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process", .flags = 0, }, + { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process", .flags = 0, }, + { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, + { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, + { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, + { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, + { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, + { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, + { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, }, #ifdef HAVE_OPENVZ - "CTID", "VPID", + { .name = "CTID", .title = " CTID ", .description = "OpenVZ container ID (a.k.a. virtual environment ID)", .flags = PROCESS_FLAG_OPENVZ, }, + { .name = "VPID", .title = " VPID ", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_OPENVZ, }, #endif #ifdef HAVE_VSERVER - "VXID", + { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_VSERVER, }, #endif #ifdef HAVE_TASKSTATS - "RCHAR", "WCHAR", "SYSCR", "SYSCW", "RBYTES", "WBYTES", "CNCLWB", - "IO_READ_RATE", "IO_WRITE_RATE", "IO_RATE", + { .name = "RCHAR", .title = " RD_CHAR ", .description = "Number of bytes the process has read", .flags = PROCESS_FLAG_IO, }, + { .name = "WCHAR", .title = " WR_CHAR ", .description = "Number of bytes the process has written", .flags = PROCESS_FLAG_IO, }, + { .name = "SYSCR", .title = " RD_SYSC ", .description = "Number of read(2) syscalls for the process", .flags = PROCESS_FLAG_IO, }, + { .name = "SYSCW", .title = " WR_SYSC ", .description = "Number of write(2) syscalls for the process", .flags = PROCESS_FLAG_IO, }, + { .name = "RBYTES", .title = " IO_RBYTES ", .description = "Bytes of read(2) I/O for the process", .flags = PROCESS_FLAG_IO, }, + { .name = "WBYTES", .title = " IO_WBYTES ", .description = "Bytes of write(2) I/O for the process", .flags = PROCESS_FLAG_IO, }, + { .name = "CNCLWB", .title = " IO_CANCEL ", .description = "Bytes of cancelled write(2) I/O", .flags = PROCESS_FLAG_IO, }, + { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, }, + { .name = "IO_WRITE_RATE", .title = " DISK WRITE ", .description = "The I/O rate of write(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, }, + { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, }, #endif #ifdef HAVE_CGROUP - "CGROUP", + { .name = "CGROUP", .title = " CGROUP ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_CGROUP, }, #endif #ifdef HAVE_OOM - "OOM", + { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = 0, }, #endif - "IO_PRIORITY", -"*** report bug! ***" -}; - -const int Process_fieldFlags[] = { - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, -#ifdef HAVE_OPENVZ - PROCESS_FLAG_OPENVZ, PROCESS_FLAG_OPENVZ, -#endif -#ifdef HAVE_VSERVER - PROCESS_FLAG_VSERVER, -#endif -#ifdef HAVE_TASKSTATS - PROCESS_FLAG_IO, PROCESS_FLAG_IO, PROCESS_FLAG_IO, PROCESS_FLAG_IO, PROCESS_FLAG_IO, PROCESS_FLAG_IO, PROCESS_FLAG_IO, - PROCESS_FLAG_IO, PROCESS_FLAG_IO, PROCESS_FLAG_IO, -#endif -#ifdef HAVE_CGROUP - PROCESS_FLAG_CGROUP, -#endif -#ifdef HAVE_OOM - 0, -#endif - PROCESS_FLAG_IOPRIO -}; - -const char *Process_fieldTitles[] = { - "", " PID ", "Command ", "S ", " PPID ", " PGRP ", " SESN ", - " TTY ", " TPGID ", "- ", " MINFLT ", " CMINFLT ", " MAJFLT ", " CMAJFLT ", - " UTIME+ ", " STIME+ ", " CUTIME+ ", " CSTIME+ ", "PRI ", " NI ", "- ", - "START ", "- ", "- ", "- ", "- ", "- ", "- ", - "- ", "- ", "- ", "- ", "- ", "- ", "- ", - "- ", "- ", "- ", "CPU ", " VIRT ", " RES ", " SHR ", - " CODE ", " DATA ", " LIB ", " DIRTY ", " UID ", "CPU% ", "MEM% ", - "USER ", " TIME+ ", "NLWP ", " TGID ", -#ifdef HAVE_OPENVZ - " CTID ", " VPID ", -#endif -#ifdef HAVE_VSERVER - " VXID ", -#endif -#ifdef HAVE_TASKSTATS - " RD_CHAR ", " WR_CHAR ", " RD_SYSC ", " WR_SYSC ", " IO_RBYTES ", " IO_WBYTES ", " IO_CANCEL ", - " IORR ", " IOWR ", " IORW ", -#endif -#ifdef HAVE_CGROUP - " CGROUP ", -#endif -#ifdef HAVE_OOM - " OOM ", -#endif - "IO ", -"*** report bug! ***" + { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_IOPRIO, }, + { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, }; static int Process_getuid = -1; @@ -289,32 +288,32 @@ void Process_setupColumnWidths() { int maxPid = Platform_getMaxPid(); if (maxPid == -1) return; if (maxPid > 99999) { - Process_fieldTitles[PID] = " PID "; - Process_fieldTitles[PPID] = " PPID "; + Process_fields[PID].title = " PID "; + Process_fields[PPID].title = " PPID "; #ifdef HAVE_OPENVZ - Process_fieldTitles[VPID] = " VPID "; + Process_fields[VPID].title = " VPID "; #endif - Process_fieldTitles[TPGID] = " TPGID "; - Process_fieldTitles[TGID] = " TGID "; - Process_fieldTitles[PGRP] = " PGRP "; - Process_fieldTitles[SESSION] = " SESN "; + Process_fields[TPGID].title = " TPGID "; + Process_fields[TGID].title = " TGID "; + Process_fields[PGRP].title = " PGRP "; + Process_fields[SESSION].title = " SESN "; #ifdef HAVE_OOM - Process_fieldTitles[OOM] = " OOM "; + Process_fields[OOM].title = " OOM "; #endif Process_pidFormat = "%7u "; Process_tpgidFormat = "%7d "; } else { - Process_fieldTitles[PID] = " PID "; - Process_fieldTitles[PPID] = " PPID "; + Process_fields[PID].title = " PID "; + Process_fields[PPID].title = " PPID "; #ifdef HAVE_OPENVZ - Process_fieldTitles[VPID] = " VPID "; + Process_fields[VPID].title = " VPID "; #endif - Process_fieldTitles[TPGID] = "TPGID "; - Process_fieldTitles[TGID] = " TGID "; - Process_fieldTitles[PGRP] = " PGRP "; - Process_fieldTitles[SESSION] = " SESN "; + Process_fields[TPGID].title = "TPGID "; + Process_fields[TGID].title = " TGID "; + Process_fields[PGRP].title = " PGRP "; + Process_fields[SESSION].title = " SESN "; #ifdef HAVE_OOM - Process_fieldTitles[OOM] = " OOM "; + Process_fields[OOM].title = " OOM "; #endif Process_pidFormat = "%5u "; Process_tpgidFormat = "%5d "; @@ -436,7 +435,7 @@ static void Process_printTime(RichString* str, unsigned long long t) { static inline void Process_writeCommand(Process* this, int attr, int baseattr, RichString* str) { int start = RichString_size(str); RichString_append(str, attr, this->comm); - if (this->pl->highlightBaseName) { + if (this->settings->highlightBaseName) { int finish = RichString_size(str) - 1; if (this->basenameOffset != -1) finish = (start + this->basenameOffset) - 1; @@ -455,27 +454,35 @@ static inline void Process_writeCommand(Process* this, int attr, int baseattr, R } } -static inline void Process_outputRate(RichString* str, int attr, char* buffer, int n, double rate, int coloring) { - rate = rate / 1024; - if (rate < 0.01) - snprintf(buffer, n, " 0 "); - else if (rate <= 10) - snprintf(buffer, n, "%5.2f ", rate); - else if (rate <= 100) - snprintf(buffer, n, "%5.1f ", rate); - else { - Process_humanNumber(str, rate, coloring); - return; +static inline void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring) { + int largeNumberColor = CRT_colors[LARGE_NUMBER]; + int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; + int processColor = CRT_colors[PROCESS]; + if (!coloring) { + largeNumberColor = CRT_colors[PROCESS]; + processMegabytesColor = CRT_colors[PROCESS]; + } + if (rate < ONE_K) { + int len = snprintf(buffer, n, "%7.2f B/s ", rate); + RichString_appendn(str, processColor, buffer, len); + } else if (rate < ONE_K * ONE_K) { + int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K); + RichString_appendn(str, processColor, buffer, len); + } else if (rate < ONE_K * ONE_K * ONE_K) { + int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_K / ONE_K); + RichString_appendn(str, processMegabytesColor, buffer, len); + } else { + int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_K / ONE_K / ONE_K); + RichString_appendn(str, largeNumberColor, buffer, len); } - RichString_append(str, attr, buffer); } -static void Process_writeField(Process* this, RichString* str, ProcessField field) { +void Process_writeDefaultField(Process* this, RichString* str, ProcessField field) { char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; int baseattr = CRT_colors[PROCESS_BASENAME]; int n = sizeof(buffer) - 1; - bool coloring = this->pl->highlightMegabytes; + bool coloring = this->settings->highlightMegabytes; switch (field) { case PID: snprintf(buffer, n, Process_pidFormat, this->pid); break; @@ -489,24 +496,21 @@ static void Process_writeField(Process* this, RichString* str, ProcessField fiel case CMINFLT: Process_colorNumber(str, this->cminflt, coloring); return; case MAJFLT: Process_colorNumber(str, this->majflt, coloring); return; case CMAJFLT: Process_colorNumber(str, this->cmajflt, coloring); return; - case PROCESSOR: snprintf(buffer, n, "%3d ", ProcessList_cpuId(this->pl, this->processor)); break; + case PROCESSOR: snprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break; case NLWP: snprintf(buffer, n, "%4ld ", this->nlwp); break; case COMM: { - if (this->pl->highlightThreads && Process_isThread(this)) { + if (this->settings->highlightThreads && Process_isThread(this)) { attr = CRT_colors[PROCESS_THREAD]; baseattr = CRT_colors[PROCESS_THREAD_BASENAME]; } - if (!this->pl->treeView || this->indent == 0) { + if (!this->settings->treeView || this->indent == 0) { Process_writeCommand(this, attr, baseattr, str); return; } else { char* buf = buffer; int maxIndent = 0; - const char **treeStr = this->pl->treeStr; bool lastItem = (this->indent < 0); int indent = (this->indent < 0 ? -this->indent : this->indent); - if (treeStr == NULL) - treeStr = ProcessList_treeStrAscii; for (int i = 0; i < 32; i++) if (indent & (1 << i)) @@ -514,14 +518,14 @@ static void Process_writeField(Process* this, RichString* str, ProcessField fiel for (int i = 0; i < maxIndent - 1; i++) { int written; if (indent & (1 << i)) - written = snprintf(buf, n, "%s ", treeStr[TREE_STR_VERT]); + written = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]); else written = snprintf(buf, n, " "); buf += written; n -= written; } - const char* draw = treeStr[lastItem ? (this->pl->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE]; - snprintf(buf, n, "%s%s ", draw, this->showChildren ? treeStr[TREE_STR_SHUT] : treeStr[TREE_STR_OPEN] ); + const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE]; + snprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); RichString_append(str, CRT_colors[PROCESS_TREE], buffer); Process_writeCommand(this, attr, baseattr, str); return; @@ -614,9 +618,9 @@ static void Process_writeField(Process* this, RichString* str, ProcessField fiel case RBYTES: Process_colorNumber(str, this->io_read_bytes, coloring); return; case WBYTES: Process_colorNumber(str, this->io_write_bytes, coloring); return; case CNCLWB: Process_colorNumber(str, this->io_cancelled_write_bytes, coloring); return; - case IO_READ_RATE: Process_outputRate(str, attr, buffer, n, this->io_rate_read_bps, coloring); return; - case IO_WRITE_RATE: Process_outputRate(str, attr, buffer, n, this->io_rate_write_bps, coloring); return; - case IO_RATE: Process_outputRate(str, attr, buffer, n, this->io_rate_read_bps + this->io_rate_write_bps, coloring); return; + case IO_READ_RATE: Process_outputRate(str, buffer, n, this->io_rate_read_bps, coloring); return; + case IO_WRITE_RATE: Process_outputRate(str, buffer, n, this->io_rate_write_bps, coloring); return; + case IO_RATE: Process_outputRate(str, buffer, n, this->io_rate_read_bps + this->io_rate_write_bps, coloring); return; #endif #ifdef HAVE_CGROUP case CGROUP: snprintf(buffer, n, "%-10s ", this->cgroup); break; @@ -632,11 +636,11 @@ static void Process_writeField(Process* this, RichString* str, ProcessField fiel static void Process_display(Object* cast, RichString* out) { Process* this = (Process*) cast; - ProcessField* fields = this->pl->fields; + ProcessField* fields = this->settings->fields; RichString_prune(out); for (int i = 0; fields[i]; i++) Process_writeField(this, out, fields[i]); - if (this->pl->shadowOtherUsers && (int)this->st_uid != Process_getuid) + if (this->settings->shadowOtherUsers && (int)this->st_uid != Process_getuid) RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]); if (this->tag == true) RichString_setAttr(out, CRT_colors[PROCESS_TAG]); @@ -660,11 +664,11 @@ ObjectClass Process_class = { .compare = Process_compare }; -Process* Process_new(struct ProcessList_ *pl) { +Process* Process_new(struct Settings_* settings) { Process* this = calloc(1, sizeof(Process)); Object_setClass(this, Class(Process)); this->pid = 0; - this->pl = pl; + this->settings = settings; this->tag = false; this->showChildren = true; this->show = true; @@ -698,65 +702,6 @@ bool Process_changePriorityBy(Process* this, size_t delta) { return Process_setPriority(this, this->nice + delta); } -#ifdef HAVE_LIBHWLOC - -Affinity* Process_getAffinity(Process* this) { - hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); - bool ok = (hwloc_linux_get_tid_cpubind(this->pl->topology, this->pid, cpuset) == 0); - Affinity* affinity = NULL; - if (ok) { - affinity = Affinity_new(); - if (hwloc_bitmap_last(cpuset) == -1) { - for (int i = 0; i < this->pl->cpuCount; i++) { - Affinity_add(affinity, i); - } - } else { - unsigned int id; - hwloc_bitmap_foreach_begin(id, cpuset); - Affinity_add(affinity, id); - hwloc_bitmap_foreach_end(); - } - } - hwloc_bitmap_free(cpuset); - return affinity; -} - -bool Process_setAffinity(Process* this, Affinity* affinity) { - hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); - for (int i = 0; i < affinity->used; i++) { - hwloc_bitmap_set(cpuset, affinity->cpus[i]); - } - bool ok = (hwloc_linux_set_tid_cpubind(this->pl->topology, this->pid, cpuset) == 0); - hwloc_bitmap_free(cpuset); - return ok; -} - -#elif HAVE_NATIVE_AFFINITY - -Affinity* Process_getAffinity(Process* this) { - cpu_set_t cpuset; - bool ok = (sched_getaffinity(this->pid, sizeof(cpu_set_t), &cpuset) == 0); - if (!ok) return NULL; - Affinity* affinity = Affinity_new(); - for (int i = 0; i < this->pl->cpuCount; i++) { - if (CPU_ISSET(i, &cpuset)) - Affinity_add(affinity, i); - } - return affinity; -} - -bool Process_setAffinity(Process* this, Affinity* affinity) { - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - for (int i = 0; i < affinity->used; i++) { - CPU_SET(affinity->cpus[i], &cpuset); - } - bool ok = (sched_setaffinity(this->pid, sizeof(unsigned long), &cpuset) == 0); - return ok; -} - -#endif - void Process_sendSignal(Process* this, size_t sgn) { kill(this->pid, (int) sgn); } @@ -767,10 +712,10 @@ long Process_pidCompare(const void* v1, const void* v2) { return (p1->pid - p2->pid); } -long Process_compare(const void* v1, const void* v2) { +long Process_defaultCompare(const void* v1, const void* v2) { Process *p1, *p2; - ProcessList *pl = ((Process*)v1)->pl; - if (pl->direction == 1) { + Settings *settings = ((Process*)v1)->settings; + if (settings->direction == 1) { p1 = (Process*)v1; p2 = (Process*)v2; } else { @@ -778,7 +723,7 @@ long Process_compare(const void* v1, const void* v2) { p1 = (Process*)v2; } long long diff; - switch (pl->sortKey) { + switch (settings->sortKey) { case PID: return (p1->pid - p2->pid); case PPID: diff --git a/Process.h b/Process.h index 6bd40686..489769c7 100644 --- a/Process.h +++ b/Process.h @@ -9,9 +9,6 @@ Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ -#ifdef HAVE_LIBHWLOC -#endif - // On Linux, this works only with glibc 2.1+. On earlier versions // the behavior is similar to have a hardcoded page size. #ifndef PAGE_SIZE @@ -20,7 +17,6 @@ in the source distribution for its full text. #define PAGE_SIZE_KB ( PAGE_SIZE / ONE_K ) #include "Object.h" -#include "Affinity.h" #include @@ -67,12 +63,10 @@ typedef enum ProcessField_ { LAST_PROCESSFIELD } ProcessField; -struct ProcessList_; - typedef struct Process_ { Object super; - struct ProcessList_ *pl; + struct Settings_* settings; pid_t pid; char* comm; @@ -112,10 +106,10 @@ typedef struct Process_ { unsigned long long io_read_bytes; unsigned long long io_write_bytes; unsigned long long io_cancelled_write_bytes; - double io_rate_read_bps; unsigned long long io_rate_read_time; - double io_rate_write_bps; unsigned long long io_rate_write_time; + double io_rate_read_bps; + double io_rate_write_bps; #endif int processor; @@ -171,12 +165,18 @@ typedef struct Process_ { } Process; +typedef struct ProcessFieldData_ { + const char* name; + const char* title; + const char* description; + int flags; +} ProcessFieldData; -extern const char *Process_fieldNames[]; +void Process_writeField(Process* this, RichString* str, ProcessField field); +long Process_compare(const void* v1, const void* v2); -extern const int Process_fieldFlags[]; -extern const char *Process_fieldTitles[]; +extern ProcessFieldData Process_fields[]; void Process_setupColumnWidths(); @@ -189,11 +189,13 @@ void Process_setupColumnWidths(); #define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K) #define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K) +void Process_writeDefaultField(Process* this, RichString* str, ProcessField field); + void Process_delete(Object* cast); extern ObjectClass Process_class; -Process* Process_new(struct ProcessList_ *pl); +Process* Process_new(struct Settings_* settings); void Process_toggleTag(Process* this); @@ -201,24 +203,10 @@ bool Process_setPriority(Process* this, int priority); bool Process_changePriorityBy(Process* this, size_t delta); -#ifdef HAVE_LIBHWLOC - -Affinity* Process_getAffinity(Process* this); - -bool Process_setAffinity(Process* this, Affinity* affinity); - -#elif HAVE_NATIVE_AFFINITY - -Affinity* Process_getAffinity(Process* this); - -bool Process_setAffinity(Process* this, Affinity* affinity); - -#endif - void Process_sendSignal(Process* this, size_t sgn); long Process_pidCompare(const void* v1, const void* v2); -long Process_compare(const void* v1, const void* v2); +long Process_defaultCompare(const void* v1, const void* v2); #endif diff --git a/ProcessList.c b/ProcessList.c index d164fa05..74c3302a 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -19,6 +19,7 @@ in the source distribution for its full text. #include "UsersTable.h" #include "Panel.h" #include "Process.h" +#include "Settings.h" #ifndef MAX_NAME #define MAX_NAME 128 @@ -28,51 +29,9 @@ in the source distribution for its full text. #define MAX_READ 2048 #endif -#ifndef ProcessList_cpuId -#define ProcessList_cpuId(pl, cpu) ((pl)->countCPUsFromZero ? (cpu) : (cpu)+1) -#endif - -typedef enum TreeStr_ { - TREE_STR_HORZ, - TREE_STR_VERT, - TREE_STR_RTEE, - TREE_STR_BEND, - TREE_STR_TEND, - TREE_STR_OPEN, - TREE_STR_SHUT, - TREE_STR_COUNT -} TreeStr; - -typedef struct CPUData_ { - unsigned long long int totalTime; - unsigned long long int userTime; - unsigned long long int systemTime; - unsigned long long int systemAllTime; - unsigned long long int idleAllTime; - unsigned long long int idleTime; - unsigned long long int niceTime; - unsigned long long int ioWaitTime; - unsigned long long int irqTime; - unsigned long long int softIrqTime; - unsigned long long int stealTime; - unsigned long long int guestTime; - - unsigned long long int totalPeriod; - unsigned long long int userPeriod; - unsigned long long int systemPeriod; - unsigned long long int systemAllPeriod; - unsigned long long int idleAllPeriod; - unsigned long long int idlePeriod; - unsigned long long int nicePeriod; - unsigned long long int ioWaitPeriod; - unsigned long long int irqPeriod; - unsigned long long int softIrqPeriod; - unsigned long long int stealPeriod; - unsigned long long int guestPeriod; -} CPUData; - typedef struct ProcessList_ { - const char **treeStr; + Settings* settings; + Vector* processes; Vector* processes2; Hashtable* processTable; @@ -84,90 +43,33 @@ typedef struct ProcessList_ { const char* incFilter; Hashtable* pidWhiteList; - int cpuCount; - int totalTasks; - int userlandThreads; - int kernelThreads; - int runningTasks; - #ifdef HAVE_LIBHWLOC hwloc_topology_t topology; bool topologyOk; #endif - CPUData* cpus; - unsigned long long int totalMem; - unsigned long long int usedMem; - unsigned long long int freeMem; - unsigned long long int sharedMem; - unsigned long long int buffersMem; - unsigned long long int cachedMem; - unsigned long long int totalSwap; - unsigned long long int usedSwap; - unsigned long long int freeSwap; - - int flags; - ProcessField* fields; - ProcessField sortKey; - int direction; - bool hideThreads; - bool shadowOtherUsers; - bool showThreadNames; - bool showingThreadNames; - bool hideKernelThreads; - bool hideUserlandThreads; - bool treeView; - bool highlightBaseName; - bool highlightMegabytes; - bool highlightThreads; - bool detailedCPUTime; - bool countCPUsFromZero; - bool updateProcessNames; - bool accountGuestInCPUMeter; - bool userOnly; + int cpuCount; } ProcessList; -ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList); +ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList, uid_t userId); void ProcessList_delete(ProcessList* pl); void ProcessList_scan(ProcessList* pl); }*/ -static ProcessField defaultHeaders[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; - -const char *ProcessList_treeStrAscii[TREE_STR_COUNT] = { - "-", // TREE_STR_HORZ - "|", // TREE_STR_VERT - "`", // TREE_STR_RTEE - "`", // TREE_STR_BEND - ",", // TREE_STR_TEND - "+", // TREE_STR_OPEN - "-", // TREE_STR_SHUT -}; - -const char *ProcessList_treeStrUtf8[TREE_STR_COUNT] = { - "\xe2\x94\x80", // TREE_STR_HORZ ─ - "\xe2\x94\x82", // TREE_STR_VERT │ - "\xe2\x94\x9c", // TREE_STR_RTEE ├ - "\xe2\x94\x94", // TREE_STR_BEND └ - "\xe2\x94\x8c", // TREE_STR_TEND ┌ - "+", // TREE_STR_OPEN + - "\xe2\x94\x80", // TREE_STR_SHUT ─ -}; - -ProcessList* ProcessList_init(ProcessList* this, UsersTable* usersTable, Hashtable* pidWhiteList) { +ProcessList* ProcessList_init(ProcessList* this, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { this->processes = Vector_new(Class(Process), true, DEFAULT_SIZE); this->processTable = Hashtable_new(140, false); this->usersTable = usersTable; this->pidWhiteList = pidWhiteList; + this->userId = userId; // tree-view auxiliary buffers this->processes2 = Vector_new(Class(Process), true, DEFAULT_SIZE); // set later by platform-specific code this->cpuCount = 0; - this->cpus = NULL; #ifdef HAVE_LIBHWLOC this->topologyOk = false; @@ -180,35 +82,8 @@ ProcessList* ProcessList_init(ProcessList* this, UsersTable* usersTable, Hashtab } #endif - this->fields = calloc(LAST_PROCESSFIELD+1, sizeof(ProcessField)); - // TODO: turn 'fields' into a Vector, - // (and ProcessFields into proper objects). - this->flags = 0; - for (int i = 0; defaultHeaders[i]; i++) { - this->fields[i] = defaultHeaders[i]; - this->flags |= Process_fieldFlags[defaultHeaders[i]]; - } - - this->sortKey = PERCENT_CPU; - this->direction = 1; - this->hideThreads = false; - this->shadowOtherUsers = false; - this->showThreadNames = false; - this->showingThreadNames = false; - this->hideKernelThreads = false; - this->hideUserlandThreads = false; - this->treeView = false; - this->highlightBaseName = false; - this->highlightMegabytes = false; - this->detailedCPUTime = false; - this->countCPUsFromZero = false; - this->updateProcessNames = false; - this->treeStr = NULL; this->following = -1; - if (CRT_utf8) - this->treeStr = CRT_utf8 ? ProcessList_treeStrUtf8 : ProcessList_treeStrAscii; - return this; } @@ -216,27 +91,19 @@ void ProcessList_done(ProcessList* this) { Hashtable_delete(this->processTable); Vector_delete(this->processes); Vector_delete(this->processes2); - free(this->cpus); - free(this->fields); } void ProcessList_setPanel(ProcessList* this, Panel* panel) { this->panel = panel; } -void ProcessList_invertSortOrder(ProcessList* this) { - if (this->direction == 1) - this->direction = -1; - else - this->direction = 1; -} - void ProcessList_printHeader(ProcessList* this, RichString* header) { RichString_prune(header); - ProcessField* fields = this->fields; + ProcessField* fields = this->settings->fields; for (int i = 0; fields[i]; i++) { - const char* field = Process_fieldTitles[fields[i]]; - if (!this->treeView && this->sortKey == fields[i]) + const char* field = Process_fields[fields[i]].title; + if (!field) field = "- "; + if (!this->settings->treeView && this->settings->sortKey == fields[i]) RichString_append(header, CRT_colors[PANEL_HIGHLIGHT_FOCUS], field); else RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field); @@ -308,19 +175,19 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i } void ProcessList_sort(ProcessList* this) { - if (!this->treeView) { + if (!this->settings->treeView) { Vector_insertionSort(this->processes); } else { // Save settings - int direction = this->direction; - int sortKey = this->sortKey; + int direction = this->settings->direction; + int sortKey = this->settings->sortKey; // Sort by PID - this->sortKey = PID; - this->direction = 1; + this->settings->sortKey = PID; + this->settings->direction = 1; Vector_quickSort(this->processes); // Restore settings - this->sortKey = sortKey; - this->direction = direction; + this->settings->sortKey = sortKey; + this->settings->direction = direction; // Take PID 1 as root and add to the new listing int vsize = Vector_size(this->processes); Process* init = (Process*) (Vector_take(this->processes, 0)); @@ -351,10 +218,12 @@ void ProcessList_sort(ProcessList* this) { ProcessField ProcessList_keyAt(ProcessList* this, int at) { int x = 0; - ProcessField* fields = this->fields; + ProcessField* fields = this->settings->fields; ProcessField field; for (int i = 0; (field = fields[i]); i++) { - int len = strlen(Process_fieldTitles[field]); + const char* title = Process_fields[field].title; + if (!title) title = "- "; + int len = strlen(title); if (at >= x && at <= x + len) { return field; } @@ -371,17 +240,12 @@ void ProcessList_expandTree(ProcessList* this) { } } -void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, const char* incFilter) { - if (!flags) { - following = this->following; - incFilter = this->incFilter; - } else { - this->following = following; - this->incFilter = incFilter; - } +void ProcessList_rebuildPanel(ProcessList* this) { + int following = this->following; + const char* incFilter = this->incFilter; int currPos = Panel_getSelectedIndex(this->panel); - pid_t currPid = following != -1 ? following : 0; + pid_t currPid = this->following != -1 ? this->following : 0; int currScrollV = this->panel->scrollV; Panel_prune(this->panel); @@ -392,14 +256,14 @@ void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, cons Process* p = ProcessList_get(this, i); if ( (!p->show) - || (this->userOnly && (p->st_uid != this->userId)) + || (this->userId != (uid_t) -1 && (p->st_uid != this->userId)) || (incFilter && !(String_contains_i(p->comm, incFilter))) || (this->pidWhiteList && !Hashtable_get(this->pidWhiteList, p->pid)) ) hidden = true; if (!hidden) { Panel_set(this->panel, idx, (Object*)p); - if ((following == -1 && idx == currPos) || (following != -1 && p->pid == currPid)) { + if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == currPid)) { Panel_setSelected(this->panel, idx); this->panel->scrollV = currScrollV; } diff --git a/ProcessList.h b/ProcessList.h index 916039c3..e4c266c6 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -14,6 +14,7 @@ in the source distribution for its full text. #include "UsersTable.h" #include "Panel.h" #include "Process.h" +#include "Settings.h" #ifndef MAX_NAME #define MAX_NAME 128 @@ -23,51 +24,9 @@ in the source distribution for its full text. #define MAX_READ 2048 #endif -#ifndef ProcessList_cpuId -#define ProcessList_cpuId(pl, cpu) ((pl)->countCPUsFromZero ? (cpu) : (cpu)+1) -#endif - -typedef enum TreeStr_ { - TREE_STR_HORZ, - TREE_STR_VERT, - TREE_STR_RTEE, - TREE_STR_BEND, - TREE_STR_TEND, - TREE_STR_OPEN, - TREE_STR_SHUT, - TREE_STR_COUNT -} TreeStr; - -typedef struct CPUData_ { - unsigned long long int totalTime; - unsigned long long int userTime; - unsigned long long int systemTime; - unsigned long long int systemAllTime; - unsigned long long int idleAllTime; - unsigned long long int idleTime; - unsigned long long int niceTime; - unsigned long long int ioWaitTime; - unsigned long long int irqTime; - unsigned long long int softIrqTime; - unsigned long long int stealTime; - unsigned long long int guestTime; - - unsigned long long int totalPeriod; - unsigned long long int userPeriod; - unsigned long long int systemPeriod; - unsigned long long int systemAllPeriod; - unsigned long long int idleAllPeriod; - unsigned long long int idlePeriod; - unsigned long long int nicePeriod; - unsigned long long int ioWaitPeriod; - unsigned long long int irqPeriod; - unsigned long long int softIrqPeriod; - unsigned long long int stealPeriod; - unsigned long long int guestPeriod; -} CPUData; - typedef struct ProcessList_ { - const char **treeStr; + Settings* settings; + Vector* processes; Vector* processes2; Hashtable* processTable; @@ -79,67 +38,26 @@ typedef struct ProcessList_ { const char* incFilter; Hashtable* pidWhiteList; - int cpuCount; - int totalTasks; - int userlandThreads; - int kernelThreads; - int runningTasks; - #ifdef HAVE_LIBHWLOC hwloc_topology_t topology; bool topologyOk; #endif - CPUData* cpus; - unsigned long long int totalMem; - unsigned long long int usedMem; - unsigned long long int freeMem; - unsigned long long int sharedMem; - unsigned long long int buffersMem; - unsigned long long int cachedMem; - unsigned long long int totalSwap; - unsigned long long int usedSwap; - unsigned long long int freeSwap; - - int flags; - ProcessField* fields; - ProcessField sortKey; - int direction; - bool hideThreads; - bool shadowOtherUsers; - bool showThreadNames; - bool showingThreadNames; - bool hideKernelThreads; - bool hideUserlandThreads; - bool treeView; - bool highlightBaseName; - bool highlightMegabytes; - bool highlightThreads; - bool detailedCPUTime; - bool countCPUsFromZero; - bool updateProcessNames; - bool accountGuestInCPUMeter; - bool userOnly; + int cpuCount; } ProcessList; -ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList); +ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList, uid_t userId); void ProcessList_delete(ProcessList* pl); void ProcessList_scan(ProcessList* pl); -extern const char *ProcessList_treeStrAscii[TREE_STR_COUNT]; - -extern const char *ProcessList_treeStrUtf8[TREE_STR_COUNT]; - -ProcessList* ProcessList_init(ProcessList* this, UsersTable* usersTable, Hashtable* pidWhiteList); +ProcessList* ProcessList_init(ProcessList* this, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); void ProcessList_done(ProcessList* this); void ProcessList_setPanel(ProcessList* this, Panel* panel); -void ProcessList_invertSortOrder(ProcessList* this); - void ProcessList_printHeader(ProcessList* this, RichString* header); void ProcessList_add(ProcessList* this, Process* p); @@ -156,7 +74,7 @@ ProcessField ProcessList_keyAt(ProcessList* this, int at); void ProcessList_expandTree(ProcessList* this); -void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, const char* incFilter); +void ProcessList_rebuildPanel(ProcessList* this); #endif diff --git a/ScreenManager.c b/ScreenManager.c index e7a0872a..aad1b024 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -6,8 +6,8 @@ in the source distribution for its full text. */ #include "ScreenManager.h" +#include "ProcessList.h" -#include "Panel.h" #include "Object.h" #include @@ -19,6 +19,8 @@ in the source distribution for its full text. #include "FunctionBar.h" #include "Vector.h" #include "Header.h" +#include "Settings.h" +#include "Panel.h" typedef enum Orientation_ { VERTICAL, @@ -36,14 +38,14 @@ typedef struct ScreenManager_ { int panelCount; const FunctionBar* fuBar; const Header* header; - time_t lastScan; + const Settings* settings; bool owner; bool allowFocusChange; } ScreenManager; }*/ -ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, bool owner) { +ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner) { ScreenManager* this; this = malloc(sizeof(ScreenManager)); this->x1 = x1; @@ -56,6 +58,7 @@ ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation ori this->fuBars = Vector_new(Class(FunctionBar), true, DEFAULT_SIZE); this->panelCount = 0; this->header = header; + this->settings = settings; this->owner = owner; this->allowFocusChange = true; return this; @@ -130,37 +133,68 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { Panel* panelFocus = (Panel*) Vector_get(this->panels, focus); if (this->fuBar) FunctionBar_draw(this->fuBar, NULL); - - this->lastScan = 0; - int ch = 0; + struct timeval tv; + double oldTime = 0.0; + + int ch = ERR; + int closeTimeout = 0; + + bool drawPanel = true; + bool timeToRecalculate = true; + bool doRefresh = true; + bool forceRecalculate = false; + int sortTimeout = 0; + int resetSortTimeout = 5; + while (!quit) { int panels = this->panelCount; if (this->header) { - time_t now = time(NULL); - if (now > this->lastScan) { - ProcessList_scan(this->header->pl); - ProcessList_sort(this->header->pl); - this->lastScan = now; + gettimeofday(&tv, NULL); + double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000); + timeToRecalculate = (newTime - oldTime > this->settings->delay); + if (newTime < oldTime) timeToRecalculate = true; // clock was adjusted? + + if (timeToRecalculate) { + Header_draw(this->header); + oldTime = newTime; } - Header_draw(this->header); - ProcessList_rebuildPanel(this->header->pl, false, false, NULL); + + if (doRefresh) { + if (timeToRecalculate || forceRecalculate) { + ProcessList_scan(this->header->pl); + forceRecalculate = false; + } + if (sortTimeout == 0 || this->settings->treeView) { + ProcessList_sort(this->header->pl); + sortTimeout = 1; + } + //this->header->pl->incFilter = IncSet_filter(inc); + ProcessList_rebuildPanel(this->header->pl); + drawPanel = true; + } + doRefresh = true; } - for (int i = 0; i < panels; i++) { - Panel* panel = (Panel*) Vector_get(this->panels, i); - Panel_draw(panel, i == focus); - if (i < panels) { - if (this->orientation == HORIZONTAL) { - mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1); + + if (drawPanel) { + for (int i = 0; i < panels; i++) { + Panel* panel = (Panel*) Vector_get(this->panels, i); + Panel_draw(panel, i == focus); + if (i < panels) { + if (this->orientation == HORIZONTAL) { + mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1); + } } } } + FunctionBar* bar = (FunctionBar*) Vector_get(this->fuBars, focus); if (bar) this->fuBar = bar; if (this->fuBar) FunctionBar_draw(this->fuBar, NULL); + int prevCh = ch; ch = getch(); if (ch == KEY_MOUSE) { @@ -187,17 +221,36 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { if (Panel_eventHandlerFn(panelFocus)) { HandlerResult result = Panel_eventHandler(panelFocus, ch); - if (result == HANDLED) { + if (result & REFRESH) { + doRefresh = true; + sortTimeout = 0; + } + if (result & RECALCULATE) { + forceRecalculate = true; + sortTimeout = 0; + } + if (result & HANDLED) { continue; - } else if (result == BREAK_LOOP) { + } else if (result & BREAK_LOOP) { quit = true; continue; } } - switch (ch) { - case ERR: + if (ch == ERR) { + if (prevCh == ch && !timeToRecalculate) { + closeTimeout++; + if (closeTimeout == 100) { + break; + } + } else + closeTimeout = 0; + drawPanel = false; continue; + } + drawPanel = true; + + switch (ch) { case KEY_RESIZE: { ScreenManager_resize(this, this->x1, this->y1, this->x2, this->y2); @@ -237,6 +290,8 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { } } - *lastFocus = panelFocus; - *lastKey = ch; + if (lastFocus) + *lastFocus = panelFocus; + if (lastKey) + *lastKey = ch; } diff --git a/ScreenManager.h b/ScreenManager.h index 5179fe97..8d364ab5 100644 --- a/ScreenManager.h +++ b/ScreenManager.h @@ -12,6 +12,8 @@ in the source distribution for its full text. #include "FunctionBar.h" #include "Vector.h" #include "Header.h" +#include "Settings.h" +#include "Panel.h" typedef enum Orientation_ { VERTICAL, @@ -29,13 +31,13 @@ typedef struct ScreenManager_ { int panelCount; const FunctionBar* fuBar; const Header* header; - time_t lastScan; + const Settings* settings; bool owner; bool allowFocusChange; } ScreenManager; -ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, bool owner); +ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner); void ScreenManager_delete(ScreenManager* this); diff --git a/Settings.c b/Settings.c index 1dc8b1b2..e78f9918 100644 --- a/Settings.c +++ b/Settings.c @@ -18,64 +18,138 @@ in the source distribution for its full text. #define DEFAULT_DELAY 15 /*{ -#include "ProcessList.h" -#include "Header.h" +#include "Process.h" #include +typedef struct { + int len; + char** names; + int* modes; +} MeterColumnSettings; + typedef struct Settings_ { - char* userSettings; - ProcessList* pl; - Header* header; + char* filename; + + MeterColumnSettings columns[2]; + + ProcessField* fields; + int flags; int colorScheme; int delay; + + int direction; + ProcessField sortKey; + + bool countCPUsFromZero; + bool detailedCPUTime; + bool treeView; + bool hideThreads; + bool shadowOtherUsers; + bool showThreadNames; + bool hideKernelThreads; + bool hideUserlandThreads; + bool highlightBaseName; + bool highlightMegabytes; + bool highlightThreads; + bool updateProcessNames; + bool accountGuestInCPUMeter; + bool headerMargin; + bool changed; } Settings; +#ifndef Settings_cpuId +#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromZero ? (cpu) : (cpu)+1) +#endif + }*/ +static ProcessField defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; + +//static ProcessField defaultIoFields[] = { PID, IO_PRIORITY, USER, IO_READ_RATE, IO_WRITE_RATE, IO_RATE, COMM, 0 }; + void Settings_delete(Settings* this) { - free(this->userSettings); + free(this->filename); + free(this->fields); + for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) { + String_freeArray(this->columns[i].names); + free(this->columns[i].modes); + } free(this); } -static void Settings_readMeters(Settings* this, char* line, HeaderSide side) { +static void Settings_readMeters(Settings* this, char* line, int column) { char* trim = String_trim(line); int nIds; char** ids = String_split(trim, ' ', &nIds); free(trim); - for (int i = 0; ids[i]; i++) { - Header_createMeter(this->header, ids[i], side); - } - String_freeArray(ids); + this->columns[column].names = ids; } -static void Settings_readMeterModes(Settings* this, char* line, HeaderSide side) { +static void Settings_readMeterModes(Settings* this, char* line, int column) { char* trim = String_trim(line); int nIds; char** ids = String_split(trim, ' ', &nIds); free(trim); + int len = 0; for (int i = 0; ids[i]; i++) { - int mode = atoi(ids[i]); - Header_setMode(this->header, i, mode, side); + len++; + } + this->columns[column].len = len; + int* modes = calloc(len, sizeof(int)); + for (int i = 0; i < len; i++) { + modes[i] = atoi(ids[i]); } String_freeArray(ids); + this->columns[column].modes = modes; } -static void Settings_defaultMeters(Header* header, int cpuCount) { +static void Settings_defaultMeters(Settings* this, int cpuCount) { + int sizes[] = { 3, 3 }; + if (cpuCount > 4) { + sizes[1]++; + } + for (int i = 0; i < 2; i++) { + this->columns[i].names = calloc(sizes[i], sizeof(char*)); + this->columns[i].modes = calloc(sizes[i], sizeof(int)); + } + + int r = 0; if (cpuCount > 8) { - Header_createMeter(header, "LeftCPUs2", LEFT_HEADER); - Header_createMeter(header, "RightCPUs2", RIGHT_HEADER); + this->columns[0].names[0] = strdup("LeftCPUs2"); + this->columns[1].names[r++] = strdup("RightCPUs2"); } else if (cpuCount > 4) { - Header_createMeter(header, "LeftCPUs", LEFT_HEADER); - Header_createMeter(header, "RightCPUs", RIGHT_HEADER); + this->columns[0].names[0] = strdup("LeftCPUs"); + this->columns[1].names[r++] = strdup("RightCPUs"); } else { - Header_createMeter(header, "AllCPUs", LEFT_HEADER); + this->columns[0].names[0] = strdup("AllCPUs"); } - Header_createMeter(header, "Memory", LEFT_HEADER); - Header_createMeter(header, "Swap", LEFT_HEADER); - Header_createMeter(header, "Tasks", RIGHT_HEADER); - Header_createMeter(header, "LoadAverage", RIGHT_HEADER); - Header_createMeter(header, "Uptime", RIGHT_HEADER); + this->columns[0].names[1] = strdup("Memory"); + this->columns[0].names[2] = strdup("Swap"); + + this->columns[1].names[r++] = strdup("Tasks"); + this->columns[1].names[r++] = strdup("LoadAverage"); + this->columns[1].names[r++] = strdup("Uptime"); +} + +static void readFields(ProcessField* fields, int* flags, const char* line) { + char* trim = String_trim(line); + int nIds; + char** ids = String_split(trim, ' ', &nIds); + free(trim); + int i, j; + *flags = 0; + for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i]; i++) { + // This "+1" is for compatibility with the older enum format. + int id = atoi(ids[i]) + 1; + if (id > 0 && id < LAST_PROCESSFIELD) { + fields[j] = id; + *flags |= Process_fields[id].flags; + j++; + } + } + fields[j] = (ProcessField) NULL; + String_freeArray(ids); } static bool Settings_read(Settings* this, const char* fileName, int cpuCount) { @@ -94,59 +168,43 @@ static bool Settings_read(Settings* this, const char* fileName, int cpuCount) { continue; } if (String_eq(option[0], "fields")) { - char* trim = String_trim(option[1]); - int nIds; - char** ids = String_split(trim, ' ', &nIds); - free(trim); - int i, j; - this->pl->flags = 0; - for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i]; i++) { - // This "+1" is for compatibility with the older enum format. - int id = atoi(ids[i]) + 1; - if (id > 0 && id < LAST_PROCESSFIELD) { - this->pl->fields[j] = id; - this->pl->flags |= Process_fieldFlags[id]; - j++; - } - } - this->pl->fields[j] = (ProcessField) NULL; - String_freeArray(ids); + readFields(this->fields, &(this->flags), option[1]); } else if (String_eq(option[0], "sort_key")) { // This "+1" is for compatibility with the older enum format. - this->pl->sortKey = atoi(option[1]) + 1; + this->sortKey = atoi(option[1]) + 1; } else if (String_eq(option[0], "sort_direction")) { - this->pl->direction = atoi(option[1]); + this->direction = atoi(option[1]); } else if (String_eq(option[0], "tree_view")) { - this->pl->treeView = atoi(option[1]); + this->treeView = atoi(option[1]); } else if (String_eq(option[0], "hide_threads")) { - this->pl->hideThreads = atoi(option[1]); + this->hideThreads = atoi(option[1]); } else if (String_eq(option[0], "hide_kernel_threads")) { - this->pl->hideKernelThreads = atoi(option[1]); + this->hideKernelThreads = atoi(option[1]); } else if (String_eq(option[0], "hide_userland_threads")) { - this->pl->hideUserlandThreads = atoi(option[1]); + this->hideUserlandThreads = atoi(option[1]); } else if (String_eq(option[0], "shadow_other_users")) { - this->pl->shadowOtherUsers = atoi(option[1]); + this->shadowOtherUsers = atoi(option[1]); } else if (String_eq(option[0], "show_thread_names")) { - this->pl->showThreadNames = atoi(option[1]); + this->showThreadNames = atoi(option[1]); } else if (String_eq(option[0], "highlight_base_name")) { - this->pl->highlightBaseName = atoi(option[1]); + this->highlightBaseName = atoi(option[1]); } else if (String_eq(option[0], "highlight_megabytes")) { - this->pl->highlightMegabytes = atoi(option[1]); + this->highlightMegabytes = atoi(option[1]); } else if (String_eq(option[0], "highlight_threads")) { - this->pl->highlightThreads = atoi(option[1]); + this->highlightThreads = atoi(option[1]); } else if (String_eq(option[0], "header_margin")) { - this->header->margin = atoi(option[1]); + this->headerMargin = atoi(option[1]); } else if (String_eq(option[0], "expand_system_time")) { // Compatibility option. - this->pl->detailedCPUTime = atoi(option[1]); + this->detailedCPUTime = atoi(option[1]); } else if (String_eq(option[0], "detailed_cpu_time")) { - this->pl->detailedCPUTime = atoi(option[1]); + this->detailedCPUTime = atoi(option[1]); } else if (String_eq(option[0], "cpu_count_from_zero")) { - this->pl->countCPUsFromZero = atoi(option[1]); + this->countCPUsFromZero = atoi(option[1]); } else if (String_eq(option[0], "update_process_names")) { - this->pl->updateProcessNames = atoi(option[1]); + this->updateProcessNames = atoi(option[1]); } else if (String_eq(option[0], "account_guest_in_cpu_meter")) { - this->pl->accountGuestInCPUMeter = atoi(option[1]); + this->accountGuestInCPUMeter = atoi(option[1]); } else if (String_eq(option[0], "delay")) { this->delay = atoi(option[1]); } else if (String_eq(option[0], "color_scheme")) { @@ -154,96 +212,118 @@ static bool Settings_read(Settings* this, const char* fileName, int cpuCount) { if (this->colorScheme < 0) this->colorScheme = 0; if (this->colorScheme > 5) this->colorScheme = 5; } else if (String_eq(option[0], "left_meters")) { - Settings_readMeters(this, option[1], LEFT_HEADER); + Settings_readMeters(this, option[1], 0); readMeters = true; } else if (String_eq(option[0], "right_meters")) { - Settings_readMeters(this, option[1], RIGHT_HEADER); + Settings_readMeters(this, option[1], 1); readMeters = true; } else if (String_eq(option[0], "left_meter_modes")) { - Settings_readMeterModes(this, option[1], LEFT_HEADER); + Settings_readMeterModes(this, option[1], 0); readMeters = true; } else if (String_eq(option[0], "right_meter_modes")) { - Settings_readMeterModes(this, option[1], RIGHT_HEADER); + Settings_readMeterModes(this, option[1], 1); readMeters = true; } String_freeArray(option); } fclose(fd); if (!readMeters) { - Settings_defaultMeters(this->header, cpuCount); + Settings_defaultMeters(this, cpuCount); } return true; } +static void writeFields(FILE* fd, ProcessField* fields, const char* name) { + fprintf(fd, "%s=", name); + for (int i = 0; fields[i]; i++) { + // This "-1" is for compatibility with the older enum format. + fprintf(fd, "%d ", (int) fields[i]-1); + } + fprintf(fd, "\n"); +} + +static void writeMeters(Settings* this, FILE* fd, int column) { + for (int i = 0; i < this->columns[column].len; i++) { + fprintf(fd, "%s ", this->columns[column].names[i]); + } + fprintf(fd, "\n"); +} + +static void writeMeterModes(Settings* this, FILE* fd, int column) { + for (int i = 0; i < this->columns[column].len; i++) { + fprintf(fd, "%d ", this->columns[column].modes[i]); + } + fprintf(fd, "\n"); +} + bool Settings_write(Settings* this) { - // TODO: implement File object and make - // file I/O object-oriented. FILE* fd; - fd = fopen(this->userSettings, "w"); + fd = fopen(this->filename, "w"); if (fd == NULL) { return false; } fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n"); fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n"); - fprintf(fd, "fields="); - for (int i = 0; this->pl->fields[i]; i++) { - // This "-1" is for compatibility with the older enum format. - fprintf(fd, "%d ", (int) this->pl->fields[i]-1); - } - fprintf(fd, "\n"); + writeFields(fd, this->fields, "fields"); // This "-1" is for compatibility with the older enum format. - fprintf(fd, "sort_key=%d\n", (int) this->pl->sortKey-1); - fprintf(fd, "sort_direction=%d\n", (int) this->pl->direction); - fprintf(fd, "hide_threads=%d\n", (int) this->pl->hideThreads); - fprintf(fd, "hide_kernel_threads=%d\n", (int) this->pl->hideKernelThreads); - fprintf(fd, "hide_userland_threads=%d\n", (int) this->pl->hideUserlandThreads); - fprintf(fd, "shadow_other_users=%d\n", (int) this->pl->shadowOtherUsers); - fprintf(fd, "show_thread_names=%d\n", (int) this->pl->showThreadNames); - fprintf(fd, "highlight_base_name=%d\n", (int) this->pl->highlightBaseName); - fprintf(fd, "highlight_megabytes=%d\n", (int) this->pl->highlightMegabytes); - fprintf(fd, "highlight_threads=%d\n", (int) this->pl->highlightThreads); - fprintf(fd, "tree_view=%d\n", (int) this->pl->treeView); - fprintf(fd, "header_margin=%d\n", (int) this->header->margin); - fprintf(fd, "detailed_cpu_time=%d\n", (int) this->pl->detailedCPUTime); - fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->pl->countCPUsFromZero); - fprintf(fd, "update_process_names=%d\n", (int) this->pl->updateProcessNames); - fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->pl->accountGuestInCPUMeter); + fprintf(fd, "sort_key=%d\n", (int) this->sortKey-1); + fprintf(fd, "sort_direction=%d\n", (int) this->direction); + fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads); + fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads); + fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads); + fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers); + fprintf(fd, "show_thread_names=%d\n", (int) this->showThreadNames); + fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName); + fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes); + fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads); + fprintf(fd, "tree_view=%d\n", (int) this->treeView); + fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); + fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); + fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero); + fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames); + fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter); fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme); fprintf(fd, "delay=%d\n", (int) this->delay); - fprintf(fd, "left_meters="); - for (int i = 0; i < Header_size(this->header, LEFT_HEADER); i++) { - char* name = Header_readMeterName(this->header, i, LEFT_HEADER); - fprintf(fd, "%s ", name); - free(name); - } - fprintf(fd, "\n"); - fprintf(fd, "left_meter_modes="); - for (int i = 0; i < Header_size(this->header, LEFT_HEADER); i++) - fprintf(fd, "%d ", Header_readMeterMode(this->header, i, LEFT_HEADER)); - fprintf(fd, "\n"); - fprintf(fd, "right_meters="); - for (int i = 0; i < Header_size(this->header, RIGHT_HEADER); i++) { - char* name = Header_readMeterName(this->header, i, RIGHT_HEADER); - fprintf(fd, "%s ", name); - free(name); - } - fprintf(fd, "\n"); - fprintf(fd, "right_meter_modes="); - for (int i = 0; i < Header_size(this->header, RIGHT_HEADER); i++) - fprintf(fd, "%d ", Header_readMeterMode(this->header, i, RIGHT_HEADER)); - fprintf(fd, "\n"); + fprintf(fd, "left_meters="); writeMeters(this, fd, 0); + fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0); + fprintf(fd, "right_meters="); writeMeters(this, fd, 1); + fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1); fclose(fd); return true; } -Settings* Settings_new(ProcessList* pl, Header* header, int cpuCount) { - Settings* this = malloc(sizeof(Settings)); - this->pl = pl; - this->header = header; +Settings* Settings_new(int cpuCount) { + + Settings* this = calloc(1, sizeof(Settings)); + + this->sortKey = PERCENT_CPU; + this->direction = 1; + this->hideThreads = false; + this->shadowOtherUsers = false; + this->showThreadNames = false; + this->hideKernelThreads = false; + this->hideUserlandThreads = false; + this->treeView = false; + this->highlightBaseName = false; + this->highlightMegabytes = false; + this->detailedCPUTime = false; + this->countCPUsFromZero = false; + this->updateProcessNames = false; + + this->fields = calloc(LAST_PROCESSFIELD+1, sizeof(ProcessField)); + // TODO: turn 'fields' into a Vector, + // (and ProcessFields into proper objects). + this->flags = 0; + ProcessField* defaults = defaultFields; + for (int i = 0; defaults[i]; i++) { + this->fields[i] = defaults[i]; + this->flags |= Process_fields[defaults[i]].flags; + } + char* legacyDotfile = NULL; char* rcfile = getenv("HTOPRC"); if (rcfile) { - this->userSettings = strdup(rcfile); + this->filename = strdup(rcfile); } else { const char* home = getenv("HOME"); if (!home) home = ""; @@ -251,11 +331,11 @@ Settings* Settings_new(ProcessList* pl, Header* header, int cpuCount) { char* configDir = NULL; char* htopDir = NULL; if (xdgConfigHome) { - this->userSettings = String_cat(xdgConfigHome, "/htop/htoprc"); + this->filename = String_cat(xdgConfigHome, "/htop/htoprc"); configDir = strdup(xdgConfigHome); htopDir = String_cat(xdgConfigHome, "/htop"); } else { - this->userSettings = String_cat(home, "/.config/htop/htoprc"); + this->filename = String_cat(home, "/.config/htop/htoprc"); configDir = String_cat(home, "/.config"); htopDir = String_cat(home, "/.config/htop"); } @@ -276,7 +356,7 @@ Settings* Settings_new(ProcessList* pl, Header* header, int cpuCount) { this->colorScheme = 0; this->changed = false; this->delay = DEFAULT_DELAY; - bool ok = Settings_read(this, legacyDotfile ? legacyDotfile : this->userSettings, cpuCount); + bool ok = Settings_read(this, legacyDotfile ? legacyDotfile : this->filename, cpuCount); if (ok) { if (legacyDotfile) { // Transition to new location and delete old configuration file @@ -290,12 +370,19 @@ Settings* Settings_new(ProcessList* pl, Header* header, int cpuCount) { ok = Settings_read(this, systemSettings, cpuCount); free(systemSettings); if (!ok) { - Settings_defaultMeters(this->header, cpuCount); - pl->hideKernelThreads = true; - pl->highlightMegabytes = true; - pl->highlightThreads = false; + Settings_defaultMeters(this, cpuCount); + this->hideKernelThreads = true; + this->highlightMegabytes = true; + this->highlightThreads = false; } } free(legacyDotfile); return this; } + +void Settings_invertSortOrder(Settings* this) { + if (this->direction == 1) + this->direction = -1; + else + this->direction = 1; +} diff --git a/Settings.h b/Settings.h index 28fe5520..2ac431f0 100644 --- a/Settings.h +++ b/Settings.h @@ -11,24 +11,57 @@ in the source distribution for its full text. #define DEFAULT_DELAY 15 -#include "ProcessList.h" -#include "Header.h" +#include "Process.h" #include +typedef struct { + int len; + char** names; + int* modes; +} MeterColumnSettings; + typedef struct Settings_ { - char* userSettings; - ProcessList* pl; - Header* header; + char* filename; + + MeterColumnSettings columns[2]; + + ProcessField* fields; + int flags; int colorScheme; int delay; + + int direction; + ProcessField sortKey; + + bool countCPUsFromZero; + bool detailedCPUTime; + bool treeView; + bool hideThreads; + bool shadowOtherUsers; + bool showThreadNames; + bool hideKernelThreads; + bool hideUserlandThreads; + bool highlightBaseName; + bool highlightMegabytes; + bool highlightThreads; + bool updateProcessNames; + bool accountGuestInCPUMeter; + bool headerMargin; + bool changed; } Settings; +#ifndef Settings_cpuId +#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromZero ? (cpu) : (cpu)+1) +#endif + void Settings_delete(Settings* this); bool Settings_write(Settings* this); -Settings* Settings_new(ProcessList* pl, Header* header, int cpuCount); +Settings* Settings_new(int cpuCount); + +void Settings_invertSortOrder(Settings* this); #endif diff --git a/SwapMeter.c b/SwapMeter.c index 2cb86d5e..b8bb1814 100644 --- a/SwapMeter.c +++ b/SwapMeter.c @@ -8,7 +8,7 @@ in the source distribution for its full text. #include "SwapMeter.h" #include "CRT.h" -#include "ProcessList.h" +#include "Platform.h" #include #include @@ -34,11 +34,8 @@ static void SwapMeter_humanNumber(char* buffer, const long int* value) { } static void SwapMeter_setValues(Meter* this, char* buffer, int len) { - long int usedSwap = this->pl->usedSwap; - this->total = this->pl->totalSwap; - this->values[0] = usedSwap; - - snprintf(buffer, len, "%ld/%ldMB", (long int) usedSwap / MEGABYTE, (long int) this->total / MEGABYTE); + Platform_setSwapValues(this); + snprintf(buffer, len, "%ld/%ldMB", (long int) this->values[0] / MEGABYTE, (long int) this->total / MEGABYTE); } static void SwapMeter_display(Object* cast, RichString* out) { diff --git a/TasksMeter.c b/TasksMeter.c index 22a125e5..adcc55d9 100644 --- a/TasksMeter.c +++ b/TasksMeter.c @@ -7,7 +7,7 @@ in the source distribution for its full text. #include "TasksMeter.h" -#include "ProcessList.h" +#include "Platform.h" #include "CRT.h" /*{ @@ -15,42 +15,43 @@ in the source distribution for its full text. }*/ int TasksMeter_attributes[] = { - TASKS_RUNNING + CPU_KERNEL, PROCESS_THREAD, PROCESS, TASKS_RUNNING }; static void TasksMeter_setValues(Meter* this, char* buffer, int len) { - ProcessList* pl = this->pl; - this->total = pl->totalTasks; - this->values[0] = pl->runningTasks; + Platform_setTasksValues(this); snprintf(buffer, len, "%d/%d", (int) this->values[0], (int) this->total); } static void TasksMeter_display(Object* cast, RichString* out) { Meter* this = (Meter*)cast; - ProcessList* pl = this->pl; + Settings* settings = this->pl->settings; char buffer[20]; - sprintf(buffer, "%d", (int)(this->total - pl->userlandThreads - pl->kernelThreads)); + + int processes = (int) this->values[2]; + + sprintf(buffer, "%d", processes); RichString_write(out, CRT_colors[METER_VALUE], buffer); int threadValueColor = CRT_colors[METER_VALUE]; int threadCaptionColor = CRT_colors[METER_TEXT]; - if (pl->highlightThreads) { + if (settings->highlightThreads) { threadValueColor = CRT_colors[PROCESS_THREAD_BASENAME]; threadCaptionColor = CRT_colors[PROCESS_THREAD]; } - if (!pl->hideUserlandThreads) { + if (!settings->hideUserlandThreads) { RichString_append(out, CRT_colors[METER_TEXT], ", "); - sprintf(buffer, "%d", (int)pl->userlandThreads); + sprintf(buffer, "%d", (int)this->values[1]); RichString_append(out, threadValueColor, buffer); RichString_append(out, threadCaptionColor, " thr"); } - if (!pl->hideKernelThreads) { + if (!settings->hideKernelThreads) { RichString_append(out, CRT_colors[METER_TEXT], ", "); - sprintf(buffer, "%d", (int)pl->kernelThreads); + sprintf(buffer, "%d", (int)this->values[0]); RichString_append(out, threadValueColor, buffer); RichString_append(out, threadCaptionColor, " kthr"); } RichString_append(out, CRT_colors[METER_TEXT], "; "); - sprintf(buffer, "%d", (int)this->values[0]); + sprintf(buffer, "%d", (int)this->values[3]); RichString_append(out, CRT_colors[TASKS_RUNNING], buffer); RichString_append(out, CRT_colors[METER_TEXT], " running"); } @@ -64,6 +65,7 @@ MeterClass TasksMeter_class = { .setValues = TasksMeter_setValues, .defaultMode = TEXT_METERMODE, .total = 100.0, + .maxItems = 4, .attributes = TasksMeter_attributes, .name = "Tasks", .uiName = "Task counter", diff --git a/configure.ac b/configure.ac index bc586d99..b0801441 100644 --- a/configure.ac +++ b/configure.ac @@ -4,6 +4,8 @@ AC_PREREQ(2.65) AC_INIT([htop],[1.0.3],[hisham@gobolinux.org]) +year=$(date +%Y) + # The following two lines are required by hwloc scripts AC_USE_SYSTEM_EXTENSIONS AC_CANONICAL_TARGET @@ -180,6 +182,8 @@ if test ! -z "$missing_headers"; then AC_MSG_ERROR([missing headers: $missing_headers]) fi +AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-$year Hisham Muhammad", [Copyright message.]) + # We're done, let's go! # ---------------------------------------------------------------------- AM_CONDITIONAL([HTOP_LINUX], [test "$my_htop_platform" = linux]) diff --git a/htop.c b/htop.c index 66088e7a..bb948b80 100644 --- a/htop.c +++ b/htop.c @@ -5,45 +5,28 @@ Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ -#include "ProcessList.h" +#include "config.h" -#include "CRT.h" -#include "Panel.h" -#include "UsersTable.h" -#include "RichString.h" -#include "Settings.h" -#include "ScreenManager.h" #include "FunctionBar.h" -#include "ListItem.h" -#include "String.h" +#include "Hashtable.h" #include "ColumnsPanel.h" -#include "CategoriesPanel.h" -#include "SignalsPanel.h" -#include "TraceScreen.h" -#include "OpenFilesScreen.h" -#include "AffinityPanel.h" -#include "Platform.h" -#include "IncSet.h" -#include "Action.h" -#include "htop.h" +#include "CRT.h" +#include "MainPanel.h" +#include "ProcessList.h" +#include "ScreenManager.h" +#include "Settings.h" +#include "UsersTable.h" -#include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include #include -#include -#include #include +#include //#link m -#define COPYRIGHT "(C) 2004-2014 Hisham Muhammad" - static void printVersionFlag() { fputs("htop " VERSION " - " COPYRIGHT "\n" "Released under the GNU GPL.\n\n", @@ -71,197 +54,106 @@ static void printHelpFlag() { exit(0); } -static struct { const char* key; const char* info; } helpLeft[] = { - { .key = " Arrows: ", .info = "scroll process list" }, - { .key = " Digits: ", .info = "incremental PID search" }, - { .key = " F3 /: ", .info = "incremental name search" }, - { .key = " F4 \\: ",.info = "incremental name filtering" }, - { .key = " F5 t: ", .info = "tree view" }, - { .key = " u: ", .info = "show processes of a single user" }, - { .key = " H: ", .info = "hide/show user threads" }, - { .key = " K: ", .info = "hide/show kernel threads" }, - { .key = " F: ", .info = "cursor follows process" }, - { .key = " F6 + -: ", .info = "expand/collapse tree" }, - { .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" }, - { .key = " I: ", .info = "invert sort order" }, - { .key = " F6 >: ", .info = "select sort column" }, - { .key = NULL, .info = NULL } -}; +// ---------------------------------------- -static struct { const char* key; const char* info; } helpRight[] = { - { .key = " Space: ", .info = "tag process" }, - { .key = " c: ", .info = "tag process and its children" }, - { .key = " U: ", .info = "untag all processes" }, - { .key = " F9 k: ", .info = "kill process/tagged processes" }, - { .key = " F7 ]: ", .info = "higher priority (root only)" }, - { .key = " F8 [: ", .info = "lower priority (+ nice)" }, -#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY) - { .key = " a: ", .info = "set CPU affinity" }, -#endif - { .key = " i: ", .info = "set IO prority" }, - { .key = " l: ", .info = "list open files with lsof" }, - { .key = " s: ", .info = "trace syscalls with strace" }, - { .key = " ", .info = "" }, - { .key = " F2 S: ", .info = "setup" }, - { .key = " F1 h: ", .info = "show this help screen" }, - { .key = " F10 q: ", .info = "quit" }, - { .key = NULL, .info = NULL } -}; +typedef struct CommandLineSettings_ { + Hashtable* pidWhiteList; + uid_t userId; + int sortKey; + int delay; + bool useColors; +} CommandLineSettings; -static void showHelp(ProcessList* pl) { - clear(); - attrset(CRT_colors[HELP_BOLD]); +static CommandLineSettings parseArguments(int argc, char** argv) { - for (int i = 0; i < LINES-1; i++) - mvhline(i, 0, ' ', COLS); + CommandLineSettings flags = { + .pidWhiteList = NULL, + .userId = -1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2)) + .sortKey = 0, + .delay = -1, + .useColors = true, + }; - mvaddstr(0, 0, "htop " VERSION " - " COPYRIGHT); - mvaddstr(1, 0, "Released under the GNU GPL. See 'man' page for more info."); + static struct option long_opts[] = + { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'v'}, + {"delay", required_argument, 0, 'd'}, + {"sort-key", required_argument, 0, 's'}, + {"user", required_argument, 0, 'u'}, + {"no-color", no_argument, 0, 'C'}, + {"no-colour",no_argument, 0, 'C'}, + {"pid", required_argument, 0, 'p'}, + {"io", no_argument, 0, 'i'}, + {0,0,0,0} + }; - attrset(CRT_colors[DEFAULT_COLOR]); - mvaddstr(3, 0, "CPU usage bar: "); - #define addattrstr(a,s) attrset(a);addstr(s) - addattrstr(CRT_colors[BAR_BORDER], "["); - if (pl->detailedCPUTime) { - addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/"); - addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); - addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/"); - addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/"); - addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/"); - addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/"); - addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/"); - addattrstr(CRT_colors[CPU_IOWAIT], "io-wait"); - addattrstr(CRT_colors[BAR_SHADOW], " used%"); - } else { - addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/"); - addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); - addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/"); - addattrstr(CRT_colors[CPU_STEAL], "virtualiz"); - addattrstr(CRT_colors[BAR_SHADOW], " used%"); - } - addattrstr(CRT_colors[BAR_BORDER], "]"); - attrset(CRT_colors[DEFAULT_COLOR]); - mvaddstr(4, 0, "Memory bar: "); - addattrstr(CRT_colors[BAR_BORDER], "["); - addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/"); - addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/"); - addattrstr(CRT_colors[MEMORY_CACHE], "cache"); - addattrstr(CRT_colors[BAR_SHADOW], " used/total"); - addattrstr(CRT_colors[BAR_BORDER], "]"); - attrset(CRT_colors[DEFAULT_COLOR]); - mvaddstr(5, 0, "Swap bar: "); - addattrstr(CRT_colors[BAR_BORDER], "["); - addattrstr(CRT_colors[SWAP], "used"); - addattrstr(CRT_colors[BAR_SHADOW], " used/total"); - addattrstr(CRT_colors[BAR_BORDER], "]"); - attrset(CRT_colors[DEFAULT_COLOR]); - mvaddstr(6,0, "Type and layout of header meters are configurable in the setup screen."); - if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { - mvaddstr(7, 0, "In monochrome, meters are displayed through different chars, in order: |#*@$%&"); - } - mvaddstr( 8, 0, " Status: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep"); - for (int i = 0; helpLeft[i].info; i++) { mvaddstr(9+i, 9, helpLeft[i].info); } - for (int i = 0; helpRight[i].info; i++) { mvaddstr(9+i, 49, helpRight[i].info); } - attrset(CRT_colors[HELP_BOLD]); - for (int i = 0; helpLeft[i].key; i++) { mvaddstr(9+i, 0, helpLeft[i].key); } - for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); } + int opt, opti=0; + /* Parse arguments */ + while ((opt = getopt_long(argc, argv, "hvCs:d:u:p:i", long_opts, &opti))) { + if (opt == EOF) break; + switch (opt) { + case 'h': + printHelpFlag(); + break; + case 'v': + printVersionFlag(); + break; + case 's': + if (strcmp(optarg, "help") == 0) { + for (int j = 1; j < LAST_PROCESSFIELD; j++) { + const char* name = Process_fields[j].name; + if (name) printf ("%s\n", name); + } + exit(0); + } + flags.sortKey = ColumnsPanel_fieldNameToIndex(optarg); + if (flags.sortKey == -1) { + fprintf(stderr, "Error: invalid column \"%s\".\n", optarg); + exit(1); + } + break; + case 'd': + if (sscanf(optarg, "%16d", &(flags.delay)) == 1) { + if (flags.delay < 1) flags.delay = 1; + if (flags.delay > 100) flags.delay = 100; + } else { + fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg); + exit(1); + } + break; + case 'u': + if (!Action_setUserOnly(optarg, &(flags.userId))) { + fprintf(stderr, "Error: invalid user \"%s\".\n", optarg); + exit(1); + } + break; + case 'C': + flags.useColors = false; + break; + case 'p': { + char* argCopy = strdup(optarg); + char* saveptr; + char* pid = strtok_r(argCopy, ",", &saveptr); - attrset(CRT_colors[HELP_BOLD]); - mvaddstr(23,0, "Press any key to return."); - attrset(CRT_colors[DEFAULT_COLOR]); - refresh(); - CRT_readKey(); - clear(); -} + if( !flags.pidWhiteList ) { + flags.pidWhiteList = Hashtable_new(8, false); + } -static const char* CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; + while( pid ) { + unsigned int num_pid = atoi(pid); + Hashtable_put(flags.pidWhiteList, num_pid, (void *) 1); + pid = strtok_r(NULL, ",", &saveptr); + } + free(argCopy); -static void Setup_run(Settings* settings, const Header* header) { - ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, true); - CategoriesPanel* panelCategories = CategoriesPanel_new(settings, scr); - ScreenManager_add(scr, (Panel*) panelCategories, FunctionBar_new(CategoriesFunctions, NULL, NULL), 16); - CategoriesPanel_makeMetersPage(panelCategories); - Panel* panelFocus; - int ch; - ScreenManager_run(scr, &panelFocus, &ch); - ScreenManager_delete(scr); -} - -static bool changePriority(Panel* panel, int delta) { - bool anyTagged; - bool ok = Action_foreachProcess(panel, (Action_ForeachProcessFn) Process_changePriorityBy, delta, &anyTagged); - if (!ok) - beep(); - return anyTagged; -} - -static void addUserToVector(int key, void* userCast, void* panelCast) { - char* user = (char*) userCast; - Panel* panel = (Panel*) panelCast; - Panel_add(panel, (Object*) ListItem_new(user, key)); -} - -static bool setUserOnly(const char* userName, bool* userOnly, uid_t* userId) { - struct passwd* user = getpwnam(userName); - if (user) { - *userOnly = true; - *userId = user->pw_uid; - return true; - } - return false; -} - -static const char* getMainPanelValue(Panel* panel, int i) { - Process* p = (Process*) Panel_get(panel, i); - if (p) - return p->comm; - return ""; -} - -static void tagAllChildren(Panel* panel, Process* parent) { - parent->tag = true; - pid_t ppid = parent->pid; - for (int i = 0; i < Panel_size(panel); i++) { - Process* p = (Process*) Panel_get(panel, i); - if (!p->tag && p->ppid == ppid) { - tagAllChildren(panel, p); + break; + } + default: + exit(1); } } -} - -static bool expandCollapse(Panel* panel) { - Process* p = (Process*) Panel_getSelected(panel); - if (!p) return false; - p->showChildren = !p->showChildren; - return true; -} - -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_setHeader(sortPanel, "Sort by"); - const char* fuFunctions[] = {"Sort ", "Cancel ", NULL}; - ProcessField* fields = pl->fields; - for (int i = 0; fields[i]; i++) { - char* name = String_trim(Process_fieldNames[fields[i]]); - Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); - if (fields[i] == pl->sortKey) - Panel_setSelected(sortPanel, i); - free(name); - } - ListItem* field = (ListItem*) Action_pickFromVector(panel, sortPanel, 15, fuFunctions, header); - if (field) { - reaction |= setSortKey(pl, field->key); - } - Object_delete(sortPanel); - return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; + return flags; } static void millisleep(unsigned long millisec) { @@ -274,316 +166,8 @@ 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 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 = Action_pickFromVector(panel, affinityPanel, 15, fuFunctions, header); - if (set) { - Affinity* affinity = AffinityPanel_getAffinity(affinityPanel); - bool ok = Action_foreachProcess(panel, (Action_ForeachProcessFn) Process_setAffinity, (size_t) affinity, NULL); - if (!ok) beep(); - Affinity_delete(affinity); - } - Panel_delete((Object*)affinityPanel); -#else - (void) panel; - (void) header; -#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*) Action_pickFromVector(panel, signalsPanel, 15, fuFunctions, header); - if (sgn) { - if (sgn->key != 0) { - Panel_setHeader(panel, "Sending..."); - Panel_draw(panel, true); - refresh(); - Action_foreachProcess(panel, (Action_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*) Action_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; -} - -static 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[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 delay = -1; - bool userOnly = false; - uid_t userId = 0; - int usecolors = 1; - char *argCopy; - char *pid; - Hashtable *pidWhiteList = NULL; - - int opt, opti=0; - static struct option long_opts[] = - { - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'v'}, - {"delay", required_argument, 0, 'd'}, - {"sort-key", required_argument, 0, 's'}, - {"user", required_argument, 0, 'u'}, - {"no-color", no_argument, 0, 'C'}, - {"no-colour",no_argument, 0, 'C'}, - {"pid", required_argument, 0, 'p'}, - {0,0,0,0} - }; - int sortKey = 0; - char *lc_ctype = getenv("LC_CTYPE"); if(lc_ctype != NULL) setlocale(LC_CTYPE, lc_ctype); @@ -592,127 +176,65 @@ int main(int argc, char** argv) { else setlocale(LC_CTYPE, ""); - /* Parse arguments */ - while ((opt = getopt_long(argc, argv, "hvCs:d:u:p:", long_opts, &opti))) { - if (opt == EOF) break; - switch (opt) { - case 'h': - printHelpFlag(); - break; - case 'v': - printVersionFlag(); - break; - case 's': - if (strcmp(optarg, "help") == 0) { - for (int j = 1; j < LAST_PROCESSFIELD; j++) - printf ("%s\n", Process_fieldNames[j]); - exit(0); - } - - sortKey = ColumnsPanel_fieldNameToIndex(optarg); - if (sortKey == -1) { - fprintf(stderr, "Error: invalid column \"%s\".\n", optarg); - exit(1); - } - break; - case 'd': - if (sscanf(optarg, "%16d", &delay) == 1) { - if (delay < 1) delay = 1; - if (delay > 100) delay = 100; - } else { - fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg); - exit(1); - } - break; - case 'u': - if (!setUserOnly(optarg, &userOnly, &userId)) { - fprintf(stderr, "Error: invalid user \"%s\".\n", optarg); - exit(1); - } - break; - case 'C': - usecolors=0; - break; - case 'p': { - argCopy = strdup(optarg); - char* saveptr; - pid = strtok_r(argCopy, ",", &saveptr); - - if( !pidWhiteList ) { - pidWhiteList = Hashtable_new(8, false); - } - - while( pid ) { - unsigned int num_pid = atoi(pid); - Hashtable_put(pidWhiteList, num_pid, (void *) 1); - pid = strtok_r(NULL, ",", &saveptr); - } - free(argCopy); - - break; - } - default: - exit(1); - } - } - + CommandLineSettings flags = parseArguments(argc, argv); // may exit() if (access(PROCDIR, R_OK) != 0) { fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR); exit(1); } - -#ifdef HAVE_LIBNCURSESW - char *locale = setlocale(LC_ALL, NULL); - if (locale == NULL || locale[0] == '\0') - locale = setlocale(LC_CTYPE, NULL); - if (locale != NULL && - (strstr(locale, "UTF-8") || - strstr(locale, "utf-8") || - strstr(locale, "UTF8") || - strstr(locale, "utf8"))) - CRT_utf8 = true; - else - CRT_utf8 = false; -#endif - UsersTable* ut = UsersTable_new(); - ProcessList* pl = ProcessList_new(ut, pidWhiteList); - pl->userOnly = userOnly; - pl->userId = userId; Process_setupColumnWidths(); - Header* header = Header_new(pl); - Settings* settings = Settings_new(pl, header, pl->cpuCount); - int headerHeight = Header_calculateHeight(header); + UsersTable* ut = UsersTable_new(); + ProcessList* pl = ProcessList_new(ut, flags.pidWhiteList, flags.userId); + + Header* header = Header_new(pl, 2); + Settings* settings = Settings_new(pl->cpuCount); + pl->settings = settings; - // FIXME: move delay code to settings - if (delay != -1) - settings->delay = delay; - if (!usecolors) + if (flags.delay != -1) + settings->delay = flags.delay; + if (!flags.useColors) settings->colorScheme = COLORSCHEME_MONOCHROME; CRT_init(settings->delay, settings->colorScheme); - Panel* panel = Panel_new(0, headerHeight, COLS, LINES - headerHeight - 2, false, &Process_class); - ProcessList_setPanel(pl, panel); - FunctionBar* defaultBar = FunctionBar_new(defaultFunctions, NULL, NULL); - updateTreeFunctions(defaultBar, pl->treeView); - if (sortKey > 0) { - pl->sortKey = sortKey; - pl->treeView = false; - pl->direction = 1; + MainPanel* panel = MainPanel_new(defaultBar); + ProcessList_setPanel(pl, (Panel*) panel); + + MainPanel_updateTreeFunctions(defaultBar, settings->treeView); + + if (flags.sortKey > 0) { + settings->sortKey = flags.sortKey; + settings->treeView = false; + settings->direction = 1; } - ProcessList_printHeader(pl, Panel_getHeader(panel)); + ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel)); IncSet* inc = IncSet_new(defaultBar); + State state = { + .inc = inc, + .settings = settings, + .ut = ut, + .pl = pl, + .panel = (Panel*) panel, + .header = header, + }; + MainPanel_setState(panel, &state); + + ScreenManager* scr = ScreenManager_new(0, 0, 0, -1, HORIZONTAL, header, settings, true); + ScreenManager_add(scr, (Panel*) panel, defaultBar, 0); + ProcessList_scan(pl); millisleep(75); ProcessList_scan(pl); + + ScreenManager_run(scr, NULL, NULL); + /* FunctionBar_draw(defaultBar, NULL); int acc = 0; @@ -731,12 +253,6 @@ int main(int argc, char** argv) { Htop_Action keys[KEY_MAX] = { NULL }; setBindings(keys); Platform_setBindings(keys); - - State state = { - .inc = inc, - .settings = settings, - .ut = ut, - }; bool quit = false; int sortTimeout = 0; @@ -759,7 +275,7 @@ int main(int argc, char** argv) { ProcessList_scan(pl); forceRecalculate = false; } - if (sortTimeout == 0 || pl->treeView) { + if (sortTimeout == 0 || settings->treeView) { ProcessList_sort(pl); sortTimeout = 1; } @@ -768,7 +284,7 @@ int main(int argc, char** argv) { } doRefresh = true; - if (pl->treeView) { + if (settings->treeView) { Process* p = (Process*) Panel_getSelected(panel); if (p) { if (!p->showChildren && !collapsed) { @@ -818,12 +334,12 @@ int main(int argc, char** argv) { if (mevent.y == panel->y) { int x = panel->scrollH + mevent.x + 1; ProcessField field = ProcessList_keyAt(pl, x); - if (field == pl->sortKey) { - ProcessList_invertSortOrder(pl); - pl->treeView = false; + if (field == settings->sortKey) { + Settings_invertSortOrder(settings); + settings->treeView = false; reaction |= HTOP_REDRAW_BAR; } else { - reaction |= setSortKey(pl, field); + reaction |= setSortKey(settings, field); } sortTimeout = 0; ch = ERR; @@ -871,7 +387,7 @@ int main(int argc, char** argv) { } if(ch != ERR && keys[ch]) { - reaction |= (keys[ch])(panel, pl, header, &state); + reaction |= (keys[ch])(&state); } else { doRefresh = false; sortTimeout = resetSortTimeout; @@ -881,7 +397,7 @@ int main(int argc, char** argv) { // Reaction handlers: if (reaction & HTOP_REDRAW_BAR) { - updateTreeFunctions(defaultBar, pl->treeView); + updateTreeFunctions(defaultBar, settings->treeView); IncSet_drawBar(inc); } if (reaction & HTOP_UPDATE_PANELHDR) { @@ -903,6 +419,9 @@ int main(int argc, char** argv) { } follow = (reaction & HTOP_KEEP_FOLLOWING); } + + */ + attron(CRT_colors[RESET_COLOR]); mvhline(LINES-1, 0, ' ', COLS); attroff(CRT_colors[RESET_COLOR]); @@ -913,16 +432,20 @@ int main(int argc, char** argv) { Settings_write(settings); Header_delete(header); ProcessList_delete(pl); - + + /* FunctionBar_delete((Object*)defaultBar); Panel_delete((Object*)panel); + */ + + ScreenManager_delete(scr); IncSet_delete(inc); UsersTable_delete(ut); Settings_delete(settings); - if(pidWhiteList) { - Hashtable_delete(pidWhiteList); + if(flags.pidWhiteList) { + Hashtable_delete(flags.pidWhiteList); } return 0; } diff --git a/htop.h b/htop.h index 77f6e2e8..7faa6c64 100644 --- a/htop.h +++ b/htop.h @@ -11,11 +11,8 @@ in the source distribution for its full text. //#link m -#define COPYRIGHT "(C) 2004-2014 Hisham Muhammad" - // ---------------------------------------- -// ---------------------------------------- int main(int argc, char** argv); diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index be4e627a..8a67f608 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -45,22 +45,23 @@ bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) { return (LinuxProcess_updateIOPriority(this) == ioprio); } -void LinuxProcess_writeField(LinuxProcess* this, RichString* str, ProcessField field) { +void Process_writeField(Process* this, RichString* str, ProcessField field) { + LinuxProcess* lp = (LinuxProcess*) this; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; int n = sizeof(buffer) - 1; switch (field) { case IO_PRIORITY: { - int klass = IOPriority_class(this->ioPriority); + int klass = IOPriority_class(lp->ioPriority); if (klass == IOPRIO_CLASS_NONE) { // see note [1] above - snprintf(buffer, n, "B%1d ", (int) (this->super.nice + 20) / 5); + snprintf(buffer, n, "B%1d ", (int) (this->nice + 20) / 5); } else if (klass == IOPRIO_CLASS_BE) { - snprintf(buffer, n, "B%1d ", IOPriority_data(this->ioPriority)); + snprintf(buffer, n, "B%1d ", IOPriority_data(lp->ioPriority)); } else if (klass == IOPRIO_CLASS_RT) { attr = CRT_colors[PROCESS_HIGH_PRIORITY]; - snprintf(buffer, n, "R%1d ", IOPriority_data(this->ioPriority)); - } else if (this->ioPriority == IOPriority_Idle) { + snprintf(buffer, n, "R%1d ", IOPriority_data(lp->ioPriority)); + } else if (lp->ioPriority == IOPriority_Idle) { attr = CRT_colors[PROCESS_LOW_PRIORITY]; snprintf(buffer, n, "id "); } else { @@ -69,25 +70,26 @@ void LinuxProcess_writeField(LinuxProcess* this, RichString* str, ProcessField f break; } default: - snprintf(buffer, n, "- "); + Process_writeDefaultField(this, str, field); + return; } RichString_append(str, attr, buffer); } -long LinuxProcess_compare(const void* v1, const void* v2) { +long Process_compare(const void* v1, const void* v2) { LinuxProcess *p1, *p2; - ProcessList *pl = ((Process*)v1)->pl; - if (pl->direction == 1) { + Settings *settings = ((Process*)v1)->settings; + if (settings->direction == 1) { p1 = (LinuxProcess*)v1; p2 = (LinuxProcess*)v2; } else { p2 = (LinuxProcess*)v1; p1 = (LinuxProcess*)v2; } - switch (pl->sortKey) { + switch (settings->sortKey) { case IO_PRIORITY: return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2); default: - return (p1->super.pid - p2->super.pid); + return Process_defaultCompare(v1, v2); } } diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 97ddf90b..beafff64 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -32,8 +32,8 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this); bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio); -void LinuxProcess_writeField(LinuxProcess* this, RichString* str, ProcessField field); +void Process_writeField(Process* this, RichString* str, ProcessField field); -long LinuxProcess_compare(const void* v1, const void* v2); +long Process_compare(const void* v1, const void* v2); #endif diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index d4c536a0..aed530dd 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -31,6 +31,56 @@ in the source distribution for its full text. #include "ProcessList.h" +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int systemTime; + unsigned long long int systemAllTime; + unsigned long long int idleAllTime; + unsigned long long int idleTime; + unsigned long long int niceTime; + unsigned long long int ioWaitTime; + unsigned long long int irqTime; + unsigned long long int softIrqTime; + unsigned long long int stealTime; + unsigned long long int guestTime; + + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int systemPeriod; + unsigned long long int systemAllPeriod; + unsigned long long int idleAllPeriod; + unsigned long long int idlePeriod; + unsigned long long int nicePeriod; + unsigned long long int ioWaitPeriod; + unsigned long long int irqPeriod; + unsigned long long int softIrqPeriod; + unsigned long long int stealPeriod; + unsigned long long int guestPeriod; +} CPUData; + +typedef struct LinuxProcessList_ { + ProcessList super; + + int totalTasks; + int userlandThreads; + int kernelThreads; + int runningTasks; + + CPUData* cpus; + + unsigned long long int totalMem; + unsigned long long int usedMem; + unsigned long long int freeMem; + unsigned long long int sharedMem; + unsigned long long int buffersMem; + unsigned long long int cachedMem; + unsigned long long int totalSwap; + unsigned long long int usedSwap; + unsigned long long int freeSwap; + +} LinuxProcessList; + #ifndef PROCDIR #define PROCDIR "/proc" #endif @@ -45,9 +95,10 @@ in the source distribution for its full text. }*/ -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList) { - ProcessList* this = calloc(1, sizeof(ProcessList)); - ProcessList_init(this, usersTable, pidWhiteList); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { + LinuxProcessList* this = calloc(1, sizeof(LinuxProcessList)); + ProcessList* pl = &(this->super); + ProcessList_init(pl, usersTable, pidWhiteList, userId); // Update CPU count: FILE* file = fopen(PROCSTATFILE, "r"); @@ -62,7 +113,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList) { } while (String_startsWith(buffer, "cpu")); fclose(file); - this->cpuCount = MAX(cpus - 1, 1); + pl->cpuCount = MAX(cpus - 1, 1); this->cpus = calloc(cpus, sizeof(CPUData)); for (int i = 0; i < cpus; i++) { @@ -74,11 +125,13 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList) { this->flags |= PROCESS_FLAG_OPENVZ; #endif - return this; + return pl; } -void ProcessList_delete(ProcessList* this) { - ProcessList_done(this); +void ProcessList_delete(ProcessList* pl) { + LinuxProcessList* this = (LinuxProcessList*) pl; + ProcessList_done(pl); + free(this->cpus); free(this); } @@ -166,7 +219,6 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, location += 1; assert(location != NULL); process->processor = strtol(location, &location, 10); - assert(location == NULL); return true; } @@ -200,8 +252,11 @@ static void LinuxProcessList_readIoFile(Process* process, const char* dirname, c snprintf(filename, MAX_NAME, "%s/%s/io", dirname, name); int fd = open(filename, O_RDONLY); - if (fd == -1) + if (fd == -1) { + process->io_rate_read_bps = -1; + process->io_rate_write_bps = -1; return; + } char buffer[1024]; ssize_t buflen = xread(fd, buffer, 1023); @@ -420,9 +475,10 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna return true; } -static bool LinuxProcessList_processEntries(ProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) { +static bool LinuxProcessList_processEntries(LinuxProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) { DIR* dir; struct dirent* entry; + Settings* settings = this->super.settings; time_t curTime = tv.tv_sec; #ifdef HAVE_TASKSTATS @@ -431,15 +487,15 @@ static bool LinuxProcessList_processEntries(ProcessList* this, const char* dirna dir = opendir(dirname); if (!dir) return false; - int cpus = this->cpuCount; - bool hideKernelThreads = this->hideKernelThreads; - bool hideUserlandThreads = this->hideUserlandThreads; + int cpus = this->super.cpuCount; + bool hideKernelThreads = settings->hideKernelThreads; + bool hideUserlandThreads = settings->hideUserlandThreads; while ((entry = readdir(dir)) != NULL) { char* name = entry->d_name; // The RedHat kernel hides threads with a dot. // I believe this is non-standard. - if ((!this->hideThreads) && name[0] == '.') { + if ((!settings->hideThreads) && name[0] == '.') { name++; } @@ -458,14 +514,14 @@ static bool LinuxProcessList_processEntries(ProcessList* this, const char* dirna continue; Process* process = NULL; - Process* existingProcess = (Process*) Hashtable_get(this->processTable, pid); + Process* existingProcess = (Process*) Hashtable_get(this->super.processTable, pid); if (existingProcess) { assert(Vector_indexOf(this->processes, existingProcess, Process_pidCompare) != -1); process = existingProcess; assert(process->pid == pid); } else { - process = Process_new(this); + process = Process_new(settings); assert(process->comm == NULL); process->pid = pid; process->tgid = parent ? parent->pid : pid; @@ -476,7 +532,7 @@ static bool LinuxProcessList_processEntries(ProcessList* this, const char* dirna LinuxProcessList_processEntries(this, subdirname, process, period, tv); #ifdef HAVE_TASKSTATS - if (this->flags & PROCESS_FLAG_IO) + if (settings->flags & PROCESS_FLAG_IO) LinuxProcessList_readIoFile(process, dirname, name, now); #endif @@ -489,7 +545,7 @@ static bool LinuxProcessList_processEntries(ProcessList* this, const char* dirna unsigned long long int lasttimes = (process->utime + process->stime); if (! LinuxProcessList_readStatFile(process, dirname, name, command)) goto errorReadingProcess; - if (this->flags & PROCESS_FLAG_IOPRIO) + if (settings->flags & PROCESS_FLAG_IOPRIO) LinuxProcess_updateIOPriority((LinuxProcess*)process); float percent_cpu = (process->utime + process->stime - lasttimes) / period * 100.0; process->percent_cpu = MAX(MIN(percent_cpu, cpus*100.0), 0.0); @@ -501,30 +557,30 @@ static bool LinuxProcessList_processEntries(ProcessList* this, const char* dirna if (! LinuxProcessList_statProcessDir(process, dirname, name, curTime)) goto errorReadingProcess; - process->user = UsersTable_getRef(this->usersTable, process->st_uid); + process->user = UsersTable_getRef(this->super.usersTable, process->st_uid); #ifdef HAVE_OPENVZ LinuxProcessList_readOpenVZData(this, process, dirname, name); #endif #ifdef HAVE_VSERVER - if (this->flags & PROCESS_FLAG_VSERVER) + if (settings->flags & PROCESS_FLAG_VSERVER) LinuxProcessList_readVServerData(process, dirname, name); #endif if (! LinuxProcessList_readCmdlineFile(process, dirname, name)) goto errorReadingProcess; - ProcessList_add(this, process); + ProcessList_add((ProcessList*)this, process); } else { - if (this->updateProcessNames) { + if (settings->updateProcessNames) { if (! LinuxProcessList_readCmdlineFile(process, dirname, name)) goto errorReadingProcess; } } #ifdef HAVE_CGROUP - if (this->flags & PROCESS_FLAG_CGROUP) + if (settings->flags & PROCESS_FLAG_CGROUP) LinuxProcessList_readCGroupFile(process, dirname, name); #endif @@ -537,11 +593,11 @@ static bool LinuxProcessList_processEntries(ProcessList* this, const char* dirna process->basenameOffset = -1; process->comm = strdup(command); } else if (Process_isThread(process)) { - if (this->showThreadNames || Process_isKernelThread(process) || process->state == 'Z') { + if (settings->showThreadNames || Process_isKernelThread(process) || process->state == 'Z') { free(process->comm); process->basenameOffset = -1; process->comm = strdup(command); - } else if (this->showingThreadNames) { + } else if (settings->showThreadNames) { if (! LinuxProcessList_readCmdlineFile(process, dirname, name)) goto errorReadingProcess; } @@ -567,7 +623,7 @@ static bool LinuxProcessList_processEntries(ProcessList* this, const char* dirna process->comm = NULL; } if (existingProcess) - ProcessList_remove(this, process); + ProcessList_remove((ProcessList*)this, process); else Process_delete((Object*)process); } @@ -576,7 +632,7 @@ static bool LinuxProcessList_processEntries(ProcessList* this, const char* dirna return true; } -static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { +static inline void LinuxProcessList_scanMemoryInfo(LinuxProcessList* this) { unsigned long long int swapFree = 0; FILE* file = fopen(PROCMEMINFOFILE, "r"); @@ -617,14 +673,14 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { fclose(file); } -static inline double LinuxProcessList_scanCPUTime(ProcessList* this) { +static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) { unsigned long long int usertime, nicetime, systemtime, idletime; FILE* file = fopen(PROCSTATFILE, "r"); if (file == NULL) { CRT_fatalError("Cannot open " PROCSTATFILE); } - int cpus = this->cpuCount; + int cpus = this->super.cpuCount; assert(cpus > 0); for (int i = 0; i <= cpus; i++) { char buffer[256]; @@ -693,15 +749,16 @@ static inline double LinuxProcessList_scanCPUTime(ProcessList* this) { return period; } -void ProcessList_scan(ProcessList* this) { +void ProcessList_scan(ProcessList* super) { + LinuxProcessList* this = (LinuxProcessList*) super; LinuxProcessList_scanMemoryInfo(this); double period = LinuxProcessList_scanCPUTime(this); // mark all process as "dirty" - for (int i = 0; i < Vector_size(this->processes); i++) { - Process* p = (Process*) Vector_get(this->processes, i); + for (int i = 0; i < Vector_size(super->processes); i++) { + Process* p = (Process*) Vector_get(super->processes, i); p->updated = false; } @@ -714,12 +771,10 @@ void ProcessList_scan(ProcessList* this) { gettimeofday(&tv, NULL); LinuxProcessList_processEntries(this, PROCDIR, NULL, period, tv); - this->showingThreadNames = this->showThreadNames; - - for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { - Process* p = (Process*) Vector_get(this->processes, i); + for (int i = Vector_size(this->super.processes) - 1; i >= 0; i--) { + Process* p = (Process*) Vector_get(this->super.processes, i); if (p->updated == false) - ProcessList_remove(this, p); + ProcessList_remove(super, p); else p->updated = false; } diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 4f9482fa..cfd01828 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -12,6 +12,56 @@ in the source distribution for its full text. #include "ProcessList.h" +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int systemTime; + unsigned long long int systemAllTime; + unsigned long long int idleAllTime; + unsigned long long int idleTime; + unsigned long long int niceTime; + unsigned long long int ioWaitTime; + unsigned long long int irqTime; + unsigned long long int softIrqTime; + unsigned long long int stealTime; + unsigned long long int guestTime; + + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int systemPeriod; + unsigned long long int systemAllPeriod; + unsigned long long int idleAllPeriod; + unsigned long long int idlePeriod; + unsigned long long int nicePeriod; + unsigned long long int ioWaitPeriod; + unsigned long long int irqPeriod; + unsigned long long int softIrqPeriod; + unsigned long long int stealPeriod; + unsigned long long int guestPeriod; +} CPUData; + +typedef struct LinuxProcessList_ { + ProcessList super; + + int totalTasks; + int userlandThreads; + int kernelThreads; + int runningTasks; + + CPUData* cpus; + + unsigned long long int totalMem; + unsigned long long int usedMem; + unsigned long long int freeMem; + unsigned long long int sharedMem; + unsigned long long int buffersMem; + unsigned long long int cachedMem; + unsigned long long int totalSwap; + unsigned long long int usedSwap; + unsigned long long int freeSwap; + +} LinuxProcessList; + #ifndef PROCDIR #define PROCDIR "/proc" #endif @@ -25,9 +75,9 @@ in the source distribution for its full text. #endif -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); -void ProcessList_delete(ProcessList* this); +void ProcessList_delete(ProcessList* pl); #ifdef HAVE_TASKSTATS @@ -50,7 +100,7 @@ void ProcessList_delete(ProcessList* this); #endif -void ProcessList_scan(ProcessList* this); +void ProcessList_scan(ProcessList* super); #endif diff --git a/linux/Platform.c b/linux/Platform.c index ee5abf81..a5704d23 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -9,6 +9,7 @@ in the source distribution for its full text. #include "IOPriority.h" #include "IOPriorityPanel.h" #include "LinuxProcess.h" +#include "LinuxProcessList.h" #include "Battery.h" #include "Meter.h" @@ -29,14 +30,15 @@ in the source distribution for its full text. #include "BatteryMeter.h" }*/ -static Htop_Reaction Platform_actionSetIOPriority(Panel* panel, ProcessList* pl, Header* header) { - (void) panel, (void) pl; +static Htop_Reaction Platform_actionSetIOPriority(State* st) { + Panel* panel = st->panel; + LinuxProcess* p = (LinuxProcess*) 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 = Action_pickFromVector(panel, ioprioPanel, 21, fuFunctions, header); + void* set = Action_pickFromVector(st, ioprioPanel, 21, fuFunctions); if (set) { IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel); bool ok = Action_foreachProcess(panel, (Action_ForeachProcessFn) LinuxProcess_setIOPriority, (size_t) ioprio, NULL); @@ -120,3 +122,63 @@ void Platform_getBatteryLevel(double* level, ACPresence* isOnAC) { *isOnAC = Battery_isOnAC(); *level = percent; } + +double Platform_setCPUValues(Meter* this, int cpu) { + LinuxProcessList* pl = (LinuxProcessList*) this->pl; + CPUData* cpuData = &(pl->cpus[cpu]); + double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod); + double percent; + double* v = this->values; + v[0] = cpuData->nicePeriod / total * 100.0; + v[1] = cpuData->userPeriod / total * 100.0; + if (this->pl->settings->detailedCPUTime) { + v[2] = cpuData->systemPeriod / total * 100.0; + v[3] = cpuData->irqPeriod / total * 100.0; + v[4] = cpuData->softIrqPeriod / total * 100.0; + v[5] = cpuData->stealPeriod / total * 100.0; + v[6] = cpuData->guestPeriod / total * 100.0; + v[7] = cpuData->ioWaitPeriod / total * 100.0; + Meter_setItems(this, 8); + if (this->pl->settings->accountGuestInCPUMeter) { + percent = v[0]+v[1]+v[2]+v[3]+v[4]+v[5]+v[6]; + } else { + percent = v[0]+v[1]+v[2]+v[3]+v[4]; + } + } else { + v[2] = cpuData->systemAllPeriod / total * 100.0; + v[3] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0; + Meter_setItems(this, 4); + percent = v[0]+v[1]+v[2]+v[3]; + } + percent = MIN(100.0, MAX(0.0, percent)); + if (isnan(percent)) percent = 0.0; + return percent; +} + +void Platform_setMemoryValues(Meter* this) { + LinuxProcessList* pl = (LinuxProcessList*) this->pl; + long int usedMem = pl->usedMem; + long int buffersMem = pl->buffersMem; + long int cachedMem = pl->cachedMem; + usedMem -= buffersMem + cachedMem; + this->total = pl->totalMem; + this->values[0] = usedMem; + this->values[1] = buffersMem; + this->values[2] = cachedMem; +} + +void Platform_setSwapValues(Meter* this) { + LinuxProcessList* pl = (LinuxProcessList*) this->pl; + this->total = pl->totalSwap; + this->values[0] = pl->usedSwap; +} + +void Platform_setTasksValues(Meter* this) { + LinuxProcessList* pl = (LinuxProcessList*) this->pl; + this->values[0] = pl->kernelThreads; + this->values[1] = pl->userlandThreads; + this->values[2] = pl->totalTasks - pl->kernelThreads - pl->userlandThreads; + this->values[3] = pl->runningTasks; + if (pl->totalTasks > this->total) + this->total = pl->totalTasks; +} diff --git a/linux/Platform.h b/linux/Platform.h index 2058419c..6fb6b543 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -24,4 +24,12 @@ int Platform_getMaxPid(); void Platform_getBatteryLevel(double* level, ACPresence* isOnAC); +double Platform_setCPUValues(Meter* this, int cpu); + +void Platform_setMemoryValues(Meter* this); + +void Platform_setSwapValues(Meter* this); + +void Platform_setTasksValues(Meter* this); + #endif diff --git a/scripts/MakeHeader.py b/scripts/MakeHeader.py index 74bdb4fd..bcf1005a 100755 --- a/scripts/MakeHeader.py +++ b/scripts/MakeHeader.py @@ -56,6 +56,8 @@ for line in file.readlines(): state = SKIP elif equals != -1: out.write("extern " + line[:equals] + ";" ) + elif line.startswith("typedef struct"): + state = SKIP elif line[-1] == "{": out.write( line[:-2].replace("inline", "extern") + ";" ) state = SKIP