From 2338ad5820c6d265db61794f005875f9845b3e40 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Fri, 14 Mar 2008 18:50:49 +0000 Subject: Ability to change sort column with the mouse by clicking column titles (click again to invert order). Also, add a consolidated IO kbyte/s column. --- ChangeLog | 2 ++ Process.c | 101 ++++++++++++++++++++++++++++------------------------------ Process.h | 6 ++-- ProcessList.c | 16 +++++++++- ProcessList.h | 2 ++ htop.c | 53 ++++++++++++++++++------------ 6 files changed, 102 insertions(+), 78 deletions(-) diff --git a/ChangeLog b/ChangeLog index 11feffef..4d396702 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,8 @@ What's new in version 0.7.1 +* Ability to change sort column with the mouse by + clicking column titles (click again to invert order) * Add support for Linux per-process IO statistics, enabled with the --enable-taskstats flag, which requires a kernel compiled with taskstats support. diff --git a/Process.c b/Process.c index adb89795..b6e64b86 100644 --- a/Process.c +++ b/Process.c @@ -50,7 +50,7 @@ typedef enum ProcessField_ { VEID, VPID, #endif #ifdef HAVE_TASKSTATS - RCHAR, WCHAR, SYSCR, SYSCW, RBYTES, WBYTES, CNCLWB, IO_READ_RATE, IO_WRITE_RATE, + RCHAR, WCHAR, SYSCR, SYSCW, RBYTES, WBYTES, CNCLWB, IO_READ_RATE, IO_WRITE_RATE, IO_RATE, #endif LAST_PROCESSFIELD } ProcessField; @@ -148,16 +148,42 @@ char* PROCESS_CLASS = "Process"; #endif 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", + "", "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", #ifdef HAVE_OPENVZ "VEID", "VPID", #endif #ifdef HAVE_TASKSTATS - "RCHAR", "WCHAR", "SYSCR", "SYSCW", "RBYTES", "WBYTES", "CNCLWB", "IO_READ_RATE", "IO_WRITE_RATE", + "RCHAR", "WCHAR", "SYSCR", "SYSCW", "RBYTES", "WBYTES", "CNCLWB", + "IO_READ_RATE", "IO_WRITE_RATE", "IO_RATE", #endif "*** report bug! ***" }; +char *Process_fieldTitles[] = { + "", " PID ", "Command ", "S ", " PPID ", " PGRP ", " SESN ", + " TTY ", "TPGID ", "- ", "- ", "- ", "- ", "- ", + " UTIME+ ", " STIME+ ", "- ", "- ", "PRI ", " NI ", "- ", + "- ", "- ", "- ", "- ", "- ", "- ", "- ", + "- ", "- ", "- ", "- ", "- ", "- ", "- ", + "- ", "- ", "- ", "CPU ", " VIRT ", " RES ", " SHR ", + " CODE ", " DATA ", " LIB ", " DIRTY ", " UID ", "CPU% ", "MEM% ", + "USER ", " TIME+ ", "NLWP ", " TGID ", +#ifdef HAVE_OPENVZ + " VEID ", " VPID ", +#endif +#ifdef HAVE_TASKSTATS + " RD_CHAR ", " WR_CHAR ", " RD_SYSC ", " WR_SYSC ", " IO_RD ", " IO_WR ", " IO_CANCEL ", + " IORR ", " IOWR ", " IO ", +#endif +}; + static int Process_getuid = -1; #define ONE_K 1024 @@ -230,6 +256,21 @@ static inline void Process_writeCommand(Process* this, int attr, int baseattr, R } } +static inline void Process_outputRate(Process* this, RichString* str, int attr, char* buffer, int n, double rate) { + 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_printLargeNumber(this, str, rate); + return; + } + RichString_append(str, attr, buffer); +} + static void Process_writeField(Process* this, RichString* str, ProcessField field) { char buffer[PROCESS_COMM_LEN]; int attr = CRT_colors[DEFAULT_COLOR]; @@ -355,8 +396,9 @@ static void Process_writeField(Process* this, RichString* str, ProcessField fiel case RBYTES: snprintf(buffer, n, "%10llu ", this->io_read_bytes); break; case WBYTES: snprintf(buffer, n, "%10llu ", this->io_write_bytes); break; case CNCLWB: snprintf(buffer, n, "%10llu ", this->io_cancelled_write_bytes); break; - case IO_READ_RATE: Process_printLargeNumber(this, str, this->io_rate_read_bps / 1024); return; - case IO_WRITE_RATE: Process_printLargeNumber(this, str, this->io_rate_write_bps / 1024); return; + case IO_READ_RATE: Process_outputRate(this, str, attr, buffer, n, this->io_rate_read_bps); return; + case IO_WRITE_RATE: Process_outputRate(this, str, attr, buffer, n, this->io_rate_write_bps); return; + case IO_RATE: Process_outputRate(this, str, attr, buffer, n, this->io_rate_read_bps + this->io_rate_write_bps); return; #endif default: @@ -524,6 +566,7 @@ int Process_compare(const void* v1, const void* v2) { case CNCLWB: diff = p2->io_cancelled_write_bytes - p1->io_cancelled_write_bytes; goto test_diff; case IO_READ_RATE: diff = p2->io_rate_read_bps - p1->io_rate_read_bps; goto test_diff; case IO_WRITE_RATE: diff = p2->io_rate_write_bps - p1->io_rate_write_bps; goto test_diff; + case IO_RATE: diff = (p2->io_rate_read_bps + p2->io_rate_write_bps) - (p1->io_rate_read_bps + p1->io_rate_write_bps); goto test_diff; #endif default: @@ -532,51 +575,3 @@ int Process_compare(const void* v1, const void* v2) { test_diff: return (diff > 0) ? 1 : (diff < 0 ? -1 : 0); } - -char* Process_printField(ProcessField field) { - switch (field) { - case PID: return " PID "; - case PPID: return " PPID "; - case PGRP: return " PGRP "; - case SESSION: return " SESN "; - case TTY_NR: return " TTY "; - case TGID: return " TGID "; - case TPGID: return "TPGID "; - case COMM: return "Command "; - case STATE: return "S "; - case PRIORITY: return "PRI "; - case NICE: return " NI "; - case M_DRS: return " DATA "; - case M_DT: return " DIRTY "; - case M_LRS: return " LIB "; - case M_TRS: return " CODE "; - case M_SIZE: return " VIRT "; - case M_RESIDENT: return " RES "; - case M_SHARE: return " SHR "; - case ST_UID: return " UID "; - case USER: return "USER "; - case UTIME: return " UTIME+ "; - case STIME: return " STIME+ "; - case TIME: return " TIME+ "; - case PERCENT_CPU: return "CPU% "; - case PERCENT_MEM: return "MEM% "; - case PROCESSOR: return "CPU "; - case NLWP: return "NLWP "; - #ifdef HAVE_OPENVZ - case VEID: return " VEID "; - case VPID: return " VPID "; - #endif - #ifdef HAVE_TASKSTATS - case RCHAR: return " RD_CHAR "; - case WCHAR: return " WR_CHAR "; - case SYSCR: return " RD_SYSC "; - case SYSCW: return " WR_SYSC "; - case RBYTES: return " IO_RD "; - case WBYTES: return " IO_WR "; - case CNCLWB: return " IO_CANCEL "; - case IO_READ_RATE: return " IORR "; - case IO_WRITE_RATE: return " IOWR "; - #endif - default: return "- "; - } -} diff --git a/Process.h b/Process.h index ddb56a59..204ccfbf 100644 --- a/Process.h +++ b/Process.h @@ -52,7 +52,7 @@ typedef enum ProcessField_ { VEID, VPID, #endif #ifdef HAVE_TASKSTATS - RCHAR, WCHAR, SYSCR, SYSCW, RBYTES, WBYTES, CNCLWB, IO_READ_RATE, IO_WRITE_RATE, + RCHAR, WCHAR, SYSCR, SYSCW, RBYTES, WBYTES, CNCLWB, IO_READ_RATE, IO_WRITE_RATE, IO_RATE, #endif LAST_PROCESSFIELD } ProcessField; @@ -150,6 +150,8 @@ extern char* PROCESS_CLASS; extern char *Process_fieldNames[]; +extern char *Process_fieldTitles[]; + #define ONE_K 1024 #define ONE_M (ONE_K * ONE_K) #define ONE_G (ONE_M * ONE_K) @@ -174,6 +176,4 @@ int Process_pidCompare(const void* v1, const void* v2); int Process_compare(const void* v1, const void* v2); -char* Process_printField(ProcessField field); - #endif diff --git a/ProcessList.c b/ProcessList.c index df11050b..a32e6ec6 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -286,7 +286,7 @@ RichString ProcessList_printHeader(ProcessList* this) { RichString_initVal(out); ProcessField* fields = this->fields; for (int i = 0; fields[i]; i++) { - char* field = Process_printField(fields[i]); + char* field = Process_fieldTitles[fields[i]]; if (this->sortKey == fields[i]) RichString_append(&out, CRT_colors[PANEL_HIGHLIGHT_FOCUS], field); else @@ -816,3 +816,17 @@ void ProcessList_scan(ProcessList* this) { } } + +ProcessField ProcessList_keyAt(ProcessList* this, int at) { + int x = 0; + ProcessField* fields = this->fields; + ProcessField field; + for (int i = 0; (field = fields[i]); i++) { + int len = strlen(Process_fieldTitles[field]); + if (at >= x && at <= x + len) { + return field; + } + x += len; + } + return COMM; +} diff --git a/ProcessList.h b/ProcessList.h index 04334d09..5248cac5 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -162,4 +162,6 @@ void ProcessList_sort(ProcessList* this); void ProcessList_scan(ProcessList* this); +ProcessField ProcessList_keyAt(ProcessList* this, int at); + #endif diff --git a/htop.c b/htop.c index b0cb7151..4a226b42 100644 --- a/htop.c +++ b/htop.c @@ -215,6 +215,14 @@ static void setUserOnly(const char* userName, bool* userOnly, uid_t* userId) { } } +static inline void setSortKey(ProcessList* pl, ProcessField sortKey, Panel* panel, Settings* settings) { + pl->sortKey = sortKey; + pl->direction = 1; + pl->treeView = false; + settings->changed = true; + Panel_setRichHeader(panel, ProcessList_printHeader(pl)); +} + int main(int argc, char** argv) { int delay = -1; @@ -288,10 +296,6 @@ int main(int argc, char** argv) { Header* header = Header_new(pl); settings = Settings_new(pl, header); - if (sortKey > 0) { - pl->sortKey = sortKey; - pl->treeView = false; - } int headerHeight = Header_calculateHeight(header); // FIXME: move delay code to settings @@ -301,6 +305,11 @@ int main(int argc, char** argv) { CRT_init(settings->delay, settings->colorScheme); panel = Panel_new(0, headerHeight, COLS, LINES - headerHeight - 2, PROCESS_CLASS, false, NULL); + if (sortKey > 0) { + pl->sortKey = sortKey; + pl->treeView = false; + pl->direction = 1; + } Panel_setRichHeader(panel, ProcessList_printHeader(pl)); char* searchFunctions[3] = {"Next ", "Exit ", " Search: "}; @@ -447,7 +456,18 @@ int main(int argc, char** argv) { MEVENT mevent; int ok = getmouse(&mevent); if (ok == OK) { - if (mevent.y >= panel->y + 1 && mevent.y < LINES - 1) { + if (mevent.y == panel->y) { + int x = panel->scrollH + mevent.x; + ProcessField field = ProcessList_keyAt(pl, x); + if (field == pl->sortKey) { + ProcessList_invertSortOrder(pl); + pl->treeView = false; + } else { + setSortKey(pl, field, panel, settings); + } + refreshTimeout = 0; + continue; + } else if (mevent.y >= panel->y + 1 && mevent.y < LINES - 1) { Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1); doRefresh = false; refreshTimeout = resetRefreshTimeout; @@ -474,19 +494,13 @@ int main(int argc, char** argv) { case 'M': { refreshTimeout = 0; - pl->sortKey = PERCENT_MEM; - pl->treeView = false; - settings->changed = true; - Panel_setRichHeader(panel, ProcessList_printHeader(pl)); + setSortKey(pl, PERCENT_MEM, panel, settings); break; } case 'T': { refreshTimeout = 0; - pl->sortKey = TIME; - pl->treeView = false; - settings->changed = true; - Panel_setRichHeader(panel, ProcessList_printHeader(pl)); + setSortKey(pl, TIME, panel, settings); break; } case 'U': @@ -501,10 +515,7 @@ int main(int argc, char** argv) { case 'P': { refreshTimeout = 0; - pl->sortKey = PERCENT_CPU; - pl->treeView = false; - settings->changed = true; - Panel_setRichHeader(panel, ProcessList_printHeader(pl)); + setSortKey(pl, PERCENT_CPU, panel, settings); break; } case KEY_F(1): @@ -663,7 +674,7 @@ int main(int argc, char** argv) { char* fuFunctions[2] = {"Sort ", "Cancel "}; ProcessField* fields = pl->fields; for (int i = 0; fields[i]; i++) { - char* name = String_trim(Process_printField(fields[i])); + char* name = String_trim(Process_fieldTitles[fields[i]]); Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); if (fields[i] == pl->sortKey) Panel_setSelected(sortPanel, i); @@ -671,12 +682,12 @@ int main(int argc, char** argv) { } ListItem* field = (ListItem*) pickFromList(panel, sortPanel, 15, headerHeight, fuFunctions, defaultBar); if (field) { - pl->treeView = false; settings->changed = true; - pl->sortKey = field->key; + setSortKey(pl, field->key, panel, settings); + } else { + Panel_setRichHeader(panel, ProcessList_printHeader(pl)); } ((Object*)sortPanel)->delete((Object*)sortPanel); - Panel_setRichHeader(panel, ProcessList_printHeader(pl)); refreshTimeout = 0; break; } -- cgit v1.2.3