1 Commits
3.0.1 ... 0.6.3

Author SHA1 Message Date
4300897d0c Tag release 0.6.3 in revision history 2006-07-23 23:21:07 +00:00
204 changed files with 5972 additions and 21761 deletions

View File

@ -1,17 +0,0 @@
# EditorConfig configuration for htop
# http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file, utf-8 charset
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
# match C source and header files, set indent to three spaces
[*.{c,h}]
indent_style = space
indent_size = 3
trim_trailing_whitespace = true

View File

@ -1,63 +0,0 @@
name: CI
on: [ push, pull_request ]
jobs:
build-ubuntu-latest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: sudo apt-get install libncursesw5-dev
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror
- name: Build
run: make
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
build-ubuntu-clang-latest:
runs-on: ubuntu-latest
env:
CC: clang-10
steps:
- uses: actions/checkout@v2
- name: install clang repo
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' -y
sudo apt-get update -q
- name: Install Dependencies
run: sudo apt-get install clang-10 libncursesw5-dev
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror
- name: Build
run: make
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
build-ubuntu-latest-full-featured:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: sudo apt-get install libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror --enable-openvz --enable-cgroup --enable-vserver --enable-ancient-vserver --enable-taskstats --enable-unicode --enable-linux-affinity --enable-hwloc --enable-setuid --enable-delayacct
- name: Build
run: make
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-cgroup --enable-vserver --enable-ancient-vserver --enable-taskstats --enable-unicode --enable-linux-affinity --enable-hwloc --enable-setuid --enable-delayacct'
whitespace_check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: check-whitespaces
run: git diff-tree --check $(git hash-object -t tree /dev/null) HEAD

43
.gitignore vendored
View File

@ -1,43 +0,0 @@
# the binary:
htop
# all object files
*.o
# skip all backups
*.bak
*~
.*.sw?
# skip coverage files
*.gcda
*/*.gcda
*.gcno
*/*.gcno
*.h.gch
*/.dirstamp
.deps/
Makefile
Makefile.in
INSTALL
aclocal.m4
autom4te.cache/
compile
config.guess
config.h
config.h.in
config.log
config.status
config.cache
config.sub
configure
depcomp
htop.1
install-sh
libtool
ltmain.sh
m4/
missing
scripts/MakeHeader.py
stamp-h1

View File

@ -1,11 +0,0 @@
language: c
compiler:
- clang
- gcc
os:
- linux
- osx
script: ./autogen.sh && ./configure && make

619
Action.c
View File

@ -1,619 +0,0 @@
/*
htop - Action.c
(C) 2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h"
#include "Action.h"
#include "Affinity.h"
#include "AffinityPanel.h"
#include "CategoriesPanel.h"
#include "CRT.h"
#include "EnvScreen.h"
#include "MainPanel.h"
#include "OpenFilesScreen.h"
#include "Process.h"
#include "ScreenManager.h"
#include "SignalsPanel.h"
#include "StringUtils.h"
#include "TraceScreen.h"
#include "Platform.h"
#include <ctype.h>
#include <math.h>
#include <pwd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/param.h>
#include <sys/time.h>
/*{
#include "IncSet.h"
#include "Settings.h"
#include "Header.h"
#include "UsersTable.h"
#include "ProcessList.h"
#include "Panel.h"
typedef enum {
HTOP_OK = 0x00,
HTOP_REFRESH = 0x01,
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH
HTOP_SAVE_SETTINGS = 0x04,
HTOP_KEEP_FOLLOWING = 0x08,
HTOP_QUIT = 0x10,
HTOP_REDRAW_BAR = 0x20,
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
} Htop_Reaction;
typedef Htop_Reaction (*Htop_Action)();
typedef struct State_ {
Settings* settings;
UsersTable* ut;
ProcessList* pl;
Panel* panel;
Header* header;
} State;
}*/
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) {
Panel* panel = st->panel;
Header* header = st->header;
Settings* settings = st->settings;
int y = panel->y;
ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, false);
scr->allowFocusChange = false;
ScreenManager_add(scr, list, x - 1);
ScreenManager_add(scr, panel, -1);
Panel* panelFocus;
int ch;
bool unfollow = false;
int pid = followProcess ? MainPanel_selectedPid((MainPanel*)panel) : -1;
if (followProcess && header->pl->following == -1) {
header->pl->following = pid;
unfollow = true;
}
ScreenManager_run(scr, &panelFocus, &ch);
if (unfollow) {
header->pl->following = -1;
}
ScreenManager_delete(scr);
Panel_move(panel, 0, y);
Panel_resize(panel, COLS, LINES-y-1);
if (panelFocus == list && ch == 13) {
if (followProcess) {
Process* selected = (Process*)Panel_getSelected(panel);
if (selected && selected->pid == pid)
return Panel_getSelected(list);
else
beep();
} else {
return Panel_getSelected(list);
}
}
return NULL;
}
// ----------------------------------------
static void Action_runSetup(Settings* settings, const Header* header, ProcessList* pl) {
ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, true);
CategoriesPanel* panelCategories = CategoriesPanel_new(scr, settings, (Header*) header, pl);
ScreenManager_add(scr, (Panel*) panelCategories, 16);
CategoriesPanel_makeMetersPage(panelCategories);
Panel* panelFocus;
int ch;
ScreenManager_run(scr, &panelFocus, &ch);
ScreenManager_delete(scr);
if (settings->changed) {
Header_writeBackToSettings(header);
}
}
static bool changePriority(MainPanel* panel, int delta) {
bool anyTagged;
bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, (Arg){ .i = delta }, &anyTagged);
if (!ok)
beep();
return anyTagged;
}
static void addUserToVector(int key, void* userCast, void* panelCast) {
char* user = (char*) userCast;
Panel* panel = (Panel*) panelCast;
Panel_add(panel, (Object*) ListItem_new(user, key));
}
bool Action_setUserOnly(const char* userName, uid_t* userId) {
struct passwd* user = getpwnam(userName);
if (user) {
*userId = user->pw_uid;
return true;
}
*userId = -1;
return false;
}
static void tagAllChildren(Panel* panel, Process* parent) {
parent->tag = true;
pid_t ppid = parent->pid;
for (int i = 0; i < Panel_size(panel); i++) {
Process* p = (Process*) Panel_get(panel, i);
if (!p->tag && Process_isChildOf(p, ppid)) {
tagAllChildren(panel, p);
}
}
}
static bool expandCollapse(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false;
p->showChildren = !p->showChildren;
return true;
}
static bool collapseIntoParent(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false;
pid_t ppid = Process_getParentPid(p);
for (int i = 0; i < Panel_size(panel); i++) {
Process* q = (Process*) Panel_get(panel, i);
if (q->pid == ppid) {
q->showChildren = false;
Panel_setSelected(panel, i);
return true;
}
}
return false;
}
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey;
settings->direction = 1;
settings->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction sortBy(State* st) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), 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)
Panel_setSelected(sortPanel, i);
free(name);
}
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false);
if (field) {
reaction |= Action_setSortKey(st->settings, field->key);
}
Object_delete(sortPanel);
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 actionSortByMemory(State* st) {
return Action_setSortKey(st->settings, PERCENT_MEM);
}
static Htop_Reaction actionSortByCPU(State* st) {
return Action_setSortKey(st->settings, PERCENT_CPU);
}
static Htop_Reaction actionSortByTime(State* st) {
return Action_setSortKey(st->settings, TIME);
}
static Htop_Reaction actionToggleKernelThreads(State* st) {
st->settings->hideKernelThreads = !st->settings->hideKernelThreads;
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionToggleUserlandThreads(State* st) {
st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads;
st->settings->hideThreads = st->settings->hideUserlandThreads;
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionToggleProgramPath(State* st) {
st->settings->showProgramPath = !st->settings->showProgramPath;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView;
if (st->settings->treeView) st->settings->direction = 1;
ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionIncFilter(State* st) {
IncSet* inc = ((MainPanel*)st->panel)->inc;
IncSet_activate(inc, INC_FILTER, st->panel);
st->pl->incFilter = IncSet_filter(inc);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionIncSearch(State* st) {
IncSet_reset(((MainPanel*)st->panel)->inc, INC_SEARCH);
IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel);
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;
}
static Htop_Reaction actionLowerPriority(State* st) {
bool changed = changePriority((MainPanel*)st->panel, 1);
return changed ? HTOP_REFRESH : HTOP_OK;
}
static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionSetSortColumn(State* st) {
return sortBy(st);
}
static Htop_Reaction actionExpandOrCollapse(State* st) {
bool changed = expandCollapse(st->panel);
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionCollapseIntoParent(State* st) {
if (!st->settings->treeView) {
return HTOP_OK;
}
bool changed = collapseIntoParent(st->panel);
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}
static Htop_Reaction actionQuit() {
return HTOP_QUIT;
}
static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1)
return HTOP_OK;
#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
Panel* panel = st->panel;
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK;
Affinity* affinity1 = Affinity_get(p, st->pl);
if (!affinity1) return HTOP_OK;
int width;
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width);
width += 1; /* we add a gap between the panels */
Affinity_delete(affinity1);
void* set = Action_pickFromVector(st, affinityPanel, width, true);
if (set) {
Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity2 }, NULL);
if (!ok) beep();
Affinity_delete(affinity2);
}
Panel_delete((Object*)affinityPanel);
#endif
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionKill(State* st) {
Panel* signalsPanel = (Panel*) SignalsPanel_new();
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true);
if (sgn) {
if (sgn->key != 0) {
Panel_setHeader(st->panel, "Sending...");
Panel_draw(st->panel, true);
refresh();
MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (Arg){ .i = sgn->key }, NULL);
napms(500);
}
}
Panel_delete((Object*)signalsPanel);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionFilterByUser(State* st) {
Panel* usersPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Show ", "Cancel "));
Panel_setHeader(usersPanel, "Show processes of:");
UsersTable_foreach(st->ut, addUserToVector, usersPanel);
Vector_insertionSort(usersPanel->items);
ListItem* allUsers = ListItem_new("All users", -1);
Panel_insert(usersPanel, 0, (Object*) allUsers);
ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false);
if (picked) {
if (picked == allUsers) {
st->pl->userId = -1;
} else {
Action_setUserOnly(ListItem_getRef(picked), &(st->pl->userId));
}
}
Panel_delete((Object*)usersPanel);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
Htop_Reaction Action_follow(State* st) {
st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel);
Panel_setSelectionColor(st->panel, CRT_colors[PANEL_SELECTION_FOLLOW]);
return HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionSetup(State* st) {
Action_runSetup(st->settings, st->header, st->pl);
// TODO: shouldn't need this, colors should be dynamic
int headerHeight = Header_calculateHeight(st->header);
Panel_move(st->panel, 0, headerHeight);
Panel_resize(st->panel, COLS, LINES-headerHeight-1);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionLsof(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
OpenFilesScreen* ofs = OpenFilesScreen_new(p);
InfoScreen_run((InfoScreen*)ofs);
OpenFilesScreen_delete((Object*)ofs);
clear();
CRT_enableDelay();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static Htop_Reaction actionStrace(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
TraceScreen* ts = TraceScreen_new(p);
bool ok = TraceScreen_forkTracer(ts);
if (ok) {
InfoScreen_run((InfoScreen*)ts);
}
TraceScreen_delete((Object*)ts);
clear();
CRT_enableDelay();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static Htop_Reaction actionTag(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
Process_toggleTag(p);
Panel_onKey(st->panel, KEY_DOWN);
return HTOP_OK;
}
static Htop_Reaction actionRedraw() {
clear();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static const struct { const char* key; const char* info; } helpLeft[] = {
{ .key = " Arrows: ", .info = "scroll process list" },
{ .key = " Digits: ", .info = "incremental PID search" },
{ .key = " F3 /: ", .info = "incremental name search" },
{ .key = " F4 \\: ",.info = "incremental name filtering" },
{ .key = " F5 t: ", .info = "tree view" },
{ .key = " p: ", .info = "toggle program path" },
{ .key = " u: ", .info = "show processes of a single user" },
{ .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 = " I: ", .info = "invert sort order" },
{ .key = " F6 > .: ", .info = "select sort column" },
{ .key = NULL, .info = NULL }
};
static const struct { const char* key; const char* info; } helpRight[] = {
{ .key = " Space: ", .info = "tag process" },
{ .key = " c: ", .info = "tag process and its children" },
{ .key = " U: ", .info = "untag all processes" },
{ .key = " F9 k: ", .info = "kill process/tagged processes" },
{ .key = " F7 ]: ", .info = "higher priority (root only)" },
{ .key = " F8 [: ", .info = "lower priority (+ nice)" },
#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
{ .key = " a: ", .info = "set CPU affinity" },
#endif
{ .key = " e: ", .info = "show process environment" },
{ .key = " i: ", .info = "set IO priority" },
{ .key = " l: ", .info = "list open files with lsof" },
{ .key = " s: ", .info = "trace syscalls with strace" },
{ .key = " ", .info = "" },
{ .key = " F2 C S: ", .info = "setup" },
{ .key = " F1 h: ", .info = "show this help screen" },
{ .key = " F10 q: ", .info = "quit" },
{ .key = NULL, .info = NULL }
};
static Htop_Reaction actionHelp(State* st) {
Settings* settings = st->settings;
clear();
attrset(CRT_colors[HELP_BOLD]);
for (int i = 0; i < LINES-1; i++)
mvhline(i, 0, ' ', COLS);
mvaddstr(0, 0, "htop " VERSION " - " COPYRIGHT);
mvaddstr(1, 0, "Released under the GNU GPL. See 'man' page for more info.");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(3, 0, "CPU usage bar: ");
#define addattrstr(a,s) attrset(a);addstr(s)
addattrstr(CRT_colors[BAR_BORDER], "[");
if (settings->detailedCPUTime) {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/");
addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/");
addattrstr(CRT_colors[CPU_IOWAIT], "io-wait");
addattrstr(CRT_colors[BAR_SHADOW], " used%");
} else {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_GUEST], "virtualiz");
addattrstr(CRT_colors[BAR_SHADOW], " used%");
}
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(4, 0, "Memory bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/");
addattrstr(CRT_colors[MEMORY_CACHE], "cache");
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(5, 0, "Swap bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[SWAP], "used");
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(6,0, "Type and layout of header meters are configurable in the setup screen.");
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
mvaddstr(7, 0, "In monochrome, meters display as different chars, in order: |#*@$%&.");
}
mvaddstr( 8, 0, " Status: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep");
for (int i = 0; helpLeft[i].info; i++) { mvaddstr(9+i, 9, helpLeft[i].info); }
for (int i = 0; helpRight[i].info; i++) { mvaddstr(9+i, 49, helpRight[i].info); }
attrset(CRT_colors[HELP_BOLD]);
for (int i = 0; helpLeft[i].key; i++) { mvaddstr(9+i, 0, helpLeft[i].key); }
for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); }
attrset(CRT_colors[PROCESS_THREAD]);
mvaddstr(16, 32, "threads");
mvaddstr(17, 26, "threads");
attrset(CRT_colors[DEFAULT_COLOR]);
attrset(CRT_colors[HELP_BOLD]);
mvaddstr(23,0, "Press any key to return.");
attrset(CRT_colors[DEFAULT_COLOR]);
refresh();
CRT_readKey();
clear();
return HTOP_RECALCULATE | HTOP_REDRAW_BAR;
}
static Htop_Reaction actionUntagAll(State* st) {
for (int i = 0; i < Panel_size(st->panel); i++) {
Process* p = (Process*) Panel_get(st->panel, i);
p->tag = false;
}
return HTOP_REFRESH;
}
static Htop_Reaction actionTagAllChildren(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
tagAllChildren(st->panel, p);
return HTOP_OK;
}
static Htop_Reaction actionShowEnvScreen(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
EnvScreen* es = EnvScreen_new(p);
InfoScreen_run((InfoScreen*)es);
EnvScreen_delete((Object*)es);
clear();
CRT_enableDelay();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
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['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)] = actionExpandCollapseOrSortColumn;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys['<'] = actionSetSortColumn;
keys[','] = actionSetSortColumn;
keys['>'] = actionSetSortColumn;
keys['.'] = actionSetSortColumn;
keys[KEY_F(10)] = actionQuit;
keys['q'] = actionQuit;
keys['a'] = actionSetAffinity;
keys[KEY_F(9)] = actionKill;
keys['k'] = actionKill;
keys[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['l'] = actionLsof;
keys['s'] = actionStrace;
keys[' '] = actionTag;
keys['\014'] = actionRedraw; // Ctrl+L
keys[KEY_F(1)] = actionHelp;
keys['h'] = actionHelp;
keys['?'] = actionHelp;
keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
}

View File

@ -1,57 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Action
#define HEADER_Action
/*
htop - Action.h
(C) 2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "IncSet.h"
#include "Settings.h"
#include "Header.h"
#include "UsersTable.h"
#include "ProcessList.h"
#include "Panel.h"
typedef enum {
HTOP_OK = 0x00,
HTOP_REFRESH = 0x01,
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH
HTOP_SAVE_SETTINGS = 0x04,
HTOP_KEEP_FOLLOWING = 0x08,
HTOP_QUIT = 0x10,
HTOP_REDRAW_BAR = 0x20,
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
} Htop_Reaction;
typedef Htop_Reaction (*Htop_Action)();
typedef struct State_ {
Settings* settings;
UsersTable* ut;
ProcessList* pl;
Panel* panel;
Header* header;
} State;
extern Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess);
// ----------------------------------------
extern bool Action_setUserOnly(const char* userName, uid_t* userId);
extern Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
// ----------------------------------------
extern Htop_Reaction Action_follow(State* st);
extern void Action_setBindings(Htop_Action* keys);
#endif

View File

@ -1,119 +0,0 @@
/*
htop - Affinity.c
(C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Affinity.h"
#include <stdlib.h>
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
#if __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif
#elif HAVE_LINUX_AFFINITY
#include <sched.h>
#endif
/*{
#include "Process.h"
#include "ProcessList.h"
typedef struct Affinity_ {
ProcessList* pl;
int size;
int used;
int* cpus;
} Affinity;
}*/
Affinity* Affinity_new(ProcessList* pl) {
Affinity* this = xCalloc(1, sizeof(Affinity));
this->size = 8;
this->cpus = xCalloc(this->size, sizeof(int));
this->pl = pl;
return this;
}
void Affinity_delete(Affinity* this) {
free(this->cpus);
free(this);
}
void Affinity_add(Affinity* this, int id) {
if (this->used == this->size) {
this->size *= 2;
this->cpus = xRealloc(this->cpus, sizeof(int) * this->size);
}
this->cpus[this->used] = id;
this->used++;
}
#ifdef HAVE_LIBHWLOC
Affinity* Affinity_get(Process* proc, ProcessList* pl) {
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
bool ok = (hwloc_get_proc_cpubind(pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
Affinity* affinity = NULL;
if (ok) {
affinity = Affinity_new(pl);
if (hwloc_bitmap_last(cpuset) == -1) {
for (int i = 0; i < pl->cpuCount; i++) {
Affinity_add(affinity, i);
}
} else {
unsigned int id;
hwloc_bitmap_foreach_begin(id, cpuset);
Affinity_add(affinity, id);
hwloc_bitmap_foreach_end();
}
}
hwloc_bitmap_free(cpuset);
return affinity;
}
bool Affinity_set(Process* proc, Arg arg) {
Affinity *this = arg.v;
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
for (int i = 0; i < this->used; i++) {
hwloc_bitmap_set(cpuset, this->cpus[i]);
}
bool ok = (hwloc_set_proc_cpubind(this->pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
hwloc_bitmap_free(cpuset);
return ok;
}
#elif HAVE_LINUX_AFFINITY
Affinity* Affinity_get(Process* proc, ProcessList* pl) {
cpu_set_t cpuset;
bool ok = (sched_getaffinity(proc->pid, sizeof(cpu_set_t), &cpuset) == 0);
if (!ok) return NULL;
Affinity* affinity = Affinity_new(pl);
for (int i = 0; i < pl->cpuCount; i++) {
if (CPU_ISSET(i, &cpuset))
Affinity_add(affinity, i);
}
return affinity;
}
bool Affinity_set(Process* proc, Arg arg) {
Affinity *this = arg.v;
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
for (int i = 0; i < this->used; i++) {
CPU_SET(this->cpus[i], &cpuset);
}
bool ok = (sched_setaffinity(proc->pid, sizeof(unsigned long), &cpuset) == 0);
return ok;
}
#endif

View File

@ -1,53 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Affinity
#define HEADER_Affinity
/*
htop - Affinity.h
(C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#ifdef HAVE_LIBHWLOC
#if __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif
#elif HAVE_LINUX_AFFINITY
#endif
#include "Process.h"
#include "ProcessList.h"
typedef struct Affinity_ {
ProcessList* pl;
int size;
int used;
int* cpus;
} Affinity;
extern Affinity* Affinity_new(ProcessList* pl);
extern void Affinity_delete(Affinity* this);
extern void Affinity_add(Affinity* this, int id);
#ifdef HAVE_LIBHWLOC
extern Affinity* Affinity_get(Process* proc, ProcessList* pl);
extern bool Affinity_set(Process* proc, Arg arg);
#elif HAVE_LINUX_AFFINITY
extern Affinity* Affinity_get(Process* proc, ProcessList* pl);
extern bool Affinity_set(Process* proc, Arg arg);
#endif
#endif

View File

@ -1,425 +0,0 @@
/*
htop - AffinityPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "AffinityPanel.h"
#include "CRT.h"
#include "Vector.h"
#include <assert.h>
#include <string.h>
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
#endif
/*{
#include "Panel.h"
#include "Affinity.h"
#include "ProcessList.h"
}*/
typedef struct MaskItem_ {
Object super;
const char* text;
const char* indent; /* used also as an condition whether this is a tree node */
int value; /* tri-state: 0 - off, 1 - some set, 2 - all set */
int sub_tree; /* tri-state: 0 - no sub-tree, 1 - open sub-tree, 2 - closed sub-tree */
Vector *children;
#ifdef HAVE_LIBHWLOC
bool ownCpuset;
hwloc_bitmap_t cpuset;
#else
int cpu;
#endif
} MaskItem;
static void MaskItem_delete(Object* cast) {
MaskItem* this = (MaskItem*) cast;
free((void*)this->text);
if (this->indent)
free((void*)this->indent);
Vector_delete(this->children);
#ifdef HAVE_LIBHWLOC
if (this->ownCpuset)
hwloc_bitmap_free(this->cpuset);
#endif
free(this);
}
static void MaskItem_display(Object* cast, RichString* out) {
MaskItem* this = (MaskItem*)cast;
assert (this != NULL);
RichString_append(out, CRT_colors[CHECK_BOX], "[");
if (this->value == 2)
RichString_append(out, CRT_colors[CHECK_MARK], "x");
else if (this->value == 1)
RichString_append(out, CRT_colors[CHECK_MARK], "o");
else
RichString_append(out, CRT_colors[CHECK_MARK], " ");
RichString_append(out, CRT_colors[CHECK_BOX], "]");
RichString_append(out, CRT_colors[CHECK_TEXT], " ");
if (this->indent) {
RichString_append(out, CRT_colors[PROCESS_TREE], this->indent);
RichString_append(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_append(out, CRT_colors[CHECK_TEXT], this->text);
}
static ObjectClass MaskItem_class = {
.display = MaskItem_display,
.delete = MaskItem_delete
};
#ifdef HAVE_LIBHWLOC
static MaskItem* MaskItem_newMask(const char* text, const char* indent, hwloc_bitmap_t cpuset, bool owner) {
MaskItem* this = AllocThis(MaskItem);
this->text = xStrdup(text);
this->indent = xStrdup(indent); /* nonnull for tree node */
this->value = 0;
this->ownCpuset = owner;
this->cpuset = cpuset;
this->sub_tree = hwloc_bitmap_weight(cpuset) > 1 ? 1 : 0;
this->children = Vector_new(Class(MaskItem), true, DEFAULT_SIZE);
return this;
}
#endif
static MaskItem* MaskItem_newSingleton(const char* text, int cpu, bool isSet) {
MaskItem* this = AllocThis(MaskItem);
this->text = xStrdup(text);
this->indent = NULL; /* not a tree node */
this->sub_tree = 0;
this->children = Vector_new(Class(MaskItem), true, DEFAULT_SIZE);
#ifdef HAVE_LIBHWLOC
this->ownCpuset = true;
this->cpuset = hwloc_bitmap_alloc();
hwloc_bitmap_set(this->cpuset, cpu);
(void)isSet;
#else
this->cpu = cpu;
#endif
this->value = 2 * isSet;
return this;
}
typedef struct AffinityPanel_ {
Panel super;
ProcessList* pl;
bool topoView;
Vector *cpuids;
unsigned width;
#ifdef HAVE_LIBHWLOC
MaskItem *topoRoot;
hwloc_const_cpuset_t allCpuset;
hwloc_bitmap_t workCpuset;
#endif
} AffinityPanel;
static void AffinityPanel_delete(Object* cast) {
AffinityPanel* this = (AffinityPanel*) cast;
Panel* super = (Panel*) this;
Panel_done(super);
Vector_delete(this->cpuids);
#ifdef HAVE_LIBHWLOC
hwloc_bitmap_free(this->workCpuset);
MaskItem_delete((Object*) this->topoRoot);
#endif
free(this);
}
#ifdef HAVE_LIBHWLOC
static void AffinityPanel_updateItem(AffinityPanel* this, MaskItem* item) {
Panel* super = (Panel*) this;
item->value = hwloc_bitmap_isincluded(item->cpuset, this->workCpuset) ? 2 :
hwloc_bitmap_intersects(item->cpuset, this->workCpuset) ? 1 : 0;
Panel_add(super, (Object*) item);
}
static void AffinityPanel_updateTopo(AffinityPanel* this, MaskItem* item) {
AffinityPanel_updateItem(this, item);
if (item->sub_tree == 2)
return;
for (int i = 0; i < Vector_size(item->children); i++)
AffinityPanel_updateTopo(this, (MaskItem*) Vector_get(item->children, i));
}
#endif
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, NULL);
int oldSelected = Panel_getSelectedIndex(super);
Panel_prune(super);
#ifdef HAVE_LIBHWLOC
if (this->topoView)
AffinityPanel_updateTopo(this, this->topoRoot);
else {
for (int i = 0; i < Vector_size(this->cpuids); i++)
AffinityPanel_updateItem(this, (MaskItem*) Vector_get(this->cpuids, i));
}
#else
Panel_splice(super, this->cpuids);
#endif
if (keepSelected)
Panel_setSelected(super, oldSelected);
super->needsRedraw = true;
}
static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) {
AffinityPanel* this = (AffinityPanel*) super;
HandlerResult result = IGNORED;
MaskItem* selected = (MaskItem*) Panel_getSelected(super);
bool keepSelected = true;
switch(ch) {
case KEY_MOUSE:
case KEY_RECLICK:
case ' ':
#ifdef HAVE_LIBHWLOC
if (selected->value == 2) {
/* Item was selected, so remove this mask from the top cpuset. */
hwloc_bitmap_andnot(this->workCpuset, this->workCpuset, selected->cpuset);
selected->value = 0;
} else {
/* Item was not or only partial selected, so set all bits from this object
in the top cpuset. */
hwloc_bitmap_or(this->workCpuset, this->workCpuset, selected->cpuset);
selected->value = 2;
}
#else
selected->value = 2 * !selected->value; /* toggle between 0 and 2 */
#endif
result = HANDLED;
break;
#ifdef HAVE_LIBHWLOC
case KEY_F(1):
hwloc_bitmap_copy(this->workCpuset, this->allCpuset);
result = HANDLED;
break;
case KEY_F(2):
this->topoView = !this->topoView;
keepSelected = false;
result = HANDLED;
break;
case KEY_F(3):
case '-':
case '+':
if (selected->sub_tree)
selected->sub_tree = 1 + !(selected->sub_tree - 1); /* toggle between 1 and 2 */
result = HANDLED;
break;
#endif
case 0x0a:
case 0x0d:
case KEY_ENTER:
result = BREAK_LOOP;
break;
}
if (HANDLED == result)
AffinityPanel_update(this, keepSelected);
return result;
}
#ifdef HAVE_LIBHWLOC
static MaskItem *AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem *parent) {
const char* type_name = hwloc_obj_type_string(obj->type);
const char* index_prefix = "#";
unsigned depth = obj->depth;
unsigned index = obj->logical_index;
size_t off = 0, left = 10 * depth;
char buf[64], indent_buf[left + 1];
if (obj->type == HWLOC_OBJ_PU) {
index = Settings_cpuId(this->pl->settings, obj->os_index);
type_name = "CPU";
index_prefix = "";
}
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] : " ");
size_t len = strlen(&indent_buf[off]);
off += len, left -= len;
}
xSnprintf(&indent_buf[off], left, "%s",
obj->next_sibling ? CRT_treeStr[TREE_STR_RTEE] : CRT_treeStr[TREE_STR_BEND]);
size_t len = strlen(&indent_buf[off]);
off += len, left -= len;
}
xSnprintf(buf, 64, "%s %s%u", type_name, index_prefix, index);
MaskItem *item = MaskItem_newMask(buf, indent_buf, obj->complete_cpuset, false);
if (parent)
Vector_add(parent->children, item);
if (item->sub_tree && parent && parent->sub_tree == 1) {
/* if obj is fully included or fully excluded, collapse the item */
hwloc_bitmap_t result = hwloc_bitmap_alloc();
hwloc_bitmap_and(result, obj->complete_cpuset, this->workCpuset);
int weight = hwloc_bitmap_weight(result);
hwloc_bitmap_free(result);
if (weight == 0 || weight == (hwloc_bitmap_weight(this->workCpuset) + hwloc_bitmap_weight(obj->complete_cpuset)))
item->sub_tree = 2;
}
/* "[x] " + "|- " * depth + ("- ")?(if root node) + name */
unsigned width = 4 + 3 * depth + (2 * !depth) + strlen(buf);
if (width > this->width)
this->width = width;
return item;
}
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);
} else {
indent &= ~(1u << obj->depth);
}
for (unsigned i = 0; i < obj->arity; i++)
AffinityPanel_buildTopology(this, obj->children[i], indent, item);
return parent == NULL ? item : NULL;
}
#endif
PanelClass AffinityPanel_class = {
.super = {
.extends = Class(Panel),
.delete = AffinityPanel_delete
},
.eventHandler = AffinityPanel_eventHandler
};
static const char* const AffinityPanelFunctions[] = {
"Set ",
"Cancel ",
#ifdef HAVE_LIBHWLOC
"All",
"Topology",
" ",
#endif
NULL
};
static const char* const AffinityPanelKeys[] = {"Enter", "Esc", "F1", "F2", "F3"};
static const int AffinityPanelEvents[] = {13, 27, KEY_F(1), KEY_F(2), KEY_F(3)};
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
AffinityPanel* this = AllocThis(AffinityPanel);
Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, Class(MaskItem), false, FunctionBar_new(AffinityPanelFunctions, AffinityPanelKeys, AffinityPanelEvents));
this->pl = pl;
/* defaults to 15, this also includes the gap between the panels,
* but this will be added by the caller */
this->width = 14;
this->cpuids = Vector_new(Class(MaskItem), true, DEFAULT_SIZE);
#ifdef HAVE_LIBHWLOC
this->topoView = pl->settings->topologyAffinity;
#else
this->topoView = false;
#endif
#ifdef HAVE_LIBHWLOC
this->allCpuset = hwloc_topology_get_complete_cpuset(pl->topology);
this->workCpuset = hwloc_bitmap_alloc();
#endif
Panel_setHeader(super, "Use CPUs:");
int curCpu = 0;
for (int i = 0; i < pl->cpuCount; i++) {
char number[16];
xSnprintf(number, 9, "CPU %d", Settings_cpuId(pl->settings, i));
unsigned width = 4 + strlen(number);
if (width > this->width)
this->width = width;
bool isSet = false;
if (curCpu < affinity->used && affinity->cpus[curCpu] == i) {
#ifdef HAVE_LIBHWLOC
hwloc_bitmap_set(this->workCpuset, i);
#endif
isSet = true;
curCpu++;
}
MaskItem* cpuItem = MaskItem_newSingleton(number, i, isSet);
Vector_add(this->cpuids, (Object*) cpuItem);
}
#ifdef HAVE_LIBHWLOC
this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(pl->topology), 0, NULL);
#endif
if (width)
*width = this->width;
AffinityPanel_update(this, false);
return super;
}
Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) {
AffinityPanel* this = (AffinityPanel*) super;
Affinity* affinity = Affinity_new(pl);
#ifdef HAVE_LIBHWLOC
int i;
hwloc_bitmap_foreach_begin(i, this->workCpuset)
Affinity_add(affinity, i);
hwloc_bitmap_foreach_end();
#else
for (int i = 0; i < this->pl->cpuCount; i++) {
MaskItem* item = (MaskItem*)Vector_get(this->cpuids, i);
if (item->value)
Affinity_add(affinity, item->cpu);
}
#endif
return affinity;
}

View File

@ -1,40 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_AffinityPanel
#define HEADER_AffinityPanel
/*
htop - AffinityPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#ifdef HAVE_LIBHWLOC
#endif
#include "Panel.h"
#include "Affinity.h"
#include "ProcessList.h"
#ifdef HAVE_LIBHWLOC
#endif
#ifdef HAVE_LIBHWLOC
#endif
#ifdef HAVE_LIBHWLOC
#endif
extern PanelClass AffinityPanel_class;
extern Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width);
extern Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl);
#endif

View File

@ -1,43 +1,57 @@
/*
htop - AvailableColumnsPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "AvailableColumnsPanel.h" #include "AvailableColumnsPanel.h"
#include "Platform.h" #include "Settings.h"
#include "Header.h" #include "Header.h"
#include "ScreenManager.h"
#include "ColumnsPanel.h" #include "ColumnsPanel.h"
#include "Panel.h"
#include "debug.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
/*{ /*{
#include "Panel.h"
typedef struct AvailableColumnsPanel_ { typedef struct AvailableColumnsPanel_ {
Panel super; Panel super;
Panel* columns; Panel* columns;
Settings* settings;
ScreenManager* scr;
} AvailableColumnsPanel; } AvailableColumnsPanel;
}*/ }*/
static const char* const AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL}; AvailableColumnsPanel* AvailableColumnsPanel_new(Settings* settings, Panel* columns, ScreenManager* scr) {
AvailableColumnsPanel* this = (AvailableColumnsPanel*) malloc(sizeof(AvailableColumnsPanel));
Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, LISTITEM_CLASS, true);
((Object*)this)->delete = AvailableColumnsPanel_delete;
static void AvailableColumnsPanel_delete(Object* object) { this->settings = settings;
this->scr = scr;
super->eventHandler = AvailableColumnsPanel_eventHandler;
Panel_setHeader(super, "Available Columns");
for (int i = 1; i < LAST_PROCESSFIELD; i++) {
if (i != COMM)
Panel_add(super, (Object*) ListItem_new(Process_fieldNames[i], 0));
}
this->columns = columns;
return this;
}
void AvailableColumnsPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
AvailableColumnsPanel* this = (AvailableColumnsPanel*) object; AvailableColumnsPanel* this = (AvailableColumnsPanel*) object;
Panel_done(super); Panel_done(super);
free(this); free(this);
} }
static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) { HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
AvailableColumnsPanel* this = (AvailableColumnsPanel*) super; AvailableColumnsPanel* this = (AvailableColumnsPanel*) super;
int key = ((ListItem*) Panel_getSelected(super))->key; char* text = ((ListItem*) Panel_getSelected(super))->value;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
switch(ch) { switch(ch) {
@ -45,46 +59,14 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
case KEY_ENTER: case KEY_ENTER:
case KEY_F(5): case KEY_F(5):
{ {
int at = Panel_getSelectedIndex(this->columns); int at = Panel_getSelectedIndex(this->columns) + 1;
Panel_insert(this->columns, at, (Object*) ListItem_new(Process_fields[key].name, key)); if (at == Panel_getSize(this->columns))
Panel_setSelected(this->columns, at+1); at--;
Panel_insert(this->columns, at, (Object*) ListItem_new(text, 0));
ColumnsPanel_update(this->columns); ColumnsPanel_update(this->columns);
result = HANDLED; result = HANDLED;
break; break;
} }
default:
{
if (ch < 255 && isalpha(ch))
result = Panel_selectByTyping(super, ch);
break;
}
} }
return result; return result;
} }
PanelClass AvailableColumnsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = AvailableColumnsPanel_delete
},
.eventHandler = AvailableColumnsPanel_eventHandler
};
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
AvailableColumnsPanel* this = AllocThis(AvailableColumnsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(AvailableColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
Panel_setHeader(super, "Available Columns");
for (int i = 1; i < Platform_numberOfFields; 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);
Panel_add(super, (Object*) ListItem_new(description, i));
}
}
this->columns = columns;
return this;
}

View File

@ -2,23 +2,31 @@
#ifndef HEADER_AvailableColumnsPanel #ifndef HEADER_AvailableColumnsPanel
#define HEADER_AvailableColumnsPanel #define HEADER_AvailableColumnsPanel
/*
htop - AvailableColumnsPanel.h #include "Settings.h"
(C) 2004-2011 Hisham H. Muhammad #include "Header.h"
Released under the GNU GPL, see the COPYING file #include "ScreenManager.h"
in the source distribution for its full text. #include "ColumnsPanel.h"
*/
#include "Panel.h" #include "Panel.h"
#include "debug.h"
#include <assert.h>
typedef struct AvailableColumnsPanel_ { typedef struct AvailableColumnsPanel_ {
Panel super; Panel super;
Panel* columns; Panel* columns;
Settings* settings;
ScreenManager* scr;
} AvailableColumnsPanel; } AvailableColumnsPanel;
extern PanelClass AvailableColumnsPanel_class; AvailableColumnsPanel* AvailableColumnsPanel_new(Settings* settings, Panel* columns, ScreenManager* scr);
extern AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns); void AvailableColumnsPanel_delete(Object* object);
HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch);
#endif #endif

View File

@ -1,133 +1,54 @@
/*
htop - AvailableMetersPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "AvailableMetersPanel.h" #include "AvailableMetersPanel.h"
#include "MetersPanel.h" #include "Settings.h"
#include "CPUMeter.h"
#include "Header.h" #include "Header.h"
#include "ListItem.h" #include "ScreenManager.h"
#include "Platform.h" #include "CPUMeter.h"
#include "Panel.h"
#include "debug.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h>
/*{ /*{
#include "Settings.h"
#include "Panel.h"
#include "ScreenManager.h"
#include "ProcessList.h"
typedef struct AvailableMetersPanel_ { typedef struct AvailableMetersPanel_ {
Panel super; Panel super;
ScreenManager* scr;
Settings* settings; Settings* settings;
Header* header;
Panel* leftPanel; Panel* leftPanel;
Panel* rightPanel; Panel* rightPanel;
ScreenManager* scr;
} AvailableMetersPanel; } AvailableMetersPanel;
}*/ }*/
static void AvailableMetersPanel_delete(Object* object) { AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr) {
Panel* super = (Panel*) object; AvailableMetersPanel* this = (AvailableMetersPanel*) malloc(sizeof(AvailableMetersPanel));
AvailableMetersPanel* this = (AvailableMetersPanel*) object;
Panel_done(super);
free(this);
}
static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, MeterClass* type, int param, int column) {
Meter* meter = (Meter*) Header_addMeterByClass(header, type, param, column);
Panel_add(panel, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(panel, Panel_size(panel) - 1);
MetersPanel_setMoving((MetersPanel*)panel, true);
FunctionBar_draw(panel->currentBar, NULL);
}
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
AvailableMetersPanel* this = (AvailableMetersPanel*) super;
Header* header = this->header;
ListItem* selected = (ListItem*) Panel_getSelected(super);
int param = selected->key & 0xff;
int type = selected->key >> 16;
HandlerResult result = IGNORED;
bool update = false;
switch(ch) {
case KEY_F(5):
case 'l':
case 'L':
{
AvailableMetersPanel_addMeter(header, this->leftPanel, Platform_meterTypes[type], param, 0);
result = HANDLED;
update = true;
break;
}
case 0x0a:
case 0x0d:
case KEY_ENTER:
case KEY_F(6):
case 'r':
case 'R':
{
AvailableMetersPanel_addMeter(header, this->rightPanel, Platform_meterTypes[type], param, 1);
result = (KEY_LEFT << 16) | SYNTH_KEY;
update = true;
break;
}
}
if (update) {
this->settings->changed = true;
Header_calculateHeight(header);
Header_draw(header);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
}
return result;
}
PanelClass AvailableMetersPanel_class = {
.super = {
.extends = Class(Panel),
.delete = AvailableMetersPanel_delete
},
.eventHandler = AvailableMetersPanel_eventHandler
};
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl) {
AvailableMetersPanel* this = AllocThis(AvailableMetersPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done "); Panel_init(super, 1, 1, 1, 1, LISTITEM_CLASS, true);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); ((Object*)this)->delete = AvailableMetersPanel_delete;
this->settings = settings; this->settings = settings;
this->header = header;
this->leftPanel = leftMeters; this->leftPanel = leftMeters;
this->rightPanel = rightMeters; this->rightPanel = rightMeters;
this->scr = scr; this->scr = scr;
super->eventHandler = AvailableMetersPanel_EventHandler;
Panel_setHeader(super, "Available meters"); Panel_setHeader(super, "Available meters");
// Platform_meterTypes[0] should be always (&CPUMeter_class), which we will for (int i = 1; Meter_types[i]; i++) {
// handle separately in the code below. MeterType* type = Meter_types[i];
for (int i = 1; Platform_meterTypes[i]; i++) { if (type != &CPUMeter) {
MeterClass* type = Platform_meterTypes[i]; Panel_add(super, (Object*) ListItem_new(type->uiName, i << 16));
assert(type != &CPUMeter_class);
const char* label = type->description ? type->description : type->uiName;
Panel_add(super, (Object*) ListItem_new(label, i << 16));
} }
// Handle (&CPUMeter_class) }
MeterClass* type = &CPUMeter_class; MeterType* type = &CPUMeter;
int cpus = pl->cpuCount; int processors = settings->pl->processorCount;
if (cpus > 1) { if (processors > 1) {
Panel_add(super, (Object*) ListItem_new("CPU average", 0)); Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (int i = 1; i <= cpus; i++) { for (int i = 1; i <= processors; i++) {
char buffer[50]; char buffer[50];
xSnprintf(buffer, 50, "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1)); sprintf(buffer, "%s %d", type->uiName, i);
Panel_add(super, (Object*) ListItem_new(buffer, i)); Panel_add(super, (Object*) ListItem_new(buffer, i));
} }
} else { } else {
@ -135,3 +56,51 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
} }
return this; return this;
} }
void AvailableMetersPanel_delete(Object* object) {
Panel* super = (Panel*) object;
AvailableMetersPanel* this = (AvailableMetersPanel*) object;
Panel_done(super);
free(this);
}
static inline void AvailableMetersPanel_addHeader(Header* header, Panel* panel, MeterType* type, int param, HeaderSide side) {
Meter* meter = (Meter*) Header_addMeter(header, type, param, side);
Panel_add(panel, (Object*) Meter_toListItem(meter));
}
HandlerResult AvailableMetersPanel_EventHandler(Panel* super, int ch) {
AvailableMetersPanel* this = (AvailableMetersPanel*) super;
Header* header = this->settings->header;
ListItem* selected = (ListItem*) Panel_getSelected(super);
int param = selected->key & 0xff;
int type = selected->key >> 16;
HandlerResult result = IGNORED;
switch(ch) {
case KEY_F(5):
case 'l':
case 'L':
{
AvailableMetersPanel_addHeader(header, this->leftPanel, Meter_types[type], param, LEFT_HEADER);
result = HANDLED;
break;
}
case KEY_F(6):
case 'r':
case 'R':
{
AvailableMetersPanel_addHeader(header, this->rightPanel, Meter_types[type], param, RIGHT_HEADER);
result = HANDLED;
break;
}
}
if (result == HANDLED) {
this->settings->changed = true;
Header_calculateHeight(header);
Header_draw(header);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
}
return result;
}

View File

@ -2,31 +2,32 @@
#ifndef HEADER_AvailableMetersPanel #ifndef HEADER_AvailableMetersPanel
#define HEADER_AvailableMetersPanel #define HEADER_AvailableMetersPanel
/*
htop - AvailableMetersPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Settings.h" #include "Settings.h"
#include "Panel.h" #include "Header.h"
#include "ScreenManager.h" #include "ScreenManager.h"
#include "ProcessList.h" #include "CPUMeter.h"
#include "Panel.h"
#include "debug.h"
#include <assert.h>
typedef struct AvailableMetersPanel_ { typedef struct AvailableMetersPanel_ {
Panel super; Panel super;
ScreenManager* scr;
Settings* settings; Settings* settings;
Header* header;
Panel* leftPanel; Panel* leftPanel;
Panel* rightPanel; Panel* rightPanel;
ScreenManager* scr;
} AvailableMetersPanel; } AvailableMetersPanel;
extern PanelClass AvailableMetersPanel_class; AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr);
extern AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl); void AvailableMetersPanel_delete(Object* object);
HandlerResult AvailableMetersPanel_EventHandler(Panel* super, int ch);
#endif #endif

View File

@ -1,84 +0,0 @@
/*
htop - BatteryMeter.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
*/
#include "BatteryMeter.h"
#include "Battery.h"
#include "ProcessList.h"
#include "CRT.h"
#include "StringUtils.h"
#include "Platform.h"
#include <string.h>
#include <stdlib.h>
/*{
#include "Meter.h"
typedef enum ACPresence_ {
AC_ABSENT,
AC_PRESENT,
AC_ERROR
} ACPresence;
}*/
int BatteryMeter_attributes[] = {
BATTERY
};
static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) {
ACPresence isOnAC;
double percent;
Battery_getData(&percent, &isOnAC);
if (percent == -1) {
this->values[0] = 0;
xSnprintf(buffer, len, "n/a");
return;
}
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)";
}
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);
}
return;
}
MeterClass BatteryMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete
},
.updateValues = BatteryMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = BatteryMeter_attributes,
.name = "Battery",
.uiName = "Battery",
.caption = "Battery: "
};

View File

@ -1,26 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_BatteryMeter
#define HEADER_BatteryMeter
/*
htop - BatteryMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
*/
#include "Meter.h"
typedef enum ACPresence_ {
AC_ABSENT,
AC_PRESENT,
AC_ERROR
} ACPresence;
extern int BatteryMeter_attributes[];
extern MeterClass BatteryMeter_class;
#endif

View File

@ -1,34 +0,0 @@
Contributing Guide
==================
Thank you so much for taking the time to contribute in to htop!
Bug Reports
-----------
Bug reports should be posted in the [Github issue
tracker](https://github.com/htop-dev/htop/issues).
Bug reports are extremely important since it's impossible for us to test
htop in every possible system, distribution and scenario. Your feedback
is what keeps the tool stable and always improving! Thank you!
Pull Requests
-------------
Code contributions are most welcome! Just [fork the
repo](https://github.com/htop-dev/htop) and send a [pull
request](https://github.com/htop-dev/htop/pulls). Help is especially
appreciated for support of platforms other than Linux. If proposing new
features, please be mindful that htop is a system tool that needs to keep a
small footprint and perform well on systems under stress -- so unfortunately
we can't accept every new feature proposed, as we need to keep the tool slim
and maintainable. Great ideas backed by a PR are always carefully considered
for inclusion though! Also, PRs containing bug fixes and portability tweaks
are always included, please send those in!
Feature Requests
----------------
Please label Github issues that are feature requests with the [`feature
request`](https://github.com/htop-dev/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+)
label.

16
COPYING
View File

@ -337,19 +337,3 @@ proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General library. If this is what you want to do, use the GNU Library General
Public License instead of this License. Public License instead of this License.
Appendix 2: Special exception concerning PLPA
In the following exception, "PLPA" means (i) code released by the
Portable Linux Processor Affinity Project, or (ii) derivative works of
such code, in both cases provided that the code is covered entirely by
free software licensing terms.
As a special exception to the GNU GPL, the licensors of htop give you
permission to combine GNU GPL-licensed code in htop (and derivative
works of such code) with PLPA. You may copy and distribute such a
combined work following the terms of the GNU GPL for htop and the
applicable licenses of the version of PLPA used in your combined work,
provided that you include the source code of such version of PLPA when
and as the GNU GPL requires distribution of source code.

View File

@ -1,41 +1,52 @@
/* /*
htop - CPUMeter.c htop - CPUMeter.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "CPUMeter.h" #include "CPUMeter.h"
#include "Meter.h"
#include "CRT.h" #include "ProcessList.h"
#include "Settings.h"
#include "Platform.h"
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <curses.h>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
/*{ #include "debug.h"
#include "Meter.h" #include <assert.h>
typedef enum {
CPU_METER_NICE = 0,
CPU_METER_NORMAL = 1,
CPU_METER_KERNEL = 2,
CPU_METER_IRQ = 3,
CPU_METER_SOFTIRQ = 4,
CPU_METER_STEAL = 5,
CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7,
CPU_METER_FREQUENCY = 8,
CPU_METER_ITEMCOUNT = 9, // number of entries in this enum
} CPUMeterValues;
}*/
int CPUMeter_attributes[] = { int CPUMeter_attributes[] = {
CPU_NICE, CPU_NORMAL, CPU_SYSTEM, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, CPU_IOWAIT CPU_NICE, CPU_NORMAL, CPU_KERNEL
};
MeterType CPUMeter = {
.setValues = CPUMeter_setValues,
.display = CPUMeter_display,
.mode = BAR_METERMODE,
.items = 3,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "CPU",
.uiName = "CPU",
.caption = "CPU",
.init = CPUMeter_init
};
MeterType AllCPUsMeter = {
.mode = 0,
.items = 1,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "AllCPUs",
.uiName = "All CPUs",
.caption = "CPU",
.draw = AllCPUsMeter_draw,
.init = AllCPUsMeter_init,
.setMode = AllCPUsMeter_setMode,
.done = AllCPUsMeter_done
}; };
#ifndef MIN #ifndef MIN
@ -45,399 +56,73 @@ int CPUMeter_attributes[] = {
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
static void CPUMeter_init(Meter* this) { void CPUMeter_init(Meter* this) {
int cpu = this->param; int processor = this->param;
if (this->pl->cpuCount > 1) { if (this->pl->processorCount > 1) {
char caption[10]; char caption[10];
xSnprintf(caption, sizeof(caption), "%-3d", Settings_cpuId(this->pl->settings, cpu - 1)); sprintf(caption, "%-3d", processor);
Meter_setCaption(this, caption); Meter_setCaption(this, caption);
} }
if (this->param == 0) if (this->param == 0)
Meter_setCaption(this, "Avg"); Meter_setCaption(this, "Avg");
} }
static void CPUMeter_updateValues(Meter* this, char* buffer, int size) { void CPUMeter_setValues(Meter* this, char* buffer, int size) {
int cpu = this->param; ProcessList* pl = this->pl;
if (cpu > this->pl->cpuCount) { int processor = this->param;
xSnprintf(buffer, size, "absent"); double total = (double) pl->totalPeriod[processor];
return; this->values[0] = pl->nicePeriod[processor] / total * 100.0;
} this->values[1] = pl->userPeriod[processor] / total * 100.0;
memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT); this->values[2] = pl->systemPeriod[processor] / total * 100.0;
double percent = Platform_setCPUValues(this, cpu); double cpu = MIN(100.0, MAX(0.0, (this->values[0]+this->values[1]+this->values[2])));
if (this->pl->settings->showCPUFrequency) { snprintf(buffer, size, "%5.1f%%", cpu );
/* Initial frequency is in MHz. Emit it as GHz if it's larger than 1000MHz */
double cpuFrequency = this->values[CPU_METER_FREQUENCY];
char unit = 'M';
char cpuFrequencyBuffer[16];
if (cpuFrequency < 0) {
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A");
} else {
if (cpuFrequency > 1000) {
cpuFrequency /= 1000;
unit = 'G';
}
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%.3f%cHz", cpuFrequency, unit);
}
if (this->pl->settings->showCPUUsage) {
xSnprintf(buffer, size, "%5.1f%% %s", percent, cpuFrequencyBuffer);
} else {
xSnprintf(buffer, size, "%s", cpuFrequencyBuffer);
}
} else if (this->pl->settings->showCPUUsage) {
xSnprintf(buffer, size, "%5.1f%%", percent);
} else if (size > 0) {
buffer[0] = '\0';
}
} }
static void CPUMeter_display(Object* cast, RichString* out) { void CPUMeter_display(Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
Meter* this = (Meter*)cast; Meter* this = (Meter*)cast;
RichString_prune(out); RichString_init(out);
if (this->param > this->pl->cpuCount) { sprintf(buffer, "%5.1f%% ", this->values[1]);
RichString_append(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[METER_TEXT], ":");
RichString_append(out, CRT_colors[CPU_NORMAL], buffer); RichString_append(out, CRT_colors[CPU_NORMAL], buffer);
if (this->pl->settings->detailedCPUTime) { sprintf(buffer, "%5.1f%% ", this->values[2]);
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);
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);
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);
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);
if (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);
}
if (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);
}
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);
} 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[METER_TEXT], "sys:");
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer); RichString_append(out, CRT_colors[CPU_KERNEL], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); sprintf(buffer, "%5.1f%% ", this->values[0]);
RichString_append(out, CRT_colors[METER_TEXT], "low:"); RichString_append(out, CRT_colors[METER_TEXT], "low:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_append(out, CRT_colors[CPU_NICE], buffer);
if (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);
}
}
} }
static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) { void AllCPUsMeter_init(Meter* this) {
int cpus = this->pl->cpuCount; int processors = this->pl->processorCount;
switch(Meter_name(this)[0]) { this->drawBuffer = malloc(sizeof(Meter*) * processors);
default: Meter** meters = (Meter**) this->drawBuffer;
case 'A': // All for (int i = 0; i < processors; i++)
*start = 0; meters[i] = Meter_new(this->pl, i+1, &CPUMeter);
*count = cpus; this->h = processors;
break;
case 'L': // First Half
*start = 0;
*count = (cpus+1) / 2;
break;
case 'R': // Second Half
*start = (cpus+1) / 2;
*count = cpus / 2;
break;
}
}
static int MapClassnameToColumncount(Meter* this){
if (strchr(Meter_name(this), '4'))
return 4;
else if (strchr(Meter_name(this), '2'))
return 2;
else
return 1;
}
static void AllCPUsMeter_init(Meter* this) {
int cpus = this->pl->cpuCount;
if (!this->drawData)
this->drawData = xCalloc(cpus, sizeof(Meter*));
Meter** meters = (Meter**) this->drawData;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++) {
if (!meters[i])
meters[i] = Meter_new(this->pl, start+i+1, (MeterClass*) Class(CPUMeter));
Meter_init(meters[i]);
}
if (this->mode == 0)
this->mode = BAR_METERMODE; this->mode = BAR_METERMODE;
int h = Meter_modes[this->mode]->h;
int ncol = MapClassnameToColumncount(this);
this->h = h * ((count + ncol - 1)/ ncol);
} }
static void AllCPUsMeter_done(Meter* this) { void AllCPUsMeter_done(Meter* this) {
Meter** meters = (Meter**) this->drawData; int processors = this->pl->processorCount;
int start, count; Meter** meters = (Meter**) this->drawBuffer;
AllCPUsMeter_getRange(this, &start, &count); for (int i = 0; i < processors; i++)
for (int i = 0; i < count; i++)
Meter_delete((Object*)meters[i]); Meter_delete((Object*)meters[i]);
} }
static void AllCPUsMeter_updateMode(Meter* this, int mode) { void AllCPUsMeter_setMode(Meter* this, int mode) {
Meter** meters = (Meter**) this->drawData;
this->mode = mode; this->mode = mode;
int h = Meter_modes[mode]->h; int processors = this->pl->processorCount;
int start, count; int h = Meter_modes[this->mode]->h;
AllCPUsMeter_getRange(this, &start, &count); this->h = h * processors;
for (int i = 0; i < count; i++) {
Meter_setMode(meters[i], mode);
}
int ncol = MapClassnameToColumncount(this);
this->h = h * ((count + ncol - 1)/ ncol);
} }
static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) { void AllCPUsMeter_draw(Meter* this, int x, int y, int w) {
Meter** meters = (Meter**) this->drawData; int processors = this->pl->processorCount;
int start, count; Meter** meters = (Meter**) this->drawBuffer;
int pad = this->pl->settings->headerMargin ? 2 : 0; for (int i = 0; i < processors; i++) {
AllCPUsMeter_getRange(this, &start, &count); Meter_setMode(meters[i], this->mode);
int height = (count+1)/2;
int startY = y;
for (int i = 0; i < height; i++) {
meters[i]->draw(meters[i], x, y, (w-pad)/2);
y += meters[i]->h;
}
y = startY;
for (int i = height; i < count; i++) {
meters[i]->draw(meters[i], x+(w-1)/2+1+(pad/2), y, (w-pad)/2);
y += meters[i]->h;
}
}
static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) {
Meter** meters = (Meter**) this->drawData;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++) {
meters[i]->draw(meters[i], x, y, w); meters[i]->draw(meters[i], x, y, w);
y += meters[i]->h; y += meters[i]->h;
} }
} }
static void MultiColCPUsMeter_draw(Meter* this, int x, int y, int w){
Meter** meters = (Meter**) this->drawData;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
int ncol = MapClassnameToColumncount(this);
int colwidth = (w-ncol)/ncol + 1;
int diff = (w - (colwidth * ncol));
int nrows = (count + ncol - 1) / ncol;
for (int i = 0; i < count; i++){
int d = (i/nrows) > diff ? diff : (i / nrows) ; // dynamic spacer
int xpos = x + ((i / nrows) * colwidth) + d;
int ypos = y + ((i % nrows) * meters[0]->h);
meters[i]->draw(meters[i], xpos, ypos, colwidth);
}
}
MeterClass CPUMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.updateValues = CPUMeter_updateValues,
.defaultMode = BAR_METERMODE,
.maxItems = CPU_METER_ITEMCOUNT,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "CPU",
.uiName = "CPU",
.caption = "CPU",
.init = CPUMeter_init
};
MeterClass AllCPUsMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "AllCPUs",
.uiName = "CPUs (1/1)",
.description = "CPUs (1/1): all CPUs",
.caption = "CPU",
.draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass AllCPUs2Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "AllCPUs2",
.uiName = "CPUs (1&2/2)",
.description = "CPUs (1&2/2): all CPUs in 2 shorter columns",
.caption = "CPU",
.draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass LeftCPUsMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs",
.uiName = "CPUs (1/2)",
.description = "CPUs (1/2): first half of list",
.caption = "CPU",
.draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass RightCPUsMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "RightCPUs",
.uiName = "CPUs (2/2)",
.description = "CPUs (2/2): second half of list",
.caption = "CPU",
.draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass LeftCPUs2Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs2",
.uiName = "CPUs (1&2/4)",
.description = "CPUs (1&2/4): first half in 2 shorter columns",
.caption = "CPU",
.draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass RightCPUs2Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "RightCPUs2",
.uiName = "CPUs (3&4/4)",
.description = "CPUs (3&4/4): second half in 2 shorter columns",
.caption = "CPU",
.draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass AllCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "AllCPUs4",
.uiName = "CPUs (1&2&3&4/4)",
.description = "CPUs (1&2&3&4/4): all CPUs in 4 shorter columns",
.caption = "CPU",
.draw = MultiColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass LeftCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs4",
.uiName = "CPUs (1-4/8)",
.description = "CPUs (1-4/8): first half in 4 shorter columns",
.caption = "CPU",
.draw = MultiColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass RightCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "RightCPUs4",
.uiName = "CPUs (5-8/8)",
.description = "CPUs (5-8/8): second half in 4 shorter columns",
.caption = "CPU",
.draw = MultiColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};

View File

@ -4,29 +4,29 @@
#define HEADER_CPUMeter #define HEADER_CPUMeter
/* /*
htop - CPUMeter.h htop - CPUMeter.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.h"
typedef enum { #include "ProcessList.h"
CPU_METER_NICE = 0,
CPU_METER_NORMAL = 1,
CPU_METER_KERNEL = 2,
CPU_METER_IRQ = 3,
CPU_METER_SOFTIRQ = 4,
CPU_METER_STEAL = 5,
CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7,
CPU_METER_FREQUENCY = 8,
CPU_METER_ITEMCOUNT = 9, // number of entries in this enum
} CPUMeterValues;
#include <stdlib.h>
#include <curses.h>
#include <string.h>
#include <math.h>
#include "debug.h"
#include <assert.h>
extern int CPUMeter_attributes[]; extern int CPUMeter_attributes[];
extern MeterType CPUMeter;
extern MeterType AllCPUsMeter;
#ifndef MIN #ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b))
#endif #endif
@ -34,24 +34,18 @@ extern int CPUMeter_attributes[];
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
extern MeterClass CPUMeter_class; void CPUMeter_init(Meter* this);
extern MeterClass AllCPUsMeter_class; void CPUMeter_setValues(Meter* this, char* buffer, int size);
extern MeterClass AllCPUs2Meter_class; void CPUMeter_display(Object* cast, RichString* out);
extern MeterClass LeftCPUsMeter_class; void AllCPUsMeter_init(Meter* this);
extern MeterClass RightCPUsMeter_class; void AllCPUsMeter_done(Meter* this);
extern MeterClass LeftCPUs2Meter_class; void AllCPUsMeter_setMode(Meter* this, int mode);
extern MeterClass RightCPUs2Meter_class; void AllCPUsMeter_draw(Meter* this, int x, int y, int w);
extern MeterClass AllCPUs4Meter_class;
extern MeterClass LeftCPUs4Meter_class;
extern MeterClass RightCPUs4Meter_class;
#endif #endif

1029
CRT.c

File diff suppressed because it is too large Load Diff

160
CRT.h
View File

@ -4,17 +4,29 @@
#define HEADER_CRT #define HEADER_CRT
/* /*
htop - CRT.h htop - CRT.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#if HAVE_SETUID_ENABLED
#endif
#define ColorIndex(i,j) ((7-i)*8+j) #include <curses.h>
#include <signal.h>
#include <stdlib.h>
#include <stdbool.h>
#define ColorPair(i,j) COLOR_PAIR(ColorIndex(i,j)) #include "String.h"
#include "debug.h"
#define ColorPair(i,j) COLOR_PAIR((7-i)*8+j)
#define COLORSCHEME_DEFAULT 0
#define COLORSCHEME_MONOCHROME 1
#define COLORSCHEME_BLACKONWHITE 2
#define COLORSCHEME_BLACKONWHITE2 3
#define COLORSCHEME_MIDNIGHT 4
#define COLORSCHEME_BLACKNIGHT 5
#define Black COLOR_BLACK #define Black COLOR_BLACK
#define Red COLOR_RED #define Red COLOR_RED
@ -25,38 +37,10 @@ in the source distribution for its full text.
#define Cyan COLOR_CYAN #define Cyan COLOR_CYAN
#define White COLOR_WHITE #define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
#define KEY_RECLICK KEY_F(22)
//#link curses //#link curses
#include <stdbool.h> bool CRT_hasColors;
typedef enum TreeStr_ {
TREE_STR_HORZ,
TREE_STR_VERT,
TREE_STR_RTEE,
TREE_STR_BEND,
TREE_STR_TEND,
TREE_STR_OPEN,
TREE_STR_SHUT,
TREE_STR_COUNT
} TreeStr;
typedef enum ColorSchemes_ {
COLORSCHEME_DEFAULT = 0,
COLORSCHEME_MONOCHROME = 1,
COLORSCHEME_BLACKONWHITE = 2,
COLORSCHEME_LIGHTTERMINAL = 3,
COLORSCHEME_MIDNIGHT = 4,
COLORSCHEME_BLACKNIGHT = 5,
COLORSCHEME_BROKENGRAY = 6,
LAST_COLORSCHEME = 7,
} ColorSchemes;
typedef enum ColorElements_ { typedef enum ColorElements_ {
RESET_COLOR, RESET_COLOR,
@ -66,15 +50,14 @@ typedef enum ColorElements_ {
FAILED_SEARCH, FAILED_SEARCH,
PANEL_HEADER_FOCUS, PANEL_HEADER_FOCUS,
PANEL_HEADER_UNFOCUS, PANEL_HEADER_UNFOCUS,
PANEL_SELECTION_FOCUS, PANEL_HIGHLIGHT_FOCUS,
PANEL_SELECTION_FOLLOW, PANEL_HIGHLIGHT_UNFOCUS,
PANEL_SELECTION_UNFOCUS,
LARGE_NUMBER, LARGE_NUMBER,
METER_TEXT, METER_TEXT,
METER_VALUE, METER_VALUE,
LED_COLOR, LED_COLOR,
UPTIME, UPTIME,
BATTERY, TASKS_TOTAL,
TASKS_RUNNING, TASKS_RUNNING,
SWAP, SWAP,
PROCESS, PROCESS,
@ -83,19 +66,22 @@ typedef enum ColorElements_ {
PROCESS_MEGABYTES, PROCESS_MEGABYTES,
PROCESS_TREE, PROCESS_TREE,
PROCESS_R_STATE, PROCESS_R_STATE,
PROCESS_D_STATE,
PROCESS_BASENAME, PROCESS_BASENAME,
PROCESS_HIGH_PRIORITY, PROCESS_HIGH_PRIORITY,
PROCESS_LOW_PRIORITY, PROCESS_LOW_PRIORITY,
PROCESS_THREAD,
PROCESS_THREAD_BASENAME,
BAR_BORDER, BAR_BORDER,
BAR_SHADOW, BAR_SHADOW,
GRAPH_1, GRAPH_1,
GRAPH_2, GRAPH_2,
GRAPH_3,
GRAPH_4,
GRAPH_5,
GRAPH_6,
GRAPH_7,
GRAPH_8,
GRAPH_9,
MEMORY_USED, MEMORY_USED,
MEMORY_BUFFERS, MEMORY_BUFFERS,
MEMORY_BUFFERS_TEXT,
MEMORY_CACHE, MEMORY_CACHE,
LOAD, LOAD,
LOAD_AVERAGE_FIFTEEN, LOAD_AVERAGE_FIFTEEN,
@ -105,102 +91,40 @@ typedef enum ColorElements_ {
CHECK_MARK, CHECK_MARK,
CHECK_TEXT, CHECK_TEXT,
CLOCK, CLOCK,
HELP_BOLD,
HOSTNAME,
CPU_NICE, CPU_NICE,
CPU_NICE_TEXT,
CPU_NORMAL, CPU_NORMAL,
CPU_SYSTEM, CPU_KERNEL,
CPU_IOWAIT, HELP_BOLD,
CPU_IRQ,
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
PRESSURE_STALL_TEN,
PRESSURE_STALL_SIXTY,
PRESSURE_STALL_THREEHUNDRED,
ZFS_MFU,
ZFS_MRU,
ZFS_ANON,
ZFS_HEADER,
ZFS_OTHER,
ZFS_COMPRESSED,
ZFS_RATIO,
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
extern void CRT_fatalError(const char* note) __attribute__ ((noreturn));
extern void CRT_handleSIGSEGV(int sgn); // TODO: centralize these in Settings.
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
extern const char *CRT_treeStrAscii[TREE_STR_COUNT];
#ifdef HAVE_LIBNCURSESW
extern const char *CRT_treeStrUtf8[TREE_STR_COUNT];
extern bool CRT_utf8;
#endif
extern const char **CRT_treeStr;
extern int CRT_delay; extern int CRT_delay;
extern int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX;
extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount;
extern char* CRT_termType;
// TODO move color scheme to Settings, perhaps?
extern int CRT_colorScheme; extern int CRT_colorScheme;
extern void *backtraceArray[128]; extern int CRT_colors[LAST_COLORELEMENT];
#if HAVE_SETUID_ENABLED char* CRT_termType;
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
extern void CRT_dropPrivileges();
extern void CRT_restorePrivileges();
#else
/* Turn setuid operations into NOPs */
#ifndef CRT_dropPrivileges
#define CRT_dropPrivileges()
#define CRT_restorePrivileges()
#endif
#endif
// TODO: pass an instance of Settings instead. // TODO: pass an instance of Settings instead.
extern void CRT_init(int delay, int colorScheme, bool allowUnicode); void CRT_init(int delay, int colorScheme);
extern void CRT_done(); void CRT_done();
extern void CRT_fatalError(const char* note); int CRT_readKey();
extern int CRT_readKey(); void CRT_disableDelay();
extern void CRT_disableDelay(); void CRT_enableDelay();
extern void CRT_enableDelay(); void CRT_handleSIGSEGV(int signal);
extern void CRT_setColors(int colorScheme); void CRT_handleSIGTERM(int signal);
void CRT_setColors(int colorScheme);
#endif #endif

View File

@ -1,12 +1,5 @@
/*
htop - CategoriesPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "CategoriesPanel.h" #include "CategoriesPanel.h"
#include "AvailableMetersPanel.h" #include "AvailableMetersPanel.h"
#include "MetersPanel.h" #include "MetersPanel.h"
#include "DisplayOptionsPanel.h" #include "DisplayOptionsPanel.h"
@ -14,96 +7,75 @@ in the source distribution for its full text.
#include "ColorsPanel.h" #include "ColorsPanel.h"
#include "AvailableColumnsPanel.h" #include "AvailableColumnsPanel.h"
#include "Panel.h"
#include "debug.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h>
/*{ /*{
#include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
#include "ProcessList.h"
typedef struct CategoriesPanel_ { typedef struct CategoriesPanel_ {
Panel super; Panel super;
ScreenManager* scr;
Settings* settings; Settings* settings;
Header* header; ScreenManager* scr;
ProcessList* pl;
} CategoriesPanel; } CategoriesPanel;
}*/ }*/
static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; static char* MetersFunctions[10] = {" ", " ", " ", "Type ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done "};
static void CategoriesPanel_delete(Object* object) { static char* AvailableMetersFunctions[10] = {" ", " ", " ", " ", "Add L ", "Add R ", " ", " ", " ", "Done "};
static char* DisplayOptionsFunctions[10] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done "};
static char* ColumnsFunctions[10] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done "};
static char* ColorsFunctions[10] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done "};
static char* AvailableColumnsFunctions[10] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done "};
CategoriesPanel* CategoriesPanel_new(Settings* settings, ScreenManager* scr) {
CategoriesPanel* this = (CategoriesPanel*) malloc(sizeof(CategoriesPanel));
Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, LISTITEM_CLASS, true);
((Object*)this)->delete = CategoriesPanel_delete;
this->settings = settings;
this->scr = scr;
super->eventHandler = CategoriesPanel_eventHandler;
Panel_setHeader(super, "Setup");
Panel_add(super, (Object*) ListItem_new("Meters", 0));
Panel_add(super, (Object*) ListItem_new("Display options", 0));
Panel_add(super, (Object*) ListItem_new("Colors", 0));
Panel_add(super, (Object*) ListItem_new("Columns", 0));
return this;
}
void CategoriesPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
CategoriesPanel* this = (CategoriesPanel*) object; CategoriesPanel* this = (CategoriesPanel*) object;
Panel_done(super); Panel_done(super);
free(this); free(this);
} }
void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
MetersPanel* leftMeters = MetersPanel_new(this->settings, "Left column", this->header->columns[0], this->scr);
MetersPanel* rightMeters = MetersPanel_new(this->settings, "Right column", this->header->columns[1], this->scr);
leftMeters->rightNeighbor = rightMeters;
rightMeters->leftNeighbor = leftMeters;
Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, (Panel*) leftMeters, (Panel*) rightMeters, this->scr, this->pl);
ScreenManager_add(this->scr, (Panel*) leftMeters, 20);
ScreenManager_add(this->scr, (Panel*) rightMeters, 20);
ScreenManager_add(this->scr, availableMeters, -1);
}
static void CategoriesPanel_makeDisplayOptionsPage(CategoriesPanel* this) {
Panel* displayOptions = (Panel*) DisplayOptionsPanel_new(this->settings, this->scr);
ScreenManager_add(this->scr, displayOptions, -1);
}
static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
Panel* colors = (Panel*) ColorsPanel_new(this->settings, this->scr);
ScreenManager_add(this->scr, colors, -1);
}
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns);
ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, -1);
}
static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
CategoriesPanel* this = (CategoriesPanel*) super; CategoriesPanel* this = (CategoriesPanel*) super;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
int selected = Panel_getSelectedIndex(super); int previous = Panel_getSelectedIndex(super);
switch (ch) { switch (ch) {
case EVENT_SET_SELECTED:
result = HANDLED;
break;
case KEY_UP: case KEY_UP:
case KEY_CTRL('P'):
case KEY_DOWN: case KEY_DOWN:
case KEY_CTRL('N'):
case KEY_NPAGE: case KEY_NPAGE:
case KEY_PPAGE: case KEY_PPAGE:
case KEY_HOME: case KEY_HOME:
case KEY_END: { case KEY_END: {
int previous = selected;
Panel_onKey(super, ch); Panel_onKey(super, ch);
selected = Panel_getSelectedIndex(super); int selected = Panel_getSelectedIndex(super);
if (previous != selected) if (previous != selected) {
result = HANDLED;
break;
}
default:
if (ch < 255 && isalpha(ch))
result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP)
result = IGNORED;
break;
}
if (result == HANDLED) {
int size = ScreenManager_size(this->scr); int size = ScreenManager_size(this->scr);
for (int i = 1; i < size; i++) for (int i = 1; i < size; i++)
ScreenManager_remove(this->scr, 1); ScreenManager_remove(this->scr, 1);
@ -122,31 +94,35 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
break; break;
} }
} }
result = HANDLED;
}
}
return result; return result;
} }
PanelClass CategoriesPanel_class = { void CategoriesPanel_makeMetersPage(CategoriesPanel* this) {
.super = { Panel* leftMeters = (Panel*) MetersPanel_new(this->settings, "Left column", this->settings->header->leftMeters, this->scr);
.extends = Class(Panel), Panel* rightMeters = (Panel*) MetersPanel_new(this->settings, "Right column", this->settings->header->rightMeters, this->scr);
.delete = CategoriesPanel_delete Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, leftMeters, rightMeters, this->scr);
}, ScreenManager_add(this->scr, leftMeters, FunctionBar_new(10, MetersFunctions, NULL, NULL), 20);
.eventHandler = CategoriesPanel_eventHandler ScreenManager_add(this->scr, rightMeters, FunctionBar_new(10, MetersFunctions, NULL, NULL), 20);
}; ScreenManager_add(this->scr, availableMeters, FunctionBar_new(10, AvailableMetersFunctions, NULL, NULL), -1);
}
CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl) {
CategoriesPanel* this = AllocThis(CategoriesPanel); void CategoriesPanel_makeDisplayOptionsPage(CategoriesPanel* this) {
Panel* super = (Panel*) this; Panel* displayOptions = (Panel*) DisplayOptionsPanel_new(this->settings, this->scr);
FunctionBar* fuBar = FunctionBar_new(CategoriesFunctions, NULL, NULL); ScreenManager_add(this->scr, displayOptions, FunctionBar_new(10, DisplayOptionsFunctions, NULL, NULL), -1);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); }
this->scr = scr; void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
this->settings = settings; Panel* colors = (Panel*) ColorsPanel_new(this->settings, this->scr);
this->header = header; ScreenManager_add(this->scr, colors, FunctionBar_new(10, ColorsFunctions, NULL, NULL), -1);
this->pl = pl; }
Panel_setHeader(super, "Setup");
Panel_add(super, (Object*) ListItem_new("Meters", 0)); void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
Panel_add(super, (Object*) ListItem_new("Display options", 0)); Panel* columns = (Panel*) ColumnsPanel_new(this->settings, this->scr);
Panel_add(super, (Object*) ListItem_new("Colors", 0)); Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(this->settings, columns, this->scr);
Panel_add(super, (Object*) ListItem_new("Columns", 0)); ScreenManager_add(this->scr, columns, FunctionBar_new(10, ColumnsFunctions, NULL, NULL), 20);
return this; ScreenManager_add(this->scr, availableColumns, FunctionBar_new(10, AvailableColumnsFunctions, NULL, NULL), -1);
} }

View File

@ -2,32 +2,40 @@
#ifndef HEADER_CategoriesPanel #ifndef HEADER_CategoriesPanel
#define HEADER_CategoriesPanel #define HEADER_CategoriesPanel
/*
htop - CategoriesPanel.h #include "AvailableMetersPanel.h"
(C) 2004-2011 Hisham H. Muhammad #include "MetersPanel.h"
Released under the GNU GPL, see the COPYING file #include "DisplayOptionsPanel.h"
in the source distribution for its full text. #include "ColumnsPanel.h"
*/ #include "ColorsPanel.h"
#include "AvailableColumnsPanel.h"
#include "Panel.h" #include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h" #include "debug.h"
#include "ProcessList.h" #include <assert.h>
typedef struct CategoriesPanel_ { typedef struct CategoriesPanel_ {
Panel super; Panel super;
ScreenManager* scr;
Settings* settings; Settings* settings;
Header* header; ScreenManager* scr;
ProcessList* pl;
} CategoriesPanel; } CategoriesPanel;
extern void CategoriesPanel_makeMetersPage(CategoriesPanel* this); CategoriesPanel* CategoriesPanel_new(Settings* settings, ScreenManager* scr);
extern PanelClass CategoriesPanel_class; void CategoriesPanel_delete(Object* object);
extern CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl); HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch);
void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
void CategoriesPanel_makeDisplayOptionsPage(CategoriesPanel* this);
void CategoriesPanel_makeColorsPage(CategoriesPanel* this);
void CategoriesPanel_makeColumnsPage(CategoriesPanel* this);
#endif #endif

418
ChangeLog
View File

@ -1,421 +1,3 @@
What's new in version 3.0.1
* Coverity fixes, CI improvements, documentation updates
* BUGFIX: Fix early exit with longer sysfs battery paths
* BUGFIX: Improve OOM output, fix sorting
(thanks to Christian Göttsche)
* Rework check buttons and tree open/closed
(thanks to Bert Wesarg)
* Add -U/--no-unicode option to disable unicode
(thanks to Christian Hesse)
* Improvements to the affinity panel
(thanks to Bert Wesarg)
What's new in version 3.0.0
* New maintainers - after a prolonged period of inactivity
from Hisham, the creator and original maintainer, a team
of community maintainers have volunteered to take over a
fork at https://htop.dev and https://github.com/htop-dev
to keep the project going.
* Support ZFS ARC statistics
(thanks to Ross Williams)
* Support more than 2 smaller CPU meter columns
(thanks to Christoph Budziszewski)
* Support Linux proportional set size metrics
(thanks to @linvinus, @ntninja and @himikof)
* Support Linux pressure stall information metrics
(thanks to Ran Benita)
* New display option to show CPU frequency in CPU meters
(thanks to Arnav Singh)
* Update Linux sysfs battery discovery for recent kernels
(thanks to @smattie)
* Add hardware topology information in the affinity panel
(thanks to Bert Wesarg)
* Add timestamp reporting to the strace screen
(thanks to Mario Harjac)
* Add simple, optional vim key mapping mode
(thanks to Daniel Flanagan)
* Added an option to disable the mouse
(thanks to MartinJM)
* Add Solaris11 compatibility
(thanks to Jan Senolt)
* Without an argument -u uses $USER value automatically
(thanks to @solanav)
* Support less(1) search navigation shortcuts
(thanks to @syrrim)
* Update the FreeBSD maximum PID to match FreeBSD change
(thanks to @multiplexd)
* Report values larger than 100 terabytes
(thanks to @adrien1018)
* Widen ST_UID (UID) column to allow for UIDs > 9999
(thanks to DLange)
* BUGFIX: fix makefiles for building with clang
(thanks to Jorge Pereira)
* BUGFIX: fix <sys/sysmacros.h> major() usage
(thanks to @wataash and Kang-Che Sung)
* BUGFIX: fix the STARTTIME column on FreeBSD
(thanks to Rob Crowston)
* BUGFIX: truncate overwide jail names on FreeBSD
(thanks to Rob Crowston)
* BUGFIX: fix reported memory values on FreeBSD
(thanks to Tobias Kortkamp)
* BUGFIX: fix reported CPU meter values on OpenBSD
(thanks to @motet-a)
* BUGFIX: correctly identify other types of zombie process
(thanks to @joder)
* BUGFIX: improve follow-process handling in some situations
(thanks to @wangqr)
* BUGFIX: fix custom meters reverting to unexpected setting
(thanks to @wangqr)
* BUGFIX: close pipe after running lsof(1)
(thanks to Jesin)
* BUGFIX: meters honour setting of counting CPUs from 0/1
(thanks to @rnsanchez)
What's new in version 2.2.0
* Solaris/Illumos/OpenIndiana support
(thanks to Guy M. Broome)
* -t/--tree flag for starting in tree-view mode
(thanks to Daniel Flanagan)
* macOS: detects High Sierra version to avoid OS bug
(thanks to Pierre Malhaire)
* OpenBSD: read battery data
(thanks to @nerd972)
* Various automake and build improvements
(thanks to Kang-Che Sung)
* Check for pkg-config when building with --enable-delayacct
(thanks to @florian2833z for the report)
* Avoid some bashisms in configure script
(thanks to Jesin)
* Use CFLAGS from ncurses*-config if present
(thanks to Michael Klein)
* Header generator supports non-UTF-8 environments
(thanks to @volkov-am)
* Linux: changed detection of kernel threads
* Collapse current subtree pressing Backspace
* BUGFIX: fix behavior of SYSCR column
(thanks to Marc Kleine-Budde)
* BUGFIX: obtain exit code of lsof correctly
(thanks to @wangqr)
* BUGFIX: fix crash with particular keycodes
(thanks to Wellington Torrejais da Silva for the report)
* BUGFIX: fix issue with small terminals
(thanks to Daniel Elf for the report)
* BUGFIX: fix terminal color issues
(thanks to Kang-Che Sung for the report)
* BUGFIX: preserve LDFLAGS when building
(thanks to Lance Frederickson for the report)
* BUGFIX: fixed overflow for systems with >= 100 signals
What's new in version 2.1.0
* Linux: Delay accounting metrics
(thanks to André Carvalho)
* DragonFlyBSD support
(thanks to Diederik de Groot)
* Support for real-time signals
(thanks to Kang-Che Sung)
* 'c' key now works with threads as well
* Session column renamed from SESN to SID
(thanks to Kamyar Rasta)
* Improved UI for meter style selection
(thanks to Kang-Che Sung)
* Improved code for constructing process tree
(thanks to wangqr)
* Compile-time option to disable setuid
* Error checking of various standard library operations
* Replacement of sprintf with snprintf
(thanks to Tomasz Kramkowski)
* Linux: performance improvements in battery meter
* Linux: update process TTY device
* Linux: add support for sorting TASK_IDLE
(thanks to Vladimir Panteleev)
* Linux: add upper-bound to running process counter
(thanks to Lucas Correia Villa Real)
* BUGFIX: avoid crash when battery is removed
(thanks to Jan Chren)
* BUGFIX: macOS: fix infinite loop in tree view
(thanks to Wataru Ashihara)
What's new in version 2.0.2
* Mac OS X: stop trying when task_for_pid fails for a process,
stops spamming logs with errors.
* Add Ctrl+A and Ctrl+E to go to beginning and end of line
* FreeBSD: fixes for CPU calculation
(thanks to Tim Creech, Andy Pilate)
* Usability: auto-follow process after a search.
* Use Linux backend on GNU Hurd
* Improvement for reproducible builds.
* BUGFIX: Fix behavior of Alt-key combinations
(thanks to Kang-Che Sung)
* Various code tweaks and cleanups
(thanks to Kang-Che Sung)
What's new in version 2.0.1
* OpenBSD: Various fixes and improvements
(thanks to Michael McConville and Juan Francisco Cantero Hurtado)
* FreeBSD: fix CPU and memory readings
(thanks to Tim Creech, Hung-Yi Chen, Bernard Spil, Greg V)
* FreeBSD: add battery support
(thanks to Greg V)
* Linux: Retain last-obtained name of a zombie process
* Mac OS X: Improve portability for OS X versions
(thanks to Michael Klein)
* Mac OS X: Fix reading command-line arguments and basename
* Mac OS X: Fix process state information
* Mac OS X: Fix tree view collapsing/expanding
* Mac OS X: Fix tree organization
* Mac OS X: Fix memory accounting
* Fix crash when emptying a column of meters
* Make Esc key more responsive
What's new in version 2.0.0
* Platform abstraction layer
* Initial FreeBSD support
* Initial Mac OS X support
(thanks to David Hunt)
* Swap meter for Mac OSX
(thanks to Ștefan Rusu)
* OpenBSD port
(thanks to Michael McConville)
* FreeBSD support improvements
(thanks to Martin Misuth)
* Support for NCurses 6 ABI, including mouse wheel support
* Much improved mouse responsiveness
* Process environment variables screen
(thanks to Michael Klein)
* Higher-resolution UTF-8 based Graph mode
(Thanks to James Hall from vtop for the idea!)
* Show program path settings
(thanks to Tobias Geerinckx-Rice)
* BUGFIX: Fix crash when scrolling an empty filtered list.
* Use dynamic units for text display, and several fixes
(thanks to Christian Hesse)
* BUGFIX: fix error caused by overflow in usertime calculation.
(thanks to Patrick Marlier)
* Catch all memory allocation errors
(thanks to Michael McConville for the push)
* Several tweaks and bugfixes
(See the Git log for details and contributors!)
What's new in version 1.0.3
* Tag all children ('c' key)
* Fixes in accounting of guest time when using virtualization
(thanks to Patrick Marlier)
* Performance improvements
(thanks to Jann Horn)
* Further performance improvements due to conditional parsing
of IO data depending on selected fields.
* Better consistency in coloring.
* Increase limit of buffer when tracing a deep nested process tree.
* Display pagefault stats.
* BUGFIX: Fix crash when adding meters and toggling detailed CPU time.
(thanks to Dawid Gajownik)
* Add column to track the OOM-killer score of processes
(thanks to Leigh Simpson)
What's new in version 1.0.2
* Add IO priority support ('i' key)
* Avoid deleting .htoprc if it is a symlink
* Fail gracefully when /proc is not mounted
(thanks to Philipp Hagemeister)
* Option to update process names on every refresh
(thanks to Rob Hoelz)
* BUGFIX: Fix crashes when process list is empty
What's new in version 1.0.1
* Move .htoprc to XDG-compliant path ~/.config/htop/htoprc,
respecting $XDG_CONFIG_HOME
(thanks to Hadzhimurad Ustarkhan for the suggestion.)
* Safer behavior on the kill screen, to make it harder to kill the wrong process.
* Fix for building in FreeBSD 8.2
(thanks to Trond Endrestol)
* BUGFIX: behavior of 'F' (follow) key was broken, also affecting the
persistence of mouse selections.
* BUGFIX: keep main panel up-to-date when running the screen manager,
to fix crash when processes die while on the F9/Kill screen.
What's new in version 1.0
* Performance improvements
* Support for splitting CPU meters into two or four columns
(thanks to Wim Heirman)
* Switch from PLPA, which is now deprecated, to HWLOC.
* Bring back support for native Linux sched_setaffinity,
so we don't have to use HWLOC where we don't need to.
* Support for typing in user names and column fields in selection panels.
* Support for UTF-8 tree drawing
(thanks to Bin Guo)
* Option for counting CPUs from zero
(thanks to Sean Noonan)
* Meters update in every screen (no longer halting while on Setup, etc.)
* Stricter checks for command-line options
(thanks to Sebastian Pipping)
* Incremental filtering
(thanks to Seth Heeren for the idea and initial implementation)
* Try harder to find the ncurses header
(thanks to Moritz Barsnick)
* Man page updates
(thanks to Vincent Launchbury)
* BUGFIX: Support larger numbers for process times.
(thanks to Tristan Nakagawa for the report.)
* BUGFIX: Segfault in BarMeterMode_draw() for small terminal widths
(patch by Sebastian Pipping)
What's new in version 0.9
* Add support for "steal"/guest CPU time measurement
in virtualization environments
* Expand and collapse subtrees using '+' and '-' when in tree-view
* Support for cgroups
(thanks to Guillaume Zitta and Daniel Lezcano)
* Show custom thread names
(thanks to Anders Torger)
* Add support for STARTTIME field
* Upgrade PLPA to version 1.3.2
* Fix license terms with regard to PLPA
(thanks to Tom Callaway)
* getopt-based long options and --no-color
(thanks to Vincent Launchbury)
* BUGFIX: Fix display of nan% in CPU meters
(thanks to Steven Hampson)
* BUGFIX: Fix memory leak
(thanks to Pavol Rusnak)
* Add Bash/emacs style navigation keys
(thanks to Daniel Schuler)
* Improve battery meter support
(thanks to Richard W.)
* BUGFIX: Fix IO-wait color in "Black on White" scheme
* BUGFIX: Fix search by process name when list is filtered by user.
(thanks to Sergej Pupykin for the report.)
* BUGFIX: Fix alignment for display of memory values above 100G (sign of the times!)
(thanks to Jan van Haarst for the report.)
What's new in version 0.8.3
* BUGFIX: Fix crash on F6 key
(thanks to Rainer Suhm)
* BUGFIX: Fix a minor bug which affected the build process.
What's new in version 0.8.2
* Integrated lsof (press 'l')
* Fix display of gigabyte-sized values
(thanks to Andika Triwidada)
* Option to display hostname in the meters area
* Rename VEID to CTID in OpenVZ systems
(thanks to Thorsten Schifferdecker)
* Corrections to the desktop entry file
(thanks by Samuli Suominen)
* BUGFIX: Correct page size calculation for FreeBSD systems
(thanks to Andrew Paulsen)
* Allow compilation without PLPA on systems that don't support it
(thanks to Timothy Redaelli)
* BUGFIX: Fix missing tree view when userland threads are hidden
(thanks to Josh Stone)
* BUGFIX: Fix for VPID on OpenVZ systems
(thanks to Wolfgang Frisch)
What's new in version 0.8.1
* Linux-VServer support
(thanks to Jonathan Sambrook and Benedikt Bohm)
* Battery meter
(thanks to Ian Page Hands)
* BUGFIX: Fix collection of IO stats in multithreaded processes
(thanks to Gerhard Heift)
* Remove assertion that fails on hardened kernels
(thanks to Wolfram Schlich for the report)
What's new in version 0.8
* Ability to change sort column with the mouse by
clicking column titles (click again to invert order)
* Add support for Linux per-process IO statistics,
enabled with the --enable-taskstats flag, which
requires a kernel compiled with taskstats support.
(thanks to Tobias Oetiker)
* Add Unicode support, enabled with the --enable-unicode
flag, which requires libncursesw.
(thanks to Sergej Pupykin)
* BUGFIX: Fix display of CPU count for threaded processes.
When user threads are hidden, process now shows the
sum of processor usage for all processors. When user
threads are displayed, each thread shows its own
processor usage, including the root thread.
(thanks to Bert Wesarg for the report)
* BUGFIX: avoid crashing when using many meters
(thanks to David Cho for the report)
What's new in version 0.7
* CPU affinity configuration ('a' key)
* Improve display of tree view, properly nesting
threads of the same app based on TGID.
* IO-wait time now counts as idle time, which is a more
accurate description. It is still available in
split time, now called detailed CPU time.
(thanks to Samuel Thibault for the report)
* BUGFIX: Correct display of TPGID field
* Add TGID field
* BUGFIX: Don't crash with invalid command-line flags
(thanks to Nico Golde for the report)
* Fix GCC 4.3 compilation issues
(thanks to Martin Michlmayr for the report)
* OpenVZ support, enabled at compile-time with
the --enable-openvz flag.
(thanks to Sergey Lychko)
What's new in version 0.6.6
* Add support of NLWP field
(thanks to Bert Wesarg)
* BUGFIX: Fix use of configurable /proc location
(thanks to Florent Thoumie)
* Fix memory percentage calculation and make it saner
(thanks to Olev Kartau for the report)
* Added display of DRS, DT, LRS and TRS
(thanks to Matthias Lederhofer)
* BUGFIX: LRS and DRS memory values were flipped
(thanks to Matthias Lederhofer)
* BUGFIX: Don't crash on very high UIDs
(thanks to Egmont Koblinger)
What's new in version 0.6.5
* Add hardened-debug flags for debugging with Hardened GCC
* BUGFIX: Handle error condition when a directory vanishes
from /proc
* BUGFIX: Fix leak of process command line
* BUGFIX: Collect orphaned items when arranging the tree view.
(thanks to Wolfram Schlich for assistance with debugging)
* Separate proc and memory debugging into separate #defines.
* BUGFIX: Fix message when configure fails due to
missing libraries
(thanks to Jon)
* BUGFIX: Don't truncate value when displaying a very large
process
(thanks to Bo Liu)
What's new in version 0.6.4
* Add an option to split the display of kernel time
in the CPU meter into system, IO-wait, IRQ and soft-IRQ.
(thanks to Philipp Richter)
* --sort-key flag in the command-line, overriding the
saved setting in .htoprc for the session.
(thanks to Rodolfo Borges)
* BUGFIX: Fixed string overflow on uptime display.
(thanks to Marc Cahalan)
What's new in version 0.6.3 What's new in version 0.6.3

View File

@ -1,30 +1,43 @@
/* /*
htop - CheckItem.c htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "CheckItem.h" #include "CheckItem.h"
#include "Object.h"
#include "CRT.h" #include "CRT.h"
#include <assert.h> #include "debug.h"
#include <stdlib.h>
/*{ /*{
#include "Object.h"
typedef struct CheckItem_ { typedef struct CheckItem_ {
Object super; Object super;
char* text; char* text;
bool* ref; bool* value;
bool value;
} CheckItem; } CheckItem;
}*/ }*/
static void CheckItem_delete(Object* cast) { #ifdef DEBUG
char* CHECKITEM_CLASS = "CheckItem";
#else
#define CHECKITEM_CLASS NULL
#endif
CheckItem* CheckItem_new(char* text, bool* value) {
CheckItem* this = malloc(sizeof(CheckItem));
Object_setClass(this, CHECKITEM_CLASS);
((Object*)this)->display = CheckItem_display;
((Object*)this)->delete = CheckItem_delete;
this->text = text;
this->value = value;
return this;
}
void CheckItem_delete(Object* cast) {
CheckItem* this = (CheckItem*)cast; CheckItem* this = (CheckItem*)cast;
assert (this != NULL); assert (this != NULL);
@ -32,49 +45,14 @@ static void CheckItem_delete(Object* cast) {
free(this); free(this);
} }
static void CheckItem_display(Object* cast, RichString* out) { void CheckItem_display(Object* cast, RichString* out) {
CheckItem* this = (CheckItem*)cast; CheckItem* this = (CheckItem*)cast;
assert (this != NULL); assert (this != NULL);
RichString_write(out, CRT_colors[CHECK_BOX], "["); RichString_write(out, CRT_colors[CHECK_BOX], "[");
if (CheckItem_get(this)) if (*(this->value))
RichString_append(out, CRT_colors[CHECK_MARK], "x"); RichString_append(out, CRT_colors[CHECK_MARK], "x");
else else
RichString_append(out, CRT_colors[CHECK_MARK], " "); RichString_append(out, CRT_colors[CHECK_MARK], " ");
RichString_append(out, CRT_colors[CHECK_BOX], "] "); RichString_append(out, CRT_colors[CHECK_BOX], "] ");
RichString_append(out, CRT_colors[CHECK_TEXT], this->text); RichString_append(out, CRT_colors[CHECK_TEXT], this->text);
} }
ObjectClass CheckItem_class = {
.display = CheckItem_display,
.delete = CheckItem_delete
};
CheckItem* CheckItem_newByRef(char* text, bool* ref) {
CheckItem* this = AllocThis(CheckItem);
this->text = text;
this->value = false;
this->ref = ref;
return this;
}
CheckItem* CheckItem_newByVal(char* text, bool value) {
CheckItem* this = AllocThis(CheckItem);
this->text = text;
this->value = value;
this->ref = NULL;
return this;
}
void CheckItem_set(CheckItem* this, bool value) {
if (this->ref)
*(this->ref) = value;
else
this->value = value;
}
bool CheckItem_get(CheckItem* this) {
if (this->ref)
return *(this->ref);
else
return this->value;
}

View File

@ -3,30 +3,35 @@
#ifndef HEADER_CheckItem #ifndef HEADER_CheckItem
#define HEADER_CheckItem #define HEADER_CheckItem
/* /*
htop - CheckItem.h htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Object.h" #include "Object.h"
#include "CRT.h"
#include "debug.h"
typedef struct CheckItem_ { typedef struct CheckItem_ {
Object super; Object super;
char* text; char* text;
bool* ref; bool* value;
bool value;
} CheckItem; } CheckItem;
extern ObjectClass CheckItem_class; #ifdef DEBUG
extern char* CHECKITEM_CLASS;
#else
#define CHECKITEM_CLASS NULL
#endif
extern CheckItem* CheckItem_newByRef(char* text, bool* ref); CheckItem* CheckItem_new(char* text, bool* value);
extern CheckItem* CheckItem_newByVal(char* text, bool value); void CheckItem_delete(Object* cast);
extern void CheckItem_set(CheckItem* this, bool value); void CheckItem_display(Object* cast, RichString* out);
extern bool CheckItem_get(CheckItem* this);
#endif #endif

View File

@ -1,43 +1,36 @@
/* /*
htop - ClockMeter.c htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "ClockMeter.h" #include "ClockMeter.h"
#include "Meter.h"
#include "CRT.h"
#include <time.h> #include <time.h>
/*{ #include "debug.h"
#include "Meter.h"
}*/
int ClockMeter_attributes[] = { int ClockMeter_attributes[] = {
CLOCK CLOCK
}; };
static void ClockMeter_updateValues(Meter* this, char* buffer, int size) { MeterType ClockMeter = {
time_t t = time(NULL); .setValues = ClockMeter_setValues,
struct tm result; .display = NULL,
struct tm *lt = localtime_r(&t, &result); .mode = TEXT_METERMODE,
this->values[0] = lt->tm_hour * 60 + lt->tm_min; .total = 100.0,
strftime(buffer, size, "%H:%M:%S", lt); .items = 1,
}
MeterClass ClockMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete
},
.updateValues = ClockMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 1440, /* 24*60 */
.attributes = ClockMeter_attributes, .attributes = ClockMeter_attributes,
.name = "Clock", .name = "Clock",
.uiName = "Clock", .uiName = "Clock",
.caption = "Time: ", .caption = "Time: ",
}; };
void ClockMeter_setValues(Meter* this, char* buffer, int size) {
time_t t = time(NULL);
struct tm *lt = localtime(&t);
this->values[0] = lt->tm_hour * 60 + lt->tm_min;
strftime(buffer, size, "%H:%M:%S", lt);
}

View File

@ -3,16 +3,22 @@
#ifndef HEADER_ClockMeter #ifndef HEADER_ClockMeter
#define HEADER_ClockMeter #define HEADER_ClockMeter
/* /*
htop - ClockMeter.h htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.h"
#include <time.h>
#include "debug.h"
extern int ClockMeter_attributes[]; extern int ClockMeter_attributes[];
extern MeterClass ClockMeter_class; extern MeterType ClockMeter;
void ClockMeter_setValues(Meter* this, char* buffer, int size);
#endif #endif

View File

@ -1,60 +1,70 @@
/*
htop - ColorsPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ColorsPanel.h"
#include "CRT.h" #include "CRT.h"
#include "CheckItem.h" #include "ColorsPanel.h"
#include "Panel.h"
#include "CheckItem.h"
#include "Settings.h"
#include "ScreenManager.h"
#include "debug.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h>
#include <string.h>
// TO ADD A NEW SCHEME: // TO ADD A NEW SCHEME:
// * Increment the size of bool check in ColorsPanel.h // * Increment the size of bool check in ColorsPanel.h
// * Add the entry in the ColorSchemeNames array below in the file // * Add the entry in the ColorSchemes array below in the file
// * Add a define in CRT.h that matches the order of the array // * Add a define in CRT.h that matches the order of the array
// * Add the colors in CRT_setColors // * Add the colors in CRT_setColors
/*{ /*{
#include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
typedef struct ColorsPanel_ { typedef struct ColorsPanel_ {
Panel super; Panel super;
Settings* settings; Settings* settings;
ScreenManager* scr; ScreenManager* scr;
bool check[5];
} ColorsPanel; } ColorsPanel;
}*/ }*/
static const char* const ColorsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; static char* ColorSchemes[] = {
static const char* const ColorSchemeNames[] = {
"Default", "Default",
"Monochromatic", "Monochromatic",
"Black on White", "Black on White",
"Light Terminal", "Light Terminal",
"MC", "MC",
"Black Night", "Black Night",
"Broken Gray",
NULL NULL
}; };
static void ColorsPanel_delete(Object* object) { ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) {
ColorsPanel* this = (ColorsPanel*) malloc(sizeof(ColorsPanel));
Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, CHECKITEM_CLASS, true);
((Object*)this)->delete = ColorsPanel_delete;
this->settings = settings;
this->scr = scr;
super->eventHandler = ColorsPanel_EventHandler;
Panel_setHeader(super, "Colors");
for (int i = 0; ColorSchemes[i] != NULL; i++) {
Panel_add(super, (Object*) CheckItem_new(String_copy(ColorSchemes[i]), &(this->check[i])));
this->check[i] = false;
}
this->check[settings->colorScheme] = true;
return this;
}
void ColorsPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
ColorsPanel* this = (ColorsPanel*) object; ColorsPanel* this = (ColorsPanel*) object;
Panel_done(super); Panel_done(super);
free(this); free(this);
} }
static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) { HandlerResult ColorsPanel_EventHandler(Panel* super, int ch) {
ColorsPanel* this = (ColorsPanel*) super; ColorsPanel* this = (ColorsPanel*) super;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
@ -64,22 +74,20 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
case 0x0a: case 0x0a:
case 0x0d: case 0x0d:
case KEY_ENTER: case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
case ' ': case ' ':
for (int i = 0; ColorSchemeNames[i] != NULL; i++) for (int i = 0; ColorSchemes[i] != NULL; i++) {
CheckItem_set((CheckItem*)Panel_get(super, i), false); this->check[i] = false;
CheckItem_set((CheckItem*)Panel_get(super, mark), true); }
this->check[mark] = true;
this->settings->colorScheme = mark; this->settings->colorScheme = mark;
result = HANDLED; result = HANDLED;
} }
if (result == HANDLED) { if (result == HANDLED) {
this->settings->changed = true; this->settings->changed = true;
const Header* header = this->scr->header; Header* header = this->settings->header;
CRT_setColors(mark); CRT_setColors(mark);
clear(); Panel* menu = (Panel*) Vector_get(this->scr->items, 0);
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
Header_draw(header); Header_draw(header);
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]); RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);
RichString_setAttr(&(menu->header), CRT_colors[PANEL_HEADER_UNFOCUS]); RichString_setAttr(&(menu->header), CRT_colors[PANEL_HEADER_UNFOCUS]);
@ -88,27 +96,3 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
return result; return result;
} }
PanelClass ColorsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ColorsPanel_delete
},
.eventHandler = ColorsPanel_eventHandler
};
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) {
ColorsPanel* this = AllocThis(ColorsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColorsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);
this->settings = settings;
this->scr = scr;
Panel_setHeader(super, "Colors");
for (int i = 0; ColorSchemeNames[i] != NULL; i++) {
Panel_add(super, (Object*) CheckItem_newByVal(xStrdup(ColorSchemeNames[i]), false));
}
CheckItem_set((CheckItem*)Panel_get(super, settings->colorScheme), true);
return this;
}

View File

@ -2,33 +2,38 @@
#ifndef HEADER_ColorsPanel #ifndef HEADER_ColorsPanel
#define HEADER_ColorsPanel #define HEADER_ColorsPanel
/*
htop - ColorsPanel.h #include "CRT.h"
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file #include "Panel.h"
in the source distribution for its full text. #include "CheckItem.h"
*/ #include "Settings.h"
#include "ScreenManager.h"
#include "debug.h"
#include <assert.h>
// TO ADD A NEW SCHEME: // TO ADD A NEW SCHEME:
// * Increment the size of bool check in ColorsPanel.h // * Increment the size of bool check in ColorsPanel.h
// * Add the entry in the ColorSchemeNames array below in the file // * Add the entry in the ColorSchemes array below in the file
// * Add a define in CRT.h that matches the order of the array // * Add a define in CRT.h that matches the order of the array
// * Add the colors in CRT_setColors // * Add the colors in CRT_setColors
#include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
typedef struct ColorsPanel_ { typedef struct ColorsPanel_ {
Panel super; Panel super;
Settings* settings; Settings* settings;
ScreenManager* scr; ScreenManager* scr;
bool check[5];
} ColorsPanel; } ColorsPanel;
extern PanelClass ColorsPanel_class; ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr);
void ColorsPanel_delete(Object* object);
HandlerResult ColorsPanel_EventHandler(Panel* super, int ch);
extern ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr);
#endif #endif

View File

@ -1,72 +1,75 @@
/*
htop - ColumnsPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ColumnsPanel.h" #include "ColumnsPanel.h"
#include "Platform.h"
#include "StringUtils.h"
#include "ListItem.h"
#include "CRT.h"
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
/*{
#include "Panel.h" #include "Panel.h"
#include "Settings.h" #include "Settings.h"
#include "ScreenManager.h"
#include "debug.h"
#include <assert.h>
/*{
typedef struct ColumnsPanel_ { typedef struct ColumnsPanel_ {
Panel super; Panel super;
Settings* settings; Settings* settings;
bool moving; ScreenManager* scr;
} ColumnsPanel; } ColumnsPanel;
}*/ }*/
static const char* const ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL}; ColumnsPanel* ColumnsPanel_new(Settings* settings, ScreenManager* scr) {
ColumnsPanel* this = (ColumnsPanel*) malloc(sizeof(ColumnsPanel));
Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, LISTITEM_CLASS, true);
((Object*)this)->delete = ColumnsPanel_delete;
static void ColumnsPanel_delete(Object* object) { this->settings = settings;
this->scr = scr;
super->eventHandler = ColumnsPanel_eventHandler;
Panel_setHeader(super, "Active Columns");
ProcessField* fields = this->settings->pl->fields;
for (; *fields; fields++) {
Panel_add(super, (Object*) ListItem_new(Process_fieldNames[*fields], 0));
}
return this;
}
void ColumnsPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
ColumnsPanel* this = (ColumnsPanel*) object; ColumnsPanel* this = (ColumnsPanel*) object;
Panel_done(super); Panel_done(super);
free(this); free(this);
} }
static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) { void ColumnsPanel_update(Panel* super) {
ColumnsPanel* const this = (ColumnsPanel*) super; ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_getSize(super);
this->settings->changed = true;
// FIXME: this is crappily inefficient
free(this->settings->pl->fields);
this->settings->pl->fields = (ProcessField*) malloc(sizeof(ProcessField) * (size+1));
for (int i = 0; i < size; i++) {
char* text = ((ListItem*) Panel_get(super, i))->value;
for (int j = 1; j <= LAST_PROCESSFIELD; j++) {
if (String_eq(text, Process_fieldNames[j])) {
this->settings->pl->fields[i] = j;
break;
}
}
}
this->settings->pl->fields[size] = 0;
}
HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
int selected = Panel_getSelectedIndex(super); int selected = Panel_getSelectedIndex(super);
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
int size = Panel_size(super); int size = Panel_getSize(super);
switch(ch) { switch(ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
{
if (selected < size - 1) {
this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
((ListItem*)Panel_getSelected(super))->moving = this->moving;
result = HANDLED;
}
break;
}
case KEY_UP:
{
if (!this->moving) {
break;
}
}
/* else fallthrough */
case KEY_F(7): case KEY_F(7):
case '[': case '[':
case '-': case '-':
@ -76,13 +79,6 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
result = HANDLED; result = HANDLED;
break; break;
} }
case KEY_DOWN:
{
if (!this->moving) {
break;
}
}
/* else fallthrough */
case KEY_F(8): case KEY_F(8):
case ']': case ']':
case '+': case '+':
@ -101,66 +97,8 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
result = HANDLED; result = HANDLED;
break; break;
} }
default:
{
if (ch < 255 && isalpha(ch))
result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP)
result = IGNORED;
break;
}
} }
if (result == HANDLED) if (result == HANDLED)
ColumnsPanel_update(super); ColumnsPanel_update(super);
return result; return result;
} }
PanelClass ColumnsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ColumnsPanel_delete
},
.eventHandler = ColumnsPanel_eventHandler
};
ColumnsPanel* ColumnsPanel_new(Settings* settings) {
ColumnsPanel* this = AllocThis(ColumnsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->moving = false;
Panel_setHeader(super, "Active Columns");
ProcessField* fields = this->settings->fields;
for (; *fields; fields++) {
if (Process_fields[*fields].name) {
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
}
}
return this;
}
int ColumnsPanel_fieldNameToIndex(const char* name) {
for (int j = 1; j <= Platform_numberOfFields; j++) {
if (String_eq(name, Process_fields[j].name)) {
return j;
}
}
return -1;
}
void ColumnsPanel_update(Panel* super) {
ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size+1));
this->settings->flags = 0;
for (int i = 0; i < size; i++) {
int key = ((ListItem*) Panel_get(super, i))->key;
this->settings->fields[i] = key;
this->settings->flags |= Process_fields[key].flags;
}
this->settings->fields[size] = 0;
}

View File

@ -2,30 +2,30 @@
#ifndef HEADER_ColumnsPanel #ifndef HEADER_ColumnsPanel
#define HEADER_ColumnsPanel #define HEADER_ColumnsPanel
/*
htop - ColumnsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h" #include "Panel.h"
#include "Settings.h" #include "Settings.h"
#include "ScreenManager.h"
#include "debug.h"
#include <assert.h>
typedef struct ColumnsPanel_ { typedef struct ColumnsPanel_ {
Panel super; Panel super;
Settings* settings; Settings* settings;
bool moving; ScreenManager* scr;
} ColumnsPanel; } ColumnsPanel;
extern PanelClass ColumnsPanel_class; ColumnsPanel* ColumnsPanel_new(Settings* settings, ScreenManager* scr);
extern ColumnsPanel* ColumnsPanel_new(Settings* settings); void ColumnsPanel_delete(Object* object);
extern int ColumnsPanel_fieldNameToIndex(const char* name); void ColumnsPanel_update(Panel* super);
extern void ColumnsPanel_update(Panel* super); HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch);
#endif #endif

223
DebugMemory.c Normal file
View File

@ -0,0 +1,223 @@
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#undef strdup
#undef malloc
#undef realloc
#undef calloc
#undef free
#include "DebugMemory.h"
/*{
typedef struct DebugMemoryItem_ DebugMemoryItem;
struct DebugMemoryItem_ {
int magic;
void* data;
char* file;
int line;
DebugMemoryItem* next;
};
typedef struct DebugMemory_ {
DebugMemoryItem* first;
int allocations;
int deallocations;
int size;
bool totals;
FILE* file;
} DebugMemory;
}*/
#if defined(DEBUG)
static DebugMemory* singleton = NULL;
void DebugMemory_new() {
if (singleton)
return;
singleton = malloc(sizeof(DebugMemory));
singleton->first = NULL;
singleton->allocations = 0;
singleton->deallocations = 0;
singleton->size = 0;
singleton->file = fopen("/tmp/htop-debug-alloc.txt", "w");
singleton->totals = true;
//singleton->file = NULL;
}
void* DebugMemory_malloc(int size, char* file, int line, char* str) {
void* data = malloc(size);
DebugMemory_registerAllocation(data, file, line);
if (singleton->file) {
if (singleton->totals) fprintf(singleton->file, "%d\t", singleton->size);
fprintf(singleton->file, "%d\t%s:%d (%s)\n", size, file, line, str);
}
return data;
}
void* DebugMemory_calloc(int a, int b, char* file, int line) {
void* data = calloc(a, b);
DebugMemory_registerAllocation(data, file, line);
if (singleton->file) {
if (singleton->totals) fprintf(singleton->file, "%d\t", singleton->size);
fprintf(singleton->file, "%d\t%s:%d\n", a*b, file, line);
}
return data;
}
void* DebugMemory_realloc(void* ptr, int size, char* file, int line, char* str) {
if (ptr != NULL)
DebugMemory_registerDeallocation(ptr, file, line);
void* data = realloc(ptr, size);
DebugMemory_registerAllocation(data, file, line);
if (singleton->file) {
if (singleton->totals) fprintf(singleton->file, "%d\t", singleton->size);
fprintf(singleton->file, "%d\t%s:%d (%s)\n", size, file, line, str);
}
return data;
}
void* DebugMemory_strdup(char* str, char* file, int line) {
assert(str);
char* data = strdup(str);
DebugMemory_registerAllocation(data, file, line);
if (singleton->file) {
if (singleton->totals) fprintf(singleton->file, "%d\t", singleton->size);
fprintf(singleton->file, "%d\t%s:%d\n", (int) strlen(str), file, line);
}
return data;
}
void DebugMemory_free(void* data, char* file, int line) {
assert(data);
DebugMemory_registerDeallocation(data, file, line);
if (singleton->file) {
if (singleton->totals) fprintf(singleton->file, "%d\t", singleton->size);
fprintf(singleton->file, "free\t%s:%d\n", file, line);
}
free(data);
}
void DebugMemory_assertSize() {
if (!singleton->first) {
assert (singleton->size == 0);
}
DebugMemoryItem* walk = singleton->first;
int i = 0;
while (walk != NULL) {
assert(walk->magic == 11061980);
i++;
walk = walk->next;
}
assert (i == singleton->size);
}
int DebugMemory_getBlockCount() {
if (!singleton->first) {
return 0;
}
DebugMemoryItem* walk = singleton->first;
int i = 0;
while (walk != NULL) {
assert(walk->magic == 11061980);
i++;
walk = walk->next;
}
return i;
}
void DebugMemory_registerAllocation(void* data, char* file, int line) {
if (!singleton)
DebugMemory_new();
DebugMemory_assertSize();
DebugMemoryItem* item = (DebugMemoryItem*) malloc(sizeof(DebugMemoryItem));
item->magic = 11061980;
item->data = data;
item->file = file;
item->line = line;
item->next = NULL;
int val = DebugMemory_getBlockCount();
if (singleton->first == NULL) {
assert (val == 0);
singleton->first = item;
} else {
DebugMemoryItem* walk = singleton->first;
while (true) {
if (walk->next == NULL) {
walk->next = item;
break;
}
assert(walk->magic == 11061980);
walk = walk->next;
}
}
int nval = DebugMemory_getBlockCount();
assert(nval == val + 1);
singleton->allocations++;
singleton->size++;
DebugMemory_assertSize();
}
void DebugMemory_registerDeallocation(void* data, char* file, int line) {
assert(singleton);
assert(singleton->first);
DebugMemoryItem* walk = singleton->first;
DebugMemoryItem* prev = NULL;
int val = DebugMemory_getBlockCount();
while (walk != NULL) {
assert(walk->magic == 11061980);
if (walk->data == data) {
if (prev == NULL) {
singleton->first = walk->next;
} else {
prev->next = walk->next;
}
free(walk);
assert(DebugMemory_getBlockCount() == val - 1);
singleton->deallocations++;
singleton->size--;
DebugMemory_assertSize();
return;
}
DebugMemoryItem* tmp = walk;
walk = walk->next;
prev = tmp;
}
DebugMemory_report();
fprintf(stderr, "Couldn't find allocation for memory freed at %s:%d\n", file, line);
assert(false);
}
void DebugMemory_report() {
assert(singleton);
DebugMemoryItem* walk = singleton->first;
int i = 0;
while (walk != NULL) {
assert(walk->magic == 11061980);
i++;
fprintf(stderr, "%p %s:%d\n", walk->data, walk->file, walk->line);
walk = walk->next;
}
fprintf(stderr, "Total:\n");
fprintf(stderr, "%d allocations\n", singleton->allocations);
fprintf(stderr, "%d deallocations\n", singleton->deallocations);
fprintf(stderr, "%d size\n", singleton->size);
fprintf(stderr, "%d non-freed blocks\n", i);
if (singleton->file)
fclose(singleton->file);
}
#elif defined(DEBUGLITE)
//#include "efence.h"
#endif

71
DebugMemory.h Normal file
View File

@ -0,0 +1,71 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_DebugMemory
#define HEADER_DebugMemory
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#undef strdup
#undef malloc
#undef realloc
#undef calloc
#undef free
typedef struct DebugMemoryItem_ DebugMemoryItem;
struct DebugMemoryItem_ {
int magic;
void* data;
char* file;
int line;
DebugMemoryItem* next;
};
typedef struct DebugMemory_ {
DebugMemoryItem* first;
int allocations;
int deallocations;
int size;
bool totals;
FILE* file;
} DebugMemory;
#if defined(DEBUG)
void DebugMemory_new();
void* DebugMemory_malloc(int size, char* file, int line, char* str);
void* DebugMemory_calloc(int a, int b, char* file, int line);
void* DebugMemory_realloc(void* ptr, int size, char* file, int line, char* str);
void* DebugMemory_strdup(char* str, char* file, int line);
void DebugMemory_free(void* data, char* file, int line);
void DebugMemory_assertSize();
int DebugMemory_getBlockCount();
void DebugMemory_registerAllocation(void* data, char* file, int line);
void DebugMemory_registerDeallocation(void* data, char* file, int line);
void DebugMemory_report();
#elif defined(DEBUGLITE)
//#include "efence.h"
#endif
#endif

View File

@ -1,24 +1,16 @@
/*
htop - DisplayOptionsPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "DisplayOptionsPanel.h" #include "DisplayOptionsPanel.h"
#include "CheckItem.h"
#include "CRT.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
/*{
#include "Panel.h" #include "Panel.h"
#include "CheckItem.h"
#include "Settings.h" #include "Settings.h"
#include "ScreenManager.h" #include "ScreenManager.h"
#include "debug.h"
#include <assert.h>
/*{
typedef struct DisplayOptionsPanel_ { typedef struct DisplayOptionsPanel_ {
Panel super; Panel super;
@ -28,16 +20,35 @@ typedef struct DisplayOptionsPanel_ {
}*/ }*/
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr) {
DisplayOptionsPanel* this = (DisplayOptionsPanel*) malloc(sizeof(DisplayOptionsPanel));
Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, CHECKITEM_CLASS, true);
((Object*)this)->delete = DisplayOptionsPanel_delete;
static void DisplayOptionsPanel_delete(Object* object) { this->settings = settings;
this->scr = scr;
super->eventHandler = DisplayOptionsPanel_EventHandler;
Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_new(String_copy("Tree view"), &(settings->pl->treeView)));
Panel_add(super, (Object*) CheckItem_new(String_copy("Shadow other users' processes"), &(settings->pl->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_new(String_copy("Hide kernel threads"), &(settings->pl->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_new(String_copy("Hide userland threads"), &(settings->pl->hideUserlandThreads)));
Panel_add(super, (Object*) CheckItem_new(String_copy("Highlight program \"basename\""), &(settings->pl->highlightBaseName)));
Panel_add(super, (Object*) CheckItem_new(String_copy("Highlight megabytes in memory counters"), &(settings->pl->highlightMegabytes)));
Panel_add(super, (Object*) CheckItem_new(String_copy("Leave a margin around header"), &(settings->header->margin)));
return this;
}
void DisplayOptionsPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
DisplayOptionsPanel* this = (DisplayOptionsPanel*) object; DisplayOptionsPanel* this = (DisplayOptionsPanel*) object;
Panel_done(super); Panel_done(super);
free(this); free(this);
} }
static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) { HandlerResult DisplayOptionsPanel_EventHandler(Panel* super, int ch) {
DisplayOptionsPanel* this = (DisplayOptionsPanel*) super; DisplayOptionsPanel* this = (DisplayOptionsPanel*) super;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
@ -47,62 +58,18 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
case 0x0a: case 0x0a:
case 0x0d: case 0x0d:
case KEY_ENTER: case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
case ' ': case ' ':
CheckItem_set(selected, ! (CheckItem_get(selected)) ); *(selected->value) = ! *(selected->value);
result = HANDLED; result = HANDLED;
} }
if (result == HANDLED) { if (result == HANDLED) {
this->settings->changed = true; this->settings->changed = true;
const Header* header = this->scr->header; Header* header = this->settings->header;
Header_calculateHeight((Header*) header); Header_calculateHeight(header);
Header_reinit((Header*) header);
Header_draw(header); Header_draw(header);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2); ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
} }
return result; return result;
} }
PanelClass DisplayOptionsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = DisplayOptionsPanel_delete
},
.eventHandler = DisplayOptionsPanel_eventHandler
};
DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr) {
DisplayOptionsPanel* this = AllocThis(DisplayOptionsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(DisplayOptionsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);
this->settings = settings;
this->scr = scr;
Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Display threads in a different color"), &(settings->highlightThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show custom thread names"), &(settings->showThreadNames)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show program path"), &(settings->showProgramPath)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight program \"basename\""), &(settings->highlightBaseName)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight large numbers in memory counters"), &(settings->highlightMegabytes)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Leave a margin around header"), &(settings->headerMargin)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)"), &(settings->detailedCPUTime)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU percentage numerically"), &(settings->showCPUUsage)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable the mouse"), &(settings->enableMouse)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable vim mode"), &(settings->vimMode)));
#ifdef HAVE_LIBHWLOC
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show topology when selecting affinity by default"), &(settings->topologyAffinity)));
#endif
return this;
}

View File

@ -2,17 +2,17 @@
#ifndef HEADER_DisplayOptionsPanel #ifndef HEADER_DisplayOptionsPanel
#define HEADER_DisplayOptionsPanel #define HEADER_DisplayOptionsPanel
/*
htop - DisplayOptionsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h" #include "Panel.h"
#include "CheckItem.h"
#include "Settings.h" #include "Settings.h"
#include "ScreenManager.h" #include "ScreenManager.h"
#include "debug.h"
#include <assert.h>
typedef struct DisplayOptionsPanel_ { typedef struct DisplayOptionsPanel_ {
Panel super; Panel super;
@ -21,8 +21,11 @@ typedef struct DisplayOptionsPanel_ {
} DisplayOptionsPanel; } DisplayOptionsPanel;
extern PanelClass DisplayOptionsPanel_class; DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr);
void DisplayOptionsPanel_delete(Object* object);
HandlerResult DisplayOptionsPanel_EventHandler(Panel* super, int ch);
extern DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr);
#endif #endif

View File

@ -1,66 +0,0 @@
#include "EnvScreen.h"
#include "config.h"
#include "CRT.h"
#include "IncSet.h"
#include "ListItem.h"
#include "Platform.h"
#include "StringUtils.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/*{
#include "InfoScreen.h"
typedef struct EnvScreen_ {
InfoScreen super;
} EnvScreen;
}*/
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, " ");
}
void EnvScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this));
}
void EnvScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, this->process->comm);
}
void EnvScreen_scan(InfoScreen* this) {
Panel* panel = this->display;
int idx = MAX(Panel_getSelectedIndex(panel), 0);
Panel_prune(panel);
CRT_dropPrivileges();
char* env = Platform_getProcessEnv(this->process->pid);
CRT_restorePrivileges();
if (env) {
for (char *p = env; *p; p = strrchr(p, 0)+1)
InfoScreen_addLine(this, p);
free(env);
}
else {
InfoScreen_addLine(this, "Could not read process environment.");
}
Vector_insertionSort(this->lines);
Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx);
}

View File

@ -1,22 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_EnvScreen
#define HEADER_EnvScreen
#include "InfoScreen.h"
typedef struct EnvScreen_ {
InfoScreen super;
} EnvScreen;
extern InfoScreenClass EnvScreen_class;
extern EnvScreen* EnvScreen_new(Process* process);
extern void EnvScreen_delete(Object* this);
extern void EnvScreen_draw(InfoScreen* this);
extern void EnvScreen_scan(InfoScreen* this);
#endif

View File

@ -1,24 +1,26 @@
/* /*
htop - FunctionBar.c htop - FunctionBar.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Object.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "CRT.h" #include "CRT.h"
#include "RichString.h"
#include "XAlloc.h"
#include "debug.h"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
#include <curses.h>
/*{ /*{
#include <stdbool.h>
typedef struct FunctionBar_ { typedef struct FunctionBar_ {
Object super;
int size; int size;
char** functions; char** functions;
char** keys; char** keys;
@ -28,79 +30,74 @@ typedef struct FunctionBar_ {
}*/ }*/
static const char* const FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL}; #ifdef DEBUG
char* FUNCTIONBAR_CLASS = "FunctionBar";
#else
#define FUNCTIONBAR_CLASS NULL
#endif
static const char* const FunctionBar_FLabels[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", NULL}; static char* FunctionBar_FKeys[10] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10"};
static int FunctionBar_FEvents[] = {KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10)}; static char* FunctionBar_FLabels[10] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", " "};
static const char* const FunctionBar_EnterEscKeys[] = {"Enter", "Esc", NULL}; static int FunctionBar_FEvents[10] = {KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10)};
static const int FunctionBar_EnterEscEvents[] = {13, 27};
FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc) { FunctionBar* FunctionBar_new(int size, char** functions, char** keys, int* events) {
const char* functions[] = {enter, esc, NULL}; FunctionBar* this = malloc(sizeof(FunctionBar));
return FunctionBar_new(functions, FunctionBar_EnterEscKeys, FunctionBar_EnterEscEvents); Object_setClass(this, FUNCTIONBAR_CLASS);
} ((Object*) this)->delete = FunctionBar_delete;
this->functions = functions;
FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events) { this->size = size;
FunctionBar* this = xCalloc(1, sizeof(FunctionBar));
this->functions = xCalloc(16, sizeof(char*));
if (!functions) {
functions = FunctionBar_FLabels;
}
for (int i = 0; i < 15 && functions[i]; i++) {
this->functions[i] = xStrdup(functions[i]);
}
if (keys && events) { if (keys && events) {
this->staticData = false; this->staticData = false;
this->keys = xCalloc(15, sizeof(char*)); this->functions = malloc(sizeof(char*) * size);
this->events = xCalloc(15, sizeof(int)); this->keys = malloc(sizeof(char*) * size);
int i = 0; this->events = malloc(sizeof(int) * size);
while (i < 15 && functions[i]) { for (int i = 0; i < size; i++) {
this->keys[i] = xStrdup(keys[i]); this->functions[i] = String_copy(functions[i]);
this->keys[i] = String_copy(keys[i]);
this->events[i] = events[i]; this->events[i] = events[i];
i++;
} }
this->size = i;
} else { } else {
this->staticData = true; this->staticData = true;
this->keys = (char**) FunctionBar_FKeys; this->functions = functions ? functions : FunctionBar_FLabels;
this->keys = FunctionBar_FKeys;
this->events = FunctionBar_FEvents; this->events = FunctionBar_FEvents;
this->size = 10; assert((!functions) || this->size == 10);
} }
return this; return this;
} }
void FunctionBar_delete(FunctionBar* this) { void FunctionBar_delete(Object* cast) {
for (int i = 0; i < 15 && this->functions[i]; i++) { FunctionBar* this = (FunctionBar*) cast;
free(this->functions[i]);
}
free(this->functions);
if (!this->staticData) { if (!this->staticData) {
for (int i = 0; i < this->size; i++) { for (int i = 0; i < this->size; i++) {
free(this->functions[i]);
free(this->keys[i]); free(this->keys[i]);
} }
free(this->functions);
free(this->keys); free(this->keys);
free(this->events); free(this->events);
} }
free(this); free(this);
} }
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) { void FunctionBar_setLabel(FunctionBar* this, int event, char* text) {
assert(!this->staticData);
for (int i = 0; i < this->size; i++) { for (int i = 0; i < this->size; i++) {
if (this->events[i] == event) { if (this->events[i] == event) {
free(this->functions[i]); free(this->functions[i]);
this->functions[i] = xStrdup(text); this->functions[i] = String_copy(text);
break; break;
} }
} }
} }
void FunctionBar_draw(const FunctionBar* this, char* buffer) { void FunctionBar_draw(FunctionBar* this, char* buffer) {
FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]); FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
} }
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) { void FunctionBar_drawAttr(FunctionBar* this, char* buffer, int attr) {
attrset(CRT_colors[FUNCTION_BAR]); attrset(CRT_colors[FUNCTION_BAR]);
mvhline(LINES-1, 0, ' ', COLS); mvhline(LINES-1, 0, ' ', COLS);
int x = 0; int x = 0;
@ -112,18 +109,14 @@ void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
mvaddstr(LINES-1, x, this->functions[i]); mvaddstr(LINES-1, x, this->functions[i]);
x += strlen(this->functions[i]); x += strlen(this->functions[i]);
} }
if (buffer) { if (buffer != NULL) {
attrset(attr); attrset(attr);
mvaddstr(LINES-1, x, buffer); mvaddstr(LINES-1, x, buffer);
CRT_cursorX = x + strlen(buffer);
curs_set(1);
} else {
curs_set(0);
} }
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
} }
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) { int FunctionBar_synthesizeEvent(FunctionBar* this, int pos) {
int x = 0; int x = 0;
for (int i = 0; i < this->size; i++) { for (int i = 0; i < this->size; i++) {
x += strlen(this->keys[i]); x += strlen(this->keys[i]);

View File

@ -4,15 +4,25 @@
#define HEADER_FunctionBar #define HEADER_FunctionBar
/* /*
htop - FunctionBar.h htop - FunctionBar.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Object.h"
#include "CRT.h"
#include "debug.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <curses.h>
typedef struct FunctionBar_ { typedef struct FunctionBar_ {
Object super;
int size; int size;
char** functions; char** functions;
char** keys; char** keys;
@ -21,19 +31,22 @@ typedef struct FunctionBar_ {
} FunctionBar; } FunctionBar;
#ifdef DEBUG
extern char* FUNCTIONBAR_CLASS;
#else
#define FUNCTIONBAR_CLASS NULL
#endif
extern FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc); FunctionBar* FunctionBar_new(int size, char** functions, char** keys, int* events);
extern FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events); void FunctionBar_delete(Object* cast);
extern void FunctionBar_delete(FunctionBar* this); void FunctionBar_setLabel(FunctionBar* this, int event, char* text);
extern void FunctionBar_setLabel(FunctionBar* this, int event, const char* text); void FunctionBar_draw(FunctionBar* this, char* buffer);
extern void FunctionBar_draw(const FunctionBar* this, char* buffer); void FunctionBar_drawAttr(FunctionBar* this, char* buffer, int attr);
extern void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr); int FunctionBar_synthesizeEvent(FunctionBar* this, int pos);
extern int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);
#endif #endif

View File

@ -1,25 +1,24 @@
/* /*
htop - Hashtable.c htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Hashtable.h" #include "Hashtable.h"
#include "XAlloc.h"
#include <stdlib.h> #include <stdlib.h>
#include <assert.h>
/*{
#include <stdbool.h> #include <stdbool.h>
#include "debug.h"
/*{
typedef struct Hashtable_ Hashtable; typedef struct Hashtable_ Hashtable;
typedef void(*Hashtable_PairFunction)(int, void*, void*); typedef void(*Hashtable_PairFunction)(int, void*, void*);
typedef struct HashtableItem { typedef struct HashtableItem {
unsigned int key; int key;
void* value; void* value;
struct HashtableItem* next; struct HashtableItem* next;
} HashtableItem; } HashtableItem;
@ -32,39 +31,10 @@ struct Hashtable_ {
}; };
}*/ }*/
#ifdef DEBUG HashtableItem* HashtableItem_new(int key, void* value) {
static bool Hashtable_isConsistent(Hashtable* this) {
int items = 0;
for (int i = 0; i < this->size; i++) {
HashtableItem* bucket = this->buckets[i];
while (bucket) {
items++;
bucket = bucket->next;
}
}
return items == this->items;
}
int Hashtable_count(Hashtable* this) {
int items = 0;
for (int i = 0; i < this->size; i++) {
HashtableItem* bucket = this->buckets[i];
while (bucket) {
items++;
bucket = bucket->next;
}
}
assert(items == this->items);
return items;
}
#endif
static HashtableItem* HashtableItem_new(unsigned int key, void* value) {
HashtableItem* this; HashtableItem* this;
this = xMalloc(sizeof(HashtableItem)); this = (HashtableItem*) malloc(sizeof(HashtableItem));
this->key = key; this->key = key;
this->value = value; this->value = value;
this->next = NULL; this->next = NULL;
@ -74,17 +44,14 @@ static HashtableItem* HashtableItem_new(unsigned int key, void* value) {
Hashtable* Hashtable_new(int size, bool owner) { Hashtable* Hashtable_new(int size, bool owner) {
Hashtable* this; Hashtable* this;
this = xMalloc(sizeof(Hashtable)); this = (Hashtable*) malloc(sizeof(Hashtable));
this->items = 0;
this->size = size; this->size = size;
this->buckets = (HashtableItem**) xCalloc(size, sizeof(HashtableItem*)); this->buckets = (HashtableItem**) calloc(sizeof(HashtableItem*), size);
this->owner = owner; this->owner = owner;
assert(Hashtable_isConsistent(this));
return this; return this;
} }
void Hashtable_delete(Hashtable* this) { void Hashtable_delete(Hashtable* this) {
assert(Hashtable_isConsistent(this));
for (int i = 0; i < this->size; i++) { for (int i = 0; i < this->size; i++) {
HashtableItem* walk = this->buckets[i]; HashtableItem* walk = this->buckets[i];
while (walk != NULL) { while (walk != NULL) {
@ -99,8 +66,12 @@ void Hashtable_delete(Hashtable* this) {
free(this); free(this);
} }
void Hashtable_put(Hashtable* this, unsigned int key, void* value) { inline int Hashtable_size(Hashtable* this) {
unsigned int index = key % this->size; return this->items;
}
void Hashtable_put(Hashtable* this, int key, void* value) {
int index = key % this->size;
HashtableItem** bucketPtr = &(this->buckets[index]); HashtableItem** bucketPtr = &(this->buckets[index]);
while (true) while (true)
if (*bucketPtr == NULL) { if (*bucketPtr == NULL) {
@ -114,53 +85,47 @@ void Hashtable_put(Hashtable* this, unsigned int key, void* value) {
break; break;
} else } else
bucketPtr = &((*bucketPtr)->next); bucketPtr = &((*bucketPtr)->next);
assert(Hashtable_isConsistent(this));
} }
void* Hashtable_remove(Hashtable* this, unsigned int key) { void* Hashtable_remove(Hashtable* this, int key) {
unsigned int index = key % this->size; int index = key % this->size;
HashtableItem** bucketPtr = &(this->buckets[index]);
assert(Hashtable_isConsistent(this)); while (true)
if (*bucketPtr == NULL) {
HashtableItem** bucket; return NULL;
for (bucket = &(this->buckets[index]); *bucket; bucket = &((*bucket)->next) ) { break;
if ((*bucket)->key == key) { } else if ((*bucketPtr)->key == key) {
void* value = (*bucket)->value; void* savedValue = (*bucketPtr)->value;
HashtableItem* next = (*bucket)->next; HashtableItem* savedNext = (*bucketPtr)->next;
free(*bucket); free(*bucketPtr);
(*bucket) = next; (*bucketPtr) = savedNext;
this->items--; this->items--;
if (this->owner) { if (this->owner) {
free(value); free(savedValue);
assert(Hashtable_isConsistent(this));
return NULL; return NULL;
} else { } else {
assert(Hashtable_isConsistent(this)); return savedValue;
return value;
} }
} } else
} bucketPtr = &((*bucketPtr)->next);
assert(Hashtable_isConsistent(this));
return NULL;
} }
//#include <stdio.h>
inline void* Hashtable_get(Hashtable* this, unsigned int key) { inline void* Hashtable_get(Hashtable* this, int key) {
unsigned int index = key % this->size; int index = key % this->size;
HashtableItem* bucketPtr = this->buckets[index]; HashtableItem* bucketPtr = this->buckets[index];
// fprintf(stderr, "%d -> %d\n", key, index);
while (true) { while (true) {
if (bucketPtr == NULL) { if (bucketPtr == NULL) {
assert(Hashtable_isConsistent(this));
return NULL; return NULL;
} else if (bucketPtr->key == key) { } else if (bucketPtr->key == key) {
assert(Hashtable_isConsistent(this));
return bucketPtr->value; return bucketPtr->value;
} else } else
bucketPtr = bucketPtr->next; bucketPtr = bucketPtr->next;
// fprintf(stderr, "*\n");
} }
} }
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) { void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) {
assert(Hashtable_isConsistent(this));
for (int i = 0; i < this->size; i++) { for (int i = 0; i < this->size; i++) {
HashtableItem* walk = this->buckets[i]; HashtableItem* walk = this->buckets[i];
while (walk != NULL) { while (walk != NULL) {
@ -168,5 +133,4 @@ void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData
walk = walk->next; walk = walk->next;
} }
} }
assert(Hashtable_isConsistent(this));
} }

View File

@ -3,20 +3,24 @@
#ifndef HEADER_Hashtable #ifndef HEADER_Hashtable
#define HEADER_Hashtable #define HEADER_Hashtable
/* /*
htop - Hashtable.h htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include "debug.h"
typedef struct Hashtable_ Hashtable; typedef struct Hashtable_ Hashtable;
typedef void(*Hashtable_PairFunction)(int, void*, void*); typedef void(*Hashtable_PairFunction)(int, void*, void*);
typedef struct HashtableItem { typedef struct HashtableItem {
unsigned int key; int key;
void* value; void* value;
struct HashtableItem* next; struct HashtableItem* next;
} HashtableItem; } HashtableItem;
@ -28,22 +32,20 @@ struct Hashtable_ {
bool owner; bool owner;
}; };
#ifdef DEBUG HashtableItem* HashtableItem_new(int key, void* value);
extern int Hashtable_count(Hashtable* this); Hashtable* Hashtable_new(int size, bool owner);
#endif void Hashtable_delete(Hashtable* this);
extern Hashtable* Hashtable_new(int size, bool owner); inline int Hashtable_size(Hashtable* this);
extern void Hashtable_delete(Hashtable* this); void Hashtable_put(Hashtable* this, int key, void* value);
extern void Hashtable_put(Hashtable* this, unsigned int key, void* value); void* Hashtable_remove(Hashtable* this, int key);
//#include <stdio.h>
extern void* Hashtable_remove(Hashtable* this, unsigned int key); inline void* Hashtable_get(Hashtable* this, int key);
extern void* Hashtable_get(Hashtable* this, unsigned int key); void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);
extern void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);
#endif #endif

208
Header.c
View File

@ -1,33 +1,30 @@
/* /*
htop - Header.c htop - Header.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Header.h" #include "Header.h"
#include "Meter.h"
#include "CRT.h" #include "debug.h"
#include "StringUtils.h"
#include "Platform.h"
#include <assert.h> #include <assert.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
/*{ /*{
#include "Meter.h"
#include "Settings.h" typedef enum HeaderSide_ {
#include "Vector.h" LEFT_HEADER,
RIGHT_HEADER
} HeaderSide;
typedef struct Header_ { typedef struct Header_ {
Vector** columns; Vector* leftMeters;
Settings* settings; Vector* rightMeters;
struct ProcessList_* pl; ProcessList* pl;
int nrColumns; bool margin;
int pad;
int height; int height;
int pad;
} Header; } Header;
}*/ }*/
@ -36,184 +33,137 @@ typedef struct Header_ {
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#ifndef Header_forEachColumn Header* Header_new(ProcessList* pl) {
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_)) Header* this = malloc(sizeof(Header));
#endif this->leftMeters = Vector_new(METER_CLASS, true, DEFAULT_SIZE, NULL);
this->rightMeters = Vector_new(METER_CLASS, true, DEFAULT_SIZE, NULL);
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) { this->margin = true;
Header* this = xCalloc(1, sizeof(Header));
this->columns = xCalloc(nrColumns, sizeof(Vector*));
this->settings = settings;
this->pl = pl; this->pl = pl;
this->nrColumns = nrColumns;
Header_forEachColumn(this, i) {
this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE);
}
return this; return this;
} }
void Header_delete(Header* this) { void Header_delete(Header* this) {
Header_forEachColumn(this, i) { Vector_delete(this->leftMeters);
Vector_delete(this->columns[i]); Vector_delete(this->rightMeters);
}
free(this->columns);
free(this); free(this);
} }
void Header_populateFromSettings(Header* this) { void Header_createMeter(Header* this, char* name, HeaderSide side) {
Header_forEachColumn(this, col) { Vector* meters = side == LEFT_HEADER
MeterColumnSettings* colSettings = &this->settings->columns[col]; ? this->leftMeters
for (int i = 0; i < colSettings->len; i++) { : this->rightMeters;
Header_addMeterByName(this, colSettings->names[i], col);
if (colSettings->modes[i] != 0) {
Header_setMode(this, i, colSettings->modes[i], col);
}
}
}
Header_calculateHeight(this);
}
void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col];
String_freeArray(colSettings->names);
free(colSettings->modes);
Vector* vec = this->columns[col];
int len = Vector_size(vec);
colSettings->names = xCalloc(len+1, sizeof(char*));
colSettings->modes = xCalloc(len, sizeof(int));
colSettings->len = len;
for (int i = 0; i < len; i++) {
Meter* meter = (Meter*) Vector_get(vec, i);
char* name = xCalloc(64, sizeof(char));
if (meter->param) {
xSnprintf(name, 63, "%s(%d)", As_Meter(meter)->name, meter->param);
} else {
xSnprintf(name, 63, "%s", As_Meter(meter)->name);
}
colSettings->names[i] = name;
colSettings->modes[i] = meter->mode;
}
}
}
MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
Vector* meters = this->columns[column];
char* paren = strchr(name, '('); char* paren = strchr(name, '(');
int param = 0; int param = 0;
if (paren) { if (paren) {
int ok = sscanf(paren, "(%10d)", &param); int ok = sscanf(paren, "(%d)", &param);
if (!ok) param = 0; if (!ok) param = 0;
*paren = '\0'; *paren = '\0';
} }
MeterModeId mode = TEXT_METERMODE; for (MeterType** type = Meter_types; *type; type++) {
for (MeterClass** type = Platform_meterTypes; *type; type++) {
if (String_eq(name, (*type)->name)) { if (String_eq(name, (*type)->name)) {
Meter* meter = Meter_new(this->pl, param, *type); Vector_add(meters, Meter_new(this->pl, param, *type));
Vector_add(meters, meter);
mode = meter->mode;
break; break;
} }
} }
if (paren)
*paren = '(';
return mode;
} }
void Header_setMode(Header* this, int i, MeterModeId mode, int column) { void Header_setMode(Header* this, int i, MeterModeId mode, HeaderSide side) {
Vector* meters = this->columns[column]; Vector* meters = side == LEFT_HEADER
? this->leftMeters
: this->rightMeters;
if (i >= Vector_size(meters))
return;
Meter* meter = (Meter*) Vector_get(meters, i); Meter* meter = (Meter*) Vector_get(meters, i);
Meter_setMode(meter, mode); Meter_setMode(meter, mode);
} }
Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column) { Meter* Header_addMeter(Header* this, MeterType* type, int param, HeaderSide side) {
Vector* meters = this->columns[column]; Vector* meters = side == LEFT_HEADER
? this->leftMeters
: this->rightMeters;
Meter* meter = Meter_new(this->pl, param, type); Meter* meter = Meter_new(this->pl, param, type);
Vector_add(meters, meter); Vector_add(meters, meter);
return meter; return meter;
} }
int Header_size(Header* this, int column) { int Header_size(Header* this, HeaderSide side) {
Vector* meters = this->columns[column]; Vector* meters = side == LEFT_HEADER
? this->leftMeters
: this->rightMeters;
return Vector_size(meters); return Vector_size(meters);
} }
char* Header_readMeterName(Header* this, int i, int column) { char* Header_readMeterName(Header* this, int i, HeaderSide side) {
Vector* meters = this->columns[column]; Vector* meters = side == LEFT_HEADER
? this->leftMeters
: this->rightMeters;
Meter* meter = (Meter*) Vector_get(meters, i); Meter* meter = (Meter*) Vector_get(meters, i);
int nameLen = strlen(Meter_name(meter)); int nameLen = strlen(meter->type->name);
int len = nameLen + 100; int len = nameLen + 100;
char* name = xMalloc(len); char* name = malloc(len);
memcpy(name, Meter_name(meter), nameLen); strncpy(name, meter->type->name, nameLen);
name[nameLen] = '\0'; name[nameLen] = '\0';
if (meter->param) if (meter->param)
xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param); snprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
return name; return name;
} }
MeterModeId Header_readMeterMode(Header* this, int i, int column) { MeterModeId Header_readMeterMode(Header* this, int i, HeaderSide side) {
Vector* meters = this->columns[column]; Vector* meters = side == LEFT_HEADER
? this->leftMeters
: this->rightMeters;
Meter* meter = (Meter*) Vector_get(meters, i); Meter* meter = (Meter*) Vector_get(meters, i);
return meter->mode; return meter->mode;
} }
void Header_reinit(Header* this) { void Header_defaultMeters(Header* this) {
Header_forEachColumn(this, col) { Vector_add(this->leftMeters, Meter_new(this->pl, 0, &AllCPUsMeter));
for (int i = 0; i < Vector_size(this->columns[col]); i++) { Vector_add(this->leftMeters, Meter_new(this->pl, 0, &MemoryMeter));
Meter* meter = (Meter*) Vector_get(this->columns[col], i); Vector_add(this->leftMeters, Meter_new(this->pl, 0, &SwapMeter));
if (Meter_initFn(meter)) Vector_add(this->rightMeters, Meter_new(this->pl, 0, &TasksMeter));
Meter_init(meter); Vector_add(this->rightMeters, Meter_new(this->pl, 0, &LoadAverageMeter));
} Vector_add(this->rightMeters, Meter_new(this->pl, 0, &UptimeMeter));
}
} }
void Header_draw(const Header* this) { void Header_draw(Header* this) {
int height = this->height; int height = this->height;
int pad = this->pad; int pad = this->pad;
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
mvhline(y, 0, ' ', COLS); mvhline(y, 0, ' ', COLS);
} }
int width = COLS / this->nrColumns - (pad * this->nrColumns - 1) - 1; for (int y = (pad / 2), i = 0; i < Vector_size(this->leftMeters); i++) {
int x = pad; Meter* meter = (Meter*) Vector_get(this->leftMeters, i);
meter->draw(meter, pad, y, COLS / 2 - (pad * 2 - 1) - 1);
Header_forEachColumn(this, col) {
Vector* meters = this->columns[col];
for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i);
meter->draw(meter, x, y, width);
y += meter->h; y += meter->h;
} }
x += width + pad; for (int y = (pad / 2), i = 0; i < Vector_size(this->rightMeters); i++) {
Meter* meter = (Meter*) Vector_get(this->rightMeters, i);
meter->draw(meter, COLS / 2 + pad, y, COLS / 2 - (pad * 2 - 1) - 1);
y += meter->h;
} }
} }
int Header_calculateHeight(Header* this) { int Header_calculateHeight(Header* this) {
int pad = this->settings->headerMargin ? 2 : 0; int pad = this->margin ? 2 : 0;
int maxHeight = pad; int leftHeight = pad;
int rightHeight = pad;
Header_forEachColumn(this, col) { for (int i = 0; i < Vector_size(this->leftMeters); i++) {
Vector* meters = this->columns[col]; Meter* meter = (Meter*) Vector_get(this->leftMeters, i);
int height = pad; leftHeight += meter->h;
for (int i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i);
height += meter->h;
} }
maxHeight = MAX(maxHeight, height); for (int i = 0; i < Vector_size(this->rightMeters); i++) {
Meter* meter = (Meter*) Vector_get(this->rightMeters, i);
rightHeight += meter->h;
} }
this->height = maxHeight;
this->pad = pad; this->pad = pad;
return maxHeight; this->height = MAX(leftHeight, rightHeight);
return this->height;
} }

View File

@ -4,22 +4,29 @@
#define HEADER_Header #define HEADER_Header
/* /*
htop - Header.h htop - Header.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.h"
#include "Settings.h"
#include "Vector.h" #include "debug.h"
#include <assert.h>
typedef enum HeaderSide_ {
LEFT_HEADER,
RIGHT_HEADER
} HeaderSide;
typedef struct Header_ { typedef struct Header_ {
Vector** columns; Vector* leftMeters;
Settings* settings; Vector* rightMeters;
struct ProcessList_* pl; ProcessList* pl;
int nrColumns; bool margin;
int pad;
int height; int height;
int pad;
} Header; } Header;
@ -27,34 +34,26 @@ typedef struct Header_ {
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#ifndef Header_forEachColumn Header* Header_new(ProcessList* pl);
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
#endif
extern Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns); void Header_delete(Header* this);
extern void Header_delete(Header* this); void Header_createMeter(Header* this, char* name, HeaderSide side);
extern void Header_populateFromSettings(Header* this); void Header_setMode(Header* this, int i, MeterModeId mode, HeaderSide side);
extern void Header_writeBackToSettings(const Header* this); Meter* Header_addMeter(Header* this, MeterType* type, int param, HeaderSide side);
extern MeterModeId Header_addMeterByName(Header* this, char* name, int column); int Header_size(Header* this, HeaderSide side);
extern void Header_setMode(Header* this, int i, MeterModeId mode, int column); char* Header_readMeterName(Header* this, int i, HeaderSide side);
extern Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column); MeterModeId Header_readMeterMode(Header* this, int i, HeaderSide side);
extern int Header_size(Header* this, int column); void Header_defaultMeters(Header* this);
extern char* Header_readMeterName(Header* this, int i, int column); void Header_draw(Header* this);
extern MeterModeId Header_readMeterMode(Header* this, int i, int column); int Header_calculateHeight(Header* this);
extern void Header_reinit(Header* this);
extern void Header_draw(const Header* this);
extern int Header_calculateHeight(Header* this);
#endif #endif

View File

@ -1,40 +0,0 @@
/*
htop - HostnameMeter.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "HostnameMeter.h"
#include "CRT.h"
#include <unistd.h>
/*{
#include "Meter.h"
}*/
int HostnameMeter_attributes[] = {
HOSTNAME
};
static void HostnameMeter_updateValues(Meter* this, char* buffer, int size) {
(void) this;
gethostname(buffer, size-1);
}
MeterClass HostnameMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete
},
.updateValues = HostnameMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 0,
.total = 100.0,
.attributes = HostnameMeter_attributes,
.name = "Hostname",
.uiName = "Hostname",
.caption = "Hostname: ",
};

View File

@ -1,18 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_HostnameMeter
#define HEADER_HostnameMeter
/*
htop - HostnameMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
extern int HostnameMeter_attributes[];
extern MeterClass HostnameMeter_class;
#endif

229
INSTALL Normal file
View File

@ -0,0 +1,229 @@
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
These are generic installation instructions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. (Caching is
disabled by default to prevent problems with accidental use of stale
cache files.)
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You only need
`configure.ac' if you want to change it or regenerate `configure' using
a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes awhile. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not support the `VPATH'
variable, you have to compile the package for one architecture at a
time in the source code directory. After you have installed the
package for one architecture, use `make distclean' before reconfiguring
for another architecture.
Installation Names
==================
By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH', the package will use
PATH as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=PATH' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the `--target=TYPE' option to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
will cause the specified gcc to be used as the C compiler (unless it is
overridden in the site shell script).
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

253
IncSet.c
View File

@ -1,253 +0,0 @@
/*
htop - IncSet.c
(C) 2005-2012 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "IncSet.h"
#include "StringUtils.h"
#include "Panel.h"
#include "ListItem.h"
#include "CRT.h"
#include <string.h>
#include <stdlib.h>
/*{
#include "FunctionBar.h"
#include "Panel.h"
#include <stdbool.h>
#define INCMODE_MAX 40
typedef enum {
INC_SEARCH = 0,
INC_FILTER = 1
} IncType;
#define IncSet_filter(inc_) (inc_->filtering ? inc_->modes[INC_FILTER].buffer : NULL)
typedef struct IncMode_ {
char buffer[INCMODE_MAX+1];
int index;
FunctionBar* bar;
bool isFilter;
} IncMode;
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
FunctionBar* defaultBar;
bool filtering;
bool found;
} IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
}*/
static void IncMode_reset(IncMode* mode) {
mode->index = 0;
mode->buffer[0] = 0;
}
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 inline void IncMode_initSearch(IncMode* search) {
memset(search, 0, sizeof(IncMode));
search->bar = FunctionBar_new(searchFunctions, searchKeys, searchEvents);
search->isFilter = false;
}
static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
static const char* const filterKeys[] = {"Enter", "Esc", " "};
static int filterEvents[] = {13, 27, ERR};
static inline void IncMode_initFilter(IncMode* filter) {
memset(filter, 0, sizeof(IncMode));
filter->bar = FunctionBar_new(filterFunctions, filterKeys, filterEvents);
filter->isFilter = true;
}
static inline void IncMode_done(IncMode* mode) {
FunctionBar_delete(mode->bar);
}
IncSet* IncSet_new(FunctionBar* bar) {
IncSet* this = xCalloc(1, sizeof(IncSet));
IncMode_initSearch(&(this->modes[INC_SEARCH]));
IncMode_initFilter(&(this->modes[INC_FILTER]));
this->active = NULL;
this->filtering = false;
this->defaultBar = bar;
return this;
}
void IncSet_delete(IncSet* this) {
IncMode_done(&(this->modes[0]));
IncMode_done(&(this->modes[1]));
free(this);
}
static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
Object* selected = Panel_getSelected(panel);
Panel_prune(panel);
if (this->filtering) {
int n = 0;
const char* incFilter = this->modes[INC_FILTER].buffer;
for (int i = 0; i < Vector_size(lines); i++) {
ListItem* line = (ListItem*)Vector_get(lines, i);
if (String_contains_i(line->value, incFilter)) {
Panel_add(panel, (Object*)line);
if (selected == (Object*)line) Panel_setSelected(panel, n);
n++;
}
}
} else {
for (int i = 0; i < Vector_size(lines); i++) {
Object* line = Vector_get(lines, i);
Panel_add(panel, line);
if (selected == line) Panel_setSelected(panel, i);
}
}
}
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;
}
}
if (found)
FunctionBar_draw(mode->bar, mode->buffer);
else
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
return found;
}
static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
int size = Panel_size(panel);
int here = Panel_getSelectedIndex(panel);
int i = here;
for(;;) {
i+=step;
if (i == size) i = 0;
if (i == -1) i = size - 1;
if (i == here) return false;
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
return true;
}
}
}
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;
IncMode* mode = this->active;
int size = Panel_size(panel);
bool filterChanged = false;
bool doSearch = true;
if (ch == KEY_F(3)) {
if (size == 0) return true;
IncMode_find(mode, panel, getPanelValue, 1);
doSearch = false;
} else if (ch < 255 && isprint((char)ch)) {
if (mode->index < INCMODE_MAX) {
mode->buffer[mode->index] = ch;
mode->index++;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
filterChanged = true;
if (mode->index == 1) this->filtering = true;
}
}
} else if ((ch == KEY_BACKSPACE || ch == 127)) {
if (mode->index > 0) {
mode->index--;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
filterChanged = true;
if (mode->index == 0) {
this->filtering = false;
IncMode_reset(mode);
}
}
} else {
doSearch = false;
}
} else if (ch == KEY_RESIZE) {
Panel_resize(panel, COLS, LINES-panel->y-1);
} else {
if (mode->isFilter) {
filterChanged = true;
if (ch == 27) {
this->filtering = false;
IncMode_reset(mode);
}
} else {
if (ch == 27) {
IncMode_reset(mode);
}
}
this->active = NULL;
Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar, NULL);
doSearch = false;
}
if (doSearch) {
this->found = search(mode, panel, getPanelValue);
}
if (filterChanged && lines) {
updateWeakPanel(this, panel, lines);
}
return filterChanged;
}
const char* IncSet_getListItemValue(Panel* panel, int i) {
ListItem* l = (ListItem*) Panel_get(panel, i);
if (l)
return l->value;
return "";
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
FunctionBar_draw(this->active->bar, this->active->buffer);
panel->currentBar = this->active->bar;
}
void IncSet_drawBar(IncSet* this) {
if (this->active) {
FunctionBar_draw(this->active->bar, this->active->buffer);
} else {
FunctionBar_draw(this->defaultBar, NULL);
}
}
int IncSet_synthesizeEvent(IncSet* this, int x) {
if (this->active) {
return FunctionBar_synthesizeEvent(this->active->bar, x);
} else {
return FunctionBar_synthesizeEvent(this->defaultBar, x);
}
}

View File

@ -1,64 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_IncSet
#define HEADER_IncSet
/*
htop - IncSet.h
(C) 2005-2012 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "FunctionBar.h"
#include "Panel.h"
#include <stdbool.h>
#define INCMODE_MAX 40
typedef enum {
INC_SEARCH = 0,
INC_FILTER = 1
} IncType;
#define IncSet_filter(inc_) (inc_->filtering ? inc_->modes[INC_FILTER].buffer : NULL)
typedef struct IncMode_ {
char buffer[INCMODE_MAX+1];
int index;
FunctionBar* bar;
bool isFilter;
} IncMode;
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
FunctionBar* defaultBar;
bool filtering;
bool found;
} IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
extern void IncSet_reset(IncSet* this, IncType type);
extern IncSet* IncSet_new(FunctionBar* bar);
extern void IncSet_delete(IncSet* this);
extern bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
extern bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
extern bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
extern const char* IncSet_getListItemValue(Panel* panel, int i);
extern void IncSet_activate(IncSet* this, IncType type, Panel* panel);
extern void IncSet_drawBar(IncSet* this);
extern int IncSet_synthesizeEvent(IncSet* this, int x);
#endif

View File

@ -1,185 +0,0 @@
#include "InfoScreen.h"
#include "config.h"
#include "Object.h"
#include "CRT.h"
#include "IncSet.h"
#include "ListItem.h"
#include "Platform.h"
#include "StringUtils.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
/*{
#include "Process.h"
#include "Panel.h"
#include "FunctionBar.h"
#include "IncSet.h"
typedef struct InfoScreen_ InfoScreen;
typedef void(*InfoScreen_Scan)(InfoScreen*);
typedef void(*InfoScreen_Draw)(InfoScreen*);
typedef void(*InfoScreen_OnErr)(InfoScreen*);
typedef bool(*InfoScreen_OnKey)(InfoScreen*, int);
typedef struct InfoScreenClass_ {
ObjectClass super;
const InfoScreen_Scan scan;
const InfoScreen_Draw draw;
const InfoScreen_OnErr onErr;
const InfoScreen_OnKey onKey;
} InfoScreenClass;
#define As_InfoScreen(this_) ((InfoScreenClass*)(((InfoScreen*)(this_))->super.klass))
#define InfoScreen_scan(this_) As_InfoScreen(this_)->scan((InfoScreen*)(this_))
#define InfoScreen_draw(this_) As_InfoScreen(this_)->draw((InfoScreen*)(this_))
#define InfoScreen_onErr(this_) As_InfoScreen(this_)->onErr((InfoScreen*)(this_))
#define InfoScreen_onKey(this_, ch_) As_InfoScreen(this_)->onKey((InfoScreen*)(this_), ch_)
struct InfoScreen_ {
Object super;
Process* process;
Panel* display;
FunctionBar* bar;
IncSet* inc;
Vector* lines;
};
}*/
static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL};
static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
InfoScreen* InfoScreen_init(InfoScreen* this, 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->inc = IncSet_new(bar);
this->lines = Vector_new(this->display->items->type, true, DEFAULT_SIZE);
Panel_setHeader(this->display, panelHeader);
return this;
}
InfoScreen* InfoScreen_done(InfoScreen* this) {
Panel_delete((Object*)this->display);
IncSet_delete(this->inc);
Vector_delete(this->lines);
return this;
}
void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
attrset(CRT_colors[METER_TEXT]);
mvhline(0, 0, ' ', COLS);
(void) wmove(stdscr, 0, 0);
vw_printw(stdscr, fmt, ap);
attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true;
Panel_draw(this->display, true);
IncSet_drawBar(this->inc);
va_end(ap);
}
void InfoScreen_addLine(InfoScreen* this, const char* line) {
Vector_add(this->lines, (Object*) ListItem_new(line, 0));
const char* incFilter = IncSet_filter(this->inc);
if (!incFilter || String_contains_i(line, incFilter))
Panel_add(this->display, (Object*)Vector_get(this->lines, Vector_size(this->lines)-1));
}
void InfoScreen_appendLine(InfoScreen* this, const char* line) {
ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines)-1);
ListItem_append(last, line);
const char* incFilter = IncSet_filter(this->inc);
if (incFilter && Panel_get(this->display, Panel_size(this->display)-1) != (Object*)last && String_contains_i(line, incFilter))
Panel_add(this->display, (Object*)last);
}
void InfoScreen_run(InfoScreen* this) {
Panel* panel = this->display;
if (As_InfoScreen(this)->scan) InfoScreen_scan(this);
InfoScreen_draw(this);
bool looping = true;
while (looping) {
Panel_draw(panel, true);
if (this->inc->active) {
(void) move(LINES-1, CRT_cursorX);
}
set_escdelay(25);
int ch = getch();
if (ch == ERR) {
if (As_InfoScreen(this)->onErr) {
InfoScreen_onErr(this);
continue;
}
}
if (ch == KEY_MOUSE) {
MEVENT mevent;
int ok = getmouse(&mevent);
if (ok == OK) {
if (mevent.y >= panel->y && mevent.y < LINES - 1) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV);
ch = 0;
} else if (mevent.y == LINES - 1) {
ch = IncSet_synthesizeEvent(this->inc, mevent.x);
}
}
}
if (this->inc->active) {
IncSet_handleKey(this->inc, ch, panel, IncSet_getListItemValue, this->lines);
continue;
}
switch(ch) {
case ERR:
continue;
case KEY_F(3):
case '/':
IncSet_activate(this->inc, INC_SEARCH, panel);
break;
case KEY_F(4):
case '\\':
IncSet_activate(this->inc, INC_FILTER, panel);
break;
case KEY_F(5):
clear();
if (As_InfoScreen(this)->scan) InfoScreen_scan(this);
InfoScreen_draw(this);
break;
case '\014': // Ctrl+L
clear();
InfoScreen_draw(this);
break;
case 'q':
case 27:
case KEY_F(10):
looping = false;
break;
case KEY_RESIZE:
Panel_resize(panel, COLS, LINES-2);
InfoScreen_draw(this);
break;
default:
if (As_InfoScreen(this)->onKey && InfoScreen_onKey(this, ch)) {
continue;
}
Panel_onKey(panel, ch);
}
}
}

View File

@ -1,53 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_InfoScreen
#define HEADER_InfoScreen
#include "Process.h"
#include "Panel.h"
#include "FunctionBar.h"
#include "IncSet.h"
typedef struct InfoScreen_ InfoScreen;
typedef void(*InfoScreen_Scan)(InfoScreen*);
typedef void(*InfoScreen_Draw)(InfoScreen*);
typedef void(*InfoScreen_OnErr)(InfoScreen*);
typedef bool(*InfoScreen_OnKey)(InfoScreen*, int);
typedef struct InfoScreenClass_ {
ObjectClass super;
const InfoScreen_Scan scan;
const InfoScreen_Draw draw;
const InfoScreen_OnErr onErr;
const InfoScreen_OnKey onKey;
} InfoScreenClass;
#define As_InfoScreen(this_) ((InfoScreenClass*)(((InfoScreen*)(this_))->super.klass))
#define InfoScreen_scan(this_) As_InfoScreen(this_)->scan((InfoScreen*)(this_))
#define InfoScreen_draw(this_) As_InfoScreen(this_)->draw((InfoScreen*)(this_))
#define InfoScreen_onErr(this_) As_InfoScreen(this_)->onErr((InfoScreen*)(this_))
#define InfoScreen_onKey(this_, ch_) As_InfoScreen(this_)->onKey((InfoScreen*)(this_), ch_)
struct InfoScreen_ {
Object super;
Process* process;
Panel* display;
FunctionBar* bar;
IncSet* inc;
Vector* lines;
};
extern InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, const char* panelHeader);
extern InfoScreen* InfoScreen_done(InfoScreen* this);
extern void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...);
extern void InfoScreen_addLine(InfoScreen* this, const char* line);
extern void InfoScreen_appendLine(InfoScreen* this, const char* line);
extern void InfoScreen_run(InfoScreen* this);
#endif

View File

@ -1,87 +1,73 @@
/* /*
htop - ListItem.c htop - ListItem.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "ListItem.h" #include "ListItem.h"
#include "String.h"
#include "CRT.h" #include "Object.h"
#include "StringUtils.h"
#include "RichString.h" #include "RichString.h"
#include <string.h> #include <string.h>
#include <assert.h>
#include <stdlib.h> #include "debug.h"
/*{ /*{
#include "Object.h"
typedef struct ListItem_ { typedef struct ListItem_ {
Object super; Object super;
char* value; char* value;
int key; int key;
bool moving;
} ListItem; } ListItem;
}*/ }*/
static void ListItem_delete(Object* cast) { #ifdef DEBUG
char* LISTITEM_CLASS = "ListItem";
#else
#define LISTITEM_CLASS NULL
#endif
ListItem* ListItem_new(char* value, int key) {
ListItem* this = malloc(sizeof(ListItem));
Object_setClass(this, LISTITEM_CLASS);
((Object*)this)->display = ListItem_display;
((Object*)this)->delete = ListItem_delete;
this->value = String_copy(value);
this->key = key;
return this;
}
void ListItem_append(ListItem* this, char* text) {
char* buf = malloc(strlen(this->value) + strlen(text) + 1);
sprintf(buf, "%s%s", this->value, text);
free(this->value);
this->value = buf;
}
void ListItem_delete(Object* cast) {
ListItem* this = (ListItem*)cast; ListItem* this = (ListItem*)cast;
free(this->value); free(this->value);
free(this); free(this);
} }
static void ListItem_display(Object* cast, RichString* out) { void ListItem_display(Object* cast, RichString* out) {
ListItem* const this = (ListItem*)cast; ListItem* this = (ListItem*)cast;
assert (this != NULL); assert (this != NULL);
/*
int len = strlen(this->value)+1; int len = strlen(this->value)+1;
char buffer[len+1]; char buffer[len+1];
xSnprintf(buffer, len, "%s", this->value); snprintf(buffer, len, "%s", this->value);
*/ RichString_write(out, CRT_colors[DEFAULT_COLOR], buffer);
if (this->moving) {
RichString_write(out, CRT_colors[DEFAULT_COLOR],
#ifdef HAVE_LIBNCURSESW
CRT_utf8 ? "" :
#endif
"+ ");
} else {
RichString_prune(out);
}
RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value/*buffer*/);
}
ObjectClass ListItem_class = {
.display = ListItem_display,
.delete = ListItem_delete,
.compare = ListItem_compare
};
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
this->value = xStrdup(value);
this->key = key;
this->moving = false;
return this;
}
void ListItem_append(ListItem* this, const char* text) {
int oldLen = strlen(this->value);
int textLen = strlen(text);
int newLen = strlen(this->value) + textLen;
this->value = xRealloc(this->value, newLen + 1);
memcpy(this->value + oldLen, text, textLen);
this->value[newLen] = '\0';
} }
const char* ListItem_getRef(ListItem* this) { const char* ListItem_getRef(ListItem* this) {
return this->value; return this->value;
} }
long ListItem_compare(const void* cast1, const void* cast2) { int ListItem_compare(const void* cast1, const void* cast2) {
ListItem* obj1 = (ListItem*) cast1; ListItem* obj1 = (ListItem*) cast1;
ListItem* obj2 = (ListItem*) cast2; ListItem* obj2 = (ListItem*) cast2;
return strcmp(obj1->value, obj2->value); return strcmp(obj1->value, obj2->value);
} }

View File

@ -4,29 +4,43 @@
#define HEADER_ListItem #define HEADER_ListItem
/* /*
htop - ListItem.h htop - ListItem.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "String.h"
#include "Object.h" #include "Object.h"
#include "RichString.h"
#include <string.h>
#include "debug.h"
typedef struct ListItem_ { typedef struct ListItem_ {
Object super; Object super;
char* value; char* value;
int key; int key;
bool moving;
} ListItem; } ListItem;
extern ObjectClass ListItem_class; #ifdef DEBUG
extern char* LISTITEM_CLASS;
#else
#define LISTITEM_CLASS NULL
#endif
extern ListItem* ListItem_new(const char* value, int key); ListItem* ListItem_new(char* value, int key);
extern void ListItem_append(ListItem* this, const char* text); void ListItem_append(ListItem* this, char* text);
extern const char* ListItem_getRef(ListItem* this); void ListItem_delete(Object* cast);
void ListItem_display(Object* cast, RichString* out);
const char* ListItem_getRef(ListItem* this);
int ListItem_compare(const void* cast1, const void* cast2);
extern long ListItem_compare(const void* cast1, const void* cast2);
#endif #endif

View File

@ -1,87 +1,87 @@
/* /*
htop - LoadAverageMeter.c htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "LoadAverageMeter.h" #include "LoadAverageMeter.h"
#include "CRT.h"
#include "Platform.h"
/*{
#include "Meter.h" #include "Meter.h"
}*/
#include <curses.h>
#include "debug.h"
int LoadAverageMeter_attributes[] = { int LoadAverageMeter_attributes[] = {
LOAD_AVERAGE_ONE, LOAD_AVERAGE_FIVE, LOAD_AVERAGE_FIFTEEN LOAD_AVERAGE_FIFTEEN, LOAD_AVERAGE_FIVE, LOAD_AVERAGE_ONE
}; };
int LoadMeter_attributes[] = { LOAD }; MeterType LoadAverageMeter = {
.setValues = LoadAverageMeter_setValues,
static void LoadAverageMeter_updateValues(Meter* this, char* buffer, int size) {
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
}
static void LoadAverageMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
}
static void LoadMeter_updateValues(Meter* this, char* buffer, int size) {
double five, fifteen;
Platform_getLoadAverage(&this->values[0], &five, &fifteen);
if (this->values[0] > this->total) {
this->total = this->values[0];
}
xSnprintf(buffer, size, "%.2f", this->values[0]);
}
static void LoadMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", ((Meter*)this)->values[0]);
RichString_write(out, CRT_colors[LOAD], buffer);
}
MeterClass LoadAverageMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = LoadAverageMeter_display, .display = LoadAverageMeter_display,
}, .mode = TEXT_METERMODE,
.updateValues = LoadAverageMeter_updateValues, .items = 3,
.defaultMode = TEXT_METERMODE,
.maxItems = 3,
.total = 100.0, .total = 100.0,
.attributes = LoadAverageMeter_attributes, .attributes = LoadAverageMeter_attributes,
.name = "LoadAverage", .name = "LoadAverage",
.uiName = "Load average", .uiName = "Load average",
.description = "Load averages: 1 minute, 5 minutes, 15 minutes",
.caption = "Load average: " .caption = "Load average: "
}; };
MeterClass LoadMeter_class = { int LoadMeter_attributes[] = { LOAD };
.super = {
.extends = Class(Meter), MeterType LoadMeter = {
.delete = Meter_delete, .setValues = LoadMeter_setValues,
.display = LoadMeter_display, .display = LoadMeter_display,
}, .mode = TEXT_METERMODE,
.updateValues = LoadMeter_updateValues, .items = 1,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0, .total = 100.0,
.attributes = LoadMeter_attributes, .attributes = LoadMeter_attributes,
.name = "Load", .name = "Load",
.uiName = "Load", .uiName = "Load",
.description = "Load: average of ready processes in the last minute",
.caption = "Load: " .caption = "Load: "
}; };
static inline void LoadAverageMeter_scan(double* one, double* five, double* fifteen) {
int activeProcs, totalProcs, lastProc;
FILE *fd = fopen(PROCDIR "/loadavg", "r");
int read = fscanf(fd, "%lf %lf %lf %d/%d %d", one, five, fifteen,
&activeProcs, &totalProcs, &lastProc);
(void) read;
assert(read == 6);
fclose(fd);
}
void LoadAverageMeter_setValues(Meter* this, char* buffer, int size) {
LoadAverageMeter_scan(&this->values[2], &this->values[1], &this->values[0]);
snprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[2], this->values[1], this->values[0]);
}
void LoadAverageMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
char buffer[20];
RichString_init(out);
sprintf(buffer, "%.2f ", this->values[2]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
sprintf(buffer, "%.2f ", this->values[1]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
sprintf(buffer, "%.2f ", this->values[0]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
}
void LoadMeter_setValues(Meter* this, char* buffer, int size) {
double five, fifteen;
LoadAverageMeter_scan(&this->values[0], &five, &fifteen);
if (this->values[0] > this->total) {
this->total = this->values[0];
}
snprintf(buffer, size, "%.2f", this->values[0]);
}
void LoadMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
char buffer[20];
RichString_init(out);
sprintf(buffer, "%.2f ", ((Meter*)this)->values[0]);
RichString_append(out, CRT_colors[LOAD], buffer);
}

View File

@ -3,20 +3,32 @@
#ifndef HEADER_LoadAverageMeter #ifndef HEADER_LoadAverageMeter
#define HEADER_LoadAverageMeter #define HEADER_LoadAverageMeter
/* /*
htop - LoadAverageMeter.h htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.h"
#include <curses.h>
#include "debug.h"
extern int LoadAverageMeter_attributes[]; extern int LoadAverageMeter_attributes[];
extern MeterType LoadAverageMeter;
extern int LoadMeter_attributes[]; extern int LoadMeter_attributes[];
extern MeterClass LoadAverageMeter_class; extern MeterType LoadMeter;
extern MeterClass LoadMeter_class; void LoadAverageMeter_setValues(Meter* this, char* buffer, int size);
void LoadAverageMeter_display(Object* cast, RichString* out);
void LoadMeter_setValues(Meter* this, char* buffer, int size);
void LoadMeter_display(Object* cast, RichString* out);
#endif #endif

View File

@ -1,203 +0,0 @@
/*
htop - ColumnsPanel.c
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "MainPanel.h"
#include "Process.h"
#include "Platform.h"
#include "CRT.h"
#include <stdlib.h>
/*{
#include "Panel.h"
#include "Action.h"
#include "Settings.h"
typedef struct MainPanel_ {
Panel super;
State* state;
IncSet* inc;
Htop_Action *keys;
pid_t pidSearch;
} MainPanel;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
}*/
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search ", "Filter ", "Tree ", "SortBy ", "Nice - ", "Nice + ", "Kill ", "Quit ", NULL};
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this);
if (mode) {
FunctionBar_setLabel(bar, KEY_F(5), "Sorted");
FunctionBar_setLabel(bar, KEY_F(6), "Collap");
} else {
FunctionBar_setLabel(bar, KEY_F(5), "Tree ");
FunctionBar_setLabel(bar, KEY_F(6), "SortBy");
}
}
void MainPanel_pidSearch(MainPanel* this, int ch) {
Panel* super = (Panel*) this;
pid_t pid = ch-48 + this->pidSearch;
for (int i = 0; i < Panel_size(super); i++) {
Process* p = (Process*) Panel_get(super, i);
if (p && p->pid == pid) {
Panel_setSelected(super, i);
break;
}
}
this->pidSearch = pid * 10;
if (this->pidSearch > 10000000) {
this->pidSearch = 0;
}
}
static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
MainPanel* this = (MainPanel*) super;
HandlerResult result = IGNORED;
Htop_Reaction reaction = HTOP_OK;
if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch);
ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) {
Settings_invertSortOrder(settings);
settings->treeView = false;
} else {
reaction |= Action_setSortKey(settings, field);
}
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);
if (filterChanged) {
this->state->pl->incFilter = IncSet_filter(this->inc);
reaction = HTOP_REFRESH | HTOP_REDRAW_BAR;
}
if (this->inc->found) {
reaction |= Action_follow(this->state);
reaction |= HTOP_KEEP_FOLLOWING;
}
result = HANDLED;
} else if (ch == 27) {
return HANDLED;
} else if (ch != ERR && ch > 0 && ch < KEY_MAX && this->keys[ch]) {
reaction |= (this->keys[ch])(this->state);
result = HANDLED;
} else if (isdigit(ch)) {
MainPanel_pidSearch(this, ch);
} else {
if (ch != ERR) {
this->pidSearch = 0;
} else {
reaction |= HTOP_KEEP_FOLLOWING;
}
}
if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc);
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
}
if (reaction & HTOP_REFRESH) {
result |= REDRAW;
}
if (reaction & HTOP_RECALCULATE) {
result |= RESCAN;
}
if (reaction & HTOP_SAVE_SETTINGS) {
this->state->settings->changed = true;
}
if (reaction & HTOP_QUIT) {
return BREAK_LOOP;
}
if (!(reaction & HTOP_KEEP_FOLLOWING)) {
this->state->pl->following = -1;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
}
return result;
}
int MainPanel_selectedPid(MainPanel* this) {
Process* p = (Process*) Panel_getSelected((Panel*)this);
if (p) {
return p->pid;
}
return -1;
}
const char* MainPanel_getValue(MainPanel* this, int i) {
Process* p = (Process*) Panel_get((Panel*)this, i);
if (p)
return p->comm;
return "";
}
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
Panel* super = (Panel*) this;
bool ok = true;
bool anyTagged = false;
for (int i = 0; i < Panel_size(super); i++) {
Process* p = (Process*) Panel_get(super, i);
if (p->tag) {
ok = fn(p, arg) && ok;
anyTagged = true;
}
}
if (!anyTagged) {
Process* p = (Process*) Panel_getSelected(super);
if (p) ok = fn(p, arg) && ok;
}
if (wasAnyTagged)
*wasAnyTagged = anyTagged;
return ok;
}
PanelClass MainPanel_class = {
.super = {
.extends = Class(Panel),
.delete = MainPanel_delete
},
.eventHandler = MainPanel_eventHandler
};
MainPanel* MainPanel_new() {
MainPanel* this = AllocThis(MainPanel);
Panel_init((Panel*) this, 1, 1, 1, 1, Class(Process), false, FunctionBar_new(MainFunctions, NULL, NULL));
this->keys = xCalloc(KEY_MAX, sizeof(Htop_Action));
this->inc = IncSet_new(MainPanel_getFunctionBar(this));
Action_setBindings(this->keys);
Platform_setBindings(this->keys);
return this;
}
void MainPanel_setState(MainPanel* this, State* state) {
this->state = state;
}
void MainPanel_delete(Object* object) {
Panel* super = (Panel*) object;
MainPanel* this = (MainPanel*) object;
Panel_done(super);
IncSet_delete(this->inc);
free(this->keys);
free(this);
}

View File

@ -1,48 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_MainPanel
#define HEADER_MainPanel
/*
htop - ColumnsPanel.h
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "Action.h"
#include "Settings.h"
typedef struct MainPanel_ {
Panel super;
State* state;
IncSet* inc;
Htop_Action *keys;
pid_t pidSearch;
} MainPanel;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
extern void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
extern void MainPanel_pidSearch(MainPanel* this, int ch);
extern int MainPanel_selectedPid(MainPanel* this);
extern const char* MainPanel_getValue(MainPanel* this, int i);
extern bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
extern PanelClass MainPanel_class;
extern MainPanel* MainPanel_new();
extern void MainPanel_setState(MainPanel* this, State* state);
extern void MainPanel_delete(Object* object);
#endif

View File

@ -1,9 +1,5 @@
ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = subdir-objects
bin_PROGRAMS = htop bin_PROGRAMS = htop
dist_man_MANS = htop.1 dist_man_MANS = htop.1
EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png scripts/MakeHeader.py \ EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png scripts/MakeHeader.py \
install-sh autogen.sh missing install-sh autogen.sh missing
@ -12,235 +8,31 @@ applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png pixmap_DATA = htop.png
AM_CFLAGS += -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)" AM_CFLAGS = -pedantic -Wall -std=c99
AM_LDFLAGS = AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\"
AM_CPPFLAGS = -DNDEBUG
myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \ htop_SOURCES = AvailableMetersPanel.c CategoriesPanel.c ClockMeter.c \
ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \ CPUMeter.c CRT.c DebugMemory.c DisplayOptionsPanel.c FunctionBar.c \
DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \ Hashtable.c Header.c htop.c Panel.c ListItem.c LoadAverageMeter.c \
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \ MemoryMeter.c Meter.c MetersPanel.c Object.c Process.c \
BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \ ProcessList.c RichString.c ScreenManager.c Settings.c SignalItem.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \ SignalsPanel.c String.c SwapMeter.c TasksMeter.c Vector.c \
TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \ UptimeMeter.c UsersTable.c AvailableMetersPanel.h CategoriesPanel.h \
HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \ ClockMeter.h config.h CPUMeter.h CRT.h debug.h DebugMemory.h \
InfoScreen.c XAlloc.c DisplayOptionsPanel.h FunctionBar.h Hashtable.h Header.h htop.h Panel.h \
ListItem.h LoadAverageMeter.h MemoryMeter.h Meter.h \
myhtopheaders = AvailableColumnsPanel.h AvailableMetersPanel.h \ MetersPanel.h Object.h Process.h ProcessList.h RichString.h ScreenManager.h \
CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \ Settings.h SignalItem.h SignalsPanel.h String.h SwapMeter.h TasksMeter.h \
CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \ Vector.h UptimeMeter.h UsersTable.h CheckItem.c CheckItem.h \
Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \ ColorsPanel.c ColorsPanel.h TraceScreen.c TraceScreen.h \
BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \ AvailableColumnsPanel.c AvailableColumnsPanel.h ColumnsPanel.c \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \ ColumnsPanel.h
TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \
AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \
EnvScreen.h InfoScreen.h XAlloc.h
all_platform_headers =
# Linux
# -----
linux_platform_headers = \
linux/Platform.h \
linux/IOPriorityPanel.h \
linux/IOPriority.h \
linux/LinuxProcess.h \
linux/LinuxProcessList.h \
linux/LinuxCRT.h \
linux/Battery.h \
linux/PressureStallMeter.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
all_platform_headers += $(linux_platform_headers)
if HTOP_LINUX
AM_LDFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \
linux/PressureStallMeter.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(linux_platform_headers)
endif
# FreeBSD
# -------
freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \
freebsd/FreeBSDCRT.h \
freebsd/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
all_platform_headers += $(freebsd_platform_headers)
if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(freebsd_platform_headers)
endif
# DragonFlyBSD
# ------------
dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/DragonFlyBSDCRT.h \
dragonflybsd/Battery.h
all_platform_headers += $(dragonflybsd_platform_headers)
if HTOP_DRAGONFLYBSD
AM_LDFLAGS += -lkvm -lkinfo -lexecinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c
myhtopplatheaders = $(dragonflybsd_platform_headers)
endif
# OpenBSD
# -------
openbsd_platform_headers = \
openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h \
openbsd/OpenBSDCRT.h \
openbsd/Battery.h
all_platform_headers += $(openbsd_platform_headers)
if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c
myhtopplatheaders = $(openbsd_platform_headers)
endif
# Darwin
# ------
darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
darwin/DarwinCRT.h \
darwin/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
all_platform_headers += $(darwin_platform_headers)
if HTOP_DARWIN
AM_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(darwin_platform_headers)
endif
# Solaris
# -------
solaris_platform_headers = \
solaris/Platform.h \
solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \
solaris/SolarisCRT.h \
solaris/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
all_platform_headers += $(solaris_platform_headers)
if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \
solaris/SolarisCRT.c solaris/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(solaris_platform_headers)
endif
# Unsupported
# -----------
unsupported_platform_headers = \
unsupported/Platform.h \
unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h \
unsupported/Battery.h
all_platform_headers += $(unsupported_platform_headers)
if HTOP_UNSUPPORTED
myhtopplatsources = unsupported/Platform.c \
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \
unsupported/UnsupportedCRT.c unsupported/Battery.c
myhtopplatheaders = $(unsupported_platform_headers)
endif
# ----
SUFFIXES = .h
BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
nodist_htop_SOURCES = config.h
.PHONY: htop-headers clean-htop-headers
htop-headers: $(myhtopheaders) $(all_platform_headers)
clean-htop-headers:
-rm -f $(myhtopheaders) $(all_platform_headers)
target:
echo $(htop_SOURCES)
profile: profile:
$(MAKE) all CFLAGS="-pg" AM_CPPFLAGS="-pg -O2 -DNDEBUG" $(MAKE) all CFLAGS="-pg -O2"
debug: debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG" $(MAKE) all CFLAGS="-g -DDEBUG"
coverage: debuglite:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov" $(MAKE) all CFLAGS="-g -DDEBUGLITE"
.c.h:
./scripts/MakeHeader.py $<
cppcheck:
cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS
dist-hook: $(top_distdir)/configure
@if grep 'pkg_m4_absent' '$(top_distdir)/configure'; then \
echo 'configure is generated without pkg.m4. Please supply pkg.m4 and run ./autogen.sh to rebuild the configure script.'>&2; \
(exit 1); \
else :; \
fi
.PHONY: lcov
lcov:
mkdir -p lcov
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory lcov

View File

@ -1,71 +1,71 @@
/* /*
htop - MemoryMeter.c htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "MemoryMeter.h" #include "MemoryMeter.h"
#include "Meter.h"
#include "CRT.h" #include "ProcessList.h"
#include "Platform.h"
#include <stdlib.h> #include <stdlib.h>
#include <curses.h>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <sys/param.h> #include <sys/param.h>
#include <assert.h>
/*{ #include "debug.h"
#include "Meter.h" #include <assert.h>
}*/
int MemoryMeter_attributes[] = { int MemoryMeter_attributes[] = {
MEMORY_USED, MEMORY_BUFFERS, MEMORY_CACHE MEMORY_USED, MEMORY_BUFFERS, MEMORY_CACHE
}; };
static void MemoryMeter_updateValues(Meter* this, char* buffer, int size) { MeterType MemoryMeter = {
int written; .setValues = MemoryMeter_setValues,
Platform_setMemoryValues(this);
written = Meter_humanUnit(buffer, this->values[0], size);
buffer += written;
if ((size -= written) > 0) {
*buffer++ = '/';
size--;
Meter_humanUnit(buffer, this->total, size);
}
}
static void MemoryMeter_display(Object* cast, RichString* out) {
char buffer[50];
Meter* this = (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);
}
MeterClass MemoryMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = MemoryMeter_display, .display = MemoryMeter_display,
}, .mode = BAR_METERMODE,
.updateValues = MemoryMeter_updateValues, .items = 3,
.defaultMode = BAR_METERMODE,
.maxItems = 3,
.total = 100.0, .total = 100.0,
.attributes = MemoryMeter_attributes, .attributes = MemoryMeter_attributes,
.name = "Memory", "Memory",
.uiName = "Memory", "Memory",
.caption = "Mem" "Mem"
}; };
void MemoryMeter_setValues(Meter* this, char* buffer, int size) {
long int usedMem = this->pl->usedMem;
long int buffersMem = this->pl->buffersMem;
long int cachedMem = this->pl->cachedMem;
usedMem -= buffersMem + cachedMem;
this->total = this->pl->totalMem;
this->values[0] = usedMem;
this->values[1] = buffersMem;
this->values[2] = cachedMem;
snprintf(buffer, size, "%ld/%ldMB", (long int) usedMem / 1024, (long int) this->total / 1024);
}
void MemoryMeter_display(Object* cast, RichString* out) {
char buffer[50];
Meter* this = (Meter*)cast;
int div = 1024; char* format = "%ldM ";
long int totalMem = this->total / div;
long int usedMem = this->values[0] / div;
long int buffersMem = this->values[1] / div;
long int cachedMem = this->values[2] / div;
RichString_init(out);
RichString_append(out, CRT_colors[METER_TEXT], ":");
sprintf(buffer, format, totalMem);
RichString_append(out, CRT_colors[METER_VALUE], buffer);
sprintf(buffer, format, usedMem);
RichString_append(out, CRT_colors[METER_TEXT], "used:");
RichString_append(out, CRT_colors[MEMORY_USED], buffer);
sprintf(buffer, format, buffersMem);
RichString_append(out, CRT_colors[METER_TEXT], "buffers:");
RichString_append(out, CRT_colors[MEMORY_BUFFERS], buffer);
sprintf(buffer, format, cachedMem);
RichString_append(out, CRT_colors[METER_TEXT], "cache:");
RichString_append(out, CRT_colors[MEMORY_CACHE], buffer);
}

View File

@ -3,16 +3,31 @@
#ifndef HEADER_MemoryMeter #ifndef HEADER_MemoryMeter
#define HEADER_MemoryMeter #define HEADER_MemoryMeter
/* /*
htop - MemoryMeter.h htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.h"
#include "ProcessList.h"
#include <stdlib.h>
#include <curses.h>
#include <string.h>
#include <math.h>
#include <sys/param.h>
#include "debug.h"
#include <assert.h>
extern int MemoryMeter_attributes[]; extern int MemoryMeter_attributes[];
extern MeterClass MemoryMeter_class; extern MeterType MemoryMeter;
void MemoryMeter_setValues(Meter* this, char* buffer, int size);
void MemoryMeter_display(Object* cast, RichString* out);
#endif #endif

799
Meter.c
View File

@ -1,491 +1,128 @@
/* /*
htop - Meter.c htop - Meter.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #define _GNU_SOURCE
#include "RichString.h"
#include "Object.h"
#include "CRT.h"
#include "StringUtils.h"
#include "ListItem.h"
#include "Settings.h"
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <curses.h>
#include <stdarg.h> #include <stdarg.h>
#include "Meter.h"
#include "Object.h"
#include "CRT.h"
#include "ListItem.h"
#include "String.h"
#include "ProcessList.h"
#include "debug.h"
#include <assert.h> #include <assert.h>
#include <sys/time.h>
#define METER_BUFFER_LEN 256 #ifndef USE_FUNKY_MODES
#define USE_FUNKY_MODES 1
#endif
#define GRAPH_DELAY (DEFAULT_DELAY/2) #define METER_BUFFER_LEN 128
#define GRAPH_HEIGHT 4 /* Unit: rows (lines) */
/*{ /*{
#include "ListItem.h"
#include <sys/time.h>
typedef struct Meter_ Meter; typedef struct Meter_ Meter;
typedef struct MeterType_ MeterType;
typedef struct MeterMode_ MeterMode;
typedef void(*Meter_Init)(Meter*); typedef void(*MeterType_Init)(Meter*);
typedef void(*Meter_Done)(Meter*); typedef void(*MeterType_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int); typedef void(*MeterType_SetMode)(Meter*, int);
typedef void(*Meter_UpdateValues)(Meter*, char*, int); typedef void(*Meter_SetValues)(Meter*, char*, int);
typedef void(*Meter_Draw)(Meter*, int, int, int); typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ { struct MeterMode_ {
ObjectClass super; Meter_Draw draw;
const Meter_Init init; char* uiName;
const Meter_Done done; int h;
const Meter_UpdateMode updateMode; };
const Meter_Draw draw;
const Meter_UpdateValues updateValues;
const int defaultMode;
const double total;
const int* attributes;
const char* name;
const char* uiName;
const char* caption;
const char* description;
const char maxItems;
char curItems;
} MeterClass;
#define As_Meter(this_) ((MeterClass*)((this_)->super.klass)) struct MeterType_ {
#define Meter_initFn(this_) As_Meter(this_)->init Meter_SetValues setValues;
#define Meter_init(this_) As_Meter(this_)->init((Meter*)(this_)) Object_Display display;
#define Meter_done(this_) As_Meter(this_)->done((Meter*)(this_)) int mode;
#define Meter_updateModeFn(this_) As_Meter(this_)->updateMode int items;
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_) double total;
#define Meter_drawFn(this_) As_Meter(this_)->draw int* attributes;
#define Meter_doneFn(this_) As_Meter(this_)->done char* name;
#define Meter_updateValues(this_, buf_, sz_) \ char* uiName;
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_) char* caption;
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode MeterType_Init init;
#define Meter_getItems(this_) As_Meter(this_)->curItems MeterType_Done done;
#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_) MeterType_SetMode setMode;
#define Meter_attributes(this_) As_Meter(this_)->attributes Meter_Draw draw;
#define Meter_name(this_) As_Meter(this_)->name };
#define Meter_uiName(this_) As_Meter(this_)->uiName
struct Meter_ { struct Meter_ {
Object super; Object super;
Meter_Draw draw;
char* caption; char* caption;
MeterType* type;
int mode; int mode;
int param; int param;
void* drawData; Meter_Draw draw;
void* drawBuffer;
int h; int h;
struct ProcessList_* pl; ProcessList* pl;
double* values; double* values;
double total; double total;
}; };
typedef struct MeterMode_ {
Meter_Draw draw;
const char* uiName;
int h;
} MeterMode;
typedef enum { typedef enum {
CUSTOM_METERMODE = 0, CUSTOM_METERMODE = 0,
BAR_METERMODE, BAR_METERMODE,
TEXT_METERMODE, TEXT_METERMODE,
#ifdef USE_FUNKY_MODES
GRAPH_METERMODE, GRAPH_METERMODE,
LED_METERMODE, LED_METERMODE,
#endif
LAST_METERMODE LAST_METERMODE
} MeterModeId; } MeterModeId;
typedef struct GraphData_ {
struct timeval time;
double values[METER_BUFFER_LEN];
} GraphData;
}*/ }*/
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "UptimeMeter.h"
#include "ClockMeter.h"
#ifndef MIN #ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b))
#endif #endif
#ifndef MAX #ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #ifdef DEBUG
char* METER_CLASS = "Meter";
#else
#define METER_CLASS NULL
#endif #endif
MeterClass Meter_class = { MeterType* Meter_types[] = {
.super = { &CPUMeter,
.extends = Class(Object) &ClockMeter,
} &LoadAverageMeter,
&LoadMeter,
&MemoryMeter,
&SwapMeter,
&TasksMeter,
&UptimeMeter,
&AllCPUsMeter,
NULL
}; };
Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type) {
Meter* this = xCalloc(1, sizeof(Meter));
Object_setClass(this, type);
this->h = 1;
this->param = param;
this->pl = pl;
type->curItems = type->maxItems;
this->values = xCalloc(type->maxItems, sizeof(double));
this->total = type->total;
this->caption = xStrdup(type->caption);
if (Meter_initFn(this))
Meter_init(this);
Meter_setMode(this, type->defaultMode);
return this;
}
int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
const char * prefix = "KMGTPEZY";
unsigned long int powi = 1;
unsigned int written, powj = 1, precision = 2;
for(;;) {
if (value / 1024 < powi)
break;
if (prefix[1] == '\0')
break;
powi *= 1024;
++prefix;
}
if (*prefix == 'K')
precision = 0;
for (; precision > 0; precision--) {
powj *= 10;
if (value / powi < powj)
break;
}
written = snprintf(buffer, size, "%.*f%c",
precision, (double) value / powi, *prefix);
return written;
}
void Meter_delete(Object* cast) {
if (!cast)
return;
Meter* this = (Meter*) cast;
if (Meter_doneFn(this)) {
Meter_done(this);
}
free(this->drawData);
free(this->caption);
free(this->values);
free(this);
}
void Meter_setCaption(Meter* this, const char* caption) {
free(this->caption);
this->caption = xStrdup(caption);
}
static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* out) {
if (Object_displayFn(this)) {
Object_display(this, out);
} else {
RichString_write(out, CRT_colors[Meter_attributes(this)[0]], buffer);
}
}
void Meter_setMode(Meter* this, int modeIndex) {
if (modeIndex > 0 && modeIndex == this->mode)
return;
if (!modeIndex)
modeIndex = 1;
assert(modeIndex < LAST_METERMODE);
if (Meter_defaultMode(this) == CUSTOM_METERMODE) {
this->draw = Meter_drawFn(this);
if (Meter_updateModeFn(this))
Meter_updateMode(this, modeIndex);
} else {
assert(modeIndex >= 1);
free(this->drawData);
this->drawData = NULL;
MeterMode* mode = Meter_modes[modeIndex];
this->draw = mode->draw;
this->h = mode->h;
}
this->mode = modeIndex;
}
ListItem* Meter_toListItem(Meter* this, bool moving) {
char mode[21];
if (this->mode)
xSnprintf(mode, 20, " [%s]", Meter_modes[this->mode]->uiName);
else
mode[0] = '\0';
char number[11];
if (this->param > 0)
xSnprintf(number, 10, " %d", this->param);
else
number[0] = '\0';
char buffer[51];
xSnprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode);
ListItem* li = ListItem_new(buffer, 0);
li->moving = moving;
return li;
}
/* ---------- TextMeterMode ---------- */
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;
attrset(CRT_colors[METER_TEXT]);
mvaddstr(y, x, this->caption);
int captionLen = strlen(this->caption);
x += captionLen;
attrset(CRT_colors[RESET_COLOR]);
RichString_begin(out);
Meter_displayBuffer(this, buffer, &out);
RichString_printVal(out, y, x);
RichString_end(out);
}
/* ---------- BarMeterMode ---------- */
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);
w -= 2;
attrset(CRT_colors[METER_TEXT]);
int captionLen = 3;
mvaddnstr(y, x, this->caption, captionLen);
x += captionLen;
w -= captionLen;
attrset(CRT_colors[BAR_BORDER]);
mvaddch(y, x, '[');
mvaddch(y, x + w, ']');
w--;
x++;
if (w < 1) {
attrset(CRT_colors[RESET_COLOR]);
return;
}
char bar[w + 1];
int blockSizes[10];
xSnprintf(bar, w + 1, "%*.*s", w, w, buffer);
// First draw in the bar[] buffer...
int offset = 0;
int items = Meter_getItems(this);
for (int i = 0; i < items; i++) {
double value = this->values[i];
value = CLAMP(value, 0.0, this->total);
if (value > 0) {
blockSizes[i] = ceil((value/this->total) * w);
} else {
blockSizes[i] = 0;
}
int nextOffset = offset + blockSizes[i];
// (Control against invalid values)
nextOffset = CLAMP(nextOffset, 0, w);
for (int j = offset; j < nextOffset; j++)
if (bar[j] == ' ') {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
bar[j] = BarMeterMode_characters[i];
} else {
bar[j] = '|';
}
}
offset = nextOffset;
}
// ...then print the buffer.
offset = 0;
for (int i = 0; i < items; i++) {
attrset(CRT_colors[Meter_attributes(this)[i]]);
mvaddnstr(y, x + offset, bar + offset, blockSizes[i]);
offset += blockSizes[i];
offset = CLAMP(offset, 0, w);
}
if (offset < w) {
attrset(CRT_colors[BAR_SHADOW]);
mvaddnstr(y, x + offset, bar + offset, w - offset);
}
move(y, x + w + 1);
attrset(CRT_colors[RESET_COLOR]);
}
/* ---------- GraphMeterMode ---------- */
#ifdef HAVE_LIBNCURSESW
#define PIXPERROW_UTF8 4
static const char* const GraphMeterMode_dotsUtf8[] = {
/*00*/" ", /*01*/"", /*02*/"", /*03*/"", /*04*/ "",
/*10*/"", /*11*/"", /*12*/"", /*13*/"", /*14*/ "",
/*20*/"", /*21*/"", /*22*/"", /*23*/"", /*24*/ "",
/*30*/"", /*31*/"", /*32*/"", /*33*/"", /*34*/ "",
/*40*/"", /*41*/"", /*42*/"", /*43*/"", /*44*/ ""
};
#endif
#define PIXPERROW_ASCII 2
static const char* const GraphMeterMode_dotsAscii[] = {
/*00*/" ", /*01*/".", /*02*/":",
/*10*/".", /*11*/".", /*12*/":",
/*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) this->drawData = xCalloc(1, sizeof(GraphData));
GraphData* data = (GraphData*) this->drawData;
const int nValues = METER_BUFFER_LEN;
#ifdef HAVE_LIBNCURSESW
if (CRT_utf8) {
GraphMeterMode_dots = GraphMeterMode_dotsUtf8;
GraphMeterMode_pixPerRow = PIXPERROW_UTF8;
} else
#endif
{
GraphMeterMode_dots = GraphMeterMode_dotsAscii;
GraphMeterMode_pixPerRow = PIXPERROW_ASCII;
}
attrset(CRT_colors[METER_TEXT]);
int captionLen = 3;
mvaddnstr(y, x, this->caption, captionLen);
x += captionLen;
w -= captionLen;
struct timeval now;
gettimeofday(&now, NULL);
if (!timercmp(&now, &(data->time), <)) {
struct timeval delay = { .tv_sec = (int)(CRT_delay/10), .tv_usec = (CRT_delay-((int)(CRT_delay/10)*10)) * 100000 };
timeradd(&now, &delay, &(data->time));
for (int i = 0; i < nValues - 1; i++)
data->values[i] = data->values[i+1];
char buffer[nValues];
Meter_updateValues(this, buffer, nValues - 1);
double value = 0.0;
int items = Meter_getItems(this);
for (int i = 0; i < items; i++)
value += this->values[i];
value /= this->total;
data->values[nValues - 1] = value;
}
int i = nValues - (w*2) + 2, k = 0;
if (i < 0) {
k = -i/2;
i = 0;
}
for (; i < nValues - 1; i+=2, k++) {
int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix);
int v2 = CLAMP((int) lround(data->values[i+1] * pix), 1, pix);
int colorIdx = GRAPH_1;
for (int line = 0; line < GRAPH_HEIGHT; line++) {
int line1 = CLAMP(v1 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line)), 0, GraphMeterMode_pixPerRow);
int line2 = CLAMP(v2 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line)), 0, GraphMeterMode_pixPerRow);
attrset(CRT_colors[colorIdx]);
mvaddstr(y+line, x+k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]);
colorIdx = GRAPH_2;
}
}
attrset(CRT_colors[RESET_COLOR]);
}
/* ---------- LEDMeterMode ---------- */
static const char* const LEDMeterMode_digitsAscii[] = {
" __ "," "," __ "," __ "," "," __ "," __ "," __ "," __ "," __ ",
"| |"," |"," __|"," __|","|__|","|__ ","|__ "," |","|__|","|__|",
"|__|"," |","|__ "," __|"," |"," __|","|__|"," |","|__|"," __|"
};
#ifdef HAVE_LIBNCURSESW
static const char* const LEDMeterMode_digitsUtf8[] = {
"┌──┐","","╶──┐","╶──┐","╷ ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐",
"│ │","","┌──┘"," ──┤","└──┤","└──┐","├──┐","","├──┤","└──┤",
"└──┘","","└──╴","╶──┘","","╶──┘","└──┘","","└──┘"," ──┘"
};
#endif
static const char* const* LEDMeterMode_digits;
static void LEDMeterMode_drawDigit(int x, int y, int n) {
for (int i = 0; i < 3; i++)
mvaddstr(y+i, x, LEDMeterMode_digits[i * 10 + n]);
}
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
(void) w;
#ifdef HAVE_LIBNCURSESW
if (CRT_utf8)
LEDMeterMode_digits = LEDMeterMode_digitsUtf8;
else
#endif
LEDMeterMode_digits = LEDMeterMode_digitsAscii;
char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
RichString_begin(out);
Meter_displayBuffer(this, buffer, &out);
int yText =
#ifdef HAVE_LIBNCURSESW
CRT_utf8 ? y+1 :
#endif
y+2;
attrset(CRT_colors[LED_COLOR]);
mvaddstr(yText, x, this->caption);
int xx = x + strlen(this->caption);
int len = RichString_sizeVal(out);
for (int i = 0; i < len; i++) {
char c = RichString_getCharVal(out, i);
if (c >= '0' && c <= '9') {
LEDMeterMode_drawDigit(xx, y, c-48);
xx += 4;
} else {
mvaddch(yText, xx, c);
xx += 1;
}
}
attrset(CRT_colors[RESET_COLOR]);
RichString_end(out);
}
static MeterMode BarMeterMode = { static MeterMode BarMeterMode = {
.uiName = "Bar", .uiName = "Bar",
.h = 1, .h = 1,
@ -498,9 +135,11 @@ static MeterMode TextMeterMode = {
.draw = TextMeterMode_draw, .draw = TextMeterMode_draw,
}; };
#ifdef USE_FUNKY_MODES
static MeterMode GraphMeterMode = { static MeterMode GraphMeterMode = {
.uiName = "Graph", .uiName = "Graph",
.h = GRAPH_HEIGHT, .h = 3,
.draw = GraphMeterMode_draw, .draw = GraphMeterMode_draw,
}; };
@ -510,45 +149,285 @@ static MeterMode LEDMeterMode = {
.draw = LEDMeterMode_draw, .draw = LEDMeterMode_draw,
}; };
#endif
MeterMode* Meter_modes[] = { MeterMode* Meter_modes[] = {
NULL, NULL,
&BarMeterMode, &BarMeterMode,
&TextMeterMode, &TextMeterMode,
#ifdef USE_FUNKY_MODES
&GraphMeterMode, &GraphMeterMode,
&LEDMeterMode, &LEDMeterMode,
#endif
NULL NULL
}; };
/* Blank meter */ static RichString Meter_stringBuffer;
static void BlankMeter_updateValues(Meter* this, char* buffer, int size) { Meter* Meter_new(ProcessList* pl, int param, MeterType* type) {
(void) this; (void) buffer; (void) size; Meter* this = calloc(sizeof(Meter), 1);
if (size > 0) { Object_setClass(this, METER_CLASS);
*buffer = 0; ((Object*)this)->delete = Meter_delete;
((Object*)this)->display = type->display;
this->h = 1;
this->type = type;
this->param = param;
this->pl = pl;
this->values = calloc(sizeof(double), type->items);
this->total = type->total;
this->caption = strdup(type->caption);
Meter_setMode(this, type->mode);
if (this->type->init)
this->type->init(this);
return this;
}
void Meter_delete(Object* cast) {
Meter* this = (Meter*) cast;
assert (this != NULL);
if (this->type->done) {
this->type->done(this);
}
if (this->drawBuffer)
free(this->drawBuffer);
free(this->caption);
free(this->values);
free(this);
}
void Meter_setCaption(Meter* this, char* caption) {
free(this->caption);
this->caption = strdup(caption);
}
static inline void Meter_displayToStringBuffer(Meter* this, char* buffer) {
MeterType* type = this->type;
Object_Display display = ((Object*)this)->display;
if (display) {
display((Object*)this, &Meter_stringBuffer);
} else {
RichString_initVal(Meter_stringBuffer);
RichString_append(&Meter_stringBuffer, CRT_colors[type->attributes[0]], buffer);
} }
} }
static void BlankMeter_display(Object* cast, RichString* out) { void Meter_setMode(Meter* this, int modeIndex) {
(void) cast; if (modeIndex > 0 && modeIndex == this->mode)
RichString_prune(out); return;
if (!modeIndex)
modeIndex = 1;
assert(modeIndex < LAST_METERMODE);
if (this->type->mode == 0) {
this->draw = this->type->draw;
if (this->type->setMode)
this->type->setMode(this, modeIndex);
} else {
assert(modeIndex >= 1);
if (this->drawBuffer)
free(this->drawBuffer);
this->drawBuffer = NULL;
MeterMode* mode = Meter_modes[modeIndex];
this->draw = mode->draw;
this->h = mode->h;
}
this->mode = modeIndex;
} }
int BlankMeter_attributes[] = { ListItem* Meter_toListItem(Meter* this) {
DEFAULT_COLOR MeterType* type = this->type;
char mode[21];
if (this->mode)
snprintf(mode, 20, " [%s]", Meter_modes[this->mode]->uiName);
else
mode[0] = '\0';
char number[11];
if (this->param > 0)
snprintf(number, 10, " %d", this->param);
else
number[0] = '\0';
char buffer[51];
snprintf(buffer, 50, "%s%s%s", type->uiName, number, mode);
return ListItem_new(buffer, 0);
}
/* ---------- TextMeterMode ---------- */
void TextMeterMode_draw(Meter* this, int x, int y, int w) {
MeterType* type = this->type;
char buffer[METER_BUFFER_LEN];
type->setValues(this, buffer, METER_BUFFER_LEN - 1);
attrset(CRT_colors[METER_TEXT]);
mvaddstr(y, x, this->caption);
int captionLen = strlen(this->caption);
w -= captionLen;
x += captionLen;
Meter_displayToStringBuffer(this, buffer);
mvhline(y, x, ' ', CRT_colors[DEFAULT_COLOR]);
attrset(CRT_colors[RESET_COLOR]);
mvaddchstr(y, x, Meter_stringBuffer.chstr);
}
/* ---------- BarMeterMode ---------- */
static char BarMeterMode_characters[] = "|#*@$%&";
void BarMeterMode_draw(Meter* this, int x, int y, int w) {
MeterType* type = this->type;
char buffer[METER_BUFFER_LEN];
type->setValues(this, buffer, METER_BUFFER_LEN - 1);
w -= 2;
attrset(CRT_colors[METER_TEXT]);
mvaddstr(y, x, this->caption);
int captionLen = strlen(this->caption);
x += captionLen;
w -= captionLen;
attrset(CRT_colors[BAR_BORDER]);
mvaddch(y, x, '[');
mvaddch(y, x + w, ']');
w--;
x++;
char bar[w];
int blockSizes[10];
for (int i = 0; i < w; i++)
bar[i] = ' ';
sprintf(bar + (w-strlen(buffer)), "%s", buffer);
// First draw in the bar[] buffer...
double total = 0.0;
int offset = 0;
for (int i = 0; i < type->items; i++) {
double value = this->values[i];
value = MAX(value, 0);
value = MIN(value, this->total);
if (value > 0) {
blockSizes[i] = ceil((value/this->total) * w);
} else {
blockSizes[i] = 0;
}
int nextOffset = offset + blockSizes[i];
// (Control against invalid values)
nextOffset = MIN(MAX(nextOffset, 0), w);
for (int j = offset; j < nextOffset; j++)
if (bar[j] == ' ') {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
bar[j] = BarMeterMode_characters[i];
} else {
bar[j] = '|';
}
}
offset = nextOffset;
total += this->values[i];
}
// ...then print the buffer.
offset = 0;
for (int i = 0; i < type->items; i++) {
attrset(CRT_colors[type->attributes[i]]);
mvaddnstr(y, x + offset, bar + offset, blockSizes[i]);
offset += blockSizes[i];
offset = MAX(offset, 0);
offset = MIN(offset, w);
}
if (offset < w) {
attrset(CRT_colors[BAR_SHADOW]);
mvaddnstr(y, x + offset, bar + offset, w - offset);
}
move(y, x + w + 1);
attrset(CRT_colors[RESET_COLOR]);
}
#ifdef USE_FUNKY_MODES
/* ---------- GraphMeterMode ---------- */
#define DrawDot(a,y,c) do { attrset(a); mvaddch(y, x+k, c); } while(0)
static int GraphMeterMode_colors[21] = {
GRAPH_1, GRAPH_1, GRAPH_1,
GRAPH_2, GRAPH_2, GRAPH_2,
GRAPH_3, GRAPH_3, GRAPH_3,
GRAPH_4, GRAPH_4, GRAPH_4,
GRAPH_5, GRAPH_5, GRAPH_6,
GRAPH_7, GRAPH_7, GRAPH_7,
GRAPH_8, GRAPH_8, GRAPH_9
}; };
MeterClass BlankMeter_class = { static char* GraphMeterMode_characters = "^`'-.,_~'`-.,_~'`-.,_";
.super = {
.extends = Class(Meter), void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
.delete = Meter_delete,
.display = BlankMeter_display, if (!this->drawBuffer) this->drawBuffer = calloc(sizeof(double), METER_BUFFER_LEN);
}, double* drawBuffer = (double*) this->drawBuffer;
.updateValues = BlankMeter_updateValues,
.defaultMode = TEXT_METERMODE, for (int i = 0; i < METER_BUFFER_LEN - 1; i++)
.maxItems = 0, drawBuffer[i] = drawBuffer[i+1];
.total = 100.0,
.attributes = BlankMeter_attributes, MeterType* type = this->type;
.name = "Blank", char buffer[METER_BUFFER_LEN];
.uiName = "Blank", type->setValues(this, buffer, METER_BUFFER_LEN - 1);
.caption = ""
double value = 0.0;
for (int i = 0; i < type->items; i++)
value += this->values[i];
value /= this->total;
drawBuffer[METER_BUFFER_LEN - 1] = value;
for (int i = METER_BUFFER_LEN - w, k = 0; i < METER_BUFFER_LEN; i++, k++) {
double value = drawBuffer[i];
DrawDot( CRT_colors[DEFAULT_COLOR], y, ' ' );
DrawDot( CRT_colors[DEFAULT_COLOR], y+1, ' ' );
DrawDot( CRT_colors[DEFAULT_COLOR], y+2, ' ' );
double threshold = 1.00;
for (int i = 0; i < 21; i++, threshold -= 0.05)
if (value >= threshold) {
DrawDot(CRT_colors[GraphMeterMode_colors[i]], y+(i/7.0), GraphMeterMode_characters[i]);
break;
}
}
attrset(CRT_colors[RESET_COLOR]);
}
/* ---------- LEDMeterMode ---------- */
static char* LEDMeterMode_digits[3][10] = {
{ " __ "," "," __ "," __ "," "," __ "," __ "," __ "," __ "," __ "},
{ "| |"," |"," __|"," __|","|__|","|__ ","|__ "," |","|__|","|__|"},
{ "|__|"," |","|__ "," __|"," |"," __|","|__|"," |","|__|"," __|"},
}; };
static void LEDMeterMode_drawDigit(int x, int y, int n) {
for (int i = 0; i < 3; i++)
mvaddstr(y+i, x, LEDMeterMode_digits[i][n]);
}
void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
MeterType* type = this->type;
char buffer[METER_BUFFER_LEN];
type->setValues(this, buffer, METER_BUFFER_LEN - 1);
Meter_displayToStringBuffer(this, buffer);
attrset(CRT_colors[LED_COLOR]);
mvaddstr(y+2, x, this->caption);
int xx = x + strlen(this->caption);
for (int i = 0; i < Meter_stringBuffer.len; i++) {
char c = Meter_stringBuffer.chstr[i];
if (c >= '0' && c <= '9') {
LEDMeterMode_drawDigit(xx, y, c-48);
xx += 4;
} else {
mvaddch(y+2, xx, c);
xx += 1;
}
}
attrset(CRT_colors[RESET_COLOR]);
}
#endif

187
Meter.h
View File

@ -4,98 +4,99 @@
#define HEADER_Meter #define HEADER_Meter
/* /*
htop - Meter.h htop - Meter.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#define METER_BUFFER_LEN 256 #define _GNU_SOURCE
#include <math.h>
#define GRAPH_DELAY (DEFAULT_DELAY/2) #include <string.h>
#include <stdlib.h>
#define GRAPH_HEIGHT 4 /* Unit: rows (lines) */ #include <curses.h>
#include <stdarg.h>
#include "Object.h"
#include "CRT.h"
#include "ListItem.h" #include "ListItem.h"
#include "String.h"
#include "ProcessList.h"
#include "debug.h"
#include <assert.h>
#ifndef USE_FUNKY_MODES
#define USE_FUNKY_MODES 1
#endif
#define METER_BUFFER_LEN 128
#include <sys/time.h>
typedef struct Meter_ Meter; typedef struct Meter_ Meter;
typedef struct MeterType_ MeterType;
typedef struct MeterMode_ MeterMode;
typedef void(*Meter_Init)(Meter*); typedef void(*MeterType_Init)(Meter*);
typedef void(*Meter_Done)(Meter*); typedef void(*MeterType_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int); typedef void(*MeterType_SetMode)(Meter*, int);
typedef void(*Meter_UpdateValues)(Meter*, char*, int); typedef void(*Meter_SetValues)(Meter*, char*, int);
typedef void(*Meter_Draw)(Meter*, int, int, int); typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ { struct MeterMode_ {
ObjectClass super; Meter_Draw draw;
const Meter_Init init; char* uiName;
const Meter_Done done; int h;
const Meter_UpdateMode updateMode; };
const Meter_Draw draw;
const Meter_UpdateValues updateValues;
const int defaultMode;
const double total;
const int* attributes;
const char* name;
const char* uiName;
const char* caption;
const char* description;
const char maxItems;
char curItems;
} MeterClass;
#define As_Meter(this_) ((MeterClass*)((this_)->super.klass)) struct MeterType_ {
#define Meter_initFn(this_) As_Meter(this_)->init Meter_SetValues setValues;
#define Meter_init(this_) As_Meter(this_)->init((Meter*)(this_)) Object_Display display;
#define Meter_done(this_) As_Meter(this_)->done((Meter*)(this_)) int mode;
#define Meter_updateModeFn(this_) As_Meter(this_)->updateMode int items;
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_) double total;
#define Meter_drawFn(this_) As_Meter(this_)->draw int* attributes;
#define Meter_doneFn(this_) As_Meter(this_)->done char* name;
#define Meter_updateValues(this_, buf_, sz_) \ char* uiName;
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_) char* caption;
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode MeterType_Init init;
#define Meter_getItems(this_) As_Meter(this_)->curItems MeterType_Done done;
#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_) MeterType_SetMode setMode;
#define Meter_attributes(this_) As_Meter(this_)->attributes Meter_Draw draw;
#define Meter_name(this_) As_Meter(this_)->name };
#define Meter_uiName(this_) As_Meter(this_)->uiName
struct Meter_ { struct Meter_ {
Object super; Object super;
Meter_Draw draw;
char* caption; char* caption;
MeterType* type;
int mode; int mode;
int param; int param;
void* drawData; Meter_Draw draw;
void* drawBuffer;
int h; int h;
struct ProcessList_* pl; ProcessList* pl;
double* values; double* values;
double total; double total;
}; };
typedef struct MeterMode_ {
Meter_Draw draw;
const char* uiName;
int h;
} MeterMode;
typedef enum { typedef enum {
CUSTOM_METERMODE = 0, CUSTOM_METERMODE = 0,
BAR_METERMODE, BAR_METERMODE,
TEXT_METERMODE, TEXT_METERMODE,
#ifdef USE_FUNKY_MODES
GRAPH_METERMODE, GRAPH_METERMODE,
LED_METERMODE, LED_METERMODE,
#endif
LAST_METERMODE LAST_METERMODE
} MeterModeId; } MeterModeId;
typedef struct GraphData_ {
struct timeval time;
double values[METER_BUFFER_LEN];
} GraphData;
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "UptimeMeter.h"
#include "ClockMeter.h"
#ifndef MIN #ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b))
@ -103,49 +104,51 @@ typedef struct GraphData_ {
#ifndef MAX #ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #ifdef DEBUG
extern char* METER_CLASS;
#else
#define METER_CLASS NULL
#endif #endif
extern MeterClass Meter_class; extern MeterType* Meter_types[];
extern Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type); #ifdef USE_FUNKY_MODES
extern int Meter_humanUnit(char* buffer, unsigned long int value, int size);
extern void Meter_delete(Object* cast);
extern void Meter_setCaption(Meter* this, const char* caption);
extern void Meter_setMode(Meter* this, int modeIndex);
extern ListItem* Meter_toListItem(Meter* this, bool moving);
/* ---------- TextMeterMode ---------- */
/* ---------- BarMeterMode ---------- */
/* ---------- GraphMeterMode ---------- */
#ifdef HAVE_LIBNCURSESW
#define PIXPERROW_UTF8 4
#endif
#define PIXPERROW_ASCII 2
/* ---------- LEDMeterMode ---------- */
#ifdef HAVE_LIBNCURSESW
#endif #endif
extern MeterMode* Meter_modes[]; extern MeterMode* Meter_modes[];
/* Blank meter */ Meter* Meter_new(ProcessList* pl, int param, MeterType* type);
extern int BlankMeter_attributes[]; void Meter_delete(Object* cast);
extern MeterClass BlankMeter_class; void Meter_setCaption(Meter* this, char* caption);
void Meter_setMode(Meter* this, int modeIndex);
ListItem* Meter_toListItem(Meter* this);
/* ---------- TextMeterMode ---------- */
void TextMeterMode_draw(Meter* this, int x, int y, int w);
/* ---------- BarMeterMode ---------- */
void BarMeterMode_draw(Meter* this, int x, int y, int w);
#ifdef USE_FUNKY_MODES
/* ---------- GraphMeterMode ---------- */
#define DrawDot(a,y,c) do { attrset(a); mvaddch(y, x+k, c); } while(0)
void GraphMeterMode_draw(Meter* this, int x, int y, int w);
/* ---------- LEDMeterMode ---------- */
void LEDMeterMode_draw(Meter* this, int x, int y, int w);
#endif
#endif #endif

View File

@ -1,135 +1,71 @@
/*
htop - MetersPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "MetersPanel.h" #include "MetersPanel.h"
#include <stdlib.h>
#include <assert.h>
#include "CRT.h"
/*{
#include "Panel.h" #include "Panel.h"
#include "Settings.h" #include "Settings.h"
#include "ScreenManager.h" #include "ScreenManager.h"
typedef struct MetersPanel_ MetersPanel; #include "debug.h"
#include <assert.h>
struct MetersPanel_ { /*{
typedef struct MetersPanel_ {
Panel super; Panel super;
Settings* settings; Settings* settings;
Vector* meters; Vector* meters;
ScreenManager* scr; ScreenManager* scr;
MetersPanel* leftNeighbor; } MetersPanel;
MetersPanel* rightNeighbor;
bool moving;
};
}*/ }*/
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI MetersPanel* MetersPanel_new(Settings* settings, char* header, Vector* meters, ScreenManager* scr) {
// we call them "Styles". MetersPanel* this = (MetersPanel*) malloc(sizeof(MetersPanel));
static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL}; Panel* super = (Panel*) this;
static const char* const MetersKeys[] = {"Space", "Enter", " ", "Del", "F10"}; Panel_init(super, 1, 1, 1, 1, LISTITEM_CLASS, true);
static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)}; ((Object*)this)->delete = MetersPanel_delete;
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese this->settings = settings;
// terminals, breaking our aligning. this->meters = meters;
// In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are this->scr = scr;
// considered "Ambiguous characters". super->eventHandler = MetersPanel_EventHandler;
static const char* const MetersMovingFunctions[] = {"Style ", "Lock ", "Up ", "Down ", "Left ", "Right ", " ", "Delete", "Done ", NULL}; Panel_setHeader(super, header);
static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-", "->", " ", "Del", "F10"}; for (int i = 0; i < Vector_size(meters); i++) {
static int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)}; Meter* meter = (Meter*) Vector_get(meters, i);
static FunctionBar* Meters_movingBar = NULL; Panel_add(super, (Object*) Meter_toListItem(meter));
}
return this;
}
static void MetersPanel_delete(Object* object) { void MetersPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
MetersPanel* this = (MetersPanel*) object; MetersPanel* this = (MetersPanel*) object;
Panel_done(super); Panel_done(super);
free(this); free(this);
} }
void MetersPanel_setMoving(MetersPanel* this, bool moving) { HandlerResult MetersPanel_EventHandler(Panel* super, int ch) {
Panel* super = (Panel*) this;
this->moving = moving;
ListItem* selected = (ListItem*)Panel_getSelected(super);
if (selected) {
selected->moving = moving;
}
if (!moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
Panel_setDefaultBar(super);
} else {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]);
super->currentBar = Meters_movingBar;
}
FunctionBar_draw(this->super.currentBar, NULL);
}
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {
Panel* super = (Panel*) this;
if (this->moving) {
if (neighbor) {
if (selected < Vector_size(this->meters)) {
MetersPanel_setMoving(this, false);
Meter* meter = (Meter*) Vector_take(this->meters, selected);
Panel_remove(super, selected);
Vector_insert(neighbor->meters, selected, meter);
Panel_insert(&(neighbor->super), selected, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(&(neighbor->super), selected);
MetersPanel_setMoving(neighbor, true);
return true;
}
}
}
return false;
}
static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
MetersPanel* this = (MetersPanel*) super; MetersPanel* this = (MetersPanel*) super;
int selected = Panel_getSelectedIndex(super); int selected = Panel_getSelectedIndex(super);
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
bool sideMove = false;
switch(ch) { switch(ch) {
case 0x0a: case 0x0a:
case 0x0d: case 0x0d:
case KEY_ENTER: case KEY_ENTER:
{
if (!Vector_size(this->meters))
break;
MetersPanel_setMoving(this, !(this->moving));
result = HANDLED;
break;
}
case ' ':
case KEY_F(4): case KEY_F(4):
case 't': case 't':
{ {
if (!Vector_size(this->meters))
break;
Meter* meter = (Meter*) Vector_get(this->meters, selected); Meter* meter = (Meter*) Vector_get(this->meters, selected);
int mode = meter->mode + 1; int mode = meter->mode + 1;
if (mode == LAST_METERMODE) mode = 1; if (mode == LAST_METERMODE) mode = 1;
Meter_setMode(meter, mode); Meter_setMode(meter, mode);
Panel_set(super, selected, (Object*) Meter_toListItem(meter, this->moving)); Panel_set(super, selected, (Object*) Meter_toListItem(meter));
result = HANDLED; result = HANDLED;
break; break;
} }
case KEY_UP:
{
if (!this->moving) {
break;
}
}
/* else fallthrough */
case KEY_F(7): case KEY_F(7):
case '[': case '[':
case '-': case '-':
@ -139,13 +75,6 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
result = HANDLED; result = HANDLED;
break; break;
} }
case KEY_DOWN:
{
if (!this->moving) {
break;
}
}
/* else fallthrough */
case KEY_F(8): case KEY_F(8):
case ']': case ']':
case '+': case '+':
@ -155,76 +84,22 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
result = HANDLED; result = HANDLED;
break; break;
} }
case KEY_RIGHT:
{
sideMove = moveToNeighbor(this, this->rightNeighbor, selected);
if (this->moving && !sideMove) {
// lock user here until it exits positioning-mode
result = HANDLED;
}
// if user is free, don't set HANDLED;
// let ScreenManager handle focus.
break;
}
case KEY_LEFT:
{
sideMove = moveToNeighbor(this, this->leftNeighbor, selected);
if (this->moving && !sideMove) {
result = HANDLED;
}
break;
}
case KEY_F(9): case KEY_F(9):
case KEY_DC: case KEY_DC:
{ {
if (!Vector_size(this->meters))
break;
if (selected < Vector_size(this->meters)) { if (selected < Vector_size(this->meters)) {
Vector_remove(this->meters, selected); Vector_remove(this->meters, selected);
Panel_remove(super, selected); Panel_remove(super, selected);
} }
MetersPanel_setMoving(this, false);
result = HANDLED; result = HANDLED;
break; break;
} }
} }
if (result == HANDLED || sideMove) { if (result == HANDLED) {
Header* header = (Header*) this->scr->header; Header* header = this->settings->header;
this->settings->changed = true;
Header_calculateHeight(header); Header_calculateHeight(header);
Header_draw(header); Header_draw(header);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2); ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
} }
return result; return result;
} }
PanelClass MetersPanel_class = {
.super = {
.extends = Class(Panel),
.delete = MetersPanel_delete
},
.eventHandler = MetersPanel_eventHandler
};
MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr) {
MetersPanel* this = AllocThis(MetersPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(MetersFunctions, MetersKeys, MetersEvents);
if (!Meters_movingBar) {
Meters_movingBar = FunctionBar_new(MetersMovingFunctions, MetersMovingKeys, MetersMovingEvents);
}
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->meters = meters;
this->scr = scr;
this->moving = false;
this->rightNeighbor = NULL;
this->leftNeighbor = NULL;
Panel_setHeader(super, header);
for (int i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i);
Panel_add(super, (Object*) Meter_toListItem(meter, false));
}
return this;
}

View File

@ -2,42 +2,29 @@
#ifndef HEADER_MetersPanel #ifndef HEADER_MetersPanel
#define HEADER_MetersPanel #define HEADER_MetersPanel
/*
htop - MetersPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h" #include "Panel.h"
#include "Settings.h" #include "Settings.h"
#include "ScreenManager.h" #include "ScreenManager.h"
typedef struct MetersPanel_ MetersPanel; #include "debug.h"
#include <assert.h>
struct MetersPanel_ {
typedef struct MetersPanel_ {
Panel super; Panel super;
Settings* settings; Settings* settings;
Vector* meters; Vector* meters;
ScreenManager* scr; ScreenManager* scr;
MetersPanel* leftNeighbor; } MetersPanel;
MetersPanel* rightNeighbor;
bool moving;
};
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI MetersPanel* MetersPanel_new(Settings* settings, char* header, Vector* meters, ScreenManager* scr);
// we call them "Styles".
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning.
// In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are
// considered "Ambiguous characters".
extern void MetersPanel_setMoving(MetersPanel* this, bool moving); void MetersPanel_delete(Object* object);
extern PanelClass MetersPanel_class; HandlerResult MetersPanel_EventHandler(Panel* super, int ch);
extern MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr);
#endif #endif

5
NEWS
View File

@ -1,4 +1,5 @@
See the commit history for news of the past. See the ChangeLog for news of the past.
See the bug tracker for news of the future. See the TODO list for news of the future.
Run the program for news of the present. Run the program for news of the present.

View File

@ -1,69 +1,57 @@
/* /*
htop - Object.c htop
(C) 2004-2012 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Object.h" #include "Object.h"
#include "RichString.h"
#include "CRT.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "debug.h"
/*{ /*{
#include "RichString.h"
#include "XAlloc.h" #ifndef DEBUG
#define Object_setClass(obj, class)
#endif
typedef struct Object_ Object; typedef struct Object_ Object;
typedef void(*Object_Display)(Object*, RichString*); typedef void(*Object_Display)(Object*, RichString*);
typedef long(*Object_Compare)(const void*, const void*); typedef int(*Object_Compare)(const void*, const void*);
typedef void(*Object_Delete)(Object*); typedef void(*Object_Delete)(Object*);
#define Object_getClass(obj_) ((Object*)(obj_))->klass
#define Object_setClass(obj_, class_) Object_getClass(obj_) = (ObjectClass*) class_
#define Object_delete(obj_) Object_getClass(obj_)->delete((Object*)(obj_))
#define Object_displayFn(obj_) Object_getClass(obj_)->display
#define Object_display(obj_, str_) Object_getClass(obj_)->display((Object*)(obj_), str_)
#define Object_compare(obj_, other_) Object_getClass(obj_)->compare((const void*)(obj_), other_)
#define Class(class_) ((ObjectClass*)(&(class_ ## _class)))
#define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_));
typedef struct ObjectClass_ {
const void* extends;
const Object_Display display;
const Object_Delete delete;
const Object_Compare compare;
} ObjectClass;
struct Object_ { struct Object_ {
ObjectClass* klass; #ifdef DEBUG
char* class;
#endif
Object_Display display;
Object_Delete delete;
}; };
typedef union {
int i;
void* v;
} Arg;
}*/ }*/
ObjectClass Object_class = { #ifdef DEBUG
.extends = NULL char* OBJECT_CLASS = "Object";
};
#else
#define OBJECT_CLASS NULL
#endif
#ifdef DEBUG #ifdef DEBUG
bool Object_isA(Object* o, const ObjectClass* klass) { void Object_setClass(void* this, char* class) {
if (!o) ((Object*)this)->class = class;
return false; }
const ObjectClass* type = o->klass;
while (type) { void Object_display(Object* this, RichString* out) {
if (type == klass) char objAddress[50];
return true; sprintf(objAddress, "%s @ %p", this->class, (void*) this);
type = type->extends; RichString_write(out, CRT_colors[DEFAULT_COLOR], objAddress);
}
return false;
} }
#endif #endif

View File

@ -3,56 +3,51 @@
#ifndef HEADER_Object #ifndef HEADER_Object
#define HEADER_Object #define HEADER_Object
/* /*
htop - Object.h htop
(C) 2004-2012 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "RichString.h" #include "RichString.h"
#include "XAlloc.h" #include "CRT.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "debug.h"
#ifndef DEBUG
#define Object_setClass(obj, class)
#endif
typedef struct Object_ Object; typedef struct Object_ Object;
typedef void(*Object_Display)(Object*, RichString*); typedef void(*Object_Display)(Object*, RichString*);
typedef long(*Object_Compare)(const void*, const void*); typedef int(*Object_Compare)(const void*, const void*);
typedef void(*Object_Delete)(Object*); typedef void(*Object_Delete)(Object*);
#define Object_getClass(obj_) ((Object*)(obj_))->klass
#define Object_setClass(obj_, class_) Object_getClass(obj_) = (ObjectClass*) class_
#define Object_delete(obj_) Object_getClass(obj_)->delete((Object*)(obj_))
#define Object_displayFn(obj_) Object_getClass(obj_)->display
#define Object_display(obj_, str_) Object_getClass(obj_)->display((Object*)(obj_), str_)
#define Object_compare(obj_, other_) Object_getClass(obj_)->compare((const void*)(obj_), other_)
#define Class(class_) ((ObjectClass*)(&(class_ ## _class)))
#define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_));
typedef struct ObjectClass_ {
const void* extends;
const Object_Display display;
const Object_Delete delete;
const Object_Compare compare;
} ObjectClass;
struct Object_ { struct Object_ {
ObjectClass* klass; #ifdef DEBUG
char* class;
#endif
Object_Display display;
Object_Delete delete;
}; };
typedef union { #ifdef DEBUG
int i; extern char* OBJECT_CLASS;
void* v;
} Arg;
#else
extern ObjectClass Object_class; #define OBJECT_CLASS NULL
#endif
#ifdef DEBUG #ifdef DEBUG
extern bool Object_isA(Object* o, const ObjectClass* klass); void Object_setClass(void* this, char* class);
void Object_display(Object* this, RichString* out);
#endif #endif

View File

@ -1,181 +0,0 @@
/*
htop - OpenFilesScreen.c
(C) 2005-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "OpenFilesScreen.h"
#include "CRT.h"
#include "ProcessList.h"
#include "IncSet.h"
#include "StringUtils.h"
#include "FunctionBar.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
/*{
#include "InfoScreen.h"
typedef struct OpenFiles_Data_ {
char* data[256];
} OpenFiles_Data;
typedef struct OpenFiles_ProcessData_ {
OpenFiles_Data data;
int error;
struct OpenFiles_FileData_* files;
} OpenFiles_ProcessData;
typedef struct OpenFiles_FileData_ {
OpenFiles_Data data;
struct OpenFiles_FileData_* next;
} OpenFiles_FileData;
typedef struct OpenFilesScreen_ {
InfoScreen super;
pid_t pid;
} OpenFilesScreen;
}*/
InfoScreenClass OpenFilesScreen_class = {
.super = {
.extends = Class(Object),
.delete = OpenFilesScreen_delete
},
.scan = OpenFilesScreen_scan,
.draw = OpenFilesScreen_draw
};
OpenFilesScreen* OpenFilesScreen_new(Process* process) {
OpenFilesScreen* this = xMalloc(sizeof(OpenFilesScreen));
Object_setClass(this, Class(OpenFilesScreen));
if (Process_isThread(process))
this->pid = process->tgid;
else
this->pid = process->pid;
return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " FD TYPE DEVICE SIZE NODE NAME");
}
void OpenFilesScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this));
}
void OpenFilesScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Snapshot of files open in process %d - %s", ((OpenFilesScreen*)this)->pid, this->process->comm);
}
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
char buffer[1025];
xSnprintf(buffer, 1024, "%d", pid);
OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_FileData* fdata = NULL;
OpenFiles_Data* item = &(pdata->data);
int fdpair[2];
if (pipe(fdpair) == -1) {
pdata->error = 1;
return pdata;
}
pid_t child = fork();
if (child == -1) {
pdata->error = 1;
return pdata;
}
if (child == 0) {
close(fdpair[0]);
dup2(fdpair[1], STDOUT_FILENO);
close(fdpair[1]);
int fdnull = open("/dev/null", O_WRONLY);
if (fdnull < 0)
exit(1);
dup2(fdnull, STDERR_FILENO);
close(fdnull);
execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
exit(127);
}
close(fdpair[1]);
FILE* fd = fdopen(fdpair[0], "r");
for (;;) {
char* line = String_readLine(fd);
if (!line) {
break;
}
unsigned char cmd = line[0];
if (cmd == 'f') {
OpenFiles_FileData* nextFile = xCalloc(1, sizeof(OpenFiles_FileData));
if (fdata == NULL) {
pdata->files = nextFile;
} else {
fdata->next = nextFile;
}
fdata = nextFile;
item = &(fdata->data);
}
item->data[cmd] = xStrdup(line + 1);
free(line);
}
fclose(fd);
int wstatus;
if (waitpid(child, &wstatus, 0) == -1) {
pdata->error = 1;
return pdata;
}
if (!WIFEXITED(wstatus))
pdata->error = 1;
else
pdata->error = WEXITSTATUS(wstatus);
return pdata;
}
static inline void OpenFiles_Data_clear(OpenFiles_Data* data) {
for (int i = 0; i < 255; i++)
if (data->data[i])
free(data->data[i]);
}
void OpenFilesScreen_scan(InfoScreen* this) {
Panel* panel = this->display;
int idx = Panel_getSelectedIndex(panel);
Panel_prune(panel);
OpenFiles_ProcessData* pdata = OpenFilesScreen_getProcessData(((OpenFilesScreen*)this)->pid);
if (pdata->error == 127) {
InfoScreen_addLine(this, "Could not execute 'lsof'. Please make sure it is available in your $PATH.");
} else if (pdata->error == 1) {
InfoScreen_addLine(this, "Failed listing open files.");
} else {
OpenFiles_FileData* fdata = pdata->files;
while (fdata) {
char** data = fdata->data.data;
int lenN = data['n'] ? strlen(data['n']) : 0;
int sizeEntry = 5 + 7 + 10 + 10 + 10 + lenN + 5 /*spaces*/ + 1 /*null*/;
char entry[sizeEntry];
xSnprintf(entry, sizeEntry, "%5.5s %7.7s %10.10s %10.10s %10.10s %s",
data['f'] ? data['f'] : "",
data['t'] ? data['t'] : "",
data['D'] ? data['D'] : "",
data['s'] ? data['s'] : "",
data['i'] ? data['i'] : "",
data['n'] ? data['n'] : "");
InfoScreen_addLine(this, entry);
OpenFiles_Data_clear(&fdata->data);
OpenFiles_FileData* old = fdata;
fdata = fdata->next;
free(old);
}
OpenFiles_Data_clear(&pdata->data);
}
free(pdata);
Vector_insertionSort(this->lines);
Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx);
}

View File

@ -1,45 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_OpenFilesScreen
#define HEADER_OpenFilesScreen
/*
htop - OpenFilesScreen.h
(C) 2005-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "InfoScreen.h"
typedef struct OpenFiles_Data_ {
char* data[256];
} OpenFiles_Data;
typedef struct OpenFiles_ProcessData_ {
OpenFiles_Data data;
int error;
struct OpenFiles_FileData_* files;
} OpenFiles_ProcessData;
typedef struct OpenFiles_FileData_ {
OpenFiles_Data data;
struct OpenFiles_FileData_* next;
} OpenFiles_FileData;
typedef struct OpenFilesScreen_ {
InfoScreen super;
pid_t pid;
} OpenFilesScreen;
extern InfoScreenClass OpenFilesScreen_class;
extern OpenFilesScreen* OpenFilesScreen_new(Process* process);
extern void OpenFilesScreen_delete(Object* this);
extern void OpenFilesScreen_draw(InfoScreen* this);
extern void OpenFilesScreen_scan(InfoScreen* this);
#endif

386
Panel.c
View File

@ -1,79 +1,52 @@
/* /*
htop - Panel.c htop - Panel.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Object.h"
#include "Panel.h" #include "Panel.h"
#include "Vector.h"
#include "CRT.h" #include "CRT.h"
#include "RichString.h" #include "RichString.h"
#include "ListItem.h" #include "ListItem.h"
#include "StringUtils.h"
#include <math.h> #include <math.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
#include <ctype.h> #include "debug.h"
#include <string.h>
#include <assert.h> #include <assert.h>
#include <curses.h>
//#link curses //#link curses
/*{ /*{
#include "Object.h"
#include "Vector.h"
#include "FunctionBar.h"
typedef struct Panel_ Panel; typedef struct Panel_ Panel;
typedef enum HandlerResult_ { typedef enum HandlerResult_ {
HANDLED = 0x01, HANDLED,
IGNORED = 0x02, IGNORED,
BREAK_LOOP = 0x04, BREAK_LOOP
REDRAW = 0x08,
RESCAN = 0x10,
SYNTH_KEY = 0x20,
} HandlerResult; } HandlerResult;
#define EVENT_SET_SELECTED -1
#define EVENT_HEADER_CLICK(x_) (-10000 + x_)
#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 struct PanelClass_ {
const ObjectClass super;
const Panel_EventHandler eventHandler;
} PanelClass;
#define As_Panel(this_) ((PanelClass*)((this_)->super.klass))
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
#define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_)
struct Panel_ { struct Panel_ {
Object super; Object super;
int x, y, w, h; int x, y, w, h;
WINDOW* window; WINDOW* window;
Vector* items; Vector* items;
int selected; int selected;
int scrollV, scrollH;
int scrollHAmount;
int oldSelected; int oldSelected;
int selectedLen;
void* eventHandlerState;
int scrollV;
short scrollH;
bool needsRedraw; bool needsRedraw;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header; RichString header;
int selectionColor; Panel_EventHandler eventHandler;
}; };
#define Panel_setDefaultBar(this_) do{ (this_)->currentBar = (this_)->defaultBar; }while(0)
}*/ }*/
#ifndef MIN #ifndef MIN
@ -83,21 +56,18 @@ struct Panel_ {
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#define KEY_CTRL(l) ((l)-'A'+1) #ifdef DEBUG
char* PANEL_CLASS = "Panel";
#else
#define PANEL_CLASS NULL
#endif
PanelClass Panel_class = {
.super = {
.extends = Class(Object),
.delete = Panel_delete
},
.eventHandler = Panel_selectByTyping,
};
Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar) { Panel* Panel_new(int x, int y, int w, int h, char* type, bool owner, Object_Compare compare) {
Panel* this; Panel* this;
this = xMalloc(sizeof(Panel)); this = malloc(sizeof(Panel));
Object_setClass(this, Class(Panel)); Panel_init(this, x, y, w, h, type, owner);
Panel_init(this, x, y, w, h, type, owner, fuBar); this->items->compare = compare;
return this; return this;
} }
@ -107,46 +77,46 @@ void Panel_delete(Object* cast) {
free(this); free(this);
} }
void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar) { void Panel_init(Panel* this, int x, int y, int w, int h, char* type, bool owner) {
Object* super = (Object*) this;
Object_setClass(this, PANEL_CLASS);
super->delete = Panel_delete;
this->x = x; this->x = x;
this->y = y; this->y = y;
this->w = w; this->w = w;
this->h = h; this->h = h;
this->eventHandlerState = NULL; this->eventHandler = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE); this->items = Vector_new(type, owner, DEFAULT_SIZE, ListItem_compare);
this->scrollV = 0; this->scrollV = 0;
this->scrollH = 0; this->scrollH = 0;
this->selected = 0; this->selected = 0;
this->oldSelected = 0; this->oldSelected = 0;
this->needsRedraw = true; this->needsRedraw = true;
RichString_beginAllocated(this->header); this->header.len = 0;
this->defaultBar = fuBar; if (String_eq(CRT_termType, "linux"))
this->currentBar = fuBar; this->scrollHAmount = 40;
this->selectionColor = CRT_colors[PANEL_SELECTION_FOCUS]; else
this->scrollHAmount = 5;
} }
void Panel_done(Panel* this) { void Panel_done(Panel* this) {
assert (this != NULL); assert (this != NULL);
free(this->eventHandlerState);
Vector_delete(this->items); Vector_delete(this->items);
FunctionBar_delete(this->defaultBar);
RichString_end(this->header);
} }
void Panel_setSelectionColor(Panel* this, int color) { inline void Panel_setRichHeader(Panel* this, RichString header) {
this->selectionColor = color;
}
RichString* Panel_getHeader(Panel* this) {
assert (this != NULL); assert (this != NULL);
this->header = header;
this->needsRedraw = true; this->needsRedraw = true;
return &(this->header);
} }
inline void Panel_setHeader(Panel* this, const char* header) { inline void Panel_setHeader(Panel* this, char* header) {
RichString_write(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header); Panel_setRichHeader(this, RichString_quickString(CRT_colors[PANEL_HEADER_FOCUS], header));
this->needsRedraw = true; }
void Panel_setEventHandler(Panel* this, Panel_EventHandler eh) {
this->eventHandler = eh;
} }
void Panel_move(Panel* this, int x, int y) { void Panel_move(Panel* this, int x, int y) {
@ -160,7 +130,7 @@ void Panel_move(Panel* this, int x, int y) {
void Panel_resize(Panel* this, int w, int h) { void Panel_resize(Panel* this, int w, int h) {
assert (this != NULL); assert (this != NULL);
if (RichString_sizeVal(this->header) > 0) if (this->header.len > 0)
h--; h--;
this->w = w; this->w = w;
this->h = h; this->h = h;
@ -215,10 +185,8 @@ Object* Panel_remove(Panel* this, int i) {
Object* Panel_getSelected(Panel* this) { Object* Panel_getSelected(Panel* this) {
assert (this != NULL); assert (this != NULL);
if (Vector_size(this->items) > 0)
return Vector_get(this->items, this->selected); return Vector_get(this->items, this->selected);
else
return NULL;
} }
void Panel_moveSelectedUp(Panel* this) { void Panel_moveSelectedUp(Panel* this) {
@ -243,7 +211,7 @@ int Panel_getSelectedIndex(Panel* this) {
return this->selected; return this->selected;
} }
int Panel_size(Panel* this) { int Panel_getSize(Panel* this) {
assert (this != NULL); assert (this != NULL);
return Vector_size(this->items); return Vector_size(this->items);
@ -252,258 +220,142 @@ int Panel_size(Panel* this) {
void Panel_setSelected(Panel* this, int selected) { void Panel_setSelected(Panel* this, int selected) {
assert (this != NULL); assert (this != NULL);
int size = Vector_size(this->items); selected = MAX(0, MIN(Vector_size(this->items) - 1, selected));
if (selected >= size) {
selected = size - 1;
}
if (selected < 0)
selected = 0;
this->selected = selected; this->selected = selected;
if (Panel_eventHandlerFn(this)) {
Panel_eventHandler(this, EVENT_SET_SELECTED);
}
}
void Panel_splice(Panel *this, Vector* from) {
assert (this != NULL);
assert (from != NULL);
Vector_splice(this->items, from);
this->needsRedraw = true;
} }
void Panel_draw(Panel* this, bool focus) { void Panel_draw(Panel* this, bool focus) {
assert (this != NULL); assert (this != NULL);
int size = Vector_size(this->items); int first, last;
int itemCount = Vector_size(this->items);
int scrollH = this->scrollH; int scrollH = this->scrollH;
int y = this->y; int y = this->y; int x = this->x;
int x = this->x; first = this->scrollV;
int h = this->h;
int headerLen = RichString_sizeVal(this->header); if (this->h > itemCount) {
if (headerLen > 0) { last = this->scrollV + itemCount;
move(y + last, x + 0);
} else {
last = MIN(itemCount, this->scrollV + this->h);
}
if (this->selected < first) {
first = this->selected;
this->scrollV = first;
this->needsRedraw = true;
}
if (this->selected >= last) {
last = MIN(itemCount, this->selected + 1);
first = MAX(0, last - this->h);
this->scrollV = first;
this->needsRedraw = true;
}
assert(first >= 0);
assert(last <= itemCount);
if (this->header.len > 0) {
int attr = focus int attr = focus
? CRT_colors[PANEL_HEADER_FOCUS] ? CRT_colors[PANEL_HEADER_FOCUS]
: CRT_colors[PANEL_HEADER_UNFOCUS]; : CRT_colors[PANEL_HEADER_UNFOCUS];
attrset(attr); attrset(attr);
mvhline(y, x, ' ', this->w); mvhline(y, x, ' ', this->w);
if (scrollH < headerLen) { if (scrollH < this->header.len) {
RichString_printoffnVal(this->header, y, x, scrollH, mvaddchnstr(y, x, this->header.chstr + scrollH,
MIN(headerLen - scrollH, this->w)); MIN(this->header.len - scrollH, this->w));
} }
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
y++; y++;
} }
// ensure scroll area is on screen int highlight = focus
if (this->scrollV < 0) { ? CRT_colors[PANEL_HIGHLIGHT_FOCUS]
this->scrollV = 0; : CRT_colors[PANEL_HIGHLIGHT_UNFOCUS];
this->needsRedraw = true;
} else if (this->scrollV >= size) {
this->scrollV = MAX(size - 1, 0);
this->needsRedraw = true;
}
// ensure selection is on screen
if (this->selected < this->scrollV) {
this->scrollV = this->selected;
this->needsRedraw = true;
} else if (this->selected >= this->scrollV + h) {
this->scrollV = this->selected - h + 1;
this->needsRedraw = true;
}
int first = this->scrollV;
int upTo = MIN(first + h, size);
int selectionColor = focus
? this->selectionColor
: CRT_colors[PANEL_SELECTION_UNFOCUS];
if (this->needsRedraw) { if (this->needsRedraw) {
int line = 0;
for(int i = first; line < h && i < upTo; i++) { for(int i = first, j = 0; j < this->h && i < last; i++, j++) {
Object* itemObj = Vector_get(this->items, i); Object* itemObj = Vector_get(this->items, i);
assert(itemObj); if(!itemObj) continue; RichString itemRef;
RichString_begin(item); RichString_initVal(itemRef);
Object_display(itemObj, &item); itemObj->display(itemObj, &itemRef);
int itemLen = RichString_sizeVal(item); int amt = MIN(itemRef.len - scrollH, this->w);
int amt = MIN(itemLen - scrollH, this->w); if (i == this->selected) {
bool selected = (i == this->selected); attrset(highlight);
if (selected) { RichString_setAttr(&itemRef, highlight);
attrset(selectionColor); mvhline(y + j, x+0, ' ', this->w);
RichString_setAttr(&item, selectionColor);
this->selectedLen = itemLen;
}
mvhline(y + line, x, ' ', this->w);
if (amt > 0) if (amt > 0)
RichString_printoffnVal(item, y + line, x, scrollH, amt); mvaddchnstr(y+j, x+0, itemRef.chstr + scrollH, amt);
if (selected)
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
RichString_end(item); } else {
line++; mvhline(y+j, x+0, ' ', this->w);
if (amt > 0)
mvaddchnstr(y+j, x+0, itemRef.chstr + scrollH, amt);
} }
while (line < h) {
mvhline(y + line, x, ' ', this->w);
line++;
} }
for (int i = y + (last - first); i < y + this->h; i++)
mvhline(i, x+0, ' ', this->w);
this->needsRedraw = false; this->needsRedraw = false;
} else { } else {
Object* oldObj = Vector_get(this->items, this->oldSelected); Object* oldObj = Vector_get(this->items, this->oldSelected);
assert(oldObj); RichString oldRef;
RichString_begin(old); RichString_initVal(oldRef);
Object_display(oldObj, &old); oldObj->display(oldObj, &oldRef);
int oldLen = RichString_sizeVal(old);
Object* newObj = Vector_get(this->items, this->selected); Object* newObj = Vector_get(this->items, this->selected);
RichString_begin(new); RichString newRef;
Object_display(newObj, &new); RichString_initVal(newRef);
int newLen = RichString_sizeVal(new); newObj->display(newObj, &newRef);
this->selectedLen = newLen; mvhline(y+ this->oldSelected - this->scrollV, x+0, ' ', this->w);
mvhline(y+ this->oldSelected - first, x+0, ' ', this->w); if (scrollH < oldRef.len)
if (scrollH < oldLen) mvaddchnstr(y+ this->oldSelected - this->scrollV, x+0, oldRef.chstr + this->scrollH, MIN(oldRef.len - scrollH, this->w));
RichString_printoffnVal(old, y+this->oldSelected - first, x, attrset(highlight);
scrollH, MIN(oldLen - scrollH, this->w)); mvhline(y+this->selected - this->scrollV, x+0, ' ', this->w);
attrset(selectionColor); RichString_setAttr(&newRef, highlight);
mvhline(y+this->selected - first, x+0, ' ', this->w); if (scrollH < newRef.len)
RichString_setAttr(&new, selectionColor); mvaddchnstr(y+this->selected - this->scrollV, x+0, newRef.chstr + this->scrollH, MIN(newRef.len - scrollH, this->w));
if (scrollH < newLen)
RichString_printoffnVal(new, y+this->selected - first, x,
scrollH, MIN(newLen - scrollH, this->w));
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
RichString_end(new);
RichString_end(old);
} }
this->oldSelected = this->selected; this->oldSelected = this->selected;
move(0, 0); move(0, 0);
} }
bool Panel_onKey(Panel* this, int key) { void Panel_onKey(Panel* this, int key) {
assert (this != NULL); assert (this != NULL);
int size = Vector_size(this->items);
switch (key) { switch (key) {
case KEY_DOWN: case KEY_DOWN:
case KEY_CTRL('N'): if (this->selected + 1 < Vector_size(this->items))
this->selected++; this->selected++;
break; break;
case KEY_UP: case KEY_UP:
case KEY_CTRL('P'): if (this->selected > 0)
this->selected--; this->selected--;
break; break;
#ifdef KEY_C_DOWN
case KEY_C_DOWN:
this->selected++;
break;
#endif
#ifdef KEY_C_UP
case KEY_C_UP:
this->selected--;
break;
#endif
case KEY_LEFT: case KEY_LEFT:
case KEY_CTRL('B'):
if (this->scrollH > 0) { if (this->scrollH > 0) {
this->scrollH -= MAX(CRT_scrollHAmount, 0); this->scrollH -= this->scrollHAmount;
this->needsRedraw = true; this->needsRedraw = true;
} }
break; break;
case KEY_RIGHT: case KEY_RIGHT:
case KEY_CTRL('F'): this->scrollH += this->scrollHAmount;
this->scrollH += CRT_scrollHAmount;
this->needsRedraw = true; this->needsRedraw = true;
break; break;
case KEY_PPAGE: case KEY_PPAGE:
this->selected -= (this->h - 1); this->selected -= this->h;
this->scrollV = MAX(0, this->scrollV - this->h + 1); if (this->selected < 0)
this->needsRedraw = true; this->selected = 0;
break; break;
case KEY_NPAGE: case KEY_NPAGE:
this->selected += (this->h - 1); this->selected += this->h;
this->scrollV = MAX(0, MIN(Vector_size(this->items) - this->h, int size = Vector_size(this->items);
this->scrollV + this->h - 1)); if (this->selected >= size)
this->needsRedraw = true; this->selected = size - 1;
break; break;
case KEY_WHEELUP:
this->selected -= CRT_scrollWheelVAmount;
this->scrollV -= CRT_scrollWheelVAmount;
this->needsRedraw = true;
break;
case KEY_WHEELDOWN:
{
this->selected += CRT_scrollWheelVAmount;
this->scrollV += CRT_scrollWheelVAmount;
if (this->scrollV > Vector_size(this->items) - this->h) {
this->scrollV = Vector_size(this->items) - this->h;
}
this->needsRedraw = true;
break;
}
case KEY_HOME: case KEY_HOME:
this->selected = 0; this->selected = 0;
break; break;
case KEY_END: case KEY_END:
this->selected = size - 1; this->selected = Vector_size(this->items) - 1;
break; break;
case KEY_CTRL('A'):
case '^':
this->scrollH = 0;
this->needsRedraw = true;
break;
case KEY_CTRL('E'):
case '$':
this->scrollH = MAX(this->selectedLen - this->w, 0);
this->needsRedraw = true;
break;
default:
return false;
} }
// ensure selection within bounds
if (this->selected < 0 || size == 0) {
this->selected = 0;
this->needsRedraw = true;
} else if (this->selected >= size) {
this->selected = size - 1;
this->needsRedraw = true;
}
return true;
}
HandlerResult Panel_selectByTyping(Panel* this, int ch) {
int size = Panel_size(this);
if (!this->eventHandlerState)
this->eventHandlerState = xCalloc(100, sizeof(char));
char* buffer = this->eventHandlerState;
if (ch > 0 && ch < 255 && isalnum(ch)) {
int len = strlen(buffer);
if (len < 99) {
buffer[len] = ch;
buffer[len+1] = '\0';
}
for (int try = 0; try < 2; try++) {
len = strlen(buffer);
for (int i = 0; i < size; i++) {
char* cur = ((ListItem*) Panel_get(this, i))->value;
while (*cur == ' ') cur++;
if (strncasecmp(cur, buffer, len) == 0) {
Panel_setSelected(this, i);
return HANDLED;
}
}
// if current word did not match,
// retry considering the character the start of a new word.
buffer[0] = ch;
buffer[1] = '\0';
}
return HANDLED;
} else if (ch != ERR) {
buffer[0] = '\0';
}
if (ch == 13) {
return BREAK_LOOP;
}
return IGNORED;
} }

111
Panel.h
View File

@ -4,65 +4,51 @@
#define HEADER_Panel #define HEADER_Panel
/* /*
htop - Panel.h htop - Panel.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
//#link curses
#include "Object.h" #include "Object.h"
#include "Vector.h" #include "Vector.h"
#include "FunctionBar.h" #include "CRT.h"
#include "RichString.h"
#include "ListItem.h"
#include <math.h>
#include <stdbool.h>
#include "debug.h"
#include <assert.h>
#include <curses.h>
//#link curses
typedef struct Panel_ Panel; typedef struct Panel_ Panel;
typedef enum HandlerResult_ { typedef enum HandlerResult_ {
HANDLED = 0x01, HANDLED,
IGNORED = 0x02, IGNORED,
BREAK_LOOP = 0x04, BREAK_LOOP
REDRAW = 0x08,
RESCAN = 0x10,
SYNTH_KEY = 0x20,
} HandlerResult; } HandlerResult;
#define EVENT_SET_SELECTED -1
#define EVENT_HEADER_CLICK(x_) (-10000 + x_)
#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 struct PanelClass_ {
const ObjectClass super;
const Panel_EventHandler eventHandler;
} PanelClass;
#define As_Panel(this_) ((PanelClass*)((this_)->super.klass))
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
#define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_)
struct Panel_ { struct Panel_ {
Object super; Object super;
int x, y, w, h; int x, y, w, h;
WINDOW* window; WINDOW* window;
Vector* items; Vector* items;
int selected; int selected;
int scrollV, scrollH;
int scrollHAmount;
int oldSelected; int oldSelected;
int selectedLen;
void* eventHandlerState;
int scrollV;
short scrollH;
bool needsRedraw; bool needsRedraw;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header; RichString header;
int selectionColor; Panel_EventHandler eventHandler;
}; };
#define Panel_setDefaultBar(this_) do{ (this_)->currentBar = (this_)->defaultBar; }while(0)
#ifndef MIN #ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b))
@ -71,58 +57,57 @@ struct Panel_ {
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#define KEY_CTRL(l) ((l)-'A'+1) #ifdef DEBUG
extern char* PANEL_CLASS;
#else
#define PANEL_CLASS NULL
#endif
extern PanelClass Panel_class;
extern Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar); Panel* Panel_new(int x, int y, int w, int h, char* type, bool owner, Object_Compare compare);
extern void Panel_delete(Object* cast); void Panel_delete(Object* cast);
extern void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar); void Panel_init(Panel* this, int x, int y, int w, int h, char* type, bool owner);
extern void Panel_done(Panel* this); void Panel_done(Panel* this);
extern void Panel_setSelectionColor(Panel* this, int color); inline void Panel_setRichHeader(Panel* this, RichString header);
extern RichString* Panel_getHeader(Panel* this); inline void Panel_setHeader(Panel* this, char* header);
extern void Panel_setHeader(Panel* this, const char* header); void Panel_setEventHandler(Panel* this, Panel_EventHandler eh);
extern void Panel_move(Panel* this, int x, int y); void Panel_move(Panel* this, int x, int y);
extern void Panel_resize(Panel* this, int w, int h); void Panel_resize(Panel* this, int w, int h);
extern void Panel_prune(Panel* this); void Panel_prune(Panel* this);
extern void Panel_add(Panel* this, Object* o); void Panel_add(Panel* this, Object* o);
extern void Panel_insert(Panel* this, int i, Object* o); void Panel_insert(Panel* this, int i, Object* o);
extern void Panel_set(Panel* this, int i, Object* o); void Panel_set(Panel* this, int i, Object* o);
extern Object* Panel_get(Panel* this, int i); Object* Panel_get(Panel* this, int i);
extern Object* Panel_remove(Panel* this, int i); Object* Panel_remove(Panel* this, int i);
extern Object* Panel_getSelected(Panel* this); Object* Panel_getSelected(Panel* this);
extern void Panel_moveSelectedUp(Panel* this); void Panel_moveSelectedUp(Panel* this);
extern void Panel_moveSelectedDown(Panel* this); void Panel_moveSelectedDown(Panel* this);
extern int Panel_getSelectedIndex(Panel* this); int Panel_getSelectedIndex(Panel* this);
extern int Panel_size(Panel* this); int Panel_getSize(Panel* this);
extern void Panel_setSelected(Panel* this, int selected); void Panel_setSelected(Panel* this, int selected);
extern void Panel_draw(Panel* this, bool focus); void Panel_draw(Panel* this, bool focus);
extern void Panel_splice(Panel *this, Vector* from); void Panel_onKey(Panel* this, int key);
extern bool Panel_onKey(Panel* this, int key);
extern HandlerResult Panel_selectByTyping(Panel* this, int ch);
#endif #endif

858
Process.c
View File

@ -1,140 +1,81 @@
/* /*
htop - Process.c htop - Process.c
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Process.h" #define _GNU_SOURCE
#include "Settings.h" #include "ProcessList.h"
#include "Object.h"
#include "config.h"
#include "CRT.h" #include "CRT.h"
#include "StringUtils.h" #include "String.h"
#include "RichString.h" #include "Process.h"
#include "Platform.h"
#include "debug.h"
#include <stdio.h> #include <stdio.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h>
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <pwd.h> #include <pwd.h>
#include <time.h>
#include <assert.h>
#include <math.h>
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS)
#include <sys/sysmacros.h>
#endif
#ifdef __ANDROID__ // This works only with glibc 2.1+. On earlier versions
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif
// On Linux, this works only with glibc 2.1+. On earlier versions
// the behavior is similar to have a hardcoded page size. // the behavior is similar to have a hardcoded page size.
#ifndef PAGE_SIZE #define PAGE_SIZE ( sysconf(_SC_PAGESIZE) / 1024 )
#define PAGE_SIZE ( sysconf(_SC_PAGESIZE) )
#endif #define PROCESS_COMM_LEN 300
#define PAGE_SIZE_KB ( PAGE_SIZE / ONE_K )
/*{ /*{
#include "Object.h"
#include <sys/types.h> typedef enum ProcessField_ {
PID = 1, COMM, STATE, PPID, PGRP, SESSION, TTY_NR, TPGID, FLAGS, MINFLT, CMINFLT, MAJFLT, CMAJFLT, UTIME,
#define PROCESS_FLAG_IO 0x0001 STIME, CUTIME, CSTIME, PRIORITY, NICE, ITREALVALUE, STARTTIME, VSIZE, RSS, RLIM, STARTCODE, ENDCODE,
STARTSTACK, KSTKESP, KSTKEIP, SIGNAL, BLOCKED, SSIGIGNORE, SIGCATCH, WCHAN, NSWAP, CNSWAP, EXIT_SIGNAL,
typedef enum ProcessFields { PROCESSOR, M_SIZE, M_RESIDENT, M_SHARE, M_TRS, M_DRS, M_LRS, M_DT, ST_UID, PERCENT_CPU, PERCENT_MEM,
NULL_PROCESSFIELD = 0, USER, TIME, LAST_PROCESSFIELD
PID = 1,
COMM = 2,
STATE = 3,
PPID = 4,
PGRP = 5,
SESSION = 6,
TTY_NR = 7,
TPGID = 8,
MINFLT = 10,
MAJFLT = 12,
PRIORITY = 18,
NICE = 19,
STARTTIME = 21,
PROCESSOR = 38,
M_SIZE = 39,
M_RESIDENT = 40,
ST_UID = 46,
PERCENT_CPU = 47,
PERCENT_MEM = 48,
USER = 49,
TIME = 50,
NLWP = 51,
TGID = 52,
} ProcessField; } ProcessField;
typedef struct ProcessPidColumn_ { struct ProcessList_;
int id;
const char* label;
} ProcessPidColumn;
typedef struct Process_ { typedef struct Process_ {
Object super; Object super;
struct Settings_* settings; struct ProcessList_ *pl;
unsigned long long int time;
pid_t pid;
pid_t ppid;
pid_t tgid;
char* comm;
int commLen;
int indent;
int basenameOffset;
bool updated; bool updated;
int pid;
char* comm;
int indent;
char state; char state;
bool tag; bool tag;
bool showChildren; int ppid;
bool show; int pgrp;
unsigned int pgrp; int session;
unsigned int session; int tty_nr;
unsigned int tty_nr;
int tpgid; int tpgid;
uid_t st_uid;
unsigned long int flags; unsigned long int flags;
int processor; #ifdef DEBUG
unsigned long int minflt;
float percent_cpu; unsigned long int cminflt;
float percent_mem; unsigned long int majflt;
char* user; unsigned long int cmajflt;
#endif
unsigned long int utime;
unsigned long int stime;
long int cutime;
long int cstime;
long int priority; long int priority;
long int nice; long int nice;
long int nlwp;
char starttime_show[8];
time_t starttime_ctime;
long m_size;
long m_resident;
int exit_signal;
unsigned long int minflt;
unsigned long int majflt;
#ifdef DEBUG #ifdef DEBUG
long int itrealvalue; long int itrealvalue;
unsigned long int starttime;
unsigned long int vsize; unsigned long int vsize;
long int rss; long int rss;
unsigned long int rlim; unsigned long int rlim;
@ -151,497 +92,348 @@ typedef struct Process_ {
unsigned long int nswap; unsigned long int nswap;
unsigned long int cnswap; unsigned long int cnswap;
#endif #endif
int exit_signal;
int processor;
int m_size;
int m_resident;
int m_share;
int m_trs;
int m_drs;
int m_lrs;
int m_dt;
uid_t st_uid;
float percent_cpu;
float percent_mem;
char* user;
} Process; } Process;
typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
} ProcessFieldData;
// Implemented in platform-specific code:
void Process_writeField(Process* this, RichString* str, ProcessField field);
long Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast);
bool Process_isThread(Process* this);
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern char Process_pidFormat[20];
typedef Process*(*Process_New)(struct Settings_*);
typedef void (*Process_WriteField)(Process*, RichString*, ProcessField);
typedef struct ProcessClass_ {
const ObjectClass super;
const Process_WriteField writeField;
} ProcessClass;
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
}*/ }*/
#ifdef DEBUG
char* PROCESS_CLASS = "Process";
#else
#define PROCESS_CLASS NULL
#endif
char *Process_fieldNames[] = {
"", "PID", "Command", "STATE", "PPID", "PGRP", "SESSION", "TTY_NR", "TPGID", "FLAGS", "MINFLT", "CMINFLT", "MAJFLT", "CMAJFLT", "UTIME", "STIME", "CUTIME", "CSTIME", "PRIORITY", "NICE", "ITREALVALUE", "STARTTIME", "VSIZE", "RSS", "RLIM", "STARTCODE", "ENDCODE", "STARTSTACK", "KSTKESP", "KSTKEIP", "SIGNAL", "BLOCKED", "SIGIGNORE", "SIGCATCH", "WCHAN", "NSWAP", "CNSWAP", "EXIT_SIGNAL", "PROCESSOR", "M_SIZE", "M_RESIDENT", "M_SHARE", "M_TRS", "M_DRS", "M_LRS", "M_DT", "ST_UID", "PERCENT_CPU", "PERCENT_MEM", "USER", "TIME", "*** report bug! ***"
};
static int Process_getuid = -1; static int Process_getuid = -1;
#define ONE_K 1024L Process* Process_new(struct ProcessList_ *pl) {
#define ONE_M (ONE_K * ONE_K) Process* this = malloc(sizeof(Process));
#define ONE_G (ONE_M * ONE_K) Object_setClass(this, PROCESS_CLASS);
#define ONE_T ((long long)ONE_G * ONE_K) ((Object*)this)->display = Process_display;
((Object*)this)->delete = Process_delete;
#define ONE_DECIMAL_K 1000L this->pl = pl;
#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K) this->tag = false;
#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K) this->updated = false;
#define ONE_DECIMAL_T ((long long)ONE_DECIMAL_G * ONE_DECIMAL_K) this->utime = 0;
this->stime = 0;
char Process_pidFormat[20] = "%7d "; this->comm = NULL;
if (Process_getuid == -1) Process_getuid = getuid();
static char Process_titleBuffer[20][20]; return this;
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);
} }
void Process_humanNumber(RichString* str, unsigned long number, bool coloring) { Process* Process_clone(Process* this) {
char buffer[11]; Process* clone = malloc(sizeof(Process));
int len; memcpy(clone, this, sizeof(Process));
return clone;
int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
int processColor = CRT_colors[PROCESS];
if (!coloring) {
largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS];
}
if(number >= (10 * ONE_DECIMAL_M)) {
#ifdef __LP64__
if(number >= (100 * ONE_DECIMAL_G)) {
len = snprintf(buffer, 10, "%4luT ", number / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len);
return;
} else if (number >= (1000 * ONE_DECIMAL_M)) {
len = snprintf(buffer, 10, "%4.1lfT ", (double)number / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len);
return;
}
#endif
if(number >= (100 * ONE_DECIMAL_M)) {
len = snprintf(buffer, 10, "%4luG ", number / ONE_M);
RichString_appendn(str, largeNumberColor, buffer, len);
return;
}
len = snprintf(buffer, 10, "%4.1lfG ", (double)number / ONE_M);
RichString_appendn(str, largeNumberColor, buffer, len);
return;
} else if (number >= 100000) {
len = snprintf(buffer, 10, "%4luM ", number / ONE_K);
RichString_appendn(str, processMegabytesColor, buffer, len);
return;
} else if (number >= 1000) {
len = snprintf(buffer, 10, "%2lu", number/1000);
RichString_appendn(str, processMegabytesColor, buffer, len);
number %= 1000;
len = snprintf(buffer, 10, "%03lu ", number);
RichString_appendn(str, processColor, buffer, len);
return;
}
len = snprintf(buffer, 10, "%5lu ", number);
RichString_appendn(str, processColor, buffer, len);
} }
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring) { void Process_delete(Object* cast) {
char buffer[14]; Process* this = (Process*) cast;
if (this->comm) free(this->comm);
int largeNumberColor = CRT_colors[LARGE_NUMBER]; assert (this != NULL);
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; free(this);
int processColor = CRT_colors[PROCESS];
int processShadowColor = CRT_colors[PROCESS_SHADOW];
if (!coloring) {
largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS];
processShadowColor = CRT_colors[PROCESS];
}
if ((long long) number == -1LL) {
int len = snprintf(buffer, 13, " no perm ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (number >= 100000LL * ONE_DECIMAL_T) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_G);
RichString_appendn(str, largeNumberColor, buffer, 12);
} else if (number >= 100LL * ONE_DECIMAL_T) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_M);
RichString_appendn(str, largeNumberColor, buffer, 8);
RichString_appendn(str, processMegabytesColor, buffer+8, 4);
} else if (number >= 10LL * ONE_DECIMAL_G) {
xSnprintf(buffer, 13, "%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);
} else {
xSnprintf(buffer, 13, "%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);
}
}
void Process_printTime(RichString* str, unsigned long long totalHundredths) {
unsigned long long totalSeconds = totalHundredths / 100;
unsigned long long hours = totalSeconds / 3600;
int minutes = (totalSeconds / 60) % 60;
int seconds = totalSeconds % 60;
int hundredths = totalHundredths - (totalSeconds * 100);
char buffer[11];
if (hours >= 100) {
xSnprintf(buffer, 10, "%7lluh ", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
} else {
if (hours) {
xSnprintf(buffer, 10, "%2lluh", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
xSnprintf(buffer, 10, "%02d:%02d ", minutes, seconds);
} else {
xSnprintf(buffer, 10, "%2d:%02d.%02d ", minutes, seconds, hundredths);
}
RichString_append(str, CRT_colors[DEFAULT_COLOR], buffer);
}
}
static inline void Process_writeCommand(Process* this, int attr, int baseattr, RichString* str) {
int start = RichString_size(str), finish = 0;
char* comm = this->comm;
if (this->settings->highlightBaseName || !this->settings->showProgramPath) {
int i, basename = 0;
for (i = 0; i < this->basenameOffset; i++) {
if (comm[i] == '/') {
basename = i + 1;
} else if (comm[i] == ':') {
finish = i + 1;
break;
}
}
if (!finish) {
if (this->settings->showProgramPath)
start += basename;
else
comm += basename;
finish = this->basenameOffset - basename;
}
finish += start - 1;
}
RichString_append(str, attr, comm);
if (this->settings->highlightBaseName)
RichString_setAttrn(str, baseattr, start, finish);
}
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring) {
int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
int processColor = CRT_colors[PROCESS];
if (!coloring) {
largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS];
}
if (rate == -1) {
int len = snprintf(buffer, n, " no perm ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (rate < ONE_K) {
int len = snprintf(buffer, n, "%7.2f B/s ", rate);
RichString_appendn(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);
} else if (rate < ONE_G) {
int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M);
RichString_appendn(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);
} else {
int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T);
RichString_appendn(str, largeNumberColor, buffer, len);
}
}
void Process_writeField(Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int baseattr = CRT_colors[PROCESS_BASENAME];
int n = sizeof(buffer) - 1;
bool coloring = this->settings->highlightMegabytes;
switch (field) {
case PERCENT_CPU: {
if (this->percent_cpu > 999.9) {
xSnprintf(buffer, n, "%4u ", (unsigned int)this->percent_cpu);
} else if (this->percent_cpu > 99.9) {
xSnprintf(buffer, n, "%3u. ", (unsigned int)this->percent_cpu);
} else {
xSnprintf(buffer, n, "%4.1f ", this->percent_cpu);
}
break;
}
case PERCENT_MEM: {
if (this->percent_mem > 99.9) {
xSnprintf(buffer, n, "100. ");
} else {
xSnprintf(buffer, n, "%4.1f ", this->percent_mem);
}
break;
}
case COMM: {
if (this->settings->highlightThreads && Process_isThread(this)) {
attr = CRT_colors[PROCESS_THREAD];
baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
}
if (!this->settings->treeView || this->indent == 0) {
Process_writeCommand(this, attr, baseattr, str);
return;
} else {
char* buf = buffer;
int maxIndent = 0;
bool lastItem = (this->indent < 0);
int indent = (this->indent < 0 ? -this->indent : this->indent);
for (int i = 0; i < 32; i++)
if (indent & (1U << i))
maxIndent = i+1;
for (int i = 0; i < maxIndent - 1; i++) {
int written, ret;
if (indent & (1 << i))
ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
else
ret = snprintf(buf, n, " ");
if (ret < 0 || ret >= n) {
written = n;
} else {
written = ret;
}
buf += written;
n -= written;
}
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : 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);
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 * PAGE_SIZE_KB, coloring); return;
case M_SIZE: Process_humanNumber(str, this->m_size * PAGE_SIZE_KB, coloring); return;
case NICE: {
xSnprintf(buffer, n, "%3ld ", this->nice);
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
: this->nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY]
: attr;
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 PRIORITY: {
if(this->priority <= -100)
xSnprintf(buffer, n, " RT ");
else
xSnprintf(buffer, n, "%3ld ", this->priority);
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 STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
case STATE: {
xSnprintf(buffer, n, "%c ", this->state);
switch(this->state) {
case 'R':
attr = CRT_colors[PROCESS_R_STATE];
break;
case 'D':
attr = CRT_colors[PROCESS_D_STATE];
break;
}
break;
}
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 TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break;
case USER: {
if (Process_getuid != (int) this->st_uid)
attr = CRT_colors[PROCESS_SHADOW];
if (this->user) {
xSnprintf(buffer, n, "%-9s ", this->user);
} else {
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);
} }
void Process_display(Object* cast, RichString* out) { void Process_display(Object* cast, RichString* out) {
Process* this = (Process*) cast; Process* this = (Process*) cast;
ProcessField* fields = this->settings->fields; ProcessField* fields = this->pl->fields;
RichString_prune(out); RichString_init(out);
for (int i = 0; fields[i]; i++) for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]); Process_writeField(this, out, fields[i]);
if (this->settings->shadowOtherUsers && (int)this->st_uid != Process_getuid) if (this->pl->shadowOtherUsers && this->st_uid != Process_getuid)
RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]); RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]);
if (this->tag == true) if (this->tag == true)
RichString_setAttr(out, CRT_colors[PROCESS_TAG]); RichString_setAttr(out, CRT_colors[PROCESS_TAG]);
assert(out->chlen > 0); assert(out->len > 0);
}
void Process_done(Process* this) {
assert (this != NULL);
free(this->comm);
}
ProcessClass Process_class = {
.super = {
.extends = Class(Object),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = Process_writeField,
};
void Process_init(Process* this, struct Settings_* settings) {
this->settings = settings;
this->tag = false;
this->showChildren = true;
this->show = true;
this->updated = false;
this->basenameOffset = -1;
if (Process_getuid == -1) Process_getuid = getuid();
} }
void Process_toggleTag(Process* this) { void Process_toggleTag(Process* this) {
this->tag = this->tag == true ? false : true; this->tag = this->tag == true ? false : true;
} }
bool Process_setPriority(Process* this, int priority) { void Process_setPriority(Process* this, int priority) {
CRT_dropPrivileges();
int old_prio = getpriority(PRIO_PROCESS, this->pid); int old_prio = getpriority(PRIO_PROCESS, this->pid);
int err = setpriority(PRIO_PROCESS, this->pid, priority); int err = setpriority(PRIO_PROCESS, this->pid, priority);
CRT_restorePrivileges();
if (err == 0 && old_prio != getpriority(PRIO_PROCESS, this->pid)) { if (err == 0 && old_prio != getpriority(PRIO_PROCESS, this->pid)) {
this->nice = priority; this->nice = priority;
} }
return (err == 0);
} }
bool Process_changePriorityBy(Process* this, Arg delta) { void Process_sendSignal(Process* this, int signal) {
return Process_setPriority(this, this->nice + delta.i); kill(this->pid, signal);
} }
bool Process_sendSignal(Process* this, Arg sgn) { #define ONE_K 1024
CRT_dropPrivileges(); #define ONE_M (ONE_K * ONE_K)
bool ok = (kill(this->pid, sgn.i) == 0); #define ONE_G (ONE_M * ONE_K)
CRT_restorePrivileges();
return ok; static void Process_printLargeNumber(Process* this, RichString *str, unsigned int number) {
char buffer[11];
int len;
if(number >= (1000 * ONE_M)) {
len = snprintf(buffer, 10, "%4.2fG ", (float)number / ONE_M);
RichString_appendn(str, CRT_colors[LARGE_NUMBER], buffer, len);
} else if(number >= (100000)) {
len = snprintf(buffer, 10, "%4dM ", number / ONE_K);
int attr = this->pl->highlightMegabytes
? CRT_colors[PROCESS_MEGABYTES]
: CRT_colors[PROCESS];
RichString_appendn(str, attr, buffer, len);
} else if (this->pl->highlightMegabytes && number >= 1000) {
len = snprintf(buffer, 10, "%2d", number/1000);
RichString_appendn(str, CRT_colors[PROCESS_MEGABYTES], buffer, len);
number %= 1000;
len = snprintf(buffer, 10, "%03d ", number);
RichString_appendn(str, CRT_colors[PROCESS], buffer, len);
} else {
len = snprintf(buffer, 10, "%5d ", number);
RichString_appendn(str, CRT_colors[PROCESS], buffer, len);
}
} }
long Process_pidCompare(const void* v1, const void* v2) { static double jiffy = 0.0;
static void Process_printTime(RichString* str, unsigned long t) {
if(jiffy == 0.0) jiffy = sysconf(_SC_CLK_TCK);
double jiffytime = 1.0 / jiffy;
double realTime = t * jiffytime;
int iRealTime = (int) realTime;
int hours = iRealTime / 3600;
int minutes = (iRealTime / 60) % 60;
int seconds = iRealTime % 60;
int hundredths = (realTime - iRealTime) * 100;
char buffer[11];
if (hours) {
snprintf(buffer, 10, "%2dh", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
snprintf(buffer, 10, "%02d:%02d ", minutes, seconds);
} else {
snprintf(buffer, 10, "%2d:%02d.%02d ", minutes, seconds, hundredths);
}
RichString_append(str, CRT_colors[DEFAULT_COLOR], buffer);
}
static inline void Process_writeCommand(Process* this, int attr, RichString* str) {
if (this->pl->highlightBaseName) {
char* firstSpace = strchr(this->comm, ' ');
if (firstSpace) {
char* slash = firstSpace;
while (slash > this->comm && *slash != '/')
slash--;
if (slash > this->comm) {
slash++;
RichString_appendn(str, attr, this->comm, slash - this->comm);
}
RichString_appendn(str, CRT_colors[PROCESS_BASENAME], slash, firstSpace - slash);
RichString_append(str, attr, firstSpace);
} else {
RichString_append(str, CRT_colors[PROCESS_BASENAME], this->comm);
}
} else {
RichString_append(str, attr, this->comm);
}
}
void Process_writeField(Process* this, RichString* str, ProcessField field) {
char buffer[PROCESS_COMM_LEN];
int attr = CRT_colors[DEFAULT_COLOR];
int n = PROCESS_COMM_LEN;
switch (field) {
case PID: snprintf(buffer, n, "%5d ", this->pid); break;
case PPID: snprintf(buffer, n, "%5d ", this->ppid); break;
case PGRP: snprintf(buffer, n, "%5d ", this->pgrp); break;
case SESSION: snprintf(buffer, n, "%5d ", this->session); break;
case TTY_NR: snprintf(buffer, n, "%5d ", this->tty_nr); break;
case TPGID: snprintf(buffer, n, "%5d ", this->tpgid); break;
case PROCESSOR: snprintf(buffer, n, "%3d ", this->processor+1); break;
case COMM: {
if (!this->pl->treeView || this->indent == 0) {
Process_writeCommand(this, attr, str);
return;
} else {
char* buf = buffer;
int maxIndent = 0;
for (int i = 0; i < 32; i++)
if (this->indent & (1 << i))
maxIndent = i+1;
for (int i = 0; i < maxIndent - 1; i++) {
if (this->indent & (1 << i))
snprintf(buf, n, " | ");
else
snprintf(buf, n, " ");
buf += 4;
n -= 4;
}
if (this->pl->direction == 1)
snprintf(buf, n, " `- ");
else
snprintf(buf, n, " ,- ");
RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
Process_writeCommand(this, attr, str);
return;
}
}
case STATE: {
snprintf(buffer, n, "%c ", this->state);
attr = this->state == 'R'
? CRT_colors[PROCESS_R_STATE]
: attr;
break;
}
case PRIORITY: {
if(this->priority == -100)
snprintf(buffer, n, " RT ");
else
snprintf(buffer, n, "%3ld ", this->priority);
break;
}
case NICE: {
snprintf(buffer, n, "%3ld ", this->nice);
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
: this->nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY]
: attr;
break;
}
case M_SIZE: Process_printLargeNumber(this, str, this->m_size * PAGE_SIZE); return;
case M_RESIDENT: Process_printLargeNumber(this, str, this->m_resident * PAGE_SIZE); return;
case M_SHARE: Process_printLargeNumber(this, str, this->m_share * PAGE_SIZE); return;
case ST_UID: snprintf(buffer, n, "%4d ", this->st_uid); break;
case USER: {
if (Process_getuid != this->st_uid)
attr = CRT_colors[PROCESS_SHADOW];
if (this->user) {
snprintf(buffer, n, "%-8s ", this->user);
} else {
snprintf(buffer, n, "%-8d ", this->st_uid);
}
if (buffer[8] != '\0') {
buffer[8] = ' ';
buffer[9] = '\0';
}
break;
}
case UTIME: Process_printTime(str, this->utime); return;
case STIME: Process_printTime(str, this->stime); return;
case CUTIME: Process_printTime(str, this->cutime); return;
case CSTIME: Process_printTime(str, this->cstime); return;
case TIME: Process_printTime(str, this->utime + this->stime); return;
case PERCENT_CPU: {
if (this->percent_cpu > 99.9) {
snprintf(buffer, n, "100. ");
} else {
snprintf(buffer, n, "%4.1f ", this->percent_cpu);
}
break;
}
case PERCENT_MEM: {
if (this->percent_mem > 99.9) {
snprintf(buffer, n, "100. ");
} else {
snprintf(buffer, n, "%4.1f ", this->percent_mem);
}
break;
}
default:
snprintf(buffer, n, "- ");
}
RichString_append(str, attr, buffer);
return;
}
int Process_pidCompare(const void* v1, const void* v2) {
Process* p1 = (Process*)v1; Process* p1 = (Process*)v1;
Process* p2 = (Process*)v2; Process* p2 = (Process*)v2;
return (p1->pid - p2->pid); return (p1->pid - p2->pid);
} }
long Process_compare(const void* v1, const void* v2) { int Process_compare(const void* v1, const void* v2) {
Process *p1, *p2; Process *p1, *p2;
Settings *settings = ((Process*)v1)->settings; ProcessList *pl = ((Process*)v1)->pl;
if (settings->direction == 1) { if (pl->direction == 1) {
p1 = (Process*)v1; p1 = (Process*)v1;
p2 = (Process*)v2; p2 = (Process*)v2;
} else { } else {
p2 = (Process*)v1; p2 = (Process*)v1;
p1 = (Process*)v2; p1 = (Process*)v2;
} }
switch (settings->sortKey) { switch (pl->sortKey) {
case PERCENT_CPU:
return (p2->percent_cpu > p1->percent_cpu ? 1 : -1);
case PERCENT_MEM:
return (p2->m_resident - p1->m_resident);
case COMM:
return strcmp(p1->comm, p2->comm);
case MAJFLT:
return (p2->majflt - p1->majflt);
case MINFLT:
return (p2->minflt - p1->minflt);
case M_RESIDENT:
return (p2->m_resident - p1->m_resident);
case M_SIZE:
return (p2->m_size - p1->m_size);
case NICE:
return (p1->nice - p2->nice);
case NLWP:
return (p1->nlwp - p2->nlwp);
case PGRP:
return (p1->pgrp - p2->pgrp);
case PID: case PID:
return (p1->pid - p2->pid); return (p1->pid - p2->pid);
case PPID: case PPID:
return (p1->ppid - p2->ppid); return (p1->ppid - p2->ppid);
case USER:
return strcmp(p1->user, p2->user);
case PRIORITY: case PRIORITY:
return (p1->priority - p2->priority); return (p1->priority - p2->priority);
case PROCESSOR:
return (p1->processor - p2->processor);
case SESSION:
return (p1->session - p2->session);
case STARTTIME: {
if (p1->starttime_ctime == p2->starttime_ctime)
return (p1->pid - p2->pid);
else
return (p1->starttime_ctime - p2->starttime_ctime);
}
case STATE: case STATE:
return (Process_sortState(p1->state) - Process_sortState(p2->state)); return (p1->state - p2->state);
case ST_UID: case NICE:
return (p1->st_uid - p2->st_uid); return (p1->nice - p2->nice);
case M_SIZE:
return (p2->m_size - p1->m_size);
case M_RESIDENT:
return (p2->m_resident - p1->m_resident);
case M_SHARE:
return (p2->m_share - p1->m_share);
case PERCENT_CPU:
return (p2->percent_cpu > p1->percent_cpu ? 1 : -1);
case PERCENT_MEM:
return (p2->m_resident - p1->m_resident);
case UTIME:
return (p2->utime - p1->utime);
case STIME:
return (p2->stime - p1->stime);
case TIME: case TIME:
return ((p2->time) - (p1->time)); return ((p2->utime+p2->stime) - (p1->utime+p1->stime));
case TGID: case COMM:
return (p1->tgid - p2->tgid); return strcmp(p1->comm, p2->comm);
case TPGID:
return (p1->tpgid - p2->tpgid);
case TTY_NR:
return (p1->tty_nr - p2->tty_nr);
case USER:
return strcmp(p1->user ? p1->user : "", p2->user ? p2->user : "");
default: default:
return (p1->pid - p2->pid); return (p1->pid - p2->pid);
} }
}
char* Process_printField(ProcessField field) {
switch (field) {
case PID: return " PID ";
case PPID: return " PPID ";
case PGRP: return " PGRP ";
case SESSION: return " SESN ";
case TTY_NR: return " TTY ";
case TPGID: return " TGID ";
case COMM: return "Command ";
case STATE: return "S ";
case PRIORITY: return "PRI ";
case NICE: return " NI ";
case M_SIZE: return " VIRT ";
case M_RESIDENT: return " RES ";
case M_SHARE: return " SHR ";
case ST_UID: return " UID ";
case USER: return "USER ";
case UTIME: return " UTIME+ ";
case STIME: return " STIME+ ";
case TIME: return " TIME+ ";
case PERCENT_CPU: return "CPU% ";
case PERCENT_MEM: return "MEM% ";
case PROCESSOR: return "CPU ";
default: return "- ";
}
} }

229
Process.h
View File

@ -4,113 +4,80 @@
#define HEADER_Process #define HEADER_Process
/* /*
htop - Process.h htop - Process.h
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#ifdef MAJOR_IN_MKDEV #define _GNU_SOURCE
#elif defined(MAJOR_IN_SYSMACROS) #include "ProcessList.h"
#endif
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif
// On Linux, this works only with glibc 2.1+. On earlier versions
// the behavior is similar to have a hardcoded page size.
#ifndef PAGE_SIZE
#define PAGE_SIZE ( sysconf(_SC_PAGESIZE) )
#endif
#define PAGE_SIZE_KB ( PAGE_SIZE / ONE_K )
#include "Object.h" #include "Object.h"
#include "CRT.h"
#include "String.h"
#include "debug.h"
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/param.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdbool.h>
#include <pwd.h>
#define PROCESS_FLAG_IO 0x0001 // This works only with glibc 2.1+. On earlier versions
// the behavior is similar to have a hardcoded page size.
#define PAGE_SIZE ( sysconf(_SC_PAGESIZE) / 1024 )
typedef enum ProcessFields { #define PROCESS_COMM_LEN 300
NULL_PROCESSFIELD = 0,
PID = 1,
COMM = 2, typedef enum ProcessField_ {
STATE = 3, PID = 1, COMM, STATE, PPID, PGRP, SESSION, TTY_NR, TPGID, FLAGS, MINFLT, CMINFLT, MAJFLT, CMAJFLT, UTIME,
PPID = 4, STIME, CUTIME, CSTIME, PRIORITY, NICE, ITREALVALUE, STARTTIME, VSIZE, RSS, RLIM, STARTCODE, ENDCODE,
PGRP = 5, STARTSTACK, KSTKESP, KSTKEIP, SIGNAL, BLOCKED, SSIGIGNORE, SIGCATCH, WCHAN, NSWAP, CNSWAP, EXIT_SIGNAL,
SESSION = 6, PROCESSOR, M_SIZE, M_RESIDENT, M_SHARE, M_TRS, M_DRS, M_LRS, M_DT, ST_UID, PERCENT_CPU, PERCENT_MEM,
TTY_NR = 7, USER, TIME, LAST_PROCESSFIELD
TPGID = 8,
MINFLT = 10,
MAJFLT = 12,
PRIORITY = 18,
NICE = 19,
STARTTIME = 21,
PROCESSOR = 38,
M_SIZE = 39,
M_RESIDENT = 40,
ST_UID = 46,
PERCENT_CPU = 47,
PERCENT_MEM = 48,
USER = 49,
TIME = 50,
NLWP = 51,
TGID = 52,
} ProcessField; } ProcessField;
typedef struct ProcessPidColumn_ { struct ProcessList_;
int id;
const char* label;
} ProcessPidColumn;
typedef struct Process_ { typedef struct Process_ {
Object super; Object super;
struct Settings_* settings; struct ProcessList_ *pl;
unsigned long long int time;
pid_t pid;
pid_t ppid;
pid_t tgid;
char* comm;
int commLen;
int indent;
int basenameOffset;
bool updated; bool updated;
int pid;
char* comm;
int indent;
char state; char state;
bool tag; bool tag;
bool showChildren; int ppid;
bool show; int pgrp;
unsigned int pgrp; int session;
unsigned int session; int tty_nr;
unsigned int tty_nr;
int tpgid; int tpgid;
uid_t st_uid;
unsigned long int flags; unsigned long int flags;
int processor; #ifdef DEBUG
unsigned long int minflt;
float percent_cpu; unsigned long int cminflt;
float percent_mem; unsigned long int majflt;
char* user; unsigned long int cmajflt;
#endif
unsigned long int utime;
unsigned long int stime;
long int cutime;
long int cstime;
long int priority; long int priority;
long int nice; long int nice;
long int nlwp;
char starttime_show[8];
time_t starttime_ctime;
long m_size;
long m_resident;
int exit_signal;
unsigned long int minflt;
unsigned long int majflt;
#ifdef DEBUG #ifdef DEBUG
long int itrealvalue; long int itrealvalue;
unsigned long int starttime;
unsigned long int vsize; unsigned long int vsize;
long int rss; long int rss;
unsigned long int rlim; unsigned long int rlim;
@ -127,84 +94,54 @@ typedef struct Process_ {
unsigned long int nswap; unsigned long int nswap;
unsigned long int cnswap; unsigned long int cnswap;
#endif #endif
int exit_signal;
int processor;
int m_size;
int m_resident;
int m_share;
int m_trs;
int m_drs;
int m_lrs;
int m_dt;
uid_t st_uid;
float percent_cpu;
float percent_mem;
char* user;
} Process; } Process;
typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
} ProcessFieldData;
// Implemented in platform-specific code: #ifdef DEBUG
void Process_writeField(Process* this, RichString* str, ProcessField field); extern char* PROCESS_CLASS;
long Process_compare(const void* v1, const void* v2); #else
#define PROCESS_CLASS NULL
#endif
extern char *Process_fieldNames[];
Process* Process_new(struct ProcessList_ *pl);
Process* Process_clone(Process* this);
void Process_delete(Object* cast); void Process_delete(Object* cast);
bool Process_isThread(Process* this);
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern char Process_pidFormat[20];
typedef Process*(*Process_New)(struct Settings_*); void Process_display(Object* cast, RichString* out);
typedef void (*Process_WriteField)(Process*, RichString*, ProcessField);
typedef struct ProcessClass_ { void Process_toggleTag(Process* this);
const ObjectClass super;
const Process_WriteField writeField;
} ProcessClass;
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass)) void Process_setPriority(Process* this, int priority);
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid) void Process_sendSignal(Process* this, int signal);
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_)) #define ONE_K 1024
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
#define ONE_K 1024L
#define ONE_M (ONE_K * ONE_K) #define ONE_M (ONE_K * ONE_K)
#define ONE_G (ONE_M * ONE_K) #define ONE_G (ONE_M * ONE_K)
#define ONE_T ((long long)ONE_G * ONE_K)
#define ONE_DECIMAL_K 1000L void Process_writeField(Process* this, RichString* str, ProcessField field);
#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K)
#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K)
#define ONE_DECIMAL_T ((long long)ONE_DECIMAL_G * ONE_DECIMAL_K)
extern char Process_pidFormat[20]; int Process_pidCompare(const void* v1, const void* v2);
extern void Process_setupColumnWidths(); int Process_compare(const void* v1, const void* v2);
extern void Process_humanNumber(RichString* str, unsigned long number, bool coloring); char* Process_printField(ProcessField field);
extern void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
extern void Process_printTime(RichString* str, unsigned long long totalHundredths);
extern void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
extern void Process_writeField(Process* this, RichString* str, ProcessField field);
extern void Process_display(Object* cast, RichString* out);
extern void Process_done(Process* this);
extern ProcessClass Process_class;
extern void Process_init(Process* this, struct Settings_* settings);
extern void Process_toggleTag(Process* this);
extern bool Process_setPriority(Process* this, int priority);
extern bool Process_changePriorityBy(Process* this, Arg delta);
extern bool Process_sendSignal(Process* this, Arg sgn);
extern long Process_pidCompare(const void* v1, const void* v2);
extern long Process_compare(const void* v1, const void* v2);
#endif #endif

View File

@ -5,25 +5,43 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#ifndef CONFIG_H
#define CONFIG_H
#include "config.h"
#endif
#include "ProcessList.h" #include "ProcessList.h"
#include "Platform.h" #include "Process.h"
#include "Vector.h"
#include "CRT.h" #include "UsersTable.h"
#include "StringUtils.h" #include "Hashtable.h"
#include "String.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <stdio.h>
#include <signal.h>
#include <stdbool.h>
#include <sys/utsname.h>
#include <stdarg.h>
#include "debug.h"
#include <assert.h>
/*{ /*{
#include "Vector.h" #ifndef PROCDIR
#include "Hashtable.h" #define PROCDIR "/proc"
#include "UsersTable.h" #endif
#include "Panel.h"
#include "Process.h"
#include "Settings.h"
#ifdef HAVE_LIBHWLOC #ifndef PROCSTATFILE
#include <hwloc.h> #define PROCSTATFILE "/proc/stat"
#endif
#ifndef PROCMEMINFOFILE
#define PROCMEMINFOFILE "/proc/meminfo"
#endif #endif
#ifndef MAX_NAME #ifndef MAX_NAME
@ -34,29 +52,35 @@ in the source distribution for its full text.
#define MAX_READ 2048 #define MAX_READ 2048
#endif #endif
typedef struct ProcessList_ { }*/
Settings* settings;
/*{
#ifdef DEBUG
typedef int(*vxscanf)(void*, const char*, va_list);
#endif
typedef struct ProcessList_ {
Vector* processes; Vector* processes;
Vector* processes2; Vector* processes2;
Hashtable* processTable; Hashtable* processTable;
Process* prototype;
UsersTable* usersTable; UsersTable* usersTable;
Panel* panel; int processorCount;
int following;
uid_t userId;
const char* incFilter;
Hashtable* pidWhiteList;
#ifdef HAVE_LIBHWLOC
hwloc_topology_t topology;
bool topologyOk;
#endif
int totalTasks; int totalTasks;
int runningTasks; int runningTasks;
int userlandThreads;
int kernelThreads; unsigned long long int* totalTime;
unsigned long long int* userTime;
unsigned long long int* systemTime;
unsigned long long int* idleTime;
unsigned long long int* niceTime;
unsigned long long int* totalPeriod;
unsigned long long int* userPeriod;
unsigned long long int* systemPeriod;
unsigned long long int* idlePeriod;
unsigned long long int* nicePeriod;
unsigned long long int totalMem; unsigned long long int totalMem;
unsigned long long int usedMem; unsigned long long int usedMem;
@ -68,298 +92,601 @@ typedef struct ProcessList_ {
unsigned long long int usedSwap; unsigned long long int usedSwap;
unsigned long long int freeSwap; unsigned long long int freeSwap;
int cpuCount; ProcessField* fields;
ProcessField sortKey;
int direction;
bool hideThreads;
bool shadowOtherUsers;
bool hideKernelThreads;
bool hideUserlandThreads;
bool treeView;
bool highlightBaseName;
bool highlightMegabytes;
#ifdef DEBUG
FILE* traceFile;
#endif
} ProcessList; } ProcessList;
ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
void ProcessList_goThroughEntries(ProcessList* pl);
}*/ }*/
ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { static ProcessField defaultHeaders[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
this->processes = Vector_new(klass, true, DEFAULT_SIZE);
this->processTable = Hashtable_new(140, false);
this->usersTable = usersTable;
this->pidWhiteList = pidWhiteList;
this->userId = userId;
// tree-view auxiliary buffer #ifdef DEBUG
this->processes2 = Vector_new(klass, true, DEFAULT_SIZE);
// set later by platform-specific code #define ProcessList_read(this, buffer, format, ...) ProcessList_xread(this, (vxscanf) vsscanf, buffer, format, ## __VA_ARGS__ )
this->cpuCount = 0; #define ProcessList_fread(this, file, format, ...) ProcessList_xread(this, (vxscanf) vfscanf, file, format, ## __VA_ARGS__ )
#ifdef HAVE_LIBHWLOC static FILE* ProcessList_fopen(ProcessList* this, const char* path, const char* mode) {
this->topologyOk = false; fprintf(this->traceFile, "[%s]\n", path);
if (hwloc_topology_init(&this->topology) == 0) { return fopen(path, mode);
this->topologyOk = }
#if HWLOC_API_VERSION < 0x00020000
/* try to ignore the top-level machine object type */ static inline int ProcessList_xread(ProcessList* this, vxscanf fn, void* buffer, char* format, ...) {
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_MACHINE) && va_list ap;
/* ignore caches, which don't add structure */ va_start(ap, format);
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CORE) && int num = fn(buffer, format, ap);
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CACHE) && va_end(format);
0 == hwloc_topology_set_flags(this->topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) && va_start(ap, format);
#else while (*format) {
0 == hwloc_topology_set_all_types_filter(this->topology, HWLOC_TYPE_FILTER_KEEP_STRUCTURE) && char ch = *format;
#endif char* c; int* d;
0 == hwloc_topology_load(this->topology); long int* ld; unsigned long int* lu;
long long int* lld; unsigned long long int* llu;
char** s;
if (ch != '%') {
fprintf(this->traceFile, "%c", ch);
format++;
continue;
} }
format++;
switch(*format) {
case 'c': c = va_arg(ap, char*); fprintf(this->traceFile, "%c", *c); break;
case 'd': d = va_arg(ap, int*); fprintf(this->traceFile, "%d", *d); break;
case 's': s = va_arg(ap, char**); fprintf(this->traceFile, "%s", *s); break;
case 'l':
format++;
switch (*format) {
case 'd': ld = va_arg(ap, long int*); fprintf(this->traceFile, "%ld", *ld); break;
case 'u': lu = va_arg(ap, unsigned long int*); fprintf(this->traceFile, "%lu", *lu); break;
case 'l':
format++;
switch (*format) {
case 'd': lld = va_arg(ap, long long int*); fprintf(this->traceFile, "%lld", *lld); break;
case 'u': llu = va_arg(ap, unsigned long long int*); fprintf(this->traceFile, "%llu", *llu); break;
}
}
}
format++;
}
fprintf(this->traceFile, "\n");
va_end(format);
return num;
}
#else
#ifndef ProcessList_read
#define ProcessList_fopen(this, path, mode) fopen(path, mode)
#define ProcessList_read(this, buffer, format, ...) sscanf(buffer, format, ## __VA_ARGS__ )
#define ProcessList_fread(this, file, format, ...) fscanf(file, format, ## __VA_ARGS__ )
#endif #endif
this->following = -1; #endif
ProcessList* ProcessList_new(UsersTable* usersTable) {
ProcessList* this;
this = malloc(sizeof(ProcessList));
this->processes = Vector_new(PROCESS_CLASS, true, DEFAULT_SIZE, Process_compare);
this->processTable = Hashtable_new(70, false);
this->prototype = Process_new(this);
this->usersTable = usersTable;
/* tree-view auxiliary buffers */
this->processes2 = Vector_new(PROCESS_CLASS, true, DEFAULT_SIZE, Process_compare);
#ifdef DEBUG
this->traceFile = fopen("/tmp/htop-proc-trace", "w");
#endif
FILE* status = fopen(PROCSTATFILE, "r");
assert(status != NULL);
char buffer[256];
int procs = -1;
do {
procs++;
fgets(buffer, 255, status);
} while (String_startsWith(buffer, "cpu"));
fclose(status);
this->processorCount = procs - 1;
this->totalTime = calloc(procs, sizeof(long long int));
this->userTime = calloc(procs, sizeof(long long int));
this->systemTime = calloc(procs, sizeof(long long int));
this->niceTime = calloc(procs, sizeof(long long int));
this->idleTime = calloc(procs, sizeof(long long int));
this->totalPeriod = calloc(procs, sizeof(long long int));
this->userPeriod = calloc(procs, sizeof(long long int));
this->systemPeriod = calloc(procs, sizeof(long long int));
this->nicePeriod = calloc(procs, sizeof(long long int));
this->idlePeriod = calloc(procs, sizeof(long long int));
for (int i = 0; i < procs; i++) {
this->totalTime[i] = 1;
this->totalPeriod[i] = 1;
}
this->fields = calloc(sizeof(ProcessField), LAST_PROCESSFIELD+1);
// TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
for (int i = 0; defaultHeaders[i]; i++) {
this->fields[i] = defaultHeaders[i];
}
this->sortKey = PERCENT_CPU;
this->direction = 1;
this->hideThreads = false;
this->shadowOtherUsers = false;
this->hideKernelThreads = false;
this->hideUserlandThreads = false;
this->treeView = false;
this->highlightBaseName = false;
this->highlightMegabytes = false;
return this; return this;
} }
void ProcessList_done(ProcessList* this) { void ProcessList_delete(ProcessList* this) {
#ifdef HAVE_LIBHWLOC
if (this->topologyOk) {
hwloc_topology_destroy(this->topology);
}
#endif
Hashtable_delete(this->processTable); Hashtable_delete(this->processTable);
Vector_delete(this->processes); Vector_delete(this->processes);
Vector_delete(this->processes2); Vector_delete(this->processes2);
Process_delete((Object*)this->prototype);
free(this->totalTime);
free(this->userTime);
free(this->systemTime);
free(this->niceTime);
free(this->idleTime);
free(this->totalPeriod);
free(this->userPeriod);
free(this->systemPeriod);
free(this->nicePeriod);
free(this->idlePeriod);
#ifdef DEBUG
fclose(this->traceFile);
#endif
free(this->fields);
free(this);
} }
void ProcessList_setPanel(ProcessList* this, Panel* panel) { void ProcessList_invertSortOrder(ProcessList* this) {
this->panel = panel; if (this->direction == 1)
} this->direction = -1;
void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header);
ProcessField* fields = this->settings->fields;
for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title;
if (!field) field = "- ";
if (!this->settings->treeView && this->settings->sortKey == fields[i])
RichString_append(header, CRT_colors[PANEL_SELECTION_FOCUS], field);
else else
RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field); this->direction = 1;
}
RichString ProcessList_printHeader(ProcessList* this) {
RichString out;
RichString_init(&out);
ProcessField* fields = this->fields;
for (int i = 0; fields[i]; i++) {
char* field = Process_printField(fields[i]);
if (this->sortKey == fields[i])
RichString_append(&out, CRT_colors[PANEL_HIGHLIGHT_FOCUS], field);
else
RichString_append(&out, CRT_colors[PANEL_HEADER_FOCUS], field);
} }
return out;
}
void ProcessList_prune(ProcessList* this) {
Vector_prune(this->processes);
} }
void ProcessList_add(ProcessList* this, Process* p) { void ProcessList_add(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
assert(Hashtable_get(this->processTable, p->pid) == NULL);
Vector_add(this->processes, p); Vector_add(this->processes, p);
Hashtable_put(this->processTable, p->pid, p); Hashtable_put(this->processTable, p->pid, p);
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
assert(Hashtable_get(this->processTable, p->pid) != NULL);
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
} }
void ProcessList_remove(ProcessList* this, Process* p) { void ProcessList_remove(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1); Hashtable_remove(this->processTable, p->pid);
assert(Hashtable_get(this->processTable, p->pid) != NULL); int index = Vector_indexOf(this->processes, p, Process_pidCompare);
Process* pp = Hashtable_remove(this->processTable, p->pid); Vector_remove(this->processes, index);
assert(pp == p); (void)pp;
unsigned int pid = p->pid;
int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
assert(idx != -1);
if (idx >= 0) Vector_remove(this->processes, idx);
assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
} }
Process* ProcessList_get(ProcessList* this, int idx) { Process* ProcessList_get(ProcessList* this, int index) {
return (Process*) (Vector_get(this->processes, idx)); return (Process*) (Vector_get(this->processes, index));
} }
int ProcessList_size(ProcessList* this) { int ProcessList_size(ProcessList* this) {
return (Vector_size(this->processes)); return (Vector_size(this->processes));
} }
static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show) { static void ProcessList_buildTree(ProcessList* this, int pid, int level, int indent, int direction) {
Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE); Vector* children = Vector_new(PROCESS_CLASS, false, DEFAULT_SIZE, Process_compare);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { for (int i = 0; i < Vector_size(this->processes); i++) {
Process* process = (Process*) (Vector_get(this->processes, i)); Process* process = (Process*) (Vector_get(this->processes, i));
if (process->show && Process_isChildOf(process, pid)) { if (process->ppid == pid) {
process = (Process*) (Vector_take(this->processes, i)); Process* process = (Process*) (Vector_take(this->processes, i));
Vector_add(children, process); Vector_add(children, process);
i--;
} }
} }
int size = Vector_size(children); int size = Vector_size(children);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
Process* process = (Process*) (Vector_get(children, i)); Process* process = (Process*) (Vector_get(children, i));
if (!show)
process->show = false;
int s = this->processes2->items;
if (direction == 1) if (direction == 1)
Vector_add(this->processes2, process); Vector_add(this->processes2, process);
else else
Vector_insert(this->processes2, 0, process); Vector_insert(this->processes2, 0, process);
assert(this->processes2->items == s+1); (void)s; int nextIndent = indent;
int nextIndent = indent | (1 << level); if (i < size - 1)
ProcessList_buildTree(this, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false); nextIndent = indent | (1 << level);
if (i == size - 1) ProcessList_buildTree(this, process->pid, level+1, nextIndent, direction);
process->indent = -nextIndent; process->indent = indent | (1 << level);
else
process->indent = nextIndent;
} }
Vector_delete(children); Vector_delete(children);
} }
void ProcessList_sort(ProcessList* this) { void ProcessList_sort(ProcessList* this) {
if (!this->settings->treeView) { if (!this->treeView) {
Vector_insertionSort(this->processes); Vector_sort(this->processes);
} else { } else {
// Save settings int direction = this->direction;
int direction = this->settings->direction; int sortKey = this->sortKey;
int sortKey = this->settings->sortKey; this->sortKey = PID;
// Sort by PID this->direction = 1;
this->settings->sortKey = PID; Vector_sort(this->processes);
this->settings->direction = 1; this->sortKey = sortKey;
Vector_quickSort(this->processes); this->direction = direction;
// Restore settings Process* init = (Process*) (Vector_take(this->processes, 0));
this->settings->sortKey = sortKey; assert(init->pid == 1);
this->settings->direction = direction; init->indent = 0;
int vsize = Vector_size(this->processes); Vector_add(this->processes2, init);
// Find all processes whose parent is not visible ProcessList_buildTree(this, init->pid, 0, 0, direction);
int size;
while ((size = Vector_size(this->processes))) {
int i;
for (i = 0; i < size; i++) {
Process* process = (Process*)(Vector_get(this->processes, i));
// Immediately consume not shown processes
if (!process->show) {
process = (Process*)(Vector_take(this->processes, i));
process->indent = 0;
Vector_add(this->processes2, process);
ProcessList_buildTree(this, process->pid, 0, 0, direction, false);
break;
}
pid_t ppid = Process_getParentPid(process);
// Bisect the process vector to find parent
int l = 0, r = size;
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
// on Mac OS X 10.11.6) cancel bisecting and regard this process as
// root.
if (process->pid == ppid)
r = 0;
while (l < r) {
int c = (l + r) / 2;
pid_t pid = ((Process*)(Vector_get(this->processes, c)))->pid;
if (ppid == pid) {
break;
} else if (ppid < pid) {
r = c;
} else {
l = c + 1;
}
}
// If parent not found, then construct the tree with this root
if (l >= r) {
process = (Process*)(Vector_take(this->processes, i));
process->indent = 0;
Vector_add(this->processes2, process);
ProcessList_buildTree(this, process->pid, 0, 0, direction, process->showChildren);
break;
}
}
// There should be no loop in the process tree
assert(i < size);
}
assert(Vector_size(this->processes2) == vsize); (void)vsize;
assert(Vector_size(this->processes) == 0);
// Swap listings around
Vector* t = this->processes; Vector* t = this->processes;
this->processes = this->processes2; this->processes = this->processes2;
this->processes2 = t; this->processes2 = t;
} }
} }
static int ProcessList_readStatFile(ProcessList* this, Process *proc, FILE *f, char *command) {
static char buf[MAX_READ];
unsigned long int zero;
ProcessField ProcessList_keyAt(ProcessList* this, int at) { int size = fread(buf, 1, MAX_READ, f);
int x = 0; if(!size) return 0;
ProcessField* fields = this->settings->fields;
ProcessField field; proc->pid = atoi(buf);
for (int i = 0; (field = fields[i]); i++) { char *location = strchr(buf, ' ');
const char* title = Process_fields[field].title; if(!location) return 0;
if (!title) title = "- ";
int len = strlen(title); location += 2;
if (at >= x && at <= x + len) { char *end = strrchr(location, ')');
return field; if(!end) return 0;
}
x += len; int commsize = end - location;
} memcpy(command, location, commsize);
return COMM; command[commsize] = '\0';
location = end + 2;
#ifdef DEBUG
int num = ProcessList_read(this, location,
"%c %d %d %d %d %d %lu %lu %lu %lu "
"%lu %lu %lu %ld %ld %ld %ld %ld %ld "
"%lu %lu %ld %lu %lu %lu %lu %lu "
"%lu %lu %lu %lu %lu %lu %lu %lu "
"%d %d",
&proc->state, &proc->ppid, &proc->pgrp, &proc->session, &proc->tty_nr,
&proc->tpgid, &proc->flags,
&proc->minflt, &proc->cminflt, &proc->majflt, &proc->cmajflt,
&proc->utime, &proc->stime, &proc->cutime, &proc->cstime,
&proc->priority, &proc->nice, &zero, &proc->itrealvalue,
&proc->starttime, &proc->vsize, &proc->rss, &proc->rlim,
&proc->startcode, &proc->endcode, &proc->startstack, &proc->kstkesp,
&proc->kstkeip, &proc->signal, &proc->blocked, &proc->sigignore,
&proc->sigcatch, &proc->wchan, &proc->nswap, &proc->cnswap,
&proc->exit_signal, &proc->processor);
#else
long int uzero;
int num = ProcessList_read(this, location,
"%c %d %d %d %d %d %lu %lu %lu %lu "
"%lu %lu %lu %ld %ld %ld %ld %ld %ld "
"%lu %lu %ld %lu %lu %lu %lu %lu "
"%lu %lu %lu %lu %lu %lu %lu %lu "
"%d %d",
&proc->state, &proc->ppid, &proc->pgrp, &proc->session, &proc->tty_nr,
&proc->tpgid, &proc->flags,
&zero, &zero, &zero, &zero,
&proc->utime, &proc->stime, &proc->cutime, &proc->cstime,
&proc->priority, &proc->nice, &uzero, &uzero,
&zero, &zero, &uzero, &zero,
&zero, &zero, &zero, &zero,
&zero, &zero, &zero, &zero,
&zero, &zero, &zero, &zero,
&proc->exit_signal, &proc->processor);
#endif
// This assert is always valid on 2.4, but reportedly not always valid on 2.6.
// TODO: Check if the semantics of this field has changed.
// assert(zero == 0);
if(num != 37) return 0;
return 1;
} }
void ProcessList_expandTree(ProcessList* this) { bool ProcessList_readStatusFile(ProcessList* this, Process* proc, char* dirname, char* name) {
int size = Vector_size(this->processes); char statusfilename[MAX_NAME+1];
for (int i = 0; i < size; i++) { statusfilename[MAX_NAME] = '\0';
Process* process = (Process*) Vector_get(this->processes, i); /*
process->showChildren = true; bool success = false;
char buffer[256];
buffer[255] = '\0';
snprintf(statusfilename, MAX_NAME, "%s/%s/status", dirname, name);
FILE* status = ProcessList_fopen(this, statusfilename, "r");
if (status) {
while (!feof(status)) {
char* ok = fgets(buffer, 255, status);
if (!ok)
break;
if (String_startsWith(buffer, "Uid:")) {
int uid1, uid2, uid3, uid4;
// TODO: handle other uid's.
int ok = ProcessList_read(this, buffer, "Uid:\t%d\t%d\t%d\t%d", &uid1, &uid2, &uid3, &uid4);
if (ok >= 1) {
proc->st_uid = uid1;
success = true;
} }
break;
}
}
fclose(status);
}
if (!success) {
*/
snprintf(statusfilename, MAX_NAME, "%s/%s", dirname, name);
struct stat sstat;
int statok = stat(statusfilename, &sstat);
if (statok == -1)
return false;
proc->st_uid = sstat.st_uid;
return true;
/*
} else
return true;
*/
} }
void ProcessList_rebuildPanel(ProcessList* this) { void ProcessList_processEntries(ProcessList* this, char* dirname, int parent, float period) {
const char* incFilter = this->incFilter; DIR* dir;
struct dirent* entry;
Process* prototype = this->prototype;
int currPos = Panel_getSelectedIndex(this->panel); dir = opendir(dirname);
pid_t currPid = this->following != -1 ? this->following : 0; assert(dir != NULL);
int currScrollV = this->panel->scrollV; while ((entry = readdir(dir)) != NULL) {
char* name = entry->d_name;
int pid;
// filename is a number: process directory
pid = atoi(name);
Panel_prune(this->panel); // The RedHat kernel hides threads with a dot.
int size = ProcessList_size(this); // I believe this is non-standard.
int idx = 0; bool isThread = false;
for (int i = 0; i < size; i++) { if ((!this->hideThreads) && pid == 0 && name[0] == '.') {
bool hidden = false; char* tname = name + 1;
Process* p = ProcessList_get(this, i); pid = atoi(tname);
if (pid > 0)
if ( (!p->show) isThread = true;
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|| (incFilter && !(String_contains_i(p->comm, incFilter)))
|| (this->pidWhiteList && !Hashtable_get(this->pidWhiteList, p->tgid)) )
hidden = true;
if (!hidden) {
Panel_set(this->panel, idx, (Object*)p);
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == currPid)) {
Panel_setSelected(this->panel, idx);
this->panel->scrollV = currScrollV;
} }
idx++;
if (pid > 0 && pid != parent) {
if (!this->hideUserlandThreads) {
char subdirname[MAX_NAME+1];
snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
if (access(subdirname, X_OK) == 0) {
ProcessList_processEntries(this, subdirname, pid, period);
} }
} }
}
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) { FILE* status;
Process* proc = (Process*) Hashtable_get(this->processTable, pid); char statusfilename[MAX_NAME+1];
*preExisting = proc; char command[PROCESS_COMM_LEN + 1];
if (proc) {
assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1); Process* process;
assert(proc->pid == pid);
Process* existingProcess = (Process*) Hashtable_get(this->processTable, pid);
if (existingProcess) {
process = existingProcess;
} else { } else {
proc = constructor(this->settings); process = prototype;
assert(proc->comm == NULL); process->comm = NULL;
proc->pid = pid; process->pid = pid;
if (! ProcessList_readStatusFile(this, process, dirname, name))
goto errorReadingProcess;
} }
return proc; process->updated = true;
snprintf(statusfilename, MAX_NAME, "%s/%s/statm", dirname, name);
status = ProcessList_fopen(this, statusfilename, "r");
if(!status) {
goto errorReadingProcess;
}
int num = ProcessList_fread(this, status, "%d %d %d %d %d %d %d",
&process->m_size, &process->m_resident, &process->m_share,
&process->m_trs, &process->m_drs, &process->m_lrs,
&process->m_dt);
fclose(status);
if(num != 7)
goto errorReadingProcess;
if (this->hideKernelThreads && process->m_size == 0)
goto errorReadingProcess;
int lasttimes = (process->utime + process->stime);
snprintf(statusfilename, MAX_NAME, "%s/%s/stat", dirname, name);
status = ProcessList_fopen(this, statusfilename, "r");
if (status == NULL)
goto errorReadingProcess;
int success = ProcessList_readStatFile(this, process, status, command);
fclose(status);
if(!success)
goto errorReadingProcess;
if(!existingProcess) {
process->user = UsersTable_getRef(this->usersTable, process->st_uid);
snprintf(statusfilename, MAX_NAME, "%s/%s/cmdline", dirname, name);
status = ProcessList_fopen(this, statusfilename, "r");
if (!status) {
goto errorReadingProcess;
}
int amtRead = fread(command, 1, PROCESS_COMM_LEN - 1, status);
if (amtRead > 0) {
for (int i = 0; i < amtRead; i++)
if (command[i] == '\0' || command[i] == '\n')
command[i] = ' ';
command[amtRead] = '\0';
}
command[PROCESS_COMM_LEN] = '\0';
process->comm = String_copy(command);
fclose(status);
}
process->percent_cpu = (process->utime + process->stime - lasttimes) /
period * 100.0;
process->percent_mem = process->m_resident /
(float)(this->usedMem - this->cachedMem - this->buffersMem) *
100.0;
this->totalTasks++;
if (process->state == 'R') {
this->runningTasks++;
}
if (!existingProcess) {
process = Process_clone(process);
ProcessList_add(this, process);
}
continue;
// Exception handler.
errorReadingProcess: {
if (existingProcess)
ProcessList_remove(this, process);
else {
if (process->comm)
free(process->comm);
}
}
}
}
prototype->comm = NULL;
closedir(dir);
} }
void ProcessList_scan(ProcessList* this) { void ProcessList_scan(ProcessList* this) {
unsigned long long int usertime, nicetime, systemtime, idletime, totaltime;
unsigned long long int swapFree;
FILE* status;
char buffer[128];
status = ProcessList_fopen(this, PROCMEMINFOFILE, "r");
assert(status != NULL);
while (!feof(status)) {
fgets(buffer, 128, status);
switch (buffer[0]) {
case 'M':
if (String_startsWith(buffer, "MemTotal:"))
ProcessList_read(this, buffer, "MemTotal: %llu kB", &this->totalMem);
else if (String_startsWith(buffer, "MemFree:"))
ProcessList_read(this, buffer, "MemFree: %llu kB", &this->freeMem);
else if (String_startsWith(buffer, "MemShared:"))
ProcessList_read(this, buffer, "MemShared: %llu kB", &this->sharedMem);
break;
case 'B':
if (String_startsWith(buffer, "Buffers:"))
ProcessList_read(this, buffer, "Buffers: %llu kB", &this->buffersMem);
break;
case 'C':
if (String_startsWith(buffer, "Cached:"))
ProcessList_read(this, buffer, "Cached: %llu kB", &this->cachedMem);
break;
case 'S':
if (String_startsWith(buffer, "SwapTotal:"))
ProcessList_read(this, buffer, "SwapTotal: %llu kB", &this->totalSwap);
if (String_startsWith(buffer, "SwapFree:"))
ProcessList_read(this, buffer, "SwapFree: %llu kB", &swapFree);
break;
}
}
this->usedMem = this->totalMem - this->freeMem;
this->usedSwap = this->totalSwap - swapFree;
fclose(status);
status = ProcessList_fopen(this, PROCSTATFILE, "r");
assert(status != NULL);
for (int i = 0; i <= this->processorCount; i++) {
char buffer[256];
int cpuid;
unsigned long long int ioWait, irq, softIrq, steal;
ioWait = irq = softIrq = steal = 0;
// Dependending on your kernel version,
// 5, 7 or 8 of these fields will be set.
// The rest will remain at zero.
fgets(buffer, 255, status);
if (i == 0)
ProcessList_read(this, buffer, "cpu %llu %llu %llu %llu %llu %llu %llu %llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal);
else {
ProcessList_read(this, buffer, "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal);
assert(cpuid == i - 1);
}
// Fields existing on kernels >= 2.6
// (and RHEL's patched kernel 2.4...)
systemtime += ioWait + irq + softIrq + steal;
totaltime = usertime + nicetime + systemtime + idletime;
assert (usertime >= this->userTime[i]);
assert (nicetime >= this->niceTime[i]);
assert (systemtime >= this->systemTime[i]);
assert (idletime >= this->idleTime[i]);
assert (totaltime >= this->totalTime[i]);
this->userPeriod[i] = usertime - this->userTime[i];
this->nicePeriod[i] = nicetime - this->niceTime[i];
this->systemPeriod[i] = systemtime - this->systemTime[i];
this->idlePeriod[i] = idletime - this->idleTime[i];
this->totalPeriod[i] = totaltime - this->totalTime[i];
this->userTime[i] = usertime;
this->niceTime[i] = nicetime;
this->systemTime[i] = systemtime;
this->idleTime[i] = idletime;
this->totalTime[i] = totaltime;
}
float period = (float)this->totalPeriod[0] / this->processorCount;
fclose(status);
// mark all process as "dirty" // mark all process as "dirty"
for (int i = 0; i < Vector_size(this->processes); i++) { for (int i = 0; i < Vector_size(this->processes); i++) {
Process* p = (Process*) Vector_get(this->processes, i); Process* p = (Process*) Vector_get(this->processes, i);
p->updated = false; p->updated = false;
p->show = true;
} }
this->totalTasks = 0; this->totalTasks = 0;
this->userlandThreads = 0;
this->kernelThreads = 0;
this->runningTasks = 0; this->runningTasks = 0;
ProcessList_goThroughEntries(this); ProcessList_processEntries(this, PROCDIR, 0, period);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* p = (Process*) Vector_get(this->processes, i); Process* p = (Process*) Vector_get(this->processes, i);
@ -368,4 +695,5 @@ void ProcessList_scan(ProcessList* this) {
else else
p->updated = false; p->updated = false;
} }
} }

View File

@ -9,15 +9,41 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Vector.h" #ifndef CONFIG_H
#include "Hashtable.h" #define CONFIG_H
#include "UsersTable.h" #include "config.h"
#include "Panel.h" #endif
#include "Process.h"
#include "Settings.h"
#ifdef HAVE_LIBHWLOC #include "Process.h"
#include <hwloc.h> #include "Vector.h"
#include "UsersTable.h"
#include "Hashtable.h"
#include "String.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <stdbool.h>
#include <sys/utsname.h>
#include <stdarg.h>
#include "debug.h"
#include <assert.h>
#ifndef PROCDIR
#define PROCDIR "/proc"
#endif
#ifndef PROCSTATFILE
#define PROCSTATFILE "/proc/stat"
#endif
#ifndef PROCMEMINFOFILE
#define PROCMEMINFOFILE "/proc/meminfo"
#endif #endif
#ifndef MAX_NAME #ifndef MAX_NAME
@ -28,29 +54,33 @@ in the source distribution for its full text.
#define MAX_READ 2048 #define MAX_READ 2048
#endif #endif
typedef struct ProcessList_ {
Settings* settings;
#ifdef DEBUG
typedef int(*vxscanf)(void*, const char*, va_list);
#endif
typedef struct ProcessList_ {
Vector* processes; Vector* processes;
Vector* processes2; Vector* processes2;
Hashtable* processTable; Hashtable* processTable;
Process* prototype;
UsersTable* usersTable; UsersTable* usersTable;
Panel* panel; int processorCount;
int following;
uid_t userId;
const char* incFilter;
Hashtable* pidWhiteList;
#ifdef HAVE_LIBHWLOC
hwloc_topology_t topology;
bool topologyOk;
#endif
int totalTasks; int totalTasks;
int runningTasks; int runningTasks;
int userlandThreads;
int kernelThreads; unsigned long long int* totalTime;
unsigned long long int* userTime;
unsigned long long int* systemTime;
unsigned long long int* idleTime;
unsigned long long int* niceTime;
unsigned long long int* totalPeriod;
unsigned long long int* userPeriod;
unsigned long long int* systemPeriod;
unsigned long long int* idlePeriod;
unsigned long long int* nicePeriod;
unsigned long long int totalMem; unsigned long long int totalMem;
unsigned long long int usedMem; unsigned long long int usedMem;
@ -62,41 +92,62 @@ typedef struct ProcessList_ {
unsigned long long int usedSwap; unsigned long long int usedSwap;
unsigned long long int freeSwap; unsigned long long int freeSwap;
int cpuCount; ProcessField* fields;
ProcessField sortKey;
int direction;
bool hideThreads;
bool shadowOtherUsers;
bool hideKernelThreads;
bool hideUserlandThreads;
bool treeView;
bool highlightBaseName;
bool highlightMegabytes;
#ifdef DEBUG
FILE* traceFile;
#endif
} ProcessList; } ProcessList;
ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList, uid_t userId); #ifdef DEBUG
void ProcessList_delete(ProcessList* pl);
void ProcessList_goThroughEntries(ProcessList* pl);
#define ProcessList_read(this, buffer, format, ...) ProcessList_xread(this, (vxscanf) vsscanf, buffer, format, ## __VA_ARGS__ )
#define ProcessList_fread(this, file, format, ...) ProcessList_xread(this, (vxscanf) vfscanf, file, format, ## __VA_ARGS__ )
extern ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); #else
extern void ProcessList_done(ProcessList* this); #ifndef ProcessList_read
#define ProcessList_fopen(this, path, mode) fopen(path, mode)
extern void ProcessList_setPanel(ProcessList* this, Panel* panel); #define ProcessList_read(this, buffer, format, ...) sscanf(buffer, format, ## __VA_ARGS__ )
#define ProcessList_fread(this, file, format, ...) fscanf(file, format, ## __VA_ARGS__ )
extern void ProcessList_printHeader(ProcessList* this, RichString* header); #endif
extern void ProcessList_add(ProcessList* this, Process* p); #endif
extern void ProcessList_remove(ProcessList* this, Process* p); ProcessList* ProcessList_new(UsersTable* usersTable);
extern Process* ProcessList_get(ProcessList* this, int idx); void ProcessList_delete(ProcessList* this);
extern int ProcessList_size(ProcessList* this); void ProcessList_invertSortOrder(ProcessList* this);
extern void ProcessList_sort(ProcessList* this); RichString ProcessList_printHeader(ProcessList* this);
extern ProcessField ProcessList_keyAt(ProcessList* this, int at);
void ProcessList_prune(ProcessList* this);
extern void ProcessList_expandTree(ProcessList* this);
void ProcessList_add(ProcessList* this, Process* p);
extern void ProcessList_rebuildPanel(ProcessList* this);
void ProcessList_remove(ProcessList* this, Process* p);
extern Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor);
Process* ProcessList_get(ProcessList* this, int index);
extern void ProcessList_scan(ProcessList* this);
int ProcessList_size(ProcessList* this);
void ProcessList_sort(ProcessList* this);
bool ProcessList_readStatusFile(ProcessList* this, Process* proc, char* dirname, char* name);
void ProcessList_processEntries(ProcessList* this, char* dirname, int parent, float period);
void ProcessList_scan(ProcessList* this);
#endif #endif

76
README
View File

@ -1,44 +1,50 @@
# [![htop](htop.png)](https://htop.dev)
[![CI](https://github.com/htop-dev/htop/workflows/CI/badge.svg)](https://github.com/htop-dev/htop/actions) htop
[![Coverity Scan Build Status](https://scan.coverity.com/projects/21665/badge.svg)](https://scan.coverity.com/projects/21665) by Hisham Muhammad <loderunner@users.sourceforge.net>
[![Mailing List](https://img.shields.io/badge/Mailing%20List-htop-blue.svg)](https://groups.io/g/htop)
[![IRC #htop](https://img.shields.io/badge/IRC-htop-blue.svg)](https://webchat.freenode.net/#htop)
[![Github Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest)
[![Download](https://api.bintray.com/packages/htop/source/htop/images/download.svg)](https://bintray.com/htop/source/htop/_latestVersion)
## Introduction May, 2004 - July, 2006
`htop` is a cross-platform interactive process viewer. Introduction
It requires `ncurses`. ~~~~~~~~~~~~
For more information and details on how to contribute to `htop` This is htop, an interactive process viewer.
visit [htop.dev](https://htop.dev). It requires ncurses. It is tested with Linux 2.6,
but is also reported to work (and was originally developed)
with the 2.4 series.
## Build instructions Note that, while, htop is Linux specific -- it is based
on the Linux /proc filesystem -- it is also reported to work
with FreeBSD systems featuring a Linux-compatible /proc.
This software has evolved considerably during the last months,
and is reasonably complete, but there is still room for
improvement. Read the TODO file to see what's known to be missing.
Comparison between 'htop' and 'top'
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* In 'htop' you can scroll the list vertically and horizontally
to see all processes and full command lines.
* In 'top' you are subject to a delay for each unassigned
key you press (especially annoying when multi-key escape
sequences are triggered by accident).
* 'htop' starts faster ('top' seems to collect data for a while
before displaying anything).
* In 'htop' you don't need to type the process number to
kill a process, in 'top' you do.
* In 'htop' you don't need to type the process number or
the priority value to renice a process, in 'top' you do.
* In 'htop' you can kill multiple processes at once.
* 'top' is older, hence, more tested.
Compilation instructions
~~~~~~~~~~~~~~~~~~~~~~~~
This program is distributed as a standard autotools-based package. This program is distributed as a standard autotools-based package.
For detailed instructions see the `INSTALL` file, which See the INSTALL file for detailed instructions, but you are
is created after `./autogen.sh` is run. probably used to the common "configure/make/make install" routine.
When compiling from a [release tarball](https://github.com/htop-dev/htop/releases/), run: See the manual page (man htop) or the on-line help ('F1' or 'h'
inside htop) for a list of supported key commands.
~~~ shell if not all keys work check your curses configuration.
./configure && make
~~~
For compiling sources downloaded from the Git repository, run:
~~~ shell
./autogen.sh && ./configure && make
~~~
By default `make install` will install into `/usr/local`, for changing
the path use `./configure --prefix=/some/path`.
See the manual page (`man htop`) or the on-line help ('F1' or 'h'
inside `htop`) for a list of supported key commands.
## License
GNU General Public License, version 2 (GPL-2.0)

View File

@ -1 +0,0 @@
README

View File

@ -1,180 +1,67 @@
/*
htop - RichString.c
(C) 2004,2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "RichString.h" #include "RichString.h"
#include "XAlloc.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <curses.h>
#define RICHSTRING_MAXLEN 350 #include "debug.h"
#include <assert.h>
#define RICHSTRING_MAXLEN 300
/*{ /*{
#include "config.h"
#include <ctype.h>
#include <assert.h> #define RichString_init(this) (this)->len = 0
#ifdef HAVE_NCURSESW_CURSES_H #define RichString_initVal(this) (this).len = 0
#include <ncursesw/curses.h>
#elif HAVE_NCURSES_NCURSES_H
#include <ncurses/ncurses.h>
#elif HAVE_NCURSES_CURSES_H
#include <ncurses/curses.h>
#elif HAVE_NCURSES_H
#include <ncurses.h>
#elif HAVE_CURSES_H
#include <curses.h>
#endif
#ifdef HAVE_LIBNCURSESW
#include <wctype.h>
#endif
#define RichString_size(this) ((this)->chlen)
#define RichString_sizeVal(this) ((this).chlen)
#define RichString_begin(this) RichString (this); memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
#define RichString_beginAllocated(this) memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
#define RichString_end(this) RichString_prune(&(this));
#ifdef HAVE_LIBNCURSESW
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr)
#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + off, n)
#define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255)
#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while(0)
#define CharType cchar_t
#else
#define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr)
#define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, (this).chptr + off, n)
#define RichString_getCharVal(this, i) ((this).chptr[i])
#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)] = ch; } while(0)
#define CharType chtype
#endif
typedef struct RichString_ { typedef struct RichString_ {
int chlen; int len;
CharType* chptr; chtype chstr[RICHSTRING_MAXLEN+1];
CharType chstr[RICHSTRING_MAXLEN+1];
} RichString; } RichString;
}*/ }*/
#ifndef CLAMP #ifndef MIN
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define MIN(a,b) ((a)<(b)?(a):(b))
#endif #endif
#define charBytes(n) (sizeof(CharType) * (n)) void RichString_write(RichString* this, int attrs, char* data) {
RichString_init(this);
static void RichString_extendLen(RichString* this, int len) { RichString_append(this, attrs, data);
if (this->chlen <= RICHSTRING_MAXLEN) {
if (len > RICHSTRING_MAXLEN) {
this->chptr = xMalloc(charBytes(len + 1));
memcpy(this->chptr, this->chstr, charBytes(this->chlen));
}
} else {
if (len <= RICHSTRING_MAXLEN) {
memcpy(this->chstr, this->chptr, charBytes(len));
free(this->chptr);
this->chptr = this->chstr;
} else {
this->chptr = xRealloc(this->chptr, charBytes(len + 1));
}
}
RichString_setChar(this, len, 0);
this->chlen = len;
} }
#define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0) inline void RichString_append(RichString* this, int attrs, char* data) {
RichString_appendn(this, attrs, data, strlen(data));
#ifdef HAVE_LIBNCURSESW
static inline void RichString_writeFrom(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;
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] : '?') } };
}
} }
inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) { inline void RichString_appendn(RichString* this, int attrs, char* data, int len) {
cchar_t* ch = this->chptr + start; int last = MIN(RICHSTRING_MAXLEN - 1, len + this->len);
finish = CLAMP(finish, 0, this->chlen - 1); for (int i = this->len, j = 0; i < last; i++, j++)
for (int i = start; i <= finish; i++) { this->chstr[i] = data[j] | attrs;
ch->attr = attrs; this->chstr[last] = 0;
ch++; this->len = last;
}
} }
int RichString_findChar(RichString* this, char c, int start) { void RichString_setAttr(RichString *this, int attrs) {
wchar_t wc = btowc(c); chtype* ch = this->chstr;
cchar_t* ch = this->chptr + start; for (int i = 0; i < this->len; i++) {
for (int i = start; i < this->chlen; i++) {
if (ch->chars[0] == wc)
return i;
ch++;
}
return -1;
}
#else
static inline void RichString_writeFrom(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[newLen] = 0;
}
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 = (*ch & 0xff) | attrs;
ch++; ch++;
} }
} }
int RichString_findChar(RichString* this, char c, int start) { void RichString_applyAttr(RichString *this, int attrs) {
chtype* ch = this->chptr + start; chtype* ch = this->chstr;
for (int i = start; i < this->chlen; i++) { for (int i = 0; i < this->len; i++) {
if ((*ch & 0xff) == (chtype) c) *ch |= attrs;
return i;
ch++; ch++;
} }
return -1;
} }
#endif RichString RichString_quickString(int attrs, char* data) {
RichString str;
void RichString_prune(RichString* this) { RichString_initVal(str);
if (this->chlen > RICHSTRING_MAXLEN) RichString_write(&str, attrs, data);
free(this->chptr); return str;
memset(this, 0, sizeof(RichString));
this->chptr = this->chstr;
}
void RichString_setAttr(RichString* this, int attrs) {
RichString_setAttrn(this, attrs, 0, this->chlen - 1);
}
void RichString_append(RichString* this, int attrs, const char* data) {
RichString_writeFrom(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);
}
void RichString_write(RichString* this, int attrs, const char* data) {
RichString_writeFrom(this, attrs, data, 0, strlen(data));
} }

View File

@ -2,93 +2,41 @@
#ifndef HEADER_RichString #ifndef HEADER_RichString
#define HEADER_RichString #define HEADER_RichString
/*
htop - RichString.h
(C) 2004,2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define RICHSTRING_MAXLEN 350
#include "config.h" #include <stdlib.h>
#include <ctype.h> #include <string.h>
#include <assert.h>
#ifdef HAVE_NCURSESW_CURSES_H
#include <ncursesw/curses.h>
#elif HAVE_NCURSES_NCURSES_H
#include <ncurses/ncurses.h>
#elif HAVE_NCURSES_CURSES_H
#include <ncurses/curses.h>
#elif HAVE_NCURSES_H
#include <ncurses.h>
#elif HAVE_CURSES_H
#include <curses.h> #include <curses.h>
#endif
#ifdef HAVE_LIBNCURSESW #include "debug.h"
#include <wctype.h> #include <assert.h>
#endif
#define RichString_size(this) ((this)->chlen) #define RICHSTRING_MAXLEN 300
#define RichString_sizeVal(this) ((this).chlen)
#define RichString_begin(this) RichString (this); memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
#define RichString_beginAllocated(this) memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
#define RichString_end(this) RichString_prune(&(this));
#ifdef HAVE_LIBNCURSESW #define RichString_init(this) (this)->len = 0
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr) #define RichString_initVal(this) (this).len = 0
#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + off, n)
#define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255)
#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while(0)
#define CharType cchar_t
#else
#define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr)
#define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, (this).chptr + off, n)
#define RichString_getCharVal(this, i) ((this).chptr[i])
#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)] = ch; } while(0)
#define CharType chtype
#endif
typedef struct RichString_ { typedef struct RichString_ {
int chlen; int len;
CharType* chptr; chtype chstr[RICHSTRING_MAXLEN+1];
CharType chstr[RICHSTRING_MAXLEN+1];
} RichString; } RichString;
#ifndef CLAMP #ifndef MIN
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define MIN(a,b) ((a)<(b)?(a):(b))
#endif #endif
#define charBytes(n) (sizeof(CharType) * (n)) void RichString_write(RichString* this, int attrs, char* data);
#define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0) inline void RichString_append(RichString* this, int attrs, char* data);
#ifdef HAVE_LIBNCURSESW inline void RichString_appendn(RichString* this, int attrs, char* data, int len);
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish); void RichString_setAttr(RichString *this, int attrs);
extern int RichString_findChar(RichString* this, char c, int start); void RichString_applyAttr(RichString *this, int attrs);
#else RichString RichString_quickString(int attrs, char* data);
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
extern int RichString_findChar(RichString* this, char c, int start);
#endif
extern void RichString_prune(RichString* this);
extern void RichString_setAttr(RichString* this, int attrs);
extern void RichString_append(RichString* this, int attrs, const char* data);
extern void RichString_appendn(RichString* this, int attrs, const char* data, int len);
extern void RichString_write(RichString* this, int attrs, const char* data);
#endif #endif

View File

@ -1,27 +1,22 @@
/* /*
htop - ScreenManager.c htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "ScreenManager.h" #include "ScreenManager.h"
#include "ProcessList.h" #include "Panel.h"
#include "Object.h" #include "Object.h"
#include "CRT.h" #include "Vector.h"
#include "FunctionBar.h"
#include "debug.h"
#include <assert.h> #include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
/*{ /*{
#include "FunctionBar.h"
#include "Vector.h"
#include "Header.h"
#include "Settings.h"
#include "Panel.h"
typedef enum Orientation_ { typedef enum Orientation_ {
VERTICAL, VERTICAL,
@ -34,291 +29,184 @@ typedef struct ScreenManager_ {
int x2; int x2;
int y2; int y2;
Orientation orientation; Orientation orientation;
Vector* panels; Vector* items;
int panelCount; Vector* fuBars;
const Header* header; int itemCount;
const Settings* settings; FunctionBar* fuBar;
bool owner; bool owner;
bool allowFocusChange;
} ScreenManager; } ScreenManager;
}*/ }*/
ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner) { ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, bool owner) {
ScreenManager* this; ScreenManager* this;
this = xMalloc(sizeof(ScreenManager)); this = malloc(sizeof(ScreenManager));
this->x1 = x1; this->x1 = x1;
this->y1 = y1; this->y1 = y1;
this->x2 = x2; this->x2 = x2;
this->y2 = y2; this->y2 = y2;
this->fuBar = NULL;
this->orientation = orientation; this->orientation = orientation;
this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE); this->items = Vector_new(PANEL_CLASS, owner, DEFAULT_SIZE, NULL);
this->panelCount = 0; this->fuBars = Vector_new(FUNCTIONBAR_CLASS, true, DEFAULT_SIZE, NULL);
this->header = header; this->itemCount = 0;
this->settings = settings;
this->owner = owner; this->owner = owner;
this->allowFocusChange = true;
return this; return this;
} }
void ScreenManager_delete(ScreenManager* this) { void ScreenManager_delete(ScreenManager* this) {
Vector_delete(this->panels); Vector_delete(this->items);
Vector_delete(this->fuBars);
free(this); free(this);
} }
inline int ScreenManager_size(ScreenManager* this) { inline int ScreenManager_size(ScreenManager* this) {
return this->panelCount; return this->itemCount;
} }
void ScreenManager_add(ScreenManager* this, Panel* item, int size) { void ScreenManager_add(ScreenManager* this, Panel* item, FunctionBar* fuBar, int size) {
if (this->orientation == HORIZONTAL) { if (this->orientation == HORIZONTAL) {
int lastX = 0; int lastX = 0;
if (this->panelCount > 0) { if (this->itemCount > 0) {
Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1); Panel* last = (Panel*) Vector_get(this->items, this->itemCount - 1);
lastX = last->x + last->w + 1; lastX = last->x + last->w + 1;
} }
int height = LINES - this->y1 + this->y2;
if (size > 0) { if (size > 0) {
Panel_resize(item, size, height); Panel_resize(item, size, LINES-this->y1+this->y2);
} else { } else {
Panel_resize(item, COLS-this->x1+this->x2-lastX, height); Panel_resize(item, COLS-this->x1+this->x2-lastX, LINES-this->y1+this->y2);
} }
Panel_move(item, lastX, this->y1); Panel_move(item, lastX, this->y1);
} }
// TODO: VERTICAL // TODO: VERTICAL
Vector_add(this->panels, item); Vector_add(this->items, item);
if (fuBar)
Vector_add(this->fuBars, fuBar);
else
Vector_add(this->fuBars, FunctionBar_new(0, NULL, NULL, NULL));
if (!this->fuBar && fuBar) this->fuBar = fuBar;
item->needsRedraw = true; item->needsRedraw = true;
this->panelCount++; this->itemCount++;
} }
Panel* ScreenManager_remove(ScreenManager* this, int idx) { Panel* ScreenManager_remove(ScreenManager* this, int index) {
assert(this->panelCount > idx); assert(this->itemCount > index);
Panel* panel = (Panel*) Vector_remove(this->panels, idx); Panel* panel = (Panel*) Vector_remove(this->items, index);
this->panelCount--; Vector_remove(this->fuBars, index);
this->fuBar = NULL;
this->itemCount--;
return panel; return panel;
} }
void ScreenManager_setFunctionBar(ScreenManager* this, FunctionBar* fuBar) {
if (this->owner && this->fuBar)
FunctionBar_delete((Object*)this->fuBar);
this->fuBar = fuBar;
}
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) { void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) {
this->x1 = x1; this->x1 = x1;
this->y1 = y1; this->y1 = y1;
this->x2 = x2; this->x2 = x2;
this->y2 = y2; this->y2 = y2;
int panels = this->panelCount; int items = this->itemCount;
if (this->orientation == HORIZONTAL) {
int lastX = 0; int lastX = 0;
for (int i = 0; i < panels - 1; i++) { for (int i = 0; i < items - 1; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i); Panel* panel = (Panel*) Vector_get(this->items, i);
Panel_resize(panel, panel->w, LINES-y1+y2); Panel_resize(panel, panel->w, LINES-y1+y2);
Panel_move(panel, lastX, y1); Panel_move(panel, lastX, y1);
lastX = panel->x + panel->w + 1; lastX = panel->x + panel->w + 1;
} }
Panel* panel = (Panel*) Vector_get(this->panels, panels-1); Panel* panel = (Panel*) Vector_get(this->items, items-1);
Panel_resize(panel, COLS-x1+x2-lastX, LINES-y1+y2); Panel_resize(panel, COLS-x1+x2-lastX, LINES-y1+y2);
Panel_move(panel, lastX, y1); Panel_move(panel, lastX, y1);
}
// TODO: VERTICAL
}
static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool *rescan, bool *timedOut) {
ProcessList* pl = this->header->pl;
struct timeval tv;
gettimeofday(&tv, NULL);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
*timedOut = (newTime - *oldTime > this->settings->delay);
*rescan = *rescan || *timedOut;
if (newTime < *oldTime) *rescan = true; // clock was adjusted?
if (*rescan) {
*oldTime = newTime;
ProcessList_scan(pl);
if (*sortTimeout == 0 || this->settings->treeView) {
ProcessList_sort(pl);
*sortTimeout = 1;
}
*redraw = true;
}
if (*redraw) {
ProcessList_rebuildPanel(pl);
Header_draw(this->header);
}
*rescan = false;
}
static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
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);
if (this->orientation == HORIZONTAL) {
mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
}
}
}
static Panel* setCurrentPanel(Panel* panel) {
FunctionBar_draw(panel->currentBar, NULL);
return panel;
} }
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false; bool quit = false;
int focus = 0; int focus = 0;
Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus)); Panel* panelFocus = (Panel*) Vector_get(this->items, focus);
if (this->fuBar)
double oldTime = 0.0; FunctionBar_draw(this->fuBar, NULL);
int ch = ERR;
int closeTimeout = 0;
bool timedOut = true;
bool redraw = true;
bool rescan = false;
int sortTimeout = 0;
int resetSortTimeout = 5;
int ch = 0;
while (!quit) { while (!quit) {
if (this->header) { int items = this->itemCount;
checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut); for (int i = 0; i < items; i++) {
Panel* panel = (Panel*) Vector_get(this->items, i);
Panel_draw(panel, i == focus);
if (i < items) {
if (this->orientation == HORIZONTAL) {
mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
} }
if (redraw) {
ScreenManager_drawPanels(this, focus);
} }
}
FunctionBar* bar = (FunctionBar*) Vector_get(this->fuBars, focus);
if (bar)
this->fuBar = bar;
if (this->fuBar)
FunctionBar_draw(this->fuBar, NULL);
int prevCh = ch;
set_escdelay(25);
ch = getch(); ch = getch();
if (this->settings->vimMode) { bool loop = false;
switch (ch) { if (ch == KEY_MOUSE) {
case 'h': ch = KEY_LEFT; break;
case 'j': ch = KEY_DOWN; break;
case 'k': ch = KEY_UP; break;
case 'l': ch = KEY_RIGHT; break;
case KEY_LEFT: ch = 'h'; break;
case KEY_DOWN: ch = 'j'; break;
case KEY_UP: ch = 'k'; break;
case KEY_RIGHT: ch = 'l'; break;
case 'K': ch = 'k'; break;
case 'J': ch = 'K'; break;
case 'L': ch = 'l'; break;
}
}
HandlerResult result = IGNORED;
if (ch == KEY_MOUSE && this->settings->enableMouse) {
ch = ERR;
MEVENT mevent; MEVENT mevent;
int ok = getmouse(&mevent); int ok = getmouse(&mevent);
if (ok == OK) { if (ok == OK) {
if (mevent.bstate & BUTTON1_RELEASED) {
if (mevent.y == LINES - 1) { if (mevent.y == LINES - 1) {
ch = FunctionBar_synthesizeEvent(panelFocus->currentBar, mevent.x); ch = FunctionBar_synthesizeEvent(this->fuBar, mevent.x);
} else { } else {
for (int i = 0; i < this->panelCount; i++) { for (int i = 0; i < this->itemCount; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i); Panel* panel = (Panel*) Vector_get(this->items, i);
if (mevent.x >= panel->x && mevent.x <= panel->x+panel->w) { if (mevent.x > panel->x && mevent.x <= panel->x+panel->w &&
if (mevent.y == panel->y) { mevent.y > panel->y && mevent.y <= panel->y+panel->h) {
ch = EVENT_HEADER_CLICK(mevent.x - panel->x);
break;
} else if (mevent.y > panel->y && mevent.y <= panel->y+panel->h) {
ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) {
focus = i; focus = i;
panelFocus = setCurrentPanel(panel); panelFocus = panel;
Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1); Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) { loop = true;
ch = KEY_RECLICK;
}
}
break; break;
} }
} }
} }
} }
#if NCURSES_MOUSE_VERSION > 1
} else if (mevent.bstate & BUTTON4_PRESSED) {
ch = KEY_WHEELUP;
} else if (mevent.bstate & BUTTON5_PRESSED) {
ch = KEY_WHEELDOWN;
#endif
} }
} if (loop) continue;
}
if (ch == ERR) { if (panelFocus->eventHandler) {
sortTimeout--; HandlerResult result = panelFocus->eventHandler(panelFocus, ch);
if (prevCh == ch && !timedOut) { if (result == HANDLED) {
closeTimeout++;
if (closeTimeout == 100) {
break;
}
} else
closeTimeout = 0;
redraw = false;
continue; continue;
} } else if (result == BREAK_LOOP) {
switch (ch) {
case KEY_ALT('H'): ch = KEY_LEFT; break;
case KEY_ALT('J'): ch = KEY_DOWN; break;
case KEY_ALT('K'): ch = KEY_UP; break;
case KEY_ALT('L'): ch = KEY_RIGHT; break;
}
redraw = true;
if (Panel_eventHandlerFn(panelFocus)) {
result = Panel_eventHandler(panelFocus, ch);
}
if (result & SYNTH_KEY) {
ch = result >> 16;
}
if (result & REDRAW) {
sortTimeout = 0;
}
if (result & RESCAN) {
rescan = true;
sortTimeout = 0;
}
if (result & HANDLED) {
continue;
} else if (result & BREAK_LOOP) {
quit = true; quit = true;
continue; continue;
} }
}
switch (ch) { switch (ch) {
case ERR:
continue;
case KEY_RESIZE: case KEY_RESIZE:
{ {
ScreenManager_resize(this, this->x1, this->y1, this->x2, this->y2); ScreenManager_resize(this, this->x1, this->y1, this->x2, this->y2);
continue; continue;
} }
case KEY_LEFT: case KEY_LEFT:
case KEY_CTRL('B'):
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange)
break;
tryLeft: tryLeft:
if (focus > 0) if (focus > 0)
focus--; focus--;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus)); panelFocus = (Panel*) Vector_get(this->items, focus);
if (Panel_size(panelFocus) == 0 && focus > 0) if (Panel_getSize(panelFocus) == 0 && focus > 0)
goto tryLeft; goto tryLeft;
break; break;
case KEY_RIGHT: case KEY_RIGHT:
case KEY_CTRL('F'):
case 9: case 9:
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange)
break;
tryRight: tryRight:
if (focus < this->panelCount - 1) if (focus < this->itemCount - 1)
focus++; focus++;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus)); panelFocus = (Panel*) Vector_get(this->items, focus);
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) if (Panel_getSize(panelFocus) == 0 && focus < this->itemCount - 1)
goto tryRight; goto tryRight;
break; break;
case KEY_F(10): case KEY_F(10):
@ -327,15 +215,11 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
quit = true; quit = true;
continue; continue;
default: default:
defaultHandler:
sortTimeout = resetSortTimeout;
Panel_onKey(panelFocus, ch); Panel_onKey(panelFocus, ch);
break; break;
} }
} }
if (lastFocus)
*lastFocus = panelFocus; *lastFocus = panelFocus;
if (lastKey)
*lastKey = ch; *lastKey = ch;
} }

View File

@ -3,17 +3,22 @@
#ifndef HEADER_ScreenManager #ifndef HEADER_ScreenManager
#define HEADER_ScreenManager #define HEADER_ScreenManager
/* /*
htop - ScreenManager.h htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "FunctionBar.h"
#include "Vector.h"
#include "Header.h"
#include "Settings.h"
#include "Panel.h" #include "Panel.h"
#include "Object.h"
#include "Vector.h"
#include "FunctionBar.h"
#include "debug.h"
#include <assert.h>
#include <stdbool.h>
typedef enum Orientation_ { typedef enum Orientation_ {
VERTICAL, VERTICAL,
@ -26,27 +31,28 @@ typedef struct ScreenManager_ {
int x2; int x2;
int y2; int y2;
Orientation orientation; Orientation orientation;
Vector* panels; Vector* items;
int panelCount; Vector* fuBars;
const Header* header; int itemCount;
const Settings* settings; FunctionBar* fuBar;
bool owner; bool owner;
bool allowFocusChange;
} ScreenManager; } ScreenManager;
extern ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner); ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, bool owner);
extern void ScreenManager_delete(ScreenManager* this); void ScreenManager_delete(ScreenManager* this);
extern int ScreenManager_size(ScreenManager* this); inline int ScreenManager_size(ScreenManager* this);
extern void ScreenManager_add(ScreenManager* this, Panel* item, int size); void ScreenManager_add(ScreenManager* this, Panel* item, FunctionBar* fuBar, int size);
extern Panel* ScreenManager_remove(ScreenManager* this, int idx); Panel* ScreenManager_remove(ScreenManager* this, int index);
extern void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2); void ScreenManager_setFunctionBar(ScreenManager* this, FunctionBar* fuBar);
extern void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey); void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey);
#endif #endif

View File

@ -1,462 +1,223 @@
/* /*
htop - Settings.c htop - Settings.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Settings.h" #include "Settings.h"
#include "Platform.h" #include "String.h"
#include "ProcessList.h"
#include "Header.h"
#include "StringUtils.h" #include "debug.h"
#include "Vector.h"
#include "CRT.h"
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define DEFAULT_DELAY 15 #define DEFAULT_DELAY 15
/*{ /*{
#include "Process.h"
#include <stdbool.h>
typedef struct {
int len;
char** names;
int* modes;
} MeterColumnSettings;
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* userSettings;
ProcessList* pl;
MeterColumnSettings columns[2]; Header* header;
ProcessField* fields;
int flags;
int colorScheme; int colorScheme;
int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool showCPUUsage;
bool showCPUFrequency;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
bool showThreadNames;
bool hideKernelThreads;
bool hideUserlandThreads;
bool highlightBaseName;
bool highlightMegabytes;
bool highlightThreads;
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
bool enableMouse;
bool vimMode;
#ifdef HAVE_LIBHWLOC
bool topologyAffinity;
#endif
bool changed; bool changed;
int delay;
} Settings; } Settings;
#ifndef Settings_cpuId
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromZero ? (cpu) : (cpu)+1)
#endif
}*/ }*/
void Settings_delete(Settings* this) { Settings* Settings_new(ProcessList* pl, Header* header) {
free(this->filename); Settings* this = malloc(sizeof(Settings));
free(this->fields); this->pl = pl;
for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) { this->header = header;
String_freeArray(this->columns[i].names); char* home;
free(this->columns[i].modes); char* rcfile;
} home = getenv("HOME_ETC");
free(this); if (!home) home = getenv("HOME");
}
static void Settings_readMeters(Settings* this, char* line, int column) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
free(trim);
this->columns[column].names = ids;
}
static void Settings_readMeterModes(Settings* this, char* line, int column) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
free(trim);
int len = 0;
for (int i = 0; ids[i]; i++) {
len++;
}
this->columns[column].len = len;
int* modes = xCalloc(len, sizeof(int));
for (int i = 0; i < len; i++) {
modes[i] = atoi(ids[i]);
}
String_freeArray(ids);
this->columns[column].modes = modes;
}
static void Settings_defaultMeters(Settings* this) {
int sizes[] = { 3, 3 };
if (this->cpuCount > 4) {
sizes[1]++;
}
for (int i = 0; i < 2; i++) {
this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->columns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i];
}
int r = 0;
if (this->cpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2");
this->columns[1].modes[r++] = BAR_METERMODE;
} else if (this->cpuCount > 4) {
this->columns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs");
this->columns[1].modes[r++] = BAR_METERMODE;
} else {
this->columns[0].names[0] = xStrdup("AllCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
}
this->columns[0].names[1] = xStrdup("Memory");
this->columns[0].modes[1] = BAR_METERMODE;
this->columns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("Tasks");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("LoadAverage");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("Uptime");
this->columns[1].modes[r++] = TEXT_METERMODE;
}
static void readFields(ProcessField* fields, int* flags, const char* line) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
free(trim);
int i, j;
*flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
fields[j] = id;
*flags |= Process_fields[id].flags;
j++;
}
}
fields[j] = NULL_PROCESSFIELD;
String_freeArray(ids);
}
static bool Settings_read(Settings* this, const char* fileName) {
FILE* fd;
CRT_dropPrivileges();
fd = fopen(fileName, "r");
CRT_restorePrivileges();
if (!fd)
return false;
bool didReadMeters = false;
bool didReadFields = false;
for (;;) {
char* line = String_readLine(fd);
if (!line) {
break;
}
int nOptions;
char** option = String_split(line, '=', &nOptions);
free (line);
if (nOptions < 2) {
String_freeArray(option);
continue;
}
if (String_eq(option[0], "fields")) {
readFields(this->fields, &(this->flags), option[1]);
didReadFields = true;
} 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], "sort_direction")) {
this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
} else if (String_eq(option[0], "hide_threads")) {
this->hideThreads = 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")) {
this->hideUserlandThreads = atoi(option[1]);
} else if (String_eq(option[0], "shadow_other_users")) {
this->shadowOtherUsers = atoi(option[1]);
} else if (String_eq(option[0], "show_thread_names")) {
this->showThreadNames = atoi(option[1]);
} else if (String_eq(option[0], "show_program_path")) {
this->showProgramPath = atoi(option[1]);
} else if (String_eq(option[0], "highlight_base_name")) {
this->highlightBaseName = atoi(option[1]);
} else if (String_eq(option[0], "highlight_megabytes")) {
this->highlightMegabytes = atoi(option[1]);
} else if (String_eq(option[0], "highlight_threads")) {
this->highlightThreads = atoi(option[1]);
} else if (String_eq(option[0], "header_margin")) {
this->headerMargin = atoi(option[1]);
} else if (String_eq(option[0], "expand_system_time")) {
// Compatibility option.
this->detailedCPUTime = atoi(option[1]);
} else if (String_eq(option[0], "detailed_cpu_time")) {
this->detailedCPUTime = atoi(option[1]);
} else if (String_eq(option[0], "cpu_count_from_zero")) {
this->countCPUsFromZero = atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_usage")) {
this->showCPUUsage = atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_frequency")) {
this->showCPUFrequency = atoi(option[1]);
} else if (String_eq(option[0], "update_process_names")) {
this->updateProcessNames = atoi(option[1]);
} else if (String_eq(option[0], "account_guest_in_cpu_meter")) {
this->accountGuestInCPUMeter = atoi(option[1]);
} else if (String_eq(option[0], "delay")) {
this->delay = atoi(option[1]);
} else if (String_eq(option[0], "color_scheme")) {
this->colorScheme = atoi(option[1]);
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0;
} else if (String_eq(option[0], "enable_mouse")) {
this->enableMouse = atoi(option[1]);
} else if (String_eq(option[0], "left_meters")) {
Settings_readMeters(this, option[1], 0);
didReadMeters = true;
} else if (String_eq(option[0], "right_meters")) {
Settings_readMeters(this, option[1], 1);
didReadMeters = true;
} else if (String_eq(option[0], "left_meter_modes")) {
Settings_readMeterModes(this, option[1], 0);
didReadMeters = true;
} else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1);
didReadMeters = true;
} else if (String_eq(option[0], "vim_mode")) {
this->vimMode = atoi(option[1]);
#ifdef HAVE_LIBHWLOC
} else if (String_eq(option[0], "topology_affinity")) {
this->topologyAffinity = !!atoi(option[1]);
#endif
}
String_freeArray(option);
}
fclose(fd);
if (!didReadMeters) {
Settings_defaultMeters(this);
}
return didReadFields;
}
static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
fprintf(fd, "%s=", name);
const char* sep = "";
for (int i = 0; fields[i]; i++) {
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i]-1);
sep = " ";
}
fprintf(fd, "\n");
}
static void writeMeters(Settings* this, FILE* fd, int column) {
const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%s", sep, this->columns[column].names[i]);
sep = " ";
}
fprintf(fd, "\n");
}
static void writeMeterModes(Settings* this, FILE* fd, int column) {
const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%d", sep, this->columns[column].modes[i]);
sep = " ";
}
fprintf(fd, "\n");
}
bool Settings_write(Settings* this) {
FILE* fd;
CRT_dropPrivileges();
fd = fopen(this->filename, "w");
CRT_restorePrivileges();
if (fd == NULL) {
return false;
}
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
writeFields(fd, this->fields, "fields");
// 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, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers);
fprintf(fd, "show_thread_names=%d\n", (int) this->showThreadNames);
fprintf(fd, "show_program_path=%d\n", (int) this->showProgramPath);
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero);
fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage);
fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency);
fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames);
fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter);
fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
fprintf(fd, "enable_mouse=%d\n", (int) this->enableMouse);
fprintf(fd, "delay=%d\n", (int) this->delay);
fprintf(fd, "left_meters="); writeMeters(this, fd, 0);
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
fprintf(fd, "vim_mode=%d\n", (int) this->vimMode);
#ifdef HAVE_LIBHWLOC
fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity);
#endif
fclose(fd);
return true;
}
Settings* Settings_new(int cpuCount) {
Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->direction = 1;
this->hideThreads = false;
this->shadowOtherUsers = false;
this->showThreadNames = false;
this->hideKernelThreads = false;
this->hideUserlandThreads = false;
this->treeView = false;
this->highlightBaseName = false;
this->highlightMegabytes = false;
this->detailedCPUTime = false;
this->countCPUsFromZero = false;
this->showCPUUsage = true;
this->showCPUFrequency = false;
this->updateProcessNames = false;
this->cpuCount = cpuCount;
this->showProgramPath = true;
this->highlightThreads = true;
#ifdef HAVE_LIBHWLOC
this->topologyAffinity = false;
#endif
this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
this->flags = 0;
ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
}
char* legacyDotfile = NULL;
char* rcfile = getenv("HTOPRC");
if (rcfile) {
this->filename = xStrdup(rcfile);
} else {
const char* home = getenv("HOME");
if (!home) home = ""; if (!home) home = "";
const char* xdgConfigHome = getenv("XDG_CONFIG_HOME"); rcfile = getenv("HOMERC");
char* configDir = NULL; if (!rcfile)
char* htopDir = NULL; this->userSettings = String_cat(home, "/.htoprc");
if (xdgConfigHome) { else
this->filename = String_cat(xdgConfigHome, "/htop/htoprc"); this->userSettings = String_copy(rcfile);
configDir = xStrdup(xdgConfigHome);
htopDir = String_cat(xdgConfigHome, "/htop");
} else {
this->filename = String_cat(home, "/.config/htop/htoprc");
configDir = String_cat(home, "/.config");
htopDir = String_cat(home, "/.config/htop");
}
legacyDotfile = String_cat(home, "/.htoprc");
CRT_dropPrivileges();
(void) mkdir(configDir, 0700);
(void) mkdir(htopDir, 0700);
free(htopDir);
free(configDir);
struct stat st;
int err = lstat(legacyDotfile, &st);
if (err || S_ISLNK(st.st_mode)) {
free(legacyDotfile);
legacyDotfile = NULL;
}
CRT_restorePrivileges();
}
this->colorScheme = 0; this->colorScheme = 0;
this->enableMouse = true;
this->changed = false; this->changed = false;
this->delay = DEFAULT_DELAY; this->delay = DEFAULT_DELAY;
bool ok = false; bool ok = Settings_read(this, this->userSettings);
if (legacyDotfile) {
ok = Settings_read(this, legacyDotfile);
if (ok) {
// Transition to new location and delete old configuration file
if (Settings_write(this))
unlink(legacyDotfile);
}
free(legacyDotfile);
}
if (!ok) {
ok = Settings_read(this, this->filename);
}
if (!ok) { if (!ok) {
this->changed = true; this->changed = true;
// TODO: how to get SYSCONFDIR correctly through Autoconf? // TODO: how to get SYSCONFDIR correctly through Autoconf?
char* systemSettings = String_cat(SYSCONFDIR, "/htoprc"); char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
ok = Settings_read(this, systemSettings); ok = Settings_read(this, systemSettings);
free(systemSettings); free(systemSettings);
}
if (!ok) { if (!ok) {
Settings_defaultMeters(this); Header_defaultMeters(this->header);
this->hideKernelThreads = true; pl->hideKernelThreads = true;
this->highlightMegabytes = true; pl->highlightMegabytes = true;
this->highlightThreads = true; }
this->headerMargin = true;
} }
return this; return this;
} }
void Settings_invertSortOrder(Settings* this) { void Settings_delete(Settings* this) {
if (this->direction == 1) free(this->userSettings);
this->direction = -1; free(this);
else }
this->direction = 1;
static void Settings_readMeters(Settings* this, char* line, HeaderSide side) {
char* trim = String_trim(line);
char** ids = String_split(trim, ' ');
free(trim);
int i;
for (i = 0; ids[i] != NULL; i++) {
Header_createMeter(this->header, ids[i], side);
}
String_freeArray(ids);
}
static void Settings_readMeterModes(Settings* this, char* line, HeaderSide side) {
char* trim = String_trim(line);
char** ids = String_split(trim, ' ');
free(trim);
int i;
for (i = 0; ids[i] != NULL; i++) {
int mode = atoi(ids[i]);
Header_setMode(this->header, i, mode, side);
}
String_freeArray(ids);
}
bool Settings_read(Settings* this, char* fileName) {
// TODO: implement File object and make
// file I/O object-oriented.
FILE* fd;
fd = fopen(fileName, "r");
if (fd == NULL) {
return false;
}
const int maxLine = 512;
char buffer[maxLine];
bool readMeters = false;
while (!feof(fd)) {
buffer[0] = '\0';
fgets(buffer, maxLine, fd);
char** option = String_split(buffer, '=');
if (String_eq(option[0], "fields")) {
char* trim = String_trim(option[1]);
char** ids = String_split(trim, ' ');
free(trim);
int i, j;
for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i] != NULL; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
if (id > 0 && id < LAST_PROCESSFIELD) {
this->pl->fields[j] = id;
j++;
}
}
this->pl->fields[j] = (ProcessField) NULL;
String_freeArray(ids);
} else if (String_eq(option[0], "sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->pl->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) {
this->pl->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->pl->treeView = atoi(option[1]);
} else if (String_eq(option[0], "hide_threads")) {
this->pl->hideThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) {
this->pl->hideKernelThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_userland_threads")) {
this->pl->hideUserlandThreads = atoi(option[1]);
} else if (String_eq(option[0], "shadow_other_users")) {
this->pl->shadowOtherUsers = atoi(option[1]);
} else if (String_eq(option[0], "highlight_base_name")) {
this->pl->highlightBaseName = atoi(option[1]);
} else if (String_eq(option[0], "highlight_megabytes")) {
this->pl->highlightMegabytes = atoi(option[1]);
} else if (String_eq(option[0], "header_margin")) {
this->header->margin = atoi(option[1]);
} else if (String_eq(option[0], "delay")) {
this->delay = atoi(option[1]);
} else if (String_eq(option[0], "color_scheme")) {
this->colorScheme = atoi(option[1]);
if (this->colorScheme < 0) this->colorScheme = 0;
if (this->colorScheme > 5) this->colorScheme = 5;
} else if (String_eq(option[0], "left_meters")) {
Settings_readMeters(this, option[1], LEFT_HEADER);
readMeters = true;
} else if (String_eq(option[0], "right_meters")) {
Settings_readMeters(this, option[1], RIGHT_HEADER);
readMeters = true;
} else if (String_eq(option[0], "left_meter_modes")) {
Settings_readMeterModes(this, option[1], LEFT_HEADER);
readMeters = true;
} else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], RIGHT_HEADER);
readMeters = true;
}
String_freeArray(option);
}
fclose(fd);
if (!readMeters) {
Header_defaultMeters(this->header);
}
return true;
}
bool Settings_write(Settings* this) {
// TODO: implement File object and make
// file I/O object-oriented.
FILE* fd;
fd = fopen(this->userSettings, "w");
if (fd == NULL) {
return false;
}
fprintf(fd, "# Beware! This file is rewritten every time htop exits.\n");
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
fprintf(fd, "# (I know, it's in the todo list).\n");
fprintf(fd, "fields=");
for (int i = 0; this->pl->fields[i]; i++) {
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "%d ", (int) this->pl->fields[i]-1);
}
fprintf(fd, "\n");
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->pl->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->pl->direction);
fprintf(fd, "hide_threads=%d\n", (int) this->pl->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->pl->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->pl->hideUserlandThreads);
fprintf(fd, "shadow_other_users=%d\n", (int) this->pl->shadowOtherUsers);
fprintf(fd, "highlight_base_name=%d\n", (int) this->pl->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->pl->highlightMegabytes);
fprintf(fd, "tree_view=%d\n", (int) this->pl->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->header->margin);
fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
fprintf(fd, "delay=%d\n", (int) this->delay);
fprintf(fd, "left_meters=");
for (int i = 0; i < Header_size(this->header, LEFT_HEADER); i++) {
char* name = Header_readMeterName(this->header, i, LEFT_HEADER);
fprintf(fd, "%s ", name);
free(name);
}
fprintf(fd, "\n");
fprintf(fd, "left_meter_modes=");
for (int i = 0; i < Header_size(this->header, LEFT_HEADER); i++)
fprintf(fd, "%d ", Header_readMeterMode(this->header, i, LEFT_HEADER));
fprintf(fd, "\n");
fprintf(fd, "right_meters=");
for (int i = 0; i < Header_size(this->header, RIGHT_HEADER); i++) {
char* name = Header_readMeterName(this->header, i, RIGHT_HEADER);
fprintf(fd, "%s ", name);
free(name);
}
fprintf(fd, "\n");
fprintf(fd, "right_meter_modes=");
for (int i = 0; i < Header_size(this->header, RIGHT_HEADER); i++)
fprintf(fd, "%d ", Header_readMeterMode(this->header, i, RIGHT_HEADER));
fclose(fd);
return true;
} }

View File

@ -4,73 +4,36 @@
#define HEADER_Settings #define HEADER_Settings
/* /*
htop - Settings.h htop - Settings.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "String.h"
#include "ProcessList.h"
#include "Header.h"
#include "debug.h"
#define DEFAULT_DELAY 15 #define DEFAULT_DELAY 15
#include "Process.h"
#include <stdbool.h>
typedef struct {
int len;
char** names;
int* modes;
} MeterColumnSettings;
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* userSettings;
ProcessList* pl;
MeterColumnSettings columns[2]; Header* header;
ProcessField* fields;
int flags;
int colorScheme; int colorScheme;
int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool showCPUUsage;
bool showCPUFrequency;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
bool showThreadNames;
bool hideKernelThreads;
bool hideUserlandThreads;
bool highlightBaseName;
bool highlightMegabytes;
bool highlightThreads;
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
bool enableMouse;
bool vimMode;
#ifdef HAVE_LIBHWLOC
bool topologyAffinity;
#endif
bool changed; bool changed;
int delay;
} Settings; } Settings;
#ifndef Settings_cpuId
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromZero ? (cpu) : (cpu)+1)
#endif
Settings* Settings_new(ProcessList* pl, Header* header);
extern void Settings_delete(Settings* this); void Settings_delete(Settings* this);
extern bool Settings_write(Settings* this); bool Settings_read(Settings* this, char* fileName);
extern Settings* Settings_new(int cpuCount); bool Settings_write(Settings* this);
extern void Settings_invertSortOrder(Settings* this);
#endif #endif

101
SignalItem.c Normal file
View File

@ -0,0 +1,101 @@
/*
htop - SignalItem.c
(C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "SignalItem.h"
#include "String.h"
#include "Object.h"
#include "RichString.h"
#include <string.h>
#include "debug.h"
#define SIGNAL_COUNT 34
/*{
typedef struct Signal_ {
Object super;
char* name;
int number;
} Signal;
}*/
#ifdef DEBUG
char* SIGNAL_CLASS = "Signal";
#else
#define SIGNAL_CLASS NULL
#endif
Signal* Signal_new(char* name, int number) {
Signal* this = malloc(sizeof(Signal));
Object_setClass(this, SIGNAL_CLASS);
((Object*)this)->display = Signal_display;
((Object*)this)->delete = Signal_delete;
this->name = name;
this->number = number;
return this;
}
void Signal_delete(Object* cast) {
Signal* this = (Signal*)cast;
assert (this != NULL);
// names are string constants, so we're not deleting them.
free(this);
}
void Signal_display(Object* cast, RichString* out) {
Signal* this = (Signal*)cast;
assert (this != NULL);
char buffer[31];
snprintf(buffer, 30, "%2d %s", this->number, this->name);
RichString_write(out, CRT_colors[DEFAULT_COLOR], buffer);
}
int Signal_getSignalCount() {
return SIGNAL_COUNT;
}
Signal** Signal_getSignalTable() {
Signal** signals = malloc(sizeof(Signal*) * SIGNAL_COUNT);
signals[0] = Signal_new("Cancel", 0);
signals[1] = Signal_new("SIGHUP", 1);
signals[2] = Signal_new("SIGINT", 2);
signals[3] = Signal_new("SIGQUIT", 3);
signals[4] = Signal_new("SIGILL", 4);
signals[5] = Signal_new("SIGTRAP", 5);
signals[6] = Signal_new("SIGABRT", 6);
signals[7] = Signal_new("SIGIOT", 6);
signals[8] = Signal_new("SIGBUS", 7);
signals[9] = Signal_new("SIGFPE", 8);
signals[10] = Signal_new("SIGKILL", 9);
signals[11] = Signal_new("SIGUSR1", 10);
signals[12] = Signal_new("SIGSEGV", 11);
signals[13] = Signal_new("SIGUSR2", 12);
signals[14] = Signal_new("SIGPIPE", 13);
signals[15] = Signal_new("SIGALRM", 14);
signals[16] = Signal_new("SIGTERM", 15);
signals[17] = Signal_new("SIGSTKFLT", 16);
signals[18] = Signal_new("SIGCHLD", 17);
signals[19] = Signal_new("SIGCONT", 18);
signals[20] = Signal_new("SIGSTOP", 19);
signals[21] = Signal_new("SIGTSTP", 20);
signals[22] = Signal_new("SIGTTIN", 21);
signals[23] = Signal_new("SIGTTOU", 22);
signals[24] = Signal_new("SIGURG", 23);
signals[25] = Signal_new("SIGXCPU", 24);
signals[26] = Signal_new("SIGXFSZ", 25);
signals[27] = Signal_new("SIGVTALRM", 26);
signals[28] = Signal_new("SIGPROF", 27);
signals[29] = Signal_new("SIGWINCH", 28);
signals[30] = Signal_new("SIGIO", 29);
signals[31] = Signal_new("SIGPOLL", 29);
signals[32] = Signal_new("SIGPWR", 30);
signals[33] = Signal_new("SIGSYS", 31);
return signals;
}

45
SignalItem.h Normal file
View File

@ -0,0 +1,45 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_SignalItem
#define HEADER_SignalItem
/*
htop - SignalItem.h
(C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "String.h"
#include "Object.h"
#include "RichString.h"
#include <string.h>
#include "debug.h"
#define SIGNAL_COUNT 34
typedef struct Signal_ {
Object super;
char* name;
int number;
} Signal;
#ifdef DEBUG
extern char* SIGNAL_CLASS;
#else
#define SIGNAL_CLASS NULL
#endif
Signal* Signal_new(char* name, int number);
void Signal_delete(Object* cast);
void Signal_display(Object* cast, RichString* out);
int Signal_getSignalCount();
Signal** Signal_getSignalTable();
#endif

View File

@ -1,58 +1,77 @@
/*
htop - SignalsPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
#include "Platform.h" #include "Panel.h"
#include "SignalItem.h"
#include "ListItem.h"
#include "RichString.h" #include "RichString.h"
#include <stdlib.h> #include "debug.h"
#include <assert.h> #include <assert.h>
#include <signal.h>
#include <ctype.h> #include <ctype.h>
/*{ /*{
typedef struct SignalItem_ { typedef struct SignalsPanel_ {
const char* name; Panel super;
int number;
} SignalItem; int state;
Signal** signals;
} SignalsPanel;
}*/ }*/
Panel* SignalsPanel_new() { SignalsPanel* SignalsPanel_new(int x, int y, int w, int h) {
Panel* this = Panel_new(1, 1, 1, 1, true, Class(ListItem), FunctionBar_newEnterEsc("Send ", "Cancel ")); SignalsPanel* this = (SignalsPanel*) malloc(sizeof(SignalsPanel));
const int defaultSignal = SIGTERM; Panel* super = (Panel*) this;
int defaultPosition = 15; Panel_init(super, x, y, w, h, SIGNAL_CLASS, true);
unsigned int i; ((Object*)this)->delete = SignalsPanel_delete;
for (i = 0; i < Platform_numberOfSignals; i++) {
Panel_set(this, i, (Object*) ListItem_new(Platform_signals[i].name, Platform_signals[i].number)); this->signals = Signal_getSignalTable();
// signal 15 is not always the 15th signal in the table super->eventHandler = SignalsPanel_EventHandler;
if (Platform_signals[i].number == defaultSignal) { int sigCount = Signal_getSignalCount();
defaultPosition = i; for(int i = 0; i < sigCount; i++)
} Panel_set(super, i, (Object*) this->signals[i]);
} SignalsPanel_reset(this);
#if (defined(SIGRTMIN) && defined(SIGRTMAX))
if (SIGRTMAX - SIGRTMIN <= 100) {
static char buf[16];
for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) {
int n = sig - SIGRTMIN;
xSnprintf(buf, 16, "%2d SIGRTMIN%-+3d", sig, n);
if (n == 0) {
buf[11] = '\0';
}
Panel_set(this, i, (Object*) ListItem_new(buf, sig));
}
}
#endif
Panel_setHeader(this, "Send signal:");
Panel_setSelected(this, defaultPosition);
return this; return this;
} }
void SignalsPanel_delete(Object* object) {
Panel* super = (Panel*) object;
SignalsPanel* this = (SignalsPanel*) object;
Panel_done(super);
free(this->signals);
free(this);
}
void SignalsPanel_reset(SignalsPanel* this) {
Panel* super = (Panel*) this;
Panel_setHeader(super, "Send signal:");
Panel_setSelected(super, 16); // 16th item is SIGTERM
this->state = 0;
}
HandlerResult SignalsPanel_EventHandler(Panel* super, int ch) {
SignalsPanel* this = (SignalsPanel*) super;
int size = Panel_getSize(super);
if (ch <= 255 && isdigit(ch)) {
int signal = ch-48 + this->state;
for (int i = 0; i < size; i++)
if (((Signal*) Panel_get(super, i))->number == signal) {
Panel_setSelected(super, i);
break;
}
this->state = signal * 10;
if (this->state > 100)
this->state = 0;
return HANDLED;
} else {
this->state = 0;
}
if (ch == 13) {
return BREAK_LOOP;
}
return IGNORED;
}

View File

@ -2,20 +2,31 @@
#ifndef HEADER_SignalsPanel #ifndef HEADER_SignalsPanel
#define HEADER_SignalsPanel #define HEADER_SignalsPanel
/*
htop - SignalsPanel.h #include "Panel.h"
(C) 2004-2011 Hisham H. Muhammad #include "SignalItem.h"
Released under the GNU GPL, see the COPYING file #include "RichString.h"
in the source distribution for its full text.
*/ #include "debug.h"
#include <assert.h>
#include <ctype.h>
typedef struct SignalItem_ { typedef struct SignalsPanel_ {
const char* name; Panel super;
int number;
} SignalItem; int state;
Signal** signals;
} SignalsPanel;
extern Panel* SignalsPanel_new(); SignalsPanel* SignalsPanel_new(int x, int y, int w, int h);
void SignalsPanel_delete(Object* object);
void SignalsPanel_reset(SignalsPanel* this);
HandlerResult SignalsPanel_EventHandler(Panel* super, int ch);
#endif #endif

160
String.c Normal file
View File

@ -0,0 +1,160 @@
/*
htop
(C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define _GNU_SOURCE
#include "String.h"
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include "debug.h"
/*{
#define String_startsWith(s, match) (strstr((s), (match)) == (s))
}*/
inline void String_delete(char* s) {
free(s);
}
inline char* String_copy(char* orig) {
return strdup(orig);
}
char* String_cat(char* s1, char* s2) {
int l1 = strlen(s1);
int l2 = strlen(s2);
char* out = malloc(l1 + l2 + 1);
strncpy(out, s1, l1);
strncpy(out+l1, s2, l2+1);
return out;
}
char* String_trim(char* in) {
while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') {
in++;
}
int len = strlen(in);
while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) {
len--;
}
char* out = malloc(len+1);
strncpy(out, in, len);
out[len] = '\0';
return out;
}
char* String_copyUpTo(char* orig, char upTo) {
int len;
int origLen = strlen(orig);
char* at = strchr(orig, upTo);
if (at != NULL)
len = at - orig;
else
len = origLen;
char* copy = (char*) malloc(len+1);
strncpy(copy, orig, len);
copy[len] = '\0';
return copy;
}
char* String_sub(char* orig, int from, int to) {
char* copy;
int len;
len = strlen(orig);
if (to > len)
to = len;
if (from > len)
to = len;
len = to-from+1;
copy = (char*) malloc(len+1);
strncpy(copy, orig+from, len);
copy[len] = '\0';
return copy;
}
void String_println(char* s) {
printf("%s\n", s);
}
void String_print(char* s) {
printf("%s", s);
}
void String_printInt(int i) {
printf("%i", i);
}
void String_printPointer(void* p) {
printf("%p", p);
}
inline int String_eq(char* s1, char* s2) {
if (s1 == NULL || s2 == NULL) {
if (s1 == NULL && s2 == NULL)
return 1;
else
return 0;
}
return (strcmp(s1, s2) == 0);
}
char** String_split(char* s, char sep) {
const int rate = 10;
char** out = (char**) malloc(sizeof(char*) * rate);
int ctr = 0;
int blocks = rate;
char* where;
while ((where = strchr(s, sep)) != NULL) {
int size = where - s;
char* token = (char*) malloc(size + 1);
strncpy(token, s, size);
token[size] = '\0';
out[ctr] = token;
ctr++;
if (ctr == blocks) {
blocks += rate;
out = (char**) realloc(out, sizeof(char*) * blocks);
}
s += size + 1;
}
if (s[0] != '\0') {
int size = strlen(s);
char* token = (char*) malloc(size + 1);
strncpy(token, s, size + 1);
out[ctr] = token;
ctr++;
}
out = realloc(out, sizeof(char*) * (ctr + 1));
out[ctr] = NULL;
return out;
}
void String_freeArray(char** s) {
for (int i = 0; s[i] != NULL; i++) {
free(s[i]);
}
free(s);
}
int String_startsWith_i(char* s, char* match) {
return (strncasecmp(s, match, strlen(match)) == 0);
}
int String_contains_i(char* s, char* match) {
int lens = strlen(s);
int lenmatch = strlen(match);
for (int i = 0; i < lens-lenmatch; i++) {
if (strncasecmp(s, match, strlen(match)) == 0)
return 1;
s++;
}
return 0;
}

52
String.h Normal file
View File

@ -0,0 +1,52 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_String
#define HEADER_String
/*
htop
(C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define _GNU_SOURCE
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include "debug.h"
#define String_startsWith(s, match) (strstr((s), (match)) == (s))
inline void String_delete(char* s);
inline char* String_copy(char* orig);
char* String_cat(char* s1, char* s2);
char* String_trim(char* in);
char* String_copyUpTo(char* orig, char upTo);
char* String_sub(char* orig, int from, int to);
void String_println(char* s);
void String_print(char* s);
void String_printInt(int i);
void String_printPointer(void* p);
inline int String_eq(char* s1, char* s2);
char** String_split(char* s, char sep);
void String_freeArray(char** s);
int String_startsWith_i(char* s, char* match);
int String_contains_i(char* s, char* match);
#endif

View File

@ -1,154 +0,0 @@
/*
htop - StringUtils.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "StringUtils.h"
#include "XAlloc.h"
#include "config.h"
#include <string.h>
#include <strings.h>
#include <stdlib.h>
/*{
#include <stdio.h>
#define String_startsWith(s, match) (strncmp((s),(match),strlen(match)) == 0)
#define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL)
}*/
/*
* String_startsWith gives better performance if strlen(match) can be computed
* at compile time (e.g. when they are immutable string literals). :)
*/
char* String_cat(const char* s1, const char* s2) {
int l1 = strlen(s1);
int l2 = strlen(s2);
char* out = xMalloc(l1 + l2 + 1);
memcpy(out, s1, l1);
memcpy(out+l1, s2, l2+1);
out[l1 + l2] = '\0';
return out;
}
char* String_trim(const char* in) {
while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') {
in++;
}
int len = strlen(in);
while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) {
len--;
}
char* out = xMalloc(len+1);
strncpy(out, in, len);
out[len] = '\0';
return out;
}
inline int String_eq(const char* s1, const char* s2) {
if (s1 == NULL || s2 == NULL) {
if (s1 == NULL && s2 == NULL)
return 1;
else
return 0;
}
return (strcmp(s1, s2) == 0);
}
char** String_split(const char* s, char sep, int* n) {
*n = 0;
const int rate = 10;
char** out = xCalloc(rate, sizeof(char*));
int ctr = 0;
int blocks = rate;
char* where;
while ((where = strchr(s, sep)) != NULL) {
int size = where - s;
char* token = xMalloc(size + 1);
strncpy(token, s, size);
token[size] = '\0';
out[ctr] = token;
ctr++;
if (ctr == blocks) {
blocks += rate;
out = (char**) xRealloc(out, sizeof(char*) * blocks);
}
s += size + 1;
}
if (s[0] != '\0') {
out[ctr] = xStrdup(s);
ctr++;
}
out = xRealloc(out, sizeof(char*) * (ctr + 1));
out[ctr] = NULL;
*n = ctr;
return out;
}
void String_freeArray(char** s) {
if (!s) {
return;
}
for (int i = 0; s[i] != NULL; i++) {
free(s[i]);
}
free(s);
}
char* String_getToken(const char* line, const unsigned short int numMatch) {
const unsigned short int len = strlen(line);
char inWord = 0;
unsigned short int count = 0;
char match[50];
unsigned short int foundCount = 0;
for (unsigned short int i = 0; i < len; i++) {
char lastState = inWord;
inWord = line[i] == ' ' ? 0:1;
if (lastState == 0 && inWord == 1)
count++;
if(inWord == 1){
if (count == numMatch && line[i] != ' ' && line[i] != '\0' && line[i] != '\n' && line[i] != (char)EOF) {
match[foundCount] = line[i];
foundCount++;
}
}
}
match[foundCount] = '\0';
return((char*)xStrdup(match));
}
char* String_readLine(FILE* fd) {
const int step = 1024;
int bufSize = step;
char* buffer = xMalloc(step + 1);
char* at = buffer;
for (;;) {
char* ok = fgets(at, step + 1, fd);
if (!ok) {
free(buffer);
return NULL;
}
char* newLine = strrchr(at, '\n');
if (newLine) {
*newLine = '\0';
return buffer;
} else {
if (feof(fd)) {
return buffer;
}
}
bufSize += step;
buffer = xRealloc(buffer, bufSize + 1);
at = buffer + bufSize - step;
}
}

View File

@ -1,36 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_StringUtils
#define HEADER_StringUtils
/*
htop - StringUtils.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include <stdio.h>
#define String_startsWith(s, match) (strncmp((s),(match),strlen(match)) == 0)
#define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL)
/*
* String_startsWith gives better performance if strlen(match) can be computed
* at compile time (e.g. when they are immutable string literals). :)
*/
extern char* String_cat(const char* s1, const char* s2);
extern char* String_trim(const char* in);
extern int String_eq(const char* s1, const char* s2);
extern char** String_split(const char* s, char sep, int* n);
extern void String_freeArray(char** s);
extern char* String_getToken(const char* line, const unsigned short int numMatch);
extern char* String_readLine(FILE* fd);
#endif

View File

@ -1,65 +1,56 @@
/* /*
htop - SwapMeter.c htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "SwapMeter.h" #include "SwapMeter.h"
#include "Meter.h"
#include "CRT.h" #include "ProcessList.h"
#include "Platform.h"
#include <stdlib.h> #include <stdlib.h>
#include <curses.h>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <sys/param.h> #include <sys/param.h>
#include <assert.h>
/*{ #include "debug.h"
#include "Meter.h" #include <assert.h>
}*/
int SwapMeter_attributes[] = { int SwapMeter_attributes[] = {
SWAP SWAP
}; };
static void SwapMeter_updateValues(Meter* this, char* buffer, int size) { MeterType SwapMeter = {
int written; .setValues = SwapMeter_setValues,
Platform_setSwapValues(this);
written = Meter_humanUnit(buffer, this->values[0], size);
buffer += written;
if ((size -= written) > 0) {
*buffer++ = '/';
size--;
Meter_humanUnit(buffer, this->total, size);
}
}
static void SwapMeter_display(Object* cast, RichString* out) {
char buffer[50];
Meter* this = (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);
}
MeterClass SwapMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = SwapMeter_display, .display = SwapMeter_display,
}, .mode = BAR_METERMODE,
.updateValues = SwapMeter_updateValues, .items = 1,
.defaultMode = BAR_METERMODE,
.maxItems = 1,
.total = 100.0, .total = 100.0,
.attributes = SwapMeter_attributes, .attributes = SwapMeter_attributes,
.name = "Swap", .name = "Swap",
.uiName = "Swap", .uiName = "Swap",
.caption = "Swp" .caption = "Swp"
}; };
void SwapMeter_setValues(Meter* this, char* buffer, int len) {
long int usedSwap = this->pl->usedSwap;
this->total = this->pl->totalSwap;
this->values[0] = usedSwap;
snprintf(buffer, len, "%ld/%ldMB", (long int) usedSwap / 1024, (long int) this->total / 1024);
}
void SwapMeter_display(Object* cast, RichString* out) {
char buffer[50];
Meter* this = (Meter*)cast;
long int swap = (long int) this->values[0];
RichString_init(out);
RichString_append(out, CRT_colors[METER_TEXT], ":");
sprintf(buffer, "%ldM ", (long int) this->total / 1024);
RichString_append(out, CRT_colors[METER_VALUE], buffer);
sprintf(buffer, "%ldk", swap);
RichString_append(out, CRT_colors[METER_TEXT], "used:");
RichString_append(out, CRT_colors[METER_VALUE], buffer);
}

View File

@ -3,16 +3,31 @@
#ifndef HEADER_SwapMeter #ifndef HEADER_SwapMeter
#define HEADER_SwapMeter #define HEADER_SwapMeter
/* /*
htop - SwapMeter.h htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.h"
#include "ProcessList.h"
#include <stdlib.h>
#include <curses.h>
#include <string.h>
#include <math.h>
#include <sys/param.h>
#include "debug.h"
#include <assert.h>
extern int SwapMeter_attributes[]; extern int SwapMeter_attributes[];
extern MeterClass SwapMeter_class; extern MeterType SwapMeter;
void SwapMeter_setValues(Meter* this, char* buffer, int len);
void SwapMeter_display(Object* cast, RichString* out);
#endif #endif

154
TESTPLAN
View File

@ -1,154 +0,0 @@
Main screen:
For all views, all modes:
Mouse click header - nothing happens.
Mouse click on ProcessList title bar - exit Tree view, update FunctionBar, title bar updates, sort by clicked field.
*** FAILING: wrong FB update depending on mode; does not change sort in wip branch
click on same entry - invert sort.
click on another entry - sort another field.
Mouse click on a process - select that process.
for each entry in FunctionBar:
Mouse click entry - perform action of associated key.
In Normal mode, Sorted view:
<+> or <-> - do nothing.
<F6> - enter SortBy screen.
In Normal mode, Tree view:
select process - update F6 in FunctionBar if subtree is collapsed or expanded.
<F6>, <+> or <-> - expand/collapse subtree.
In Normal mode, either Sorted or Tree view:
<F3>, </> - activate Search mode.
<F4>, <\> - activate Filter mode.
<F7>, <]> - as root only, decrease process NICE value.
<F8>, <[> - increase process NICE value.
<a> - enter Affinity screen.
<b> - do nothing.
<c> - select process and all its children.
<d>, <e>, <f>, <g> - do nothing.
<F1>, <h>, <?> - enter Help screen.
<i> - on Linux, enter IOPriority screen.
<j> - do nothing.
<F9>, <k> - enter Kill screen.
<l> - enter LSOF screen.
<m>, <n>, <o>, <p> - do nothing.
<F10>, <q> - quit program.
<r> - do nothing.
<s> - enter STrace screen.
<F5>, <t> - toggle between Tree and Sorted view, update F5 in FunctionBar, follow process
<u> - enter User screen.
<v>, <w>, <x>, <y>, <z> - do nothing.
<A>, <B> - do nothing.
<F2>, <C>, <S> - enter Setup screen.
<D>, <E> - do nothing.
<F> - follow process.
<G> - do nothing.
<H> - toggle show/hide userland threads.
<I> - invert sort order.
<J> - do nothing.
<K> - toggle show/hide kernel threads.
<L> - do nothing.
<M> - enter Sorted view, update function bar, sort by MEM%.
<N>, <O> - do nothing.
<P> - enter Sorted view, update function bar, sort by CPU%.
<Q>, <R> - do nothing.
<T> - enter Sorted view, update function bar, sort by TIME.
<U> - untag all processes.
<V>, <W>, <X>, <Y>, <Z> - do nothing.
<<>, <>>, <,>, <.> - enter SortBy screen.
space - tag current process, move down cursor.
numbers - incremental PID search.
In Search mode:
TODO
In Filter mode:
TODO
Setup screen:
TODO
SortBy screen:
TODO
User screen:
TODO
Kill screen:
TODO
Affinity screen:
TODO
Help screen:
any key - back to Main screen.
IOPriority screen:
TODO
STrace screen:
TODO
LSOF screen:
TODO

18
TODO Normal file
View File

@ -0,0 +1,18 @@
BUGS:
* tagged files are cleared if 'kill' fails
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=375219
* add swap column for swap usage in MB
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=365038
FEATURES:
* expand/collapse on tree
* handle saving of .htoprc more elegantly
* make bars display refresh independent from list refresh
* auto-calibrate delay
* add some more 'top' features
* add more command-line parameters
* show 'process view'
* make keybindings configurable, blah blah blah...

View File

@ -1,83 +1,49 @@
/* /*
htop - TasksMeter.c htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "TasksMeter.h" #include "TasksMeter.h"
#include "Meter.h"
#include "ProcessList.h"
#include "Platform.h"
#include "CRT.h" #include "CRT.h"
/*{ #include "debug.h"
#include "Meter.h"
}*/
int TasksMeter_attributes[] = { int TasksMeter_attributes[] = {
CPU_SYSTEM, PROCESS_THREAD, PROCESS, TASKS_RUNNING TASKS_RUNNING
}; };
static void TasksMeter_updateValues(Meter* this, char* buffer, int len) { MeterType TasksMeter = {
ProcessList* pl = this->pl; .setValues = TasksMeter_setValues,
this->values[0] = pl->kernelThreads;
this->values[1] = pl->userlandThreads;
this->values[2] = pl->totalTasks - pl->kernelThreads - pl->userlandThreads;
this->values[3] = MIN(pl->runningTasks, pl->cpuCount);
if (pl->totalTasks > this->total) {
this->total = pl->totalTasks;
}
if (this->pl->settings->hideKernelThreads) {
this->values[0] = 0;
}
xSnprintf(buffer, len, "%d/%d", (int) this->values[3], (int) this->total);
}
static void TasksMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
Settings* settings = this->pl->settings;
char buffer[20];
int processes = (int) this->values[2];
xSnprintf(buffer, sizeof(buffer), "%d", processes);
RichString_write(out, CRT_colors[METER_VALUE], buffer);
int threadValueColor = CRT_colors[METER_VALUE];
int threadCaptionColor = CRT_colors[METER_TEXT];
if (settings->highlightThreads) {
threadValueColor = CRT_colors[PROCESS_THREAD_BASENAME];
threadCaptionColor = CRT_colors[PROCESS_THREAD];
}
if (!settings->hideUserlandThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", ");
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]);
RichString_append(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " thr");
}
if (!settings->hideKernelThreads) {
RichString_append(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_append(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");
}
MeterClass TasksMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = TasksMeter_display, .display = TasksMeter_display,
}, .mode = TEXT_METERMODE,
.updateValues = TasksMeter_updateValues, .items = 1,
.defaultMode = TEXT_METERMODE,
.maxItems = 4,
.total = 100.0, .total = 100.0,
.attributes = TasksMeter_attributes, .attributes = TasksMeter_attributes,
.name = "Tasks", .name = "Tasks",
.uiName = "Task counter", .uiName = "Task counter",
.caption = "Tasks: " .caption = "Tasks: "
}; };
void TasksMeter_setValues(Meter* this, char* buffer, int len) {
this->total = this->pl->totalTasks;
this->values[0] = this->pl->runningTasks;
snprintf(buffer, len, "%d/%d", (int) this->values[0], (int) this->total);
}
void TasksMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
RichString_init(out);
char buffer[20];
sprintf(buffer, "%d", (int)this->total);
RichString_append(out, CRT_colors[METER_VALUE], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " total, ");
sprintf(buffer, "%d", (int)this->values[0]);
RichString_append(out, CRT_colors[TASKS_RUNNING], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " running");
}

View File

@ -3,16 +3,26 @@
#ifndef HEADER_TasksMeter #ifndef HEADER_TasksMeter
#define HEADER_TasksMeter #define HEADER_TasksMeter
/* /*
htop - TasksMeter.h htop
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.h"
#include "ProcessList.h"
#include "CRT.h"
#include "debug.h"
extern int TasksMeter_attributes[]; extern int TasksMeter_attributes[];
extern MeterClass TasksMeter_class; extern MeterType TasksMeter;
void TasksMeter_setValues(Meter* this, char* buffer, int len);
void TasksMeter_display(Object* cast, RichString* out);
#endif #endif

View File

@ -5,170 +5,164 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "TraceScreen.h" #define _GNU_SOURCE
#include "CRT.h"
#include "InfoScreen.h"
#include "ProcessList.h"
#include "ListItem.h"
#include "IncSet.h"
#include "StringUtils.h"
#include "FunctionBar.h"
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <signal.h>
#include "TraceScreen.h"
#include "ProcessList.h"
#include "Process.h"
#include "ListItem.h"
#include "Panel.h"
#include "FunctionBar.h"
/*{ /*{
#include "InfoScreen.h"
typedef struct TraceScreen_ { typedef struct TraceScreen_ {
InfoScreen super; Process* process;
Panel* display;
FunctionBar* bar;
bool tracing; bool tracing;
int fdpair[2];
int child;
FILE* strace;
int fd_strace;
bool contLine;
bool follow;
} TraceScreen; } TraceScreen;
}*/ }*/
static const char* const TraceScreenFunctions[] = {"Search ", "Filter ", "AutoScroll ", "Stop Tracing ", "Done ", NULL}; static char* tbFunctions[3] = {"AutoScroll ", "Stop Tracing ", "Done "};
static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"}; static char* tbKeys[3] = {"F4", "F5", "Esc"};
static int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27}; static int tbEvents[3] = {KEY_F(4), KEY_F(5), 27};
InfoScreenClass TraceScreen_class = {
.super = {
.extends = Class(Object),
.delete = TraceScreen_delete
},
.draw = TraceScreen_draw,
.onErr = TraceScreen_updateTrace,
.onKey = TraceScreen_onKey,
};
TraceScreen* TraceScreen_new(Process* process) { TraceScreen* TraceScreen_new(Process* process) {
TraceScreen* this = xMalloc(sizeof(TraceScreen)); TraceScreen* this = (TraceScreen*) malloc(sizeof(TraceScreen));
Object_setClass(this, Class(TraceScreen)); this->process = process;
this->display = Panel_new(0, 1, COLS, LINES-2, LISTITEM_CLASS, true, ListItem_compare);
this->bar = FunctionBar_new(3, tbFunctions, tbKeys, tbEvents);
this->tracing = true; this->tracing = true;
this->contLine = false; return this;
this->follow = false;
FunctionBar* fuBar = FunctionBar_new(TraceScreenFunctions, TraceScreenKeys, TraceScreenEvents);
CRT_disableDelay();
return (TraceScreen*) InfoScreen_init(&this->super, process, fuBar, LINES-2, "");
} }
void TraceScreen_delete(Object* cast) { void TraceScreen_delete(TraceScreen* this) {
TraceScreen* this = (TraceScreen*) cast; Panel_delete((Object*)this->display);
if (this->child > 0) { FunctionBar_delete((Object*)this->bar);
kill(this->child, SIGTERM); free(this);
waitpid(this->child, NULL, 0);
fclose(this->strace);
}
CRT_enableDelay();
free(InfoScreen_done((InfoScreen*)cast));
} }
void TraceScreen_draw(InfoScreen* this) { void TraceScreen_draw(TraceScreen* this) {
attrset(CRT_colors[PANEL_HEADER_FOCUS]); attrset(CRT_colors[PANEL_HEADER_FOCUS]);
mvhline(0, 0, ' ', COLS); mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm); mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm);
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(this->inc); FunctionBar_draw(this->bar, NULL);
} }
bool TraceScreen_forkTracer(TraceScreen* this) { void TraceScreen_run(TraceScreen* this) {
// if (this->process->pid == getpid()) return;
char buffer[1001]; char buffer[1001];
int error = pipe(this->fdpair); int fdpair[2];
if (error == -1) return false; int err = pipe(fdpair);
this->child = fork(); if (err == -1) return;
if (this->child == -1) return false; int child = fork();
if (this->child == 0) { if (child == -1) return;
CRT_dropPrivileges(); if (child == 0) {
dup2(this->fdpair[1], STDERR_FILENO); dup2(fdpair[1], STDERR_FILENO);
int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK); fcntl(fdpair[1], F_SETFL, O_NONBLOCK);
if (ok != -1) { sprintf(buffer, "%d", this->process->pid);
xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid); execlp("strace", "strace", "-p", buffer, NULL);
execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, NULL);
}
const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH."; const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH.";
ssize_t written = write(this->fdpair[1], message, strlen(message)); write(fdpair[1], message, strlen(message));
(void) written;
exit(1); exit(1);
} }
int ok = fcntl(this->fdpair[0], F_SETFL, O_NONBLOCK); fcntl(fdpair[0], F_SETFL, O_NONBLOCK);
if (ok == -1) return false; FILE* strace = fdopen(fdpair[0], "r");
this->strace = fdopen(this->fdpair[0], "r"); Panel* panel = this->display;
this->fd_strace = fileno(this->strace); int fd_strace = fileno(strace);
return true; TraceScreen_draw(this);
} CRT_disableDelay();
bool contLine = false;
void TraceScreen_updateTrace(InfoScreen* super) { bool follow = false;
TraceScreen* this = (TraceScreen*) super; bool looping = true;
char buffer[1001]; while (looping) {
fd_set fds; fd_set fds;
FD_ZERO(&fds); FD_ZERO(&fds);
// FD_SET(STDIN_FILENO, &fds); FD_SET(fd_strace, &fds);
FD_SET(this->fd_strace, &fds);
struct timeval tv; struct timeval tv;
tv.tv_sec = 0; tv.tv_usec = 500; tv.tv_sec = 0; tv.tv_usec = 500;
int ready = select(this->fd_strace+1, &fds, NULL, NULL, &tv); int ready = select(fd_strace+1, &fds, NULL, NULL, &tv);
int nread = 0; int nread = 0;
if (ready > 0 && FD_ISSET(this->fd_strace, &fds)) if (ready > 0)
nread = fread(buffer, 1, 1000, this->strace); nread = fread(buffer, 1, 1000, strace);
if (nread && this->tracing) { if (nread && this->tracing) {
char* line = buffer; char* line = buffer;
buffer[nread] = '\0'; buffer[nread] = '\0';
for (int i = 0; i < nread; i++) { for (int i = 0; i < nread; i++) {
if (buffer[i] == '\n') { if (buffer[i] == '\n') {
buffer[i] = '\0'; buffer[i] = '\0';
if (this->contLine) { if (contLine) {
InfoScreen_appendLine(&this->super, line); ListItem_append((ListItem*)Panel_get(panel,
this->contLine = false; Panel_getSize(panel)-1), line);
contLine = false;
} else { } else {
InfoScreen_addLine(&this->super, line); Panel_add(panel, (Object*) ListItem_new(line, 0));
} }
line = buffer+i+1; line = buffer+i+1;
} }
} }
if (line < buffer+nread) { if (line < buffer+nread) {
InfoScreen_addLine(&this->super, line); Panel_add(panel, (Object*) ListItem_new(line, 0));
buffer[nread] = '\0'; buffer[nread] = '\0';
this->contLine = true; contLine = true;
} }
if (this->follow) if (follow)
Panel_setSelected(this->super.display, Panel_size(this->super.display)-1); Panel_setSelected(panel, Panel_getSize(panel)-1);
Panel_draw(panel, true);
}
int ch = getch();
if (ch == KEY_MOUSE) {
MEVENT mevent;
int ok = getmouse(&mevent);
if (ok == OK)
if (mevent.y >= panel->y && mevent.y < LINES - 1) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV);
follow = false;
ch = 0;
} if (mevent.y == LINES - 1)
ch = FunctionBar_synthesizeEvent(this->bar, mevent.x);
} }
}
bool TraceScreen_onKey(InfoScreen* super, int ch) {
TraceScreen* this = (TraceScreen*) super;
switch(ch) { switch(ch) {
case 'f': case ERR:
case KEY_F(8): continue;
this->follow = !(this->follow); case KEY_F(5):
if (this->follow)
Panel_setSelected(super->display, Panel_size(super->display)-1);
return true;
case 't':
case KEY_F(9):
this->tracing = !this->tracing; this->tracing = !this->tracing;
FunctionBar_setLabel(super->display->defaultBar, KEY_F(9), this->tracing?"Stop Tracing ":"Resume Tracing "); FunctionBar_setLabel(this->bar, KEY_F(5), this->tracing?"Stop Tracing ":"Resume Tracing ");
InfoScreen_draw(this); TraceScreen_draw(this);
return true; break;
case 'f':
case KEY_F(4):
follow = !follow;
if (follow)
Panel_setSelected(panel, Panel_getSize(panel)-1);
break;
case 'q':
case 27:
looping = false;
break;
case KEY_RESIZE:
Panel_resize(panel, COLS, LINES-2);
TraceScreen_draw(this);
break;
default:
follow = false;
Panel_onKey(panel, ch);
} }
this->follow = false; Panel_draw(panel, true);
return false; }
kill(child, SIGTERM);
waitpid(child, NULL, 0);
fclose(strace);
} }

View File

@ -9,32 +9,36 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "InfoScreen.h" #define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "ProcessList.h"
#include "Process.h"
#include "ListItem.h"
#include "Panel.h"
#include "FunctionBar.h"
typedef struct TraceScreen_ { typedef struct TraceScreen_ {
InfoScreen super; Process* process;
Panel* display;
FunctionBar* bar;
bool tracing; bool tracing;
int fdpair[2];
int child;
FILE* strace;
int fd_strace;
bool contLine;
bool follow;
} TraceScreen; } TraceScreen;
extern InfoScreenClass TraceScreen_class; TraceScreen* TraceScreen_new(Process* process);
extern TraceScreen* TraceScreen_new(Process* process); void TraceScreen_delete(TraceScreen* this);
extern void TraceScreen_delete(Object* cast); void TraceScreen_draw(TraceScreen* this);
extern void TraceScreen_draw(InfoScreen* this); void TraceScreen_run(TraceScreen* this);
extern bool TraceScreen_forkTracer(TraceScreen* this);
extern void TraceScreen_updateTrace(InfoScreen* super);
extern bool TraceScreen_onKey(InfoScreen* super, int ch);
#endif #endif

Some files were not shown because too many files have changed in this diff Show More