Highlight new and old processes (#74)

This commit is contained in:
Adam Saponara 2020-10-30 21:56:16 -04:00
parent bbf01054bf
commit dde71c6637
14 changed files with 155 additions and 35 deletions

12
CRT.c
View File

@ -111,6 +111,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,
@ -188,6 +190,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,
@ -265,6 +269,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),
@ -342,6 +348,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),
@ -419,6 +427,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),
@ -498,6 +508,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

@ -72,6 +72,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

@ -266,16 +266,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

@ -6,6 +6,7 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep #include "config.h" // IWYU pragma: keep
#include "Process.h" #include "Process.h"
@ -381,6 +382,12 @@ void Process_display(const Object* cast, RichString* out) {
RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]); RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]);
if (this->tag == true) if (this->tag == true)
RichString_setAttr(out, CRT_colors[PROCESS_TAG]); RichString_setAttr(out, CRT_colors[PROCESS_TAG]);
if (this->settings->highlightChanges) {
if (Process_isNew(this))
out->highlightAttr = CRT_colors[PROCESS_NEW];
if (Process_isTomb(this))
out->highlightAttr = CRT_colors[PROCESS_TOMB];
}
assert(out->chlen > 0); assert(out->chlen > 0);
} }
@ -413,6 +420,16 @@ 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) {
if (this->processList && 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

@ -10,17 +10,18 @@ in the source distribution for its full text.
#include <stdbool.h> #include <stdbool.h>
#include <sys/types.h> #include <sys/types.h>
#include <time.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;
@ -99,6 +101,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;
@ -172,6 +177,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,11 +9,11 @@ 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"
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
this->processes = Vector_new(klass, true, DEFAULT_SIZE); this->processes = Vector_new(klass, true, DEFAULT_SIZE);
this->processTable = Hashtable_new(140, false); this->processTable = Hashtable_new(140, false);
@ -27,6 +27,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) {
@ -81,6 +84,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);
@ -283,6 +294,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) {
@ -302,13 +314,35 @@ 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) {
ProcessList_remove(this, p); // remove tombed process
else if (this->scanTs >= p->tombTs) {
ProcessList_remove(this, p);
}
} else if (p->updated == false) {
// process no longer exists
if (this->settings->highlightChanges) {
// mark tombed
p->tombTs = this->scanTs + this->settings->highlightDelaySecs;
} else {
// immediately remove
ProcessList_remove(this, p);
}
} 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 = *rescan || *timedOut; *rescan = *rescan || *timedOut;
if (newTime < *oldTime) *rescan = true; // clock was adjusted? if (newTime < *oldTime) *rescan = true; // clock was adjusted?

View File

@ -158,6 +158,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);
@ -307,6 +313,7 @@ Settings* Settings_new(int initialCpuCount) {
this->updateProcessNames = false; this->updateProcessNames = false;
this->showProgramPath = true; this->showProgramPath = true;
this->highlightThreads = true; this->highlightThreads = true;
this->highlightDelaySecs = DEFAULT_HIGHLIGHT_SECS;
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
this->topologyAffinity = false; this->topologyAffinity = false;
#endif #endif

View File

@ -48,6 +48,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

75
htop.c
View File

@ -46,17 +46,18 @@ static void printHelpFlag(void) {
fputs("htop " VERSION "\n" fputs("htop " VERSION "\n"
COPYRIGHT "\n" COPYRIGHT "\n"
"Released under the GNU GPLv2.\n\n" "Released under the GNU GPLv2.\n\n"
"-C --no-color Use a monochrome color scheme\n" "-C --no-color Use a monochrome color scheme\n"
"-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"
"-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"
"-U --no-unicode Do not use unicode but plain ASCII\n" "-U --no-unicode Do not use unicode but plain ASCII\n"
"-V --version Print version info\n" "-V --version Print version info\n"
"-H --highlight-changes[=DELAY] Highlight new and old processes\n"
"\n" "\n"
"Long options may be passed with a single dash.\n\n" "Long options may be passed with a single dash.\n\n"
"Press F1 inside htop for online help.\n" "Press F1 inside htop for online help.\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,28 +93,31 @@ 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[] =
{ {
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'}, {"version", no_argument, 0, 'V'},
{"delay", required_argument, 0, 'd'}, {"delay", required_argument, 0, 'd'},
{"sort-key", required_argument, 0, 's'}, {"sort-key", required_argument, 0, 's'},
{"user", optional_argument, 0, 'u'}, {"user", optional_argument, 0, 'u'},
{"no-color", no_argument, 0, 'C'}, {"no-color", no_argument, 0, 'C'},
{"no-colour", no_argument, 0, 'C'}, {"no-colour", no_argument, 0, 'C'},
{"no-mouse", no_argument, 0, 'M'}, {"no-mouse", no_argument, 0, 'M'},
{"no-unicode", no_argument, 0, 'U'}, {"no-unicode", no_argument, 0, 'U'},
{"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'},
{"highlight-changes", optional_argument, 0, 'H'},
{0,0,0,0} {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':
@ -198,6 +204,23 @@ 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);
} }
@ -271,6 +294,10 @@ int main(int argc, char** argv) {
settings->enableMouse = false; settings->enableMouse = false;
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);