110 Commits

Author SHA1 Message Date
0b989ee38c Bump version number for 3.0.4 release 2020-12-22 17:39:42 +11:00
3fb0024fd3 Merge branch 'misc' of https://github.com/cgzones/htop into cgzones-misc 2020-12-22 17:30:29 +11:00
dfb9b82607 Resolve clang-analyzer signed/unsigned comparison CI failure 2020-12-22 16:58:17 +11:00
fc7aead36b Merge branch 'harden_makecommandstr' of https://github.com/BenBE/htop into BenBE-harden_makecommandstr 2020-12-22 16:55:11 +11:00
737cd6167a Merge branch 'resize_bar' of https://github.com/cgzones/htop into cgzones-resize_bar 2020-12-22 15:25:08 +11:00
6502b02666 DiD: Ensure string offsets are inside string boundaries 2020-12-21 22:35:38 +01:00
cdfd407e2e Panel_init: initialize selectedLen member 2020-12-21 22:34:50 +01:00
64c05a1ed5 EnvScreen: mark local class functions static 2020-12-21 22:34:50 +01:00
a7612b0b7d TraceScreen: mark local class functions static 2020-12-21 22:34:50 +01:00
3ec8f67ab2 InfoScreen: drop unused member 2020-12-21 22:34:50 +01:00
10c6810bff Avoid NULL dereference on zombie processes
Fixes #361
2020-12-21 22:29:18 +01:00
068561351f Document dynamic bindings and assumed external configuration 2020-12-21 22:15:28 +01:00
9b8b380c32 De-lazy @cgzones :) 2020-12-21 20:40:00 +01:00
a09ad6b8b4 Action: sort key binding assignments
Avoid accidental duplicate usage.
2020-12-21 15:53:42 +01:00
9a86577cf2 DragonFlyBSD update
- move some functions to file scope
- drop unused global variable
2020-12-20 18:32:04 +01:00
8db8b9edac DragonFlyBSD update
- drop unused kinfo includes and link argument
- detect kvm library necessity at configure step
- fix variable typo
2020-12-20 18:22:41 +01:00
4a73e80338 Make remaining number literals use uppercase 2020-12-20 17:17:51 +01:00
5fa1c7040d Minor typo and comment clarification 2020-12-20 17:15:51 +01:00
3f9c63d5c0 MetersPanel: drop color interruption in FunctionBar 2020-12-20 17:02:20 +01:00
358d20687f Use variable-length-array instead of small dynamic allocation 2020-12-20 17:01:50 +01:00
e3862aa67e Rework drawing of FunctionBar
Draw the FunctionBar within Panel_draw instead of manually throughout
the code.
Add an optional PanelClass function drawFunctionBar, to allow specific
panels to override the default FunctionBar_draw call.
Rework the code on color change, to really change all colors (selection
markers and panel headers).

Closes: #402
2020-12-20 17:01:50 +01:00
7e7a53c415 Mark event arrays const 2020-12-20 16:58:37 +01:00
6b100b0cf4 Use upper case numeric literals
See https://rules.sonarsource.com/c/RSPEC-818
2020-12-20 16:58:17 +01:00
6e46fd6f1f BarMeter: rework text padding
In case the text is too long for the bar, try to fit by truncating at a
space character.

E.g.
    [|24.1% 2000Mhz 40°C]
    [24.1% 2000Mhz 40°C]
    [||||24.1% 2000Mhz]
    [|||24.1% 2000Mhz]
    [||24.1% 2000Mhz]
    [|24.1% 2000Mhz]
    [24.1% 2000Mhz]
    [||||   24.1%]
    [||||  24.1%]
    [|||| 24.1%]
    [||||24.1%]
    [|||24.1%]
    [||24.1%]
    [|24.1%]
    [24.1%]
    [24.1]
    [24.]
    [24]
    [2]
2020-12-20 16:55:17 +01:00
22da57d621 CPUMeter: drop minimum width of CPU usage in bar mode
The usage percentage is the first text, no need to set a minimum width.
The BarMeter does already add padding.
2020-12-20 16:55:17 +01:00
c5e31ba4aa Meter: fix artifacts with very tiny width
- The Bar Meter might override the right border
- The TextMeter might wrap-around into the next line
2020-12-20 16:55:17 +01:00
f878f302ca Remove duplicate newline in CRT_fatalError calls 2020-12-19 21:30:39 +01:00
67ccd6b909 Unhardcode tick-to-ms conversion
Division by 100000.0 worked because `sysconf(_SC_CLK_TCK)` happened to be 100.

By unhardcoding:

1) It becomes more clear what this 100000.0 figure comes from.
2) It protects against bugs in the case `sysconf(_SC_CLK_TCK)` ever changes.
2020-12-19 21:30:39 +01:00
f614b8a19f Mark Platform_defaultFields const 2020-12-19 21:13:32 +01:00
c150e4bde9 Enable -Wformat=2
Now that the global format variable Process_pidFormat is gone, enable
the compiler warning -Wformat=2.
2020-12-19 21:13:32 +01:00
9f68c8d341 Merge Process_pidColumns into Process_fields and rework auto-fit for PID-like columns 2020-12-19 21:13:32 +01:00
89473cc9ae Rework enum ProcessField
Use only one enum instead of a global and a platform specific one.
Drop Platform_numberOfFields global variable.
Set known size of Process_fields array
2020-12-19 21:13:32 +01:00
d872e36308 LinuxProcess: drop dead Process columns 2020-12-19 21:13:32 +01:00
77db240b48 Split boilerplate and platform-independent field comparison
This acheives two things:
- Allows for simple tie-breaking if values compare equal (needed to make sorting the tree-view stable)
- Allows for platform-dependent overriding of the sort-order for specific fields

Also fixes a small oversight on DragonFlyBSD when default-sorting.
2020-12-19 16:02:34 +01:00
2327260ee8 Separate tree and list sort orders
Implements the suggestion from https://github.com/htop-dev/htop/issues/399#issuecomment-747861013

Thanks to the refactors from 0bd5c8fb5da and 6393baa74e5, this was really easy
and clean to do.

It maintains the "Tree view always by PID" option in the Settings, which
results in some specific behaviors such as "clicking on the column header to
exit tree view" and "picking a new sort order to exit tree view", for the sake
of the muscle memory of long time htop users. :)
2020-12-19 16:02:34 +01:00
e8c6994f40 Add "Tree view is always sorted by PID" option to mimic htop 2 behavior 2020-12-19 16:02:34 +01:00
3d1703f16f Invert Process_compare resolution so that superclass matches run first
* This removes duplicated code that adjusts the sort direction from every
  OS-specific folder.
* Most fields in a regular htop screen are OS-independent, so trying
  Process_compare first and only falling back to the OS-specific
  compareByKey function if it's an OS-specific field makes sense.
* This will allow us to override the sortKey in a global way without having
  to edit each OS-specific file.
2020-12-19 16:02:34 +01:00
52fa4e7ee4 Fix typo 2020-12-18 23:35:28 +01:00
27b8d81ed2 ProcessList: save scan time in millisecond
The delay is saved in deciseconds, use a bigger resolution to avoid
timing irregularities.
2020-12-18 22:43:21 +01:00
26993d2d2b Support clock_gettime() on OSX El Capitan and earlier 2020-12-18 22:43:21 +01:00
0401df8cbd Update key mapping documentation for sorting 2020-12-18 07:37:23 +01:00
0cb257586a Move macro definitions close to usage 2020-12-16 19:13:56 +01:00
1193c6e349 Use common naming for bare enum types 2020-12-16 19:13:56 +01:00
edd6130be7 MainPanel: use actual KEY_RESIZE instead of KEY_SHUFFLE
KEY_RESIZE (0632) is equal to KEY_SHUFFLE (0x19a)
2020-12-16 19:12:50 +01:00
107e3c8aa5 MainPanel: do not reset hideProcessSelection on KEY_SHUFFLE
KEY_SHUFFLE might get send from time to time, e.g. in a tmux session.
2020-12-15 14:23:09 +01:00
4eeeb63647 LibSensors: fix unversioned libsensors library name 2020-12-15 13:54:32 +01:00
eb36385a6b LibSensors: restore temperature for Raspberry Pi
sensors output:
  cpu_thermal-virtual-0
  Adapter: Virtual device
  temp1:        +58.0 C  (crit = +90.0 C)
2020-12-15 13:46:46 +01:00
79970f05f3 Meter: restore non-wide-character build
Use mbstowcs() only with wide ncurses support.

Closes: #401
2020-12-15 12:05:39 +01:00
61b8e31b41 Misc CRT cleanup 2020-12-14 21:11:20 +01:00
c9583c692d Handle absence of package CPU temperature
Resolves: #389
2020-12-14 21:07:07 +01:00
4507911cc3 Merge pull request #398 from natoscott/harden-linux-btime-init
Harden the extraction of boot time for the Linux platform
2020-12-14 17:53:24 +11:00
b7836515e8 Harden the extraction of boot time for the Linux platform
There is a possible path - albeit theoretical really - through
the btime initialization code in Linux ProcessList_new(), when
String_startsWith() is always false, which can result in btime
not being initialized.

This commit refactors the code to remove that possibility.
2020-12-14 12:16:32 +11:00
a3db2da4a7 Cleanup initialization of jiffies on the Linux platform
Small cleanups - add error handling, remove a local static
variable and refactor LinuxProcess_adjustTime (also rename
it, as its in LinuxProcessList.c not LinuxProcess.c) - and
while there, move the related 'btime' global variable into
LinuxProcessList.c so it can be made static.

Resolves https://github.com/htop-dev/htop/issues/384
2020-12-14 11:56:13 +11:00
cf982f2928 Merge pull request #395 from natoscott/man-page-linting
Remove superflous breaks around man page section heads
2020-12-14 11:48:01 +11:00
8d69a9a53e Simplify initialization of the Linux haveSmapsRollup variable 2020-12-14 01:46:29 +01:00
366b78edd9 Remove superflous breaks around man page section heads
There is no need to start a paragraph explicitly after
a section header (SH) in troff - some man linters will
complain about this as well.
2020-12-14 11:03:46 +11:00
f8a610e6e1 Merge branch 'fix-dlopen-libsensors-debian' of fasterit/htop 2020-12-13 20:09:06 +01:00
4b1a4a4ebd Merge branch 'fix_mach_timebase' of benbe/htop 2020-12-13 20:02:38 +01:00
3655b6ca0b Add column in darwin to indicate whether the the process is running under translation 2020-12-13 17:58:16 +01:00
1506283aff Move Process_fields from darwin/Platform to darwin/DarwinProcess 2020-12-13 17:58:16 +01:00
4b877eb16a Move Process_fields from unsupported/Platform to unsupported/UnsupportedProcess 2020-12-13 17:58:16 +01:00
f32f0188cd Correct timebase for non-x86 CPUs on Darwin
Fixes: #368
2020-12-13 11:47:34 +01:00
e65cdf947c Sort include in Darwin platform headers 2020-12-13 11:47:34 +01:00
ab60f59ed8 Check if clock_gettime needs linking of librt 2020-12-13 00:55:50 +01:00
8149823d56 Define O_PATH if not already defined 2020-12-13 00:55:50 +01:00
12421f460a Fix dlopen issue for libsensors5 in Debian Buster, Bullseye
libsensors.so is provided only by the -dev package, so search for
libsensors.so.5 (installed from the libsensors5 package) explicitly

see: dpkg-query -S libsensors.so
2020-12-12 20:08:17 +01:00
880eecabf5 Indentation and line continuation fixes in configure.ac 2020-12-12 19:49:52 +01:00
738d31b903 Add sys/dirent.h to iwyu/htop.imp 2020-12-11 20:57:19 +01:00
28bc087d8a Drop redundant sys/dirent.h include
sys/dirent.h is included by dirent.h in FreeBSD, and does not exist in Debian GNU/kFreeBSD
2020-12-11 20:57:19 +01:00
2700d99069 Merge pull request #379 from natoscott/streamline-pagesize-variables
Cull the definitions of pageSize and pageSizeKB from CRT.c
2020-12-11 11:06:40 +11:00
75e9f9a8d9 Cull the definitions of pageSize and pageSizeKB from CRT.c
By storing the per-process m_resident and m_virt values in the form
htop wants to display them in (KB, not pages), we no longer need to
have definitions of pageSize and pageSizeKB in the common CRT code.

These variables were never really CRT (i.e. display) related in the
first place.  It turns out the darwin platform code doesn't need to
use these at all (the process values are extracted from the kernel
in bytes not pages) and the other platforms can each use their own
local pagesize variables, in more appropriate locations.

Some platforms were actually already doing this, so this change is
removing duplication of logic and variables there.
2020-12-10 11:57:48 +11:00
db5687a355 Sort in paused mode after inverting sort order 2020-12-09 13:43:07 +01:00
7b739b6292 Fix pause mode ("Z") in tree view 2020-12-09 13:28:15 +01:00
ded9c5d363 PSI Meter: use constant width and only print ten-duration as bar 2020-12-08 23:09:35 +01:00
2d231d77ca Process: simplify 2020-12-08 22:37:15 +01:00
f6613db5cd Additional code simplification
Additional correction for #375
2020-12-08 21:24:19 +01:00
4c44a70f96 Fix broken tree display on inverted sort order
Fixes #375
2020-12-08 21:12:54 +01:00
157086e750 Split RichString_(append|appendn|write) into wide and ascii
RichString_writeFrom takes a top spot during performance analysis due to the
calls to mbstowcs() and iswprint().

Most of the time we know in advance that we are only going to print regular
ASCII characters.
2020-12-08 20:58:40 +01:00
5506925b34 Use sizeof buffer instead of magic number 2020-12-08 16:36:00 +01:00
c6d9fa279b travis CI: drop macOS and Linux builds
They are covered by GitHub CI
Also testing on s390x does not serve much
2020-12-08 16:07:45 +01:00
dcf7ad386c GitHub CI: add macOS build 2020-12-08 16:07:45 +01:00
30bf212185 Merge branch 'gentoo' of cgzones/htop 2020-12-07 16:29:52 +01:00
05969998c1 SELinuxMeter: silence comparison warning on 32-bit
linux/SELinuxMeter.c: In function ‘hasSELinuxMount’:
linux/SELinuxMeter.c:38:21: warning: comparison of integer expressions of different signedness: ‘__fsword_t’ {aka ‘int’} and ‘unsigned int’ [-Wsign-compare]
   38 |    if (sfbuf.f_type != SELINUX_MAGIC) {
      |                     ^~

Origin: 7df27b78e9/libselinux/src/init.c (L40)
2020-12-07 16:05:12 +01:00
ead978bce6 configure: check for additional linker flags for keypad(3)
Gentoo requires an explicit addition of -ltinfo

Resolves: https://bugs.gentoo.org/show_bug.cgi?id=690840
2020-12-07 15:33:16 +01:00
4f88d38256 Correct the version of htop development repo 2020-12-07 19:57:44 +11:00
f03f48a0fb Change version string to note development repo build 2020-12-07 12:16:06 +11:00
ad8aa2ce77 Bump version number for 3.0.3 release 2020-12-07 11:49:14 +11:00
b92cfa7d7a Merge branch 'conversion' of https://github.com/cgzones/htop into cgzones-conversion 2020-12-07 11:41:22 +11:00
57d9ecc551 OpenBSD update
- compilation failures like `return &this->this;` -> `return &this->super;`
- iwyu update
- misc cleanup
2020-12-06 16:20:55 +01:00
ad764ff972 Introduce METER_BUFFER_CHECK and METER_BUFFER_APPEND_CHR to cleanup writing to bar buffers
Closes: #294
2020-12-06 16:03:44 +01:00
77ec86aff4 Use size_t as type for buffer length in Process 2020-12-06 16:03:44 +01:00
e1ce141bc3 Use size_t as len type for Meter_UpdateValues
Most of the time the parameter is passed to snprintf type functions
2020-12-06 16:03:44 +01:00
d9224c66a4 Use size_t as len type for xSnprintf
Like the C snprintf function
2020-12-06 16:03:44 +01:00
3d15ba5197 Remove unused function Header_readMeterName 2020-12-06 16:03:25 +01:00
7ba25aa3c4 IWYU update 2020-12-06 15:32:16 +01:00
22f8f8000c Initialize buffer for retrieved path
This avoids a warning on GCC 11.

Fixes #369
2020-12-06 11:51:03 +01:00
4c4ba9d949 DragonFlyBSDProcessList: fix missing type 2020-12-06 00:43:41 +01:00
8d1595a20e FreeBSD: fix crash on empty environment
e.g. on kernel threads
2020-12-05 20:34:23 +01:00
876194492f LinuxProcessList: add underscore suffix for raw struct name
Fit the general coding style
2020-12-05 20:25:54 +01:00
5f528b7455 Meter: fix bar coloring without wide ncurses support
attrset() seems to not work with mvaddchnstr()
2020-12-05 20:01:10 +01:00
641fd2c4ad RichString: avoid signed integer misuse 2020-12-05 20:01:10 +01:00
f913680020 Hide degree character without wide ncurses support 2020-12-05 20:01:10 +01:00
f0a9dfc37e Resolve conversion from int to char 2020-12-05 19:58:32 +01:00
1e9b184367 Resolve conversion from int to unsigned and back 2020-12-05 19:58:32 +01:00
ba1549f99b Resolve conversion from int to short 2020-12-05 19:58:32 +01:00
f61e74a4af Resolve conversion from ssize_t to int for readlink return value 2020-12-05 19:58:32 +01:00
8029e9af04 Update htop logo, provide .svg file as well 2020-12-05 13:46:34 +01:00
ef0fc7129e Update AUTHORS file with htop-dev team 2020-12-05 11:07:32 +01:00
bc16fa037f Convert personal copyright authorship to team 2020-12-04 13:55:55 +01:00
cc7f16bb8f Some minor additions to the changelog 2020-12-04 07:51:33 +01:00
119 changed files with 1932 additions and 1638 deletions

View File

@ -102,6 +102,23 @@ jobs:
- name: Build - name: Build
run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)" run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)"
build-macos-latest-clang:
runs-on: macOS-latest
env:
CC: clang
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: brew install automake
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror
- name: Build
run: make -k
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror"
whitespace_check: whitespace_check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -6,17 +6,6 @@ compiler:
os: os:
- freebsd - freebsd
- linux
- osx
arch:
- amd64
- s390x
before_script:
if [[ ${TRAVIS_CPU_ARCH} == 's390x' ]]; then
sudo apt-get update && sudo apt-get install -y libncursesw5-dev ;
fi
script: script:
- ./autogen.sh - ./autogen.sh

12
AUTHORS
View File

@ -1 +1,11 @@
Hisham H. Muhammad Originally authored by:
Hisham H. Muhammad
Currently maintained by the htop dev team:
Benny Baumann
Christian Göttsche
Daniel Lange
Nathan Scott
For the full list of contributors see:
git log --format="%aN" | sort -u

114
Action.c
View File

@ -158,8 +158,7 @@ static bool collapseIntoParent(Panel* panel) {
} }
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) { Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey; Settings_setSortKey(settings, sortKey);
settings->direction = 1;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
} }
@ -171,7 +170,7 @@ static Htop_Reaction sortBy(State* st) {
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
char* name = String_trim(Process_fields[fields[i]].name); char* name = String_trim(Process_fields[fields[i]].name);
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == st->settings->sortKey) if (fields[i] == Settings_getActiveSortKey(st->settings))
Panel_setSelected(sortPanel, i); Panel_setSelected(sortPanel, i);
free(name); free(name);
@ -231,7 +230,7 @@ static Htop_Reaction actionToggleMergedCommand(State* st) {
static Htop_Reaction actionToggleTreeView(State* st) { static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView; st->settings->treeView = !st->settings->treeView;
if (st->settings->treeView) { if (st->settings->treeView) {
st->settings->direction = 1; st->settings->treeDirection = 1;
} }
ProcessList_expandTree(st->pl); ProcessList_expandTree(st->pl);
@ -273,6 +272,8 @@ static Htop_Reaction actionLowerPriority(State* st) {
static Htop_Reaction actionInvertSortOrder(State* st) { static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings); Settings_invertSortOrder(st->settings);
if (st->pauseProcessUpdate)
ProcessList_sort(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS; return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
} }
@ -340,7 +341,7 @@ static Htop_Reaction actionKill(State* st) {
if (sgn) { if (sgn) {
if (sgn->key != 0) { if (sgn->key != 0) {
Panel_setHeader(st->panel, "Sending..."); Panel_setHeader(st->panel, "Sending...");
Panel_draw(st->panel, true, true); Panel_draw(st->panel, false, true, true);
refresh(); refresh();
MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL); MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL);
napms(500); napms(500);
@ -371,7 +372,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
Htop_Reaction Action_follow(State* st) { Htop_Reaction Action_follow(State* st) {
st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel); st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel);
Panel_setSelectionColor(st->panel, CRT_colors[PANEL_SELECTION_FOLLOW]); Panel_setSelectionColor(st->panel, PANEL_SELECTION_FOLLOW);
return HTOP_KEEP_FOLLOWING; return HTOP_KEEP_FOLLOWING;
} }
@ -460,7 +461,7 @@ static const struct {
{ .key = " H: ", .info = "hide/show user process threads" }, { .key = " H: ", .info = "hide/show user process threads" },
{ .key = " K: ", .info = "hide/show kernel threads" }, { .key = " K: ", .info = "hide/show kernel threads" },
{ .key = " F: ", .info = "cursor follows process" }, { .key = " F: ", .info = "cursor follows process" },
{ .key = " F6 + -: ", .info = "expand/collapse tree" }, { .key = " + -: ", .info = "expand/collapse tree" },
{ .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" }, { .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" },
{ .key = " I: ", .info = "invert sort order" }, { .key = " I: ", .info = "invert sort order" },
{ .key = " F6 > .: ", .info = "select sort column" }, { .key = " F6 > .: ", .info = "select sort column" },
@ -638,60 +639,59 @@ static Htop_Reaction actionShowCommandScreen(State* st) {
} }
void Action_setBindings(Htop_Action* keys) { void Action_setBindings(Htop_Action* keys) {
keys[KEY_RESIZE] = actionResize;
keys['M'] = actionSortByMemory;
keys['T'] = actionSortByTime;
keys['P'] = actionSortByCPU;
keys['H'] = actionToggleUserlandThreads;
keys['K'] = actionToggleKernelThreads;
keys['p'] = actionToggleProgramPath;
keys['m'] = actionToggleMergedCommand;
keys['t'] = actionToggleTreeView;
keys[KEY_F(5)] = actionToggleTreeView;
keys[KEY_F(4)] = actionIncFilter;
keys['\\'] = actionIncFilter;
keys[KEY_F(3)] = actionIncSearch;
keys['/'] = actionIncSearch;
keys['n'] = actionIncNext;
keys['N'] = actionIncPrev;
keys[']'] = actionHigherPriority;
keys[KEY_F(7)] = actionHigherPriority;
keys['['] = actionLowerPriority;
keys[KEY_F(8)] = actionLowerPriority;
keys['I'] = actionInvertSortOrder;
keys[KEY_F(6)] = actionSetSortColumn;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys['<'] = actionSetSortColumn;
keys[','] = actionSetSortColumn;
keys['>'] = actionSetSortColumn;
keys['.'] = actionSetSortColumn;
keys[KEY_F(10)] = actionQuit;
keys['q'] = actionQuit;
keys['a'] = actionSetAffinity;
keys[KEY_F(9)] = actionKill;
keys['k'] = actionKill;
keys[KEY_RECLICK] = actionExpandOrCollapse;
keys['+'] = actionExpandOrCollapse;
keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse;
keys['\177'] = actionCollapseIntoParent;
keys['u'] = actionFilterByUser;
keys['F'] = Action_follow;
keys['S'] = actionSetup;
keys['C'] = actionSetup;
keys[KEY_F(2)] = actionSetup;
keys['x'] = actionShowLocks;
keys['l'] = actionLsof;
keys['s'] = actionStrace;
keys[' '] = actionTag; keys[' '] = actionTag;
keys['\014'] = actionRedraw; // Ctrl+L keys['+'] = actionExpandOrCollapse;
keys[KEY_F(1)] = actionHelp; keys[','] = actionSetSortColumn;
keys['h'] = actionHelp; keys['-'] = actionExpandOrCollapse;
keys['.'] = actionSetSortColumn;
keys['/'] = actionIncSearch;
keys['<'] = actionSetSortColumn;
keys['='] = actionExpandOrCollapse;
keys['>'] = actionSetSortColumn;
keys['?'] = actionHelp; keys['?'] = actionHelp;
keys['C'] = actionSetup;
keys['F'] = Action_follow;
keys['H'] = actionToggleUserlandThreads;
keys['I'] = actionInvertSortOrder;
keys['K'] = actionToggleKernelThreads;
keys['M'] = actionSortByMemory;
keys['N'] = actionIncPrev;
keys['P'] = actionSortByCPU;
keys['S'] = actionSetup;
keys['T'] = actionSortByTime;
keys['U'] = actionUntagAll; keys['U'] = actionUntagAll;
keys['Z'] = actionTogglePauseProcessUpdate;
keys['['] = actionLowerPriority;
keys['\014'] = actionRedraw; // Ctrl+L
keys['\177'] = actionCollapseIntoParent;
keys['\\'] = actionIncFilter;
keys[']'] = actionHigherPriority;
keys['a'] = actionSetAffinity;
keys['c'] = actionTagAllChildren; keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen; keys['e'] = actionShowEnvScreen;
keys['h'] = actionHelp;
keys['k'] = actionKill;
keys['l'] = actionLsof;
keys['m'] = actionToggleMergedCommand;
keys['n'] = actionIncNext;
keys['p'] = actionToggleProgramPath;
keys['q'] = actionQuit;
keys['s'] = actionStrace;
keys['t'] = actionToggleTreeView;
keys['u'] = actionFilterByUser;
keys['w'] = actionShowCommandScreen; keys['w'] = actionShowCommandScreen;
keys['Z'] = actionTogglePauseProcessUpdate; keys['x'] = actionShowLocks;
keys[KEY_F(1)] = actionHelp;
keys[KEY_F(2)] = actionSetup;
keys[KEY_F(3)] = actionIncSearch;
keys[KEY_F(4)] = actionIncFilter;
keys[KEY_F(5)] = actionToggleTreeView;
keys[KEY_F(6)] = actionSetSortColumn;
keys[KEY_F(7)] = actionHigherPriority;
keys[KEY_F(8)] = actionLowerPriority;
keys[KEY_F(9)] = actionKill;
keys[KEY_F(10)] = actionQuit;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys[KEY_RECLICK] = actionExpandOrCollapse;
keys[KEY_RESIZE] = actionResize;
} }

View File

@ -59,25 +59,25 @@ static void MaskItem_delete(Object* cast) {
static void MaskItem_display(const Object* cast, RichString* out) { static void MaskItem_display(const Object* cast, RichString* out) {
const MaskItem* this = (const MaskItem*)cast; const MaskItem* this = (const MaskItem*)cast;
assert (this != NULL); assert (this != NULL);
RichString_append(out, CRT_colors[CHECK_BOX], "["); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "[");
if (this->value == 2) { if (this->value == 2) {
RichString_append(out, CRT_colors[CHECK_MARK], "x"); RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
} else if (this->value == 1) { } else if (this->value == 1) {
RichString_append(out, CRT_colors[CHECK_MARK], "o"); RichString_appendAscii(out, CRT_colors[CHECK_MARK], "o");
} else { } else {
RichString_append(out, CRT_colors[CHECK_MARK], " "); RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
} }
RichString_append(out, CRT_colors[CHECK_BOX], "]"); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
RichString_append(out, CRT_colors[CHECK_TEXT], " "); RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
if (this->indent) { if (this->indent) {
RichString_append(out, CRT_colors[PROCESS_TREE], this->indent); RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->indent);
RichString_append(out, CRT_colors[PROCESS_TREE], RichString_appendWide(out, CRT_colors[PROCESS_TREE],
this->sub_tree == 2 this->sub_tree == 2
? CRT_treeStr[TREE_STR_OPEN] ? CRT_treeStr[TREE_STR_OPEN]
: CRT_treeStr[TREE_STR_SHUT]); : CRT_treeStr[TREE_STR_SHUT]);
RichString_append(out, CRT_colors[CHECK_TEXT], " "); RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
} }
RichString_append(out, CRT_colors[CHECK_TEXT], this->text); RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->text);
} }
static const ObjectClass MaskItem_class = { static const ObjectClass MaskItem_class = {
@ -173,7 +173,6 @@ static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) {
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : ""); FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : "");
FunctionBar_draw(super->currentBar);
int oldSelected = Panel_getSelectedIndex(super); int oldSelected = Panel_getSelectedIndex(super);
Panel_prune(super); Panel_prune(super);
@ -281,7 +280,7 @@ static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
indent_buf[0] = '\0'; indent_buf[0] = '\0';
if (depth > 0) { if (depth > 0) {
for (unsigned i = 1; i < depth; i++) { for (unsigned i = 1; i < depth; i++) {
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1u << i)) ? CRT_treeStr[TREE_STR_VERT] : " "); xSnprintf(&indent_buf[off], left, "%s ", (indent & (1U << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
size_t len = strlen(&indent_buf[off]); size_t len = strlen(&indent_buf[off]);
off += len; off += len;
left -= len; left -= len;
@ -323,9 +322,9 @@ static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) { static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent); MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent);
if (obj->next_sibling) { if (obj->next_sibling) {
indent |= (1u << obj->depth); indent |= (1U << obj->depth);
} else { } else {
indent &= ~(1u << obj->depth); indent &= ~(1U << obj->depth);
} }
for (unsigned i = 0; i < obj->arity; i++) { for (unsigned i = 0; i < obj->arity; i++) {

View File

@ -77,7 +77,7 @@ AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
Panel_setHeader(super, "Available Columns"); Panel_setHeader(super, "Available Columns");
for (int i = 1; i < Platform_numberOfFields; i++) { for (int i = 1; i < LAST_PROCESSFIELD; i++) {
if (i != COMM && Process_fields[i].description) { if (i != COMM && Process_fields[i].description) {
char description[256]; char description[256];
xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description); xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);

View File

@ -35,7 +35,6 @@ static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, c
Panel_add(panel, (Object*) Meter_toListItem(meter, false)); Panel_add(panel, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(panel, Panel_size(panel) - 1); Panel_setSelected(panel, Panel_size(panel) - 1);
MetersPanel_setMoving((MetersPanel*)panel, true); MetersPanel_setMoving((MetersPanel*)panel, true);
FunctionBar_draw(panel->currentBar);
} }
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {

View File

@ -21,7 +21,7 @@ static const int BatteryMeter_attributes[] = {
BATTERY BATTERY
}; };
static void BatteryMeter_updateValues(Meter* this, char* buffer, int len) { static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) {
ACPresence isOnAC; ACPresence isOnAC;
double percent; double percent;
@ -35,24 +35,21 @@ static void BatteryMeter_updateValues(Meter* this, char* buffer, int len) {
this->values[0] = percent; this->values[0] = percent;
const char *onAcText, *onBatteryText, *unknownText; const char* text;
switch (isOnAC) {
unknownText = "%.1f%%"; case AC_PRESENT:
if (this->mode == TEXT_METERMODE) { text = this->mode == TEXT_METERMODE ? " (Running on A/C)" : "(A/C)";
onAcText = "%.1f%% (Running on A/C)"; break;
onBatteryText = "%.1f%% (Running on battery)"; case AC_ABSENT:
} else { text = this->mode == TEXT_METERMODE ? " (Running on battery)" : "(bat)";
onAcText = "%.1f%%(A/C)"; break;
onBatteryText = "%.1f%%(bat)"; case AC_ERROR:
default:
text = "";
break;
} }
if (isOnAC == AC_PRESENT) { xSnprintf(buffer, len, "%.1f%%%s", percent, text);
xSnprintf(buffer, len, onAcText, percent);
} else if (isOnAC == AC_ABSENT) {
xSnprintf(buffer, len, onBatteryText, percent);
} else {
xSnprintf(buffer, len, unknownText, percent);
}
} }
const MeterClass BatteryMeter_class = { const MeterClass BatteryMeter_class = {

View File

@ -50,7 +50,7 @@ static void CPUMeter_init(Meter* this) {
Meter_setCaption(this, "Avg"); Meter_setCaption(this, "Avg");
} }
static void CPUMeter_updateValues(Meter* this, char* buffer, int size) { static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
int cpu = this->param; int cpu = this->param;
if (cpu > this->pl->cpuCount) { if (cpu > this->pl->cpuCount) {
xSnprintf(buffer, size, "absent"); xSnprintf(buffer, size, "absent");
@ -67,7 +67,7 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) {
double percent = Platform_setCPUValues(this, cpu); double percent = Platform_setCPUValues(this, cpu);
if (this->pl->settings->showCPUUsage) { if (this->pl->settings->showCPUUsage) {
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%5.1f%%", percent); xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent);
} }
if (this->pl->settings->showCPUFrequency) { if (this->pl->settings->showCPUFrequency) {
@ -104,49 +104,49 @@ static void CPUMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_prune(out); RichString_prune(out);
if (this->param > this->pl->cpuCount) { if (this->param > this->pl->cpuCount) {
RichString_append(out, CRT_colors[METER_TEXT], "absent"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "absent");
return; return;
} }
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]);
RichString_append(out, CRT_colors[METER_TEXT], ":"); RichString_appendAscii(out, CRT_colors[METER_TEXT], ":");
RichString_append(out, CRT_colors[CPU_NORMAL], buffer); RichString_appendAscii(out, CRT_colors[CPU_NORMAL], buffer);
if (this->pl->settings->detailedCPUTime) { if (this->pl->settings->detailedCPUTime) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sy:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "sy:");
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer); RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "ni:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "ni:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "hi:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "hi:");
RichString_append(out, CRT_colors[CPU_IRQ], buffer); RichString_appendAscii(out, CRT_colors[CPU_IRQ], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "si:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "si:");
RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer); RichString_appendAscii(out, CRT_colors[CPU_SOFTIRQ], buffer);
if (!isnan(this->values[CPU_METER_STEAL])) { if (!isnan(this->values[CPU_METER_STEAL])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", 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_appendAscii(out, CRT_colors[METER_TEXT], "st:");
RichString_append(out, CRT_colors[CPU_STEAL], buffer); RichString_appendAscii(out, CRT_colors[CPU_STEAL], buffer);
} }
if (!isnan(this->values[CPU_METER_GUEST])) { if (!isnan(this->values[CPU_METER_GUEST])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", 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_appendAscii(out, CRT_colors[METER_TEXT], "gu:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer); RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer);
} }
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
RichString_append(out, CRT_colors[METER_TEXT], "wa:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "wa:");
RichString_append(out, CRT_colors[CPU_IOWAIT], buffer); RichString_appendAscii(out, CRT_colors[CPU_IOWAIT], buffer);
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sys:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "sys:");
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer); RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "low:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "low:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
if (!isnan(this->values[CPU_METER_IRQ])) { if (!isnan(this->values[CPU_METER_IRQ])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", 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_appendAscii(out, CRT_colors[METER_TEXT], "vir:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer); RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer);
} }
} }
@ -161,8 +161,8 @@ static void CPUMeter_display(const Object* cast, RichString* out) {
} else { } else {
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign); xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign);
} }
RichString_append(out, CRT_colors[METER_TEXT], "temp:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "temp:");
RichString_append(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer); RichString_appendWide(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer);
} }
#endif #endif
} }

71
CRT.c
View File

@ -11,7 +11,6 @@ in the source distribution for its full text.
#include <errno.h> #include <errno.h>
#include <langinfo.h> #include <langinfo.h>
#include <locale.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -42,28 +41,26 @@ in the source distribution for its full text.
#define ColorPairGrayBlack ColorPair(Magenta,Magenta) #define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta) #define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
static const char* const CRT_treeStrAscii[TREE_STR_COUNT] = { static const char* const CRT_treeStrAscii[LAST_TREE_STR] = {
"-", // TREE_STR_HORZ [TREE_STR_VERT] = "|",
"|", // TREE_STR_VERT [TREE_STR_RTEE] = "`",
"`", // TREE_STR_RTEE [TREE_STR_BEND] = "`",
"`", // TREE_STR_BEND [TREE_STR_TEND] = ",",
",", // TREE_STR_TEND [TREE_STR_OPEN] = "+",
"+", // TREE_STR_OPEN [TREE_STR_SHUT] = "-",
"-", // TREE_STR_SHUT
}; };
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
static const char* const CRT_treeStrUtf8[TREE_STR_COUNT] = { static const char* const CRT_treeStrUtf8[LAST_TREE_STR] = {
"\xe2\x94\x80", // TREE_STR_HORZ ─ [TREE_STR_VERT] = "\xe2\x94\x82", //
"\xe2\x94\x82", // TREE_STR_VERT │ [TREE_STR_RTEE] = "\xe2\x94\x9c", //
"\xe2\x94\x9c", // TREE_STR_RTEE ├ [TREE_STR_BEND] = "\xe2\x94\x94", //
"\xe2\x94\x94", // TREE_STR_BEND └ [TREE_STR_TEND] = "\xe2\x94\x8c", //
"\xe2\x94\x8c", // TREE_STR_TEND ┌ [TREE_STR_OPEN] = "+", // +, TODO use 🮯 'BOX DRAWINGS LIGHT HORIZONTAL
"+", // TREE_STR_OPEN +, TODO use 🮯 'BOX DRAWINGS LIGHT HORIZONTAL // WITH VERTICAL STROKE' (U+1FBAF, "\xf0\x9f\xae\xaf") when
// WITH VERTICAL STROKE' (U+1FBAF, "\xf0\x9f\xae\xaf") when // Unicode 13 is common
// Unicode 13 is common [TREE_STR_SHUT] = "\xe2\x94\x80", // ─
"\xe2\x94\x80", // TREE_STR_SHUT ─
}; };
bool CRT_utf8 = false; bool CRT_utf8 = false;
@ -80,20 +77,20 @@ static const char* initDegreeSign(void) {
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
if (CRT_utf8) if (CRT_utf8)
return "\xc2\xb0"; return "\xc2\xb0";
#endif
static char buffer[4]; static char buffer[4];
// this might fail if the current locale does not support wide characters // this might fail if the current locale does not support wide characters
int r = snprintf(buffer, sizeof(buffer), "%lc", 176); int r = snprintf(buffer, sizeof(buffer), "%lc", 176);
if (r > 0) if (r > 0)
return buffer; return buffer;
#endif
return ""; return "";
} }
const int* CRT_colors; const int* CRT_colors;
int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[COLORSCHEME_DEFAULT] = { [COLORSCHEME_DEFAULT] = {
[RESET_COLOR] = ColorPair(White, Black), [RESET_COLOR] = ColorPair(White, Black),
[DEFAULT_COLOR] = ColorPair(White, Black), [DEFAULT_COLOR] = ColorPair(White, Black),
@ -605,12 +602,7 @@ int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10; int CRT_scrollWheelVAmount = 10;
const char* CRT_termType; ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT;
int CRT_colorScheme = 0;
long CRT_pageSize = -1;
long CRT_pageSizeKB = -1;
ATTR_NORETURN ATTR_NORETURN
static void CRT_handleSIGTERM(int sgn) { static void CRT_handleSIGTERM(int sgn) {
@ -677,14 +669,14 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
start_color(); start_color();
} }
CRT_termType = getenv("TERM"); const char* termType = getenv("TERM");
if (String_eq(CRT_termType, "linux")) { if (termType && String_eq(termType, "linux")) {
CRT_scrollHAmount = 20; CRT_scrollHAmount = 20;
} else { } else {
CRT_scrollHAmount = 5; CRT_scrollHAmount = 5;
} }
if (String_startsWith(CRT_termType, "xterm") || String_eq(CRT_termType, "vt220")) { if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) {
define_key("\033[H", KEY_HOME); define_key("\033[H", KEY_HOME);
define_key("\033[F", KEY_END); define_key("\033[F", KEY_END);
define_key("\033[7~", KEY_HOME); define_key("\033[7~", KEY_HOME);
@ -747,11 +739,6 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
mousemask(BUTTON1_RELEASED, NULL); mousemask(BUTTON1_RELEASED, NULL);
#endif #endif
CRT_pageSize = sysconf(_SC_PAGESIZE);
if (CRT_pageSize == -1)
CRT_fatalError("Fatal error: Can not get PAGE_SIZE by sysconf(_SC_PAGESIZE)");
CRT_pageSizeKB = CRT_pageSize / 1024;
CRT_degreeSign = initDegreeSign(); CRT_degreeSign = initDegreeSign();
} }
@ -761,7 +748,7 @@ void CRT_done() {
} }
void CRT_fatalError(const char* note) { void CRT_fatalError(const char* note) {
char* sysMsg = strerror(errno); const char* sysMsg = strerror(errno);
CRT_done(); CRT_done();
fprintf(stderr, "%s: %s\n", note, sysMsg); fprintf(stderr, "%s: %s\n", note, sysMsg);
exit(2); exit(2);
@ -789,10 +776,10 @@ void CRT_enableDelay() {
void CRT_setColors(int colorScheme) { void CRT_setColors(int colorScheme) {
CRT_colorScheme = colorScheme; CRT_colorScheme = colorScheme;
for (int i = 0; i < 8; i++) { for (short int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) { for (short int j = 0; j < 8; j++) {
if (ColorIndex(i, j) != ColorIndexGrayBlack) { if (ColorIndex(i, j) != ColorIndexGrayBlack) {
int bg = (colorScheme != COLORSCHEME_BLACKNIGHT) short int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? (j == 0 ? -1 : j) ? (j == 0 ? -1 : j)
: j; : j;
init_pair(ColorIndex(i, j), i, bg); init_pair(ColorIndex(i, j), i, bg);
@ -800,10 +787,8 @@ void CRT_setColors(int colorScheme) {
} }
} }
int grayBlackFg = COLORS > 8 ? 8 : 0; short int grayBlackFg = COLORS > 8 ? 8 : 0;
int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT) short int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT) ? -1 : 0;
? -1
: 0;
init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg); init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg);
CRT_colors = CRT_colorSchemes[colorScheme]; CRT_colors = CRT_colorSchemes[colorScheme];

20
CRT.h
View File

@ -16,26 +16,25 @@ in the source distribution for its full text.
typedef enum TreeStr_ { typedef enum TreeStr_ {
TREE_STR_HORZ,
TREE_STR_VERT, TREE_STR_VERT,
TREE_STR_RTEE, TREE_STR_RTEE,
TREE_STR_BEND, TREE_STR_BEND,
TREE_STR_TEND, TREE_STR_TEND,
TREE_STR_OPEN, TREE_STR_OPEN,
TREE_STR_SHUT, TREE_STR_SHUT,
TREE_STR_COUNT LAST_TREE_STR
} TreeStr; } TreeStr;
typedef enum ColorSchemes_ { typedef enum ColorScheme_ {
COLORSCHEME_DEFAULT = 0, COLORSCHEME_DEFAULT,
COLORSCHEME_MONOCHROME, COLORSCHEME_MONOCHROME,
COLORSCHEME_BLACKONWHITE, COLORSCHEME_BLACKONWHITE,
COLORSCHEME_LIGHTTERMINAL, COLORSCHEME_LIGHTTERMINAL,
COLORSCHEME_MIDNIGHT, COLORSCHEME_MIDNIGHT,
COLORSCHEME_BLACKNIGHT, COLORSCHEME_BLACKNIGHT,
COLORSCHEME_BROKENGRAY, COLORSCHEME_BROKENGRAY,
LAST_COLORSCHEME, LAST_COLORSCHEME
} ColorSchemes; } ColorScheme;
typedef enum ColorElements_ { typedef enum ColorElements_ {
RESET_COLOR, RESET_COLOR,
@ -144,20 +143,13 @@ extern const char* const* CRT_treeStr;
extern const int* CRT_colors; extern const int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX; extern int CRT_cursorX;
extern int CRT_scrollHAmount; extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount; extern int CRT_scrollWheelVAmount;
extern const char* CRT_termType; extern ColorScheme CRT_colorScheme;
extern int CRT_colorScheme;
extern long CRT_pageSize;
extern long CRT_pageSizeKB;
#ifdef HAVE_SETUID_ENABLED #ifdef HAVE_SETUID_ENABLED

View File

@ -1,3 +1,34 @@
What's new in version 3.0.4
* Separate tree and list sort orders
* Invert Process_compare so that superclass matches run first
(thanks to Hisham Muhammad)
* Unhardcode Mac OS tick-to-milliseconds conversion
(thanks to Alexander Momchilov)
* Check if clock_gettime needs linking of librt
* Define O_PATH if not already defined
(thanks to Chris Burr)
* Add column on Mac for processes running under translation
(thanks to Dániel Bakai)
* Configure check for additional linker flags for keypad(3)
* PSI Meter: constant width and only print ten-duration as bar
* Sort in paused mode after inverting sort order
* Handle absence of package CPU temperature
* Meter: restore non-wide-character build
* LibSensors: restore temperature for Raspberry Pi
* MainPanel: do not reset hideProcessSelection on KEY_SHUFFLE
* BarMeter: rework text padding
* Panel: rework drawing of FunctionBar
* Meter: fix artifacts with very tiny width
* DragonFlyBSD updates
* BUGFIX: Fix dlopen issue for libsensors5 for some platforms
* BUGFIX: Fix broken tree display on inverted sort order
* BUGFIX: Fix pause mode ("Z") in tree view
* BUGFIX: Correct timebase for non-x86 CPUs on Darwin
* BUGFIX: Avoid NULL dereference on zombie processes
* Document dynamic bindings and assumed external configuration
* Update key mapping documentation for sorting
What's new in version 3.0.3 What's new in version 3.0.3
* Process sorting in 'tree' mode * Process sorting in 'tree' mode
@ -5,8 +36,9 @@ What's new in version 3.0.3
* Improved command display/sort functionality * Improved command display/sort functionality
(thanks to Narendran Gopalakrishnan) (thanks to Narendran Gopalakrishnan)
* Add screen for active file locks * Add screen for active file locks
(thanks to Fynn J. Wulf)
* Calculate library size (M_LRS column) from maps file * Calculate library size (M_LRS column) from maps file
(thanks to Fynn Wulf) (thanks to Fynn J. Wulf)
* Add a Zram meter * Add a Zram meter
(thanks to Murloc Knight) (thanks to Murloc Knight)
* Add Linux cwd process column * Add Linux cwd process column
@ -49,8 +81,10 @@ What's new in version 3.0.3
* Avoid expensive build of process tree when not using it * Avoid expensive build of process tree when not using it
* Include documentation for COMM and EXE * Include documentation for COMM and EXE
* Distinguish display of no permissions for reading M_LRS * Distinguish display of no permissions for reading M_LRS
* Only calculate M_LRS size every 5 seconds * Only calculate M_LRS size every 2 seconds
* Improvements to comm / cmdline display functionality * Improvements to comm / cmdline display functionality
* Merged view for COMM, EXE and cmdline
(thanks to Narendran Gopalakrishnan and Benny Baumann)
* Consistent kernel thread display for COMM/EXE columns * Consistent kernel thread display for COMM/EXE columns
* Central fault handling for all platforms * Central fault handling for all platforms
* Handle parsing envID & VPid from process status file * Handle parsing envID & VPid from process status file

View File

@ -19,7 +19,7 @@ static const int ClockMeter_attributes[] = {
CLOCK CLOCK
}; };
static void ClockMeter_updateValues(Meter* this, char* buffer, int size) { static void ClockMeter_updateValues(Meter* this, char* buffer, size_t size) {
time_t t = time(NULL); time_t t = time(NULL);
struct tm result; struct tm result;
struct tm* lt = localtime_r(&t, &result); struct tm* lt = localtime_r(&t, &result);

View File

@ -18,7 +18,6 @@ in the source distribution for its full text.
#include "ProvideCurses.h" #include "ProvideCurses.h"
#include "RichString.h" #include "RichString.h"
#include "Vector.h" #include "Vector.h"
#include "XUtils.h"
// TO ADD A NEW SCHEME: // TO ADD A NEW SCHEME:
@ -61,26 +60,21 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
case KEY_MOUSE: case KEY_MOUSE:
case KEY_RECLICK: case KEY_RECLICK:
case ' ': case ' ':
assert(mark >= 0);
assert(mark < LAST_COLORSCHEME);
for (int i = 0; ColorSchemeNames[i] != NULL; i++) for (int i = 0; ColorSchemeNames[i] != NULL; i++)
CheckItem_set((CheckItem*)Panel_get(super, i), false); CheckItem_set((CheckItem*)Panel_get(super, i), false);
CheckItem_set((CheckItem*)Panel_get(super, mark), true); CheckItem_set((CheckItem*)Panel_get(super, mark), true);
this->settings->colorScheme = mark; this->settings->colorScheme = mark;
result = HANDLED;
}
if (result == HANDLED) {
this->settings->changed = true; this->settings->changed = true;
const Header* header = this->scr->header;
CRT_setColors(mark); CRT_setColors(mark);
clear(); clear();
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
Header_draw(header); result = HANDLED | REDRAW;
FunctionBar_draw(super->currentBar);
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);
RichString_setAttr(&(menu->header), CRT_colors[PANEL_HEADER_UNFOCUS]);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
} }
return result; return result;
} }
@ -101,6 +95,8 @@ ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) {
this->settings = settings; this->settings = settings;
this->scr = scr; this->scr = scr;
assert(ARRAYSIZE(ColorSchemeNames) == LAST_COLORSCHEME + 1);
Panel_setHeader(super, "Colors"); Panel_setHeader(super, "Colors");
for (int i = 0; ColorSchemeNames[i] != NULL; i++) { for (int i = 0; ColorSchemeNames[i] != NULL; i++) {
Panel_add(super, (Object*) CheckItem_newByVal(ColorSchemeNames[i], false)); Panel_add(super, (Object*) CheckItem_newByVal(ColorSchemeNames[i], false));

View File

@ -44,7 +44,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
{ {
if (selected < size - 1) { if (selected < size - 1) {
this->moving = !(this->moving); this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]); Panel_setSelectionColor(super, this->moving ? PANEL_SELECTION_FOLLOW : PANEL_SELECTION_FOCUS);
ListItem* selectedItem = (ListItem*) Panel_getSelected(super); ListItem* selectedItem = (ListItem*) Panel_getSelected(super);
if (selectedItem) if (selectedItem)
selectedItem->moving = this->moving; selectedItem->moving = this->moving;

View File

@ -17,9 +17,10 @@ static void CommandScreen_scan(InfoScreen* this) {
Panel_prune(panel); Panel_prune(panel);
const char* p = Process_getCommand(this->process); const char* p = Process_getCommand(this->process);
char* line = xMalloc(COLS + 1); char line[COLS + 1];
int line_offset = 0, last_spc = -1, len; int line_offset = 0, last_spc = -1, len;
for (; *p != '\0'; p++, line_offset++) { for (; *p != '\0'; p++, line_offset++) {
assert(line_offset >= 0 && (size_t)line_offset < sizeof(line));
line[line_offset] = *p; line[line_offset] = *p;
if (*p == ' ') { if (*p == ' ') {
last_spc = line_offset; last_spc = line_offset;
@ -41,7 +42,6 @@ static void CommandScreen_scan(InfoScreen* this) {
InfoScreen_addLine(this, line); InfoScreen_addLine(this, line);
} }
free(line);
Panel_setSelected(panel, idx); Panel_setSelected(panel, idx);
} }

View File

@ -1,6 +1,6 @@
/* /*
htop - Compat.c htop - Compat.c
(C) 2020 Christian Göttsche (C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -11,12 +11,18 @@ in the source distribution for its full text.
#include <errno.h> #include <errno.h>
#include <fcntl.h> // IWYU pragma: keep #include <fcntl.h> // IWYU pragma: keep
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> // IWYU pragma: keep #include <sys/types.h> // IWYU pragma: keep
#include "XUtils.h" // IWYU pragma: keep #include "XUtils.h" // IWYU pragma: keep
#ifdef HAVE_HOST_GET_CLOCK_SERVICE
#include <mach/clock.h>
#include <mach/mach.h>
#endif
int Compat_faccessat(int dirfd, int Compat_faccessat(int dirfd,
const char* pathname, const char* pathname,
@ -94,11 +100,11 @@ int Compat_openat(const char* dirpath,
#endif /* !HAVE_OPENAT */ #endif /* !HAVE_OPENAT */
int Compat_readlinkat(int dirfd, ssize_t Compat_readlinkat(int dirfd,
const char* dirpath, const char* dirpath,
const char* pathname, const char* pathname,
char* buf, char* buf,
size_t bufsize) { size_t bufsize) {
#ifdef HAVE_READLINKAT #ifdef HAVE_READLINKAT
@ -117,3 +123,31 @@ int Compat_readlinkat(int dirfd,
#endif #endif
} }
int Compat_clock_monotonic_gettime(struct timespec *tp) {
#if defined(HAVE_CLOCK_GETTIME)
return clock_gettime(CLOCK_MONOTONIC, tp);
#elif defined(HAVE_HOST_GET_CLOCK_SERVICE)
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
tp->tv_sec = mts.tv_sec;
tp->tv_nsec = mts.tv_nsec;
return 0;
#else
#error No Compat_clock_monotonic_gettime() implementation!
#endif
}

View File

@ -2,7 +2,7 @@
#define HEADER_Compat #define HEADER_Compat
/* /*
htop - Compat.h htop - Compat.h
(C) 2020 Christian Göttsche (C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -12,7 +12,7 @@ in the source distribution for its full text.
#include <fcntl.h> #include <fcntl.h>
#include <stddef.h> #include <stddef.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h> // IWYU pragma: keep
int Compat_faccessat(int dirfd, int Compat_faccessat(int dirfd,
@ -50,10 +50,12 @@ int Compat_openat(openat_arg_t dirpath, const char* pathname, int flags);
#endif /* HAVE_OPENAT */ #endif /* HAVE_OPENAT */
int Compat_readlinkat(int dirfd, ssize_t Compat_readlinkat(int dirfd,
const char* dirpath, const char* dirpath,
const char* pathname, const char* pathname,
char* buf, char* buf,
size_t bufsize); size_t bufsize);
int Compat_clock_monotonic_gettime(struct timespec *tp);
#endif /* HEADER_Compat */ #endif /* HEADER_Compat */

View File

@ -19,7 +19,7 @@ static const int DateMeter_attributes[] = {
DATE DATE
}; };
static void DateMeter_updateValues(Meter* this, char* buffer, int size) { static void DateMeter_updateValues(Meter* this, char* buffer, size_t size) {
time_t t = time(NULL); time_t t = time(NULL);
struct tm result; struct tm result;
struct tm* lt = localtime_r(&t, &result); struct tm* lt = localtime_r(&t, &result);

View File

@ -19,7 +19,7 @@ static const int DateTimeMeter_attributes[] = {
DATETIME DATETIME
}; };
static void DateTimeMeter_updateValues(Meter* this, char* buffer, int size) { static void DateTimeMeter_updateValues(Meter* this, char* buffer, size_t size) {
time_t t = time(NULL); time_t t = time(NULL);
struct tm result; struct tm result;
struct tm* lt = localtime_r(&t, &result); struct tm* lt = localtime_r(&t, &result);

View File

@ -1,6 +1,6 @@
/* /*
htop - DiskIOMeter.c htop - DiskIOMeter.c
(C) 2020 Christian Göttsche (C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -30,7 +30,7 @@ static unsigned long int cached_read_diff = 0;
static unsigned long int cached_write_diff = 0; static unsigned long int cached_write_diff = 0;
static double cached_utilisation_diff = 0.0; static double cached_utilisation_diff = 0.0;
static void DiskIOMeter_updateValues(Meter* this, char* buffer, int len) { static void DiskIOMeter_updateValues(Meter* this, char* buffer, size_t len) {
static unsigned long long int cached_last_update = 0; static unsigned long long int cached_last_update = 0;
struct timeval tv; struct timeval tv;
@ -88,7 +88,7 @@ static void DiskIOMeter_updateValues(Meter* this, char* buffer, int len) {
static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
if (!hasData) { if (!hasData) {
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data"); RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
return; return;
} }
@ -96,15 +96,15 @@ static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out)
int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE; int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff); xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
RichString_write(out, CRT_colors[color], buffer); RichString_writeAscii(out, CRT_colors[color], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " read: "); RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: ");
Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer)); Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " write: "); RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: ");
Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer)); Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
} }
const MeterClass DiskIOMeter_class = { const MeterClass DiskIOMeter_class = {

View File

@ -1,8 +1,8 @@
#ifndef HEADER_DiskIOMeter #ifndef HEADER_DiskIOMeter
#define HEADER_DiskIOMeter #define HEADER_DiskIOMeter
/* /*
h top - DiskIOMeter*.h htop - DiskIOMeter.h
(C) 2020 Christian Göttsche (C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */

View File

@ -18,7 +18,6 @@ in the source distribution for its full text.
#include "Object.h" #include "Object.h"
#include "OptionItem.h" #include "OptionItem.h"
#include "ProvideCurses.h" #include "ProvideCurses.h"
#include "XUtils.h"
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@ -98,6 +97,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_setHeader(super, "Display options"); Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView))); Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID)));
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers))); Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));

View File

@ -14,15 +14,6 @@
#include "XUtils.h" #include "XUtils.h"
const InfoScreenClass EnvScreen_class = {
.super = {
.extends = Class(Object),
.delete = EnvScreen_delete
},
.scan = EnvScreen_scan,
.draw = EnvScreen_draw
};
EnvScreen* EnvScreen_new(Process* process) { EnvScreen* EnvScreen_new(Process* process) {
EnvScreen* this = xMalloc(sizeof(EnvScreen)); EnvScreen* this = xMalloc(sizeof(EnvScreen));
Object_setClass(this, Class(EnvScreen)); Object_setClass(this, Class(EnvScreen));
@ -33,11 +24,11 @@ void EnvScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this)); free(InfoScreen_done((InfoScreen*)this));
} }
void EnvScreen_draw(InfoScreen* this) { static void EnvScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process)); InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process));
} }
void EnvScreen_scan(InfoScreen* this) { static void EnvScreen_scan(InfoScreen* this) {
Panel* panel = this->display; Panel* panel = this->display;
int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0); int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
@ -59,3 +50,12 @@ void EnvScreen_scan(InfoScreen* this) {
Vector_insertionSort(panel->items); Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx); Panel_setSelected(panel, idx);
} }
const InfoScreenClass EnvScreen_class = {
.super = {
.extends = Class(Object),
.delete = EnvScreen_delete
},
.scan = EnvScreen_scan,
.draw = EnvScreen_draw
};

View File

@ -15,8 +15,4 @@ EnvScreen* EnvScreen_new(Process* process);
void EnvScreen_delete(Object* this); void EnvScreen_delete(Object* this);
void EnvScreen_draw(InfoScreen* this);
void EnvScreen_scan(InfoScreen* this);
#endif #endif

View File

@ -112,10 +112,11 @@ void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr
attrset(attr); attrset(attr);
} }
mvaddstr(LINES - 1, x, buffer); mvaddstr(LINES - 1, x, buffer);
attrset(CRT_colors[RESET_COLOR]);
x += strlen(buffer); x += strlen(buffer);
} }
attrset(CRT_colors[RESET_COLOR]);
if (setCursor) { if (setCursor) {
CRT_cursorX = x; CRT_cursorX = x;
curs_set(1); curs_set(1);
@ -132,10 +133,10 @@ void FunctionBar_append(const char* buffer, int attr) {
} else { } else {
attrset(attr); attrset(attr);
} }
mvaddstr(LINES - 1, currentLen, buffer); mvaddstr(LINES - 1, currentLen + 1, buffer);
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
currentLen += strlen(buffer); currentLen += strlen(buffer) + 1;
} }
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) { int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {

View File

@ -13,6 +13,7 @@ in the source distribution for its full text.
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "Macros.h" #include "Macros.h"
#include "XUtils.h" #include "XUtils.h"

View File

@ -131,21 +131,6 @@ int Header_size(Header* this, int column) {
return Vector_size(meters); return Vector_size(meters);
} }
char* Header_readMeterName(Header* this, int i, int column) {
Vector* meters = this->columns[column];
Meter* meter = (Meter*) Vector_get(meters, i);
int nameLen = strlen(Meter_name(meter));
int len = nameLen + 100;
char* name = xMalloc(len);
memcpy(name, Meter_name(meter), nameLen);
name[nameLen] = '\0';
if (meter->param)
xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
return name;
}
MeterModeId Header_readMeterMode(Header* this, int i, int column) { MeterModeId Header_readMeterMode(Header* this, int i, int column) {
Vector* meters = this->columns[column]; Vector* meters = this->columns[column];

View File

@ -39,8 +39,6 @@ Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, i
int Header_size(Header* this, int column); int Header_size(Header* this, int column);
char* Header_readMeterName(Header* this, int i, int column);
MeterModeId Header_readMeterMode(Header* this, int i, int column); MeterModeId Header_readMeterMode(Header* this, int i, int column);
void Header_reinit(Header* this); void Header_reinit(Header* this);

View File

@ -19,7 +19,7 @@ static const int HostnameMeter_attributes[] = {
HOSTNAME HOSTNAME
}; };
static void HostnameMeter_updateValues(Meter* this, char* buffer, int size) { static void HostnameMeter_updateValues(Meter* this, char* buffer, size_t size) {
(void) this; (void) this;
gethostname(buffer, size - 1); gethostname(buffer, size - 1);
} }

View File

@ -31,7 +31,7 @@ void IncSet_reset(IncSet* this, IncType type) {
static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL}; static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL};
static const char* const searchKeys[] = {"F3", "Esc", " "}; static const char* const searchKeys[] = {"F3", "Esc", " "};
static int searchEvents[] = {KEY_F(3), 27, ERR}; static const int searchEvents[] = {KEY_F(3), 27, ERR};
static inline void IncMode_initSearch(IncMode* search) { static inline void IncMode_initSearch(IncMode* search) {
memset(search, 0, sizeof(IncMode)); memset(search, 0, sizeof(IncMode));
@ -41,7 +41,7 @@ static inline void IncMode_initSearch(IncMode* search) {
static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL}; static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
static const char* const filterKeys[] = {"Enter", "Esc", " "}; static const char* const filterKeys[] = {"Enter", "Esc", " "};
static int filterEvents[] = {13, 27, ERR}; static const int filterEvents[] = {13, 27, ERR};
static inline void IncMode_initFilter(IncMode* filter) { static inline void IncMode_initFilter(IncMode* filter) {
memset(filter, 0, sizeof(IncMode)); memset(filter, 0, sizeof(IncMode));
@ -54,12 +54,13 @@ static inline void IncMode_done(IncMode* mode) {
} }
IncSet* IncSet_new(FunctionBar* bar) { IncSet* IncSet_new(FunctionBar* bar) {
IncSet* this = xCalloc(1, sizeof(IncSet)); IncSet* this = xMalloc(sizeof(IncSet));
IncMode_initSearch(&(this->modes[INC_SEARCH])); IncMode_initSearch(&(this->modes[INC_SEARCH]));
IncMode_initFilter(&(this->modes[INC_FILTER])); IncMode_initFilter(&(this->modes[INC_FILTER]));
this->active = NULL; this->active = NULL;
this->filtering = false;
this->defaultBar = bar; this->defaultBar = bar;
this->filtering = false;
this->found = false;
return this; return this;
} }
@ -99,20 +100,14 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) { static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel); int size = Panel_size(panel);
bool found = false;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) { if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i); Panel_setSelected(panel, i);
found = true; return true;
break;
} }
} }
FunctionBar_drawExtra(mode->bar, return false;
mode->buffer,
found ? -1 : CRT_colors[FAILED_SEARCH],
true);
return found;
} }
static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) { static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
@ -202,7 +197,6 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} }
this->active = NULL; this->active = NULL;
Panel_setDefaultBar(panel); Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar);
doSearch = false; doSearch = false;
} }
if (doSearch) { if (doSearch) {
@ -221,13 +215,12 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
void IncSet_activate(IncSet* this, IncType type, Panel* panel) { void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]); this->active = &(this->modes[type]);
FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
panel->currentBar = this->active->bar; panel->currentBar = this->active->bar;
} }
void IncSet_drawBar(const IncSet* this) { void IncSet_drawBar(const IncSet* this) {
if (this->active) { if (this->active) {
FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true); FunctionBar_drawExtra(this->active->bar, this->active->buffer, (this->active->isFilter || this->found) ? -1 : CRT_colors[FAILED_SEARCH], true);
} else { } else {
FunctionBar_draw(this->defaultBar); FunctionBar_draw(this->defaultBar);
} }

View File

@ -19,7 +19,7 @@ static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh
static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"}; static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27}; static const int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader) { InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader) {
this->process = process; this->process = process;
@ -44,8 +44,10 @@ void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
char* title = xMalloc(COLS + 1); char title[COLS + 1];
int len = vsnprintf(title, COLS + 1, fmt, ap); int len = vsnprintf(title, sizeof(title), fmt, ap);
va_end(ap);
if (len > COLS) { if (len > COLS) {
memset(&title[COLS - 3], '.', 3); memset(&title[COLS - 3], '.', 3);
} }
@ -54,11 +56,9 @@ void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
mvhline(0, 0, ' ', COLS); mvhline(0, 0, ' ', COLS);
mvwprintw(stdscr, 0, 0, title); mvwprintw(stdscr, 0, 0, title);
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true; Panel_draw(this->display, true, true, true);
Panel_draw(this->display, true, true);
IncSet_drawBar(this->inc); IncSet_drawBar(this->inc);
free(title);
va_end(ap);
} }
void InfoScreen_addLine(InfoScreen* this, const char* line) { void InfoScreen_addLine(InfoScreen* this, const char* line) {
@ -89,7 +89,8 @@ void InfoScreen_run(InfoScreen* this) {
bool looping = true; bool looping = true;
while (looping) { while (looping) {
Panel_draw(panel, true, true); Panel_draw(panel, false, true, true);
IncSet_drawBar(this->inc);
if (this->inc->active) { if (this->inc->active) {
(void) move(LINES - 1, CRT_cursorX); (void) move(LINES - 1, CRT_cursorX);

View File

@ -16,7 +16,6 @@ typedef struct InfoScreen_ {
Object super; Object super;
const Process* process; const Process* process;
Panel* display; Panel* display;
FunctionBar* bar;
IncSet* inc; IncSet* inc;
Vector* lines; Vector* lines;
} InfoScreen; } InfoScreen;

View File

@ -29,7 +29,7 @@ static void ListItem_display(const Object* cast, RichString* out) {
assert (this != NULL); assert (this != NULL);
if (this->moving) { if (this->moving) {
RichString_write(out, CRT_colors[DEFAULT_COLOR], RichString_writeWide(out, CRT_colors[DEFAULT_COLOR],
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
CRT_utf8 ? "" : CRT_utf8 ? "" :
#endif #endif
@ -37,7 +37,7 @@ static void ListItem_display(const Object* cast, RichString* out) {
} else { } else {
RichString_prune(out); RichString_prune(out);
} }
RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value); RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value);
} }
ListItem* ListItem_new(const char* value, int key) { ListItem* ListItem_new(const char* value, int key) {

View File

@ -24,7 +24,7 @@ static const int LoadMeter_attributes[] = {
LOAD LOAD
}; };
static void LoadAverageMeter_updateValues(Meter* this, char* buffer, int size) { static void LoadAverageMeter_updateValues(Meter* this, char* buffer, size_t size) {
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]); 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]); xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
} }
@ -33,14 +33,14 @@ static void LoadAverageMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
char buffer[20]; char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer); RichString_writeAscii(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer); RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer); RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
} }
static void LoadMeter_updateValues(Meter* this, char* buffer, int size) { static void LoadMeter_updateValues(Meter* this, char* buffer, size_t size) {
double five, fifteen; double five, fifteen;
Platform_getLoadAverage(&this->values[0], &five, &fifteen); Platform_getLoadAverage(&this->values[0], &five, &fifteen);
if (this->values[0] > this->total) { if (this->values[0] > this->total) {
@ -53,7 +53,7 @@ static void LoadMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
char buffer[20]; char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD], buffer); RichString_writeAscii(out, CRT_colors[LOAD], buffer);
} }
const MeterClass LoadAverageMeter_class = { const MeterClass LoadAverageMeter_class = {

View File

@ -25,7 +25,7 @@ static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filt
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) { void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this); FunctionBar* bar = MainPanel_getFunctionBar(this);
FunctionBar_setLabel(bar, KEY_F(5), mode ? "Sorted" : "Tree "); FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree ");
} }
void MainPanel_pidSearch(MainPanel* this, int ch) { void MainPanel_pidSearch(MainPanel* this, int ch) {
@ -51,7 +51,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
if (ch != ERR) if (ch != ERR && ch != KEY_RESIZE)
this->state->hideProcessSelection = false; this->state->hideProcessSelection = false;
if (EVENT_IS_HEADER_CLICK(ch)) { if (EVENT_IS_HEADER_CLICK(ch)) {
@ -60,7 +60,11 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Settings* settings = this->state->settings; Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1; int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx); ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) { if (settings->treeView && settings->treeViewAlwaysByPID) {
settings->treeView = false;
settings->direction = 1;
reaction |= Action_setSortKey(settings, field);
} else if (field == Settings_getActiveSortKey(settings)) {
Settings_invertSortOrder(settings); Settings_invertSortOrder(settings);
} else { } else {
reaction |= Action_setSortKey(settings, field); reaction |= Action_setSortKey(settings, field);
@ -96,16 +100,12 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
if (reaction & HTOP_REDRAW_BAR) { if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView); MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc);
if (this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}
} }
if (reaction & HTOP_UPDATE_PANELHDR) { if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super)); ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
} }
if (reaction & HTOP_REFRESH) { if (reaction & HTOP_REFRESH) {
result |= REDRAW; result |= REFRESH;
} }
if (reaction & HTOP_RECALCULATE) { if (reaction & HTOP_RECALCULATE) {
result |= RESCAN; result |= RESCAN;
@ -118,7 +118,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} }
if (!(reaction & HTOP_KEEP_FOLLOWING)) { if (!(reaction & HTOP_KEEP_FOLLOWING)) {
this->state->pl->following = -1; this->state->pl->following = -1;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]); Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
} }
return result; return result;
} }
@ -160,12 +160,21 @@ bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Ar
return ok; return ok;
} }
static void MainPanel_drawFunctionBar(Panel* super) {
MainPanel* this = (MainPanel*) super;
IncSet_drawBar(this->inc);
if (this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}
}
const PanelClass MainPanel_class = { const PanelClass MainPanel_class = {
.super = { .super = {
.extends = Class(Panel), .extends = Class(Panel),
.delete = MainPanel_delete .delete = MainPanel_delete
}, },
.eventHandler = MainPanel_eventHandler .eventHandler = MainPanel_eventHandler,
.drawFunctionBar = MainPanel_drawFunctionBar
}; };
MainPanel* MainPanel_new() { MainPanel* MainPanel_new() {

View File

@ -3,12 +3,14 @@ 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 \ EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png htop.svg \
install-sh autogen.sh missing install-sh autogen.sh missing
applicationsdir = $(datadir)/applications applicationsdir = $(datadir)/applications
applications_DATA = htop.desktop applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png pixmap_DATA = htop.png
appicondir = $(datadir)/icons/hicolor/scalable/apps
appicon_DATA = htop.svg
AM_CFLAGS += -pedantic -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)" AM_CFLAGS += -pedantic -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
AM_LDFLAGS = AM_LDFLAGS =
@ -131,6 +133,7 @@ linux_platform_headers = \
linux/LinuxProcessList.h \ linux/LinuxProcessList.h \
linux/Platform.h \ linux/Platform.h \
linux/PressureStallMeter.h \ linux/PressureStallMeter.h \
linux/ProcessField.h \
linux/SELinuxMeter.h \ linux/SELinuxMeter.h \
linux/SystemdMeter.h \ linux/SystemdMeter.h \
linux/ZramMeter.h \ linux/ZramMeter.h \
@ -162,9 +165,10 @@ endif
# ------- # -------
freebsd_platform_headers = \ freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \ freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \ freebsd/FreeBSDProcess.h \
freebsd/Platform.h \
freebsd/ProcessField.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \ zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \ zfs/ZfsArcStats.h \
@ -182,14 +186,16 @@ endif
# ------------ # ------------
dragonflybsd_platform_headers = \ dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \ dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/Platform.h \
dragonflybsd/ProcessField.h
if HTOP_DRAGONFLYBSD if HTOP_DRAGONFLYBSD
AM_LDFLAGS += -lkvm -lkinfo myhtopplatsources = \
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \ dragonflybsd/Platform.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c
myhtopplatheaders = $(dragonflybsd_platform_headers) myhtopplatheaders = $(dragonflybsd_platform_headers)
endif endif
@ -198,9 +204,10 @@ endif
# ------- # -------
openbsd_platform_headers = \ openbsd_platform_headers = \
openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \ openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h openbsd/OpenBSDProcess.h \
openbsd/Platform.h \
openbsd/ProcessField.h
if HTOP_OPENBSD if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \ myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
@ -213,9 +220,10 @@ endif
# ------ # ------
darwin_platform_headers = \ darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \ darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \ darwin/DarwinProcessList.h \
darwin/Platform.h \
darwin/ProcessField.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \ zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \ zfs/ZfsArcStats.h \
@ -235,6 +243,7 @@ endif
solaris_platform_headers = \ solaris_platform_headers = \
solaris/Platform.h \ solaris/Platform.h \
solaris/ProcessField.h \
solaris/SolarisProcess.h \ solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \ solaris/SolarisProcessList.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
@ -254,6 +263,7 @@ endif
unsupported_platform_headers = \ unsupported_platform_headers = \
unsupported/Platform.h \ unsupported/Platform.h \
unsupported/ProcessField.h \
unsupported/UnsupportedProcess.h \ unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h unsupported/UnsupportedProcessList.h

View File

@ -19,34 +19,33 @@ static const int MemoryMeter_attributes[] = {
MEMORY_CACHE MEMORY_CACHE
}; };
static void MemoryMeter_updateValues(Meter* this, char* buffer, int size) { static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) {
int written; int written;
Platform_setMemoryValues(this); Platform_setMemoryValues(this);
written = Meter_humanUnit(buffer, this->values[0], size); written = Meter_humanUnit(buffer, this->values[0], size);
buffer += written; METER_BUFFER_CHECK(buffer, size, written);
if ((size -= written) > 0) {
*buffer++ = '/'; METER_BUFFER_APPEND_CHR(buffer, size, '/');
size--;
Meter_humanUnit(buffer, this->total, size); Meter_humanUnit(buffer, this->total, size);
}
} }
static void MemoryMeter_display(const Object* cast, RichString* out) { static void MemoryMeter_display(const Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":"); RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, 50); Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], 50); Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[MEMORY_USED], buffer); RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer);
Meter_humanUnit(buffer, this->values[1], 50); Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " buffers:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:");
RichString_append(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer); RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
Meter_humanUnit(buffer, this->values[2], 50); Meter_humanUnit(buffer, this->values[2], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " cache:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:");
RichString_append(out, CRT_colors[MEMORY_CACHE], buffer); RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer);
} }
const MeterClass MemoryMeter_class = { const MeterClass MemoryMeter_class = {

89
Meter.c
View File

@ -20,6 +20,7 @@ in the source distribution for its full text.
#include "Object.h" #include "Object.h"
#include "ProvideCurses.h" #include "ProvideCurses.h"
#include "RichString.h" #include "RichString.h"
#include "Settings.h"
#include "XUtils.h" #include "XUtils.h"
@ -48,10 +49,10 @@ Meter* Meter_new(const struct ProcessList_* pl, int param, const MeterClass* typ
return this; return this;
} }
int Meter_humanUnit(char* buffer, unsigned long int value, int size) { int Meter_humanUnit(char* buffer, unsigned long int value, size_t size) {
const char* prefix = "KMGTPEZY"; const char* prefix = "KMGTPEZY";
unsigned long int powi = 1; unsigned long int powi = 1;
unsigned int written, powj = 1, precision = 2; unsigned int powj = 1, precision = 2;
for (;;) { for (;;) {
if (value / 1024 < powi) if (value / 1024 < powi)
@ -73,10 +74,7 @@ int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
break; break;
} }
written = snprintf(buffer, size, "%.*f%c", return snprintf(buffer, size, "%.*f%c", precision, (double) value / powi, *prefix);
precision, (double) value / powi, *prefix);
return written;
} }
void Meter_delete(Object* cast) { void Meter_delete(Object* cast) {
@ -102,7 +100,7 @@ static inline void Meter_displayBuffer(const Meter* this, const char* buffer, Ri
if (Object_displayFn(this)) { if (Object_displayFn(this)) {
Object_display(this, out); Object_display(this, out);
} else { } else {
RichString_write(out, CRT_colors[Meter_attributes(this)[0]], buffer); RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], buffer);
} }
} }
@ -157,17 +155,21 @@ ListItem* Meter_toListItem(Meter* this, bool moving) {
static void TextMeterMode_draw(Meter* this, int x, int y, int w) { static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN]; char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1); Meter_updateValues(this, buffer, sizeof(buffer));
(void) w;
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
mvaddstr(y, x, this->caption); mvaddnstr(y, x, this->caption, w - 1);
attrset(CRT_colors[RESET_COLOR]);
int captionLen = strlen(this->caption); int captionLen = strlen(this->caption);
x += captionLen; x += captionLen;
attrset(CRT_colors[RESET_COLOR]); w -= captionLen;
if (w <= 0)
return;
RichString_begin(out); RichString_begin(out);
Meter_displayBuffer(this, buffer, &out); Meter_displayBuffer(this, buffer, &out);
RichString_printVal(out, y, x); RichString_printoffnVal(out, y, x, 0, w - 1);
RichString_end(out); RichString_end(out);
} }
@ -177,7 +179,7 @@ static const char BarMeterMode_characters[] = "|#*@$%&.";
static void BarMeterMode_draw(Meter* this, int x, int y, int w) { static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN]; char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1); Meter_updateValues(this, buffer, sizeof(buffer));
w -= 2; w -= 2;
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
@ -187,25 +189,39 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
w -= captionLen; w -= captionLen;
attrset(CRT_colors[BAR_BORDER]); attrset(CRT_colors[BAR_BORDER]);
mvaddch(y, x, '['); mvaddch(y, x, '[');
mvaddch(y, x + w, ']'); mvaddch(y, x + MAXIMUM(w, 0), ']');
attrset(CRT_colors[RESET_COLOR]);
w--; w--;
x++; x++;
if (w < 1) { if (w < 1)
attrset(CRT_colors[RESET_COLOR]);
return; return;
}
// The text in the bar is right aligned; // The text in the bar is right aligned;
// calculate needed padding and generate leading spaces // Pad with maximal spaces and then calculate needed starting position offset
const int textLen = mbstowcs(NULL, buffer, 0);
const int padding = MAXIMUM(w - textLen, 0);
RichString_begin(bar); RichString_begin(bar);
RichString_appendChr(&bar, ' ', padding); RichString_appendChr(&bar, ' ', w);
RichString_append(&bar, 0, buffer); RichString_appendWide(&bar, 0, buffer);
assert(RichString_sizeVal(bar) >= w); int startPos = RichString_sizeVal(bar) - w;
if (startPos > w) {
// Text is too large for bar
// Truncate meter text at a space character
for (int pos = 2 * w; pos > w; pos--) {
if (RichString_getCharVal(bar, pos) == ' ') {
while (pos > w && RichString_getCharVal(bar, pos - 1) == ' ')
pos--;
startPos = pos - w;
break;
}
}
// If still to large, print the start not the end
startPos = MINIMUM(startPos, w);
}
assert(startPos >= 0);
assert(startPos <= w);
assert(startPos + w <= RichString_sizeVal(bar));
int blockSizes[10]; int blockSizes[10];
@ -223,11 +239,11 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// (Control against invalid values) // (Control against invalid values)
nextOffset = CLAMP(nextOffset, 0, w); nextOffset = CLAMP(nextOffset, 0, w);
for (int j = offset; j < nextOffset; j++) for (int j = offset; j < nextOffset; j++)
if (RichString_getCharVal(bar, j) == ' ') { if (RichString_getCharVal(bar, startPos + j) == ' ') {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
RichString_setChar(&bar, j, BarMeterMode_characters[i]); RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]);
} else { } else {
RichString_setChar(&bar, j, '|'); RichString_setChar(&bar, startPos + j, '|');
} }
} }
offset = nextOffset; offset = nextOffset;
@ -236,14 +252,14 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// ...then print the buffer. // ...then print the buffer.
offset = 0; offset = 0;
for (uint8_t i = 0; i < this->curItems; i++) { for (uint8_t i = 0; i < this->curItems; i++) {
attrset(CRT_colors[Meter_attributes(this)[i]]); RichString_setAttrn(&bar, CRT_colors[Meter_attributes(this)[i]], startPos + offset, startPos + offset + blockSizes[i] - 1);
RichString_printoffnVal(bar, y, x + offset, offset, blockSizes[i]); RichString_printoffnVal(bar, y, x + offset, startPos + offset, MINIMUM(blockSizes[i], w - offset));
offset += blockSizes[i]; offset += blockSizes[i];
offset = CLAMP(offset, 0, w); offset = CLAMP(offset, 0, w);
} }
if (offset < w) { if (offset < w) {
attrset(CRT_colors[BAR_SHADOW]); RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, startPos + w - 1);
RichString_printoffnVal(bar, y, x + offset, offset, w - offset); RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
} }
RichString_end(bar); RichString_end(bar);
@ -312,8 +328,8 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
for (int i = 0; i < nValues - 1; i++) for (int i = 0; i < nValues - 1; i++)
data->values[i] = data->values[i + 1]; data->values[i] = data->values[i + 1];
char buffer[nValues]; char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, nValues - 1); Meter_updateValues(this, buffer, sizeof(buffer));
double value = 0.0; double value = 0.0;
for (uint8_t i = 0; i < this->curItems; i++) for (uint8_t i = 0; i < this->curItems; i++)
@ -381,7 +397,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
LEDMeterMode_digits = LEDMeterMode_digitsAscii; LEDMeterMode_digits = LEDMeterMode_digitsAscii;
char buffer[METER_BUFFER_LEN]; char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1); Meter_updateValues(this, buffer, sizeof(buffer));
RichString_begin(out); RichString_begin(out);
Meter_displayBuffer(this, buffer, &out); Meter_displayBuffer(this, buffer, &out);
@ -396,7 +412,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
int xx = x + strlen(this->caption); int xx = x + strlen(this->caption);
int len = RichString_sizeVal(out); int len = RichString_sizeVal(out);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
char c = RichString_getCharVal(out, i); int c = RichString_getCharVal(out, i);
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
LEDMeterMode_drawDigit(xx, y, c - 48); LEDMeterMode_drawDigit(xx, y, c - 48);
xx += 4; xx += 4;
@ -444,8 +460,7 @@ const MeterMode* const Meter_modes[] = {
/* Blank meter */ /* Blank meter */
static void BlankMeter_updateValues(Meter* this, char* buffer, int size) { static void BlankMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t size) {
(void) this; (void) buffer; (void) size;
if (size > 0) { if (size > 0) {
*buffer = 0; *buffer = 0;
} }

27
Meter.h
View File

@ -20,13 +20,36 @@ in the source distribution for its full text.
#define METER_BUFFER_LEN 256 #define METER_BUFFER_LEN 256
#define METER_BUFFER_CHECK(buffer, size, written) \
do { \
if ((written) < 0 || (size_t)(written) >= (size)) { \
return; \
} \
(buffer) += (written); \
(size) -= (size_t)(written); \
} while (0)
#define METER_BUFFER_APPEND_CHR(buffer, size, c) \
do { \
if ((size) < 2) { \
return; \
} \
*(buffer)++ = c; \
*(buffer) = '\0'; \
(size)--; \
if ((size) == 0) { \
return; \
} \
} while (0)
struct Meter_; struct Meter_;
typedef struct Meter_ Meter; typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*); typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*); typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int); typedef void(*Meter_UpdateMode)(Meter*, int);
typedef void(*Meter_UpdateValues)(Meter*, char*, int); typedef void(*Meter_UpdateValues)(Meter*, char*, size_t);
typedef void(*Meter_Draw)(Meter*, int, int, int); typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ { typedef struct MeterClass_ {
@ -101,7 +124,7 @@ extern const MeterClass Meter_class;
Meter* Meter_new(const ProcessList* pl, int param, const MeterClass* type); Meter* Meter_new(const ProcessList* pl, int param, const MeterClass* type);
int Meter_humanUnit(char* buffer, unsigned long int value, int size); int Meter_humanUnit(char* buffer, unsigned long int value, size_t size);
void Meter_delete(Object* cast); void Meter_delete(Object* cast);

View File

@ -20,9 +20,9 @@ in the source distribution for its full text.
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI // Note: In code the meters are known to have bar/text/graph "Modes", but in UI
// we call them "Styles". // we call them "Styles".
static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL}; static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL};
static const char* const MetersKeys[] = {"Space", "Enter", " ", "Del", "F10"}; static const char* const MetersKeys[] = {"Space", "Enter", "", "Del", "F10"};
static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)}; static const int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese // We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning. // terminals, breaking our aligning.
@ -30,7 +30,7 @@ static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
// considered "Ambiguous characters". // considered "Ambiguous characters".
static const char* const MetersMovingFunctions[] = {"Style ", "Lock ", "Up ", "Down ", "Left ", "Right ", " ", "Delete", "Done ", NULL}; static const char* const MetersMovingFunctions[] = {"Style ", "Lock ", "Up ", "Down ", "Left ", "Right ", " ", "Delete", "Done ", NULL};
static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-", "->", " ", "Del", "F10"}; static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-", "->", " ", "Del", "F10"};
static int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)}; static const int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
static FunctionBar* Meters_movingBar = NULL; static FunctionBar* Meters_movingBar = NULL;
void MetersPanel_cleanup() { void MetersPanel_cleanup() {
@ -55,13 +55,12 @@ void MetersPanel_setMoving(MetersPanel* this, bool moving) {
selected->moving = moving; selected->moving = moving;
} }
if (!moving) { if (!moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]); Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
Panel_setDefaultBar(super); Panel_setDefaultBar(super);
} else { } else {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]); Panel_setSelectionColor(super, PANEL_SELECTION_FOLLOW);
super->currentBar = Meters_movingBar; super->currentBar = Meters_movingBar;
} }
FunctionBar_draw(this->super.currentBar);
} }
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) { static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {

View File

@ -24,7 +24,7 @@ static unsigned long int cached_rxp_diff = 0;
static unsigned long int cached_txb_diff = 0; static unsigned long int cached_txb_diff = 0;
static unsigned long int cached_txp_diff = 0; static unsigned long int cached_txp_diff = 0;
static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, int len) { static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t len) {
static unsigned long long int cached_last_update = 0; static unsigned long long int cached_last_update = 0;
struct timeval tv; struct timeval tv;
@ -88,24 +88,24 @@ static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, i
static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
if (!hasData) { if (!hasData) {
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data"); RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
return; return;
} }
char buffer[64]; char buffer[64];
RichString_write(out, CRT_colors[METER_TEXT], "rx: "); RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: ");
Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer)); Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], "iB/s"); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
RichString_append(out, CRT_colors[METER_TEXT], " tx: "); RichString_appendAscii(out, CRT_colors[METER_TEXT], " tx: ");
Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer)); Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff); xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff);
RichString_append(out, CRT_colors[METER_TEXT], buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], buffer);
} }
const MeterClass NetworkIOMeter_class = { const MeterClass NetworkIOMeter_class = {

View File

@ -12,7 +12,9 @@ in the source distribution for its full text.
#include <stdlib.h> #include <stdlib.h>
#include "CRT.h" #include "CRT.h"
#include "Macros.h"
#include "RichString.h" #include "RichString.h"
#include "XUtils.h"
static void OptionItem_delete(Object* cast) { static void OptionItem_delete(Object* cast) {
@ -27,14 +29,14 @@ static void CheckItem_display(const Object* cast, RichString* out) {
const CheckItem* this = (const CheckItem*)cast; const CheckItem* this = (const CheckItem*)cast;
assert (this != NULL); assert (this != NULL);
RichString_write(out, CRT_colors[CHECK_BOX], "["); RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
if (CheckItem_get(this)) { if (CheckItem_get(this)) {
RichString_append(out, CRT_colors[CHECK_MARK], "x"); RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
} else { } else {
RichString_append(out, CRT_colors[CHECK_MARK], " "); RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
} }
RichString_append(out, CRT_colors[CHECK_BOX], "] "); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "] ");
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text); RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
} }
static void NumberItem_display(const Object* cast, RichString* out) { static void NumberItem_display(const Object* cast, RichString* out) {
@ -42,7 +44,7 @@ static void NumberItem_display(const Object* cast, RichString* out) {
assert (this != NULL); assert (this != NULL);
char buffer[12]; char buffer[12];
RichString_write(out, CRT_colors[CHECK_BOX], "["); RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
int written; int written;
if (this->scale < 0) { if (this->scale < 0) {
written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this)); written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this));
@ -51,12 +53,12 @@ static void NumberItem_display(const Object* cast, RichString* out) {
} else { } else {
written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this)); written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this));
} }
RichString_append(out, CRT_colors[CHECK_MARK], buffer); RichString_appendAscii(out, CRT_colors[CHECK_MARK], buffer);
RichString_append(out, CRT_colors[CHECK_BOX], "]"); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
for (int i = written; i < 5; i++) { for (int i = written; i < 5; i++) {
RichString_append(out, CRT_colors[CHECK_BOX], " "); RichString_appendAscii(out, CRT_colors[CHECK_BOX], " ");
} }
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text); RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
} }
const OptionItemClass OptionItem_class = { const OptionItemClass OptionItem_class = {

28
Panel.c
View File

@ -55,11 +55,13 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
this->scrollH = 0; this->scrollH = 0;
this->selected = 0; this->selected = 0;
this->oldSelected = 0; this->oldSelected = 0;
this->selectedLen = 0;
this->needsRedraw = true; this->needsRedraw = true;
this->wasFocus = false;
RichString_beginAllocated(this->header); RichString_beginAllocated(this->header);
this->defaultBar = fuBar; this->defaultBar = fuBar;
this->currentBar = fuBar; this->currentBar = fuBar;
this->selectionColor = CRT_colors[PANEL_SELECTION_FOCUS]; this->selectionColorId = PANEL_SELECTION_FOCUS;
} }
void Panel_done(Panel* this) { void Panel_done(Panel* this) {
@ -70,8 +72,8 @@ void Panel_done(Panel* this) {
RichString_end(this->header); RichString_end(this->header);
} }
void Panel_setSelectionColor(Panel* this, int color) { void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
this->selectionColor = color; this->selectionColorId = colorId;
} }
RichString* Panel_getHeader(Panel* this) { RichString* Panel_getHeader(Panel* this) {
@ -82,7 +84,7 @@ RichString* Panel_getHeader(Panel* this) {
} }
inline void Panel_setHeader(Panel* this, const char* header) { inline void Panel_setHeader(Panel* this, const char* header) {
RichString_write(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header); RichString_writeWide(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
this->needsRedraw = true; this->needsRedraw = true;
} }
@ -217,7 +219,7 @@ void Panel_splice(Panel* this, Vector* from) {
this->needsRedraw = true; this->needsRedraw = true;
} }
void Panel_draw(Panel* this, bool focus, bool highlightSelected) { void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected) {
assert (this != NULL); assert (this != NULL);
int size = Vector_size(this->items); int size = Vector_size(this->items);
@ -234,6 +236,7 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
attrset(attr); attrset(attr);
mvhline(y, x, ' ', this->w); mvhline(y, x, ' ', this->w);
if (scrollH < headerLen) { if (scrollH < headerLen) {
RichString_setAttr(&this->header, attr);
RichString_printoffnVal(this->header, y, x, scrollH, RichString_printoffnVal(this->header, y, x, scrollH,
MINIMUM(headerLen - scrollH, this->w)); MINIMUM(headerLen - scrollH, this->w));
} }
@ -262,10 +265,10 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
int upTo = MINIMUM(first + h, size); int upTo = MINIMUM(first + h, size);
int selectionColor = focus int selectionColor = focus
? this->selectionColor ? CRT_colors[this->selectionColorId]
: CRT_colors[PANEL_SELECTION_UNFOCUS]; : CRT_colors[PANEL_SELECTION_UNFOCUS];
if (this->needsRedraw) { if (this->needsRedraw || force_redraw) {
int line = 0; int line = 0;
for (int i = first; line < h && i < upTo; i++) { for (int i = first; line < h && i < upTo; i++) {
Object* itemObj = Vector_get(this->items, i); Object* itemObj = Vector_get(this->items, i);
@ -293,7 +296,6 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
mvhline(y + line, x, ' ', this->w); mvhline(y + line, x, ' ', this->w);
line++; line++;
} }
this->needsRedraw = false;
} else { } else {
Object* oldObj = Vector_get(this->items, this->oldSelected); Object* oldObj = Vector_get(this->items, this->oldSelected);
@ -319,7 +321,17 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
RichString_end(new); RichString_end(new);
RichString_end(old); RichString_end(old);
} }
if (focus && (this->needsRedraw || force_redraw || !this->wasFocus)) {
if (Panel_drawFunctionBarFn(this))
Panel_drawFunctionBar(this);
else
FunctionBar_draw(this->currentBar);
}
this->oldSelected = this->selected; this->oldSelected = this->selected;
this->wasFocus = focus;
this->needsRedraw = false;
move(0, 0); move(0, 0);
} }

27
Panel.h
View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include <stdbool.h> #include <stdbool.h>
#include "CRT.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Object.h" #include "Object.h"
#include "RichString.h" #include "RichString.h"
@ -22,9 +23,10 @@ typedef enum HandlerResult_ {
HANDLED = 0x01, HANDLED = 0x01,
IGNORED = 0x02, IGNORED = 0x02,
BREAK_LOOP = 0x04, BREAK_LOOP = 0x04,
REDRAW = 0x08, REFRESH = 0x08,
RESCAN = 0x10, REDRAW = 0x10,
SYNTH_KEY = 0x20, RESCAN = 0x20,
SYNTH_KEY = 0x40,
} HandlerResult; } HandlerResult;
#define EVENT_SET_SELECTED (-1) #define EVENT_SET_SELECTED (-1)
@ -33,16 +35,20 @@ typedef enum HandlerResult_ {
#define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000) #define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000) #define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000)
typedef HandlerResult(*Panel_EventHandler)(Panel*, int); typedef HandlerResult (*Panel_EventHandler)(Panel*, int);
typedef void (*Panel_DrawFunctionBar)(Panel*);
typedef struct PanelClass_ { typedef struct PanelClass_ {
const ObjectClass super; const ObjectClass super;
const Panel_EventHandler eventHandler; const Panel_EventHandler eventHandler;
const Panel_DrawFunctionBar drawFunctionBar;
} PanelClass; } PanelClass;
#define As_Panel(this_) ((const PanelClass*)((this_)->super.klass)) #define As_Panel(this_) ((const PanelClass*)((this_)->super.klass))
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler #define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
#define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_) #define Panel_eventHandler(this_, ev_) (assert(As_Panel(this_)->eventHandler), As_Panel(this_)->eventHandler((Panel*)(this_), ev_))
#define Panel_drawFunctionBarFn(this_) As_Panel(this_)->drawFunctionBar
#define Panel_drawFunctionBar(this_) (assert(As_Panel(this_)->drawFunctionBar), As_Panel(this_)->drawFunctionBar((Panel*)(this_)))
struct Panel_ { struct Panel_ {
Object super; Object super;
@ -55,10 +61,11 @@ struct Panel_ {
int scrollV; int scrollV;
short scrollH; short scrollH;
bool needsRedraw; bool needsRedraw;
bool wasFocus;
FunctionBar* currentBar; FunctionBar* currentBar;
FunctionBar* defaultBar; FunctionBar* defaultBar;
RichString header; RichString header;
int selectionColor; ColorElements selectionColorId;
}; };
#define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0) #define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0)
@ -75,7 +82,7 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
void Panel_done(Panel* this); void Panel_done(Panel* this);
void Panel_setSelectionColor(Panel* this, int color); void Panel_setSelectionColor(Panel* this, ColorElements colorId);
RichString* Panel_getHeader(Panel* this); RichString* Panel_getHeader(Panel* this);
@ -109,7 +116,7 @@ int Panel_size(Panel* this);
void Panel_setSelected(Panel* this, int selected); void Panel_setSelected(Panel* this, int selected);
void Panel_draw(Panel* this, bool focus, bool highlightSelected); void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected);
void Panel_splice(Panel* this, Vector* from); void Panel_splice(Panel* this, Vector* from);

139
Process.c
View File

@ -38,23 +38,15 @@ in the source distribution for its full text.
static uid_t Process_getuid = (uid_t)-1; static uid_t Process_getuid = (uid_t)-1;
char Process_pidFormat[20] = "%7d "; int Process_pidDigits = 7;
static char Process_titleBuffer[20][20];
void Process_setupColumnWidths() { void Process_setupColumnWidths() {
int maxPid = Platform_getMaxPid(); int maxPid = Platform_getMaxPid();
if (maxPid == -1) if (maxPid == -1)
return; return;
int digits = ceil(log10(maxPid)); Process_pidDigits = ceil(log10(maxPid));
assert(digits < 20); assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS);
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 long number, bool coloring) { void Process_humanNumber(RichString* str, unsigned long long number, bool coloring) {
@ -74,53 +66,53 @@ void Process_humanNumber(RichString* str, unsigned long long number, bool colori
if (number < 1000) { if (number < 1000) {
//Plain number, no markings //Plain number, no markings
len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number); len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number);
RichString_appendn(str, processColor, buffer, len); RichString_appendnAscii(str, processColor, buffer, len);
} else if (number < 100000) { } else if (number < 100000) {
//2 digit MB, 3 digit KB //2 digit MB, 3 digit KB
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/1000); len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/1000);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendnAscii(str, processMegabytesColor, buffer, len);
number %= 1000; number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number); len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number);
RichString_appendn(str, processColor, buffer, len); RichString_appendnAscii(str, processColor, buffer, len);
} else if (number < 1000 * ONE_K) { } else if (number < 1000 * ONE_K) {
//3 digit MB //3 digit MB
number /= ONE_K; number /= ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number); len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendnAscii(str, processMegabytesColor, buffer, len);
} else if (number < 10000 * ONE_K) { } else if (number < 10000 * ONE_K) {
//1 digit GB, 3 digit MB //1 digit GB, 3 digit MB
number /= ONE_K; number /= ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000); len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
RichString_appendn(str, processGigabytesColor, buffer, len); RichString_appendnAscii(str, processGigabytesColor, buffer, len);
number %= 1000; number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number); len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendnAscii(str, processMegabytesColor, buffer, len);
} else if (number < 100000 * ONE_K) { } else if (number < 100000 * ONE_K) {
//2 digit GB, 1 digit MB //2 digit GB, 1 digit MB
number /= 100 * ONE_K; number /= 100 * ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/10); len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/10);
RichString_appendn(str, processGigabytesColor, buffer, len); RichString_appendnAscii(str, processGigabytesColor, buffer, len);
number %= 10; number %= 10;
len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number); len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendnAscii(str, processMegabytesColor, buffer, len);
RichString_append(str, processGigabytesColor, "G "); RichString_appendAscii(str, processGigabytesColor, "G ");
} else if (number < 1000 * ONE_M) { } else if (number < 1000 * ONE_M) {
//3 digit GB //3 digit GB
number /= ONE_M; number /= ONE_M;
len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number); len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number);
RichString_appendn(str, processGigabytesColor, buffer, len); RichString_appendnAscii(str, processGigabytesColor, buffer, len);
} else if (number < 10000ULL * ONE_M) { } else if (number < 10000ULL * ONE_M) {
//1 digit TB, 3 digit GB //1 digit TB, 3 digit GB
number /= ONE_M; number /= ONE_M;
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000); len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendnAscii(str, largeNumberColor, buffer, len);
number %= 1000; number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number); len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number);
RichString_appendn(str, processGigabytesColor, buffer, len); RichString_appendnAscii(str, processGigabytesColor, buffer, len);
} else { } else {
//2 digit TB and above //2 digit TB and above
len = xSnprintf(buffer, sizeof(buffer), "%4.1lfT ", (double)number/ONE_G); len = xSnprintf(buffer, sizeof(buffer), "%4.1lfT ", (double)number/ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendnAscii(str, largeNumberColor, buffer, len);
} }
} }
@ -139,26 +131,25 @@ void Process_colorNumber(RichString* str, unsigned long long number, bool colori
} }
if (number == ULLONG_MAX) { if (number == ULLONG_MAX) {
int len = xSnprintf(buffer, sizeof(buffer), " N/A "); RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (number >= 100000LL * ONE_DECIMAL_T) { } else if (number >= 100000LL * ONE_DECIMAL_T) {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G); xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G);
RichString_appendn(str, largeNumberColor, buffer, 12); RichString_appendnAscii(str, largeNumberColor, buffer, 12);
} else if (number >= 100LL * ONE_DECIMAL_T) { } else if (number >= 100LL * ONE_DECIMAL_T) {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M); xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M);
RichString_appendn(str, largeNumberColor, buffer, 8); RichString_appendnAscii(str, largeNumberColor, buffer, 8);
RichString_appendn(str, processMegabytesColor, buffer+8, 4); RichString_appendnAscii(str, processMegabytesColor, buffer+8, 4);
} else if (number >= 10LL * ONE_DECIMAL_G) { } else if (number >= 10LL * ONE_DECIMAL_G) {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K); xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K);
RichString_appendn(str, largeNumberColor, buffer, 5); RichString_appendnAscii(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3); RichString_appendnAscii(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4); RichString_appendnAscii(str, processColor, buffer+8, 4);
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number); xSnprintf(buffer, sizeof(buffer), "%11llu ", number);
RichString_appendn(str, largeNumberColor, buffer, 2); RichString_appendnAscii(str, largeNumberColor, buffer, 2);
RichString_appendn(str, processMegabytesColor, buffer+2, 3); RichString_appendnAscii(str, processMegabytesColor, buffer+2, 3);
RichString_appendn(str, processColor, buffer+5, 3); RichString_appendnAscii(str, processColor, buffer+5, 3);
RichString_appendn(str, processShadowColor, buffer+8, 4); RichString_appendnAscii(str, processShadowColor, buffer+8, 4);
} }
} }
@ -172,16 +163,16 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths) {
char buffer[10]; char buffer[10];
if (hours >= 100) { if (hours >= 100) {
xSnprintf(buffer, sizeof(buffer), "%7lluh ", hours); xSnprintf(buffer, sizeof(buffer), "%7lluh ", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer); RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer);
} else { } else {
if (hours) { if (hours) {
xSnprintf(buffer, sizeof(buffer), "%2lluh", hours); xSnprintf(buffer, sizeof(buffer), "%2lluh", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer); RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer);
xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds); xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds);
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths); xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths);
} }
RichString_append(str, CRT_colors[DEFAULT_COLOR], buffer); RichString_appendAscii(str, CRT_colors[DEFAULT_COLOR], buffer);
} }
} }
@ -216,14 +207,14 @@ static inline void Process_writeCommand(const Process* this, int attr, int basea
finish += start - 1; finish += start - 1;
} }
RichString_append(str, attr, comm); RichString_appendWide(str, attr, comm);
if (this->settings->highlightBaseName) { if (this->settings->highlightBaseName) {
RichString_setAttrn(str, baseattr, start, finish); RichString_setAttrn(str, baseattr, start, finish);
} }
} }
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring) { void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring) {
int largeNumberColor = CRT_colors[LARGE_NUMBER]; int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
int processColor = CRT_colors[PROCESS]; int processColor = CRT_colors[PROCESS];
@ -234,23 +225,22 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
} }
if (isnan(rate)) { if (isnan(rate)) {
int len = xSnprintf(buffer, n, " N/A "); RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (rate < ONE_K) { } else if (rate < ONE_K) {
int len = snprintf(buffer, n, "%7.2f B/s ", rate); int len = snprintf(buffer, n, "%7.2f B/s ", rate);
RichString_appendn(str, processColor, buffer, len); RichString_appendnAscii(str, processColor, buffer, len);
} else if (rate < ONE_M) { } else if (rate < ONE_M) {
int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K); int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K);
RichString_appendn(str, processColor, buffer, len); RichString_appendnAscii(str, processColor, buffer, len);
} else if (rate < ONE_G) { } else if (rate < ONE_G) {
int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M); int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendnAscii(str, processMegabytesColor, buffer, len);
} else if (rate < ONE_T) { } else if (rate < ONE_T) {
int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G); int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendnAscii(str, largeNumberColor, buffer, len);
} else { } else {
int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T); int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendnAscii(str, largeNumberColor, buffer, len);
} }
} }
@ -258,7 +248,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
int baseattr = CRT_colors[PROCESS_BASENAME]; int baseattr = CRT_colors[PROCESS_BASENAME];
int n = sizeof(buffer) - 1; size_t n = sizeof(buffer) - 1;
bool coloring = this->settings->highlightMegabytes; bool coloring = this->settings->highlightMegabytes;
switch (field) { switch (field) {
@ -312,7 +302,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
} else { } else {
ret = snprintf(buf, n, " "); ret = snprintf(buf, n, " ");
} }
if (ret < 0 || ret >= n) { if (ret < 0 || (size_t)ret >= n) {
written = n; written = n;
} else { } else {
written = ret; written = ret;
@ -320,17 +310,18 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
buf += written; buf += written;
n -= written; n -= written;
} }
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
RichString_append(str, CRT_colors[PROCESS_TREE], buffer); RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
Process_writeCommand(this, attr, baseattr, str); Process_writeCommand(this, attr, baseattr, str);
return; return;
} }
} }
case MAJFLT: Process_colorNumber(str, this->majflt, coloring); return; case MAJFLT: Process_colorNumber(str, this->majflt, coloring); return;
case MINFLT: Process_colorNumber(str, this->minflt, coloring); return; case MINFLT: Process_colorNumber(str, this->minflt, coloring); return;
case M_RESIDENT: Process_humanNumber(str, this->m_resident * CRT_pageSizeKB, coloring); return; case M_RESIDENT: Process_humanNumber(str, this->m_resident, coloring); return;
case M_VIRT: Process_humanNumber(str, this->m_virt * CRT_pageSizeKB, coloring); return; case M_VIRT: Process_humanNumber(str, this->m_virt, coloring); return;
case NICE: { case NICE: {
xSnprintf(buffer, n, "%3ld ", this->nice); xSnprintf(buffer, n, "%3ld ", this->nice);
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY] attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
@ -339,9 +330,9 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
break; break;
} }
case NLWP: xSnprintf(buffer, n, "%4ld ", this->nlwp); break; case NLWP: xSnprintf(buffer, n, "%4ld ", this->nlwp); break;
case PGRP: xSnprintf(buffer, n, Process_pidFormat, this->pgrp); break; case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break;
case PID: xSnprintf(buffer, n, Process_pidFormat, this->pid); break; case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break;
case PPID: xSnprintf(buffer, n, Process_pidFormat, this->ppid); break; case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break;
case PRIORITY: { case PRIORITY: {
if(this->priority <= -100) if(this->priority <= -100)
xSnprintf(buffer, n, " RT "); xSnprintf(buffer, n, " RT ");
@ -350,7 +341,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
break; break;
} }
case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break; case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break;
case SESSION: xSnprintf(buffer, n, Process_pidFormat, this->session); break; case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break;
case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break; case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
case STATE: { case STATE: {
xSnprintf(buffer, n, "%c ", this->state); xSnprintf(buffer, n, "%c ", this->state);
@ -366,8 +357,8 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
} }
case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break; case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break;
case TIME: Process_printTime(str, this->time); return; case TIME: Process_printTime(str, this->time); return;
case TGID: xSnprintf(buffer, n, Process_pidFormat, this->tgid); break; case TGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tgid); break;
case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break; case TPGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tpgid); break;
case TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break; case TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break;
case USER: { case USER: {
if (Process_getuid != this->st_uid) if (Process_getuid != this->st_uid)
@ -386,7 +377,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
default: default:
xSnprintf(buffer, n, "- "); xSnprintf(buffer, n, "- ");
} }
RichString_append(str, attr, buffer); RichString_appendWide(str, attr, buffer);
} }
void Process_display(const Object* cast, RichString* out) { void Process_display(const Object* cast, RichString* out) {
@ -449,13 +440,13 @@ void Process_init(Process* this, const struct Settings_* settings) {
} }
void Process_toggleTag(Process* this) { void Process_toggleTag(Process* this) {
this->tag = this->tag == true ? false : true; this->tag = !this->tag;
} }
bool Process_isNew(const Process* this) { bool Process_isNew(const Process* this) {
assert(this->processList); assert(this->processList);
if (this->processList->scanTs >= this->seenTs) { if (this->processList->scanTs >= this->seenTs) {
return this->processList->scanTs - this->seenTs <= this->processList->settings->highlightDelaySecs; return this->processList->scanTs - this->seenTs <= 1000 * this->processList->settings->highlightDelaySecs;
} }
return false; return false;
} }
@ -489,15 +480,15 @@ bool Process_sendSignal(Process* this, Arg sgn) {
long Process_pidCompare(const void* v1, const void* v2) { long Process_pidCompare(const void* v1, const void* v2) {
const Process* p1 = (const Process*)v1; const Process* p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2; const Process* p2 = (const Process*)v2;
return (p1->pid - p2->pid);
return SPACESHIP_NUMBER(p1->pid, p2->pid);
} }
long Process_compare(const void* v1, const void* v2) { long Process_compare(const void* v1, const void* v2) {
const Process *p1, *p2; const Process *p1, *p2;
const Settings *settings = ((const Process*)v1)->settings; const Settings *settings = ((const Process*)v1)->settings;
int r;
if (settings->direction == 1) { if (Settings_getActiveDirection(settings) == 1) {
p1 = (const Process*)v1; p1 = (const Process*)v1;
p2 = (const Process*)v2; p2 = (const Process*)v2;
} else { } else {
@ -505,7 +496,21 @@ long Process_compare(const void* v1, const void* v2) {
p1 = (const Process*)v2; p1 = (const Process*)v2;
} }
switch (settings->sortKey) { ProcessField key = Settings_getActiveSortKey(settings);
long result = Process_compareByKey(p1, p2, key);
// Implement tie-breaker (needed to make tree mode more stable)
if (!result)
result = SPACESHIP_NUMBER(p1->pid, p2->pid);
return result;
}
long Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) {
int r;
switch (key) {
case PERCENT_CPU: case PERCENT_CPU:
case PERCENT_NORM_CPU: case PERCENT_NORM_CPU:
return SPACESHIP_NUMBER(p2->percent_cpu, p1->percent_cpu); return SPACESHIP_NUMBER(p2->percent_cpu, p1->percent_cpu);

View File

@ -10,21 +10,17 @@ in the source distribution for its full text.
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include "Object.h" #include "Object.h"
#include "ProcessField.h"
#include "RichString.h" #include "RichString.h"
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x0001
#define DEFAULT_HIGHLIGHT_SECS 5 #define DEFAULT_HIGHLIGHT_SECS 5
typedef enum ProcessFields { typedef enum ProcessField_ {
NULL_PROCESSFIELD = 0, NULL_PROCESSFIELD = 0,
PID = 1, PID = 1,
COMM = 2, COMM = 2,
@ -50,12 +46,12 @@ typedef enum ProcessFields {
NLWP = 51, NLWP = 51,
TGID = 52, TGID = 52,
PERCENT_NORM_CPU = 53, PERCENT_NORM_CPU = 53,
} ProcessField;
typedef struct ProcessPidColumn_ { /* Platform specific fields, defined in ${platform}/ProcessField.h */
int id; PLATFORM_PROCESS_FIELDS
const char* label;
} ProcessPidColumn; LAST_PROCESSFIELD
} ProcessField;
struct Settings_; struct Settings_;
@ -121,6 +117,7 @@ typedef struct ProcessFieldData_ {
const char* title; const char* title;
const char* description; const char* description;
uint32_t flags; uint32_t flags;
bool pidColumn;
} ProcessFieldData; } ProcessFieldData;
// Implemented in platform-specific code: // Implemented in platform-specific code:
@ -128,23 +125,26 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
long Process_compare(const void* v1, const void* v2); long Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast); void Process_delete(Object* cast);
bool Process_isThread(const Process* this); bool Process_isThread(const Process* this);
extern ProcessFieldData Process_fields[]; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern ProcessPidColumn Process_pidColumns[]; #define PROCESS_MAX_PID_DIGITS 19
extern char Process_pidFormat[20]; extern int Process_pidDigits;
typedef Process*(*Process_New)(const struct Settings_*); typedef Process*(*Process_New)(const struct Settings_*);
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField); typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
typedef long (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
typedef const char* (*Process_GetCommandStr)(const Process*); typedef const char* (*Process_GetCommandStr)(const Process*);
typedef struct ProcessClass_ { typedef struct ProcessClass_ {
const ObjectClass super; const ObjectClass super;
const Process_WriteField writeField; const Process_WriteField writeField;
const Process_CompareByKey compareByKey;
const Process_GetCommandStr getCommandStr; const Process_GetCommandStr getCommandStr;
} ProcessClass; } ProcessClass;
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass)) #define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm) #define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm)
#define Process_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_))
static inline pid_t Process_getParentPid(const Process* this) { static inline pid_t Process_getParentPid(const Process* this) {
return this->tgid == this->pid ? this->ppid : this->tgid; return this->tgid == this->pid ? this->ppid : this->tgid;
@ -177,7 +177,7 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_fillStarttimeBuffer(Process* this); void Process_fillStarttimeBuffer(Process* this);
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring); void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring);
void Process_display(const Object* cast, RichString* out); void Process_display(const Object* cast, RichString* out);
@ -201,4 +201,6 @@ bool Process_sendSignal(Process* this, Arg sgn);
long Process_pidCompare(const void* v1, const void* v2); long Process_pidCompare(const void* v1, const void* v2);
long Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
#endif #endif

View File

@ -10,10 +10,11 @@ in the source distribution for its full text.
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include "Compat.h"
#include "CRT.h" #include "CRT.h"
#include "Hashtable.h" #include "Hashtable.h"
#include "Macros.h"
#include "Vector.h" #include "Vector.h"
#include "XUtils.h" #include "XUtils.h"
@ -78,22 +79,41 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
this->panel = panel; this->panel = panel;
} }
static const char* alignedProcessFieldTitle(ProcessField field) {
const char* title = Process_fields[field].title;
if (!title)
return "- ";
if (!Process_fields[field].pidColumn)
return title;
static char titleBuffer[PROCESS_MAX_PID_DIGITS + /* space */ 1 + /* null-terminator */ + 1];
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
return titleBuffer;
}
void ProcessList_printHeader(ProcessList* this, RichString* header) { void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header); RichString_prune(header);
const ProcessField* fields = this->settings->fields; const Settings* settings = this->settings;
const ProcessField* fields = settings->fields;
ProcessField key = Settings_getActiveSortKey(settings);
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title; int color;
if (!field) { if (settings->treeView && settings->treeViewAlwaysByPID) {
field = "- "; color = CRT_colors[PANEL_HEADER_FOCUS];
} else if (key == fields[i]) {
color = CRT_colors[PANEL_SELECTION_FOCUS];
} else {
color = CRT_colors[PANEL_HEADER_FOCUS];
} }
int color = (this->settings->sortKey == fields[i]) ? RichString_appendWide(header, color, alignedProcessFieldTitle(fields[i]));
CRT_colors[PANEL_SELECTION_FOCUS] : CRT_colors[PANEL_HEADER_FOCUS]; if (COMM == fields[i] && settings->showMergedCommand) {
RichString_append(header, color, field); RichString_appendAscii(header, color, "(merged)");
if (COMM == fields[i] && this->settings->showMergedCommand) {
RichString_append(header, color, "(merged)");
} }
} }
} }
@ -148,7 +168,7 @@ int ProcessList_size(ProcessList* this) {
// //
// The algorithm is based on `depth-first search`, // The algorithm is based on `depth-first search`,
// even though `breadth-first search` approach may be more efficient on first glance, // even though `breadth-first search` approach may be more efficient on first glance,
// after comparision it may be not, as it's not safe to go deeper without first updating the tree structure. // after comparison it may be not, as it's not safe to go deeper without first updating the tree structure.
// If it would be safe that approach would likely bring an advantage in performance. // If it would be safe that approach would likely bring an advantage in performance.
// //
// Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth. // Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
@ -346,7 +366,7 @@ static long ProcessList_treeProcessCompareByPID(const void* v1, const void* v2)
static void ProcessList_buildTree(ProcessList* this) { static void ProcessList_buildTree(ProcessList* this) {
int node_counter = 1; int node_counter = 1;
int node_index = 0; int node_index = 0;
int direction = this->settings->direction; int direction = Settings_getActiveDirection(this->settings);
// Sort by PID // Sort by PID
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID); Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
@ -445,12 +465,7 @@ ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
const ProcessField* fields = this->settings->fields; const ProcessField* fields = this->settings->fields;
ProcessField field; ProcessField field;
for (int i = 0; (field = fields[i]); i++) { for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title; int len = strlen(alignedProcessFieldTitle(field));
if (!title) {
title = "- ";
}
int len = strlen(title);
if (at >= x && at <= x + len) { if (at >= x && at <= x + len) {
return field; return field;
} }
@ -478,23 +493,20 @@ void ProcessList_rebuildPanel(ProcessList* this) {
int size = ProcessList_size(this); int size = ProcessList_size(this);
int idx = 0; int idx = 0;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
bool hidden = false;
Process* p = ProcessList_get(this, i); Process* p = ProcessList_get(this, i);
if ( (!p->show) if ( (!p->show)
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId)) || (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter))) || (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) ) || (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
hidden = true; continue;
if (!hidden) { Panel_set(this->panel, idx, (Object*)p);
Panel_set(this->panel, idx, (Object*)p); if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == currPid)) {
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == currPid)) { Panel_setSelected(this->panel, idx);
Panel_setSelected(this->panel, idx); this->panel->scrollV = currScrollV;
this->panel->scrollV = currScrollV;
}
idx++;
} }
idx++;
} }
} }
@ -540,8 +552,10 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
if (!firstScanDone) { if (!firstScanDone) {
this->scanTs = 0; this->scanTs = 0;
firstScanDone = true; firstScanDone = true;
} else if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) { } else if (Compat_clock_monotonic_gettime(&now) == 0) {
this->scanTs = now.tv_sec; // save time in millisecond, so with a delay in deciseconds
// there are no irregularities
this->scanTs = 1000 * now.tv_sec + now.tv_nsec / 1000000;
} }
ProcessList_goThroughEntries(this, false); ProcessList_goThroughEntries(this, false);
@ -557,7 +571,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
// process no longer exists // process no longer exists
if (this->settings->highlightChanges && p->wasShown) { if (this->settings->highlightChanges && p->wasShown) {
// mark tombed // mark tombed
p->tombTs = this->scanTs + this->settings->highlightDelaySecs; p->tombTs = this->scanTs + 1000 * this->settings->highlightDelaySecs;
} else { } else {
// immediately remove // immediately remove
ProcessList_remove(this, p); ProcessList_remove(this, p);

View File

@ -7,6 +7,7 @@ in the source distribution for its full text.
#include "RichString.h" #include "RichString.h"
#include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -47,7 +48,7 @@ static void RichString_setLen(RichString* this, int len) {
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) { static inline void RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
wchar_t data[len + 1]; wchar_t data[len + 1];
len = mbstowcs(data, data_c, len); len = mbstowcs(data, data_c, len);
if (len < 0) if (len < 0)
@ -60,6 +61,14 @@ static inline void RichString_writeFrom(RichString* this, int attrs, const char*
} }
} }
static inline void RichString_writeFromAscii(RichString* this, int attrs, const char* data, int from, int len) {
int newLen = from + len;
RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++) {
this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint(data[j]) ? data[j] : '?') } };
}
}
inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) { inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
cchar_t* ch = this->chptr + start; cchar_t* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1); finish = CLAMP(finish, 0, this->chlen - 1);
@ -82,15 +91,19 @@ int RichString_findChar(RichString* this, char c, int start) {
#else /* HAVE_LIBNCURSESW */ #else /* HAVE_LIBNCURSESW */
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) { static inline void RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
int newLen = from + len; int newLen = from + len;
RichString_setLen(this, newLen); RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++) { for (int i = from, j = 0; i < newLen; i++, j++) {
this->chptr[i] = (data_c[j] >= 32 ? data_c[j] : '?') | attrs; this->chptr[i] = (((unsigned char)data_c[j]) >= 32 ? ((unsigned char)data_c[j]) : '?') | attrs;
} }
this->chptr[newLen] = 0; this->chptr[newLen] = 0;
} }
static inline void RichString_writeFromAscii(RichString* this, int attrs, const char* data_c, int from, int len) {
RichString_writeFromWide(this, attrs, data_c, from, len);
}
void RichString_setAttrn(RichString* this, int attrs, int start, int finish) { void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
chtype* ch = this->chptr + start; chtype* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1); finish = CLAMP(finish, 0, this->chlen - 1);
@ -132,14 +145,26 @@ void RichString_setAttr(RichString* this, int attrs) {
RichString_setAttrn(this, attrs, 0, this->chlen - 1); RichString_setAttrn(this, attrs, 0, this->chlen - 1);
} }
void RichString_append(RichString* this, int attrs, const char* data) { void RichString_appendWide(RichString* this, int attrs, const char* data) {
RichString_writeFrom(this, attrs, data, this->chlen, strlen(data)); RichString_writeFromWide(this, attrs, data, this->chlen, strlen(data));
} }
void RichString_appendn(RichString* this, int attrs, const char* data, int len) { void RichString_appendnWide(RichString* this, int attrs, const char* data, int len) {
RichString_writeFrom(this, attrs, data, this->chlen, len); RichString_writeFromWide(this, attrs, data, this->chlen, len);
} }
void RichString_write(RichString* this, int attrs, const char* data) { void RichString_writeWide(RichString* this, int attrs, const char* data) {
RichString_writeFrom(this, attrs, data, 0, strlen(data)); RichString_writeFromWide(this, attrs, data, 0, strlen(data));
}
void RichString_appendAscii(RichString* this, int attrs, const char* data) {
RichString_writeFromAscii(this, attrs, data, this->chlen, strlen(data));
}
void RichString_appendnAscii(RichString* this, int attrs, const char* data, int len) {
RichString_writeFromAscii(this, attrs, data, this->chlen, len);
}
void RichString_writeAscii(RichString* this, int attrs, const char* data) {
RichString_writeFromAscii(this, attrs, data, 0, strlen(data));
} }

View File

@ -52,10 +52,16 @@ void RichString_setAttr(RichString* this, int attrs);
void RichString_appendChr(RichString* this, char c, int count); void RichString_appendChr(RichString* this, char c, int count);
void RichString_append(RichString* this, int attrs, const char* data); void RichString_appendWide(RichString* this, int attrs, const char* data);
void RichString_appendn(RichString* this, int attrs, const char* data, int len); void RichString_appendnWide(RichString* this, int attrs, const char* data, int len);
void RichString_write(RichString* this, int attrs, const char* data); void RichString_writeWide(RichString* this, int attrs, const char* data);
void RichString_appendAscii(RichString* this, int attrs, const char* data);
void RichString_appendnAscii(RichString* this, int attrs, const char* data, int len);
void RichString_writeAscii(RichString* this, int attrs, const char* data);
#endif #endif

View File

@ -106,7 +106,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) { if (*rescan) {
*oldTime = newTime; *oldTime = newTime;
ProcessList_scan(pl, this->state->pauseProcessUpdate); ProcessList_scan(pl, this->state->pauseProcessUpdate);
if (*sortTimeout == 0 || this->settings->treeView) { if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) {
ProcessList_sort(pl); ProcessList_sort(pl);
*sortTimeout = 1; *sortTimeout = 1;
} }
@ -119,29 +119,20 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
*rescan = false; *rescan = false;
} }
static void ScreenManager_drawPanels(ScreenManager* this, int focus) { static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_redraw) {
const int nPanels = this->panelCount; const int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) { for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i); Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_draw(panel, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection)); Panel_draw(panel, force_redraw, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection));
mvvline(panel->y, panel->x + panel->w, ' ', panel->h + 1); mvvline(panel->y, panel->x + panel->w, ' ', panel->h + 1);
} }
} }
static Panel* setCurrentPanel(const ScreenManager* this, Panel* panel) {
FunctionBar_draw(panel->currentBar);
if (panel == this->state->panel && this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}
return panel;
}
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false; bool quit = false;
int focus = 0; int focus = 0;
Panel* panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus)); Panel* panelFocus = (Panel*) Vector_get(this->panels, focus);
double oldTime = 0.0; double oldTime = 0.0;
@ -150,6 +141,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool timedOut = true; bool timedOut = true;
bool redraw = true; bool redraw = true;
bool force_redraw = false;
bool rescan = false; bool rescan = false;
int sortTimeout = 0; int sortTimeout = 0;
int resetSortTimeout = 5; int resetSortTimeout = 5;
@ -159,8 +151,9 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut); checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut);
} }
if (redraw) { if (redraw || force_redraw) {
ScreenManager_drawPanels(this, focus); ScreenManager_drawPanels(this, focus, force_redraw);
force_redraw = false;
} }
int prevCh = ch; int prevCh = ch;
@ -187,7 +180,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
ch = KEY_MOUSE; ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) { if (panel == panelFocus || this->allowFocusChange) {
focus = i; focus = i;
panelFocus = setCurrentPanel(this, panel); panelFocus = panel;
Object* oldSelection = Panel_getSelected(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) { if (Panel_getSelected(panel) == oldSelection) {
@ -234,9 +227,12 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (result & SYNTH_KEY) { if (result & SYNTH_KEY) {
ch = result >> 16; ch = result >> 16;
} }
if (result & REDRAW) { if (result & REFRESH) {
sortTimeout = 0; sortTimeout = 0;
} }
if (result & REDRAW) {
force_redraw = true;
}
if (result & RESCAN) { if (result & RESCAN) {
rescan = true; rescan = true;
sortTimeout = 0; sortTimeout = 0;
@ -269,7 +265,7 @@ tryLeft:
focus--; focus--;
} }
panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus)); panelFocus = (Panel*) Vector_get(this->panels, focus);
if (Panel_size(panelFocus) == 0 && focus > 0) { if (Panel_size(panelFocus) == 0 && focus > 0) {
goto tryLeft; goto tryLeft;
} }
@ -290,7 +286,7 @@ tryRight:
focus++; focus++;
} }
panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus)); panelFocus = (Panel*) Vector_get(this->panels, focus);
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) { if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) {
goto tryRight; goto tryRight;
} }

View File

@ -96,10 +96,10 @@ static void readFields(ProcessField* fields, uint32_t* flags, const char* line)
free(trim); free(trim);
int i, j; int i, j;
*flags = 0; *flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) { for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format. // This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1; int id = atoi(ids[i]) + 1;
if (id > 0 && id < Platform_numberOfFields && Process_fields[id].name) { if (id > 0 && id < LAST_PROCESSFIELD && Process_fields[id].name) {
fields[j] = id; fields[j] = id;
*flags |= Process_fields[id].flags; *flags |= Process_fields[id].flags;
j++; j++;
@ -137,10 +137,17 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
} else if (String_eq(option[0], "sort_key")) { } else if (String_eq(option[0], "sort_key")) {
// This "+1" is for compatibility with the older enum format. // This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1; this->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "tree_sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->treeSortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) { } else if (String_eq(option[0], "sort_direction")) {
this->direction = atoi(option[1]); this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_sort_direction")) {
this->treeDirection = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) { } else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]); this->treeView = atoi(option[1]);
} else if (String_eq(option[0], "tree_view_always_by_pid")) {
this->treeViewAlwaysByPID = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) { } else if (String_eq(option[0], "hide_kernel_threads")) {
this->hideKernelThreads = atoi(option[1]); this->hideKernelThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_userland_threads")) { } else if (String_eq(option[0], "hide_userland_threads")) {
@ -273,6 +280,8 @@ bool Settings_write(Settings* this) {
// This "-1" is for compatibility with the older enum format. // This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1); fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction); fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "tree_sort_key=%d\n", (int) this->treeSortKey - 1);
fprintf(fd, "tree_sort_direction=%d\n", (int) this->treeDirection);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads); fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads); fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers); fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers);
@ -287,6 +296,7 @@ bool Settings_write(Settings* this) {
fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline); fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline);
fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand); fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand);
fprintf(fd, "tree_view=%d\n", (int) this->treeView); fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "tree_view_always_by_pid=%d\n", (int) this->treeViewAlwaysByPID);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne); fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne);
@ -316,7 +326,9 @@ Settings* Settings_new(int initialCpuCount) {
Settings* this = xCalloc(1, sizeof(Settings)); Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU; this->sortKey = PERCENT_CPU;
this->treeSortKey = PID;
this->direction = 1; this->direction = 1;
this->treeDirection = 1;
this->shadowOtherUsers = false; this->shadowOtherUsers = false;
this->showThreadNames = false; this->showThreadNames = false;
this->hideKernelThreads = false; this->hideKernelThreads = false;
@ -343,11 +355,11 @@ Settings* Settings_new(int initialCpuCount) {
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
this->topologyAffinity = false; this->topologyAffinity = false;
#endif #endif
this->fields = xCalloc(Platform_numberOfFields + 1, sizeof(ProcessField)); this->fields = xCalloc(LAST_PROCESSFIELD + 1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector, // TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects). // (and ProcessFields into proper objects).
this->flags = 0; this->flags = 0;
ProcessField* defaults = Platform_defaultFields; const ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) { for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i]; this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags; this->flags |= Process_fields[defaults[i]].flags;
@ -427,9 +439,17 @@ Settings* Settings_new(int initialCpuCount) {
} }
void Settings_invertSortOrder(Settings* this) { void Settings_invertSortOrder(Settings* this) {
if (this->direction == 1) { int* attr = (this->treeView) ? &(this->treeDirection) : &(this->direction);
this->direction = -1; *attr = (*attr == 1) ? -1 : 1;
} else { }
void Settings_setSortKey(Settings* this, ProcessField sortKey) {
if (this->treeViewAlwaysByPID || !this->treeView) {
this->sortKey = sortKey;
this->direction = 1; this->direction = 1;
this->treeView = false;
} else {
this->treeSortKey = sortKey;
this->treeDirection = 1;
} }
} }

View File

@ -10,6 +10,7 @@ in the source distribution for its full text.
#include "config.h" // IWYU pragma: keep #include "config.h" // IWYU pragma: keep
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include "Process.h" #include "Process.h"
@ -32,7 +33,9 @@ typedef struct Settings_ {
int delay; int delay;
int direction; int direction;
int treeDirection;
ProcessField sortKey; ProcessField sortKey;
ProcessField treeSortKey;
bool countCPUsFromOne; bool countCPUsFromOne;
bool detailedCPUTime; bool detailedCPUTime;
@ -43,6 +46,7 @@ typedef struct Settings_ {
bool degreeFahrenheit; bool degreeFahrenheit;
#endif #endif
bool treeView; bool treeView;
bool treeViewAlwaysByPID;
bool showProgramPath; bool showProgramPath;
bool shadowOtherUsers; bool shadowOtherUsers;
bool showThreadNames; bool showThreadNames;
@ -69,6 +73,16 @@ typedef struct Settings_ {
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu)) #define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))
static inline ProcessField Settings_getActiveSortKey(const Settings* this) {
return (this->treeView)
? (this->treeViewAlwaysByPID ? PID : this->treeSortKey)
: this->sortKey;
}
static inline int Settings_getActiveDirection(const Settings* this) {
return this->treeView ? this->treeDirection : this->direction;
}
void Settings_delete(Settings* this); void Settings_delete(Settings* this);
bool Settings_write(Settings* this); bool Settings_write(Settings* this);
@ -77,4 +91,6 @@ Settings* Settings_new(int initialCpuCount);
void Settings_invertSortOrder(Settings* this); void Settings_invertSortOrder(Settings* this);
void Settings_setSortKey(Settings* this, ProcessField sortKey);
#endif #endif

View File

@ -17,28 +17,27 @@ static const int SwapMeter_attributes[] = {
SWAP SWAP
}; };
static void SwapMeter_updateValues(Meter* this, char* buffer, int size) { static void SwapMeter_updateValues(Meter* this, char* buffer, size_t size) {
int written; int written;
Platform_setSwapValues(this); Platform_setSwapValues(this);
written = Meter_humanUnit(buffer, this->values[0], size); written = Meter_humanUnit(buffer, this->values[0], size);
buffer += written; METER_BUFFER_CHECK(buffer, size, written);
if ((size -= written) > 0) {
*buffer++ = '/'; METER_BUFFER_APPEND_CHR(buffer, size, '/');
size--;
Meter_humanUnit(buffer, this->total, size); Meter_humanUnit(buffer, this->total, size);
}
} }
static void SwapMeter_display(const Object* cast, RichString* out) { static void SwapMeter_display(const Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":"); RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, 50); Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], 50); Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
} }
const MeterClass SwapMeter_class = { const MeterClass SwapMeter_class = {

View File

@ -23,7 +23,7 @@ static const int TasksMeter_attributes[] = {
TASKS_RUNNING TASKS_RUNNING
}; };
static void TasksMeter_updateValues(Meter* this, char* buffer, int len) { static void TasksMeter_updateValues(Meter* this, char* buffer, size_t len) {
const ProcessList* pl = this->pl; const ProcessList* pl = this->pl;
this->values[0] = pl->kernelThreads; this->values[0] = pl->kernelThreads;
this->values[1] = pl->userlandThreads; this->values[1] = pl->userlandThreads;
@ -46,7 +46,7 @@ static void TasksMeter_display(const Object* cast, RichString* out) {
int processes = (int) this->values[2]; int processes = (int) this->values[2];
xSnprintf(buffer, sizeof(buffer), "%d", processes); xSnprintf(buffer, sizeof(buffer), "%d", processes);
RichString_write(out, CRT_colors[METER_VALUE], buffer); RichString_writeAscii(out, CRT_colors[METER_VALUE], buffer);
int threadValueColor = CRT_colors[METER_VALUE]; int threadValueColor = CRT_colors[METER_VALUE];
int threadCaptionColor = CRT_colors[METER_TEXT]; int threadCaptionColor = CRT_colors[METER_TEXT];
if (settings->highlightThreads) { if (settings->highlightThreads) {
@ -54,21 +54,21 @@ static void TasksMeter_display(const Object* cast, RichString* out) {
threadCaptionColor = CRT_colors[PROCESS_THREAD]; threadCaptionColor = CRT_colors[PROCESS_THREAD];
} }
if (!settings->hideUserlandThreads) { if (!settings->hideUserlandThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", "); RichString_appendAscii(out, CRT_colors[METER_TEXT], ", ");
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]);
RichString_append(out, threadValueColor, buffer); RichString_appendAscii(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " thr"); RichString_appendAscii(out, threadCaptionColor, " thr");
} }
if (!settings->hideKernelThreads) { if (!settings->hideKernelThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", "); RichString_appendAscii(out, CRT_colors[METER_TEXT], ", ");
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[0]); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[0]);
RichString_append(out, threadValueColor, buffer); RichString_appendAscii(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " kthr"); RichString_appendAscii(out, threadCaptionColor, " kthr");
} }
RichString_append(out, CRT_colors[METER_TEXT], "; "); RichString_appendAscii(out, CRT_colors[METER_TEXT], "; ");
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[3]); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[3]);
RichString_append(out, CRT_colors[TASKS_RUNNING], buffer); RichString_appendAscii(out, CRT_colors[TASKS_RUNNING], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " running"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " running");
} }
const MeterClass TasksMeter_class = { const MeterClass TasksMeter_class = {

View File

@ -33,17 +33,7 @@ static const char* const TraceScreenFunctions[] = {"Search ", "Filter ", "AutoSc
static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"}; static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"};
static int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27}; static const int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27};
const InfoScreenClass TraceScreen_class = {
.super = {
.extends = Class(Object),
.delete = TraceScreen_delete
},
.draw = TraceScreen_draw,
.onErr = TraceScreen_updateTrace,
.onKey = TraceScreen_onKey,
};
TraceScreen* TraceScreen_new(Process* process) { TraceScreen* TraceScreen_new(Process* process) {
// This initializes all TraceScreen variables to "false" so only default = true ones need to be set below // This initializes all TraceScreen variables to "false" so only default = true ones need to be set below
@ -70,12 +60,8 @@ void TraceScreen_delete(Object* cast) {
free(InfoScreen_done((InfoScreen*)this)); free(InfoScreen_done((InfoScreen*)this));
} }
void TraceScreen_draw(InfoScreen* this) { static void TraceScreen_draw(InfoScreen* this) {
attrset(CRT_colors[PANEL_HEADER_FOCUS]); InfoScreen_drawTitled(this, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process));
mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process));
attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(this->inc);
} }
bool TraceScreen_forkTracer(TraceScreen* this) { bool TraceScreen_forkTracer(TraceScreen* this) {
@ -131,7 +117,7 @@ err:
return false; return false;
} }
void TraceScreen_updateTrace(InfoScreen* super) { static void TraceScreen_updateTrace(InfoScreen* super) {
TraceScreen* this = (TraceScreen*) super; TraceScreen* this = (TraceScreen*) super;
char buffer[1025]; char buffer[1025];
@ -176,7 +162,7 @@ void TraceScreen_updateTrace(InfoScreen* super) {
} }
} }
bool TraceScreen_onKey(InfoScreen* super, int ch) { static bool TraceScreen_onKey(InfoScreen* super, int ch) {
TraceScreen* this = (TraceScreen*) super; TraceScreen* this = (TraceScreen*) super;
switch(ch) { switch(ch) {
case 'f': case 'f':
@ -195,3 +181,13 @@ bool TraceScreen_onKey(InfoScreen* super, int ch) {
this->follow = false; this->follow = false;
return false; return false;
} }
const InfoScreenClass TraceScreen_class = {
.super = {
.extends = Class(Object),
.delete = TraceScreen_delete
},
.draw = TraceScreen_draw,
.onErr = TraceScreen_updateTrace,
.onKey = TraceScreen_onKey,
};

View File

@ -32,12 +32,6 @@ TraceScreen* TraceScreen_new(Process* process);
void TraceScreen_delete(Object* cast); void TraceScreen_delete(Object* cast);
void TraceScreen_draw(InfoScreen* this);
bool TraceScreen_forkTracer(TraceScreen* this); bool TraceScreen_forkTracer(TraceScreen* this);
void TraceScreen_updateTrace(InfoScreen* super);
bool TraceScreen_onKey(InfoScreen* super, int ch);
#endif #endif

View File

@ -17,7 +17,7 @@ static const int UptimeMeter_attributes[] = {
UPTIME UPTIME
}; };
static void UptimeMeter_updateValues(Meter* this, char* buffer, int len) { static void UptimeMeter_updateValues(Meter* this, char* buffer, size_t len) {
int totalseconds = Platform_getUptime(); int totalseconds = Platform_getUptime();
if (totalseconds == -1) { if (totalseconds == -1) {
xSnprintf(buffer, len, "(unknown)"); xSnprintf(buffer, len, "(unknown)");

View File

@ -11,6 +11,7 @@ in the source distribution for its full text.
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -183,13 +184,13 @@ int xAsprintf(char** strp, const char* fmt, ...) {
return r; return r;
} }
int xSnprintf(char* buf, int len, const char* fmt, ...) { int xSnprintf(char* buf, size_t len, const char* fmt, ...) {
va_list vl; va_list vl;
va_start(vl, fmt); va_start(vl, fmt);
int n = vsnprintf(buf, len, fmt, vl); int n = vsnprintf(buf, len, fmt, vl);
va_end(vl); va_end(vl);
if (n < 0 || n >= len) { if (n < 0 || (size_t)n >= len) {
fail(); fail();
} }

View File

@ -13,6 +13,7 @@ in the source distribution for its full text.
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> // IWYU pragma: keep #include <stdlib.h> // IWYU pragma: keep
#include <string.h> // IWYU pragma: keep #include <string.h> // IWYU pragma: keep
#include <sys/types.h>
#include "Compat.h" #include "Compat.h"
#include "Macros.h" #include "Macros.h"
@ -58,7 +59,7 @@ ATTR_FORMAT(printf, 2, 3)
int xAsprintf(char** strp, const char* fmt, ...); int xAsprintf(char** strp, const char* fmt, ...);
ATTR_FORMAT(printf, 3, 4) ATTR_FORMAT(printf, 3, 4)
int xSnprintf(char* buf, int len, const char* fmt, ...); int xSnprintf(char* buf, size_t len, const char* fmt, ...);
char* xStrdup(const char* str) ATTR_NONNULL; char* xStrdup(const char* str) ATTR_NONNULL;

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65) AC_PREREQ(2.65)
AC_INIT([htop],[3.0.3-rc1],[htop@groups.io]) AC_INIT([htop],[3.0.4],[htop@groups.io])
AC_CONFIG_SRCDIR([htop.c]) AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_AUX_DIR([.]) AC_CONFIG_AUX_DIR([.])
@ -93,13 +93,16 @@ AC_TYPE_UINT64_T
AC_FUNC_CLOSEDIR_VOID AC_FUNC_CLOSEDIR_VOID
AC_FUNC_STAT AC_FUNC_STAT
AC_CHECK_FUNCS([\ AC_CHECK_FUNCS([\
clock_gettime\
faccessat\ faccessat\
fstatat\ fstatat\
host_get_clock_service\
openat\ openat\
readlinkat\ readlinkat\
]) ])
AC_SEARCH_LIBS([dlopen], [dl dld]) AC_SEARCH_LIBS([dlopen], [dl dld])
AC_SEARCH_LIBS([clock_gettime], [rt])
save_cflags="${CFLAGS}" save_cflags="${CFLAGS}"
CFLAGS="${CFLAGS} -std=c99" CFLAGS="${CFLAGS} -std=c99"
@ -165,7 +168,7 @@ m4_define([HTOP_CHECK_SCRIPT],
LIBS="$htop_config_script_libs $LIBS " LIBS="$htop_config_script_libs $LIBS "
htop_script_success=yes htop_script_success=yes
], [ ], [
CFLAGS="$htop_save_CFLAGS" CFLAGS="$htop_save_CFLAGS"
]) ])
LDFLAGS="$htop_save_LDFLAGS" LDFLAGS="$htop_save_LDFLAGS"
fi fi
@ -188,15 +191,15 @@ AC_DEFUN([AX_CHECK_COMPILE_FLAG],
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
[AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])]) [AS_VAR_SET(CACHEVAR,[no])])
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
AS_VAR_IF(CACHEVAR,yes, AS_VAR_IF(CACHEVAR,yes,
[m4_default([$2], :)], [m4_default([$2], :)],
[m4_default([$3], :)]) [m4_default([$3], :)])
AS_VAR_POPDEF([CACHEVAR])dnl AS_VAR_POPDEF([CACHEVAR])dnl
])dnl AX_CHECK_COMPILE_FLAGS ])dnl AX_CHECK_COMPILE_FLAGS
@ -209,14 +212,18 @@ if test "x$enable_unicode" = xyes; then
HTOP_CHECK_LIB([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW], HTOP_CHECK_LIB([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], HTOP_CHECK_LIB([ncursesw], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncurses], [addnwstr], [HAVE_LIBNCURSESW], HTOP_CHECK_LIB([ncurses], [addnwstr], [HAVE_LIBNCURSESW],
missing_libraries="$missing_libraries libncursesw" missing_libraries="$missing_libraries libncursesw"
AC_MSG_ERROR([You may want to use --disable-unicode or install libncursesw.]) AC_MSG_ERROR([You may want to use --disable-unicode or install libncursesw.])
))))))) )))))))
AC_CHECK_HEADERS([ncursesw/curses.h],[:], AC_CHECK_HEADERS([ncursesw/curses.h],[:],
[AC_CHECK_HEADERS([ncurses/ncurses.h],[:], [AC_CHECK_HEADERS([ncurses/ncurses.h],[:],
[AC_CHECK_HEADERS([ncurses/curses.h],[:], [AC_CHECK_HEADERS([ncurses/curses.h],[:],
[AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])]) [AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])])
# check if additional linker flags are needed for keypad(3)
# (at this point we already link against a working ncurses library with wide character support)
AC_SEARCH_LIBS([keypad], [tinfow tinfo])
else else
HTOP_CHECK_SCRIPT([ncurses6], [refresh], [HAVE_LIBNCURSES], "ncurses6-config", HTOP_CHECK_SCRIPT([ncurses6], [refresh], [HAVE_LIBNCURSES], "ncurses6-config",
HTOP_CHECK_SCRIPT([ncurses], [refresh], [HAVE_LIBNCURSES], "ncurses5-config", HTOP_CHECK_SCRIPT([ncurses], [refresh], [HAVE_LIBNCURSES], "ncurses5-config",
@ -229,6 +236,19 @@ else
[AC_CHECK_HEADERS([ncurses/curses.h],[:], [AC_CHECK_HEADERS([ncurses/curses.h],[:],
[AC_CHECK_HEADERS([ncurses/ncurses.h],[:], [AC_CHECK_HEADERS([ncurses/ncurses.h],[:],
[AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])]) [AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])])
# check if additional linker flags are needed for keypad(3)
# (at this point we already link against a working ncurses library)
AC_SEARCH_LIBS([keypad], [tinfo])
fi
if test "$my_htop_platform" = "darwin"; then
AC_CHECK_HEADERS([mach/mach_time.h])
AC_CHECK_FUNCS([mach_timebase_info])
fi
if test "$my_htop_platform" = "dragonflybsd"; then
AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
fi fi
if test "$my_htop_platform" = "freebsd"; then if test "$my_htop_platform" = "freebsd"; then
@ -246,8 +266,7 @@ if test "$my_htop_platform" = "solaris"; then
fi fi
AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity, disables Linux affinity])],, enable_hwloc="no") AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity, disables Linux affinity])],, enable_hwloc="no")
if test "x$enable_hwloc" = xyes if test "x$enable_hwloc" = xyes; then
then
AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [missing_libraries="$missing_libraries libhwloc"]) AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [missing_libraries="$missing_libraries libhwloc"])
AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"]) AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"])
fi fi
@ -269,9 +288,9 @@ if test "x$enable_linux_affinity" = xcheck; then
if (errno == ENOSYS) return 1; if (errno == ENOSYS) return 1;
]])], ]])],
[enable_linux_affinity=yes [enable_linux_affinity=yes
AC_MSG_RESULT([yes])], AC_MSG_RESULT([yes])],
[enable_linux_affinity=no [enable_linux_affinity=no
AC_MSG_RESULT([no])], AC_MSG_RESULT([no])],
[AC_MSG_RESULT([yes (assumed while cross compiling)])]) [AC_MSG_RESULT([yes (assumed while cross compiling)])])
fi fi
fi fi
@ -279,20 +298,17 @@ if test "x$enable_linux_affinity" = xyes; then
AC_DEFINE(HAVE_LINUX_AFFINITY, 1, [Define if Linux sched_setaffinity and sched_getaffinity are to be used.]) AC_DEFINE(HAVE_LINUX_AFFINITY, 1, [Define if Linux sched_setaffinity and sched_getaffinity are to be used.])
fi fi
if test "x$enable_linux_affinity" = xyes -a "x$enable_hwloc" = xyes if test "x$enable_linux_affinity" = xyes -a "x$enable_hwloc" = xyes; then
then
AC_MSG_ERROR([--enable-hwloc and --enable-linux-affinity are mutual exclusive. Specify at most one of them.]) AC_MSG_ERROR([--enable-hwloc and --enable-linux-affinity are mutual exclusive. Specify at most one of them.])
fi fi
AC_ARG_ENABLE(setuid, [AS_HELP_STRING([--enable-setuid], [enable setuid support for platforms that need it])],, enable_setuid="no") AC_ARG_ENABLE(setuid, [AS_HELP_STRING([--enable-setuid], [enable setuid support for platforms that need it])],, enable_setuid="no")
if test "x$enable_setuid" = xyes if test "x$enable_setuid" = xyes; then
then
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.]) AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
fi fi
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no") AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no")
if test "x$enable_delayacct" = xyes if test "x$enable_delayacct" = xyes; then
then
m4_ifdef([PKG_PROG_PKG_CONFIG], [ m4_ifdef([PKG_PROG_PKG_CONFIG], [
PKG_PROG_PKG_CONFIG() PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"]) PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"])
@ -321,6 +337,7 @@ AM_CFLAGS="\
-Wcast-qual\ -Wcast-qual\
-Wextra\ -Wextra\
-Wfloat-equal\ -Wfloat-equal\
-Wformat=2\
-Wmissing-format-attribute\ -Wmissing-format-attribute\
-Wmissing-noreturn\ -Wmissing-noreturn\
-Wmissing-prototypes\ -Wmissing-prototypes\
@ -346,10 +363,10 @@ AC_SUBST([AM_CPPFLAGS])
# Bail out on errors. # Bail out on errors.
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
if test ! -z "$missing_libraries"; then if test ! -z "$missing_libraries"; then
AC_MSG_ERROR([missing libraries: $missing_libraries]) AC_MSG_ERROR([missing libraries: $missing_libraries])
fi fi
if test ! -z "$missing_headers"; then if test ! -z "$missing_headers"; then
AC_MSG_ERROR([missing headers: $missing_headers]) AC_MSG_ERROR([missing headers: $missing_headers])
fi fi
AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2019 Hisham Muhammad. (C) 2020 htop dev team.", [Copyright message.]) AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2019 Hisham Muhammad. (C) 2020 htop dev team.", [Copyright message.])
@ -367,8 +384,7 @@ AC_SUBST(my_htop_platform)
AC_CONFIG_FILES([Makefile htop.1]) AC_CONFIG_FILES([Makefile htop.1])
AC_OUTPUT AC_OUTPUT
if test "$my_htop_platform" = "unsupported" if test "$my_htop_platform" = "unsupported"; then
then
echo "" echo ""
echo "****************************************************************" echo "****************************************************************"
echo "WARNING! This platform is not currently supported by htop." echo "WARNING! This platform is not currently supported by htop."

View File

@ -14,17 +14,37 @@ in the source distribution for its full text.
#include <mach/mach.h> #include <mach/mach.h>
#include "CRT.h" #include "CRT.h"
#include "Platform.h"
#include "Process.h" #include "Process.h"
const ProcessClass DarwinProcess_class = { const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
.super = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
.extends = Class(Process), [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
.display = Process_display, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
.delete = Process_delete, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
.compare = Process_compare [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
}, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
.writeField = Process_writeField, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
[TRANSLATED] = { .name = "TRANSLATED", .title = "T ", .description = "Translation info (T translated, N native)", .flags = 0, },
}; };
Process* DarwinProcess_new(const Settings* settings) { Process* DarwinProcess_new(const Settings* settings) {
@ -35,6 +55,7 @@ Process* DarwinProcess_new(const Settings* settings) {
this->utime = 0; this->utime = 0;
this->stime = 0; this->stime = 0;
this->taskAccess = true; this->taskAccess = true;
this->translated = false;
return &this->super; return &this->super;
} }
@ -46,6 +67,34 @@ void Process_delete(Object* cast) {
free(this); free(this);
} }
static void DarwinProcess_writeField(const Process* this, RichString* str, ProcessField field) {
const DarwinProcess* dp = (const DarwinProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch (field) {
// add Platform-specific fields here
case TRANSLATED: xSnprintf(buffer, n, "%c ", dp->translated ? 'T' : 'N'); break;
default:
Process_writeField(this, str, field);
return;
}
RichString_appendWide(str, attr, buffer);
}
static long DarwinProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const DarwinProcess* p1 = (const DarwinProcess*)v1;
const DarwinProcess* p2 = (const DarwinProcess*)v2;
switch (key) {
// add Platform-specific fields here
case TRANSLATED:
return SPACESHIP_NUMBER(p1->translated, p2->translated);
default:
return Process_compareByKey_Base(v1, v2, key);
}
}
bool Process_isThread(const Process* this) { bool Process_isThread(const Process* this) {
(void) this; (void) this;
return false; return false;
@ -195,6 +244,8 @@ ERROR_A:
} }
void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) { void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) {
DarwinProcess* dp = (DarwinProcess*)proc;
const struct extern_proc* ep = &ps->kp_proc; const struct extern_proc* ep = &ps->kp_proc;
/* UNSET HERE : /* UNSET HERE :
@ -223,6 +274,7 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
/* e_tdev = (major << 24) | (minor & 0xffffff) */ /* e_tdev = (major << 24) | (minor & 0xffffff) */
/* e_tdev == -1 for "no device" */ /* e_tdev == -1 for "no device" */
proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */ proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */
dp->translated = ps->kp_proc.p_flag & P_TRANSLATED;
proc->starttime_ctime = ep->p_starttime.tv_sec; proc->starttime_ctime = ep->p_starttime.tv_sec;
Process_fillStarttimeBuffer(proc); Process_fillStarttimeBuffer(proc);
@ -240,26 +292,24 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
proc->updated = true; proc->updated = true;
} }
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl) { void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval) {
struct proc_taskinfo pti; struct proc_taskinfo pti;
if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) {
if (0 != proc->utime || 0 != proc->stime) { uint64_t total_existing_time = proc->stime + proc->utime;
uint64_t diff = (pti.pti_total_system - proc->stime) uint64_t total_current_time = pti.pti_total_system + pti.pti_total_user;
+ (pti.pti_total_user - proc->utime);
proc->super.percent_cpu = (double)diff * (double)dpl->super.cpuCount if (total_existing_time && 1E-6 < time_interval) {
/ ((double)dpl->global_diff * 100000.0); uint64_t total_time_diff = total_current_time - total_existing_time;
proc->super.percent_cpu = ((double)total_time_diff / time_interval) * 100.0;
// fprintf(stderr, "%f %llu %llu %llu %llu %llu\n", proc->super.percent_cpu, } else {
// proc->stime, proc->utime, pti.pti_total_system, pti.pti_total_user, dpl->global_diff); proc->super.percent_cpu = 0.0;
// exit(7);
} }
proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000; proc->super.time = total_current_time / 10000000;
proc->super.nlwp = pti.pti_threadnum; proc->super.nlwp = pti.pti_threadnum;
proc->super.m_virt = pti.pti_virtual_size / CRT_pageSize; proc->super.m_virt = pti.pti_virtual_size / ONE_K;
proc->super.m_resident = pti.pti_resident_size / CRT_pageSize; proc->super.m_resident = pti.pti_resident_size / ONE_K;
proc->super.majflt = pti.pti_faults; proc->super.majflt = pti.pti_faults;
proc->super.percent_mem = (double)pti.pti_resident_size * 100.0 proc->super.percent_mem = (double)pti.pti_resident_size * 100.0
/ (double)dpl->host_info.max_mem; / (double)dpl->host_info.max_mem;
@ -341,3 +391,15 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) {
} }
proc->state = state; proc->state = state;
} }
const ProcessClass DarwinProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = DarwinProcess_writeField,
.compareByKey = DarwinProcess_compareByKey,
};

View File

@ -19,10 +19,13 @@ typedef struct DarwinProcess_ {
uint64_t utime; uint64_t utime;
uint64_t stime; uint64_t stime;
bool taskAccess; bool taskAccess;
bool translated;
} DarwinProcess; } DarwinProcess;
extern const ProcessClass DarwinProcess_class; extern const ProcessClass DarwinProcess_class;
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
Process* DarwinProcess_new(const Settings* settings); Process* DarwinProcess_new(const Settings* settings);
void Process_delete(Object* cast); void Process_delete(Object* cast);
@ -31,7 +34,7 @@ bool Process_isThread(const Process* this);
void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists); void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists);
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl); void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval);
/* /*
* Scan threads for process state information. * Scan threads for process state information.

View File

@ -21,6 +21,7 @@ in the source distribution for its full text.
#include "CRT.h" #include "CRT.h"
#include "DarwinProcess.h" #include "DarwinProcess.h"
#include "Platform.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "zfs/openzfs_sysctl.h" #include "zfs/openzfs_sysctl.h"
#include "zfs/ZfsArcStats.h" #include "zfs/ZfsArcStats.h"
@ -71,14 +72,14 @@ static void ProcessList_getHostInfo(host_basic_info_data_t* p) {
mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT; mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT;
if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) { if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
CRT_fatalError("Unable to retrieve host info\n"); CRT_fatalError("Unable to retrieve host info");
} }
} }
static void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t* p) { static void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t* p) {
if (NULL != p && NULL != *p) { if (NULL != p && NULL != *p) {
if (0 != munmap(*p, vm_page_size)) { if (0 != munmap(*p, vm_page_size)) {
CRT_fatalError("Unable to free old CPU load information\n"); CRT_fatalError("Unable to free old CPU load information");
} }
*p = NULL; *p = NULL;
} }
@ -90,7 +91,7 @@ static unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t* p) {
// TODO Improving the accuracy of the load counts woule help a lot. // TODO Improving the accuracy of the load counts woule help a lot.
if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) { if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) {
CRT_fatalError("Unable to retrieve CPU info\n"); CRT_fatalError("Unable to retrieve CPU info");
} }
return cpu_count; return cpu_count;
@ -100,7 +101,7 @@ static void ProcessList_getVMStats(vm_statistics_t p) {
mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT; mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) { if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) {
CRT_fatalError("Unable to retrieve VM statistics\n"); CRT_fatalError("Unable to retrieve VM statistics");
} }
} }
@ -131,7 +132,7 @@ static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) {
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList));
ProcessList_init(&this->super, Class(Process), usersTable, pidMatchList, userId); ProcessList_init(&this->super, Class(DarwinProcess), usersTable, pidMatchList, userId);
/* Initialize the CPU information */ /* Initialize the CPU information */
this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load); this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load);
@ -158,6 +159,11 @@ void ProcessList_delete(ProcessList* this) {
free(this); free(this);
} }
static double ticksToNanoseconds(const double ticks) {
const double nanos_per_sec = 1e9;
return ticks / (double) Platform_clockTicksPerSec * Platform_timebaseToNS * nanos_per_sec;
}
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
DarwinProcessList* dpl = (DarwinProcessList*)super; DarwinProcessList* dpl = (DarwinProcessList*)super;
bool preExisting = true; bool preExisting = true;
@ -185,6 +191,8 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
} }
} }
const double time_interval = ticksToNanoseconds(dpl->global_diff) / (double) dpl->super.cpuCount;
/* Clear the thread counts */ /* Clear the thread counts */
super->kernelThreads = 0; super->kernelThreads = 0;
super->userlandThreads = 0; super->userlandThreads = 0;
@ -204,7 +212,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
proc = (DarwinProcess*)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, DarwinProcess_new); proc = (DarwinProcess*)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, DarwinProcess_new);
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], preExisting); DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl); DarwinProcess_setFromLibprocPidinfo(proc, dpl, time_interval);
// Disabled for High Sierra due to bug in macOS High Sierra // Disabled for High Sierra due to bug in macOS High Sierra
bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0); bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0);

View File

@ -6,32 +6,42 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Platform.h" #include "config.h" // IWYU pragma: keep
#include "Macros.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "ClockMeter.h"
#include "DateMeter.h"
#include "DateTimeMeter.h"
#include "HostnameMeter.h"
#include "ProcessLocksScreen.h"
#include "UptimeMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#include "DarwinProcessList.h"
#include "Platform.h"
#include <errno.h>
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFString.h> #include <CoreFoundation/CFString.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/ps/IOPowerSources.h> #include <IOKit/ps/IOPowerSources.h>
#include <IOKit/ps/IOPSKeys.h> #include <IOKit/ps/IOPSKeys.h>
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; #include "ClockMeter.h"
#include "CPUMeter.h"
#include "CRT.h"
#include "DarwinProcessList.h"
#include "DateMeter.h"
#include "DateTimeMeter.h"
#include "HostnameMeter.h"
#include "LoadAverageMeter.h"
#include "Macros.h"
#include "MemoryMeter.h"
#include "ProcessLocksScreen.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "UptimeMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#ifdef HAVE_MACH_MACH_TIME_H
#include <mach/mach_time.h>
#endif
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 }, { .name = " 0 Cancel", .number = 0 },
@ -71,35 +81,6 @@ const SignalItem Platform_signals[] = {
const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
const MeterClass* const Platform_meterTypes[] = { const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class, &CPUMeter_class,
&ClockMeter_class, &ClockMeter_class,
@ -131,10 +112,28 @@ const MeterClass* const Platform_meterTypes[] = {
NULL NULL
}; };
int Platform_numberOfFields = 100; double Platform_timebaseToNS = 1.0;
long Platform_clockTicksPerSec = -1;
void Platform_init(void) { void Platform_init(void) {
/* no platform-specific setup needed */ // Check if we can determine the timebase used on this system.
// If the API is unavailable assume we get our timebase in nanoseconds.
#ifdef HAVE_MACH_TIMEBASE_INFO
mach_timebase_info_data_t info;
mach_timebase_info(&info);
Platform_timebaseToNS = (double)info.numer / (double)info.denom;
#else
Platform_timebaseToNS = 1.0;
#endif
// Determine the number of clock ticks per second
errno = 0;
Platform_clockTicksPerSec = sysconf(_SC_CLK_TCK);
if (errno || Platform_clockTicksPerSec < 1) {
CRT_fatalError("Unable to retrieve clock tick rate");
}
} }
void Platform_done(void) { void Platform_done(void) {
@ -179,16 +178,6 @@ int Platform_getMaxPid() {
return 99999; return 99999;
} }
ProcessPidColumn Process_pidColumns[] = {
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
static double Platform_setCPUAverageValues(Meter* mtr) { static double Platform_setCPUAverageValues(Meter* mtr) {
const ProcessList* dpl = mtr->pl; const ProcessList* dpl = mtr->pl;
int cpus = dpl->cpuCount; int cpus = dpl->cpuCount;

View File

@ -19,11 +19,12 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h" #include "ProcessLocksScreen.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
extern ProcessField Platform_defaultFields[]; extern const ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields; extern double Platform_timebaseToNS;
extern long Platform_clockTicksPerSec;
extern const SignalItem Platform_signals[]; extern const SignalItem Platform_signals[];
@ -43,8 +44,6 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen);
int Platform_getMaxPid(void); int Platform_getMaxPid(void);
extern ProcessPidColumn Process_pidColumns[];
double Platform_setCPUValues(Meter* mtr, int cpu); double Platform_setCPUValues(Meter* mtr, int cpu);
void Platform_setMemoryValues(Meter* mtr); void Platform_setMemoryValues(Meter* mtr);

16
darwin/ProcessField.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef HEADER_DarwinProcessField
#define HEADER_DarwinProcessField
/*
htop - darwin/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
TRANSLATED = 100, \
// End of list
#endif /* HEADER_DarwinProcessField */

View File

@ -18,26 +18,16 @@ in the source distribution for its full text.
#include <sys/syscall.h> #include <sys/syscall.h>
const ProcessClass DragonFlyBSDProcess_class = { const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = DragonFlyBSDProcess_compare
},
.writeField = DragonFlyBSDProcess_writeField,
};
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping (<20s), I Idle, Q Queued for Run, R running, D disk, Z zombie, T traced, W paging, B Blocked, A AskedPage, C Core, J Jailed)", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping (<20s), I Idle, Q Queued for Run, R running, D disk, Z zombie, T traced, W paging, B Blocked, A AskedPage, C Core, J Jailed)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, }, [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
@ -53,21 +43,9 @@ ProcessFieldData Process_fields[] = {
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
[JID] = { .name = "JID", .title = " JID ", .description = "Jail prison ID", .flags = 0, }, [JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, },
[JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, }, [JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = JID, .label = "JID" },
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
}; };
Process* DragonFlyBSDProcess_new(const Settings* settings) { Process* DragonFlyBSDProcess_new(const Settings* settings) {
@ -84,15 +62,15 @@ void Process_delete(Object* cast) {
free(this); free(this);
} }
void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) { static void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) {
const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) this; const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) this;
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1; size_t n = sizeof(buffer) - 1;
switch ((int) field) { switch (field) {
// add Platform-specific fields here // add Platform-specific fields here
case PID: xSnprintf(buffer, n, Process_pidFormat, (fp->kernel ? -1 : this->pid)); break; case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, (fp->kernel ? -1 : this->pid)); break;
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break; case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break;
case JAIL: { case JAIL: {
xSnprintf(buffer, n, "%-11s ", fp->jname); xSnprintf(buffer, n, "%-11s ", fp->jname);
if (buffer[11] != '\0') { if (buffer[11] != '\0') {
@ -105,29 +83,21 @@ void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, Proces
Process_writeField(this, str, field); Process_writeField(this, str, field);
return; return;
} }
RichString_append(str, attr, buffer); RichString_appendWide(str, attr, buffer);
} }
long DragonFlyBSDProcess_compare(const void* v1, const void* v2) { static long DragonFlyBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const DragonFlyBSDProcess *p1, *p2; const DragonFlyBSDProcess* p1 = (const DragonFlyBSDProcess*)v1;
const Settings *settings = ((const Process*)v1)->settings; const DragonFlyBSDProcess* p2 = (const DragonFlyBSDProcess*)v2;
if (settings->direction == 1) { switch (key) {
p1 = (const DragonFlyBSDProcess*)v1;
p2 = (const DragonFlyBSDProcess*)v2;
} else {
p2 = (const DragonFlyBSDProcess*)v1;
p1 = (const DragonFlyBSDProcess*)v2;
}
switch ((int) settings->sortKey) {
// add Platform-specific fields here // add Platform-specific fields here
case JID: case JID:
return SPACESHIP_NUMBER(p1->jid, p2->jid); return SPACESHIP_NUMBER(p1->jid, p2->jid);
case JAIL: case JAIL:
return SPACESHIP_NULLSTR(p1->jname, p2->jname); return SPACESHIP_NULLSTR(p1->jname, p2->jname);
default: default:
return Process_compare(v1, v2); return Process_compareByKey_Base(v1, v2, key);
} }
} }
@ -140,3 +110,14 @@ bool Process_isThread(const Process* this) {
return (Process_isUserlandThread(this)); return (Process_isUserlandThread(this));
} }
} }
const ProcessClass DragonFlyBSDProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = DragonFlyBSDProcess_writeField,
.compareByKey = DragonFlyBSDProcess_compareByKey
};

View File

@ -8,13 +8,6 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
typedef enum DragonFlyBSDProcessFields {
// Add platform-specific fields here, with ids >= 100
JID = 100,
JAIL = 101,
LAST_PROCESSFIELD = 102,
} DragonFlyBSDProcessField;
typedef struct DragonFlyBSDProcess_ { typedef struct DragonFlyBSDProcess_ {
Process super; Process super;
int kernel; int kernel;
@ -29,18 +22,12 @@ typedef struct DragonFlyBSDProcess_ {
extern const ProcessClass DragonFlyBSDProcess_class; extern const ProcessClass DragonFlyBSDProcess_class;
extern ProcessFieldData Process_fields[]; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern ProcessPidColumn Process_pidColumns[];
Process* DragonFlyBSDProcess_new(const Settings* settings); Process* DragonFlyBSDProcess_new(const Settings* settings);
void Process_delete(Object* cast); void Process_delete(Object* cast);
void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field);
long DragonFlyBSDProcess_compare(const void* v1, const void* v2);
bool Process_isThread(const Process* this); bool Process_isThread(const Process* this);
#endif #endif

View File

@ -55,12 +55,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len); len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len);
len = sizeof(pageSize); len = sizeof(pageSize);
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) { if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1)
pageSize = CRT_pageSize; CRT_fatalError("Cannot get pagesize by sysctl");
pageSizeKb = CRT_pageSizeKB; pageSizeKb = pageSize / ONE_K;
} else {
pageSizeKb = pageSize / ONE_K;
}
// usable page count vm.stats.vm.v_page_count // usable page count vm.stats.vm.v_page_count
// actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size // actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size
@ -265,7 +262,7 @@ static inline void DragonFlyBSDProcessList_scanMemoryInfo(ProcessList* pl) {
pl->usedSwap *= pageSizeKb; pl->usedSwap *= pageSizeKb;
} }
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) { static char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, const struct kinfo_proc* kproc, int* basenameEnd) {
char** argv = kvm_getargv(kd, kproc, 0); char** argv = kvm_getargv(kd, kproc, 0);
if (!argv) { if (!argv) {
return xStrdup(kproc->kp_comm); return xStrdup(kproc->kp_comm);
@ -346,7 +343,7 @@ retry:
free(jls); free(jls);
} }
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) { static char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) {
char* hostname; char* hostname;
char* jname; char* jname;
@ -376,10 +373,10 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
int count = 0; int count = 0;
// TODO Kernel Threads seem to be skipped, need to figure out the correct flag // TODO Kernel Threads seem to be skipped, need to figure out the correct flag
struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count); const struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
struct kinfo_proc* kproc = &kprocs[i]; const struct kinfo_proc* kproc = &kprocs[i];
bool preExisting = false; bool preExisting = false;
bool ATTR_UNUSED isIdleProcess = false; bool ATTR_UNUSED isIdleProcess = false;
@ -433,13 +430,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
} }
} }
proc->m_virt = kproc->kp_vm_map_size / pageSize; proc->m_virt = kproc->kp_vm_map_size / ONE_K;
proc->m_resident = kproc->kp_vm_rssize; proc->m_resident = kproc->kp_vm_rssize * pageSizeKb;
proc->nlwp = kproc->kp_nthreads; // number of lwp thread proc->nlwp = kproc->kp_nthreads; // number of lwp thread
proc->time = (kproc->kp_swtime + 5000) / 10000; proc->time = (kproc->kp_swtime + 5000) / 10000;
proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale); proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale);
proc->percent_mem = 100.0 * (proc->m_resident * pageSizeKb) / (double)(super->totalMem); proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem);
if (proc->percent_cpu > 0.1) { if (proc->percent_cpu > 0.1) {
// system idle process should own all CPU time left regardless of CPU count // system idle process should own all CPU time left regardless of CPU count

View File

@ -11,16 +11,12 @@ in the source distribution for its full text.
#include <kvm.h> #include <kvm.h>
#include <sys/param.h> #include <sys/param.h>
#include <osreldate.h> #include <osreldate.h>
#include <sys/kinfo.h>
#include <kinfo.h>
#include <sys/jail.h> #include <sys/jail.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/resource.h> #include <sys/resource.h>
#include "Hashtable.h" #include "Hashtable.h"
#include "DragonFlyBSDProcess.h" #include "DragonFlyBSDProcess.h"
#define JAIL_ERRMSGLEN 1024
extern char jail_errmsg[JAIL_ERRMSGLEN];
typedef struct CPUData_ { typedef struct CPUData_ {
double userPercent; double userPercent;
@ -55,10 +51,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
void ProcessList_delete(ProcessList* this); void ProcessList_delete(ProcessList* this);
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid);
void ProcessList_goThroughEntries(ProcessList* super, pauseProcessUpdate);
#endif #endif

View File

@ -31,9 +31,7 @@ in the source distribution for its full text.
#include <math.h> #include <math.h>
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 }, { .name = " 0 Cancel", .number = 0 },

View File

@ -17,11 +17,8 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h" #include "ProcessLocksScreen.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
extern ProcessField Platform_defaultFields[]; extern const ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const SignalItem Platform_signals[]; extern const SignalItem Platform_signals[];

View File

@ -0,0 +1,17 @@
#ifndef HEADER_DragonFlyBSDProcessField
#define HEADER_DragonFlyBSDProcessField
/*
htop - dragonflybsd/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
JID = 100, \
JAIL = 101, \
// End of list
#endif /* HEADER_DragonFlyBSDProcessField */

View File

@ -18,16 +18,16 @@ in the source distribution for its full text.
const char* const nodevStr = "nodev"; const char* const nodevStr = "nodev";
ProcessFieldData Process_fields[] = { const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = PROCESS_FLAG_FREEBSD_TTY, }, [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = PROCESS_FLAG_FREEBSD_TTY, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
@ -44,21 +44,9 @@ ProcessFieldData Process_fields[] = {
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
[JID] = { .name = "JID", .title = " JID ", .description = "Jail prison ID", .flags = 0, }, [JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, },
[JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, }, [JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = JID, .label = "JID" },
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
}; };
Process* FreeBSDProcess_new(const Settings* settings) { Process* FreeBSDProcess_new(const Settings* settings) {
@ -80,9 +68,9 @@ static void FreeBSDProcess_writeField(const Process* this, RichString* str, Proc
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1; int n = sizeof(buffer) - 1;
switch ((int) field) { switch (field) {
// add FreeBSD-specific fields here // add FreeBSD-specific fields here
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break; case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break;
case JAIL: { case JAIL: {
xSnprintf(buffer, n, "%-11s ", fp->jname); xSnprintf(buffer, n, "%-11s ", fp->jname);
if (buffer[11] != '\0') { if (buffer[11] != '\0') {
@ -105,22 +93,14 @@ static void FreeBSDProcess_writeField(const Process* this, RichString* str, Proc
Process_writeField(this, str, field); Process_writeField(this, str, field);
return; return;
} }
RichString_append(str, attr, buffer); RichString_appendWide(str, attr, buffer);
} }
static long FreeBSDProcess_compare(const void* v1, const void* v2) { static long FreeBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const FreeBSDProcess *p1, *p2; const FreeBSDProcess* p1 = (const FreeBSDProcess*)v1;
const Settings *settings = ((const Process*)v1)->settings; const FreeBSDProcess* p2 = (const FreeBSDProcess*)v2;
if (settings->direction == 1) { switch (key) {
p1 = (const FreeBSDProcess*)v1;
p2 = (const FreeBSDProcess*)v2;
} else {
p2 = (const FreeBSDProcess*)v1;
p1 = (const FreeBSDProcess*)v2;
}
switch ((int) settings->sortKey) {
// add FreeBSD-specific fields here // add FreeBSD-specific fields here
case JID: case JID:
return SPACESHIP_NUMBER(p1->jid, p2->jid); return SPACESHIP_NUMBER(p1->jid, p2->jid);
@ -129,7 +109,7 @@ static long FreeBSDProcess_compare(const void* v1, const void* v2) {
case TTY_NR: case TTY_NR:
return SPACESHIP_NULLSTR(p1->ttyPath, p2->ttyPath); return SPACESHIP_NULLSTR(p1->ttyPath, p2->ttyPath);
default: default:
return Process_compare(v1, v2); return Process_compareByKey_Base(v1, v2, key);
} }
} }
@ -148,7 +128,8 @@ const ProcessClass FreeBSDProcess_class = {
.extends = Class(Process), .extends = Class(Process),
.display = Process_display, .display = Process_display,
.delete = Process_delete, .delete = Process_delete,
.compare = FreeBSDProcess_compare .compare = Process_compare
}, },
.writeField = FreeBSDProcess_writeField, .writeField = FreeBSDProcess_writeField,
.compareByKey = FreeBSDProcess_compareByKey
}; };

View File

@ -18,13 +18,6 @@ in the source distribution for its full text.
extern const char* const nodevStr; extern const char* const nodevStr;
typedef enum FreeBSDProcessFields_ {
// Add platform-specific fields here, with ids >= 100
JID = 100,
JAIL = 101,
LAST_PROCESSFIELD = 102,
} FreeBSDProcessField;
typedef struct FreeBSDProcess_ { typedef struct FreeBSDProcess_ {
Process super; Process super;
int kernel; int kernel;
@ -43,9 +36,7 @@ static inline bool Process_isUserlandThread(const Process* this) {
extern const ProcessClass FreeBSDProcess_class; extern const ProcessClass FreeBSDProcess_class;
extern ProcessFieldData Process_fields[]; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern ProcessPidColumn Process_pidColumns[];
Process* FreeBSDProcess_new(const Settings* settings); Process* FreeBSDProcess_new(const Settings* settings);

View File

@ -14,7 +14,6 @@ in the source distribution for its full text.
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/_iovec.h> #include <sys/_iovec.h>
#include <sys/dirent.h>
#include <sys/errno.h> #include <sys/errno.h>
#include <sys/param.h> // needs to be included before <sys/jail.h> for MAXPATHLEN #include <sys/param.h> // needs to be included before <sys/jail.h> for MAXPATHLEN
#include <sys/jail.h> #include <sys/jail.h>
@ -72,12 +71,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len); len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len);
len = sizeof(pageSize); len = sizeof(pageSize);
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) { if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1)
pageSize = CRT_pageSize; CRT_fatalError("Cannot get pagesize by sysctl");
pageSizeKb = CRT_pageSize; pageSizeKb = pageSize / ONE_K;
} else {
pageSizeKb = pageSize / ONE_K;
}
// usable page count vm.stats.vm.v_page_count // usable page count vm.stats.vm.v_page_count
// actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size // actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size
@ -526,13 +522,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
} }
// from FreeBSD source /src/usr.bin/top/machine.c // from FreeBSD source /src/usr.bin/top/machine.c
proc->m_virt = kproc->ki_size / pageSize; proc->m_virt = kproc->ki_size / ONE_K;
proc->m_resident = kproc->ki_rssize; proc->m_resident = kproc->ki_rssize * pageSizeKb;
proc->nlwp = kproc->ki_numthreads; proc->nlwp = kproc->ki_numthreads;
proc->time = (kproc->ki_runtime + 5000) / 10000; proc->time = (kproc->ki_runtime + 5000) / 10000;
proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale); proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale);
proc->percent_mem = 100.0 * (proc->m_resident * pageSizeKb) / (double)(super->totalMem); proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem);
/* /*
* TODO * TODO

View File

@ -47,9 +47,7 @@ in the source distribution for its full text.
#include "zfs/ZfsCompressedArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h"
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 }, { .name = " 0 Cancel", .number = 0 },
@ -248,7 +246,7 @@ char* Platform_getProcessEnv(pid_t pid) {
char* env = xMalloc(capacity); char* env = xMalloc(capacity);
int err = sysctl(mib, 4, env, &capacity, NULL, 0); int err = sysctl(mib, 4, env, &capacity, NULL, 0);
if (err) { if (err || capacity == 0) {
free(env); free(env);
return NULL; return NULL;
} }

View File

@ -19,11 +19,7 @@ in the source distribution for its full text.
#include "SignalsPanel.h" #include "SignalsPanel.h"
extern ProcessFieldData Process_fields[]; extern const ProcessField Platform_defaultFields[];
extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const SignalItem Platform_signals[]; extern const SignalItem Platform_signals[];

17
freebsd/ProcessField.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef HEADER_FreeBSDProcessField
#define HEADER_FreeBSDProcessField
/*
htop - freebsd/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
JID = 100, \
JAIL = 101, \
// End of list
#endif /* HEADER_FreeBSDProcessField */

View File

@ -2,11 +2,9 @@
.SH "NAME" .SH "NAME"
htop \- interactive process viewer htop \- interactive process viewer
.SH "SYNOPSIS" .SH "SYNOPSIS"
.LP
.B htop .B htop
.RB [ \-dCFhpustvH ] .RB [ \-dCFhpustvH ]
.SH "DESCRIPTION" .SH "DESCRIPTION"
.LP
.B htop .B htop
is a cross-platform ncurses-based process viewer. is a cross-platform ncurses-based process viewer.
.LP .LP
@ -22,9 +20,7 @@ Tasks related to processes (killing, renicing) can be done without
entering their PIDs. entering their PIDs.
.br .br
.SH "COMMAND-LINE OPTIONS" .SH "COMMAND-LINE OPTIONS"
.LP
Mandatory arguments to long options are mandatory for short options too. Mandatory arguments to long options are mandatory for short options too.
.LP
.TP .TP
\fB\-d \-\-delay=DELAY\fR \fB\-d \-\-delay=DELAY\fR
Delay between updates, in tenths of seconds. If the delay value is Delay between updates, in tenths of seconds. If the delay value is
@ -66,10 +62,8 @@ Show processes in tree view
\fB\-H \-\-highlight-changes=DELAY\fR \fB\-H \-\-highlight-changes=DELAY\fR
Highlight new and old processes Highlight new and old processes
.SH "INTERACTIVE COMMANDS" .SH "INTERACTIVE COMMANDS"
.LP
The following commands are supported while in The following commands are supported while in
.BR htop : .BR htop :
.LP
.TP 5 .TP 5
.B Up, Alt-k .B Up, Alt-k
Select (highlight) the previous process in the process list. Scroll the list Select (highlight) the previous process in the process list. Scroll the list
@ -157,11 +151,9 @@ between them as a tree. Toggling the key will switch between tree and
your previously selected sort view. Selecting a sort view will exit your previously selected sort view. Selecting a sort view will exit
tree view. tree view.
.TP .TP
.B F6 .B F6, <, >
On sorted view, select a field for sorting, also accessible through < and >. Selects a field for sorting, also accessible through < and >.
The current sort field is indicated by a highlight in the header. The current sort field is indicated by a highlight in the header.
On tree view, expand or collapse the current subtree. A "+" indicator in the
tree node indicates that it is collapsed.
.TP .TP
.B F7, ] .B F7, ]
Increase the selected process's priority (subtract from 'nice' value). Increase the selected process's priority (subtract from 'nice' value).
@ -232,7 +224,6 @@ Refresh: redraw screen and recalculate values.
PID search: type in process ID and the selection highlight will be moved to it. PID search: type in process ID and the selection highlight will be moved to it.
.PD .PD
.SH "COLUMNS" .SH "COLUMNS"
.LP
The following columns can display data about each process. A value of '\-' in The following columns can display data about each process. A value of '\-' in
all the rows indicates that a column is unsupported on your system, or all the rows indicates that a column is unsupported on your system, or
currently unimplemented in currently unimplemented in
@ -242,7 +233,6 @@ The names below are the ones used in the
shown in shown in
.BR htop 's .BR htop 's
main screen, it is shown below in parenthesis. main screen, it is shown below in parenthesis.
.LP
.TP 5 .TP 5
.B Command .B Command
The full command line of the process (i.e. program name and arguments). If the The full command line of the process (i.e. program name and arguments). If the
@ -460,8 +450,48 @@ The executable file of the process as reported by the kernel. Requires CAP_SYS_P
.TP .TP
.B All other flags .B All other flags
Currently unsupported (always displays '-'). Currently unsupported (always displays '-').
.SH "EXTERNAL LIBRARIES"
While
.B htop
depends on most of the libraries it uses at build time there are two
noteworthy exceptions to this rule. These exceptions both relate to
data displayed in meters displayed in the header of
.B htop
and were intentionally created as optional runtime dependencies instead.
These exceptions are described below:
.TP
.B libsystemd
The bindings for libsystemd are used in the SystemD meter to determine
the number of active services and the overall system state. Looking for
the functions to determine these information at runtime allows for
builds to support these meters without forcing the package manager
to install these libraries on systems that otherwise don't use systemd.
Summary: no build time dependency, optional runtime dependency on
.B libsystemd
via dynamic loading, with
.B systemctl(1)
fallback.
.TP
.B libsensors
The bindings for libsensors are used for the CPU temperature readings
in the CPU usage meters if displaying the temperature is enabled through
the setup screen. In order for
.B htop
to show these temperatures correctly though, a proper configuration
of libsensors through its usual configuration files is assumed and that
all CPU cores correspond to temperature sensors from the
.B coretemp
driver with core 0 corresponding to a sensor labelled "Core 0". The
package temperature may be given as "Package id 0". If missing it is
inferred as the maximum value from the available per-core readings.
Summary: build time dependency on
.B libsensors(3)
C header files, optional runtime dependency on
.B libsensors(3)
via dynamic loading.
.SH "CONFIG FILE" .SH "CONFIG FILE"
.LP
By default By default
.B htop .B htop
reads its configuration from the XDG-compliant path reads its configuration from the XDG-compliant path
@ -479,7 +509,6 @@ You may override the location of the configuration file using the $HTOPRC
environment variable (so you can have multiple configurations for different environment variable (so you can have multiple configurations for different
machines that share the same home directory, for example). machines that share the same home directory, for example).
.SH "MEMORY SIZES" .SH "MEMORY SIZES"
.LP
Memory sizes in Memory sizes in
.B htop .B htop
are displayed in a human-readable form. are displayed in a human-readable form.
@ -497,7 +526,6 @@ space and make memory size representations consistent throughout
and and
.BR limits.conf (5). .BR limits.conf (5).
.SH "AUTHORS" .SH "AUTHORS"
.LP
.B htop .B htop
was originally developed by Hisham Muhammad. was originally developed by Hisham Muhammad.
Nowadays it is maintained by the community at <htop@groups.io>. Nowadays it is maintained by the community at <htop@groups.io>.

13
htop.c
View File

@ -125,14 +125,14 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
case 's': case 's':
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */ assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
if (String_eq(optarg, "help")) { if (String_eq(optarg, "help")) {
for (int j = 1; j < Platform_numberOfFields; j++) { for (int j = 1; j < LAST_PROCESSFIELD; j++) {
const char* name = Process_fields[j].name; const char* name = Process_fields[j].name;
if (name) printf ("%s\n", name); if (name) printf ("%s\n", name);
} }
exit(0); exit(0);
} }
flags.sortKey = 0; flags.sortKey = 0;
for (int j = 1; j < Platform_numberOfFields; j++) { for (int j = 1; j < LAST_PROCESSFIELD; j++) {
if (Process_fields[j].name == NULL) if (Process_fields[j].name == NULL)
continue; continue;
if (String_eq(optarg, Process_fields[j].name)) { if (String_eq(optarg, Process_fields[j].name)) {
@ -298,9 +298,12 @@ int main(int argc, char** argv) {
if (flags.highlightDelaySecs != -1) if (flags.highlightDelaySecs != -1)
settings->highlightDelaySecs = flags.highlightDelaySecs; settings->highlightDelaySecs = flags.highlightDelaySecs;
if (flags.sortKey > 0) { if (flags.sortKey > 0) {
settings->sortKey = flags.sortKey; // -t -s <key> means "tree sorted by key"
settings->treeView = false; // -s <key> means "list sorted by key" (previous existing behavior)
settings->direction = 1; if (!flags.treeView) {
settings->treeView = false;
}
Settings_setSortKey(settings, flags.sortKey);
} }
CRT_init(&(settings->delay), settings->colorScheme, flags.allowUnicode); CRT_init(&(settings->delay), settings->colorScheme, flags.allowUnicode);

BIN
htop.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

112
htop.svg Normal file
View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="30.398mm" height="30.399mm" version="1.1" viewBox="0 0 30.398 30.399" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="aj" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(41.404 221.45)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
<linearGradient id="c">
<stop stop-color="#363d36" offset="0"/>
<stop stop-color="#565d56" offset=".2"/>
<stop stop-color="#6c766c" offset=".8"/>
<stop stop-color="#8c968c" offset="1"/>
</linearGradient>
<filter id="f" x="-.063963" y="-.025049" width="1.1279" height="1.0501" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.072789412"/>
</filter>
<linearGradient id="ai" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(41.508 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<linearGradient id="a">
<stop stop-color="#8bf18b" offset="0"/>
<stop stop-color="#5bc15b" offset=".2"/>
<stop stop-color="#47a347" offset=".8"/>
<stop stop-color="#177317" offset="1"/>
</linearGradient>
<filter id="l" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.065909587"/>
</filter>
<linearGradient id="d" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(24.234,-9.2667)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
<filter id="e" x="-.063963" y="-.025049" width="1.1279" height="1.0501" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.072789412"/>
</filter>
<linearGradient id="ah" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(44.242 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<filter id="k" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.065909587"/>
</filter>
<linearGradient id="ag" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(46.976 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<filter id="j" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.065909587"/>
</filter>
<linearGradient id="af" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(49.709 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<filter id="am" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.065909587"/>
</filter>
<linearGradient id="ae" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(52.443 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<filter id="al" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.065909587"/>
</filter>
<linearGradient id="ad" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(55.177 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<filter id="ak" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.065909587"/>
</filter>
<linearGradient id="ac" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(57.91 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<filter id="i" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.065909587"/>
</filter>
<linearGradient id="ab" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(60.644 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
<linearGradient id="b">
<stop stop-color="#fe7f7f" offset="0"/>
<stop stop-color="#ce4f4f" offset=".2"/>
<stop stop-color="#b23030" offset=".8"/>
<stop stop-color="#920000" offset="1"/>
</linearGradient>
<filter id="h" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.065909587"/>
</filter>
<linearGradient id="aa" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(63.376 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
<filter id="g" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.065909587"/>
</filter>
<linearGradient id="z" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(41.404 210.02)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
<linearGradient id="y" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(41.508 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<linearGradient id="x" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(44.242 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<linearGradient id="w" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(46.976 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<linearGradient id="v" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(57.91 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<linearGradient id="u" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(63.376 210)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
<linearGradient id="t" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(41.404 232.87)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
<linearGradient id="s" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(41.508 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<linearGradient id="r" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(44.242 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<linearGradient id="q" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(46.976 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<linearGradient id="p" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(60.644 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
<linearGradient id="o" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(63.376 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
<linearGradient id="n" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(57.91 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
<linearGradient id="m" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(60.644 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
</defs>
<g transform="translate(.08011 -.58599)">
<g transform="matrix(.98769 0 0 .99741 -80.905 -258.93)" fill-rule="evenodd">
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m82.188 271.97 0.0251 6.963 2.7061-8e-3v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#aj)" filter="url(#f)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m85.401 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ai)" filter="url(#l)"/>
<path transform="matrix(-.99856 0 0 1.2772 177.16 218.32)" d="m65.017 41.255 0.02508 6.963 2.7061-0.0084v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#d)" filter="url(#e)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m88.135 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ah)" filter="url(#k)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m90.868 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ag)" filter="url(#j)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m93.602 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#af)" filter="url(#am)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m96.335 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ae)" filter="url(#al)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m99.069 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ad)" filter="url(#ak)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m101.8 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ac)" filter="url(#i)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m104.54 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ab)" filter="url(#h)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m107.27 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#aa)" filter="url(#g)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m82.188 260.54 0.0251 6.963 2.7061-8e-3v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#z)" filter="url(#f)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m85.401 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#y)" filter="url(#l)"/>
<path transform="matrix(-.99856 0 0 1.2772 177.16 207.94)" d="m65.017 41.255 0.02508 6.963 2.7061-0.0084v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#d)" filter="url(#e)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m88.135 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#x)" filter="url(#k)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m90.868 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#w)" filter="url(#j)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m101.8 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#v)" filter="url(#i)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m107.27 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#u)" filter="url(#g)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m82.188 283.39 0.0251 6.963 2.7061-8e-3v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#t)" filter="url(#f)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m85.401 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#s)" filter="url(#l)"/>
<path transform="matrix(-.99856 0 0 1.2772 177.16 228.65)" d="m65.017 41.255 0.02508 6.963 2.7061-0.0084v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#d)" filter="url(#e)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m88.135 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#r)" filter="url(#k)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m90.868 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#q)" filter="url(#j)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m104.54 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#p)" filter="url(#h)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m107.27 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#o)" filter="url(#g)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m101.8 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#n)" filter="url(#h)"/>
<path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m104.54 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#m)" filter="url(#i)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -8,6 +8,8 @@
{ include: ["<bits/getopt_core.h>", "private", "<unistd.h>", "public"] }, { include: ["<bits/getopt_core.h>", "private", "<unistd.h>", "public"] },
{ include: ["<sys/dirent.h>", "private", "<dirent.h>", "public"] },
{ include: ["<sys/signal.h>", "private", "<signal.h>", "public"] }, { include: ["<sys/signal.h>", "private", "<signal.h>", "public"] },
{ include: ["<sys/_stdarg.h>", "private", "<stdarg.h>", "public"] }, { include: ["<sys/_stdarg.h>", "private", "<stdarg.h>", "public"] },

View File

@ -4,7 +4,7 @@
#include <dlfcn.h> #include <dlfcn.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <math.h>
#include <sensors/sensors.h> #include <sensors/sensors.h>
#include "XUtils.h" #include "XUtils.h"
@ -17,14 +17,19 @@ static int (*sym_sensors_snprintf_chip_name)(char*, size_t, const sensors_chip_n
static const sensors_feature* (*sym_sensors_get_features)(const sensors_chip_name*, int*); static const sensors_feature* (*sym_sensors_get_features)(const sensors_chip_name*, int*);
static const sensors_subfeature* (*sym_sensors_get_subfeature)(const sensors_chip_name*, const sensors_feature*, sensors_subfeature_type); static const sensors_subfeature* (*sym_sensors_get_subfeature)(const sensors_chip_name*, const sensors_feature*, sensors_subfeature_type);
static int (*sym_sensors_get_value)(const sensors_chip_name*, int, double*); static int (*sym_sensors_get_value)(const sensors_chip_name*, int, double*);
static char* (*sym_sensors_get_label)(const sensors_chip_name*, const sensors_feature*);
static void* dlopenHandle = NULL; static void* dlopenHandle = NULL;
int LibSensors_init(FILE* input) { int LibSensors_init(FILE* input) {
if (!dlopenHandle) { if (!dlopenHandle) {
dlopenHandle = dlopen("libsensors.so", RTLD_LAZY); dlopenHandle = dlopen("libsensors.so", RTLD_LAZY);
if (!dlopenHandle) if (!dlopenHandle) {
goto dlfailure; /* Debian contains no unversioned .so in libsensors5, only in the -dev package, so work around that: */
dlopenHandle = dlopen("libsensors.so.5", RTLD_LAZY);
if (!dlopenHandle)
goto dlfailure;
}
/* Clear any errors */ /* Clear any errors */
dlerror(); dlerror();
@ -42,6 +47,7 @@ int LibSensors_init(FILE* input) {
resolve(sensors_get_features); resolve(sensors_get_features);
resolve(sensors_get_subfeature); resolve(sensors_get_subfeature);
resolve(sensors_get_value); resolve(sensors_get_value);
resolve(sensors_get_label);
#undef resolve #undef resolve
} }
@ -65,11 +71,14 @@ void LibSensors_cleanup(void) {
} }
} }
int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) { void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) {
if (!dlopenHandle) for (unsigned int i = 0; i <= cpuCount; i++)
return -ENOTSUP; cpus[i].temperature = NAN;
int tempCount = 0; if (!dlopenHandle)
return;
unsigned int coreTempCount = 0;
int n = 0; int n = 0;
for (const sensors_chip_name *chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) { for (const sensors_chip_name *chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) {
@ -83,7 +92,25 @@ int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) {
if (feature->type != SENSORS_FEATURE_TEMP) if (feature->type != SENSORS_FEATURE_TEMP)
continue; continue;
if (feature->number > cpuCount) char* label = sym_sensors_get_label(chip, feature);
if (!label)
continue;
unsigned int tempId;
if (String_startsWith(label, "Package ")) {
tempId = 0;
} else if (String_startsWith(label, "temp")) {
/* Raspberry Pi has only temp1 */
tempId = 0;
} else if (String_startsWith(label, "Core ")) {
tempId = 1 + atoi(label + strlen("Core "));
} else {
tempId = UINT_MAX;
}
free(label);
if (tempId > cpuCount)
continue; continue;
const sensors_subfeature *sub_feature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT); const sensors_subfeature *sub_feature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT);
@ -93,13 +120,43 @@ int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) {
if (r != 0) if (r != 0)
continue; continue;
cpus[feature->number].temperature = temp; cpus[tempId].temperature = temp;
tempCount++; if (tempId > 0)
coreTempCount++;
} }
} }
} }
return tempCount; const double packageTemp = cpus[0].temperature;
/* Only package temperature - copy to all cpus */
if (coreTempCount == 0 && !isnan(packageTemp)) {
for (unsigned int i = 1; i <= cpuCount; i++)
cpus[i].temperature = packageTemp;
return;
}
/* No package temperature - set to max core temperature */
if (isnan(packageTemp) && coreTempCount != 0) {
double maxTemp = NAN;
for (unsigned int i = 1; i <= cpuCount; i++) {
const double coreTemp = cpus[i].temperature;
if (isnan(coreTemp))
continue;
maxTemp = MAXIMUM(maxTemp, coreTemp);
}
cpus[0].temperature = maxTemp;
}
/* Half the temperatures, probably HT/SMT - copy to second half */
const unsigned int delta = cpuCount / 2;
if (coreTempCount == delta) {
for (unsigned int i = 1; i <= delta; i++)
cpus[i + delta].temperature = cpus[i].temperature;
}
} }
#endif /* HAVE_SENSORS_SENSORS_H */ #endif /* HAVE_SENSORS_SENSORS_H */

View File

@ -11,6 +11,6 @@
int LibSensors_init(FILE* input); int LibSensors_init(FILE* input);
void LibSensors_cleanup(void); void LibSensors_cleanup(void);
int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount); void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount);
#endif /* HEADER_LibSensors */ #endif /* HEADER_LibSensors */

View File

@ -11,6 +11,7 @@ in the source distribution for its full text.
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <syscall.h> #include <syscall.h>
#include <unistd.h> #include <unistd.h>
@ -23,22 +24,22 @@ in the source distribution for its full text.
/* semi-global */ /* semi-global */
long long btime; int pageSize;
int pageSizeKB;
/* Used to identify kernel threads in Comm and Exe columns */ /* Used to identify kernel threads in Comm and Exe columns */
static const char *const kthreadID = "KTHREAD"; static const char *const kthreadID = "KTHREAD";
ProcessFieldData Process_fields[] = { const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging, I idle)", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging, I idle)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = "TTY ", .description = "Controlling terminal", .flags = 0, }, [TTY_NR] = { .name = "TTY_NR", .title = "TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[FLAGS] = { .name = "FLAGS", .title = NULL, .description = NULL, .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[CMINFLT] = { .name = "CMINFLT", .title = " CMINFLT ", .description = "Children processes' minor faults", .flags = 0, }, [CMINFLT] = { .name = "CMINFLT", .title = " CMINFLT ", .description = "Children processes' minor faults", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
@ -49,24 +50,7 @@ ProcessFieldData Process_fields[] = {
[CSTIME] = { .name = "CSTIME", .title = " CSTIME+ ", .description = "Children processes' system CPU time", .flags = 0, }, [CSTIME] = { .name = "CSTIME", .title = " CSTIME+ ", .description = "Children processes' system CPU time", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, }, [NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[ITREALVALUE] = { .name = "ITREALVALUE", .title = NULL, .description = NULL, .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, }, [STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[VSIZE] = { .name = "VSIZE", .title = NULL, .description = NULL, .flags = 0, },
[RSS] = { .name = "RSS", .title = NULL, .description = NULL, .flags = 0, },
[RLIM] = { .name = "RLIM", .title = NULL, .description = NULL, .flags = 0, },
[STARTCODE] = { .name = "STARTCODE", .title = NULL, .description = NULL, .flags = 0, },
[ENDCODE] = { .name = "ENDCODE", .title = NULL, .description = NULL, .flags = 0, },
[STARTSTACK] = { .name = "STARTSTACK", .title = NULL, .description = NULL, .flags = 0, },
[KSTKESP] = { .name = "KSTKESP", .title = NULL, .description = NULL, .flags = 0, },
[KSTKEIP] = { .name = "KSTKEIP", .title = NULL, .description = NULL, .flags = 0, },
[SIGNAL] = { .name = "SIGNAL", .title = NULL, .description = NULL, .flags = 0, },
[BLOCKED] = { .name = "BLOCKED", .title = NULL, .description = NULL, .flags = 0, },
[SSIGIGNORE] = { .name = "SIGIGNORE", .title = NULL, .description = NULL, .flags = 0, },
[SIGCATCH] = { .name = "SIGCATCH", .title = NULL, .description = NULL, .flags = 0, },
[WCHAN] = { .name = "WCHAN", .title = NULL, .description = NULL, .flags = 0, },
[NSWAP] = { .name = "NSWAP", .title = NULL, .description = NULL, .flags = 0, },
[CNSWAP] = { .name = "CNSWAP", .title = NULL, .description = NULL, .flags = 0, },
[EXIT_SIGNAL] = { .name = "EXIT_SIGNAL", .title = NULL, .description = NULL, .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
@ -82,10 +66,10 @@ ProcessFieldData Process_fields[] = {
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
#ifdef HAVE_OPENVZ #ifdef HAVE_OPENVZ
[CTID] = { .name = "CTID", .title = " CTID ", .description = "OpenVZ container ID (a.k.a. virtual environment ID)", .flags = PROCESS_FLAG_LINUX_OPENVZ, }, [CTID] = { .name = "CTID", .title = " CTID ", .description = "OpenVZ container ID (a.k.a. virtual environment ID)", .flags = PROCESS_FLAG_LINUX_OPENVZ, },
[VPID] = { .name = "VPID", .title = " VPID ", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_LINUX_OPENVZ, }, [VPID] = { .name = "VPID", .title = "VPID", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_LINUX_OPENVZ, .pidColumn = true, },
#endif #endif
#ifdef HAVE_VSERVER #ifdef HAVE_VSERVER
[VXID] = { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_LINUX_VSERVER, }, [VXID] = { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_LINUX_VSERVER, },
@ -116,20 +100,6 @@ ProcessFieldData Process_fields[] = {
[PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, }, [PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, },
[PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, }, [PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, },
[CWD] = { .name ="CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_LINUX_CWD, }, [CWD] = { .name ="CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_LINUX_CWD, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
#ifdef HAVE_OPENVZ
{ .id = VPID, .label = "VPID" },
#endif
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
}; };
/* This function returns the string displayed in Command column, so that sorting /* This function returns the string displayed in Command column, so that sorting
@ -182,6 +152,11 @@ static int LinuxProcess_effectiveIOPriority(const LinuxProcess* this) {
return this->ioPriority; return this->ioPriority;
} }
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif
IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) { IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) {
IOPriority ioprio = 0; IOPriority ioprio = 0;
// Other OSes masquerading as Linux (NetBSD?) don't have this syscall // Other OSes masquerading as Linux (NetBSD?) don't have this syscall
@ -393,6 +368,16 @@ void LinuxProcess_makeCommandStr(Process* this) {
char *str = strStart; char *str = strStart;
int cmdlineBasenameOffset = lp->procCmdlineBasenameOffset; int cmdlineBasenameOffset = lp->procCmdlineBasenameOffset;
int cmdlineBasenameEnd = lp->procCmdlineBasenameEnd;
if (!cmdline) {
cmdlineBasenameOffset = 0;
cmdlineBasenameEnd = 0;
cmdline = "(zombie)";
}
assert(cmdlineBasenameOffset >= 0);
assert(cmdlineBasenameOffset <= (int)strlen(cmdline));
if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */ if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */
if (showMergedCommand && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */ if (showMergedCommand && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */
@ -410,11 +395,11 @@ void LinuxProcess_makeCommandStr(Process* this) {
if (showProgramPath) { if (showProgramPath) {
(void) stpcpyWithNewlineConversion(str, cmdline); (void) stpcpyWithNewlineConversion(str, cmdline);
mc->baseStart = cmdlineBasenameOffset; mc->baseStart = cmdlineBasenameOffset;
mc->baseEnd = lp->procCmdlineBasenameEnd; mc->baseEnd = cmdlineBasenameEnd;
} else { } else {
(void) stpcpyWithNewlineConversion(str, cmdline + cmdlineBasenameOffset); (void) stpcpyWithNewlineConversion(str, cmdline + cmdlineBasenameOffset);
mc->baseStart = 0; mc->baseStart = 0;
mc->baseEnd = lp->procCmdlineBasenameEnd - cmdlineBasenameOffset; mc->baseEnd = cmdlineBasenameEnd - cmdlineBasenameOffset;
} }
if (mc->sep1) { if (mc->sep1) {
@ -429,6 +414,9 @@ void LinuxProcess_makeCommandStr(Process* this) {
int exeBasenameOffset = lp->procExeBasenameOffset; int exeBasenameOffset = lp->procExeBasenameOffset;
int exeBasenameLen = exeLen - exeBasenameOffset; int exeBasenameLen = exeLen - exeBasenameOffset;
assert(exeBasenameOffset >= 0);
assert(exeBasenameOffset <= (int)strlen(procExe));
/* Start with copying exe */ /* Start with copying exe */
if (showProgramPath) { if (showProgramPath) {
str = stpcpy(str, procExe); str = stpcpy(str, procExe);
@ -535,7 +523,7 @@ static void LinuxProcess_writeCommand(const Process* this, int attr, int baseAtt
if(lp->procExeDeleted) if(lp->procExeDeleted)
baseAttr = CRT_colors[FAILED_READ]; baseAttr = CRT_colors[FAILED_READ];
RichString_append(str, attr, lp->mergedCommand.str); RichString_appendWide(str, attr, lp->mergedCommand.str);
if (lp->mergedCommand.commEnd) { if (lp->mergedCommand.commEnd) {
if (!lp->mergedCommand.separateComm && commStart == baseStart && highlightBaseName) { if (!lp->mergedCommand.separateComm && commStart == baseStart && highlightBaseName) {
@ -604,10 +592,11 @@ static void LinuxProcess_writeCommandField(const Process *this, RichString *str,
buf = stpcpy(buf, " "); buf = stpcpy(buf, " ");
} }
} }
n -= (buf - buffer); n -= (buf - buffer);
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE]; const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
RichString_append(str, CRT_colors[PROCESS_TREE], buffer); RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
LinuxProcess_writeCommand(this, attr, baseattr, str); LinuxProcess_writeCommand(this, attr, baseattr, str);
} }
} }
@ -617,8 +606,8 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
bool coloring = this->settings->highlightMegabytes; bool coloring = this->settings->highlightMegabytes;
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1; size_t n = sizeof(buffer) - 1;
switch ((int)field) { switch (field) {
case TTY_NR: { case TTY_NR: {
if (lp->ttyDevice) { if (lp->ttyDevice) {
xSnprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */); xSnprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */);
@ -630,19 +619,19 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
} }
case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return; case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return;
case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return; case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return;
case M_DRS: Process_humanNumber(str, lp->m_drs * CRT_pageSizeKB, coloring); return; case M_DRS: Process_humanNumber(str, lp->m_drs * pageSizeKB, coloring); return;
case M_DT: Process_humanNumber(str, lp->m_dt * CRT_pageSizeKB, coloring); return; case M_DT: Process_humanNumber(str, lp->m_dt * pageSizeKB, coloring); return;
case M_LRS: case M_LRS:
if (lp->m_lrs) { if (lp->m_lrs) {
Process_humanNumber(str, lp->m_lrs * CRT_pageSizeKB, coloring); Process_humanNumber(str, lp->m_lrs * pageSizeKB, coloring);
return; return;
} }
attr = CRT_colors[PROCESS_SHADOW]; attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A "); xSnprintf(buffer, n, " N/A ");
break; break;
case M_TRS: Process_humanNumber(str, lp->m_trs * CRT_pageSizeKB, coloring); return; case M_TRS: Process_humanNumber(str, lp->m_trs * pageSizeKB, coloring); return;
case M_SHARE: Process_humanNumber(str, lp->m_share * CRT_pageSizeKB, coloring); return; case M_SHARE: Process_humanNumber(str, lp->m_share * pageSizeKB, coloring); return;
case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return; case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return;
case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return; case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return;
case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return; case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return;
@ -673,7 +662,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
} }
#ifdef HAVE_OPENVZ #ifdef HAVE_OPENVZ
case CTID: xSnprintf(buffer, n, "%-8s ", lp->ctid ? lp->ctid : ""); break; case CTID: xSnprintf(buffer, n, "%-8s ", lp->ctid ? lp->ctid : ""); break;
case VPID: xSnprintf(buffer, n, Process_pidFormat, lp->vpid); break; case VPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, lp->vpid); break;
#endif #endif
#ifdef HAVE_VSERVER #ifdef HAVE_VSERVER
case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break; case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break;
@ -756,22 +745,14 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
Process_writeField(this, str, field); Process_writeField(this, str, field);
return; return;
} }
RichString_append(str, attr, buffer); RichString_appendWide(str, attr, buffer);
} }
static long LinuxProcess_compare(const void* v1, const void* v2) { static long LinuxProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const LinuxProcess *p1, *p2; const LinuxProcess* p1 = (const LinuxProcess*)v1;
const Settings *settings = ((const Process*)v1)->settings; const LinuxProcess* p2 = (const LinuxProcess*)v2;
if (settings->direction == 1) { switch (key) {
p1 = (const LinuxProcess*)v1;
p2 = (const LinuxProcess*)v2;
} else {
p2 = (const LinuxProcess*)v1;
p1 = (const LinuxProcess*)v2;
}
switch ((int)settings->sortKey) {
case M_DRS: case M_DRS:
return SPACESHIP_NUMBER(p2->m_drs, p1->m_drs); return SPACESHIP_NUMBER(p2->m_drs, p1->m_drs);
case M_DT: case M_DT:
@ -857,7 +838,7 @@ static long LinuxProcess_compare(const void* v1, const void* v2) {
case CWD: case CWD:
return SPACESHIP_NULLSTR(p1->cwd, p2->cwd); return SPACESHIP_NULLSTR(p1->cwd, p2->cwd);
default: default:
return Process_compare(v1, v2); return Process_compareByKey_Base(v1, v2, key);
} }
} }
@ -870,8 +851,9 @@ const ProcessClass LinuxProcess_class = {
.extends = Class(Process), .extends = Class(Process),
.display = Process_display, .display = Process_display,
.delete = Process_delete, .delete = Process_delete,
.compare = LinuxProcess_compare .compare = Process_compare
}, },
.writeField = LinuxProcess_writeField, .writeField = LinuxProcess_writeField,
.getCommandStr = LinuxProcess_getCommandStr .getCommandStr = LinuxProcess_getCommandStr,
.compareByKey = LinuxProcess_compareByKey
}; };

View File

@ -8,10 +8,9 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" #include "config.h" // IWYU pragma: keep
#include <stdbool.h> #include <stdbool.h>
#include <sys/types.h>
#include "IOPriority.h" #include "IOPriority.h"
#include "Object.h" #include "Object.h"
@ -30,74 +29,6 @@ in the source distribution for its full text.
#define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000 #define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000
#define PROCESS_FLAG_LINUX_CWD 0x00020000 #define PROCESS_FLAG_LINUX_CWD 0x00020000
typedef enum UnsupportedProcessFields {
FLAGS = 9,
ITREALVALUE = 20,
VSIZE = 22,
RSS = 23,
RLIM = 24,
STARTCODE = 25,
ENDCODE = 26,
STARTSTACK = 27,
KSTKESP = 28,
KSTKEIP = 29,
SIGNAL = 30,
BLOCKED = 31,
SSIGIGNORE = 32,
SIGCATCH = 33,
WCHAN = 34,
NSWAP = 35,
CNSWAP = 36,
EXIT_SIGNAL = 37,
} UnsupportedProcessField;
typedef enum LinuxProcessFields {
CMINFLT = 11,
CMAJFLT = 13,
UTIME = 14,
STIME = 15,
CUTIME = 16,
CSTIME = 17,
M_SHARE = 41,
M_TRS = 42,
M_DRS = 43,
M_LRS = 44,
M_DT = 45,
#ifdef HAVE_OPENVZ
CTID = 100,
VPID = 101,
#endif
#ifdef HAVE_VSERVER
VXID = 102,
#endif
RCHAR = 103,
WCHAR = 104,
SYSCR = 105,
SYSCW = 106,
RBYTES = 107,
WBYTES = 108,
CNCLWB = 109,
IO_READ_RATE = 110,
IO_WRITE_RATE = 111,
IO_RATE = 112,
CGROUP = 113,
OOM = 114,
IO_PRIORITY = 115,
#ifdef HAVE_DELAYACCT
PERCENT_CPU_DELAY = 116,
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
M_PSS = 119,
M_SWAP = 120,
M_PSSWP = 121,
CTXT = 122,
SECATTR = 123,
PROC_COMM = 124,
PROC_EXE = 125,
CWD = 126,
LAST_PROCESSFIELD = 127,
} LinuxProcessField;
/* LinuxProcessMergedCommand is populated by LinuxProcess_makeCommandStr: It /* LinuxProcessMergedCommand is populated by LinuxProcess_makeCommandStr: It
* contains the merged Command string, and the information needed by * contains the merged Command string, and the information needed by
@ -192,11 +123,11 @@ static inline bool Process_isUserlandThread(const Process* this) {
return this->pid != this->tgid; return this->pid != this->tgid;
} }
extern long long btime; extern int pageSize;
extern ProcessFieldData Process_fields[]; extern int pageSizeKB;
extern ProcessPidColumn Process_pidColumns[]; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern const ProcessClass LinuxProcess_class; extern const ProcessClass LinuxProcess_class;

View File

@ -13,18 +13,17 @@ in the source distribution for its full text.
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h> #include <limits.h>
#include <math.h> #include <math.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/utsname.h>
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
#include <linux/netlink.h> #include <linux/netlink.h>
@ -58,6 +57,16 @@ in the source distribution for its full text.
#endif #endif
// CentOS 6's kernel doesn't provide a definition of O_PATH
// based on definition taken from uapi/asm-generic/fcnth.h in Linux kernel tree
#ifndef O_PATH
# define O_PATH 010000000
#endif
static long long btime;
static long jiffy;
static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) { static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) {
assert(String_eq(mode, "r")); /* only currently supported mode */ assert(String_eq(mode, "r")); /* only currently supported mode */
@ -203,35 +212,39 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
LinuxProcessList_initNetlinkSocket(this); LinuxProcessList_initNetlinkSocket(this);
#endif #endif
// Check for /proc/*/smaps_rollup availability (improves smaps parsing speed, Linux 4.14+) // Initialize page size
FILE* file = fopen(PROCDIR "/self/smaps_rollup", "r"); pageSize = sysconf(_SC_PAGESIZE);
if (file != NULL) { if (pageSize == -1)
this->haveSmapsRollup = true; CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)");
fclose(file); pageSizeKB = pageSize / ONE_K;
} else {
this->haveSmapsRollup = false;
}
// Read btime // Initialize clock ticks
jiffy = sysconf(_SC_CLK_TCK);
if (jiffy == -1)
CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)");
// Test /proc/PID/smaps_rollup availability (faster to parse, Linux 4.14+)
this->haveSmapsRollup = (access(PROCDIR "/self/smaps_rollup", R_OK) == 0);
// Read btime (the kernel boot time, as number of seconds since the epoch)
{ {
FILE* statfile = fopen(PROCSTATFILE, "r"); FILE* statfile = fopen(PROCSTATFILE, "r");
if (statfile == NULL) { if (statfile == NULL)
CRT_fatalError("Cannot open " PROCSTATFILE); CRT_fatalError("Cannot open " PROCSTATFILE);
}
while (true) { while (true) {
char buffer[PROC_LINE_LENGTH + 1]; char buffer[PROC_LINE_LENGTH + 1];
if (fgets(buffer, sizeof(buffer), statfile) == NULL) { if (fgets(buffer, sizeof(buffer), statfile) == NULL)
CRT_fatalError("No btime in " PROCSTATFILE);
} else if (String_startsWith(buffer, "btime ")) {
if (sscanf(buffer, "btime %lld\n", &btime) != 1) {
CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
}
break; break;
} if (String_startsWith(buffer, "btime ") == false)
continue;
if (sscanf(buffer, "btime %lld\n", &btime) == 1)
break;
CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
} }
fclose(statfile); fclose(statfile);
if (!btime)
CRT_fatalError("No btime in " PROCSTATFILE);
} }
// Initialize CPU count // Initialize CPU count
@ -268,16 +281,7 @@ void ProcessList_delete(ProcessList* pl) {
free(this); free(this);
} }
static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) { static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long t) {
static long jiffy = -1;
if (jiffy == -1) {
errno = 0;
jiffy = sysconf(_SC_CLK_TCK);
if (errno || -1 == jiffy) {
jiffy = -1;
return t; // Assume 100Hz clock
}
}
return t * 100 / jiffy; return t * 100 / jiffy;
} }
@ -330,13 +334,13 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
location += 1; location += 1;
lp->cmajflt = strtoull(location, &location, 10); lp->cmajflt = strtoull(location, &location, 10);
location += 1; location += 1;
lp->utime = LinuxProcess_adjustTime(strtoull(location, &location, 10)); lp->utime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1; location += 1;
lp->stime = LinuxProcess_adjustTime(strtoull(location, &location, 10)); lp->stime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1; location += 1;
lp->cutime = LinuxProcess_adjustTime(strtoull(location, &location, 10)); lp->cutime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1; location += 1;
lp->cstime = LinuxProcess_adjustTime(strtoull(location, &location, 10)); lp->cstime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1; location += 1;
process->priority = strtol(location, &location, 10); process->priority = strtol(location, &location, 10);
location += 1; location += 1;
@ -346,7 +350,7 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
location += 1; location += 1;
location = strchr(location, ' ') + 1; location = strchr(location, ' ') + 1;
if (process->starttime_ctime == 0) { if (process->starttime_ctime == 0) {
process->starttime_ctime = btime + LinuxProcess_adjustTime(strtoll(location, &location, 10)) / 100; process->starttime_ctime = btime + LinuxProcessList_adjustTime(strtoll(location, &location, 10)) / 100;
} else { } else {
location = strchr(location, ' ') + 1; location = strchr(location, ' ') + 1;
} }
@ -437,7 +441,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t proc
} }
} }
typedef struct LibraryData { typedef struct LibraryData_ {
uint64_t size; uint64_t size;
bool exec; bool exec;
} LibraryData; } LibraryData;
@ -574,7 +578,7 @@ static uint64_t LinuxProcessList_calcLibSize(openat_arg_t procFd) {
Hashtable_delete(ht); Hashtable_delete(ht);
return total_size / CRT_pageSize; return total_size / pageSize;
} }
static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, bool performLookup, unsigned long long now) { static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, bool performLookup, unsigned long long now) {
@ -594,6 +598,9 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t p
fclose(statmfile); fclose(statmfile);
if (r == 7) { if (r == 7) {
process->super.m_virt *= pageSizeKB;
process->super.m_resident *= pageSizeKB;
if (tmp_m_lrs) { if (tmp_m_lrs) {
process->m_lrs = tmp_m_lrs; process->m_lrs = tmp_m_lrs;
} else if (performLookup) { } else if (performLookup) {
@ -883,7 +890,7 @@ static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t
} }
static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd) { static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd) {
char pathBuffer[PATH_MAX + 1]; char pathBuffer[PATH_MAX + 1] = {0};
#if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT) #if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT)
ssize_t r = readlinkat(procFd, "cwd", pathBuffer, sizeof(pathBuffer) - 1); ssize_t r = readlinkat(procFd, "cwd", pathBuffer, sizeof(pathBuffer) - 1);
@ -892,6 +899,7 @@ static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd)
xSnprintf(filename, sizeof(filename), "%s/cwd", procFd); xSnprintf(filename, sizeof(filename), "%s/cwd", procFd);
ssize_t r = readlink(filename, pathBuffer, sizeof(pathBuffer) - 1); ssize_t r = readlink(filename, pathBuffer, sizeof(pathBuffer) - 1);
#endif #endif
if (r < 0) { if (r < 0) {
free(process->cwd); free(process->cwd);
process->cwd = NULL; process->cwd = NULL;
@ -1363,9 +1371,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
} }
/* period might be 0 after system sleep */ /* period might be 0 after system sleep */
float percent_cpu = (period < 1e-6) ? 0.0f : ((lp->utime + lp->stime - lasttimes) / period * 100.0); float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0);
proc->percent_cpu = CLAMP(percent_cpu, 0.0f, cpus * 100.0f); proc->percent_cpu = CLAMP(percent_cpu, 0.0F, cpus * 100.0F);
proc->percent_mem = (proc->m_resident * CRT_pageSizeKB) / (double)(pl->totalMem) * 100.0; proc->percent_mem = proc->m_resident / (double)(pl->totalMem) * 100.0;
if (!preExisting) { if (!preExisting) {
@ -1824,41 +1832,6 @@ static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
scanCPUFreqencyFromCPUinfo(this); scanCPUFreqencyFromCPUinfo(this);
} }
#ifdef HAVE_SENSORS_SENSORS_H
static void LinuxProcessList_scanCPUTemperature(LinuxProcessList* this) {
const int cpuCount = this->super.cpuCount;
for (int i = 0; i <= cpuCount; i++) {
this->cpus[i].temperature = NAN;
}
int r = LibSensors_getCPUTemperatures(this->cpus, cpuCount);
/* No temperature - nothing to do */
if (r <= 0)
return;
/* Only package temperature - copy to all cpus */
if (r == 1 && !isnan(this->cpus[0].temperature)) {
double packageTemp = this->cpus[0].temperature;
for (int i = 1; i <= cpuCount; i++) {
this->cpus[i].temperature = packageTemp;
}
return;
}
/* Half the temperatures, probably HT/SMT - copy to second half */
if (r >= 2 && (r - 1) == (cpuCount / 2)) {
for (int i = cpuCount / 2 + 1; i <= cpuCount; i++) {
this->cpus[i].temperature = this->cpus[i/2].temperature;
}
return;
}
}
#endif
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
LinuxProcessList* this = (LinuxProcessList*) super; LinuxProcessList* this = (LinuxProcessList*) super;
const Settings* settings = super->settings; const Settings* settings = super->settings;
@ -1876,7 +1849,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
#ifdef HAVE_SENSORS_SENSORS_H #ifdef HAVE_SENSORS_SENSORS_H
if (settings->showCPUTemperature) if (settings->showCPUTemperature)
LinuxProcessList_scanCPUTemperature(this); LibSensors_getCPUTemperatures(this->cpus, this->super.cpuCount);
#endif #endif
// in pause mode only gather global data for meters (CPU/memory/...) // in pause mode only gather global data for meters (CPU/memory/...)

View File

@ -12,7 +12,6 @@ in the source distribution for its full text.
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
@ -66,9 +65,7 @@ in the source distribution for its full text.
#endif #endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, (int)M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 }, { .name = " 0 Cancel", .number = 0 },
@ -371,7 +368,7 @@ char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
struct stat sb; struct stat sb;
struct dirent *de; struct dirent *de;
DIR *dirp; DIR *dirp;
size_t len; ssize_t len;
int fd; int fd;
char path[PATH_MAX]; char path[PATH_MAX];

View File

@ -18,9 +18,7 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h" #include "ProcessLocksScreen.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
extern ProcessField Platform_defaultFields[]; extern const ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const SignalItem Platform_signals[]; extern const SignalItem Platform_signals[];

View File

@ -25,7 +25,7 @@ static const int PressureStallMeter_attributes[] = {
PRESSURE_STALL_THREEHUNDRED PRESSURE_STALL_THREEHUNDRED
}; };
static void PressureStallMeter_updateValues(Meter* this, char* buffer, int len) { static void PressureStallMeter_updateValues(Meter* this, char* buffer, size_t len) {
const char* file; const char* file;
if (strstr(Meter_name(this), "CPU")) { if (strstr(Meter_name(this), "CPU")) {
file = "cpu"; file = "cpu";
@ -43,18 +43,22 @@ static void PressureStallMeter_updateValues(Meter* this, char* buffer, int len)
} }
Platform_getPressureStall(file, some, &this->values[0], &this->values[1], &this->values[2]); Platform_getPressureStall(file, some, &this->values[0], &this->values[1], &this->values[2]);
xSnprintf(buffer, len, "%s %s %.2lf%% %.2lf%% %.2lf%%", some ? "some" : "full", file, this->values[0], this->values[1], this->values[2]);
/* only print bar for ten (not sixty and threehundred), cause the sum is meaningless */
this->curItems = 1;
xSnprintf(buffer, len, "%s %s %5.2lf%% %5.2lf%% %5.2lf%%", some ? "some" : "full", file, this->values[0], this->values[1], this->values[2]);
} }
static void PressureStallMeter_display(const Object* cast, RichString* out) { static void PressureStallMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
char buffer[20]; char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[0]); xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[0]);
RichString_write(out, CRT_colors[PRESSURE_STALL_TEN], buffer); RichString_writeAscii(out, CRT_colors[PRESSURE_STALL_TEN], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[1]); xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[1]);
RichString_append(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer); RichString_appendAscii(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[2]); xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[2]);
RichString_append(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer); RichString_appendAscii(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer);
} }
const MeterClass PressureStallCPUSomeMeter_class = { const MeterClass PressureStallCPUSomeMeter_class = {

53
linux/ProcessField.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef HEADER_LinuxProcessField
#define HEADER_LinuxProcessField
/*
htop - linux/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
CMINFLT = 11, \
CMAJFLT = 13, \
UTIME = 14, \
STIME = 15, \
CUTIME = 16, \
CSTIME = 17, \
M_SHARE = 41, \
M_TRS = 42, \
M_DRS = 43, \
M_LRS = 44, \
M_DT = 45, \
CTID = 100, \
VPID = 101, \
VXID = 102, \
RCHAR = 103, \
WCHAR = 104, \
SYSCR = 105, \
SYSCW = 106, \
RBYTES = 107, \
WBYTES = 108, \
CNCLWB = 109, \
IO_READ_RATE = 110, \
IO_WRITE_RATE = 111, \
IO_RATE = 112, \
CGROUP = 113, \
OOM = 114, \
IO_PRIORITY = 115, \
PERCENT_CPU_DELAY = 116, \
PERCENT_IO_DELAY = 117, \
PERCENT_SWAP_DELAY = 118, \
M_PSS = 119, \
M_SWAP = 120, \
M_PSSWP = 121, \
CTXT = 122, \
SECATTR = 123, \
PROC_COMM = 124, \
PROC_EXE = 125, \
CWD = 126, \
// End of list
#endif /* HEADER_LinuxProcessField */

View File

@ -1,6 +1,6 @@
/* /*
htop - SELinuxMeter.c htop - SELinuxMeter.c
(C) 2020 Christian Goettsche (C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -9,7 +9,6 @@ in the source distribution for its full text.
#include "CRT.h" #include "CRT.h"
#include <fcntl.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
@ -36,7 +35,7 @@ static bool hasSELinuxMount(void) {
return false; return false;
} }
if (sfbuf.f_type != SELINUX_MAGIC) { if ((uint32_t)sfbuf.f_type != (uint32_t)SELINUX_MAGIC) {
return false; return false;
} }
@ -71,7 +70,7 @@ static bool isSelinuxEnforcing(void) {
return !!enforce; return !!enforce;
} }
static void SELinuxMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, int len) { static void SELinuxMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t len) {
enabled = isSelinuxEnabled(); enabled = isSelinuxEnabled();
enforcing = isSelinuxEnforcing(); enforcing = isSelinuxEnforcing();

View File

@ -2,7 +2,7 @@
#define HEADER_SELinuxMeter #define HEADER_SELinuxMeter
/* /*
htop - SELinuxMeter.h htop - SELinuxMeter.h
(C) 2020 Christian Goettsche (C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */

View File

@ -1,6 +1,6 @@
/* /*
htop - SystemdMeter.c htop - SystemdMeter.c
(C) 2020 Christian Göttsche (C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -229,7 +229,7 @@ static void updateViaExec(void) {
fclose(commandOutput); fclose(commandOutput);
} }
static void SystemdMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, int size) { static void SystemdMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t size) {
free(systemState); free(systemState);
systemState = NULL; systemState = NULL;
nFailedUnits = nInstalledJobs = nNames = nJobs = INVALID_VALUE; nFailedUnits = nInstalledJobs = nNames = nJobs = INVALID_VALUE;
@ -267,9 +267,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
char buffer[16]; char buffer[16];
int color = (systemState && 0 == strcmp(systemState, "running")) ? METER_VALUE_OK : METER_VALUE_ERROR; int color = (systemState && 0 == strcmp(systemState, "running")) ? METER_VALUE_OK : METER_VALUE_ERROR;
RichString_write(out, CRT_colors[color], systemState ? systemState : "???"); RichString_writeAscii(out, CRT_colors[color], systemState ? systemState : "N/A");
RichString_append(out, CRT_colors[METER_TEXT], " ("); RichString_appendAscii(out, CRT_colors[METER_TEXT], " (");
if (nFailedUnits == INVALID_VALUE) { if (nFailedUnits == INVALID_VALUE) {
buffer[0] = '?'; buffer[0] = '?';
@ -277,9 +277,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits); xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits);
} }
RichString_append(out, zeroDigitColor(nFailedUnits), buffer); RichString_appendAscii(out, zeroDigitColor(nFailedUnits), buffer);
RichString_append(out, CRT_colors[METER_TEXT], "/"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
if (nNames == INVALID_VALUE) { if (nNames == INVALID_VALUE) {
buffer[0] = '?'; buffer[0] = '?';
@ -287,9 +287,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%u", nNames); xSnprintf(buffer, sizeof(buffer), "%u", nNames);
} }
RichString_append(out, valueDigitColor(nNames), buffer); RichString_appendAscii(out, valueDigitColor(nNames), buffer);
RichString_append(out, CRT_colors[METER_TEXT], " failed) ("); RichString_appendAscii(out, CRT_colors[METER_TEXT], " failed) (");
if (nJobs == INVALID_VALUE) { if (nJobs == INVALID_VALUE) {
buffer[0] = '?'; buffer[0] = '?';
@ -297,9 +297,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%u", nJobs); xSnprintf(buffer, sizeof(buffer), "%u", nJobs);
} }
RichString_append(out, zeroDigitColor(nJobs), buffer); RichString_appendAscii(out, zeroDigitColor(nJobs), buffer);
RichString_append(out, CRT_colors[METER_TEXT], "/"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
if (nInstalledJobs == INVALID_VALUE) { if (nInstalledJobs == INVALID_VALUE) {
buffer[0] = '?'; buffer[0] = '?';
@ -307,9 +307,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs); xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs);
} }
RichString_append(out, valueDigitColor(nInstalledJobs), buffer); RichString_appendAscii(out, valueDigitColor(nInstalledJobs), buffer);
RichString_append(out, CRT_colors[METER_TEXT], " jobs)"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " jobs)");
} }
static const int SystemdMeter_attributes[] = { static const int SystemdMeter_attributes[] = {

View File

@ -3,7 +3,7 @@
/* /*
htop - SystemdMeter.h htop - SystemdMeter.h
(C) 2020 Christian Göttsche (C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */

View File

@ -11,7 +11,7 @@ static const int ZramMeter_attributes[] = {
ZRAM ZRAM
}; };
static void ZramMeter_updateValues(Meter* this, char* buffer, int size) { static void ZramMeter_updateValues(Meter* this, char* buffer, size_t size) {
int written; int written;
Platform_setZramValues(this); Platform_setZramValues(this);
@ -20,45 +20,36 @@ static void ZramMeter_updateValues(Meter* this, char* buffer, int size) {
this->curItems = 1; this->curItems = 1;
written = Meter_humanUnit(buffer, this->values[0], size); written = Meter_humanUnit(buffer, this->values[0], size);
buffer += written; METER_BUFFER_CHECK(buffer, size, written);
size -= written;
if (size <= 0) { METER_BUFFER_APPEND_CHR(buffer, size, '(');
return;
}
*buffer++ = '(';
size--;
if (size <= 0) {
return;
}
written = Meter_humanUnit(buffer, this->values[1], size); written = Meter_humanUnit(buffer, this->values[1], size);
buffer += written; METER_BUFFER_CHECK(buffer, size, written);
size -= written;
if (size <= 0) { METER_BUFFER_APPEND_CHR(buffer, size, ')');
return;
} METER_BUFFER_APPEND_CHR(buffer, size, '/');
*buffer++ = ')';
size--; Meter_humanUnit(buffer, this->total, size);
if ((size -= written) > 0) {
*buffer++ = '/';
size--;
Meter_humanUnit(buffer, this->total, size);
}
} }
static void ZramMeter_display(const Object* cast, RichString* out) { static void ZramMeter_display(const Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " uncompressed:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " uncompressed:");
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
} }
const MeterClass ZramMeter_class = { const MeterClass ZramMeter_class = {

View File

@ -6,28 +6,17 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Process.h"
#include "ProcessList.h"
#include "OpenBSDProcess.h" #include "OpenBSDProcess.h"
#include "Platform.h"
#include "CRT.h"
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h> #include "CRT.h"
#include "Process.h"
#include "RichString.h"
#include "XUtils.h"
const ProcessClass OpenBSDProcess_class = { const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = OpenBSDProcess_compare
},
.writeField = OpenBSDProcess_writeField,
};
ProcessFieldData Process_fields[] = {
[0] = { [0] = {
.name = "", .name = "",
.title = NULL, .title = NULL,
@ -36,9 +25,10 @@ ProcessFieldData Process_fields[] = {
}, },
[PID] = { [PID] = {
.name = "PID", .name = "PID",
.title = " PID ", .title = "PID",
.description = "Process/thread ID", .description = "Process/thread ID",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[COMM] = { [COMM] = {
.name = "Command", .name = "Command",
@ -54,21 +44,24 @@ ProcessFieldData Process_fields[] = {
}, },
[PPID] = { [PPID] = {
.name = "PPID", .name = "PPID",
.title = " PPID ", .title = "PPID",
.description = "Parent process ID", .description = "Parent process ID",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[PGRP] = { [PGRP] = {
.name = "PGRP", .name = "PGRP",
.title = " PGRP ", .title = "PGRP",
.description = "Process group ID", .description = "Process group ID",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[SESSION] = { [SESSION] = {
.name = "SESSION", .name = "SESSION",
.title = " SESN ", .title = "SESN",
.description = "Process's session ID", .description = "Process's session ID",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[TTY_NR] = { [TTY_NR] = {
.name = "TTY_NR", .name = "TTY_NR",
@ -78,9 +71,10 @@ ProcessFieldData Process_fields[] = {
}, },
[TPGID] = { [TPGID] = {
.name = "TPGID", .name = "TPGID",
.title = " TPGID ", .title = "TPGID",
.description = "Process ID of the fg process group of the controlling terminal", .description = "Process ID of the fg process group of the controlling terminal",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[MINFLT] = { [MINFLT] = {
.name = "MINFLT", .name = "MINFLT",
@ -174,33 +168,18 @@ ProcessFieldData Process_fields[] = {
}, },
[TGID] = { [TGID] = {
.name = "TGID", .name = "TGID",
.title = " TGID ", .title = "TGID",
.description = "Thread group ID (i.e. process ID)", .description = "Thread group ID (i.e. process ID)",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[LAST_PROCESSFIELD] = {
.name = "*** report bug! ***",
.title = NULL,
.description = NULL,
.flags = 0,
},
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SESN" },
{ .id = 0, .label = NULL },
}; };
Process* OpenBSDProcess_new(const Settings* settings) { Process* OpenBSDProcess_new(const Settings* settings) {
OpenBSDProcess* this = xCalloc(sizeof(OpenBSDProcess), 1); OpenBSDProcess* this = xCalloc(sizeof(OpenBSDProcess), 1);
Object_setClass(this, Class(OpenBSDProcess)); Object_setClass(this, Class(OpenBSDProcess));
Process_init(&this->super, settings); Process_init(&this->super, settings);
return &this->this; return &this->super;
} }
void Process_delete(Object* cast) { void Process_delete(Object* cast) {
@ -209,8 +188,8 @@ void Process_delete(Object* cast) {
free(this); free(this);
} }
void OpenBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) { static void OpenBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) {
//OpenBSDProcess* fp = (OpenBSDProcess*) this; //const OpenBSDProcess* op = (const OpenBSDProcess*) this;
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
//int n = sizeof(buffer) - 1; //int n = sizeof(buffer) - 1;
@ -220,28 +199,34 @@ void OpenBSDProcess_writeField(const Process* this, RichString* str, ProcessFiel
Process_writeField(this, str, field); Process_writeField(this, str, field);
return; return;
} }
RichString_append(str, attr, buffer); RichString_appendWide(str, attr, buffer);
} }
long OpenBSDProcess_compare(const void* v1, const void* v2) { static long OpenBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const OpenBSDProcess *p1, *p2; const OpenBSDProcess* p1 = (const OpenBSDProcess*)v1;
const Settings *settings = ((const Process*)v1)->settings; const OpenBSDProcess* p2 = (const OpenBSDProcess*)v2;
if (settings->direction == 1) { // remove if actually used
p1 = (const OpenBSDProcess*)v1; (void)p1; (void)p2;
p2 = (const OpenBSDProcess*)v2;
} else {
p2 = (const OpenBSDProcess*)v1;
p1 = (const OpenBSDProcess*)v2;
}
switch (settings->sortKey) { switch (key) {
// add OpenBSD-specific fields here // add OpenBSD-specific fields here
default: default:
return Process_compare(v1, v2); return Process_compareByKey_Base(v1, v2, key);
} }
} }
const ProcessClass OpenBSDProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = OpenBSDProcess_writeField,
.compareByKey = OpenBSDProcess_compareByKey
};
bool Process_isThread(const Process* this) { bool Process_isThread(const Process* this) {
return (Process_isKernelThread(this)); return Process_isKernelThread(this) || Process_isUserlandThread(this);
} }

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