Merge branch 'hili-new-old' of adsr/htop into highlight-new-old-processes

This commit is contained in:
Daniel Lange 2020-11-16 12:13:47 +01:00
commit 0951090fa4
14 changed files with 166 additions and 37 deletions

12
CRT.c
View File

@ -112,6 +112,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
[PROCESS_NEW] = ColorPair(Black,Green),
[PROCESS_TOMB] = ColorPair(Black,Red),
[PROCESS_THREAD] = ColorPair(Green, Black), [PROCESS_THREAD] = ColorPair(Green, Black),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Black), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Black),
[BAR_BORDER] = A_BOLD, [BAR_BORDER] = A_BOLD,
@ -191,6 +193,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD, [PROCESS_D_STATE] = A_BOLD,
[PROCESS_HIGH_PRIORITY] = A_BOLD, [PROCESS_HIGH_PRIORITY] = A_BOLD,
[PROCESS_LOW_PRIORITY] = A_DIM, [PROCESS_LOW_PRIORITY] = A_DIM,
[PROCESS_NEW] = A_BOLD,
[PROCESS_TOMB] = A_DIM,
[PROCESS_THREAD] = A_BOLD, [PROCESS_THREAD] = A_BOLD,
[PROCESS_THREAD_BASENAME] = A_REVERSE, [PROCESS_THREAD_BASENAME] = A_REVERSE,
[BAR_BORDER] = A_BOLD, [BAR_BORDER] = A_BOLD,
@ -270,6 +274,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, White), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, White),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, White), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, White),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, White), [PROCESS_LOW_PRIORITY] = ColorPair(Green, White),
[PROCESS_NEW] = ColorPair(White,Green),
[PROCESS_TOMB] = ColorPair(White,Red),
[PROCESS_THREAD] = ColorPair(Blue, White), [PROCESS_THREAD] = ColorPair(Blue, White),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, White), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, White),
[BAR_BORDER] = ColorPair(Blue, White), [BAR_BORDER] = ColorPair(Blue, White),
@ -349,6 +355,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
[PROCESS_NEW] = ColorPair(Black,Green),
[PROCESS_TOMB] = ColorPair(Black,Red),
[PROCESS_THREAD] = ColorPair(Blue, Black), [PROCESS_THREAD] = ColorPair(Blue, Black),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black),
[BAR_BORDER] = ColorPair(Blue, Black), [BAR_BORDER] = ColorPair(Blue, Black),
@ -428,6 +436,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Blue), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Blue),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Blue), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Blue),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Blue), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Blue),
[PROCESS_NEW] = ColorPair(Blue,Green),
[PROCESS_TOMB] = ColorPair(Blue,Red),
[PROCESS_THREAD] = ColorPair(Green, Blue), [PROCESS_THREAD] = ColorPair(Green, Blue),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Blue), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Blue),
[BAR_BORDER] = A_BOLD | ColorPair(Yellow, Blue), [BAR_BORDER] = A_BOLD | ColorPair(Yellow, Blue),
@ -509,6 +519,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
[PROCESS_NEW] = ColorPair(Black,Green),
[PROCESS_TOMB] = ColorPair(Black,Red),
[BAR_BORDER] = A_BOLD | ColorPair(Green, Black), [BAR_BORDER] = A_BOLD | ColorPair(Green, Black),
[BAR_SHADOW] = ColorPair(Cyan, Black), [BAR_SHADOW] = ColorPair(Cyan, Black),
[SWAP] = ColorPair(Red, Black), [SWAP] = ColorPair(Red, Black),

2
CRT.h
View File

@ -73,6 +73,8 @@ typedef enum ColorElements_ {
PROCESS_BASENAME, PROCESS_BASENAME,
PROCESS_HIGH_PRIORITY, PROCESS_HIGH_PRIORITY,
PROCESS_LOW_PRIORITY, PROCESS_LOW_PRIORITY,
PROCESS_NEW,
PROCESS_TOMB,
PROCESS_THREAD, PROCESS_THREAD,
PROCESS_THREAD_BASENAME, PROCESS_THREAD_BASENAME,
BAR_BORDER, BAR_BORDER,

View File

@ -93,6 +93,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU percentage numerically"), &(settings->showCPUUsage))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU percentage numerically"), &(settings->showCPUUsage)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable the mouse"), &(settings->enableMouse))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable the mouse"), &(settings->enableMouse)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight new and old processes"), &(settings->highlightChanges)));
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show topology when selecting affinity by default"), &(settings->topologyAffinity))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show topology when selecting affinity by default"), &(settings->topologyAffinity)));
#endif #endif

12
Panel.c
View File

@ -273,16 +273,18 @@ void Panel_draw(Panel* this, bool focus) {
Object_display(itemObj, &item); Object_display(itemObj, &item);
int itemLen = RichString_sizeVal(item); int itemLen = RichString_sizeVal(item);
int amt = MINIMUM(itemLen - scrollH, this->w); int amt = MINIMUM(itemLen - scrollH, this->w);
bool selected = (i == this->selected); if (i == this->selected) {
if (selected) { item.highlightAttr = selectionColor;
attrset(selectionColor); }
RichString_setAttr(&item, selectionColor); if (item.highlightAttr) {
attrset(item.highlightAttr);
RichString_setAttr(&item, item.highlightAttr);
this->selectedLen = itemLen; this->selectedLen = itemLen;
} }
mvhline(y + line, x, ' ', this->w); mvhline(y + line, x, ' ', this->w);
if (amt > 0) if (amt > 0)
RichString_printoffnVal(item, y + line, x, scrollH, amt); RichString_printoffnVal(item, y + line, x, scrollH, amt);
if (selected) if (item.highlightAttr)
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
RichString_end(item); RichString_end(item);
line++; line++;

View File

@ -76,7 +76,7 @@ void Process_humanNumber(RichString* str, unsigned long long number, bool colori
RichString_appendn(str, processColor, buffer, len); RichString_appendn(str, processColor, buffer, len);
} else if (number < 100000) { } else if (number < 100000) {
//2 digit MB, 3 digit KB //2 digit MB, 3 digit KB
len = snprintf(buffer, 10, "%2llu", number / 1000); len = snprintf(buffer, 10, "%2llu", number/1000);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendn(str, processMegabytesColor, buffer, len);
number %= 1000; number %= 1000;
len = snprintf(buffer, 10, "%03llu ", number); len = snprintf(buffer, 10, "%03llu ", number);
@ -89,7 +89,7 @@ void Process_humanNumber(RichString* str, unsigned long long number, bool colori
} else if (number < 10000 * ONE_K) { } else if (number < 10000 * ONE_K) {
//1 digit GB, 3 digit MB //1 digit GB, 3 digit MB
number /= ONE_K; number /= ONE_K;
len = snprintf(buffer, 10, "%1llu", number / 1000); len = snprintf(buffer, 10, "%1llu", number/1000);
RichString_appendn(str, processGigabytesColor, buffer, len); RichString_appendn(str, processGigabytesColor, buffer, len);
number %= 1000; number %= 1000;
len = snprintf(buffer, 10, "%03lluM ", number); len = snprintf(buffer, 10, "%03lluM ", number);
@ -97,7 +97,7 @@ void Process_humanNumber(RichString* str, unsigned long long number, bool colori
} else if (number < 100000 * ONE_K) { } else if (number < 100000 * ONE_K) {
//2 digit GB, 1 digit MB //2 digit GB, 1 digit MB
number /= 100 * ONE_K; number /= 100 * ONE_K;
len = snprintf(buffer, 10, "%2llu", number / 10); len = snprintf(buffer, 10, "%2llu", number/10);
RichString_appendn(str, processGigabytesColor, buffer, len); RichString_appendn(str, processGigabytesColor, buffer, len);
number %= 10; number %= 10;
len = snprintf(buffer, 10, ".%1lluG ", number); len = snprintf(buffer, 10, ".%1lluG ", number);
@ -110,14 +110,14 @@ void Process_humanNumber(RichString* str, unsigned long long number, bool colori
} else if (number < 10000ULL * ONE_M) { } else if (number < 10000ULL * ONE_M) {
//1 digit TB, 3 digit GB //1 digit TB, 3 digit GB
number /= ONE_M; number /= ONE_M;
len = snprintf(buffer, 10, "%1llu", number / 1000); len = snprintf(buffer, 10, "%1llu", number/1000);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendn(str, largeNumberColor, buffer, len);
number %= 1000; number %= 1000;
len = snprintf(buffer, 10, "%03lluG ", number); len = snprintf(buffer, 10, "%03lluG ", number);
RichString_appendn(str, processGigabytesColor, buffer, len); RichString_appendn(str, processGigabytesColor, buffer, len);
} else { } else {
//2 digit TB and above //2 digit TB and above
len = snprintf(buffer, 10, "%4.1lfT ", (double)number / ONE_G); len = snprintf(buffer, 10, "%4.1lfT ", (double)number/ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendn(str, largeNumberColor, buffer, len);
} }
} }
@ -144,18 +144,18 @@ void Process_colorNumber(RichString* str, unsigned long long number, bool colori
} else if (number >= 100LL * ONE_DECIMAL_T) { } else if (number >= 100LL * ONE_DECIMAL_T) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_M); xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_M);
RichString_appendn(str, largeNumberColor, buffer, 8); RichString_appendn(str, largeNumberColor, buffer, 8);
RichString_appendn(str, processMegabytesColor, buffer + 8, 4); RichString_appendn(str, processMegabytesColor, buffer+8, 4);
} else if (number >= 10LL * ONE_DECIMAL_G) { } else if (number >= 10LL * ONE_DECIMAL_G) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_K); xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_K);
RichString_appendn(str, largeNumberColor, buffer, 5); RichString_appendn(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer + 5, 3); RichString_appendn(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer + 8, 4); RichString_appendn(str, processColor, buffer+8, 4);
} else { } else {
xSnprintf(buffer, 13, "%11llu ", number); xSnprintf(buffer, 13, "%11llu ", number);
RichString_appendn(str, largeNumberColor, buffer, 2); RichString_appendn(str, largeNumberColor, buffer, 2);
RichString_appendn(str, processMegabytesColor, buffer + 2, 3); RichString_appendn(str, processMegabytesColor, buffer+2, 3);
RichString_appendn(str, processColor, buffer + 5, 3); RichString_appendn(str, processColor, buffer+5, 3);
RichString_appendn(str, processShadowColor, buffer + 8, 4); RichString_appendn(str, processShadowColor, buffer+8, 4);
} }
} }
@ -291,11 +291,11 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
for (int i = 0; i < 32; i++) { for (int i = 0; i < 32; i++) {
if (indent & (1U << i)) { if (indent & (1U << i)) {
maxIndent = i + 1; maxIndent = i+1;
} }
} }
for (int i = 0; i < maxIndent - 1; i++) { for (int i = 0; i < maxIndent - 1; i++) {
int written, ret; int written, ret;
if (indent & (1 << i)) { if (indent & (1 << i)) {
ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]); ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
@ -333,7 +333,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
case PID: xSnprintf(buffer, n, Process_pidFormat, this->pid); break; case PID: xSnprintf(buffer, n, Process_pidFormat, this->pid); break;
case PPID: xSnprintf(buffer, n, Process_pidFormat, this->ppid); break; case PPID: xSnprintf(buffer, n, Process_pidFormat, this->ppid); break;
case PRIORITY: { case PRIORITY: {
if (this->priority <= -100) if(this->priority <= -100)
xSnprintf(buffer, n, " RT "); xSnprintf(buffer, n, " RT ");
else else
xSnprintf(buffer, n, "%3ld ", this->priority); xSnprintf(buffer, n, "%3ld ", this->priority);
@ -394,6 +394,14 @@ void Process_display(const Object* cast, RichString* out) {
RichString_setAttr(out, CRT_colors[PROCESS_TAG]); RichString_setAttr(out, CRT_colors[PROCESS_TAG]);
} }
if (this->settings->highlightChanges) {
if (Process_isTomb(this)) {
out->highlightAttr = CRT_colors[PROCESS_TOMB];
} else if (Process_isNew(this)) {
out->highlightAttr = CRT_colors[PROCESS_NEW];
}
}
assert(out->chlen > 0); assert(out->chlen > 0);
} }
@ -429,6 +437,18 @@ void Process_toggleTag(Process* this) {
this->tag = this->tag == true ? false : true; this->tag = this->tag == true ? false : true;
} }
bool Process_isNew(const Process* this) {
assert(this->processList);
if (this->processList->scanTs >= this->seenTs) {
return this->processList->scanTs - this->seenTs <= this->processList->settings->highlightDelaySecs;
}
return false;
}
bool Process_isTomb(const Process* this) {
return this->tombTs > 0;
}
bool Process_setPriority(Process* this, int priority) { bool Process_setPriority(Process* this, int priority) {
CRT_dropPrivileges(); CRT_dropPrivileges();
int old_prio = getpriority(PRIO_PROCESS, this->pid); int old_prio = getpriority(PRIO_PROCESS, this->pid);

View File

@ -9,18 +9,19 @@ in the source distribution for its full text.
*/ */
#include <stdbool.h> #include <stdbool.h>
#include <time.h>
#include <sys/types.h> #include <sys/types.h>
#include "Object.h" #include "Object.h"
#include "RichString.h" #include "RichString.h"
#ifdef __ANDROID__ #ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get #define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set #define SYS_ioprio_set __NR_ioprio_set
#endif #endif
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x0001
#define DEFAULT_HIGHLIGHT_SECS 5
typedef enum ProcessFields { typedef enum ProcessFields {
NULL_PROCESSFIELD = 0, NULL_PROCESSFIELD = 0,
@ -59,6 +60,7 @@ struct Settings_;
typedef struct Process_ { typedef struct Process_ {
Object super; Object super;
const struct ProcessList_* processList;
const struct Settings_* settings; const struct Settings_* settings;
unsigned long long int time; unsigned long long int time;
@ -76,6 +78,7 @@ typedef struct Process_ {
bool tag; bool tag;
bool showChildren; bool showChildren;
bool show; bool show;
bool wasShown;
unsigned int pgrp; unsigned int pgrp;
unsigned int session; unsigned int session;
unsigned int tty_nr; unsigned int tty_nr;
@ -99,6 +102,9 @@ typedef struct Process_ {
int exit_signal; int exit_signal;
time_t seenTs;
time_t tombTs;
unsigned long int minflt; unsigned long int minflt;
unsigned long int majflt; unsigned long int majflt;
} Process; } Process;
@ -119,7 +125,7 @@ extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[]; extern ProcessPidColumn Process_pidColumns[];
extern char Process_pidFormat[20]; extern char Process_pidFormat[20];
typedef Process* (* Process_New)(const struct Settings_*); typedef Process*(*Process_New)(const struct Settings_*);
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField); typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
typedef struct ProcessClass_ { typedef struct ProcessClass_ {
@ -172,6 +178,10 @@ void Process_init(Process* this, const struct Settings_* settings);
void Process_toggleTag(Process* this); void Process_toggleTag(Process* this);
bool Process_isNew(const Process* this);
bool Process_isTomb(const Process* this);
bool Process_setPriority(Process* this, int priority); bool Process_setPriority(Process* this, int priority);
bool Process_changePriorityBy(Process* this, Arg delta); bool Process_changePriorityBy(Process* this, Arg delta);

View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <time.h>
#include "CRT.h" #include "CRT.h"
#include "XUtils.h" #include "XUtils.h"
@ -27,6 +28,9 @@ ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, Users
// set later by platform-specific code // set later by platform-specific code
this->cpuCount = 0; this->cpuCount = 0;
this->scanTs = 0;
this->firstScanTs = 0;
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
this->topologyOk = false; this->topologyOk = false;
if (hwloc_topology_init(&this->topology) == 0) { if (hwloc_topology_init(&this->topology) == 0) {
@ -85,6 +89,14 @@ void ProcessList_printHeader(ProcessList* this, RichString* header) {
void ProcessList_add(ProcessList* this, Process* p) { void ProcessList_add(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1); assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
assert(Hashtable_get(this->processTable, p->pid) == NULL); assert(Hashtable_get(this->processTable, p->pid) == NULL);
p->processList = this;
if (this->scanTs == this->firstScanTs) {
// prevent highlighting processes found in first scan
p->seenTs = this->firstScanTs - this->settings->highlightDelaySecs - 1;
} else {
p->seenTs = this->scanTs;
}
Vector_add(this->processes, p); Vector_add(this->processes, p);
Hashtable_put(this->processTable, p->pid, p); Hashtable_put(this->processTable, p->pid, p);
@ -145,10 +157,10 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i
Vector_insert(this->processes2, 0, process); Vector_insert(this->processes2, 0, process);
} }
assert(Vector_size(this->processes2) == s + 1); (void)s; assert(Vector_size(this->processes2) == s+1); (void)s;
int nextIndent = indent | (1 << level); int nextIndent = indent | (1 << level);
ProcessList_buildTree(this, process->pid, level + 1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false); ProcessList_buildTree(this, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false);
if (i == size - 1) { if (i == size - 1) {
process->indent = -nextIndent; process->indent = -nextIndent;
@ -160,8 +172,8 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i
} }
static long ProcessList_treeProcessCompare(const void* v1, const void* v2) { static long ProcessList_treeProcessCompare(const void* v1, const void* v2) {
const Process* p1 = (const Process*)v1; const Process *p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2; const Process *p2 = (const Process*)v2;
return p1->pid - p2->pid; return p1->pid - p2->pid;
} }
@ -304,6 +316,7 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting,
} }
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
struct timespec now;
// in pause mode only gather global data for meters (CPU/memory/...) // in pause mode only gather global data for meters (CPU/memory/...)
if (pauseProcessUpdate) { if (pauseProcessUpdate) {
@ -315,6 +328,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
for (int i = 0; i < Vector_size(this->processes); i++) { for (int i = 0; i < Vector_size(this->processes); i++) {
Process* p = (Process*) Vector_get(this->processes, i); Process* p = (Process*) Vector_get(this->processes, i);
p->updated = false; p->updated = false;
p->wasShown = p->show;
p->show = true; p->show = true;
} }
@ -323,12 +337,33 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
this->kernelThreads = 0; this->kernelThreads = 0;
this->runningTasks = 0; this->runningTasks = 0;
// set scanTs
if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
if (this->firstScanTs == 0) {
this->firstScanTs = now.tv_sec;
}
this->scanTs = now.tv_sec;
}
ProcessList_goThroughEntries(this, false); ProcessList_goThroughEntries(this, false);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* p = (Process*) Vector_get(this->processes, i); Process* p = (Process*) Vector_get(this->processes, i);
if (p->updated == false) { if (p->tombTs > 0) {
// remove tombed process
if (this->scanTs >= p->tombTs) {
ProcessList_remove(this, p); ProcessList_remove(this, p);
}
} else if (p->updated == false) {
// process no longer exists
if (this->settings->highlightChanges && p->wasShown) {
// mark tombed
p->tombTs = this->scanTs + this->settings->highlightDelaySecs;
} else {
// immediately remove
ProcessList_remove(this, p);
}
} else { } else {
p->updated = false; p->updated = false;
} }

View File

@ -70,6 +70,8 @@ typedef struct ProcessList_ {
int cpuCount; int cpuCount;
time_t scanTs;
time_t firstScanTs;
} ProcessList; } ProcessList;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);

View File

@ -39,6 +39,7 @@ typedef struct RichString_ {
int chlen; int chlen;
CharType* chptr; CharType* chptr;
CharType chstr[RICHSTRING_MAXLEN + 1]; CharType chstr[RICHSTRING_MAXLEN + 1];
int highlightAttr;
} RichString; } RichString;
void RichString_setAttrn(RichString* this, int attrs, int start, int finish); void RichString_setAttrn(RichString* this, int attrs, int start, int finish);

View File

@ -102,6 +102,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
struct timeval tv; struct timeval tv;
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000); double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
*timedOut = (newTime - *oldTime > this->settings->delay); *timedOut = (newTime - *oldTime > this->settings->delay);
*rescan |= *timedOut; *rescan |= *timedOut;

View File

@ -157,6 +157,10 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
this->highlightMegabytes = atoi(option[1]); this->highlightMegabytes = atoi(option[1]);
} else if (String_eq(option[0], "highlight_threads")) { } else if (String_eq(option[0], "highlight_threads")) {
this->highlightThreads = atoi(option[1]); this->highlightThreads = atoi(option[1]);
} else if (String_eq(option[0], "highlight_changes")) {
this->highlightChanges = atoi(option[1]);
} else if (String_eq(option[0], "highlight_changes_delay_secs")) {
this->highlightDelaySecs = atoi(option[1]);
} else if (String_eq(option[0], "header_margin")) { } else if (String_eq(option[0], "header_margin")) {
this->headerMargin = atoi(option[1]); this->headerMargin = atoi(option[1]);
} else if (String_eq(option[0], "expand_system_time")) { } else if (String_eq(option[0], "expand_system_time")) {
@ -265,6 +269,8 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName); fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes); fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads); fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
fprintf(fd, "highlight_changes=%d\n", (int) this->highlightChanges);
fprintf(fd, "highlight_changes_delay_secs=%d\n", (int) this->highlightDelaySecs);
fprintf(fd, "tree_view=%d\n", (int) this->treeView); fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
@ -306,6 +312,8 @@ Settings* Settings_new(int initialCpuCount) {
this->updateProcessNames = false; this->updateProcessNames = false;
this->showProgramPath = true; this->showProgramPath = true;
this->highlightThreads = true; this->highlightThreads = true;
this->highlightChanges = false;
this->highlightDelaySecs = DEFAULT_HIGHLIGHT_SECS;
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
this->topologyAffinity = false; this->topologyAffinity = false;
#endif #endif

View File

@ -47,6 +47,8 @@ typedef struct Settings_ {
bool highlightBaseName; bool highlightBaseName;
bool highlightMegabytes; bool highlightMegabytes;
bool highlightThreads; bool highlightThreads;
bool highlightChanges;
int highlightDelaySecs;
bool updateProcessNames; bool updateProcessNames;
bool accountGuestInCPUMeter; bool accountGuestInCPUMeter;
bool headerMargin; bool headerMargin;

View File

@ -4,7 +4,7 @@ htop \- interactive process viewer
.SH "SYNOPSIS" .SH "SYNOPSIS"
.LP .LP
.B htop .B htop
.RB [ \-dCFhpustv ] .RB [ \-dCFhpustvH ]
.SH "DESCRIPTION" .SH "DESCRIPTION"
.LP .LP
.B htop .B htop
@ -62,6 +62,9 @@ Output version information and exit
.TP .TP
\fB\-t \-\-tree \fB\-t \-\-tree
Show processes in tree view Show processes in tree view
.TP
\fB\-H \-\-highlight-changes=DELAY\fR
Highlight new and old processes
.SH "INTERACTIVE COMMANDS" .SH "INTERACTIVE COMMANDS"
.LP .LP
The following commands are supported while in The following commands are supported while in

50
htop.c
View File

@ -50,8 +50,9 @@ static void printHelpFlag(void) {
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n" "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
"-F --filter=FILTER Show only the commands matching the given filter\n" "-F --filter=FILTER Show only the commands matching the given filter\n"
"-h --help Print this help screen\n" "-h --help Print this help screen\n"
"-H --highlight-changes[=DELAY] Highlight new and old processes\n"
"-M --no-mouse Disable the mouse\n" "-M --no-mouse Disable the mouse\n"
"-p --pid=PID,[,PID,PID...] Show only the given PIDs\n" "-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
"-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n" "-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n"
"-t --tree Show the tree view by default\n" "-t --tree Show the tree view by default\n"
"-u --user[=USERNAME] Show only processes for a given user (or $USER)\n" "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
@ -76,6 +77,8 @@ typedef struct CommandLineSettings_ {
bool enableMouse; bool enableMouse;
bool treeView; bool treeView;
bool allowUnicode; bool allowUnicode;
bool highlightChanges;
int highlightDelaySecs;
} CommandLineSettings; } CommandLineSettings;
static CommandLineSettings parseArguments(int argc, char** argv) { static CommandLineSettings parseArguments(int argc, char** argv) {
@ -90,6 +93,8 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.enableMouse = true, .enableMouse = true,
.treeView = false, .treeView = false,
.allowUnicode = true, .allowUnicode = true,
.highlightChanges = false,
.highlightDelaySecs = -1,
}; };
static struct option long_opts[] = static struct option long_opts[] =
@ -106,12 +111,13 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
{"tree", no_argument, 0, 't'}, {"tree", no_argument, 0, 't'},
{"pid", required_argument, 0, 'p'}, {"pid", required_argument, 0, 'p'},
{"filter", required_argument, 0, 'F'}, {"filter", required_argument, 0, 'F'},
{0, 0, 0, 0} {"highlight-changes", optional_argument, 0, 'H'},
{0,0,0,0}
}; };
int opt, opti = 0; int opt, opti=0;
/* Parse arguments */ /* Parse arguments */
while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:", long_opts, &opti))) { while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:H::", long_opts, &opti))) {
if (opt == EOF) break; if (opt == EOF) break;
switch (opt) { switch (opt) {
case 'h': case 'h':
@ -186,11 +192,11 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
char* saveptr; char* saveptr;
char* pid = strtok_r(argCopy, ",", &saveptr); char* pid = strtok_r(argCopy, ",", &saveptr);
if (!flags.pidMatchList) { if(!flags.pidMatchList) {
flags.pidMatchList = Hashtable_new(8, false); flags.pidMatchList = Hashtable_new(8, false);
} }
while (pid) { while(pid) {
unsigned int num_pid = atoi(pid); unsigned int num_pid = atoi(pid);
// deepcode ignore CastIntegerToAddress: we just want a non-NUll pointer here // deepcode ignore CastIntegerToAddress: we just want a non-NUll pointer here
Hashtable_put(flags.pidMatchList, num_pid, (void *) 1); Hashtable_put(flags.pidMatchList, num_pid, (void *) 1);
@ -206,6 +212,24 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
break; break;
} }
case 'H': {
const char *delay = optarg;
if (!delay && optind < argc && argv[optind] != NULL &&
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
delay = argv[optind++];
}
if (delay) {
if (sscanf(delay, "%16d", &(flags.highlightDelaySecs)) == 1) {
if (flags.highlightDelaySecs < 1)
flags.highlightDelaySecs = 1;
} else {
fprintf(stderr, "Error: invalid highlight delay value \"%s\".\n", delay);
exit(1);
}
}
flags.highlightChanges = true;
break;
}
default: default:
exit(1); exit(1);
} }
@ -218,7 +242,7 @@ static void millisleep(unsigned long millisec) {
.tv_sec = 0, .tv_sec = 0,
.tv_nsec = millisec * 1000000L .tv_nsec = millisec * 1000000L
}; };
while (nanosleep(&req, &req) == -1) { while(nanosleep(&req,&req)==-1) {
continue; continue;
} }
} }
@ -242,7 +266,7 @@ static void setCommFilter(State* state, char** commFilter) {
int main(int argc, char** argv) { int main(int argc, char** argv) {
char* lc_ctype = getenv("LC_CTYPE"); char *lc_ctype = getenv("LC_CTYPE");
if (lc_ctype != NULL) { if (lc_ctype != NULL) {
setlocale(LC_CTYPE, lc_ctype); setlocale(LC_CTYPE, lc_ctype);
} else if ((lc_ctype = getenv("LC_ALL"))) { } else if ((lc_ctype = getenv("LC_ALL"))) {
@ -284,6 +308,12 @@ int main(int argc, char** argv) {
if (flags.treeView) { if (flags.treeView) {
settings->treeView = true; settings->treeView = true;
} }
if (flags.highlightChanges) {
settings->highlightChanges = true;
}
if (flags.highlightDelaySecs != -1) {
settings->highlightDelaySecs = flags.highlightDelaySecs;
}
CRT_init(settings->delay, settings->colorScheme, flags.allowUnicode); CRT_init(settings->delay, settings->colorScheme, flags.allowUnicode);
@ -323,7 +353,7 @@ int main(int argc, char** argv) {
ScreenManager_run(scr, NULL, NULL); ScreenManager_run(scr, NULL, NULL);
attron(CRT_colors[RESET_COLOR]); attron(CRT_colors[RESET_COLOR]);
mvhline(LINES - 1, 0, ' ', COLS); mvhline(LINES-1, 0, ' ', COLS);
attroff(CRT_colors[RESET_COLOR]); attroff(CRT_colors[RESET_COLOR]);
refresh(); refresh();
@ -339,7 +369,7 @@ int main(int argc, char** argv) {
UsersTable_delete(ut); UsersTable_delete(ut);
Settings_delete(settings); Settings_delete(settings);
if (flags.pidMatchList) { if(flags.pidMatchList) {
Hashtable_delete(flags.pidMatchList); Hashtable_delete(flags.pidMatchList);
} }
return 0; return 0;