293 Commits

Author SHA1 Message Date
4e6ec4a087 Update changelog in preparation for htop-3.2.1 release 2022-06-03 10:54:39 +10:00
abaec509e6 Merge branch 'cmdline-render-cache-rework' of BenBE/htop 2022-06-01 08:00:55 +02:00
f156dfecd5 Fix typo 2022-05-31 22:21:52 +02:00
2999fff88e Refactor code for rendering command line cache
Fixes #1008
2022-05-31 13:55:43 +02:00
2613db4b0d Merge branch 'fix-strip-exe-from-cmdline' of benbe/htop 2022-05-31 09:29:11 +02:00
9eed30949b Restore functionality of stripExeFromCmdline setting
This was accidentally lost in fbec3e4005
2022-05-30 22:38:59 +02:00
ce50095323 Merge branch 'fix-allBranchesCollapsed' of tanriol/htop 2022-05-30 10:37:19 +02:00
17e28d5264 actionExpandOrCollapseAllBranches: NOP in flat mode
This shortcut does not have any visible effect in flat mode, so disable
it completely to avoid possible confusion.
2022-05-30 10:52:12 +03:00
da97d2625a ProcessList_collapseAllBranches: actually build tree
As the loop checks `tree_depth`, a tree build is needed to ensure
they're filled in correctly. Note that this breaks the display list sort
order in case it's non-tree-based (either startup in flat mode, or `*`
hotkey in flat mode), so the display list will need to be sorted again.
2022-05-30 10:52:12 +03:00
7694dbc821 Implement PCP support for minimum ZFS ARC size 2022-05-30 07:50:57 +02:00
c0a9e92eea Implement FreeBSD support for minimum ZFS ARC size 2022-05-30 07:50:57 +02:00
491c6f1044 consider only shrinkable ZFS ARC as cache on Linux 2022-05-30 07:50:57 +02:00
98cbdc6dca Correct PROCESS_MAX_UID_DIGITS constant
The PROCESS_MAX_UID_DIGITS=19 introduced in
696f79fe50 doesn't make sense.
The `uid_t` type does not require to be signed in POSIX. If we are to
support 64 bits as the maximum size of `uid_t`, then
PROCESS_MAX_UID_DIGITS should be 20. (= floor(log10(UINT64_MAX)) + 1).

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2022-05-30 07:50:32 +02:00
9ed9d73ab5 Correct titleBuffer size and share it in alignedProcessFieldTitle()
* The size of titleBuffer should be 257 bytes, not 256.

* Remove redundant `static char titleBuffer[]` delarations within
  `alignedProcessFieldTitle()` and let the subroutine use one shared
  buffer for printing field title. This reduces code size.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2022-05-30 07:50:32 +02:00
999801464a Add some headers in the Setup -> Display options panel 2022-05-27 19:47:06 +02:00
0e29174211 Do not scan new processes for deleted libs 2022-05-26 15:38:24 +02:00
WHR
efe09a5e39 Fix process time scaling error on Solaris 2022-05-26 15:31:30 +02:00
038f2ae777 Linux: Increase field width of CPUD% and SWAPD% to 5
Title width of "CPUD%" and "SWAPD%" is 5 and there value cannot go
beyond "100.0%", so increase their field width to 5.

"IOD%" is similar to "MEM%" column, title width is 4 and maximum value
cannot go beyond "100.0%". So in case of "IOD%" column, there is no need
to increase title width to "5". "Process_printPercentage()" function
will handle the maximum value case, it will display value beyond "99.9%"
as "100" instead of "100.0".
2022-05-26 15:03:39 +02:00
0af08bcfc9 Process: Display single digit precision for CPU% greater than 99.9%
Since commit edf319e[1], we're dynamically adjusting column width of
"CPU%", showing single digit precision also for values greater than
"99.9%" makes "CPU%" column consistent with all other values.

[1]: edf319e53d

Change "Process_printPercentage()" function's logic to always display
value (i.e. "val") with single precision. Except when value is greater
than "99.9%" for columns like "MEM%", whose width is fixed to "4" and
value cannot go beyond "100%".

Credits: @Explorer09, thanks for the patch[2] to fix title alignment
         issue.

[2]: https://github.com/htop-dev/htop/pull/959#issuecomment-1092480951

Closes: #957
2022-05-26 15:03:39 +02:00
e053446cbd Fix typo, thx Explorer09 2022-05-21 10:24:42 +02:00
WHR
3d8fa0b926 Mark item separator in default color on help screen
Closes: #1014
2022-05-20 21:54:19 +02:00
d73cc70566 fix typo (dist -> disk)
This was changed in commit 37e01cbe33,
probably unintentional.
2022-05-20 12:58:12 +02:00
37e01cbe33 Colorize process state characters in help screen
Thanks to @Low-power for the idea
Closes #1010
2022-05-20 12:30:37 +02:00
WHR
d22667725a Call mousemask(3X) to truly enable or disable mouse control 2022-05-19 20:23:22 +02:00
ef4cbae5ea Minor code style update 2022-05-19 18:13:46 +02:00
44091705db Use of NULL in execlp() must have a pointer cast.
Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2022-05-19 18:42:44 +08:00
87793b8555 Merge branch 'lxc-cpu-count-fix' of fasterit/htop 2022-05-17 19:10:40 +02:00
fe7f238e2c Increasing niceness is also disabled by --readonly mode 2022-05-13 09:43:56 +02:00
c24681a078 Fix heap buffer overflow in Vector_compact
Fixes: #1006
2022-05-10 12:12:46 +02:00
2da8f71209 Merge branch 'fix_running_containerized_for_lxc' of ilyam8/htop 2022-05-09 12:21:15 +02:00
51228b6239 fix container detection for LXC 2022-05-08 21:46:53 +03:00
33973f7e40 Limit active CPU count under LXC as well
Fixes the initial htoprc not being configured to amount for the host CPU count
2022-05-07 16:05:11 +02:00
79db69c48d Fix Solaris / OmniOS build 2022-05-06 15:22:08 +02:00
9fc72c1e9c Ensure buffer for environment is large enough on Solaris 2022-05-06 14:35:50 +02:00
db93268968 Ensure buffer for environment is large enough on OpenBSD 2022-05-06 14:35:50 +02:00
4f1269cc9f Ensure buffer for environment is large enough on NetBSD 2022-05-06 14:35:50 +02:00
0388b30077 Hashtable: fix handling of NULL pointer in Hashtable_dump
This fixes an issus in Hashtable_dump where `"(nil"` is passed as an
argument to `%p` in fprintf. This prints the static address of `"(nil)"`
not "(nil)". This commit changes the code to just pass the NULL pointer
to fprintf, which will consistently print "0x0".
2022-05-06 06:34:17 +02:00
4b8b61fe18 ProcessList.h: remove ProcessList_remove
As the "highlight dying processes" option has to keep processes in the
list when they disappear, no code except the cleanup loop in
`ProcessList_scan` should remove processes from the list directly.
Remove the export to prevent random process removals from being
reintroduced accidentally.
2022-05-05 10:00:34 +02:00
fae7ff6f03 LinuxProcessList_recurseProcTree: keep on read error
If a process goes away while reading its fields, but we already have
that process in the list, we should keep it in case the "highlight dying
processes" mode is active. Not only is that expected in this mode, but
this should also ensure parents are in the list when their children are
(wanted for tree mode consistency).
2022-05-05 10:00:34 +02:00
e07fce7014 LinuxProcessList_recurseProcTree: open dirfd first
A process can die between reading the directory listing and opening the
directory FD (if HAVE_OPENAT) or /proc files (otherwise) for reading the
process data. This race would cause LinuxProcessList_recurseProcTree to
remove it from the list immediately, which is unexpected in the
"highlight dying processes" mode and can break the tree structure.
This patch closes this race in the HAVE_OPENAT case by only accessing
the process entry after the directory FD has been opened.
2022-05-05 10:00:34 +02:00
e08eec813c Remove redundant sscanf calls (in (s)scanf a blank validates _zero_ or more whitespace)
man sscanf(3):
A sequence of white-space characters (space, tab, newline, etc.; see isspace(3)).
This directive matches any amount of white space, including none, in the input.
2022-05-05 09:34:25 +02:00
549fcb6bb8 Always abort on overflow in String_cat
Not only in debug mode.
2022-05-05 09:19:14 +02:00
08166b27b1 ProcessList: fix quadratic process removal when scanning
This commit changes ProcessList_scan to lazily remove Processes by
index, which is known, instead of performing a brute-force search by
pid and immediately reclaiming the lost vector space via compaction.

Searching by pid is potentially quadratic in ProcessList_scan because
the process we are searching for is always at the back of the vector
(the scan starts from the back of the vector). Additionally, removal
via Vector_remove immediately reclaims space (by sliding elements
down).

With these changes process removal in ProcessList_scan is now linear.

Changes:
  * ProcessList: add new ProcessList_removeIndex function to remove
    by index
  * Vector: add Vector_softRemove and Vector_compact functions to
    support lazy removal/deletion of entries Vector_softRemove
    Vector_compact
  * Vector: replace Vector_count with Vector_countEquals since it only
    used for consistency assertions.
2022-05-05 09:17:51 +02:00
0d53245cf9 LXC: Limit CPU count to what is given in /proc/cpuinfo despite the container seeing the real host CPUs 2022-05-04 18:21:41 +02:00
c7413fd677 Merge branch 'natoscott-changelog-3.2.0' 2022-05-01 16:31:20 +10:00
8f0475cd73 Merge branch 'changelog-3.2.0' of https://github.com/natoscott/htop into natoscott-changelog-3.2.0 2022-05-01 16:31:11 +10:00
a155fd0f8b Merge branch 'natoscott-coverity-scan' 2022-05-01 16:30:51 +10:00
549543f8e4 Merge branch 'coverity-scan' of https://github.com/natoscott/htop into natoscott-coverity-scan 2022-05-01 16:29:55 +10:00
10b541b5e4 Update Settings_newScreen with single-line sortKey checking.
Co-authored-by: BenBE <BenBE@geshi.org>
2022-05-01 16:21:13 +10:00
73f08debe0 Add note that the Tree view setting is per Screen tab now 2022-04-30 19:45:00 +02:00
8b98d3effb Document screen tab switching (TAB, Shift-TAB keys) 2022-04-30 17:06:59 +02:00
7e66ee1d28 FreeBSD: free emulation string 2022-04-30 17:05:59 +02:00
a7a6571d14 Fix typo 2022-04-30 17:03:29 +02:00
cde72dd0b0 Remove redundant null checks on Settings_write (covscan)
Coverity scan reports that there is dead code in Settings_write
checking for nulls that have already been dereferenced on every
code path leading to the check.  This is likely a hangover from
times when the screens pointer was only conditionally allocated
- they're not needed anymore.
2022-04-30 13:55:56 +10:00
cb61865bb9 Add array bounds checking for the Process_fields array (covscan)
Coverity scan reports there may be a code path that would cause
an overrun in the (relatively new) ScreenSettings code where it
evaluates default sort direction.  Add bounds check and default
to descending instead of a potentially invalid array access.
2022-04-30 13:50:25 +10:00
c144bf9ae5 Add changelog entries for pending htop-3.2.0 release, update version 2022-04-29 18:03:53 +10:00
ae518e20b7 Merge branch 'main' of thesamesam/htop 2022-04-26 13:56:40 +02:00
cdf3f3c50b Remove stray fprintf left from testing (introduced in 7039abe) 2022-04-26 13:35:35 +02:00
1f2f4fe891 Assume process just started when kproc->ki_start returns garbage 2022-04-21 08:56:56 +02:00
ec809b7f71 Avoid extremely large year values when printing time 2022-04-21 08:56:56 +02:00
b83ce85d89 Force elapsed time display to zero if process seems started in the future 2022-04-21 08:56:56 +02:00
ee1bf2f917 Process: Fix PID & UID column widths off-by-one error
If the max PID or UID value for a platform is exactly a power of ten
(10000, 100000, etc.) the column widths of PID and UID would be 1 char
less than the correct number of digits. This is caused by the wrong
rounding function (ceil(x)); change to the correct one (trunc(x) + 1).

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2022-04-18 12:30:40 +02:00
afc4a9d657 README: Add macOS build dependency install command 2022-04-14 20:28:11 +02:00
3f3691886a Merge branch 'allow-multiple-filters-and-search-strings' into main
Closes #961
2022-04-14 16:45:29 +02:00
99aa906bc5 Merge branch 'improve-filter-label' into main 2022-04-14 16:44:51 +02:00
df955c8991 Make CLANG happy (-Wembedded-directive) 2022-04-07 12:33:22 +02:00
6a7b3fdc7d Do not show help for -M / --no-mouse if HAVE_GETMOUSE is undefined 2022-04-07 11:52:22 +02:00
72c56691ec Fix a compile warning: comparison of unsigned enum expression < 0 is always false
Get this warning when compiling Settings.c on the Mac OS X with clang-800.0.42.1.
Settings.c:447:28: warning: comparison of unsigned enum expression < 0 is always false [-Wtautological-compare]
         if (this->hLayout < 0 || this->hLayout >= LAST_HEADER_LAYOUT)
             ~~~~~~~~~~~~~ ^ ~

This patch fixes the problem.
2022-04-05 09:23:03 +02:00
4ccad46045 configure.ac: fix static build with hwloc
Retrieve hwloc dependencies through pkg-config to avoid the following
static build failure:

checking for hwloc_get_proc_cpubind in -lhwloc... no
configure: error: can not find required library libhwloc

This build failure is raised because without pkg-config, hwloc
dependencies such as libxml2 are not retrieved:

configure:8999: checking for hwloc_get_proc_cpubind in -lhwloc
configure:9022: /home/autobuild/autobuild/instance-0/output-1/host/bin/powerpc-buildroot-linux-uclibc-gcc -o conftest -D_GNU_SOURCE -I/home/autobuild/autobuild/instance-0/output-1/host/powerpc-buildroot-linux-uclibc/sysroot/usr/bin/../../usr/include -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Og -g0  -static -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -static conftest.c -lhwloc  -llzma -L/home/autobuild/autobuild/instance-0/output-1/host/powerpc-buildroot-linux-uclibc/sysroot/usr/bin/../../usr/lib -lncurses -lm   >&5
/home/autobuild/autobuild/instance-0/output-1/host/lib/gcc/powerpc-buildroot-linux-uclibc/10.3.0/../../../../powerpc-buildroot-linux-uclibc/bin/ld: /home/autobuild/autobuild/instance-0/output-1/host/powerpc-buildroot-linux-uclibc/sysroot/usr/bin/../../usr/lib/libhwloc.a(topology-xml-libxml.o): in function `hwloc_libxml_free_buffer':
topology-xml-libxml.c:(.text+0x6a): undefined reference to `xmlFree'

Fixes:
 - http://autobuild.buildroot.org/results/5d815ec08c580005a863df6ac9ac29deff7d4128

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2022-04-03 12:59:30 +02:00
7039abe109 Guess lxc or docker from /proc/1/mounts
At the moment this is used to make the memory meter report sane values even
if the host has ZFS and that leaks through into a containerized environment

Fixes #863

Includes a clever check for magic PROC_PID_INIT_INO in /proc/self/ns/pid thanks to Pavel Snajdr (snajpa)
2022-04-02 14:02:06 +02:00
2b7504b522 Merge branch 'full_meter' of cgzones/htop into main
Closes #786
2022-04-02 12:45:07 +02:00
8b927ba596 Merge branch 'main' (Linux: fix crash in LXD, fixes #965) of er-azh/htop into main 2022-04-02 12:38:00 +02:00
0ffd772d28 Fix header layout and meters reset if a header column is empty
Closes #880
Patch from BenBE and cgzones
2022-04-02 12:30:30 +02:00
64fb7181ee use xCalloc for allocating cpuData 2022-03-27 12:23:56 +04:30
ba4c67942c Linux: allocate cpuData before reading cpu count. 2022-03-26 17:27:36 +04:30
3f0c172a60 Linux: fix crash in LXD 2022-03-26 15:48:12 +04:30
7c43e02591 Improve String_contains_i to allow for multiple terms
This enables:
* Multiple filters in the main panel and strace etc. views
* Multiple search terms

The search terms are separated by "|" and are still fixed strings
matched case-insensitive.

Added a multi flag at request of BenBE.
2022-03-25 17:19:59 +01:00
c6f946edd2 Improve MainPanel Label on active inc filter (Filter <-> FILTER) 2022-03-25 14:51:14 +01:00
a2ca7583a9 README: Add Archlinux build dependency install command
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-03-24 15:49:13 +01:00
469ae7a0bd Make sure License is correctly specified as GNU GPLv2+ in some more file headers 2022-03-14 07:33:40 +01:00
c4239335b9 Skip system slice name
This shortens paths like /system.slice/system-postgres.slice/postgres@12-main.service to /[S]/postgres@12-main.
Without this some cgroup names for getty processes explode in length.
2022-03-06 19:56:25 +01:00
23b56193d7 Reduce column width spam by snapd 2022-03-06 19:56:25 +01:00
edf319e53d Auto-size (normalized) CPU usage columns 2022-03-06 19:56:25 +01:00
b6c0667eae Linux: dynamically adjust column width of CGROUP C(ompressed)CGROUP 2022-03-06 19:56:25 +01:00
3ba695293c Linux: dynamically adjust the SECATTR column width
SELinux contexts can be quite long; adjust the column width dynamically
at each cycle to the longest value.

Also with the recent addition of multiple screens, over-long columns can
be moved into their own screen.
2022-03-06 19:56:25 +01:00
6133cac721 Process: Handle rounding ambiguity between 99.9 and 100.0
Depending upon default behavior of the compiler and floating-point
environment, compiler could round down the value between "99.9" and
"100.0" to "99.0", instead of rounding it to the nearest value, "100.0".

Note: The floating-point environment access and modification is only
meaningful when "#pragma STD FENV_ACCESS" is set to "ON"[1]. Otherwise
implementation is free to assume that floating-point control modes are
always the default. So it would be a good idea to address the rounding
ambiguity between "99.9" and "100.0" to become compiler agnostic.

[1]: https://en.cppreference.com/w/c/numeric/fenv

Credits: @Explorer09, thanks for the suggestion.
2022-02-19 12:21:26 +01:00
da653f8148 Process: Show only integer value when CPU% more than 99.9%
When we run a process which utilizes CPU between 100.0% and 999.9%, htop
shows an unnecessary decimal character at the end of the value. For
example, '100.x' and '247.x' become '100.' and '247.' respectively.

When CPU utilization is less than and equal to '99.9%', show the result
with single digit precision and if result is less than four characters,
pad it with the blank space. When CPU utilization is greater than
'99.9%', show only integral part of the result and if it's less than
four characters, pad it with the blank space.

Closes: #946
2022-02-19 12:21:26 +01:00
d35db47c9a darwin: lazily set process TTY name
Fetching the TTY name of a process is extremely expensive on darwin and
the call to devname accounts for 95% of htop's CPU usage when there is
high process turnover (this is mostly due to devname calling lstat,
which is incredibly slow). This can make htop unresponsive.

To mitigate this only set the process TTY name if the it is being
actively displayed (PROCESS_FLAG_TTY), which by default it is not
on darwin.
2022-02-18 09:07:41 +01:00
978a7c894f ProcessList_buildTreeBranch: drop extra assert
It essentially only checks Vector_add, and there's already an assert for
that in the vector code.
2022-02-13 19:50:16 +01:00
79a6f6cb5b ProcessList_buildTree: skip hashtable if known root
Don't waste time looking up the parent if the current process is already
known to be a root process.
2022-02-13 19:50:16 +01:00
deb05fe7c2 ProcessList: delay tree rebuild until panel rebuild 2022-02-13 19:50:16 +01:00
82d34deaf1 ProcessList: cleanup the tree set sorting remains
They're no longer needed as rebuilding the tree from scratch is just as
fast.
2022-02-13 19:50:16 +01:00
fa3e0d06c2 ProcessList_buildTree: produce sorted tree
ProcessList_buildTree does not need any particular sort order for
children of the same process or roots. Switching these to the sort order
configured by the user produces sorted tree automatically, making repeat
sort unnecessary.
2022-02-13 19:50:16 +01:00
82dce5cf8e ProcessList_buildTree: sort by parent for fast search
ProcessList_buildTreeBranch used to search for children with a linear
scan of the process table, which made tree build time quadratic in
process count. Pre-sorting the list by parent PID (if known) makes it
possible to select the correct slice by bisection much faster.
2022-02-13 19:50:16 +01:00
8d987864e4 ProcessList_buildTree: drop sort direction checking
This is only a partial check that does not take into account the sort
field used and is overridden later anyway.
2022-02-13 19:50:16 +01:00
58b42e4cac ProcessList: introduce displayList
Separate `processes` (the vector owning the processes, sorted in
whatever order is needed right now internally) and `displayList` (a
vector referencing the processes in the same order they're to be
displayed).
2022-02-13 19:50:16 +01:00
2477a5a018 ProcessList_buildTree: handle every process once
Special-casing hidden processes does not serve any obvious purpose and
depends on the move from processes to processes2 which will be removed
in a later commit.
2022-02-13 19:50:16 +01:00
1a403eb7eb ProcessList_buildTree: lookup parent via hashtable
While this change does not significantly affect performance, it removes
the internal requirement to have the process list sorted by PID.
2022-02-13 19:50:16 +01:00
a3a7958721 ProcessList: sort before panel rebuild if needed 2022-02-13 19:50:16 +01:00
4aeb146ce8 Remove duplicate sections on COMM and EXE
Closes #934

Thank you, Narendran Gopalakrishnan (gnarendran)!
2022-02-13 17:22:39 +01:00
8c99683b04 Fix custom thread name display issue
Fixes #877
2022-02-13 16:59:29 +01:00
265a7b8a50 Fix division by zero when calculating IO rates
Fixes #935
2022-02-03 17:48:18 +01:00
939685dff9 build: use AC_CANONICAL_HOST, not AC_CANONICAL_TARGET
htop is a program which will be run on CHOST after cross-compilation;
CTARGET is only for a small number of cases where a program itself outputs
code (so you might cross-compile a compiler which spits out code for a third
architecture/platform).

We want to use AC_CANONICAL_HOST to check CHOST for the platform currently
being used to build htop.

The confusion around this issue was compounded by a mistake in autoconf-archive
which has since been fixed (AX_PTHREAD pulled it in incorrectly).

See: https://github.com/libstatgrab/libstatgrab/pull/131
See: https://github.com/fenrus75/powertop/pull/90#discussion_r705803725
Signed-off-by: Sam James <sam@gentoo.org>
2022-01-22 03:57:14 +00:00
3e1a27a981 freebsd/dragonfly: Stop aligning equals signs in PLATFORM_PROCESS_FIELDS
ProcessField doesn't do this, nor does any other OS, and it just makes
it more annoying to add a new field.
2022-01-18 07:23:36 +01:00
9512fd7930 FreeBSD: Add support for showing process emulation
This displays the same output as ps's -o emul, which is the system call
emulation environment, or ABI, in use. This will typically be FreeBSD
ELF32 or ELF64, but can also be Linux ELF32 or Linux ELF64 when running
Linux binaries under FreeBSD's Linuxulator binary compatibility layer.
The column width of 16 is chosen to match KI_EMULNAMELEN's value of 16,
most of which is normally used up as FreeBSD ELF32/64 is 13 characters.
2022-01-16 17:21:18 +01:00
4a664c0df8 Help: Linux swap consistency
On the help screen's depiction of the swap bar, the / separator between
used and cache should be coloured for consistency with the other bars.

I tried removing the coloured /s from the other bars to make them
consistent, but found that less visually appealing.
2022-01-13 19:45:01 +01:00
d0d9f202c5 Avoid zombie processes on signal races
The system curses library can handle terminal size changes with
SIGWINCH without asking system calls to restart, which effectively
stops system calls with -1 and EINTR. An example is ncurses on
Linux systems.

One of these system calls is waitpid. While waiting for the lsof child
to complete, a badly timed SIGWINCH can interrupt the waitpid call,
effectively never clearing the state of the child, keeping the zombie
until htop exits.

Proof of Concept:

 #include <unistd.h>
 int main(void) {
   close(1); close(2);
   sleep(5);
   return 0;
 }

Compile this as a replacement "lsof" and put it into your path. Make
sure that it's called instead of the real lsof.

Press "l" to list open files and resize your terminal within the next
5 seconds. You will see that a zombie process is kept by htop when the
timeout finishes.
2022-01-11 22:56:27 +01:00
a0ad0697a8 Always set SIGCHLD to default handling
The parent process of htop might have set SIGCHLD to ignore, which can
be inherited by htop (Linux does this, OpenBSD resets to default).

If SIGCHLD is ignored then waitpid returns -1 which is not properly
handled by htop for lsof output.

Proof of Concept (Linux):

 #include <signal.h>
 #include <unistd.h>
 int main(void) {
   char *arg[] = { "htop", NULL };
   signal(SIGCHLD, SIG_IGN);
   execv("htop", arg);
   return 1;
 }

If you run htop with ignored SIGCHLD then pressing "l" always fails,
i.e. it is not possible to list open files even if lsof is installed.
2022-01-11 22:56:27 +01:00
a133ffd829 Removed unused String_getToken function
Since String_getToken is not used anymore and currently only supports
a 50 char token, simply remove it for now.
2022-01-11 21:42:57 +01:00
fde1243443 Fix out of boundary writes in XUtils
It is possible to exceed the unsigned int data type on 64 bit systems
with enough available RAM. Use size_t in all places instead.

Proof of Concept: Create a 4 GB line in .htoprc file and run htop

$ dd if=/dev/zero bs=1024 count=4194304 | tr '\0' 'a' > ~/.htoprc
$ htop
Segmentation fault

Also avoid overflow of stack based "match" array in String_getToken.
2022-01-11 21:42:57 +01:00
6eab39c0ab Fix typo
This typo has been found with codespell.
2022-01-11 19:56:27 +01:00
2c3a64ac9c Year 2022 updates 2022-01-03 18:01:18 +01:00
442c1596f6 GH Actions: enable Werror in PCP build
Just exclude the singe warning type currently issued.

Avoids e64269df ("Fix process state handling compiler warning on PCP platform")
2021-12-20 10:54:12 +01:00
f782f821f7 LinuxProcessList: do not collect LRS per thread
It's a memory map property, so it's process-wide and collecting it just
once should be enough.
2021-12-18 12:31:36 +01:00
5b78ad2d53 Set correct default sorting direction
Respect the field option defaultSortDesc for the default screen sort
direction, e.g. for CPU%.
2021-12-17 14:45:15 +01:00
1ef8c0e12f Drop getCommandStr member of Process
Formatting the merged command string is now implemented in an platform
independent way. Drop the Process member getCommandStr designed for
overrides of individual platforms.
2021-12-17 14:45:15 +01:00
6fcb1994c8 Do not combine default and configuration process fields
When reading a configuration file with the syntax previous to the
screens update Settings_defaultScreens() will add the default fields and
later ScreenSettings_readFields() will add the ones from the
configuration file. This will duplicate some fields and corrupt the
columns due to the boundless Command field.
2021-12-17 14:45:15 +01:00
5bc988ad6d Use correct command field as default field
The default htop command process field has the enum identifier `COMM`
but the name `Command` (`COMM` is the field name for /proc/<PID>/comm).
2021-12-17 14:45:15 +01:00
6e9a5e9e49 Mark ScreenDefaults const 2021-12-17 14:45:15 +01:00
14f428a172 Drop unused Platform variables 2021-12-17 14:45:15 +01:00
6388033e10 configure: support libunwind of LLVM
The libunwind headers of LLVM are located in the subdirectory
/usr/include/libunwind. Search that subdirectory when the default
header test fails. Also extend the include path due to the transitive
include of `<__libunwind_config.h>`.

Closes: #894
2021-12-16 17:51:15 +01:00
b45eaf2fe1 Hashtable: use a minimum size of 7
With a size of 2 or 3 the grow factor does not reach 70% for one empty
entry. This will cause the following assert violation:

    htop: Hashtable.c:236: void Hashtable_put(Hashtable *, ht_key_t, void *): Assertion `this->size > this->items' failed.
2021-12-13 21:17:58 +01:00
230dc9c3c1 Hashtable: adjust shrink-on-remove factor
Due to the use of prime numbers Hashtable_remove used to never shrink
from some sizes. For example, a size 8191 hashtable would try to shrink
to 4095, which nextPrime would round back to 8191 instead of the
intended 4093. A factor of 3 is enough to allow every prime size used to
shrink to the previous one.
2021-12-13 21:05:22 +01:00
d084a80023 Hashtable: skip rehashing if the size is the same
Hashtable_setSize should not rehash if the actual size stays the same as
the number of buckets and natural positions stay the same too.
2021-12-13 21:05:22 +01:00
5c8670717a Do not leave empty last column in header
Do not leave empty last column in header meters by refactoring the width
and separator logic.

Closes: #784
2021-12-09 17:52:00 +01:00
bc08c7dc2a Merge pull request #889 from cgzones/screens_update
Screens update
2021-12-09 08:38:25 +11:00
1e94b92226 Linux: read generic sysfs batteries
Not all batteries entries in /sys/class/power_supply start with either
BAT or AC, but might have device specific names, e.g. CMB1.
Detect the types of those entries and parse them accordingly.

Closes: #881
Fixes: 3e70de64 ("Code clean up for reading battery info")
2021-12-08 20:50:11 +01:00
63fafb4844 IOMeters: rework initial display
Show a non highlighted string at the start of htop, not the failure
text.
Also the original fix only handled the text mode, not the bar mode.

Improves: 2977414d ("Discard stale information from DiskIO and NetworkIO meters")
Related: #860
2021-12-08 19:08:20 +01:00
c85e5bbf5c ScreenPanels: free ScreenSettings of deleted screens 2021-12-08 16:34:15 +01:00
c9e0bd2002 ScreenPanel: misc updates
- use ASCII escape sequences
- use array allocation wrappers
2021-12-08 16:34:15 +01:00
df1914f429 Add ScreenSettings_delete helper 2021-12-08 16:34:15 +01:00
3cfdf66d9a Settings: initialize default sort key for new screenpanel
Use C99 struct initialization, which also makes using calloc redundant.

htop: Process.c:1179: int Process_compareByKey_Base(const Process *, const Process *, ProcessField): Assertion `0 && "Process_compareByKey_Base: default key reached"' failed.
2021-12-08 14:55:14 +01:00
fa9f260f63 Process: print default key 2021-12-08 14:48:20 +01:00
1da78b5818 CRT: add debug printing function 2021-12-08 14:48:20 +01:00
2ae1906479 Panel: initialize cursorOn member
Panel.c:496:14: runtime error: load of value 190, which is not a valid value for type 'bool'
2021-12-08 12:40:13 +01:00
0e58784224 Fix memory leak on shutdown in new screen settings code.
Also tidy up the calloc call parameters in the initial allocation
of this pointer, thanks to @BenBE for noticing.
2021-12-07 17:04:49 +11:00
4ef5e4296e fix CI issue
Signed-off-by: Sohaib Mohamed <sohaib.amhmd@gmail.com>
2021-12-07 17:04:49 +11:00
ba3a1df806 Fix misc styleguide issues and add missing header files
Signed-off-by: Sohaib Mohamed <sohaib.amhmd@gmail.com>
2021-12-07 17:04:49 +11:00
b672e60886 Enable tabs for a fresh install of htop Only
If the new htop is configured with htoprc having no tabs (eg on upgrade)
then the interface will not automatically introduce/enable them.
However, for a fresh install of htop, enabling them automatically

Signed-off-by: Sohaib Mohamed <sohaib.amhmd@gmail.com>
2021-12-07 17:04:49 +11:00
cd6457ef88 Fixup tabs with dynamic Columns - add missing Dynamic() 2021-12-07 17:04:49 +11:00
31fe29c5a7 Pass correct ColorElements values to Panel_setSelectionColor 2021-12-07 17:04:49 +11:00
cc2547fcf0 Improvements to the tab code after initial feedback 2021-12-07 17:04:49 +11:00
72ba20fa5f Introduce screen tabs
This is a forward port (by nathans) of Hisham's original code.
2021-12-07 17:04:49 +11:00
ff4f44b22a Pre-select the last sent signal in SignalsPanel
Instead of pre-selecting SIGTERM every time, select the signal last
send in the same htop session.

Closes: #862
2021-12-05 19:36:36 +01:00
a38f48481e Process: highlight UNINTERRUPTIBLE_WAIT state (D)
Commit d8dfbbd3 ("Tidy up process state handling") did change the
highlighting of the UNINTERRUPTIBLE_WAIT state (D) from red to gray.
As this state might means the process probably still has work to do and
can hint at bottlenecks, revert this particular change.

Fixes: d8dfbbd3 ("Tidy up process state handling")
2021-12-05 19:29:10 +01:00
61c9fe44a3 CGroupUtils: avoid unsigned integer underflow
Do not underflow count at the last iteration, which triggers UBSAN when
using -fsanitize=unsigned-integer-overflow. This is useful as those
underflows can be a result of a flawed counting logic (e.g. a counter
gets reduced more than increased).
2021-12-05 19:28:07 +01:00
ff0ea41c86 Fix issue where last line is not cleared when SIGINT is received
When we close the application using the quit function F10, the last line
is cleared so that on terminals which do not support ALTBUF the last
line is not clobbered. This do not happen when the application exits as
a result of a signal (SIGINT,SIGTERM,SIGQUIT).

Move the logic to clear the last line into the CRT_done function. This
ensures that it will be executed when the CRT_handleSIGTERM is called.
2021-12-01 17:08:13 +01:00
43e9be5a8f Update version number to 3.2.0-dev to identify git repo builds 2021-11-30 12:10:54 +11:00
d73783d6db Release 3.1.2 2021-11-30 12:03:21 +11:00
89b7c4c9f9 Merge branch 'release_action' of https://github.com/rofl0r/htop into rofl0r-release_action 2021-11-30 11:16:57 +11:00
78aefc2a99 Minor typo fix and wordsmith-ing in the changelog 2021-11-30 11:15:35 +11:00
697c502b7e Update changelog to include recent changes 2021-11-28 23:34:41 +01:00
171aa0faaa Add a fix to use gettimeofday if monotonic is not available. 2021-11-28 23:13:46 +01:00
ddfacb8694 Get file size using stat() for OpenFilesScreen if missing from lsof 2021-11-28 20:32:02 +01:00
5beef3e737 Strip prefix "0t" from OFFSET column of OpenFilesScreen 2021-11-28 20:32:02 +01:00
27a18830d5 Add OFFSET column to OpenFilesScreen 2021-11-28 20:32:02 +01:00
b98a4f8d56 Include support for legacy LXC cgroup naming 2021-11-28 20:19:10 +01:00
55e073a455 Use helpers for parsing of cgroup labels 2021-11-28 20:19:10 +01:00
915b558bbe Document CCGROUP column 2021-11-28 20:19:10 +01:00
550100327b Extract string writing/buffer handling into some callback 2021-11-28 20:19:10 +01:00
9dc964bb42 Compress cgroup names based on some heuristics 2021-11-28 20:19:10 +01:00
ea4282784d Filter leading colons in CGROUP name 2021-11-28 20:19:10 +01:00
1c3c149d20 Ensure maximum width for CGROUP column 2021-11-28 20:19:10 +01:00
42c0493021 add github workflow to build release tarball 2021-11-23 16:29:25 +00:00
1284ab4835 Reduce allocation size of cp_time_n and cp_time_o on FreeBSD and DragonFlyBSD 2021-11-19 12:46:48 +01:00
07496eafb0 Linux: use proper way to detect kernel threads
Use PF_KTHREAD flag in /proc/[pid]/stat to detect kernel threads.
This fixed an issue when a process's cmdline is empty, htop think
it is a kernel thread.
2021-11-09 19:04:25 +01:00
e7a8d14cbd Correct the order of xCalloc parameters in a couple of places
No functional change.  Thanks to @BenBE for pointing these out.
2021-11-05 09:22:09 +11:00
21cb1c4d59 Cleanup: Fix misc styleguide issues
Signed-off-by: Sohaib Mohamed <sohaib.amhmd@gmail.com>
2021-11-04 23:13:38 +01:00
e64269df2c Fix process state handling compiler warning on PCP platform 2021-11-03 15:46:20 +11:00
d8dfbbd37c Tidy up process state handling 2021-11-02 20:02:54 +01:00
afa3fe4af1 Drop unicode whitespace 2021-11-01 12:12:13 +01:00
2ef70ad7f6 Early program termination only from main() 2021-10-31 15:07:00 +01:00
2977414d54 Discard stale information from DiskIO and NetworkIO meters
This ensures the initial read of the data is not misinterpreted as arbitrarily large values.
Also this forces the maximum update interval between two subsequent reads to within 30s.

Fixes #860
2021-10-30 10:57:14 +02:00
696f79fe50 Dynamically scale the ST_UID size to support 32-bit UIDs
While most Unix-like systems use 16-bit user IDs,
Linux supports 32-bit UIDs since version 2.6.
UIDs above 65535 are used for UID namespacing of containers,
where a container has its own set of 16-bit user IDs.
Processes in such containers will have (much) larger UIDs than 65535.

Because the current format strings for `ST_UID` and `USER`
are `%5d` and `%9d` respectively, processes with such UIDs
lead to misaligned columns.

Dynamically scale the `ST_UID` column and increase the size of `USER`
to 10 characters (length of UINT32_MAX) to ensure that the user ID always fits.

Additionally: clean up how the titlebuffer size calculation and ensure
the PID column has a minimum size of 5.
2021-10-27 21:20:59 +02:00
4374a267be Linux: restore battery state with numbered AC's 2021-10-26 10:58:59 +02:00
3e70de64b3 Code clean up for reading battery info 2021-10-25 22:43:10 +02:00
3f9f52fd29 Linux: ignore mapped /dev/zero
Virtualbox maps /dev/zero for memory allocation. That results in
false positive, so ignore.
2021-10-25 21:05:28 +02:00
dfa62506b7 Allow -u UID
Defaults to username, of course, like all POSIX utilities do
2021-10-24 04:39:58 +02:00
731b4003a2 Fix Security Attributes column header width
Limit the maximum width (instead of only the minimum width), pad the
header width accordingly, and also remove extra stray spaces from the
format string (the main spacing should just come from the alignment of
the value).

Fixes #850.
2021-10-20 15:02:26 +02:00
30c5004cbb Merge branch 'del-exe-lib-docs' of BenBE/htop into main 2021-10-19 06:59:11 +02:00
9d3a1d4981 Elaborate the highlighting of outdated/deleted executables and libraries 2021-10-19 00:39:24 +02:00
60cfa2edce FreeBSD: add comment for memory leak workaround
Follow-up of #841

[ci skip]
2021-10-15 22:22:30 +02:00
604744f68d Header: allocate memory for terminating entry
The names array is terminated by a NULL entry, thus allocate space for
one more than entries.

Fixes: #844

==6708==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6060000045b8 at pc 0x000000589ee1 bp 0x7ffcd1dee220 sp 0x7ffcd1dee218
READ of size 8 at 0x6060000045b8 thread T0
    #0 0x589ee0 in String_freeArray ./XUtils.c:157:23
    #1 0x56c9af in Settings_delete ./Settings.c:31:7
    #2 0x4ee44b in CommandLine_run ./CommandLine.c:395:4
    #3 0x4d6fb2 in main ./htop.c:15:11
    #4 0x7ff3b8154e49 in __libc_start_main csu/../csu/libc-start.c:314:16
    #5 0x428aa9 in _start (./htop+0x428aa9)

0x6060000045b8 is located 0 bytes to the right of 56-byte region [0x606000004580,0x6060000045b8)
allocated by thread T0 here:
    #0 0x4a53f2 in __interceptor_calloc (./htop+0x4a53f2)
    #1 0x5890ba in xCalloc ./XUtils.c:55:17
    #2 0x50a044 in Header_writeBackToSettings ./Header.c:148:34
    #3 0x4de861 in Action_runSetup ./Action.c:91:7
    #4 0x4de861 in actionSetup ./Action.c:386:4
    #5 0x515caf in MainPanel_eventHandler ./MainPanel.c:106:19
    #6 0x56a5c1 in ScreenManager_run ./ScreenManager.c:235:19
    #7 0x4ee13b in CommandLine_run ./CommandLine.c:364:4
    #8 0x4d6fb2 in main ./htop.c:15:11
    #9 0x7ff3b8154e49 in __libc_start_main csu/../csu/libc-start.c:314:16
2021-10-15 12:16:41 +02:00
13fe58f54a Update version number to 3.2.0-dev to identify git repo builds 2021-10-14 23:53:41 +02:00
e785693ef9 Release 3.1.1 2021-10-14 23:42:05 +02:00
4cf74de1f1 Suppress reporting memory leaks originating from libdevstat
Based on https://cgit.freebsd.org/src/tree/lib/libdevstat/devstat.c
the following four functions call memory allocation functions:

- devstat_getdevs()
- devstat_selectdevs()
- devstat_buildmatch()
- get_devstat_kvm()
2021-10-14 23:25:19 +02:00
0a5890d332 Updates to the ChangeLog for 3.1.1 release 2021-10-13 17:03:35 +11:00
2128edfba2 Plug the memory leak for the Disk I/O meter
There are no functions in libdevstat to initialise or clean up memory. The simplest change is to mark the local variable info as static.
2021-10-12 09:09:00 +02:00
d1c833fe95 Merge branch 'revert-used-memory' 2021-10-12 17:47:03 +11:00
868b0b3574 Ensure consistent reporting of MemoryMeter used memory
Resolves #821
2021-10-12 17:46:44 +11:00
04871a2d04 Report hugepage memory as real and used memory (as before)
This is real, physical memory available for applications to
use.  We should not try to pretend otherwise; its confusing
for users and inconsistent with all other tools.
2021-10-12 17:46:16 +11:00
b7248f6cb8 Handle procExeDeleted & usesDeletedLib without mergedCommandline mode 2021-10-07 08:42:08 +02:00
74afca7bc1 Validate meter configuration before proceeding 2021-10-06 08:59:02 +02:00
2a13e15ad0 Properly release memory on partially read configuration 2021-10-06 08:59:02 +02:00
8ac8542b6e Handle interrupted sampling from within libpcp PDU transfers
This situation can arise if pcp-htop screen is resized right
at the same time sampling from pmcd(1) is happening.  Have a
couple more goes at it before giving up entirely; once there
is no data available though we cannot proceed into accessing
the sample result data structure (segv will result) so a new
short-circuit guard is added there also.
2021-10-05 15:53:18 +11:00
f75a8bc3a1 Memory leak on OpenBSD when querying full command line 2021-10-03 19:18:25 +02:00
d520199e62 Memory leak on NetBSD when querying full command line 2021-10-03 19:18:25 +02:00
9d8d87e1d5 Memory leak on DragonFlyBSD when querying full command line 2021-10-03 19:18:25 +02:00
57c1538887 Don't force allocation, as failure is handled 2021-10-03 19:18:25 +02:00
9eb52d5932 Release memory for cmdline when Process_updateCmdline() returns.
This closes a nasty memory leak. There is at least another leak looming somewhere when Disk I/O is shown in the header area. That could very well be an issue within libdevstat native to FreeBSD.
2021-10-03 19:18:11 +02:00
970885edc3 Linux: improve tryRead macro safety
Add an explicit else clause so a following else branch for a prior if
condition does not get mixed up.

Also force a trailing semicolon and thereby silence current
-Wextra-semi-stmt warnings.

Improve readability of the hwloc_bitmap_foreach_begin loop macro.
2021-10-01 18:36:35 +02:00
ae1816e563 Settings: use size_t for meter count in header
Header.c:150:26: error: implicit conversion loses integer precision: 'int' to 'uint8_t' (aka 'unsigned char') [-Werror,-Wimplicit-int-conversion]
          colSettings->len = len;
                           ~ ^~~
2021-10-01 18:36:35 +02:00
53732ab0bb Resolve -Wimplicit-int-conversion warnings
Panel.c:414:49: error: implicit conversion loses integer precision: 'int' to 'short' [-Werror,-Wimplicit-int-conversion]
      this->scrollH = MAXIMUM(this->selectedLen - this->w, 0);
                    ~         ~~~~~~~~~~~~~~~~~~^~~~~~~~~
./Macros.h:11:54: note: expanded from macro 'MAXIMUM'
#define MAXIMUM(a, b)                  ((a) > (b) ? (a) : (b))
                                                     ^

IncSet.c:159:38: error: implicit conversion loses integer precision: 'int' to 'char' [-Werror,-Wimplicit-int-conversion]
         mode->buffer[mode->index] = ch;
                                   ~ ^~

Panel.c:456:24: error: implicit conversion loses integer precision: 'int' to 'char' [-Werror,-Wimplicit-int-conversion]
         buffer[len] = ch;
                     ~ ^~
Panel.c:473:22: error: implicit conversion loses integer precision: 'int' to 'char' [-Werror,-Wimplicit-int-conversion]
         buffer[0] = ch;
                   ~ ^~
2021-10-01 18:36:35 +02:00
8932efece9 astyle: Add --pad-header option
Insert space padding between a header (e.g. 'if', 'for', 'while'...) and the
following paren. ex:

if(isFoo((a+2), b))
    bar(a, b);

becomes:

if (isFoo((a+2), b))
    bar(a, b);

Link: http://astyle.sourceforge.net/astyle.html#_pad-paren
Signed-off-by: Sohaib Mohamed <sohaib.amhmd@gmail.com>
2021-10-01 15:01:51 +02:00
d23627fda9 Adds a missing HAVE_GETMOUSE check.
Fixes builds failing with error
MainPanel.c:65:62: error: 'Settings {aka struct Settings_}' has no member named 'enableMouse'
    if (ch != ERR && (ch != KEY_MOUSE || this->state->settings->enableMouse))
2021-09-29 22:21:53 +02:00
4c846921a6 Improve configuration file version diagnostic formatting slightly 2021-09-27 15:13:01 +10:00
c5e789db1b Prepare changelog contents for the 3.1.1 bugfix release 2021-09-27 13:33:48 +10:00
eb94c4f9c9 Merge branch 'amomchilov-workaround-for-FB9546856' 2021-09-27 13:15:58 +10:00
8361c6c1ae Merge branch 'workaround-for-FB9546856' of https://github.com/amomchilov/htop into amomchilov-workaround-for-FB9546856 2021-09-27 13:15:50 +10:00
07f934ccfb Update the README to drop now redundant glibc version requirement 2021-09-27 13:14:23 +10:00
6f751d5929 Merge branch 'o_path' of https://github.com/cgzones/htop into cgzones-o_path 2021-09-27 13:13:13 +10:00
e26a2cf431 Workaround for Rosetta 2 on Darwin
rdar://FB9546856

https://openradar.appspot.com/radar?id=5055988478509056
2021-09-26 10:58:47 -04:00
d527bc9132 Refactor Platform_CompareKernelVersion API 2021-09-26 10:58:47 -04:00
dadcb87ad0 Extract Darwin "PlatformHelpers" 2021-09-26 10:58:47 -04:00
754c0d6bb9 Linux: always compute procExeDeleted if already set
A process, whose executable has been replaced and thus marked by htop,
can be re-executed with the replaced executable, with the same PID, in
two ways: the Linux feature checkpoint/restore or re-execution of PID 1.
The actual check is just a string comparison, like the dropped
condition, leading to (almost) no computation overhead.
2021-09-24 22:03:16 +02:00
9f4a4faab9 Merge branch 'license' of cgzones/htop 2021-09-24 21:25:00 +02:00
a710deeaa7 Linux: define O_PATH if not defined
Defining O_PATH if not defined by <fcntl.h> does not really add any
maintenance cost and might improve some portability.

Related: #804
2021-09-24 20:31:48 +02:00
466d6f99e2 Update remaining license headers to explicitly say GPLv2+
Also change ReadMe badge.
2021-09-24 20:28:26 +02:00
1601931bbf Merge branch 'fasterit-clarify-license' 2021-09-24 11:59:41 +10:00
41f9c0ab77 Merge branch 'clarify-license' of https://github.com/fasterit/htop into fasterit-clarify-license 2021-09-24 11:59:32 +10:00
e28d022716 Merge branch 'automake-compat' 2021-09-24 11:59:09 +10:00
d5ac75a5c7 Merge branch 'fasterit-fix-mouse-wheel' 2021-09-24 11:58:03 +10:00
2ba7d5ff36 Merge branch 'fix-mouse-wheel' of https://github.com/fasterit/htop into fasterit-fix-mouse-wheel 2021-09-24 11:57:52 +10:00
3d99c306b9 Document minimum versions for glibc and libcap. Thank you to James Brown (Roguelazer).
Closes #804
2021-09-23 18:10:51 +02:00
3d3474b3fc Adjust Makefile.am macro definitions for older automake versions
Fixes: #803
2021-09-23 10:06:38 +10:00
94ad111391 Update license headers to explicitly say GPLv2+ 2021-09-22 14:28:19 +02:00
976fa3b121 Make the "Released under the GNU GPLv2+. See 'man' page for more info." from ScreenManager.c valid 2021-09-22 14:23:49 +02:00
61227793de Clarify licensing
htop has been licensed as GPLv2 but there was inconsistency regarding the
option to choose "any later version" as granted by the license.

This commit clarifies the htop dev team is fine with that choice.
2021-09-22 14:23:38 +02:00
e6ded48d1a Update version to 3.1.1-dev 2021-09-22 11:49:25 +02:00
87c992739b Fix mouse wheel collision with autogroups nice adjustment
Fixes #805
2021-09-22 09:00:31 +02:00
29e1fcfa05 Use libunwind for printing backtrace 2021-09-21 09:06:30 +02:00
29983ff83a Release 3.1.0 2021-09-21 16:30:46 +10:00
36b7f57200 Some minor editorial changes to the readme 2021-09-20 16:48:39 +02:00
c5fe142256 Bump to rc3, likely final release commits for 3.1.0 2021-09-20 16:34:39 +10:00
691b7dd5af Merge branch 'cgzones-readme' 2021-09-20 16:31:31 +10:00
ad0f9c58bf Merge branch 'readme' of https://github.com/cgzones/htop into cgzones-readme 2021-09-20 16:31:24 +10:00
a8d0f2a7fe Merge branch 'cgzones-multicolumn' 2021-09-20 16:30:18 +10:00
e6596cbd69 Merge branch 'multicolumn' of https://github.com/cgzones/htop into cgzones-multicolumn 2021-09-20 16:29:44 +10:00
40c9f89971 Merge branch 'cgzones-mouse' 2021-09-20 16:29:17 +10:00
09ad8067f4 Merge branch 'mouse' of https://github.com/cgzones/htop into cgzones-mouse 2021-09-20 16:29:05 +10:00
bda3ee2b81 Update ReadMe
Add license and repology badge, improve several wording, improve
several sections.

[skip ci]
2021-09-19 14:47:32 +02:00
bdb015ffa3 Header: do not let multi-column meters expand to empty neighbors
Extending to right neighbors is intended for text meters with an
overlong content, so the whole text is shown if possible.
Multi column meters, like the combined memory and swap meter, position
its text depending on the given total width; keep the position to the
original assigned header slot.

Short term resolution for #796
2021-09-18 17:21:32 +02:00
80a515abcc Fix typo 2021-09-18 15:55:21 +02:00
5000cefc13 Fix typo in gettime implementation on darwin 2021-09-18 14:14:23 +02:00
bf7d98e7ac MainPanel: do not reset on disabled mouse events
Do not reset the hidden process selection (and hidden function bar) on mouse events, when mouse support is disabled.
2021-09-18 11:48:32 +02:00
c096712b8d Merge pull request #779 from BenBE/percent-highlight
Highlight percentages similar to large numbers
2021-09-14 12:53:33 +10:00
be82448bd5 Process_printPercentage using one color transition
Update Process_printPercentage such that color change happens only once at 100% and beyond.
2021-09-14 11:16:34 +10:00
68c00b9cdb Header: cache number of columns in HeaderLayout_getColumns
The header layout, and therefore the number of columns, should never
be changed within such loop.
2021-09-12 18:35:24 +02:00
3c8d586a1c Linux: recalculate LRS value
The LRS value is only zero in the first cycle; drop the check to
recalculate while running.
2021-09-12 18:20:40 +02:00
3869c43393 valgrind: rewrite ncurses suppressions
Simplify and update valgrind suppressions for possible leak and
reachable memory inside ncurses.
2021-09-11 00:07:23 +02:00
fcca4c2f2d valgrind: track origin of uninitialised values 2021-09-11 00:07:23 +02:00
dd88510dcd HeaderOptionsPanel: select the current not saved option
Select the current active header layout, not the current saved layout
from the settings, as the value gets only saved back from the active
header to settings on closing the setup menu.

Closes: #785
2021-09-11 00:06:41 +02:00
43ffdb0eda Linux: zero CPU data after allocation
Zero all the CPU data, like totalPeriod, after its memory allocation via
realloc(3).

    Conditional jump or move depends on uninitialised value(s)
       at 0x132A9B: LinuxProcessList_scanCPUTime (LinuxProcessList.c:1928)
       by 0x1358C3: ProcessList_goThroughEntries (LinuxProcessList.c:2079)
        by 0x12A79A: ProcessList_scan (ProcessList.c:627)
        by 0x11CA67: CommandLine_run (CommandLine.c:357)
        by 0x4A81E49: (below main) (libc-start.c:314)
      Uninitialised value was created by a heap allocation
        at 0x48396C5: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
        by 0x12F633: xRealloc (XUtils.c:64)
        by 0x12F633: xReallocArray (XUtils.c:78)
        by 0x1325A8: LinuxProcessList_updateCPUcount (LinuxProcessList.c:207)
        by 0x134E0A: ProcessList_new (LinuxProcessList.c:284)
        by 0x11C8D0: CommandLine_run (CommandLine.c:301)
        by 0x4A81E49: (below main) (libc-start.c:314)
2021-09-11 00:04:00 +02:00
bf395e10c5 Add xReallocArrayZero() helper
Add helper function to reallocate an dynamic allocated array including
zeroing the newly allocated memory.
2021-09-11 00:04:00 +02:00
8f259bc5e1 MemorySwapMeter: align with CPU meter
Use the same width for each sub meter to align with CPU meter.
Currently if the total width is even, so it does not split exactly into
2 equal parts plus 1 (for the middle space character column), the extra
column is added to the second meter width.

Closes: #783
2021-09-10 17:09:48 +02:00
38e6136b82 CRT: close backup stderr file after reset
Close the backup file descriptor of original stderr once it has been
restored at stderr.
2021-09-10 17:01:26 +02:00
589b0733d9 Settings: set ok when reading configuration from sysconfdir
Without this htoprc from sysconfdir is ignored and default meters
are loaded.
2021-09-10 08:58:58 +02:00
c9b58c7fbe Add missing end-of-line to htoprc file version mismatch warning 2021-09-08 12:13:56 +10:00
ca06e68037 Fix a typo in a configure.ac comment 2021-09-08 12:11:58 +10:00
a516e0852e Update configure to reflect rc2 in the version 2021-09-05 19:07:29 +02:00
3f805cf347 Highlight large percentages similar to large memory columns 2021-09-05 18:49:05 +02:00
556d7c03e8 Add a Process_printPercentage helper routine
Replace several open-coded variants of percentage formatting.
This function has been ported from Hishams old 'next' branch.
2021-09-05 18:47:07 +02:00
0925c54caa Drop redundant semicolons 2021-09-04 14:17:37 +02:00
d0f5b61aa5 hwloc: use int in hwloc_bitmap_foreach_begin loop
Affinity.c:67:10: runtime error: implicit conversion from type 'int' of value -1 (32-bit, signed) to type 'unsigned int' changed the value to 4294967295 (32-bit, unsigned)
2021-09-04 09:48:53 +02:00
284f8c5e0b configure: output vserver as implied if ancient-vserver is enabled
The build time configuration ancient-vserver implies the configuration
vserver; say so in the configure status report if only ancient-vserver
has been specified.

Also indent with 3 spaces.
2021-09-04 09:48:53 +02:00
11b65a2861 Header: use upper case floating point suffix 2021-09-04 09:48:53 +02:00
b85b718e69 Settings: enclose casted macro argument in parenthesis 2021-09-04 09:48:53 +02:00
7891cdc552 Reduce variable scope
Also avoid declaring variables of different type, pointer and array, in
the same line.
2021-09-04 09:48:53 +02:00
b9fdf1c2a1 ColumnsPanel: drop unused struct declaration 2021-09-04 09:48:53 +02:00
2844429f15 NetBSD: scale CPU frequencies
Use a value type of 'long int' to avoid ENOMEM failures of sysctl(3).

Also check for "machdep.tsc_freq", scaled in MHz.
2021-09-03 12:17:45 +02:00
3834f2a68f NetBSD: misc const additions 2021-09-03 12:17:45 +02:00
343c2e58be NetBSD: update process fields
Populate fields to ttyname, minflt, majflt and processor.
2021-09-03 12:17:45 +02:00
fd2c4f5ddd NetBSD: fix reading environment variables of processes
kvm_getenvv(3) seems not to work with kvm_openfiles(..., KVM_NO_FILES,
...)
2021-09-03 12:17:45 +02:00
7f95ed8528 NetBSD: simplify Platform_setMemoryValues 2021-09-03 12:17:45 +02:00
9579d9b7aa NetBSD: silence dropping const qualifier on define_key(3)
On NetBSD define_key(3) has the prototype

    int define_key(char *sequence, int key_symbol);
2021-09-03 12:17:45 +02:00
0580dbb202 NetBSD: color process state P as running
On NetBSD state 'R' means runnable not running.

Improve the color identifier name accordingly.
2021-09-03 12:17:45 +02:00
c0c2bb98a2 Add completion handling for dynamic meters and columns
Be sure to free dynamic memory allocated for meters and
columns strings, no-op on platforms other than pcp.

Closes #774
2021-09-03 09:47:01 +02:00
9b30870eec Merge pull request #775 from cgzones/pcp
PCP: do not set exe to empty string
2021-09-03 12:08:00 +10:00
25c945e2ef PCP: do not set exe to empty string
In case the executable is an empty string, e.g. if pcp is run by an
unprivileged user, do not set procExe to an empty value, which breaks
the formatting of the PROCEXE column and the merged-cmdline logic.
2021-09-02 23:37:53 +02:00
f94934472f Linux: rework disk-io parsing
Generalize sub-diskname handling, like sdb1/sdb2, to not count the
usage twice with the aggregate top-diskname, like sdb.
Rely on /proc/diskstats being ordered, e.g. no sub-diskname precedes its
top-diskname.

Closes: #675
2021-09-02 22:12:58 +02:00
becd33795c Settings: create default meters on no existing config file
If htop is started for the first time and no configuration file exists
the header is empty cause no meters are added as a default.

Add the default meters if parsing all available configuration paths
failed.
2021-09-02 08:03:21 +02:00
74f99e3693 linux: simplify recheck condition
`recheck` is calculated modulo 2048, so its maximum value is 2047.
Drop the quite similar (up to 27 milliseconds) explicit check against
2000.
2021-09-02 08:03:21 +02:00
e1f4645bd1 Process: drop unused merged-command bit fields 2021-09-02 08:03:21 +02:00
0afd0fe572 linux: color void delay accounting values gray
Use the color gray, similar to other process fields, if the delay
accounting value is either 0 (or very small) or cannot be accessed, e.g.
by an unprivileged user.
2021-09-02 08:03:21 +02:00
ff336b652c linux: drop unused macro IOPriority_error 2021-09-02 08:03:21 +02:00
58a59c11f4 linux: drop dead process field column DIRTY
The field for dirty pages in /proc/[pid]/statm is always 0 since Linux
2.6 (see man:proc(5)).
2021-09-02 08:03:21 +02:00
3f806368e0 CPUMeter: use correct buffer size 2021-09-02 08:03:21 +02:00
4855d92469 travis-ci: update
Drop explicit CFLAGS specification as `-Wno-c11-extensions` is enabled
on FreeBSD by the configure script.

Run and check `make install` and `make installcheck`.
2021-09-02 08:03:21 +02:00
b81bb9038c Fix resource leaks dealing with unrecognised config file version
Plug leaks of an open file descriptor and dynamically allocated
'option' when we bail out early reading unknown config version.
2021-08-31 08:08:01 +02:00
1f2f567ca1 Fix meterPanels size calculation for dynamic array allocation 2021-08-31 08:08:01 +02:00
393330239e Small editorial fixes to ChangeLog 2021-08-27 09:31:06 +02:00
d2c34259b4 Remove trailing whitespace in changelog for CI checks 2021-08-27 12:19:50 +10:00
858ad8029d Update configure to reflect rc1 in the version 2021-08-27 12:14:23 +10:00
221 changed files with 4374 additions and 1945 deletions

51
.github/workflows/build_release.yml vendored Normal file
View File

@ -0,0 +1,51 @@
name: Build Source Release
# Trigger whenever a release is created
on:
release:
types:
- created
jobs:
build:
name: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
submodules: true
- name: archive
id: archive
run: |
VERSION=${{ github.event.release.tag_name }}
PKGNAME="htop-$VERSION"
SHASUM=$PKGNAME.tar.xz.sha256
autoreconf -i
mkdir -p /tmp/$PKGNAME
mv * /tmp/$PKGNAME
mv /tmp/$PKGNAME .
TARBALL=$PKGNAME.tar.xz
tar cJf $TARBALL $PKGNAME
sha256sum $TARBALL > $SHASUM
echo "::set-output name=tarball::$TARBALL"
echo "::set-output name=shasum::$SHASUM"
- name: upload tarball
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./${{ steps.archive.outputs.tarball }}
asset_name: ${{ steps.archive.outputs.tarball }}
asset_content_type: application/gzip
- name: upload shasum
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./${{ steps.archive.outputs.shasum }}
asset_name: ${{ steps.archive.outputs.shasum }}
asset_content_type: text/plain

View File

@ -113,6 +113,10 @@ jobs:
build-ubuntu-latest-pcp:
# Turns out 'ubuntu-latest' can be older than 20.04, we want PCP v5+
runs-on: ubuntu-20.04
env:
# Until Ubuntu catches up with pcp-5.2.3+:
# pcp/Platform.c:309:45: warning: passing argument 2 of pmLookupName from incompatible pointer type [-Wincompatible-pointer-types]
CFLAGS: -Wno-error=incompatible-pointer-types
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
@ -120,9 +124,7 @@ jobs:
- name: Bootstrap
run: ./autogen.sh
- name: Configure
# Until Ubuntu catches up with pcp-5.2.3+, cannot use -werror due to:
# passing argument 2 of pmLookupName from incompatible pointer type
run: ./configure --enable-pcp --enable-unicode
run: ./configure --enable-werror --enable-pcp --enable-unicode
- name: Build
run: make -k

View File

@ -9,7 +9,8 @@ os:
script:
- ./autogen.sh
# clang might warn about C11 generic selections in isnan()
- CFLAGS=-Wno-c11-extensions ./configure --enable-werror
- ./configure --enable-werror
- make -k
- CFLAGS=-Wno-c11-extensions make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
- make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
- sudo make install
- make installcheck

170
Action.c
View File

@ -1,7 +1,7 @@
/*
htop - Action.c
(C) 2015 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -58,7 +58,7 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
header->pl->following = pid;
unfollow = true;
}
ScreenManager_run(scr, &panelFocus, &ch);
ScreenManager_run(scr, &panelFocus, &ch, NULL);
if (unfollow) {
header->pl->following = -1;
}
@ -85,9 +85,10 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
static void Action_runSetup(State* st) {
ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true);
CategoriesPanel_new(scr, st->settings, st->header, st->pl);
ScreenManager_run(scr, NULL, NULL);
ScreenManager_run(scr, NULL, NULL, "Setup");
ScreenManager_delete(scr);
if (st->settings->changed) {
CRT_setMouse(st->settings->enableMouse);
Header_writeBackToSettings(st->header);
}
}
@ -154,7 +155,7 @@ static bool collapseIntoParent(Panel* panel) {
}
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
Settings_setSortKey(settings, sortKey);
ScreenSettings_setSortKey(settings->ss, sortKey);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
}
@ -164,8 +165,9 @@ static Htop_Reaction actionSetSortColumn(State* st) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by");
const ProcessField* fields = st->settings->fields;
Hashtable* dynamicColumns = st->settings->dynamicColumns;
const Settings* settings = st->settings;
const ProcessField* fields = settings->ss->fields;
Hashtable* dynamicColumns = settings->dynamicColumns;
for (int i = 0; fields[i]; i++) {
char* name = NULL;
if (fields[i] >= LAST_PROCESSFIELD) {
@ -177,7 +179,7 @@ static Htop_Reaction actionSetSortColumn(State* st) {
name = String_trim(Process_fields[fields[i]].name);
}
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == Settings_getActiveSortKey(st->settings))
if (fields[i] == ScreenSettings_getActiveSortKey(settings->ss))
Panel_setSelected(sortPanel, i);
free(name);
@ -188,8 +190,7 @@ static Htop_Reaction actionSetSortColumn(State* st) {
}
Object_delete(sortPanel);
if (st->pauseProcessUpdate)
ProcessList_sort(st->pl);
st->pl->needsSort = true;
return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
@ -231,16 +232,21 @@ static Htop_Reaction actionToggleMergedCommand(State* st) {
}
static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView;
ScreenSettings* ss = st->settings->ss;
ss->treeView = !ss->treeView;
if (!st->settings->allBranchesCollapsed)
if (!ss->allBranchesCollapsed)
ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) {
st->settings->allBranchesCollapsed = !st->settings->allBranchesCollapsed;
if (st->settings->allBranchesCollapsed)
ScreenSettings* ss = st->settings->ss;
if (!ss->treeView) {
return HTOP_OK;
}
ss->allBranchesCollapsed = !ss->allBranchesCollapsed;
if (ss->allBranchesCollapsed)
ProcessList_collapseAllBranches(st->pl);
else
ProcessList_expandTree(st->pl);
@ -277,9 +283,8 @@ static Htop_Reaction actionLowerPriority(State* st) {
}
static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings);
if (st->pauseProcessUpdate)
ProcessList_sort(st->pl);
ScreenSettings_invertSortOrder(st->settings->ss);
st->pl->needsSort = true;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING;
}
@ -289,7 +294,7 @@ static Htop_Reaction actionExpandOrCollapse(State* st) {
}
static Htop_Reaction actionCollapseIntoParent(State* st) {
if (!st->settings->treeView) {
if (!st->settings->ss->treeView) {
return HTOP_OK;
}
bool changed = collapseIntoParent((Panel*)st->mainPanel);
@ -297,7 +302,46 @@ static Htop_Reaction actionCollapseIntoParent(State* st) {
}
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}
static Htop_Reaction actionNextScreen(State* st) {
Settings* settings = st->settings;
settings->ssIndex++;
if (settings->ssIndex == settings->nScreens) {
settings->ssIndex = 0;
}
settings->ss = settings->screens[settings->ssIndex];
return HTOP_REFRESH;
}
static Htop_Reaction actionPrevScreen(State* st) {
Settings* settings = st->settings;
if (settings->ssIndex == 0) {
settings->ssIndex = settings->nScreens - 1;
} else {
settings->ssIndex--;
}
settings->ss = settings->screens[settings->ssIndex];
return HTOP_REFRESH;
}
Htop_Reaction Action_setScreenTab(Settings* settings, int x) {
int s = 2;
for (unsigned int i = 0; i < settings->nScreens; i++) {
if (x < s) {
return 0;
}
const char* name = settings->screens[i]->name;
int len = strlen(name);
if (x <= s + len + 1) {
settings->ssIndex = i;
settings->ss = settings->screens[i];
return HTOP_REFRESH;
}
s += len + 3;
}
return 0;
}
static Htop_Reaction actionQuit(ATTR_UNUSED State* st) {
@ -344,9 +388,12 @@ static Htop_Reaction actionKill(State* st) {
if (Settings_isReadonly())
return HTOP_OK;
Panel* signalsPanel = SignalsPanel_new();
static int preSelectedSignal = SIGNALSPANEL_INITSELECTEDSIGNAL;
Panel* signalsPanel = SignalsPanel_new(preSelectedSignal);
const ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 14, true);
if (sgn && sgn->key != 0) {
preSelectedSignal = sgn->key;
Panel_setHeader((Panel*)st->mainPanel, "Sending...");
Panel_draw((Panel*)st->mainPanel, false, true, true, State_hideFunctionBar(st));
refresh();
@ -459,6 +506,7 @@ static const struct {
bool roInactive;
const char* info;
} helpLeft[] = {
{ .key = " Tab: ", .roInactive = false, .info = "switch to next screen tab" },
{ .key = " Arrows: ", .roInactive = false, .info = "scroll process list" },
{ .key = " Digits: ", .roInactive = false, .info = "incremental PID search" },
{ .key = " F3 /: ", .roInactive = false, .info = "incremental name search" },
@ -483,12 +531,13 @@ static const struct {
bool roInactive;
const char* info;
} helpRight[] = {
{ .key = " S-Tab: ", .roInactive = false, .info = "switch to previous screen tab" },
{ .key = " Space: ", .roInactive = false, .info = "tag process" },
{ .key = " c: ", .roInactive = false, .info = "tag process and its children" },
{ .key = " U: ", .roInactive = false, .info = "untag all processes" },
{ .key = " F9 k: ", .roInactive = true, .info = "kill process/tagged processes" },
{ .key = " F7 ]: ", .roInactive = true, .info = "higher priority (root only)" },
{ .key = " F8 [: ", .roInactive = false, .info = "lower priority (+ nice)" },
{ .key = " F8 [: ", .roInactive = true, .info = "lower priority (+ nice)" },
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
{ .key = " a: ", .roInactive = true, .info = "set CPU affinity" },
#endif
@ -519,52 +568,63 @@ static Htop_Reaction actionHelp(State* st) {
int line = 0;
mvaddstr(line++, 0, "htop " VERSION " - " COPYRIGHT);
mvaddstr(line++, 0, "Released under the GNU GPLv2. See 'man' page for more info.");
mvaddstr(line++, 0, "Released under the GNU GPLv2+. See 'man' page for more info.");
attrset(CRT_colors[DEFAULT_COLOR]);
line++;
mvaddstr(line++, 0, "CPU usage bar: ");
#define addbartext(attr, prefix, text) \
do { \
addattrstr(CRT_colors[DEFAULT_COLOR], prefix); \
addattrstr(attr, text); \
} while(0)
addattrstr(CRT_colors[BAR_BORDER], "[");
addbartext(CRT_colors[CPU_NICE_TEXT], "", "low");
addbartext(CRT_colors[CPU_NORMAL], "/", "normal");
addbartext(CRT_colors[CPU_SYSTEM], "/", "kernel");
if (st->settings->detailedCPUTime) {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/");
addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/");
addattrstr(CRT_colors[CPU_IOWAIT], "io-wait");
addattrstr(CRT_colors[BAR_SHADOW], " used%");
addbartext(CRT_colors[CPU_IRQ], "/", "irq");
addbartext(CRT_colors[CPU_SOFTIRQ], "/", "soft-irq");
addbartext(CRT_colors[CPU_STEAL], "/", "steal");
addbartext(CRT_colors[CPU_GUEST], "/", "guest");
addbartext(CRT_colors[CPU_IOWAIT], "/", "io-wait");
addbartext(CRT_colors[BAR_SHADOW], " ", "used%");
} else {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_GUEST], "virtualized");
addattrstr(CRT_colors[BAR_SHADOW], " used%");
addbartext(CRT_colors[CPU_GUEST], "/", "guest");
addbartext(CRT_colors[BAR_SHADOW], " ", "used%");
}
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line++, 0, "Memory bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/");
addattrstr(CRT_colors[MEMORY_SHARED], "shared"); addstr("/");
addattrstr(CRT_colors[MEMORY_CACHE], "cache");
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addbartext(CRT_colors[MEMORY_USED], "", "used");
addbartext(CRT_colors[MEMORY_BUFFERS_TEXT], "/", "buffers");
addbartext(CRT_colors[MEMORY_SHARED], "/", "shared");
addbartext(CRT_colors[MEMORY_CACHE], "/", "cache");
addbartext(CRT_colors[BAR_SHADOW], " ", "used");
addbartext(CRT_colors[BAR_SHADOW], "/", "total");
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line++, 0, "Swap bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[SWAP], "used");
addbartext(CRT_colors[SWAP], "", "used");
#ifdef HTOP_LINUX
addattrstr(CRT_colors[BAR_SHADOW], "/");
addattrstr(CRT_colors[SWAP_CACHE], "cache");
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addbartext(CRT_colors[SWAP_CACHE], "/", "cache");
#else
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addbartext(CRT_colors[SWAP_CACHE], " ", "");
#endif
addbartext(CRT_colors[BAR_SHADOW], " ", "used");
addbartext(CRT_colors[BAR_SHADOW], "/", "total");
addattrstr(CRT_colors[BAR_BORDER], "]");
line++;
#undef addbartext
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen.");
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
@ -572,9 +632,23 @@ static Htop_Reaction actionHelp(State* st) {
}
line++;
mvaddstr(line++, 0, "Process state: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep");
#define addattrstatestr(attr, state, desc) \
do { \
addattrstr(attr, state); \
addattrstr(CRT_colors[DEFAULT_COLOR], ": " desc); \
} while(0)
line++;
mvaddstr(line, 0, "Process state: ");
addattrstatestr(CRT_colors[PROCESS_RUN_STATE], "R", "running; ");
addattrstatestr(CRT_colors[PROCESS_SHADOW], "S", "sleeping; ");
addattrstatestr(CRT_colors[PROCESS_RUN_STATE], "t", "traced/stopped; ");
addattrstatestr(CRT_colors[PROCESS_D_STATE], "Z", "zombie; ");
addattrstatestr(CRT_colors[PROCESS_D_STATE], "D", "disk sleep");
attrset(CRT_colors[DEFAULT_COLOR]);
#undef addattrstatestr
line += 2;
const bool readonly = Settings_isReadonly();
@ -711,4 +785,6 @@ void Action_setBindings(Htop_Action* keys) {
keys[KEY_F(10)] = actionQuit;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys[KEY_RECLICK] = actionExpandOrCollapse;
keys[KEY_SHIFT_TAB] = actionPrevScreen;
keys['\t'] = actionNextScreen;
}

View File

@ -3,7 +3,7 @@
/*
htop - Action.h
(C) 2015 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -57,6 +57,8 @@ bool Action_setUserOnly(const char* userName, uid_t* userId);
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
Htop_Reaction Action_setScreenTab(Settings* settings, int x);
Htop_Reaction Action_follow(State* st);
void Action_setBindings(Htop_Action* keys);

View File

@ -2,7 +2,7 @@
htop - Affinity.c
(C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -63,9 +63,9 @@ Affinity* Affinity_get(const Process* proc, ProcessList* pl) {
Affinity_add(affinity, i);
}
} else {
unsigned int id;
hwloc_bitmap_foreach_begin(id, cpuset);
Affinity_add(affinity, id);
int id;
hwloc_bitmap_foreach_begin(id, cpuset)
Affinity_add(affinity, (unsigned)id);
hwloc_bitmap_foreach_end();
}
}

View File

@ -4,7 +4,7 @@
htop - Affinity.h
(C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - AffinityPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -425,9 +425,9 @@ Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) {
Affinity* affinity = Affinity_new(pl);
#ifdef HAVE_LIBHWLOC
unsigned int i;
int i;
hwloc_bitmap_foreach_begin(i, this->workCpuset)
Affinity_add(affinity, i);
Affinity_add(affinity, (unsigned)i);
hwloc_bitmap_foreach_end();
#else
for (int i = 0; i < Vector_size(this->cpuids); i++) {

View File

@ -3,7 +3,7 @@
/*
htop - AffinityPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - AvailableColumnsPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -3,7 +3,7 @@
/*
htop - AvailableColumnsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - AvailableMetersPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -79,6 +79,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
}
if (update) {
this->settings->changed = true;
this->settings->lastUpdate++;
Header_calculateHeight(header);
Header_updateData(header);
Header_draw(header);

View File

@ -3,7 +3,7 @@
/*
htop - AvailableMetersPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - BatteryMeter.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).

View File

@ -3,7 +3,7 @@
/*
htop - BatteryMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).

View File

@ -1,7 +1,7 @@
/*
htop - CPUMeter.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -52,9 +52,9 @@ static void CPUMeter_init(Meter* this) {
// Custom uiName runtime logic to include the param (processor)
static void CPUMeter_getUiName(const Meter* this, char* buffer, size_t length) {
if (this->param > 0)
xSnprintf(buffer, sizeof(length), "%s %u", Meter_uiName(this), this->param);
xSnprintf(buffer, length, "%s %u", Meter_uiName(this), this->param);
else
xSnprintf(buffer, sizeof(length), "%s", Meter_uiName(this));
xSnprintf(buffer, length, "%s", Meter_uiName(this));
}
static void CPUMeter_updateValues(Meter* this) {
@ -381,6 +381,7 @@ const MeterClass AllCPUs2Meter_class = {
},
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "AllCPUs2",
@ -401,6 +402,7 @@ const MeterClass LeftCPUsMeter_class = {
},
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs",
@ -421,6 +423,7 @@ const MeterClass RightCPUsMeter_class = {
},
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "RightCPUs",
@ -441,6 +444,7 @@ const MeterClass LeftCPUs2Meter_class = {
},
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs2",
@ -461,6 +465,7 @@ const MeterClass RightCPUs2Meter_class = {
},
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "RightCPUs2",
@ -481,6 +486,7 @@ const MeterClass AllCPUs4Meter_class = {
},
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "AllCPUs4",
@ -501,6 +507,7 @@ const MeterClass LeftCPUs4Meter_class = {
},
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs4",
@ -521,6 +528,7 @@ const MeterClass RightCPUs4Meter_class = {
},
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "RightCPUs4",
@ -541,6 +549,7 @@ const MeterClass AllCPUs8Meter_class = {
},
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "AllCPUs8",
@ -561,6 +570,7 @@ const MeterClass LeftCPUs8Meter_class = {
},
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs8",
@ -581,6 +591,7 @@ const MeterClass RightCPUs8Meter_class = {
},
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "RightCPUs8",

View File

@ -3,7 +3,7 @@
/*
htop - CPUMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

176
CRT.c
View File

@ -1,7 +1,7 @@
/*
htop - CRT.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -13,6 +13,7 @@ in the source distribution for its full text.
#include <fcntl.h>
#include <langinfo.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -21,14 +22,22 @@ in the source distribution for its full text.
#include "ProvideCurses.h"
#include "XUtils.h"
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
#if !defined(NDEBUG) && defined(HAVE_MEMFD_CREATE)
#include <sys/mman.h>
#endif
#if defined(HAVE_LIBUNWIND_H) && defined(HAVE_LIBUNWIND)
# define PRINT_BACKTRACE
# define UNW_LOCAL_ONLY
# include <libunwind.h>
# if defined(HAVE_DLADDR)
# include <dlfcn.h>
# endif
#elif defined(HAVE_EXECINFO_H)
# define PRINT_BACKTRACE
# include <execinfo.h>
#endif
#define ColorIndex(i,j) ((7-(i))*8+(j))
@ -138,7 +147,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_GIGABYTES] = ColorPair(Green, Black),
[PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Black),
[PROCESS_TREE] = ColorPair(Cyan, Black),
[PROCESS_R_STATE] = ColorPair(Green, Black),
[PROCESS_RUN_STATE] = ColorPair(Green, Black),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
@ -185,6 +194,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Magenta, Black),
[CPU_STEAL] = ColorPair(Cyan, Black),
[CPU_GUEST] = ColorPair(Cyan, Black),
[PANEL_EDIT] = ColorPair(White, Blue),
[SCREENS_OTH_BORDER] = ColorPair(Blue, Blue),
[SCREENS_OTH_TEXT] = ColorPair(Black, Blue),
[SCREENS_CUR_BORDER] = ColorPair(Green, Green),
[SCREENS_CUR_TEXT] = ColorPair(Black, Green),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Cyan, Black),
[PRESSURE_STALL_SIXTY] = A_BOLD | ColorPair(Cyan, Black),
[PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Black),
@ -240,7 +254,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_GIGABYTES] = A_BOLD,
[PROCESS_BASENAME] = A_BOLD,
[PROCESS_TREE] = A_BOLD,
[PROCESS_R_STATE] = A_BOLD,
[PROCESS_RUN_STATE] = A_BOLD,
[PROCESS_D_STATE] = A_BOLD,
[PROCESS_HIGH_PRIORITY] = A_BOLD,
[PROCESS_LOW_PRIORITY] = A_DIM,
@ -287,6 +301,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = A_BOLD,
[CPU_STEAL] = A_DIM,
[CPU_GUEST] = A_DIM,
[PANEL_EDIT] = A_BOLD,
[SCREENS_OTH_BORDER] = A_DIM,
[SCREENS_OTH_TEXT] = A_DIM,
[SCREENS_CUR_BORDER] = A_REVERSE,
[SCREENS_CUR_TEXT] = A_REVERSE,
[PRESSURE_STALL_THREEHUNDRED] = A_DIM,
[PRESSURE_STALL_SIXTY] = A_NORMAL,
[PRESSURE_STALL_TEN] = A_BOLD,
@ -342,7 +361,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_GIGABYTES] = ColorPair(Green, White),
[PROCESS_BASENAME] = ColorPair(Blue, White),
[PROCESS_TREE] = ColorPair(Green, White),
[PROCESS_R_STATE] = ColorPair(Green, White),
[PROCESS_RUN_STATE] = ColorPair(Green, White),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, White),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, White),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, White),
@ -389,6 +408,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue, White),
[CPU_STEAL] = ColorPair(Cyan, White),
[CPU_GUEST] = ColorPair(Cyan, White),
[PANEL_EDIT] = ColorPair(White,Blue),
[SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Black,White),
[SCREENS_OTH_TEXT] = A_BOLD | ColorPair(Black,White),
[SCREENS_CUR_BORDER] = ColorPair(Green,Green),
[SCREENS_CUR_TEXT] = ColorPair(Black,Green),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, White),
[PRESSURE_STALL_SIXTY] = ColorPair(Black, White),
[PRESSURE_STALL_TEN] = ColorPair(Black, White),
@ -444,7 +468,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_GIGABYTES] = ColorPair(Green, Black),
[PROCESS_BASENAME] = ColorPair(Green, Black),
[PROCESS_TREE] = ColorPair(Blue, Black),
[PROCESS_R_STATE] = ColorPair(Green, Black),
[PROCESS_RUN_STATE] = ColorPair(Green, Black),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
@ -491,6 +515,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue, Black),
[CPU_STEAL] = ColorPair(Black, Black),
[CPU_GUEST] = ColorPair(Black, Black),
[PANEL_EDIT] = ColorPair(White,Blue),
[SCREENS_OTH_BORDER] = ColorPair(Blue,Black),
[SCREENS_OTH_TEXT] = ColorPair(Blue,Black),
[SCREENS_CUR_BORDER] = ColorPair(Green,Green),
[SCREENS_CUR_TEXT] = ColorPair(Black,Green),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, Black),
[PRESSURE_STALL_SIXTY] = ColorPair(Black, Black),
[PRESSURE_STALL_TEN] = ColorPair(Black, Black),
@ -546,7 +575,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_GIGABYTES] = ColorPair(Green, Blue),
[PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Blue),
[PROCESS_TREE] = ColorPair(Cyan, Blue),
[PROCESS_R_STATE] = ColorPair(Green, Blue),
[PROCESS_RUN_STATE] = ColorPair(Green, Blue),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Blue),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Blue),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Blue),
@ -593,6 +622,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Black, Blue),
[CPU_STEAL] = ColorPair(White, Blue),
[CPU_GUEST] = ColorPair(White, Blue),
[PANEL_EDIT] = ColorPair(White,Blue),
[SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Yellow,Blue),
[SCREENS_OTH_TEXT] = ColorPair(Cyan,Blue),
[SCREENS_CUR_BORDER] = ColorPair(Cyan,Cyan),
[SCREENS_CUR_TEXT] = ColorPair(Black,Cyan),
[PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black, Blue),
[PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White, Blue),
[PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Blue),
@ -652,7 +686,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black),
[PROCESS_COMM] = ColorPair(Magenta, Black),
[PROCESS_THREAD_COMM] = ColorPair(Yellow, Black),
[PROCESS_R_STATE] = ColorPair(Green, Black),
[PROCESS_RUN_STATE] = ColorPair(Green, Black),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
@ -693,6 +727,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue, Black),
[CPU_STEAL] = ColorPair(Cyan, Black),
[CPU_GUEST] = ColorPair(Cyan, Black),
[PANEL_EDIT] = ColorPair(White,Cyan),
[SCREENS_OTH_BORDER] = ColorPair(White,Black),
[SCREENS_OTH_TEXT] = ColorPair(Cyan,Black),
[SCREENS_CUR_BORDER] = A_BOLD | ColorPair(White,Black),
[SCREENS_CUR_TEXT] = A_BOLD | ColorPair(Green,Black),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green, Black),
[PRESSURE_STALL_SIXTY] = ColorPair(Green, Black),
[PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green, Black),
@ -717,8 +756,6 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
};
int CRT_cursorX = 0;
int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10;
@ -772,6 +809,8 @@ static void dumpStderr(void) {
fsync(STDERR_FILENO);
dup2(stderrRedirectBackupFd, STDERR_FILENO);
close(stderrRedirectBackupFd);
stderrRedirectBackupFd = -1;
lseek(stderrRedirectNewFd, 0, SEEK_SET);
bool header = false;
@ -806,6 +845,16 @@ static void dumpStderr(void) {
stderrRedirectNewFd = -1;
}
void CRT_debug_impl(const char* file, size_t lineno, const char* func, const char* fmt, ...) {
va_list args;
fprintf(stderr, "[%s:%zu (%s)]: ", file, lineno, func);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
}
#else /* !NDEBUG */
static void redirectStderr(void) {
@ -831,6 +880,7 @@ static void CRT_installSignalHandlers(void) {
sigaction (SIGSYS, &act, &old_sig_handler[SIGSYS]);
sigaction (SIGABRT, &act, &old_sig_handler[SIGABRT]);
signal(SIGCHLD, SIG_DFL);
signal(SIGINT, CRT_handleSIGTERM);
signal(SIGTERM, CRT_handleSIGTERM);
signal(SIGQUIT, CRT_handleSIGTERM);
@ -850,6 +900,20 @@ void CRT_resetSignalHandlers(void) {
signal(SIGQUIT, SIG_DFL);
}
void CRT_setMouse(bool enabled) {
#ifdef HAVE_GETMOUSE
if (enabled) {
#if NCURSES_MOUSE_VERSION > 1
mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
#else
mousemask(BUTTON1_RELEASED, NULL);
#endif
} else {
mousemask(0, NULL);
}
#endif
}
void CRT_init(const Settings* settings, bool allowUnicode) {
redirectStderr();
@ -886,6 +950,10 @@ void CRT_init(const Settings* settings, bool allowUnicode) {
}
if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) {
#ifdef HTOP_NETBSD
#define define_key(s_, k_) define_key((char*)s_, k_)
IGNORE_WCASTQUAL_BEGIN
#endif
define_key("\033[H", KEY_HOME);
define_key("\033[F", KEY_END);
define_key("\033[7~", KEY_HOME);
@ -901,11 +969,19 @@ void CRT_init(const Settings* settings, bool allowUnicode) {
define_key("\033[14~", KEY_F(4));
define_key("\033[14;2~", KEY_F(15));
define_key("\033[17;2~", KEY_F(18));
define_key("\033[Z", KEY_SHIFT_TAB);
char sequence[3] = "\033a";
for (char c = 'a'; c <= 'z'; c++) {
sequence[1] = c;
define_key(sequence, KEY_ALT('A' + (c - 'a')));
}
#ifdef HTOP_NETBSD
IGNORE_WCASTQUAL_END
#undef define_key
#endif
}
if (termType && (String_startsWith(termType, "rxvt"))) {
define_key("\033[Z", KEY_SHIFT_TAB);
}
CRT_installSignalHandlers();
@ -931,18 +1007,17 @@ void CRT_init(const Settings* settings, bool allowUnicode) {
#endif
CRT_treeStrAscii;
#ifdef HAVE_GETMOUSE
#if NCURSES_MOUSE_VERSION > 1
mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
#else
mousemask(BUTTON1_RELEASED, NULL);
#endif
#endif
CRT_setMouse(settings->enableMouse);
CRT_degreeSign = initDegreeSign();
}
void CRT_done() {
attron(CRT_colors[RESET_COLOR]);
mvhline(LINES - 1, 0, ' ', COLS);
attroff(CRT_colors[RESET_COLOR]);
refresh();
curs_set(1);
endwin();
@ -998,6 +1073,59 @@ void CRT_setColors(int colorScheme) {
CRT_colors = CRT_colorSchemes[colorScheme];
}
#ifdef PRINT_BACKTRACE
static void print_backtrace(void) {
#if defined(HAVE_LIBUNWIND_H) && defined(HAVE_LIBUNWIND)
unw_context_t context;
unw_getcontext(&context);
unw_cursor_t cursor;
unw_init_local(&cursor, &context);
unsigned int item = 0;
while (unw_step(&cursor) > 0) {
unw_word_t pc;
unw_get_reg(&cursor, UNW_REG_IP, &pc);
if (pc == 0)
break;
char symbolName[256] = "?";
unw_word_t offset = 0;
unw_get_proc_name(&cursor, symbolName, sizeof(symbolName), &offset);
unw_proc_info_t pip;
pip.unwind_info = NULL;
const char* fname = "?";
const void* ptr = 0;
if (unw_get_proc_info(&cursor, &pip) == 0) {
ptr = (const void*)(pip.start_ip + offset);
#ifdef HAVE_DLADDR
Dl_info dlinfo;
if (dladdr(ptr, &dlinfo) && dlinfo.dli_fname && *dlinfo.dli_fname)
fname = dlinfo.dli_fname;
#endif
}
const char* frame = "";
if (unw_is_signal_frame(&cursor) > 0)
frame = "{signal frame}";
fprintf(stderr, "%2u: %#14lx %s (%s+%#lx) [%p]%s%s\n", item++, pc, fname, symbolName, offset, ptr, frame ? " " : "", frame);
}
#elif defined(HAVE_EXECINFO_H)
void* backtraceArray[256];
size_t size = backtrace(backtraceArray, ARRAYSIZE(backtraceArray));
backtrace_symbols_fd(backtraceArray, size, STDERR_FILENO);
#else
#error No implementation for print_backtrace()!
#endif
}
#endif
void CRT_handleSIGSEGV(int signal) {
CRT_done();
@ -1012,7 +1140,7 @@ void CRT_handleSIGSEGV(int signal) {
" - Likely steps to reproduce (How did it happen?)\n"
);
#ifdef HAVE_EXECINFO_H
#ifdef PRINT_BACKTRACE
fprintf(stderr, " - Backtrace of the issue (see below)\n");
#endif
@ -1038,16 +1166,14 @@ void CRT_handleSIGSEGV(int signal) {
Settings_write(CRT_crashSettings, true);
fprintf(stderr, "\n\n");
#ifdef HAVE_EXECINFO_H
#ifdef PRINT_BACKTRACE
fprintf(stderr,
"Backtrace information:\n"
"----------------------\n"
);
void* backtraceArray[256];
print_backtrace();
size_t size = backtrace(backtraceArray, ARRAYSIZE(backtraceArray));
backtrace_symbols_fd(backtraceArray, size, STDERR_FILENO);
fprintf(stderr,
"\n"
"To make the above information more practical to work with, "

25
CRT.h
View File

@ -3,7 +3,7 @@
/*
htop - CRT.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -74,7 +74,7 @@ typedef enum ColorElements_ {
PROCESS_MEGABYTES,
PROCESS_GIGABYTES,
PROCESS_TREE,
PROCESS_R_STATE,
PROCESS_RUN_STATE,
PROCESS_D_STATE,
PROCESS_BASENAME,
PROCESS_HIGH_PRIORITY,
@ -120,6 +120,11 @@ typedef enum ColorElements_ {
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
PANEL_EDIT,
SCREENS_OTH_BORDER,
SCREENS_OTH_TEXT,
SCREENS_CUR_BORDER,
SCREENS_CUR_TEXT,
PRESSURE_STALL_TEN,
PRESSURE_STALL_SIXTY,
PRESSURE_STALL_THREEHUNDRED,
@ -145,11 +150,19 @@ typedef enum ColorElements_ {
void CRT_fatalError(const char* note) ATTR_NORETURN;
#ifdef NDEBUG
# define CRT_debug(...)
#else
void CRT_debug_impl(const char* file, size_t lineno, const char* func, const char* fmt, ...) ATTR_FORMAT(printf, 4, 5);
# define CRT_debug(...) CRT_debug_impl(__FILE__, __LINE__, __func__, __VA_ARGS__)
#endif
void CRT_handleSIGSEGV(int signal) ATTR_NORETURN;
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
#define KEY_RECLICK KEY_F(22)
#define KEY_WHEELUP KEY_F(30)
#define KEY_WHEELDOWN KEY_F(31)
#define KEY_RECLICK KEY_F(32)
#define KEY_SHIFT_TAB KEY_F(33)
#define KEY_ALT(x) (KEY_F(64 - 26) + ((x) - 'A'))
extern const char* CRT_degreeSign;
@ -172,6 +185,8 @@ extern int CRT_scrollWheelVAmount;
extern ColorScheme CRT_colorScheme;
void CRT_setMouse(bool enabled);
void CRT_init(const Settings* settings, bool allowUnicode);
void CRT_done(void);

View File

@ -1,7 +1,7 @@
/*
htop - CategoriesPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -14,7 +14,6 @@ in the source distribution for its full text.
#include "AvailableColumnsPanel.h"
#include "AvailableMetersPanel.h"
#include "ColorsPanel.h"
#include "ColumnsPanel.h"
#include "DisplayOptionsPanel.h"
#include "FunctionBar.h"
#include "Header.h"
@ -25,6 +24,7 @@ in the source distribution for its full text.
#include "MetersPanel.h"
#include "Object.h"
#include "ProvideCurses.h"
#include "ScreensPanel.h"
#include "Vector.h"
#include "XUtils.h"
@ -40,7 +40,7 @@ static void CategoriesPanel_delete(Object* object) {
static void CategoriesPanel_makeMetersPage(CategoriesPanel* this) {
size_t columns = HeaderLayout_getColumns(this->scr->header->headerLayout);
MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel));
MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel*));
for (size_t i = 0; i < columns; i++) {
char titleBuffer[32];
@ -69,9 +69,11 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
ScreenManager_add(this->scr, colors, -1);
}
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) {
Panel* screens = (Panel*) ScreensPanel_new(this->settings);
Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns;
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns, this->settings->dynamicColumns);
ScreenManager_add(this->scr, screens, 20);
ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, -1);
}
@ -91,7 +93,7 @@ static const CategoriesPanelPage categoriesPanelPages[] = {
{ .name = "Display options", .ctor = CategoriesPanel_makeDisplayOptionsPage },
{ .name = "Header layout", .ctor = CategoriesPanel_makeHeaderOptionsPage },
{ .name = "Meters", .ctor = CategoriesPanel_makeMetersPage },
{ .name = "Columns", .ctor = CategoriesPanel_makeColumnsPage },
{ .name = "Screens", .ctor = CategoriesPanel_makeScreensPage },
{ .name = "Colors", .ctor = CategoriesPanel_makeColorsPage },
};
@ -157,7 +159,7 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
this->settings = settings;
this->header = header;
this->pl = pl;
Panel_setHeader(super, "Setup");
Panel_setHeader(super, "Categories");
for (size_t i = 0; i < ARRAYSIZE(categoriesPanelPages); i++)
Panel_add(super, (Object*) ListItem_new(categoriesPanelPages[i].name, 0));

View File

@ -3,7 +3,7 @@
/*
htop - CategoriesPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

105
ChangeLog
View File

@ -1,5 +1,91 @@
What's new in version 3.1.0
What's new in version 3.2.1
* Fix setting to show all branches collapsed by default
* Restore functionality of stripExeFromCmdline setting
* Fix some command line display settings not being honored without restart
* Display single digit precision for CPU% greater than 99.9%
* On Linux, FreeBSD and PCP consider only shrinkable ZFS ARC as cache
* On Linux, increase field width of CPUD% and SWAPD% columns
* Colorize process state characters in help screen
* Use mousemask(3X) to enable and disable mouse control
* Fix heap buffer overflow in Vector_compact
* On Solaris, fix a process time scaling error
* On Solaris, fix the build
* On NetBSD, OpenBSD and Solaris ensure env buffer size is sufficient
* On Linux, resolve processes exiting interfering with sampling
* Fix ProcessList quadratic removal when scanning processes
* Under LXC, limit CPU count to that given by /proc/cpuinfo
* Improve container detection for LXC
* Some minor documentation fixes
What's new in version 3.2.0
* Support for displaying multiple tabs in the user interface
* Allow multiple filter and search terms (logical OR, separate by "|")
* Set correct default sorting direction (defaultSortDesc)
* Improve performance for process lookup and update
* Rework the IOMeters initial display
* Removed duplicate sections on COMM and EXE
* Highlight process UNINTERRUPTIBLE_WAIT state (D)
* Show only integer value when CPU% more than 99.9%
* Handle rounding ambiguity between 99.9 and 100.0%
* No longer leaves empty the last column in header
* Fix header layout and meters reset if a header column is empty
* Fix PID and UID column widths off-by-one error
* On Linux, read generic sysfs batteries
* On Linux, do not collect LRS per thread (it is process-wide)
* On Linux, dynamically adjust the SECATTR and CGROUP column widths
* On Linux, fix a crash in LXD
* On FreeBSD, add support for showing process emulation
* On Darwin, lazily set process TTY name
* Always set SIGCHLD to default handling
* Avoid zombie processes on signal races
* Ensure last line is cleared when SIGINT is received
* Instead of SIGTERM, pre-select the last sent signal
* Internal Hashtable performance and sizing improvements
* Add heuristics for guessing LXC or Docker from /proc/1/mounts
* Force elapsed time display to zero if process started in the future
* Avoid extremely large year values when printing time
* Fix division by zero when calculating IO rates
* Fix out of boundary writes in XUtils
* Fix custom thread name display issue
* Use AC_CANONICAL_HOST, not AC_CANONICAL_TARGET in configure.ac
* Support libunwind of LLVM
What's new in version 3.1.2
* Bugfix for crash when storing modified settings at exit
* Generate xz-compressed source tarball (with configure) using github actions
* Allow -u UID with numerical value as argument
* Added documentation for obsolete/state libraries/program files highlighting
* Some obsolete/stale library highlighting refinements
* Column width issues resolved
* Dynamic UID column sizing improved
* Discard stale information from Disk and Network I/O meters
* Refined Linux kernel thread detection
* Reworked process state handling
* New CCGROUP column showing abbreviated cgroup name
* New OFFSET column in the list of open files screen
What's new in version 3.1.1
* Update license headers to explicitly say GPLv2+
* Document minimum version for libcap (thanks to James Brown)
* Fix mouse wheel collision with autogroups nice adjustment
* Adjust Makefile.am macro definitions for older automake versions
* Ensure consistent reporting of MemoryMeter 'used' memory
* Report hugepage memory as real and used memory (as before)
* Handle procExeDeleted, usesDeletedLib without mergedCommandline mode
* Validate meter configuration before proceeding beyond htoprc parsing
* Properly release memory on partially read configuration
* Handle interrupted sampling from within libpcp PDU transfers
* On Linux, provide O_PATH value if not defined
* On Linux, always compute procExeDeleted if already set
* Workaround for Rosetta 2 on Darwin (thanks to Alexander Momchilov)
* Fix FreeBSD cmdline memory leak in Process_updateCmdline, and
* Plug a Disk I/O meter memory leak on FreeBSD (thanks to Ximalas)
What's new in version 3.1.0
* Updated COPYING file to remove the PLPA exemption (appendix 2)
With this change the license is now GPLv2 without any additional wording.
@ -26,10 +112,11 @@ What's new in version 3.1.0
Moving this column to be handled as a platform-independently available
information simplifies the markup of the command line.
* Introduce configuration file versioning and config_reader_min_version
Starting with this version the configuration file contains an version
Starting with this version the configuration file contains a version
identifying the minimum version of the configuration parser needed to
fully understand the configuration file format.
Old configuration file formats are automatically upgraded on startup.
Old configuration file formats are automatically upgraded when
saving the config file (htoprc).
* Make the configuration parser friendlier to users (thanks to Bart Bakker)
With this change only settings that cannot be parsed properly are
reset to their defaults.
@ -48,12 +135,12 @@ What's new in version 3.1.0
* Option and key ("*") to collapse / expand all branches under PID 1
(and PID 2 if kernel threads are shown) (thanks to Krishna Chaitanya)
* Keep following a process when inverting the sort order, displaying
the help screen or hiding/unhiding userland threads
the help screen or hiding/unhiding userland threads.
If a thread is currently selected the selection is updated to point
to the thread's parent process. (thanks to Gonzalo, et.al.)
* Reorder process scanning to be performed before updating the display
of the meters in the header
* Always check the user for a process for any changes
* Always check the user for a process for any changes.
This affects multiple platforms that previously didn't correctly handle
the user field for a process to change at runtime (e.g. due to seteuid
or similar syscalls).
@ -105,14 +192,14 @@ What's new in version 3.1.0
* Correctly color the ZFS ARC ratio (thanks to Ross Williams)
* Bugfixes related to CPU time display/calculations for darwin on M1 systems
(thanks to Alexander Momchilov)
* Harmonize the handling of multiple batteries across different platforms
* Harmonize the handling of multiple batteries across different platforms.
The system is now considered to run on AC if at least one power supply
marked as AC is found in the system.
Battery capacity is summed up over all batteries found.
This also changes the old behavior that batteries reported by the
system after the first AC adapter where sometimes ignored.
* Correctly handle multiple batteries on Darwin
Resolves a possible memory leak on systems with multiple batteries
* Correctly handle multiple batteries on Darwin.
Resolves a possible memory leak on systems with multiple batteries.
* Handle Linux Shmem being part of Cached in the MemoryMeter
* Add SwapCached to the Linux swap meter (thanks to David Zarzycki)
* Convert process time to days if applicable (thanks to David Zarzycki)
@ -138,7 +225,7 @@ What's new in version 3.1.0
* Add command line option to drop Linux capabilities
* Support scheduler affinity on platforms beyond Linux
* Report on any failure to write the configuration file
* Cache stderr to be able to print assert messages
* Cache stderr to be able to print assert messages.
These messages are shown in case htop terminates unexpectedly.
* Print current settings on crash
* Reset signal handlers on program exit

View File

@ -1,7 +1,7 @@
/*
htop - ClockMeter.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -3,7 +3,7 @@
/*
htop - ClockMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - ColorsPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -52,7 +52,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
HandlerResult result = IGNORED;
int mark;
switch(ch) {
switch (ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
@ -68,6 +68,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
this->settings->colorScheme = mark;
this->settings->changed = true;
this->settings->lastUpdate++;
CRT_setColors(mark);
clear();

View File

@ -3,7 +3,7 @@
/*
htop - ColorsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - ColumnsPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -118,12 +118,6 @@ const PanelClass ColumnsPanel_class = {
.eventHandler = ColumnsPanel_eventHandler
};
typedef struct {
Panel* super;
unsigned int id;
unsigned int offset;
} DynamicIterator;
static void ColumnsPanel_add(Panel* super, unsigned int key, Hashtable* columns) {
const char* name;
if (key < LAST_PROCESSFIELD) {
@ -144,20 +138,26 @@ static void ColumnsPanel_add(Panel* super, unsigned int key, Hashtable* columns)
Panel_add(super, (Object*) ListItem_new(name, key));
}
ColumnsPanel* ColumnsPanel_new(Settings* settings) {
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss, Hashtable* columns) {
Panel* super = (Panel*) this;
Panel_prune(super);
for (const ProcessField* fields = ss->fields; *fields; fields++)
ColumnsPanel_add(super, *fields, columns);
this->ss = ss;
}
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, Hashtable* columns, bool* changed) {
ColumnsPanel* this = AllocThis(ColumnsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->ss = ss;
this->changed = changed;
this->moving = false;
Panel_setHeader(super, "Active Columns");
Hashtable* dynamicColumns = settings->dynamicColumns;
const ProcessField* fields = settings->fields;
for (; *fields; fields++)
ColumnsPanel_add(super, *fields, dynamicColumns);
ColumnsPanel_fill(this, ss, columns);
return this;
}
@ -165,14 +165,14 @@ ColumnsPanel* ColumnsPanel_new(Settings* settings) {
void ColumnsPanel_update(Panel* super) {
ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size + 1));
this->settings->flags = 0;
*(this->changed) = true;
this->ss->fields = xRealloc(this->ss->fields, sizeof(ProcessField) * (size + 1));
this->ss->flags = 0;
for (int i = 0; i < size; i++) {
int key = ((ListItem*) Panel_get(super, i))->key;
this->settings->fields[i] = key;
this->ss->fields[i] = key;
if (key < LAST_PROCESSFIELD)
this->settings->flags |= Process_fields[key].flags;
this->ss->flags |= Process_fields[key].flags;
}
this->settings->fields[size] = 0;
this->ss->fields[size] = 0;
}

View File

@ -3,7 +3,7 @@
/*
htop - ColumnsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -15,14 +15,17 @@ in the source distribution for its full text.
typedef struct ColumnsPanel_ {
Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving;
} ColumnsPanel;
extern const PanelClass ColumnsPanel_class;
ColumnsPanel* ColumnsPanel_new(Settings* settings);
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, Hashtable* columns, bool* changed);
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss, Hashtable* columns);
void ColumnsPanel_update(Panel* super);

View File

@ -2,7 +2,7 @@
htop - CommandLine.c
(C) 2004-2011 Hisham H. Muhammad
(C) 2020-2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -11,6 +11,7 @@ in the source distribution for its full text.
#include "CommandLine.h"
#include <assert.h>
#include <ctype.h>
#include <getopt.h>
#include <locale.h>
#include <stdbool.h>
@ -47,20 +48,22 @@ static void printVersionFlag(const char* name) {
static void printHelpFlag(const char* name) {
printf("%s " VERSION "\n"
COPYRIGHT "\n"
"Released under the GNU GPLv2.\n\n"
"Released under the GNU GPLv2+.\n\n"
"-C --no-color Use a monochrome color scheme\n"
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
"-F --filter=FILTER Show only the commands matching the given filter\n"
"-h --help Print this help screen\n"
"-H --highlight-changes[=DELAY] Highlight new and old processes\n"
"-M --no-mouse Disable the mouse\n"
"-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
"-H --highlight-changes[=DELAY] Highlight new and old processes\n", name);
#ifdef HAVE_GETMOUSE
printf("-M --no-mouse Disable the mouse\n");
#endif
printf("-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
" --readonly Disable all system and process changing features\n"
"-s --sort-key=COLUMN Sort by COLUMN in list view (try --sort-key=help for a list)\n"
"-t --tree Show the tree view (can be combined with -s)\n"
"-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
"-U --no-unicode Do not use unicode but plain ASCII\n"
"-V --version Print version info\n", name);
"-V --version Print version info\n");
Platform_longOptionsUsage(name);
printf("\n"
"Long options may be passed with a single dash.\n\n"
@ -85,9 +88,9 @@ typedef struct CommandLineSettings_ {
bool readonly;
} CommandLineSettings;
static CommandLineSettings parseArguments(const char* program, int argc, char** argv) {
static CommandLineStatus parseArguments(const char* program, int argc, char** argv, CommandLineSettings* flags) {
CommandLineSettings flags = {
*flags = (CommandLineSettings) {
.pidMatchList = NULL,
.commFilter = NULL,
.userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2))
@ -130,10 +133,10 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
switch (opt) {
case 'h':
printHelpFlag(program);
exit(0);
return STATUS_OK_EXIT;
case 'V':
printVersionFlag(program);
exit(0);
return STATUS_OK_EXIT;
case 's':
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
if (String_eq(optarg, "help")) {
@ -142,29 +145,29 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
const char* description = Process_fields[j].description;
if (name) printf("%19s %s\n", name, description);
}
exit(0);
return STATUS_OK_EXIT;
}
flags.sortKey = 0;
flags->sortKey = 0;
for (int j = 1; j < LAST_PROCESSFIELD; j++) {
if (Process_fields[j].name == NULL)
continue;
if (String_eq(optarg, Process_fields[j].name)) {
flags.sortKey = j;
flags->sortKey = j;
break;
}
}
if (flags.sortKey == 0) {
if (flags->sortKey == 0) {
fprintf(stderr, "Error: invalid column \"%s\".\n", optarg);
exit(1);
return STATUS_ERROR_EXIT;
}
break;
case 'd':
if (sscanf(optarg, "%16d", &(flags.delay)) == 1) {
if (flags.delay < 1) flags.delay = 1;
if (flags.delay > 100) flags.delay = 100;
if (sscanf(optarg, "%16d", &(flags->delay)) == 1) {
if (flags->delay < 1) flags->delay = 1;
if (flags->delay > 100) flags->delay = 100;
} else {
fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg);
exit(1);
return STATUS_ERROR_EXIT;
}
break;
case 'u':
@ -176,26 +179,30 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
}
if (!username) {
flags.userId = geteuid();
} else if (!Action_setUserOnly(username, &(flags.userId))) {
fprintf(stderr, "Error: invalid user \"%s\".\n", username);
exit(1);
flags->userId = geteuid();
} else if (!Action_setUserOnly(username, &(flags->userId))) {
for (const char *itr = username; *itr; ++itr)
if (!isdigit((unsigned char)*itr)) {
fprintf(stderr, "Error: invalid user \"%s\".\n", username);
return STATUS_ERROR_EXIT;
}
flags->userId = atol(username);
}
break;
}
case 'C':
flags.useColors = false;
flags->useColors = false;
break;
case 'M':
#ifdef HAVE_GETMOUSE
flags.enableMouse = false;
flags->enableMouse = false;
#endif
break;
case 'U':
flags.allowUnicode = false;
flags->allowUnicode = false;
break;
case 't':
flags.treeView = true;
flags->treeView = true;
break;
case 'p': {
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
@ -203,14 +210,14 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
char* saveptr;
const char* pid = strtok_r(argCopy, ",", &saveptr);
if (!flags.pidMatchList) {
flags.pidMatchList = Hashtable_new(8, false);
if (!flags->pidMatchList) {
flags->pidMatchList = Hashtable_new(8, false);
}
while(pid) {
unsigned int num_pid = atoi(pid);
// deepcode ignore CastIntegerToAddress: we just want a non-NUll pointer here
Hashtable_put(flags.pidMatchList, num_pid, (void *) 1);
// deepcode ignore CastIntegerToAddress: we just want a non-NULL pointer here
Hashtable_put(flags->pidMatchList, num_pid, (void *) 1);
pid = strtok_r(NULL, ",", &saveptr);
}
free(argCopy);
@ -219,7 +226,7 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
}
case 'F': {
assert(optarg);
free_and_xStrdup(&flags.commFilter, optarg);
free_and_xStrdup(&flags->commFilter, optarg);
break;
}
case 'H': {
@ -229,28 +236,30 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
delay = argv[optind++];
}
if (delay) {
if (sscanf(delay, "%16d", &(flags.highlightDelaySecs)) == 1) {
if (flags.highlightDelaySecs < 1)
flags.highlightDelaySecs = 1;
if (sscanf(delay, "%16d", &(flags->highlightDelaySecs)) == 1) {
if (flags->highlightDelaySecs < 1)
flags->highlightDelaySecs = 1;
} else {
fprintf(stderr, "Error: invalid highlight delay value \"%s\".\n", delay);
exit(1);
return STATUS_ERROR_EXIT;
}
}
flags.highlightChanges = true;
flags->highlightChanges = true;
break;
}
case 128:
flags.readonly = true;
flags->readonly = true;
break;
default:
if (Platform_getLongOption(opt, argc, argv) == false)
exit(1);
break;
default: {
CommandLineStatus status;
if ((status = Platform_getLongOption(opt, argc, argv)) != STATUS_OK)
return status;
break;
}
}
}
return flags;
return STATUS_OK;
}
static void CommandLine_delay(ProcessList* pl, unsigned long millisec) {
@ -283,12 +292,17 @@ int CommandLine_run(const char* name, int argc, char** argv) {
else
setlocale(LC_CTYPE, "");
CommandLineSettings flags = parseArguments(name, argc, argv);
CommandLineStatus status = STATUS_OK;
CommandLineSettings flags = { 0 };
if ((status = parseArguments(name, argc, argv, &flags)) != STATUS_OK)
return status != STATUS_OK_EXIT ? 1 : 0;
if (flags.readonly)
Settings_enableReadonly();
Platform_init();
if (!Platform_init())
return 1;
Process_setupColumnWidths();
@ -316,7 +330,7 @@ int CommandLine_run(const char* name, int argc, char** argv) {
settings->enableMouse = false;
#endif
if (flags.treeView)
settings->treeView = true;
settings->ss->treeView = true;
if (flags.highlightChanges)
settings->highlightChanges = true;
if (flags.highlightDelaySecs != -1)
@ -325,9 +339,9 @@ int CommandLine_run(const char* name, int argc, char** argv) {
// -t -s <key> means "tree sorted by key"
// -s <key> means "list sorted by key" (previous existing behavior)
if (!flags.treeView) {
settings->treeView = false;
settings->ss->treeView = false;
}
Settings_setSortKey(settings, flags.sortKey);
ScreenSettings_setSortKey(settings->ss, flags.sortKey);
}
CRT_init(settings, flags.allowUnicode);
@ -335,7 +349,7 @@ int CommandLine_run(const char* name, int argc, char** argv) {
MainPanel* panel = MainPanel_new();
ProcessList_setPanel(pl, (Panel*) panel);
MainPanel_updateTreeFunctions(panel, settings->treeView);
MainPanel_updateLabels(panel, settings->ss->treeView, flags.commFilter);
State state = {
.settings = settings,
@ -358,15 +372,10 @@ int CommandLine_run(const char* name, int argc, char** argv) {
CommandLine_delay(pl, 75);
ProcessList_scan(pl, false);
if (settings->allBranchesCollapsed)
if (settings->ss->allBranchesCollapsed)
ProcessList_collapseAllBranches(pl);
ScreenManager_run(scr, NULL, NULL);
attron(CRT_colors[RESET_COLOR]);
mvhline(LINES - 1, 0, ' ', COLS);
attroff(CRT_colors[RESET_COLOR]);
refresh();
ScreenManager_run(scr, NULL, NULL, NULL);
Platform_done();
@ -393,10 +402,8 @@ int CommandLine_run(const char* name, int argc, char** argv) {
/* Delete these last, since they can get accessed in the crash handler */
Settings_delete(settings);
if (dc)
Hashtable_delete(dc);
if (dm)
Hashtable_delete(dm);
DynamicColumns_delete(dc);
DynamicMeters_delete(dm);
return 0;
}

View File

@ -4,10 +4,15 @@
htop - CommandLine.h
(C) 2004-2011 Hisham H. Muhammad
(C) 2020-2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
typedef enum {
STATUS_OK,
STATUS_ERROR_EXIT,
STATUS_OK_EXIT
} CommandLineStatus;
int CommandLine_run(const char* name, int argc, char** argv);

View File

@ -1,7 +1,7 @@
/*
htop - Compat.c
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -44,7 +44,7 @@ int Compat_faccessat(int dirfd,
// Fallback to stat(2)/lstat(2) depending on flags
struct stat statinfo;
if(flags) {
if (flags) {
ret = lstat(pathname, &statinfo);
} else {
ret = stat(pathname, &statinfo);

View File

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

View File

@ -1,7 +1,7 @@
/*
htop - DateMeter.c
(C) 2004-2020 Hisham H. Muhammad, Michael Schönitzer
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -3,7 +3,7 @@
/*
htop - DateMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - DateTimeMeter.c
(C) 2004-2020 Hisham H. Muhammad, Michael Schönitzer
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -3,7 +3,7 @@
/*
htop - DateTimeMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - DiskIOMeter.c
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -12,6 +12,7 @@ in the source distribution for its full text.
#include "CRT.h"
#include "Macros.h"
#include "Meter.h"
#include "Object.h"
#include "Platform.h"
#include "ProcessList.h"
@ -25,7 +26,7 @@ static const int DiskIOMeter_attributes[] = {
METER_VALUE_IOWRITE,
};
static bool hasData = false;
static MeterRateStatus status = RATESTATUS_INIT;
static uint32_t cached_read_diff;
static uint32_t cached_write_diff;
static double cached_utilisation_diff;
@ -36,20 +37,27 @@ static void DiskIOMeter_updateValues(Meter* this) {
static uint64_t cached_last_update;
uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update;
/* update only every 500ms */
/* update only every 500ms to have a sane span for rate calculation */
if (passedTimeInMs > 500) {
static uint64_t cached_read_total;
static uint64_t cached_write_total;
static uint64_t cached_msTimeSpend_total;
uint64_t diff;
DiskIOData data;
if (!Platform_getDiskIO(&data)) {
status = RATESTATUS_NODATA;
} else if (cached_last_update == 0) {
status = RATESTATUS_INIT;
} else if (passedTimeInMs > 30000) {
status = RATESTATUS_STALE;
} else {
status = RATESTATUS_DATA;
}
cached_last_update = pl->realtimeMs;
DiskIOData data;
hasData = Platform_getDiskIO(&data);
if (!hasData) {
this->values[0] = 0;
if (status == RATESTATUS_NODATA) {
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data");
return;
}
@ -81,6 +89,15 @@ static void DiskIOMeter_updateValues(Meter* this) {
cached_msTimeSpend_total = data.totalMsTimeSpend;
}
if (status == RATESTATUS_INIT) {
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init");
return;
}
if (status == RATESTATUS_STALE) {
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale");
return;
}
this->values[0] = cached_utilisation_diff;
this->total = MAXIMUM(this->values[0], 100.0); /* fix total after (initial) spike */
@ -91,9 +108,18 @@ static void DiskIOMeter_updateValues(Meter* this) {
}
static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
if (!hasData) {
switch (status) {
case RATESTATUS_NODATA:
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
return;
case RATESTATUS_INIT:
RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing...");
return;
case RATESTATUS_STALE:
RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data");
return;
case RATESTATUS_DATA:
break;
}
char buffer[16];

View File

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

View File

@ -1,7 +1,7 @@
/*
htop - DisplayOptionsPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -18,6 +18,7 @@ in the source distribution for its full text.
#include "Object.h"
#include "OptionItem.h"
#include "ProvideCurses.h"
#include "ScreensPanel.h"
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@ -43,6 +44,8 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
case KEY_RECLICK:
case ' ':
switch (OptionItem_kind(selected)) {
case OPTION_ITEM_TEXT:
break;
case OPTION_ITEM_CHECK:
CheckItem_toggle((CheckItem*)selected);
result = HANDLED;
@ -69,6 +72,7 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
if (result == HANDLED) {
this->settings->changed = true;
this->settings->lastUpdate++;
Header* header = this->scr->header;
Header_calculateHeight(header);
Header_reinit(header);
@ -97,9 +101,18 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
this->scr = scr;
Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->allBranchesCollapsed)));
#define TABMSG "For current screen tab: \0"
char tabheader[sizeof(TABMSG) + SCREEN_NAME_LEN + 1] = TABMSG;
strncat(tabheader, settings->ss->name, SCREEN_NAME_LEN);
Panel_add(super, (Object*) TextItem_new(tabheader));
#undef TABMSG
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->ss->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->ss->treeViewAlwaysByPID)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->ss->allBranchesCollapsed)));
Panel_add(super, (Object*) TextItem_new("Global options:"));
Panel_add(super, (Object*) CheckItem_newByRef("Show tabs for screens", &(settings->screenTabs)));
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));
@ -107,7 +120,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_add(super, (Object*) CheckItem_newByRef("Show custom thread names", &(settings->showThreadNames)));
Panel_add(super, (Object*) CheckItem_newByRef("Show program path", &(settings->showProgramPath)));
Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName)));
Panel_add(super, (Object*) CheckItem_newByRef("Highlight out-dated/removed programs", &(settings->highlightDeletedExe)));
Panel_add(super, (Object*) CheckItem_newByRef("Highlight out-dated/removed programs (red) / libraries (yellow)", &(settings->highlightDeletedExe)));
Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand)));
Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline)));
Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline)));

View File

@ -3,7 +3,7 @@
/*
htop - DisplayOptionsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -3,7 +3,7 @@ htop - DynamicColumn.c
(C) 2021 Sohaib Mohammed
(C) 2021 htop dev team
(C) 2021 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -22,6 +22,13 @@ Hashtable* DynamicColumns_new(void) {
return Platform_dynamicColumns();
}
void DynamicColumns_delete(Hashtable* dynamics) {
if (dynamics) {
Platform_dynamicColumnsDone(dynamics);
Hashtable_delete(dynamics);
}
}
const char* DynamicColumn_init(unsigned int key) {
return Platform_dynamicColumnInit(key);
}

View File

@ -21,6 +21,8 @@ typedef struct DynamicColumn_ {
Hashtable* DynamicColumns_new(void);
void DynamicColumns_delete(Hashtable* dynamics);
const char* DynamicColumn_init(unsigned int key);
const DynamicColumn* DynamicColumn_lookup(Hashtable* dynamics, unsigned int key);

View File

@ -2,7 +2,7 @@
htop - DynamicMeter.c
(C) 2021 htop dev team
(C) 2021 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -38,6 +38,13 @@ Hashtable* DynamicMeters_new(void) {
return Platform_dynamicMeters();
}
void DynamicMeters_delete(Hashtable* dynamics) {
if (dynamics) {
Platform_dynamicMetersDone(dynamics);
Hashtable_delete(dynamics);
}
}
typedef struct {
unsigned int key;
const char* name;

View File

@ -17,6 +17,8 @@ typedef struct DynamicMeter_ {
Hashtable* DynamicMeters_new(void);
void DynamicMeters_delete(Hashtable* dynamics);
const char* DynamicMeter_lookup(Hashtable* dynamics, unsigned int key);
bool DynamicMeter_search(Hashtable* dynamics, const char* name, unsigned int* key);

View File

@ -1,7 +1,7 @@
/*
htop - FunctionBar.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -88,11 +88,12 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
}
}
void FunctionBar_draw(const FunctionBar* this) {
FunctionBar_drawExtra(this, NULL, -1, false);
int FunctionBar_draw(const FunctionBar* this) {
return FunctionBar_drawExtra(this, NULL, -1, false);
}
void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor) {
int FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor) {
int cursorX = 0;
attrset(CRT_colors[FUNCTION_BAR]);
mvhline(LINES - 1, 0, ' ', COLS);
int x = 0;
@ -113,18 +114,20 @@ void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr
}
mvaddstr(LINES - 1, x, buffer);
x += strlen(buffer);
cursorX = x;
}
attrset(CRT_colors[RESET_COLOR]);
if (setCursor) {
CRT_cursorX = x;
curs_set(1);
} else {
curs_set(0);
}
currentLen = x;
return cursorX;
}
void FunctionBar_append(const char* buffer, int attr) {

View File

@ -3,7 +3,7 @@
/*
htop - FunctionBar.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -29,9 +29,9 @@ void FunctionBar_delete(FunctionBar* this);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
void FunctionBar_draw(const FunctionBar* this);
int FunctionBar_draw(const FunctionBar* this);
void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor);
int FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor);
void FunctionBar_append(const char* buffer, int attr);

View File

@ -1,7 +1,7 @@
/*
htop - Hashtable.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -52,7 +52,7 @@ static void Hashtable_dump(const Hashtable* this) {
i,
this->buckets[i].key,
this->buckets[i].probe,
this->buckets[i].value ? (const void*)this->buckets[i].value : "(nil)");
this->buckets[i].value);
if (this->buckets[i].value)
items++;
@ -90,7 +90,7 @@ size_t Hashtable_count(const Hashtable* this) {
/* https://oeis.org/A014234 */
static const uint64_t OEISprimes[] = {
2, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191,
7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191,
16381, 32749, 65521, 131071, 262139, 524287, 1048573,
2097143, 4194301, 8388593, 16777213, 33554393,
67108859, 134217689, 268435399, 536870909, 1073741789,
@ -191,10 +191,14 @@ void Hashtable_setSize(Hashtable* this, size_t size) {
if (size <= this->items)
return;
size_t newSize = nextPrime(size);
if (newSize == this->size)
return;
HashtableItem* oldBuckets = this->buckets;
size_t oldSize = this->size;
this->size = nextPrime(size);
this->size = newSize;
this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem));
this->items = 0;
@ -282,7 +286,7 @@ void* Hashtable_remove(Hashtable* this, ht_key_t key) {
/* shrink on load-factor < 0.125 */
if (8 * this->items < this->size)
Hashtable_setSize(this, this->size / 2);
Hashtable_setSize(this, this->size / 3); /* account for nextPrime rounding up */
return res;
}

View File

@ -3,7 +3,7 @@
/*
htop - Hashtable.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - Header.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -86,8 +86,9 @@ static void Header_addMeterByName(Header* this, const char* name, MeterModeId mo
if (paren) {
int ok = sscanf(paren, "(%10u)", &param); // CPUMeter
if (!ok) {
char* end, dynamic[32] = {0};
char dynamic[32] = {0};
if (sscanf(paren, "(%30s)", dynamic)) { // DynamicMeter
char* end;
if ((end = strrchr(dynamic, ')')) == NULL)
return; // htoprc parse failure
*end = '\0';
@ -144,7 +145,7 @@ void Header_writeBackToSettings(const Header* this) {
const Vector* vec = this->columns[col];
int len = Vector_size(vec);
colSettings->names = len ? xCalloc(len, sizeof(char*)) : NULL;
colSettings->names = len ? xCalloc(len + 1, sizeof(char*)) : NULL;
colSettings->modes = len ? xCalloc(len, sizeof(int)) : NULL;
colSettings->len = len;
@ -193,27 +194,32 @@ void Header_draw(const Header* this) {
for (int y = 0; y < height; y++) {
mvhline(y, 0, ' ', COLS);
}
const int width = COLS - pad;
const int numCols = HeaderLayout_getColumns(this->headerLayout);
const int width = COLS - 2 * pad - (numCols - 1);
int x = pad;
float roundingLoss = 0.0f;
float roundingLoss = 0.0F;
Header_forEachColumn(this, col) {
Vector* meters = this->columns[col];
float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0f;
float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0F;
roundingLoss += colWidth - floorf(colWidth);
if (roundingLoss >= 1.0f) {
colWidth += 1.0f;
roundingLoss -= 1.0f;
if (roundingLoss >= 1.0F) {
colWidth += 1.0F;
roundingLoss -= 1.0F;
}
for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i);
float actualWidth = colWidth;
if (meter->mode == TEXT_METERMODE) {
/* Let meters in text mode expand to the right on empty neighbors;
except for multi column meters. */
if (meter->mode == TEXT_METERMODE && !Meter_isMultiColumn(meter)) {
for (int j = 1; j < meter->columnWidthCount; j++) {
actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0f;
actualWidth++; /* separator column */
actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0F;
}
}
@ -223,6 +229,7 @@ void Header_draw(const Header* this) {
}
x += floorf(colWidth);
x++; /* separator column */
}
}
@ -279,6 +286,9 @@ int Header_calculateHeight(Header* this) {
}
maxHeight = MAXIMUM(maxHeight, height);
}
if (this->settings->screenTabs) {
maxHeight++;
}
this->height = maxHeight;
this->pad = pad;
return maxHeight;

View File

@ -3,7 +3,7 @@
/*
htop - Header.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -23,7 +23,7 @@ typedef struct Header_ {
int height;
} Header;
#define Header_forEachColumn(this_, i_) for (size_t (i_)=0; (i_) < HeaderLayout_getColumns((this_)->headerLayout); ++(i_))
#define Header_forEachColumn(this_, i_) for (size_t (i_)=0, H_fEC_numColumns_ = HeaderLayout_getColumns((this_)->headerLayout); (i_) < H_fEC_numColumns_; ++(i_))
Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout);

View File

@ -3,7 +3,7 @@
/*
htop - HeaderLayout.h
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -18,6 +18,7 @@ in the source distribution for its full text.
typedef enum HeaderLayout_ {
HF_INVALID = -1,
HF_TWO_50_50,
HF_TWO_33_67,
HF_TWO_67_33,

View File

@ -1,7 +1,7 @@
/*
htop - HeaderOptionsPanel.c
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -35,7 +35,7 @@ static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) {
HandlerResult result = IGNORED;
int mark;
switch(ch) {
switch (ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
@ -52,6 +52,7 @@ static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) {
Header_setLayout(this->scr->header, mark);
this->settings->changed = true;
this->settings->lastUpdate++;
ScreenManager_resize(this->scr);
@ -82,6 +83,6 @@ HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* sc
for (int i = 0; i < LAST_HEADER_LAYOUT; i++) {
Panel_add(super, (Object*) CheckItem_newByVal(HeaderLayout_layouts[i].description, false));
}
CheckItem_set((CheckItem*)Panel_get(super, settings->hLayout), true);
CheckItem_set((CheckItem*)Panel_get(super, scr->header->headerLayout), true);
return this;
}

View File

@ -3,7 +3,7 @@
/*
htop - ColorsPanel.h
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - HostnameMeter.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -3,7 +3,7 @@
/*
htop - HostnameMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - IncSet.c
(C) 2005-2012 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -85,7 +85,7 @@ static void updateWeakPanel(const IncSet* this, Panel* panel, Vector* lines) {
const char* incFilter = this->modes[INC_FILTER].buffer;
for (int i = 0; i < Vector_size(lines); i++) {
ListItem* line = (ListItem*)Vector_get(lines, i);
if (String_contains_i(line->value, incFilter)) {
if (String_contains_i(line->value, incFilter, true)) {
Panel_add(panel, (Object*)line);
if (selected == (Object*)line) {
Panel_setSelected(panel, n);
@ -105,10 +105,10 @@ static void updateWeakPanel(const IncSet* this, Panel* panel, Vector* lines) {
}
}
static bool search(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
static bool search(const IncSet* this, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel);
for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
if (String_contains_i(getPanelValue(panel, i), this->active->buffer, true)) {
Panel_setSelected(panel, i);
return true;
}
@ -117,6 +117,21 @@ static bool search(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getP
return false;
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
panel->currentBar = this->active->bar;
panel->cursorOn = true;
this->panel = panel;
IncSet_drawBar(this, CRT_colors[FUNCTION_BAR]);
}
static void IncSet_deactivate(IncSet* this, Panel* panel) {
this->active = NULL;
Panel_setDefaultBar(panel);
panel->cursorOn = false;
FunctionBar_draw(this->defaultBar);
}
static bool IncMode_find(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
int size = Panel_size(panel);
int here = Panel_getSelectedIndex(panel);
@ -133,7 +148,7 @@ static bool IncMode_find(const IncMode* mode, Panel* panel, IncMode_GetPanelValu
return false;
}
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer, true)) {
Panel_setSelected(panel, i);
return true;
}
@ -156,7 +171,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
doSearch = false;
} else if (0 < ch && ch < 255 && isprint((unsigned char)ch)) {
if (mode->index < INCMODE_MAX) {
mode->buffer[mode->index] = ch;
mode->buffer[mode->index] = (char) ch;
mode->index++;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
@ -194,12 +209,11 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
IncMode_reset(mode);
}
}
this->active = NULL;
Panel_setDefaultBar(panel);
IncSet_deactivate(this, panel);
doSearch = false;
}
if (doSearch) {
this->found = search(mode, panel, getPanelValue);
this->found = search(this, panel, getPanelValue);
}
if (filterChanged && lines) {
updateWeakPanel(this, panel, lines);
@ -212,14 +226,13 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
return l ? l->value : "";
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
panel->currentBar = this->active->bar;
}
void IncSet_drawBar(const IncSet* this) {
void IncSet_drawBar(const IncSet* this, int attr) {
if (this->active) {
FunctionBar_drawExtra(this->active->bar, this->active->buffer, (this->active->isFilter || this->found) ? -1 : CRT_colors[FAILED_SEARCH], true);
if (!this->active->isFilter && !this->found)
attr = CRT_colors[FAILED_SEARCH];
int cursorX = FunctionBar_drawExtra(this->active->bar, this->active->buffer, attr, true);
this->panel->cursorY = LINES - 1;
this->panel->cursorX = cursorX;
} else {
FunctionBar_draw(this->defaultBar);
}

View File

@ -3,7 +3,7 @@
/*
htop - IncSet.h
(C) 2005-2012 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -32,6 +32,7 @@ typedef struct IncMode_ {
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
Panel* panel;
FunctionBar* defaultBar;
bool filtering;
bool found;
@ -57,7 +58,7 @@ const char* IncSet_getListItemValue(Panel* panel, int i);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
void IncSet_drawBar(const IncSet* this);
void IncSet_drawBar(const IncSet* this, int attr);
int IncSet_synthesizeEvent(IncSet* this, int x);

View File

@ -57,13 +57,13 @@ void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
attrset(CRT_colors[DEFAULT_COLOR]);
Panel_draw(this->display, true, true, true, false);
IncSet_drawBar(this->inc);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
}
void InfoScreen_addLine(InfoScreen* this, const char* line) {
Vector_add(this->lines, (Object*) ListItem_new(line, 0));
const char* incFilter = IncSet_filter(this->inc);
if (!incFilter || String_contains_i(line, incFilter)) {
if (!incFilter || String_contains_i(line, incFilter, true)) {
Panel_add(this->display, Vector_get(this->lines, Vector_size(this->lines) - 1));
}
}
@ -72,7 +72,7 @@ void InfoScreen_appendLine(InfoScreen* this, const char* line) {
ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines) - 1);
ListItem_append(last, line);
const char* incFilter = IncSet_filter(this->inc);
if (incFilter && Panel_get(this->display, Panel_size(this->display) - 1) != (Object*)last && String_contains_i(line, incFilter)) {
if (incFilter && Panel_get(this->display, Panel_size(this->display) - 1) != (Object*)last && String_contains_i(line, incFilter, true)) {
Panel_add(this->display, (Object*)last);
}
}
@ -89,15 +89,9 @@ void InfoScreen_run(InfoScreen* this) {
while (looping) {
Panel_draw(panel, false, true, true, false);
IncSet_drawBar(this->inc);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
if (this->inc->active) {
(void) move(LINES - 1, CRT_cursorX);
}
#ifdef HAVE_SET_ESCDELAY
set_escdelay(25);
#endif
int ch = getch();
int ch = Panel_getCh(panel);
if (ch == ERR) {
if (As_InfoScreen(this)->onErr) {
@ -135,7 +129,7 @@ void InfoScreen_run(InfoScreen* this) {
continue;
}
switch(ch) {
switch (ch) {
case ERR:
continue;
case KEY_F(3):

View File

@ -1,7 +1,7 @@
/*
htop - ListItem.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -18,13 +18,13 @@ in the source distribution for its full text.
#include "XUtils.h"
static void ListItem_delete(Object* cast) {
void ListItem_delete(Object* cast) {
ListItem* this = (ListItem*)cast;
free(this->value);
free(this);
}
static void ListItem_display(const Object* cast, RichString* out) {
void ListItem_display(const Object* cast, RichString* out) {
const ListItem* const this = (const ListItem*)cast;
assert (this != NULL);
@ -38,11 +38,15 @@ static void ListItem_display(const Object* cast, RichString* out) {
RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value);
}
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
void ListItem_init(ListItem* this, const char* value, int key) {
this->value = xStrdup(value);
this->key = key;
this->moving = false;
}
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
ListItem_init(this, value, key);
return this;
}
@ -55,7 +59,7 @@ void ListItem_append(ListItem* this, const char* text) {
this->value[newLen] = '\0';
}
static int ListItem_compare(const void* cast1, const void* cast2) {
int ListItem_compare(const void* cast1, const void* cast2) {
const ListItem* obj1 = (const ListItem*) cast1;
const ListItem* obj2 = (const ListItem*) cast2;
return strcmp(obj1->value, obj2->value);

View File

@ -3,7 +3,7 @@
/*
htop - ListItem.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -21,10 +21,18 @@ typedef struct ListItem_ {
extern const ObjectClass ListItem_class;
void ListItem_delete(Object* cast);
void ListItem_display(const Object* cast, RichString* out);
void ListItem_init(ListItem* this, const char* value, int key);
ListItem* ListItem_new(const char* value, int key);
void ListItem_append(ListItem* this, const char* text);
int ListItem_compare(const void* cast1, const void* cast2);
static inline const char* ListItem_getRef(const ListItem* this) {
return this->value;
}

View File

@ -1,7 +1,7 @@
/*
htop - LoadAverageMeter.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -3,7 +3,7 @@
/*
htop - LoadAverageMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -2,7 +2,7 @@
htop - ColumnsPanel.c
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -24,9 +24,10 @@ in the source distribution for its full text.
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
static const char* const MainFunctions_ro[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", " ", " ", " ", "Quit ", NULL};
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
void MainPanel_updateLabels(MainPanel* this, bool list, bool filter) {
FunctionBar* bar = MainPanel_getFunctionBar(this);
FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree ");
FunctionBar_setLabel(bar, KEY_F(5), list ? "List " : "Tree ");
FunctionBar_setLabel(bar, KEY_F(4), filter ? "FILTER" : "Filter");
}
static void MainPanel_pidSearch(MainPanel* this, int ch) {
@ -62,26 +63,38 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
return IGNORED;
/* reset on every normal key */
if (ch != ERR)
bool needReset = ch != ERR;
#ifdef HAVE_GETMOUSE
/* except mouse events while mouse support is disabled */
if (!(ch != KEY_MOUSE || this->state->settings->enableMouse))
needReset = false;
#endif
if (needReset)
this->state->hideProcessSelection = false;
Settings* settings = this->state->settings;
ScreenSettings* ss = settings->ss;
if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch);
const ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx);
if (settings->treeView && settings->treeViewAlwaysByPID) {
settings->treeView = false;
settings->direction = 1;
if (ss->treeView && ss->treeViewAlwaysByPID) {
ss->treeView = false;
ss->direction = 1;
reaction |= Action_setSortKey(settings, field);
} else if (field == Settings_getActiveSortKey(settings)) {
Settings_invertSortOrder(settings);
} else if (field == ScreenSettings_getActiveSortKey(ss)) {
ScreenSettings_invertSortOrder(ss);
} else {
reaction |= Action_setSortKey(settings, field);
}
reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS;
result = HANDLED;
} else if (EVENT_IS_SCREEN_TAB_CLICK(ch)) {
int x = EVENT_SCREEN_TAB_GET_X(ch);
reaction |= Action_setScreenTab(settings, x);
result = HANDLED;
} else if (ch != ERR && this->inc->active) {
bool filterChanged = IncSet_handleKey(this->inc, ch, super, MainPanel_getValue, NULL);
if (filterChanged) {
@ -110,7 +123,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
}
if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
MainPanel_updateLabels(this, settings->ss->treeView, this->state->pl->incFilter);
}
if (reaction & HTOP_RESIZE) {
result |= RESIZE;
@ -176,7 +189,7 @@ static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) {
if (hideFunctionBar && !this->inc->active)
return;
IncSet_drawBar(this->inc);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
if (this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}

View File

@ -4,7 +4,7 @@
htop - ColumnsPanel.h
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -32,7 +32,8 @@ typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
// update the Label Keys in the MainPanel bar, list: list / tree mode, filter: filter (inc) active / inactive
void MainPanel_updateLabels(MainPanel* this, bool list, bool filter);
int MainPanel_selectedPid(MainPanel* this);

View File

@ -1,3 +1,11 @@
if !HTOP_PCP
bin_PROGRAMS = htop
myhtopplatprogram = htop.c
else
bin_PROGRAMS = pcp-htop
myhtopplatprogram = pcp-htop.c
endif
dist_man_MANS = htop.1
EXTRA_DIST = \
$(dist_man_MANS) \
@ -66,6 +74,7 @@ myhtopsources = \
ProcessLocksScreen.c \
RichString.c \
ScreenManager.c \
ScreensPanel.c \
Settings.c \
SignalsPanel.c \
SwapMeter.c \
@ -127,6 +136,7 @@ myhtopheaders = \
ProvideCurses.h \
RichString.h \
ScreenManager.h \
ScreensPanel.h \
Settings.h \
SignalsPanel.h \
SwapMeter.h \
@ -145,6 +155,7 @@ linux_platform_headers = \
generic/gettime.h \
generic/hostname.h \
generic/uname.h \
linux/CGroupUtils.h \
linux/HugePageMeter.h \
linux/IOPriority.h \
linux/IOPriorityPanel.h \
@ -166,6 +177,7 @@ linux_platform_sources = \
generic/gettime.c \
generic/hostname.c \
generic/uname.c \
linux/CGroupUtils.c \
linux/HugePageMeter.c \
linux/IOPriorityPanel.c \
linux/LibSensors.c \
@ -181,7 +193,6 @@ linux_platform_sources = \
if HTOP_LINUX
AM_LDFLAGS += -rdynamic
myhtopplatprogram = htop
myhtopplatheaders = $(linux_platform_headers)
myhtopplatsources = $(linux_platform_sources)
endif
@ -214,7 +225,6 @@ freebsd_platform_sources = \
zfs/ZfsCompressedArcMeter.c
if HTOP_FREEBSD
myhtopplatprogram = htop
myhtopplatheaders = $(freebsd_platform_headers)
myhtopplatsources = $(freebsd_platform_sources)
endif
@ -240,7 +250,6 @@ dragonflybsd_platform_sources = \
generic/uname.c
if HTOP_DRAGONFLYBSD
myhtopplatprogram = htop
myhtopplatheaders = $(dragonflybsd_platform_headers)
myhtopplatsources = $(dragonflybsd_platform_sources)
endif
@ -266,7 +275,6 @@ netbsd_platform_sources = \
netbsd/NetBSDProcessList.c
if HTOP_NETBSD
myhtopplatprogram = htop
myhtopplatheaders = $(netbsd_platform_headers)
myhtopplatsources = $(netbsd_platform_sources)
endif
@ -292,7 +300,6 @@ openbsd_platform_sources = \
openbsd/Platform.c
if HTOP_OPENBSD
myhtopplatprogram = htop
myhtopplatheaders = $(openbsd_platform_headers)
myhtopplatsources = $(openbsd_platform_sources)
endif
@ -304,6 +311,7 @@ darwin_platform_headers = \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
darwin/Platform.h \
darwin/PlatformHelpers.h \
darwin/ProcessField.h \
generic/gettime.h \
generic/hostname.h \
@ -315,6 +323,7 @@ darwin_platform_headers = \
darwin_platform_sources = \
darwin/Platform.c \
darwin/PlatformHelpers.c \
darwin/DarwinProcess.c \
darwin/DarwinProcessList.c \
generic/gettime.c \
@ -326,7 +335,6 @@ darwin_platform_sources = \
if HTOP_DARWIN
AM_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatprogram = htop
myhtopplatheaders = $(darwin_platform_headers)
myhtopplatsources = $(darwin_platform_sources)
endif
@ -357,7 +365,6 @@ solaris_platform_sources = \
zfs/ZfsCompressedArcMeter.c
if HTOP_SOLARIS
myhtopplatprogram = htop
myhtopplatheaders = $(solaris_platform_headers)
myhtopplatsources = $(solaris_platform_sources)
endif
@ -393,10 +400,9 @@ pcp_platform_sources = \
zfs/ZfsCompressedArcMeter.c
if HTOP_PCP
myhtopplatprogram = pcp-htop
myhtopplatheaders = $(pcp_platform_headers)
myhtopplatsources = $(pcp_platform_sources)
pcp_htop_SOURCES = $(myhtopplatprogram).c $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
pcp_htop_SOURCES = $(myhtopplatprogram) $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
endif
# Unsupported
@ -416,15 +422,13 @@ unsupported_platform_sources = \
unsupported/UnsupportedProcessList.c
if HTOP_UNSUPPORTED
myhtopplatprogram = htop
myhtopplatsources = $(unsupported_platform_sources)
myhtopplatheaders = $(unsupported_platform_headers)
endif
# ----
bin_PROGRAMS = $(myhtopplatprogram)
htop_SOURCES = $(myhtopplatprogram).c $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
htop_SOURCES = $(myhtopplatprogram) $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
nodist_htop_SOURCES = config.h
target:

View File

@ -1,7 +1,7 @@
/*
htop - MemoryMeter.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -36,7 +36,7 @@ static void MemoryMeter_updateValues(Meter* this) {
/* Do not print available memory in bar mode */
this->curItems = 4;
written = Meter_humanUnit(buffer, isnan(this->values[4]) ? this->values[0] : this->total - this->values[4], size);
written = Meter_humanUnit(buffer, this->values[0], size);
METER_BUFFER_CHECK(buffer, size, written);
METER_BUFFER_APPEND_CHR(buffer, size, '/');

View File

@ -3,7 +3,7 @@
/*
htop - MemoryMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - MemorySwapMeter.c
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -33,10 +33,14 @@ static void MemorySwapMeter_updateValues(Meter* this) {
static void MemorySwapMeter_draw(Meter* this, int x, int y, int w) {
MemorySwapMeterData* data = this->meterData;
/* Use the same width for each sub meter to align with CPU meter */
const int colwidth = w / 2;
const int diff = w - colwidth * 2;
assert(data->memoryMeter->draw);
data->memoryMeter->draw(data->memoryMeter, x, y, w / 2);
data->memoryMeter->draw(data->memoryMeter, x, y, colwidth);
assert(data->swapMeter->draw);
data->swapMeter->draw(data->swapMeter, x + w / 2, y, w - w / 2);
data->swapMeter->draw(data->swapMeter, x + colwidth + diff, y, colwidth);
}
static void MemorySwapMeter_init(Meter* this) {
@ -91,6 +95,7 @@ const MeterClass MemorySwapMeter_class = {
},
.updateValues = MemorySwapMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.name = "MemorySwap",
.uiName = "Memory & Swap",
.description = "Combined memory and swap usage",

View File

@ -3,7 +3,7 @@
/*
htop - MemorySwapMeter.h
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

12
Meter.c
View File

@ -1,7 +1,7 @@
/*
htop - Meter.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -155,7 +155,7 @@ ListItem* Meter_toListItem(const Meter* this, bool moving) {
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
const char* caption = Meter_getCaption(this);
attrset(CRT_colors[METER_TEXT]);
mvaddnstr(y, x, caption, w - 1);
mvaddnstr(y, x, caption, w);
attrset(CRT_colors[RESET_COLOR]);
int captionLen = strlen(caption);
@ -166,7 +166,7 @@ static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
RichString_begin(out);
Meter_displayBuffer(this, &out);
RichString_printoffnVal(out, y, x, 0, w - 1);
RichString_printoffnVal(out, y, x, 0, w);
RichString_delete(&out);
}
@ -176,7 +176,6 @@ static const char BarMeterMode_characters[] = "|#*@$%&.";
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
const char* caption = Meter_getCaption(this);
w -= 2;
attrset(CRT_colors[METER_TEXT]);
int captionLen = 3;
mvaddnstr(y, x, caption, captionLen);
@ -184,10 +183,11 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
w -= captionLen;
attrset(CRT_colors[BAR_BORDER]);
mvaddch(y, x, '[');
w--;
mvaddch(y, x + MAXIMUM(w, 0), ']');
w--;
attrset(CRT_colors[RESET_COLOR]);
w--;
x++;
if (w < 1)
@ -329,7 +329,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
data->values[nValues - 1] = value;
}
int i = nValues - (w * 2) + 2, k = 0;
int i = nValues - (w * 2), k = 0;
if (i < 0) {
k = -i / 2;
i = 0;

11
Meter.h
View File

@ -3,7 +3,7 @@
/*
htop - Meter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -73,6 +73,7 @@ typedef struct MeterClass_ {
const char* const caption; /* prefix in the actual header */
const char* const description; /* optional meter description in header setup menu */
const uint8_t maxItems;
const bool isMultiColumn; /* whether the meter draws multiple sub-columns (defaults to false) */
} MeterClass;
#define As_Meter(this_) ((const MeterClass*)((this_)->super.klass))
@ -92,6 +93,7 @@ typedef struct MeterClass_ {
#define Meter_attributes(this_) As_Meter(this_)->attributes
#define Meter_name(this_) As_Meter(this_)->name
#define Meter_uiName(this_) As_Meter(this_)->uiName
#define Meter_isMultiColumn(this_) As_Meter(this_)->isMultiColumn
typedef struct GraphData_ {
struct timeval time;
@ -132,6 +134,13 @@ typedef enum {
LAST_METERMODE
} MeterModeId;
typedef enum {
RATESTATUS_DATA,
RATESTATUS_INIT,
RATESTATUS_NODATA,
RATESTATUS_STALE
} MeterRateStatus;
extern const MeterClass Meter_class;
Meter* Meter_new(const ProcessList* pl, unsigned int param, const MeterClass* type);

View File

@ -1,7 +1,7 @@
/*
htop - MetersPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -184,6 +184,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
if (result == HANDLED || sideMove) {
Header* header = this->scr->header;
this->settings->changed = true;
this->settings->lastUpdate++;
Header_calculateHeight(header);
ScreenManager_resize(this->scr);
}

View File

@ -3,7 +3,7 @@
/*
htop - MetersPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -5,6 +5,7 @@
#include "CRT.h"
#include "Macros.h"
#include "Meter.h"
#include "Object.h"
#include "Platform.h"
#include "Process.h"
@ -18,8 +19,7 @@ static const int NetworkIOMeter_attributes[] = {
METER_VALUE_IOWRITE,
};
static bool hasData = false;
static MeterRateStatus status = RATESTATUS_INIT;
static uint32_t cached_rxb_diff;
static uint32_t cached_rxp_diff;
static uint32_t cached_txb_diff;
@ -31,7 +31,7 @@ static void NetworkIOMeter_updateValues(Meter* this) {
uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update;
/* update only every 500ms */
/* update only every 500ms to have a sane span for rate calculation */
if (passedTimeInMs > 500) {
static uint64_t cached_rxb_total;
static uint64_t cached_rxp_total;
@ -39,11 +39,20 @@ static void NetworkIOMeter_updateValues(Meter* this) {
static uint64_t cached_txp_total;
uint64_t diff;
NetworkIOData data;
if (!Platform_getNetworkIO(&data)) {
status = RATESTATUS_NODATA;
} else if (cached_last_update == 0) {
status = RATESTATUS_INIT;
} else if (passedTimeInMs > 30000) {
status = RATESTATUS_STALE;
} else {
status = RATESTATUS_DATA;
}
cached_last_update = pl->realtimeMs;
NetworkIOData data;
hasData = Platform_getNetworkIO(&data);
if (!hasData) {
if (status == RATESTATUS_NODATA) {
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data");
return;
}
@ -85,6 +94,15 @@ static void NetworkIOMeter_updateValues(Meter* this) {
cached_txp_total = data.packetsTransmitted;
}
if (status == RATESTATUS_INIT) {
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init");
return;
}
if (status == RATESTATUS_STALE) {
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale");
return;
}
this->values[0] = cached_rxb_diff;
this->values[1] = cached_txb_diff;
if (cached_rxb_diff + cached_txb_diff > this->total) {
@ -98,9 +116,18 @@ static void NetworkIOMeter_updateValues(Meter* this) {
}
static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
if (!hasData) {
switch (status) {
case RATESTATUS_NODATA:
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
return;
case RATESTATUS_INIT:
RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing...");
return;
case RATESTATUS_STALE:
RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data");
return;
case RATESTATUS_DATA:
break;
}
char buffer[64];

View File

@ -2,7 +2,7 @@
htop - Object.c
(C) 2004-2012 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -4,7 +4,7 @@
htop - Object.h
(C) 2004-2012 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - OpenFilesScreen.c
(C) 2005-2006 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -9,13 +9,16 @@ in the source distribution for its full text.
#include "OpenFilesScreen.h"
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include "Macros.h"
#include "Panel.h"
@ -25,7 +28,7 @@ in the source distribution for its full text.
typedef struct OpenFiles_Data_ {
char* data[7];
char* data[8];
} OpenFiles_Data;
typedef struct OpenFiles_ProcessData_ {
@ -55,6 +58,8 @@ static size_t getIndexForType(char type) {
return 5;
case 't':
return 6;
case 'o':
return 7;
}
/* should never reach here */
@ -74,7 +79,7 @@ OpenFilesScreen* OpenFilesScreen_new(const Process* process) {
} else {
this->pid = process->pid;
}
return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE MODE DEVICE SIZE NODE NAME");
return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE MODE DEVICE SIZE OFFSET NODE NAME");
}
void OpenFilesScreen_delete(Object* this) {
@ -115,13 +120,16 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
close(fdnull);
char buffer[32] = {0};
xSnprintf(buffer, sizeof(buffer), "%d", pid);
execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
// Use of NULL in variadic functions must have a pointer cast.
// The NULL constant is not required by standard to have a pointer type.
execlp("lsof", "lsof", "-P", "-o", "-p", buffer, "-F", (char *)NULL);
exit(127);
}
close(fdpair[1]);
OpenFiles_Data* item = &(pdata->data);
OpenFiles_FileData* fdata = NULL;
bool lsofIncludesFileSize = false;
FILE* fd = fdopen(fdpair[0], "r");
if (!fd) {
@ -155,8 +163,17 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
case 't': /* file's type */
{
size_t index = getIndexForType(cmd);
free(item->data[index]);
item->data[index] = xStrdup(line + 1);
free_and_xStrdup(&item->data[index], line + 1);
break;
}
case 'o': /* file's offset */
{
size_t index = getIndexForType(cmd);
if (String_startsWith(line + 1, "0t")) {
free_and_xStrdup(&item->data[index], line + 3);
} else {
free_and_xStrdup(&item->data[index], line + 1);
}
break;
}
case 'c': /* process command name */
@ -166,7 +183,6 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
case 'k': /* link count */
case 'l': /* file's lock status */
case 'L': /* process login name */
case 'o': /* file's offset */
case 'p': /* process ID */
case 'P': /* protocol name */
case 'R': /* parent process ID */
@ -175,15 +191,20 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
/* ignore */
break;
}
if (cmd == 's')
lsofIncludesFileSize = true;
free(line);
}
fclose(fd);
int wstatus;
if (waitpid(child, &wstatus, 0) == -1) {
pdata->error = 1;
return pdata;
}
while (waitpid(child, &wstatus, 0) == -1)
if (errno != EINTR) {
pdata->error = 1;
return pdata;
}
if (!WIFEXITED(wstatus)) {
pdata->error = 1;
@ -191,6 +212,25 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
pdata->error = WEXITSTATUS(wstatus);
}
/* We got all information we need; no post-processing needed */
if (lsofIncludesFileSize)
return pdata;
/* On linux, `lsof -o -F` omits SIZE, so add it back. */
/* On macOS, `lsof -o -F` includes SIZE, so this block isn't needed. If no open files have a filesize, this will still run, unfortunately. */
size_t fileSizeIndex = getIndexForType('s');
for (fdata = pdata->files; fdata != NULL; fdata = fdata->next) {
item = &fdata->data;
const char* filename = getDataForType(item, 'n');
struct stat st;
if (stat(filename, &st) == 0) {
char fileSizeBuf[21]; /* 20 (long long) + 1 (NULL) */
xSnprintf(fileSizeBuf, sizeof(fileSizeBuf), "%"PRIu64, st.st_size); /* st.st_size is long long on macOS, long on linux */
free_and_xStrdup(&item->data[fileSizeIndex], fileSizeBuf);
}
}
return pdata;
}
@ -213,14 +253,15 @@ static void OpenFilesScreen_scan(InfoScreen* this) {
while (fdata) {
OpenFiles_Data* data = &fdata->data;
size_t lenN = strlen(getDataForType(data, 'n'));
size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + lenN + 7 /*spaces*/ + 1 /*null*/;
size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + 10 + lenN + 8 /*spaces*/ + 1 /*null*/;
char entry[sizeEntry];
xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %s",
xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %10.10s %s",
getDataForType(data, 'f'),
getDataForType(data, 't'),
getDataForType(data, 'a'),
getDataForType(data, 'D'),
getDataForType(data, 's'),
getDataForType(data, 'o'),
getDataForType(data, 'i'),
getDataForType(data, 'n'));
InfoScreen_addLine(this, entry);

View File

@ -3,7 +3,7 @@
/*
htop - OpenFilesScreen.h
(C) 2005-2006 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - OptionItem.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -25,6 +25,13 @@ static void OptionItem_delete(Object* cast) {
free(this);
}
static void TextItem_display(const Object* cast, RichString* out) {
const TextItem* this = (const TextItem*)cast;
assert (this != NULL);
RichString_appendWide(out, CRT_colors[HELP_BOLD], this->super.text);
}
static void CheckItem_display(const Object* cast, RichString* out) {
const CheckItem* this = (const CheckItem*)cast;
assert (this != NULL);
@ -68,6 +75,16 @@ const OptionItemClass OptionItem_class = {
}
};
const OptionItemClass TextItem_class = {
.super = {
.extends = Class(OptionItem),
.delete = OptionItem_delete,
.display = TextItem_display
},
.kind = OPTION_ITEM_TEXT
};
const OptionItemClass CheckItem_class = {
.super = {
.extends = Class(OptionItem),
@ -77,6 +94,7 @@ const OptionItemClass CheckItem_class = {
.kind = OPTION_ITEM_CHECK
};
const OptionItemClass NumberItem_class = {
.super = {
.extends = Class(OptionItem),
@ -86,6 +104,12 @@ const OptionItemClass NumberItem_class = {
.kind = OPTION_ITEM_NUMBER
};
TextItem* TextItem_new(const char* text) {
TextItem* this = AllocThis(TextItem);
this->super.text = xStrdup(text);
return this;
}
CheckItem* CheckItem_newByRef(const char* text, bool* ref) {
CheckItem* this = AllocThis(CheckItem);
this->super.text = xStrdup(text);

View File

@ -3,7 +3,7 @@
/*
htop - OptionItem.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -13,6 +13,7 @@ in the source distribution for its full text.
enum OptionItemType {
OPTION_ITEM_TEXT,
OPTION_ITEM_CHECK,
OPTION_ITEM_NUMBER,
};
@ -32,6 +33,12 @@ typedef struct OptionItem_ {
char* text;
} OptionItem;
typedef struct TextItem_ {
OptionItem super;
char* text;
} TextItem;
typedef struct CheckItem_ {
OptionItem super;
@ -51,9 +58,12 @@ typedef struct NumberItem_ {
} NumberItem;
extern const OptionItemClass OptionItem_class;
extern const OptionItemClass TextItem_class;
extern const OptionItemClass CheckItem_class;
extern const OptionItemClass NumberItem_class;
TextItem* TextItem_new(const char* text);
CheckItem* CheckItem_newByRef(const char* text, bool* ref);
CheckItem* CheckItem_newByVal(const char* text, bool value);
bool CheckItem_get(const CheckItem* this);

28
Panel.c
View File

@ -1,7 +1,7 @@
/*
htop - Panel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -49,6 +49,8 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
this->y = y;
this->w = w;
this->h = h;
this->cursorX = 0;
this->cursorY = 0;
this->eventHandlerState = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE);
this->scrollV = 0;
@ -57,6 +59,7 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
this->oldSelected = 0;
this->selectedLen = 0;
this->needsRedraw = true;
this->cursorOn = false;
this->wasFocus = false;
RichString_beginAllocated(this->header);
this->defaultBar = fuBar;
@ -72,6 +75,11 @@ void Panel_done(Panel* this) {
RichString_delete(&this->header);
}
void Panel_setCursorToSelection(Panel* this) {
this->cursorY = this->y + this->selected - this->scrollV + 1;
this->cursorX = this->x + this->selectedLen - this->scrollH;
}
void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
this->selectionColorId = colorId;
}
@ -330,7 +338,6 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
this->oldSelected = this->selected;
this->wasFocus = focus;
this->needsRedraw = false;
move(0, 0);
}
static int Panel_headerHeight(const Panel* this) {
@ -453,7 +460,7 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
}
if (len < 99) {
buffer[len] = ch;
buffer[len] = (char) ch;
buffer[len + 1] = '\0';
}
@ -470,7 +477,7 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
// if current word did not match,
// retry considering the character the start of a new word.
buffer[0] = ch;
buffer[0] = (char) ch;
buffer[1] = '\0';
}
@ -485,3 +492,16 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
return IGNORED;
}
int Panel_getCh(Panel* this) {
if (this->cursorOn) {
move(this->cursorY, this->cursorX);
curs_set(1);
} else {
curs_set(0);
}
#ifdef HAVE_SET_ESCDELAY
set_escdelay(25);
#endif
return getch();
}

14
Panel.h
View File

@ -3,7 +3,7 @@
/*
htop - Panel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -39,6 +39,10 @@ typedef enum HandlerResult_ {
#define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000)
#define EVENT_SCREEN_TAB_CLICK(x_) (-20000 + (x_))
#define EVENT_IS_SCREEN_TAB_CLICK(ev_) ((ev_) >= -20000 && (ev_) < -10000)
#define EVENT_SCREEN_TAB_GET_X(ev_) ((ev_) + 20000)
typedef HandlerResult (*Panel_EventHandler)(Panel*, int);
typedef void (*Panel_DrawFunctionBar)(Panel*, bool);
typedef void (*Panel_PrintHeader)(Panel*);
@ -61,14 +65,16 @@ typedef struct PanelClass_ {
struct Panel_ {
Object super;
int x, y, w, h;
int cursorX, cursorY;
Vector* items;
int selected;
int oldSelected;
int selectedLen;
void* eventHandlerState;
int scrollV;
short scrollH;
int scrollH;
bool needsRedraw;
bool cursorOn;
bool wasFocus;
FunctionBar* currentBar;
FunctionBar* defaultBar;
@ -90,6 +96,8 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
void Panel_done(Panel* this);
void Panel_setCursorToSelection(Panel* this);
void Panel_setSelectionColor(Panel* this, ColorElements colorId);
void Panel_setHeader(Panel* this, const char* header);
@ -130,4 +138,6 @@ bool Panel_onKey(Panel* this, int key);
HandlerResult Panel_selectByTyping(Panel* this, int ch);
int Panel_getCh(Panel* this);
#endif

322
Process.c
View File

@ -2,7 +2,7 @@
htop - Process.c
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -41,17 +41,33 @@ static const char* const kthreadID = "KTHREAD";
static uid_t Process_getuid = (uid_t)-1;
int Process_pidDigits = 7;
int Process_pidDigits = PROCESS_MIN_PID_DIGITS;
int Process_uidDigits = PROCESS_MIN_UID_DIGITS;
void Process_setupColumnWidths() {
int maxPid = Platform_getMaxPid();
if (maxPid == -1)
return;
Process_pidDigits = ceil(log10(maxPid));
if (maxPid < (int)pow(10, PROCESS_MIN_PID_DIGITS)) {
Process_pidDigits = PROCESS_MIN_PID_DIGITS;
return;
}
Process_pidDigits = (int)log10(maxPid) + 1;
assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS);
}
void Process_setUidColumnWidth(uid_t maxUid) {
if (maxUid < (uid_t)pow(10, PROCESS_MIN_UID_DIGITS)) {
Process_uidDigits = PROCESS_MIN_UID_DIGITS;
return;
}
Process_uidDigits = (int)log10(maxUid) + 1;
assert(Process_uidDigits <= PROCESS_MAX_UID_DIGITS);
}
void Process_printBytes(RichString* str, unsigned long long number, bool coloring) {
char buffer[16];
int len;
@ -195,7 +211,12 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths, bool
int years = days / 365;
int daysLeft = days - 365 * years;
if (daysLeft >= 100) {
if (years >= 10000000) {
RichString_appendnAscii(str, yearColor, "eternity ", 9);
} else if (years >= 1000) {
len = xSnprintf(buffer, sizeof(buffer), "%7dy ", years);
RichString_appendnAscii(str, yearColor, buffer, len);
} else if (daysLeft >= 100) {
len = xSnprintf(buffer, sizeof(buffer), "%3dy", years);
RichString_appendnAscii(str, yearColor, buffer, len);
len = xSnprintf(buffer, sizeof(buffer), "%3dd ", daysLeft);
@ -381,7 +402,7 @@ static inline char* stpcpyWithNewlineConversion(char* dstStr, const char* srcStr
* This function makes the merged Command string. It also stores the offsets of the
* basename, comm w.r.t the merged Command string - these offsets will be used by
* Process_writeCommand() for coloring. The merged Command string is also
* returned by Process_getCommandStr() for searching, sorting and filtering.
* returned by Process_getCommand() for searching, sorting and filtering.
*/
void Process_makeCommandStr(Process* this) {
ProcessMergedCommand* mc = &this->mergedCommand;
@ -393,60 +414,37 @@ void Process_makeCommandStr(Process* this) {
bool stripExeFromCmdline = settings->stripExeFromCmdline;
bool showThreadNames = settings->showThreadNames;
uint64_t settingsStamp = settings->lastUpdate;
/* Nothing to do to (Re)Generate the Command string, if the process is:
* - a kernel thread, or
* - a zombie from before being under htop's watch, or
* - a user thread and showThreadNames is not set */
if (Process_isKernelThread(this))
return;
if (this->state == 'Z' && !this->mergedCommand.str)
return;
if (Process_isUserlandThread(this) && settings->showThreadNames && (showThreadNames == mc->prevShowThreadNames))
if (this->state == ZOMBIE && !this->mergedCommand.str)
return;
/* this->mergedCommand.str needs updating only if its state or contents changed.
* Its content is based on the fields cmdline, comm, and exe. */
if (
mc->prevMergeSet == showMergedCommand &&
mc->prevPathSet == showProgramPath &&
mc->prevCommSet == searchCommInCmdline &&
mc->prevCmdlineSet == stripExeFromCmdline &&
mc->prevShowThreadNames == showThreadNames &&
!mc->cmdlineChanged &&
!mc->commChanged &&
!mc->exeChanged
) {
if (mc->lastUpdate >= settingsStamp)
return;
}
mc->lastUpdate = settingsStamp;
/* The field separtor "│" has been chosen such that it will not match any
* valid string used for searching or filtering */
const char* SEPARATOR = CRT_treeStr[TREE_STR_VERT];
const int SEPARATOR_LEN = strlen(SEPARATOR);
/* Check for any changed fields since we last built this string */
if (mc->cmdlineChanged || mc->commChanged || mc->exeChanged) {
free(mc->str);
/* Accommodate the column text, two field separators and terminating NUL */
size_t maxLen = 2 * SEPARATOR_LEN + 1;
maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)");
maxLen += this->procComm ? strlen(this->procComm) : 0;
maxLen += this->procExe ? strlen(this->procExe) : 0;
/* Accommodate the column text, two field separators and terminating NUL */
size_t maxLen = 2 * SEPARATOR_LEN + 1;
maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)");
maxLen += this->procComm ? strlen(this->procComm) : 0;
maxLen += this->procExe ? strlen(this->procExe) : 0;
mc->str = xCalloc(1, maxLen);
}
/* Preserve the settings used in this run */
mc->prevMergeSet = showMergedCommand;
mc->prevPathSet = showProgramPath;
mc->prevCommSet = searchCommInCmdline;
mc->prevCmdlineSet = stripExeFromCmdline;
mc->prevShowThreadNames = showThreadNames;
/* Mark everything as unchanged */
mc->cmdlineChanged = false;
mc->commChanged = false;
mc->exeChanged = false;
free(mc->str);
mc->str = xCalloc(1, maxLen);
/* Reset all locations that need extra handling when actually displaying */
mc->highlightCount = 0;
@ -500,17 +498,26 @@ void Process_makeCommandStr(Process* this) {
assert(cmdlineBasenameStart <= (int)strlen(cmdline));
if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */
if (showMergedCommand && (!Process_isUserlandThread(this) || showThreadNames) && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */
if ((showMergedCommand || (Process_isUserlandThread(this) && showThreadNames)) && procComm && strlen(procComm)) { /* set column to or prefix it with comm */
if (strncmp(cmdline + cmdlineBasenameStart, procComm, MINIMUM(TASK_COMM_LEN - 1, strlen(procComm))) != 0) {
WRITE_HIGHLIGHT(0, strlen(procComm), commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
str = stpcpy(str, procComm);
if(!showMergedCommand)
return;
WRITE_SEPARATOR;
}
}
if (cmdlineBasenameEnd > cmdlineBasenameStart)
WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME);
if (this->procExeDeleted)
WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, delExeAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
else if (this->usesDeletedLib)
WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, delLibAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
(void)stpcpyWithNewlineConversion(str, cmdline + (showProgramPath ? 0 : cmdlineBasenameStart));
return;
@ -571,11 +578,15 @@ void Process_makeCommandStr(Process* this) {
}
if (matchLen) {
/* strip the matched exe prefix */
cmdline += matchLen;
if (stripExeFromCmdline) {
/* strip the matched exe prefix */
cmdline += matchLen;
commStart -= matchLen;
commEnd -= matchLen;
commStart -= matchLen;
commEnd -= matchLen;
} else {
matchLen = 0;
}
}
if (!matchLen || (haveCommField && *cmdline)) {
@ -707,6 +718,50 @@ void Process_printLeftAlignedField(RichString* str, int attr, const char* conten
RichString_appendChr(str, attr, ' ', width + 1 - columns);
}
void Process_printPercentage(float val, char* buffer, int n, uint8_t width, int* attr) {
if (val >= 0) {
if (val < 0.05F)
*attr = CRT_colors[PROCESS_SHADOW];
else if (val >= 99.9F)
*attr = CRT_colors[PROCESS_MEGABYTES];
int precision = 1;
// Display "val" as "100" for columns like "MEM%".
if (width == 4 && val > 99.9F) {
precision = 0;
val = 100.0F;
}
xSnprintf(buffer, n, "%*.*f ", width, precision, val);
} else {
*attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, "%*.*s ", width, width, "N/A");
}
}
static inline char processStateChar(ProcessState state) {
switch (state) {
case UNKNOWN: return '?';
case RUNNABLE: return 'U';
case RUNNING: return 'R';
case QUEUED: return 'Q';
case WAITING: return 'W';
case UNINTERRUPTIBLE_WAIT: return 'D';
case BLOCKED: return 'B';
case PAGING: return 'P';
case STOPPED: return 'T';
case TRACED: return 't';
case ZOMBIE: return 'Z';
case DEFUNCT: return 'X';
case IDLE: return 'I';
case SLEEPING: return 'S';
default:
assert(0);
return '!';
}
}
void Process_writeField(const Process* this, RichString* str, ProcessField field) {
char buffer[256];
size_t n = sizeof(buffer);
@ -720,7 +775,8 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
attr = CRT_colors[PROCESS_THREAD];
baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
}
if (!this->settings->treeView || this->indent == 0) {
const ScreenSettings* ss = this->settings->ss;
if (!ss->treeView || this->indent == 0) {
Process_writeCommand(this, attr, baseattr, str);
return;
}
@ -804,7 +860,15 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
Process_printLeftAlignedField(str, attr, cwd, 25);
return;
}
case ELAPSED: Process_printTime(str, /* convert to hundreds of a second */ this->processList->realtimeMs / 10 - 100 * this->starttime_ctime, coloring); return;
case ELAPSED: {
const uint64_t rt = this->processList->realtimeMs;
const uint64_t st = this->starttime_ctime * 1000;
const uint64_t dt =
rt < st ? 0 :
rt - st;
Process_printTime(str, /* convert to hundreds of a second */ dt / 10, coloring);
return;
}
case MAJFLT: Process_printCount(str, this->majflt, coloring); return;
case MINFLT: Process_printCount(str, this->minflt, coloring); return;
case M_RESIDENT: Process_printKBytes(str, this->m_resident, coloring); return;
@ -821,34 +885,13 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
xSnprintf(buffer, n, "%4ld ", this->nlwp);
break;
case PERCENT_CPU:
case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, Process_fieldWidths[PERCENT_CPU], &attr); break;
case PERCENT_NORM_CPU: {
float cpuPercentage = this->percent_cpu;
if (field == PERCENT_NORM_CPU) {
cpuPercentage /= this->processList->activeCPUs;
}
if (cpuPercentage > 999.9F) {
xSnprintf(buffer, n, "%4u ", (unsigned int)cpuPercentage);
} else if (cpuPercentage > 99.9F) {
xSnprintf(buffer, n, "%3u. ", (unsigned int)cpuPercentage);
} else {
if (cpuPercentage < 0.05F)
attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, "%4.1f ", cpuPercentage);
}
float cpuPercentage = this->percent_cpu / this->processList->activeCPUs;
Process_printPercentage(cpuPercentage, buffer, n, Process_fieldWidths[PERCENT_CPU], &attr);
break;
}
case PERCENT_MEM:
if (this->percent_mem > 99.9F) {
xSnprintf(buffer, n, "100. ");
} else {
if (this->percent_mem < 0.05F)
attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, "%4.1f ", this->percent_mem);
}
break;
case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, 4, &attr); break;
case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break;
case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break;
case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break;
@ -862,21 +905,35 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break;
case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
case STATE:
xSnprintf(buffer, n, "%c ", this->state);
xSnprintf(buffer, n, "%c ", processStateChar(this->state));
switch (this->state) {
case 'R':
attr = CRT_colors[PROCESS_R_STATE];
case RUNNABLE:
case RUNNING:
case TRACED:
attr = CRT_colors[PROCESS_RUN_STATE];
break;
case 'D':
case BLOCKED:
case DEFUNCT:
case STOPPED:
case UNINTERRUPTIBLE_WAIT:
case ZOMBIE:
attr = CRT_colors[PROCESS_D_STATE];
break;
case 'I':
case 'S':
case QUEUED:
case WAITING:
case IDLE:
case SLEEPING:
attr = CRT_colors[PROCESS_SHADOW];
break;
case UNKNOWN:
case PAGING:
break;
}
break;
case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break;
case ST_UID: xSnprintf(buffer, n, "%*d ", Process_uidDigits, this->st_uid); break;
case TIME: Process_printTime(str, this->time, coloring); return;
case TGID:
if (this->tgid == this->pid)
@ -899,11 +956,11 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
attr = CRT_colors[PROCESS_SHADOW];
if (this->user) {
Process_printLeftAlignedField(str, attr, this->user, 9);
Process_printLeftAlignedField(str, attr, this->user, 10);
return;
}
xSnprintf(buffer, n, "%-9d ", this->st_uid);
xSnprintf(buffer, n, "%-10d ", this->st_uid);
break;
default:
if (DynamicColumn_writeField(this, str, field))
@ -917,7 +974,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
void Process_display(const Object* cast, RichString* out) {
const Process* this = (const Process*) cast;
const ProcessField* fields = this->settings->fields;
const ProcessField* fields = this->settings->ss->fields;
for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]);
@ -953,7 +1010,7 @@ void Process_done(Process* this) {
/* This function returns the string displayed in Command column, so that sorting
* happens on what is displayed - whether comm, full path, basename, etc.. So
* this follows Process_writeField(COMM) and Process_writeCommand */
const char* Process_getCommandStr(const Process* this) {
const char* Process_getCommand(const Process* this) {
if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !this->mergedCommand.str) {
return this->cmdline;
}
@ -969,7 +1026,6 @@ const ProcessClass Process_class = {
.compare = Process_compare
},
.writeField = Process_writeField,
.getCommandStr = Process_getCommandStr,
};
void Process_init(Process* this, const Settings* settings) {
@ -1023,20 +1079,14 @@ bool Process_sendSignal(Process* this, Arg sgn) {
return kill(this->pid, sgn.i) == 0;
}
int Process_pidCompare(const void* v1, const void* v2) {
const Process* p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2;
return SPACESHIP_NUMBER(p1->pid, p2->pid);
}
int Process_compare(const void* v1, const void* v2) {
const Process* p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2;
const Settings* settings = p1->settings;
const ScreenSettings* ss = settings->ss;
ProcessField key = Settings_getActiveSortKey(settings);
ProcessField key = ScreenSettings_getActiveSortKey(ss);
int result = Process_compareByKey(p1, p2, key);
@ -1044,45 +1094,7 @@ int Process_compare(const void* v1, const void* v2) {
if (!result)
return SPACESHIP_NUMBER(p1->pid, p2->pid);
return (Settings_getActiveDirection(settings) == 1) ? result : -result;
}
static uint8_t stateCompareValue(char state) {
switch (state) {
case 'S':
return 10;
case 'I':
return 9;
case 'X':
return 8;
case 'Z':
return 7;
case 't':
return 6;
case 'T':
return 5;
case 'L':
return 4;
case 'D':
return 3;
case 'R':
return 2;
case '?':
return 1;
default:
return 0;
}
return (ScreenSettings_getActiveDirection(ss) == 1) ? result : -result;
}
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) {
@ -1139,7 +1151,7 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField
r = SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid);
case STATE:
return SPACESHIP_NUMBER(stateCompareValue(p1->state), stateCompareValue(p2->state));
return SPACESHIP_NUMBER(p1->state, p2->state);
case ST_UID:
return SPACESHIP_NUMBER(p1->st_uid, p2->st_uid);
case TIME:
@ -1154,6 +1166,7 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField
case USER:
return SPACESHIP_NULLSTR(p1->user, p2->user);
default:
CRT_debug("Process_compareByKey_Base() called with key %d", key);
assert(0 && "Process_compareByKey_Base: default key reached"); /* should never be reached */
return SPACESHIP_NUMBER(p1->pid, p2->pid);
}
@ -1168,7 +1181,8 @@ void Process_updateComm(Process* this, const char* comm) {
free(this->procComm);
this->procComm = comm ? xStrdup(comm) : NULL;
this->mergedCommand.commChanged = true;
this->mergedCommand.lastUpdate = 0;
}
static int skipPotentialPath(const char* cmdline, int end) {
@ -1208,7 +1222,8 @@ void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart
this->cmdline = cmdline ? xStrdup(cmdline) : NULL;
this->cmdlineBasenameStart = (basenameStart || !cmdline) ? basenameStart : skipPotentialPath(cmdline, basenameEnd);
this->cmdlineBasenameEnd = basenameEnd;
this->mergedCommand.cmdlineChanged = true;
this->mergedCommand.lastUpdate = 0;
}
void Process_updateExe(Process* this, const char* exe) {
@ -1227,5 +1242,40 @@ void Process_updateExe(Process* this, const char* exe) {
this->procExe = NULL;
this->procExeBasenameOffset = 0;
}
this->mergedCommand.exeChanged = true;
this->mergedCommand.lastUpdate = 0;
}
uint8_t Process_fieldWidths[LAST_PROCESSFIELD] = { 0 };
void Process_resetFieldWidths() {
for (size_t i = 0; i < LAST_PROCESSFIELD; i++) {
if (!Process_fields[i].autoWidth)
continue;
size_t len = strlen(Process_fields[i].title);
assert(len <= UINT8_MAX);
Process_fieldWidths[i] = (uint8_t)len;
}
}
void Process_updateFieldWidth(ProcessField key, size_t width) {
if (width > UINT8_MAX)
Process_fieldWidths[key] = UINT8_MAX;
else if (width > Process_fieldWidths[key])
Process_fieldWidths[key] = (uint8_t)width;
}
void Process_updateCPUFieldWidths(float percentage) {
if (percentage < 99.9F) {
Process_updateFieldWidth(PERCENT_CPU, 4);
Process_updateFieldWidth(PERCENT_NORM_CPU, 4);
return;
}
// Add additional two characters, one for "." and another for precision.
uint8_t width = ceil(log10(percentage + 0.1)) + 2;
Process_updateFieldWidth(PERCENT_CPU, width);
Process_updateFieldWidth(PERCENT_NORM_CPU, width);
}

View File

@ -4,7 +4,7 @@
htop - Process.h
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -60,6 +60,26 @@ typedef enum ProcessField_ {
LAST_PROCESSFIELD
} ProcessField;
/* Core process states (shared by platforms)
* NOTE: The enum has an ordering that is important!
* See processStateChar in process.c for ProcessSate -> letter mapping */
typedef enum ProcessState_ {
UNKNOWN = 1,
RUNNABLE,
RUNNING,
QUEUED,
WAITING,
UNINTERRUPTIBLE_WAIT,
BLOCKED,
PAGING,
STOPPED,
TRACED,
ZOMBIE,
DEFUNCT,
IDLE,
SLEEPING
} ProcessState;
struct Settings_;
/* Holds information about regions of the cmdline that should be
@ -76,19 +96,10 @@ typedef struct ProcessCmdlineHighlight_ {
* Process_writeCommand to color the string. str will be NULL for kernel
* threads and zombies */
typedef struct ProcessMergedCommand_ {
uint64_t lastUpdate; /* Marker based on settings->lastUpdate to track when the rendering needs refreshing */
char* str; /* merged Command string */
size_t highlightCount; /* how many portions of cmdline to highlight */
ProcessCmdlineHighlight highlights[8]; /* which portions of cmdline to highlight */
bool separateComm : 1; /* whether comm is a separate field */
bool unmatchedExe : 1; /* whether exe matched with cmdline */
bool cmdlineChanged : 1; /* whether cmdline changed */
bool exeChanged : 1; /* whether exe changed */
bool commChanged : 1; /* whether comm changed */
bool prevMergeSet : 1; /* whether showMergedCommand was set */
bool prevPathSet : 1; /* whether showProgramPath was set */
bool prevCommSet : 1; /* whether findCommInCmdline was set */
bool prevCmdlineSet : 1; /* whether stripExeFromCmdline was set */
bool prevShowThreadNames : 1; /* whether showThreadNames was set */
} ProcessMergedCommand;
typedef struct Process_ {
@ -204,20 +215,8 @@ typedef struct Process_ {
/* Number of major faults the process has made which have required loading a memory page from disk */
unsigned long int majflt;
/*
* Process state (platform dependent):
* D - Waiting
* I - Idle
* L - Acquiring lock
* R - Running
* S - Sleeping
* T - Stopped (on a signal)
* X - Dead
* Z - Zombie
* t - Tracing stop
* ? - Unknown
*/
char state;
/* Process state enum field (platform dependent) */
ProcessState state;
/* Whether the process was updated during the current scan */
bool updated;
@ -244,10 +243,10 @@ typedef struct Process_ {
* Internal state for tree-mode.
*/
int indent;
unsigned int tree_left;
unsigned int tree_right;
unsigned int tree_depth;
unsigned int tree_index;
/* Has no known parent process */
bool isRoot;
/*
* Internal state for merged Command display
@ -273,6 +272,9 @@ typedef struct ProcessFieldData_ {
/* Whether the column should be sorted in descending order by default */
bool defaultSortDesc;
/* Whether the column width is dynamically adjusted (the minimum width is determined by the title length) */
bool autoWidth;
} ProcessFieldData;
// Implemented in platform-specific code:
@ -280,24 +282,26 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
int Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast);
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern uint8_t Process_fieldWidths[LAST_PROCESSFIELD];
#define PROCESS_MIN_PID_DIGITS 5
#define PROCESS_MAX_PID_DIGITS 19
#define PROCESS_MIN_UID_DIGITS 5
#define PROCESS_MAX_UID_DIGITS 20
extern int Process_pidDigits;
extern int Process_uidDigits;
typedef Process* (*Process_New)(const struct Settings_*);
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
typedef const char* (*Process_GetCommandStr)(const Process*);
typedef struct ProcessClass_ {
const ObjectClass super;
const Process_WriteField writeField;
const Process_CompareByKey compareByKey;
const Process_GetCommandStr getCommandStr;
} ProcessClass;
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : Process_getCommandStr((const Process*)(this_)))
#define Process_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_))
static inline pid_t Process_getParentPid(const Process* this) {
@ -339,6 +343,9 @@ static inline bool Process_isThread(const Process* this) {
void Process_setupColumnWidths(void);
/* Sets the size of the UID column based on the passed UID */
void Process_setUidColumnWidth(uid_t maxUid);
/* Takes number in bytes (base 1024). Prints 6 columns. */
void Process_printBytes(RichString* str, unsigned long long number, bool coloring);
@ -358,6 +365,8 @@ void Process_fillStarttimeBuffer(Process* this);
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width);
void Process_printPercentage(float val, char* buffer, int n, uint8_t width, int* attr);
void Process_display(const Object* cast, RichString* out);
void Process_done(Process* this);
@ -378,21 +387,28 @@ bool Process_changePriorityBy(Process* this, Arg delta);
bool Process_sendSignal(Process* this, Arg sgn);
int Process_pidCompare(const void* v1, const void* v2);
static inline int Process_pidEqualCompare(const void* v1, const void* v2) {
const pid_t p1 = ((const Process*)v1)->pid;
const pid_t p2 = ((const Process*)v2)->pid;
return p1 != p2; /* return zero when equal */
}
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
// Avoid direct calls, use Process_getCommand instead
const char* Process_getCommandStr(const Process* this);
const char* Process_getCommand(const Process* this);
void Process_updateComm(Process* this, const char* comm);
void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart, int basenameEnd);
void Process_updateExe(Process* this, const char* exe);
/* This function constructs the string that is displayed by
* Process_writeCommand and also returned by Process_getCommandStr */
* Process_writeCommand and also returned by Process_getCommand */
void Process_makeCommandStr(Process* this);
void Process_writeCommand(const Process* this, int attr, int baseAttr, RichString* str);
void Process_resetFieldWidths(void);
void Process_updateFieldWidth(ProcessField key, size_t width);
void Process_updateCPUFieldWidths(float percentage);
#endif

View File

@ -1,7 +1,7 @@
/*
htop - ProcessList.c
(C) 2004,2005 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -22,11 +22,10 @@ in the source distribution for its full text.
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) {
this->processes = Vector_new(klass, true, DEFAULT_SIZE);
this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); // tree-view auxiliary buffer
this->displayList = Vector_new(klass, false, DEFAULT_SIZE);
this->processTable = Hashtable_new(200, false);
this->displayTreeSet = Hashtable_new(200, false);
this->draftingTreeSet = Hashtable_new(200, false);
this->needsSort = true;
this->usersTable = usersTable;
this->pidMatchList = pidMatchList;
@ -73,11 +72,9 @@ void ProcessList_done(ProcessList* this) {
}
#endif
Hashtable_delete(this->draftingTreeSet);
Hashtable_delete(this->displayTreeSet);
Hashtable_delete(this->processTable);
Vector_delete(this->processes2);
Vector_delete(this->displayList);
Vector_delete(this->processes);
}
@ -85,46 +82,63 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
this->panel = panel;
}
static const char* alignedDynamicColumnTitle(const ProcessList* this, int key) {
static const char* alignedDynamicColumnTitle(const ProcessList* this, int key, char* titleBuffer, size_t titleBufferSize) {
const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key);
if (column == NULL)
return "- ";
static char titleBuffer[DYNAMIC_MAX_COLUMN_WIDTH + /* space */ 1 + /* null terminator */ + 1];
int width = column->width;
if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s", width, column->heading);
xSnprintf(titleBuffer, titleBufferSize, "%*s", width, column->heading);
return titleBuffer;
}
static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessField field) {
static char titleBuffer[UINT8_MAX + sizeof(" ")];
assert(sizeof(titleBuffer) >= DYNAMIC_MAX_COLUMN_WIDTH + sizeof(" "));
assert(sizeof(titleBuffer) >= PROCESS_MAX_PID_DIGITS + sizeof(" "));
assert(sizeof(titleBuffer) >= PROCESS_MAX_UID_DIGITS + sizeof(" "));
if (field >= LAST_PROCESSFIELD)
return alignedDynamicColumnTitle(this, field);
return alignedDynamicColumnTitle(this, field, titleBuffer, sizeof(titleBuffer));
const char* title = Process_fields[field].title;
if (!title)
return "- ";
if (!Process_fields[field].pidColumn)
return title;
if (Process_fields[field].pidColumn) {
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
return titleBuffer;
}
static char titleBuffer[PROCESS_MAX_PID_DIGITS + /* space */ 1 + /* null-terminator */ + 1];
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
if (field == ST_UID) {
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_uidDigits, title);
return titleBuffer;
}
return titleBuffer;
if (Process_fields[field].autoWidth) {
if (field == PERCENT_CPU)
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_fieldWidths[field], title);
else
xSnprintf(titleBuffer, sizeof(titleBuffer), "%-*.*s ", Process_fieldWidths[field], Process_fieldWidths[field], title);
return titleBuffer;
}
return title;
}
void ProcessList_printHeader(const ProcessList* this, RichString* header) {
RichString_rewind(header, RichString_size(header));
const Settings* settings = this->settings;
const ProcessField* fields = settings->fields;
const ScreenSettings* ss = settings->ss;
const ProcessField* fields = ss->fields;
ProcessField key = Settings_getActiveSortKey(settings);
ProcessField key = ScreenSettings_getActiveSortKey(ss);
for (int i = 0; fields[i]; i++) {
int color;
if (settings->treeView && settings->treeViewAlwaysByPID) {
if (ss->treeView && ss->treeViewAlwaysByPID) {
color = CRT_colors[PANEL_HEADER_FOCUS];
} else if (key == fields[i]) {
color = CRT_colors[PANEL_SELECTION_FOCUS];
@ -134,10 +148,11 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header) {
RichString_appendWide(header, color, alignedProcessFieldTitle(this, fields[i]));
if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') {
bool ascending = ScreenSettings_getActiveDirection(ss) == 1;
RichString_rewind(header, 1); // rewind to override space
RichString_appendnWide(header,
CRT_colors[PANEL_SELECTION_FOCUS],
CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_ASC : TREE_STR_DESC],
CRT_treeStr[ascending ? TREE_STR_ASC : TREE_STR_DESC],
1);
}
if (COMM == fields[i] && settings->showMergedCommand) {
@ -147,7 +162,7 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header) {
}
void ProcessList_add(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) == -1);
assert(Hashtable_get(this->processTable, p->pid) == NULL);
p->processList = this;
@ -157,25 +172,23 @@ void ProcessList_add(ProcessList* this, Process* p) {
Vector_add(this->processes, p);
Hashtable_put(this->processTable, p->pid, p);
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) != -1);
assert(Hashtable_get(this->processTable, p->pid) != NULL);
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable)));
}
void ProcessList_remove(ProcessList* this, const Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
assert(Hashtable_get(this->processTable, p->pid) != NULL);
const Process* pp = Hashtable_remove(this->processTable, p->pid);
assert(pp == p); (void)pp;
// ProcessList_removeIndex removes Process p from the list's map and soft deletes
// it from its vector. Vector_compact *must* be called once the caller is done
// removing items.
// Should only be called from ProcessList_scan to avoid breaking dying process highlighting.
static void ProcessList_removeIndex(ProcessList* this, const Process* p, int idx) {
pid_t pid = p->pid;
int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
assert(idx != -1);
if (idx >= 0) {
Vector_remove(this->processes, idx);
}
assert(p == (Process*)Vector_get(this->processes, idx));
assert(Hashtable_get(this->processTable, pid) != NULL);
Hashtable_remove(this->processTable, pid);
Vector_softRemove(this->processes, idx);
if (this->following != -1 && this->following == pid) {
this->following = -1;
@ -183,316 +196,148 @@ void ProcessList_remove(ProcessList* this, const Process* p) {
}
assert(Hashtable_get(this->processTable, pid) == NULL);
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable)));
}
// ProcessList_updateTreeSetLayer sorts this->displayTreeSet,
// relying only on itself.
//
// Algorithm
//
// The algorithm is based on `depth-first search`,
// even though `breadth-first search` approach may be more efficient on first glance,
// after comparison it may be not, as it's not safe to go deeper without first updating the tree structure.
// If it would be safe that approach would likely bring an advantage in performance.
//
// Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
// First it sorts a list. Then it runs the function recursively for each element of the sorted list.
// After that it updates the settings of processes.
//
// It relies on `leftBound` and `rightBound` as an optimization to cut the list size at the time it builds a 'layer'.
//
// It uses a temporary Hashtable `draftingTreeSet` because it's not safe to traverse a tree
// and at the same time make changes in it.
//
static void ProcessList_updateTreeSetLayer(ProcessList* this, unsigned int leftBound, unsigned int rightBound, unsigned int deep, unsigned int left, unsigned int right, unsigned int* index, unsigned int* treeIndex, int indent) {
// It's guaranteed that layer_size is enough space
// but most likely it needs less. Specifically on first iteration.
int layerSize = (right - left) / 2;
// check if we reach `children` of `leaves`
if (layerSize == 0)
return;
Vector* layer = Vector_new(Vector_type(this->processes), false, layerSize);
// Find all processes on the same layer (process with the same `deep` value
// and included in a range from `leftBound` to `rightBound`).
//
// This loop also keeps track of left_bound and right_bound of these processes
// in order not to lose this information once the list is sorted.
//
// The variables left_bound and right_bound are different from what the values lhs and rhs represent.
// While left_bound and right_bound define a range of processes to look at, the values given by lhs and rhs are indices into an array
//
// In the below example note how filtering a range of indices i is different from filtering for processes in the bounds left_bound < x < right_bound …
//
// The nested tree set is sorted by left value, which is guaranteed upon entry/exit of this function.
//
// i | l | r
// 1 | 1 | 9
// 2 | 2 | 8
// 3 | 4 | 5
// 4 | 6 | 7
for (unsigned int i = leftBound; i < rightBound; i++) {
Process* proc = (Process*)Hashtable_get(this->displayTreeSet, i);
assert(proc);
if (proc && proc->tree_depth == deep && proc->tree_left > left && proc->tree_right < right) {
if (Vector_size(layer) > 0) {
Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
// Make a 'right_bound' of previous_process in a layer the current process's index.
//
// Use 'tree_depth' as a temporal variable.
// It's safe to do as later 'tree_depth' will be renovated.
previous_process->tree_depth = proc->tree_index;
}
Vector_add(layer, proc);
}
}
// The loop above changes just up to process-1.
// So the last process of the layer isn't updated by the above code.
//
// Thus, if present, set the `rightBound` to the last process on the layer
if (Vector_size(layer) > 0) {
Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
previous_process->tree_depth = rightBound;
}
Vector_quickSort(layer);
int size = Vector_size(layer);
for (int i = 0; i < size; i++) {
Process* proc = (Process*)Vector_get(layer, i);
unsigned int idx = (*index)++;
int newLeft = (*treeIndex)++;
int level = deep == 0 ? 0 : (int)deep - 1;
int currentIndent = indent == -1 ? 0 : indent | (1 << level);
int nextIndent = indent == -1 ? 0 : ((i < size - 1) ? currentIndent : indent);
unsigned int newLeftBound = proc->tree_index;
unsigned int newRightBound = proc->tree_depth;
ProcessList_updateTreeSetLayer(this, newLeftBound, newRightBound, deep + 1, proc->tree_left, proc->tree_right, index, treeIndex, nextIndent);
int newRight = (*treeIndex)++;
proc->tree_left = newLeft;
proc->tree_right = newRight;
proc->tree_index = idx;
proc->tree_depth = deep;
if (indent == -1) {
proc->indent = 0;
} else if (i == size - 1) {
proc->indent = -currentIndent;
} else {
proc->indent = currentIndent;
}
Hashtable_put(this->draftingTreeSet, proc->tree_index, proc);
// It's not strictly necessary to do this, but doing so anyways
// allows for checking the correctness of the inner workings.
Hashtable_remove(this->displayTreeSet, newLeftBound);
}
Vector_delete(layer);
}
static void ProcessList_updateTreeSet(ProcessList* this) {
unsigned int index = 0;
unsigned int tree_index = 1;
const int vsize = Vector_size(this->processes);
assert(Hashtable_count(this->draftingTreeSet) == 0);
assert((int)Hashtable_count(this->displayTreeSet) == vsize);
ProcessList_updateTreeSetLayer(this, 0, vsize, 0, 0, vsize * 2 + 1, &index, &tree_index, -1);
Hashtable* tmp = this->draftingTreeSet;
this->draftingTreeSet = this->displayTreeSet;
this->displayTreeSet = tmp;
assert(Hashtable_count(this->draftingTreeSet) == 0);
assert((int)Hashtable_count(this->displayTreeSet) == vsize);
}
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show, int* node_counter, int* node_index) {
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, bool show) {
// On OpenBSD the kernel thread 'swapper' has pid 0.
// Do not treat it as root of any tree.
if (pid == 0)
return;
Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* process = (Process*)Vector_get(this->processes, i);
if (process->show && Process_isChildOf(process, pid)) {
process = (Process*)Vector_take(this->processes, i);
Vector_add(children, process);
// The vector is sorted by parent PID, find the start of the range by bisection
int vsize = Vector_size(this->processes);
int l = 0;
int r = vsize;
while (l < r) {
int c = (l + r) / 2;
Process* process = (Process*)Vector_get(this->processes, c);
pid_t ppid = process->isRoot ? 0 : Process_getParentPid(process);
if (ppid < pid) {
l = c + 1;
} else {
r = c;
}
}
// Find the end to know the last line for indent handling purposes
int lastShown = r;
while (r < vsize) {
Process* process = (Process*)Vector_get(this->processes, r);
if (!Process_isChildOf(process, pid))
break;
if (process->show)
lastShown = r;
r++;
}
int size = Vector_size(children);
for (int i = 0; i < size; i++) {
int index = (*node_index)++;
Process* process = (Process*)Vector_get(children, i);
int lft = (*node_counter)++;
for (int i = l; i < r; i++) {
Process* process = (Process*)Vector_get(this->processes, i);
if (!show) {
process->show = false;
}
int s = Vector_size(this->processes2);
if (direction == 1) {
Vector_add(this->processes2, process);
} else {
Vector_insert(this->processes2, 0, process);
}
assert(Vector_size(this->processes2) == s + 1); (void)s;
Vector_add(this->displayList, process);
int nextIndent = indent | (1 << level);
ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false, node_counter, node_index);
if (i == size - 1) {
ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < lastShown) ? nextIndent : indent, process->show && process->showChildren);
if (i == lastShown) {
process->indent = -nextIndent;
} else {
process->indent = nextIndent;
}
int rht = (*node_counter)++;
process->tree_left = lft;
process->tree_right = rht;
process->tree_depth = level + 1;
process->tree_index = index;
Hashtable_put(this->displayTreeSet, index, process);
}
Vector_delete(children);
}
static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
static int compareProcessByKnownParentThenNatural(const void* v1, const void* v2) {
const Process* p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2;
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
}
int result = SPACESHIP_NUMBER(
p1->isRoot ? 0 : Process_getParentPid(p1),
p2->isRoot ? 0 : Process_getParentPid(p2)
);
static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
const Process* p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2;
if (result != 0)
return result;
return SPACESHIP_NUMBER(p1->pid, p2->pid);
return Process_compare(v1, v2);
}
// Builds a sorted tree from scratch, without relying on previously gathered information
static void ProcessList_buildTree(ProcessList* this) {
int node_counter = 1;
int node_index = 0;
int direction = Settings_getActiveDirection(this->settings);
Vector_prune(this->displayList);
// Sort by PID
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
// Mark root processes
int vsize = Vector_size(this->processes);
for (int i = 0; i < vsize; i++) {
Process* process = (Process*)Vector_get(this->processes, i);
pid_t ppid = Process_getParentPid(process);
process->isRoot = false;
// Find all processes whose parent is not visible
int size;
while ((size = Vector_size(this->processes))) {
int i;
for (i = 0; i < size; i++) {
Process* process = (Process*)Vector_get(this->processes, i);
// Immediately consume processes hidden from view
if (!process->show) {
process = (Process*)Vector_take(this->processes, i);
process->indent = 0;
process->tree_depth = 0;
process->tree_left = node_counter++;
process->tree_index = node_index++;
Vector_add(this->processes2, process);
ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, false, &node_counter, &node_index);
process->tree_right = node_counter++;
Hashtable_put(this->displayTreeSet, process->tree_index, process);
break;
}
pid_t ppid = Process_getParentPid(process);
// Bisect the process vector to find parent
int l = 0;
int r = size;
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
// on Mac OS X 10.11.6) cancel bisecting and regard this process as
// root.
if (process->pid == ppid)
r = 0;
// On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2)
// use a ppid of 0. As that PID can't exist, we can skip searching for it.
if (!ppid)
r = 0;
while (l < r) {
int c = (l + r) / 2;
pid_t pid = ((Process*)Vector_get(this->processes, c))->pid;
if (ppid == pid) {
break;
} else if (ppid < pid) {
r = c;
} else {
l = c + 1;
}
}
// If parent not found, then construct the tree with this node as root
if (l >= r) {
process = (Process*)Vector_take(this->processes, i);
process->indent = 0;
process->tree_depth = 0;
process->tree_left = node_counter++;
process->tree_index = node_index++;
Vector_add(this->processes2, process);
Hashtable_put(this->displayTreeSet, process->tree_index, process);
ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, process->showChildren, &node_counter, &node_index);
process->tree_right = node_counter++;
break;
}
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
// on Mac OS X 10.11.6) regard this process as root.
if (process->pid == ppid) {
process->isRoot = true;
continue;
}
// There should be no loop in the process tree
assert(i < size);
// On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2)
// use a ppid of 0. As that PID can't exist, we can skip searching for it.
if (!ppid) {
process->isRoot = true;
continue;
}
// We don't know about its parent for whatever reason
if (ProcessList_findProcess(this, ppid) == NULL)
process->isRoot = true;
}
// Swap listings around
Vector* t = this->processes;
this->processes = this->processes2;
this->processes2 = t;
// Sort by known parent PID (roots first), then PID
Vector_quickSortCustomCompare(this->processes, compareProcessByKnownParentThenNatural);
// Find all processes whose parent is not visible
for (int i = 0; i < vsize; i++) {
Process* process = (Process*)Vector_get(this->processes, i);
// If parent not found, then construct the tree with this node as root
if (process->isRoot) {
process = (Process*)Vector_get(this->processes, i);
process->indent = 0;
process->tree_depth = 0;
Vector_add(this->displayList, process);
ProcessList_buildTreeBranch(this, process->pid, 0, 0, process->showChildren);
continue;
}
}
this->needsSort = false;
// Check consistency of the built structures
assert(Vector_size(this->processes) == vsize); (void)vsize;
assert(Vector_size(this->processes2) == 0);
assert(Vector_size(this->displayList) == vsize); (void)vsize;
}
void ProcessList_sort(ProcessList* this) {
if (this->settings->treeView) {
ProcessList_updateTreeSet(this);
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompare);
void ProcessList_updateDisplayList(ProcessList* this) {
if (this->settings->ss->treeView) {
if (this->needsSort)
ProcessList_buildTree(this);
} else {
Vector_insertionSort(this->processes);
if (this->needsSort)
Vector_insertionSort(this->processes);
Vector_prune(this->displayList);
int size = Vector_size(this->processes);
for (int i = 0; i < size; i++)
Vector_add(this->displayList, Vector_get(this->processes, i));
}
this->needsSort = false;
}
ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
int x = 0;
const ProcessField* fields = this->settings->fields;
const ProcessField* fields = this->settings->ss->fields;
ProcessField field;
for (int i = 0; (field = fields[i]); i++) {
int len = strlen(alignedProcessFieldTitle(this, field));
@ -512,7 +357,10 @@ void ProcessList_expandTree(ProcessList* this) {
}
}
// Called on collapse-all toggle and on startup, possibly in non-tree mode
void ProcessList_collapseAllBranches(ProcessList* this) {
ProcessList_buildTree(this); // Update `tree_depth` fields of the processes
this->needsSort = true; // ProcessList is sorted by parent now, force new sort
int size = Vector_size(this->processes);
for (int i = 0; i < size; i++) {
Process* process = (Process*) Vector_get(this->processes, i);
@ -523,6 +371,8 @@ void ProcessList_collapseAllBranches(ProcessList* this) {
}
void ProcessList_rebuildPanel(ProcessList* this) {
ProcessList_updateDisplayList(this);
const char* incFilter = this->incFilter;
const int currPos = Panel_getSelectedIndex(this->panel);
@ -540,16 +390,16 @@ void ProcessList_rebuildPanel(ProcessList* this) {
}
}
const int processCount = Vector_size(this->processes);
const int processCount = Vector_size(this->displayList);
int idx = 0;
bool foundFollowed = false;
for (int i = 0; i < processCount; i++) {
Process* p = (Process*) Vector_get(this->processes, i);
Process* p = (Process*) Vector_get(this->displayList, i);
if ( (!p->show)
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter, true)))
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
continue;
@ -584,7 +434,7 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting,
Process* proc = (Process*) Hashtable_get(this->processTable, pid);
*preExisting = proc != NULL;
if (proc) {
assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1);
assert(Vector_indexOf(this->processes, proc, Process_pidEqualCompare) != -1);
assert(proc->pid == pid);
} else {
proc = constructor(this->settings);
@ -614,6 +464,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
this->kernelThreads = 0;
this->runningTasks = 0;
Process_resetFieldWidths();
// set scan timestamp
static bool firstScanDone = false;
@ -626,14 +477,19 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
ProcessList_goThroughEntries(this, false);
uid_t maxUid = 0;
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* p = (Process*) Vector_get(this->processes, i);
Process_makeCommandStr(p);
// keep track of the highest UID for column scaling
if (p->st_uid > maxUid)
maxUid = p->st_uid;
if (p->tombStampMs > 0) {
// remove tombed process
if (this->monotonicMs >= p->tombStampMs) {
ProcessList_remove(this, p);
ProcessList_removeIndex(this, p, i);
}
} else if (p->updated == false) {
// process no longer exists
@ -642,18 +498,14 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs;
} else {
// immediately remove
ProcessList_remove(this, p);
ProcessList_removeIndex(this, p, i);
}
}
}
if (this->settings->treeView) {
// Clear out the hashtable to avoid any left-over processes from previous build
//
// The sorting algorithm relies on the fact that
// len(this->displayTreeSet) == len(this->processes)
Hashtable_clear(this->displayTreeSet);
// Compact the processes vector in case of any deletions
Vector_compact(this->processes);
ProcessList_buildTree(this);
}
// Set UID column width based on max UID.
Process_setUidColumnWidth(maxUid);
}

View File

@ -3,7 +3,7 @@
/*
htop - ProcessList.h
(C) 2004,2005 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -43,13 +43,13 @@ typedef unsigned long long int memory_t;
typedef struct ProcessList_ {
const Settings* settings;
Vector* processes;
Vector* processes2;
Hashtable* processTable;
Vector* processes; /* all known processes; sort order can vary and differ from display order */
Vector* displayList; /* process tree flattened in display order (borrowed);
updated in ProcessList_updateDisplayList when rebuilding panel */
Hashtable* processTable; /* fast known process lookup by PID */
UsersTable* usersTable;
Hashtable* displayTreeSet;
Hashtable* draftingTreeSet;
bool needsSort;
Hashtable* dynamicMeters; /* runtime-discovered meters */
Hashtable* dynamicColumns; /* runtime-discovered Columns */
@ -106,9 +106,7 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header);
void ProcessList_add(ProcessList* this, Process* p);
void ProcessList_remove(ProcessList* this, const Process* p);
void ProcessList_sort(ProcessList* this);
void ProcessList_updateDisplayList(ProcessList* this);
ProcessField ProcessList_keyAt(const ProcessList* this, int at);

View File

@ -1,7 +1,7 @@
/*
htop - ProcessLocksScreen.c
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

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

View File

@ -3,7 +3,7 @@
/*
htop - RichString.h
(C) 2004,2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

124
README
View File

@ -1,10 +1,12 @@
# [![htop](htop.png)](https://htop.dev)
# [![htop logo](htop.png)](https://htop.dev)
[![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://web.libera.chat/#htop)
[![Github Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest)
[![GitHub Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest)
[![Packaging status](https://repology.org/badge/tiny-repos/htop.svg)](https://repology.org/project/htop/versions)
[![License: GPL v2+](https://img.shields.io/badge/License-GPL%20v2+-blue.svg)](COPYING?raw=true)
![Screenshot of htop](docs/images/screenshot.png?raw=true)
@ -13,26 +15,30 @@
`htop` is a cross-platform interactive process viewer.
`htop` allows scrolling the list of processes vertically and horizontally to see their full command lines and related information like memory and CPU consumption.
Also system wide information, like load average or swap usage, is shown.
The information displayed is configurable through a graphical setup and can be sorted and filtered interactively.
Tasks related to processes (e.g. killing and renicing) can be done without entering their PIDs.
Running `htop` requires `ncurses` libraries (typically named libncursesw*).
Running `htop` requires `ncurses` libraries, typically named libncurses(w).
For more information and details on how to contribute to `htop` visit [htop.dev](https://htop.dev).
`htop` is written in C.
For more information and details visit [htop.dev](https://htop.dev).
## Build instructions
### Prerequisite
List of build-time dependencies:
* `build-essential` standard GNU autotools-based
* `autoconf`
* `autotools`
* `ncurses`
* standard GNU autotools-based C toolchain
- C99 compliant compiler
- `autoconf`
- `autotools`
* `ncurses`
**Note about `ncurses`:**
> htop requires ncurses 6.0. Be aware the appropriate package is sometimes still called libncurses5 (on Debian/Ubuntu). Also ncurses usually comes in two flavours:
> `htop` requires `ncurses` 6.0. Be aware the appropriate package is sometimes still called libncurses5 (on Debian/Ubuntu). Also `ncurses` usually comes in two flavours:
>* With Unicode support.
>* Without Unicode support.
>
@ -41,28 +47,37 @@ List of build-time dependencies:
List of additional build-time dependencies (based on feature flags):
* `sensors`
* `hwloc`
* `libcap`
* `libcap` (v2.21 or later)
* `libnl-3`
Compiling `htop` requires the header files for `ncurses` . Install these and other required packages for C development from your package manager.
Install these and other required packages for C development from your package manager.
**Debian/Ubuntu**
~~~ shell
sudo apt install libncursesw5-dev autotools-dev autoconf
sudo apt install libncursesw5-dev autotools-dev autoconf build-essential
~~~
**Fedora/RHEL**
~~~ shell
sudo dnf install ncurses-devel automake autoconf
sudo dnf install ncurses-devel automake autoconf gcc
~~~
### Compiling from source:
To compile from sources downloaded from the Git repository (`git clone` or downloads from [Github releases](https://github.com/htop-dev/htop/releases/)), then run:
**Archlinux/Manjaro**
~~~ shell
sudo pacman -S ncurses automake autoconf gcc
~~~
**macOS**
~~~ shell
brew install ncurses automake autoconf gcc
~~~
### Compile from source:
To compile from source, download from the Git repository (`git clone` or downloads from [GitHub releases](https://github.com/htop-dev/htop/releases/)), then run:
~~~ shell
./autogen.sh && ./configure && make
~~~
By default `make install` will install into `/usr/local`, for changing the path use `./configure --prefix=/some/path`.
### Install
To install on the local system run `make install`. By default `make install` installs into `/usr/local`. To change this path use `./configure --prefix=/some/path`.
@ -74,52 +89,55 @@ To install on the local system run `make install`. By default `make install` ins
* `--enable-unicode`:
enable Unicode support
dependency: *libncursesw*
default: *yes*
* `--enable-pcp`:
enable Performance Co-Pilot support via a new pcp-htop utility
dependency: *libpcp*
default: *no*
- dependency: *libncursesw*
- default: *yes*
* `--enable-affinity`:
enable `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc
default: *check*
- default: *check*
* `--enable-hwloc`:
enable hwloc support for CPU affinity; disables affinity support
dependency: *libhwloc*
default: *no*
- dependency: *libhwloc*
- default: *no*
* `--enable-static`:
build a static htop binary; hwloc and delay accounting are not supported
default: *no*
- default: *no*
* `--enable-debug`:
Enable asserts and internal sanity checks; implies a performance penalty
default: *no*
- default: *no*
#### Performance Co-Pilot
* `--enable-pcp`:
enable Performance Co-Pilot support via a new pcp-htop utility
- dependency: *libpcp*
- default: *no*
#### Linux
* `--enable-sensors`:
enable libsensors(3) support for reading temperature data
dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available
default: *check*
- dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available
- default: *check*
* `--enable-capabilities`:
enable Linux capabilities support
dependency: *libcap*
default: *check*
- dependency: *libcap*
- default: *check*
* `--with-proc`:
location of a Linux-compatible proc filesystem
default: */proc*
- default: */proc*
* `--enable-openvz`:
enable OpenVZ support
default: *no*
- default: *no*
* `--enable-vserver`:
enable VServer support
default: *no*
- default: *no*
* `--enable-ancient-vserver`:
enable ancient VServer support (implies `--enable-vserver`)
default: *no*
- default: *no*
* `--enable-delayacct`:
enable Linux delay accounting support
dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
default: *check*
- dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
- default: *check*
## Runtime dependencies:
@ -128,40 +146,42 @@ To install on the local system run `make install`. By default `make install` ins
### Runtime optional dependencies:
`htop` has a set of fixed optional dependencies, depending on build/configure option used:
* `libdl`, if not building static and support for some of the optional libraries is enabled, is always required when support for to optionally load dependencies (i.e. `libsensors`, `systemd`) is present.
* `libcap`, user-space interfaces to the POSIX 1003.1e, is always required when `--enable-capabilities` was used to configure `htop`.
#### Linux
* `libdl`, if not building a static binary, is always required when support for optional dependencies (i.e. `libsensors`, `libsystemd`) is present.
* `libcap`, user-space interfaces to POSIX 1003.1e capabilities, is always required when `--enable-capabilities` was used to configure `htop`.
* `libsensors`, readout of temperatures and CPU speeds, is optional even when `--enable-sensors` was used to configure `htop`.
* `systemd` is optional when `--enable-static` was not used to configure `htop` (Linux only). If building statically and `libsystemd` is not found by `configure` support for the SystemD meter is disabled entirely.
* `libsystemd` is optional when `--enable-static` was not used to configure `htop`. If building statically and `libsystemd` is not found by `configure`, support for the systemd meter is disabled entirely.
`htop` checks for the availability of the actual runtime lib as `htop` runs.
`htop` checks for the availability of the actual runtime libraries as `htop` runs.
**BSD**
On most *BSD systems you also have `kvm` as a static requirement to read all the kernel information.
#### BSD
On most BSD systems `kvm` is a requirement to read kernel information.
More information on required and optional dependencies can be found in [configure.ac](configure.ac).
## Usage
See the manual page (`man htop`) or the on-line help ('F1' or 'h' inside `htop`) for a list of supported key commands.
See the manual page (`man htop`) or the help menu (**F1** or **h** inside `htop`) for a list of supported key commands.
## Support
If you have trouble running `htop` please consult your Operating System / Linux distribution documentation for getting support and filing bugs.
If you have trouble running `htop` please consult your operating system / Linux distribution documentation for getting support and filing bugs.
## Bugs, development feedback
We have a [development mailing list](https://htop.dev/mailinglist.html). Feel free to subscribe for release announcements or asking questions on the development of htop.
We have a [development mailing list](https://htop.dev/mailinglist.html). Feel free to subscribe for release announcements or asking questions on the development of `htop`.
You can also join our IRC channel #htop on Libera.Chat and talk to the developers there.
You can also join our IRC channel [#htop on Libera.Chat](https://web.libera.chat/#htop) and talk to the developers there.
If you have found an issue with the source of htop, please check whether this has already been reported in our [Github issue tracker](https://github.com/htop-dev/htop/issues).
If not, please file a new issue describing the problem you have found, the location in the source code you are referring to and a possible fix.
If you have found an issue within the source of `htop`, please check whether this has already been reported in our [GitHub issue tracker](https://github.com/htop-dev/htop/issues).
If not, please file a new issue describing the problem you have found, the potential location in the source code you are referring to and a possible fix if available.
## History
`htop` was invented, developed and maintained by Hisham Muhammad from 2004 to 2019. His [legacy repository](https://github.com/hishamhm/htop/) has been archived to preserve the history.
`htop` was invented, developed and maintained by [Hisham Muhammad](https://hisham.hm/) from 2004 to 2019. His [legacy repository](https://github.com/hishamhm/htop/) has been archived to preserve the history.
In 2020 a [team](https://github.com/orgs/htop-dev/people) took over the development amicably and continues to maintain `htop` collaboratively.
## License
GNU General Public License, version 2 (GPL-2.0)
GNU General Public License, version 2 (GPL-2.0) or, at your option, any later version.

View File

@ -1,7 +1,7 @@
/*
htop - RichString.c
(C) 2004,2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -3,7 +3,7 @@
/*
htop - RichString.h
(C) 2004,2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

View File

@ -1,7 +1,7 @@
/*
htop - ScreenManager.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -16,6 +16,7 @@ in the source distribution for its full text.
#include "CRT.h"
#include "FunctionBar.h"
#include "Macros.h"
#include "Object.h"
#include "Platform.h"
#include "ProcessList.h"
@ -49,27 +50,43 @@ inline int ScreenManager_size(const ScreenManager* this) {
}
void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
ScreenManager_insert(this, item, size, Vector_size(this->panels));
}
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx) {
int lastX = 0;
if (this->panelCount > 0) {
const Panel* last = (const Panel*) Vector_get(this->panels, this->panelCount - 1);
if (idx > 0) {
const Panel* last = (const Panel*) Vector_get(this->panels, idx - 1);
lastX = last->x + last->w + 1;
}
int height = LINES - this->y1 - (this->header ? this->header->height : 0) + this->y2;
if (size > 0) {
Panel_resize(item, size, height);
} else {
Panel_resize(item, COLS - this->x1 + this->x2 - lastX, height);
if (size <= 0) {
size = COLS - this->x1 + this->x2 - lastX;
}
Panel_resize(item, size, height);
Panel_move(item, lastX, this->y1 + (this->header ? this->header->height : 0));
Vector_add(this->panels, item);
if (idx < this->panelCount) {
for (int i = idx + 1; i <= this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x + size, p->y);
}
}
Vector_insert(this->panels, idx, item);
item->needsRedraw = true;
this->panelCount++;
}
Panel* ScreenManager_remove(ScreenManager* this, int idx) {
assert(this->panelCount > idx);
int w = ((Panel*) Vector_get(this->panels, idx))->w;
Panel* panel = (Panel*) Vector_remove(this->panels, idx);
this->panelCount--;
if (idx < this->panelCount) {
for (int i = idx; i < this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x - w, p->y);
}
}
return panel;
}
@ -88,7 +105,7 @@ void ScreenManager_resize(ScreenManager* this) {
Panel_move(panel, lastX, y1_header);
}
static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut) {
static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut, bool *force_redraw) {
ProcessList* pl = this->header->pl;
Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs);
@ -103,13 +120,18 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) {
*oldTime = newTime;
int oldUidDigits = Process_uidDigits;
if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->ss->treeView)) {
pl->needsSort = true;
*sortTimeout = 1;
}
// scan processes first - some header values are calculated there
ProcessList_scan(pl, this->state->pauseProcessUpdate);
// always update header, especially to avoid gaps in graph meters
Header_updateData(this->header);
if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) {
ProcessList_sort(pl);
*sortTimeout = 1;
// force redraw if the number of UID digits was changed
if (Process_uidDigits != oldUidDigits) {
*force_redraw = true;
}
*redraw = true;
}
@ -120,7 +142,53 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
*rescan = false;
}
static inline bool drawTab(int* y, int* x, int l, const char* name, bool cur) {
attrset(CRT_colors[cur ? SCREENS_CUR_BORDER : SCREENS_OTH_BORDER]);
mvaddch(*y, *x, '[');
(*x)++;
if (*x >= l)
return false;
int nameLen = strlen(name);
int n = MINIMUM(l - *x, nameLen);
attrset(CRT_colors[cur ? SCREENS_CUR_TEXT : SCREENS_OTH_TEXT]);
mvaddnstr(*y, *x, name, n);
*x += n;
if (*x >= l)
return false;
attrset(CRT_colors[cur ? SCREENS_CUR_BORDER : SCREENS_OTH_BORDER]);
mvaddch(*y, *x, ']');
*x += 2;
if (*x >= l)
return false;
return true;
}
static void ScreenManager_drawScreenTabs(ScreenManager* this) {
ScreenSettings** screens = this->settings->screens;
int cur = this->settings->ssIndex;
int l = COLS;
Panel* panel = (Panel*) Vector_get(this->panels, 0);
int y = panel->y - 1;
int x = 2;
if (this->name) {
drawTab(&y, &x, l, this->name, true);
return;
}
for (int s = 0; screens[s]; s++) {
bool ok = drawTab(&y, &x, l, screens[s]->name, s == cur);
if (!ok) {
break;
}
}
attrset(CRT_colors[RESET_COLOR]);
}
static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_redraw) {
if (this->settings->screenTabs) {
ScreenManager_drawScreenTabs(this);
}
const int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i);
@ -133,7 +201,7 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_
}
}
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, const char* name) {
bool quit = false;
int focus = 0;
@ -151,9 +219,11 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
int sortTimeout = 0;
int resetSortTimeout = 5;
this->name = name;
while (!quit) {
if (this->header) {
checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut);
checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut, &force_redraw);
}
if (redraw || force_redraw) {
@ -162,10 +232,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
}
int prevCh = ch;
#ifdef HAVE_SET_ESCDELAY
set_escdelay(25);
#endif
ch = getch();
ch = Panel_getCh(panelFocus);
HandlerResult result = IGNORED;
#ifdef HAVE_GETMOUSE
@ -184,6 +251,9 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (mevent.y == panel->y) {
ch = EVENT_HEADER_CLICK(mevent.x - panel->x);
break;
} else if (this->settings->screenTabs && mevent.y == panel->y - 1) {
ch = EVENT_SCREEN_TAB_CLICK(mevent.x);
break;
} else if (mevent.y > panel->y && mevent.y <= panel->y + panel->h) {
ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) {

View File

@ -3,7 +3,7 @@
/*
htop - ScreenManager.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -22,6 +22,7 @@ typedef struct ScreenManager_ {
int x2;
int y2;
Vector* panels;
const char* name;
int panelCount;
Header* header;
const Settings* settings;
@ -37,10 +38,12 @@ int ScreenManager_size(const ScreenManager* this);
void ScreenManager_add(ScreenManager* this, Panel* item, int size);
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx);
Panel* ScreenManager_remove(ScreenManager* this, int idx);
void ScreenManager_resize(ScreenManager* this);
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey);
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, const char* name);
#endif

328
ScreensPanel.c Normal file
View File

@ -0,0 +1,328 @@
/*
htop - ScreensPanel.c
(C) 2004-2011 Hisham H. Muhammad
(C) 2020-2022 htop dev team
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
#include "ScreensPanel.h"
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "CRT.h"
#include "FunctionBar.h"
#include "Hashtable.h"
#include "ProvideCurses.h"
#include "Settings.h"
#include "XUtils.h"
static void ScreenListItem_delete(Object* cast) {
ScreenListItem* this = (ScreenListItem*)cast;
if (this->ss) {
ScreenSettings_delete(this->ss);
}
ListItem_delete(cast);
}
ObjectClass ScreenListItem_class = {
.extends = Class(ListItem),
.display = ListItem_display,
.delete = ScreenListItem_delete,
.compare = ListItem_compare
};
ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss) {
ScreenListItem* this = AllocThis(ScreenListItem);
ListItem_init((ListItem*)this, value, 0);
this->ss = ss;
return this;
}
static const char* const ScreensFunctions[] = {" ", "Rename", " ", " ", "New ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
static void ScreensPanel_delete(Object* object) {
Panel* super = (Panel*) object;
ScreensPanel* this = (ScreensPanel*) object;
/* do not delete screen settings still in use */
int n = Panel_size(super);
for (int i = 0; i < n; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
item->ss = NULL;
}
Panel_done(super);
free(this);
}
static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
if (ch >= 32 && ch < 127 && ch != '=') {
if (this->cursor < SCREEN_NAME_LEN - 1) {
this->buffer[this->cursor] = (char)ch;
this->cursor++;
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
} else {
switch(ch) {
case 127:
case KEY_BACKSPACE:
{
if (this->cursor > 0) {
this->cursor--;
this->buffer[this->cursor] = '\0';
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
break;
}
case '\n':
case '\r':
case KEY_ENTER:
{
ListItem* item = (ListItem*) Panel_getSelected(super);
if (!item)
break;
free(this->saved);
item->value = xStrdup(this->buffer);
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
ScreensPanel_update(super);
break;
}
case 27: // Esc
{
ListItem* item = (ListItem*) Panel_getSelected(super);
if (!item)
break;
item->value = this->saved;
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
break;
}
}
}
return HANDLED;
}
static void startRenaming(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
ListItem* item = (ListItem*) Panel_getSelected(super);
if (item == NULL)
return;
this->renaming = true;
super->cursorOn = true;
char* name = item->value;
this->saved = name;
strncpy(this->buffer, name, SCREEN_NAME_LEN);
this->buffer[SCREEN_NAME_LEN] = '\0';
this->cursor = strlen(this->buffer);
item->value = this->buffer;
Panel_setSelectionColor(super, PANEL_EDIT);
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
static void rebuildSettingsArray(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
int n = Panel_size(super);
free(this->settings->screens);
this->settings->screens = xMallocArray(n + 1, sizeof(ScreenSettings*));
this->settings->screens[n] = NULL;
for (int i = 0; i < n; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
this->settings->screens[i] = item->ss;
}
this->settings->nScreens = n;
}
static void addNewScreen(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
const char* name = "New";
ScreenSettings* ss = Settings_newScreen(this->settings, &(const ScreenDefaults){ .name = name, .columns = "PID Command", .sortKey = "PID" });
ScreenListItem* item = ScreenListItem_new(name, ss);
int idx = Panel_getSelectedIndex(super);
Panel_insert(super, idx + 1, (Object*) item);
Panel_setSelected(super, idx + 1);
}
static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
int selected = Panel_getSelectedIndex(super);
ScreenListItem* oldFocus = (ScreenListItem*) Panel_getSelected(super);
bool shouldRebuildArray = false;
HandlerResult result = IGNORED;
switch(ch) {
case '\n':
case '\r':
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
{
this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? PANEL_SELECTION_FOLLOW : PANEL_SELECTION_FOCUS);
ListItem* item = (ListItem*) Panel_getSelected(super);
if (item)
item->moving = this->moving;
result = HANDLED;
break;
}
case EVENT_SET_SELECTED:
result = HANDLED;
break;
case KEY_NPAGE:
case KEY_PPAGE:
case KEY_HOME:
case KEY_END: {
Panel_onKey(super, ch);
break;
}
case KEY_F(2):
case KEY_CTRL('R'):
{
startRenaming(super);
result = HANDLED;
break;
}
case KEY_F(5):
case KEY_CTRL('N'):
{
addNewScreen(super);
startRenaming(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_UP:
{
if (!this->moving) {
Panel_onKey(super, ch);
break;
}
/* else fallthrough */
} /* FALLTHRU */
case KEY_F(7):
case '[':
case '-':
{
Panel_moveSelectedUp(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_DOWN:
{
if (!this->moving) {
Panel_onKey(super, ch);
break;
}
/* else fallthrough */
} /* FALLTHRU */
case KEY_F(8):
case ']':
case '+':
{
Panel_moveSelectedDown(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_F(9):
//case KEY_DC:
{
if (Panel_size(super) > 1) {
Panel_remove(super, selected);
}
shouldRebuildArray = true;
result = HANDLED;
break;
}
default:
{
if (ch < 255 && isalpha(ch))
result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP)
result = IGNORED;
break;
}
}
ScreenListItem* newFocus = (ScreenListItem*) Panel_getSelected(super);
if (newFocus && oldFocus != newFocus) {
ColumnsPanel_fill(this->columns, newFocus->ss, this->settings->dynamicColumns);
result = HANDLED;
}
if (shouldRebuildArray)
rebuildSettingsArray(super);
if (result == HANDLED)
ScreensPanel_update(super);
return result;
}
static HandlerResult ScreensPanel_eventHandler(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
if (this->renaming) {
return ScreensPanel_eventHandlerRenaming(super, ch);
} else {
return ScreensPanel_eventHandlerNormal(super, ch);
}
}
PanelClass ScreensPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ScreensPanel_delete
},
.eventHandler = ScreensPanel_eventHandler
};
ScreensPanel* ScreensPanel_new(Settings* settings) {
ScreensPanel* this = AllocThis(ScreensPanel);
Panel* super = (Panel*) this;
Hashtable* columns = settings->dynamicColumns;
FunctionBar* fuBar = FunctionBar_new(ScreensFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->columns = ColumnsPanel_new(settings->screens[0], columns, &(settings->changed));
this->moving = false;
this->renaming = false;
super->cursorOn = false;
this->cursor = 0;
Panel_setHeader(super, "Screens");
for (unsigned int i = 0; i < settings->nScreens; i++) {
ScreenSettings* ss = settings->screens[i];
char* name = ss->name;
Panel_add(super, (Object*) ScreenListItem_new(name, ss));
}
return this;
}
void ScreensPanel_update(Panel* super) {
ScreensPanel* this = (ScreensPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
this->settings->lastUpdate++;
this->settings->screens = xReallocArray(this->settings->screens, size + 1, sizeof(ScreenSettings*));
for (int i = 0; i < size; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
ScreenSettings* ss = item->ss;
free(ss->name);
this->settings->screens[i] = ss;
ss->name = xStrdup(((ListItem*) item)->value);
}
this->settings->screens[size] = NULL;
}

53
ScreensPanel.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef HEADER_ScreensPanel
#define HEADER_ScreensPanel
/*
htop - ScreensPanel.h
(C) 2004-2011 Hisham H. Muhammad
(C) 2020-2022 htop dev team
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
#include <stdbool.h>
#include "ColumnsPanel.h"
#include "ListItem.h"
#include "Object.h"
#include "Panel.h"
#include "ScreenManager.h"
#include "Settings.h"
#ifndef SCREEN_NAME_LEN
#define SCREEN_NAME_LEN 20
#endif
typedef struct ScreensPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
ColumnsPanel* columns;
char buffer[SCREEN_NAME_LEN + 1];
char* saved;
int cursor;
bool moving;
bool renaming;
} ScreensPanel;
typedef struct ScreenListItem_ {
ListItem super;
ScreenSettings* ss;
} ScreenListItem;
extern ObjectClass ScreenListItem_class;
ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss);
extern PanelClass ScreensPanel_class;
ScreensPanel* ScreensPanel_new(Settings* settings);
void ScreensPanel_update(Panel* super);
#endif

View File

@ -1,7 +1,7 @@
/*
htop - Settings.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -24,18 +24,61 @@ in the source distribution for its full text.
#include "XUtils.h"
/*
static char** readQuotedList(char* line) {
int n = 0;
char** list = xCalloc(sizeof(char*), 1);
int start = 0;
for (;;) {
while (line[start] && line[start] == ' ') {
start++;
}
if (line[start] != '"') {
break;
}
start++;
int close = start;
while (line[close] && line[close] != '"') {
close++;
}
int len = close - start;
char* item = xMalloc(len + 1);
strncpy(item, line + start, len);
item[len] = '\0';
list[n] = item;
n++;
list = xRealloc(list, sizeof(char*) * (n + 1));
start = close + 1;
}
list[n] = NULL;
return list;
}
static void writeQuotedList(FILE* fd, char** list) {
const char* sep = "";
for (int i = 0; list[i]; i++) {
fprintf(fd, "%s\"%s\"", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
*/
void Settings_delete(Settings* this) {
free(this->filename);
free(this->fields);
for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
if (this->hColumns[i].names) {
for (uint8_t j = 0; j < this->hColumns[i].len; j++)
free(this->hColumns[i].names[j]);
free(this->hColumns[i].names);
}
String_freeArray(this->hColumns[i].names);
free(this->hColumns[i].modes);
}
free(this->hColumns);
if (this->screens) {
for (unsigned int i = 0; this->screens[i]; i++) {
ScreenSettings_delete(this->screens[i]);
}
free(this->screens);
}
free(this);
}
@ -65,16 +108,58 @@ static void Settings_readMeterModes(Settings* this, const char* line, unsigned i
this->hColumns[column].modes = modes;
}
static bool Settings_validateMeters(Settings* this) {
const size_t colCount = HeaderLayout_getColumns(this->hLayout);
bool anyMeter = false;
for (size_t column = 0; column < colCount; column++) {
char** names = this->hColumns[column].names;
const int* modes = this->hColumns[column].modes;
const size_t len = this->hColumns[column].len;
if (!len)
continue;
if (!names || !modes)
return false;
anyMeter |= !!len;
// Check for each mode there is an entry with a non-NULL name
for (size_t meterIdx = 0; meterIdx < len; meterIdx++)
if (!names[meterIdx])
return false;
if (names[len])
return false;
}
return anyMeter;
}
static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount) {
int sizes[] = { 3, 3 };
if (initialCpuCount > 4 && initialCpuCount <= 128) {
sizes[1]++;
}
for (int i = 0; i < 2; i++) {
// Release any previously allocated memory
for (size_t i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
String_freeArray(this->hColumns[i].names);
free(this->hColumns[i].modes);
}
free(this->hColumns);
this->hLayout = HF_TWO_50_50;
this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting));
for (size_t i = 0; i < 2; i++) {
this->hColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->hColumns[i].modes = xCalloc(sizes[i], sizeof(int));
this->hColumns[i].len = sizes[i];
}
int r = 0;
if (initialCpuCount > 128) {
@ -117,50 +202,121 @@ static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount)
this->hColumns[1].modes[r++] = TEXT_METERMODE;
}
static void Settings_readFields(Settings* settings, const char* line) {
static const char* toFieldName(Hashtable* columns, int id) {
if (id < 0)
return NULL;
if (id >= LAST_PROCESSFIELD) {
const DynamicColumn* column = DynamicColumn_lookup(columns, id);
return column->name;
}
return Process_fields[id].name;
}
static int toFieldIndex(Hashtable* columns, const char* str) {
if (isdigit(str[0])) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(str) + 1;
if (toFieldName(columns, id)) {
return id;
}
} else {
// Dynamically-defined columns are always stored by-name.
char dynamic[32] = {0};
if (sscanf(str, "Dynamic(%30s)", dynamic)) {
char* end;
if ((end = strrchr(dynamic, ')')) != NULL) {
bool success;
unsigned int key;
*end = '\0';
success = DynamicColumn_search(columns, dynamic, &key) != NULL;
*end = ')';
if (success)
return key;
}
}
// Fallback to iterative scan of table of fields by-name.
for (int p = 1; p < LAST_PROCESSFIELD; p++) {
const char* pName = toFieldName(columns, p);
if (pName && strcmp(pName, str) == 0)
return p;
}
}
return -1;
}
static void ScreenSettings_readFields(ScreenSettings* ss, Hashtable* columns, const char* line) {
char* trim = String_trim(line);
char** ids = String_split(trim, ' ', NULL);
free(trim);
settings->flags = 0;
/* reset default fields */
memset(ss->fields, '\0', LAST_PROCESSFIELD * sizeof(ProcessField));
unsigned int i, j;
for (j = 0, i = 0; ids[i]; i++) {
for (size_t j = 0, i = 0; ids[i]; i++) {
if (j >= UINT_MAX / sizeof(ProcessField))
continue;
if (j >= LAST_PROCESSFIELD) {
settings->fields = xRealloc(settings->fields, j * sizeof(ProcessField));
memset(&settings->fields[j], 0, sizeof(ProcessField));
}
// Dynamically-defined columns are always stored by-name.
char* end, dynamic[32] = {0};
if (sscanf(ids[i], "Dynamic(%30s)", dynamic)) {
if ((end = strrchr(dynamic, ')')) == NULL)
continue;
*end = '\0';
unsigned int key;
if (!DynamicColumn_search(settings->dynamicColumns, dynamic, &key))
continue;
settings->fields[j++] = key;
continue;
}
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
if (id > 0 && id < LAST_PROCESSFIELD && Process_fields[id].name) {
settings->flags |= Process_fields[id].flags;
settings->fields[j++] = id;
ss->fields = xRealloc(ss->fields, (j + 1) * sizeof(ProcessField));
memset(&ss->fields[j], 0, sizeof(ProcessField));
}
int id = toFieldIndex(columns, ids[i]);
if (id >= 0)
ss->fields[j] = id;
if (id > 0 && id < LAST_PROCESSFIELD)
ss->flags |= Process_fields[id].flags;
j++;
}
settings->fields[j] = NULL_PROCESSFIELD;
String_freeArray(ids);
}
ScreenSettings* Settings_newScreen(Settings* this, const ScreenDefaults* defaults) {
int sortKey = defaults->sortKey ? toFieldIndex(this->dynamicColumns, defaults->sortKey) : PID;
int sortDesc = (sortKey >= 0 && sortKey < LAST_PROCESSFIELD) ? Process_fields[sortKey].defaultSortDesc : 1;
ScreenSettings* ss = xMalloc(sizeof(ScreenSettings));
*ss = (ScreenSettings) {
.name = xStrdup(defaults->name),
.fields = xCalloc(LAST_PROCESSFIELD, sizeof(ProcessField)),
.flags = 0,
.direction = sortDesc ? -1 : 1,
.treeDirection = 1,
.sortKey = sortKey,
.treeSortKey = PID,
.treeView = false,
.treeViewAlwaysByPID = false,
.allBranchesCollapsed = false,
};
ScreenSettings_readFields(ss, this->dynamicColumns, defaults->columns);
this->screens[this->nScreens] = ss;
this->nScreens++;
this->screens = xRealloc(this->screens, sizeof(ScreenSettings*) * (this->nScreens + 1));
this->screens[this->nScreens] = NULL;
return ss;
}
void ScreenSettings_delete(ScreenSettings* this) {
free(this->name);
free(this->fields);
free(this);
}
static ScreenSettings* Settings_defaultScreens(Settings* this) {
if (this->nScreens)
return this->screens[0];
for (unsigned int i = 0; i < Platform_numberOfDefaultScreens; i++) {
const ScreenDefaults* defaults = &Platform_defaultScreens[i];
Settings_newScreen(this, defaults);
}
return this->screens[0];
}
static bool Settings_read(Settings* this, const char* fileName, unsigned int initialCpuCount) {
FILE* fd = fopen(fileName, "r");
if (!fd)
return false;
ScreenSettings* screen = NULL;
bool didReadMeters = false;
bool didReadAny = false;
for (;;) {
@ -180,28 +336,47 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini
this->config_version = atoi(option[1]);
if (this->config_version > CONFIG_READER_MIN_VERSION) {
// the version of the config file on disk is newer than what we can read
fprintf(stderr, "WARNING: %s specifies configuration format version v%d, but this %s binary supports up to v%d.", fileName, this->config_version, PACKAGE, CONFIG_READER_MIN_VERSION);
fprintf(stderr, " The configuration version will be downgraded to v%d when %s exits.\n", CONFIG_READER_MIN_VERSION, PACKAGE);
fprintf(stderr, "WARNING: %s specifies configuration format\n", fileName);
fprintf(stderr, " version v%d, but this %s binary only supports up to version v%d.\n", this->config_version, PACKAGE, CONFIG_READER_MIN_VERSION);
fprintf(stderr, " The configuration file will be downgraded to v%d when %s exits.\n", CONFIG_READER_MIN_VERSION, PACKAGE);
String_freeArray(option);
fclose(fd);
return false;
}
} else if (String_eq(option[0], "fields")) {
Settings_readFields(this, option[1]);
} else if (String_eq(option[0], "sort_key")) {
} else if (String_eq(option[0], "fields") && this->config_version <= 2) {
// old (no screen) naming also supported for backwards compatibility
screen = Settings_defaultScreens(this);
ScreenSettings_readFields(screen, this->dynamicColumns, option[1]);
} else if (String_eq(option[0], "sort_key") && this->config_version <= 2) {
// old (no screen) naming also supported for backwards compatibility
// This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "tree_sort_key")) {
screen = Settings_defaultScreens(this);
screen->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "tree_sort_key") && this->config_version <= 2) {
// old (no screen) naming also supported for backwards compatibility
// This "+1" is for compatibility with the older enum format.
this->treeSortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) {
this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_sort_direction")) {
this->treeDirection = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
} else if (String_eq(option[0], "tree_view_always_by_pid")) {
this->treeViewAlwaysByPID = atoi(option[1]);
} else if (String_eq(option[0], "all_branches_collapsed")) {
this->allBranchesCollapsed = atoi(option[1]);
screen = Settings_defaultScreens(this);
screen->treeSortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction") && this->config_version <= 2) {
// old (no screen) naming also supported for backwards compatibility
screen = Settings_defaultScreens(this);
screen->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_sort_direction") && this->config_version <= 2) {
// old (no screen) naming also supported for backwards compatibility
screen = Settings_defaultScreens(this);
screen->treeDirection = atoi(option[1]);
} else if (String_eq(option[0], "tree_view") && this->config_version <= 2) {
// old (no screen) naming also supported for backwards compatibility
screen = Settings_defaultScreens(this);
screen->treeView = atoi(option[1]);
} else if (String_eq(option[0], "tree_view_always_by_pid") && this->config_version <= 2) {
// old (no screen) naming also supported for backwards compatibility
screen = Settings_defaultScreens(this);
screen->treeViewAlwaysByPID = atoi(option[1]);
} else if (String_eq(option[0], "all_branches_collapsed") && this->config_version <= 2) {
// old (no screen) naming also supported for backwards compatibility
screen = Settings_defaultScreens(this);
screen->allBranchesCollapsed = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) {
this->hideKernelThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_userland_threads")) {
@ -232,6 +407,8 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini
this->showMergedCommand = atoi(option[1]);
} else if (String_eq(option[0], "header_margin")) {
this->headerMargin = atoi(option[1]);
} else if (String_eq(option[0], "screen_tabs")) {
this->screenTabs = atoi(option[1]);
} else if (String_eq(option[0], "expand_system_time")) {
// Compatibility option.
this->detailedCPUTime = atoi(option[1]);
@ -297,23 +474,49 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini
} else if (String_eq(option[0], "topology_affinity")) {
this->topologyAffinity = !!atoi(option[1]);
#endif
} else if (strncmp(option[0], "screen:", 7) == 0) {
screen = Settings_newScreen(this, &(const ScreenDefaults){ .name = option[0] + 7, .columns = option[1] });
} else if (String_eq(option[0], ".sort_key")) {
if (screen)
screen->sortKey = toFieldIndex(this->dynamicColumns, option[1]);
} else if (String_eq(option[0], ".tree_sort_key")) {
if (screen)
screen->treeSortKey = toFieldIndex(this->dynamicColumns, option[1]);
} else if (String_eq(option[0], ".sort_direction")) {
if (screen)
screen->direction = atoi(option[1]);
} else if (String_eq(option[0], ".tree_sort_direction")) {
if (screen)
screen->treeDirection = atoi(option[1]);
} else if (String_eq(option[0], ".tree_view")) {
if (screen)
screen->treeView = atoi(option[1]);
} else if (String_eq(option[0], ".tree_view_always_by_pid")) {
if (screen)
screen->treeViewAlwaysByPID = atoi(option[1]);
} else if (String_eq(option[0], ".all_branches_collapsed")) {
if (screen)
screen->allBranchesCollapsed = atoi(option[1]);
}
String_freeArray(option);
}
fclose(fd);
if (!didReadMeters) {
if (!didReadMeters || !Settings_validateMeters(this))
Settings_defaultMeters(this, initialCpuCount);
}
if (!this->nScreens)
Settings_defaultScreens(this);
return didReadAny;
}
static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns, const char* name, char separator) {
fprintf(fd, "%s=", name);
static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns, bool byName, char separator) {
const char* sep = "";
for (unsigned int i = 0; fields[i]; i++) {
if (fields[i] >= LAST_PROCESSFIELD) {
const DynamicColumn* column = DynamicColumn_lookup(columns, fields[i]);
fprintf(fd, "%sDynamic(%s)", sep, column->name);
if (fields[i] < LAST_PROCESSFIELD && byName) {
const char* pName = toFieldName(columns, fields[i]);
fprintf(fd, "%s%s", sep, pName);
} else if (fields[i] >= LAST_PROCESSFIELD && byName) {
const char* pName = toFieldName(columns, fields[i]);
fprintf(fd, " Dynamic(%s)", pName);
} else {
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i] - 1);
@ -323,18 +526,22 @@ static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns
fputc(separator, fd);
}
static void writeMeters(const Settings* this, FILE* fd, char separator, unsigned int column) {
static void writeList(FILE* fd, char** list, int len, char separator) {
const char* sep = "";
for (uint8_t i = 0; i < this->hColumns[column].len; i++) {
fprintf(fd, "%s%s", sep, this->hColumns[column].names[i]);
for (int i = 0; i < len; i++) {
fprintf(fd, "%s%s", sep, list[i]);
sep = " ";
}
fputc(separator, fd);
}
static void writeMeters(const Settings* this, FILE* fd, char separator, unsigned int column) {
writeList(fd, this->hColumns[column].names, this->hColumns[column].len, separator);
}
static void writeMeterModes(const Settings* this, FILE* fd, char separator, unsigned int column) {
const char* sep = "";
for (uint8_t i = 0; i < this->hColumns[column].len; i++) {
for (size_t i = 0; i < this->hColumns[column].len; i++) {
fprintf(fd, "%s%d", sep, this->hColumns[column].modes[i]);
sep = " ";
}
@ -355,9 +562,9 @@ int Settings_write(const Settings* this, bool onCrash) {
}
#define printSettingInteger(setting_, value_) \
fprintf(fd, setting_ "=%d%c", (int) value_, separator);
fprintf(fd, setting_ "=%d%c", (int) (value_), separator)
#define printSettingString(setting_, value_) \
fprintf(fd, setting_ "=%s%c", value_, separator);
fprintf(fd, setting_ "=%s%c", value_, separator)
if (!onCrash) {
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
@ -365,12 +572,7 @@ int Settings_write(const Settings* this, bool onCrash) {
}
printSettingString("htop_version", VERSION);
printSettingInteger("config_reader_min_version", CONFIG_READER_MIN_VERSION);
writeFields(fd, this->fields, this->dynamicColumns, "fields", separator);
// This "-1" is for compatibility with the older enum format.
printSettingInteger("sort_key", this->sortKey - 1);
printSettingInteger("sort_direction", this->direction);
printSettingInteger("tree_sort_key", this->treeSortKey - 1);
printSettingInteger("tree_sort_direction", this->treeDirection);
fprintf(fd, "fields="); writeFields(fd, this->screens[0]->fields, this->dynamicColumns, false, separator);
printSettingInteger("hide_kernel_threads", this->hideKernelThreads);
printSettingInteger("hide_userland_threads", this->hideUserlandThreads);
printSettingInteger("shadow_other_users", this->shadowOtherUsers);
@ -385,10 +587,8 @@ int Settings_write(const Settings* this, bool onCrash) {
printSettingInteger("find_comm_in_cmdline", this->findCommInCmdline);
printSettingInteger("strip_exe_from_cmdline", this->stripExeFromCmdline);
printSettingInteger("show_merged_command", this->showMergedCommand);
printSettingInteger("tree_view", this->treeView);
printSettingInteger("tree_view_always_by_pid", this->treeViewAlwaysByPID);
printSettingInteger("all_branches_collapsed", this->allBranchesCollapsed);
printSettingInteger("header_margin", this->headerMargin);
printSettingInteger("screen_tabs", this->screenTabs);
printSettingInteger("detailed_cpu_time", this->detailedCPUTime);
printSettingInteger("cpu_count_from_one", this->countCPUsFromOne);
printSettingInteger("show_cpu_usage", this->showCPUUsage);
@ -417,6 +617,29 @@ int Settings_write(const Settings* this, bool onCrash) {
writeMeterModes(this, fd, separator, i);
}
// Legacy compatibility with older versions of htop
printSettingInteger("tree_view", this->screens[0]->treeView);
// This "-1" is for compatibility with the older enum format.
printSettingInteger("sort_key", this->screens[0]->sortKey - 1);
printSettingInteger("tree_sort_key", this->screens[0]->treeSortKey - 1);
printSettingInteger("sort_direction", this->screens[0]->direction);
printSettingInteger("tree_sort_direction", this->screens[0]->treeDirection);
printSettingInteger("tree_view_always_by_pid", this->screens[0]->treeViewAlwaysByPID);
printSettingInteger("all_branches_collapsed", this->screens[0]->allBranchesCollapsed);
for (unsigned int i = 0; i < this->nScreens; i++) {
ScreenSettings* ss = this->screens[i];
fprintf(fd, "screen:%s=", ss->name);
writeFields(fd, ss->fields, this->dynamicColumns, true, separator);
printSettingString(".sort_key", toFieldName(this->dynamicColumns, ss->sortKey));
printSettingString(".tree_sort_key", toFieldName(this->dynamicColumns, ss->treeSortKey));
printSettingInteger(".tree_view", ss->treeView);
printSettingInteger(".tree_view_always_by_pid", ss->treeViewAlwaysByPID);
printSettingInteger(".sort_direction", ss->direction);
printSettingInteger(".tree_sort_direction", ss->treeDirection);
printSettingInteger(".all_branches_collapsed", ss->allBranchesCollapsed);
}
#undef printSettingString
#undef printSettingInteger
@ -440,16 +663,11 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
this->dynamicColumns = dynamicColumns;
this->hLayout = HF_TWO_50_50;
this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting));
this->sortKey = PERCENT_CPU;
this->treeSortKey = PID;
this->direction = -1;
this->treeDirection = 1;
this->shadowOtherUsers = false;
this->showThreadNames = false;
this->hideKernelThreads = true;
this->hideUserlandThreads = false;
this->treeView = false;
this->allBranchesCollapsed = false;
this->highlightBaseName = false;
this->highlightDeletedExe = true;
this->highlightMegabytes = true;
@ -474,15 +692,9 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
#ifdef HAVE_LIBHWLOC
this->topologyAffinity = false;
#endif
this->fields = xCalloc(LAST_PROCESSFIELD + 1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
this->flags = 0;
const ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
}
this->screens = xCalloc(Platform_numberOfDefaultScreens * sizeof(ScreenSettings*), 1);
this->nScreens = 0;
char* legacyDotfile = NULL;
const char* rcfile = getenv("HTOPRC");
@ -538,18 +750,29 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
ok = Settings_read(this, this->filename, initialCpuCount);
}
if (!ok) {
this->screenTabs = true;
this->changed = true;
Settings_read(this, SYSCONFDIR "/htoprc", initialCpuCount);
ok = Settings_read(this, SYSCONFDIR "/htoprc", initialCpuCount);
}
if (!ok) {
Settings_defaultMeters(this, initialCpuCount);
Settings_defaultScreens(this);
}
this->ssIndex = 0;
this->ss = this->screens[this->ssIndex];
this->lastUpdate = 1;
return this;
}
void Settings_invertSortOrder(Settings* this) {
void ScreenSettings_invertSortOrder(ScreenSettings* this) {
int* attr = (this->treeView) ? &(this->treeDirection) : &(this->direction);
*attr = (*attr == 1) ? -1 : 1;
}
void Settings_setSortKey(Settings* this, ProcessField sortKey) {
void ScreenSettings_setSortKey(ScreenSettings* this, ProcessField sortKey) {
if (this->treeViewAlwaysByPID || !this->treeView) {
this->sortKey = sortKey;
this->direction = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1;
@ -580,7 +803,7 @@ void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout) {
} else if (newColumns < oldColumns) {
for (unsigned int i = newColumns; i < oldColumns; i++) {
if (this->hColumns[i].names) {
for (uint8_t j = 0; j < this->hColumns[i].len; j++)
for (size_t j = 0; j < this->hColumns[i].len; j++)
free(this->hColumns[i].names[j]);
free(this->hColumns[i].names);
}

View File

@ -3,7 +3,7 @@
/*
htop - Settings.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@ -19,14 +19,33 @@ in the source distribution for its full text.
#define DEFAULT_DELAY 15
#define CONFIG_READER_MIN_VERSION 2
#define CONFIG_READER_MIN_VERSION 3
typedef struct {
uint8_t len;
const char* name;
const char* columns;
const char* sortKey;
} ScreenDefaults;
typedef struct {
size_t len;
char** names;
int* modes;
} MeterColumnSetting;
typedef struct {
char* name;
ProcessField* fields;
uint32_t flags;
int direction;
int treeDirection;
ProcessField sortKey;
ProcessField treeSortKey;
bool treeView;
bool treeViewAlwaysByPID;
bool allBranchesCollapsed;
} ScreenSettings;
typedef struct Settings_ {
char* filename;
int config_version;
@ -34,16 +53,14 @@ typedef struct Settings_ {
MeterColumnSetting* hColumns;
Hashtable* dynamicColumns;
ProcessField* fields;
uint32_t flags;
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
int colorScheme;
int delay;
int direction;
int treeDirection;
ProcessField sortKey;
ProcessField treeSortKey;
bool countCPUsFromOne;
bool detailedCPUTime;
bool showCPUUsage;
@ -52,9 +69,6 @@ typedef struct Settings_ {
bool showCPUTemperature;
bool degreeFahrenheit;
#endif
bool treeView;
bool treeViewAlwaysByPID;
bool allBranchesCollapsed;
bool showProgramPath;
bool shadowOtherUsers;
bool showThreadNames;
@ -72,6 +86,7 @@ typedef struct Settings_ {
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
bool screenTabs;
#ifdef HAVE_GETMOUSE
bool enableMouse;
#endif
@ -81,17 +96,18 @@ typedef struct Settings_ {
#endif
bool changed;
uint64_t lastUpdate;
} Settings;
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))
static inline ProcessField Settings_getActiveSortKey(const Settings* this) {
static inline ProcessField ScreenSettings_getActiveSortKey(const ScreenSettings* this) {
return (this->treeView)
? (this->treeViewAlwaysByPID ? PID : this->treeSortKey)
: this->sortKey;
}
static inline int Settings_getActiveDirection(const Settings* this) {
static inline int ScreenSettings_getActiveDirection(const ScreenSettings* this) {
return this->treeView ? this->treeDirection : this->direction;
}
@ -101,9 +117,13 @@ int Settings_write(const Settings* this, bool onCrash);
Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns);
void Settings_invertSortOrder(Settings* this);
ScreenSettings* Settings_newScreen(Settings* this, const ScreenDefaults* defaults);
void Settings_setSortKey(Settings* this, ProcessField sortKey);
void ScreenSettings_delete(ScreenSettings* this);
void ScreenSettings_invertSortOrder(ScreenSettings* this);
void ScreenSettings_setSortKey(ScreenSettings* this, ProcessField sortKey);
void Settings_enableReadonly(void);

View File

@ -1,13 +1,13 @@
/*
htop - SignalsPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
#include "SignalsPanel.h"
// the above contains #include <signal.h> so do not add that here again (breaks Solaris build)
#include <signal.h>
#include <stdbool.h>
#include "FunctionBar.h"
@ -18,15 +18,14 @@ in the source distribution for its full text.
#include "XUtils.h"
Panel* SignalsPanel_new() {
Panel* SignalsPanel_new(int preSelectedSignal) {
Panel* this = Panel_new(1, 1, 1, 1, Class(ListItem), true, FunctionBar_newEnterEsc("Send ", "Cancel "));
const int defaultSignal = SIGTERM;
int defaultPosition = 15;
unsigned int i;
for (i = 0; i < Platform_numberOfSignals; i++) {
Panel_set(this, i, (Object*) ListItem_new(Platform_signals[i].name, Platform_signals[i].number));
// signal 15 is not always the 15th signal in the table
if (Platform_signals[i].number == defaultSignal) {
if (Platform_signals[i].number == preSelectedSignal) {
defaultPosition = i;
}
}

View File

@ -3,10 +3,16 @@
/*
htop - SignalsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#ifndef HTOP_SOLARIS
#include <signal.h>
#endif
#include "Panel.h"
@ -15,6 +21,8 @@ typedef struct SignalItem_ {
int number;
} SignalItem;
Panel* SignalsPanel_new(void);
#define SIGNALSPANEL_INITSELECTEDSIGNAL SIGTERM
Panel* SignalsPanel_new(int preSelectedSignal);
#endif

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