mirror of
https://github.com/xzeldon/htop.git
synced 2025-07-15 21:44:36 +03:00
Compare commits
172 Commits
Author | SHA1 | Date | |
---|---|---|---|
ce6d60e7de | |||
5d92a9f20d | |||
b3500ac3b7 | |||
2ba8a81d47 | |||
f2f1c99ad9 | |||
1ffe5d79bd | |||
8502f4e64f | |||
a5db139a0a | |||
8a67d7f086 | |||
0b89c66f58 | |||
3bb731c645 | |||
fbaa0cd146 | |||
a076488809 | |||
6301d5c1da | |||
4979245aa5 | |||
0155980fd6 | |||
2af90b711f | |||
d55f394541 | |||
c7d93a8f30 | |||
2d2a2df6f2 | |||
de645ea16c | |||
6ae56f2578 | |||
bfcb8ca019 | |||
d800d7a3ce | |||
27db9297b7 | |||
330d4fe22f | |||
8e10cde800 | |||
ca2c01bd16 | |||
7043a93eba | |||
8fe04b7494 | |||
43d5c61884 | |||
e103ec0317 | |||
7ff654f2df | |||
7386c6fed0 | |||
ce9e7fd14f | |||
badeaf9e82 | |||
a3cced9fb6 | |||
24c5ca9ddf | |||
eb6f8d569d | |||
8c8149d146 | |||
a150a81669 | |||
90ea3ac3c9 | |||
293c16e22d | |||
f6aa5d29bb | |||
2c06566405 | |||
d609c04fe4 | |||
ca9d7cd708 | |||
debeac49cd | |||
a0b899f29d | |||
8b83a9f055 | |||
495f2292dc | |||
1cc3f8074f | |||
aa08279964 | |||
5359eae28b | |||
f1463fdd64 | |||
3edb6e1ea3 | |||
71ddc6a6a1 | |||
b9336af76f | |||
f46ddd3230 | |||
94d7f0b585 | |||
86d2931255 | |||
0672be7db1 | |||
0b989ee38c | |||
3fb0024fd3 | |||
dfb9b82607 | |||
fc7aead36b | |||
737cd6167a | |||
6502b02666 | |||
cdfd407e2e | |||
64c05a1ed5 | |||
a7612b0b7d | |||
3ec8f67ab2 | |||
10c6810bff | |||
068561351f | |||
9b8b380c32 | |||
a09ad6b8b4 | |||
9a86577cf2 | |||
8db8b9edac | |||
4a73e80338 | |||
5fa1c7040d | |||
3f9c63d5c0 | |||
358d20687f | |||
e3862aa67e | |||
7e7a53c415 | |||
6b100b0cf4 | |||
6e46fd6f1f | |||
22da57d621 | |||
c5e31ba4aa | |||
f878f302ca | |||
67ccd6b909 | |||
f614b8a19f | |||
c150e4bde9 | |||
9f68c8d341 | |||
89473cc9ae | |||
d872e36308 | |||
77db240b48 | |||
2327260ee8 | |||
e8c6994f40 | |||
3d1703f16f | |||
52fa4e7ee4 | |||
27b8d81ed2 | |||
26993d2d2b | |||
0401df8cbd | |||
0cb257586a | |||
1193c6e349 | |||
edd6130be7 | |||
107e3c8aa5 | |||
4eeeb63647 | |||
eb36385a6b | |||
79970f05f3 | |||
61b8e31b41 | |||
c9583c692d | |||
4507911cc3 | |||
b7836515e8 | |||
a3db2da4a7 | |||
cf982f2928 | |||
8d69a9a53e | |||
366b78edd9 | |||
f8a610e6e1 | |||
4b1a4a4ebd | |||
3655b6ca0b | |||
1506283aff | |||
4b877eb16a | |||
f32f0188cd | |||
e65cdf947c | |||
ab60f59ed8 | |||
8149823d56 | |||
12421f460a | |||
880eecabf5 | |||
738d31b903 | |||
28bc087d8a | |||
2700d99069 | |||
75e9f9a8d9 | |||
db5687a355 | |||
7b739b6292 | |||
ded9c5d363 | |||
2d231d77ca | |||
f6613db5cd | |||
4c44a70f96 | |||
157086e750 | |||
5506925b34 | |||
c6d9fa279b | |||
dcf7ad386c | |||
30bf212185 | |||
05969998c1 | |||
ead978bce6 | |||
4f88d38256 | |||
f03f48a0fb | |||
ad8aa2ce77 | |||
b92cfa7d7a | |||
57d9ecc551 | |||
ad764ff972 | |||
77ec86aff4 | |||
e1ce141bc3 | |||
d9224c66a4 | |||
3d15ba5197 | |||
7ba25aa3c4 | |||
22f8f8000c | |||
4c4ba9d949 | |||
8d1595a20e | |||
876194492f | |||
5f528b7455 | |||
641fd2c4ad | |||
f913680020 | |||
f0a9dfc37e | |||
1e9b184367 | |||
ba1549f99b | |||
f61e74a4af | |||
8029e9af04 | |||
ef0fc7129e | |||
bc16fa037f | |||
cc7f16bb8f |
26
.github/workflows/ci.yml
vendored
26
.github/workflows/ci.yml
vendored
@ -2,6 +2,11 @@ name: CI
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
env:
|
||||
# Enable format attributes in ncurses headers
|
||||
# Enable fortified memory/string handling
|
||||
CPPFLAGS: -DGCC_PRINTF -DGCC_SCANF -D_FORTIFY_SOURCE=2
|
||||
|
||||
jobs:
|
||||
build-ubuntu-latest-minimal-gcc:
|
||||
runs-on: ubuntu-latest
|
||||
@ -47,6 +52,10 @@ jobs:
|
||||
|
||||
build-ubuntu-latest-full-featured-gcc:
|
||||
runs-on: ubuntu-latest
|
||||
# Enable LTO, might trigger additional warnings on advanced inlining
|
||||
env:
|
||||
CFLAGS: -O3 -g -flto
|
||||
LDFLAGS: -O3 -g -flto
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
@ -102,6 +111,23 @@ jobs:
|
||||
- name: Build
|
||||
run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)"
|
||||
|
||||
build-macos-latest-clang:
|
||||
runs-on: macOS-latest
|
||||
env:
|
||||
CC: clang
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: brew install automake
|
||||
- name: Bootstrap
|
||||
run: ./autogen.sh
|
||||
- name: Configure
|
||||
run: ./configure --enable-werror
|
||||
- name: Build
|
||||
run: make -k
|
||||
- name: Distcheck
|
||||
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror"
|
||||
|
||||
whitespace_check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
11
.travis.yml
11
.travis.yml
@ -6,17 +6,6 @@ compiler:
|
||||
|
||||
os:
|
||||
- freebsd
|
||||
- linux
|
||||
- osx
|
||||
|
||||
arch:
|
||||
- amd64
|
||||
- s390x
|
||||
|
||||
before_script:
|
||||
if [[ ${TRAVIS_CPU_ARCH} == 's390x' ]]; then
|
||||
sudo apt-get update && sudo apt-get install -y libncursesw5-dev ;
|
||||
fi
|
||||
|
||||
script:
|
||||
- ./autogen.sh
|
||||
|
12
AUTHORS
12
AUTHORS
@ -1 +1,11 @@
|
||||
Hisham H. Muhammad
|
||||
Originally authored by:
|
||||
Hisham H. Muhammad
|
||||
|
||||
Currently maintained by the htop dev team:
|
||||
Benny Baumann
|
||||
Christian Göttsche
|
||||
Daniel Lange
|
||||
Nathan Scott
|
||||
|
||||
For the full list of contributors see:
|
||||
git log --format="%aN" | sort -u
|
||||
|
158
Action.c
158
Action.c
@ -104,7 +104,7 @@ static bool changePriority(MainPanel* panel, int delta) {
|
||||
return anyTagged;
|
||||
}
|
||||
|
||||
static void addUserToVector(hkey_t key, void* userCast, void* panelCast) {
|
||||
static void addUserToVector(ht_key_t key, void* userCast, void* panelCast) {
|
||||
const char* user = userCast;
|
||||
Panel* panel = panelCast;
|
||||
Panel_add(panel, (Object*) ListItem_new(user, key));
|
||||
@ -158,20 +158,21 @@ static bool collapseIntoParent(Panel* panel) {
|
||||
}
|
||||
|
||||
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
|
||||
settings->sortKey = sortKey;
|
||||
settings->direction = 1;
|
||||
Settings_setSortKey(settings, sortKey);
|
||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction sortBy(State* st) {
|
||||
// ----------------------------------------
|
||||
|
||||
static Htop_Reaction actionSetSortColumn(State* st) {
|
||||
Htop_Reaction reaction = HTOP_OK;
|
||||
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
|
||||
Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel "));
|
||||
Panel_setHeader(sortPanel, "Sort by");
|
||||
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)
|
||||
if (fields[i] == Settings_getActiveSortKey(st->settings))
|
||||
Panel_setSelected(sortPanel, i);
|
||||
|
||||
free(name);
|
||||
@ -188,12 +189,8 @@ static Htop_Reaction sortBy(State* st) {
|
||||
return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
static Htop_Reaction actionResize(State* st) {
|
||||
clear();
|
||||
Panel_resize(st->panel, COLS, LINES - (st->panel->y) - 1);
|
||||
return HTOP_REDRAW_BAR;
|
||||
static Htop_Reaction actionSortByPID(State* st) {
|
||||
return Action_setSortKey(st->settings, PID);
|
||||
}
|
||||
|
||||
static Htop_Reaction actionSortByMemory(State* st) {
|
||||
@ -231,7 +228,7 @@ static Htop_Reaction actionToggleMergedCommand(State* st) {
|
||||
static Htop_Reaction actionToggleTreeView(State* st) {
|
||||
st->settings->treeView = !st->settings->treeView;
|
||||
if (st->settings->treeView) {
|
||||
st->settings->direction = 1;
|
||||
st->settings->treeDirection = 1;
|
||||
}
|
||||
|
||||
ProcessList_expandTree(st->pl);
|
||||
@ -251,16 +248,6 @@ static Htop_Reaction actionIncSearch(State* st) {
|
||||
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionIncNext(State* st) {
|
||||
IncSet_next(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
|
||||
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionIncPrev(State* st) {
|
||||
IncSet_prev(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
|
||||
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionHigherPriority(State* st) {
|
||||
bool changed = changePriority((MainPanel*)st->panel, -1);
|
||||
return changed ? HTOP_REFRESH : HTOP_OK;
|
||||
@ -273,13 +260,11 @@ static Htop_Reaction actionLowerPriority(State* st) {
|
||||
|
||||
static Htop_Reaction actionInvertSortOrder(State* st) {
|
||||
Settings_invertSortOrder(st->settings);
|
||||
if (st->pauseProcessUpdate)
|
||||
ProcessList_sort(st->pl);
|
||||
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;
|
||||
@ -340,7 +325,7 @@ static Htop_Reaction actionKill(State* st) {
|
||||
if (sgn) {
|
||||
if (sgn->key != 0) {
|
||||
Panel_setHeader(st->panel, "Sending...");
|
||||
Panel_draw(st->panel, true, true);
|
||||
Panel_draw(st->panel, false, true, true, State_hideFunctionBar(st));
|
||||
refresh();
|
||||
MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL);
|
||||
napms(500);
|
||||
@ -351,7 +336,7 @@ static Htop_Reaction actionKill(State* st) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionFilterByUser(State* st) {
|
||||
Panel* usersPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Show ", "Cancel "));
|
||||
Panel* usersPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Show ", "Cancel "));
|
||||
Panel_setHeader(usersPanel, "Show processes of:");
|
||||
UsersTable_foreach(st->ut, addUserToVector, usersPanel);
|
||||
Vector_insertionSort(usersPanel->items);
|
||||
@ -371,7 +356,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
|
||||
|
||||
Htop_Reaction Action_follow(State* st) {
|
||||
st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel);
|
||||
Panel_setSelectionColor(st->panel, CRT_colors[PANEL_SELECTION_FOLLOW]);
|
||||
Panel_setSelectionColor(st->panel, PANEL_SELECTION_FOLLOW);
|
||||
return HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
@ -460,8 +445,8 @@ static const struct {
|
||||
{ .key = " H: ", .info = "hide/show user process 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 = " + -: ", .info = "expand/collapse tree" },
|
||||
{ .key = "N P M T: ", .info = "sort by PID, CPU%, MEM% or TIME" },
|
||||
{ .key = " I: ", .info = "invert sort order" },
|
||||
{ .key = " F6 > .: ", .info = "select sort column" },
|
||||
{ .key = NULL, .info = NULL }
|
||||
@ -562,24 +547,24 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
int item;
|
||||
for (item = 0; helpLeft[item].key; item++) {
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line + item, 9, helpLeft[item].info);
|
||||
mvaddstr(line + item, 10, helpLeft[item].info);
|
||||
attrset(CRT_colors[HELP_BOLD]);
|
||||
mvaddstr(line + item, 0, helpLeft[item].key);
|
||||
mvaddstr(line + item, 1, helpLeft[item].key);
|
||||
if (String_eq(helpLeft[item].key, " H: ")) {
|
||||
attrset(CRT_colors[PROCESS_THREAD]);
|
||||
mvaddstr(line + item, 32, "threads");
|
||||
mvaddstr(line + item, 33, "threads");
|
||||
} else if (String_eq(helpLeft[item].key, " K: ")) {
|
||||
attrset(CRT_colors[PROCESS_THREAD]);
|
||||
mvaddstr(line + item, 26, "threads");
|
||||
mvaddstr(line + item, 27, "threads");
|
||||
}
|
||||
}
|
||||
int leftHelpItems = item;
|
||||
|
||||
for (item = 0; helpRight[item].key; item++) {
|
||||
attrset(CRT_colors[HELP_BOLD]);
|
||||
mvaddstr(line + item, 40, helpRight[item].key);
|
||||
mvaddstr(line + item, 41, helpRight[item].key);
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line + item, 49, helpRight[item].info);
|
||||
mvaddstr(line + item, 50, helpRight[item].info);
|
||||
}
|
||||
line += MAXIMUM(leftHelpItems, item);
|
||||
line++;
|
||||
@ -638,60 +623,57 @@ static Htop_Reaction actionShowCommandScreen(State* st) {
|
||||
}
|
||||
|
||||
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['p'] = actionToggleProgramPath;
|
||||
keys['m'] = actionToggleMergedCommand;
|
||||
keys['t'] = actionToggleTreeView;
|
||||
keys[KEY_F(5)] = actionToggleTreeView;
|
||||
keys[KEY_F(4)] = actionIncFilter;
|
||||
keys['\\'] = actionIncFilter;
|
||||
keys[KEY_F(3)] = actionIncSearch;
|
||||
keys['/'] = actionIncSearch;
|
||||
keys['n'] = actionIncNext;
|
||||
keys['N'] = actionIncPrev;
|
||||
|
||||
keys[']'] = actionHigherPriority;
|
||||
keys[KEY_F(7)] = actionHigherPriority;
|
||||
keys['['] = actionLowerPriority;
|
||||
keys[KEY_F(8)] = actionLowerPriority;
|
||||
keys['I'] = actionInvertSortOrder;
|
||||
keys[KEY_F(6)] = actionSetSortColumn;
|
||||
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[KEY_RECLICK] = actionExpandOrCollapse;
|
||||
keys['+'] = actionExpandOrCollapse;
|
||||
keys['='] = actionExpandOrCollapse;
|
||||
keys['-'] = actionExpandOrCollapse;
|
||||
keys['\177'] = actionCollapseIntoParent;
|
||||
keys['u'] = actionFilterByUser;
|
||||
keys['F'] = Action_follow;
|
||||
keys['S'] = actionSetup;
|
||||
keys['C'] = actionSetup;
|
||||
keys[KEY_F(2)] = actionSetup;
|
||||
keys['x'] = actionShowLocks;
|
||||
keys['l'] = actionLsof;
|
||||
keys['s'] = actionStrace;
|
||||
keys[' '] = actionTag;
|
||||
keys['\014'] = actionRedraw; // Ctrl+L
|
||||
keys[KEY_F(1)] = actionHelp;
|
||||
keys['h'] = actionHelp;
|
||||
keys['+'] = actionExpandOrCollapse;
|
||||
keys[','] = actionSetSortColumn;
|
||||
keys['-'] = actionExpandOrCollapse;
|
||||
keys['.'] = actionSetSortColumn;
|
||||
keys['/'] = actionIncSearch;
|
||||
keys['<'] = actionSetSortColumn;
|
||||
keys['='] = actionExpandOrCollapse;
|
||||
keys['>'] = actionSetSortColumn;
|
||||
keys['?'] = actionHelp;
|
||||
keys['C'] = actionSetup;
|
||||
keys['F'] = Action_follow;
|
||||
keys['H'] = actionToggleUserlandThreads;
|
||||
keys['I'] = actionInvertSortOrder;
|
||||
keys['K'] = actionToggleKernelThreads;
|
||||
keys['M'] = actionSortByMemory;
|
||||
keys['N'] = actionSortByPID;
|
||||
keys['P'] = actionSortByCPU;
|
||||
keys['S'] = actionSetup;
|
||||
keys['T'] = actionSortByTime;
|
||||
keys['U'] = actionUntagAll;
|
||||
keys['Z'] = actionTogglePauseProcessUpdate;
|
||||
keys['['] = actionLowerPriority;
|
||||
keys['\014'] = actionRedraw; // Ctrl+L
|
||||
keys['\177'] = actionCollapseIntoParent;
|
||||
keys['\\'] = actionIncFilter;
|
||||
keys[']'] = actionHigherPriority;
|
||||
keys['a'] = actionSetAffinity;
|
||||
keys['c'] = actionTagAllChildren;
|
||||
keys['e'] = actionShowEnvScreen;
|
||||
keys['h'] = actionHelp;
|
||||
keys['k'] = actionKill;
|
||||
keys['l'] = actionLsof;
|
||||
keys['m'] = actionToggleMergedCommand;
|
||||
keys['p'] = actionToggleProgramPath;
|
||||
keys['q'] = actionQuit;
|
||||
keys['s'] = actionStrace;
|
||||
keys['t'] = actionToggleTreeView;
|
||||
keys['u'] = actionFilterByUser;
|
||||
keys['w'] = actionShowCommandScreen;
|
||||
keys['Z'] = actionTogglePauseProcessUpdate;
|
||||
keys['x'] = actionShowLocks;
|
||||
keys[KEY_F(1)] = actionHelp;
|
||||
keys[KEY_F(2)] = actionSetup;
|
||||
keys[KEY_F(3)] = actionIncSearch;
|
||||
keys[KEY_F(4)] = actionIncFilter;
|
||||
keys[KEY_F(5)] = actionToggleTreeView;
|
||||
keys[KEY_F(6)] = actionSetSortColumn;
|
||||
keys[KEY_F(7)] = actionHigherPriority;
|
||||
keys[KEY_F(8)] = actionLowerPriority;
|
||||
keys[KEY_F(9)] = actionKill;
|
||||
keys[KEY_F(10)] = actionQuit;
|
||||
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
|
||||
keys[KEY_RECLICK] = actionExpandOrCollapse;
|
||||
}
|
||||
|
4
Action.h
4
Action.h
@ -41,6 +41,10 @@ typedef struct State_ {
|
||||
bool hideProcessSelection;
|
||||
} State;
|
||||
|
||||
static inline bool State_hideFunctionBar(const State* st) {
|
||||
return st->settings->hideFunctionBar == 2 || (st->settings->hideFunctionBar == 1 && st->hideProcessSelection);
|
||||
}
|
||||
|
||||
typedef Htop_Reaction (*Htop_Action)(State* st);
|
||||
|
||||
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess);
|
||||
|
@ -59,25 +59,25 @@ static void MaskItem_delete(Object* cast) {
|
||||
static void MaskItem_display(const Object* cast, RichString* out) {
|
||||
const MaskItem* this = (const MaskItem*)cast;
|
||||
assert (this != NULL);
|
||||
RichString_append(out, CRT_colors[CHECK_BOX], "[");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "[");
|
||||
if (this->value == 2) {
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], "x");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
|
||||
} else if (this->value == 1) {
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], "o");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "o");
|
||||
} else {
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], " ");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
|
||||
}
|
||||
RichString_append(out, CRT_colors[CHECK_BOX], "]");
|
||||
RichString_append(out, CRT_colors[CHECK_TEXT], " ");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
|
||||
if (this->indent) {
|
||||
RichString_append(out, CRT_colors[PROCESS_TREE], this->indent);
|
||||
RichString_append(out, CRT_colors[PROCESS_TREE],
|
||||
RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->indent);
|
||||
RichString_appendWide(out, CRT_colors[PROCESS_TREE],
|
||||
this->sub_tree == 2
|
||||
? CRT_treeStr[TREE_STR_OPEN]
|
||||
: CRT_treeStr[TREE_STR_SHUT]);
|
||||
RichString_append(out, CRT_colors[CHECK_TEXT], " ");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
|
||||
}
|
||||
RichString_append(out, CRT_colors[CHECK_TEXT], this->text);
|
||||
RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->text);
|
||||
}
|
||||
|
||||
static const ObjectClass MaskItem_class = {
|
||||
@ -173,7 +173,6 @@ static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) {
|
||||
Panel* super = (Panel*) this;
|
||||
|
||||
FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : "");
|
||||
FunctionBar_draw(super->currentBar);
|
||||
|
||||
int oldSelected = Panel_getSelectedIndex(super);
|
||||
Panel_prune(super);
|
||||
@ -281,7 +280,7 @@ static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
|
||||
indent_buf[0] = '\0';
|
||||
if (depth > 0) {
|
||||
for (unsigned i = 1; i < depth; i++) {
|
||||
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1u << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
|
||||
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1U << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
|
||||
size_t len = strlen(&indent_buf[off]);
|
||||
off += len;
|
||||
left -= len;
|
||||
@ -323,9 +322,9 @@ static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
|
||||
static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
|
||||
MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent);
|
||||
if (obj->next_sibling) {
|
||||
indent |= (1u << obj->depth);
|
||||
indent |= (1U << obj->depth);
|
||||
} else {
|
||||
indent &= ~(1u << obj->depth);
|
||||
indent &= ~(1U << obj->depth);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < obj->arity; i++) {
|
||||
|
@ -77,7 +77,7 @@ AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
|
||||
|
||||
Panel_setHeader(super, "Available Columns");
|
||||
|
||||
for (int i = 1; i < Platform_numberOfFields; i++) {
|
||||
for (int i = 1; i < LAST_PROCESSFIELD; i++) {
|
||||
if (i != COMM && Process_fields[i].description) {
|
||||
char description[256];
|
||||
xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);
|
||||
|
@ -35,7 +35,6 @@ static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, c
|
||||
Panel_add(panel, (Object*) Meter_toListItem(meter, false));
|
||||
Panel_setSelected(panel, Panel_size(panel) - 1);
|
||||
MetersPanel_setMoving((MetersPanel*)panel, true);
|
||||
FunctionBar_draw(panel->currentBar);
|
||||
}
|
||||
|
||||
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
|
||||
|
@ -21,7 +21,7 @@ static const int BatteryMeter_attributes[] = {
|
||||
BATTERY
|
||||
};
|
||||
|
||||
static void BatteryMeter_updateValues(Meter* this, char* buffer, int len) {
|
||||
static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) {
|
||||
ACPresence isOnAC;
|
||||
double percent;
|
||||
|
||||
@ -35,24 +35,21 @@ static void BatteryMeter_updateValues(Meter* this, char* buffer, int len) {
|
||||
|
||||
this->values[0] = percent;
|
||||
|
||||
const char *onAcText, *onBatteryText, *unknownText;
|
||||
|
||||
unknownText = "%.1f%%";
|
||||
if (this->mode == TEXT_METERMODE) {
|
||||
onAcText = "%.1f%% (Running on A/C)";
|
||||
onBatteryText = "%.1f%% (Running on battery)";
|
||||
} else {
|
||||
onAcText = "%.1f%%(A/C)";
|
||||
onBatteryText = "%.1f%%(bat)";
|
||||
const char* text;
|
||||
switch (isOnAC) {
|
||||
case AC_PRESENT:
|
||||
text = this->mode == TEXT_METERMODE ? " (Running on A/C)" : "(A/C)";
|
||||
break;
|
||||
case AC_ABSENT:
|
||||
text = this->mode == TEXT_METERMODE ? " (Running on battery)" : "(bat)";
|
||||
break;
|
||||
case AC_ERROR:
|
||||
default:
|
||||
text = "";
|
||||
break;
|
||||
}
|
||||
|
||||
if (isOnAC == AC_PRESENT) {
|
||||
xSnprintf(buffer, len, onAcText, percent);
|
||||
} else if (isOnAC == AC_ABSENT) {
|
||||
xSnprintf(buffer, len, onBatteryText, percent);
|
||||
} else {
|
||||
xSnprintf(buffer, len, unknownText, percent);
|
||||
}
|
||||
xSnprintf(buffer, len, "%.1f%%%s", percent, text);
|
||||
}
|
||||
|
||||
const MeterClass BatteryMeter_class = {
|
||||
|
54
CPUMeter.c
54
CPUMeter.c
@ -50,7 +50,7 @@ static void CPUMeter_init(Meter* this) {
|
||||
Meter_setCaption(this, "Avg");
|
||||
}
|
||||
|
||||
static void CPUMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
int cpu = this->param;
|
||||
if (cpu > this->pl->cpuCount) {
|
||||
xSnprintf(buffer, size, "absent");
|
||||
@ -67,7 +67,7 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
double percent = Platform_setCPUValues(this, cpu);
|
||||
|
||||
if (this->pl->settings->showCPUUsage) {
|
||||
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%5.1f%%", percent);
|
||||
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent);
|
||||
}
|
||||
|
||||
if (this->pl->settings->showCPUFrequency) {
|
||||
@ -104,49 +104,49 @@ static void CPUMeter_display(const Object* cast, RichString* out) {
|
||||
const Meter* this = (const Meter*)cast;
|
||||
RichString_prune(out);
|
||||
if (this->param > this->pl->cpuCount) {
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "absent");
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "absent");
|
||||
return;
|
||||
}
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], ":");
|
||||
RichString_append(out, CRT_colors[CPU_NORMAL], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], ":");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_NORMAL], buffer);
|
||||
if (this->pl->settings->detailedCPUTime) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "sy:");
|
||||
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sy:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "ni:");
|
||||
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "ni:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "hi:");
|
||||
RichString_append(out, CRT_colors[CPU_IRQ], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "hi:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_IRQ], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "si:");
|
||||
RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "si:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_SOFTIRQ], buffer);
|
||||
if (!isnan(this->values[CPU_METER_STEAL])) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "st:");
|
||||
RichString_append(out, CRT_colors[CPU_STEAL], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "st:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_STEAL], buffer);
|
||||
}
|
||||
if (!isnan(this->values[CPU_METER_GUEST])) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "gu:");
|
||||
RichString_append(out, CRT_colors[CPU_GUEST], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "gu:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer);
|
||||
}
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "wa:");
|
||||
RichString_append(out, CRT_colors[CPU_IOWAIT], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "wa:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_IOWAIT], buffer);
|
||||
} else {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "sys:");
|
||||
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sys:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "low:");
|
||||
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "low:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
|
||||
if (!isnan(this->values[CPU_METER_IRQ])) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "vir:");
|
||||
RichString_append(out, CRT_colors[CPU_GUEST], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "vir:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,8 +161,8 @@ static void CPUMeter_display(const Object* cast, RichString* out) {
|
||||
} else {
|
||||
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign);
|
||||
}
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "temp:");
|
||||
RichString_append(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "temp:");
|
||||
RichString_appendWide(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
149
CRT.c
149
CRT.c
@ -11,7 +11,6 @@ in the source distribution for its full text.
|
||||
|
||||
#include <errno.h>
|
||||
#include <langinfo.h>
|
||||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -42,28 +41,33 @@ in the source distribution for its full text.
|
||||
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
|
||||
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
|
||||
|
||||
static const char* const 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
|
||||
#define ColorPairWhiteDefault ColorPair(Red, Red)
|
||||
#define ColorIndexWhiteDefault ColorIndex(Red, Red)
|
||||
|
||||
static const char* const CRT_treeStrAscii[LAST_TREE_STR] = {
|
||||
[TREE_STR_VERT] = "|",
|
||||
[TREE_STR_RTEE] = "`",
|
||||
[TREE_STR_BEND] = "`",
|
||||
[TREE_STR_TEND] = ",",
|
||||
[TREE_STR_OPEN] = "+",
|
||||
[TREE_STR_SHUT] = "-",
|
||||
[TREE_STR_ASC] = "+",
|
||||
[TREE_STR_DESC] = "-",
|
||||
};
|
||||
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
|
||||
static const char* const 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 +, TODO use 🮯 'BOX DRAWINGS LIGHT HORIZONTAL
|
||||
static const char* const CRT_treeStrUtf8[LAST_TREE_STR] = {
|
||||
[TREE_STR_VERT] = "\xe2\x94\x82", // │
|
||||
[TREE_STR_RTEE] = "\xe2\x94\x9c", // ├
|
||||
[TREE_STR_BEND] = "\xe2\x94\x94", // └
|
||||
[TREE_STR_TEND] = "\xe2\x94\x8c", // ┌
|
||||
[TREE_STR_OPEN] = "+", // +, TODO use 🮯 'BOX DRAWINGS LIGHT HORIZONTAL
|
||||
// WITH VERTICAL STROKE' (U+1FBAF, "\xf0\x9f\xae\xaf") when
|
||||
// Unicode 13 is common
|
||||
"\xe2\x94\x80", // TREE_STR_SHUT ─
|
||||
[TREE_STR_SHUT] = "\xe2\x94\x80", // ─
|
||||
[TREE_STR_ASC] = "\xe2\x96\xb3", // △
|
||||
[TREE_STR_DESC] = "\xe2\x96\xbd", // ▽
|
||||
};
|
||||
|
||||
bool CRT_utf8 = false;
|
||||
@ -80,20 +84,20 @@ static const char* initDegreeSign(void) {
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
if (CRT_utf8)
|
||||
return "\xc2\xb0";
|
||||
#endif
|
||||
|
||||
static char buffer[4];
|
||||
// this might fail if the current locale does not support wide characters
|
||||
int r = snprintf(buffer, sizeof(buffer), "%lc", 176);
|
||||
if (r > 0)
|
||||
return buffer;
|
||||
#endif
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
const int* CRT_colors;
|
||||
|
||||
int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[COLORSCHEME_DEFAULT] = {
|
||||
[RESET_COLOR] = ColorPair(White, Black),
|
||||
[DEFAULT_COLOR] = ColorPair(White, Black),
|
||||
@ -117,6 +121,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[METER_VALUE_IOWRITE] = ColorPair(Blue, Black),
|
||||
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black),
|
||||
[METER_VALUE_OK] = ColorPair(Green, Black),
|
||||
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
|
||||
[LED_COLOR] = ColorPair(Green, Black),
|
||||
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black),
|
||||
[PROCESS] = A_NORMAL,
|
||||
@ -201,6 +206,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[METER_VALUE_IOWRITE] = A_NORMAL,
|
||||
[METER_VALUE_NOTICE] = A_BOLD,
|
||||
[METER_VALUE_OK] = A_NORMAL,
|
||||
[METER_VALUE_WARN] = A_BOLD,
|
||||
[LED_COLOR] = A_NORMAL,
|
||||
[TASKS_RUNNING] = A_BOLD,
|
||||
[PROCESS] = A_NORMAL,
|
||||
@ -285,6 +291,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[METER_VALUE_IOWRITE] = ColorPair(Yellow, White),
|
||||
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, White),
|
||||
[METER_VALUE_OK] = ColorPair(Green, White),
|
||||
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, White),
|
||||
[LED_COLOR] = ColorPair(Green, White),
|
||||
[TASKS_RUNNING] = ColorPair(Green, White),
|
||||
[PROCESS] = ColorPair(Black, White),
|
||||
@ -347,10 +354,10 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[ZRAM] = ColorPair(Yellow, White)
|
||||
},
|
||||
[COLORSCHEME_LIGHTTERMINAL] = {
|
||||
[RESET_COLOR] = ColorPair(Blue, Black),
|
||||
[DEFAULT_COLOR] = ColorPair(Blue, Black),
|
||||
[RESET_COLOR] = ColorPair(Black, Black),
|
||||
[DEFAULT_COLOR] = ColorPair(Black, Black),
|
||||
[FUNCTION_BAR] = ColorPair(Black, Cyan),
|
||||
[FUNCTION_KEY] = ColorPair(Blue, Black),
|
||||
[FUNCTION_KEY] = ColorPair(Black, Black),
|
||||
[PANEL_HEADER_FOCUS] = ColorPair(Black, Green),
|
||||
[PANEL_HEADER_UNFOCUS] = ColorPair(Black, Green),
|
||||
[PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan),
|
||||
@ -363,17 +370,18 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[BATTERY] = ColorPair(Yellow, Black),
|
||||
[LARGE_NUMBER] = ColorPair(Red, Black),
|
||||
[METER_TEXT] = ColorPair(Blue, Black),
|
||||
[METER_VALUE] = ColorPair(Blue, Black),
|
||||
[METER_VALUE] = ColorPair(Black, Black),
|
||||
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
|
||||
[METER_VALUE_IOREAD] = ColorPair(Green, Black),
|
||||
[METER_VALUE_IOWRITE] = ColorPair(Yellow, Black),
|
||||
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, Black),
|
||||
[METER_VALUE_NOTICE] = A_BOLD | ColorPairWhiteDefault,
|
||||
[METER_VALUE_OK] = ColorPair(Green, Black),
|
||||
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
|
||||
[LED_COLOR] = ColorPair(Green, Black),
|
||||
[TASKS_RUNNING] = ColorPair(Green, Black),
|
||||
[PROCESS] = ColorPair(Blue, Black),
|
||||
[PROCESS] = ColorPair(Black, Black),
|
||||
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||
[PROCESS_TAG] = ColorPair(Yellow, Blue),
|
||||
[PROCESS_TAG] = ColorPair(White, Blue),
|
||||
[PROCESS_MEGABYTES] = ColorPair(Blue, Black),
|
||||
[PROCESS_GIGABYTES] = ColorPair(Green, Black),
|
||||
[PROCESS_BASENAME] = ColorPair(Green, Black),
|
||||
@ -397,34 +405,34 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[MEMORY_BUFFERS] = ColorPair(Cyan, Black),
|
||||
[MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, Black),
|
||||
[MEMORY_CACHE] = ColorPair(Yellow, Black),
|
||||
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Blue, Black),
|
||||
[LOAD_AVERAGE_FIVE] = ColorPair(Blue, Black),
|
||||
[LOAD_AVERAGE_ONE] = ColorPair(Yellow, Black),
|
||||
[LOAD] = ColorPair(Yellow, Black),
|
||||
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, Black),
|
||||
[LOAD_AVERAGE_FIVE] = ColorPair(Black, Black),
|
||||
[LOAD_AVERAGE_ONE] = ColorPair(Black, Black),
|
||||
[LOAD] = ColorPairWhiteDefault,
|
||||
[HELP_BOLD] = ColorPair(Blue, Black),
|
||||
[CLOCK] = ColorPair(Yellow, Black),
|
||||
[DATE] = ColorPair(White, Black),
|
||||
[DATETIME] = ColorPair(White, Black),
|
||||
[CLOCK] = ColorPairWhiteDefault,
|
||||
[DATE] = ColorPairWhiteDefault,
|
||||
[DATETIME] = ColorPairWhiteDefault,
|
||||
[CHECK_BOX] = ColorPair(Blue, Black),
|
||||
[CHECK_MARK] = ColorPair(Blue, Black),
|
||||
[CHECK_TEXT] = ColorPair(Blue, Black),
|
||||
[HOSTNAME] = ColorPair(Yellow, Black),
|
||||
[CHECK_MARK] = ColorPair(Black, Black),
|
||||
[CHECK_TEXT] = ColorPair(Black, Black),
|
||||
[HOSTNAME] = ColorPairWhiteDefault,
|
||||
[CPU_NICE] = ColorPair(Cyan, Black),
|
||||
[CPU_NICE_TEXT] = ColorPair(Cyan, Black),
|
||||
[CPU_NORMAL] = ColorPair(Green, Black),
|
||||
[CPU_SYSTEM] = ColorPair(Red, Black),
|
||||
[CPU_IOWAIT] = A_BOLD | ColorPair(Blue, Black),
|
||||
[CPU_IOWAIT] = A_BOLD | ColorPair(Black, Black),
|
||||
[CPU_IRQ] = A_BOLD | ColorPair(Blue, Black),
|
||||
[CPU_SOFTIRQ] = ColorPair(Blue, Black),
|
||||
[CPU_STEAL] = ColorPair(Blue, Black),
|
||||
[CPU_GUEST] = ColorPair(Blue, Black),
|
||||
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Blue, Black),
|
||||
[PRESSURE_STALL_SIXTY] = ColorPair(Blue, Black),
|
||||
[PRESSURE_STALL_TEN] = ColorPair(Blue, Black),
|
||||
[CPU_STEAL] = ColorPair(Black, Black),
|
||||
[CPU_GUEST] = ColorPair(Black, Black),
|
||||
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, Black),
|
||||
[PRESSURE_STALL_SIXTY] = ColorPair(Black, Black),
|
||||
[PRESSURE_STALL_TEN] = ColorPair(Black, Black),
|
||||
[ZFS_MFU] = ColorPair(Cyan, Black),
|
||||
[ZFS_MRU] = ColorPair(Yellow, Black),
|
||||
[ZFS_ANON] = A_BOLD | ColorPair(Magenta, Black),
|
||||
[ZFS_HEADER] = ColorPair(Blue, Black),
|
||||
[ZFS_HEADER] = ColorPair(Black, Black),
|
||||
[ZFS_OTHER] = A_BOLD | ColorPair(Magenta, Black),
|
||||
[ZFS_COMPRESSED] = ColorPair(Cyan, Black),
|
||||
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black),
|
||||
@ -453,6 +461,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[METER_VALUE_IOWRITE] = ColorPair(Black, Blue),
|
||||
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Blue),
|
||||
[METER_VALUE_OK] = ColorPair(Green, Blue),
|
||||
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
|
||||
[LED_COLOR] = ColorPair(Green, Blue),
|
||||
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Blue),
|
||||
[PROCESS] = ColorPair(White, Blue),
|
||||
@ -535,8 +544,9 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
|
||||
[METER_VALUE_IOREAD] = ColorPair(Green, Black),
|
||||
[METER_VALUE_IOWRITE] = ColorPair(Blue, Black),
|
||||
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, Black),
|
||||
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black),
|
||||
[METER_VALUE_OK] = ColorPair(Green, Black),
|
||||
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
|
||||
[LED_COLOR] = ColorPair(Green, Black),
|
||||
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black),
|
||||
[PROCESS] = ColorPair(Cyan, Black),
|
||||
@ -605,12 +615,7 @@ int CRT_scrollHAmount = 5;
|
||||
|
||||
int CRT_scrollWheelVAmount = 10;
|
||||
|
||||
const char* CRT_termType;
|
||||
|
||||
int CRT_colorScheme = 0;
|
||||
|
||||
long CRT_pageSize = -1;
|
||||
long CRT_pageSizeKB = -1;
|
||||
ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT;
|
||||
|
||||
ATTR_NORETURN
|
||||
static void CRT_handleSIGTERM(int sgn) {
|
||||
@ -677,14 +682,14 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
|
||||
start_color();
|
||||
}
|
||||
|
||||
CRT_termType = getenv("TERM");
|
||||
if (String_eq(CRT_termType, "linux")) {
|
||||
const char* termType = getenv("TERM");
|
||||
if (termType && String_eq(termType, "linux")) {
|
||||
CRT_scrollHAmount = 20;
|
||||
} else {
|
||||
CRT_scrollHAmount = 5;
|
||||
}
|
||||
|
||||
if (String_startsWith(CRT_termType, "xterm") || String_eq(CRT_termType, "vt220")) {
|
||||
if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) {
|
||||
define_key("\033[H", KEY_HOME);
|
||||
define_key("\033[F", KEY_END);
|
||||
define_key("\033[7~", KEY_HOME);
|
||||
@ -697,6 +702,7 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
|
||||
define_key("\033[12~", KEY_F(2));
|
||||
define_key("\033[13~", KEY_F(3));
|
||||
define_key("\033[14~", KEY_F(4));
|
||||
define_key("\033[14;2~", KEY_F(15));
|
||||
define_key("\033[17;2~", KEY_F(18));
|
||||
char sequence[3] = "\033a";
|
||||
for (char c = 'a'; c <= 'z'; c++) {
|
||||
@ -747,11 +753,6 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
|
||||
mousemask(BUTTON1_RELEASED, NULL);
|
||||
#endif
|
||||
|
||||
CRT_pageSize = sysconf(_SC_PAGESIZE);
|
||||
if (CRT_pageSize == -1)
|
||||
CRT_fatalError("Fatal error: Can not get PAGE_SIZE by sysconf(_SC_PAGESIZE)");
|
||||
CRT_pageSizeKB = CRT_pageSize / 1024;
|
||||
|
||||
CRT_degreeSign = initDegreeSign();
|
||||
}
|
||||
|
||||
@ -761,7 +762,7 @@ void CRT_done() {
|
||||
}
|
||||
|
||||
void CRT_fatalError(const char* note) {
|
||||
char* sysMsg = strerror(errno);
|
||||
const char* sysMsg = strerror(errno);
|
||||
CRT_done();
|
||||
fprintf(stderr, "%s: %s\n", note, sysMsg);
|
||||
exit(2);
|
||||
@ -789,10 +790,10 @@ void CRT_enableDelay() {
|
||||
void CRT_setColors(int colorScheme) {
|
||||
CRT_colorScheme = colorScheme;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (ColorIndex(i, j) != ColorIndexGrayBlack) {
|
||||
int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
|
||||
for (short int i = 0; i < 8; i++) {
|
||||
for (short int j = 0; j < 8; j++) {
|
||||
if (ColorIndex(i, j) != ColorIndexGrayBlack && ColorIndex(i, j) != ColorIndexWhiteDefault) {
|
||||
short int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
|
||||
? (j == 0 ? -1 : j)
|
||||
: j;
|
||||
init_pair(ColorIndex(i, j), i, bg);
|
||||
@ -800,12 +801,12 @@ void CRT_setColors(int colorScheme) {
|
||||
}
|
||||
}
|
||||
|
||||
int grayBlackFg = COLORS > 8 ? 8 : 0;
|
||||
int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT)
|
||||
? -1
|
||||
: 0;
|
||||
short int grayBlackFg = COLORS > 8 ? 8 : 0;
|
||||
short int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT) ? -1 : 0;
|
||||
init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg);
|
||||
|
||||
init_pair(ColorIndexWhiteDefault, White, -1);
|
||||
|
||||
CRT_colors = CRT_colorSchemes[colorScheme];
|
||||
}
|
||||
|
||||
@ -822,9 +823,13 @@ void CRT_handleSIGSEGV(int signal) {
|
||||
"- Your OS and kernel version (uname -a)\n"
|
||||
"- Your distribution and release (lsb_release -a)\n"
|
||||
"- Likely steps to reproduce (How did it happened?)\n"
|
||||
);
|
||||
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
"- Backtrace of the issue (see below)\n"
|
||||
fprintf(stderr, "- Backtrace of the issue (see below)\n");
|
||||
#endif
|
||||
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
);
|
||||
|
||||
@ -859,11 +864,15 @@ void CRT_handleSIGSEGV(int signal) {
|
||||
"you should provide a disassembly of your binary.\n"
|
||||
"This can usually be done by running the following command:\n"
|
||||
"\n"
|
||||
);
|
||||
|
||||
#ifdef HTOP_DARWIN
|
||||
" otool -tvV `which htop` > ~/htop.otool\n"
|
||||
fprintf(stderr, " otool -tvV `which htop` > ~/htop.otool\n");
|
||||
#else
|
||||
" objdump -d -S -w `which htop` > ~/htop.objdump\n"
|
||||
fprintf(stderr, " objdump -d -S -w `which htop` > ~/htop.objdump\n");
|
||||
#endif
|
||||
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
"Please include the generated file in your report.\n"
|
||||
"\n"
|
||||
|
23
CRT.h
23
CRT.h
@ -16,26 +16,27 @@ in the source distribution for its full text.
|
||||
|
||||
|
||||
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
|
||||
TREE_STR_ASC,
|
||||
TREE_STR_DESC,
|
||||
LAST_TREE_STR
|
||||
} TreeStr;
|
||||
|
||||
typedef enum ColorSchemes_ {
|
||||
COLORSCHEME_DEFAULT = 0,
|
||||
typedef enum ColorScheme_ {
|
||||
COLORSCHEME_DEFAULT,
|
||||
COLORSCHEME_MONOCHROME,
|
||||
COLORSCHEME_BLACKONWHITE,
|
||||
COLORSCHEME_LIGHTTERMINAL,
|
||||
COLORSCHEME_MIDNIGHT,
|
||||
COLORSCHEME_BLACKNIGHT,
|
||||
COLORSCHEME_BROKENGRAY,
|
||||
LAST_COLORSCHEME,
|
||||
} ColorSchemes;
|
||||
LAST_COLORSCHEME
|
||||
} ColorScheme;
|
||||
|
||||
typedef enum ColorElements_ {
|
||||
RESET_COLOR,
|
||||
@ -58,6 +59,7 @@ typedef enum ColorElements_ {
|
||||
METER_VALUE_IOWRITE,
|
||||
METER_VALUE_NOTICE,
|
||||
METER_VALUE_OK,
|
||||
METER_VALUE_WARN,
|
||||
LED_COLOR,
|
||||
UPTIME,
|
||||
BATTERY,
|
||||
@ -144,20 +146,13 @@ extern const char* const* CRT_treeStr;
|
||||
|
||||
extern const int* CRT_colors;
|
||||
|
||||
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
|
||||
|
||||
extern int CRT_cursorX;
|
||||
|
||||
extern int CRT_scrollHAmount;
|
||||
|
||||
extern int CRT_scrollWheelVAmount;
|
||||
|
||||
extern const char* CRT_termType;
|
||||
|
||||
extern int CRT_colorScheme;
|
||||
|
||||
extern long CRT_pageSize;
|
||||
extern long CRT_pageSizeKB;
|
||||
extern ColorScheme CRT_colorScheme;
|
||||
|
||||
#ifdef HAVE_SETUID_ENABLED
|
||||
|
||||
|
63
ChangeLog
63
ChangeLog
@ -1,3 +1,59 @@
|
||||
What's new in version 3.0.5
|
||||
|
||||
* BUGFIX / SECURITY: InfoScreen: fix uncontrolled format string
|
||||
* BUGFIX: Improve white text in the Light Terminal colour scheme
|
||||
(both of the above thanks to V)
|
||||
* Enable the function bar on the main screen to be hidden (see Setup -> Display options)
|
||||
* BUGFIX: Reduce layout issues esp. around printing wide characters (not complete yet)
|
||||
* BUGFIX: Make the follow function exit cleanly after followed process died
|
||||
* Solaris: make Process callbacks static
|
||||
* Update help and man page for improved -t / -s options
|
||||
* Drop usage of formatted error messages from <err.h>
|
||||
* Show arrow indicating order of sorted process column
|
||||
* Lots of plumbing around the internal Hashtable, hardening and code cleanups
|
||||
* LibSensors: add support for Ryzen CPUs (vor 5 Tagen)
|
||||
(thanks to Matej Dian)
|
||||
* BUGFIX: Fix CPU percentage on M1 silicon Macs
|
||||
(thanks to Luke Groeninger)
|
||||
* LoadMeter: dynamically adjust color and total of bar
|
||||
* Find libsensors.so.4 for Fedora and friends
|
||||
* Add support to display CPU frequencies on Solarish platforms
|
||||
(thanks to Dominik Hassler)
|
||||
* Enable going back to previous search matches (Shift-F3)
|
||||
* Added keybind 'N' for sorting by PID (drops 'n'/'N' as not used before much)
|
||||
(thanks to Jake Mannens)
|
||||
|
||||
What's new in version 3.0.4
|
||||
|
||||
* Separate tree and list sort orders
|
||||
* Invert Process_compare so that superclass matches run first
|
||||
(thanks to Hisham Muhammad)
|
||||
* Unhardcode Mac OS tick-to-milliseconds conversion
|
||||
(thanks to Alexander Momchilov)
|
||||
* Check if clock_gettime needs linking of librt
|
||||
* Define O_PATH if not already defined
|
||||
(thanks to Chris Burr)
|
||||
* Add column on Mac for processes running under translation
|
||||
(thanks to Dániel Bakai)
|
||||
* Configure check for additional linker flags for keypad(3)
|
||||
* PSI Meter: constant width and only print ten-duration as bar
|
||||
* Sort in paused mode after inverting sort order
|
||||
* Handle absence of package CPU temperature
|
||||
* Meter: restore non-wide-character build
|
||||
* LibSensors: restore temperature for Raspberry Pi
|
||||
* MainPanel: do not reset hideProcessSelection on KEY_SHUFFLE
|
||||
* BarMeter: rework text padding
|
||||
* Panel: rework drawing of FunctionBar
|
||||
* Meter: fix artifacts with very tiny width
|
||||
* DragonFlyBSD updates
|
||||
* BUGFIX: Fix dlopen issue for libsensors5 for some platforms
|
||||
* BUGFIX: Fix broken tree display on inverted sort order
|
||||
* BUGFIX: Fix pause mode ("Z") in tree view
|
||||
* BUGFIX: Correct timebase for non-x86 CPUs on Darwin
|
||||
* BUGFIX: Avoid NULL dereference on zombie processes
|
||||
* Document dynamic bindings and assumed external configuration
|
||||
* Update key mapping documentation for sorting
|
||||
|
||||
What's new in version 3.0.3
|
||||
|
||||
* Process sorting in 'tree' mode
|
||||
@ -5,8 +61,9 @@ What's new in version 3.0.3
|
||||
* Improved command display/sort functionality
|
||||
(thanks to Narendran Gopalakrishnan)
|
||||
* Add screen for active file locks
|
||||
(thanks to Fynn J. Wulf)
|
||||
* Calculate library size (M_LRS column) from maps file
|
||||
(thanks to Fynn Wulf)
|
||||
(thanks to Fynn J. Wulf)
|
||||
* Add a Zram meter
|
||||
(thanks to Murloc Knight)
|
||||
* Add Linux cwd process column
|
||||
@ -49,8 +106,10 @@ What's new in version 3.0.3
|
||||
* Avoid expensive build of process tree when not using it
|
||||
* Include documentation for COMM and EXE
|
||||
* Distinguish display of no permissions for reading M_LRS
|
||||
* Only calculate M_LRS size every 5 seconds
|
||||
* Only calculate M_LRS size every 2 seconds
|
||||
* Improvements to comm / cmdline display functionality
|
||||
* Merged view for COMM, EXE and cmdline
|
||||
(thanks to Narendran Gopalakrishnan and Benny Baumann)
|
||||
* Consistent kernel thread display for COMM/EXE columns
|
||||
* Central fault handling for all platforms
|
||||
* Handle parsing envID & VPid from process status file
|
||||
|
@ -19,7 +19,7 @@ static const int ClockMeter_attributes[] = {
|
||||
CLOCK
|
||||
};
|
||||
|
||||
static void ClockMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
static void ClockMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
time_t t = time(NULL);
|
||||
struct tm result;
|
||||
struct tm* lt = localtime_r(&t, &result);
|
||||
|
@ -18,7 +18,6 @@ in the source distribution for its full text.
|
||||
#include "ProvideCurses.h"
|
||||
#include "RichString.h"
|
||||
#include "Vector.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
// TO ADD A NEW SCHEME:
|
||||
@ -61,26 +60,21 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
|
||||
case KEY_MOUSE:
|
||||
case KEY_RECLICK:
|
||||
case ' ':
|
||||
assert(mark >= 0);
|
||||
assert(mark < LAST_COLORSCHEME);
|
||||
for (int i = 0; ColorSchemeNames[i] != NULL; i++)
|
||||
CheckItem_set((CheckItem*)Panel_get(super, i), false);
|
||||
CheckItem_set((CheckItem*)Panel_get(super, mark), true);
|
||||
|
||||
this->settings->colorScheme = mark;
|
||||
result = HANDLED;
|
||||
}
|
||||
|
||||
if (result == HANDLED) {
|
||||
this->settings->changed = true;
|
||||
const Header* header = this->scr->header;
|
||||
|
||||
CRT_setColors(mark);
|
||||
clear();
|
||||
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
|
||||
Header_draw(header);
|
||||
FunctionBar_draw(super->currentBar);
|
||||
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);
|
||||
RichString_setAttr(&(menu->header), CRT_colors[PANEL_HEADER_UNFOCUS]);
|
||||
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
|
||||
|
||||
result = HANDLED | REDRAW;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -101,6 +95,8 @@ ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) {
|
||||
this->settings = settings;
|
||||
this->scr = scr;
|
||||
|
||||
assert(ARRAYSIZE(ColorSchemeNames) == LAST_COLORSCHEME + 1);
|
||||
|
||||
Panel_setHeader(super, "Colors");
|
||||
for (int i = 0; ColorSchemeNames[i] != NULL; i++) {
|
||||
Panel_add(super, (Object*) CheckItem_newByVal(ColorSchemeNames[i], false));
|
||||
|
@ -44,7 +44,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
|
||||
{
|
||||
if (selected < size - 1) {
|
||||
this->moving = !(this->moving);
|
||||
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
|
||||
Panel_setSelectionColor(super, this->moving ? PANEL_SELECTION_FOLLOW : PANEL_SELECTION_FOCUS);
|
||||
ListItem* selectedItem = (ListItem*) Panel_getSelected(super);
|
||||
if (selectedItem)
|
||||
selectedItem->moving = this->moving;
|
||||
|
@ -17,9 +17,10 @@ static void CommandScreen_scan(InfoScreen* this) {
|
||||
Panel_prune(panel);
|
||||
|
||||
const char* p = Process_getCommand(this->process);
|
||||
char* line = xMalloc(COLS + 1);
|
||||
char line[COLS + 1];
|
||||
int line_offset = 0, last_spc = -1, len;
|
||||
for (; *p != '\0'; p++, line_offset++) {
|
||||
assert(line_offset >= 0 && (size_t)line_offset < sizeof(line));
|
||||
line[line_offset] = *p;
|
||||
if (*p == ' ') {
|
||||
last_spc = line_offset;
|
||||
@ -41,7 +42,6 @@ static void CommandScreen_scan(InfoScreen* this) {
|
||||
InfoScreen_addLine(this, line);
|
||||
}
|
||||
|
||||
free(line);
|
||||
Panel_setSelected(panel, idx);
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ const InfoScreenClass CommandScreen_class = {
|
||||
|
||||
CommandScreen* CommandScreen_new(Process* process) {
|
||||
CommandScreen* this = AllocThis(CommandScreen);
|
||||
return (CommandScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " ");
|
||||
return (CommandScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ");
|
||||
}
|
||||
|
||||
void CommandScreen_delete(Object* this) {
|
||||
|
38
Compat.c
38
Compat.c
@ -1,6 +1,6 @@
|
||||
/*
|
||||
htop - Compat.c
|
||||
(C) 2020 Christian Göttsche
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
@ -11,12 +11,18 @@ in the source distribution for its full text.
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> // IWYU pragma: keep
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h> // IWYU pragma: keep
|
||||
|
||||
#include "XUtils.h" // IWYU pragma: keep
|
||||
|
||||
#ifdef HAVE_HOST_GET_CLOCK_SERVICE
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
|
||||
int Compat_faccessat(int dirfd,
|
||||
const char* pathname,
|
||||
@ -94,7 +100,7 @@ int Compat_openat(const char* dirpath,
|
||||
|
||||
#endif /* !HAVE_OPENAT */
|
||||
|
||||
int Compat_readlinkat(int dirfd,
|
||||
ssize_t Compat_readlinkat(int dirfd,
|
||||
const char* dirpath,
|
||||
const char* pathname,
|
||||
char* buf,
|
||||
@ -117,3 +123,31 @@ int Compat_readlinkat(int dirfd,
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
int Compat_clock_monotonic_gettime(struct timespec *tp) {
|
||||
|
||||
#if defined(HAVE_CLOCK_GETTIME)
|
||||
|
||||
return clock_gettime(CLOCK_MONOTONIC, tp);
|
||||
|
||||
#elif defined(HAVE_HOST_GET_CLOCK_SERVICE)
|
||||
|
||||
clock_serv_t cclock;
|
||||
mach_timespec_t mts;
|
||||
|
||||
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
|
||||
clock_get_time(cclock, &mts);
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
|
||||
tp->tv_sec = mts.tv_sec;
|
||||
tp->tv_nsec = mts.tv_nsec;
|
||||
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
#error No Compat_clock_monotonic_gettime() implementation!
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
8
Compat.h
8
Compat.h
@ -2,7 +2,7 @@
|
||||
#define HEADER_Compat
|
||||
/*
|
||||
htop - Compat.h
|
||||
(C) 2020 Christian Göttsche
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
@ -12,7 +12,7 @@ in the source distribution for its full text.
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/stat.h> // IWYU pragma: keep
|
||||
|
||||
|
||||
int Compat_faccessat(int dirfd,
|
||||
@ -50,10 +50,12 @@ int Compat_openat(openat_arg_t dirpath, const char* pathname, int flags);
|
||||
|
||||
#endif /* HAVE_OPENAT */
|
||||
|
||||
int Compat_readlinkat(int dirfd,
|
||||
ssize_t Compat_readlinkat(int dirfd,
|
||||
const char* dirpath,
|
||||
const char* pathname,
|
||||
char* buf,
|
||||
size_t bufsize);
|
||||
|
||||
int Compat_clock_monotonic_gettime(struct timespec *tp);
|
||||
|
||||
#endif /* HEADER_Compat */
|
||||
|
@ -19,7 +19,7 @@ static const int DateMeter_attributes[] = {
|
||||
DATE
|
||||
};
|
||||
|
||||
static void DateMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
static void DateMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
time_t t = time(NULL);
|
||||
struct tm result;
|
||||
struct tm* lt = localtime_r(&t, &result);
|
||||
|
@ -19,7 +19,7 @@ static const int DateTimeMeter_attributes[] = {
|
||||
DATETIME
|
||||
};
|
||||
|
||||
static void DateTimeMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
static void DateTimeMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
time_t t = time(NULL);
|
||||
struct tm result;
|
||||
struct tm* lt = localtime_r(&t, &result);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
htop - DiskIOMeter.c
|
||||
(C) 2020 Christian Göttsche
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
@ -30,7 +30,7 @@ static unsigned long int cached_read_diff = 0;
|
||||
static unsigned long int cached_write_diff = 0;
|
||||
static double cached_utilisation_diff = 0.0;
|
||||
|
||||
static void DiskIOMeter_updateValues(Meter* this, char* buffer, int len) {
|
||||
static void DiskIOMeter_updateValues(Meter* this, char* buffer, size_t len) {
|
||||
static unsigned long long int cached_last_update = 0;
|
||||
|
||||
struct timeval tv;
|
||||
@ -88,7 +88,7 @@ static void DiskIOMeter_updateValues(Meter* this, char* buffer, int len) {
|
||||
|
||||
static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
|
||||
if (!hasData) {
|
||||
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data");
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -96,15 +96,15 @@ static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out)
|
||||
|
||||
int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
|
||||
xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
|
||||
RichString_write(out, CRT_colors[color], buffer);
|
||||
RichString_writeAscii(out, CRT_colors[color], buffer);
|
||||
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " read: ");
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: ");
|
||||
Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer));
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
|
||||
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " write: ");
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: ");
|
||||
Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer));
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
|
||||
}
|
||||
|
||||
const MeterClass DiskIOMeter_class = {
|
||||
|
@ -1,8 +1,8 @@
|
||||
#ifndef HEADER_DiskIOMeter
|
||||
#define HEADER_DiskIOMeter
|
||||
/*
|
||||
h top - DiskIOMeter*.h
|
||||
(C) 2020 Christian Göttsche
|
||||
htop - DiskIOMeter.h
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
@ -18,7 +18,6 @@ in the source distribution for its full text.
|
||||
#include "Object.h"
|
||||
#include "OptionItem.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
|
||||
@ -98,6 +97,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
|
||||
|
||||
Panel_setHeader(super, "Display options");
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));
|
||||
@ -124,6 +124,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
|
||||
Panel_add(super, (Object*) NumberItem_newByRef("Update interval (in seconds)", &(settings->delay), -1, 1, 255));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Highlight new and old processes", &(settings->highlightChanges)));
|
||||
Panel_add(super, (Object*) NumberItem_newByRef("- Highlight time (in seconds)", &(settings->highlightDelaySecs), 0, 1, 24*60*60));
|
||||
Panel_add(super, (Object*) NumberItem_newByRef("Hide main function bar (0 - off, 1 - on ESC until next input, 2 - permanently)", &(settings->hideFunctionBar), 0, 0, 2));
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Show topology when selecting affinity by default", &(settings->topologyAffinity)));
|
||||
#endif
|
||||
|
24
EnvScreen.c
24
EnvScreen.c
@ -14,30 +14,21 @@
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
const InfoScreenClass EnvScreen_class = {
|
||||
.super = {
|
||||
.extends = Class(Object),
|
||||
.delete = EnvScreen_delete
|
||||
},
|
||||
.scan = EnvScreen_scan,
|
||||
.draw = EnvScreen_draw
|
||||
};
|
||||
|
||||
EnvScreen* EnvScreen_new(Process* process) {
|
||||
EnvScreen* this = xMalloc(sizeof(EnvScreen));
|
||||
Object_setClass(this, Class(EnvScreen));
|
||||
return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " ");
|
||||
return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ");
|
||||
}
|
||||
|
||||
void EnvScreen_delete(Object* this) {
|
||||
free(InfoScreen_done((InfoScreen*)this));
|
||||
}
|
||||
|
||||
void EnvScreen_draw(InfoScreen* this) {
|
||||
static void EnvScreen_draw(InfoScreen* this) {
|
||||
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process));
|
||||
}
|
||||
|
||||
void EnvScreen_scan(InfoScreen* this) {
|
||||
static void EnvScreen_scan(InfoScreen* this) {
|
||||
Panel* panel = this->display;
|
||||
int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
|
||||
|
||||
@ -59,3 +50,12 @@ void EnvScreen_scan(InfoScreen* this) {
|
||||
Vector_insertionSort(panel->items);
|
||||
Panel_setSelected(panel, idx);
|
||||
}
|
||||
|
||||
const InfoScreenClass EnvScreen_class = {
|
||||
.super = {
|
||||
.extends = Class(Object),
|
||||
.delete = EnvScreen_delete
|
||||
},
|
||||
.scan = EnvScreen_scan,
|
||||
.draw = EnvScreen_draw
|
||||
};
|
||||
|
@ -15,8 +15,4 @@ EnvScreen* EnvScreen_new(Process* process);
|
||||
|
||||
void EnvScreen_delete(Object* this);
|
||||
|
||||
void EnvScreen_draw(InfoScreen* this);
|
||||
|
||||
void EnvScreen_scan(InfoScreen* this);
|
||||
|
||||
#endif
|
||||
|
@ -112,10 +112,11 @@ void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr
|
||||
attrset(attr);
|
||||
}
|
||||
mvaddstr(LINES - 1, x, buffer);
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
x += strlen(buffer);
|
||||
}
|
||||
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
|
||||
if (setCursor) {
|
||||
CRT_cursorX = x;
|
||||
curs_set(1);
|
||||
@ -132,10 +133,10 @@ void FunctionBar_append(const char* buffer, int attr) {
|
||||
} else {
|
||||
attrset(attr);
|
||||
}
|
||||
mvaddstr(LINES - 1, currentLen, buffer);
|
||||
mvaddstr(LINES - 1, currentLen + 1, buffer);
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
|
||||
currentLen += strlen(buffer);
|
||||
currentLen += strlen(buffer) + 1;
|
||||
}
|
||||
|
||||
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {
|
||||
|
91
Hashtable.c
91
Hashtable.c
@ -13,23 +13,39 @@ in the source distribution for its full text.
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Macros.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
typedef struct HashtableItem_ {
|
||||
ht_key_t key;
|
||||
size_t probe;
|
||||
void* value;
|
||||
} HashtableItem;
|
||||
|
||||
struct Hashtable_ {
|
||||
size_t size;
|
||||
HashtableItem* buckets;
|
||||
size_t items;
|
||||
bool owner;
|
||||
};
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
static void Hashtable_dump(const Hashtable* this) {
|
||||
fprintf(stderr, "Hashtable %p: size=%u items=%u owner=%s\n",
|
||||
fprintf(stderr, "Hashtable %p: size=%zu items=%zu owner=%s\n",
|
||||
(const void*)this,
|
||||
this->size,
|
||||
this->items,
|
||||
this->owner ? "yes" : "no");
|
||||
|
||||
unsigned int items = 0;
|
||||
for (unsigned int i = 0; i < this->size; i++) {
|
||||
fprintf(stderr, " item %5u: key = %5u probe = %2u value = %p\n",
|
||||
size_t items = 0;
|
||||
for (size_t i = 0; i < this->size; i++) {
|
||||
fprintf(stderr, " item %5zu: key = %5u probe = %2zu value = %p\n",
|
||||
i,
|
||||
this->buckets[i].key,
|
||||
this->buckets[i].probe,
|
||||
@ -39,15 +55,15 @@ static void Hashtable_dump(const Hashtable* this) {
|
||||
items++;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Hashtable %p: items=%u counted=%u\n",
|
||||
fprintf(stderr, "Hashtable %p: items=%zu counted=%zu\n",
|
||||
(const void*)this,
|
||||
this->items,
|
||||
items);
|
||||
}
|
||||
|
||||
static bool Hashtable_isConsistent(const Hashtable* this) {
|
||||
unsigned int items = 0;
|
||||
for (unsigned int i = 0; i < this->size; i++) {
|
||||
size_t items = 0;
|
||||
for (size_t i = 0; i < this->size; i++) {
|
||||
if (this->buckets[i].value)
|
||||
items++;
|
||||
}
|
||||
@ -57,9 +73,9 @@ static bool Hashtable_isConsistent(const Hashtable* this) {
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned int Hashtable_count(const Hashtable* this) {
|
||||
unsigned int items = 0;
|
||||
for (unsigned int i = 0; i < this->size; i++) {
|
||||
size_t Hashtable_count(const Hashtable* this) {
|
||||
size_t items = 0;
|
||||
for (size_t i = 0; i < this->size; i++) {
|
||||
if (this->buckets[i].value)
|
||||
items++;
|
||||
}
|
||||
@ -79,18 +95,17 @@ static const uint64_t OEISprimes[] = {
|
||||
34359738337, 68719476731, 137438953447
|
||||
};
|
||||
|
||||
static uint64_t nextPrime(unsigned int n) {
|
||||
assert(n <= OEISprimes[ARRAYSIZE(OEISprimes) - 1]);
|
||||
|
||||
for (unsigned int i = 0; i < ARRAYSIZE(OEISprimes); i++) {
|
||||
static uint64_t nextPrime(size_t n) {
|
||||
/* on 32-bit make sure we do not return primes not fitting in size_t */
|
||||
for (size_t i = 0; i < ARRAYSIZE(OEISprimes) && OEISprimes[i] < SIZE_MAX; i++) {
|
||||
if (n <= OEISprimes[i])
|
||||
return OEISprimes[i];
|
||||
}
|
||||
|
||||
return OEISprimes[ARRAYSIZE(OEISprimes) - 1];
|
||||
CRT_fatalError("Hashtable: no prime found");
|
||||
}
|
||||
|
||||
Hashtable* Hashtable_new(unsigned int size, bool owner) {
|
||||
Hashtable* Hashtable_new(size_t size, bool owner) {
|
||||
Hashtable* this;
|
||||
|
||||
this = xMalloc(sizeof(Hashtable));
|
||||
@ -114,7 +129,7 @@ void Hashtable_clear(Hashtable* this) {
|
||||
assert(Hashtable_isConsistent(this));
|
||||
|
||||
if (this->owner)
|
||||
for (unsigned int i = 0; i < this->size; i++)
|
||||
for (size_t i = 0; i < this->size; i++)
|
||||
free(this->buckets[i].value);
|
||||
|
||||
memset(this->buckets, 0, this->size * sizeof(HashtableItem));
|
||||
@ -123,11 +138,11 @@ void Hashtable_clear(Hashtable* this) {
|
||||
assert(Hashtable_isConsistent(this));
|
||||
}
|
||||
|
||||
static void insert(Hashtable* this, hkey_t key, void* value) {
|
||||
unsigned int index = key % this->size;
|
||||
unsigned int probe = 0;
|
||||
static void insert(Hashtable* this, ht_key_t key, void* value) {
|
||||
size_t index = key % this->size;
|
||||
size_t probe = 0;
|
||||
#ifndef NDEBUG
|
||||
unsigned int origIndex = index;
|
||||
size_t origIndex = index;
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
@ -166,7 +181,7 @@ static void insert(Hashtable* this, hkey_t key, void* value) {
|
||||
}
|
||||
}
|
||||
|
||||
void Hashtable_setSize(Hashtable* this, unsigned int size) {
|
||||
void Hashtable_setSize(Hashtable* this, size_t size) {
|
||||
|
||||
assert(Hashtable_isConsistent(this));
|
||||
|
||||
@ -174,14 +189,14 @@ void Hashtable_setSize(Hashtable* this, unsigned int size) {
|
||||
return;
|
||||
|
||||
HashtableItem* oldBuckets = this->buckets;
|
||||
unsigned int oldSize = this->size;
|
||||
size_t oldSize = this->size;
|
||||
|
||||
this->size = nextPrime(size);
|
||||
this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem));
|
||||
this->items = 0;
|
||||
|
||||
/* rehash */
|
||||
for (unsigned int i = 0; i < oldSize; i++) {
|
||||
for (size_t i = 0; i < oldSize; i++) {
|
||||
if (!oldBuckets[i].value)
|
||||
continue;
|
||||
|
||||
@ -193,15 +208,19 @@ void Hashtable_setSize(Hashtable* this, unsigned int size) {
|
||||
assert(Hashtable_isConsistent(this));
|
||||
}
|
||||
|
||||
void Hashtable_put(Hashtable* this, hkey_t key, void* value) {
|
||||
void Hashtable_put(Hashtable* this, ht_key_t key, void* value) {
|
||||
|
||||
assert(Hashtable_isConsistent(this));
|
||||
assert(this->size > 0);
|
||||
assert(value);
|
||||
|
||||
/* grow on load-factor > 0.7 */
|
||||
if (10 * this->items > 7 * this->size)
|
||||
if (10 * this->items > 7 * this->size) {
|
||||
if (SIZE_MAX / 2 < this->size)
|
||||
CRT_fatalError("Hashtable: size overflow");
|
||||
|
||||
Hashtable_setSize(this, 2 * this->size);
|
||||
}
|
||||
|
||||
insert(this, key, value);
|
||||
|
||||
@ -210,11 +229,11 @@ void Hashtable_put(Hashtable* this, hkey_t key, void* value) {
|
||||
assert(this->size > this->items);
|
||||
}
|
||||
|
||||
void* Hashtable_remove(Hashtable* this, hkey_t key) {
|
||||
unsigned int index = key % this->size;
|
||||
unsigned int probe = 0;
|
||||
void* Hashtable_remove(Hashtable* this, ht_key_t key) {
|
||||
size_t index = key % this->size;
|
||||
size_t probe = 0;
|
||||
#ifndef NDEBUG
|
||||
unsigned int origIndex = index;
|
||||
size_t origIndex = index;
|
||||
#endif
|
||||
|
||||
assert(Hashtable_isConsistent(this));
|
||||
@ -229,7 +248,7 @@ void* Hashtable_remove(Hashtable* this, hkey_t key) {
|
||||
res = this->buckets[index].value;
|
||||
}
|
||||
|
||||
unsigned int next = (index + 1) % this->size;
|
||||
size_t next = (index + 1) % this->size;
|
||||
|
||||
while (this->buckets[next].value && this->buckets[next].probe > 0) {
|
||||
this->buckets[index] = this->buckets[next];
|
||||
@ -265,12 +284,12 @@ void* Hashtable_remove(Hashtable* this, hkey_t key) {
|
||||
return res;
|
||||
}
|
||||
|
||||
void* Hashtable_get(Hashtable* this, hkey_t key) {
|
||||
unsigned int index = key % this->size;
|
||||
unsigned int probe = 0;
|
||||
void* Hashtable_get(Hashtable* this, ht_key_t key) {
|
||||
size_t index = key % this->size;
|
||||
size_t probe = 0;
|
||||
void* res = NULL;
|
||||
#ifndef NDEBUG
|
||||
unsigned int origIndex = index;
|
||||
size_t origIndex = index;
|
||||
#endif
|
||||
|
||||
assert(Hashtable_isConsistent(this));
|
||||
@ -295,7 +314,7 @@ void* Hashtable_get(Hashtable* this, hkey_t key) {
|
||||
|
||||
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) {
|
||||
assert(Hashtable_isConsistent(this));
|
||||
for (unsigned int i = 0; i < this->size; i++) {
|
||||
for (size_t i = 0; i < this->size; i++) {
|
||||
HashtableItem* walk = &this->buckets[i];
|
||||
if (walk->value)
|
||||
f(walk->key, walk->value, userData);
|
||||
|
30
Hashtable.h
30
Hashtable.h
@ -8,44 +8,34 @@ in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
typedef unsigned int hkey_t;
|
||||
typedef unsigned int ht_key_t;
|
||||
|
||||
typedef void(*Hashtable_PairFunction)(hkey_t key, void* value, void* userdata);
|
||||
typedef void(*Hashtable_PairFunction)(ht_key_t key, void* value, void* userdata);
|
||||
|
||||
typedef struct HashtableItem_ {
|
||||
hkey_t key;
|
||||
unsigned int probe;
|
||||
void* value;
|
||||
} HashtableItem;
|
||||
|
||||
typedef struct Hashtable_ {
|
||||
unsigned int size;
|
||||
HashtableItem* buckets;
|
||||
unsigned int items;
|
||||
bool owner;
|
||||
} Hashtable;
|
||||
typedef struct Hashtable_ Hashtable;
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
unsigned int Hashtable_count(const Hashtable* this);
|
||||
size_t Hashtable_count(const Hashtable* this);
|
||||
|
||||
#endif /* NDEBUG */
|
||||
|
||||
Hashtable* Hashtable_new(unsigned int size, bool owner);
|
||||
Hashtable* Hashtable_new(size_t size, bool owner);
|
||||
|
||||
void Hashtable_delete(Hashtable* this);
|
||||
|
||||
void Hashtable_clear(Hashtable* this);
|
||||
|
||||
void Hashtable_setSize(Hashtable* this, unsigned int size);
|
||||
void Hashtable_setSize(Hashtable* this, size_t size);
|
||||
|
||||
void Hashtable_put(Hashtable* this, hkey_t key, void* value);
|
||||
void Hashtable_put(Hashtable* this, ht_key_t key, void* value);
|
||||
|
||||
void* Hashtable_remove(Hashtable* this, hkey_t key);
|
||||
void* Hashtable_remove(Hashtable* this, ht_key_t key);
|
||||
|
||||
void* Hashtable_get(Hashtable* this, hkey_t key);
|
||||
void* Hashtable_get(Hashtable* this, ht_key_t key);
|
||||
|
||||
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);
|
||||
|
||||
|
15
Header.c
15
Header.c
@ -131,21 +131,6 @@ int Header_size(Header* this, int column) {
|
||||
return Vector_size(meters);
|
||||
}
|
||||
|
||||
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));
|
||||
int len = nameLen + 100;
|
||||
char* name = xMalloc(len);
|
||||
memcpy(name, Meter_name(meter), nameLen);
|
||||
name[nameLen] = '\0';
|
||||
if (meter->param)
|
||||
xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
MeterModeId Header_readMeterMode(Header* this, int i, int column) {
|
||||
Vector* meters = this->columns[column];
|
||||
|
||||
|
2
Header.h
2
Header.h
@ -39,8 +39,6 @@ Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, i
|
||||
|
||||
int Header_size(Header* this, int column);
|
||||
|
||||
char* Header_readMeterName(Header* this, int i, int column);
|
||||
|
||||
MeterModeId Header_readMeterMode(Header* this, int i, int column);
|
||||
|
||||
void Header_reinit(Header* this);
|
||||
|
@ -19,7 +19,7 @@ static const int HostnameMeter_attributes[] = {
|
||||
HOSTNAME
|
||||
};
|
||||
|
||||
static void HostnameMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
static void HostnameMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
(void) this;
|
||||
gethostname(buffer, size - 1);
|
||||
}
|
||||
|
43
IncSet.c
43
IncSet.c
@ -29,9 +29,9 @@ void IncSet_reset(IncSet* this, IncType type) {
|
||||
IncMode_reset(&this->modes[type]);
|
||||
}
|
||||
|
||||
static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL};
|
||||
static const char* const searchKeys[] = {"F3", "Esc", " "};
|
||||
static int searchEvents[] = {KEY_F(3), 27, ERR};
|
||||
static const char* const searchFunctions[] = {"Next ", "Prev ", "Cancel ", " Search: ", NULL};
|
||||
static const char* const searchKeys[] = {"F3", "S-F3", "Esc", " "};
|
||||
static const int searchEvents[] = {KEY_F(3), KEY_F(15), 27, ERR};
|
||||
|
||||
static inline void IncMode_initSearch(IncMode* search) {
|
||||
memset(search, 0, sizeof(IncMode));
|
||||
@ -41,7 +41,7 @@ static inline void IncMode_initSearch(IncMode* search) {
|
||||
|
||||
static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
|
||||
static const char* const filterKeys[] = {"Enter", "Esc", " "};
|
||||
static int filterEvents[] = {13, 27, ERR};
|
||||
static const int filterEvents[] = {13, 27, ERR};
|
||||
|
||||
static inline void IncMode_initFilter(IncMode* filter) {
|
||||
memset(filter, 0, sizeof(IncMode));
|
||||
@ -54,12 +54,13 @@ static inline void IncMode_done(IncMode* mode) {
|
||||
}
|
||||
|
||||
IncSet* IncSet_new(FunctionBar* bar) {
|
||||
IncSet* this = xCalloc(1, sizeof(IncSet));
|
||||
IncSet* this = xMalloc(sizeof(IncSet));
|
||||
IncMode_initSearch(&(this->modes[INC_SEARCH]));
|
||||
IncMode_initFilter(&(this->modes[INC_FILTER]));
|
||||
this->active = NULL;
|
||||
this->filtering = false;
|
||||
this->defaultBar = bar;
|
||||
this->filtering = false;
|
||||
this->found = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -99,20 +100,14 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
|
||||
|
||||
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
int size = Panel_size(panel);
|
||||
bool found = false;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
|
||||
Panel_setSelected(panel, i);
|
||||
found = true;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
FunctionBar_drawExtra(mode->bar,
|
||||
mode->buffer,
|
||||
found ? -1 : CRT_colors[FAILED_SEARCH],
|
||||
true);
|
||||
return found;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
|
||||
@ -138,14 +133,6 @@ static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getP
|
||||
}
|
||||
}
|
||||
|
||||
bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
return IncMode_find(&this->modes[type], panel, getPanelValue, 1);
|
||||
}
|
||||
|
||||
bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
return IncMode_find(&this->modes[type], panel, getPanelValue, -1);
|
||||
}
|
||||
|
||||
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
|
||||
if (ch == ERR)
|
||||
return true;
|
||||
@ -154,11 +141,11 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
|
||||
int size = Panel_size(panel);
|
||||
bool filterChanged = false;
|
||||
bool doSearch = true;
|
||||
if (ch == KEY_F(3)) {
|
||||
if (ch == KEY_F(3) || ch == KEY_F(15)) {
|
||||
if (size == 0)
|
||||
return true;
|
||||
|
||||
IncMode_find(mode, panel, getPanelValue, 1);
|
||||
IncMode_find(mode, panel, getPanelValue, ch == KEY_F(3) ? 1 : -1);
|
||||
doSearch = false;
|
||||
} else if (0 < ch && ch < 255 && isprint((unsigned char)ch)) {
|
||||
if (mode->index < INCMODE_MAX) {
|
||||
@ -172,7 +159,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((ch == KEY_BACKSPACE || ch == 127)) {
|
||||
} else if (ch == KEY_BACKSPACE || ch == 127) {
|
||||
if (mode->index > 0) {
|
||||
mode->index--;
|
||||
mode->buffer[mode->index] = 0;
|
||||
@ -187,7 +174,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
|
||||
doSearch = false;
|
||||
}
|
||||
} else if (ch == KEY_RESIZE) {
|
||||
Panel_resize(panel, COLS, LINES - panel->y - 1);
|
||||
doSearch = (mode->index > 0);
|
||||
} else {
|
||||
if (mode->isFilter) {
|
||||
filterChanged = true;
|
||||
@ -202,7 +189,6 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
|
||||
}
|
||||
this->active = NULL;
|
||||
Panel_setDefaultBar(panel);
|
||||
FunctionBar_draw(this->defaultBar);
|
||||
doSearch = false;
|
||||
}
|
||||
if (doSearch) {
|
||||
@ -221,13 +207,12 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
|
||||
|
||||
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
|
||||
this->active = &(this->modes[type]);
|
||||
FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
|
||||
panel->currentBar = this->active->bar;
|
||||
}
|
||||
|
||||
void IncSet_drawBar(const IncSet* this) {
|
||||
if (this->active) {
|
||||
FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
|
||||
FunctionBar_drawExtra(this->active->bar, this->active->buffer, (this->active->isFilter || this->found) ? -1 : CRT_colors[FAILED_SEARCH], true);
|
||||
} else {
|
||||
FunctionBar_draw(this->defaultBar);
|
||||
}
|
||||
|
4
IncSet.h
4
IncSet.h
@ -48,10 +48,6 @@ IncSet* IncSet_new(FunctionBar* bar);
|
||||
|
||||
void IncSet_delete(IncSet* this);
|
||||
|
||||
bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
|
||||
|
||||
bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
|
||||
|
||||
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
|
||||
|
||||
const char* IncSet_getListItemValue(Panel* panel, int i);
|
||||
|
21
InfoScreen.c
21
InfoScreen.c
@ -19,14 +19,14 @@ static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh
|
||||
|
||||
static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
|
||||
|
||||
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
|
||||
static const int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
|
||||
|
||||
InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader) {
|
||||
this->process = process;
|
||||
if (!bar) {
|
||||
bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents);
|
||||
}
|
||||
this->display = Panel_new(0, 1, COLS, height, false, Class(ListItem), bar);
|
||||
this->display = Panel_new(0, 1, COLS, height, Class(ListItem), false, bar);
|
||||
this->inc = IncSet_new(bar);
|
||||
this->lines = Vector_new(this->display->items->type, true, DEFAULT_SIZE);
|
||||
Panel_setHeader(this->display, panelHeader);
|
||||
@ -44,21 +44,21 @@ void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
char* title = xMalloc(COLS + 1);
|
||||
int len = vsnprintf(title, COLS + 1, fmt, ap);
|
||||
char title[COLS + 1];
|
||||
int len = vsnprintf(title, sizeof(title), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len > COLS) {
|
||||
memset(&title[COLS - 3], '.', 3);
|
||||
}
|
||||
|
||||
attrset(CRT_colors[METER_TEXT]);
|
||||
mvhline(0, 0, ' ', COLS);
|
||||
mvwprintw(stdscr, 0, 0, title);
|
||||
mvaddstr(0, 0, title);
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
this->display->needsRedraw = true;
|
||||
Panel_draw(this->display, true, true);
|
||||
Panel_draw(this->display, true, true, true, false);
|
||||
|
||||
IncSet_drawBar(this->inc);
|
||||
free(title);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void InfoScreen_addLine(InfoScreen* this, const char* line) {
|
||||
@ -89,7 +89,8 @@ void InfoScreen_run(InfoScreen* this) {
|
||||
bool looping = true;
|
||||
while (looping) {
|
||||
|
||||
Panel_draw(panel, true, true);
|
||||
Panel_draw(panel, false, true, true, false);
|
||||
IncSet_drawBar(this->inc);
|
||||
|
||||
if (this->inc->active) {
|
||||
(void) move(LINES - 1, CRT_cursorX);
|
||||
|
@ -16,7 +16,6 @@ typedef struct InfoScreen_ {
|
||||
Object super;
|
||||
const Process* process;
|
||||
Panel* display;
|
||||
FunctionBar* bar;
|
||||
IncSet* inc;
|
||||
Vector* lines;
|
||||
} InfoScreen;
|
||||
|
@ -29,7 +29,7 @@ static void ListItem_display(const Object* cast, RichString* out) {
|
||||
assert (this != NULL);
|
||||
|
||||
if (this->moving) {
|
||||
RichString_write(out, CRT_colors[DEFAULT_COLOR],
|
||||
RichString_writeWide(out, CRT_colors[DEFAULT_COLOR],
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
CRT_utf8 ? "↕ " :
|
||||
#endif
|
||||
@ -37,7 +37,7 @@ static void ListItem_display(const Object* cast, RichString* out) {
|
||||
} else {
|
||||
RichString_prune(out);
|
||||
}
|
||||
RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value);
|
||||
RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value);
|
||||
}
|
||||
|
||||
ListItem* ListItem_new(const char* value, int key) {
|
||||
@ -57,7 +57,7 @@ void ListItem_append(ListItem* this, const char* text) {
|
||||
this->value[newLen] = '\0';
|
||||
}
|
||||
|
||||
static long ListItem_compare(const void* cast1, const void* cast2) {
|
||||
static int ListItem_compare(const void* cast1, const void* cast2) {
|
||||
const ListItem* obj1 = (const ListItem*) cast1;
|
||||
const ListItem* obj2 = (const ListItem*) cast2;
|
||||
return strcmp(obj1->value, obj2->value);
|
||||
|
@ -24,8 +24,36 @@ static const int LoadMeter_attributes[] = {
|
||||
LOAD
|
||||
};
|
||||
|
||||
static void LoadAverageMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
static const int OK_attributes[] = {
|
||||
METER_VALUE_OK
|
||||
};
|
||||
|
||||
static const int Medium_attributes[] = {
|
||||
METER_VALUE_WARN
|
||||
};
|
||||
|
||||
static const int High_attributes[] = {
|
||||
METER_VALUE_ERROR
|
||||
};
|
||||
|
||||
static void LoadAverageMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
|
||||
|
||||
// only show bar for 1min value
|
||||
this->curItems = 1;
|
||||
|
||||
// change bar color and total based on value
|
||||
if (this->values[0] < 1.0) {
|
||||
this->curAttributes = OK_attributes;
|
||||
this->total = 1.0;
|
||||
} else if (this->values[0] < this->pl->cpuCount) {
|
||||
this->curAttributes = Medium_attributes;
|
||||
this->total = this->pl->cpuCount;
|
||||
} else {
|
||||
this->curAttributes = High_attributes;
|
||||
this->total = 2 * this->pl->cpuCount;
|
||||
}
|
||||
|
||||
xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
|
||||
}
|
||||
|
||||
@ -33,19 +61,29 @@ static void LoadAverageMeter_display(const Object* cast, RichString* out) {
|
||||
const Meter* this = (const Meter*)cast;
|
||||
char buffer[20];
|
||||
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
|
||||
RichString_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
|
||||
RichString_writeAscii(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
|
||||
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
|
||||
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
|
||||
}
|
||||
|
||||
static void LoadMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
static void LoadMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
double five, fifteen;
|
||||
Platform_getLoadAverage(&this->values[0], &five, &fifteen);
|
||||
if (this->values[0] > this->total) {
|
||||
this->total = this->values[0];
|
||||
|
||||
// change bar color and total based on value
|
||||
if (this->values[0] < 1.0) {
|
||||
this->curAttributes = OK_attributes;
|
||||
this->total = 1.0;
|
||||
} else if (this->values[0] < this->pl->cpuCount) {
|
||||
this->curAttributes = Medium_attributes;
|
||||
this->total = this->pl->cpuCount;
|
||||
} else {
|
||||
this->curAttributes = High_attributes;
|
||||
this->total = 2 * this->pl->cpuCount;
|
||||
}
|
||||
|
||||
xSnprintf(buffer, size, "%.2f", this->values[0]);
|
||||
}
|
||||
|
||||
@ -53,7 +91,7 @@ static void LoadMeter_display(const Object* cast, RichString* out) {
|
||||
const Meter* this = (const Meter*)cast;
|
||||
char buffer[20];
|
||||
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
|
||||
RichString_write(out, CRT_colors[LOAD], buffer);
|
||||
RichString_writeAscii(out, CRT_colors[LOAD], buffer);
|
||||
}
|
||||
|
||||
const MeterClass LoadAverageMeter_class = {
|
||||
|
57
MainPanel.c
57
MainPanel.c
@ -25,7 +25,7 @@ static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filt
|
||||
|
||||
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
|
||||
FunctionBar* bar = MainPanel_getFunctionBar(this);
|
||||
FunctionBar_setLabel(bar, KEY_F(5), mode ? "Sorted" : "Tree ");
|
||||
FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree ");
|
||||
}
|
||||
|
||||
void MainPanel_pidSearch(MainPanel* this, int ch) {
|
||||
@ -44,6 +44,11 @@ void MainPanel_pidSearch(MainPanel* this, int ch) {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* MainPanel_getValue(Panel* this, int i) {
|
||||
const Process* p = (const Process*) Panel_get(this, i);
|
||||
return Process_getCommand(p);
|
||||
}
|
||||
|
||||
static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
MainPanel* this = (MainPanel*) super;
|
||||
|
||||
@ -51,6 +56,11 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
|
||||
Htop_Reaction reaction = HTOP_OK;
|
||||
|
||||
/* Let supervising ScreenManager handle resize */
|
||||
if (ch == KEY_RESIZE)
|
||||
return IGNORED;
|
||||
|
||||
/* reset on every normal key */
|
||||
if (ch != ERR)
|
||||
this->state->hideProcessSelection = false;
|
||||
|
||||
@ -60,7 +70,11 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
Settings* settings = this->state->settings;
|
||||
int hx = super->scrollH + x + 1;
|
||||
ProcessField field = ProcessList_keyAt(pl, hx);
|
||||
if (field == settings->sortKey) {
|
||||
if (settings->treeView && settings->treeViewAlwaysByPID) {
|
||||
settings->treeView = false;
|
||||
settings->direction = 1;
|
||||
reaction |= Action_setSortKey(settings, field);
|
||||
} else if (field == Settings_getActiveSortKey(settings)) {
|
||||
Settings_invertSortOrder(settings);
|
||||
} else {
|
||||
reaction |= Action_setSortKey(settings, field);
|
||||
@ -68,7 +82,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS;
|
||||
result = HANDLED;
|
||||
} else if (ch != ERR && this->inc->active) {
|
||||
bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL);
|
||||
bool filterChanged = IncSet_handleKey(this->inc, ch, super, MainPanel_getValue, NULL);
|
||||
if (filterChanged) {
|
||||
this->state->pl->incFilter = IncSet_filter(this->inc);
|
||||
reaction = HTOP_REFRESH | HTOP_REDRAW_BAR;
|
||||
@ -96,16 +110,12 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
|
||||
if (reaction & HTOP_REDRAW_BAR) {
|
||||
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
|
||||
IncSet_drawBar(this->inc);
|
||||
if (this->state->pauseProcessUpdate) {
|
||||
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
|
||||
}
|
||||
}
|
||||
if (reaction & HTOP_UPDATE_PANELHDR) {
|
||||
ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
|
||||
result |= REDRAW;
|
||||
}
|
||||
if (reaction & HTOP_REFRESH) {
|
||||
result |= REDRAW;
|
||||
result |= REFRESH;
|
||||
}
|
||||
if (reaction & HTOP_RECALCULATE) {
|
||||
result |= RESCAN;
|
||||
@ -118,7 +128,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
}
|
||||
if (!(reaction & HTOP_KEEP_FOLLOWING)) {
|
||||
this->state->pl->following = -1;
|
||||
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
|
||||
Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -131,11 +141,6 @@ int MainPanel_selectedPid(MainPanel* this) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* MainPanel_getValue(MainPanel* this, int i) {
|
||||
Process* p = (Process*) Panel_get((Panel*)this, i);
|
||||
return Process_getCommand(p);
|
||||
}
|
||||
|
||||
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
|
||||
Panel* super = (Panel*) this;
|
||||
bool ok = true;
|
||||
@ -160,12 +165,32 @@ bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Ar
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) {
|
||||
MainPanel* this = (MainPanel*) super;
|
||||
|
||||
// Do not hide active search and filter bar.
|
||||
if (hideFunctionBar && !this->inc->active)
|
||||
return;
|
||||
|
||||
IncSet_drawBar(this->inc);
|
||||
if (this->state->pauseProcessUpdate) {
|
||||
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
|
||||
}
|
||||
}
|
||||
|
||||
static void MainPanel_printHeader(Panel* super) {
|
||||
MainPanel* this = (MainPanel*) super;
|
||||
ProcessList_printHeader(this->state->pl, &super->header);
|
||||
}
|
||||
|
||||
const PanelClass MainPanel_class = {
|
||||
.super = {
|
||||
.extends = Class(Panel),
|
||||
.delete = MainPanel_delete
|
||||
},
|
||||
.eventHandler = MainPanel_eventHandler
|
||||
.eventHandler = MainPanel_eventHandler,
|
||||
.drawFunctionBar = MainPanel_drawFunctionBar,
|
||||
.printHeader = MainPanel_printHeader
|
||||
};
|
||||
|
||||
MainPanel* MainPanel_new() {
|
||||
|
@ -38,8 +38,6 @@ void MainPanel_pidSearch(MainPanel* this, int ch);
|
||||
|
||||
int MainPanel_selectedPid(MainPanel* this);
|
||||
|
||||
const char* MainPanel_getValue(MainPanel* this, int i);
|
||||
|
||||
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
|
||||
|
||||
extern const PanelClass MainPanel_class;
|
||||
|
30
Makefile.am
30
Makefile.am
@ -3,12 +3,14 @@ AUTOMAKE_OPTIONS = subdir-objects
|
||||
bin_PROGRAMS = htop
|
||||
|
||||
dist_man_MANS = htop.1
|
||||
EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png \
|
||||
EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png htop.svg \
|
||||
install-sh autogen.sh missing
|
||||
applicationsdir = $(datadir)/applications
|
||||
applications_DATA = htop.desktop
|
||||
pixmapdir = $(datadir)/pixmaps
|
||||
pixmap_DATA = htop.png
|
||||
appicondir = $(datadir)/icons/hicolor/scalable/apps
|
||||
appicon_DATA = htop.svg
|
||||
|
||||
AM_CFLAGS += -pedantic -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
|
||||
AM_LDFLAGS =
|
||||
@ -131,6 +133,7 @@ linux_platform_headers = \
|
||||
linux/LinuxProcessList.h \
|
||||
linux/Platform.h \
|
||||
linux/PressureStallMeter.h \
|
||||
linux/ProcessField.h \
|
||||
linux/SELinuxMeter.h \
|
||||
linux/SystemdMeter.h \
|
||||
linux/ZramMeter.h \
|
||||
@ -162,9 +165,10 @@ endif
|
||||
# -------
|
||||
|
||||
freebsd_platform_headers = \
|
||||
freebsd/Platform.h \
|
||||
freebsd/FreeBSDProcessList.h \
|
||||
freebsd/FreeBSDProcess.h \
|
||||
freebsd/Platform.h \
|
||||
freebsd/ProcessField.h \
|
||||
zfs/ZfsArcMeter.h \
|
||||
zfs/ZfsCompressedArcMeter.h \
|
||||
zfs/ZfsArcStats.h \
|
||||
@ -182,14 +186,16 @@ endif
|
||||
# ------------
|
||||
|
||||
dragonflybsd_platform_headers = \
|
||||
dragonflybsd/Platform.h \
|
||||
dragonflybsd/DragonFlyBSDProcessList.h \
|
||||
dragonflybsd/DragonFlyBSDProcess.h
|
||||
dragonflybsd/DragonFlyBSDProcess.h \
|
||||
dragonflybsd/Platform.h \
|
||||
dragonflybsd/ProcessField.h
|
||||
|
||||
if HTOP_DRAGONFLYBSD
|
||||
AM_LDFLAGS += -lkvm -lkinfo
|
||||
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
|
||||
dragonflybsd/DragonFlyBSDProcess.c
|
||||
myhtopplatsources = \
|
||||
dragonflybsd/Platform.c \
|
||||
dragonflybsd/DragonFlyBSDProcessList.c \
|
||||
dragonflybsd/DragonFlyBSDProcess.c
|
||||
|
||||
myhtopplatheaders = $(dragonflybsd_platform_headers)
|
||||
endif
|
||||
@ -198,9 +204,10 @@ endif
|
||||
# -------
|
||||
|
||||
openbsd_platform_headers = \
|
||||
openbsd/Platform.h \
|
||||
openbsd/OpenBSDProcessList.h \
|
||||
openbsd/OpenBSDProcess.h
|
||||
openbsd/OpenBSDProcess.h \
|
||||
openbsd/Platform.h \
|
||||
openbsd/ProcessField.h
|
||||
|
||||
if HTOP_OPENBSD
|
||||
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
|
||||
@ -213,9 +220,10 @@ endif
|
||||
# ------
|
||||
|
||||
darwin_platform_headers = \
|
||||
darwin/Platform.h \
|
||||
darwin/DarwinProcess.h \
|
||||
darwin/DarwinProcessList.h \
|
||||
darwin/Platform.h \
|
||||
darwin/ProcessField.h \
|
||||
zfs/ZfsArcMeter.h \
|
||||
zfs/ZfsCompressedArcMeter.h \
|
||||
zfs/ZfsArcStats.h \
|
||||
@ -235,6 +243,7 @@ endif
|
||||
|
||||
solaris_platform_headers = \
|
||||
solaris/Platform.h \
|
||||
solaris/ProcessField.h \
|
||||
solaris/SolarisProcess.h \
|
||||
solaris/SolarisProcessList.h \
|
||||
zfs/ZfsArcMeter.h \
|
||||
@ -254,6 +263,7 @@ endif
|
||||
|
||||
unsupported_platform_headers = \
|
||||
unsupported/Platform.h \
|
||||
unsupported/ProcessField.h \
|
||||
unsupported/UnsupportedProcess.h \
|
||||
unsupported/UnsupportedProcessList.h
|
||||
|
||||
|
@ -19,34 +19,33 @@ static const int MemoryMeter_attributes[] = {
|
||||
MEMORY_CACHE
|
||||
};
|
||||
|
||||
static void MemoryMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
int written;
|
||||
Platform_setMemoryValues(this);
|
||||
|
||||
written = Meter_humanUnit(buffer, this->values[0], size);
|
||||
buffer += written;
|
||||
if ((size -= written) > 0) {
|
||||
*buffer++ = '/';
|
||||
size--;
|
||||
METER_BUFFER_CHECK(buffer, size, written);
|
||||
|
||||
METER_BUFFER_APPEND_CHR(buffer, size, '/');
|
||||
|
||||
Meter_humanUnit(buffer, this->total, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void MemoryMeter_display(const Object* cast, RichString* out) {
|
||||
char buffer[50];
|
||||
const Meter* this = (const Meter*)cast;
|
||||
RichString_write(out, CRT_colors[METER_TEXT], ":");
|
||||
Meter_humanUnit(buffer, this->total, 50);
|
||||
RichString_append(out, CRT_colors[METER_VALUE], buffer);
|
||||
Meter_humanUnit(buffer, this->values[0], 50);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " used:");
|
||||
RichString_append(out, CRT_colors[MEMORY_USED], buffer);
|
||||
Meter_humanUnit(buffer, this->values[1], 50);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " buffers:");
|
||||
RichString_append(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
|
||||
Meter_humanUnit(buffer, this->values[2], 50);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " cache:");
|
||||
RichString_append(out, CRT_colors[MEMORY_CACHE], buffer);
|
||||
RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
|
||||
Meter_humanUnit(buffer, this->total, sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
|
||||
Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
|
||||
RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer);
|
||||
Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:");
|
||||
RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
|
||||
Meter_humanUnit(buffer, this->values[2], sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:");
|
||||
RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer);
|
||||
}
|
||||
|
||||
const MeterClass MemoryMeter_class = {
|
||||
|
96
Meter.c
96
Meter.c
@ -20,6 +20,7 @@ in the source distribution for its full text.
|
||||
#include "Object.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "RichString.h"
|
||||
#include "Settings.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
@ -38,6 +39,7 @@ Meter* Meter_new(const struct ProcessList_* pl, int param, const MeterClass* typ
|
||||
this->param = param;
|
||||
this->pl = pl;
|
||||
this->curItems = type->maxItems;
|
||||
this->curAttributes = NULL;
|
||||
this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL;
|
||||
this->total = type->total;
|
||||
this->caption = xStrdup(type->caption);
|
||||
@ -48,10 +50,10 @@ Meter* Meter_new(const struct ProcessList_* pl, int param, const MeterClass* typ
|
||||
return this;
|
||||
}
|
||||
|
||||
int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
|
||||
int Meter_humanUnit(char* buffer, unsigned long int value, size_t size) {
|
||||
const char* prefix = "KMGTPEZY";
|
||||
unsigned long int powi = 1;
|
||||
unsigned int written, powj = 1, precision = 2;
|
||||
unsigned int powj = 1, precision = 2;
|
||||
|
||||
for (;;) {
|
||||
if (value / 1024 < powi)
|
||||
@ -73,10 +75,7 @@ int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
|
||||
break;
|
||||
}
|
||||
|
||||
written = snprintf(buffer, size, "%.*f%c",
|
||||
precision, (double) value / powi, *prefix);
|
||||
|
||||
return written;
|
||||
return snprintf(buffer, size, "%.*f%c", precision, (double) value / powi, *prefix);
|
||||
}
|
||||
|
||||
void Meter_delete(Object* cast) {
|
||||
@ -102,7 +101,7 @@ static inline void Meter_displayBuffer(const Meter* this, const char* buffer, Ri
|
||||
if (Object_displayFn(this)) {
|
||||
Object_display(this, out);
|
||||
} else {
|
||||
RichString_write(out, CRT_colors[Meter_attributes(this)[0]], buffer);
|
||||
RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,17 +156,21 @@ ListItem* Meter_toListItem(Meter* this, bool moving) {
|
||||
|
||||
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
char buffer[METER_BUFFER_LEN];
|
||||
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
|
||||
(void) w;
|
||||
Meter_updateValues(this, buffer, sizeof(buffer));
|
||||
|
||||
attrset(CRT_colors[METER_TEXT]);
|
||||
mvaddstr(y, x, this->caption);
|
||||
mvaddnstr(y, x, this->caption, w - 1);
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
|
||||
int captionLen = strlen(this->caption);
|
||||
x += captionLen;
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
w -= captionLen;
|
||||
if (w <= 0)
|
||||
return;
|
||||
|
||||
RichString_begin(out);
|
||||
Meter_displayBuffer(this, buffer, &out);
|
||||
RichString_printVal(out, y, x);
|
||||
RichString_printoffnVal(out, y, x, 0, w - 1);
|
||||
RichString_end(out);
|
||||
}
|
||||
|
||||
@ -177,7 +180,7 @@ static const char BarMeterMode_characters[] = "|#*@$%&.";
|
||||
|
||||
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
char buffer[METER_BUFFER_LEN];
|
||||
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
|
||||
Meter_updateValues(this, buffer, sizeof(buffer));
|
||||
|
||||
w -= 2;
|
||||
attrset(CRT_colors[METER_TEXT]);
|
||||
@ -187,25 +190,39 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
w -= captionLen;
|
||||
attrset(CRT_colors[BAR_BORDER]);
|
||||
mvaddch(y, x, '[');
|
||||
mvaddch(y, x + w, ']');
|
||||
mvaddch(y, x + MAXIMUM(w, 0), ']');
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
|
||||
w--;
|
||||
x++;
|
||||
|
||||
if (w < 1) {
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
if (w < 1)
|
||||
return;
|
||||
}
|
||||
|
||||
// The text in the bar is right aligned;
|
||||
// calculate needed padding and generate leading spaces
|
||||
const int textLen = mbstowcs(NULL, buffer, 0);
|
||||
const int padding = MAXIMUM(w - textLen, 0);
|
||||
|
||||
// Pad with maximal spaces and then calculate needed starting position offset
|
||||
RichString_begin(bar);
|
||||
RichString_appendChr(&bar, ' ', padding);
|
||||
RichString_append(&bar, 0, buffer);
|
||||
assert(RichString_sizeVal(bar) >= w);
|
||||
RichString_appendChr(&bar, ' ', w);
|
||||
RichString_appendWide(&bar, 0, buffer);
|
||||
int startPos = RichString_sizeVal(bar) - w;
|
||||
if (startPos > w) {
|
||||
// Text is too large for bar
|
||||
// Truncate meter text at a space character
|
||||
for (int pos = 2 * w; pos > w; pos--) {
|
||||
if (RichString_getCharVal(bar, pos) == ' ') {
|
||||
while (pos > w && RichString_getCharVal(bar, pos - 1) == ' ')
|
||||
pos--;
|
||||
startPos = pos - w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If still to large, print the start not the end
|
||||
startPos = MINIMUM(startPos, w);
|
||||
}
|
||||
assert(startPos >= 0);
|
||||
assert(startPos <= w);
|
||||
assert(startPos + w <= RichString_sizeVal(bar));
|
||||
|
||||
int blockSizes[10];
|
||||
|
||||
@ -223,11 +240,11 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
// (Control against invalid values)
|
||||
nextOffset = CLAMP(nextOffset, 0, w);
|
||||
for (int j = offset; j < nextOffset; j++)
|
||||
if (RichString_getCharVal(bar, j) == ' ') {
|
||||
if (RichString_getCharVal(bar, startPos + j) == ' ') {
|
||||
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
|
||||
RichString_setChar(&bar, j, BarMeterMode_characters[i]);
|
||||
RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]);
|
||||
} else {
|
||||
RichString_setChar(&bar, j, '|');
|
||||
RichString_setChar(&bar, startPos + j, '|');
|
||||
}
|
||||
}
|
||||
offset = nextOffset;
|
||||
@ -236,14 +253,15 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
// ...then print the buffer.
|
||||
offset = 0;
|
||||
for (uint8_t i = 0; i < this->curItems; i++) {
|
||||
attrset(CRT_colors[Meter_attributes(this)[i]]);
|
||||
RichString_printoffnVal(bar, y, x + offset, offset, blockSizes[i]);
|
||||
int attr = this->curAttributes ? this->curAttributes[i] : Meter_attributes(this)[i];
|
||||
RichString_setAttrn(&bar, CRT_colors[attr], startPos + offset, blockSizes[i]);
|
||||
RichString_printoffnVal(bar, y, x + offset, startPos + offset, MINIMUM(blockSizes[i], w - offset));
|
||||
offset += blockSizes[i];
|
||||
offset = CLAMP(offset, 0, w);
|
||||
}
|
||||
if (offset < w) {
|
||||
attrset(CRT_colors[BAR_SHADOW]);
|
||||
RichString_printoffnVal(bar, y, x + offset, offset, w - offset);
|
||||
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, w - offset);
|
||||
RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
|
||||
}
|
||||
|
||||
RichString_end(bar);
|
||||
@ -274,9 +292,6 @@ static const char* const GraphMeterMode_dotsAscii[] = {
|
||||
/*20*/":", /*21*/":", /*22*/":"
|
||||
};
|
||||
|
||||
static const char* const* GraphMeterMode_dots;
|
||||
static int GraphMeterMode_pixPerRow;
|
||||
|
||||
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
|
||||
if (!this->drawData) {
|
||||
@ -285,6 +300,8 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
GraphData* data = this->drawData;
|
||||
const int nValues = METER_BUFFER_LEN;
|
||||
|
||||
const char* const* GraphMeterMode_dots;
|
||||
int GraphMeterMode_pixPerRow;
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
if (CRT_utf8) {
|
||||
GraphMeterMode_dots = GraphMeterMode_dotsUtf8;
|
||||
@ -312,8 +329,8 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
for (int i = 0; i < nValues - 1; i++)
|
||||
data->values[i] = data->values[i + 1];
|
||||
|
||||
char buffer[nValues];
|
||||
Meter_updateValues(this, buffer, nValues - 1);
|
||||
char buffer[METER_BUFFER_LEN];
|
||||
Meter_updateValues(this, buffer, sizeof(buffer));
|
||||
|
||||
double value = 0.0;
|
||||
for (uint8_t i = 0; i < this->curItems; i++)
|
||||
@ -381,7 +398,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
LEDMeterMode_digits = LEDMeterMode_digitsAscii;
|
||||
|
||||
char buffer[METER_BUFFER_LEN];
|
||||
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
|
||||
Meter_updateValues(this, buffer, sizeof(buffer));
|
||||
|
||||
RichString_begin(out);
|
||||
Meter_displayBuffer(this, buffer, &out);
|
||||
@ -396,7 +413,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
int xx = x + strlen(this->caption);
|
||||
int len = RichString_sizeVal(out);
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = RichString_getCharVal(out, i);
|
||||
int c = RichString_getCharVal(out, i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
LEDMeterMode_drawDigit(xx, y, c - 48);
|
||||
xx += 4;
|
||||
@ -444,8 +461,7 @@ const MeterMode* const Meter_modes[] = {
|
||||
|
||||
/* Blank meter */
|
||||
|
||||
static void BlankMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
(void) this; (void) buffer; (void) size;
|
||||
static void BlankMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t size) {
|
||||
if (size > 0) {
|
||||
*buffer = 0;
|
||||
}
|
||||
|
28
Meter.h
28
Meter.h
@ -20,13 +20,36 @@ in the source distribution for its full text.
|
||||
|
||||
#define METER_BUFFER_LEN 256
|
||||
|
||||
#define METER_BUFFER_CHECK(buffer, size, written) \
|
||||
do { \
|
||||
if ((written) < 0 || (size_t)(written) >= (size)) { \
|
||||
return; \
|
||||
} \
|
||||
(buffer) += (written); \
|
||||
(size) -= (size_t)(written); \
|
||||
} while (0)
|
||||
|
||||
#define METER_BUFFER_APPEND_CHR(buffer, size, c) \
|
||||
do { \
|
||||
if ((size) < 2) { \
|
||||
return; \
|
||||
} \
|
||||
*(buffer)++ = c; \
|
||||
*(buffer) = '\0'; \
|
||||
(size)--; \
|
||||
if ((size) == 0) { \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
struct Meter_;
|
||||
typedef struct Meter_ Meter;
|
||||
|
||||
typedef void(*Meter_Init)(Meter*);
|
||||
typedef void(*Meter_Done)(Meter*);
|
||||
typedef void(*Meter_UpdateMode)(Meter*, int);
|
||||
typedef void(*Meter_UpdateValues)(Meter*, char*, int);
|
||||
typedef void(*Meter_UpdateValues)(Meter*, char*, size_t);
|
||||
typedef void(*Meter_Draw)(Meter*, int, int, int);
|
||||
|
||||
typedef struct MeterClass_ {
|
||||
@ -77,6 +100,7 @@ struct Meter_ {
|
||||
int h;
|
||||
const ProcessList* pl;
|
||||
uint8_t curItems;
|
||||
const int* curAttributes;
|
||||
double* values;
|
||||
double total;
|
||||
void* meterData;
|
||||
@ -101,7 +125,7 @@ extern const MeterClass Meter_class;
|
||||
|
||||
Meter* Meter_new(const ProcessList* pl, int param, const MeterClass* type);
|
||||
|
||||
int Meter_humanUnit(char* buffer, unsigned long int value, int size);
|
||||
int Meter_humanUnit(char* buffer, unsigned long int value, size_t size);
|
||||
|
||||
void Meter_delete(Object* cast);
|
||||
|
||||
|
@ -21,8 +21,8 @@ in the source distribution for its full text.
|
||||
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI
|
||||
// we call them "Styles".
|
||||
static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL};
|
||||
static const char* const MetersKeys[] = {"Space", "Enter", " ", "Del", "F10"};
|
||||
static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
|
||||
static const char* const MetersKeys[] = {"Space", "Enter", "", "Del", "F10"};
|
||||
static const int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
|
||||
|
||||
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
|
||||
// terminals, breaking our aligning.
|
||||
@ -30,7 +30,7 @@ static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
|
||||
// considered "Ambiguous characters".
|
||||
static const char* const MetersMovingFunctions[] = {"Style ", "Lock ", "Up ", "Down ", "Left ", "Right ", " ", "Delete", "Done ", NULL};
|
||||
static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-", "->", " ", "Del", "F10"};
|
||||
static int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
|
||||
static const int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
|
||||
static FunctionBar* Meters_movingBar = NULL;
|
||||
|
||||
void MetersPanel_cleanup() {
|
||||
@ -55,13 +55,12 @@ void MetersPanel_setMoving(MetersPanel* this, bool moving) {
|
||||
selected->moving = moving;
|
||||
}
|
||||
if (!moving) {
|
||||
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
|
||||
Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
|
||||
Panel_setDefaultBar(super);
|
||||
} else {
|
||||
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]);
|
||||
Panel_setSelectionColor(super, PANEL_SELECTION_FOLLOW);
|
||||
super->currentBar = Meters_movingBar;
|
||||
}
|
||||
FunctionBar_draw(this->super.currentBar);
|
||||
}
|
||||
|
||||
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {
|
||||
|
@ -24,7 +24,7 @@ static unsigned long int cached_rxp_diff = 0;
|
||||
static unsigned long int cached_txb_diff = 0;
|
||||
static unsigned long int cached_txp_diff = 0;
|
||||
|
||||
static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, int len) {
|
||||
static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t len) {
|
||||
static unsigned long long int cached_last_update = 0;
|
||||
|
||||
struct timeval tv;
|
||||
@ -88,24 +88,24 @@ static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, i
|
||||
|
||||
static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
|
||||
if (!hasData) {
|
||||
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data");
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[64];
|
||||
|
||||
RichString_write(out, CRT_colors[METER_TEXT], "rx: ");
|
||||
RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: ");
|
||||
Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer));
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer);
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
|
||||
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " tx: ");
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " tx: ");
|
||||
Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer));
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
|
||||
|
||||
xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], buffer);
|
||||
}
|
||||
|
||||
const MeterClass NetworkIOMeter_class = {
|
||||
|
2
Object.h
2
Object.h
@ -24,7 +24,7 @@ struct Object_;
|
||||
typedef struct Object_ Object;
|
||||
|
||||
typedef void(*Object_Display)(const Object*, RichString*);
|
||||
typedef long(*Object_Compare)(const void*, const void*);
|
||||
typedef int(*Object_Compare)(const void*, const void*);
|
||||
typedef void(*Object_Delete)(Object*);
|
||||
|
||||
#define Object_getClass(obj_) ((const Object*)(obj_))->klass
|
||||
|
@ -74,7 +74,7 @@ OpenFilesScreen* OpenFilesScreen_new(const Process* process) {
|
||||
} else {
|
||||
this->pid = process->pid;
|
||||
}
|
||||
return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " FD TYPE MODE DEVICE SIZE NODE NAME");
|
||||
return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE MODE DEVICE SIZE NODE NAME");
|
||||
}
|
||||
|
||||
void OpenFilesScreen_delete(Object* this) {
|
||||
|
22
OptionItem.c
22
OptionItem.c
@ -12,7 +12,9 @@ in the source distribution for its full text.
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Macros.h"
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static void OptionItem_delete(Object* cast) {
|
||||
@ -27,14 +29,14 @@ static void CheckItem_display(const Object* cast, RichString* out) {
|
||||
const CheckItem* this = (const CheckItem*)cast;
|
||||
assert (this != NULL);
|
||||
|
||||
RichString_write(out, CRT_colors[CHECK_BOX], "[");
|
||||
RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
|
||||
if (CheckItem_get(this)) {
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], "x");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
|
||||
} else {
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], " ");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
|
||||
}
|
||||
RichString_append(out, CRT_colors[CHECK_BOX], "] ");
|
||||
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text);
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "] ");
|
||||
RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
|
||||
}
|
||||
|
||||
static void NumberItem_display(const Object* cast, RichString* out) {
|
||||
@ -42,7 +44,7 @@ static void NumberItem_display(const Object* cast, RichString* out) {
|
||||
assert (this != NULL);
|
||||
|
||||
char buffer[12];
|
||||
RichString_write(out, CRT_colors[CHECK_BOX], "[");
|
||||
RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
|
||||
int written;
|
||||
if (this->scale < 0) {
|
||||
written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this));
|
||||
@ -51,12 +53,12 @@ static void NumberItem_display(const Object* cast, RichString* out) {
|
||||
} else {
|
||||
written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this));
|
||||
}
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], buffer);
|
||||
RichString_append(out, CRT_colors[CHECK_BOX], "]");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
|
||||
for (int i = written; i < 5; i++) {
|
||||
RichString_append(out, CRT_colors[CHECK_BOX], " ");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], " ");
|
||||
}
|
||||
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text);
|
||||
RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
|
||||
}
|
||||
|
||||
const OptionItemClass OptionItem_class = {
|
||||
|
90
Panel.c
90
Panel.c
@ -30,7 +30,7 @@ const PanelClass Panel_class = {
|
||||
.eventHandler = Panel_selectByTyping,
|
||||
};
|
||||
|
||||
Panel* Panel_new(int x, int y, int w, int h, bool owner, const ObjectClass* type, FunctionBar* fuBar) {
|
||||
Panel* Panel_new(int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar) {
|
||||
Panel* this;
|
||||
this = xMalloc(sizeof(Panel));
|
||||
Object_setClass(this, Class(Panel));
|
||||
@ -55,11 +55,13 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
|
||||
this->scrollH = 0;
|
||||
this->selected = 0;
|
||||
this->oldSelected = 0;
|
||||
this->selectedLen = 0;
|
||||
this->needsRedraw = true;
|
||||
this->wasFocus = false;
|
||||
RichString_beginAllocated(this->header);
|
||||
this->defaultBar = fuBar;
|
||||
this->currentBar = fuBar;
|
||||
this->selectionColor = CRT_colors[PANEL_SELECTION_FOCUS];
|
||||
this->selectionColorId = PANEL_SELECTION_FOCUS;
|
||||
}
|
||||
|
||||
void Panel_done(Panel* this) {
|
||||
@ -70,19 +72,12 @@ void Panel_done(Panel* this) {
|
||||
RichString_end(this->header);
|
||||
}
|
||||
|
||||
void Panel_setSelectionColor(Panel* this, int color) {
|
||||
this->selectionColor = color;
|
||||
}
|
||||
|
||||
RichString* Panel_getHeader(Panel* this) {
|
||||
assert (this != NULL);
|
||||
|
||||
this->needsRedraw = true;
|
||||
return &(this->header);
|
||||
void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
|
||||
this->selectionColorId = colorId;
|
||||
}
|
||||
|
||||
inline void Panel_setHeader(Panel* this, const char* header) {
|
||||
RichString_write(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
|
||||
RichString_writeWide(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
|
||||
this->needsRedraw = true;
|
||||
}
|
||||
|
||||
@ -97,10 +92,6 @@ void Panel_move(Panel* this, int x, int y) {
|
||||
void Panel_resize(Panel* this, int w, int h) {
|
||||
assert (this != NULL);
|
||||
|
||||
if (RichString_sizeVal(this->header) > 0) {
|
||||
h--;
|
||||
}
|
||||
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
this->needsRedraw = true;
|
||||
@ -217,7 +208,7 @@ void Panel_splice(Panel* this, Vector* from) {
|
||||
this->needsRedraw = true;
|
||||
}
|
||||
|
||||
void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar) {
|
||||
assert (this != NULL);
|
||||
|
||||
int size = Vector_size(this->items);
|
||||
@ -226,12 +217,21 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
int x = this->x;
|
||||
int h = this->h;
|
||||
|
||||
int headerLen = RichString_sizeVal(this->header);
|
||||
if (headerLen > 0) {
|
||||
int attr = focus
|
||||
if (hideFunctionBar)
|
||||
h++;
|
||||
|
||||
const int header_attr = focus
|
||||
? CRT_colors[PANEL_HEADER_FOCUS]
|
||||
: CRT_colors[PANEL_HEADER_UNFOCUS];
|
||||
attrset(attr);
|
||||
if (force_redraw) {
|
||||
if (Panel_printHeaderFn(this))
|
||||
Panel_printHeader(this);
|
||||
else
|
||||
RichString_setAttr(&this->header, header_attr);
|
||||
}
|
||||
int headerLen = RichString_sizeVal(this->header);
|
||||
if (headerLen > 0) {
|
||||
attrset(header_attr);
|
||||
mvhline(y, x, ' ', this->w);
|
||||
if (scrollH < headerLen) {
|
||||
RichString_printoffnVal(this->header, y, x, scrollH,
|
||||
@ -239,6 +239,7 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
}
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
y++;
|
||||
h--;
|
||||
}
|
||||
|
||||
// ensure scroll area is on screen
|
||||
@ -262,10 +263,10 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
int upTo = MINIMUM(first + h, size);
|
||||
|
||||
int selectionColor = focus
|
||||
? this->selectionColor
|
||||
? CRT_colors[this->selectionColorId]
|
||||
: CRT_colors[PANEL_SELECTION_UNFOCUS];
|
||||
|
||||
if (this->needsRedraw) {
|
||||
if (this->needsRedraw || force_redraw) {
|
||||
int line = 0;
|
||||
for (int i = first; line < h && i < upTo; i++) {
|
||||
Object* itemObj = Vector_get(this->items, i);
|
||||
@ -293,7 +294,6 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
mvhline(y + line, x, ' ', this->w);
|
||||
line++;
|
||||
}
|
||||
this->needsRedraw = false;
|
||||
|
||||
} else {
|
||||
Object* oldObj = Vector_get(this->items, this->oldSelected);
|
||||
@ -319,17 +319,35 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
RichString_end(new);
|
||||
RichString_end(old);
|
||||
}
|
||||
|
||||
if (focus && (this->needsRedraw || force_redraw || !this->wasFocus)) {
|
||||
if (Panel_drawFunctionBarFn(this))
|
||||
Panel_drawFunctionBar(this, hideFunctionBar);
|
||||
else if (!hideFunctionBar)
|
||||
FunctionBar_draw(this->currentBar);
|
||||
}
|
||||
|
||||
this->oldSelected = this->selected;
|
||||
this->wasFocus = focus;
|
||||
this->needsRedraw = false;
|
||||
move(0, 0);
|
||||
}
|
||||
|
||||
static int Panel_headerHeight(const Panel* this) {
|
||||
return RichString_sizeVal(this->header) > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
bool Panel_onKey(Panel* this, int key) {
|
||||
assert (this != NULL);
|
||||
|
||||
int size = Vector_size(this->items);
|
||||
const int size = Vector_size(this->items);
|
||||
|
||||
#define CLAMP_INDEX(var, delta, min, max) \
|
||||
CLAMP((var) + (delta), (min), MAXIMUM(0, (max)))
|
||||
#define PANEL_SCROLL(amount) \
|
||||
do { \
|
||||
this->selected += (amount); \
|
||||
this->scrollV = CLAMP(this->scrollV + (amount), 0, MAXIMUM(0, (size - this->h - Panel_headerHeight(this)))); \
|
||||
this->needsRedraw = true; \
|
||||
} while (0)
|
||||
|
||||
switch (key) {
|
||||
case KEY_DOWN:
|
||||
@ -363,27 +381,19 @@ bool Panel_onKey(Panel* this, int key) {
|
||||
break;
|
||||
|
||||
case KEY_PPAGE:
|
||||
this->selected -= (this->h - 1);
|
||||
this->scrollV = CLAMP_INDEX(this->scrollV, -(this->h - 1), 0, size - this->h);
|
||||
this->needsRedraw = true;
|
||||
PANEL_SCROLL(-(this->h - Panel_headerHeight(this)));
|
||||
break;
|
||||
|
||||
case KEY_NPAGE:
|
||||
this->selected += (this->h - 1);
|
||||
this->scrollV = CLAMP_INDEX(this->scrollV, +(this->h - 1), 0, size - this->h);
|
||||
this->needsRedraw = true;
|
||||
PANEL_SCROLL(+(this->h - Panel_headerHeight(this)));
|
||||
break;
|
||||
|
||||
case KEY_WHEELUP:
|
||||
this->selected -= CRT_scrollWheelVAmount;
|
||||
this->scrollV = CLAMP_INDEX(this->scrollV, -CRT_scrollWheelVAmount, 0, size - this->h);
|
||||
this->needsRedraw = true;
|
||||
PANEL_SCROLL(-CRT_scrollWheelVAmount);
|
||||
break;
|
||||
|
||||
case KEY_WHEELDOWN:
|
||||
this->selected += CRT_scrollWheelVAmount;
|
||||
this->scrollV = CLAMP_INDEX(this->scrollV, +CRT_scrollWheelVAmount, 0, size - this->h);
|
||||
this->needsRedraw = true;
|
||||
PANEL_SCROLL(+CRT_scrollWheelVAmount);
|
||||
break;
|
||||
|
||||
case KEY_HOME:
|
||||
@ -408,7 +418,7 @@ bool Panel_onKey(Panel* this, int key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#undef CLAMP_INDEX
|
||||
#undef PANEL_SCROLL
|
||||
|
||||
// ensure selection within bounds
|
||||
if (this->selected < 0 || size == 0) {
|
||||
|
31
Panel.h
31
Panel.h
@ -9,6 +9,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "FunctionBar.h"
|
||||
#include "Object.h"
|
||||
#include "RichString.h"
|
||||
@ -22,9 +23,10 @@ typedef enum HandlerResult_ {
|
||||
HANDLED = 0x01,
|
||||
IGNORED = 0x02,
|
||||
BREAK_LOOP = 0x04,
|
||||
REDRAW = 0x08,
|
||||
RESCAN = 0x10,
|
||||
SYNTH_KEY = 0x20,
|
||||
REFRESH = 0x08,
|
||||
REDRAW = 0x10,
|
||||
RESCAN = 0x20,
|
||||
SYNTH_KEY = 0x40,
|
||||
} HandlerResult;
|
||||
|
||||
#define EVENT_SET_SELECTED (-1)
|
||||
@ -33,16 +35,24 @@ typedef enum HandlerResult_ {
|
||||
#define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000)
|
||||
#define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000)
|
||||
|
||||
typedef HandlerResult(*Panel_EventHandler)(Panel*, int);
|
||||
typedef HandlerResult (*Panel_EventHandler)(Panel*, int);
|
||||
typedef void (*Panel_DrawFunctionBar)(Panel*, bool);
|
||||
typedef void (*Panel_PrintHeader)(Panel*);
|
||||
|
||||
typedef struct PanelClass_ {
|
||||
const ObjectClass super;
|
||||
const Panel_EventHandler eventHandler;
|
||||
const Panel_DrawFunctionBar drawFunctionBar;
|
||||
const Panel_PrintHeader printHeader;
|
||||
} PanelClass;
|
||||
|
||||
#define As_Panel(this_) ((const PanelClass*)((this_)->super.klass))
|
||||
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
|
||||
#define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_)
|
||||
#define Panel_eventHandler(this_, ev_) (assert(As_Panel(this_)->eventHandler), As_Panel(this_)->eventHandler((Panel*)(this_), ev_))
|
||||
#define Panel_drawFunctionBarFn(this_) As_Panel(this_)->drawFunctionBar
|
||||
#define Panel_drawFunctionBar(this_, hideFB_) (assert(As_Panel(this_)->drawFunctionBar), As_Panel(this_)->drawFunctionBar((Panel*)(this_), hideFB_))
|
||||
#define Panel_printHeaderFn(this_) As_Panel(this_)->printHeader
|
||||
#define Panel_printHeader(this_) (assert(As_Panel(this_)->printHeader), As_Panel(this_)->printHeader((Panel*)(this_)))
|
||||
|
||||
struct Panel_ {
|
||||
Object super;
|
||||
@ -55,10 +65,11 @@ struct Panel_ {
|
||||
int scrollV;
|
||||
short scrollH;
|
||||
bool needsRedraw;
|
||||
bool wasFocus;
|
||||
FunctionBar* currentBar;
|
||||
FunctionBar* defaultBar;
|
||||
RichString header;
|
||||
int selectionColor;
|
||||
ColorElements selectionColorId;
|
||||
};
|
||||
|
||||
#define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0)
|
||||
@ -67,7 +78,7 @@ struct Panel_ {
|
||||
|
||||
extern const PanelClass Panel_class;
|
||||
|
||||
Panel* Panel_new(int x, int y, int w, int h, bool owner, const ObjectClass* type, FunctionBar* fuBar);
|
||||
Panel* Panel_new(int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar);
|
||||
|
||||
void Panel_delete(Object* cast);
|
||||
|
||||
@ -75,9 +86,7 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
|
||||
|
||||
void Panel_done(Panel* this);
|
||||
|
||||
void Panel_setSelectionColor(Panel* this, int color);
|
||||
|
||||
RichString* Panel_getHeader(Panel* this);
|
||||
void Panel_setSelectionColor(Panel* this, ColorElements colorId);
|
||||
|
||||
void Panel_setHeader(Panel* this, const char* header);
|
||||
|
||||
@ -109,7 +118,7 @@ int Panel_size(Panel* this);
|
||||
|
||||
void Panel_setSelected(Panel* this, int selected);
|
||||
|
||||
void Panel_draw(Panel* this, bool focus, bool highlightSelected);
|
||||
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar);
|
||||
|
||||
void Panel_splice(Panel* this, Vector* from);
|
||||
|
||||
|
176
Process.c
176
Process.c
@ -38,23 +38,15 @@ in the source distribution for its full text.
|
||||
|
||||
static uid_t Process_getuid = (uid_t)-1;
|
||||
|
||||
char Process_pidFormat[20] = "%7d ";
|
||||
|
||||
static char Process_titleBuffer[20][20];
|
||||
int Process_pidDigits = 7;
|
||||
|
||||
void Process_setupColumnWidths() {
|
||||
int maxPid = Platform_getMaxPid();
|
||||
if (maxPid == -1)
|
||||
return;
|
||||
|
||||
int digits = ceil(log10(maxPid));
|
||||
assert(digits < 20);
|
||||
for (int i = 0; Process_pidColumns[i].label; i++) {
|
||||
assert(i < 20);
|
||||
xSnprintf(Process_titleBuffer[i], 20, "%*s ", digits, Process_pidColumns[i].label);
|
||||
Process_fields[Process_pidColumns[i].id].title = Process_titleBuffer[i];
|
||||
}
|
||||
xSnprintf(Process_pidFormat, sizeof(Process_pidFormat), "%%%dd ", digits);
|
||||
Process_pidDigits = ceil(log10(maxPid));
|
||||
assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS);
|
||||
}
|
||||
|
||||
void Process_humanNumber(RichString* str, unsigned long long number, bool coloring) {
|
||||
@ -74,53 +66,53 @@ void Process_humanNumber(RichString* str, unsigned long long number, bool colori
|
||||
if (number < 1000) {
|
||||
//Plain number, no markings
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number);
|
||||
RichString_appendn(str, processColor, buffer, len);
|
||||
RichString_appendnAscii(str, processColor, buffer, len);
|
||||
} else if (number < 100000) {
|
||||
//2 digit MB, 3 digit KB
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/1000);
|
||||
RichString_appendn(str, processMegabytesColor, buffer, len);
|
||||
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
|
||||
number %= 1000;
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number);
|
||||
RichString_appendn(str, processColor, buffer, len);
|
||||
RichString_appendnAscii(str, processColor, buffer, len);
|
||||
} else if (number < 1000 * ONE_K) {
|
||||
//3 digit MB
|
||||
number /= ONE_K;
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number);
|
||||
RichString_appendn(str, processMegabytesColor, buffer, len);
|
||||
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
|
||||
} else if (number < 10000 * ONE_K) {
|
||||
//1 digit GB, 3 digit MB
|
||||
number /= ONE_K;
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
|
||||
RichString_appendn(str, processGigabytesColor, buffer, len);
|
||||
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
|
||||
number %= 1000;
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number);
|
||||
RichString_appendn(str, processMegabytesColor, buffer, len);
|
||||
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
|
||||
} else if (number < 100000 * ONE_K) {
|
||||
//2 digit GB, 1 digit MB
|
||||
number /= 100 * ONE_K;
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/10);
|
||||
RichString_appendn(str, processGigabytesColor, buffer, len);
|
||||
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
|
||||
number %= 10;
|
||||
len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number);
|
||||
RichString_appendn(str, processMegabytesColor, buffer, len);
|
||||
RichString_append(str, processGigabytesColor, "G ");
|
||||
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
|
||||
RichString_appendAscii(str, processGigabytesColor, "G ");
|
||||
} else if (number < 1000 * ONE_M) {
|
||||
//3 digit GB
|
||||
number /= ONE_M;
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number);
|
||||
RichString_appendn(str, processGigabytesColor, buffer, len);
|
||||
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
|
||||
} else if (number < 10000ULL * ONE_M) {
|
||||
//1 digit TB, 3 digit GB
|
||||
number /= ONE_M;
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
|
||||
RichString_appendn(str, largeNumberColor, buffer, len);
|
||||
RichString_appendnAscii(str, largeNumberColor, buffer, len);
|
||||
number %= 1000;
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number);
|
||||
RichString_appendn(str, processGigabytesColor, buffer, len);
|
||||
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
|
||||
} else {
|
||||
//2 digit TB and above
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%4.1lfT ", (double)number/ONE_G);
|
||||
RichString_appendn(str, largeNumberColor, buffer, len);
|
||||
RichString_appendnAscii(str, largeNumberColor, buffer, len);
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,26 +131,25 @@ void Process_colorNumber(RichString* str, unsigned long long number, bool colori
|
||||
}
|
||||
|
||||
if (number == ULLONG_MAX) {
|
||||
int len = xSnprintf(buffer, sizeof(buffer), " N/A ");
|
||||
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
|
||||
RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
|
||||
} else if (number >= 100000LL * ONE_DECIMAL_T) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G);
|
||||
RichString_appendn(str, largeNumberColor, buffer, 12);
|
||||
RichString_appendnAscii(str, largeNumberColor, buffer, 12);
|
||||
} else if (number >= 100LL * ONE_DECIMAL_T) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M);
|
||||
RichString_appendn(str, largeNumberColor, buffer, 8);
|
||||
RichString_appendn(str, processMegabytesColor, buffer+8, 4);
|
||||
RichString_appendnAscii(str, largeNumberColor, buffer, 8);
|
||||
RichString_appendnAscii(str, processMegabytesColor, buffer+8, 4);
|
||||
} else if (number >= 10LL * ONE_DECIMAL_G) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K);
|
||||
RichString_appendn(str, largeNumberColor, buffer, 5);
|
||||
RichString_appendn(str, processMegabytesColor, buffer+5, 3);
|
||||
RichString_appendn(str, processColor, buffer+8, 4);
|
||||
RichString_appendnAscii(str, largeNumberColor, buffer, 5);
|
||||
RichString_appendnAscii(str, processMegabytesColor, buffer+5, 3);
|
||||
RichString_appendnAscii(str, processColor, buffer+8, 4);
|
||||
} else {
|
||||
xSnprintf(buffer, sizeof(buffer), "%11llu ", number);
|
||||
RichString_appendn(str, largeNumberColor, buffer, 2);
|
||||
RichString_appendn(str, processMegabytesColor, buffer+2, 3);
|
||||
RichString_appendn(str, processColor, buffer+5, 3);
|
||||
RichString_appendn(str, processShadowColor, buffer+8, 4);
|
||||
RichString_appendnAscii(str, largeNumberColor, buffer, 2);
|
||||
RichString_appendnAscii(str, processMegabytesColor, buffer+2, 3);
|
||||
RichString_appendnAscii(str, processColor, buffer+5, 3);
|
||||
RichString_appendnAscii(str, processShadowColor, buffer+8, 4);
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,16 +163,16 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths) {
|
||||
char buffer[10];
|
||||
if (hours >= 100) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%7lluh ", hours);
|
||||
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
|
||||
RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer);
|
||||
} else {
|
||||
if (hours) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%2lluh", hours);
|
||||
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
|
||||
RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds);
|
||||
} else {
|
||||
xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths);
|
||||
}
|
||||
RichString_append(str, CRT_colors[DEFAULT_COLOR], buffer);
|
||||
RichString_appendAscii(str, CRT_colors[DEFAULT_COLOR], buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,38 +183,38 @@ void Process_fillStarttimeBuffer(Process* this) {
|
||||
}
|
||||
|
||||
static inline void Process_writeCommand(const Process* this, int attr, int baseattr, RichString* str) {
|
||||
int start = RichString_size(str), finish = 0;
|
||||
int start = RichString_size(str);
|
||||
int len = 0;
|
||||
const char* comm = this->comm;
|
||||
|
||||
if (this->settings->highlightBaseName || !this->settings->showProgramPath) {
|
||||
int i, basename = 0;
|
||||
for (i = 0; i < this->basenameOffset; i++) {
|
||||
int basename = 0;
|
||||
for (int i = 0; i < this->basenameOffset; i++) {
|
||||
if (comm[i] == '/') {
|
||||
basename = i + 1;
|
||||
} else if (comm[i] == ':') {
|
||||
finish = i + 1;
|
||||
len = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!finish) {
|
||||
if (len == 0) {
|
||||
if (this->settings->showProgramPath) {
|
||||
start += basename;
|
||||
} else {
|
||||
comm += basename;
|
||||
}
|
||||
finish = this->basenameOffset - basename;
|
||||
len = this->basenameOffset - basename;
|
||||
}
|
||||
finish += start - 1;
|
||||
}
|
||||
|
||||
RichString_append(str, attr, comm);
|
||||
RichString_appendWide(str, attr, comm);
|
||||
|
||||
if (this->settings->highlightBaseName) {
|
||||
RichString_setAttrn(str, baseattr, start, finish);
|
||||
RichString_setAttrn(str, baseattr, start, len);
|
||||
}
|
||||
}
|
||||
|
||||
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring) {
|
||||
void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring) {
|
||||
int largeNumberColor = CRT_colors[LARGE_NUMBER];
|
||||
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
|
||||
int processColor = CRT_colors[PROCESS];
|
||||
@ -234,31 +225,35 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
|
||||
}
|
||||
|
||||
if (isnan(rate)) {
|
||||
int len = xSnprintf(buffer, n, " N/A ");
|
||||
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
|
||||
RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
|
||||
} else if (rate < ONE_K) {
|
||||
int len = snprintf(buffer, n, "%7.2f B/s ", rate);
|
||||
RichString_appendn(str, processColor, buffer, len);
|
||||
RichString_appendnAscii(str, processColor, buffer, len);
|
||||
} else if (rate < ONE_M) {
|
||||
int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K);
|
||||
RichString_appendn(str, processColor, buffer, len);
|
||||
RichString_appendnAscii(str, processColor, buffer, len);
|
||||
} else if (rate < ONE_G) {
|
||||
int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M);
|
||||
RichString_appendn(str, processMegabytesColor, buffer, len);
|
||||
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
|
||||
} else if (rate < ONE_T) {
|
||||
int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G);
|
||||
RichString_appendn(str, largeNumberColor, buffer, len);
|
||||
RichString_appendnAscii(str, largeNumberColor, buffer, len);
|
||||
} else {
|
||||
int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T);
|
||||
RichString_appendn(str, largeNumberColor, buffer, len);
|
||||
RichString_appendnAscii(str, largeNumberColor, buffer, len);
|
||||
}
|
||||
}
|
||||
|
||||
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width) {
|
||||
int c = RichString_appendnWide(str, attr, content, MINIMUM(width, strlen(content)));
|
||||
RichString_appendChr(str, ' ', width + 1 - c);
|
||||
}
|
||||
|
||||
void Process_writeField(const 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;
|
||||
size_t n = sizeof(buffer) - 1;
|
||||
bool coloring = this->settings->highlightMegabytes;
|
||||
|
||||
switch (field) {
|
||||
@ -312,7 +307,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
|
||||
} else {
|
||||
ret = snprintf(buf, n, " ");
|
||||
}
|
||||
if (ret < 0 || ret >= n) {
|
||||
if (ret < 0 || (size_t)ret >= n) {
|
||||
written = n;
|
||||
} else {
|
||||
written = ret;
|
||||
@ -320,17 +315,18 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
|
||||
buf += written;
|
||||
n -= written;
|
||||
}
|
||||
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
|
||||
|
||||
const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
|
||||
xSnprintf(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);
|
||||
RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
|
||||
Process_writeCommand(this, attr, baseattr, str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
case MAJFLT: Process_colorNumber(str, this->majflt, coloring); return;
|
||||
case MINFLT: Process_colorNumber(str, this->minflt, coloring); return;
|
||||
case M_RESIDENT: Process_humanNumber(str, this->m_resident * CRT_pageSizeKB, coloring); return;
|
||||
case M_VIRT: Process_humanNumber(str, this->m_virt * CRT_pageSizeKB, coloring); return;
|
||||
case M_RESIDENT: Process_humanNumber(str, this->m_resident, coloring); return;
|
||||
case M_VIRT: Process_humanNumber(str, this->m_virt, coloring); return;
|
||||
case NICE: {
|
||||
xSnprintf(buffer, n, "%3ld ", this->nice);
|
||||
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
|
||||
@ -339,9 +335,9 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
|
||||
break;
|
||||
}
|
||||
case NLWP: xSnprintf(buffer, n, "%4ld ", this->nlwp); break;
|
||||
case PGRP: xSnprintf(buffer, n, Process_pidFormat, this->pgrp); break;
|
||||
case PID: xSnprintf(buffer, n, Process_pidFormat, this->pid); break;
|
||||
case PPID: xSnprintf(buffer, n, Process_pidFormat, this->ppid); break;
|
||||
case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break;
|
||||
case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break;
|
||||
case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break;
|
||||
case PRIORITY: {
|
||||
if(this->priority <= -100)
|
||||
xSnprintf(buffer, n, " RT ");
|
||||
@ -350,7 +346,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
|
||||
break;
|
||||
}
|
||||
case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break;
|
||||
case SESSION: xSnprintf(buffer, n, Process_pidFormat, this->session); break;
|
||||
case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break;
|
||||
case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
|
||||
case STATE: {
|
||||
xSnprintf(buffer, n, "%c ", this->state);
|
||||
@ -366,27 +362,25 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
|
||||
}
|
||||
case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break;
|
||||
case TIME: Process_printTime(str, this->time); return;
|
||||
case TGID: xSnprintf(buffer, n, Process_pidFormat, this->tgid); break;
|
||||
case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break;
|
||||
case TGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tgid); break;
|
||||
case TPGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tpgid); break;
|
||||
case TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break;
|
||||
case USER: {
|
||||
if (Process_getuid != this->st_uid)
|
||||
attr = CRT_colors[PROCESS_SHADOW];
|
||||
|
||||
if (this->user) {
|
||||
xSnprintf(buffer, n, "%-9s ", this->user);
|
||||
} else {
|
||||
Process_printLeftAlignedField(str, attr, this->user, 9);
|
||||
return;
|
||||
}
|
||||
|
||||
xSnprintf(buffer, n, "%-9d ", this->st_uid);
|
||||
}
|
||||
if (buffer[9] != '\0') {
|
||||
buffer[9] = ' ';
|
||||
buffer[10] = '\0';
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
xSnprintf(buffer, n, "- ");
|
||||
}
|
||||
RichString_append(str, attr, buffer);
|
||||
RichString_appendWide(str, attr, buffer);
|
||||
}
|
||||
|
||||
void Process_display(const Object* cast, RichString* out) {
|
||||
@ -449,13 +443,13 @@ void Process_init(Process* this, const struct Settings_* settings) {
|
||||
}
|
||||
|
||||
void Process_toggleTag(Process* this) {
|
||||
this->tag = this->tag == true ? false : true;
|
||||
this->tag = !this->tag;
|
||||
}
|
||||
|
||||
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 this->processList->scanTs - this->seenTs <= 1000 * this->processList->settings->highlightDelaySecs;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -486,18 +480,18 @@ bool Process_sendSignal(Process* this, Arg sgn) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
long Process_pidCompare(const void* v1, const void* v2) {
|
||||
int Process_pidCompare(const void* v1, const void* v2) {
|
||||
const Process* p1 = (const Process*)v1;
|
||||
const Process* p2 = (const Process*)v2;
|
||||
return (p1->pid - p2->pid);
|
||||
|
||||
return SPACESHIP_NUMBER(p1->pid, p2->pid);
|
||||
}
|
||||
|
||||
long Process_compare(const void* v1, const void* v2) {
|
||||
int Process_compare(const void* v1, const void* v2) {
|
||||
const Process *p1, *p2;
|
||||
const Settings *settings = ((const Process*)v1)->settings;
|
||||
int r;
|
||||
|
||||
if (settings->direction == 1) {
|
||||
if (Settings_getActiveDirection(settings) == 1) {
|
||||
p1 = (const Process*)v1;
|
||||
p2 = (const Process*)v2;
|
||||
} else {
|
||||
@ -505,7 +499,21 @@ long Process_compare(const void* v1, const void* v2) {
|
||||
p1 = (const Process*)v2;
|
||||
}
|
||||
|
||||
switch (settings->sortKey) {
|
||||
ProcessField key = Settings_getActiveSortKey(settings);
|
||||
|
||||
int result = Process_compareByKey(p1, p2, key);
|
||||
|
||||
// Implement tie-breaker (needed to make tree mode more stable)
|
||||
if (!result)
|
||||
result = SPACESHIP_NUMBER(p1->pid, p2->pid);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) {
|
||||
int r;
|
||||
|
||||
switch (key) {
|
||||
case PERCENT_CPU:
|
||||
case PERCENT_NORM_CPU:
|
||||
return SPACESHIP_NUMBER(p2->percent_cpu, p1->percent_cpu);
|
||||
|
38
Process.h
38
Process.h
@ -10,21 +10,17 @@ in the source distribution for its full text.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "Object.h"
|
||||
#include "ProcessField.h"
|
||||
#include "RichString.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define SYS_ioprio_get __NR_ioprio_get
|
||||
#define SYS_ioprio_set __NR_ioprio_set
|
||||
#endif
|
||||
|
||||
#define PROCESS_FLAG_IO 0x0001
|
||||
#define DEFAULT_HIGHLIGHT_SECS 5
|
||||
|
||||
typedef enum ProcessFields {
|
||||
typedef enum ProcessField_ {
|
||||
NULL_PROCESSFIELD = 0,
|
||||
PID = 1,
|
||||
COMM = 2,
|
||||
@ -50,12 +46,12 @@ typedef enum ProcessFields {
|
||||
NLWP = 51,
|
||||
TGID = 52,
|
||||
PERCENT_NORM_CPU = 53,
|
||||
} ProcessField;
|
||||
|
||||
typedef struct ProcessPidColumn_ {
|
||||
int id;
|
||||
const char* label;
|
||||
} ProcessPidColumn;
|
||||
/* Platform specific fields, defined in ${platform}/ProcessField.h */
|
||||
PLATFORM_PROCESS_FIELDS
|
||||
|
||||
LAST_PROCESSFIELD
|
||||
} ProcessField;
|
||||
|
||||
struct Settings_;
|
||||
|
||||
@ -121,30 +117,34 @@ typedef struct ProcessFieldData_ {
|
||||
const char* title;
|
||||
const char* description;
|
||||
uint32_t flags;
|
||||
bool pidColumn;
|
||||
} ProcessFieldData;
|
||||
|
||||
// Implemented in platform-specific code:
|
||||
void Process_writeField(const Process* this, RichString* str, ProcessField field);
|
||||
long Process_compare(const void* v1, const void* v2);
|
||||
int Process_compare(const void* v1, const void* v2);
|
||||
void Process_delete(Object* cast);
|
||||
bool Process_isThread(const Process* this);
|
||||
extern ProcessFieldData Process_fields[];
|
||||
extern ProcessPidColumn Process_pidColumns[];
|
||||
extern char Process_pidFormat[20];
|
||||
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
|
||||
#define PROCESS_MAX_PID_DIGITS 19
|
||||
extern int Process_pidDigits;
|
||||
|
||||
typedef Process*(*Process_New)(const struct Settings_*);
|
||||
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
|
||||
typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
|
||||
typedef const char* (*Process_GetCommandStr)(const Process*);
|
||||
|
||||
typedef struct ProcessClass_ {
|
||||
const ObjectClass super;
|
||||
const Process_WriteField writeField;
|
||||
const Process_CompareByKey compareByKey;
|
||||
const Process_GetCommandStr getCommandStr;
|
||||
} ProcessClass;
|
||||
|
||||
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
|
||||
|
||||
#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm)
|
||||
#define Process_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_))
|
||||
|
||||
static inline pid_t Process_getParentPid(const Process* this) {
|
||||
return this->tgid == this->pid ? this->ppid : this->tgid;
|
||||
@ -177,7 +177,9 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths);
|
||||
|
||||
void Process_fillStarttimeBuffer(Process* this);
|
||||
|
||||
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
|
||||
void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring);
|
||||
|
||||
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width);
|
||||
|
||||
void Process_display(const Object* cast, RichString* out);
|
||||
|
||||
@ -199,6 +201,8 @@ bool Process_changePriorityBy(Process* this, Arg delta);
|
||||
|
||||
bool Process_sendSignal(Process* this, Arg sgn);
|
||||
|
||||
long Process_pidCompare(const void* v1, const void* v2);
|
||||
int Process_pidCompare(const void* v1, const void* v2);
|
||||
|
||||
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
|
||||
|
||||
#endif
|
||||
|
@ -10,10 +10,11 @@ in the source distribution for its full text.
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "Compat.h"
|
||||
#include "CRT.h"
|
||||
#include "Hashtable.h"
|
||||
#include "Macros.h"
|
||||
#include "Vector.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
@ -78,22 +79,48 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
|
||||
this->panel = panel;
|
||||
}
|
||||
|
||||
static const char* alignedProcessFieldTitle(ProcessField field) {
|
||||
const char* title = Process_fields[field].title;
|
||||
if (!title)
|
||||
return "- ";
|
||||
|
||||
if (!Process_fields[field].pidColumn)
|
||||
return title;
|
||||
|
||||
static char titleBuffer[PROCESS_MAX_PID_DIGITS + /* space */ 1 + /* null-terminator */ + 1];
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
|
||||
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
void ProcessList_printHeader(ProcessList* this, RichString* header) {
|
||||
RichString_prune(header);
|
||||
|
||||
const ProcessField* fields = this->settings->fields;
|
||||
const Settings* settings = this->settings;
|
||||
const ProcessField* fields = settings->fields;
|
||||
|
||||
ProcessField key = Settings_getActiveSortKey(settings);
|
||||
|
||||
for (int i = 0; fields[i]; i++) {
|
||||
const char* field = Process_fields[fields[i]].title;
|
||||
if (!field) {
|
||||
field = "- ";
|
||||
int color;
|
||||
if (settings->treeView && settings->treeViewAlwaysByPID) {
|
||||
color = CRT_colors[PANEL_HEADER_FOCUS];
|
||||
} else if (key == fields[i]) {
|
||||
color = CRT_colors[PANEL_SELECTION_FOCUS];
|
||||
} else {
|
||||
color = CRT_colors[PANEL_HEADER_FOCUS];
|
||||
}
|
||||
|
||||
int color = (this->settings->sortKey == fields[i]) ?
|
||||
CRT_colors[PANEL_SELECTION_FOCUS] : CRT_colors[PANEL_HEADER_FOCUS];
|
||||
RichString_append(header, color, field);
|
||||
if (COMM == fields[i] && this->settings->showMergedCommand) {
|
||||
RichString_append(header, color, "(merged)");
|
||||
RichString_appendWide(header, color, alignedProcessFieldTitle(fields[i]));
|
||||
if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') {
|
||||
header->chlen--; // rewind to override space
|
||||
RichString_appendnWide(header,
|
||||
CRT_colors[PANEL_SELECTION_FOCUS],
|
||||
CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_DESC : TREE_STR_ASC],
|
||||
1);
|
||||
}
|
||||
if (COMM == fields[i] && settings->showMergedCommand) {
|
||||
RichString_appendAscii(header, color, "(merged)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,7 +148,7 @@ void ProcessList_remove(ProcessList* this, Process* p) {
|
||||
Process* pp = Hashtable_remove(this->processTable, p->pid);
|
||||
assert(pp == p); (void)pp;
|
||||
|
||||
unsigned int pid = p->pid;
|
||||
pid_t pid = p->pid;
|
||||
int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
|
||||
assert(idx != -1);
|
||||
|
||||
@ -129,7 +156,12 @@ void ProcessList_remove(ProcessList* this, Process* p) {
|
||||
Vector_remove(this->processes, idx);
|
||||
}
|
||||
|
||||
assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
|
||||
if (this->following != -1 && this->following == pid) {
|
||||
this->following = -1;
|
||||
Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
|
||||
}
|
||||
|
||||
assert(Hashtable_get(this->processTable, pid) == NULL);
|
||||
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
||||
}
|
||||
|
||||
@ -148,7 +180,7 @@ int ProcessList_size(ProcessList* this) {
|
||||
//
|
||||
// The algorithm is based on `depth-first search`,
|
||||
// even though `breadth-first search` approach may be more efficient on first glance,
|
||||
// after comparision it may be not, as it's not safe to go deeper without first updating the tree structure.
|
||||
// after comparison it may be not, as it's not safe to go deeper without first updating the tree structure.
|
||||
// If it would be safe that approach would likely bring an advantage in performance.
|
||||
//
|
||||
// Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
|
||||
@ -328,14 +360,14 @@ static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level,
|
||||
Vector_delete(children);
|
||||
}
|
||||
|
||||
static long ProcessList_treeProcessCompare(const void* v1, const void* v2) {
|
||||
static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
|
||||
const Process *p1 = (const Process*)v1;
|
||||
const Process *p2 = (const Process*)v2;
|
||||
|
||||
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
|
||||
}
|
||||
|
||||
static long ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
|
||||
static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
|
||||
const Process *p1 = (const Process*)v1;
|
||||
const Process *p2 = (const Process*)v2;
|
||||
|
||||
@ -346,7 +378,7 @@ static long ProcessList_treeProcessCompareByPID(const void* v1, const void* v2)
|
||||
static void ProcessList_buildTree(ProcessList* this) {
|
||||
int node_counter = 1;
|
||||
int node_index = 0;
|
||||
int direction = this->settings->direction;
|
||||
int direction = Settings_getActiveDirection(this->settings);
|
||||
|
||||
// Sort by PID
|
||||
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
|
||||
@ -445,12 +477,7 @@ ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
|
||||
const ProcessField* fields = this->settings->fields;
|
||||
ProcessField field;
|
||||
for (int i = 0; (field = fields[i]); i++) {
|
||||
const char* title = Process_fields[field].title;
|
||||
if (!title) {
|
||||
title = "- ";
|
||||
}
|
||||
|
||||
int len = strlen(title);
|
||||
int len = strlen(alignedProcessFieldTitle(field));
|
||||
if (at >= x && at <= x + len) {
|
||||
return field;
|
||||
}
|
||||
@ -471,31 +498,27 @@ void ProcessList_rebuildPanel(ProcessList* this) {
|
||||
const char* incFilter = this->incFilter;
|
||||
|
||||
int currPos = Panel_getSelectedIndex(this->panel);
|
||||
pid_t currPid = this->following != -1 ? this->following : 0;
|
||||
int currScrollV = this->panel->scrollV;
|
||||
|
||||
Panel_prune(this->panel);
|
||||
int size = ProcessList_size(this);
|
||||
int idx = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
bool hidden = false;
|
||||
Process* p = ProcessList_get(this, i);
|
||||
|
||||
if ( (!p->show)
|
||||
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|
||||
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|
||||
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
|
||||
hidden = true;
|
||||
continue;
|
||||
|
||||
if (!hidden) {
|
||||
Panel_set(this->panel, idx, (Object*)p);
|
||||
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == currPid)) {
|
||||
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == this->following)) {
|
||||
Panel_setSelected(this->panel, idx);
|
||||
this->panel->scrollV = currScrollV;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) {
|
||||
@ -540,8 +563,10 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
|
||||
if (!firstScanDone) {
|
||||
this->scanTs = 0;
|
||||
firstScanDone = true;
|
||||
} else if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
|
||||
this->scanTs = now.tv_sec;
|
||||
} else if (Compat_clock_monotonic_gettime(&now) == 0) {
|
||||
// save time in millisecond, so with a delay in deciseconds
|
||||
// there are no irregularities
|
||||
this->scanTs = 1000 * now.tv_sec + now.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
ProcessList_goThroughEntries(this, false);
|
||||
@ -557,7 +582,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
|
||||
// process no longer exists
|
||||
if (this->settings->highlightChanges && p->wasShown) {
|
||||
// mark tombed
|
||||
p->tombTs = this->scanTs + this->settings->highlightDelaySecs;
|
||||
p->tombTs = this->scanTs + 1000 * this->settings->highlightDelaySecs;
|
||||
} else {
|
||||
// immediately remove
|
||||
ProcessList_remove(this, p);
|
||||
|
@ -27,7 +27,7 @@ ProcessLocksScreen* ProcessLocksScreen_new(const Process* process) {
|
||||
this->pid = process->tgid;
|
||||
else
|
||||
this->pid = process->pid;
|
||||
return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " ID TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME");
|
||||
return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ID TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME");
|
||||
}
|
||||
|
||||
void ProcessLocksScreen_delete(Object* this) {
|
||||
|
75
RichString.c
75
RichString.c
@ -7,6 +7,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "RichString.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -47,25 +48,35 @@ static void RichString_setLen(RichString* this, int len) {
|
||||
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
|
||||
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) {
|
||||
static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
|
||||
wchar_t data[len + 1];
|
||||
len = mbstowcs(data, data_c, len);
|
||||
if (len < 0)
|
||||
return;
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
int newLen = from + len;
|
||||
RichString_setLen(this, newLen);
|
||||
for (int i = from, j = 0; i < newLen; i++, j++) {
|
||||
this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : '?') } };
|
||||
}
|
||||
|
||||
return wcswidth(data, len);
|
||||
}
|
||||
|
||||
inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
|
||||
cchar_t* ch = this->chptr + start;
|
||||
finish = CLAMP(finish, 0, this->chlen - 1);
|
||||
for (int i = start; i <= finish; i++) {
|
||||
ch->attr = attrs;
|
||||
ch++;
|
||||
static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data, int from, int len) {
|
||||
int newLen = from + len;
|
||||
RichString_setLen(this, newLen);
|
||||
for (int i = from, j = 0; i < newLen; i++, j++) {
|
||||
this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint(data[j]) ? data[j] : '?') } };
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
inline void RichString_setAttrn(RichString* this, int attrs, int start, int charcount) {
|
||||
int end = CLAMP(start + charcount, 0, this->chlen);
|
||||
for (int i = start; i < end; i++) {
|
||||
this->chptr[i].attr = attrs;
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,21 +93,25 @@ int RichString_findChar(RichString* this, char c, int start) {
|
||||
|
||||
#else /* HAVE_LIBNCURSESW */
|
||||
|
||||
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) {
|
||||
static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
|
||||
int newLen = from + len;
|
||||
RichString_setLen(this, newLen);
|
||||
for (int i = from, j = 0; i < newLen; i++, j++) {
|
||||
this->chptr[i] = (data_c[j] >= 32 ? data_c[j] : '?') | attrs;
|
||||
this->chptr[i] = (((unsigned char)data_c[j]) >= 32 ? ((unsigned char)data_c[j]) : '?') | attrs;
|
||||
}
|
||||
this->chptr[newLen] = 0;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
|
||||
chtype* ch = this->chptr + start;
|
||||
finish = CLAMP(finish, 0, this->chlen - 1);
|
||||
for (int i = start; i <= finish; i++) {
|
||||
*ch = (*ch & 0xff) | attrs;
|
||||
ch++;
|
||||
static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data_c, int from, int len) {
|
||||
return RichString_writeFromWide(this, attrs, data_c, from, len);
|
||||
}
|
||||
|
||||
void RichString_setAttrn(RichString* this, int attrs, int start, int charcount) {
|
||||
int end = CLAMP(start + charcount, 0, this->chlen);
|
||||
for (int i = start; i < end; i++) {
|
||||
this->chptr[i] = (this->chptr[i] & 0xff) | attrs;
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,17 +144,29 @@ void RichString_appendChr(RichString* this, char c, int count) {
|
||||
}
|
||||
|
||||
void RichString_setAttr(RichString* this, int attrs) {
|
||||
RichString_setAttrn(this, attrs, 0, this->chlen - 1);
|
||||
RichString_setAttrn(this, attrs, 0, this->chlen);
|
||||
}
|
||||
|
||||
void RichString_append(RichString* this, int attrs, const char* data) {
|
||||
RichString_writeFrom(this, attrs, data, this->chlen, strlen(data));
|
||||
int RichString_appendWide(RichString* this, int attrs, const char* data) {
|
||||
return RichString_writeFromWide(this, attrs, data, this->chlen, strlen(data));
|
||||
}
|
||||
|
||||
void RichString_appendn(RichString* this, int attrs, const char* data, int len) {
|
||||
RichString_writeFrom(this, attrs, data, this->chlen, len);
|
||||
int RichString_appendnWide(RichString* this, int attrs, const char* data, int len) {
|
||||
return RichString_writeFromWide(this, attrs, data, this->chlen, len);
|
||||
}
|
||||
|
||||
void RichString_write(RichString* this, int attrs, const char* data) {
|
||||
RichString_writeFrom(this, attrs, data, 0, strlen(data));
|
||||
int RichString_writeWide(RichString* this, int attrs, const char* data) {
|
||||
return RichString_writeFromWide(this, attrs, data, 0, strlen(data));
|
||||
}
|
||||
|
||||
int RichString_appendAscii(RichString* this, int attrs, const char* data) {
|
||||
return RichString_writeFromAscii(this, attrs, data, this->chlen, strlen(data));
|
||||
}
|
||||
|
||||
int RichString_appendnAscii(RichString* this, int attrs, const char* data, int len) {
|
||||
return RichString_writeFromAscii(this, attrs, data, this->chlen, len);
|
||||
}
|
||||
|
||||
int RichString_writeAscii(RichString* this, int attrs, const char* data) {
|
||||
return RichString_writeFromAscii(this, attrs, data, 0, strlen(data));
|
||||
}
|
||||
|
14
RichString.h
14
RichString.h
@ -42,7 +42,7 @@ typedef struct RichString_ {
|
||||
int highlightAttr;
|
||||
} RichString;
|
||||
|
||||
void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
|
||||
void RichString_setAttrn(RichString* this, int attrs, int start, int charcount);
|
||||
|
||||
int RichString_findChar(RichString* this, char c, int start);
|
||||
|
||||
@ -52,10 +52,16 @@ void RichString_setAttr(RichString* this, int attrs);
|
||||
|
||||
void RichString_appendChr(RichString* this, char c, int count);
|
||||
|
||||
void RichString_append(RichString* this, int attrs, const char* data);
|
||||
int RichString_appendWide(RichString* this, int attrs, const char* data);
|
||||
|
||||
void RichString_appendn(RichString* this, int attrs, const char* data, int len);
|
||||
int RichString_appendnWide(RichString* this, int attrs, const char* data, int len);
|
||||
|
||||
void RichString_write(RichString* this, int attrs, const char* data);
|
||||
int RichString_writeWide(RichString* this, int attrs, const char* data);
|
||||
|
||||
int RichString_appendAscii(RichString* this, int attrs, const char* data);
|
||||
|
||||
int RichString_appendnAscii(RichString* this, int attrs, const char* data, int len);
|
||||
|
||||
int RichString_writeAscii(RichString* this, int attrs, const char* data);
|
||||
|
||||
#endif
|
||||
|
@ -106,7 +106,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
|
||||
if (*rescan) {
|
||||
*oldTime = newTime;
|
||||
ProcessList_scan(pl, this->state->pauseProcessUpdate);
|
||||
if (*sortTimeout == 0 || this->settings->treeView) {
|
||||
if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) {
|
||||
ProcessList_sort(pl);
|
||||
*sortTimeout = 1;
|
||||
}
|
||||
@ -119,29 +119,20 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
|
||||
*rescan = false;
|
||||
}
|
||||
|
||||
static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
|
||||
static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_redraw) {
|
||||
const int nPanels = this->panelCount;
|
||||
for (int i = 0; i < nPanels; i++) {
|
||||
Panel* panel = (Panel*) Vector_get(this->panels, i);
|
||||
Panel_draw(panel, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection));
|
||||
mvvline(panel->y, panel->x + panel->w, ' ', panel->h + 1);
|
||||
Panel_draw(panel, force_redraw, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection), State_hideFunctionBar(this->state));
|
||||
mvvline(panel->y, panel->x + panel->w, ' ', panel->h + (State_hideFunctionBar(this->state) ? 1 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
static Panel* setCurrentPanel(const ScreenManager* this, Panel* panel) {
|
||||
FunctionBar_draw(panel->currentBar);
|
||||
if (panel == this->state->panel && this->state->pauseProcessUpdate) {
|
||||
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
|
||||
}
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
|
||||
bool quit = false;
|
||||
int focus = 0;
|
||||
|
||||
Panel* panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
|
||||
Panel* panelFocus = (Panel*) Vector_get(this->panels, focus);
|
||||
|
||||
double oldTime = 0.0;
|
||||
|
||||
@ -150,6 +141,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
|
||||
|
||||
bool timedOut = true;
|
||||
bool redraw = true;
|
||||
bool force_redraw = true;
|
||||
bool rescan = false;
|
||||
int sortTimeout = 0;
|
||||
int resetSortTimeout = 5;
|
||||
@ -159,8 +151,9 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
|
||||
checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut);
|
||||
}
|
||||
|
||||
if (redraw) {
|
||||
ScreenManager_drawPanels(this, focus);
|
||||
if (redraw || force_redraw) {
|
||||
ScreenManager_drawPanels(this, focus, force_redraw);
|
||||
force_redraw = false;
|
||||
}
|
||||
|
||||
int prevCh = ch;
|
||||
@ -187,7 +180,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
|
||||
ch = KEY_MOUSE;
|
||||
if (panel == panelFocus || this->allowFocusChange) {
|
||||
focus = i;
|
||||
panelFocus = setCurrentPanel(this, panel);
|
||||
panelFocus = panel;
|
||||
Object* oldSelection = Panel_getSelected(panel);
|
||||
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
|
||||
if (Panel_getSelected(panel) == oldSelection) {
|
||||
@ -234,9 +227,12 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
|
||||
if (result & SYNTH_KEY) {
|
||||
ch = result >> 16;
|
||||
}
|
||||
if (result & REDRAW) {
|
||||
if (result & REFRESH) {
|
||||
sortTimeout = 0;
|
||||
}
|
||||
if (result & REDRAW) {
|
||||
force_redraw = true;
|
||||
}
|
||||
if (result & RESCAN) {
|
||||
rescan = true;
|
||||
sortTimeout = 0;
|
||||
@ -269,7 +265,7 @@ tryLeft:
|
||||
focus--;
|
||||
}
|
||||
|
||||
panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
|
||||
panelFocus = (Panel*) Vector_get(this->panels, focus);
|
||||
if (Panel_size(panelFocus) == 0 && focus > 0) {
|
||||
goto tryLeft;
|
||||
}
|
||||
@ -290,7 +286,7 @@ tryRight:
|
||||
focus++;
|
||||
}
|
||||
|
||||
panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
|
||||
panelFocus = (Panel*) Vector_get(this->panels, focus);
|
||||
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) {
|
||||
goto tryRight;
|
||||
}
|
||||
|
38
Settings.c
38
Settings.c
@ -96,10 +96,10 @@ static void readFields(ProcessField* fields, uint32_t* flags, const char* line)
|
||||
free(trim);
|
||||
int i, j;
|
||||
*flags = 0;
|
||||
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
|
||||
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 < Platform_numberOfFields && Process_fields[id].name) {
|
||||
if (id > 0 && id < LAST_PROCESSFIELD && Process_fields[id].name) {
|
||||
fields[j] = id;
|
||||
*flags |= Process_fields[id].flags;
|
||||
j++;
|
||||
@ -137,10 +137,17 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
|
||||
} else if (String_eq(option[0], "sort_key")) {
|
||||
// This "+1" is for compatibility with the older enum format.
|
||||
this->sortKey = atoi(option[1]) + 1;
|
||||
} else if (String_eq(option[0], "tree_sort_key")) {
|
||||
// This "+1" is for compatibility with the older enum format.
|
||||
this->treeSortKey = atoi(option[1]) + 1;
|
||||
} else if (String_eq(option[0], "sort_direction")) {
|
||||
this->direction = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "tree_sort_direction")) {
|
||||
this->treeDirection = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "tree_view")) {
|
||||
this->treeView = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "tree_view_always_by_pid")) {
|
||||
this->treeViewAlwaysByPID = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "hide_kernel_threads")) {
|
||||
this->hideKernelThreads = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "hide_userland_threads")) {
|
||||
@ -214,6 +221,8 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
|
||||
} else if (String_eq(option[0], "right_meter_modes")) {
|
||||
Settings_readMeterModes(this, option[1], 1);
|
||||
didReadMeters = true;
|
||||
} else if (String_eq(option[0], "hide_function_bar")) {
|
||||
this->hideFunctionBar = atoi(option[1]);
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
} else if (String_eq(option[0], "topology_affinity")) {
|
||||
this->topologyAffinity = !!atoi(option[1]);
|
||||
@ -273,6 +282,8 @@ bool Settings_write(Settings* this) {
|
||||
// This "-1" is for compatibility with the older enum format.
|
||||
fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1);
|
||||
fprintf(fd, "sort_direction=%d\n", (int) this->direction);
|
||||
fprintf(fd, "tree_sort_key=%d\n", (int) this->treeSortKey - 1);
|
||||
fprintf(fd, "tree_sort_direction=%d\n", (int) this->treeDirection);
|
||||
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);
|
||||
@ -287,6 +298,7 @@ bool Settings_write(Settings* this) {
|
||||
fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline);
|
||||
fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand);
|
||||
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
|
||||
fprintf(fd, "tree_view_always_by_pid=%d\n", (int) this->treeViewAlwaysByPID);
|
||||
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
|
||||
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
|
||||
fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne);
|
||||
@ -305,6 +317,7 @@ bool Settings_write(Settings* this) {
|
||||
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);
|
||||
fprintf(fd, "hide_function_bar=%d\n", (int) this->hideFunctionBar);
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity);
|
||||
#endif
|
||||
@ -316,7 +329,9 @@ Settings* Settings_new(int initialCpuCount) {
|
||||
Settings* this = xCalloc(1, sizeof(Settings));
|
||||
|
||||
this->sortKey = PERCENT_CPU;
|
||||
this->treeSortKey = PID;
|
||||
this->direction = 1;
|
||||
this->treeDirection = 1;
|
||||
this->shadowOtherUsers = false;
|
||||
this->showThreadNames = false;
|
||||
this->hideKernelThreads = false;
|
||||
@ -340,14 +355,15 @@ Settings* Settings_new(int initialCpuCount) {
|
||||
this->findCommInCmdline = true;
|
||||
this->stripExeFromCmdline = true;
|
||||
this->showMergedCommand = false;
|
||||
this->hideFunctionBar = 0;
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
this->topologyAffinity = false;
|
||||
#endif
|
||||
this->fields = xCalloc(Platform_numberOfFields + 1, sizeof(ProcessField));
|
||||
this->fields = xCalloc(LAST_PROCESSFIELD + 1, sizeof(ProcessField));
|
||||
// TODO: turn 'fields' into a Vector,
|
||||
// (and ProcessFields into proper objects).
|
||||
this->flags = 0;
|
||||
ProcessField* defaults = Platform_defaultFields;
|
||||
const ProcessField* defaults = Platform_defaultFields;
|
||||
for (int i = 0; defaults[i]; i++) {
|
||||
this->fields[i] = defaults[i];
|
||||
this->flags |= Process_fields[defaults[i]].flags;
|
||||
@ -427,9 +443,17 @@ Settings* Settings_new(int initialCpuCount) {
|
||||
}
|
||||
|
||||
void Settings_invertSortOrder(Settings* this) {
|
||||
if (this->direction == 1) {
|
||||
this->direction = -1;
|
||||
} else {
|
||||
int* attr = (this->treeView) ? &(this->treeDirection) : &(this->direction);
|
||||
*attr = (*attr == 1) ? -1 : 1;
|
||||
}
|
||||
|
||||
void Settings_setSortKey(Settings* this, ProcessField sortKey) {
|
||||
if (this->treeViewAlwaysByPID || !this->treeView) {
|
||||
this->sortKey = sortKey;
|
||||
this->direction = 1;
|
||||
this->treeView = false;
|
||||
} else {
|
||||
this->treeSortKey = sortKey;
|
||||
this->treeDirection = 1;
|
||||
}
|
||||
}
|
||||
|
17
Settings.h
17
Settings.h
@ -10,6 +10,7 @@ in the source distribution for its full text.
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Process.h"
|
||||
|
||||
@ -32,7 +33,9 @@ typedef struct Settings_ {
|
||||
int delay;
|
||||
|
||||
int direction;
|
||||
int treeDirection;
|
||||
ProcessField sortKey;
|
||||
ProcessField treeSortKey;
|
||||
|
||||
bool countCPUsFromOne;
|
||||
bool detailedCPUTime;
|
||||
@ -43,6 +46,7 @@ typedef struct Settings_ {
|
||||
bool degreeFahrenheit;
|
||||
#endif
|
||||
bool treeView;
|
||||
bool treeViewAlwaysByPID;
|
||||
bool showProgramPath;
|
||||
bool shadowOtherUsers;
|
||||
bool showThreadNames;
|
||||
@ -60,6 +64,7 @@ typedef struct Settings_ {
|
||||
bool accountGuestInCPUMeter;
|
||||
bool headerMargin;
|
||||
bool enableMouse;
|
||||
int hideFunctionBar; // 0 - off, 1 - on ESC until next input, 2 - permanently
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
bool topologyAffinity;
|
||||
#endif
|
||||
@ -69,6 +74,16 @@ typedef struct Settings_ {
|
||||
|
||||
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))
|
||||
|
||||
static inline ProcessField Settings_getActiveSortKey(const Settings* this) {
|
||||
return (this->treeView)
|
||||
? (this->treeViewAlwaysByPID ? PID : this->treeSortKey)
|
||||
: this->sortKey;
|
||||
}
|
||||
|
||||
static inline int Settings_getActiveDirection(const Settings* this) {
|
||||
return this->treeView ? this->treeDirection : this->direction;
|
||||
}
|
||||
|
||||
void Settings_delete(Settings* this);
|
||||
|
||||
bool Settings_write(Settings* this);
|
||||
@ -77,4 +92,6 @@ Settings* Settings_new(int initialCpuCount);
|
||||
|
||||
void Settings_invertSortOrder(Settings* this);
|
||||
|
||||
void Settings_setSortKey(Settings* this, ProcessField sortKey);
|
||||
|
||||
#endif
|
||||
|
@ -19,7 +19,7 @@ in the source distribution for its full text.
|
||||
|
||||
|
||||
Panel* SignalsPanel_new() {
|
||||
Panel* this = Panel_new(1, 1, 1, 1, true, Class(ListItem), FunctionBar_newEnterEsc("Send ", "Cancel "));
|
||||
Panel* this = Panel_new(1, 1, 1, 1, Class(ListItem), true, FunctionBar_newEnterEsc("Send ", "Cancel "));
|
||||
const int defaultSignal = SIGTERM;
|
||||
int defaultPosition = 15;
|
||||
unsigned int i;
|
||||
|
23
SwapMeter.c
23
SwapMeter.c
@ -17,28 +17,27 @@ static const int SwapMeter_attributes[] = {
|
||||
SWAP
|
||||
};
|
||||
|
||||
static void SwapMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
static void SwapMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
int written;
|
||||
Platform_setSwapValues(this);
|
||||
|
||||
written = Meter_humanUnit(buffer, this->values[0], size);
|
||||
buffer += written;
|
||||
if ((size -= written) > 0) {
|
||||
*buffer++ = '/';
|
||||
size--;
|
||||
METER_BUFFER_CHECK(buffer, size, written);
|
||||
|
||||
METER_BUFFER_APPEND_CHR(buffer, size, '/');
|
||||
|
||||
Meter_humanUnit(buffer, this->total, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void SwapMeter_display(const Object* cast, RichString* out) {
|
||||
char buffer[50];
|
||||
const Meter* this = (const Meter*)cast;
|
||||
RichString_write(out, CRT_colors[METER_TEXT], ":");
|
||||
Meter_humanUnit(buffer, this->total, 50);
|
||||
RichString_append(out, CRT_colors[METER_VALUE], buffer);
|
||||
Meter_humanUnit(buffer, this->values[0], 50);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " used:");
|
||||
RichString_append(out, CRT_colors[METER_VALUE], buffer);
|
||||
RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
|
||||
Meter_humanUnit(buffer, this->total, sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
|
||||
Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
|
||||
}
|
||||
|
||||
const MeterClass SwapMeter_class = {
|
||||
|
22
TasksMeter.c
22
TasksMeter.c
@ -23,7 +23,7 @@ static const int TasksMeter_attributes[] = {
|
||||
TASKS_RUNNING
|
||||
};
|
||||
|
||||
static void TasksMeter_updateValues(Meter* this, char* buffer, int len) {
|
||||
static void TasksMeter_updateValues(Meter* this, char* buffer, size_t len) {
|
||||
const ProcessList* pl = this->pl;
|
||||
this->values[0] = pl->kernelThreads;
|
||||
this->values[1] = pl->userlandThreads;
|
||||
@ -46,7 +46,7 @@ static void TasksMeter_display(const Object* cast, RichString* out) {
|
||||
int processes = (int) this->values[2];
|
||||
|
||||
xSnprintf(buffer, sizeof(buffer), "%d", processes);
|
||||
RichString_write(out, CRT_colors[METER_VALUE], buffer);
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE], buffer);
|
||||
int threadValueColor = CRT_colors[METER_VALUE];
|
||||
int threadCaptionColor = CRT_colors[METER_TEXT];
|
||||
if (settings->highlightThreads) {
|
||||
@ -54,21 +54,21 @@ static void TasksMeter_display(const Object* cast, RichString* out) {
|
||||
threadCaptionColor = CRT_colors[PROCESS_THREAD];
|
||||
}
|
||||
if (!settings->hideUserlandThreads) {
|
||||
RichString_append(out, CRT_colors[METER_TEXT], ", ");
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], ", ");
|
||||
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]);
|
||||
RichString_append(out, threadValueColor, buffer);
|
||||
RichString_append(out, threadCaptionColor, " thr");
|
||||
RichString_appendAscii(out, threadValueColor, buffer);
|
||||
RichString_appendAscii(out, threadCaptionColor, " thr");
|
||||
}
|
||||
if (!settings->hideKernelThreads) {
|
||||
RichString_append(out, CRT_colors[METER_TEXT], ", ");
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], ", ");
|
||||
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[0]);
|
||||
RichString_append(out, threadValueColor, buffer);
|
||||
RichString_append(out, threadCaptionColor, " kthr");
|
||||
RichString_appendAscii(out, threadValueColor, buffer);
|
||||
RichString_appendAscii(out, threadCaptionColor, " kthr");
|
||||
}
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "; ");
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "; ");
|
||||
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[3]);
|
||||
RichString_append(out, CRT_colors[TASKS_RUNNING], buffer);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " running");
|
||||
RichString_appendAscii(out, CRT_colors[TASKS_RUNNING], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " running");
|
||||
}
|
||||
|
||||
const MeterClass TasksMeter_class = {
|
||||
|
@ -33,17 +33,7 @@ static const char* const TraceScreenFunctions[] = {"Search ", "Filter ", "AutoSc
|
||||
|
||||
static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"};
|
||||
|
||||
static int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27};
|
||||
|
||||
const InfoScreenClass TraceScreen_class = {
|
||||
.super = {
|
||||
.extends = Class(Object),
|
||||
.delete = TraceScreen_delete
|
||||
},
|
||||
.draw = TraceScreen_draw,
|
||||
.onErr = TraceScreen_updateTrace,
|
||||
.onKey = TraceScreen_onKey,
|
||||
};
|
||||
static const int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27};
|
||||
|
||||
TraceScreen* TraceScreen_new(Process* process) {
|
||||
// This initializes all TraceScreen variables to "false" so only default = true ones need to be set below
|
||||
@ -70,12 +60,8 @@ void TraceScreen_delete(Object* cast) {
|
||||
free(InfoScreen_done((InfoScreen*)this));
|
||||
}
|
||||
|
||||
void TraceScreen_draw(InfoScreen* this) {
|
||||
attrset(CRT_colors[PANEL_HEADER_FOCUS]);
|
||||
mvhline(0, 0, ' ', COLS);
|
||||
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process));
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
IncSet_drawBar(this->inc);
|
||||
static void TraceScreen_draw(InfoScreen* this) {
|
||||
InfoScreen_drawTitled(this, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process));
|
||||
}
|
||||
|
||||
bool TraceScreen_forkTracer(TraceScreen* this) {
|
||||
@ -131,7 +117,7 @@ err:
|
||||
return false;
|
||||
}
|
||||
|
||||
void TraceScreen_updateTrace(InfoScreen* super) {
|
||||
static void TraceScreen_updateTrace(InfoScreen* super) {
|
||||
TraceScreen* this = (TraceScreen*) super;
|
||||
char buffer[1025];
|
||||
|
||||
@ -176,7 +162,7 @@ void TraceScreen_updateTrace(InfoScreen* super) {
|
||||
}
|
||||
}
|
||||
|
||||
bool TraceScreen_onKey(InfoScreen* super, int ch) {
|
||||
static bool TraceScreen_onKey(InfoScreen* super, int ch) {
|
||||
TraceScreen* this = (TraceScreen*) super;
|
||||
switch(ch) {
|
||||
case 'f':
|
||||
@ -195,3 +181,13 @@ bool TraceScreen_onKey(InfoScreen* super, int ch) {
|
||||
this->follow = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const InfoScreenClass TraceScreen_class = {
|
||||
.super = {
|
||||
.extends = Class(Object),
|
||||
.delete = TraceScreen_delete
|
||||
},
|
||||
.draw = TraceScreen_draw,
|
||||
.onErr = TraceScreen_updateTrace,
|
||||
.onKey = TraceScreen_onKey,
|
||||
};
|
||||
|
@ -32,12 +32,6 @@ TraceScreen* TraceScreen_new(Process* process);
|
||||
|
||||
void TraceScreen_delete(Object* cast);
|
||||
|
||||
void TraceScreen_draw(InfoScreen* this);
|
||||
|
||||
bool TraceScreen_forkTracer(TraceScreen* this);
|
||||
|
||||
void TraceScreen_updateTrace(InfoScreen* super);
|
||||
|
||||
bool TraceScreen_onKey(InfoScreen* super, int ch);
|
||||
|
||||
#endif
|
||||
|
@ -17,7 +17,7 @@ static const int UptimeMeter_attributes[] = {
|
||||
UPTIME
|
||||
};
|
||||
|
||||
static void UptimeMeter_updateValues(Meter* this, char* buffer, int len) {
|
||||
static void UptimeMeter_updateValues(Meter* this, char* buffer, size_t len) {
|
||||
int totalseconds = Platform_getUptime();
|
||||
if (totalseconds == -1) {
|
||||
xSnprintf(buffer, len, "(unknown)");
|
||||
|
2
Vector.h
2
Vector.h
@ -58,7 +58,7 @@ unsigned int Vector_count(const Vector* this);
|
||||
|
||||
#else /* NDEBUG */
|
||||
|
||||
static inline Object* Vector_get(Vector* this, int idx) {
|
||||
static inline Object* Vector_get(const Vector* this, int idx) {
|
||||
return this->array[idx];
|
||||
}
|
||||
|
||||
|
27
XUtils.c
27
XUtils.c
@ -11,7 +11,9 @@ in the source distribution for its full text.
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@ -35,9 +37,21 @@ void* xMalloc(size_t size) {
|
||||
return data;
|
||||
}
|
||||
|
||||
void* xMallocArray(size_t nmemb, size_t size) {
|
||||
assert(nmemb > 0);
|
||||
assert(size > 0);
|
||||
if (SIZE_MAX / nmemb < size) {
|
||||
fail();
|
||||
}
|
||||
return xMalloc(nmemb * size);
|
||||
}
|
||||
|
||||
void* xCalloc(size_t nmemb, size_t size) {
|
||||
assert(nmemb > 0);
|
||||
assert(size > 0);
|
||||
if (SIZE_MAX / nmemb < size) {
|
||||
fail();
|
||||
}
|
||||
void* data = calloc(nmemb, size);
|
||||
if (!data) {
|
||||
fail();
|
||||
@ -55,6 +69,15 @@ void* xRealloc(void* ptr, size_t size) {
|
||||
return data;
|
||||
}
|
||||
|
||||
void* xReallocArray(void* ptr, size_t nmemb, size_t size) {
|
||||
assert(nmemb > 0);
|
||||
assert(size > 0);
|
||||
if (SIZE_MAX / nmemb < size) {
|
||||
fail();
|
||||
}
|
||||
return xRealloc(ptr, nmemb * size);
|
||||
}
|
||||
|
||||
char* String_cat(const char* s1, const char* s2) {
|
||||
const size_t l1 = strlen(s1);
|
||||
const size_t l2 = strlen(s2);
|
||||
@ -183,13 +206,13 @@ int xAsprintf(char** strp, const char* fmt, ...) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int xSnprintf(char* buf, int len, const char* fmt, ...) {
|
||||
int xSnprintf(char* buf, size_t len, const char* fmt, ...) {
|
||||
va_list vl;
|
||||
va_start(vl, fmt);
|
||||
int n = vsnprintf(buf, len, fmt, vl);
|
||||
va_end(vl);
|
||||
|
||||
if (n < 0 || n >= len) {
|
||||
if (n < 0 || (size_t)n >= len) {
|
||||
fail();
|
||||
}
|
||||
|
||||
|
7
XUtils.h
7
XUtils.h
@ -13,6 +13,7 @@ in the source distribution for its full text.
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // IWYU pragma: keep
|
||||
#include <string.h> // IWYU pragma: keep
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "Compat.h"
|
||||
#include "Macros.h"
|
||||
@ -22,10 +23,14 @@ void fail(void) ATTR_NORETURN;
|
||||
|
||||
void* xMalloc(size_t size);
|
||||
|
||||
void* xMallocArray(size_t nmemb, size_t size);
|
||||
|
||||
void* xCalloc(size_t nmemb, size_t size);
|
||||
|
||||
void* xRealloc(void* ptr, size_t size);
|
||||
|
||||
void* xReallocArray(void* ptr, size_t nmemb, size_t size);
|
||||
|
||||
/*
|
||||
* String_startsWith gives better performance if strlen(match) can be computed
|
||||
* at compile time (e.g. when they are immutable string literals). :)
|
||||
@ -58,7 +63,7 @@ ATTR_FORMAT(printf, 2, 3)
|
||||
int xAsprintf(char** strp, const char* fmt, ...);
|
||||
|
||||
ATTR_FORMAT(printf, 3, 4)
|
||||
int xSnprintf(char* buf, int len, const char* fmt, ...);
|
||||
int xSnprintf(char* buf, size_t len, const char* fmt, ...);
|
||||
|
||||
char* xStrdup(const char* str) ATTR_NONNULL;
|
||||
|
||||
|
38
configure.ac
38
configure.ac
@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.65)
|
||||
AC_INIT([htop],[3.0.3-rc1],[htop@groups.io])
|
||||
AC_INIT([htop],[3.0.5],[htop@groups.io])
|
||||
|
||||
AC_CONFIG_SRCDIR([htop.c])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
@ -93,13 +93,16 @@ AC_TYPE_UINT64_T
|
||||
AC_FUNC_CLOSEDIR_VOID
|
||||
AC_FUNC_STAT
|
||||
AC_CHECK_FUNCS([\
|
||||
clock_gettime\
|
||||
faccessat\
|
||||
fstatat\
|
||||
host_get_clock_service\
|
||||
openat\
|
||||
readlinkat\
|
||||
])
|
||||
|
||||
AC_SEARCH_LIBS([dlopen], [dl dld])
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt])
|
||||
|
||||
save_cflags="${CFLAGS}"
|
||||
CFLAGS="${CFLAGS} -std=c99"
|
||||
@ -217,6 +220,10 @@ if test "x$enable_unicode" = xyes; then
|
||||
[AC_CHECK_HEADERS([ncurses/ncurses.h],[:],
|
||||
[AC_CHECK_HEADERS([ncurses/curses.h],[:],
|
||||
[AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])])
|
||||
|
||||
# check if additional linker flags are needed for keypad(3)
|
||||
# (at this point we already link against a working ncurses library with wide character support)
|
||||
AC_SEARCH_LIBS([keypad], [tinfow tinfo])
|
||||
else
|
||||
HTOP_CHECK_SCRIPT([ncurses6], [refresh], [HAVE_LIBNCURSES], "ncurses6-config",
|
||||
HTOP_CHECK_SCRIPT([ncurses], [refresh], [HAVE_LIBNCURSES], "ncurses5-config",
|
||||
@ -229,6 +236,19 @@ else
|
||||
[AC_CHECK_HEADERS([ncurses/curses.h],[:],
|
||||
[AC_CHECK_HEADERS([ncurses/ncurses.h],[:],
|
||||
[AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])])
|
||||
|
||||
# check if additional linker flags are needed for keypad(3)
|
||||
# (at this point we already link against a working ncurses library)
|
||||
AC_SEARCH_LIBS([keypad], [tinfo])
|
||||
fi
|
||||
|
||||
if test "$my_htop_platform" = "darwin"; then
|
||||
AC_CHECK_HEADERS([mach/mach_time.h])
|
||||
AC_CHECK_FUNCS([mach_timebase_info])
|
||||
fi
|
||||
|
||||
if test "$my_htop_platform" = "dragonflybsd"; then
|
||||
AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
|
||||
fi
|
||||
|
||||
if test "$my_htop_platform" = "freebsd"; then
|
||||
@ -246,8 +266,7 @@ if test "$my_htop_platform" = "solaris"; then
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity, disables Linux affinity])],, enable_hwloc="no")
|
||||
if test "x$enable_hwloc" = xyes
|
||||
then
|
||||
if test "x$enable_hwloc" = xyes; then
|
||||
AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [missing_libraries="$missing_libraries libhwloc"])
|
||||
AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"])
|
||||
fi
|
||||
@ -279,20 +298,17 @@ if test "x$enable_linux_affinity" = xyes; then
|
||||
AC_DEFINE(HAVE_LINUX_AFFINITY, 1, [Define if Linux sched_setaffinity and sched_getaffinity are to be used.])
|
||||
fi
|
||||
|
||||
if test "x$enable_linux_affinity" = xyes -a "x$enable_hwloc" = xyes
|
||||
then
|
||||
if test "x$enable_linux_affinity" = xyes -a "x$enable_hwloc" = xyes; then
|
||||
AC_MSG_ERROR([--enable-hwloc and --enable-linux-affinity are mutual exclusive. Specify at most one of them.])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(setuid, [AS_HELP_STRING([--enable-setuid], [enable setuid support for platforms that need it])],, enable_setuid="no")
|
||||
if test "x$enable_setuid" = xyes
|
||||
then
|
||||
if test "x$enable_setuid" = xyes; then
|
||||
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no")
|
||||
if test "x$enable_delayacct" = xyes
|
||||
then
|
||||
if test "x$enable_delayacct" = xyes; then
|
||||
m4_ifdef([PKG_PROG_PKG_CONFIG], [
|
||||
PKG_PROG_PKG_CONFIG()
|
||||
PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"])
|
||||
@ -321,6 +337,7 @@ AM_CFLAGS="\
|
||||
-Wcast-qual\
|
||||
-Wextra\
|
||||
-Wfloat-equal\
|
||||
-Wformat=2\
|
||||
-Wmissing-format-attribute\
|
||||
-Wmissing-noreturn\
|
||||
-Wmissing-prototypes\
|
||||
@ -367,8 +384,7 @@ AC_SUBST(my_htop_platform)
|
||||
AC_CONFIG_FILES([Makefile htop.1])
|
||||
AC_OUTPUT
|
||||
|
||||
if test "$my_htop_platform" = "unsupported"
|
||||
then
|
||||
if test "$my_htop_platform" = "unsupported"; then
|
||||
echo ""
|
||||
echo "****************************************************************"
|
||||
echo "WARNING! This platform is not currently supported by htop."
|
||||
|
@ -14,17 +14,37 @@ in the source distribution for its full text.
|
||||
#include <mach/mach.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Platform.h"
|
||||
#include "Process.h"
|
||||
|
||||
|
||||
const ProcessClass DarwinProcess_class = {
|
||||
.super = {
|
||||
.extends = Class(Process),
|
||||
.display = Process_display,
|
||||
.delete = Process_delete,
|
||||
.compare = Process_compare
|
||||
},
|
||||
.writeField = Process_writeField,
|
||||
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
|
||||
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
|
||||
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
|
||||
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
|
||||
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
|
||||
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
|
||||
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
|
||||
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
|
||||
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
|
||||
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
|
||||
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
|
||||
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
|
||||
|
||||
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
|
||||
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
|
||||
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
|
||||
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
|
||||
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
|
||||
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
|
||||
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
|
||||
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
|
||||
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
|
||||
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
|
||||
[TRANSLATED] = { .name = "TRANSLATED", .title = "T ", .description = "Translation info (T translated, N native)", .flags = 0, },
|
||||
};
|
||||
|
||||
Process* DarwinProcess_new(const Settings* settings) {
|
||||
@ -35,6 +55,7 @@ Process* DarwinProcess_new(const Settings* settings) {
|
||||
this->utime = 0;
|
||||
this->stime = 0;
|
||||
this->taskAccess = true;
|
||||
this->translated = false;
|
||||
|
||||
return &this->super;
|
||||
}
|
||||
@ -46,6 +67,34 @@ void Process_delete(Object* cast) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
static void DarwinProcess_writeField(const Process* this, RichString* str, ProcessField field) {
|
||||
const DarwinProcess* dp = (const DarwinProcess*) this;
|
||||
char buffer[256]; buffer[255] = '\0';
|
||||
int attr = CRT_colors[DEFAULT_COLOR];
|
||||
int n = sizeof(buffer) - 1;
|
||||
switch (field) {
|
||||
// add Platform-specific fields here
|
||||
case TRANSLATED: xSnprintf(buffer, n, "%c ", dp->translated ? 'T' : 'N'); break;
|
||||
default:
|
||||
Process_writeField(this, str, field);
|
||||
return;
|
||||
}
|
||||
RichString_appendWide(str, attr, buffer);
|
||||
}
|
||||
|
||||
static int DarwinProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
|
||||
const DarwinProcess* p1 = (const DarwinProcess*)v1;
|
||||
const DarwinProcess* p2 = (const DarwinProcess*)v2;
|
||||
|
||||
switch (key) {
|
||||
// add Platform-specific fields here
|
||||
case TRANSLATED:
|
||||
return SPACESHIP_NUMBER(p1->translated, p2->translated);
|
||||
default:
|
||||
return Process_compareByKey_Base(v1, v2, key);
|
||||
}
|
||||
}
|
||||
|
||||
bool Process_isThread(const Process* this) {
|
||||
(void) this;
|
||||
return false;
|
||||
@ -195,6 +244,8 @@ ERROR_A:
|
||||
}
|
||||
|
||||
void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) {
|
||||
DarwinProcess* dp = (DarwinProcess*)proc;
|
||||
|
||||
const struct extern_proc* ep = &ps->kp_proc;
|
||||
|
||||
/* UNSET HERE :
|
||||
@ -223,6 +274,7 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
|
||||
/* e_tdev = (major << 24) | (minor & 0xffffff) */
|
||||
/* e_tdev == -1 for "no device" */
|
||||
proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */
|
||||
dp->translated = ps->kp_proc.p_flag & P_TRANSLATED;
|
||||
|
||||
proc->starttime_ctime = ep->p_starttime.tv_sec;
|
||||
Process_fillStarttimeBuffer(proc);
|
||||
@ -240,26 +292,24 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
|
||||
proc->updated = true;
|
||||
}
|
||||
|
||||
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl) {
|
||||
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval) {
|
||||
struct proc_taskinfo pti;
|
||||
|
||||
if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) {
|
||||
if (0 != proc->utime || 0 != proc->stime) {
|
||||
uint64_t diff = (pti.pti_total_system - proc->stime)
|
||||
+ (pti.pti_total_user - proc->utime);
|
||||
uint64_t total_existing_time = proc->stime + proc->utime;
|
||||
uint64_t total_current_time = pti.pti_total_system + pti.pti_total_user;
|
||||
|
||||
proc->super.percent_cpu = (double)diff * (double)dpl->super.cpuCount
|
||||
/ ((double)dpl->global_diff * 100000.0);
|
||||
|
||||
// fprintf(stderr, "%f %llu %llu %llu %llu %llu\n", proc->super.percent_cpu,
|
||||
// proc->stime, proc->utime, pti.pti_total_system, pti.pti_total_user, dpl->global_diff);
|
||||
// exit(7);
|
||||
if (total_existing_time && 1E-6 < time_interval) {
|
||||
uint64_t total_time_diff = total_current_time - total_existing_time;
|
||||
proc->super.percent_cpu = ((double)total_time_diff / time_interval) * 100.0;
|
||||
} else {
|
||||
proc->super.percent_cpu = 0.0;
|
||||
}
|
||||
|
||||
proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000;
|
||||
proc->super.time = total_current_time / 10000000;
|
||||
proc->super.nlwp = pti.pti_threadnum;
|
||||
proc->super.m_virt = pti.pti_virtual_size / CRT_pageSize;
|
||||
proc->super.m_resident = pti.pti_resident_size / CRT_pageSize;
|
||||
proc->super.m_virt = pti.pti_virtual_size / ONE_K;
|
||||
proc->super.m_resident = pti.pti_resident_size / ONE_K;
|
||||
proc->super.majflt = pti.pti_faults;
|
||||
proc->super.percent_mem = (double)pti.pti_resident_size * 100.0
|
||||
/ (double)dpl->host_info.max_mem;
|
||||
@ -341,3 +391,15 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) {
|
||||
}
|
||||
proc->state = state;
|
||||
}
|
||||
|
||||
|
||||
const ProcessClass DarwinProcess_class = {
|
||||
.super = {
|
||||
.extends = Class(Process),
|
||||
.display = Process_display,
|
||||
.delete = Process_delete,
|
||||
.compare = Process_compare
|
||||
},
|
||||
.writeField = DarwinProcess_writeField,
|
||||
.compareByKey = DarwinProcess_compareByKey,
|
||||
};
|
||||
|
@ -19,10 +19,13 @@ typedef struct DarwinProcess_ {
|
||||
uint64_t utime;
|
||||
uint64_t stime;
|
||||
bool taskAccess;
|
||||
bool translated;
|
||||
} DarwinProcess;
|
||||
|
||||
extern const ProcessClass DarwinProcess_class;
|
||||
|
||||
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
|
||||
|
||||
Process* DarwinProcess_new(const Settings* settings);
|
||||
|
||||
void Process_delete(Object* cast);
|
||||
@ -31,7 +34,7 @@ bool Process_isThread(const Process* this);
|
||||
|
||||
void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists);
|
||||
|
||||
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl);
|
||||
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval);
|
||||
|
||||
/*
|
||||
* Scan threads for process state information.
|
||||
|
@ -7,7 +7,6 @@ in the source distribution for its full text.
|
||||
|
||||
#include "DarwinProcessList.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <libproc.h>
|
||||
#include <stdbool.h>
|
||||
@ -21,6 +20,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "CRT.h"
|
||||
#include "DarwinProcess.h"
|
||||
#include "Platform.h"
|
||||
#include "ProcessList.h"
|
||||
#include "zfs/openzfs_sysctl.h"
|
||||
#include "zfs/ZfsArcStats.h"
|
||||
@ -71,14 +71,14 @@ static void ProcessList_getHostInfo(host_basic_info_data_t* p) {
|
||||
mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT;
|
||||
|
||||
if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
|
||||
CRT_fatalError("Unable to retrieve host info\n");
|
||||
CRT_fatalError("Unable to retrieve host info");
|
||||
}
|
||||
}
|
||||
|
||||
static void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t* p) {
|
||||
if (NULL != p && NULL != *p) {
|
||||
if (0 != munmap(*p, vm_page_size)) {
|
||||
CRT_fatalError("Unable to free old CPU load information\n");
|
||||
CRT_fatalError("Unable to free old CPU load information");
|
||||
}
|
||||
*p = NULL;
|
||||
}
|
||||
@ -90,7 +90,7 @@ static unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t* p) {
|
||||
|
||||
// TODO Improving the accuracy of the load counts woule help a lot.
|
||||
if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) {
|
||||
CRT_fatalError("Unable to retrieve CPU info\n");
|
||||
CRT_fatalError("Unable to retrieve CPU info");
|
||||
}
|
||||
|
||||
return cpu_count;
|
||||
@ -100,7 +100,7 @@ static void ProcessList_getVMStats(vm_statistics_t p) {
|
||||
mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
|
||||
|
||||
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) {
|
||||
CRT_fatalError("Unable to retrieve VM statistics\n");
|
||||
CRT_fatalError("Unable to retrieve VM statistics");
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) {
|
||||
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
|
||||
DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList));
|
||||
|
||||
ProcessList_init(&this->super, Class(Process), usersTable, pidMatchList, userId);
|
||||
ProcessList_init(&this->super, Class(DarwinProcess), usersTable, pidMatchList, userId);
|
||||
|
||||
/* Initialize the CPU information */
|
||||
this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load);
|
||||
@ -158,6 +158,11 @@ void ProcessList_delete(ProcessList* this) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
static double ticksToNanoseconds(const double ticks) {
|
||||
const double nanos_per_sec = 1e9;
|
||||
return (ticks / Platform_timebaseToNS) * (nanos_per_sec / (double) Platform_clockTicksPerSec);
|
||||
}
|
||||
|
||||
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
|
||||
DarwinProcessList* dpl = (DarwinProcessList*)super;
|
||||
bool preExisting = true;
|
||||
@ -185,6 +190,8 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
|
||||
}
|
||||
}
|
||||
|
||||
const double time_interval = ticksToNanoseconds(dpl->global_diff) / (double) dpl->super.cpuCount;
|
||||
|
||||
/* Clear the thread counts */
|
||||
super->kernelThreads = 0;
|
||||
super->userlandThreads = 0;
|
||||
@ -204,7 +211,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
|
||||
proc = (DarwinProcess*)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, DarwinProcess_new);
|
||||
|
||||
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], preExisting);
|
||||
DarwinProcess_setFromLibprocPidinfo(proc, dpl);
|
||||
DarwinProcess_setFromLibprocPidinfo(proc, dpl, time_interval);
|
||||
|
||||
// Disabled for High Sierra due to bug in macOS High Sierra
|
||||
bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0);
|
||||
|
@ -6,32 +6,42 @@ Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Platform.h"
|
||||
#include "Macros.h"
|
||||
#include "CPUMeter.h"
|
||||
#include "MemoryMeter.h"
|
||||
#include "SwapMeter.h"
|
||||
#include "TasksMeter.h"
|
||||
#include "LoadAverageMeter.h"
|
||||
#include "ClockMeter.h"
|
||||
#include "DateMeter.h"
|
||||
#include "DateTimeMeter.h"
|
||||
#include "HostnameMeter.h"
|
||||
#include "ProcessLocksScreen.h"
|
||||
#include "UptimeMeter.h"
|
||||
#include "zfs/ZfsArcMeter.h"
|
||||
#include "zfs/ZfsCompressedArcMeter.h"
|
||||
#include "DarwinProcessList.h"
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <unistd.h>
|
||||
#include <CoreFoundation/CFString.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/ps/IOPowerSources.h>
|
||||
#include <IOKit/ps/IOPSKeys.h>
|
||||
|
||||
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
|
||||
#include "ClockMeter.h"
|
||||
#include "CPUMeter.h"
|
||||
#include "CRT.h"
|
||||
#include "DarwinProcessList.h"
|
||||
#include "DateMeter.h"
|
||||
#include "DateTimeMeter.h"
|
||||
#include "HostnameMeter.h"
|
||||
#include "LoadAverageMeter.h"
|
||||
#include "Macros.h"
|
||||
#include "MemoryMeter.h"
|
||||
#include "ProcessLocksScreen.h"
|
||||
#include "SwapMeter.h"
|
||||
#include "TasksMeter.h"
|
||||
#include "UptimeMeter.h"
|
||||
#include "zfs/ZfsArcMeter.h"
|
||||
#include "zfs/ZfsCompressedArcMeter.h"
|
||||
|
||||
#ifdef HAVE_MACH_MACH_TIME_H
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
|
||||
|
||||
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
|
||||
|
||||
const SignalItem Platform_signals[] = {
|
||||
{ .name = " 0 Cancel", .number = 0 },
|
||||
@ -71,35 +81,6 @@ const SignalItem Platform_signals[] = {
|
||||
|
||||
const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
|
||||
|
||||
ProcessFieldData Process_fields[] = {
|
||||
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
|
||||
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
|
||||
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
|
||||
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
|
||||
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
|
||||
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
|
||||
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
|
||||
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
|
||||
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
|
||||
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
|
||||
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
|
||||
|
||||
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
|
||||
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
|
||||
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
|
||||
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
|
||||
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
|
||||
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
|
||||
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
|
||||
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
|
||||
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
|
||||
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
|
||||
[100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
|
||||
};
|
||||
|
||||
const MeterClass* const Platform_meterTypes[] = {
|
||||
&CPUMeter_class,
|
||||
&ClockMeter_class,
|
||||
@ -131,10 +112,28 @@ const MeterClass* const Platform_meterTypes[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
int Platform_numberOfFields = 100;
|
||||
double Platform_timebaseToNS = 1.0;
|
||||
|
||||
long Platform_clockTicksPerSec = -1;
|
||||
|
||||
void Platform_init(void) {
|
||||
/* no platform-specific setup needed */
|
||||
// Check if we can determine the timebase used on this system.
|
||||
// If the API is unavailable assume we get our timebase in nanoseconds.
|
||||
#ifdef HAVE_MACH_TIMEBASE_INFO
|
||||
mach_timebase_info_data_t info;
|
||||
mach_timebase_info(&info);
|
||||
Platform_timebaseToNS = (double)info.numer / (double)info.denom;
|
||||
#else
|
||||
Platform_timebaseToNS = 1.0;
|
||||
#endif
|
||||
|
||||
// Determine the number of clock ticks per second
|
||||
errno = 0;
|
||||
Platform_clockTicksPerSec = sysconf(_SC_CLK_TCK);
|
||||
|
||||
if (errno || Platform_clockTicksPerSec < 1) {
|
||||
CRT_fatalError("Unable to retrieve clock tick rate");
|
||||
}
|
||||
}
|
||||
|
||||
void Platform_done(void) {
|
||||
@ -179,16 +178,6 @@ int Platform_getMaxPid() {
|
||||
return 99999;
|
||||
}
|
||||
|
||||
ProcessPidColumn Process_pidColumns[] = {
|
||||
{ .id = PID, .label = "PID" },
|
||||
{ .id = PPID, .label = "PPID" },
|
||||
{ .id = TPGID, .label = "TPGID" },
|
||||
{ .id = TGID, .label = "TGID" },
|
||||
{ .id = PGRP, .label = "PGRP" },
|
||||
{ .id = SESSION, .label = "SID" },
|
||||
{ .id = 0, .label = NULL },
|
||||
};
|
||||
|
||||
static double Platform_setCPUAverageValues(Meter* mtr) {
|
||||
const ProcessList* dpl = mtr->pl;
|
||||
int cpus = dpl->cpuCount;
|
||||
|
@ -19,11 +19,12 @@ in the source distribution for its full text.
|
||||
#include "ProcessLocksScreen.h"
|
||||
#include "SignalsPanel.h"
|
||||
|
||||
extern ProcessFieldData Process_fields[];
|
||||
|
||||
extern ProcessField Platform_defaultFields[];
|
||||
extern const ProcessField Platform_defaultFields[];
|
||||
|
||||
extern int Platform_numberOfFields;
|
||||
extern double Platform_timebaseToNS;
|
||||
|
||||
extern long Platform_clockTicksPerSec;
|
||||
|
||||
extern const SignalItem Platform_signals[];
|
||||
|
||||
@ -43,8 +44,6 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen);
|
||||
|
||||
int Platform_getMaxPid(void);
|
||||
|
||||
extern ProcessPidColumn Process_pidColumns[];
|
||||
|
||||
double Platform_setCPUValues(Meter* mtr, int cpu);
|
||||
|
||||
void Platform_setMemoryValues(Meter* mtr);
|
||||
|
16
darwin/ProcessField.h
Normal file
16
darwin/ProcessField.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef HEADER_DarwinProcessField
|
||||
#define HEADER_DarwinProcessField
|
||||
/*
|
||||
htop - darwin/ProcessField.h
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
|
||||
#define PLATFORM_PROCESS_FIELDS \
|
||||
TRANSLATED = 100, \
|
||||
// End of list
|
||||
|
||||
|
||||
#endif /* HEADER_DarwinProcessField */
|
@ -18,26 +18,16 @@ in the source distribution for its full text.
|
||||
#include <sys/syscall.h>
|
||||
|
||||
|
||||
const ProcessClass DragonFlyBSDProcess_class = {
|
||||
.super = {
|
||||
.extends = Class(Process),
|
||||
.display = Process_display,
|
||||
.delete = Process_delete,
|
||||
.compare = DragonFlyBSDProcess_compare
|
||||
},
|
||||
.writeField = DragonFlyBSDProcess_writeField,
|
||||
};
|
||||
|
||||
ProcessFieldData Process_fields[] = {
|
||||
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
|
||||
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
|
||||
[PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
|
||||
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
|
||||
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping (<20s), I Idle, Q Queued for Run, R running, D disk, Z zombie, T traced, W paging, B Blocked, A AskedPage, C Core, J Jailed)", .flags = 0, },
|
||||
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
|
||||
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
|
||||
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
|
||||
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
|
||||
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
|
||||
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
|
||||
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
|
||||
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
|
||||
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
|
||||
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
|
||||
@ -53,21 +43,9 @@ ProcessFieldData Process_fields[] = {
|
||||
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
|
||||
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
|
||||
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
|
||||
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
|
||||
[JID] = { .name = "JID", .title = " JID ", .description = "Jail prison ID", .flags = 0, },
|
||||
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
|
||||
[JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, },
|
||||
[JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, },
|
||||
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
|
||||
};
|
||||
|
||||
ProcessPidColumn Process_pidColumns[] = {
|
||||
{ .id = JID, .label = "JID" },
|
||||
{ .id = PID, .label = "PID" },
|
||||
{ .id = PPID, .label = "PPID" },
|
||||
{ .id = TPGID, .label = "TPGID" },
|
||||
{ .id = TGID, .label = "TGID" },
|
||||
{ .id = PGRP, .label = "PGRP" },
|
||||
{ .id = SESSION, .label = "SID" },
|
||||
{ .id = 0, .label = NULL },
|
||||
};
|
||||
|
||||
Process* DragonFlyBSDProcess_new(const Settings* settings) {
|
||||
@ -84,50 +62,35 @@ void Process_delete(Object* cast) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) {
|
||||
static void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) {
|
||||
const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) this;
|
||||
char buffer[256]; buffer[255] = '\0';
|
||||
int attr = CRT_colors[DEFAULT_COLOR];
|
||||
int n = sizeof(buffer) - 1;
|
||||
switch ((int) field) {
|
||||
size_t n = sizeof(buffer) - 1;
|
||||
switch (field) {
|
||||
// add Platform-specific fields here
|
||||
case PID: xSnprintf(buffer, n, Process_pidFormat, (fp->kernel ? -1 : this->pid)); break;
|
||||
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
|
||||
case JAIL: {
|
||||
xSnprintf(buffer, n, "%-11s ", fp->jname);
|
||||
if (buffer[11] != '\0') {
|
||||
buffer[11] = ' ';
|
||||
buffer[12] = '\0';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, (fp->kernel ? -1 : this->pid)); break;
|
||||
case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break;
|
||||
case JAIL: Process_printLeftAlignedField(str, attr, fp->jname, 11); return;
|
||||
default:
|
||||
Process_writeField(this, str, field);
|
||||
return;
|
||||
}
|
||||
RichString_append(str, attr, buffer);
|
||||
RichString_appendWide(str, attr, buffer);
|
||||
}
|
||||
|
||||
long DragonFlyBSDProcess_compare(const void* v1, const void* v2) {
|
||||
const DragonFlyBSDProcess *p1, *p2;
|
||||
const Settings *settings = ((const Process*)v1)->settings;
|
||||
static int DragonFlyBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
|
||||
const DragonFlyBSDProcess* p1 = (const DragonFlyBSDProcess*)v1;
|
||||
const DragonFlyBSDProcess* p2 = (const DragonFlyBSDProcess*)v2;
|
||||
|
||||
if (settings->direction == 1) {
|
||||
p1 = (const DragonFlyBSDProcess*)v1;
|
||||
p2 = (const DragonFlyBSDProcess*)v2;
|
||||
} else {
|
||||
p2 = (const DragonFlyBSDProcess*)v1;
|
||||
p1 = (const DragonFlyBSDProcess*)v2;
|
||||
}
|
||||
|
||||
switch ((int) settings->sortKey) {
|
||||
switch (key) {
|
||||
// add Platform-specific fields here
|
||||
case JID:
|
||||
return SPACESHIP_NUMBER(p1->jid, p2->jid);
|
||||
case JAIL:
|
||||
return SPACESHIP_NULLSTR(p1->jname, p2->jname);
|
||||
default:
|
||||
return Process_compare(v1, v2);
|
||||
return Process_compareByKey_Base(v1, v2, key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,3 +103,14 @@ bool Process_isThread(const Process* this) {
|
||||
return (Process_isUserlandThread(this));
|
||||
}
|
||||
}
|
||||
|
||||
const ProcessClass DragonFlyBSDProcess_class = {
|
||||
.super = {
|
||||
.extends = Class(Process),
|
||||
.display = Process_display,
|
||||
.delete = Process_delete,
|
||||
.compare = Process_compare
|
||||
},
|
||||
.writeField = DragonFlyBSDProcess_writeField,
|
||||
.compareByKey = DragonFlyBSDProcess_compareByKey
|
||||
};
|
||||
|
@ -8,13 +8,6 @@ Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
typedef enum DragonFlyBSDProcessFields {
|
||||
// Add platform-specific fields here, with ids >= 100
|
||||
JID = 100,
|
||||
JAIL = 101,
|
||||
LAST_PROCESSFIELD = 102,
|
||||
} DragonFlyBSDProcessField;
|
||||
|
||||
typedef struct DragonFlyBSDProcess_ {
|
||||
Process super;
|
||||
int kernel;
|
||||
@ -29,18 +22,12 @@ typedef struct DragonFlyBSDProcess_ {
|
||||
|
||||
extern const ProcessClass DragonFlyBSDProcess_class;
|
||||
|
||||
extern ProcessFieldData Process_fields[];
|
||||
|
||||
extern ProcessPidColumn Process_pidColumns[];
|
||||
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
|
||||
|
||||
Process* DragonFlyBSDProcess_new(const Settings* settings);
|
||||
|
||||
void Process_delete(Object* cast);
|
||||
|
||||
void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field);
|
||||
|
||||
long DragonFlyBSDProcess_compare(const void* v1, const void* v2);
|
||||
|
||||
bool Process_isThread(const Process* this);
|
||||
|
||||
#endif
|
||||
|
@ -15,7 +15,6 @@ in the source distribution for its full text.
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
@ -55,12 +54,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
|
||||
len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len);
|
||||
|
||||
len = sizeof(pageSize);
|
||||
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) {
|
||||
pageSize = CRT_pageSize;
|
||||
pageSizeKb = CRT_pageSizeKB;
|
||||
} else {
|
||||
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1)
|
||||
CRT_fatalError("Cannot get pagesize by sysctl");
|
||||
pageSizeKb = pageSize / ONE_K;
|
||||
}
|
||||
|
||||
// usable page count vm.stats.vm.v_page_count
|
||||
// actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size
|
||||
@ -115,7 +111,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
|
||||
|
||||
dfpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
|
||||
if (dfpl->kd == NULL) {
|
||||
errx(1, "kvm_open: %s", errbuf);
|
||||
CRT_fatalError("kvm_openfiles() failed");
|
||||
}
|
||||
|
||||
return pl;
|
||||
@ -265,7 +261,7 @@ static inline void DragonFlyBSDProcessList_scanMemoryInfo(ProcessList* pl) {
|
||||
pl->usedSwap *= pageSizeKb;
|
||||
}
|
||||
|
||||
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) {
|
||||
static char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, const struct kinfo_proc* kproc, int* basenameEnd) {
|
||||
char** argv = kvm_getargv(kd, kproc, 0);
|
||||
if (!argv) {
|
||||
return xStrdup(kproc->kp_comm);
|
||||
@ -297,25 +293,20 @@ static inline void DragonFlyBSDProcessList_scanJails(DragonFlyBSDProcessList* df
|
||||
char* nextpos;
|
||||
|
||||
if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) {
|
||||
fprintf(stderr, "initial sysctlbyname / jail.list failed\n");
|
||||
exit(3);
|
||||
CRT_fatalError("initial sysctlbyname / jail.list failed");
|
||||
}
|
||||
retry:
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
jls = xMalloc(len);
|
||||
if (jls == NULL) {
|
||||
fprintf(stderr, "xMalloc failed\n");
|
||||
exit(4);
|
||||
}
|
||||
|
||||
if (sysctlbyname("jail.list", jls, &len, NULL, 0) == -1) {
|
||||
if (errno == ENOMEM) {
|
||||
free(jls);
|
||||
goto retry;
|
||||
}
|
||||
fprintf(stderr, "sysctlbyname / jail.list failed\n");
|
||||
exit(5);
|
||||
CRT_fatalError("sysctlbyname / jail.list failed");
|
||||
}
|
||||
|
||||
if (dfpl->jails) {
|
||||
@ -346,7 +337,7 @@ retry:
|
||||
free(jls);
|
||||
}
|
||||
|
||||
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) {
|
||||
static char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) {
|
||||
char* hostname;
|
||||
char* jname;
|
||||
|
||||
@ -376,10 +367,10 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
|
||||
int count = 0;
|
||||
|
||||
// TODO Kernel Threads seem to be skipped, need to figure out the correct flag
|
||||
struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count);
|
||||
const struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
struct kinfo_proc* kproc = &kprocs[i];
|
||||
const struct kinfo_proc* kproc = &kprocs[i];
|
||||
bool preExisting = false;
|
||||
bool ATTR_UNUSED isIdleProcess = false;
|
||||
|
||||
@ -433,13 +424,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
|
||||
}
|
||||
}
|
||||
|
||||
proc->m_virt = kproc->kp_vm_map_size / pageSize;
|
||||
proc->m_resident = kproc->kp_vm_rssize;
|
||||
proc->m_virt = kproc->kp_vm_map_size / ONE_K;
|
||||
proc->m_resident = kproc->kp_vm_rssize * pageSizeKb;
|
||||
proc->nlwp = kproc->kp_nthreads; // number of lwp thread
|
||||
proc->time = (kproc->kp_swtime + 5000) / 10000;
|
||||
|
||||
proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale);
|
||||
proc->percent_mem = 100.0 * (proc->m_resident * pageSizeKb) / (double)(super->totalMem);
|
||||
proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem);
|
||||
|
||||
if (proc->percent_cpu > 0.1) {
|
||||
// system idle process should own all CPU time left regardless of CPU count
|
||||
|
@ -11,16 +11,12 @@ in the source distribution for its full text.
|
||||
#include <kvm.h>
|
||||
#include <sys/param.h>
|
||||
#include <osreldate.h>
|
||||
#include <sys/kinfo.h>
|
||||
#include <kinfo.h>
|
||||
#include <sys/jail.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/resource.h>
|
||||
#include "Hashtable.h"
|
||||
#include "DragonFlyBSDProcess.h"
|
||||
|
||||
#define JAIL_ERRMSGLEN 1024
|
||||
extern char jail_errmsg[JAIL_ERRMSGLEN];
|
||||
|
||||
typedef struct CPUData_ {
|
||||
double userPercent;
|
||||
@ -55,10 +51,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
|
||||
|
||||
void ProcessList_delete(ProcessList* this);
|
||||
|
||||
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd);
|
||||
|
||||
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid);
|
||||
|
||||
void ProcessList_goThroughEntries(ProcessList* super, pauseProcessUpdate);
|
||||
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
|
||||
|
||||
#endif
|
||||
|
@ -31,9 +31,7 @@ in the source distribution for its full text.
|
||||
#include <math.h>
|
||||
|
||||
|
||||
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
|
||||
|
||||
int Platform_numberOfFields = LAST_PROCESSFIELD;
|
||||
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
|
||||
|
||||
const SignalItem Platform_signals[] = {
|
||||
{ .name = " 0 Cancel", .number = 0 },
|
||||
|
@ -17,11 +17,8 @@ in the source distribution for its full text.
|
||||
#include "ProcessLocksScreen.h"
|
||||
#include "SignalsPanel.h"
|
||||
|
||||
extern ProcessFieldData Process_fields[];
|
||||
|
||||
extern ProcessField Platform_defaultFields[];
|
||||
|
||||
extern int Platform_numberOfFields;
|
||||
extern const ProcessField Platform_defaultFields[];
|
||||
|
||||
extern const SignalItem Platform_signals[];
|
||||
|
||||
|
17
dragonflybsd/ProcessField.h
Normal file
17
dragonflybsd/ProcessField.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef HEADER_DragonFlyBSDProcessField
|
||||
#define HEADER_DragonFlyBSDProcessField
|
||||
/*
|
||||
htop - dragonflybsd/ProcessField.h
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
|
||||
#define PLATFORM_PROCESS_FIELDS \
|
||||
JID = 100, \
|
||||
JAIL = 101, \
|
||||
// End of list
|
||||
|
||||
|
||||
#endif /* HEADER_DragonFlyBSDProcessField */
|
@ -18,16 +18,16 @@ in the source distribution for its full text.
|
||||
|
||||
const char* const nodevStr = "nodev";
|
||||
|
||||
ProcessFieldData Process_fields[] = {
|
||||
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
|
||||
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
|
||||
[PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
|
||||
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
|
||||
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
|
||||
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
|
||||
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
|
||||
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
|
||||
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
|
||||
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
|
||||
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = PROCESS_FLAG_FREEBSD_TTY, },
|
||||
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
|
||||
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
|
||||
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
|
||||
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
|
||||
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
|
||||
@ -44,21 +44,9 @@ ProcessFieldData Process_fields[] = {
|
||||
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
|
||||
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
|
||||
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
|
||||
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
|
||||
[JID] = { .name = "JID", .title = " JID ", .description = "Jail prison ID", .flags = 0, },
|
||||
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
|
||||
[JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, },
|
||||
[JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, },
|
||||
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
|
||||
};
|
||||
|
||||
ProcessPidColumn Process_pidColumns[] = {
|
||||
{ .id = JID, .label = "JID" },
|
||||
{ .id = PID, .label = "PID" },
|
||||
{ .id = PPID, .label = "PPID" },
|
||||
{ .id = TPGID, .label = "TPGID" },
|
||||
{ .id = TGID, .label = "TGID" },
|
||||
{ .id = PGRP, .label = "PGRP" },
|
||||
{ .id = SESSION, .label = "SID" },
|
||||
{ .id = 0, .label = NULL },
|
||||
};
|
||||
|
||||
Process* FreeBSDProcess_new(const Settings* settings) {
|
||||
@ -80,17 +68,12 @@ static void FreeBSDProcess_writeField(const Process* this, RichString* str, Proc
|
||||
char buffer[256]; buffer[255] = '\0';
|
||||
int attr = CRT_colors[DEFAULT_COLOR];
|
||||
int n = sizeof(buffer) - 1;
|
||||
switch ((int) field) {
|
||||
switch (field) {
|
||||
// add FreeBSD-specific fields here
|
||||
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
|
||||
case JAIL: {
|
||||
xSnprintf(buffer, n, "%-11s ", fp->jname);
|
||||
if (buffer[11] != '\0') {
|
||||
buffer[11] = ' ';
|
||||
buffer[12] = '\0';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break;
|
||||
case JAIL:
|
||||
Process_printLeftAlignedField(str, attr, fp->jname ? fp->jname : "N/A", 11);
|
||||
return;
|
||||
case TTY_NR:
|
||||
if (fp->ttyPath) {
|
||||
if (fp->ttyPath == nodevStr)
|
||||
@ -105,22 +88,14 @@ static void FreeBSDProcess_writeField(const Process* this, RichString* str, Proc
|
||||
Process_writeField(this, str, field);
|
||||
return;
|
||||
}
|
||||
RichString_append(str, attr, buffer);
|
||||
RichString_appendWide(str, attr, buffer);
|
||||
}
|
||||
|
||||
static long FreeBSDProcess_compare(const void* v1, const void* v2) {
|
||||
const FreeBSDProcess *p1, *p2;
|
||||
const Settings *settings = ((const Process*)v1)->settings;
|
||||
static int FreeBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
|
||||
const FreeBSDProcess* p1 = (const FreeBSDProcess*)v1;
|
||||
const FreeBSDProcess* p2 = (const FreeBSDProcess*)v2;
|
||||
|
||||
if (settings->direction == 1) {
|
||||
p1 = (const FreeBSDProcess*)v1;
|
||||
p2 = (const FreeBSDProcess*)v2;
|
||||
} else {
|
||||
p2 = (const FreeBSDProcess*)v1;
|
||||
p1 = (const FreeBSDProcess*)v2;
|
||||
}
|
||||
|
||||
switch ((int) settings->sortKey) {
|
||||
switch (key) {
|
||||
// add FreeBSD-specific fields here
|
||||
case JID:
|
||||
return SPACESHIP_NUMBER(p1->jid, p2->jid);
|
||||
@ -129,7 +104,7 @@ static long FreeBSDProcess_compare(const void* v1, const void* v2) {
|
||||
case TTY_NR:
|
||||
return SPACESHIP_NULLSTR(p1->ttyPath, p2->ttyPath);
|
||||
default:
|
||||
return Process_compare(v1, v2);
|
||||
return Process_compareByKey_Base(v1, v2, key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +123,8 @@ const ProcessClass FreeBSDProcess_class = {
|
||||
.extends = Class(Process),
|
||||
.display = Process_display,
|
||||
.delete = Process_delete,
|
||||
.compare = FreeBSDProcess_compare
|
||||
.compare = Process_compare
|
||||
},
|
||||
.writeField = FreeBSDProcess_writeField,
|
||||
.compareByKey = FreeBSDProcess_compareByKey
|
||||
};
|
||||
|
@ -18,13 +18,6 @@ in the source distribution for its full text.
|
||||
|
||||
extern const char* const nodevStr;
|
||||
|
||||
typedef enum FreeBSDProcessFields_ {
|
||||
// Add platform-specific fields here, with ids >= 100
|
||||
JID = 100,
|
||||
JAIL = 101,
|
||||
LAST_PROCESSFIELD = 102,
|
||||
} FreeBSDProcessField;
|
||||
|
||||
typedef struct FreeBSDProcess_ {
|
||||
Process super;
|
||||
int kernel;
|
||||
@ -43,9 +36,7 @@ static inline bool Process_isUserlandThread(const Process* this) {
|
||||
|
||||
extern const ProcessClass FreeBSDProcess_class;
|
||||
|
||||
extern ProcessFieldData Process_fields[];
|
||||
|
||||
extern ProcessPidColumn Process_pidColumns[];
|
||||
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
|
||||
|
||||
Process* FreeBSDProcess_new(const Settings* settings);
|
||||
|
||||
|
@ -9,12 +9,10 @@ in the source distribution for its full text.
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/_iovec.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/param.h> // needs to be included before <sys/jail.h> for MAXPATHLEN
|
||||
#include <sys/jail.h>
|
||||
@ -72,12 +70,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
|
||||
len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len);
|
||||
|
||||
len = sizeof(pageSize);
|
||||
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) {
|
||||
pageSize = CRT_pageSize;
|
||||
pageSizeKb = CRT_pageSize;
|
||||
} else {
|
||||
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1)
|
||||
CRT_fatalError("Cannot get pagesize by sysctl");
|
||||
pageSizeKb = pageSize / ONE_K;
|
||||
}
|
||||
|
||||
// usable page count vm.stats.vm.v_page_count
|
||||
// actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size
|
||||
@ -149,7 +144,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
|
||||
|
||||
fpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
|
||||
if (fpl->kd == NULL) {
|
||||
errx(1, "kvm_open: %s", errbuf);
|
||||
CRT_fatalError("kvm_openfiles() failed");
|
||||
}
|
||||
|
||||
fpl->ttys = Hashtable_new(20, true);
|
||||
@ -419,7 +414,7 @@ static char* FreeBSDProcessList_readJailName(const struct kinfo_proc* kproc) {
|
||||
char* jname = NULL;
|
||||
char jnamebuf[MAXHOSTNAMELEN];
|
||||
|
||||
if (kproc->ki_jid != 0 ) {
|
||||
if (kproc->ki_jid != 0) {
|
||||
struct iovec jiov[6];
|
||||
|
||||
memset(jnamebuf, 0, sizeof(jnamebuf));
|
||||
@ -526,13 +521,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
|
||||
}
|
||||
|
||||
// from FreeBSD source /src/usr.bin/top/machine.c
|
||||
proc->m_virt = kproc->ki_size / pageSize;
|
||||
proc->m_resident = kproc->ki_rssize;
|
||||
proc->m_virt = kproc->ki_size / ONE_K;
|
||||
proc->m_resident = kproc->ki_rssize * pageSizeKb;
|
||||
proc->nlwp = kproc->ki_numthreads;
|
||||
proc->time = (kproc->ki_runtime + 5000) / 10000;
|
||||
|
||||
proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale);
|
||||
proc->percent_mem = 100.0 * (proc->m_resident * pageSizeKb) / (double)(super->totalMem);
|
||||
proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem);
|
||||
|
||||
/*
|
||||
* TODO
|
||||
|
@ -47,9 +47,7 @@ in the source distribution for its full text.
|
||||
#include "zfs/ZfsCompressedArcMeter.h"
|
||||
|
||||
|
||||
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
|
||||
|
||||
int Platform_numberOfFields = LAST_PROCESSFIELD;
|
||||
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
|
||||
|
||||
const SignalItem Platform_signals[] = {
|
||||
{ .name = " 0 Cancel", .number = 0 },
|
||||
@ -248,7 +246,7 @@ char* Platform_getProcessEnv(pid_t pid) {
|
||||
char* env = xMalloc(capacity);
|
||||
|
||||
int err = sysctl(mib, 4, env, &capacity, NULL, 0);
|
||||
if (err) {
|
||||
if (err || capacity == 0) {
|
||||
free(env);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -19,11 +19,7 @@ in the source distribution for its full text.
|
||||
#include "SignalsPanel.h"
|
||||
|
||||
|
||||
extern ProcessFieldData Process_fields[];
|
||||
|
||||
extern ProcessField Platform_defaultFields[];
|
||||
|
||||
extern int Platform_numberOfFields;
|
||||
extern const ProcessField Platform_defaultFields[];
|
||||
|
||||
extern const SignalItem Platform_signals[];
|
||||
|
||||
|
17
freebsd/ProcessField.h
Normal file
17
freebsd/ProcessField.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef HEADER_FreeBSDProcessField
|
||||
#define HEADER_FreeBSDProcessField
|
||||
/*
|
||||
htop - freebsd/ProcessField.h
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
|
||||
#define PLATFORM_PROCESS_FIELDS \
|
||||
JID = 100, \
|
||||
JAIL = 101, \
|
||||
// End of list
|
||||
|
||||
|
||||
#endif /* HEADER_FreeBSDProcessField */
|
68
htop.1.in
68
htop.1.in
@ -2,11 +2,9 @@
|
||||
.SH "NAME"
|
||||
htop \- interactive process viewer
|
||||
.SH "SYNOPSIS"
|
||||
.LP
|
||||
.B htop
|
||||
.RB [ \-dCFhpustvH ]
|
||||
.SH "DESCRIPTION"
|
||||
.LP
|
||||
.B htop
|
||||
is a cross-platform ncurses-based process viewer.
|
||||
.LP
|
||||
@ -22,9 +20,7 @@ Tasks related to processes (killing, renicing) can be done without
|
||||
entering their PIDs.
|
||||
.br
|
||||
.SH "COMMAND-LINE OPTIONS"
|
||||
.LP
|
||||
Mandatory arguments to long options are mandatory for short options too.
|
||||
.LP
|
||||
.TP
|
||||
\fB\-d \-\-delay=DELAY\fR
|
||||
Delay between updates, in tenths of seconds. If the delay value is
|
||||
@ -46,7 +42,8 @@ Display a help message and exit
|
||||
Show only the given PIDs
|
||||
.TP
|
||||
\fB\-s \-\-sort\-key COLUMN\fR
|
||||
Sort by this column (use \-\-sort\-key help for a column list)
|
||||
Sort by this column (use \-\-sort\-key help for a column list).
|
||||
This will force a list view unless you specify -t at the same time.
|
||||
.TP
|
||||
\fB\-u \-\-user=USERNAME\fR
|
||||
Show only the processes of a given user
|
||||
@ -61,15 +58,14 @@ Disable support of mouse control
|
||||
Output version information and exit
|
||||
.TP
|
||||
\fB\-t \-\-tree
|
||||
Show processes in tree view
|
||||
Show processes in tree view. This can be used to force a tree view when
|
||||
requesting a sort order with -s.
|
||||
.TP
|
||||
\fB\-H \-\-highlight-changes=DELAY\fR
|
||||
Highlight new and old processes
|
||||
.SH "INTERACTIVE COMMANDS"
|
||||
.LP
|
||||
The following commands are supported while in
|
||||
.BR htop :
|
||||
.LP
|
||||
.TP 5
|
||||
.B Up, Alt-k
|
||||
Select (highlight) the previous process in the process list. Scroll the list
|
||||
@ -141,6 +137,7 @@ select which columns are displayed, in which order.
|
||||
Incrementally search the command lines of all the displayed processes. The
|
||||
currently selected (highlighted) command will update as you type. While in
|
||||
search mode, pressing F3 will cycle through matching occurrences.
|
||||
Pressing Shift-F3 will cycle backwards.
|
||||
|
||||
Alternatively the search can be started by simply typing the command
|
||||
you are looking for, although for the first character normal key
|
||||
@ -157,11 +154,9 @@ between them as a tree. Toggling the key will switch between tree and
|
||||
your previously selected sort view. Selecting a sort view will exit
|
||||
tree view.
|
||||
.TP
|
||||
.B F6
|
||||
On sorted view, select a field for sorting, also accessible through < and >.
|
||||
.B F6, <, >
|
||||
Selects a field for sorting, also accessible through < and >.
|
||||
The current sort field is indicated by a highlight in the header.
|
||||
On tree view, expand or collapse the current subtree. A "+" indicator in the
|
||||
tree node indicates that it is collapsed.
|
||||
.TP
|
||||
.B F7, ]
|
||||
Increase the selected process's priority (subtract from 'nice' value).
|
||||
@ -192,6 +187,9 @@ Set CPU affinity: mark which CPUs a process is allowed to use.
|
||||
.B u
|
||||
Show only processes owned by a specified user.
|
||||
.TP
|
||||
.B N
|
||||
Sort by PID.
|
||||
.TP
|
||||
.B M
|
||||
Sort by memory usage (top compatibility key).
|
||||
.TP
|
||||
@ -232,7 +230,6 @@ Refresh: redraw screen and recalculate values.
|
||||
PID search: type in process ID and the selection highlight will be moved to it.
|
||||
.PD
|
||||
.SH "COLUMNS"
|
||||
.LP
|
||||
The following columns can display data about each process. A value of '\-' in
|
||||
all the rows indicates that a column is unsupported on your system, or
|
||||
currently unimplemented in
|
||||
@ -242,7 +239,6 @@ The names below are the ones used in the
|
||||
shown in
|
||||
.BR htop 's
|
||||
main screen, it is shown below in parenthesis.
|
||||
.LP
|
||||
.TP 5
|
||||
.B Command
|
||||
The full command line of the process (i.e. program name and arguments). If the
|
||||
@ -460,8 +456,48 @@ The executable file of the process as reported by the kernel. Requires CAP_SYS_P
|
||||
.TP
|
||||
.B All other flags
|
||||
Currently unsupported (always displays '-').
|
||||
.SH "EXTERNAL LIBRARIES"
|
||||
While
|
||||
.B htop
|
||||
depends on most of the libraries it uses at build time there are two
|
||||
noteworthy exceptions to this rule. These exceptions both relate to
|
||||
data displayed in meters displayed in the header of
|
||||
.B htop
|
||||
and were intentionally created as optional runtime dependencies instead.
|
||||
These exceptions are described below:
|
||||
.TP
|
||||
.B libsystemd
|
||||
The bindings for libsystemd are used in the SystemD meter to determine
|
||||
the number of active services and the overall system state. Looking for
|
||||
the functions to determine these information at runtime allows for
|
||||
builds to support these meters without forcing the package manager
|
||||
to install these libraries on systems that otherwise don't use systemd.
|
||||
|
||||
Summary: no build time dependency, optional runtime dependency on
|
||||
.B libsystemd
|
||||
via dynamic loading, with
|
||||
.B systemctl(1)
|
||||
fallback.
|
||||
.TP
|
||||
.B libsensors
|
||||
The bindings for libsensors are used for the CPU temperature readings
|
||||
in the CPU usage meters if displaying the temperature is enabled through
|
||||
the setup screen. In order for
|
||||
.B htop
|
||||
to show these temperatures correctly though, a proper configuration
|
||||
of libsensors through its usual configuration files is assumed and that
|
||||
all CPU cores correspond to temperature sensors from the
|
||||
.B coretemp
|
||||
driver with core 0 corresponding to a sensor labelled "Core 0". The
|
||||
package temperature may be given as "Package id 0". If missing it is
|
||||
inferred as the maximum value from the available per-core readings.
|
||||
|
||||
Summary: build time dependency on
|
||||
.B libsensors(3)
|
||||
C header files, optional runtime dependency on
|
||||
.B libsensors(3)
|
||||
via dynamic loading.
|
||||
.SH "CONFIG FILE"
|
||||
.LP
|
||||
By default
|
||||
.B htop
|
||||
reads its configuration from the XDG-compliant path
|
||||
@ -479,7 +515,6 @@ You may override the location of the configuration file using the $HTOPRC
|
||||
environment variable (so you can have multiple configurations for different
|
||||
machines that share the same home directory, for example).
|
||||
.SH "MEMORY SIZES"
|
||||
.LP
|
||||
Memory sizes in
|
||||
.B htop
|
||||
are displayed in a human-readable form.
|
||||
@ -497,7 +532,6 @@ space and make memory size representations consistent throughout
|
||||
and
|
||||
.BR limits.conf (5).
|
||||
.SH "AUTHORS"
|
||||
.LP
|
||||
.B htop
|
||||
was originally developed by Hisham Muhammad.
|
||||
Nowadays it is maintained by the community at <htop@groups.io>.
|
||||
|
20
htop.c
20
htop.c
@ -49,8 +49,8 @@ static void printHelpFlag(void) {
|
||||
"-H --highlight-changes[=DELAY] Highlight new and old processes\n"
|
||||
"-M --no-mouse Disable the mouse\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"
|
||||
"-t --tree Show the tree view by default\n"
|
||||
"-s --sort-key=COLUMN Sort by COLUMN in list view (try --sort-key=help for a list)\n"
|
||||
"-t --tree Show the tree view (can be combined with -s)\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"
|
||||
"-V --version Print version info\n"
|
||||
@ -93,7 +93,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
|
||||
.highlightDelaySecs = -1,
|
||||
};
|
||||
|
||||
static struct option long_opts[] =
|
||||
const struct option long_opts[] =
|
||||
{
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
@ -125,14 +125,14 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
|
||||
case 's':
|
||||
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
|
||||
if (String_eq(optarg, "help")) {
|
||||
for (int j = 1; j < Platform_numberOfFields; j++) {
|
||||
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 = 0;
|
||||
for (int j = 1; j < Platform_numberOfFields; j++) {
|
||||
for (int j = 1; j < LAST_PROCESSFIELD; j++) {
|
||||
if (Process_fields[j].name == NULL)
|
||||
continue;
|
||||
if (String_eq(optarg, Process_fields[j].name)) {
|
||||
@ -204,6 +204,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
|
||||
}
|
||||
case 'F': {
|
||||
assert(optarg);
|
||||
free(flags.commFilter);
|
||||
flags.commFilter = xStrdup(optarg);
|
||||
|
||||
break;
|
||||
@ -298,9 +299,12 @@ int main(int argc, char** argv) {
|
||||
if (flags.highlightDelaySecs != -1)
|
||||
settings->highlightDelaySecs = flags.highlightDelaySecs;
|
||||
if (flags.sortKey > 0) {
|
||||
settings->sortKey = flags.sortKey;
|
||||
// -t -s <key> means "tree sorted by key"
|
||||
// -s <key> means "list sorted by key" (previous existing behavior)
|
||||
if (!flags.treeView) {
|
||||
settings->treeView = false;
|
||||
settings->direction = 1;
|
||||
}
|
||||
Settings_setSortKey(settings, flags.sortKey);
|
||||
}
|
||||
|
||||
CRT_init(&(settings->delay), settings->colorScheme, flags.allowUnicode);
|
||||
@ -310,8 +314,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
MainPanel_updateTreeFunctions(panel, settings->treeView);
|
||||
|
||||
ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel));
|
||||
|
||||
State state = {
|
||||
.settings = settings,
|
||||
.ut = ut,
|
||||
|
BIN
htop.png
BIN
htop.png
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.5 KiB |
112
htop.svg
Normal file
112
htop.svg
Normal file
@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="30.398mm" height="30.399mm" version="1.1" viewBox="0 0 30.398 30.399" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="aj" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(41.404 221.45)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
|
||||
<linearGradient id="c">
|
||||
<stop stop-color="#363d36" offset="0"/>
|
||||
<stop stop-color="#565d56" offset=".2"/>
|
||||
<stop stop-color="#6c766c" offset=".8"/>
|
||||
<stop stop-color="#8c968c" offset="1"/>
|
||||
</linearGradient>
|
||||
<filter id="f" x="-.063963" y="-.025049" width="1.1279" height="1.0501" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="0.072789412"/>
|
||||
</filter>
|
||||
<linearGradient id="ai" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(41.508 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<linearGradient id="a">
|
||||
<stop stop-color="#8bf18b" offset="0"/>
|
||||
<stop stop-color="#5bc15b" offset=".2"/>
|
||||
<stop stop-color="#47a347" offset=".8"/>
|
||||
<stop stop-color="#177317" offset="1"/>
|
||||
</linearGradient>
|
||||
<filter id="l" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="0.065909587"/>
|
||||
</filter>
|
||||
<linearGradient id="d" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(24.234,-9.2667)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
|
||||
<filter id="e" x="-.063963" y="-.025049" width="1.1279" height="1.0501" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="0.072789412"/>
|
||||
</filter>
|
||||
<linearGradient id="ah" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(44.242 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<filter id="k" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="0.065909587"/>
|
||||
</filter>
|
||||
<linearGradient id="ag" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(46.976 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<filter id="j" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="0.065909587"/>
|
||||
</filter>
|
||||
<linearGradient id="af" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(49.709 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<filter id="am" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="0.065909587"/>
|
||||
</filter>
|
||||
<linearGradient id="ae" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(52.443 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<filter id="al" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="0.065909587"/>
|
||||
</filter>
|
||||
<linearGradient id="ad" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(55.177 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<filter id="ak" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="0.065909587"/>
|
||||
</filter>
|
||||
<linearGradient id="ac" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(57.91 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<filter id="i" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="0.065909587"/>
|
||||
</filter>
|
||||
<linearGradient id="ab" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(60.644 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
|
||||
<linearGradient id="b">
|
||||
<stop stop-color="#fe7f7f" offset="0"/>
|
||||
<stop stop-color="#ce4f4f" offset=".2"/>
|
||||
<stop stop-color="#b23030" offset=".8"/>
|
||||
<stop stop-color="#920000" offset="1"/>
|
||||
</linearGradient>
|
||||
<filter id="h" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="0.065909587"/>
|
||||
</filter>
|
||||
<linearGradient id="aa" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(63.376 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
|
||||
<filter id="g" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="0.065909587"/>
|
||||
</filter>
|
||||
<linearGradient id="z" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(41.404 210.02)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
|
||||
<linearGradient id="y" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(41.508 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<linearGradient id="x" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(44.242 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<linearGradient id="w" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(46.976 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<linearGradient id="v" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(57.91 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<linearGradient id="u" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(63.376 210)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
|
||||
<linearGradient id="t" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(41.404 232.87)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
|
||||
<linearGradient id="s" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(41.508 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<linearGradient id="r" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(44.242 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<linearGradient id="q" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(46.976 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<linearGradient id="p" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(60.644 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
|
||||
<linearGradient id="o" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(63.376 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
|
||||
<linearGradient id="n" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(57.91 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
|
||||
<linearGradient id="m" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(60.644 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
</defs>
|
||||
<g transform="translate(.08011 -.58599)">
|
||||
<g transform="matrix(.98769 0 0 .99741 -80.905 -258.93)" fill-rule="evenodd">
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m82.188 271.97 0.0251 6.963 2.7061-8e-3v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#aj)" filter="url(#f)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m85.401 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ai)" filter="url(#l)"/>
|
||||
<path transform="matrix(-.99856 0 0 1.2772 177.16 218.32)" d="m65.017 41.255 0.02508 6.963 2.7061-0.0084v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#d)" filter="url(#e)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m88.135 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ah)" filter="url(#k)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m90.868 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ag)" filter="url(#j)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m93.602 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#af)" filter="url(#am)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m96.335 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ae)" filter="url(#al)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m99.069 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ad)" filter="url(#ak)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m101.8 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ac)" filter="url(#i)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m104.54 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ab)" filter="url(#h)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m107.27 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#aa)" filter="url(#g)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m82.188 260.54 0.0251 6.963 2.7061-8e-3v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#z)" filter="url(#f)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m85.401 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#y)" filter="url(#l)"/>
|
||||
<path transform="matrix(-.99856 0 0 1.2772 177.16 207.94)" d="m65.017 41.255 0.02508 6.963 2.7061-0.0084v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#d)" filter="url(#e)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m88.135 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#x)" filter="url(#k)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m90.868 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#w)" filter="url(#j)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m101.8 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#v)" filter="url(#i)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m107.27 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#u)" filter="url(#g)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m82.188 283.39 0.0251 6.963 2.7061-8e-3v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#t)" filter="url(#f)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m85.401 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#s)" filter="url(#l)"/>
|
||||
<path transform="matrix(-.99856 0 0 1.2772 177.16 228.65)" d="m65.017 41.255 0.02508 6.963 2.7061-0.0084v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#d)" filter="url(#e)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m88.135 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#r)" filter="url(#k)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m90.868 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#q)" filter="url(#j)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m104.54 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#p)" filter="url(#h)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m107.27 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#o)" filter="url(#g)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m101.8 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#n)" filter="url(#h)"/>
|
||||
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m104.54 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#m)" filter="url(#i)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
@ -8,6 +8,8 @@
|
||||
|
||||
{ include: ["<bits/getopt_core.h>", "private", "<unistd.h>", "public"] },
|
||||
|
||||
{ include: ["<sys/dirent.h>", "private", "<dirent.h>", "public"] },
|
||||
|
||||
{ include: ["<sys/signal.h>", "private", "<signal.h>", "public"] },
|
||||
|
||||
{ include: ["<sys/_stdarg.h>", "private", "<stdarg.h>", "public"] },
|
||||
|
@ -17,7 +17,7 @@ in the source distribution for its full text.
|
||||
|
||||
|
||||
Panel* IOPriorityPanel_new(IOPriority currPrio) {
|
||||
Panel* this = Panel_new(1, 1, 1, 1, true, Class(ListItem), FunctionBar_newEnterEsc("Set ", "Cancel "));
|
||||
Panel* this = Panel_new(1, 1, 1, 1, Class(ListItem), true, FunctionBar_newEnterEsc("Set ", "Cancel "));
|
||||
|
||||
Panel_setHeader(this, "IO Priority:");
|
||||
Panel_add(this, (Object*) ListItem_new("None (based on nice)", IOPriority_None));
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <sensors/sensors.h>
|
||||
|
||||
#include "XUtils.h"
|
||||
@ -17,12 +17,19 @@ static int (*sym_sensors_snprintf_chip_name)(char*, size_t, const sensors_chip_n
|
||||
static const sensors_feature* (*sym_sensors_get_features)(const sensors_chip_name*, int*);
|
||||
static const sensors_subfeature* (*sym_sensors_get_subfeature)(const sensors_chip_name*, const sensors_feature*, sensors_subfeature_type);
|
||||
static int (*sym_sensors_get_value)(const sensors_chip_name*, int, double*);
|
||||
static char* (*sym_sensors_get_label)(const sensors_chip_name*, const sensors_feature*);
|
||||
|
||||
static void* dlopenHandle = NULL;
|
||||
|
||||
int LibSensors_init(FILE* input) {
|
||||
if (!dlopenHandle) {
|
||||
/* Find the unversioned libsensors.so (symlink) and prefer that, but Debian has .so.5 and Fedora .so.4 without
|
||||
matching symlinks (unless people install the -dev packages) */
|
||||
dlopenHandle = dlopen("libsensors.so", RTLD_LAZY);
|
||||
if (!dlopenHandle)
|
||||
dlopenHandle = dlopen("libsensors.so.5", RTLD_LAZY);
|
||||
if (!dlopenHandle)
|
||||
dlopenHandle = dlopen("libsensors.so.4", RTLD_LAZY);
|
||||
if (!dlopenHandle)
|
||||
goto dlfailure;
|
||||
|
||||
@ -42,6 +49,7 @@ int LibSensors_init(FILE* input) {
|
||||
resolve(sensors_get_features);
|
||||
resolve(sensors_get_subfeature);
|
||||
resolve(sensors_get_value);
|
||||
resolve(sensors_get_label);
|
||||
|
||||
#undef resolve
|
||||
}
|
||||
@ -65,17 +73,23 @@ void LibSensors_cleanup(void) {
|
||||
}
|
||||
}
|
||||
|
||||
int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) {
|
||||
if (!dlopenHandle)
|
||||
return -ENOTSUP;
|
||||
void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) {
|
||||
for (unsigned int i = 0; i <= cpuCount; i++)
|
||||
cpus[i].temperature = NAN;
|
||||
|
||||
int tempCount = 0;
|
||||
if (!dlopenHandle)
|
||||
return;
|
||||
|
||||
unsigned int coreTempCount = 0;
|
||||
|
||||
int n = 0;
|
||||
for (const sensors_chip_name *chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) {
|
||||
char buffer[32];
|
||||
sym_sensors_snprintf_chip_name(buffer, sizeof(buffer), chip);
|
||||
if (!String_startsWith(buffer, "coretemp") && !String_startsWith(buffer, "cpu_thermal"))
|
||||
if (!String_startsWith(buffer, "coretemp") &&
|
||||
!String_startsWith(buffer, "cpu_thermal") &&
|
||||
!String_startsWith(buffer, "k10temp") &&
|
||||
!String_startsWith(buffer, "zenpower"))
|
||||
continue;
|
||||
|
||||
int m = 0;
|
||||
@ -83,7 +97,27 @@ int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) {
|
||||
if (feature->type != SENSORS_FEATURE_TEMP)
|
||||
continue;
|
||||
|
||||
if (feature->number > cpuCount)
|
||||
char* label = sym_sensors_get_label(chip, feature);
|
||||
if (!label)
|
||||
continue;
|
||||
|
||||
unsigned int tempId;
|
||||
if (String_startsWith(label, "Package ")) {
|
||||
tempId = 0;
|
||||
} else if (String_startsWith(label, "temp")) {
|
||||
/* Raspberry Pi has only temp1 */
|
||||
tempId = 0;
|
||||
} else if (String_startsWith(label, "Tdie")) {
|
||||
tempId = 0;
|
||||
} else if (String_startsWith(label, "Core ")) {
|
||||
tempId = 1 + atoi(label + strlen("Core "));
|
||||
} else {
|
||||
tempId = UINT_MAX;
|
||||
}
|
||||
|
||||
free(label);
|
||||
|
||||
if (tempId > cpuCount)
|
||||
continue;
|
||||
|
||||
const sensors_subfeature *sub_feature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT);
|
||||
@ -93,13 +127,43 @@ int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) {
|
||||
if (r != 0)
|
||||
continue;
|
||||
|
||||
cpus[feature->number].temperature = temp;
|
||||
tempCount++;
|
||||
cpus[tempId].temperature = temp;
|
||||
if (tempId > 0)
|
||||
coreTempCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tempCount;
|
||||
const double packageTemp = cpus[0].temperature;
|
||||
|
||||
/* Only package temperature - copy to all cpus */
|
||||
if (coreTempCount == 0 && !isnan(packageTemp)) {
|
||||
for (unsigned int i = 1; i <= cpuCount; i++)
|
||||
cpus[i].temperature = packageTemp;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* No package temperature - set to max core temperature */
|
||||
if (isnan(packageTemp) && coreTempCount != 0) {
|
||||
double maxTemp = NAN;
|
||||
for (unsigned int i = 1; i <= cpuCount; i++) {
|
||||
const double coreTemp = cpus[i].temperature;
|
||||
if (isnan(coreTemp))
|
||||
continue;
|
||||
|
||||
maxTemp = MAXIMUM(maxTemp, coreTemp);
|
||||
}
|
||||
|
||||
cpus[0].temperature = maxTemp;
|
||||
}
|
||||
|
||||
/* Half the temperatures, probably HT/SMT - copy to second half */
|
||||
const unsigned int delta = cpuCount / 2;
|
||||
if (coreTempCount == delta) {
|
||||
for (unsigned int i = 1; i <= delta; i++)
|
||||
cpus[i + delta].temperature = cpus[i].temperature;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_SENSORS_SENSORS_H */
|
||||
|
@ -11,6 +11,6 @@
|
||||
int LibSensors_init(FILE* input);
|
||||
void LibSensors_cleanup(void);
|
||||
|
||||
int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount);
|
||||
void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount);
|
||||
|
||||
#endif /* HEADER_LibSensors */
|
||||
|
@ -11,6 +11,7 @@ in the source distribution for its full text.
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -23,22 +24,22 @@ in the source distribution for its full text.
|
||||
|
||||
|
||||
/* semi-global */
|
||||
long long btime;
|
||||
int pageSize;
|
||||
int pageSizeKB;
|
||||
|
||||
/* Used to identify kernel threads in Comm and Exe columns */
|
||||
static const char *const kthreadID = "KTHREAD";
|
||||
|
||||
ProcessFieldData Process_fields[] = {
|
||||
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
|
||||
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
|
||||
[PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
|
||||
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
|
||||
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging, I idle)", .flags = 0, },
|
||||
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
|
||||
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
|
||||
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
|
||||
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
|
||||
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
|
||||
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = "TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
|
||||
[FLAGS] = { .name = "FLAGS", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
|
||||
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
|
||||
[CMINFLT] = { .name = "CMINFLT", .title = " CMINFLT ", .description = "Children processes' minor faults", .flags = 0, },
|
||||
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
|
||||
@ -49,24 +50,7 @@ ProcessFieldData Process_fields[] = {
|
||||
[CSTIME] = { .name = "CSTIME", .title = " CSTIME+ ", .description = "Children processes' system CPU time", .flags = 0, },
|
||||
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
|
||||
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
|
||||
[ITREALVALUE] = { .name = "ITREALVALUE", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
|
||||
[VSIZE] = { .name = "VSIZE", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[RSS] = { .name = "RSS", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[RLIM] = { .name = "RLIM", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[STARTCODE] = { .name = "STARTCODE", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[ENDCODE] = { .name = "ENDCODE", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[STARTSTACK] = { .name = "STARTSTACK", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[KSTKESP] = { .name = "KSTKESP", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[KSTKEIP] = { .name = "KSTKEIP", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[SIGNAL] = { .name = "SIGNAL", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[BLOCKED] = { .name = "BLOCKED", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[SSIGIGNORE] = { .name = "SIGIGNORE", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[SIGCATCH] = { .name = "SIGCATCH", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[WCHAN] = { .name = "WCHAN", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[NSWAP] = { .name = "NSWAP", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[CNSWAP] = { .name = "CNSWAP", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[EXIT_SIGNAL] = { .name = "EXIT_SIGNAL", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
|
||||
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
|
||||
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
|
||||
@ -82,10 +66,10 @@ ProcessFieldData Process_fields[] = {
|
||||
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
|
||||
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
|
||||
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
|
||||
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
|
||||
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
|
||||
#ifdef HAVE_OPENVZ
|
||||
[CTID] = { .name = "CTID", .title = " CTID ", .description = "OpenVZ container ID (a.k.a. virtual environment ID)", .flags = PROCESS_FLAG_LINUX_OPENVZ, },
|
||||
[VPID] = { .name = "VPID", .title = " VPID ", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_LINUX_OPENVZ, },
|
||||
[VPID] = { .name = "VPID", .title = "VPID", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_LINUX_OPENVZ, .pidColumn = true, },
|
||||
#endif
|
||||
#ifdef HAVE_VSERVER
|
||||
[VXID] = { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_LINUX_VSERVER, },
|
||||
@ -104,9 +88,9 @@ ProcessFieldData Process_fields[] = {
|
||||
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, },
|
||||
[IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, },
|
||||
#ifdef HAVE_DELAYACCT
|
||||
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = 0, },
|
||||
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = 0, },
|
||||
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = 0, },
|
||||
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, },
|
||||
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, },
|
||||
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, },
|
||||
#endif
|
||||
[M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it.", .flags = PROCESS_FLAG_LINUX_SMAPS, },
|
||||
[M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, },
|
||||
@ -116,20 +100,6 @@ ProcessFieldData Process_fields[] = {
|
||||
[PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, },
|
||||
[PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, },
|
||||
[CWD] = { .name ="CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_LINUX_CWD, },
|
||||
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
|
||||
};
|
||||
|
||||
ProcessPidColumn Process_pidColumns[] = {
|
||||
{ .id = PID, .label = "PID" },
|
||||
{ .id = PPID, .label = "PPID" },
|
||||
#ifdef HAVE_OPENVZ
|
||||
{ .id = VPID, .label = "VPID" },
|
||||
#endif
|
||||
{ .id = TPGID, .label = "TPGID" },
|
||||
{ .id = TGID, .label = "TGID" },
|
||||
{ .id = PGRP, .label = "PGRP" },
|
||||
{ .id = SESSION, .label = "SID" },
|
||||
{ .id = 0, .label = NULL },
|
||||
};
|
||||
|
||||
/* This function returns the string displayed in Command column, so that sorting
|
||||
@ -182,6 +152,11 @@ static int LinuxProcess_effectiveIOPriority(const LinuxProcess* this) {
|
||||
return this->ioPriority;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define SYS_ioprio_get __NR_ioprio_get
|
||||
#define SYS_ioprio_set __NR_ioprio_set
|
||||
#endif
|
||||
|
||||
IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) {
|
||||
IOPriority ioprio = 0;
|
||||
// Other OSes masquerading as Linux (NetBSD?) don't have this syscall
|
||||
@ -223,12 +198,11 @@ static bool findCommInCmdline(const char *comm, const char *cmdline, int cmdline
|
||||
/* Try to find procComm in tokenized cmdline - this might in rare cases
|
||||
* mis-identify a string or fail, if comm or cmdline had been unsuitably
|
||||
* modified by the process */
|
||||
const char *token;
|
||||
const char *tokenBase;
|
||||
size_t tokenLen;
|
||||
const size_t commLen = strlen(comm);
|
||||
|
||||
for (token = cmdline + cmdlineBasenameOffset; *token; ) {
|
||||
for (const char *token = cmdline + cmdlineBasenameOffset; *token; ) {
|
||||
for (tokenBase = token; *token && *token != '\n'; ++token) {
|
||||
if (*token == '/') {
|
||||
tokenBase = token + 1;
|
||||
@ -393,6 +367,16 @@ void LinuxProcess_makeCommandStr(Process* this) {
|
||||
char *str = strStart;
|
||||
|
||||
int cmdlineBasenameOffset = lp->procCmdlineBasenameOffset;
|
||||
int cmdlineBasenameEnd = lp->procCmdlineBasenameEnd;
|
||||
|
||||
if (!cmdline) {
|
||||
cmdlineBasenameOffset = 0;
|
||||
cmdlineBasenameEnd = 0;
|
||||
cmdline = "(zombie)";
|
||||
}
|
||||
|
||||
assert(cmdlineBasenameOffset >= 0);
|
||||
assert(cmdlineBasenameOffset <= (int)strlen(cmdline));
|
||||
|
||||
if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */
|
||||
if (showMergedCommand && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */
|
||||
@ -410,11 +394,11 @@ void LinuxProcess_makeCommandStr(Process* this) {
|
||||
if (showProgramPath) {
|
||||
(void) stpcpyWithNewlineConversion(str, cmdline);
|
||||
mc->baseStart = cmdlineBasenameOffset;
|
||||
mc->baseEnd = lp->procCmdlineBasenameEnd;
|
||||
mc->baseEnd = cmdlineBasenameEnd;
|
||||
} else {
|
||||
(void) stpcpyWithNewlineConversion(str, cmdline + cmdlineBasenameOffset);
|
||||
mc->baseStart = 0;
|
||||
mc->baseEnd = lp->procCmdlineBasenameEnd - cmdlineBasenameOffset;
|
||||
mc->baseEnd = cmdlineBasenameEnd - cmdlineBasenameOffset;
|
||||
}
|
||||
|
||||
if (mc->sep1) {
|
||||
@ -429,6 +413,9 @@ void LinuxProcess_makeCommandStr(Process* this) {
|
||||
int exeBasenameOffset = lp->procExeBasenameOffset;
|
||||
int exeBasenameLen = exeLen - exeBasenameOffset;
|
||||
|
||||
assert(exeBasenameOffset >= 0);
|
||||
assert(exeBasenameOffset <= (int)strlen(procExe));
|
||||
|
||||
/* Start with copying exe */
|
||||
if (showProgramPath) {
|
||||
str = stpcpy(str, procExe);
|
||||
@ -535,36 +522,36 @@ static void LinuxProcess_writeCommand(const Process* this, int attr, int baseAtt
|
||||
if(lp->procExeDeleted)
|
||||
baseAttr = CRT_colors[FAILED_READ];
|
||||
|
||||
RichString_append(str, attr, lp->mergedCommand.str);
|
||||
RichString_appendWide(str, attr, lp->mergedCommand.str);
|
||||
|
||||
if (lp->mergedCommand.commEnd) {
|
||||
if (!lp->mergedCommand.separateComm && commStart == baseStart && highlightBaseName) {
|
||||
/* If it was matched with procExe's basename, make it bold if needed */
|
||||
if (commEnd > baseEnd) {
|
||||
RichString_setAttrn(str, A_BOLD | baseAttr, baseStart, baseEnd - 1);
|
||||
RichString_setAttrn(str, A_BOLD | commAttr, baseEnd, commEnd - 1);
|
||||
RichString_setAttrn(str, A_BOLD | baseAttr, baseStart, baseEnd - baseStart);
|
||||
RichString_setAttrn(str, A_BOLD | commAttr, baseEnd, commEnd - baseEnd);
|
||||
} else if (commEnd < baseEnd) {
|
||||
RichString_setAttrn(str, A_BOLD | commAttr, commStart, commEnd - 1);
|
||||
RichString_setAttrn(str, A_BOLD | baseAttr, commEnd, baseEnd - 1);
|
||||
RichString_setAttrn(str, A_BOLD | commAttr, commStart, commEnd - commStart);
|
||||
RichString_setAttrn(str, A_BOLD | baseAttr, commEnd, baseEnd - commEnd);
|
||||
} else {
|
||||
// Actually should be highlighted commAttr, but marked baseAttr to reduce visual noise
|
||||
RichString_setAttrn(str, A_BOLD | baseAttr, commStart, commEnd - 1);
|
||||
RichString_setAttrn(str, A_BOLD | baseAttr, commStart, commEnd - commStart);
|
||||
}
|
||||
|
||||
baseStart = baseEnd;
|
||||
} else {
|
||||
RichString_setAttrn(str, commAttr, commStart, commEnd - 1);
|
||||
RichString_setAttrn(str, commAttr, commStart, commEnd - commStart);
|
||||
}
|
||||
}
|
||||
|
||||
if (baseStart < baseEnd && highlightBaseName) {
|
||||
RichString_setAttrn(str, baseAttr, baseStart, baseEnd - 1);
|
||||
RichString_setAttrn(str, baseAttr, baseStart, baseEnd - baseStart);
|
||||
}
|
||||
|
||||
if (mc->sep1)
|
||||
RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep1, strStart + mc->sep1);
|
||||
RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep1, 1);
|
||||
if (mc->sep2)
|
||||
RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep2, strStart + mc->sep2);
|
||||
RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep2, 1);
|
||||
}
|
||||
|
||||
static void LinuxProcess_writeCommandField(const Process *this, RichString *str, char *buffer, int n, int attr) {
|
||||
@ -604,10 +591,11 @@ static void LinuxProcess_writeCommandField(const Process *this, RichString *str,
|
||||
buf = stpcpy(buf, " ");
|
||||
}
|
||||
}
|
||||
|
||||
n -= (buf - buffer);
|
||||
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
|
||||
const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
|
||||
xSnprintf(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);
|
||||
RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
|
||||
LinuxProcess_writeCommand(this, attr, baseattr, str);
|
||||
}
|
||||
}
|
||||
@ -617,8 +605,8 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
|
||||
bool coloring = this->settings->highlightMegabytes;
|
||||
char buffer[256]; buffer[255] = '\0';
|
||||
int attr = CRT_colors[DEFAULT_COLOR];
|
||||
int n = sizeof(buffer) - 1;
|
||||
switch ((int)field) {
|
||||
size_t n = sizeof(buffer) - 1;
|
||||
switch (field) {
|
||||
case TTY_NR: {
|
||||
if (lp->ttyDevice) {
|
||||
xSnprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */);
|
||||
@ -630,19 +618,19 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
|
||||
}
|
||||
case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return;
|
||||
case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return;
|
||||
case M_DRS: Process_humanNumber(str, lp->m_drs * CRT_pageSizeKB, coloring); return;
|
||||
case M_DT: Process_humanNumber(str, lp->m_dt * CRT_pageSizeKB, coloring); return;
|
||||
case M_DRS: Process_humanNumber(str, lp->m_drs * pageSizeKB, coloring); return;
|
||||
case M_DT: Process_humanNumber(str, lp->m_dt * pageSizeKB, coloring); return;
|
||||
case M_LRS:
|
||||
if (lp->m_lrs) {
|
||||
Process_humanNumber(str, lp->m_lrs * CRT_pageSizeKB, coloring);
|
||||
Process_humanNumber(str, lp->m_lrs * pageSizeKB, coloring);
|
||||
return;
|
||||
}
|
||||
|
||||
attr = CRT_colors[PROCESS_SHADOW];
|
||||
xSnprintf(buffer, n, " N/A ");
|
||||
break;
|
||||
case M_TRS: Process_humanNumber(str, lp->m_trs * CRT_pageSizeKB, coloring); return;
|
||||
case M_SHARE: Process_humanNumber(str, lp->m_share * CRT_pageSizeKB, coloring); return;
|
||||
case M_TRS: Process_humanNumber(str, lp->m_trs * pageSizeKB, coloring); return;
|
||||
case M_SHARE: Process_humanNumber(str, lp->m_share * pageSizeKB, coloring); return;
|
||||
case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return;
|
||||
case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return;
|
||||
case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return;
|
||||
@ -673,7 +661,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
|
||||
}
|
||||
#ifdef HAVE_OPENVZ
|
||||
case CTID: xSnprintf(buffer, n, "%-8s ", lp->ctid ? lp->ctid : ""); break;
|
||||
case VPID: xSnprintf(buffer, n, Process_pidFormat, lp->vpid); break;
|
||||
case VPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, lp->vpid); break;
|
||||
#endif
|
||||
#ifdef HAVE_VSERVER
|
||||
case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break;
|
||||
@ -719,59 +707,58 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
|
||||
return;
|
||||
}
|
||||
case PROC_COMM: {
|
||||
const char* procComm;
|
||||
if (lp->procComm) {
|
||||
attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM];
|
||||
/* 15 being (TASK_COMM_LEN - 1) */
|
||||
xSnprintf(buffer, n, "%-15.15s ", lp->procComm);
|
||||
procComm = lp->procComm;
|
||||
} else {
|
||||
attr = CRT_colors[PROCESS_SHADOW];
|
||||
xSnprintf(buffer, n, "%-15.15s ", Process_isKernelThread(lp) ? kthreadID : "N/A");
|
||||
procComm = Process_isKernelThread(lp) ? kthreadID : "N/A";
|
||||
}
|
||||
break;
|
||||
/* 15 being (TASK_COMM_LEN - 1) */
|
||||
Process_printLeftAlignedField(str, attr, procComm, 15);
|
||||
return;
|
||||
}
|
||||
case PROC_EXE: {
|
||||
const char* procExe;
|
||||
if (lp->procExe) {
|
||||
attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME];
|
||||
if (lp->procExeDeleted)
|
||||
attr = CRT_colors[FAILED_READ];
|
||||
xSnprintf(buffer, n, "%-15.15s ", lp->procExe + lp->procExeBasenameOffset);
|
||||
procExe = lp->procExe + lp->procExeBasenameOffset;
|
||||
} else {
|
||||
attr = CRT_colors[PROCESS_SHADOW];
|
||||
xSnprintf(buffer, n, "%-15.15s ", Process_isKernelThread(lp) ? kthreadID : "N/A");
|
||||
procExe = Process_isKernelThread(lp) ? kthreadID : "N/A";
|
||||
}
|
||||
break;
|
||||
Process_printLeftAlignedField(str, attr, procExe, 15);
|
||||
return;
|
||||
}
|
||||
case CWD:
|
||||
case CWD: {
|
||||
const char* cwd;
|
||||
if (!lp->cwd) {
|
||||
xSnprintf(buffer, n, "%-25s ", "N/A");
|
||||
attr = CRT_colors[PROCESS_SHADOW];
|
||||
cwd = "N/A";
|
||||
} else if (String_startsWith(lp->cwd, "/proc/") && strstr(lp->cwd, " (deleted)") != NULL) {
|
||||
xSnprintf(buffer, n, "%-25s ", "main thread terminated");
|
||||
attr = CRT_colors[PROCESS_SHADOW];
|
||||
cwd = "main thread terminated";
|
||||
} else {
|
||||
xSnprintf(buffer, n, "%-25.25s ", lp->cwd);
|
||||
cwd = lp->cwd;
|
||||
}
|
||||
Process_printLeftAlignedField(str, attr, cwd, 25);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Process_writeField(this, str, field);
|
||||
return;
|
||||
}
|
||||
RichString_append(str, attr, buffer);
|
||||
RichString_appendWide(str, attr, buffer);
|
||||
}
|
||||
|
||||
static long LinuxProcess_compare(const void* v1, const void* v2) {
|
||||
const LinuxProcess *p1, *p2;
|
||||
const Settings *settings = ((const Process*)v1)->settings;
|
||||
static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
|
||||
const LinuxProcess* p1 = (const LinuxProcess*)v1;
|
||||
const LinuxProcess* p2 = (const LinuxProcess*)v2;
|
||||
|
||||
if (settings->direction == 1) {
|
||||
p1 = (const LinuxProcess*)v1;
|
||||
p2 = (const LinuxProcess*)v2;
|
||||
} else {
|
||||
p2 = (const LinuxProcess*)v1;
|
||||
p1 = (const LinuxProcess*)v2;
|
||||
}
|
||||
|
||||
switch ((int)settings->sortKey) {
|
||||
switch (key) {
|
||||
case M_DRS:
|
||||
return SPACESHIP_NUMBER(p2->m_drs, p1->m_drs);
|
||||
case M_DT:
|
||||
@ -857,7 +844,7 @@ static long LinuxProcess_compare(const void* v1, const void* v2) {
|
||||
case CWD:
|
||||
return SPACESHIP_NULLSTR(p1->cwd, p2->cwd);
|
||||
default:
|
||||
return Process_compare(v1, v2);
|
||||
return Process_compareByKey_Base(v1, v2, key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -870,8 +857,9 @@ const ProcessClass LinuxProcess_class = {
|
||||
.extends = Class(Process),
|
||||
.display = Process_display,
|
||||
.delete = Process_delete,
|
||||
.compare = LinuxProcess_compare
|
||||
.compare = Process_compare
|
||||
},
|
||||
.writeField = LinuxProcess_writeField,
|
||||
.getCommandStr = LinuxProcess_getCommandStr
|
||||
.getCommandStr = LinuxProcess_getCommandStr,
|
||||
.compareByKey = LinuxProcess_compareByKey
|
||||
};
|
||||
|
@ -8,10 +8,9 @@ Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "IOPriority.h"
|
||||
#include "Object.h"
|
||||
@ -29,75 +28,8 @@ in the source distribution for its full text.
|
||||
#define PROCESS_FLAG_LINUX_SECATTR 0x00008000
|
||||
#define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000
|
||||
#define PROCESS_FLAG_LINUX_CWD 0x00020000
|
||||
#define PROCESS_FLAG_LINUX_DELAYACCT 0x00040000
|
||||
|
||||
typedef enum UnsupportedProcessFields {
|
||||
FLAGS = 9,
|
||||
ITREALVALUE = 20,
|
||||
VSIZE = 22,
|
||||
RSS = 23,
|
||||
RLIM = 24,
|
||||
STARTCODE = 25,
|
||||
ENDCODE = 26,
|
||||
STARTSTACK = 27,
|
||||
KSTKESP = 28,
|
||||
KSTKEIP = 29,
|
||||
SIGNAL = 30,
|
||||
BLOCKED = 31,
|
||||
SSIGIGNORE = 32,
|
||||
SIGCATCH = 33,
|
||||
WCHAN = 34,
|
||||
NSWAP = 35,
|
||||
CNSWAP = 36,
|
||||
EXIT_SIGNAL = 37,
|
||||
} UnsupportedProcessField;
|
||||
|
||||
typedef enum LinuxProcessFields {
|
||||
CMINFLT = 11,
|
||||
CMAJFLT = 13,
|
||||
UTIME = 14,
|
||||
STIME = 15,
|
||||
CUTIME = 16,
|
||||
CSTIME = 17,
|
||||
M_SHARE = 41,
|
||||
M_TRS = 42,
|
||||
M_DRS = 43,
|
||||
M_LRS = 44,
|
||||
M_DT = 45,
|
||||
#ifdef HAVE_OPENVZ
|
||||
CTID = 100,
|
||||
VPID = 101,
|
||||
#endif
|
||||
#ifdef HAVE_VSERVER
|
||||
VXID = 102,
|
||||
#endif
|
||||
RCHAR = 103,
|
||||
WCHAR = 104,
|
||||
SYSCR = 105,
|
||||
SYSCW = 106,
|
||||
RBYTES = 107,
|
||||
WBYTES = 108,
|
||||
CNCLWB = 109,
|
||||
IO_READ_RATE = 110,
|
||||
IO_WRITE_RATE = 111,
|
||||
IO_RATE = 112,
|
||||
CGROUP = 113,
|
||||
OOM = 114,
|
||||
IO_PRIORITY = 115,
|
||||
#ifdef HAVE_DELAYACCT
|
||||
PERCENT_CPU_DELAY = 116,
|
||||
PERCENT_IO_DELAY = 117,
|
||||
PERCENT_SWAP_DELAY = 118,
|
||||
#endif
|
||||
M_PSS = 119,
|
||||
M_SWAP = 120,
|
||||
M_PSSWP = 121,
|
||||
CTXT = 122,
|
||||
SECATTR = 123,
|
||||
PROC_COMM = 124,
|
||||
PROC_EXE = 125,
|
||||
CWD = 126,
|
||||
LAST_PROCESSFIELD = 127,
|
||||
} LinuxProcessField;
|
||||
|
||||
/* LinuxProcessMergedCommand is populated by LinuxProcess_makeCommandStr: It
|
||||
* contains the merged Command string, and the information needed by
|
||||
@ -192,11 +124,11 @@ static inline bool Process_isUserlandThread(const Process* this) {
|
||||
return this->pid != this->tgid;
|
||||
}
|
||||
|
||||
extern long long btime;
|
||||
extern int pageSize;
|
||||
|
||||
extern ProcessFieldData Process_fields[];
|
||||
extern int pageSizeKB;
|
||||
|
||||
extern ProcessPidColumn Process_pidColumns[];
|
||||
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
|
||||
|
||||
extern const ProcessClass LinuxProcess_class;
|
||||
|
||||
|
@ -13,18 +13,17 @@ in the source distribution for its full text.
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#ifdef HAVE_DELAYACCT
|
||||
#include <linux/netlink.h>
|
||||
@ -58,6 +57,16 @@ in the source distribution for its full text.
|
||||
#endif
|
||||
|
||||
|
||||
// CentOS 6's kernel doesn't provide a definition of O_PATH
|
||||
// based on definition taken from uapi/asm-generic/fcnth.h in Linux kernel tree
|
||||
#ifndef O_PATH
|
||||
# define O_PATH 010000000
|
||||
#endif
|
||||
|
||||
static long long btime;
|
||||
|
||||
static long jiffy;
|
||||
|
||||
static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) {
|
||||
assert(String_eq(mode, "r")); /* only currently supported mode */
|
||||
|
||||
@ -93,7 +102,7 @@ static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
|
||||
|
||||
int numDrivers = 0;
|
||||
int allocd = 10;
|
||||
ttyDrivers = xMalloc(sizeof(TtyDriver) * allocd);
|
||||
ttyDrivers = xMallocArray(allocd, sizeof(TtyDriver));
|
||||
char* at = buf;
|
||||
while (*at != '\0') {
|
||||
at = strchr(at, ' '); // skip first token
|
||||
@ -127,7 +136,7 @@ static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
|
||||
numDrivers++;
|
||||
if (numDrivers == allocd) {
|
||||
allocd += 10;
|
||||
ttyDrivers = xRealloc(ttyDrivers, sizeof(TtyDriver) * allocd);
|
||||
ttyDrivers = xReallocArray(ttyDrivers, allocd, sizeof(TtyDriver));
|
||||
}
|
||||
}
|
||||
numDrivers++;
|
||||
@ -199,39 +208,39 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
|
||||
ProcessList_init(pl, Class(LinuxProcess), usersTable, pidMatchList, userId);
|
||||
LinuxProcessList_initTtyDrivers(this);
|
||||
|
||||
#ifdef HAVE_DELAYACCT
|
||||
LinuxProcessList_initNetlinkSocket(this);
|
||||
#endif
|
||||
// Initialize page size
|
||||
pageSize = sysconf(_SC_PAGESIZE);
|
||||
if (pageSize == -1)
|
||||
CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)");
|
||||
pageSizeKB = pageSize / ONE_K;
|
||||
|
||||
// Check for /proc/*/smaps_rollup availability (improves smaps parsing speed, Linux 4.14+)
|
||||
FILE* file = fopen(PROCDIR "/self/smaps_rollup", "r");
|
||||
if (file != NULL) {
|
||||
this->haveSmapsRollup = true;
|
||||
fclose(file);
|
||||
} else {
|
||||
this->haveSmapsRollup = false;
|
||||
}
|
||||
// Initialize clock ticks
|
||||
jiffy = sysconf(_SC_CLK_TCK);
|
||||
if (jiffy == -1)
|
||||
CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)");
|
||||
|
||||
// Read btime
|
||||
// Test /proc/PID/smaps_rollup availability (faster to parse, Linux 4.14+)
|
||||
this->haveSmapsRollup = (access(PROCDIR "/self/smaps_rollup", R_OK) == 0);
|
||||
|
||||
// Read btime (the kernel boot time, as number of seconds since the epoch)
|
||||
{
|
||||
FILE* statfile = fopen(PROCSTATFILE, "r");
|
||||
if (statfile == NULL) {
|
||||
if (statfile == NULL)
|
||||
CRT_fatalError("Cannot open " PROCSTATFILE);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
char buffer[PROC_LINE_LENGTH + 1];
|
||||
if (fgets(buffer, sizeof(buffer), statfile) == NULL) {
|
||||
CRT_fatalError("No btime in " PROCSTATFILE);
|
||||
} else if (String_startsWith(buffer, "btime ")) {
|
||||
if (sscanf(buffer, "btime %lld\n", &btime) != 1) {
|
||||
if (fgets(buffer, sizeof(buffer), statfile) == NULL)
|
||||
break;
|
||||
if (String_startsWith(buffer, "btime ") == false)
|
||||
continue;
|
||||
if (sscanf(buffer, "btime %lld\n", &btime) == 1)
|
||||
break;
|
||||
CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(statfile);
|
||||
|
||||
if (!btime)
|
||||
CRT_fatalError("No btime in " PROCSTATFILE);
|
||||
}
|
||||
|
||||
// Initialize CPU count
|
||||
@ -268,16 +277,7 @@ void ProcessList_delete(ProcessList* pl) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) {
|
||||
static long jiffy = -1;
|
||||
if (jiffy == -1) {
|
||||
errno = 0;
|
||||
jiffy = sysconf(_SC_CLK_TCK);
|
||||
if (errno || -1 == jiffy) {
|
||||
jiffy = -1;
|
||||
return t; // Assume 100Hz clock
|
||||
}
|
||||
}
|
||||
static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long t) {
|
||||
return t * 100 / jiffy;
|
||||
}
|
||||
|
||||
@ -330,13 +330,13 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
|
||||
location += 1;
|
||||
lp->cmajflt = strtoull(location, &location, 10);
|
||||
location += 1;
|
||||
lp->utime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
|
||||
lp->utime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
|
||||
location += 1;
|
||||
lp->stime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
|
||||
lp->stime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
|
||||
location += 1;
|
||||
lp->cutime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
|
||||
lp->cutime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
|
||||
location += 1;
|
||||
lp->cstime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
|
||||
lp->cstime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
|
||||
location += 1;
|
||||
process->priority = strtol(location, &location, 10);
|
||||
location += 1;
|
||||
@ -346,7 +346,7 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
|
||||
location += 1;
|
||||
location = strchr(location, ' ') + 1;
|
||||
if (process->starttime_ctime == 0) {
|
||||
process->starttime_ctime = btime + LinuxProcess_adjustTime(strtoll(location, &location, 10)) / 100;
|
||||
process->starttime_ctime = btime + LinuxProcessList_adjustTime(strtoll(location, &location, 10)) / 100;
|
||||
} else {
|
||||
location = strchr(location, ' ') + 1;
|
||||
}
|
||||
@ -437,7 +437,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t proc
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct LibraryData {
|
||||
typedef struct LibraryData_ {
|
||||
uint64_t size;
|
||||
bool exec;
|
||||
} LibraryData;
|
||||
@ -484,7 +484,7 @@ static inline uint64_t fast_strtoull_hex(char **str, int maxlen) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED hkey_t key, void* value, void* data) {
|
||||
static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void* value, void* data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
@ -574,7 +574,7 @@ static uint64_t LinuxProcessList_calcLibSize(openat_arg_t procFd) {
|
||||
|
||||
Hashtable_delete(ht);
|
||||
|
||||
return total_size / CRT_pageSize;
|
||||
return total_size / pageSize;
|
||||
}
|
||||
|
||||
static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, bool performLookup, unsigned long long now) {
|
||||
@ -594,6 +594,9 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t p
|
||||
fclose(statmfile);
|
||||
|
||||
if (r == 7) {
|
||||
process->super.m_virt *= pageSizeKB;
|
||||
process->super.m_resident *= pageSizeKB;
|
||||
|
||||
if (tmp_m_lrs) {
|
||||
process->m_lrs = tmp_m_lrs;
|
||||
} else if (performLookup) {
|
||||
@ -883,7 +886,7 @@ static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t
|
||||
}
|
||||
|
||||
static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd) {
|
||||
char pathBuffer[PATH_MAX + 1];
|
||||
char pathBuffer[PATH_MAX + 1] = {0};
|
||||
|
||||
#if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT)
|
||||
ssize_t r = readlinkat(procFd, "cwd", pathBuffer, sizeof(pathBuffer) - 1);
|
||||
@ -892,6 +895,7 @@ static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd)
|
||||
xSnprintf(filename, sizeof(filename), "%s/cwd", procFd);
|
||||
ssize_t r = readlink(filename, pathBuffer, sizeof(pathBuffer) - 1);
|
||||
#endif
|
||||
|
||||
if (r < 0) {
|
||||
free(process->cwd);
|
||||
process->cwd = NULL;
|
||||
@ -947,12 +951,19 @@ static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
|
||||
static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) {
|
||||
struct nl_msg* msg;
|
||||
|
||||
if (!this->netlink_socket) {
|
||||
LinuxProcessList_initNetlinkSocket(this);
|
||||
if (!this->netlink_socket) {
|
||||
goto delayacct_failure;
|
||||
}
|
||||
}
|
||||
|
||||
if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
|
||||
return;
|
||||
goto delayacct_failure;
|
||||
}
|
||||
|
||||
if (! (msg = nlmsg_alloc())) {
|
||||
return;
|
||||
goto delayacct_failure;
|
||||
}
|
||||
|
||||
if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) {
|
||||
@ -964,15 +975,19 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
|
||||
}
|
||||
|
||||
if (nl_send_sync(this->netlink_socket, msg) < 0) {
|
||||
process->swapin_delay_percent = NAN;
|
||||
process->blkio_delay_percent = NAN;
|
||||
process->cpu_delay_percent = NAN;
|
||||
return;
|
||||
goto delayacct_failure;
|
||||
}
|
||||
|
||||
if (nl_recvmsgs_default(this->netlink_socket) < 0) {
|
||||
return;
|
||||
goto delayacct_failure;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
delayacct_failure:
|
||||
process->swapin_delay_percent = NAN;
|
||||
process->blkio_delay_percent = NAN;
|
||||
process->cpu_delay_percent = NAN;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1363,9 +1378,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
|
||||
}
|
||||
|
||||
/* period might be 0 after system sleep */
|
||||
float percent_cpu = (period < 1e-6) ? 0.0f : ((lp->utime + lp->stime - lasttimes) / period * 100.0);
|
||||
proc->percent_cpu = CLAMP(percent_cpu, 0.0f, cpus * 100.0f);
|
||||
proc->percent_mem = (proc->m_resident * CRT_pageSizeKB) / (double)(pl->totalMem) * 100.0;
|
||||
float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0);
|
||||
proc->percent_cpu = CLAMP(percent_cpu, 0.0F, cpus * 100.0F);
|
||||
proc->percent_mem = proc->m_resident / (double)(pl->totalMem) * 100.0;
|
||||
|
||||
if (!preExisting) {
|
||||
|
||||
@ -1411,7 +1426,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
|
||||
}
|
||||
|
||||
#ifdef HAVE_DELAYACCT
|
||||
if (settings->flags & PROCESS_FLAG_LINUX_DELAYACCT) {
|
||||
LinuxProcessList_readDelayAcctData(this, lp);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) {
|
||||
@ -1785,7 +1802,9 @@ static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) {
|
||||
continue;
|
||||
} else if (
|
||||
(sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
|
||||
(sscanf(buffer, "cpu MHz: %lf", &frequency) == 1)
|
||||
(sscanf(buffer, "cpu MHz: %lf", &frequency) == 1) ||
|
||||
(sscanf(buffer, "clock : %lfMHz", &frequency) == 1) ||
|
||||
(sscanf(buffer, "clock: %lfMHz", &frequency) == 1)
|
||||
) {
|
||||
if (cpuid < 0 || cpuid > (cpus - 1)) {
|
||||
continue;
|
||||
@ -1824,41 +1843,6 @@ static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
|
||||
scanCPUFreqencyFromCPUinfo(this);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
static void LinuxProcessList_scanCPUTemperature(LinuxProcessList* this) {
|
||||
const int cpuCount = this->super.cpuCount;
|
||||
|
||||
for (int i = 0; i <= cpuCount; i++) {
|
||||
this->cpus[i].temperature = NAN;
|
||||
}
|
||||
|
||||
int r = LibSensors_getCPUTemperatures(this->cpus, cpuCount);
|
||||
|
||||
/* No temperature - nothing to do */
|
||||
if (r <= 0)
|
||||
return;
|
||||
|
||||
/* Only package temperature - copy to all cpus */
|
||||
if (r == 1 && !isnan(this->cpus[0].temperature)) {
|
||||
double packageTemp = this->cpus[0].temperature;
|
||||
for (int i = 1; i <= cpuCount; i++) {
|
||||
this->cpus[i].temperature = packageTemp;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Half the temperatures, probably HT/SMT - copy to second half */
|
||||
if (r >= 2 && (r - 1) == (cpuCount / 2)) {
|
||||
for (int i = cpuCount / 2 + 1; i <= cpuCount; i++) {
|
||||
this->cpus[i].temperature = this->cpus[i/2].temperature;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
|
||||
LinuxProcessList* this = (LinuxProcessList*) super;
|
||||
const Settings* settings = super->settings;
|
||||
@ -1876,7 +1860,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
|
||||
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
if (settings->showCPUTemperature)
|
||||
LinuxProcessList_scanCPUTemperature(this);
|
||||
LibSensors_getCPUTemperatures(this->cpus, this->super.cpuCount);
|
||||
#endif
|
||||
|
||||
// in pause mode only gather global data for meters (CPU/memory/...)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user