221 Commits
2.2.0 ... 3.0.1

Author SHA1 Message Date
dace850fa6 Bump version, changelog for minor htop-3.0.1 release 2020-09-03 13:23:43 +10:00
4f00a95364 Merge pull request #66 from ioquatix/patch-1
Fix image logo and titles.
2020-09-03 08:35:19 +10:00
0ab508e42b Merge pull request #57 from matthiasbeyer/patch-1
Do not link INSTALL file, because link target does not exist
2020-09-03 08:20:36 +10:00
f79591ef1b Merge branch 'eworm-de-unicode-runtime' 2020-09-02 15:09:58 +10:00
746a5f279a Fix image logo and titles. 2020-09-02 11:54:17 +12:00
8ee7d58cb0 Do not link INSTALL file, because link target does not exist 2020-09-01 14:47:00 +02:00
db5adbeae0 add option (-U, --no-unicode) to disable unicode at runtime 2020-09-01 10:09:00 +02:00
f5b3e8d2a3 Merge branch 'cgzones-oom2' 2020-09-01 15:17:32 +10:00
809e4db672 Merge branch 'oom2' of https://github.com/cgzones/htop into cgzones-oom2 2020-09-01 15:17:23 +10:00
e1e60f38dc CRT: note about possible use of replacement for + glyph in tree 2020-08-31 22:35:09 +02:00
19359cec5a affinity panel: use the tree collapsing as in the process list
With one exception, the root node does also have a `-`/`+` as a prefix.
2020-08-31 22:22:22 +02:00
b0f1336f79 affinity panel: show CPUs in the topology tree as CPU x
As it is in the non-topology list.
2020-08-31 22:18:18 +02:00
f861a2c616 Revert "Use UTF-8 for check buttons and tree open/closed"
This reverts commit 5d5913d355b3a9f03da589b3542b8f55467b4ed6.
2020-08-31 22:12:46 +02:00
e7f6d1ce5f Reduce oom cast from long to int
Oom values should never be greater then INT_MAX, they should be in the
range 0 - 1000.

Improves: d9a5dd4b91
2020-08-31 11:55:53 +02:00
47a7d0bd74 Merge branch 'configure' of https://github.com/cgzones/htop into cgzones-configure 2020-08-31 17:13:37 +10:00
b321177b08 Merge branch 'master' of github.com:htop-dev/htop 2020-08-31 16:57:46 +10:00
800d8c735d Merge branch 'cov_fixes' of https://github.com/cgzones/htop into cgzones-cov_fixes 2020-08-31 16:56:32 +10:00
f14173038e Merge branch 'oom' of https://github.com/cgzones/htop
Closes: #18, #22
2020-08-31 08:32:39 +02:00
fdf8a28e60 Merge branch 'Ckath-vim_mode_setting' 2020-08-31 16:14:48 +10:00
244630f67f Merge branch 'vim_mode_setting' of https://github.com/Ckath/htop into Ckath-vim_mode_setting 2020-08-31 16:14:40 +10:00
0a835e13bf Simplify the --version output, old dates are confusing people
Drop the copyright notice from the version output as a number
of people seem to be confused by what this means, and we can
do without all the (well intentioned) bug reports.
2020-08-31 16:14:23 +10:00
4bd0859b80 Add a badge/link to the released source tarballs 2020-08-31 16:12:44 +10:00
338bd829b0 add toggle for vim mode in options 2020-08-29 15:15:52 +02:00
5c99c6e942 Check btime sscanf parse from /proc/stat
Found by Coverity
2020-08-28 16:46:50 +02:00
a850d81bf5 Avoid use of uninitialized variables
Found by Coverity
2020-08-28 16:46:50 +02:00
05a5fdc47f Ignore sscanf return value of /proc/stat
Found by Coverity
2020-08-28 16:46:50 +02:00
af84d3dfa9 Fail on out-of-range CPU number
Found by Coverity
2020-08-28 16:46:50 +02:00
df41979afc Ignore wmove return value
Found by Coverity
2020-08-28 16:46:50 +02:00
d9a5dd4b91 Improve OOM output
* Fix sort by adding cast
* Shrink column size to 4
* Drop unnecessary maximum field width specifier in sscanf
2020-08-28 14:24:59 +02:00
a48ce9d103 Really tell gcc to ignore return value of fscanf 2020-08-28 13:10:41 +02:00
3f5784a3f0 Convert hwlock CI run to a full featured one 2020-08-28 13:10:41 +02:00
3b084db1c4 Print configured state 2020-08-28 13:10:41 +02:00
979d004214 Improve indent 2020-08-28 13:10:16 +02:00
5bee902665 Drop configure option --enable-proc
Move to HTOP_LINUX, as --enable-proc implies my_htop_platform=linux, and
the Linux features do not work without a proc fs.
2020-08-28 13:10:16 +02:00
2d14269bcd Merge pull request #14 from zdykstra/master
Normalize ZFS ARC caption
2020-08-28 16:58:06 +10:00
b992d52bcf Increae the size of sysfs power supply path buffers
Resolves https://github.com/htop-dev/htop/issues/15
2020-08-28 16:57:21 +10:00
f97fbd668a Normalize ZFS ARC caption
Other captions take the form of LABEL:<space>. This moves the
uncompressed ZFS ARC caption into the same style.
2020-08-28 00:02:35 -05:00
b5e6952cc6 Update link to Coverity project, still pending. 2020-08-27 10:42:40 +10:00
4a8ae4b5d4 Merge branch 'bertwesarg-affinity-fix-panel-width' 2020-08-27 09:36:56 +10:00
94b8c2e714 fix width of AffinitPanel
The panel size of 15 includes the gap to the next panel, thus use 14 as
the minimum size and let the caller of `AffinityPanel_new` handle the
gap.
2020-08-26 22:03:11 +02:00
df7e4fcdc0 Update changelog with Berts latest addition 2020-08-26 10:44:22 +10:00
728b04bbb5 Merge branch 'ci-hwloc-job' of https://github.com/bertwesarg/htop into bertwesarg-ci-hwloc-job 2020-08-26 10:39:43 +10:00
d0f31ede56 Merge branch 'ci-hwloc-job' of https://github.com/bertwesarg/htop into bertwesarg-ci-hwloc-job 2020-08-26 10:15:00 +10:00
ba94e0dfda Merge branch 'ci2' of https://github.com/cgzones/htop into cgzones-ci2 2020-08-26 10:08:50 +10:00
fc4f74aa47 ci: add clang build 2020-08-25 12:01:56 +02:00
4e2b9f0965 Avoid shadowing warnings 2020-08-25 12:01:56 +02:00
b4ceb83d76 MakeHeader.py.in: remove unused import 2020-08-25 12:00:08 +02:00
1130ad8b73 MakeHeader.py.in: remove executable bit 2020-08-25 12:00:08 +02:00
11f558f934 Avoid discarding const qualifiers 2020-08-25 12:00:03 +02:00
7457bfe9f3 Avoid string overflow warning
Use xStrdup instead of xMallow and strncpy

    StringUtils.c: In function ‘String_split’:
    StringUtils.c:86:7: error: ‘strncpy’ specified bound depends on the length of the source argument [-Werror=stringop-overflow=]
       86 |       strncpy(token, s, size + 1);
          |       ^
    StringUtils.c:84:18: note: length computed here
       84 |       int size = strlen(s);
          |                  ^
2020-08-25 11:59:59 +02:00
21fb56e1e2 Avoid string overflow warning
Enough memory is allocated.

    Header.c: In function ‘Header_readMeterName’:
    Header.c:157:4: error: ‘strncpy’ specified bound depends on the length of the source argument [-Werror=stringop-overflow=]
      157 |    strncpy(name, Meter_name(meter), nameLen);
          |    ^
    Header.c:154:18: note: length computed here
      154 |    int nameLen = strlen(Meter_name(meter));
          |                  ^
2020-08-25 11:59:59 +02:00
6b11769448 Avoid conversion warning
linux/Platform.c:47:90: error: implicit conversion from ‘enum LinuxProcessFields’ to ‘enum ProcessFields’ [-Werror=enum-conversion]
       47 | ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
          |
2020-08-25 11:59:53 +02:00
d64a6a2453 CI: Add a HWLOC enabled job 2020-08-25 11:42:21 +02:00
345d415537 Do not include the generated config.h header into the package 2020-08-25 11:42:16 +02:00
054b7f2801 Fix out-of-tree builds 2020-08-25 10:36:27 +02:00
e172282002 Fix in AC_CONFIG_FILES, try #2 2020-08-24 21:37:28 +02:00
0bac7c9d94 Nope, configure works but make breaks.
This reverts commit dad62b6c9e.
2020-08-24 21:08:07 +02:00
dad62b6c9e Put Makeheader.py.in into AC_CONFIG_FILES 2020-08-24 21:01:50 +02:00
9eb9064fbd Cleanup unused CI build notes and whitespace 2020-08-24 10:22:44 +10:00
9e57b5c3f4 Generate an appropriate shebang line for MakeHeader script
Use configure.ac to handle platform differences where some
build hosts have only a python3, or only python, binary.

Related to https://github.com/htop-dev/htop/pull/6
2020-08-23 11:24:52 +10:00
b3aef4ea3a Revert "MakeHeader.py: use python3 shebang"
This reverts commit 40ac7a88af.
as it causes build failure on non-python3 platforms.
2020-08-23 09:42:11 +10:00
6900e57efd Updates to project URLs in docs and embedded in source code 2020-08-22 15:47:11 +10:00
5dad65ac2a Update header files to match whitespace changes in source files 2020-08-22 15:46:31 +10:00
6315f10725 Merge branch 'ginggs-patch-1' 2020-08-22 15:36:02 +10:00
45062b26d6 Merge branch 'patch-1' of https://github.com/ginggs/htop-1 into ginggs-patch-1 2020-08-22 15:35:55 +10:00
ada780c867 Merge branch 'cgzones-ci2' 2020-08-22 15:35:02 +10:00
6aed2be247 Fix build on FreeBSD 2020-08-21 16:49:28 +02:00
9fde0835ed Avoid empty translation unit warning
zfs/ZfsArcStats.c:22: error: ISO C forbids an empty translation unit [-Werror=pedantic]
       22 | }*/
          |
2020-08-21 10:38:44 +02:00
b92f62f912 Remove trailing whitespaces 2020-08-21 10:37:33 +02:00
3856bf574b Introduce xAsprintf as checked version of asprintf 2020-08-21 10:37:29 +02:00
40ac7a88af MakeHeader.py: use python3 shebang
Also drop unused import
2020-08-21 10:37:27 +02:00
d6adc2b681 github/ci: improve ci
- split steps for readability
- fail on compiler warnings
- add whitespace check
- run on all branches
- run `make distcheck`
2020-08-21 10:37:25 +02:00
57254cdd05 configure: add option --enable-werror
Adds the compiler flag -Werror to fail on warnings.
Useful for CI runs.
2020-08-21 10:37:20 +02:00
0b276f80f1 Mention change of maintainership in ChangeLog
Related to https://github.com/hishamhm/htop/issues/992
2020-08-21 16:42:16 +10:00
bba8c3bb2e Update the changelog to reflect content in the 3.0.0 release 2020-08-21 12:10:11 +10:00
9a55efc8b5 Merge branch 'hishamhm-pull-920' 2020-08-20 18:24:35 +10:00
31391b206c Merge branch 'hishamhm-pull-1012' 2020-08-20 15:51:21 +10:00
fed14a584d Merge branch 'hishamhm-pull-970' 2020-08-20 15:33:30 +10:00
cca07e9bc9 Merge branch 'hishamhm-pull-1011' 2020-08-20 15:18:20 +10:00
9aa816873f Merge branch 'hishamhm-pull-1010' 2020-08-20 15:16:47 +10:00
0622be6ab3 Merge branch 'hishamhm-pull-1009' 2020-08-20 15:14:26 +10:00
54b6143cf6 Merge branch 'hishamhm-pull-996' 2020-08-20 15:07:59 +10:00
5c5a599a2a Merge branch 'hishamhm-pull-959' 2020-08-20 14:57:30 +10:00
e25ac8557d Merge branch 'hishamhm-pull-949' 2020-08-20 14:48:47 +10:00
6b443c5da9 Merge branch 'hishamhm-pull-932' 2020-08-20 14:47:07 +10:00
011125dab2 Merge branch 'hishamhm-pull-923' 2020-08-20 14:42:18 +10:00
ab61ae3963 Merge branch 'hishamhm-pull-974' 2020-08-20 14:24:11 +10:00
f4b0067339 Merge branch 'hishamhm-pull-971' 2020-08-20 14:22:15 +10:00
1b8bed1294 Merge branch 'hishamhm-pull-964' 2020-08-20 14:21:49 +10:00
a82fd262d7 Merge branch 'hishamhm-pull-960' 2020-08-20 14:19:53 +10:00
cdff8aea2a Merge branch 'hishamhm-pull-946' 2020-08-20 14:13:07 +10:00
b30c22d687 Merge branch 'hishamhm-pull-914' 2020-08-20 14:09:27 +10:00
42c8e47cd6 Merge branch 'hishamhm-pull-906' 2020-08-20 14:02:51 +10:00
0f76359e4e Merge branch 'hishamhm-pull-890' 2020-08-20 14:01:47 +10:00
e833bf915f Merge branch 'hishamhm-pull-904' 2020-08-20 14:01:33 +10:00
2be0992700 Merge branch 'hishamhm-pull-890' 2020-08-20 14:00:13 +10:00
fb679dfb18 Merge branch 'hishamhm-pull-884' 2020-08-20 13:57:09 +10:00
a7a5e356c2 Documentation updates to reflect community maintainership
Document the htop.dev site, #htop and htop@groups.io for contacting
the community maintainers, and the upcoming 3.0.0 release.
2020-08-20 13:16:31 +10:00
dab4144f4a Merge branch 'hishamhm-pull-872' 2020-08-20 12:30:51 +10:00
45ae6191c1 Merge branch 'hishamhm-pull-866' 2020-08-20 12:29:25 +10:00
ed2b7791df Merge branch 'hishamhm-pull-871' 2020-08-20 09:53:39 +10:00
78f4d064de Merge branch 'hishamhm-pull-868' 2020-08-20 09:53:00 +10:00
e7d2f9383a Merge branch 'hishamhm-pull-857' 2020-08-20 09:47:13 +10:00
97d700082c Merge branch 'hishamhm-pull-855' 2020-08-20 09:44:25 +10:00
46ab1aa3bd Merge branch 'hishamhm-pull-850' 2020-08-20 09:42:40 +10:00
500fb283e9 Resolve compiler warnings and errors relating to the Arg union
Promote the Arg union to a core data type in Object.c such
that it is visible everywhere (many source files need it),
and correct declarations of several functions that use it.

The Process_sendSignal function is also corrected to have
the expected return type (bool, not void) - an error being
masked by ignoring this not-quite-harmless warning.  I've
also added error checking to the kill(2) call here, which
was previously overlooked / missing (?).
2020-08-20 09:35:33 +10:00
5228f5d47a Ensure result buffer termination in String_cat utility routine 2020-08-20 09:03:45 +10:00
74d547674d Merge branch 'hishamhm-pull-917' 2020-08-19 18:15:18 +10:00
034e56b3c0 Merge branch 'hishamhm-pull-891' 2020-08-19 18:13:48 +10:00
6310e5dc3a Merge branch 'hishamhm-pull-886' 2020-08-19 18:12:30 +10:00
36ef4d4fb6 Resolve compilation warning relating to dangling else in InfoScreen_run 2020-08-19 18:10:16 +10:00
80ce69c44f Merge branch 'hishamhm-pull-883' 2020-08-19 18:09:19 +10:00
6fbee8c1f4 Merge branch 'hishamhm-pull-881' 2020-08-19 18:06:57 +10:00
00d333cc7b Merge branch 'hishamhm-pull-869' 2020-08-19 18:04:28 +10:00
eef6bc447d Correction to smaps buffer size passed to smaps path snprintf 2020-08-19 17:50:43 +10:00
f9625cacf0 Merge branch 'hishamhm-pull-843' 2020-08-19 17:47:38 +10:00
e9947acfc6 Merge branch 'hishamhm-pull-818' 2020-08-19 16:49:43 +10:00
5a17cee74d Merge branch 'hishamhm-pull-817' 2020-08-19 16:43:55 +10:00
ed6f22a847 Merge branch 'master' of github.com:htop-dev/htop 2020-08-18 20:24:11 +10:00
2f59798b3b Merge pull request #2 from leetonidas/master
fixed display of blank bars
2020-08-18 20:22:24 +10:00
9a70e43129 fixed display of blank bars
The buffer for blank bars was left uninitialized resulting in random
looking characters sometimes even overwriting the end of the bar.
2020-08-18 11:48:08 +02:00
7ac1c709b7 Re-generate all headers with latest scripts/MakeHeader.py
Sync-up missing extern declarations for many functions.
2020-08-18 17:41:49 +10:00
579995c7c2 Merge branch 'hishamhm-pull-842' 2020-08-18 17:35:56 +10:00
98e8da8bd3 Merge pull request #1 from htop-dev/natoscott-continuous-integration
Create ci.yml with a workflow for Ubuntu latest
2020-08-17 16:02:20 +10:00
7fbbf25afe Update ci.yml
Comment out MacOSX for now - seems to be missing needed aclocal/m4 toolchain components.
2020-08-17 15:53:49 +10:00
e1f48c7c7c Update ci.yml
Correction to deb package name
2020-08-17 15:48:24 +10:00
6cd1615863 Update ci.yml
Attempt to fix Ubuntu dependencies, add macosx
2020-08-17 15:40:47 +10:00
c86ac5cf98 Update ci.yml
Install libncurses-dev package on the build system
2020-08-17 15:16:49 +10:00
b3003f02fa Create ci.yml
Initial version of htop CI using github actions.
2020-08-17 14:59:23 +10:00
dfd9279f87 Resolve complation issues with -fno-common (default from gcc-10)
Extends the MakeHeader script to auto-generate correct "extern"
function declarations in some cases that it currently does not.

Related to https://github.com/hishamhm/htop/pull/981
2020-07-10 10:35:32 +10:00
b55f9320bc fixed x/y coordinate mixup 2020-06-12 23:53:05 +02:00
7fdd8d3732 adding support for more than 2 smaller cpumeter columns 2020-06-11 23:21:52 +02:00
f74cde982f removed whitespace from end of lines 2020-06-11 23:00:16 +02:00
832e77c754 Let the user know about their error
If the user informed wrong value, then let them know about that.
2020-06-11 13:44:16 -03:00
8de04986cf Fix misleading indentation 2020-06-11 13:42:41 -03:00
1b4ed916b1 Fix use of '-rdynamic'
The option should be informed to the linker.
2020-06-11 13:30:30 -03:00
05c974f6cc format color 2020-04-27 12:32:58 +08:00
6b5b8bab2c fix "Broken Gray" didn't change the color of cpu-iowait 2020-04-27 12:31:17 +08:00
87c05ac136 Fix STARTTIME column on FreeBSD. 2019-12-31 19:28:23 +00:00
ccf0e18414 update man page text about delay 2019-12-16 15:45:55 -06:00
88c9ebb8f7 Properly identify zombie processes
This closes issue #930.
2019-12-14 11:47:03 +01:00
37cc11253e Added missing option 2019-11-11 00:54:51 +01:00
63fbc3b517 Add trim_trailing_whitespace to editorconfig 2019-10-31 11:39:57 -05:00
dd33444f7e Clean up existing whitespace 2019-10-31 11:39:12 -05:00
9ed47a213b Fix whitespace 2019-10-31 11:29:28 -05:00
12805f61d8 Add simple vim mode
This commit adds a "vim_mode" setting (false/`0` by default) that causes
keys to be remapped in the following way by the `ScreenManager`:

+ h -> LEFT
+ j -> DOWN
+ k -> UP
+ l -> RIGHT
+ LEFT -> h (toggle help)
+ DOWN -> j (noop)
+ UP -> k (open kill menu)
+ RIGHT -> l (lsof current process)
+ K (Shift+K) -> k (open kill menu)
+ J (Shift+J) -> K (toggle show/hide kernel threads)
+ L (Shift+L) -> l (lsof current process)

I couldn't figure out where the manpage documentation is in the repo,
though I admittedly did not look particularly hard.

I believe this change would be a welcome option for heavy vim users like myself
who would like a familiar way to get around in htop.
2019-10-31 11:20:55 -05:00
28840683cf Merge remote-tracking branch 'upstream/master' 2019-10-31 10:31:38 -05:00
18a60d668d MainPanel: add seventh char to main functions
reason: currently, for example 'search' and 'filter' look very densely packed
2019-09-12 22:45:34 +02:00
1886117c72 Linux: fixes sysfs battery discovery 2019-09-07 15:21:04 +01:00
a267003f2f Linux fixes 2019-09-03 19:56:38 +00:00
613556faeb Support for ZFS Compressed ARC statistics 2019-09-03 18:44:19 +00:00
e450b58636 Refactor openzfs_sysctl_init() and ZfsArcMeter...
openzfs_sysctl_init() now returns void instead of int.
The ZfsArcStats->enabled flag is set inside the init function
now, instead of having to be set from its return value.
Preparation for more flag setting in Compressed ARC commit.

ZfsArcMeter_readStats() added and all Meter->values[] setting
moved to it, eliminating duplicated code in
{darwin,freebsd,linux,solaris}/Platform.c.
2019-09-03 18:21:33 +00:00
81b64691a7 Move sysfs-reading code to LinuxProcessList.c and add average frequency.
This way the frequency is read from sysfs only once per update cycle
instead of every time the UI is redrawn.

This also changes the code to read from /proc/cpuinfo instead. This is because
reading from scaling_cur_freq stalls for 10ms if the previous read for the file
was more than one second ago. [1] Since htop's update cycle is
longer than that, it would cause the read of each CPU's scaling_cur_freq file
to block the UI for 20ms. This easily led to a noticeable half-second lag on
a 20+ CPU machine.

/proc/cpuinfo also has a 10ms delay, but this applies for the whole file
so the delay does not scale with the number of CPUs. [2]

[1]: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=4815d3c56d1e10449a44089a47544d9ba84fad0d
[2]: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=7d5905dc14a87805a59f3c5bf70173aac2bb18f8
2019-08-10 22:19:32 -07:00
909bb86f05 Show N/A on unsupported platforms instead of 0KHz 2019-08-10 17:17:45 -07:00
43728b37e7 Fix typo. 2019-08-10 11:46:21 -07:00
1acfb0a752 Show N/A instead of 0KHz when CPU frequency is not available. 2019-08-10 11:37:35 -07:00
1d5e6a27a0 Add a display option to hide CPU usage number from CPU meter. 2019-08-10 11:20:21 -07:00
9703a25d1b Divide by 1000, not 1024, and show more decimals. 2019-08-09 23:22:05 -07:00
4b0600d8f8 Add new display option to also show CPU frequency in CPU meters.
The option is only implemented on Linux. On other platforms, and on Linuxes
that do not expose the relevant sysfs file, the frequency will be 0.

The "CPU average" meter does not show a frequency, only
the individual per-CPU meters.
2019-08-09 21:34:48 -07:00
7858ee6cee Added option to enable/disable the mouse. 2019-07-12 22:01:29 +02:00
b0e24cd5a5 Added an option to disable the mouse. 2019-07-12 21:41:09 +02:00
ff6914e4ad ZFS arcstats for Solaris 2019-07-07 23:10:54 -04:00
a88d2e313d Refactor common OpenZFS sysctl access
Darwin and FreeBSD export zfs kstats through the
same APIs, so moving functions into a common file.
2019-07-07 23:10:54 -04:00
fc8e9a2d3e ZFS arcstats for Darwin (macOS / OS X) 2019-07-07 23:10:46 -04:00
070fe90461 ZFS arcstats for Linux
If no pools are imported (ARC size == 0) or the
ZFS module is not in the kernel (/proc/spl/kstat/zfs/arcstats
does not exist), then the Meter reports "Unavailable".
2019-07-07 22:57:15 -04:00
a93edde1a2 Support ZFS ARC stats on FreeBSD
New meter displays same ARC stats as FreeBSD top(1).
Can be extended to other platforms that support ZFS.

Pulling kstat.zfs.misc.arcstats.c_max as the meter
total, so the meter has a meaningful value to work
up to.

The Text meter displays, first, the maximum
ARC size (Meter.total), then second, the total
ARC used, using the difference between Meter.maxItems
and Meter.curItems to "hide" the used value from the
Bar and Graph drawing functions by using an index
in Meter.values[] that is beyond curItems - 1, but
less than maxItems - 1.
2019-07-07 22:52:04 -04:00
92258e99e6 Specify correct MIB length
Could have resulted in a buffer overflow if the
FreeBSD kernel returned more bytes than expected.
2019-07-06 04:27:00 +00:00
886eb68102 Close pipe after lsof 2019-06-23 13:13:05 -04:00
3512971084 Fix configure 'major' workaround causing <sys/sysmacros.h> to miss.
A logic mistake in pull request #746 causes <sys/sysmacro.h> to be
*not* included when AC_HEADER_MAJOR (before autoconf-2.70) finds
'major' in <sys/types.h>. Though this would still build htop, it would
still bring deprecation warning in systems using glibc 2.25-2.27. Fix
the logic and suppress the warning.

Also, include config.h in Process.c for the sake of strengthening the
code.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2019-06-14 10:10:23 +08:00
01a1cc063f fixed Linux build 2019-05-30 16:34:30 +02:00
423414937b does not work within NGZ 2019-05-26 21:20:35 +02:00
131a6c6e2b CPU_KERNEL redefined 2019-05-26 21:20:00 +02:00
c3fadf6b69 Add timestamps to the strace screen 2019-04-29 15:17:05 +02:00
fabe75685a Truncate overwide jail names on BSD. 2019-04-17 10:07:13 +01:00
078c2ddde5 Linux: Use /proc/*/smaps_rollup for improved PSS parsing speed 2019-03-20 17:00:49 +01:00
fc0bf546c3 Linux: Add PSS (proportional set size), Swap and SwapPSS calculation
Original code was written by *Craig M. Brandenburg* for htop 1.0.2
Many performance improvements by GitHub user *linvinus*, ported to htop 2.0.2
2019-03-20 17:00:41 +01:00
43875d94c9 Add danish translation to htop.desktop 2019-02-19 01:25:52 +01:00
08feb8585b Add pressure stall information (PSI) meters on Linux
The pressure stall information (PSI) metrics provide useful information
on delays caused by waiting for CPU, IO and memory. Particularly on busy
servers it can provide a quick overview of what's "slowing things down".

This feature is supported on Linux >= 4.20.
The interface is documented here:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/Documentation/accounting/psi.txt
These links provide rationale:
https://lwn.net/Articles/759781/
https://facebookmicrosites.github.io/psi/

The following metrics are added, corresponding to the currently exposed
lines (see `head /proc/pressure/*`):
- PressureStallCPUSome
- PressureStallIOSome
- PressureStallIOFull
- PressureStallMemorySome
- PressureStallMemoryFull

The color scheme is the same as that used for Load Average, however I
gave it separate entries just in case someone wants to change them
specifically.

Tested on 4.20.7-arch1-1-ARCH, on the linux platform.
Also tested that other platforms still compile (changed configure to use
the unsupported platform).

Closes #879.
2019-02-13 22:38:10 +02:00
da60309fc9 Don't follow process when selecting non-process-specific options
Disable the follow process logic in Action_pickFromVector(), when
selecting sort order or user filter, since they don't apply on specific
process.

Fix #856
2019-02-12 22:15:51 -05:00
6f161e60e1 Restore meter name after used in Header_addMeterByName()
Fix #852
2019-02-12 22:13:58 -05:00
402e46bb82 Linting changes
as reported by lgtm.com
2019-02-10 14:16:46 +01:00
90518bfc84 Return of snprintf is not the number of written bytes 2019-02-10 14:16:46 +01:00
f49f545813 Fix printf() unsigned placeholders
Unsigned numbers should be using "%u".

Raised by cppcheck
2019-02-10 14:16:46 +01:00
60e557868a Prevent possible NULL pointer deference
Raised by cppcheck
2019-02-10 14:16:46 +01:00
79c939eb84 Remove duplicated if condition
The for loop already handles i being < nPanels

Raised by cppcheck
2019-02-10 14:16:46 +01:00
dc7d035238 htop.c: remove unused "--io" / "-i" (#811)
Introduced from 3383d8e556 (2.0.0) but never used.
2019-02-10 14:16:46 +01:00
6194c8dcc6 Honour setting of counting CPUs from 0/1 when presenting meters
In the listing of Available Meters for CPUs, the list of CPUs is always
presented by counting them from one.  However, if the user prefers to
count CPUs from zero, this is sometimes confusing when fine-tuning the
meters.
2019-02-07 10:39:08 -02:00
7c62db73fc User option "-u" now defaults to $USER 2019-01-31 00:28:09 +01:00
a360a80d16 fix a bug about use of unitialised variable. refer to https://github.com/hishamhm/htop/issues/882 2019-01-29 12:45:30 +08:00
9139d29bbb chore: add clarification to the docs 2019-01-26 09:56:19 -05:00
536941fb23 Deal with larger numbers in colorNumber and outputRate 2018-12-30 20:18:35 +08:00
ecfd6f685e Fix memory statistics display on FreeBSD/powerpc
Use the appropriate types when calling sysctl().

Currently, `unsigned long long int` is used for all sizes and on
FreeBSD/powerpc this causes all sysctl() calls in scanMemoryInfo()
to fail as they are actually of different sizes on powerpc, where
(sizeof(unsigned long long int), sizeof(u_long)) == (8, 4)
vs (8, 8) on amd64.  This results in bogus memory sizes being
reported by htop.

Signed-off-by: Tobias Kortkamp <tobik@FreeBSD.org>
2018-12-24 13:51:01 +01:00
f15d55c972 Fix numbers larger than 100 terabytes 2018-12-18 21:05:09 +08:00
27fe307d22 Remove a few unnecessary #includes 2018-12-16 11:34:15 +01:00
9197adf57e Fix CPU usage on OpenBSD
The current OpenBSD-specific CPU usage code is broken. The `cpu`
parameter of `Platform_setCPUValues` is an integer in the interval
[0, cpuCount], not [0, cpuCount-1]: Actual CPUs are numbered from
1, the “zero” CPU is a “virtual” one which represents the average
of actual CPUs (I guess it’s inherited from Linux’s `/proc/stats`).
This off-by-one error leads to random crashes.

Moreover, the displayed CPU usage is more detailed with system,
user and nice times.

I made the OpenBSD CPU code more similar to the Linux CPU code,
removing a few old bits from OpenBSD’s top(1). I think it will be
easier to understand, maintain and evolve.

I’d love some feedback from experienced OpenBSD people.
2018-12-16 11:30:06 +01:00
41754e5632 Remove unnecessary HAVE_SYS_SYSMACROS_H check
HAVE_SYS_SYSMACROS_H is always true if MAJOR_IN_SYSMACROS.

This way of checking is recommended in autoconf 2.70 documentation:
https://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=blobdiff;f=doc/autoconf.texi;h=4f041bd4e;hp=9ad7dc1c5f02c8ba25b2fe1218bf931c7113a5d5;hb=e17a30e987d7ee695fb4294a82d987ec3dc9b974;hpb=565a6dc50cfa01cec2fb4db894026689cdf4970c

NOTE: currently
      https://www.gnu.org/software/autoconf/manual/autoconf.html is the
      doc for autoconf 2.69.
2018-12-15 22:10:06 +09:00
8d7afb33e2 added MainPanel actions n and N for find next and find prev. closes #601 2018-11-03 15:59:55 -04:00
b7b4200f85 Fix printf() unsigned placeholders
Unsigned numbers should be using "%u".

Raised by cppcheck
2018-10-30 16:55:55 -03:00
4cb58460e5 Prevent possible NULL pointer deference
Raised by cppcheck
2018-10-30 16:55:55 -03:00
c39f710b52 Remove duplicated if condition
The for loop already handles i being < nPanels

Raised by cppcheck
2018-10-30 16:55:55 -03:00
c34be41e1c Widen ST_UID (UID) column to 5 chars to allow UIDs > 9999 without breaking alignment
Issue Github #841, Debian bug #910492
2018-10-07 11:16:12 +02:00
67e368914a htop.c: remove unused "--io" / "-i" (#811)
Introduced from 3383d8e556 (2.0.0) but never used.
2018-08-25 10:15:59 -03:00
bae27054e6 Linux: fix CPU count 2018-08-24 18:38:06 -03:00
8d01ae2054 Linux: remove warnings of unused variables 2018-08-24 18:38:06 -03:00
049af17be0 Improve Catalan translation for desktop file (#828) 2018-08-24 17:10:09 -03:00
009837b56d Improve htop.desktop file (#609)
- sort entries according to the spec
- add to the `Monitor` category
2018-08-19 01:38:05 -03:00
bd1d719a61 Linux: add process->starttime and use it for STARTTIME column (#700)
this way a remount of /proc will not reset starttimes
and we can also see startup times for processes started before the mount
of /proc

also record btime (boot time in seconds since epoch) as Linux semi-global
2018-08-19 01:29:03 -03:00
ca1cce4ce7 OpenBSD: make the STARTTIME column display correctly (#815) 2018-08-19 01:09:08 -03:00
c1fb585b6b OpenBSD: add environment reading support (#819) 2018-08-19 01:07:36 -03:00
d74b6dc8e0 Fix process name updates for shorter strings (#812)
When a process name changes from a long string to a short string,
truncate instead of just overwriting the beginning.
2018-07-28 00:08:40 -03:00
060aa2b20f remove wrongfix 2018-07-26 04:17:06 -05:00
3d79c72e9a Update OpenBSD maximum PID
The source code correctly states that the maximum PID number in
the OpenBSD kernel is fixed in sys/sys/proc.h, however this was
updated in revision 1.215 (two years ago!) from 32766 to 99999.
2018-07-17 18:46:55 +01:00
c005ffc3d7 Fix zero-index array bounds issue 2018-07-17 08:50:22 -05:00
48b807b0ff Fix CPU meters
Introduction of CP_SPIN sched state broke hard-coded state indexes
resulting in the meters incorrectly reporting bogus intr data instead of
CPU usage. Change hardcoded values to sched.h macros.
2018-07-17 08:43:50 -05:00
666e1e76b3 Fix virtualization color in help screen
Closes #785.
2018-06-13 00:47:08 -03:00
5f9e16b9da Unstage/gitignore INSTALL since it's regenerated by autogen.sh
As noted by @marcelpaulo. I've had ugly diffs that fiddled with
this file in the past, so it's good to see it gone.

Closes #800.
2018-06-13 00:36:05 -03:00
fd15ead51b Mention the "c" key (tag subtree) in the man
This was reported by @agguser.
2018-06-02 18:22:56 -03:00
103f1a497a Disable 'make dist' when pkg.m4 is unused in configure
This would prevent a careless future package maintainer from creating a
release tarball with a defective configure script. :)

Also, add a warning in the autogen.sh phase if pkg.m4 is unused.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-05-22 23:21:42 -03:00
08a9b051c5 fix option string
This broke with commit db05ba6106.
2018-05-22 23:19:19 -03:00
e1e3791363 Fix #define to match header when regenerating with MakeHeader.py (#789)
from Debian https://sources.debian.org/src/htop/2.2.0-1/debian/patches/fix-linux-process.patch/
2018-05-22 23:09:20 -03:00
724aae60d1 Add tree view flag to man page 2018-04-06 21:52:50 -05:00
149 changed files with 3177 additions and 1459 deletions

View File

@ -14,3 +14,4 @@ charset = utf-8
[*.{c,h}] [*.{c,h}]
indent_style = space indent_style = space
indent_size = 3 indent_size = 3
trim_trailing_whitespace = true

63
.github/workflows/ci.yml vendored Normal file
View File

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

2
.gitignore vendored
View File

@ -20,6 +20,7 @@ htop
.deps/ .deps/
Makefile Makefile
Makefile.in Makefile.in
INSTALL
aclocal.m4 aclocal.m4
autom4te.cache/ autom4te.cache/
compile compile
@ -38,4 +39,5 @@ libtool
ltmain.sh ltmain.sh
m4/ m4/
missing missing
scripts/MakeHeader.py
stamp-h1 stamp-h1

View File

@ -62,11 +62,11 @@ typedef struct State_ {
}*/ }*/
Object* Action_pickFromVector(State* st, Panel* list, int x) { Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) {
Panel* panel = st->panel; Panel* panel = st->panel;
Header* header = st->header; Header* header = st->header;
Settings* settings = st->settings; Settings* settings = st->settings;
int y = panel->y; int y = panel->y;
ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, false); ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, false);
scr->allowFocusChange = false; scr->allowFocusChange = false;
@ -75,8 +75,8 @@ Object* Action_pickFromVector(State* st, Panel* list, int x) {
Panel* panelFocus; Panel* panelFocus;
int ch; int ch;
bool unfollow = false; bool unfollow = false;
int pid = MainPanel_selectedPid((MainPanel*)panel); int pid = followProcess ? MainPanel_selectedPid((MainPanel*)panel) : -1;
if (header->pl->following == -1) { if (followProcess && header->pl->following == -1) {
header->pl->following = pid; header->pl->following = pid;
unfollow = true; unfollow = true;
} }
@ -88,11 +88,16 @@ Object* Action_pickFromVector(State* st, Panel* list, int x) {
Panel_move(panel, 0, y); Panel_move(panel, 0, y);
Panel_resize(panel, COLS, LINES-y-1); Panel_resize(panel, COLS, LINES-y-1);
if (panelFocus == list && ch == 13) { if (panelFocus == list && ch == 13) {
Process* selected = (Process*)Panel_getSelected(panel); if (followProcess) {
if (selected && selected->pid == pid) Process* selected = (Process*)Panel_getSelected(panel);
if (selected && selected->pid == pid)
return Panel_getSelected(list);
else
beep();
} else {
return Panel_getSelected(list); return Panel_getSelected(list);
else }
beep();
} }
return NULL; return NULL;
} }
@ -189,7 +194,7 @@ static Htop_Reaction sortBy(State* st) {
Panel_setSelected(sortPanel, i); Panel_setSelected(sortPanel, i);
free(name); free(name);
} }
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15); ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false);
if (field) { if (field) {
reaction |= Action_setSortKey(st->settings, field->key); reaction |= Action_setSortKey(st->settings, field->key);
} }
@ -248,10 +253,21 @@ static Htop_Reaction actionIncFilter(State* st) {
} }
static Htop_Reaction actionIncSearch(State* st) { static Htop_Reaction actionIncSearch(State* st) {
IncSet_reset(((MainPanel*)st->panel)->inc, INC_SEARCH);
IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel); IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
} }
static Htop_Reaction actionIncNext(State* st) {
IncSet_next(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionIncPrev(State* st) {
IncSet_prev(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionHigherPriority(State* st) { static Htop_Reaction actionHigherPriority(State* st) {
bool changed = changePriority((MainPanel*)st->panel, -1); bool changed = changePriority((MainPanel*)st->panel, -1);
return changed ? HTOP_REFRESH : HTOP_OK; return changed ? HTOP_REFRESH : HTOP_OK;
@ -297,20 +313,22 @@ static Htop_Reaction actionSetAffinity(State* st) {
return HTOP_OK; return HTOP_OK;
#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY) #if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
Panel* panel = st->panel; Panel* panel = st->panel;
Process* p = (Process*) Panel_getSelected(panel); Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK; if (!p) return HTOP_OK;
Affinity* affinity = Affinity_get(p, st->pl); Affinity* affinity1 = Affinity_get(p, st->pl);
if (!affinity) return HTOP_OK; if (!affinity1) return HTOP_OK;
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity); int width;
Affinity_delete(affinity); Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width);
width += 1; /* we add a gap between the panels */
Affinity_delete(affinity1);
void* set = Action_pickFromVector(st, affinityPanel, 15); void* set = Action_pickFromVector(st, affinityPanel, width, true);
if (set) { if (set) {
Affinity* affinity = AffinityPanel_getAffinity(affinityPanel, st->pl); Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity }, NULL); bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity2 }, NULL);
if (!ok) beep(); if (!ok) beep();
Affinity_delete(affinity); Affinity_delete(affinity2);
} }
Panel_delete((Object*)affinityPanel); Panel_delete((Object*)affinityPanel);
#endif #endif
@ -319,7 +337,7 @@ static Htop_Reaction actionSetAffinity(State* st) {
static Htop_Reaction actionKill(State* st) { static Htop_Reaction actionKill(State* st) {
Panel* signalsPanel = (Panel*) SignalsPanel_new(); Panel* signalsPanel = (Panel*) SignalsPanel_new();
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15); ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true);
if (sgn) { if (sgn) {
if (sgn->key != 0) { if (sgn->key != 0) {
Panel_setHeader(st->panel, "Sending..."); Panel_setHeader(st->panel, "Sending...");
@ -340,7 +358,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
Vector_insertionSort(usersPanel->items); Vector_insertionSort(usersPanel->items);
ListItem* allUsers = ListItem_new("All users", -1); ListItem* allUsers = ListItem_new("All users", -1);
Panel_insert(usersPanel, 0, (Object*) allUsers); Panel_insert(usersPanel, 0, (Object*) allUsers);
ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20); ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false);
if (picked) { if (picked) {
if (picked == allUsers) { if (picked == allUsers) {
st->pl->userId = -1; st->pl->userId = -1;
@ -463,7 +481,7 @@ static Htop_Reaction actionHelp(State* st) {
if (settings->detailedCPUTime) { if (settings->detailedCPUTime) {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/"); addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/"); addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/"); addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/");
addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/"); addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/"); addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
@ -473,8 +491,8 @@ static Htop_Reaction actionHelp(State* st) {
} else { } else {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/"); addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/"); addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "virtualiz"); addattrstr(CRT_colors[CPU_GUEST], "virtualiz");
addattrstr(CRT_colors[BAR_SHADOW], " used%"); addattrstr(CRT_colors[BAR_SHADOW], " used%");
} }
addattrstr(CRT_colors[BAR_BORDER], "]"); addattrstr(CRT_colors[BAR_BORDER], "]");
@ -559,6 +577,8 @@ void Action_setBindings(Htop_Action* keys) {
keys['\\'] = actionIncFilter; keys['\\'] = actionIncFilter;
keys[KEY_F(3)] = actionIncSearch; keys[KEY_F(3)] = actionIncSearch;
keys['/'] = actionIncSearch; keys['/'] = actionIncSearch;
keys['n'] = actionIncNext;
keys['N'] = actionIncPrev;
keys[']'] = actionHigherPriority; keys[']'] = actionHigherPriority;
keys[KEY_F(7)] = actionHigherPriority; keys[KEY_F(7)] = actionHigherPriority;
@ -597,4 +617,3 @@ void Action_setBindings(Htop_Action* keys) {
keys['c'] = actionTagAllChildren; keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen; keys['e'] = actionShowEnvScreen;
} }

View File

@ -39,20 +39,19 @@ typedef struct State_ {
} State; } State;
Object* Action_pickFromVector(State* st, Panel* list, int x); extern Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess);
// ---------------------------------------- // ----------------------------------------
bool Action_setUserOnly(const char* userName, uid_t* userId); extern bool Action_setUserOnly(const char* userName, uid_t* userId);
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey); extern Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
// ---------------------------------------- // ----------------------------------------
Htop_Reaction Action_follow(State* st); extern Htop_Reaction Action_follow(State* st);
void Action_setBindings(Htop_Action* keys); extern void Action_setBindings(Htop_Action* keys);
#endif #endif

View File

@ -1,6 +1,7 @@
/* /*
htop - Affinity.c htop - Affinity.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -79,7 +80,8 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
return affinity; return affinity;
} }
bool Affinity_set(Process* proc, Affinity* this) { bool Affinity_set(Process* proc, Arg arg) {
Affinity *this = arg.v;
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
for (int i = 0; i < this->used; i++) { for (int i = 0; i < this->used; i++) {
hwloc_bitmap_set(cpuset, this->cpus[i]); hwloc_bitmap_set(cpuset, this->cpus[i]);
@ -103,7 +105,8 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
return affinity; return affinity;
} }
bool Affinity_set(Process* proc, Affinity* this) { bool Affinity_set(Process* proc, Arg arg) {
Affinity *this = arg.v;
cpu_set_t cpuset; cpu_set_t cpuset;
CPU_ZERO(&cpuset); CPU_ZERO(&cpuset);
for (int i = 0; i < this->used; i++) { for (int i = 0; i < this->used; i++) {

View File

@ -5,6 +5,7 @@
/* /*
htop - Affinity.h htop - Affinity.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -29,23 +30,23 @@ typedef struct Affinity_ {
} Affinity; } Affinity;
Affinity* Affinity_new(ProcessList* pl); extern Affinity* Affinity_new(ProcessList* pl);
void Affinity_delete(Affinity* this); extern void Affinity_delete(Affinity* this);
void Affinity_add(Affinity* this, int id); extern void Affinity_add(Affinity* this, int id);
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
Affinity* Affinity_get(Process* proc, ProcessList* pl); extern Affinity* Affinity_get(Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Affinity* this); extern bool Affinity_set(Process* proc, Arg arg);
#elif HAVE_LINUX_AFFINITY #elif HAVE_LINUX_AFFINITY
Affinity* Affinity_get(Process* proc, ProcessList* pl); extern Affinity* Affinity_get(Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Affinity* this); extern bool Affinity_set(Process* proc, Arg arg);
#endif #endif

View File

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

View File

@ -9,15 +9,32 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#ifdef HAVE_LIBHWLOC
#endif
#include "Panel.h" #include "Panel.h"
#include "Affinity.h" #include "Affinity.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "ListItem.h"
#ifdef HAVE_LIBHWLOC
#endif
#ifdef HAVE_LIBHWLOC
#endif
#ifdef HAVE_LIBHWLOC
#endif
extern PanelClass AffinityPanel_class; extern PanelClass AffinityPanel_class;
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity); extern Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width);
Affinity* AffinityPanel_getAffinity(Panel* this, ProcessList* pl); extern Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl);
#endif #endif

View File

@ -19,6 +19,6 @@ typedef struct AvailableColumnsPanel_ {
extern PanelClass AvailableColumnsPanel_class; extern PanelClass AvailableColumnsPanel_class;
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns); extern AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns);
#endif #endif

View File

@ -52,7 +52,7 @@ static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, M
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
AvailableMetersPanel* this = (AvailableMetersPanel*) super; AvailableMetersPanel* this = (AvailableMetersPanel*) super;
Header* header = this->header; Header* header = this->header;
ListItem* selected = (ListItem*) Panel_getSelected(super); ListItem* selected = (ListItem*) Panel_getSelected(super);
int param = selected->key & 0xff; int param = selected->key & 0xff;
int type = selected->key >> 16; int type = selected->key >> 16;
@ -104,7 +104,7 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done "); FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done ");
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings; this->settings = settings;
this->header = header; this->header = header;
this->leftPanel = leftMeters; this->leftPanel = leftMeters;
@ -127,7 +127,7 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
Panel_add(super, (Object*) ListItem_new("CPU average", 0)); Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (int i = 1; i <= cpus; i++) { for (int i = 1; i <= cpus; i++) {
char buffer[50]; char buffer[50];
xSnprintf(buffer, 50, "%s %d", type->uiName, i); xSnprintf(buffer, 50, "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1));
Panel_add(super, (Object*) ListItem_new(buffer, i)); Panel_add(super, (Object*) ListItem_new(buffer, i));
} }
} else { } else {

View File

@ -27,6 +27,6 @@ typedef struct AvailableMetersPanel_ {
extern PanelClass AvailableMetersPanel_class; extern PanelClass AvailableMetersPanel_class;
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl); extern AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl);
#endif #endif

View File

@ -35,7 +35,7 @@ int BatteryMeter_attributes[] = {
static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) { static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) {
ACPresence isOnAC; ACPresence isOnAC;
double percent; double percent;
Battery_getData(&percent, &isOnAC); Battery_getData(&percent, &isOnAC);
if (percent == -1) { if (percent == -1) {

View File

@ -1,56 +1,34 @@
Contributing Guide Contributing Guide
================== ==================
Hello, and thank you so much for taking your time to contribute in any way to Thank you so much for taking the time to contribute in to htop!
htop! There are many ways to contribute, and I'll try to list them below. The
support from the free software community has been amazing over the years and
it is the number one thing that keeps me going, maintaining and improving
something that started as a tiny pet project back in 2004 and that nowadays is
a piece of software used all over the world, in both reality [and
fiction!](http://hisham.hm/htop/index.php?page=sightings). Cheers!
-- Hisham Muhammad
Bug Reports Bug Reports
----------- -----------
Bug reports should be posted in the [Github issue Bug reports should be posted in the [Github issue
tracker](http://github.com/hishamhm/htop/issues). (I reply to them all, but I tracker](https://github.com/htop-dev/htop/issues).
usually do it in batches! :) ) Bug reports are extremely important since it's Bug reports are extremely important since it's impossible for us to test
impossible for me to test htop in every possible system, distribution and htop in every possible system, distribution and scenario. Your feedback
scenario. Your feedback is what keeps the tool stable and always improving! is what keeps the tool stable and always improving! Thank you!
Thank you!
Pull Requests Pull Requests
------------- -------------
Code contributions are most welcome! Just [fork the Code contributions are most welcome! Just [fork the
repo](http://github.com/hishamhm/htop) and send a [pull repo](https://github.com/htop-dev/htop) and send a [pull
request](https://github.com/hishamhm/htop/pulls). Help is especially request](https://github.com/htop-dev/htop/pulls). Help is especially
appreciated for support of platforms other than Linux. If proposing new appreciated for support of platforms other than Linux. If proposing new
features, please be mindful that htop is a system tool that needs to keep a features, please be mindful that htop is a system tool that needs to keep a
small footprint and perform well on systems under stress -- so unfortunately I small footprint and perform well on systems under stress -- so unfortunately
can't accept every new feature proposed, as I need to keep the tool slim and we can't accept every new feature proposed, as we need to keep the tool slim
maintainable. Great ideas backed by a PR are always carefully considered for and maintainable. Great ideas backed by a PR are always carefully considered
inclusion, though! Also, PRs containing bug fixes and portability tweaks are a for inclusion though! Also, PRs containing bug fixes and portability tweaks
no-brainer, please send those in! are always included, please send those in!
Feature Requests Feature Requests
---------------- ----------------
Back when htop was hosted in SourceForge, there used to be separate Bug Please label Github issues that are feature requests with the [`feature
Tracker and Feature Request pages. These go all lumped together under "Issues" request`](https://github.com/htop-dev/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+)
in Github, which is a bit confusing. For this reason, I close Feature Requests label.
and file them with the [`feature
request`](https://github.com/hishamhm/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+)
label, where they remain accessible, but not mixed with actual bug reports.
This doesn't mean I'm dismissing or ignoring feature requests right away! It's
just an organizational issue (with Github, really!).
Donations
---------
If you like htop, feel free to [buy the author a
beer](http://hisham.hm/htop/index.php?page=donate). :-)

View File

@ -353,4 +353,3 @@ Public License instead of this License.
applicable licenses of the version of PLPA used in your combined work, applicable licenses of the version of PLPA used in your combined work,
provided that you include the source code of such version of PLPA when provided that you include the source code of such version of PLPA when
and as the GNU GPL requires distribution of source code. and as the GNU GPL requires distribution of source code.

View File

@ -28,13 +28,14 @@ typedef enum {
CPU_METER_STEAL = 5, CPU_METER_STEAL = 5,
CPU_METER_GUEST = 6, CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7, CPU_METER_IOWAIT = 7,
CPU_METER_ITEMCOUNT = 8, // number of entries in this enum CPU_METER_FREQUENCY = 8,
CPU_METER_ITEMCOUNT = 9, // number of entries in this enum
} CPUMeterValues; } CPUMeterValues;
}*/ }*/
int CPUMeter_attributes[] = { int CPUMeter_attributes[] = {
CPU_NICE, CPU_NORMAL, CPU_KERNEL, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, CPU_IOWAIT CPU_NICE, CPU_NORMAL, CPU_SYSTEM, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, CPU_IOWAIT
}; };
#ifndef MIN #ifndef MIN
@ -63,7 +64,30 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) {
} }
memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT); memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT);
double percent = Platform_setCPUValues(this, cpu); double percent = Platform_setCPUValues(this, cpu);
xSnprintf(buffer, size, "%5.1f%%", percent); if (this->pl->settings->showCPUFrequency) {
/* Initial frequency is in MHz. Emit it as GHz if it's larger than 1000MHz */
double cpuFrequency = this->values[CPU_METER_FREQUENCY];
char unit = 'M';
char cpuFrequencyBuffer[16];
if (cpuFrequency < 0) {
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A");
} else {
if (cpuFrequency > 1000) {
cpuFrequency /= 1000;
unit = 'G';
}
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%.3f%cHz", cpuFrequency, unit);
}
if (this->pl->settings->showCPUUsage) {
xSnprintf(buffer, size, "%5.1f%% %s", percent, cpuFrequencyBuffer);
} else {
xSnprintf(buffer, size, "%s", cpuFrequencyBuffer);
}
} else if (this->pl->settings->showCPUUsage) {
xSnprintf(buffer, size, "%5.1f%%", percent);
} else if (size > 0) {
buffer[0] = '\0';
}
} }
static void CPUMeter_display(Object* cast, RichString* out) { static void CPUMeter_display(Object* cast, RichString* out) {
@ -80,7 +104,7 @@ static void CPUMeter_display(Object* cast, RichString* out) {
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_append(out, CRT_colors[METER_TEXT], "sy:");
RichString_append(out, CRT_colors[CPU_KERNEL], buffer); RichString_append(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_append(out, CRT_colors[METER_TEXT], "ni:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
@ -106,7 +130,7 @@ static void CPUMeter_display(Object* cast, RichString* out) {
} 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_append(out, CRT_colors[METER_TEXT], "sys:");
RichString_append(out, CRT_colors[CPU_KERNEL], buffer); RichString_append(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_append(out, CRT_colors[METER_TEXT], "low:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
@ -137,6 +161,15 @@ static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) {
} }
} }
static int MapClassnameToColumncount(Meter* this){
if (strchr(Meter_name(this), '4'))
return 4;
else if (strchr(Meter_name(this), '2'))
return 2;
else
return 1;
}
static void AllCPUsMeter_init(Meter* this) { static void AllCPUsMeter_init(Meter* this) {
int cpus = this->pl->cpuCount; int cpus = this->pl->cpuCount;
if (!this->drawData) if (!this->drawData)
@ -152,10 +185,8 @@ static void AllCPUsMeter_init(Meter* this) {
if (this->mode == 0) if (this->mode == 0)
this->mode = BAR_METERMODE; this->mode = BAR_METERMODE;
int h = Meter_modes[this->mode]->h; int h = Meter_modes[this->mode]->h;
if (strchr(Meter_name(this), '2')) int ncol = MapClassnameToColumncount(this);
this->h = h * ((count+1) / 2); this->h = h * ((count + ncol - 1)/ ncol);
else
this->h = h * count;
} }
static void AllCPUsMeter_done(Meter* this) { static void AllCPUsMeter_done(Meter* this) {
@ -175,10 +206,8 @@ static void AllCPUsMeter_updateMode(Meter* this, int mode) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
Meter_setMode(meters[i], mode); Meter_setMode(meters[i], mode);
} }
if (strchr(Meter_name(this), '2')) int ncol = MapClassnameToColumncount(this);
this->h = h * ((count+1) / 2); this->h = h * ((count + ncol - 1)/ ncol);
else
this->h = h * count;
} }
static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) { static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) {
@ -209,6 +238,22 @@ static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) {
} }
} }
static void MultiColCPUsMeter_draw(Meter* this, int x, int y, int w){
Meter** meters = (Meter**) this->drawData;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
int ncol = MapClassnameToColumncount(this);
int colwidth = (w-ncol)/ncol + 1;
int diff = (w - (colwidth * ncol));
int nrows = (count + ncol - 1) / ncol;
for (int i = 0; i < count; i++){
int d = (i/nrows) > diff ? diff : (i / nrows) ; // dynamic spacer
int xpos = x + ((i / nrows) * colwidth) + d;
int ypos = y + ((i % nrows) * meters[0]->h);
meters[i]->draw(meters[i], xpos, ypos, colwidth);
}
}
MeterClass CPUMeter_class = { MeterClass CPUMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
@ -219,7 +264,7 @@ MeterClass CPUMeter_class = {
.defaultMode = BAR_METERMODE, .defaultMode = BAR_METERMODE,
.maxItems = CPU_METER_ITEMCOUNT, .maxItems = CPU_METER_ITEMCOUNT,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "CPU", .name = "CPU",
.uiName = "CPU", .uiName = "CPU",
.caption = "CPU", .caption = "CPU",
@ -234,7 +279,7 @@ MeterClass AllCPUsMeter_class = {
}, },
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "AllCPUs", .name = "AllCPUs",
.uiName = "CPUs (1/1)", .uiName = "CPUs (1/1)",
.description = "CPUs (1/1): all CPUs", .description = "CPUs (1/1): all CPUs",
@ -253,7 +298,7 @@ MeterClass AllCPUs2Meter_class = {
}, },
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "AllCPUs2", .name = "AllCPUs2",
.uiName = "CPUs (1&2/2)", .uiName = "CPUs (1&2/2)",
.description = "CPUs (1&2/2): all CPUs in 2 shorter columns", .description = "CPUs (1&2/2): all CPUs in 2 shorter columns",
@ -272,7 +317,7 @@ MeterClass LeftCPUsMeter_class = {
}, },
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "LeftCPUs", .name = "LeftCPUs",
.uiName = "CPUs (1/2)", .uiName = "CPUs (1/2)",
.description = "CPUs (1/2): first half of list", .description = "CPUs (1/2): first half of list",
@ -291,7 +336,7 @@ MeterClass RightCPUsMeter_class = {
}, },
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "RightCPUs", .name = "RightCPUs",
.uiName = "CPUs (2/2)", .uiName = "CPUs (2/2)",
.description = "CPUs (2/2): second half of list", .description = "CPUs (2/2): second half of list",
@ -310,7 +355,7 @@ MeterClass LeftCPUs2Meter_class = {
}, },
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "LeftCPUs2", .name = "LeftCPUs2",
.uiName = "CPUs (1&2/4)", .uiName = "CPUs (1&2/4)",
.description = "CPUs (1&2/4): first half in 2 shorter columns", .description = "CPUs (1&2/4): first half in 2 shorter columns",
@ -329,7 +374,7 @@ MeterClass RightCPUs2Meter_class = {
}, },
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "RightCPUs2", .name = "RightCPUs2",
.uiName = "CPUs (3&4/4)", .uiName = "CPUs (3&4/4)",
.description = "CPUs (3&4/4): second half in 2 shorter columns", .description = "CPUs (3&4/4): second half in 2 shorter columns",
@ -340,3 +385,59 @@ MeterClass RightCPUs2Meter_class = {
.done = AllCPUsMeter_done .done = AllCPUsMeter_done
}; };
MeterClass AllCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "AllCPUs4",
.uiName = "CPUs (1&2&3&4/4)",
.description = "CPUs (1&2&3&4/4): all CPUs in 4 shorter columns",
.caption = "CPU",
.draw = MultiColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass LeftCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs4",
.uiName = "CPUs (1-4/8)",
.description = "CPUs (1-4/8): first half in 4 shorter columns",
.caption = "CPU",
.draw = MultiColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass RightCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "RightCPUs4",
.uiName = "CPUs (5-8/8)",
.description = "CPUs (5-8/8): second half in 4 shorter columns",
.caption = "CPU",
.draw = MultiColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};

View File

@ -20,7 +20,8 @@ typedef enum {
CPU_METER_STEAL = 5, CPU_METER_STEAL = 5,
CPU_METER_GUEST = 6, CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7, CPU_METER_IOWAIT = 7,
CPU_METER_ITEMCOUNT = 8, // number of entries in this enum CPU_METER_FREQUENCY = 8,
CPU_METER_ITEMCOUNT = 9, // number of entries in this enum
} CPUMeterValues; } CPUMeterValues;
@ -47,5 +48,10 @@ extern MeterClass LeftCPUs2Meter_class;
extern MeterClass RightCPUs2Meter_class; extern MeterClass RightCPUs2Meter_class;
extern MeterClass AllCPUs4Meter_class;
extern MeterClass LeftCPUs4Meter_class;
extern MeterClass RightCPUs4Meter_class;
#endif #endif

108
CRT.c
View File

@ -122,18 +122,28 @@ typedef enum ColorElements_ {
CPU_NICE, CPU_NICE,
CPU_NICE_TEXT, CPU_NICE_TEXT,
CPU_NORMAL, CPU_NORMAL,
CPU_KERNEL, CPU_SYSTEM,
CPU_IOWAIT, CPU_IOWAIT,
CPU_IRQ, CPU_IRQ,
CPU_SOFTIRQ, CPU_SOFTIRQ,
CPU_STEAL, CPU_STEAL,
CPU_GUEST, CPU_GUEST,
PRESSURE_STALL_TEN,
PRESSURE_STALL_SIXTY,
PRESSURE_STALL_THREEHUNDRED,
ZFS_MFU,
ZFS_MRU,
ZFS_ANON,
ZFS_HEADER,
ZFS_OTHER,
ZFS_COMPRESSED,
ZFS_RATIO,
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
void CRT_fatalError(const char* note) __attribute__ ((noreturn)); extern void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn); extern void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A')) #define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
@ -157,7 +167,9 @@ const char *CRT_treeStrUtf8[TREE_STR_COUNT] = {
"\xe2\x94\x9c", // TREE_STR_RTEE ├ "\xe2\x94\x9c", // TREE_STR_RTEE ├
"\xe2\x94\x94", // TREE_STR_BEND └ "\xe2\x94\x94", // TREE_STR_BEND └
"\xe2\x94\x8c", // TREE_STR_TEND ┌ "\xe2\x94\x8c", // TREE_STR_TEND ┌
"+", // TREE_STR_OPEN + "+", // TREE_STR_OPEN +, TODO use 🮯 'BOX DRAWINGS LIGHT HORIZONTAL
// WITH VERTICAL STROKE' (U+1FBAF, "\xf0\x9f\xae\xaf") when
// Unicode 13 is common
"\xe2\x94\x80", // TREE_STR_SHUT ─ "\xe2\x94\x80", // TREE_STR_SHUT ─
}; };
@ -226,12 +238,22 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_NICE] = ColorPair(Blue,Black), [CPU_NICE] = ColorPair(Blue,Black),
[CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black),
[CPU_NORMAL] = ColorPair(Green,Black), [CPU_NORMAL] = ColorPair(Green,Black),
[CPU_KERNEL] = ColorPair(Red,Black), [CPU_SYSTEM] = ColorPair(Red,Black),
[CPU_IOWAIT] = A_BOLD | ColorPair(Black, Black), [CPU_IOWAIT] = A_BOLD | ColorPairGrayBlack,
[CPU_IRQ] = ColorPair(Yellow,Black), [CPU_IRQ] = ColorPair(Yellow,Black),
[CPU_SOFTIRQ] = ColorPair(Magenta,Black), [CPU_SOFTIRQ] = ColorPair(Magenta,Black),
[CPU_STEAL] = ColorPair(Cyan,Black), [CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black), [CPU_GUEST] = ColorPair(Cyan,Black),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Cyan,Black),
[PRESSURE_STALL_SIXTY] = A_BOLD | ColorPair(Cyan,Black),
[PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White,Black),
[ZFS_MFU] = ColorPair(Blue,Black),
[ZFS_MRU] = ColorPair(Yellow,Black),
[ZFS_ANON] = ColorPair(Magenta,Black),
[ZFS_HEADER] = ColorPair(Cyan,Black),
[ZFS_OTHER] = ColorPair(Magenta,Black),
[ZFS_COMPRESSED] = ColorPair(Blue,Black),
[ZFS_RATIO] = ColorPair(Magenta,Black),
}, },
[COLORSCHEME_MONOCHROME] = { [COLORSCHEME_MONOCHROME] = {
[RESET_COLOR] = A_NORMAL, [RESET_COLOR] = A_NORMAL,
@ -285,12 +307,22 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_NICE] = A_NORMAL, [CPU_NICE] = A_NORMAL,
[CPU_NICE_TEXT] = A_NORMAL, [CPU_NICE_TEXT] = A_NORMAL,
[CPU_NORMAL] = A_BOLD, [CPU_NORMAL] = A_BOLD,
[CPU_KERNEL] = A_BOLD, [CPU_SYSTEM] = A_BOLD,
[CPU_IOWAIT] = A_NORMAL, [CPU_IOWAIT] = A_NORMAL,
[CPU_IRQ] = A_BOLD, [CPU_IRQ] = A_BOLD,
[CPU_SOFTIRQ] = A_BOLD, [CPU_SOFTIRQ] = A_BOLD,
[CPU_STEAL] = A_REVERSE, [CPU_STEAL] = A_REVERSE,
[CPU_GUEST] = A_REVERSE, [CPU_GUEST] = A_REVERSE,
[PRESSURE_STALL_THREEHUNDRED] = A_DIM,
[PRESSURE_STALL_SIXTY] = A_NORMAL,
[PRESSURE_STALL_TEN] = A_BOLD,
[ZFS_MFU] = A_NORMAL,
[ZFS_MRU] = A_NORMAL,
[ZFS_ANON] = A_DIM,
[ZFS_HEADER] = A_BOLD,
[ZFS_OTHER] = A_DIM,
[ZFS_COMPRESSED] = A_BOLD,
[ZFS_RATIO] = A_BOLD,
}, },
[COLORSCHEME_BLACKONWHITE] = { [COLORSCHEME_BLACKONWHITE] = {
[RESET_COLOR] = ColorPair(Black,White), [RESET_COLOR] = ColorPair(Black,White),
@ -344,12 +376,22 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_NICE] = ColorPair(Cyan,White), [CPU_NICE] = ColorPair(Cyan,White),
[CPU_NICE_TEXT] = ColorPair(Cyan,White), [CPU_NICE_TEXT] = ColorPair(Cyan,White),
[CPU_NORMAL] = ColorPair(Green,White), [CPU_NORMAL] = ColorPair(Green,White),
[CPU_KERNEL] = ColorPair(Red,White), [CPU_SYSTEM] = ColorPair(Red,White),
[CPU_IOWAIT] = A_BOLD | ColorPair(Black, White), [CPU_IOWAIT] = A_BOLD | ColorPair(Black,White),
[CPU_IRQ] = ColorPair(Blue,White), [CPU_IRQ] = ColorPair(Blue,White),
[CPU_SOFTIRQ] = ColorPair(Blue,White), [CPU_SOFTIRQ] = ColorPair(Blue,White),
[CPU_STEAL] = ColorPair(Cyan,White), [CPU_STEAL] = ColorPair(Cyan,White),
[CPU_GUEST] = ColorPair(Cyan,White), [CPU_GUEST] = ColorPair(Cyan,White),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black,White),
[PRESSURE_STALL_SIXTY] = ColorPair(Black,White),
[PRESSURE_STALL_TEN] = ColorPair(Black,White),
[ZFS_MFU] = ColorPair(Cyan,White),
[ZFS_MRU] = ColorPair(Yellow,White),
[ZFS_ANON] = ColorPair(Magenta,White),
[ZFS_HEADER] = ColorPair(Yellow,White),
[ZFS_OTHER] = ColorPair(Magenta,White),
[ZFS_COMPRESSED] = ColorPair(Cyan,White),
[ZFS_RATIO] = ColorPair(Magenta,White),
}, },
[COLORSCHEME_LIGHTTERMINAL] = { [COLORSCHEME_LIGHTTERMINAL] = {
[RESET_COLOR] = ColorPair(Black,Black), [RESET_COLOR] = ColorPair(Black,Black),
@ -403,12 +445,22 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_NICE] = ColorPair(Cyan,Black), [CPU_NICE] = ColorPair(Cyan,Black),
[CPU_NICE_TEXT] = ColorPair(Cyan,Black), [CPU_NICE_TEXT] = ColorPair(Cyan,Black),
[CPU_NORMAL] = ColorPair(Green,Black), [CPU_NORMAL] = ColorPair(Green,Black),
[CPU_KERNEL] = ColorPair(Red,Black), [CPU_SYSTEM] = ColorPair(Red,Black),
[CPU_IOWAIT] = A_BOLD | ColorPair(Black, Black), [CPU_IOWAIT] = A_BOLD | ColorPair(Black,Black),
[CPU_IRQ] = A_BOLD | ColorPair(Blue,Black), [CPU_IRQ] = A_BOLD | ColorPair(Blue,Black),
[CPU_SOFTIRQ] = ColorPair(Blue,Black), [CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Black,Black), [CPU_STEAL] = ColorPair(Black,Black),
[CPU_GUEST] = ColorPair(Black,Black), [CPU_GUEST] = ColorPair(Black,Black),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black,Black),
[PRESSURE_STALL_SIXTY] = ColorPair(Black,Black),
[PRESSURE_STALL_TEN] = ColorPair(Black,Black),
[ZFS_MFU] = ColorPair(Cyan,Black),
[ZFS_MRU] = ColorPair(Yellow,Black),
[ZFS_ANON] = A_BOLD | ColorPair(Magenta,Black),
[ZFS_HEADER] = ColorPair(Black,Black),
[ZFS_OTHER] = A_BOLD | ColorPair(Magenta,Black),
[ZFS_COMPRESSED] = ColorPair(Cyan,Black),
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta,Black),
}, },
[COLORSCHEME_MIDNIGHT] = { [COLORSCHEME_MIDNIGHT] = {
[RESET_COLOR] = ColorPair(White,Blue), [RESET_COLOR] = ColorPair(White,Blue),
@ -462,12 +514,22 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_NICE] = A_BOLD | ColorPair(Cyan,Blue), [CPU_NICE] = A_BOLD | ColorPair(Cyan,Blue),
[CPU_NICE_TEXT] = A_BOLD | ColorPair(Cyan,Blue), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Cyan,Blue),
[CPU_NORMAL] = A_BOLD | ColorPair(Green,Blue), [CPU_NORMAL] = A_BOLD | ColorPair(Green,Blue),
[CPU_KERNEL] = A_BOLD | ColorPair(Red,Blue), [CPU_SYSTEM] = A_BOLD | ColorPair(Red,Blue),
[CPU_IOWAIT] = A_BOLD | ColorPair(Blue,Blue), [CPU_IOWAIT] = A_BOLD | ColorPair(Blue,Blue),
[CPU_IRQ] = A_BOLD | ColorPair(Black,Blue), [CPU_IRQ] = A_BOLD | ColorPair(Black,Blue),
[CPU_SOFTIRQ] = ColorPair(Black,Blue), [CPU_SOFTIRQ] = ColorPair(Black,Blue),
[CPU_STEAL] = ColorPair(White,Blue), [CPU_STEAL] = ColorPair(White,Blue),
[CPU_GUEST] = ColorPair(White,Blue), [CPU_GUEST] = ColorPair(White,Blue),
[PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black,Blue),
[PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White,Blue),
[PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White,Blue),
[ZFS_MFU] = A_BOLD | ColorPair(White,Blue),
[ZFS_MRU] = A_BOLD | ColorPair(Yellow,Blue),
[ZFS_ANON] = A_BOLD | ColorPair(Magenta,Blue),
[ZFS_HEADER] = A_BOLD | ColorPair(Yellow,Blue),
[ZFS_OTHER] = A_BOLD | ColorPair(Magenta,Blue),
[ZFS_COMPRESSED] = A_BOLD | ColorPair(White,Blue),
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta,Blue),
}, },
[COLORSCHEME_BLACKNIGHT] = { [COLORSCHEME_BLACKNIGHT] = {
[RESET_COLOR] = ColorPair(Cyan,Black), [RESET_COLOR] = ColorPair(Cyan,Black),
@ -521,12 +583,22 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_NICE] = ColorPair(Blue,Black), [CPU_NICE] = ColorPair(Blue,Black),
[CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black),
[CPU_NORMAL] = ColorPair(Green,Black), [CPU_NORMAL] = ColorPair(Green,Black),
[CPU_KERNEL] = ColorPair(Red,Black), [CPU_SYSTEM] = ColorPair(Red,Black),
[CPU_IOWAIT] = ColorPair(Yellow,Black), [CPU_IOWAIT] = ColorPair(Yellow,Black),
[CPU_IRQ] = A_BOLD | ColorPair(Blue,Black), [CPU_IRQ] = A_BOLD | ColorPair(Blue,Black),
[CPU_SOFTIRQ] = ColorPair(Blue,Black), [CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Cyan,Black), [CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black), [CPU_GUEST] = ColorPair(Cyan,Black),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green,Black),
[PRESSURE_STALL_SIXTY] = ColorPair(Green,Black),
[PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green,Black),
[ZFS_MFU] = ColorPair(Blue,Black),
[ZFS_MRU] = ColorPair(Yellow,Black),
[ZFS_ANON] = ColorPair(Magenta,Black),
[ZFS_HEADER] = ColorPair(Yellow,Black),
[ZFS_OTHER] = ColorPair(Magenta,Black),
[ZFS_COMPRESSED] = ColorPair(Blue,Black),
[ZFS_RATIO] = ColorPair(Magenta,Black),
}, },
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated. [COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
}; };
@ -595,7 +667,7 @@ void CRT_restorePrivileges() {
// TODO: pass an instance of Settings instead. // TODO: pass an instance of Settings instead.
void CRT_init(int delay, int colorScheme) { void CRT_init(int delay, int colorScheme, bool allowUnicode) {
initscr(); initscr();
noecho(); noecho();
CRT_delay = delay; CRT_delay = delay;
@ -604,12 +676,12 @@ void CRT_init(int delay, int colorScheme) {
} }
CRT_colors = CRT_colorSchemes[colorScheme]; CRT_colors = CRT_colorSchemes[colorScheme];
CRT_colorScheme = colorScheme; CRT_colorScheme = colorScheme;
for (int i = 0; i < LAST_COLORELEMENT; i++) { for (int i = 0; i < LAST_COLORELEMENT; i++) {
unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i]; unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i];
CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White,Black) : color; CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White,Black) : color;
} }
halfdelay(CRT_delay); halfdelay(CRT_delay);
nonl(); nonl();
intrflush(stdscr, false); intrflush(stdscr, false);
@ -661,10 +733,12 @@ void CRT_init(int delay, int colorScheme) {
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
if(strcmp(nl_langinfo(CODESET), "UTF-8") == 0) if (allowUnicode && strcmp(nl_langinfo(CODESET), "UTF-8") == 0)
CRT_utf8 = true; CRT_utf8 = true;
else else
CRT_utf8 = false; CRT_utf8 = false;
#else
(void) allowUnicode;
#endif #endif
CRT_treeStr = CRT_treeStr =

40
CRT.h
View File

@ -110,18 +110,28 @@ typedef enum ColorElements_ {
CPU_NICE, CPU_NICE,
CPU_NICE_TEXT, CPU_NICE_TEXT,
CPU_NORMAL, CPU_NORMAL,
CPU_KERNEL, CPU_SYSTEM,
CPU_IOWAIT, CPU_IOWAIT,
CPU_IRQ, CPU_IRQ,
CPU_SOFTIRQ, CPU_SOFTIRQ,
CPU_STEAL, CPU_STEAL,
CPU_GUEST, CPU_GUEST,
PRESSURE_STALL_TEN,
PRESSURE_STALL_SIXTY,
PRESSURE_STALL_THREEHUNDRED,
ZFS_MFU,
ZFS_MRU,
ZFS_ANON,
ZFS_HEADER,
ZFS_OTHER,
ZFS_COMPRESSED,
ZFS_RATIO,
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
void CRT_fatalError(const char* note) __attribute__ ((noreturn)); extern void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn); extern void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A')) #define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
@ -140,7 +150,7 @@ extern const char **CRT_treeStr;
extern int CRT_delay; extern int CRT_delay;
int* CRT_colors; extern int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT]; extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
@ -150,21 +160,21 @@ extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount; extern int CRT_scrollWheelVAmount;
char* CRT_termType; extern char* CRT_termType;
// TODO move color scheme to Settings, perhaps? // TODO move color scheme to Settings, perhaps?
extern int CRT_colorScheme; extern int CRT_colorScheme;
void *backtraceArray[128]; extern void *backtraceArray[128];
#if HAVE_SETUID_ENABLED #if HAVE_SETUID_ENABLED
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0) #define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
void CRT_dropPrivileges(); extern void CRT_dropPrivileges();
void CRT_restorePrivileges(); extern void CRT_restorePrivileges();
#else #else
@ -179,18 +189,18 @@ void CRT_restorePrivileges();
// TODO: pass an instance of Settings instead. // TODO: pass an instance of Settings instead.
void CRT_init(int delay, int colorScheme); extern void CRT_init(int delay, int colorScheme, bool allowUnicode);
void CRT_done(); extern void CRT_done();
void CRT_fatalError(const char* note); extern void CRT_fatalError(const char* note);
int CRT_readKey(); extern int CRT_readKey();
void CRT_disableDelay(); extern void CRT_disableDelay();
void CRT_enableDelay(); extern void CRT_enableDelay();
void CRT_setColors(int colorScheme); extern void CRT_setColors(int colorScheme);
#endif #endif

View File

@ -24,10 +24,10 @@ typedef struct CategoriesPanel_ {
} CategoriesPanel; } CategoriesPanel;
void CategoriesPanel_makeMetersPage(CategoriesPanel* this); extern void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
extern PanelClass CategoriesPanel_class; extern PanelClass CategoriesPanel_class;
CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl); extern CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl);
#endif #endif

View File

@ -1,3 +1,79 @@
What's new in version 3.0.1
* Coverity fixes, CI improvements, documentation updates
* BUGFIX: Fix early exit with longer sysfs battery paths
* BUGFIX: Improve OOM output, fix sorting
(thanks to Christian Göttsche)
* Rework check buttons and tree open/closed
(thanks to Bert Wesarg)
* Add -U/--no-unicode option to disable unicode
(thanks to Christian Hesse)
* Improvements to the affinity panel
(thanks to Bert Wesarg)
What's new in version 3.0.0
* New maintainers - after a prolonged period of inactivity
from Hisham, the creator and original maintainer, a team
of community maintainers have volunteered to take over a
fork at https://htop.dev and https://github.com/htop-dev
to keep the project going.
* Support ZFS ARC statistics
(thanks to Ross Williams)
* Support more than 2 smaller CPU meter columns
(thanks to Christoph Budziszewski)
* Support Linux proportional set size metrics
(thanks to @linvinus, @ntninja and @himikof)
* Support Linux pressure stall information metrics
(thanks to Ran Benita)
* New display option to show CPU frequency in CPU meters
(thanks to Arnav Singh)
* Update Linux sysfs battery discovery for recent kernels
(thanks to @smattie)
* Add hardware topology information in the affinity panel
(thanks to Bert Wesarg)
* Add timestamp reporting to the strace screen
(thanks to Mario Harjac)
* Add simple, optional vim key mapping mode
(thanks to Daniel Flanagan)
* Added an option to disable the mouse
(thanks to MartinJM)
* Add Solaris11 compatibility
(thanks to Jan Senolt)
* Without an argument -u uses $USER value automatically
(thanks to @solanav)
* Support less(1) search navigation shortcuts
(thanks to @syrrim)
* Update the FreeBSD maximum PID to match FreeBSD change
(thanks to @multiplexd)
* Report values larger than 100 terabytes
(thanks to @adrien1018)
* Widen ST_UID (UID) column to allow for UIDs > 9999
(thanks to DLange)
* BUGFIX: fix makefiles for building with clang
(thanks to Jorge Pereira)
* BUGFIX: fix <sys/sysmacros.h> major() usage
(thanks to @wataash and Kang-Che Sung)
* BUGFIX: fix the STARTTIME column on FreeBSD
(thanks to Rob Crowston)
* BUGFIX: truncate overwide jail names on FreeBSD
(thanks to Rob Crowston)
* BUGFIX: fix reported memory values on FreeBSD
(thanks to Tobias Kortkamp)
* BUGFIX: fix reported CPU meter values on OpenBSD
(thanks to @motet-a)
* BUGFIX: correctly identify other types of zombie process
(thanks to @joder)
* BUGFIX: improve follow-process handling in some situations
(thanks to @wangqr)
* BUGFIX: fix custom meters reverting to unexpected setting
(thanks to @wangqr)
* BUGFIX: close pipe after running lsof(1)
(thanks to Jesin)
* BUGFIX: meters honour setting of counting CPUs from 0/1
(thanks to @rnsanchez)
What's new in version 2.2.0 What's new in version 2.2.0
* Solaris/Illumos/OpenIndiana support * Solaris/Illumos/OpenIndiana support
@ -158,7 +234,7 @@ What's new in version 1.0.2
What's new in version 1.0.1 What's new in version 1.0.1
* Move .htoprc to XDG-compliant path ~/.config/htop/htoprc, * Move .htoprc to XDG-compliant path ~/.config/htop/htoprc,
respecting $XDG_CONFIG_HOME respecting $XDG_CONFIG_HOME
(thanks to Hadzhimurad Ustarkhan for the suggestion.) (thanks to Hadzhimurad Ustarkhan for the suggestion.)
* Safer behavior on the kill screen, to make it harder to kill the wrong process. * Safer behavior on the kill screen, to make it harder to kill the wrong process.
* Fix for building in FreeBSD 8.2 * Fix for building in FreeBSD 8.2
@ -546,7 +622,7 @@ What's new in version 0.3.1
What's new in version 0.3 What's new in version 0.3
* BUGFIX: no dirt left on screen on horizontal scrolling * BUGFIX: no dirt left on screen on horizontal scrolling
* Signal selection on "kill" command * Signal selection on "kill" command
* Color-coding for users, nice and process status * Color-coding for users, nice and process status
* "Follow" function * "Follow" function

View File

@ -66,14 +66,14 @@ CheckItem* CheckItem_newByVal(char* text, bool value) {
} }
void CheckItem_set(CheckItem* this, bool value) { void CheckItem_set(CheckItem* this, bool value) {
if (this->ref) if (this->ref)
*(this->ref) = value; *(this->ref) = value;
else else
this->value = value; this->value = value;
} }
bool CheckItem_get(CheckItem* this) { bool CheckItem_get(CheckItem* this) {
if (this->ref) if (this->ref)
return *(this->ref); return *(this->ref);
else else
return this->value; return this->value;

View File

@ -21,12 +21,12 @@ typedef struct CheckItem_ {
extern ObjectClass CheckItem_class; extern ObjectClass CheckItem_class;
CheckItem* CheckItem_newByRef(char* text, bool* ref); extern CheckItem* CheckItem_newByRef(char* text, bool* ref);
CheckItem* CheckItem_newByVal(char* text, bool value); extern CheckItem* CheckItem_newByVal(char* text, bool value);
void CheckItem_set(CheckItem* this, bool value); extern void CheckItem_set(CheckItem* this, bool value);
bool CheckItem_get(CheckItem* this); extern bool CheckItem_get(CheckItem* this);
#endif #endif

View File

@ -56,7 +56,7 @@ static void ColorsPanel_delete(Object* object) {
static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) { static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
ColorsPanel* this = (ColorsPanel*) super; ColorsPanel* this = (ColorsPanel*) super;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
int mark = Panel_getSelectedIndex(super); int mark = Panel_getSelectedIndex(super);

View File

@ -29,6 +29,6 @@ typedef struct ColorsPanel_ {
extern PanelClass ColorsPanel_class; extern PanelClass ColorsPanel_class;
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr); extern ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr);
#endif #endif

View File

@ -40,7 +40,7 @@ static void ColumnsPanel_delete(Object* object) {
static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) { static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
ColumnsPanel* const this = (ColumnsPanel*) super; ColumnsPanel* const this = (ColumnsPanel*) super;
int selected = Panel_getSelectedIndex(super); int selected = Panel_getSelectedIndex(super);
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
int size = Panel_size(super); int size = Panel_size(super);
@ -87,7 +87,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
case ']': case ']':
case '+': case '+':
{ {
if (selected < size - 2) if (selected < size - 2)
Panel_moveSelectedDown(super); Panel_moveSelectedDown(super);
result = HANDLED; result = HANDLED;
break; break;
@ -164,4 +164,3 @@ void ColumnsPanel_update(Panel* super) {
} }
this->settings->fields[size] = 0; this->settings->fields[size] = 0;
} }

View File

@ -22,11 +22,10 @@ typedef struct ColumnsPanel_ {
extern PanelClass ColumnsPanel_class; extern PanelClass ColumnsPanel_class;
ColumnsPanel* ColumnsPanel_new(Settings* settings); extern ColumnsPanel* ColumnsPanel_new(Settings* settings);
int ColumnsPanel_fieldNameToIndex(const char* name); extern int ColumnsPanel_fieldNameToIndex(const char* name);
void ColumnsPanel_update(Panel* super);
extern void ColumnsPanel_update(Panel* super);
#endif #endif

View File

@ -39,7 +39,7 @@ static void DisplayOptionsPanel_delete(Object* object) {
static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) { static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
DisplayOptionsPanel* this = (DisplayOptionsPanel*) super; DisplayOptionsPanel* this = (DisplayOptionsPanel*) super;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
CheckItem* selected = (CheckItem*) Panel_getSelected(super); CheckItem* selected = (CheckItem*) Panel_getSelected(super);
@ -97,5 +97,12 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU percentage numerically"), &(settings->showCPUUsage)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable the mouse"), &(settings->enableMouse)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable vim mode"), &(settings->vimMode)));
#ifdef HAVE_LIBHWLOC
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show topology when selecting affinity by default"), &(settings->topologyAffinity)));
#endif
return this; return this;
} }

View File

@ -23,6 +23,6 @@ typedef struct DisplayOptionsPanel_ {
extern PanelClass DisplayOptionsPanel_class; extern PanelClass DisplayOptionsPanel_class;
DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr); extern DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr);
#endif #endif

View File

@ -11,12 +11,12 @@ typedef struct EnvScreen_ {
extern InfoScreenClass EnvScreen_class; extern InfoScreenClass EnvScreen_class;
EnvScreen* EnvScreen_new(Process* process); extern EnvScreen* EnvScreen_new(Process* process);
void EnvScreen_delete(Object* this); extern void EnvScreen_delete(Object* this);
void EnvScreen_draw(InfoScreen* this); extern void EnvScreen_draw(InfoScreen* this);
void EnvScreen_scan(InfoScreen* this); extern void EnvScreen_scan(InfoScreen* this);
#endif #endif

View File

@ -52,7 +52,7 @@ FunctionBar* FunctionBar_new(const char* const* functions, const char* const* ke
this->functions[i] = xStrdup(functions[i]); this->functions[i] = xStrdup(functions[i]);
} }
if (keys && events) { if (keys && events) {
this->staticData = false; this->staticData = false;
this->keys = xCalloc(15, sizeof(char*)); this->keys = xCalloc(15, sizeof(char*));
this->events = xCalloc(15, sizeof(int)); this->events = xCalloc(15, sizeof(int));
int i = 0; int i = 0;

View File

@ -22,18 +22,18 @@ typedef struct FunctionBar_ {
FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc); extern FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc);
FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events); extern FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events);
void FunctionBar_delete(FunctionBar* this); extern void FunctionBar_delete(FunctionBar* this);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text); extern void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
void FunctionBar_draw(const FunctionBar* this, char* buffer); extern void FunctionBar_draw(const FunctionBar* this, char* buffer);
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr); extern void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos); extern int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);
#endif #endif

View File

@ -63,7 +63,7 @@ int Hashtable_count(Hashtable* this) {
static HashtableItem* HashtableItem_new(unsigned int key, void* value) { static HashtableItem* HashtableItem_new(unsigned int key, void* value) {
HashtableItem* this; HashtableItem* this;
this = xMalloc(sizeof(HashtableItem)); this = xMalloc(sizeof(HashtableItem));
this->key = key; this->key = key;
this->value = value; this->value = value;
@ -73,7 +73,7 @@ static HashtableItem* HashtableItem_new(unsigned int key, void* value) {
Hashtable* Hashtable_new(int size, bool owner) { Hashtable* Hashtable_new(int size, bool owner) {
Hashtable* this; Hashtable* this;
this = xMalloc(sizeof(Hashtable)); this = xMalloc(sizeof(Hashtable));
this->items = 0; this->items = 0;
this->size = size; this->size = size;
@ -119,10 +119,10 @@ void Hashtable_put(Hashtable* this, unsigned int key, void* value) {
void* Hashtable_remove(Hashtable* this, unsigned int key) { void* Hashtable_remove(Hashtable* this, unsigned int key) {
unsigned int index = key % this->size; unsigned int index = key % this->size;
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
HashtableItem** bucket; HashtableItem** bucket;
for (bucket = &(this->buckets[index]); *bucket; bucket = &((*bucket)->next) ) { for (bucket = &(this->buckets[index]); *bucket; bucket = &((*bucket)->next) ) {
if ((*bucket)->key == key) { if ((*bucket)->key == key) {
void* value = (*bucket)->value; void* value = (*bucket)->value;

View File

@ -30,20 +30,20 @@ struct Hashtable_ {
#ifdef DEBUG #ifdef DEBUG
int Hashtable_count(Hashtable* this); extern int Hashtable_count(Hashtable* this);
#endif #endif
Hashtable* Hashtable_new(int size, bool owner); extern Hashtable* Hashtable_new(int size, bool owner);
void Hashtable_delete(Hashtable* this); extern void Hashtable_delete(Hashtable* this);
void Hashtable_put(Hashtable* this, unsigned int key, void* value); extern void Hashtable_put(Hashtable* this, unsigned int key, void* value);
void* Hashtable_remove(Hashtable* this, unsigned int key); extern void* Hashtable_remove(Hashtable* this, unsigned int key);
extern void* Hashtable_get(Hashtable* this, unsigned int key); extern void* Hashtable_get(Hashtable* this, unsigned int key);
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData); extern void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);
#endif #endif

View File

@ -76,17 +76,17 @@ void Header_populateFromSettings(Header* this) {
void Header_writeBackToSettings(const Header* this) { void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col]; MeterColumnSettings* colSettings = &this->settings->columns[col];
String_freeArray(colSettings->names); String_freeArray(colSettings->names);
free(colSettings->modes); free(colSettings->modes);
Vector* vec = this->columns[col]; Vector* vec = this->columns[col];
int len = Vector_size(vec); int len = Vector_size(vec);
colSettings->names = xCalloc(len+1, sizeof(char*)); colSettings->names = xCalloc(len+1, sizeof(char*));
colSettings->modes = xCalloc(len, sizeof(int)); colSettings->modes = xCalloc(len, sizeof(int));
colSettings->len = len; colSettings->len = len;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
Meter* meter = (Meter*) Vector_get(vec, i); Meter* meter = (Meter*) Vector_get(vec, i);
char* name = xCalloc(64, sizeof(char)); char* name = xCalloc(64, sizeof(char));
@ -120,6 +120,8 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
break; break;
} }
} }
if (paren)
*paren = '(';
return mode; return mode;
} }
@ -152,7 +154,7 @@ char* Header_readMeterName(Header* this, int i, int column) {
int nameLen = strlen(Meter_name(meter)); int nameLen = strlen(Meter_name(meter));
int len = nameLen + 100; int len = nameLen + 100;
char* name = xMalloc(len); char* name = xMalloc(len);
strncpy(name, Meter_name(meter), nameLen); memcpy(name, Meter_name(meter), nameLen);
name[nameLen] = '\0'; name[nameLen] = '\0';
if (meter->param) if (meter->param)
xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param); xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
@ -186,7 +188,7 @@ void Header_draw(const Header* this) {
} }
int width = COLS / this->nrColumns - (pad * this->nrColumns - 1) - 1; int width = COLS / this->nrColumns - (pad * this->nrColumns - 1) - 1;
int x = pad; int x = pad;
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
Vector* meters = this->columns[col]; Vector* meters = this->columns[col];
for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) { for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) {

View File

@ -31,30 +31,30 @@ typedef struct Header_ {
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_)) #define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
#endif #endif
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns); extern Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns);
void Header_delete(Header* this); extern void Header_delete(Header* this);
void Header_populateFromSettings(Header* this); extern void Header_populateFromSettings(Header* this);
void Header_writeBackToSettings(const Header* this); extern void Header_writeBackToSettings(const Header* this);
MeterModeId Header_addMeterByName(Header* this, char* name, int column); extern MeterModeId Header_addMeterByName(Header* this, char* name, int column);
void Header_setMode(Header* this, int i, MeterModeId mode, int column); extern void Header_setMode(Header* this, int i, MeterModeId mode, int column);
Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column); extern Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column);
int Header_size(Header* this, int column); extern int Header_size(Header* this, int column);
char* Header_readMeterName(Header* this, int i, int column); extern char* Header_readMeterName(Header* this, int i, int column);
MeterModeId Header_readMeterMode(Header* this, int i, int column); extern MeterModeId Header_readMeterMode(Header* this, int i, int column);
void Header_reinit(Header* this); extern void Header_reinit(Header* this);
void Header_draw(const Header* this); extern void Header_draw(const Header* this);
int Header_calculateHeight(Header* this); extern int Header_calculateHeight(Header* this);
#endif #endif

370
INSTALL
View File

@ -1,370 +0,0 @@
Installation Instructions
*************************
Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without warranty of any kind.
Basic Installation
==================
Briefly, the shell command `./configure && make && make install'
should configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package. Some packages provide this
`INSTALL' file but do not implement all of the features documented
below. The lack of an optional feature in a given package is not
necessarily a bug. More recommendations for GNU packages can be found
in *note Makefile Conventions: (standards)Makefile Conventions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package, generally using the just-built uninstalled binaries.
4. Type `make install' to install the programs and any data files and
documentation. When installing into a prefix owned by root, it is
recommended that the package be configured and built as a regular
user, and only the `make install' phase executed with root
privileges.
5. Optionally, type `make installcheck' to repeat any self-tests, but
this time using the binaries in their final installed location.
This target does not install anything. Running this target as a
regular user, particularly if the prior `make install' required
root privileges, verifies that the installation completed
correctly.
6. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
7. Often, you can also type `make uninstall' to remove the installed
files again. In practice, not all packages have tested that
uninstallation works correctly, even though it is required by the
GNU Coding Standards.
8. Some packages, particularly those that use Automake, provide `make
distcheck', which can by used by developers to test that all other
targets like `make install' and `make uninstall' work correctly.
This target is generally not run by end users.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'. This
is known as a "VPATH" build.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
On MacOS X 10.5 and later systems, you can create libraries and
executables that work on multiple system types--known as "fat" or
"universal" binaries--by specifying multiple `-arch' options to the
compiler but only a single `-arch' option to the preprocessor. Like
this:
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CPP="gcc -E" CXXCPP="g++ -E"
This is not guaranteed to produce working output in all cases, you
may have to build one architecture at a time and combine the results
using the `lipo' tool if you have problems.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX', where PREFIX must be an
absolute file name.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them. In general, the
default for these options is expressed in terms of `${prefix}', so that
specifying just `--prefix' will affect all of the other directory
specifications that were not explicitly provided.
The most portable way to affect installation locations is to pass the
correct locations to `configure'; however, many packages provide one or
both of the following shortcuts of passing variable assignments to the
`make install' command line to change installation locations without
having to reconfigure or recompile.
The first method involves providing an override variable for each
affected directory. For example, `make install
prefix=/alternate/directory' will choose an alternate location for all
directory configuration variables that were expressed in terms of
`${prefix}'. Any directories that were specified during `configure',
but not in terms of `${prefix}', must each be overridden at install
time for the entire installation to be relocated. The approach of
makefile variable overrides for each directory variable is required by
the GNU Coding Standards, and ideally causes no recompilation.
However, some platforms have known limitations with the semantics of
shared libraries that end up requiring recompilation when using this
method, particularly noticeable in packages that use GNU Libtool.
The second method involves providing the `DESTDIR' variable. For
example, `make install DESTDIR=/alternate/directory' will prepend
`/alternate/directory' before all installation names. The approach of
`DESTDIR' overrides is not required by the GNU Coding Standards, and
does not work on platforms that have drive letters. On the other hand,
it does better at avoiding recompilation issues, and works well even
when some directory options were not specified in terms of `${prefix}'
at `configure' time.
Optional Features
=================
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Some packages offer the ability to configure how verbose the
execution of `make' will be. For these packages, running `./configure
--enable-silent-rules' sets the default to minimal output, which can be
overridden with `make V=1'; while running `./configure
--disable-silent-rules' sets the default to verbose, which can be
overridden with `make V=0'.
Particular systems
==================
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
CC is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
HP-UX `make' updates targets which have the same time stamps as
their prerequisites, which makes it generally unusable when shipped
generated files such as `configure' are involved. Use GNU `make'
instead.
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
a workaround. If GNU CC is not installed, it is therefore recommended
to try
./configure CC="cc"
and if that doesn't work, try
./configure CC="cc -nodtk"
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
directory contains several dysfunctional programs; working variants of
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
in your `PATH', put it _after_ `/usr/bin'.
On Haiku, software installed for all users goes in `/boot/common',
not `/usr/local'. It is recommended to use the following options:
./configure --prefix=/boot/common
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS
KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf limitation. Until the limitation is lifted, you can use
this workaround:
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of all of the options to `configure', and exit.
`--help=short'
`--help=recursive'
Print a summary of the options unique to this package's
`configure', and exit. The `short' variant lists options used
only in the top level, while the `recursive' variant lists options
also present in any nested packages.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--prefix=DIR'
Use DIR as the installation prefix. *note Installation Names::
for more details, including other options available for fine-tuning
the installation locations.
`--no-create'
`-n'
Run the configure checks, but stop before creating any output
files.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

@ -52,6 +52,10 @@ static void IncMode_reset(IncMode* mode) {
mode->buffer[0] = 0; mode->buffer[0] = 0;
} }
void IncSet_reset(IncSet* this, IncType type) {
IncMode_reset(&this->modes[type]);
}
static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL}; static const char* const 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 int searchEvents[] = {KEY_F(3), 27, ERR};
@ -132,6 +136,30 @@ static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelVa
return found; return found;
} }
static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
int size = Panel_size(panel);
int here = Panel_getSelectedIndex(panel);
int i = here;
for(;;) {
i+=step;
if (i == size) i = 0;
if (i == -1) i = size - 1;
if (i == here) return false;
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
return true;
}
}
}
bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
return IncMode_find(&this->modes[type], panel, getPanelValue, 1);
}
bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
return IncMode_find(&this->modes[type], panel, getPanelValue, -1);
}
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) { bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
if (ch == ERR) if (ch == ERR)
return true; return true;
@ -141,17 +169,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
bool doSearch = true; bool doSearch = true;
if (ch == KEY_F(3)) { if (ch == KEY_F(3)) {
if (size == 0) return true; if (size == 0) return true;
int here = Panel_getSelectedIndex(panel); IncMode_find(mode, panel, getPanelValue, 1);
int i = here;
for(;;) {
i++;
if (i == size) i = 0;
if (i == here) break;
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
break;
}
}
doSearch = false; doSearch = false;
} else if (ch < 255 && isprint((char)ch)) { } else if (ch < 255 && isprint((char)ch)) {
if (mode->index < INCMODE_MAX) { if (mode->index < INCMODE_MAX) {
@ -187,7 +205,9 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
IncMode_reset(mode); IncMode_reset(mode);
} }
} else { } else {
IncMode_reset(mode); if (ch == 27) {
IncMode_reset(mode);
}
} }
this->active = NULL; this->active = NULL;
Panel_setDefaultBar(panel); Panel_setDefaultBar(panel);

View File

@ -41,18 +41,24 @@ typedef struct IncSet_ {
typedef const char* (*IncMode_GetPanelValue)(Panel*, int); typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
IncSet* IncSet_new(FunctionBar* bar); extern void IncSet_reset(IncSet* this, IncType type);
void IncSet_delete(IncSet* this); extern IncSet* IncSet_new(FunctionBar* bar);
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines); extern void IncSet_delete(IncSet* this);
const char* IncSet_getListItemValue(Panel* panel, int i); extern bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
void IncSet_activate(IncSet* this, IncType type, Panel* panel); extern bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
void IncSet_drawBar(IncSet* this); extern bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
int IncSet_synthesizeEvent(IncSet* this, int x); extern const char* IncSet_getListItemValue(Panel* panel, int i);
extern void IncSet_activate(IncSet* this, IncType type, Panel* panel);
extern void IncSet_drawBar(IncSet* this);
extern int IncSet_synthesizeEvent(IncSet* this, int x);
#endif #endif

View File

@ -56,7 +56,7 @@ static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27}; static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, char* panelHeader) { InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, const char* panelHeader) {
this->process = process; this->process = process;
if (!bar) { if (!bar) {
bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents); bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents);
@ -75,12 +75,12 @@ InfoScreen* InfoScreen_done(InfoScreen* this) {
return this; return this;
} }
void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...) { void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
mvhline(0, 0, ' ', COLS); mvhline(0, 0, ' ', COLS);
wmove(stdscr, 0, 0); (void) wmove(stdscr, 0, 0);
vw_printw(stdscr, fmt, ap); vw_printw(stdscr, fmt, ap);
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true; this->display->needsRedraw = true;
@ -120,7 +120,7 @@ void InfoScreen_run(InfoScreen* this) {
} }
set_escdelay(25); set_escdelay(25);
int ch = getch(); int ch = getch();
if (ch == ERR) { if (ch == ERR) {
if (As_InfoScreen(this)->onErr) { if (As_InfoScreen(this)->onErr) {
InfoScreen_onErr(this); InfoScreen_onErr(this);
@ -131,19 +131,21 @@ void InfoScreen_run(InfoScreen* this) {
if (ch == KEY_MOUSE) { if (ch == KEY_MOUSE) {
MEVENT mevent; MEVENT mevent;
int ok = getmouse(&mevent); int ok = getmouse(&mevent);
if (ok == OK) if (ok == OK) {
if (mevent.y >= panel->y && mevent.y < LINES - 1) { if (mevent.y >= panel->y && mevent.y < LINES - 1) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV); Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV);
ch = 0; ch = 0;
} if (mevent.y == LINES - 1) } else if (mevent.y == LINES - 1) {
ch = IncSet_synthesizeEvent(this->inc, mevent.x); ch = IncSet_synthesizeEvent(this->inc, mevent.x);
}
}
} }
if (this->inc->active) { if (this->inc->active) {
IncSet_handleKey(this->inc, ch, panel, IncSet_getListItemValue, this->lines); IncSet_handleKey(this->inc, ch, panel, IncSet_getListItemValue, this->lines);
continue; continue;
} }
switch(ch) { switch(ch) {
case ERR: case ERR:
continue; continue;

View File

@ -38,16 +38,16 @@ struct InfoScreen_ {
Vector* lines; Vector* lines;
}; };
InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, char* panelHeader); extern InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, const char* panelHeader);
InfoScreen* InfoScreen_done(InfoScreen* this); extern InfoScreen* InfoScreen_done(InfoScreen* this);
void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...); extern void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...);
void InfoScreen_addLine(InfoScreen* this, const char* line); extern void InfoScreen_addLine(InfoScreen* this, const char* line);
void InfoScreen_appendLine(InfoScreen* this, const char* line); extern void InfoScreen_appendLine(InfoScreen* this, const char* line);
void InfoScreen_run(InfoScreen* this); extern void InfoScreen_run(InfoScreen* this);
#endif #endif

View File

@ -85,4 +85,3 @@ long ListItem_compare(const void* cast1, const void* cast2) {
ListItem* obj2 = (ListItem*) cast2; ListItem* obj2 = (ListItem*) cast2;
return strcmp(obj1->value, obj2->value); return strcmp(obj1->value, obj2->value);
} }

View File

@ -21,13 +21,12 @@ typedef struct ListItem_ {
extern ObjectClass ListItem_class; extern ObjectClass ListItem_class;
ListItem* ListItem_new(const char* value, int key); extern ListItem* ListItem_new(const char* value, int key);
void ListItem_append(ListItem* this, const char* text); extern void ListItem_append(ListItem* this, const char* text);
const char* ListItem_getRef(ListItem* this); extern const char* ListItem_getRef(ListItem* this);
long ListItem_compare(const void* cast1, const void* cast2);
extern long ListItem_compare(const void* cast1, const void* cast2);
#endif #endif

View File

@ -1,6 +1,7 @@
/* /*
htop - ColumnsPanel.c htop - ColumnsPanel.c
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -25,18 +26,13 @@ typedef struct MainPanel_ {
pid_t pidSearch; pid_t pidSearch;
} MainPanel; } MainPanel;
typedef union {
int i;
void* v;
} Arg;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg); typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar) #define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
}*/ }*/
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL}; static const char* const MainFunctions[] = {"Help ", "Setup ", "Search ", "Filter ", "Tree ", "SortBy ", "Nice - ", "Nice + ", "Kill ", "Quit ", NULL};
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) { void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this); FunctionBar* bar = MainPanel_getFunctionBar(this);
@ -69,7 +65,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
MainPanel* this = (MainPanel*) super; MainPanel* this = (MainPanel*) super;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
if (EVENT_IS_HEADER_CLICK(ch)) { if (EVENT_IS_HEADER_CLICK(ch)) {
@ -84,7 +80,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} else { } else {
reaction |= Action_setSortKey(settings, field); reaction |= Action_setSortKey(settings, field);
} }
reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS; reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS;
result = HANDLED; result = HANDLED;
} else if (ch != ERR && this->inc->active) { } else if (ch != ERR && this->inc->active) {
bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL); bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL);
@ -121,7 +117,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} }
if (reaction & HTOP_REFRESH) { if (reaction & HTOP_REFRESH) {
result |= REDRAW; result |= REDRAW;
} }
if (reaction & HTOP_RECALCULATE) { if (reaction & HTOP_RECALCULATE) {
result |= RESCAN; result |= RESCAN;
} }

View File

@ -5,6 +5,7 @@
/* /*
htop - ColumnsPanel.h htop - ColumnsPanel.h
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -21,32 +22,27 @@ typedef struct MainPanel_ {
pid_t pidSearch; pid_t pidSearch;
} MainPanel; } MainPanel;
typedef union {
int i;
void* v;
} Arg;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg); typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar) #define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode); extern void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
void MainPanel_pidSearch(MainPanel* this, int ch); extern void MainPanel_pidSearch(MainPanel* this, int ch);
int MainPanel_selectedPid(MainPanel* this); extern int MainPanel_selectedPid(MainPanel* this);
const char* MainPanel_getValue(MainPanel* this, int i); extern const char* MainPanel_getValue(MainPanel* this, int i);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged); extern bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
extern PanelClass MainPanel_class; extern PanelClass MainPanel_class;
MainPanel* MainPanel_new(); extern MainPanel* MainPanel_new();
void MainPanel_setState(MainPanel* this, State* state); extern void MainPanel_setState(MainPanel* this, State* state);
void MainPanel_delete(Object* object); extern void MainPanel_delete(Object* object);
#endif #endif

View File

@ -12,7 +12,7 @@ applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png pixmap_DATA = htop.png
AM_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)" AM_CFLAGS += -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
AM_LDFLAGS = AM_LDFLAGS =
AM_CPPFLAGS = -DNDEBUG AM_CPPFLAGS = -DNDEBUG
@ -48,14 +48,20 @@ linux_platform_headers = \
linux/LinuxProcess.h \ linux/LinuxProcess.h \
linux/LinuxProcessList.h \ linux/LinuxProcessList.h \
linux/LinuxCRT.h \ linux/LinuxCRT.h \
linux/Battery.h linux/Battery.h \
linux/PressureStallMeter.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
all_platform_headers += $(linux_platform_headers) all_platform_headers += $(linux_platform_headers)
if HTOP_LINUX if HTOP_LINUX
AM_CFLAGS += -rdynamic AM_LDFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \ myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \
linux/PressureStallMeter.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(linux_platform_headers) myhtopplatheaders = $(linux_platform_headers)
endif endif
@ -68,13 +74,18 @@ freebsd_platform_headers = \
freebsd/FreeBSDProcessList.h \ freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \ freebsd/FreeBSDProcess.h \
freebsd/FreeBSDCRT.h \ freebsd/FreeBSDCRT.h \
freebsd/Battery.h freebsd/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
all_platform_headers += $(freebsd_platform_headers) all_platform_headers += $(freebsd_platform_headers)
if HTOP_FREEBSD if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \ myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(freebsd_platform_headers) myhtopplatheaders = $(freebsd_platform_headers)
endif endif
@ -126,14 +137,19 @@ darwin_platform_headers = \
darwin/DarwinProcess.h \ darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \ darwin/DarwinProcessList.h \
darwin/DarwinCRT.h \ darwin/DarwinCRT.h \
darwin/Battery.h darwin/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
all_platform_headers += $(darwin_platform_headers) all_platform_headers += $(darwin_platform_headers)
if HTOP_DARWIN if HTOP_DARWIN
AM_LDFLAGS += -framework IOKit -framework CoreFoundation AM_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \ myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(darwin_platform_headers) myhtopplatheaders = $(darwin_platform_headers)
endif endif
@ -146,14 +162,18 @@ solaris_platform_headers = \
solaris/SolarisProcess.h \ solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \ solaris/SolarisProcessList.h \
solaris/SolarisCRT.h \ solaris/SolarisCRT.h \
solaris/Battery.h solaris/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
all_platform_headers += $(solaris_platform_headers) all_platform_headers += $(solaris_platform_headers)
if HTOP_SOLARIS if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \ myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \ solaris/SolarisProcess.c solaris/SolarisProcessList.c \
solaris/SolarisCRT.c solaris/Battery.c solaris/SolarisCRT.c solaris/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(solaris_platform_headers) myhtopplatheaders = $(solaris_platform_headers)
endif endif
@ -183,7 +203,8 @@ endif
SUFFIXES = .h SUFFIXES = .h
BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders) BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) config.h htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
nodist_htop_SOURCES = config.h
.PHONY: htop-headers clean-htop-headers .PHONY: htop-headers clean-htop-headers
@ -205,11 +226,18 @@ coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov" $(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"
.c.h: .c.h:
@srcdir@/scripts/MakeHeader.py $< ./scripts/MakeHeader.py $<
cppcheck: cppcheck:
cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS
dist-hook: $(top_distdir)/configure
@if grep 'pkg_m4_absent' '$(top_distdir)/configure'; then \
echo 'configure is generated without pkg.m4. Please supply pkg.m4 and run ./autogen.sh to rebuild the configure script.'>&2; \
(exit 1); \
else :; \
fi
.PHONY: lcov .PHONY: lcov
lcov: lcov:

View File

@ -60,7 +60,7 @@ MeterClass MemoryMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = MemoryMeter_display, .display = MemoryMeter_display,
}, },
.updateValues = MemoryMeter_updateValues, .updateValues = MemoryMeter_updateValues,
.defaultMode = BAR_METERMODE, .defaultMode = BAR_METERMODE,
.maxItems = 3, .maxItems = 3,
.total = 100.0, .total = 100.0,

19
Meter.c
View File

@ -78,7 +78,7 @@ typedef struct MeterClass_ {
struct Meter_ { struct Meter_ {
Object super; Object super;
Meter_Draw draw; Meter_Draw draw;
char* caption; char* caption;
int mode; int mode;
int param; int param;
@ -275,7 +275,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
attrset(CRT_colors[BAR_BORDER]); attrset(CRT_colors[BAR_BORDER]);
mvaddch(y, x, '['); mvaddch(y, x, '[');
mvaddch(y, x + w, ']'); mvaddch(y, x + w, ']');
w--; w--;
x++; x++;
@ -284,7 +284,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
return; return;
} }
char bar[w + 1]; char bar[w + 1];
int blockSizes[10]; int blockSizes[10];
xSnprintf(bar, w + 1, "%*.*s", w, w, buffer); xSnprintf(bar, w + 1, "%*.*s", w, w, buffer);
@ -378,7 +378,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
mvaddnstr(y, x, this->caption, captionLen); mvaddnstr(y, x, this->caption, captionLen);
x += captionLen; x += captionLen;
w -= captionLen; w -= captionLen;
struct timeval now; struct timeval now;
gettimeofday(&now, NULL); gettimeofday(&now, NULL);
if (!timercmp(&now, &(data->time), <)) { if (!timercmp(&now, &(data->time), <)) {
@ -387,10 +387,10 @@ 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[nValues];
Meter_updateValues(this, buffer, nValues - 1); Meter_updateValues(this, buffer, nValues - 1);
double value = 0.0; double value = 0.0;
int items = Meter_getItems(this); int items = Meter_getItems(this);
for (int i = 0; i < items; i++) for (int i = 0; i < items; i++)
@ -398,7 +398,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
value /= this->total; value /= this->total;
data->values[nValues - 1] = value; data->values[nValues - 1] = value;
} }
int i = nValues - (w*2) + 2, k = 0; int i = nValues - (w*2) + 2, k = 0;
if (i < 0) { if (i < 0) {
k = -i/2; k = -i/2;
@ -459,7 +459,7 @@ static void LEDMeterMode_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, METER_BUFFER_LEN - 1);
RichString_begin(out); RichString_begin(out);
Meter_displayBuffer(this, buffer, &out); Meter_displayBuffer(this, buffer, &out);
@ -523,6 +523,9 @@ MeterMode* Meter_modes[] = {
static void BlankMeter_updateValues(Meter* this, char* buffer, int size) { static void BlankMeter_updateValues(Meter* this, char* buffer, int size) {
(void) this; (void) buffer; (void) size; (void) this; (void) buffer; (void) size;
if (size > 0) {
*buffer = 0;
}
} }
static void BlankMeter_display(Object* cast, RichString* out) { static void BlankMeter_display(Object* cast, RichString* out) {

14
Meter.h
View File

@ -65,7 +65,7 @@ typedef struct MeterClass_ {
struct Meter_ { struct Meter_ {
Object super; Object super;
Meter_Draw draw; Meter_Draw draw;
char* caption; char* caption;
int mode; int mode;
int param; int param;
@ -109,17 +109,17 @@ typedef struct GraphData_ {
extern MeterClass Meter_class; extern MeterClass Meter_class;
Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type); extern Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type);
int Meter_humanUnit(char* buffer, unsigned long int value, int size); extern int Meter_humanUnit(char* buffer, unsigned long int value, int size);
void Meter_delete(Object* cast); extern void Meter_delete(Object* cast);
void Meter_setCaption(Meter* this, const char* caption); extern void Meter_setCaption(Meter* this, const char* caption);
void Meter_setMode(Meter* this, int modeIndex); extern void Meter_setMode(Meter* this, int modeIndex);
ListItem* Meter_toListItem(Meter* this, bool moving); extern ListItem* Meter_toListItem(Meter* this, bool moving);
/* ---------- TextMeterMode ---------- */ /* ---------- TextMeterMode ---------- */

View File

@ -93,7 +93,7 @@ static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int
static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
MetersPanel* this = (MetersPanel*) super; MetersPanel* this = (MetersPanel*) super;
int selected = Panel_getSelectedIndex(super); int selected = Panel_getSelectedIndex(super);
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
bool sideMove = false; bool sideMove = false;

View File

@ -34,10 +34,10 @@ struct MetersPanel_ {
// In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are // In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are
// considered "Ambiguous characters". // considered "Ambiguous characters".
void MetersPanel_setMoving(MetersPanel* this, bool moving); extern void MetersPanel_setMoving(MetersPanel* this, bool moving);
extern PanelClass MetersPanel_class; extern PanelClass MetersPanel_class;
MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr); extern MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr);
#endif #endif

1
NEWS
View File

@ -2,4 +2,3 @@
See the commit history for news of the past. See the commit history for news of the past.
See the bug tracker for news of the future. See the bug tracker for news of the future.
Run the program for news of the present. Run the program for news of the present.

View File

@ -1,6 +1,7 @@
/* /*
htop - Object.c htop - Object.c
(C) 2004-2012 Hisham H. Muhammad (C) 2004-2012 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -28,7 +29,7 @@ typedef void(*Object_Delete)(Object*);
#define Class(class_) ((ObjectClass*)(&(class_ ## _class))) #define Class(class_) ((ObjectClass*)(&(class_ ## _class)))
#define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_)); #define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_));
typedef struct ObjectClass_ { typedef struct ObjectClass_ {
const void* extends; const void* extends;
const Object_Display display; const Object_Display display;
@ -40,6 +41,11 @@ struct Object_ {
ObjectClass* klass; ObjectClass* klass;
}; };
typedef union {
int i;
void* v;
} Arg;
}*/ }*/
ObjectClass Object_class = { ObjectClass Object_class = {

View File

@ -5,6 +5,7 @@
/* /*
htop - Object.h htop - Object.h
(C) 2004-2012 Hisham H. Muhammad (C) 2004-2012 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -29,7 +30,7 @@ typedef void(*Object_Delete)(Object*);
#define Class(class_) ((ObjectClass*)(&(class_ ## _class))) #define Class(class_) ((ObjectClass*)(&(class_ ## _class)))
#define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_)); #define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_));
typedef struct ObjectClass_ { typedef struct ObjectClass_ {
const void* extends; const void* extends;
const Object_Display display; const Object_Display display;
@ -41,12 +42,17 @@ struct Object_ {
ObjectClass* klass; ObjectClass* klass;
}; };
typedef union {
int i;
void* v;
} Arg;
extern ObjectClass Object_class; extern ObjectClass Object_class;
#ifdef DEBUG #ifdef DEBUG
bool Object_isA(Object* o, const ObjectClass* klass); extern bool Object_isA(Object* o, const ObjectClass* klass);
#endif #endif

View File

@ -124,6 +124,7 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
item->data[cmd] = xStrdup(line + 1); item->data[cmd] = xStrdup(line + 1);
free(line); free(line);
} }
fclose(fd);
int wstatus; int wstatus;
if (waitpid(child, &wstatus, 0) == -1) { if (waitpid(child, &wstatus, 0) == -1) {
pdata->error = 1; pdata->error = 1;

View File

@ -34,12 +34,12 @@ typedef struct OpenFilesScreen_ {
extern InfoScreenClass OpenFilesScreen_class; extern InfoScreenClass OpenFilesScreen_class;
OpenFilesScreen* OpenFilesScreen_new(Process* process); extern OpenFilesScreen* OpenFilesScreen_new(Process* process);
void OpenFilesScreen_delete(Object* this); extern void OpenFilesScreen_delete(Object* this);
void OpenFilesScreen_draw(InfoScreen* this); extern void OpenFilesScreen_draw(InfoScreen* this);
void OpenFilesScreen_scan(InfoScreen* this); extern void OpenFilesScreen_scan(InfoScreen* this);
#endif #endif

12
Panel.c
View File

@ -264,6 +264,14 @@ void Panel_setSelected(Panel* this, int selected) {
} }
} }
void Panel_splice(Panel *this, Vector* from) {
assert (this != NULL);
assert (from != NULL);
Vector_splice(this->items, from);
this->needsRedraw = true;
}
void Panel_draw(Panel* this, bool focus) { void Panel_draw(Panel* this, bool focus) {
assert (this != NULL); assert (this != NULL);
@ -372,7 +380,7 @@ void Panel_draw(Panel* this, bool focus) {
bool Panel_onKey(Panel* this, int key) { bool Panel_onKey(Panel* this, int key) {
assert (this != NULL); assert (this != NULL);
int size = Vector_size(this->items); int size = Vector_size(this->items);
switch (key) { switch (key) {
case KEY_DOWN: case KEY_DOWN:
@ -455,7 +463,7 @@ bool Panel_onKey(Panel* this, int key) {
if (this->selected < 0 || size == 0) { if (this->selected < 0 || size == 0) {
this->selected = 0; this->selected = 0;
this->needsRedraw = true; this->needsRedraw = true;
} else if (this->selected >= size) { } else if (this->selected >= size) {
this->selected = size - 1; this->selected = size - 1;
this->needsRedraw = true; this->needsRedraw = true;
} }

48
Panel.h
View File

@ -75,52 +75,54 @@ struct Panel_ {
extern PanelClass Panel_class; extern PanelClass Panel_class;
Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar); extern Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar);
void Panel_delete(Object* cast); extern void Panel_delete(Object* cast);
void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar); extern void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar);
void Panel_done(Panel* this); extern void Panel_done(Panel* this);
void Panel_setSelectionColor(Panel* this, int color); extern void Panel_setSelectionColor(Panel* this, int color);
RichString* Panel_getHeader(Panel* this); extern RichString* Panel_getHeader(Panel* this);
extern void Panel_setHeader(Panel* this, const char* header); extern void Panel_setHeader(Panel* this, const char* header);
void Panel_move(Panel* this, int x, int y); extern void Panel_move(Panel* this, int x, int y);
void Panel_resize(Panel* this, int w, int h); extern void Panel_resize(Panel* this, int w, int h);
void Panel_prune(Panel* this); extern void Panel_prune(Panel* this);
void Panel_add(Panel* this, Object* o); extern void Panel_add(Panel* this, Object* o);
void Panel_insert(Panel* this, int i, Object* o); extern void Panel_insert(Panel* this, int i, Object* o);
void Panel_set(Panel* this, int i, Object* o); extern void Panel_set(Panel* this, int i, Object* o);
Object* Panel_get(Panel* this, int i); extern Object* Panel_get(Panel* this, int i);
Object* Panel_remove(Panel* this, int i); extern Object* Panel_remove(Panel* this, int i);
Object* Panel_getSelected(Panel* this); extern Object* Panel_getSelected(Panel* this);
void Panel_moveSelectedUp(Panel* this); extern void Panel_moveSelectedUp(Panel* this);
void Panel_moveSelectedDown(Panel* this); extern void Panel_moveSelectedDown(Panel* this);
int Panel_getSelectedIndex(Panel* this); extern int Panel_getSelectedIndex(Panel* this);
int Panel_size(Panel* this); extern int Panel_size(Panel* this);
void Panel_setSelected(Panel* this, int selected); extern void Panel_setSelected(Panel* this, int selected);
void Panel_draw(Panel* this, bool focus); extern void Panel_draw(Panel* this, bool focus);
bool Panel_onKey(Panel* this, int key); extern void Panel_splice(Panel *this, Vector* from);
HandlerResult Panel_selectByTyping(Panel* this, int ch); extern bool Panel_onKey(Panel* this, int key);
extern HandlerResult Panel_selectByTyping(Panel* this, int ch);
#endif #endif

View File

@ -1,6 +1,7 @@
/* /*
htop - Process.c htop - Process.c
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -8,6 +9,8 @@ in the source distribution for its full text.
#include "Process.h" #include "Process.h"
#include "Settings.h" #include "Settings.h"
#include "config.h"
#include "CRT.h" #include "CRT.h"
#include "StringUtils.h" #include "StringUtils.h"
#include "RichString.h" #include "RichString.h"
@ -30,8 +33,7 @@ in the source distribution for its full text.
#include <math.h> #include <math.h>
#ifdef MAJOR_IN_MKDEV #ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h> #include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS) || \ #elif defined(MAJOR_IN_SYSMACROS)
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
#endif #endif
@ -83,7 +85,7 @@ typedef enum ProcessFields {
typedef struct ProcessPidColumn_ { typedef struct ProcessPidColumn_ {
int id; int id;
char* label; const char* label;
} ProcessPidColumn; } ProcessPidColumn;
typedef struct Process_ { typedef struct Process_ {
@ -191,10 +193,12 @@ static int Process_getuid = -1;
#define ONE_K 1024L #define ONE_K 1024L
#define ONE_M (ONE_K * ONE_K) #define ONE_M (ONE_K * ONE_K)
#define ONE_G (ONE_M * ONE_K) #define ONE_G (ONE_M * ONE_K)
#define ONE_T ((long long)ONE_G * ONE_K)
#define ONE_DECIMAL_K 1000L #define ONE_DECIMAL_K 1000L
#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K) #define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K)
#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K) #define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K)
#define ONE_DECIMAL_T ((long long)ONE_DECIMAL_G * ONE_DECIMAL_K)
char Process_pidFormat[20] = "%7d "; char Process_pidFormat[20] = "%7d ";
@ -216,7 +220,7 @@ void Process_setupColumnWidths() {
void Process_humanNumber(RichString* str, unsigned long number, bool coloring) { void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
char buffer[11]; char buffer[11];
int len; int len;
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];
@ -224,11 +228,11 @@ void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
largeNumberColor = CRT_colors[PROCESS]; largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS]; processMegabytesColor = CRT_colors[PROCESS];
} }
if(number >= (10 * ONE_DECIMAL_M)) { if(number >= (10 * ONE_DECIMAL_M)) {
#ifdef __LP64__ #ifdef __LP64__
if(number >= (100 * ONE_DECIMAL_G)) { if(number >= (100 * ONE_DECIMAL_G)) {
len = snprintf(buffer, 10, "%4ldT ", number / ONE_G); len = snprintf(buffer, 10, "%4luT ", number / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendn(str, largeNumberColor, buffer, len);
return; return;
} else if (number >= (1000 * ONE_DECIMAL_M)) { } else if (number >= (1000 * ONE_DECIMAL_M)) {
@ -238,7 +242,7 @@ void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
} }
#endif #endif
if(number >= (100 * ONE_DECIMAL_M)) { if(number >= (100 * ONE_DECIMAL_M)) {
len = snprintf(buffer, 10, "%4ldG ", number / ONE_M); len = snprintf(buffer, 10, "%4luG ", number / ONE_M);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendn(str, largeNumberColor, buffer, len);
return; return;
} }
@ -246,11 +250,11 @@ void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendn(str, largeNumberColor, buffer, len);
return; return;
} else if (number >= 100000) { } else if (number >= 100000) {
len = snprintf(buffer, 10, "%4ldM ", number / ONE_K); len = snprintf(buffer, 10, "%4luM ", number / ONE_K);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendn(str, processMegabytesColor, buffer, len);
return; return;
} else if (number >= 1000) { } else if (number >= 1000) {
len = snprintf(buffer, 10, "%2ld", number/1000); len = snprintf(buffer, 10, "%2lu", number/1000);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendn(str, processMegabytesColor, buffer, len);
number %= 1000; number %= 1000;
len = snprintf(buffer, 10, "%03lu ", number); len = snprintf(buffer, 10, "%03lu ", number);
@ -277,8 +281,15 @@ void Process_colorNumber(RichString* str, unsigned long long number, bool colori
if ((long long) number == -1LL) { if ((long long) number == -1LL) {
int len = snprintf(buffer, 13, " no perm "); int len = snprintf(buffer, 13, " no perm ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len); RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (number > 10000000000) { } else if (number >= 100000LL * ONE_DECIMAL_T) {
xSnprintf(buffer, 13, "%11lld ", number / 1000); xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_G);
RichString_appendn(str, largeNumberColor, buffer, 12);
} else if (number >= 100LL * ONE_DECIMAL_T) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_M);
RichString_appendn(str, largeNumberColor, buffer, 8);
RichString_appendn(str, processMegabytesColor, buffer+8, 4);
} else if (number >= 10LL * ONE_DECIMAL_G) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_K);
RichString_appendn(str, largeNumberColor, buffer, 5); RichString_appendn(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3); RichString_appendn(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4); RichString_appendn(str, processColor, buffer+8, 4);
@ -358,14 +369,17 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
} 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_appendn(str, processColor, buffer, len);
} else if (rate < ONE_K * ONE_K) { } 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_appendn(str, processColor, buffer, len);
} else if (rate < ONE_K * ONE_K * ONE_K) { } else if (rate < ONE_G) {
int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_K / ONE_K); int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendn(str, processMegabytesColor, buffer, len);
} else if (rate < ONE_T) {
int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len);
} else { } else {
int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_K / ONE_K / ONE_K); int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendn(str, largeNumberColor, buffer, len);
} }
} }
@ -380,9 +394,9 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
switch (field) { switch (field) {
case PERCENT_CPU: { case PERCENT_CPU: {
if (this->percent_cpu > 999.9) { if (this->percent_cpu > 999.9) {
xSnprintf(buffer, n, "%4d ", (unsigned int)this->percent_cpu); xSnprintf(buffer, n, "%4u ", (unsigned int)this->percent_cpu);
} else if (this->percent_cpu > 99.9) { } else if (this->percent_cpu > 99.9) {
xSnprintf(buffer, n, "%3d. ", (unsigned int)this->percent_cpu); xSnprintf(buffer, n, "%3u. ", (unsigned int)this->percent_cpu);
} else { } else {
xSnprintf(buffer, n, "%4.1f ", this->percent_cpu); xSnprintf(buffer, n, "%4.1f ", this->percent_cpu);
} }
@ -390,7 +404,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
} }
case PERCENT_MEM: { case PERCENT_MEM: {
if (this->percent_mem > 99.9) { if (this->percent_mem > 99.9) {
xSnprintf(buffer, n, "100. "); xSnprintf(buffer, n, "100. ");
} else { } else {
xSnprintf(buffer, n, "%4.1f ", this->percent_mem); xSnprintf(buffer, n, "%4.1f ", this->percent_mem);
} }
@ -414,11 +428,16 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
if (indent & (1U << i)) if (indent & (1U << i))
maxIndent = i+1; maxIndent = i+1;
for (int i = 0; i < maxIndent - 1; i++) { for (int i = 0; i < maxIndent - 1; i++) {
int written; int written, ret;
if (indent & (1 << i)) if (indent & (1 << i))
written = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]); ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
else else
written = snprintf(buf, n, " "); ret = snprintf(buf, n, " ");
if (ret < 0 || ret >= n) {
written = n;
} else {
written = ret;
}
buf += written; buf += written;
n -= written; n -= written;
} }
@ -466,7 +485,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
} }
break; break;
} }
case ST_UID: xSnprintf(buffer, n, "%4d ", 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, Process_pidFormat, this->tgid); break;
case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break; case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break;
@ -544,14 +563,15 @@ bool Process_setPriority(Process* this, int priority) {
return (err == 0); return (err == 0);
} }
bool Process_changePriorityBy(Process* this, int delta) { bool Process_changePriorityBy(Process* this, Arg delta) {
return Process_setPriority(this, this->nice + delta); return Process_setPriority(this, this->nice + delta.i);
} }
void Process_sendSignal(Process* this, int sgn) { bool Process_sendSignal(Process* this, Arg sgn) {
CRT_dropPrivileges(); CRT_dropPrivileges();
kill(this->pid, (int) sgn); bool ok = (kill(this->pid, sgn.i) == 0);
CRT_restorePrivileges(); CRT_restorePrivileges();
return ok;
} }
long Process_pidCompare(const void* v1, const void* v2) { long Process_pidCompare(const void* v1, const void* v2) {

View File

@ -5,13 +5,13 @@
/* /*
htop - Process.h htop - Process.h
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#ifdef MAJOR_IN_MKDEV #ifdef MAJOR_IN_MKDEV
#elif defined(MAJOR_IN_SYSMACROS) || \ #elif defined(MAJOR_IN_SYSMACROS)
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#endif #endif
#ifdef __ANDROID__ #ifdef __ANDROID__
@ -61,7 +61,7 @@ typedef enum ProcessFields {
typedef struct ProcessPidColumn_ { typedef struct ProcessPidColumn_ {
int id; int id;
char* label; const char* label;
} ProcessPidColumn; } ProcessPidColumn;
typedef struct Process_ { typedef struct Process_ {
@ -166,43 +166,45 @@ typedef struct ProcessClass_ {
#define ONE_K 1024L #define ONE_K 1024L
#define ONE_M (ONE_K * ONE_K) #define ONE_M (ONE_K * ONE_K)
#define ONE_G (ONE_M * ONE_K) #define ONE_G (ONE_M * ONE_K)
#define ONE_T ((long long)ONE_G * ONE_K)
#define ONE_DECIMAL_K 1000L #define ONE_DECIMAL_K 1000L
#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K) #define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K)
#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K) #define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K)
#define ONE_DECIMAL_T ((long long)ONE_DECIMAL_G * ONE_DECIMAL_K)
extern char Process_pidFormat[20]; extern char Process_pidFormat[20];
void Process_setupColumnWidths(); extern void Process_setupColumnWidths();
void Process_humanNumber(RichString* str, unsigned long number, bool coloring); extern void Process_humanNumber(RichString* str, unsigned long number, bool coloring);
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring); extern void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
void Process_printTime(RichString* str, unsigned long long totalHundredths); extern void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring); extern void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
void Process_writeField(Process* this, RichString* str, ProcessField field); extern void Process_writeField(Process* this, RichString* str, ProcessField field);
void Process_display(Object* cast, RichString* out); extern void Process_display(Object* cast, RichString* out);
void Process_done(Process* this); extern void Process_done(Process* this);
extern ProcessClass Process_class; extern ProcessClass Process_class;
void Process_init(Process* this, struct Settings_* settings); extern void Process_init(Process* this, struct Settings_* settings);
void Process_toggleTag(Process* this); extern void Process_toggleTag(Process* this);
bool Process_setPriority(Process* this, int priority); extern bool Process_setPriority(Process* this, int priority);
bool Process_changePriorityBy(Process* this, int delta); extern bool Process_changePriorityBy(Process* this, Arg delta);
void Process_sendSignal(Process* this, int sgn); extern bool Process_sendSignal(Process* this, Arg sgn);
long Process_pidCompare(const void* v1, const void* v2); extern long Process_pidCompare(const void* v1, const void* v2);
long Process_compare(const void* v1, const void* v2); extern long Process_compare(const void* v1, const void* v2);
#endif #endif

View File

@ -84,21 +84,28 @@ ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable*
this->usersTable = usersTable; this->usersTable = usersTable;
this->pidWhiteList = pidWhiteList; this->pidWhiteList = pidWhiteList;
this->userId = userId; this->userId = userId;
// tree-view auxiliary buffer // tree-view auxiliary buffer
this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); this->processes2 = Vector_new(klass, true, DEFAULT_SIZE);
// set later by platform-specific code // set later by platform-specific code
this->cpuCount = 0; this->cpuCount = 0;
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
this->topologyOk = false; this->topologyOk = false;
int topoErr = hwloc_topology_init(&this->topology); if (hwloc_topology_init(&this->topology) == 0) {
if (topoErr == 0) { this->topologyOk =
topoErr = hwloc_topology_load(this->topology); #if HWLOC_API_VERSION < 0x00020000
} /* try to ignore the top-level machine object type */
if (topoErr == 0) { 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_MACHINE) &&
this->topologyOk = true; /* ignore caches, which don't add structure */
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CORE) &&
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CACHE) &&
0 == hwloc_topology_set_flags(this->topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) &&
#else
0 == hwloc_topology_set_all_types_filter(this->topology, HWLOC_TYPE_FILTER_KEEP_STRUCTURE) &&
#endif
0 == hwloc_topology_load(this->topology);
} }
#endif #endif
@ -138,10 +145,10 @@ void ProcessList_printHeader(ProcessList* this, RichString* header) {
void ProcessList_add(ProcessList* this, Process* p) { void ProcessList_add(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1); assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
assert(Hashtable_get(this->processTable, p->pid) == NULL); assert(Hashtable_get(this->processTable, p->pid) == NULL);
Vector_add(this->processes, p); Vector_add(this->processes, p);
Hashtable_put(this->processTable, p->pid, p); Hashtable_put(this->processTable, p->pid, p);
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1); assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
assert(Hashtable_get(this->processTable, p->pid) != NULL); assert(Hashtable_get(this->processTable, p->pid) != NULL);
assert(Hashtable_count(this->processTable) == Vector_count(this->processes)); assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
@ -353,7 +360,7 @@ void ProcessList_scan(ProcessList* this) {
this->runningTasks = 0; this->runningTasks = 0;
ProcessList_goThroughEntries(this); ProcessList_goThroughEntries(this);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* p = (Process*) Vector_get(this->processes, i); Process* p = (Process*) Vector_get(this->processes, i);
if (p->updated == false) if (p->updated == false)

View File

@ -71,32 +71,32 @@ void ProcessList_delete(ProcessList* pl);
void ProcessList_goThroughEntries(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* pl);
ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); extern ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_done(ProcessList* this); extern void ProcessList_done(ProcessList* this);
void ProcessList_setPanel(ProcessList* this, Panel* panel); extern void ProcessList_setPanel(ProcessList* this, Panel* panel);
void ProcessList_printHeader(ProcessList* this, RichString* header); extern void ProcessList_printHeader(ProcessList* this, RichString* header);
void ProcessList_add(ProcessList* this, Process* p); extern void ProcessList_add(ProcessList* this, Process* p);
void ProcessList_remove(ProcessList* this, Process* p); extern void ProcessList_remove(ProcessList* this, Process* p);
Process* ProcessList_get(ProcessList* this, int idx); extern Process* ProcessList_get(ProcessList* this, int idx);
int ProcessList_size(ProcessList* this); extern int ProcessList_size(ProcessList* this);
void ProcessList_sort(ProcessList* this); extern void ProcessList_sort(ProcessList* this);
ProcessField ProcessList_keyAt(ProcessList* this, int at); extern ProcessField ProcessList_keyAt(ProcessList* this, int at);
void ProcessList_expandTree(ProcessList* this); extern void ProcessList_expandTree(ProcessList* this);
void ProcessList_rebuildPanel(ProcessList* this); extern void ProcessList_rebuildPanel(ProcessList* this);
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor); extern Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor);
void ProcessList_scan(ProcessList* this); extern void ProcessList_scan(ProcessList* this);
#endif #endif

61
README
View File

@ -1,52 +1,37 @@
[![Build Status](https://travis-ci.org/hishamhm/htop.svg?branch=master)](https://travis-ci.org/hishamhm/htop) # [![htop](htop.png)](https://htop.dev)
[![PayPal donate](https://img.shields.io/badge/paypal-donate-green.svg)](http://hisham.hm/htop/index.php?page=donate)
[htop](http://hisham.hm/htop/) [![CI](https://github.com/htop-dev/htop/workflows/CI/badge.svg)](https://github.com/htop-dev/htop/actions)
==== [![Coverity Scan Build Status](https://scan.coverity.com/projects/21665/badge.svg)](https://scan.coverity.com/projects/21665)
[![Mailing List](https://img.shields.io/badge/Mailing%20List-htop-blue.svg)](https://groups.io/g/htop)
[![IRC #htop](https://img.shields.io/badge/IRC-htop-blue.svg)](https://webchat.freenode.net/#htop)
[![Github Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest)
[![Download](https://api.bintray.com/packages/htop/source/htop/images/download.svg)](https://bintray.com/htop/source/htop/_latestVersion)
by Hisham Muhammad <hisham@gobolinux.org> (2004 - 2016) ## Introduction
Introduction `htop` is a cross-platform interactive process viewer.
------------ It requires `ncurses`.
This is `htop`, an interactive process viewer. For more information and details on how to contribute to `htop`
It requires `ncurses`. It is developed primarily on Linux, visit [htop.dev](https://htop.dev).
but we also have code for running under FreeBSD and Mac OS X
(help and testing are wanted for these platforms!)
This software has evolved considerably over the years, ## Build instructions
and is reasonably complete, but there is always room for improvement.
Comparison between `htop` and classic `top`
-------------------------------------------
* In `htop` you can scroll the list vertically and horizontally
to see all processes and full command lines.
* In `top` you are subject to a delay for each unassigned
key you press (especially annoying when multi-key escape
sequences are triggered by accident).
* `htop` starts faster (`top` seems to collect data for a while
before displaying anything).
* In `htop` you don't need to type the process number to
kill a process, in `top` you do.
* In `htop` you don't need to type the process number or
the priority value to renice a process, in `top` you do.
* In `htop` you can kill multiple processes at once.
* `top` is older, hence, more tested.
Compilation instructions
------------------------
This program is distributed as a standard autotools-based package. This program is distributed as a standard autotools-based package.
See the [INSTALL](/INSTALL) file for detailed instructions. For detailed instructions see the `INSTALL` file, which
is created after `./autogen.sh` is run.
When compiling from a [release tarball](https://hisham.hm/htop/releases/), run: When compiling from a [release tarball](https://github.com/htop-dev/htop/releases/), run:
./configure && make ~~~ shell
./configure && make
~~~
For compiling sources downloaded from the Git repository, run: For compiling sources downloaded from the Git repository, run:
./autogen.sh && ./configure && make ~~~ shell
./autogen.sh && ./configure && make
~~~
By default `make install` will install into `/usr/local`, for changing By default `make install` will install into `/usr/local`, for changing
the path use `./configure --prefix=/some/path`. the path use `./configure --prefix=/some/path`.
@ -54,8 +39,6 @@ the path use `./configure --prefix=/some/path`.
See the manual page (`man htop`) or the on-line help ('F1' or 'h' See the manual page (`man htop`) or the on-line help ('F1' or 'h'
inside `htop`) for a list of supported key commands. inside `htop`) for a list of supported key commands.
If not all keys work check your curses configuration.
## License ## License
GNU General Public License, version 2 (GPL-2.0) GNU General Public License, version 2 (GPL-2.0)

View File

@ -67,7 +67,7 @@ typedef struct RichString_ {
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
#define charBytes(n) (sizeof(CharType) * (n)) #define charBytes(n) (sizeof(CharType) * (n))
static void RichString_extendLen(RichString* this, int len) { static void RichString_extendLen(RichString* this, int len) {
if (this->chlen <= RICHSTRING_MAXLEN) { if (this->chlen <= RICHSTRING_MAXLEN) {

View File

@ -63,7 +63,7 @@ typedef struct RichString_ {
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
#define charBytes(n) (sizeof(CharType) * (n)) #define charBytes(n) (sizeof(CharType) * (n))
#define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0) #define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0)
@ -71,24 +71,24 @@ typedef struct RichString_ {
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish); extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
int RichString_findChar(RichString* this, char c, int start); extern int RichString_findChar(RichString* this, char c, int start);
#else #else
void RichString_setAttrn(RichString* this, int attrs, int start, int finish); extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
int RichString_findChar(RichString* this, char c, int start); extern int RichString_findChar(RichString* this, char c, int start);
#endif #endif
void RichString_prune(RichString* this); extern void RichString_prune(RichString* this);
void RichString_setAttr(RichString* this, int attrs); extern void RichString_setAttr(RichString* this, int attrs);
void RichString_append(RichString* this, int attrs, const char* data); extern void RichString_append(RichString* this, int attrs, const char* data);
void RichString_appendn(RichString* this, int attrs, const char* data, int len); extern void RichString_appendn(RichString* this, int attrs, const char* data, int len);
void RichString_write(RichString* this, int attrs, const char* data); extern void RichString_write(RichString* this, int attrs, const char* data);
#endif #endif

View File

@ -145,14 +145,12 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
} }
static void ScreenManager_drawPanels(ScreenManager* this, int focus) { static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
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_draw(panel, i == focus);
if (i < nPanels) { if (this->orientation == HORIZONTAL) {
if (this->orientation == HORIZONTAL) { mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
}
} }
} }
} }
@ -165,7 +163,7 @@ static Panel* setCurrentPanel(Panel* panel) {
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false; bool quit = false;
int focus = 0; int focus = 0;
Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus)); Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
double oldTime = 0.0; double oldTime = 0.0;
@ -183,7 +181,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (this->header) { if (this->header) {
checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut); checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut);
} }
if (redraw) { if (redraw) {
ScreenManager_drawPanels(this, focus); ScreenManager_drawPanels(this, focus);
} }
@ -192,8 +190,24 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
set_escdelay(25); set_escdelay(25);
ch = getch(); ch = getch();
if (this->settings->vimMode) {
switch (ch) {
case 'h': ch = KEY_LEFT; break;
case 'j': ch = KEY_DOWN; break;
case 'k': ch = KEY_UP; break;
case 'l': ch = KEY_RIGHT; break;
case KEY_LEFT: ch = 'h'; break;
case KEY_DOWN: ch = 'j'; break;
case KEY_UP: ch = 'k'; break;
case KEY_RIGHT: ch = 'l'; break;
case 'K': ch = 'k'; break;
case 'J': ch = 'K'; break;
case 'L': ch = 'l'; break;
}
}
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
if (ch == KEY_MOUSE) { if (ch == KEY_MOUSE && this->settings->enableMouse) {
ch = ERR; ch = ERR;
MEVENT mevent; MEVENT mevent;
int ok = getmouse(&mevent); int ok = getmouse(&mevent);
@ -271,7 +285,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
quit = true; quit = true;
continue; continue;
} }
switch (ch) { switch (ch) {
case KEY_RESIZE: case KEY_RESIZE:
{ {

View File

@ -35,18 +35,18 @@ typedef struct ScreenManager_ {
} ScreenManager; } ScreenManager;
ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner); extern ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner);
void ScreenManager_delete(ScreenManager* this); extern void ScreenManager_delete(ScreenManager* this);
extern int ScreenManager_size(ScreenManager* this); extern int ScreenManager_size(ScreenManager* this);
void ScreenManager_add(ScreenManager* this, Panel* item, int size); extern void ScreenManager_add(ScreenManager* this, Panel* item, int size);
Panel* ScreenManager_remove(ScreenManager* this, int idx); extern Panel* ScreenManager_remove(ScreenManager* this, int idx);
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2); extern void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey); extern void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey);
#endif #endif

View File

@ -31,7 +31,7 @@ typedef struct {
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* filename;
MeterColumnSettings columns[2]; MeterColumnSettings columns[2];
ProcessField* fields; ProcessField* fields;
@ -45,6 +45,8 @@ typedef struct Settings_ {
bool countCPUsFromZero; bool countCPUsFromZero;
bool detailedCPUTime; bool detailedCPUTime;
bool showCPUUsage;
bool showCPUFrequency;
bool treeView; bool treeView;
bool showProgramPath; bool showProgramPath;
bool hideThreads; bool hideThreads;
@ -58,6 +60,11 @@ typedef struct Settings_ {
bool updateProcessNames; bool updateProcessNames;
bool accountGuestInCPUMeter; bool accountGuestInCPUMeter;
bool headerMargin; bool headerMargin;
bool enableMouse;
bool vimMode;
#ifdef HAVE_LIBHWLOC
bool topologyAffinity;
#endif
bool changed; bool changed;
} Settings; } Settings;
@ -114,7 +121,7 @@ static void Settings_defaultMeters(Settings* this) {
this->columns[i].modes = xCalloc(sizes[i], sizeof(int)); this->columns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i]; this->columns[i].len = sizes[i];
} }
int r = 0; int r = 0;
if (this->cpuCount > 8) { if (this->cpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2"); this->columns[0].names[0] = xStrdup("LeftCPUs2");
@ -134,7 +141,7 @@ static void Settings_defaultMeters(Settings* this) {
this->columns[0].modes[1] = BAR_METERMODE; this->columns[0].modes[1] = BAR_METERMODE;
this->columns[0].names[2] = xStrdup("Swap"); this->columns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE; this->columns[0].modes[2] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("Tasks"); this->columns[1].names[r] = xStrdup("Tasks");
this->columns[1].modes[r++] = TEXT_METERMODE; this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("LoadAverage"); this->columns[1].names[r] = xStrdup("LoadAverage");
@ -165,13 +172,13 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
static bool Settings_read(Settings* this, const char* fileName) { static bool Settings_read(Settings* this, const char* fileName) {
FILE* fd; FILE* fd;
CRT_dropPrivileges(); CRT_dropPrivileges();
fd = fopen(fileName, "r"); fd = fopen(fileName, "r");
CRT_restorePrivileges(); CRT_restorePrivileges();
if (!fd) if (!fd)
return false; return false;
bool didReadMeters = false; bool didReadMeters = false;
bool didReadFields = false; bool didReadFields = false;
for (;;) { for (;;) {
@ -223,6 +230,10 @@ static bool Settings_read(Settings* this, const char* fileName) {
this->detailedCPUTime = atoi(option[1]); this->detailedCPUTime = atoi(option[1]);
} else if (String_eq(option[0], "cpu_count_from_zero")) { } else if (String_eq(option[0], "cpu_count_from_zero")) {
this->countCPUsFromZero = atoi(option[1]); this->countCPUsFromZero = atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_usage")) {
this->showCPUUsage = atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_frequency")) {
this->showCPUFrequency = atoi(option[1]);
} else if (String_eq(option[0], "update_process_names")) { } else if (String_eq(option[0], "update_process_names")) {
this->updateProcessNames = atoi(option[1]); this->updateProcessNames = atoi(option[1]);
} else if (String_eq(option[0], "account_guest_in_cpu_meter")) { } else if (String_eq(option[0], "account_guest_in_cpu_meter")) {
@ -232,6 +243,8 @@ static bool Settings_read(Settings* this, const char* fileName) {
} else if (String_eq(option[0], "color_scheme")) { } else if (String_eq(option[0], "color_scheme")) {
this->colorScheme = atoi(option[1]); this->colorScheme = atoi(option[1]);
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0; if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0;
} else if (String_eq(option[0], "enable_mouse")) {
this->enableMouse = atoi(option[1]);
} else if (String_eq(option[0], "left_meters")) { } else if (String_eq(option[0], "left_meters")) {
Settings_readMeters(this, option[1], 0); Settings_readMeters(this, option[1], 0);
didReadMeters = true; didReadMeters = true;
@ -244,6 +257,12 @@ static bool Settings_read(Settings* this, const char* fileName) {
} else if (String_eq(option[0], "right_meter_modes")) { } else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1); Settings_readMeterModes(this, option[1], 1);
didReadMeters = true; didReadMeters = true;
} else if (String_eq(option[0], "vim_mode")) {
this->vimMode = atoi(option[1]);
#ifdef HAVE_LIBHWLOC
} else if (String_eq(option[0], "topology_affinity")) {
this->topologyAffinity = !!atoi(option[1]);
#endif
} }
String_freeArray(option); String_freeArray(option);
} }
@ -312,20 +331,27 @@ bool Settings_write(Settings* this) {
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_zero=%d\n", (int) this->countCPUsFromZero); fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero);
fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage);
fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency);
fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames); fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames);
fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter); fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter);
fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme); fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
fprintf(fd, "enable_mouse=%d\n", (int) this->enableMouse);
fprintf(fd, "delay=%d\n", (int) this->delay); fprintf(fd, "delay=%d\n", (int) this->delay);
fprintf(fd, "left_meters="); writeMeters(this, fd, 0); fprintf(fd, "left_meters="); writeMeters(this, fd, 0);
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0); fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1); fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1); fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
fprintf(fd, "vim_mode=%d\n", (int) this->vimMode);
#ifdef HAVE_LIBHWLOC
fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity);
#endif
fclose(fd); fclose(fd);
return true; return true;
} }
Settings* Settings_new(int cpuCount) { Settings* Settings_new(int cpuCount) {
Settings* this = xCalloc(1, sizeof(Settings)); Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU; this->sortKey = PERCENT_CPU;
@ -340,11 +366,16 @@ Settings* Settings_new(int cpuCount) {
this->highlightMegabytes = false; this->highlightMegabytes = false;
this->detailedCPUTime = false; this->detailedCPUTime = false;
this->countCPUsFromZero = false; this->countCPUsFromZero = false;
this->showCPUUsage = true;
this->showCPUFrequency = false;
this->updateProcessNames = false; this->updateProcessNames = false;
this->cpuCount = cpuCount; this->cpuCount = cpuCount;
this->showProgramPath = true; this->showProgramPath = true;
this->highlightThreads = true; this->highlightThreads = true;
#ifdef HAVE_LIBHWLOC
this->topologyAffinity = false;
#endif
this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField)); this->fields = xCalloc(Platform_numberOfFields+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).
@ -375,7 +406,7 @@ Settings* Settings_new(int cpuCount) {
htopDir = String_cat(home, "/.config/htop"); htopDir = String_cat(home, "/.config/htop");
} }
legacyDotfile = String_cat(home, "/.htoprc"); legacyDotfile = String_cat(home, "/.htoprc");
CRT_dropPrivileges(); CRT_dropPrivileges();
(void) mkdir(configDir, 0700); (void) mkdir(configDir, 0700);
(void) mkdir(htopDir, 0700); (void) mkdir(htopDir, 0700);
@ -390,6 +421,7 @@ Settings* Settings_new(int cpuCount) {
CRT_restorePrivileges(); CRT_restorePrivileges();
} }
this->colorScheme = 0; this->colorScheme = 0;
this->enableMouse = true;
this->changed = false; this->changed = false;
this->delay = DEFAULT_DELAY; this->delay = DEFAULT_DELAY;
bool ok = false; bool ok = false;

View File

@ -22,7 +22,7 @@ typedef struct {
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* filename;
MeterColumnSettings columns[2]; MeterColumnSettings columns[2];
ProcessField* fields; ProcessField* fields;
@ -36,6 +36,8 @@ typedef struct Settings_ {
bool countCPUsFromZero; bool countCPUsFromZero;
bool detailedCPUTime; bool detailedCPUTime;
bool showCPUUsage;
bool showCPUFrequency;
bool treeView; bool treeView;
bool showProgramPath; bool showProgramPath;
bool hideThreads; bool hideThreads;
@ -49,6 +51,11 @@ typedef struct Settings_ {
bool updateProcessNames; bool updateProcessNames;
bool accountGuestInCPUMeter; bool accountGuestInCPUMeter;
bool headerMargin; bool headerMargin;
bool enableMouse;
bool vimMode;
#ifdef HAVE_LIBHWLOC
bool topologyAffinity;
#endif
bool changed; bool changed;
} Settings; } Settings;
@ -58,12 +65,12 @@ typedef struct Settings_ {
#endif #endif
void Settings_delete(Settings* this); extern void Settings_delete(Settings* this);
bool Settings_write(Settings* this); extern bool Settings_write(Settings* this);
Settings* Settings_new(int cpuCount); extern Settings* Settings_new(int cpuCount);
void Settings_invertSortOrder(Settings* this); extern void Settings_invertSortOrder(Settings* this);
#endif #endif

View File

@ -16,6 +16,6 @@ typedef struct SignalItem_ {
} SignalItem; } SignalItem;
Panel* SignalsPanel_new(); extern Panel* SignalsPanel_new();
#endif #endif

View File

@ -30,8 +30,9 @@ char* String_cat(const char* s1, const char* s2) {
int l1 = strlen(s1); int l1 = strlen(s1);
int l2 = strlen(s2); int l2 = strlen(s2);
char* out = xMalloc(l1 + l2 + 1); char* out = xMalloc(l1 + l2 + 1);
strncpy(out, s1, l1); memcpy(out, s1, l1);
strncpy(out+l1, s2, l2+1); memcpy(out+l1, s2, l2+1);
out[l1 + l2] = '\0';
return out; return out;
} }
@ -80,10 +81,7 @@ char** String_split(const char* s, char sep, int* n) {
s += size + 1; s += size + 1;
} }
if (s[0] != '\0') { if (s[0] != '\0') {
int size = strlen(s); out[ctr] = xStrdup(s);
char* token = xMalloc(size + 1);
strncpy(token, s, size + 1);
out[ctr] = token;
ctr++; ctr++;
} }
out = xRealloc(out, sizeof(char*) * (ctr + 1)); out = xRealloc(out, sizeof(char*) * (ctr + 1));
@ -116,7 +114,7 @@ char* String_getToken(const char* line, const unsigned short int numMatch) {
if (lastState == 0 && inWord == 1) if (lastState == 0 && inWord == 1)
count++; count++;
if(inWord == 1){ if(inWord == 1){
if (count == numMatch && line[i] != ' ' && line[i] != '\0' && line[i] != '\n' && line[i] != (char)EOF) { if (count == numMatch && line[i] != ' ' && line[i] != '\0' && line[i] != '\n' && line[i] != (char)EOF) {
match[foundCount] = line[i]; match[foundCount] = line[i];

View File

@ -19,18 +19,18 @@ in the source distribution for its full text.
* at compile time (e.g. when they are immutable string literals). :) * at compile time (e.g. when they are immutable string literals). :)
*/ */
char* String_cat(const char* s1, const char* s2); extern char* String_cat(const char* s1, const char* s2);
char* String_trim(const char* in); extern char* String_trim(const char* in);
extern int String_eq(const char* s1, const char* s2); extern int String_eq(const char* s1, const char* s2);
char** String_split(const char* s, char sep, int* n); extern char** String_split(const char* s, char sep, int* n);
void String_freeArray(char** s); extern void String_freeArray(char** s);
char* String_getToken(const char* line, const unsigned short int numMatch); extern char* String_getToken(const char* line, const unsigned short int numMatch);
char* String_readLine(FILE* fd); extern char* String_readLine(FILE* fd);
#endif #endif

View File

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

View File

@ -15,7 +15,7 @@ in the source distribution for its full text.
}*/ }*/
int TasksMeter_attributes[] = { int TasksMeter_attributes[] = {
CPU_KERNEL, PROCESS_THREAD, PROCESS, TASKS_RUNNING CPU_SYSTEM, PROCESS_THREAD, PROCESS, TASKS_RUNNING
}; };
static void TasksMeter_updateValues(Meter* this, char* buffer, int len) { static void TasksMeter_updateValues(Meter* this, char* buffer, int len) {
@ -37,9 +37,9 @@ static void TasksMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast; Meter* this = (Meter*)cast;
Settings* settings = this->pl->settings; Settings* settings = this->pl->settings;
char buffer[20]; char buffer[20];
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_write(out, CRT_colors[METER_VALUE], buffer);
int threadValueColor = CRT_colors[METER_VALUE]; int threadValueColor = CRT_colors[METER_VALUE];
@ -76,7 +76,7 @@ MeterClass TasksMeter_class = {
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 4, .maxItems = 4,
.total = 100.0, .total = 100.0,
.attributes = TasksMeter_attributes, .attributes = TasksMeter_attributes,
.name = "Tasks", .name = "Tasks",
.uiName = "Task counter", .uiName = "Task counter",
.caption = "Tasks: " .caption = "Tasks: "

View File

@ -101,7 +101,7 @@ bool TraceScreen_forkTracer(TraceScreen* this) {
int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK); int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK);
if (ok != -1) { if (ok != -1) {
xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid); xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid);
execlp("strace", "strace", "-s", "512", "-p", buffer, NULL); execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, NULL);
} }
const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH."; const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH.";
ssize_t written = write(this->fdpair[1], message, strlen(message)); ssize_t written = write(this->fdpair[1], message, strlen(message));

View File

@ -25,16 +25,16 @@ typedef struct TraceScreen_ {
extern InfoScreenClass TraceScreen_class; extern InfoScreenClass TraceScreen_class;
TraceScreen* TraceScreen_new(Process* process); extern TraceScreen* TraceScreen_new(Process* process);
void TraceScreen_delete(Object* cast); extern void TraceScreen_delete(Object* cast);
void TraceScreen_draw(InfoScreen* this); extern void TraceScreen_draw(InfoScreen* this);
bool TraceScreen_forkTracer(TraceScreen* this); extern bool TraceScreen_forkTracer(TraceScreen* this);
void TraceScreen_updateTrace(InfoScreen* super); extern void TraceScreen_updateTrace(InfoScreen* super);
bool TraceScreen_onKey(InfoScreen* super, int ch); extern bool TraceScreen_onKey(InfoScreen* super, int ch);
#endif #endif

View File

@ -15,11 +15,11 @@ typedef struct UsersTable_ {
Hashtable* users; Hashtable* users;
} UsersTable; } UsersTable;
UsersTable* UsersTable_new(); extern UsersTable* UsersTable_new();
void UsersTable_delete(UsersTable* this); extern void UsersTable_delete(UsersTable* this);
char* UsersTable_getRef(UsersTable* this, unsigned int uid); extern char* UsersTable_getRef(UsersTable* this, unsigned int uid);
extern void UsersTable_foreach(UsersTable* this, Hashtable_PairFunction f, void* userData); extern void UsersTable_foreach(UsersTable* this, Hashtable_PairFunction f, void* userData);

View File

@ -195,7 +195,7 @@ void Vector_insert(Vector* this, int idx, void* data_) {
if (idx > this->items) { if (idx > this->items) {
idx = this->items; idx = this->items;
} }
Vector_checkArraySize(this); Vector_checkArraySize(this);
//assert(this->array[this->items] == NULL); //assert(this->array[this->items] == NULL);
for (int i = this->items; i > idx; i--) { for (int i = this->items; i > idx; i--) {
@ -302,7 +302,7 @@ inline int Vector_size(Vector* this) {
static void Vector_merge(Vector* this, Vector* v2) { static void Vector_merge(Vector* this, Vector* v2) {
int i; int i;
assert(Vector_isConsistent(this)); assert(Vector_isConsistent(this));
for (i = 0; i < v2->items; i++) for (i = 0; i < v2->items; i++)
Vector_add(this, v2->array[i]); Vector_add(this, v2->array[i]);
v2->items = 0; v2->items = 0;
@ -335,3 +335,15 @@ inline int Vector_indexOf(Vector* this, void* search_, Object_Compare compare) {
} }
return -1; return -1;
} }
void Vector_splice(Vector* this, Vector* from) {
assert(Vector_isConsistent(this));
assert(Vector_isConsistent(from));
assert(!(this->owner && from->owner));
int olditmes = this->items;
this->items += from->items;
Vector_checkArraySize(this);
for (int j = 0; j < from->items; j++)
this->array[olditmes + j] = from->array[j];
}

View File

@ -27,38 +27,38 @@ typedef struct Vector_ {
} Vector; } Vector;
Vector* Vector_new(ObjectClass* type, bool owner, int size); extern Vector* Vector_new(ObjectClass* type, bool owner, int size);
void Vector_delete(Vector* this); extern void Vector_delete(Vector* this);
#ifdef DEBUG #ifdef DEBUG
int Vector_count(Vector* this); extern int Vector_count(Vector* this);
#endif #endif
void Vector_prune(Vector* this); extern void Vector_prune(Vector* this);
// If I were to use only one sorting algorithm for both cases, it would probably be this one: // If I were to use only one sorting algorithm for both cases, it would probably be this one:
/* /*
*/ */
void Vector_quickSort(Vector* this); extern void Vector_quickSort(Vector* this);
void Vector_insertionSort(Vector* this); extern void Vector_insertionSort(Vector* this);
void Vector_insert(Vector* this, int idx, void* data_); extern void Vector_insert(Vector* this, int idx, void* data_);
Object* Vector_take(Vector* this, int idx); extern Object* Vector_take(Vector* this, int idx);
Object* Vector_remove(Vector* this, int idx); extern Object* Vector_remove(Vector* this, int idx);
void Vector_moveUp(Vector* this, int idx); extern void Vector_moveUp(Vector* this, int idx);
void Vector_moveDown(Vector* this, int idx); extern void Vector_moveDown(Vector* this, int idx);
void Vector_set(Vector* this, int idx, void* data_); extern void Vector_set(Vector* this, int idx, void* data_);
#ifdef DEBUG #ifdef DEBUG
@ -84,8 +84,10 @@ extern int Vector_size(Vector* this);
*/ */
void Vector_add(Vector* this, void* data_); extern void Vector_add(Vector* this, void* data_);
extern int Vector_indexOf(Vector* this, void* search_, Object_Compare compare); extern int Vector_indexOf(Vector* this, void* search_, Object_Compare compare);
void Vector_splice(Vector* this, Vector* from);
#endif #endif

View File

@ -14,7 +14,7 @@
#include <stdlib.h> #include <stdlib.h>
}*/ }*/
static inline void fail() { void fail() {
curs_set(1); curs_set(1);
endwin(); endwin();
err(1, NULL); err(1, NULL);
@ -44,6 +44,10 @@ void* xRealloc(void* ptr, size_t size) {
return data; return data;
} }
#undef xAsprintf
#define xAsprintf(strp, fmt, ...) do { int _r=asprintf(strp, fmt, __VA_ARGS__); if (_r < 0) { fail(); } } while(0)
#define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0) #define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0)
#undef xStrdup #undef xStrdup

View File

@ -11,11 +11,17 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
void* xMalloc(size_t size); extern void fail(void);
void* xCalloc(size_t nmemb, size_t size); extern void* xMalloc(size_t size);
void* xRealloc(void* ptr, size_t size); extern void* xCalloc(size_t nmemb, size_t size);
extern void* xRealloc(void* ptr, size_t size);
#undef xAsprintf
#define xAsprintf(strp, fmt, ...) do { int _r=asprintf(strp, fmt, __VA_ARGS__); if (_r < 0) { fail(); } } while(0)
#define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0) #define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0)
@ -32,9 +38,9 @@ void* xRealloc(void* ptr, size_t size);
#endif #endif
#if (__has_attribute(nonnull) || \ #if (__has_attribute(nonnull) || \
((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))) ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)))
char* xStrdup_(const char* str) __attribute__((nonnull)); extern char* xStrdup_(const char* str) __attribute__((nonnull));
#endif // __has_attribute(nonnull) || GNU C 3.3 or later #endif // __has_attribute(nonnull) || GNU C 3.3 or later
char* xStrdup_(const char* str); extern char* xStrdup_(const char* str);
#endif #endif

View File

@ -2,10 +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],[2.2.0],[hisham@gobolinux.org]) AC_INIT([htop],[3.0.1],[htop@groups.io])
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}"
year=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u "+%Y")
AC_CONFIG_SRCDIR([htop.c]) AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_AUX_DIR([.]) AC_CONFIG_AUX_DIR([.])
@ -69,7 +66,7 @@ dnl glibc 2.25 deprecates 'major' and 'minor' in <sys/types.h> and requires to
dnl include <sys/sysmacros.h>. However the logic in AC_HEADER_MAJOR has not yet dnl include <sys/sysmacros.h>. However the logic in AC_HEADER_MAJOR has not yet
dnl been updated in Autoconf 2.69, so use a workaround: dnl been updated in Autoconf 2.69, so use a workaround:
m4_version_prereq([2.70], [], m4_version_prereq([2.70], [],
[if test "x$ac_cv_header_sys_mkdev_h" = xno; then [if test "x$ac_cv_header_sys_mkdev_h" != xyes; then
AC_CHECK_HEADER(sys/sysmacros.h, [AC_DEFINE(MAJOR_IN_SYSMACROS, 1, AC_CHECK_HEADER(sys/sysmacros.h, [AC_DEFINE(MAJOR_IN_SYSMACROS, 1,
[Define to 1 if `major', `minor', and `makedev' are declared in <sys/sysmacros.h>.])]) [Define to 1 if `major', `minor', and `makedev' are declared in <sys/sysmacros.h>.])])
fi]) fi])
@ -114,20 +111,12 @@ AC_SUBST(wextra_flag)
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
PROCDIR=/proc PROCDIR=/proc
AC_ARG_ENABLE(proc, [AS_HELP_STRING([--enable-proc], [use Linux-compatible proc filesystem])], enable_proc="yes", enable_proc="no")
if test "x$enable_proc" = xyes; then
# An enabled proc assumes we're emulating Linux.
my_htop_platform=linux
AC_DEFINE(HAVE_PROC, 1, [Define if using a Linux-compatible proc filesystem.])
fi
AC_ARG_WITH(proc, [AS_HELP_STRING([--with-proc=DIR], [Location of a Linux-compatible proc filesystem (default=/proc).])], AC_ARG_WITH(proc, [AS_HELP_STRING([--with-proc=DIR], [Location of a Linux-compatible proc filesystem (default=/proc).])],
if test -n "$withval"; then
if test -n "$withval"; then AC_DEFINE_UNQUOTED(PROCDIR, "$withval", [Path of proc filesystem])
AC_DEFINE_UNQUOTED(PROCDIR, "$withval", [Path of proc filesystem]) PROCDIR="$withval"
PROCDIR="$withval" fi,
fi, AC_DEFINE(PROCDIR, "/proc", [Path of proc filesystem]))
AC_DEFINE(PROCDIR, "/proc", [Path of proc filesystem]))
if test "x$cross_compiling" = xno; then if test "x$cross_compiling" = xno; then
if test "x$enable_proc" = xyes; then if test "x$enable_proc" = xyes; then
@ -226,7 +215,7 @@ else
HTOP_CHECK_LIB([ncurses], [refresh], [HAVE_LIBNCURSES], HTOP_CHECK_LIB([ncurses], [refresh], [HAVE_LIBNCURSES],
missing_libraries="$missing_libraries libncurses" missing_libraries="$missing_libraries libncurses"
)))) ))))
AC_CHECK_HEADERS([curses.h],[:], AC_CHECK_HEADERS([curses.h],[:],
[AC_CHECK_HEADERS([ncurses/curses.h],[:], [AC_CHECK_HEADERS([ncurses/curses.h],[:],
[AC_CHECK_HEADERS([ncurses/ncurses.h],[:], [AC_CHECK_HEADERS([ncurses/ncurses.h],[:],
@ -292,10 +281,18 @@ then
LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS" LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS"
AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.]) AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.])
], [ ], [
AC_MSG_ERROR([htop on Linux requires pkg-config for checking delayacct requirements. Please install pkg-config and run ./autogen.sh to rebuild the configure script.]) pkg_m4_absent=1
m4_warning([configure is generated without pkg.m4. 'make dist' target will be disabled.])
AC_MSG_ERROR([htop on Linux requires pkg-config for checking delayacct requirements. Please install pkg-config and run ./autogen.sh to rebuild the configure script.])
]) ])
fi fi
AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], [Treat warnings as errors (default: warnings are not errors)])], [enable_werror="$enableval"], [enable_werror=no])
AS_IF([test "x$enable_werror" = "xyes"], [AM_CFLAGS="$AM_CFLAGS -Werror"])
AC_SUBST([AM_CFLAGS])
AC_CHECK_PROGS(PYTHON, [python python3 python2])
AC_SUBST(PYTHON)
# Bail out on errors. # Bail out on errors.
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
@ -306,7 +303,7 @@ 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-$year Hisham Muhammad", [Copyright message.]) AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2018 Hisham Muhammad", [Copyright message.])
# We're done, let's go! # We're done, let's go!
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
@ -319,6 +316,7 @@ AM_CONDITIONAL([HTOP_SOLARIS], [test "$my_htop_platform" = solaris])
AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported]) AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported])
AC_SUBST(my_htop_platform) AC_SUBST(my_htop_platform)
AC_CONFIG_FILES([Makefile htop.1]) AC_CONFIG_FILES([Makefile htop.1])
AC_CONFIG_FILES([scripts/MakeHeader.py], [chmod +x scripts/MakeHeader.py])
AC_OUTPUT AC_OUTPUT
if test "$my_htop_platform" = "unsupported" if test "$my_htop_platform" = "unsupported"
@ -334,3 +332,20 @@ then
echo "****************************************************************" echo "****************************************************************"
echo "" echo ""
fi fi
AC_MSG_RESULT([
${PACKAGE_NAME} ${VERSION}
platform: $my_htop_platform
proc directory: $PROCDIR
openvz: $enable_openvz
cgroup: $enable_cgroup
vserver: $enable_vserver
ancient vserver: $enable_ancient_vserver
taskstats: $enable_taskstats
unicode: $enable_unicode
linux affinity: $enable_linux_affinity
hwlock: $enable_hwloc
setuid: $enable_setuid
linux delay accounting: $enable_delayacct
])

View File

@ -72,4 +72,3 @@ void Battery_getData(double* level, ACPresence* isOnAC) {
CFRelease(power_sources); CFRelease(power_sources);
} }
} }

View File

@ -15,7 +15,7 @@ void CRT_handleSIGSEGV(int sgn) {
(void) sgn; (void) sgn;
CRT_done(); CRT_done();
#ifdef __APPLE__ #ifdef __APPLE__
fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at http://hisham.hm/htop\n"); fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at https://htop.dev\n");
#ifdef HAVE_EXECINFO_H #ifdef HAVE_EXECINFO_H
size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *)); size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
fprintf(stderr, "\n Please include in your report the following backtrace: \n"); fprintf(stderr, "\n Please include in your report the following backtrace: \n");
@ -32,4 +32,3 @@ void CRT_handleSIGSEGV(int sgn) {
#endif #endif
abort(); abort();
} }

View File

@ -212,7 +212,7 @@ ERROR_B:
ERROR_A: ERROR_A:
retval = xStrdup(k->kp_proc.p_comm); retval = xStrdup(k->kp_proc.p_comm);
*basenameOffset = strlen(retval); *basenameOffset = strlen(retval);
return retval; return retval;
} }
@ -302,11 +302,11 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList
void DarwinProcess_scanThreads(DarwinProcess *dp) { void DarwinProcess_scanThreads(DarwinProcess *dp) {
Process* proc = (Process*) dp; Process* proc = (Process*) dp;
kern_return_t ret; kern_return_t ret;
if (!dp->taskAccess) { if (!dp->taskAccess) {
return; return;
} }
if (proc->state == 'Z') { if (proc->state == 'Z') {
return; return;
} }
@ -317,7 +317,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) {
dp->taskAccess = false; dp->taskAccess = false;
return; return;
} }
task_info_data_t tinfo; task_info_data_t tinfo;
mach_msg_type_number_t task_info_count = TASK_INFO_MAX; mach_msg_type_number_t task_info_count = TASK_INFO_MAX;
ret = task_info(port, TASK_BASIC_INFO, (task_info_t) tinfo, &task_info_count); ret = task_info(port, TASK_BASIC_INFO, (task_info_t) tinfo, &task_info_count);
@ -325,7 +325,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) {
dp->taskAccess = false; dp->taskAccess = false;
return; return;
} }
thread_array_t thread_list; thread_array_t thread_list;
mach_msg_type_number_t thread_count; mach_msg_type_number_t thread_count;
ret = task_threads(port, &thread_list, &thread_count); ret = task_threads(port, &thread_list, &thread_count);
@ -334,7 +334,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) {
mach_port_deallocate(mach_task_self(), port); mach_port_deallocate(mach_task_self(), port);
return; return;
} }
integer_t run_state = 999; integer_t run_state = 999;
for (unsigned int i = 0; i < thread_count; i++) { for (unsigned int i = 0; i < thread_count; i++) {
thread_info_data_t thinfo; thread_info_data_t thinfo;

View File

@ -9,6 +9,8 @@ in the source distribution for its full text.
#include "DarwinProcess.h" #include "DarwinProcess.h"
#include "DarwinProcessList.h" #include "DarwinProcessList.h"
#include "CRT.h" #include "CRT.h"
#include "zfs/ZfsArcStats.h"
#include "zfs/openzfs_sysctl.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -54,6 +56,7 @@ int CompareKernelVersion(short int major, short int minor, short int component)
/*{ /*{
#include "ProcessList.h" #include "ProcessList.h"
#include "zfs/ZfsArcStats.h"
#include <mach/mach_host.h> #include <mach/mach_host.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
@ -67,6 +70,8 @@ typedef struct DarwinProcessList_ {
uint64_t kernel_threads; uint64_t kernel_threads;
uint64_t user_threads; uint64_t user_threads;
uint64_t global_diff; uint64_t global_diff;
ZfsArcStats zfs;
} DarwinProcessList; } DarwinProcessList;
}*/ }*/
@ -84,9 +89,8 @@ void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *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\n");
} }
*p = NULL;
} }
*p = NULL;
} }
unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) { unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
@ -132,8 +136,8 @@ struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) {
return processes; return processes;
} }
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
size_t len;
DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList));
ProcessList_init(&this->super, Class(Process), usersTable, pidWhiteList, userId); ProcessList_init(&this->super, Class(Process), usersTable, pidWhiteList, userId);
@ -146,6 +150,10 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
/* Initialize the VM statistics */ /* Initialize the VM statistics */
ProcessList_getVMStats(&this->vm_stats); ProcessList_getVMStats(&this->vm_stats);
/* Initialize the ZFS kstats, if zfs.kext loaded */
openzfs_sysctl_init(&this->zfs);
openzfs_sysctl_updateArcStats(&this->zfs);
this->super.kernelThreads = 0; this->super.kernelThreads = 0;
this->super.userlandThreads = 0; this->super.userlandThreads = 0;
this->super.totalTasks = 0; this->super.totalTasks = 0;
@ -174,6 +182,7 @@ void ProcessList_goThroughEntries(ProcessList* super) {
dpl->prev_load = dpl->curr_load; dpl->prev_load = dpl->curr_load;
ProcessList_allocateCPULoadInfo(&dpl->curr_load); ProcessList_allocateCPULoadInfo(&dpl->curr_load);
ProcessList_getVMStats(&dpl->vm_stats); ProcessList_getVMStats(&dpl->vm_stats);
openzfs_sysctl_updateArcStats(&dpl->zfs);
/* Get the time difference */ /* Get the time difference */
dpl->global_diff = 0; dpl->global_diff = 0;

View File

@ -9,7 +9,19 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
struct kern;
void GetKernelVersion(struct kern *k);
/* compare the given os version with the one installed returns:
0 if equals the installed version
positive value if less than the installed version
negative value if more than the installed version
*/
int CompareKernelVersion(short int major, short int minor, short int component);
#include "ProcessList.h" #include "ProcessList.h"
#include "zfs/ZfsArcStats.h"
#include <mach/mach_host.h> #include <mach/mach_host.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
@ -23,6 +35,8 @@ typedef struct DarwinProcessList_ {
uint64_t kernel_threads; uint64_t kernel_threads;
uint64_t user_threads; uint64_t user_threads;
uint64_t global_diff; uint64_t global_diff;
ZfsArcStats zfs;
} DarwinProcessList; } DarwinProcessList;

View File

@ -15,6 +15,8 @@ in the source distribution for its full text.
#include "ClockMeter.h" #include "ClockMeter.h"
#include "HostnameMeter.h" #include "HostnameMeter.h"
#include "UptimeMeter.h" #include "UptimeMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#include "DarwinProcessList.h" #include "DarwinProcessList.h"
#include <stdlib.h> #include <stdlib.h>
@ -90,7 +92,7 @@ ProcessFieldData Process_fields[] = {
[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_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_SIZE] = { .name = "M_SIZE", .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, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .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_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, }, [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, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
@ -117,6 +119,8 @@ MeterClass* Platform_meterTypes[] = {
&RightCPUsMeter_class, &RightCPUsMeter_class,
&LeftCPUs2Meter_class, &LeftCPUs2Meter_class,
&RightCPUs2Meter_class, &RightCPUs2Meter_class,
&ZfsArcMeter_class,
&ZfsCompressedArcMeter_class,
&BlankMeter_class, &BlankMeter_class,
NULL NULL
}; };
@ -217,6 +221,8 @@ double Platform_setCPUValues(Meter* mtr, int cpu) {
/* Convert to percent and return */ /* Convert to percent and return */
total = mtr->values[CPU_METER_NICE] + mtr->values[CPU_METER_NORMAL] + mtr->values[CPU_METER_KERNEL]; total = mtr->values[CPU_METER_NICE] + mtr->values[CPU_METER_NORMAL] + mtr->values[CPU_METER_KERNEL];
mtr->values[CPU_METER_FREQUENCY] = -1;
return CLAMP(total, 0.0, 100.0); return CLAMP(total, 0.0, 100.0);
} }
@ -241,6 +247,18 @@ void Platform_setSwapValues(Meter* mtr) {
mtr->values[0] = swapused.xsu_used / 1024; mtr->values[0] = swapused.xsu_used / 1024;
} }
void Platform_setZfsArcValues(Meter* this) {
DarwinProcessList* dpl = (DarwinProcessList*) this->pl;
ZfsArcMeter_readStats(this, &(dpl->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
DarwinProcessList* dpl = (DarwinProcessList*) this->pl;
ZfsCompressedArcMeter_readStats(this, &(dpl->zfs));
}
char* Platform_getProcessEnv(pid_t pid) { char* Platform_getProcessEnv(pid_t pid) {
char* env = NULL; char* env = NULL;

View File

@ -48,6 +48,10 @@ void Platform_setMemoryValues(Meter* mtr);
void Platform_setSwapValues(Meter* mtr); void Platform_setSwapValues(Meter* mtr);
void Platform_setZfsArcValues(Meter* this);
void Platform_setZfsCompressedArcValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid); char* Platform_getProcessEnv(pid_t pid);
#endif #endif

View File

@ -17,7 +17,7 @@ in the source distribution for its full text.
void CRT_handleSIGSEGV(int sgn) { void CRT_handleSIGSEGV(int sgn) {
(void) sgn; (void) sgn;
CRT_done(); CRT_done();
fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at http://hisham.hm/htop\n"); fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at https://htop.dev\n");
#ifdef HAVE_EXECINFO_H #ifdef HAVE_EXECINFO_H
size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *)); size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
fprintf(stderr, "\n Please include in your report the following backtrace: \n"); fprintf(stderr, "\n Please include in your report the following backtrace: \n");
@ -32,4 +32,3 @@ void CRT_handleSIGSEGV(int sgn) {
#endif #endif
abort(); abort();
} }

View File

@ -74,7 +74,7 @@ ProcessFieldData Process_fields[] = {
[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_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_SIZE] = { .name = "M_SIZE", .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, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .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_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, }, [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, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
@ -121,7 +121,7 @@ void DragonFlyBSDProcess_writeField(Process* this, RichString* str, ProcessField
case PID: xSnprintf(buffer, n, Process_pidFormat, (fp->kernel ? -1 : this->pid)); break; case PID: xSnprintf(buffer, n, Process_pidFormat, (fp->kernel ? -1 : this->pid)); break;
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break; case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
case JAIL:{ case JAIL:{
xSnprintf(buffer, n, "%-11s ", fp->jname); break; xSnprintf(buffer, n, "%-11s ", fp->jname);
if (buffer[11] != '\0') { if (buffer[11] != '\0') {
buffer[11] = ' '; buffer[11] = ' ';
buffer[12] = '\0'; buffer[12] = '\0';

View File

@ -565,7 +565,7 @@ void ProcessList_goThroughEntries(ProcessList* this) {
if (kproc->kp_flags & P_JAILED) { if (kproc->kp_flags & P_JAILED) {
proc->state = 'J'; proc->state = 'J';
} }
if (Process_isKernelThread(dfp)) { if (Process_isKernelThread(dfp)) {
this->kernelThreads++; this->kernelThreads++;
} }

View File

@ -179,6 +179,9 @@ double Platform_setCPUValues(Meter* this, int cpu) {
percent = CLAMP(percent, 0.0, 100.0); percent = CLAMP(percent, 0.0, 100.0);
if (isnan(percent)) percent = 0.0; if (isnan(percent)) percent = 0.0;
v[CPU_METER_FREQUENCY] = -1;
return percent; return percent;
} }

View File

@ -18,4 +18,3 @@ void CRT_handleSIGSEGV(int sgn) {
fprintf(stderr, "\nPlease contact your platform package maintainer!\n\n"); fprintf(stderr, "\nPlease contact your platform package maintainer!\n\n");
abort(); abort();
} }

View File

@ -73,7 +73,7 @@ ProcessFieldData Process_fields[] = {
[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_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_SIZE] = { .name = "M_SIZE", .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, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .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_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, }, [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, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
@ -119,7 +119,7 @@ void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField fiel
// 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, Process_pidFormat, fp->jid); break;
case JAIL:{ case JAIL:{
xSnprintf(buffer, n, "%-11s ", fp->jname); break; xSnprintf(buffer, n, "%-11s ", fp->jname);
if (buffer[11] != '\0') { if (buffer[11] != '\0') {
buffer[11] = ' '; buffer[11] = ' ';
buffer[12] = '\0'; buffer[12] = '\0';

View File

@ -8,6 +8,8 @@ in the source distribution for its full text.
#include "ProcessList.h" #include "ProcessList.h"
#include "FreeBSDProcessList.h" #include "FreeBSDProcessList.h"
#include "FreeBSDProcess.h" #include "FreeBSDProcess.h"
#include "zfs/ZfsArcStats.h"
#include "zfs/openzfs_sysctl.h"
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
@ -18,9 +20,12 @@ in the source distribution for its full text.
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <string.h> #include <string.h>
#include <time.h>
/*{ /*{
#include "zfs/ZfsArcStats.h"
#include <kvm.h> #include <kvm.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/jail.h> #include <sys/jail.h>
@ -45,14 +50,12 @@ typedef struct FreeBSDProcessList_ {
ProcessList super; ProcessList super;
kvm_t* kd; kvm_t* kd;
int zfsArcEnabled;
unsigned long long int memWire; unsigned long long int memWire;
unsigned long long int memActive; unsigned long long int memActive;
unsigned long long int memInactive; unsigned long long int memInactive;
unsigned long long int memFree; unsigned long long int memFree;
unsigned long long int memZfsArc;
ZfsArcStats zfs;
CPUData* cpus; CPUData* cpus;
@ -80,8 +83,6 @@ static int MIB_vm_stats_vm_v_free_count[4];
static int MIB_vfs_bufspace[2]; static int MIB_vfs_bufspace[2];
static int MIB_kstat_zfs_misc_arcstats_size[5];
static int MIB_kern_cp_time[2]; static int MIB_kern_cp_time[2];
static int MIB_kern_cp_times[2]; static int MIB_kern_cp_times[2];
static int kernelFScale; static int kernelFScale;
@ -118,15 +119,8 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len);
len = sizeof(fpl->memZfsArc); openzfs_sysctl_init(&fpl->zfs);
if (sysctlbyname("kstat.zfs.misc.arcstats.size", &fpl->memZfsArc, &len, openzfs_sysctl_updateArcStats(&fpl->zfs);
NULL, 0) == 0 && fpl->memZfsArc != 0) {
sysctlnametomib("kstat.zfs.misc.arcstats.size", MIB_kstat_zfs_misc_arcstats_size, &len);
fpl->zfsArcEnabled = 1;
} else {
fpl->zfsArcEnabled = 0;
}
int smp = 0; int smp = 0;
len = sizeof(smp); len = sizeof(smp);
@ -296,35 +290,44 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) {
// //
// htop_used = active + (wired - arc) // htop_used = active + (wired - arc)
// htop_cache = buffers + cache + arc // htop_cache = buffers + cache + arc
size_t len = sizeof(pl->totalMem); u_long totalMem;
u_int memActive, memWire, cachedMem;
long buffersMem;
uint64_t memZfsArc;
size_t len;
//disabled for now, as it is always smaller than phycal amount of memory... //disabled for now, as it is always smaller than phycal amount of memory...
//...to avoid "where is my memory?" questions //...to avoid "where is my memory?" questions
//sysctl(MIB_vm_stats_vm_v_page_count, 4, &(pl->totalMem), &len, NULL, 0); //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(pl->totalMem), &len, NULL, 0);
//pl->totalMem *= pageSizeKb; //pl->totalMem *= pageSizeKb;
sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0); len = sizeof(totalMem);
pl->totalMem /= 1024; sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0);
totalMem /= 1024;
pl->totalMem = totalMem;
sysctl(MIB_vm_stats_vm_v_active_count, 4, &(fpl->memActive), &len, NULL, 0); len = sizeof(memActive);
fpl->memActive *= pageSizeKb; sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0);
memActive *= pageSizeKb;
fpl->memActive = memActive;
sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(fpl->memWire), &len, NULL, 0); len = sizeof(memWire);
fpl->memWire *= pageSizeKb; sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(memWire), &len, NULL, 0);
memWire *= pageSizeKb;
fpl->memWire = memWire;
sysctl(MIB_vfs_bufspace, 2, &(pl->buffersMem), &len, NULL, 0); len = sizeof(buffersMem);
pl->buffersMem /= 1024; sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0);
buffersMem /= 1024;
pl->buffersMem = buffersMem;
sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0); len = sizeof(cachedMem);
pl->cachedMem *= pageSizeKb; sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(cachedMem), &len, NULL, 0);
cachedMem *= pageSizeKb;
pl->cachedMem = cachedMem;
if (fpl->zfsArcEnabled) { if (fpl->zfs.enabled) {
len = sizeof(fpl->memZfsArc); fpl->memWire -= fpl->zfs.size;
sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(fpl->memZfsArc), &len , NULL, 0); pl->cachedMem += fpl->zfs.size;
fpl->memZfsArc /= 1024;
fpl->memWire -= fpl->memZfsArc;
pl->cachedMem += fpl->memZfsArc;
// maybe when we learn how to make custom memory meter
// we could do custom arc breakdown?
} }
pl->usedMem = fpl->memActive + fpl->memWire; pl->usedMem = fpl->memActive + fpl->memWire;
@ -422,6 +425,7 @@ void ProcessList_goThroughEntries(ProcessList* this) {
bool hideKernelThreads = settings->hideKernelThreads; bool hideKernelThreads = settings->hideKernelThreads;
bool hideUserlandThreads = settings->hideUserlandThreads; bool hideUserlandThreads = settings->hideUserlandThreads;
openzfs_sysctl_updateArcStats(&fpl->zfs);
FreeBSDProcessList_scanMemoryInfo(this); FreeBSDProcessList_scanMemoryInfo(this);
FreeBSDProcessList_scanCPUTime(this); FreeBSDProcessList_scanCPUTime(this);
@ -429,10 +433,14 @@ void ProcessList_goThroughEntries(ProcessList* this) {
int count = 0; int count = 0;
struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_PROC, 0, &count); struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_PROC, 0, &count);
struct timeval tv;
gettimeofday(&tv, NULL);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
struct kinfo_proc* kproc = &kprocs[i]; struct kinfo_proc* kproc = &kprocs[i];
bool preExisting = false; bool preExisting = false;
bool isIdleProcess = false; bool isIdleProcess = false;
struct tm date;
Process* proc = ProcessList_getProcess(this, kproc->ki_pid, &preExisting, (Process_New) FreeBSDProcess_new); Process* proc = ProcessList_getProcess(this, kproc->ki_pid, &preExisting, (Process_New) FreeBSDProcess_new);
FreeBSDProcess* fp = (FreeBSDProcess*) proc; FreeBSDProcess* fp = (FreeBSDProcess*) proc;
@ -523,6 +531,9 @@ void ProcessList_goThroughEntries(ProcessList* this) {
this->kernelThreads++; this->kernelThreads++;
} }
(void) localtime_r((time_t*) &proc->starttime_ctime, &date);
strftime(proc->starttime_show, 7, ((proc->starttime_ctime > tv.tv_sec - 86400) ? "%R " : "%b%d "), &date);
this->totalTasks++; this->totalTasks++;
if (proc->state == 'R') if (proc->state == 'R')
this->runningTasks++; this->runningTasks++;

View File

@ -10,6 +10,8 @@ in the source distribution for its full text.
*/ */
#include "zfs/ZfsArcStats.h"
#include <kvm.h> #include <kvm.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/jail.h> #include <sys/jail.h>
@ -34,14 +36,12 @@ typedef struct FreeBSDProcessList_ {
ProcessList super; ProcessList super;
kvm_t* kd; kvm_t* kd;
int zfsArcEnabled;
unsigned long long int memWire; unsigned long long int memWire;
unsigned long long int memActive; unsigned long long int memActive;
unsigned long long int memInactive; unsigned long long int memInactive;
unsigned long long int memFree; unsigned long long int memFree;
unsigned long long int memZfsArc;
ZfsArcStats zfs;
CPUData* cpus; CPUData* cpus;

View File

@ -15,6 +15,8 @@ in the source distribution for its full text.
#include "UptimeMeter.h" #include "UptimeMeter.h"
#include "ClockMeter.h" #include "ClockMeter.h"
#include "HostnameMeter.h" #include "HostnameMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#include "FreeBSDProcess.h" #include "FreeBSDProcess.h"
#include "FreeBSDProcessList.h" #include "FreeBSDProcessList.h"
@ -104,6 +106,8 @@ MeterClass* Platform_meterTypes[] = {
&LeftCPUs2Meter_class, &LeftCPUs2Meter_class,
&RightCPUs2Meter_class, &RightCPUs2Meter_class,
&BlankMeter_class, &BlankMeter_class,
&ZfsArcMeter_class,
&ZfsCompressedArcMeter_class,
NULL NULL
}; };
@ -178,6 +182,9 @@ double Platform_setCPUValues(Meter* this, int cpu) {
percent = CLAMP(percent, 0.0, 100.0); percent = CLAMP(percent, 0.0, 100.0);
if (isnan(percent)) percent = 0.0; if (isnan(percent)) percent = 0.0;
v[CPU_METER_FREQUENCY] = -1;
return percent; return percent;
} }
@ -197,6 +204,18 @@ void Platform_setSwapValues(Meter* this) {
this->values[0] = pl->usedSwap; this->values[0] = pl->usedSwap;
} }
void Platform_setZfsArcValues(Meter* this) {
FreeBSDProcessList* fpl = (FreeBSDProcessList*) this->pl;
ZfsArcMeter_readStats(this, &(fpl->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
FreeBSDProcessList* fpl = (FreeBSDProcessList*) this->pl;
ZfsCompressedArcMeter_readStats(this, &(fpl->zfs));
}
void Platform_setTasksValues(Meter* this) { void Platform_setTasksValues(Meter* this) {
// TODO // TODO
} }

View File

@ -44,6 +44,10 @@ void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this); void Platform_setSwapValues(Meter* this);
void Platform_setZfsArcValues(Meter* this);
void Platform_setZfsCompressedArcValues(Meter* this);
void Platform_setTasksValues(Meter* this); void Platform_setTasksValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid); char* Platform_getProcessEnv(pid_t pid);

127
htop.1.in
View File

@ -1,31 +1,40 @@
.TH "HTOP" "1" "2015" "@PACKAGE_STRING@" "Utils" .TH "HTOP" "1" "2020" "@PACKAGE_STRING@" "User Commands"
.SH "NAME" .SH "NAME"
htop \- interactive process viewer htop \- interactive process viewer
.SH "SYNOPSIS" .SH "SYNOPSIS"
.LP
.B htop [\fI\-dChustv\fR]
.SH "DESCRIPTION"
.LP
Htop is a free (GPL) ncurses-based process viewer for Linux.
.LP .LP
It is similar to top, but allows you to scroll vertically and horizontally, .B htop
so you can see all the processes running on the system, along with their full .RB [ \-dChpustv ]
command lines, as well as viewing them as a process tree, selecting multiple .SH "DESCRIPTION"
processes and acting on them all at once. .LP
.B htop
is a cross-platform ncurses-based process viewer.
.LP
It is similar to
.BR top ,
but allows you to scroll vertically and horizontally, and interact using
a pointing device (mouse).
You can observe all processes running on the system, along with their
command line arguments, as well as view them in a tree format, select
multiple processes and acting on them all at once.
.LP .LP
Tasks related to processes (killing, renicing) can be done without 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 .LP
Mandatory arguments to long options are mandatory for short options too. Mandatory arguments to long options are mandatory for short options too.
.LP .LP
.TP .TP
\fB\-d \-\-delay=DELAY\fR \fB\-d \-\-delay=DELAY\fR
Delay between updates, in tenths of seconds Delay between updates, in tenths of seconds. If the delay value is
less than 1 it is increased to 1, i.e. 1/10 second. If the delay value
is greater than 100, it is decreased to 100, i.e. 10 seconds.
.TP .TP
\fB\-C \-\-no-color \-\-no-colour\fR \fB\-C \-\-no-color \-\-no-colour\fR
Start htop in monochrome mode Start
.B htop
in monochrome mode
.TP .TP
\fB\-h \-\-help \fB\-h \-\-help
Display a help message and exit Display a help message and exit
@ -39,17 +48,19 @@ Sort by this column (use \-\-sort\-key help for a column list)
\fB\-u \-\-user=USERNAME\fR \fB\-u \-\-user=USERNAME\fR
Show only the processes of a given user Show only the processes of a given user
.TP .TP
\fB\-U \-\-no-unicode\fR
Do not use unicode but ASCII characters for graph meters
.TP
\fB\-v \-\-version \fB\-v \-\-version
Output version information and exit Output version information and exit
.TP .TP
\fB\-t \-\-tree \fB\-t \-\-tree
Show processes in tree view Show processes in tree view
.PP
.br
.SH "INTERACTIVE COMMANDS" .SH "INTERACTIVE COMMANDS"
.LP .LP
The following commands are supported while in htop: The following commands are supported while in
.LP .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
@ -85,8 +96,13 @@ Tag or untag a process. Commands that can operate on multiple processes,
like "kill", will then apply over the list of tagged processes, instead like "kill", will then apply over the list of tagged processes, instead
of the currently highlighted one. of the currently highlighted one.
.TP .TP
.B c
Tag the current process and its children. Commands that can operate on multiple
processes, like "kill", will then apply over the list of tagged processes,
instead of the currently highlighted one.
.TP
.B U .B U
Untag all processes (remove all tags added with the Space key). Untag all processes (remove all tags added with the Space or c keys).
.TP .TP
.B s .B s
Trace process system calls: if strace(1) is installed, pressing this key Trace process system calls: if strace(1) is installed, pressing this key
@ -146,7 +162,7 @@ Quit
Invert the sort order: if sort order is increasing, switch to decreasing, and Invert the sort order: if sort order is increasing, switch to decreasing, and
vice-versa. vice-versa.
.TP .TP
.B +, \- .B +, \-
When in tree view mode, expand or collapse subtree. When a subtree is collapsed When in tree view mode, expand or collapse subtree. When a subtree is collapsed
a "+" sign shows to the left of the process name. a "+" sign shows to the left of the process name.
.TP .TP
@ -189,19 +205,22 @@ Refresh: redraw screen and recalculate values.
.B Numbers .B Numbers
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 .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 htop. The names below are the ones used in the currently unimplemented in
.BR htop .
The names below are the ones used in the
"Available Columns" section of the setup screen. If a different name is "Available Columns" section of the setup screen. If a different name is
shown in htop's main screen, it is shown below in parenthesis. shown in
.LP .BR htop 's
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). The full command line of the process (i.e. program name and arguments).
.TP .TP
.B PID .B PID
The process ID. The process ID.
.TP .TP
@ -222,7 +241,7 @@ The process's group ID.
.TP .TP
.B SESSION (SID) .B SESSION (SID)
The process's session ID. The process's session ID.
.TP .TP
.B TTY_NR (TTY) .B TTY_NR (TTY)
The controlling terminal of the process. The controlling terminal of the process.
.TP .TP
@ -384,36 +403,44 @@ The percentage of time spent swapping in pages. Requires CAP_NET_ADMIN.
.TP .TP
.B All other flags .B All other flags
Currently unsupported (always displays '-'). Currently unsupported (always displays '-').
.SH "CONFIG FILE" .SH "CONFIG FILE"
.LP .LP
By default htop reads its configuration from the XDG-compliant path By default
~/.config/htop/htoprc -- the configuration file is overwritten by htop's .B htop
in-program Setup configuration, so it should not be hand-edited. If no reads its configuration from the XDG-compliant path
user configuration exists htop tries to read the system-wide configuration .IR ~/.config/htop/htoprc .
from @sysconfdir@/htoprc and as a last resort, falls back to its The configuration file is overwritten by
hard coded defaults. .BR htop 's
in-program Setup configuration, so it should not be hand-edited.
If no user configuration exists
.B htop
tries to read the system-wide configuration from
.I @sysconfdir@/htoprc
and as a last resort, falls back to its hard coded defaults.
.LP .LP
You may override the location of the configuration file using the $HTOPRC 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 .LP
Memory sizes in htop are displayed as they are in tools from the GNU Coreutils Memory sizes in
(when ran with the --human-readable option). This means that sizes are printed .B htop
in powers of 1024. (e.g., 1023M = 1072693248 Bytes) are displayed in a human-readable form.
Sizes are printed in powers of 1024. (e.g., 1023M = 1072693248 Bytes)
.LP .LP
The decision to use this convention was made in order to conserve screen space The decision to use this convention was made in order to conserve screen
and make memory size representations consistent throughout htop. space and make memory size representations consistent throughout
.BR htop .
.SH "SEE ALSO" .SH "SEE ALSO"
proc(5), top(1), free(1), ps(1), uptime(1), limits.conf(5) .BR proc (5),
.BR top (1),
.BR free (1),
.BR ps (1),
.BR uptime (1)
and
.BR limits.conf (5).
.SH "AUTHORS" .SH "AUTHORS"
.LP
htop is developed by Hisham Muhammad <hisham@gobolinux.org>.
.LP .LP
This man page was written by Bartosz Fenski <fenio@o2.pl> for the Debian .B htop
GNU/Linux distribution (but it may be used by others). It was updated by Hisham was originally developed by Hisham Muhammad.
Muhammad, and later by Vincent Launchbury, who wrote the 'Columns' section. Nowadays it is maintained by the community at <htop@groups.io>.

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