222 Commits

Author SHA1 Message Date
4a8ae4b5d4 Merge branch 'bertwesarg-affinity-fix-panel-width' 2020-08-27 09:36:56 +10:00
94b8c2e714 fix width of AffinitPanel
The panel size of 15 includes the gap to the next panel, thus use 14 as
the minimum size and let the caller of `AffinityPanel_new` handle the
gap.
2020-08-26 22:03:11 +02:00
df7e4fcdc0 Update changelog with Berts latest addition 2020-08-26 10:44:22 +10:00
728b04bbb5 Merge branch 'ci-hwloc-job' of https://github.com/bertwesarg/htop into bertwesarg-ci-hwloc-job 2020-08-26 10:39:43 +10:00
d0f31ede56 Merge branch 'ci-hwloc-job' of https://github.com/bertwesarg/htop into bertwesarg-ci-hwloc-job 2020-08-26 10:15:00 +10:00
ba94e0dfda Merge branch 'ci2' of https://github.com/cgzones/htop into cgzones-ci2 2020-08-26 10:08:50 +10:00
fc4f74aa47 ci: add clang build 2020-08-25 12:01:56 +02:00
4e2b9f0965 Avoid shadowing warnings 2020-08-25 12:01:56 +02:00
b4ceb83d76 MakeHeader.py.in: remove unused import 2020-08-25 12:00:08 +02:00
1130ad8b73 MakeHeader.py.in: remove executable bit 2020-08-25 12:00:08 +02:00
11f558f934 Avoid discarding const qualifiers 2020-08-25 12:00:03 +02:00
7457bfe9f3 Avoid string overflow warning
Use xStrdup instead of xMallow and strncpy

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-05-22 23:21:42 -03:00
08a9b051c5 fix option string
This broke with commit db05ba6106.
2018-05-22 23:19:19 -03:00
e1e3791363 Fix #define to match header when regenerating with MakeHeader.py (#789)
from Debian https://sources.debian.org/src/htop/2.2.0-1/debian/patches/fix-linux-process.patch/
2018-05-22 23:09:20 -03:00
04cc193e3c Bump version to 2.2.0 2018-04-10 10:42:59 -03:00
5b38466eab Update ChangeLog 2018-04-10 10:42:46 -03:00
34dff098c5 Add tree view flag to man page (#777) 2018-04-10 10:36:34 -03:00
724aae60d1 Add tree view flag to man page 2018-04-06 21:52:50 -05:00
e85b072ad0 Require pkg-config as an optional build dependency on Linux (#775)
With this commit:

* if pkgconfig is installed, it will expand the code inside the shell if, adding the pkgconfig-based tests for the dependencies of Linux delay accounting.
* if pkgconfig is not installed, it will add an error message inside the test of Linux delay accounting, telling the user to install pkgconfig and rebuild the configure script if they want to use Linux delay accounting.

The end result is:

* people running Linux
  * will not need pkgconfig when not using delay accounting
  * will need pkgconfig when using delay accounting
    * if they don't have it
      * they are told by configure they need to install it and re-run autogen.sh when running from Git
      * they are told by configure they need to install it and re-run configure when running from the tarball
* people not running Linux
  * will never need pkgconfig

...and in none of the above scenarios the generated configure script produces unexpanded macros for users checking out the sources from Git.
2018-04-06 11:03:00 -03:00
7cfaa9dede MakeHeader.py: Fix for non-utf8 environments (#770)
Header creation fails with non-utf8 locale and python3.
Simply set LC_ALL="C" and use python3 to reproduce the issue.

env LC_ALL="C" ./scripts/MakeHeader.py MetersPanel.c
Traceback (most recent call last):
  File "./scripts/MakeHeader.py", line 32, in <module>
    for line in file.readlines():
  File "/usr/lib64/python3.5/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 956: ordinal not in range(128)

This changes is python2 and python3 compatible

cStringIO.StringIO module is removed because it is not able to accept unicode strings
https://docs.python.org/2/library/stringio.html#cStringIO.StringIO
2018-04-05 19:55:51 -03:00
db05ba6106 Add -t command-line flag for tree view 2018-04-05 19:52:19 -03:00
0505a7cfe1 macOS: fix the switched version test (#772) 2018-04-05 19:41:22 -03:00
cf04300d49 Solaris: update proc state letters to reflect Solaris usage 2018-04-05 19:38:13 -03:00
c53e384213 Solaris: bump copyright in Platform.{c,h} 2018-04-05 19:38:13 -03:00
da4877f48c Solaris: fix a memory leak caused by calling ProcessList_getProcess twice for each LWP 2018-04-05 19:38:13 -03:00
0969f83b21 Solaris: Implement process environment listing 2018-04-05 19:38:13 -03:00
155d7cbeee Solaris: add placeholder message about environment listing 2018-04-05 19:38:13 -03:00
1ae7625c42 Solaris: showing a dash for the top-level process is no longer necessary 2018-04-05 19:38:13 -03:00
3c96467f7b Solaris: add warning about proc_walk_f callback function 2018-04-05 19:38:13 -03:00
7f40a3a5af Solaris: condense separate process vs lwp handling down to a single workflow 2018-04-05 19:38:13 -03:00
45fad05b4a Solaris: get completely out of the file handling business using libproc 2018-04-05 19:38:13 -03:00
4ba06c51e5 Solaris: Condense conditional blocks for new vs old LWPs and procs 2018-04-05 19:38:13 -03:00
fa18ac964c Solaris: remove unneeded accumulators for process and thread counting 2018-04-05 19:38:13 -03:00
192e43c1ed Solaris: Assorted post-LWP code cleanup 2018-04-05 19:38:13 -03:00
75598c1389 Solaris: Implement kernel thread counting 2018-04-05 19:38:13 -03:00
c235b45cd6 Solaris: If a process has a running LWP, then the process is by definition running 2018-04-05 19:38:13 -03:00
0dbedf95a8 Collapse current subtree pressing Backspace 2018-04-05 19:38:13 -03:00
42c3a1fcb3 Solaris: Implement LWP enumeration (#768)
Squashed the following commits:

* Solaris: Get LWP enumeration working
* Solaris: Make showing and hiding of kernel threads behave
* Solaris: remove usage of lwpstatus that is no longer needed
* Solaris: no discrete access to parent proc structure needed
* Solaris: Restore runtime MaxPid detection after LWP changes
* Solaris: Workaround virtual PID signal issue by shadowing kill() with a macro
* Solaris: Fix unintention double-shifting of virtual PID for LWP enumeration
* Solaris: Add LWPID to default display since LWP enumeration is also default
* Solaris: use PAGE_SIZE_KB from Process.h instead of custom definition
* Solaris: stop LWP enumeration at 1023 LWPs per proc since that is all we can handle in the virtual PID
2018-03-27 13:27:12 -03:00
1cf8f429a5 OpenBSD: read Battery data
Signed-off-by: Hisham Muhammad <hisham@gobolinux.org>
2018-03-26 15:14:12 -03:00
23f96048c7 macOS: keep scanning thread for versions before High Sierra (#728)
Keep scanning threads for versions before High Sierra 13.0.0 and after 13.3.0.
2018-03-26 15:14:12 -03:00
70bc51a387 Solaris: Fix virt and resident memory sizes. Was using KiB, needed pages. 2018-03-26 15:14:12 -03:00
442b0d2576 New makefile targets to rebuild and clean htop headers.
`make htop-headers` will regenerate all '.h' headers in htop source for
all platforms.
`make clean-htop-headers` will delete all generated htop headers.

Because of the introduction of these two targets, I slightly changed
the style of platform-specific portions of makefile rules.
Please comment if you accept such a style, or need me to revert to old
style.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-03-26 15:14:12 -03:00
47cf1532b0 Linux: change how kernel threads are detected
Use the same method that ps and top use to determine if a
process is a kernel thread on Linux: check if cmdline is empty.

Thanks to @wangqr's investigation reported here:
https://github.com/hishamhm/htop/issues/761#issuecomment-375306069

Fixes #761.
2018-03-25 15:26:05 -03:00
dc050e8088 Fix overflow for signals >= 100.
Thanks to @gzip4 for tracking this down.

Closes #764.
2018-03-25 15:15:37 -03:00
aa3dc634dc strace: increase string length 2018-03-25 15:14:04 -03:00
5a75797f4d Solaris: code indentation fix 2018-03-16 11:43:48 -03:00
cda84fcddc Solaris: enough changes made to justify a copyright bump to 2018 2018-03-16 11:43:48 -03:00
69355234d0 Solaris: fix memory allocation for usernames (some empty usernames in 32-bit builds) 2018-03-16 11:43:48 -03:00
cefbe499db Solaris: fix malloc() / free() issues with zone name handling 2018-03-16 11:43:48 -03:00
76ef3682db Solaris: Link against libmalloc to fix various crashes 2018-03-16 11:43:48 -03:00
4bd68809e4 Solaris: Import backtrace-on-abort from Linux, with minor modification for Solaris 2018-03-16 11:43:48 -03:00
a042cfece2 Use fork/exec instead of popen to run lsof (#757)
Fixes #675
2018-03-16 11:39:03 -03:00
2e3c35d66d Use AM_CFLAGS and AM_LDFLAGS in Makefile.am (#760)
This reduces generated Makefile.in size by 74%.
(217319 bytes -> 56326 bytes)

Automake considers that <prog>_CFLAGS and <prog>_LDFLAGS are
program-specific build rules, and when such are specified, Automake
will generate additional code just to avoid the "generic" and
package-wide AM_CFLAGS or AM_LDFLAGS. (Especially for <prog>_CFLAGS,
Automake will rename generated object files to become "prog-foo.o" and
such, and it's _a lot_ of code to achieve this in Makefile.)

There's no reason for htop to rename intermediate object files. It's
better to make things simpler.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-03-16 11:31:48 -03:00
697f5bb9c1 Import Solaris support (#741)
This commit adds support for Solaris, squashed from PR #741:

Summary of additions:

* Initial setup of Solaris platform directory
* Add Solaris platform into autoconf template
* Uptime and load averages
* Add dependency on libkstat
* Basic process listing
* Zone name display
* CPU detection
* Per-process memory and CPU usage parsed correctly
* Uses sysconf to discover number of CPUs, instead of more complex libkstat code
* Simple memory display working
* Reduce repetitive calls to the PAGE_SIZE macro when reading memory info
* Add Project, Contract, Task, and Pool into process properties
* Use system major()/minor() implementations and remove extraneous definition of mkdev()
* Get the STARTTIME column working properly, using the Linux implementation as a guide
2018-03-02 18:20:46 -03:00
155 changed files with 3581 additions and 3095 deletions

View File

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

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

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

2
.gitignore vendored
View File

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

View File

@ -62,7 +62,7 @@ typedef struct State_ {
}*/ }*/
Object* Action_pickFromVector(State* st, Panel* list, int x) { Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) {
Panel* panel = st->panel; Panel* panel = st->panel;
Header* header = st->header; Header* header = st->header;
Settings* settings = st->settings; Settings* settings = st->settings;
@ -75,8 +75,8 @@ Object* Action_pickFromVector(State* st, Panel* list, int x) {
Panel* panelFocus; Panel* panelFocus;
int ch; int ch;
bool unfollow = false; bool unfollow = false;
int pid = MainPanel_selectedPid((MainPanel*)panel); int pid = followProcess ? MainPanel_selectedPid((MainPanel*)panel) : -1;
if (header->pl->following == -1) { if (followProcess && header->pl->following == -1) {
header->pl->following = pid; header->pl->following = pid;
unfollow = true; unfollow = true;
} }
@ -88,11 +88,16 @@ Object* Action_pickFromVector(State* st, Panel* list, int x) {
Panel_move(panel, 0, y); Panel_move(panel, 0, y);
Panel_resize(panel, COLS, LINES-y-1); Panel_resize(panel, COLS, LINES-y-1);
if (panelFocus == list && ch == 13) { if (panelFocus == list && ch == 13) {
Process* selected = (Process*)Panel_getSelected(panel); if (followProcess) {
if (selected && selected->pid == pid) Process* selected = (Process*)Panel_getSelected(panel);
if (selected && selected->pid == pid)
return Panel_getSelected(list);
else
beep();
} else {
return Panel_getSelected(list); return Panel_getSelected(list);
else }
beep();
} }
return NULL; return NULL;
} }
@ -171,10 +176,9 @@ static bool collapseIntoParent(Panel* panel) {
} }
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) { Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
ScreenSettings* ss = settings->ss; settings->sortKey = sortKey;
ss->sortKey = sortKey; settings->direction = 1;
ss->direction = 1; settings->treeView = false;
ss->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
} }
@ -182,16 +186,15 @@ static Htop_Reaction sortBy(State* st) {
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel ")); Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by"); Panel_setHeader(sortPanel, "Sort by");
ScreenSettings* ss = st->settings->ss; ProcessField* fields = st->settings->fields;
ProcessField* fields = ss->fields;
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
char* name = String_trim(Process_fields[fields[i]].name); char* name = String_trim(Process_fields[fields[i]].name);
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == ss->sortKey) if (fields[i] == st->settings->sortKey)
Panel_setSelected(sortPanel, i); Panel_setSelected(sortPanel, i);
free(name); free(name);
} }
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15); ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false);
if (field) { if (field) {
reaction |= Action_setSortKey(st->settings, field->key); reaction |= Action_setSortKey(st->settings, field->key);
} }
@ -236,9 +239,8 @@ static Htop_Reaction actionToggleProgramPath(State* st) {
} }
static Htop_Reaction actionToggleTreeView(State* st) { static Htop_Reaction actionToggleTreeView(State* st) {
ScreenSettings* ss = st->settings->ss; st->settings->treeView = !st->settings->treeView;
ss->treeView = !ss->treeView; if (st->settings->treeView) st->settings->direction = 1;
if (ss->treeView) ss->direction = 1;
ProcessList_expandTree(st->pl); ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
} }
@ -251,10 +253,21 @@ static Htop_Reaction actionIncFilter(State* st) {
} }
static Htop_Reaction actionIncSearch(State* st) { static Htop_Reaction actionIncSearch(State* st) {
IncSet_reset(((MainPanel*)st->panel)->inc, INC_SEARCH);
IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel); IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
} }
static Htop_Reaction actionIncNext(State* st) {
IncSet_next(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionIncPrev(State* st) {
IncSet_prev(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionHigherPriority(State* st) { static Htop_Reaction actionHigherPriority(State* st) {
bool changed = changePriority((MainPanel*)st->panel, -1); bool changed = changePriority((MainPanel*)st->panel, -1);
return changed ? HTOP_REFRESH : HTOP_OK; return changed ? HTOP_REFRESH : HTOP_OK;
@ -266,7 +279,7 @@ static Htop_Reaction actionLowerPriority(State* st) {
} }
static Htop_Reaction actionInvertSortOrder(State* st) { static Htop_Reaction actionInvertSortOrder(State* st) {
ScreenSettings_invertSortOrder(st->settings->ss); Settings_invertSortOrder(st->settings);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS; return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
} }
@ -280,7 +293,7 @@ static Htop_Reaction actionExpandOrCollapse(State* st) {
} }
static Htop_Reaction actionCollapseIntoParent(State* st) { static Htop_Reaction actionCollapseIntoParent(State* st) {
if (!st->settings->ss->treeView) { if (!st->settings->treeView) {
return HTOP_OK; return HTOP_OK;
} }
bool changed = collapseIntoParent(st->panel); bool changed = collapseIntoParent(st->panel);
@ -288,23 +301,13 @@ static Htop_Reaction actionCollapseIntoParent(State* st) {
} }
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) { static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st); return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
} }
static Htop_Reaction actionQuit() { static Htop_Reaction actionQuit() {
return HTOP_QUIT; return HTOP_QUIT;
} }
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 actionSetAffinity(State* st) { static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1) if (st->pl->cpuCount == 1)
return HTOP_OK; return HTOP_OK;
@ -313,17 +316,19 @@ static Htop_Reaction actionSetAffinity(State* st) {
Process* p = (Process*) Panel_getSelected(panel); Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK; if (!p) return HTOP_OK;
Affinity* affinity = Affinity_get(p, st->pl); Affinity* affinity1 = Affinity_get(p, st->pl);
if (!affinity) return HTOP_OK; if (!affinity1) return HTOP_OK;
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity); int width;
Affinity_delete(affinity); Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width);
width += 1; /* we add a gap between the panels */
Affinity_delete(affinity1);
void* set = Action_pickFromVector(st, affinityPanel, 15); void* set = Action_pickFromVector(st, affinityPanel, width, true);
if (set) { if (set) {
Affinity* affinity = AffinityPanel_getAffinity(affinityPanel, st->pl); Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity }, NULL); bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity2 }, NULL);
if (!ok) beep(); if (!ok) beep();
Affinity_delete(affinity); Affinity_delete(affinity2);
} }
Panel_delete((Object*)affinityPanel); Panel_delete((Object*)affinityPanel);
#endif #endif
@ -332,7 +337,7 @@ static Htop_Reaction actionSetAffinity(State* st) {
static Htop_Reaction actionKill(State* st) { static Htop_Reaction actionKill(State* st) {
Panel* signalsPanel = (Panel*) SignalsPanel_new(); Panel* signalsPanel = (Panel*) SignalsPanel_new();
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15); ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true);
if (sgn) { if (sgn) {
if (sgn->key != 0) { if (sgn->key != 0) {
Panel_setHeader(st->panel, "Sending..."); Panel_setHeader(st->panel, "Sending...");
@ -353,7 +358,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
Vector_insertionSort(usersPanel->items); Vector_insertionSort(usersPanel->items);
ListItem* allUsers = ListItem_new("All users", -1); ListItem* allUsers = ListItem_new("All users", -1);
Panel_insert(usersPanel, 0, (Object*) allUsers); Panel_insert(usersPanel, 0, (Object*) allUsers);
ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20); ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false);
if (picked) { if (picked) {
if (picked == allUsers) { if (picked == allUsers) {
st->pl->userId = -1; st->pl->userId = -1;
@ -476,7 +481,7 @@ static Htop_Reaction actionHelp(State* st) {
if (settings->detailedCPUTime) { if (settings->detailedCPUTime) {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/"); addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/"); addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/"); addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/");
addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/"); addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/"); addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
@ -486,8 +491,8 @@ static Htop_Reaction actionHelp(State* st) {
} else { } else {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/"); addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/"); addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "virtualiz"); addattrstr(CRT_colors[CPU_GUEST], "virtualiz");
addattrstr(CRT_colors[BAR_SHADOW], " used%"); addattrstr(CRT_colors[BAR_SHADOW], " used%");
} }
addattrstr(CRT_colors[BAR_BORDER], "]"); addattrstr(CRT_colors[BAR_BORDER], "]");
@ -572,6 +577,8 @@ void Action_setBindings(Htop_Action* keys) {
keys['\\'] = actionIncFilter; keys['\\'] = actionIncFilter;
keys[KEY_F(3)] = actionIncSearch; keys[KEY_F(3)] = actionIncSearch;
keys['/'] = actionIncSearch; keys['/'] = actionIncSearch;
keys['n'] = actionIncNext;
keys['N'] = actionIncPrev;
keys[']'] = actionHigherPriority; keys[']'] = actionHigherPriority;
keys[KEY_F(7)] = actionHigherPriority; keys[KEY_F(7)] = actionHigherPriority;
@ -609,6 +616,4 @@ void Action_setBindings(Htop_Action* keys) {
keys['U'] = actionUntagAll; keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren; keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen; keys['e'] = actionShowEnvScreen;
keys['\t'] = actionNextScreen;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -127,7 +127,7 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
Panel_add(super, (Object*) ListItem_new("CPU average", 0)); Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (int i = 1; i <= cpus; i++) { for (int i = 1; i <= cpus; i++) {
char buffer[50]; char buffer[50];
xSnprintf(buffer, 50, "%s %d", type->uiName, i); xSnprintf(buffer, 50, "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1));
Panel_add(super, (Object*) ListItem_new(buffer, i)); Panel_add(super, (Object*) ListItem_new(buffer, i));
} }
} else { } else {

View File

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

View File

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

View File

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

View File

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

View File

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

171
CRT.c
View File

@ -59,6 +59,19 @@ typedef enum TreeStr_ {
TREE_STR_COUNT TREE_STR_COUNT
} TreeStr; } TreeStr;
typedef enum CheckStr_ {
CHECK_STR_NONE,
CHECK_STR_PARTIAL,
CHECK_STR_FULL,
CHECK_STR_COUNT
} CheckStr;
typedef enum CollapStr_ {
COLLAP_STR_OPEN,
COLLAP_STR_CLOSED,
COLLAP_STR_COUNT
} CollapStr;
typedef enum ColorSchemes_ { typedef enum ColorSchemes_ {
COLORSCHEME_DEFAULT = 0, COLORSCHEME_DEFAULT = 0,
COLORSCHEME_MONOCHROME = 1, COLORSCHEME_MONOCHROME = 1,
@ -113,7 +126,6 @@ typedef enum ColorElements_ {
LOAD_AVERAGE_FIFTEEN, LOAD_AVERAGE_FIFTEEN,
LOAD_AVERAGE_FIVE, LOAD_AVERAGE_FIVE,
LOAD_AVERAGE_ONE, LOAD_AVERAGE_ONE,
CHECK_BOX,
CHECK_MARK, CHECK_MARK,
CHECK_TEXT, CHECK_TEXT,
CLOCK, CLOCK,
@ -122,19 +134,28 @@ typedef enum ColorElements_ {
CPU_NICE, CPU_NICE,
CPU_NICE_TEXT, CPU_NICE_TEXT,
CPU_NORMAL, CPU_NORMAL,
CPU_KERNEL, CPU_SYSTEM,
CPU_IOWAIT, CPU_IOWAIT,
CPU_IRQ, CPU_IRQ,
CPU_SOFTIRQ, CPU_SOFTIRQ,
CPU_STEAL, CPU_STEAL,
CPU_GUEST, CPU_GUEST,
PANEL_EDIT, PRESSURE_STALL_TEN,
PRESSURE_STALL_SIXTY,
PRESSURE_STALL_THREEHUNDRED,
ZFS_MFU,
ZFS_MRU,
ZFS_ANON,
ZFS_HEADER,
ZFS_OTHER,
ZFS_COMPRESSED,
ZFS_RATIO,
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
void CRT_fatalError(const char* note) __attribute__ ((noreturn)); extern void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn); extern void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A')) #define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
@ -150,6 +171,17 @@ const char *CRT_treeStrAscii[TREE_STR_COUNT] = {
"-", // TREE_STR_SHUT "-", // TREE_STR_SHUT
}; };
const char *CRT_checkStrAscii[CHECK_STR_COUNT] = {
"[ ]", // CHECK_STR_NONE
"[o]", // CHECK_STR_PARTIAL
"[x]", // CHECK_STR_FULL
};
const char *CRT_collapStrAscii[COLLAP_STR_COUNT] = {
"[-]", // COLLAP_STR_OPEN
"[+]", // COLLAP_STR_CLOSED
};
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
const char *CRT_treeStrUtf8[TREE_STR_COUNT] = { const char *CRT_treeStrUtf8[TREE_STR_COUNT] = {
@ -162,12 +194,27 @@ const char *CRT_treeStrUtf8[TREE_STR_COUNT] = {
"\xe2\x94\x80", // TREE_STR_SHUT ─ "\xe2\x94\x80", // TREE_STR_SHUT ─
}; };
bool CRT_utf8 = false; const char *CRT_checkStrUtf8[CHECK_STR_COUNT] = {
"\xe2\x98\x90", // CHECK_STR_NONE ☐
"\xe2\x98\x92", // CHECK_STR_PARTIAL ☒
"\xe2\x98\x91", // CHECK_STR_FULL ☑
};
const char *CRT_collapStrUtf8[COLLAP_STR_COUNT] = {
"\xe2\x8a\x9f", // COLLAP_STR_OPEN ⊟
"\xe2\x8a\x9e", // COLLAP_STR_CLOSED ⊞
};
#endif #endif
bool CRT_utf8 = false;
const char **CRT_treeStr = CRT_treeStrAscii; const char **CRT_treeStr = CRT_treeStrAscii;
const char **CRT_checkStr = CRT_checkStrAscii;
const char **CRT_collapStr = CRT_collapStrAscii;
static bool CRT_hasColors; static bool CRT_hasColors;
int CRT_delay = 0; int CRT_delay = 0;
@ -220,20 +267,28 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = A_BOLD, [LOAD] = A_BOLD,
[HELP_BOLD] = A_BOLD | ColorPair(Cyan,Black), [HELP_BOLD] = A_BOLD | ColorPair(Cyan,Black),
[CLOCK] = A_BOLD, [CLOCK] = A_BOLD,
[CHECK_BOX] = ColorPair(Cyan,Black), [CHECK_MARK] = A_BOLD | ColorPair(Cyan,Black),
[CHECK_MARK] = A_BOLD,
[CHECK_TEXT] = A_NORMAL, [CHECK_TEXT] = A_NORMAL,
[HOSTNAME] = A_BOLD, [HOSTNAME] = A_BOLD,
[CPU_NICE] = ColorPair(Blue,Black), [CPU_NICE] = ColorPair(Blue,Black),
[CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black),
[CPU_NORMAL] = ColorPair(Green,Black), [CPU_NORMAL] = ColorPair(Green,Black),
[CPU_KERNEL] = ColorPair(Red,Black), [CPU_SYSTEM] = ColorPair(Red,Black),
[CPU_IOWAIT] = A_BOLD | ColorPair(Black, Black), [CPU_IOWAIT] = A_BOLD | ColorPairGrayBlack,
[CPU_IRQ] = ColorPair(Yellow,Black), [CPU_IRQ] = ColorPair(Yellow,Black),
[CPU_SOFTIRQ] = ColorPair(Magenta,Black), [CPU_SOFTIRQ] = ColorPair(Magenta,Black),
[CPU_STEAL] = ColorPair(Cyan,Black), [CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black), [CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Blue), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Cyan,Black),
[PRESSURE_STALL_SIXTY] = A_BOLD | ColorPair(Cyan,Black),
[PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White,Black),
[ZFS_MFU] = ColorPair(Blue,Black),
[ZFS_MRU] = ColorPair(Yellow,Black),
[ZFS_ANON] = ColorPair(Magenta,Black),
[ZFS_HEADER] = ColorPair(Cyan,Black),
[ZFS_OTHER] = ColorPair(Magenta,Black),
[ZFS_COMPRESSED] = ColorPair(Blue,Black),
[ZFS_RATIO] = ColorPair(Magenta,Black),
}, },
[COLORSCHEME_MONOCHROME] = { [COLORSCHEME_MONOCHROME] = {
[RESET_COLOR] = A_NORMAL, [RESET_COLOR] = A_NORMAL,
@ -280,20 +335,28 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = A_BOLD, [LOAD] = A_BOLD,
[HELP_BOLD] = A_BOLD, [HELP_BOLD] = A_BOLD,
[CLOCK] = A_BOLD, [CLOCK] = A_BOLD,
[CHECK_BOX] = A_BOLD, [CHECK_MARK] = A_BOLD,
[CHECK_MARK] = A_NORMAL,
[CHECK_TEXT] = A_NORMAL, [CHECK_TEXT] = A_NORMAL,
[HOSTNAME] = A_BOLD, [HOSTNAME] = A_BOLD,
[CPU_NICE] = A_NORMAL, [CPU_NICE] = A_NORMAL,
[CPU_NICE_TEXT] = A_NORMAL, [CPU_NICE_TEXT] = A_NORMAL,
[CPU_NORMAL] = A_BOLD, [CPU_NORMAL] = A_BOLD,
[CPU_KERNEL] = A_BOLD, [CPU_SYSTEM] = A_BOLD,
[CPU_IOWAIT] = A_NORMAL, [CPU_IOWAIT] = A_NORMAL,
[CPU_IRQ] = A_BOLD, [CPU_IRQ] = A_BOLD,
[CPU_SOFTIRQ] = A_BOLD, [CPU_SOFTIRQ] = A_BOLD,
[CPU_STEAL] = A_REVERSE, [CPU_STEAL] = A_REVERSE,
[CPU_GUEST] = A_REVERSE, [CPU_GUEST] = A_REVERSE,
[PANEL_EDIT] = A_BOLD, [PRESSURE_STALL_THREEHUNDRED] = A_DIM,
[PRESSURE_STALL_SIXTY] = A_NORMAL,
[PRESSURE_STALL_TEN] = A_BOLD,
[ZFS_MFU] = A_NORMAL,
[ZFS_MRU] = A_NORMAL,
[ZFS_ANON] = A_DIM,
[ZFS_HEADER] = A_BOLD,
[ZFS_OTHER] = A_DIM,
[ZFS_COMPRESSED] = A_BOLD,
[ZFS_RATIO] = A_BOLD,
}, },
[COLORSCHEME_BLACKONWHITE] = { [COLORSCHEME_BLACKONWHITE] = {
[RESET_COLOR] = ColorPair(Black,White), [RESET_COLOR] = ColorPair(Black,White),
@ -340,20 +403,28 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = ColorPair(Black,White), [LOAD] = ColorPair(Black,White),
[HELP_BOLD] = ColorPair(Blue,White), [HELP_BOLD] = ColorPair(Blue,White),
[CLOCK] = ColorPair(Black,White), [CLOCK] = ColorPair(Black,White),
[CHECK_BOX] = ColorPair(Blue,White),
[CHECK_MARK] = ColorPair(Black,White), [CHECK_MARK] = ColorPair(Black,White),
[CHECK_TEXT] = ColorPair(Black,White), [CHECK_TEXT] = ColorPair(Black,White),
[HOSTNAME] = ColorPair(Black,White), [HOSTNAME] = ColorPair(Black,White),
[CPU_NICE] = ColorPair(Cyan,White), [CPU_NICE] = ColorPair(Cyan,White),
[CPU_NICE_TEXT] = ColorPair(Cyan,White), [CPU_NICE_TEXT] = ColorPair(Cyan,White),
[CPU_NORMAL] = ColorPair(Green,White), [CPU_NORMAL] = ColorPair(Green,White),
[CPU_KERNEL] = ColorPair(Red,White), [CPU_SYSTEM] = ColorPair(Red,White),
[CPU_IOWAIT] = A_BOLD | ColorPair(Black, White), [CPU_IOWAIT] = A_BOLD | ColorPair(Black,White),
[CPU_IRQ] = ColorPair(Blue,White), [CPU_IRQ] = ColorPair(Blue,White),
[CPU_SOFTIRQ] = ColorPair(Blue,White), [CPU_SOFTIRQ] = ColorPair(Blue,White),
[CPU_STEAL] = ColorPair(Cyan,White), [CPU_STEAL] = ColorPair(Cyan,White),
[CPU_GUEST] = ColorPair(Cyan,White), [CPU_GUEST] = ColorPair(Cyan,White),
[PANEL_EDIT] = ColorPair(White,Blue), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black,White),
[PRESSURE_STALL_SIXTY] = ColorPair(Black,White),
[PRESSURE_STALL_TEN] = ColorPair(Black,White),
[ZFS_MFU] = ColorPair(Cyan,White),
[ZFS_MRU] = ColorPair(Yellow,White),
[ZFS_ANON] = ColorPair(Magenta,White),
[ZFS_HEADER] = ColorPair(Yellow,White),
[ZFS_OTHER] = ColorPair(Magenta,White),
[ZFS_COMPRESSED] = ColorPair(Cyan,White),
[ZFS_RATIO] = ColorPair(Magenta,White),
}, },
[COLORSCHEME_LIGHTTERMINAL] = { [COLORSCHEME_LIGHTTERMINAL] = {
[RESET_COLOR] = ColorPair(Black,Black), [RESET_COLOR] = ColorPair(Black,Black),
@ -400,20 +471,28 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = ColorPair(White,Black), [LOAD] = ColorPair(White,Black),
[HELP_BOLD] = ColorPair(Blue,Black), [HELP_BOLD] = ColorPair(Blue,Black),
[CLOCK] = ColorPair(White,Black), [CLOCK] = ColorPair(White,Black),
[CHECK_BOX] = ColorPair(Blue,Black),
[CHECK_MARK] = ColorPair(Black,Black), [CHECK_MARK] = ColorPair(Black,Black),
[CHECK_TEXT] = ColorPair(Black,Black), [CHECK_TEXT] = ColorPair(Black,Black),
[HOSTNAME] = ColorPair(White,Black), [HOSTNAME] = ColorPair(White,Black),
[CPU_NICE] = ColorPair(Cyan,Black), [CPU_NICE] = ColorPair(Cyan,Black),
[CPU_NICE_TEXT] = ColorPair(Cyan,Black), [CPU_NICE_TEXT] = ColorPair(Cyan,Black),
[CPU_NORMAL] = ColorPair(Green,Black), [CPU_NORMAL] = ColorPair(Green,Black),
[CPU_KERNEL] = ColorPair(Red,Black), [CPU_SYSTEM] = ColorPair(Red,Black),
[CPU_IOWAIT] = A_BOLD | ColorPair(Black, Black), [CPU_IOWAIT] = A_BOLD | ColorPair(Black,Black),
[CPU_IRQ] = A_BOLD | ColorPair(Blue,Black), [CPU_IRQ] = A_BOLD | ColorPair(Blue,Black),
[CPU_SOFTIRQ] = ColorPair(Blue,Black), [CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Black,Black), [CPU_STEAL] = ColorPair(Black,Black),
[CPU_GUEST] = ColorPair(Black,Black), [CPU_GUEST] = ColorPair(Black,Black),
[PANEL_EDIT] = ColorPair(White,Blue), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black,Black),
[PRESSURE_STALL_SIXTY] = ColorPair(Black,Black),
[PRESSURE_STALL_TEN] = ColorPair(Black,Black),
[ZFS_MFU] = ColorPair(Cyan,Black),
[ZFS_MRU] = ColorPair(Yellow,Black),
[ZFS_ANON] = A_BOLD | ColorPair(Magenta,Black),
[ZFS_HEADER] = ColorPair(Black,Black),
[ZFS_OTHER] = A_BOLD | ColorPair(Magenta,Black),
[ZFS_COMPRESSED] = ColorPair(Cyan,Black),
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta,Black),
}, },
[COLORSCHEME_MIDNIGHT] = { [COLORSCHEME_MIDNIGHT] = {
[RESET_COLOR] = ColorPair(White,Blue), [RESET_COLOR] = ColorPair(White,Blue),
@ -460,20 +539,28 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = A_BOLD | ColorPair(White,Blue), [LOAD] = A_BOLD | ColorPair(White,Blue),
[HELP_BOLD] = A_BOLD | ColorPair(Cyan,Blue), [HELP_BOLD] = A_BOLD | ColorPair(Cyan,Blue),
[CLOCK] = ColorPair(White,Blue), [CLOCK] = ColorPair(White,Blue),
[CHECK_BOX] = ColorPair(Cyan,Blue), [CHECK_MARK] = A_BOLD | ColorPair(Cyan,Blue),
[CHECK_MARK] = A_BOLD | ColorPair(White,Blue),
[CHECK_TEXT] = A_NORMAL | ColorPair(White,Blue), [CHECK_TEXT] = A_NORMAL | ColorPair(White,Blue),
[HOSTNAME] = ColorPair(White,Blue), [HOSTNAME] = ColorPair(White,Blue),
[CPU_NICE] = A_BOLD | ColorPair(Cyan,Blue), [CPU_NICE] = A_BOLD | ColorPair(Cyan,Blue),
[CPU_NICE_TEXT] = A_BOLD | ColorPair(Cyan,Blue), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Cyan,Blue),
[CPU_NORMAL] = A_BOLD | ColorPair(Green,Blue), [CPU_NORMAL] = A_BOLD | ColorPair(Green,Blue),
[CPU_KERNEL] = A_BOLD | ColorPair(Red,Blue), [CPU_SYSTEM] = A_BOLD | ColorPair(Red,Blue),
[CPU_IOWAIT] = A_BOLD | ColorPair(Blue,Blue), [CPU_IOWAIT] = A_BOLD | ColorPair(Blue,Blue),
[CPU_IRQ] = A_BOLD | ColorPair(Black,Blue), [CPU_IRQ] = A_BOLD | ColorPair(Black,Blue),
[CPU_SOFTIRQ] = ColorPair(Black,Blue), [CPU_SOFTIRQ] = ColorPair(Black,Blue),
[CPU_STEAL] = ColorPair(White,Blue), [CPU_STEAL] = ColorPair(White,Blue),
[CPU_GUEST] = ColorPair(White,Blue), [CPU_GUEST] = ColorPair(White,Blue),
[PANEL_EDIT] = ColorPair(White,Blue), [PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black,Blue),
[PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White,Blue),
[PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White,Blue),
[ZFS_MFU] = A_BOLD | ColorPair(White,Blue),
[ZFS_MRU] = A_BOLD | ColorPair(Yellow,Blue),
[ZFS_ANON] = A_BOLD | ColorPair(Magenta,Blue),
[ZFS_HEADER] = A_BOLD | ColorPair(Yellow,Blue),
[ZFS_OTHER] = A_BOLD | ColorPair(Magenta,Blue),
[ZFS_COMPRESSED] = A_BOLD | ColorPair(White,Blue),
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta,Blue),
}, },
[COLORSCHEME_BLACKNIGHT] = { [COLORSCHEME_BLACKNIGHT] = {
[RESET_COLOR] = ColorPair(Cyan,Black), [RESET_COLOR] = ColorPair(Cyan,Black),
@ -520,24 +607,34 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = A_BOLD, [LOAD] = A_BOLD,
[HELP_BOLD] = A_BOLD | ColorPair(Cyan,Black), [HELP_BOLD] = A_BOLD | ColorPair(Cyan,Black),
[CLOCK] = ColorPair(Green,Black), [CLOCK] = ColorPair(Green,Black),
[CHECK_BOX] = ColorPair(Green,Black),
[CHECK_MARK] = A_BOLD | ColorPair(Green,Black), [CHECK_MARK] = A_BOLD | ColorPair(Green,Black),
[CHECK_TEXT] = ColorPair(Cyan,Black), [CHECK_TEXT] = ColorPair(Cyan,Black),
[HOSTNAME] = ColorPair(Green,Black), [HOSTNAME] = ColorPair(Green,Black),
[CPU_NICE] = ColorPair(Blue,Black), [CPU_NICE] = ColorPair(Blue,Black),
[CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black),
[CPU_NORMAL] = ColorPair(Green,Black), [CPU_NORMAL] = ColorPair(Green,Black),
[CPU_KERNEL] = ColorPair(Red,Black), [CPU_SYSTEM] = ColorPair(Red,Black),
[CPU_IOWAIT] = ColorPair(Yellow,Black), [CPU_IOWAIT] = ColorPair(Yellow,Black),
[CPU_IRQ] = A_BOLD | ColorPair(Blue,Black), [CPU_IRQ] = A_BOLD | ColorPair(Blue,Black),
[CPU_SOFTIRQ] = ColorPair(Blue,Black), [CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Cyan,Black), [CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black), [CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Cyan), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green,Black),
[PRESSURE_STALL_SIXTY] = ColorPair(Green,Black),
[PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green,Black),
[ZFS_MFU] = ColorPair(Blue,Black),
[ZFS_MRU] = ColorPair(Yellow,Black),
[ZFS_ANON] = ColorPair(Magenta,Black),
[ZFS_HEADER] = ColorPair(Yellow,Black),
[ZFS_OTHER] = ColorPair(Magenta,Black),
[ZFS_COMPRESSED] = ColorPair(Blue,Black),
[ZFS_RATIO] = ColorPair(Magenta,Black),
}, },
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated. [COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
}; };
int CRT_cursorX = 0;
int CRT_scrollHAmount = 5; int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10; int CRT_scrollWheelVAmount = 10;
@ -666,18 +763,14 @@ void CRT_init(int delay, int colorScheme) {
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
if(strcmp(nl_langinfo(CODESET), "UTF-8") == 0) if(strcmp(nl_langinfo(CODESET), "UTF-8") == 0) {
CRT_utf8 = true; CRT_utf8 = true;
else CRT_treeStr = CRT_treeStrUtf8;
CRT_utf8 = false; CRT_checkStr = CRT_checkStrUtf8;
CRT_collapStr = CRT_collapStrUtf8;
}
#endif #endif
CRT_treeStr =
#ifdef HAVE_LIBNCURSESW
CRT_utf8 ? CRT_treeStrUtf8 :
#endif
CRT_treeStrAscii;
#if NCURSES_MOUSE_VERSION > 1 #if NCURSES_MOUSE_VERSION > 1
mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL); mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
#else #else

71
CRT.h
View File

@ -47,6 +47,19 @@ typedef enum TreeStr_ {
TREE_STR_COUNT TREE_STR_COUNT
} TreeStr; } TreeStr;
typedef enum CheckStr_ {
CHECK_STR_NONE,
CHECK_STR_PARTIAL,
CHECK_STR_FULL,
CHECK_STR_COUNT
} CheckStr;
typedef enum CollapStr_ {
COLLAP_STR_OPEN,
COLLAP_STR_CLOSED,
COLLAP_STR_COUNT
} CollapStr;
typedef enum ColorSchemes_ { typedef enum ColorSchemes_ {
COLORSCHEME_DEFAULT = 0, COLORSCHEME_DEFAULT = 0,
COLORSCHEME_MONOCHROME = 1, COLORSCHEME_MONOCHROME = 1,
@ -101,7 +114,6 @@ typedef enum ColorElements_ {
LOAD_AVERAGE_FIFTEEN, LOAD_AVERAGE_FIFTEEN,
LOAD_AVERAGE_FIVE, LOAD_AVERAGE_FIVE,
LOAD_AVERAGE_ONE, LOAD_AVERAGE_ONE,
CHECK_BOX,
CHECK_MARK, CHECK_MARK,
CHECK_TEXT, CHECK_TEXT,
CLOCK, CLOCK,
@ -110,60 +122,83 @@ typedef enum ColorElements_ {
CPU_NICE, CPU_NICE,
CPU_NICE_TEXT, CPU_NICE_TEXT,
CPU_NORMAL, CPU_NORMAL,
CPU_KERNEL, CPU_SYSTEM,
CPU_IOWAIT, CPU_IOWAIT,
CPU_IRQ, CPU_IRQ,
CPU_SOFTIRQ, CPU_SOFTIRQ,
CPU_STEAL, CPU_STEAL,
CPU_GUEST, CPU_GUEST,
PANEL_EDIT, PRESSURE_STALL_TEN,
PRESSURE_STALL_SIXTY,
PRESSURE_STALL_THREEHUNDRED,
ZFS_MFU,
ZFS_MRU,
ZFS_ANON,
ZFS_HEADER,
ZFS_OTHER,
ZFS_COMPRESSED,
ZFS_RATIO,
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
void CRT_fatalError(const char* note) __attribute__ ((noreturn)); extern void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn); extern void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A')) #define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
extern const char *CRT_treeStrAscii[TREE_STR_COUNT]; extern const char *CRT_treeStrAscii[TREE_STR_COUNT];
extern const char *CRT_checkStrAscii[CHECK_STR_COUNT];
extern const char *CRT_collapStrAscii[COLLAP_STR_COUNT];
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
extern const char *CRT_treeStrUtf8[TREE_STR_COUNT]; extern const char *CRT_treeStrUtf8[TREE_STR_COUNT];
extern bool CRT_utf8; extern const char *CRT_checkStrUtf8[CHECK_STR_COUNT];
extern const char *CRT_collapStrUtf8[COLLAP_STR_COUNT];
#endif #endif
extern bool CRT_utf8;
extern const char **CRT_treeStr; extern const char **CRT_treeStr;
extern const char **CRT_checkStr;
extern const char **CRT_collapStr;
extern int CRT_delay; extern int CRT_delay;
int* CRT_colors; extern int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT]; extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX;
extern int CRT_scrollHAmount; extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount; extern int CRT_scrollWheelVAmount;
char* CRT_termType; extern char* CRT_termType;
// TODO move color scheme to Settings, perhaps? // TODO move color scheme to Settings, perhaps?
extern int CRT_colorScheme; extern int CRT_colorScheme;
void *backtraceArray[128]; extern void *backtraceArray[128];
#if HAVE_SETUID_ENABLED #if HAVE_SETUID_ENABLED
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0) #define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
void CRT_dropPrivileges(); extern void CRT_dropPrivileges();
void CRT_restorePrivileges(); extern void CRT_restorePrivileges();
#else #else
@ -178,18 +213,18 @@ void CRT_restorePrivileges();
// TODO: pass an instance of Settings instead. // TODO: pass an instance of Settings instead.
void CRT_init(int delay, int colorScheme); extern void CRT_init(int delay, int colorScheme);
void CRT_done(); extern void CRT_done();
void CRT_fatalError(const char* note); extern void CRT_fatalError(const char* note);
int CRT_readKey(); extern int CRT_readKey();
void CRT_disableDelay(); extern void CRT_disableDelay();
void CRT_enableDelay(); extern void CRT_enableDelay();
void CRT_setColors(int colorScheme); extern void CRT_setColors(int colorScheme);
#endif #endif

View File

@ -10,7 +10,7 @@ in the source distribution for its full text.
#include "AvailableMetersPanel.h" #include "AvailableMetersPanel.h"
#include "MetersPanel.h" #include "MetersPanel.h"
#include "DisplayOptionsPanel.h" #include "DisplayOptionsPanel.h"
#include "ScreensPanel.h" #include "ColumnsPanel.h"
#include "ColorsPanel.h" #include "ColorsPanel.h"
#include "AvailableColumnsPanel.h" #include "AvailableColumnsPanel.h"
@ -64,11 +64,9 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
ScreenManager_add(this->scr, colors, -1); ScreenManager_add(this->scr, colors, -1);
} }
static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) { static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
Panel* screens = (Panel*) ScreensPanel_new(this->settings); Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns;
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns); Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns);
ScreenManager_add(this->scr, screens, 20);
ScreenManager_add(this->scr, columns, 20); ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, -1); ScreenManager_add(this->scr, availableColumns, -1);
} }
@ -120,7 +118,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
CategoriesPanel_makeColorsPage(this); CategoriesPanel_makeColorsPage(this);
break; break;
case 3: case 3:
CategoriesPanel_makeScreensPage(this); CategoriesPanel_makeColumnsPage(this);
break; break;
} }
} }
@ -149,6 +147,6 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
Panel_add(super, (Object*) ListItem_new("Meters", 0)); Panel_add(super, (Object*) ListItem_new("Meters", 0));
Panel_add(super, (Object*) ListItem_new("Display options", 0)); Panel_add(super, (Object*) ListItem_new("Display options", 0));
Panel_add(super, (Object*) ListItem_new("Colors", 0)); Panel_add(super, (Object*) ListItem_new("Colors", 0));
Panel_add(super, (Object*) ListItem_new("Screens", 0)); Panel_add(super, (Object*) ListItem_new("Columns", 0));
return this; return this;
} }

View File

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

View File

@ -1,21 +1,101 @@
What's new in version 2.1.1 What's new in version 3.0.0
* New maintainers - after a prolonged period of inactivity
from Hisham, the creator and original maintainer, a team
of community maintainers have volunteered to take over a
fork at https://htop.dev and https://github.com/htop-dev
to keep the project going.
* Support ZFS ARC statistics
(thanks to Ross Williams)
* Support more than 2 smaller CPU meter columns
(thanks to Christoph Budziszewski)
* Support Linux proportional set size metrics
(thanks to @linvinus, @ntninja and @himikof)
* Support Linux pressure stall information metrics
(thanks to Ran Benita)
* New display option to show CPU frequency in CPU meters
(thanks to Arnav Singh)
* Update Linux sysfs battery discovery for recent kernels
(thanks to @smattie)
* Add hardware topology information in the affinity panel
(thanks to Bert Wesarg)
* Add timestamp reporting to the strace screen
(thanks to Mario Harjac)
* Add simple, optional vim key mapping mode
(thanks to Daniel Flanagan)
* Added an option to disable the mouse
(thanks to MartinJM)
* Add Solaris11 compatibility
(thanks to Jan Senolt)
* Without an argument -u uses $USER value automatically
(thanks to @solanav)
* Support less(1) search navigation shortcuts
(thanks to @syrrim)
* Update the FreeBSD maximum PID to match FreeBSD change
(thanks to @multiplexd)
* Report values larger than 100 terabytes
(thanks to @adrien1018)
* Widen ST_UID (UID) column to allow for UIDs > 9999
(thanks to DLange)
* BUGFIX: fix makefiles for building with clang
(thanks to Jorge Pereira)
* BUGFIX: fix <sys/sysmacros.h> major() usage
(thanks to @wataash and Kang-Che Sung)
* BUGFIX: fix the STARTTIME column on FreeBSD
(thanks to Rob Crowston)
* BUGFIX: truncate overwide jail names on FreeBSD
(thanks to Rob Crowston)
* BUGFIX: fix reported memory values on FreeBSD
(thanks to Tobias Kortkamp)
* BUGFIX: fix reported CPU meter values on OpenBSD
(thanks to @motet-a)
* BUGFIX: correctly identify other types of zombie process
(thanks to @joder)
* BUGFIX: improve follow-process handling in some situations
(thanks to @wangqr)
* BUGFIX: fix custom meters reverting to unexpected setting
(thanks to @wangqr)
* BUGFIX: close pipe after running lsof(1)
(thanks to Jesin)
* BUGFIX: meters honour setting of counting CPUs from 0/1
(thanks to @rnsanchez)
What's new in version 2.2.0
* Solaris/Illumos/OpenIndiana support
(thanks to Guy M. Broome)
* -t/--tree flag for starting in tree-view mode
(thanks to Daniel Flanagan)
* macOS: detects High Sierra version to avoid OS bug
(thanks to Pierre Malhaire)
* OpenBSD: read battery data
(thanks to @nerd972)
* Various automake and build improvements
(thanks to Kang-Che Sung)
* Check for pkg-config when building with --enable-delayacct * Check for pkg-config when building with --enable-delayacct
(thanks to @florian2833z for the report) (thanks to @florian2833z for the report)
* Avoid some bashisms in configure script
(thanks to Jesin)
* Use CFLAGS from ncurses*-config if present * Use CFLAGS from ncurses*-config if present
(thanks to Michael Klein) (thanks to Michael Klein)
* Fix build failure in Glibc 2.28 * Header generator supports non-UTF-8 environments
(thanks to Kang-Che Sung) (thanks to @volkov-am)
* Linux: changed detection of kernel threads
* Collapse current subtree pressing Backspace
* BUGFIX: fix behavior of SYSCR column * BUGFIX: fix behavior of SYSCR column
(thanks to Marc Kleine-Budde) (thanks to Marc Kleine-Budde)
* BUGFIX: preserve LDFLAGS when building * BUGFIX: obtain exit code of lsof correctly
(thanks to Lance Frederickson for the report) (thanks to @wangqr)
* BUGFIX: fix issue with small terminals
(thanks to Daniel Elf for the report)
* BUGFIX: fix crash with particular keycodes * BUGFIX: fix crash with particular keycodes
(thanks to Wellington Torrejais da Silva for the report) (thanks to Wellington Torrejais da Silva for the report)
* BUGFIX: fix issue with small terminals
(thanks to Daniel Elf for the report)
* BUGFIX: fix terminal color issues * BUGFIX: fix terminal color issues
(thanks to Kang-Che Sung for the report) (thanks to Kang-Che Sung for the report)
* BUGFIX: preserve LDFLAGS when building
(thanks to Lance Frederickson for the report)
* BUGFIX: fixed overflow for systems with >= 100 signals
What's new in version 2.1.0 What's new in version 2.1.0

View File

@ -35,12 +35,11 @@ static void CheckItem_delete(Object* cast) {
static void CheckItem_display(Object* cast, RichString* out) { static void CheckItem_display(Object* cast, RichString* out) {
CheckItem* this = (CheckItem*)cast; CheckItem* this = (CheckItem*)cast;
assert (this != NULL); assert (this != NULL);
RichString_write(out, CRT_colors[CHECK_BOX], "[");
if (CheckItem_get(this)) if (CheckItem_get(this))
RichString_append(out, CRT_colors[CHECK_MARK], "x"); RichString_append(out, CRT_colors[CHECK_MARK], CRT_checkStr[CHECK_STR_FULL]);
else else
RichString_append(out, CRT_colors[CHECK_MARK], " "); RichString_append(out, CRT_colors[CHECK_MARK], CRT_checkStr[CHECK_STR_NONE]);
RichString_append(out, CRT_colors[CHECK_BOX], "] "); RichString_append(out, CRT_colors[CHECK_TEXT], " ");
RichString_append(out, CRT_colors[CHECK_TEXT], this->text); RichString_append(out, CRT_colors[CHECK_TEXT], this->text);
} }

View File

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

View File

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

View File

@ -22,9 +22,8 @@ in the source distribution for its full text.
typedef struct ColumnsPanel_ { typedef struct ColumnsPanel_ {
Panel super; Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving; bool moving;
} ColumnsPanel; } ColumnsPanel;
@ -124,31 +123,22 @@ PanelClass ColumnsPanel_class = {
.eventHandler = ColumnsPanel_eventHandler .eventHandler = ColumnsPanel_eventHandler
}; };
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss) { ColumnsPanel* ColumnsPanel_new(Settings* settings) {
Panel* super = (Panel*) this;
Panel_prune(super);
ProcessField* fields = ss->fields;
for (; *fields; fields++) {
if (Process_fields[*fields].name) {
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
}
}
this->ss = ss;
}
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed) {
ColumnsPanel* this = AllocThis(ColumnsPanel); ColumnsPanel* this = AllocThis(ColumnsPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL); FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->ss = ss; this->settings = settings;
this->changed = changed;
this->moving = false; this->moving = false;
Panel_setHeader(super, "Active Columns"); Panel_setHeader(super, "Active Columns");
ColumnsPanel_fill(this, ss); ProcessField* fields = this->settings->fields;
for (; *fields; fields++) {
if (Process_fields[*fields].name) {
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
}
}
return this; return this;
} }
@ -164,14 +154,13 @@ int ColumnsPanel_fieldNameToIndex(const char* name) {
void ColumnsPanel_update(Panel* super) { void ColumnsPanel_update(Panel* super) {
ColumnsPanel* this = (ColumnsPanel*) super; ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_size(super); int size = Panel_size(super);
*(this->changed) = true; this->settings->changed = true;
this->ss->fields = xRealloc(this->ss->fields, sizeof(ProcessField) * (size+1)); this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size+1));
this->ss->flags = 0; this->settings->flags = 0;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
int key = ((ListItem*) Panel_get(super, i))->key; int key = ((ListItem*) Panel_get(super, i))->key;
this->ss->fields[i] = key; this->settings->fields[i] = key;
this->ss->flags |= key < 1000 ? Process_fields[key].flags : 0; this->settings->flags |= Process_fields[key].flags;
} }
this->ss->fields[size] = 0; this->settings->fields[size] = 0;
} }

View File

@ -14,22 +14,18 @@ in the source distribution for its full text.
typedef struct ColumnsPanel_ { typedef struct ColumnsPanel_ {
Panel super; Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving; bool moving;
} ColumnsPanel; } ColumnsPanel;
extern PanelClass ColumnsPanel_class; extern PanelClass ColumnsPanel_class;
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss); extern ColumnsPanel* ColumnsPanel_new(Settings* settings);
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed); extern int ColumnsPanel_fieldNameToIndex(const char* name);
int ColumnsPanel_fieldNameToIndex(const char* name);
void ColumnsPanel_update(Panel* super);
extern void ColumnsPanel_update(Panel* super);
#endif #endif

View File

@ -83,6 +83,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
this->scr = scr; this->scr = scr;
Panel_setHeader(super, "Display options"); Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));
@ -96,5 +97,11 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU percentage numerically"), &(settings->showCPUUsage)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable the mouse"), &(settings->enableMouse)));
#ifdef HAVE_LIBHWLOC
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show topology when selecting affinity by default"), &(settings->topologyAffinity)));
#endif
return this; return this;
} }

View File

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

View File

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

View File

@ -96,12 +96,11 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
} }
} }
int FunctionBar_draw(const FunctionBar* this, char* buffer) { void FunctionBar_draw(const FunctionBar* this, char* buffer) {
return FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]); FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
} }
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) { void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
int cursorX = 0;
attrset(CRT_colors[FUNCTION_BAR]); attrset(CRT_colors[FUNCTION_BAR]);
mvhline(LINES-1, 0, ' ', COLS); mvhline(LINES-1, 0, ' ', COLS);
int x = 0; int x = 0;
@ -116,10 +115,12 @@ int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
if (buffer) { if (buffer) {
attrset(attr); attrset(attr);
mvaddstr(LINES-1, x, buffer); mvaddstr(LINES-1, x, buffer);
cursorX = x + strlen(buffer); CRT_cursorX = x + strlen(buffer);
curs_set(1);
} else {
curs_set(0);
} }
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
return cursorX;
} }
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) { int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {

View File

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

View File

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

View File

@ -62,7 +62,7 @@ void Header_delete(Header* this) {
void Header_populateFromSettings(Header* this) { void Header_populateFromSettings(Header* this) {
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->meterColumns[col]; MeterColumnSettings* colSettings = &this->settings->columns[col];
for (int i = 0; i < colSettings->len; i++) { for (int i = 0; i < colSettings->len; i++) {
Header_addMeterByName(this, colSettings->names[i], col); Header_addMeterByName(this, colSettings->names[i], col);
if (colSettings->modes[i] != 0) { if (colSettings->modes[i] != 0) {
@ -75,7 +75,7 @@ void Header_populateFromSettings(Header* this) {
void Header_writeBackToSettings(const Header* this) { void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->meterColumns[col]; MeterColumnSettings* colSettings = &this->settings->columns[col];
String_freeArray(colSettings->names); String_freeArray(colSettings->names);
free(colSettings->modes); free(colSettings->modes);
@ -120,6 +120,8 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
break; break;
} }
} }
if (paren)
*paren = '(';
return mode; return mode;
} }
@ -152,7 +154,7 @@ char* Header_readMeterName(Header* this, int i, int column) {
int nameLen = strlen(Meter_name(meter)); int nameLen = strlen(Meter_name(meter));
int len = nameLen + 100; int len = nameLen + 100;
char* name = xMalloc(len); char* name = xMalloc(len);
strncpy(name, Meter_name(meter), nameLen); memcpy(name, Meter_name(meter), nameLen);
name[nameLen] = '\0'; name[nameLen] = '\0';
if (meter->param) if (meter->param)
xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param); xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param);

View File

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

370
INSTALL
View File

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

View File

@ -38,7 +38,6 @@ typedef struct IncMode_ {
typedef struct IncSet_ { typedef struct IncSet_ {
IncMode modes[2]; IncMode modes[2];
IncMode* active; IncMode* active;
Panel* panel;
FunctionBar* defaultBar; FunctionBar* defaultBar;
bool filtering; bool filtering;
bool found; bool found;
@ -53,6 +52,10 @@ static void IncMode_reset(IncMode* mode) {
mode->buffer[0] = 0; mode->buffer[0] = 0;
} }
void IncSet_reset(IncSet* this, IncType type) {
IncMode_reset(&this->modes[type]);
}
static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL}; static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL};
static const char* const searchKeys[] = {"F3", "Esc", " "}; static const char* const searchKeys[] = {"F3", "Esc", " "};
static int searchEvents[] = {KEY_F(3), 27, ERR}; static int searchEvents[] = {KEY_F(3), 27, ERR};
@ -116,33 +119,45 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
} }
} }
static bool search(IncSet* this, Panel* panel, IncMode_GetPanelValue getPanelValue) { static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel); int size = Panel_size(panel);
bool found = false; bool found = false;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), this->active->buffer)) { if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i); Panel_setSelected(panel, i);
found = true; found = true;
break; break;
} }
} }
IncSet_drawBar(this, found ? CRT_colors[FUNCTION_BAR] : CRT_colors[FAILED_SEARCH]); if (found)
FunctionBar_draw(mode->bar, mode->buffer);
else
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
return found; return found;
} }
void IncSet_activate(IncSet* this, IncType type, Panel* panel) { static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
this->active = &(this->modes[type]); int size = Panel_size(panel);
panel->currentBar = this->active->bar; int here = Panel_getSelectedIndex(panel);
panel->cursorOn = true; int i = here;
this->panel = panel; for(;;) {
IncSet_drawBar(this, CRT_colors[FUNCTION_BAR]); i+=step;
if (i == size) i = 0;
if (i == -1) i = size - 1;
if (i == here) return false;
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
return true;
}
}
} }
static void IncSet_deactivate(IncSet* this, Panel* panel) { bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
this->active = NULL; return IncMode_find(&this->modes[type], panel, getPanelValue, 1);
Panel_setDefaultBar(panel); }
panel->cursorOn = false;
FunctionBar_draw(this->defaultBar, NULL); bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
return IncMode_find(&this->modes[type], panel, getPanelValue, -1);
} }
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) { bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
@ -154,17 +169,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
bool doSearch = true; bool doSearch = true;
if (ch == KEY_F(3)) { if (ch == KEY_F(3)) {
if (size == 0) return true; if (size == 0) return true;
int here = Panel_getSelectedIndex(panel); IncMode_find(mode, panel, getPanelValue, 1);
int i = here;
for(;;) {
i++;
if (i == size) i = 0;
if (i == here) break;
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
break;
}
}
doSearch = false; doSearch = false;
} else if (ch < 255 && isprint((char)ch)) { } else if (ch < 255 && isprint((char)ch)) {
if (mode->index < INCMODE_MAX) { if (mode->index < INCMODE_MAX) {
@ -200,13 +205,17 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
IncMode_reset(mode); IncMode_reset(mode);
} }
} else { } else {
IncMode_reset(mode); if (ch == 27) {
IncMode_reset(mode);
}
} }
IncSet_deactivate(this, panel); this->active = NULL;
Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar, NULL);
doSearch = false; doSearch = false;
} }
if (doSearch) { if (doSearch) {
this->found = search(this, panel, getPanelValue); this->found = search(mode, panel, getPanelValue);
} }
if (filterChanged && lines) { if (filterChanged && lines) {
updateWeakPanel(this, panel, lines); updateWeakPanel(this, panel, lines);
@ -221,11 +230,15 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
return ""; return "";
} }
void IncSet_drawBar(IncSet* this, int attr) { void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
FunctionBar_draw(this->active->bar, this->active->buffer);
panel->currentBar = this->active->bar;
}
void IncSet_drawBar(IncSet* this) {
if (this->active) { if (this->active) {
int cursorX = FunctionBar_drawAttr(this->active->bar, this->active->buffer, attr); FunctionBar_draw(this->active->bar, this->active->buffer);
this->panel->cursorY = LINES - 1;
this->panel->cursorX = cursorX;
} else { } else {
FunctionBar_draw(this->defaultBar, NULL); FunctionBar_draw(this->defaultBar, NULL);
} }

View File

@ -33,7 +33,6 @@ typedef struct IncMode_ {
typedef struct IncSet_ { typedef struct IncSet_ {
IncMode modes[2]; IncMode modes[2];
IncMode* active; IncMode* active;
Panel* panel;
FunctionBar* defaultBar; FunctionBar* defaultBar;
bool filtering; bool filtering;
bool found; bool found;
@ -42,18 +41,24 @@ typedef struct IncSet_ {
typedef const char* (*IncMode_GetPanelValue)(Panel*, int); typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
IncSet* IncSet_new(FunctionBar* bar); extern void IncSet_reset(IncSet* this, IncType type);
void IncSet_delete(IncSet* this); extern IncSet* IncSet_new(FunctionBar* bar);
void IncSet_activate(IncSet* this, IncType type, Panel* panel); extern void IncSet_delete(IncSet* this);
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines); extern bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
const char* IncSet_getListItemValue(Panel* panel, int i); extern bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
void IncSet_drawBar(IncSet* this, int attr); extern bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
int IncSet_synthesizeEvent(IncSet* this, int x); extern const char* IncSet_getListItemValue(Panel* panel, int i);
extern void IncSet_activate(IncSet* this, IncType type, Panel* panel);
extern void IncSet_drawBar(IncSet* this);
extern int IncSet_synthesizeEvent(IncSet* this, int x);
#endif #endif

View File

@ -56,7 +56,7 @@ static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27}; static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, char* panelHeader) { InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, const char* panelHeader) {
this->process = process; this->process = process;
if (!bar) { if (!bar) {
bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents); bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents);
@ -75,7 +75,7 @@ InfoScreen* InfoScreen_done(InfoScreen* this) {
return this; return this;
} }
void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...) { void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
@ -85,7 +85,7 @@ void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...) {
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true; this->display->needsRedraw = true;
Panel_draw(this->display, true); Panel_draw(this->display, true);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]); IncSet_drawBar(this->inc);
va_end(ap); va_end(ap);
} }
@ -115,7 +115,11 @@ void InfoScreen_run(InfoScreen* this) {
Panel_draw(panel, true); Panel_draw(panel, true);
int ch = Panel_getCh(panel); if (this->inc->active) {
(void) move(LINES-1, CRT_cursorX);
}
set_escdelay(25);
int ch = getch();
if (ch == ERR) { if (ch == ERR) {
if (As_InfoScreen(this)->onErr) { if (As_InfoScreen(this)->onErr) {
@ -127,12 +131,14 @@ void InfoScreen_run(InfoScreen* this) {
if (ch == KEY_MOUSE) { if (ch == KEY_MOUSE) {
MEVENT mevent; MEVENT mevent;
int ok = getmouse(&mevent); int ok = getmouse(&mevent);
if (ok == OK) if (ok == OK) {
if (mevent.y >= panel->y && mevent.y < LINES - 1) { if (mevent.y >= panel->y && mevent.y < LINES - 1) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV); Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV);
ch = 0; ch = 0;
} if (mevent.y == LINES - 1) } else if (mevent.y == LINES - 1) {
ch = IncSet_synthesizeEvent(this->inc, mevent.x); ch = IncSet_synthesizeEvent(this->inc, mevent.x);
}
}
} }
if (this->inc->active) { if (this->inc->active) {

View File

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

View File

@ -27,13 +27,13 @@ typedef struct ListItem_ {
}*/ }*/
void ListItem_delete(Object* cast) { static void ListItem_delete(Object* cast) {
ListItem* this = (ListItem*)cast; ListItem* this = (ListItem*)cast;
free(this->value); free(this->value);
free(this); free(this);
} }
void ListItem_display(Object* cast, RichString* out) { static void ListItem_display(Object* cast, RichString* out) {
ListItem* const this = (ListItem*)cast; ListItem* const this = (ListItem*)cast;
assert (this != NULL); assert (this != NULL);
/* /*
@ -59,15 +59,11 @@ ObjectClass ListItem_class = {
.compare = ListItem_compare .compare = ListItem_compare
}; };
void ListItem_init(ListItem* this, const char* value, int key) { ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
this->value = xStrdup(value); this->value = xStrdup(value);
this->key = key; this->key = key;
this->moving = false; this->moving = false;
}
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
ListItem_init(this, value, key);
return this; return this;
} }
@ -89,4 +85,3 @@ long ListItem_compare(const void* cast1, const void* cast2) {
ListItem* obj2 = (ListItem*) cast2; ListItem* obj2 = (ListItem*) cast2;
return strcmp(obj1->value, obj2->value); return strcmp(obj1->value, obj2->value);
} }

View File

@ -19,21 +19,14 @@ typedef struct ListItem_ {
} ListItem; } ListItem;
void ListItem_delete(Object* cast);
void ListItem_display(Object* cast, RichString* out);
extern ObjectClass ListItem_class; extern ObjectClass ListItem_class;
void ListItem_init(ListItem* this, const char* value, int key); extern ListItem* ListItem_new(const char* value, int key);
ListItem* ListItem_new(const char* value, int key); extern void ListItem_append(ListItem* this, const char* text);
void ListItem_append(ListItem* this, const char* text); extern const char* ListItem_getRef(ListItem* this);
const char* ListItem_getRef(ListItem* this);
long ListItem_compare(const void* cast1, const void* cast2);
extern long ListItem_compare(const void* cast1, const void* cast2);
#endif #endif

View File

@ -1,6 +1,7 @@
/* /*
htop - ColumnsPanel.c htop - ColumnsPanel.c
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -25,18 +26,13 @@ typedef struct MainPanel_ {
pid_t pidSearch; pid_t pidSearch;
} MainPanel; } MainPanel;
typedef union {
int i;
void* v;
} Arg;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg); typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar) #define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
}*/ }*/
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL}; static const char* const MainFunctions[] = {"Help ", "Setup ", "Search ", "Filter ", "Tree ", "SortBy ", "Nice - ", "Nice + ", "Kill ", "Quit ", NULL};
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) { void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this); FunctionBar* bar = MainPanel_getFunctionBar(this);
@ -72,17 +68,15 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
Settings* settings = this->state->settings;
ScreenSettings* ss = settings->ss;
if (EVENT_IS_HEADER_CLICK(ch)) { if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch); int x = EVENT_HEADER_CLICK_GET_X(ch);
ProcessList* pl = this->state->pl; ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1; int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx); ProcessField field = ProcessList_keyAt(pl, hx);
if (field == ss->sortKey) { if (field == settings->sortKey) {
ScreenSettings_invertSortOrder(ss); Settings_invertSortOrder(settings);
ss->treeView = false; settings->treeView = false;
} else { } else {
reaction |= Action_setSortKey(settings, field); reaction |= Action_setSortKey(settings, field);
} }
@ -115,8 +109,8 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} }
if (reaction & HTOP_REDRAW_BAR) { if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, settings->ss->treeView); MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]); IncSet_drawBar(this->inc);
} }
if (reaction & HTOP_UPDATE_PANELHDR) { if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super)); ProcessList_printHeader(this->state->pl, Panel_getHeader(super));

View File

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

View File

@ -12,7 +12,7 @@ applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png pixmap_DATA = htop.png
AM_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)" AM_CFLAGS += -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
AM_LDFLAGS = AM_LDFLAGS =
AM_CPPFLAGS = -DNDEBUG AM_CPPFLAGS = -DNDEBUG
@ -21,7 +21,7 @@ ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \
DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \ DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \ LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \
BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \ BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c ScreensPanel.c \ SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \
TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \ TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \
HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \ HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \
InfoScreen.c XAlloc.c InfoScreen.c XAlloc.c
@ -31,7 +31,7 @@ CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \
CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \ CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \
Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \ Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \
BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \ BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h ScreensPanel.h \ ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \
TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \ TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \
AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \ AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \
EnvScreen.h InfoScreen.h XAlloc.h EnvScreen.h InfoScreen.h XAlloc.h
@ -49,15 +49,19 @@ linux_platform_headers = \
linux/LinuxProcessList.h \ linux/LinuxProcessList.h \
linux/LinuxCRT.h \ linux/LinuxCRT.h \
linux/Battery.h \ linux/Battery.h \
linux/PerfCounter.h linux/PressureStallMeter.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
all_platform_headers += $(linux_platform_headers) all_platform_headers += $(linux_platform_headers)
if HTOP_LINUX if HTOP_LINUX
AM_CFLAGS += -rdynamic AM_LDFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \ myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \ linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \
linux/PerfCounter.c linux/PressureStallMeter.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(linux_platform_headers) myhtopplatheaders = $(linux_platform_headers)
endif endif
@ -70,13 +74,18 @@ freebsd_platform_headers = \
freebsd/FreeBSDProcessList.h \ freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \ freebsd/FreeBSDProcess.h \
freebsd/FreeBSDCRT.h \ freebsd/FreeBSDCRT.h \
freebsd/Battery.h freebsd/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
all_platform_headers += $(freebsd_platform_headers) all_platform_headers += $(freebsd_platform_headers)
if HTOP_FREEBSD if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \ myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(freebsd_platform_headers) myhtopplatheaders = $(freebsd_platform_headers)
endif endif
@ -128,14 +137,19 @@ darwin_platform_headers = \
darwin/DarwinProcess.h \ darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \ darwin/DarwinProcessList.h \
darwin/DarwinCRT.h \ darwin/DarwinCRT.h \
darwin/Battery.h darwin/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
all_platform_headers += $(darwin_platform_headers) all_platform_headers += $(darwin_platform_headers)
if HTOP_DARWIN if HTOP_DARWIN
AM_LDFLAGS += -framework IOKit -framework CoreFoundation AM_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \ myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(darwin_platform_headers) myhtopplatheaders = $(darwin_platform_headers)
endif endif
@ -148,14 +162,18 @@ solaris_platform_headers = \
solaris/SolarisProcess.h \ solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \ solaris/SolarisProcessList.h \
solaris/SolarisCRT.h \ solaris/SolarisCRT.h \
solaris/Battery.h solaris/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
all_platform_headers += $(solaris_platform_headers) all_platform_headers += $(solaris_platform_headers)
if HTOP_SOLARIS if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \ myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \ solaris/SolarisProcess.c solaris/SolarisProcessList.c \
solaris/SolarisCRT.c solaris/Battery.c solaris/SolarisCRT.c solaris/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(solaris_platform_headers) myhtopplatheaders = $(solaris_platform_headers)
endif endif
@ -185,7 +203,8 @@ endif
SUFFIXES = .h SUFFIXES = .h
BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders) BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) config.h htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
nodist_htop_SOURCES = config.h
.PHONY: htop-headers clean-htop-headers .PHONY: htop-headers clean-htop-headers
@ -203,18 +222,22 @@ profile:
debug: debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG" $(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG"
symbols:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DNDEBUG"
coverage: coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov" $(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"
.c.h: .c.h:
@srcdir@/scripts/MakeHeader.py $< ./scripts/MakeHeader.py $<
cppcheck: cppcheck:
cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS
dist-hook: $(top_distdir)/configure
@if grep 'pkg_m4_absent' '$(top_distdir)/configure'; then \
echo 'configure is generated without pkg.m4. Please supply pkg.m4 and run ./autogen.sh to rebuild the configure script.'>&2; \
(exit 1); \
else :; \
fi
.PHONY: lcov .PHONY: lcov
lcov: lcov:

View File

@ -523,6 +523,9 @@ MeterMode* Meter_modes[] = {
static void BlankMeter_updateValues(Meter* this, char* buffer, int size) { static void BlankMeter_updateValues(Meter* this, char* buffer, int size) {
(void) this; (void) buffer; (void) size; (void) this; (void) buffer; (void) size;
if (size > 0) {
*buffer = 0;
}
} }
static void BlankMeter_display(Object* cast, RichString* out) { static void BlankMeter_display(Object* cast, RichString* out) {

12
Meter.h
View File

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

View File

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

1
NEWS
View File

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

View File

@ -1,6 +1,7 @@
/* /*
htop - Object.c htop - Object.c
(C) 2004-2012 Hisham H. Muhammad (C) 2004-2012 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -40,6 +41,11 @@ struct Object_ {
ObjectClass* klass; ObjectClass* klass;
}; };
typedef union {
int i;
void* v;
} Arg;
}*/ }*/
ObjectClass Object_class = { ObjectClass Object_class = {

View File

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

View File

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

View File

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

30
Panel.c
View File

@ -57,7 +57,6 @@ typedef struct PanelClass_ {
struct Panel_ { struct Panel_ {
Object super; Object super;
int x, y, w, h; int x, y, w, h;
int cursorX, cursorY;
WINDOW* window; WINDOW* window;
Vector* items; Vector* items;
int selected; int selected;
@ -67,7 +66,6 @@ struct Panel_ {
int scrollV; int scrollV;
short scrollH; short scrollH;
bool needsRedraw; bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar; FunctionBar* currentBar;
FunctionBar* defaultBar; FunctionBar* defaultBar;
RichString header; RichString header;
@ -87,11 +85,6 @@ struct Panel_ {
#define KEY_CTRL(l) ((l)-'A'+1) #define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this) {
this->cursorY = this->y + this->selected - this->scrollV + 1;
this->cursorX = this->x + this->selectedLen - this->scrollH;
}
PanelClass Panel_class = { PanelClass Panel_class = {
.super = { .super = {
.extends = Class(Object), .extends = Class(Object),
@ -119,8 +112,6 @@ void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool
this->y = y; this->y = y;
this->w = w; this->w = w;
this->h = h; this->h = h;
this->cursorX = 0;
this->cursorY = 0;
this->eventHandlerState = NULL; this->eventHandlerState = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE); this->items = Vector_new(type, owner, DEFAULT_SIZE);
this->scrollV = 0; this->scrollV = 0;
@ -273,6 +264,14 @@ void Panel_setSelected(Panel* this, int selected) {
} }
} }
void Panel_splice(Panel *this, Vector* from) {
assert (this != NULL);
assert (from != NULL);
Vector_splice(this->items, from);
this->needsRedraw = true;
}
void Panel_draw(Panel* this, bool focus) { void Panel_draw(Panel* this, bool focus) {
assert (this != NULL); assert (this != NULL);
@ -376,6 +375,7 @@ void Panel_draw(Panel* this, bool focus) {
RichString_end(old); RichString_end(old);
} }
this->oldSelected = this->selected; this->oldSelected = this->selected;
move(0, 0);
} }
bool Panel_onKey(Panel* this, int key) { bool Panel_onKey(Panel* this, int key) {
@ -507,15 +507,3 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
} }
return IGNORED; return IGNORED;
} }
int Panel_getCh(Panel* this) {
if (this->cursorOn) {
move(this->cursorY, this->cursorX);
curs_set(1);
} else {
curs_set(0);
}
set_escdelay(25);
return getch();
}

53
Panel.h
View File

@ -46,7 +46,6 @@ typedef struct PanelClass_ {
struct Panel_ { struct Panel_ {
Object super; Object super;
int x, y, w, h; int x, y, w, h;
int cursorX, cursorY;
WINDOW* window; WINDOW* window;
Vector* items; Vector* items;
int selected; int selected;
@ -56,7 +55,6 @@ struct Panel_ {
int scrollV; int scrollV;
short scrollH; short scrollH;
bool needsRedraw; bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar; FunctionBar* currentBar;
FunctionBar* defaultBar; FunctionBar* defaultBar;
RichString header; RichString header;
@ -75,59 +73,56 @@ struct Panel_ {
#define KEY_CTRL(l) ((l)-'A'+1) #define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this);
extern PanelClass Panel_class; extern PanelClass Panel_class;
Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar); extern Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar);
void Panel_delete(Object* cast); extern void Panel_delete(Object* cast);
void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar); extern void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar);
void Panel_done(Panel* this); extern void Panel_done(Panel* this);
void Panel_setSelectionColor(Panel* this, int color); extern void Panel_setSelectionColor(Panel* this, int color);
RichString* Panel_getHeader(Panel* this); extern RichString* Panel_getHeader(Panel* this);
extern void Panel_setHeader(Panel* this, const char* header); extern void Panel_setHeader(Panel* this, const char* header);
void Panel_move(Panel* this, int x, int y); extern void Panel_move(Panel* this, int x, int y);
void Panel_resize(Panel* this, int w, int h); extern void Panel_resize(Panel* this, int w, int h);
void Panel_prune(Panel* this); extern void Panel_prune(Panel* this);
void Panel_add(Panel* this, Object* o); extern void Panel_add(Panel* this, Object* o);
void Panel_insert(Panel* this, int i, Object* o); extern void Panel_insert(Panel* this, int i, Object* o);
void Panel_set(Panel* this, int i, Object* o); extern void Panel_set(Panel* this, int i, Object* o);
Object* Panel_get(Panel* this, int i); extern Object* Panel_get(Panel* this, int i);
Object* Panel_remove(Panel* this, int i); extern Object* Panel_remove(Panel* this, int i);
Object* Panel_getSelected(Panel* this); extern Object* Panel_getSelected(Panel* this);
void Panel_moveSelectedUp(Panel* this); extern void Panel_moveSelectedUp(Panel* this);
void Panel_moveSelectedDown(Panel* this); extern void Panel_moveSelectedDown(Panel* this);
int Panel_getSelectedIndex(Panel* this); extern int Panel_getSelectedIndex(Panel* this);
int Panel_size(Panel* this); extern int Panel_size(Panel* this);
void Panel_setSelected(Panel* this, int selected); extern void Panel_setSelected(Panel* this, int selected);
void Panel_draw(Panel* this, bool focus); extern void Panel_draw(Panel* this, bool focus);
bool Panel_onKey(Panel* this, int key); extern void Panel_splice(Panel *this, Vector* from);
HandlerResult Panel_selectByTyping(Panel* this, int ch); extern bool Panel_onKey(Panel* this, int key);
int Panel_getCh(Panel* this);
extern HandlerResult Panel_selectByTyping(Panel* this, int ch);
#endif #endif

112
Process.c
View File

@ -1,6 +1,7 @@
/* /*
htop - Process.c htop - Process.c
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -8,6 +9,8 @@ in the source distribution for its full text.
#include "Process.h" #include "Process.h"
#include "Settings.h" #include "Settings.h"
#include "config.h"
#include "CRT.h" #include "CRT.h"
#include "StringUtils.h" #include "StringUtils.h"
#include "RichString.h" #include "RichString.h"
@ -30,8 +33,7 @@ in the source distribution for its full text.
#include <math.h> #include <math.h>
#ifdef MAJOR_IN_MKDEV #ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h> #include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS) || \ #elif defined(MAJOR_IN_SYSMACROS)
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
#endif #endif
@ -51,7 +53,6 @@ in the source distribution for its full text.
#include "Object.h" #include "Object.h"
#include <sys/types.h> #include <sys/types.h>
#include <inttypes.h>
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x0001
@ -84,7 +85,7 @@ typedef enum ProcessFields {
typedef struct ProcessPidColumn_ { typedef struct ProcessPidColumn_ {
int id; int id;
char* label; const char* label;
} ProcessPidColumn; } ProcessPidColumn;
typedef struct Process_ { typedef struct Process_ {
@ -157,7 +158,7 @@ typedef struct ProcessFieldData_ {
const char* name; const char* name;
const char* title; const char* title;
const char* description; const char* description;
uint64_t flags; int flags;
} ProcessFieldData; } ProcessFieldData;
// Implemented in platform-specific code: // Implemented in platform-specific code:
@ -192,10 +193,12 @@ static int Process_getuid = -1;
#define ONE_K 1024L #define ONE_K 1024L
#define ONE_M (ONE_K * ONE_K) #define ONE_M (ONE_K * ONE_K)
#define ONE_G (ONE_M * ONE_K) #define ONE_G (ONE_M * ONE_K)
#define ONE_T ((long long)ONE_G * ONE_K)
#define ONE_DECIMAL_K 1000L #define ONE_DECIMAL_K 1000L
#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K) #define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K)
#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K) #define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K)
#define ONE_DECIMAL_T ((long long)ONE_DECIMAL_G * ONE_DECIMAL_K)
char Process_pidFormat[20] = "%7d "; char Process_pidFormat[20] = "%7d ";
@ -229,7 +232,7 @@ void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
if(number >= (10 * ONE_DECIMAL_M)) { if(number >= (10 * ONE_DECIMAL_M)) {
#ifdef __LP64__ #ifdef __LP64__
if(number >= (100 * ONE_DECIMAL_G)) { if(number >= (100 * ONE_DECIMAL_G)) {
len = snprintf(buffer, 10, "%4ldT ", number / ONE_G); len = snprintf(buffer, 10, "%4luT ", number / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendn(str, largeNumberColor, buffer, len);
return; return;
} else if (number >= (1000 * ONE_DECIMAL_M)) { } else if (number >= (1000 * ONE_DECIMAL_M)) {
@ -239,7 +242,7 @@ void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
} }
#endif #endif
if(number >= (100 * ONE_DECIMAL_M)) { if(number >= (100 * ONE_DECIMAL_M)) {
len = snprintf(buffer, 10, "%4ldG ", number / ONE_M); len = snprintf(buffer, 10, "%4luG ", number / ONE_M);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendn(str, largeNumberColor, buffer, len);
return; return;
} }
@ -247,11 +250,11 @@ void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendn(str, largeNumberColor, buffer, len);
return; return;
} else if (number >= 100000) { } else if (number >= 100000) {
len = snprintf(buffer, 10, "%4ldM ", number / ONE_K); len = snprintf(buffer, 10, "%4luM ", number / ONE_K);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendn(str, processMegabytesColor, buffer, len);
return; return;
} else if (number >= 1000) { } else if (number >= 1000) {
len = snprintf(buffer, 10, "%2ld", number/1000); len = snprintf(buffer, 10, "%2lu", number/1000);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendn(str, processMegabytesColor, buffer, len);
number %= 1000; number %= 1000;
len = snprintf(buffer, 10, "%03lu ", number); len = snprintf(buffer, 10, "%03lu ", number);
@ -278,8 +281,15 @@ void Process_colorNumber(RichString* str, unsigned long long number, bool colori
if ((long long) number == -1LL) { if ((long long) number == -1LL) {
int len = snprintf(buffer, 13, " no perm "); int len = snprintf(buffer, 13, " no perm ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len); RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (number > 10000000000) { } else if (number >= 100000LL * ONE_DECIMAL_T) {
xSnprintf(buffer, 13, "%11lld ", number / 1000); xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_G);
RichString_appendn(str, largeNumberColor, buffer, 12);
} else if (number >= 100LL * ONE_DECIMAL_T) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_M);
RichString_appendn(str, largeNumberColor, buffer, 8);
RichString_appendn(str, processMegabytesColor, buffer+8, 4);
} else if (number >= 10LL * ONE_DECIMAL_G) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_K);
RichString_appendn(str, largeNumberColor, buffer, 5); RichString_appendn(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3); RichString_appendn(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4); RichString_appendn(str, processColor, buffer+8, 4);
@ -359,30 +369,18 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
} else if (rate < ONE_K) { } else if (rate < ONE_K) {
int len = snprintf(buffer, n, "%7.2f B/s ", rate); int len = snprintf(buffer, n, "%7.2f B/s ", rate);
RichString_appendn(str, processColor, buffer, len); RichString_appendn(str, processColor, buffer, len);
} else if (rate < ONE_K * ONE_K) { } else if (rate < ONE_M) {
int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K); int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K);
RichString_appendn(str, processColor, buffer, len); RichString_appendn(str, processColor, buffer, len);
} else if (rate < ONE_K * ONE_K * ONE_K) { } else if (rate < ONE_G) {
int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_K / ONE_K); int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendn(str, processMegabytesColor, buffer, len);
} else { } else if (rate < ONE_T) {
int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_K / ONE_K / ONE_K); int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendn(str, largeNumberColor, buffer, len);
}
}
void Process_printPercentage(float val, char* buffer, int n, int* attr) {
if (val >= 0) {
if (val < 100) {
xSnprintf(buffer, n, "%4.1f ", val);
} else if (val < 1000) {
xSnprintf(buffer, n, "%3d. ", (unsigned int)val);
} else {
xSnprintf(buffer, n, "%4d ", (unsigned int)val);
}
} else { } else {
*attr = CRT_colors[PROCESS_SHADOW]; int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T);
xSnprintf(buffer, n, " N/A "); RichString_appendn(str, largeNumberColor, buffer, len);
} }
} }
@ -394,15 +392,30 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
bool coloring = this->settings->highlightMegabytes; bool coloring = this->settings->highlightMegabytes;
switch (field) { switch (field) {
case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, &attr); break; case PERCENT_CPU: {
case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, &attr); break; if (this->percent_cpu > 999.9) {
xSnprintf(buffer, n, "%4u ", (unsigned int)this->percent_cpu);
} else if (this->percent_cpu > 99.9) {
xSnprintf(buffer, n, "%3u. ", (unsigned int)this->percent_cpu);
} else {
xSnprintf(buffer, n, "%4.1f ", this->percent_cpu);
}
break;
}
case PERCENT_MEM: {
if (this->percent_mem > 99.9) {
xSnprintf(buffer, n, "100. ");
} else {
xSnprintf(buffer, n, "%4.1f ", this->percent_mem);
}
break;
}
case COMM: { case COMM: {
if (this->settings->highlightThreads && Process_isThread(this)) { if (this->settings->highlightThreads && Process_isThread(this)) {
attr = CRT_colors[PROCESS_THREAD]; attr = CRT_colors[PROCESS_THREAD];
baseattr = CRT_colors[PROCESS_THREAD_BASENAME]; baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
} }
ScreenSettings* ss = this->settings->ss; if (!this->settings->treeView || this->indent == 0) {
if (!ss->treeView || this->indent == 0) {
Process_writeCommand(this, attr, baseattr, str); Process_writeCommand(this, attr, baseattr, str);
return; return;
} else { } else {
@ -415,15 +428,20 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
if (indent & (1U << i)) if (indent & (1U << i))
maxIndent = i+1; maxIndent = i+1;
for (int i = 0; i < maxIndent - 1; i++) { for (int i = 0; i < maxIndent - 1; i++) {
int written; int written, ret;
if (indent & (1 << i)) if (indent & (1 << i))
written = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]); ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
else else
written = snprintf(buf, n, " "); ret = snprintf(buf, n, " ");
if (ret < 0 || ret >= n) {
written = n;
} else {
written = ret;
}
buf += written; buf += written;
n -= written; n -= written;
} }
const char* draw = CRT_treeStr[lastItem ? (ss->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE]; const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
RichString_append(str, CRT_colors[PROCESS_TREE], buffer); RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
Process_writeCommand(this, attr, baseattr, str); Process_writeCommand(this, attr, baseattr, str);
@ -467,7 +485,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
} }
break; break;
} }
case ST_UID: xSnprintf(buffer, n, "%4d ", this->st_uid); break; case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break;
case TIME: Process_printTime(str, this->time); return; case TIME: Process_printTime(str, this->time); return;
case TGID: xSnprintf(buffer, n, Process_pidFormat, this->tgid); break; case TGID: xSnprintf(buffer, n, Process_pidFormat, this->tgid); break;
case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break; case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break;
@ -494,7 +512,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
void Process_display(Object* cast, RichString* out) { void Process_display(Object* cast, RichString* out) {
Process* this = (Process*) cast; Process* this = (Process*) cast;
ProcessField* fields = this->settings->ss->fields; ProcessField* fields = this->settings->fields;
RichString_prune(out); RichString_prune(out);
for (int i = 0; fields[i]; i++) for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]); As_Process(this)->writeField(this, out, fields[i]);
@ -545,14 +563,15 @@ bool Process_setPriority(Process* this, int priority) {
return (err == 0); return (err == 0);
} }
bool Process_changePriorityBy(Process* this, int delta) { bool Process_changePriorityBy(Process* this, Arg delta) {
return Process_setPriority(this, this->nice + delta); return Process_setPriority(this, this->nice + delta.i);
} }
void Process_sendSignal(Process* this, int sgn) { bool Process_sendSignal(Process* this, Arg sgn) {
CRT_dropPrivileges(); CRT_dropPrivileges();
kill(this->pid, (int) sgn); bool ok = (kill(this->pid, sgn.i) == 0);
CRT_restorePrivileges(); CRT_restorePrivileges();
return ok;
} }
long Process_pidCompare(const void* v1, const void* v2) { long Process_pidCompare(const void* v1, const void* v2) {
@ -564,15 +583,14 @@ long Process_pidCompare(const void* v1, const void* v2) {
long Process_compare(const void* v1, const void* v2) { long Process_compare(const void* v1, const void* v2) {
Process *p1, *p2; Process *p1, *p2;
Settings *settings = ((Process*)v1)->settings; Settings *settings = ((Process*)v1)->settings;
ScreenSettings* ss = settings->ss; if (settings->direction == 1) {
if (ss->direction == 1) {
p1 = (Process*)v1; p1 = (Process*)v1;
p2 = (Process*)v2; p2 = (Process*)v2;
} else { } else {
p2 = (Process*)v1; p2 = (Process*)v1;
p1 = (Process*)v2; p1 = (Process*)v2;
} }
switch (ss->sortKey) { switch (settings->sortKey) {
case PERCENT_CPU: case PERCENT_CPU:
return (p2->percent_cpu > p1->percent_cpu ? 1 : -1); return (p2->percent_cpu > p1->percent_cpu ? 1 : -1);
case PERCENT_MEM: case PERCENT_MEM:

View File

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

View File

@ -93,12 +93,19 @@ ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable*
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
this->topologyOk = false; this->topologyOk = false;
int topoErr = hwloc_topology_init(&this->topology); if (hwloc_topology_init(&this->topology) == 0) {
if (topoErr == 0) { this->topologyOk =
topoErr = hwloc_topology_load(this->topology); #if HWLOC_API_VERSION < 0x00020000
} /* try to ignore the top-level machine object type */
if (topoErr == 0) { 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_MACHINE) &&
this->topologyOk = true; /* ignore caches, which don't add structure */
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CORE) &&
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CACHE) &&
0 == hwloc_topology_set_flags(this->topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) &&
#else
0 == hwloc_topology_set_all_types_filter(this->topology, HWLOC_TYPE_FILTER_KEEP_STRUCTURE) &&
#endif
0 == hwloc_topology_load(this->topology);
} }
#endif #endif
@ -124,15 +131,14 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
void ProcessList_printHeader(ProcessList* this, RichString* header) { void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header); RichString_prune(header);
ProcessField* fields = this->settings->ss->fields; ProcessField* fields = this->settings->fields;
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
unsigned int key = fields[i]; const char* field = Process_fields[fields[i]].title;
const char* field = Process_fields[key].title;
if (!field) field = "- "; if (!field) field = "- ";
int color = (!this->settings->ss->treeView && this->settings->ss->sortKey == key) if (!this->settings->treeView && this->settings->sortKey == fields[i])
? CRT_colors[PANEL_SELECTION_FOCUS] RichString_append(header, CRT_colors[PANEL_SELECTION_FOCUS], field);
: CRT_colors[PANEL_HEADER_FOCUS]; else
RichString_append(header, color, field); RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
} }
} }
@ -201,19 +207,19 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i
} }
void ProcessList_sort(ProcessList* this) { void ProcessList_sort(ProcessList* this) {
if (!this->settings->ss->treeView) { if (!this->settings->treeView) {
Vector_insertionSort(this->processes); Vector_insertionSort(this->processes);
} else { } else {
// Save settings // Save settings
int direction = this->settings->ss->direction; int direction = this->settings->direction;
int sortKey = this->settings->ss->sortKey; int sortKey = this->settings->sortKey;
// Sort by PID // Sort by PID
this->settings->ss->sortKey = PID; this->settings->sortKey = PID;
this->settings->ss->direction = 1; this->settings->direction = 1;
Vector_quickSort(this->processes); Vector_quickSort(this->processes);
// Restore settings // Restore settings
this->settings->ss->sortKey = sortKey; this->settings->sortKey = sortKey;
this->settings->ss->direction = direction; this->settings->direction = direction;
int vsize = Vector_size(this->processes); int vsize = Vector_size(this->processes);
// Find all processes whose parent is not visible // Find all processes whose parent is not visible
int size; int size;
@ -272,7 +278,7 @@ void ProcessList_sort(ProcessList* this) {
ProcessField ProcessList_keyAt(ProcessList* this, int at) { ProcessField ProcessList_keyAt(ProcessList* this, int at) {
int x = 0; int x = 0;
ProcessField* fields = this->settings->ss->fields; ProcessField* fields = this->settings->fields;
ProcessField field; ProcessField field;
for (int i = 0; (field = fields[i]); i++) { for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title; const char* title = Process_fields[field].title;

View File

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

50
README
View File

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

View File

@ -71,24 +71,24 @@ typedef struct RichString_ {
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish); extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
int RichString_findChar(RichString* this, char c, int start); extern int RichString_findChar(RichString* this, char c, int start);
#else #else
void RichString_setAttrn(RichString* this, int attrs, int start, int finish); extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
int RichString_findChar(RichString* this, char c, int start); extern int RichString_findChar(RichString* this, char c, int start);
#endif #endif
void RichString_prune(RichString* this); extern void RichString_prune(RichString* this);
void RichString_setAttr(RichString* this, int attrs); extern void RichString_setAttr(RichString* this, int attrs);
void RichString_append(RichString* this, int attrs, const char* data); extern void RichString_append(RichString* this, int attrs, const char* data);
void RichString_appendn(RichString* this, int attrs, const char* data, int len); extern void RichString_appendn(RichString* this, int attrs, const char* data, int len);
void RichString_write(RichString* this, int attrs, const char* data); extern void RichString_write(RichString* this, int attrs, const char* data);
#endif #endif

View File

@ -71,46 +71,30 @@ inline int ScreenManager_size(ScreenManager* this) {
} }
void ScreenManager_add(ScreenManager* this, Panel* item, int size) { 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) {
if (this->orientation == HORIZONTAL) { if (this->orientation == HORIZONTAL) {
int lastX = 0; int lastX = 0;
if (idx > 0) { if (this->panelCount > 0) {
Panel* last = (Panel*) Vector_get(this->panels, idx - 1); Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
lastX = last->x + last->w + 1; lastX = last->x + last->w + 1;
} }
int height = LINES - this->y1 + this->y2; int height = LINES - this->y1 + this->y2;
if (size <= 0) { if (size > 0) {
size = COLS-this->x1+this->x2-lastX; Panel_resize(item, size, height);
} else {
Panel_resize(item, COLS-this->x1+this->x2-lastX, height);
} }
Panel_resize(item, size, height);
Panel_move(item, lastX, this->y1); Panel_move(item, lastX, this->y1);
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);
}
}
} }
// TODO: VERTICAL // TODO: VERTICAL
Vector_insert(this->panels, idx, item); Vector_add(this->panels, item);
item->needsRedraw = true; item->needsRedraw = true;
this->panelCount++; this->panelCount++;
} }
Panel* ScreenManager_remove(ScreenManager* this, int idx) { Panel* ScreenManager_remove(ScreenManager* this, int idx) {
assert(this->panelCount > idx); assert(this->panelCount > idx);
int w = ((Panel*) Vector_get(this->panels, idx))->w;
Panel* panel = (Panel*) Vector_remove(this->panels, idx); Panel* panel = (Panel*) Vector_remove(this->panels, idx);
this->panelCount--; 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; return panel;
} }
@ -147,7 +131,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) { if (*rescan) {
*oldTime = newTime; *oldTime = newTime;
ProcessList_scan(pl); ProcessList_scan(pl);
if (*sortTimeout == 0 || this->settings->ss->treeView) { if (*sortTimeout == 0 || this->settings->treeView) {
ProcessList_sort(pl); ProcessList_sort(pl);
*sortTimeout = 1; *sortTimeout = 1;
} }
@ -161,20 +145,17 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
} }
static void ScreenManager_drawPanels(ScreenManager* this, int focus) { static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
int nPanels = this->panelCount; const int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) { for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i); Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_draw(panel, i == focus); Panel_draw(panel, i == focus);
if (i < nPanels) { if (this->orientation == HORIZONTAL) {
if (this->orientation == HORIZONTAL) { mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
}
} }
} }
} }
static Panel* setCurrentPanel(ScreenManager* this, int focus) { static Panel* setCurrentPanel(Panel* panel) {
Panel* panel = (Panel*) Vector_get(this->panels, focus);
FunctionBar_draw(panel->currentBar, NULL); FunctionBar_draw(panel->currentBar, NULL);
return panel; return panel;
} }
@ -183,7 +164,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false; bool quit = false;
int focus = 0; int focus = 0;
Panel* panelFocus = setCurrentPanel(this, focus); Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
double oldTime = 0.0; double oldTime = 0.0;
@ -206,10 +187,27 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
} }
int prevCh = ch; int prevCh = ch;
ch = Panel_getCh(panelFocus); set_escdelay(25);
ch = getch();
if (this->settings->vimMode) {
switch (ch) {
case 'h': ch = KEY_LEFT; break;
case 'j': ch = KEY_DOWN; break;
case 'k': ch = KEY_UP; break;
case 'l': ch = KEY_RIGHT; break;
case KEY_LEFT: ch = 'h'; break;
case KEY_DOWN: ch = 'j'; break;
case KEY_UP: ch = 'k'; break;
case KEY_RIGHT: ch = 'l'; break;
case 'K': ch = 'k'; break;
case 'J': ch = 'K'; break;
case 'L': ch = 'l'; break;
}
}
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
if (ch == KEY_MOUSE) { if (ch == KEY_MOUSE && this->settings->enableMouse) {
ch = ERR; ch = ERR;
MEVENT mevent; MEVENT mevent;
int ok = getmouse(&mevent); int ok = getmouse(&mevent);
@ -228,7 +226,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
ch = KEY_MOUSE; ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) { if (panel == panelFocus || this->allowFocusChange) {
focus = i; focus = i;
panelFocus = setCurrentPanel(this, i); panelFocus = setCurrentPanel(panel);
Object* oldSelection = Panel_getSelected(panel); Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1); Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) { if (Panel_getSelected(panel) == oldSelection) {
@ -304,7 +302,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
tryLeft: tryLeft:
if (focus > 0) if (focus > 0)
focus--; focus--;
panelFocus = setCurrentPanel(this, focus); panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
if (Panel_size(panelFocus) == 0 && focus > 0) if (Panel_size(panelFocus) == 0 && focus > 0)
goto tryLeft; goto tryLeft;
break; break;
@ -319,7 +317,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
tryRight: tryRight:
if (focus < this->panelCount - 1) if (focus < this->panelCount - 1)
focus++; focus++;
panelFocus = setCurrentPanel(this, focus); panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1)
goto tryRight; goto tryRight;
break; break;

View File

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

View File

@ -1,330 +0,0 @@
/*
htop - ScreensPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ScreensPanel.h"
#include "Platform.h"
#include "StringUtils.h"
#include "CRT.h"
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
/*{
#include "Panel.h"
#include "ScreenManager.h"
#include "ColumnsPanel.h"
#include "Settings.h"
#include "ListItem.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;
}*/
ObjectClass ScreenListItem_class = {
.extends = Class(ListItem),
.display = ListItem_display,
.delete = ListItem_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;
Panel_done(super);
free(this);
}
static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
//ListItem* item = (ListItem*)Panel_getSelected(super);
if (ch >= 32 && ch < 127 && ch != 61 && ch != 22) {
if (this->cursor < SCREEN_NAME_LEN - 1) {
this->buffer[this->cursor] = 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 0x0a:
case 0x0d:
case KEY_ENTER:
{
ListItem* item = (ListItem*) Panel_getSelected(super);
free(this->saved);
item->value = xStrdup(this->buffer);
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
ScreensPanel_update(super);
break;
}
case 27: // Esc
{
ListItem* item = (ListItem*) Panel_getSelected(super);
item->value = this->saved;
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
break;
}
}
}
return HANDLED;
}
static void startRenaming(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
ListItem* item = (ListItem*) Panel_getSelected(super);
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, CRT_colors[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 = xMalloc(sizeof(ScreenSettings*) * (n + 1));
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;
}
}
static void addNewScreen(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
char* name = "New";
ScreenSettings* ss = Settings_newScreen(this->settings, name, "PID Command");
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 0x0a:
case 0x0d:
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
{
this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
((ListItem*)Panel_getSelected(super))->moving = this->moving;
result = HANDLED;
break;
}
case 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 */
}
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 */
}
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 (oldFocus != newFocus) {
ColumnsPanel_fill(this->columns, newFocus->ss);
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;
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], &(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->screens = xRealloc(this->settings->screens, sizeof(char*) * (size+1));
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;
}

View File

@ -1,51 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_ScreensPanel
#define HEADER_ScreensPanel
/*
htop - ScreensPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "ScreenManager.h"
#include "ColumnsPanel.h"
#include "Settings.h"
#include "ListItem.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

@ -23,44 +23,31 @@ in the source distribution for its full text.
#include "Process.h" #include "Process.h"
#include <stdbool.h> #include <stdbool.h>
typedef struct {
const char* name;
const char* columns;
const char* sortKey;
} ScreenDefaults;
typedef struct { typedef struct {
int len; int len;
char** names; char** names;
int* modes; int* modes;
} MeterColumnSettings; } MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* filename;
MeterColumnSettings meterColumns[2]; MeterColumnSettings columns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme; int colorScheme;
int delay; int delay;
int cpuCount; int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero; bool countCPUsFromZero;
bool detailedCPUTime; bool detailedCPUTime;
bool showCPUUsage;
bool showCPUFrequency;
bool treeView;
bool showProgramPath; bool showProgramPath;
bool hideThreads; bool hideThreads;
bool shadowOtherUsers; bool shadowOtherUsers;
@ -73,6 +60,11 @@ typedef struct Settings_ {
bool updateProcessNames; bool updateProcessNames;
bool accountGuestInCPUMeter; bool accountGuestInCPUMeter;
bool headerMargin; bool headerMargin;
bool enableMouse;
bool vimMode;
#ifdef HAVE_LIBHWLOC
bool topologyAffinity;
#endif
bool changed; bool changed;
} Settings; } Settings;
@ -83,82 +75,25 @@ typedef struct Settings_ {
}*/ }*/
static void writeList(FILE* fd, char** list, int len) {
const char* sep = "";
for (int i = 0; i < len; i++) {
fprintf(fd, "%s%s", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
/*
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) { void Settings_delete(Settings* this) {
free(this->filename); free(this->filename);
for (unsigned int i = 0; i < (sizeof(this->meterColumns)/sizeof(MeterColumnSettings)); i++) { free(this->fields);
String_freeArray(this->meterColumns[i].names); for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) {
free(this->meterColumns[i].modes); String_freeArray(this->columns[i].names);
} free(this->columns[i].modes);
if (this->screens) {
for (unsigned int i = 0; this->screens[i]; i++) {
free(this->screens[i]->name);
free(this->screens[i]->fields);
}
free(this->screens);
} }
free(this); free(this);
} }
static void Settings_readMeters(Settings* this, char* line, int side) { static void Settings_readMeters(Settings* this, char* line, int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
int nIds; int nIds;
char** ids = String_split(trim, ' ', &nIds); char** ids = String_split(trim, ' ', &nIds);
free(trim); free(trim);
this->meterColumns[side].names = ids; this->columns[column].names = ids;
} }
static void Settings_readMeterModes(Settings* this, char* line, int side) { static void Settings_readMeterModes(Settings* this, char* line, int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
int nIds; int nIds;
char** ids = String_split(trim, ' ', &nIds); char** ids = String_split(trim, ' ', &nIds);
@ -167,13 +102,13 @@ static void Settings_readMeterModes(Settings* this, char* line, int side) {
for (int i = 0; ids[i]; i++) { for (int i = 0; ids[i]; i++) {
len++; len++;
} }
this->meterColumns[side].len = len; this->columns[column].len = len;
int* modes = xCalloc(len, sizeof(int)); int* modes = xCalloc(len, sizeof(int));
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
modes[i] = atoi(ids[i]); modes[i] = atoi(ids[i]);
} }
String_freeArray(ids); String_freeArray(ids);
this->meterColumns[side].modes = modes; this->columns[column].modes = modes;
} }
static void Settings_defaultMeters(Settings* this) { static void Settings_defaultMeters(Settings* this) {
@ -182,59 +117,37 @@ static void Settings_defaultMeters(Settings* this) {
sizes[1]++; sizes[1]++;
} }
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
this->meterColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*)); this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->meterColumns[i].modes = xCalloc(sizes[i], sizeof(int)); this->columns[i].modes = xCalloc(sizes[i], sizeof(int));
this->meterColumns[i].len = sizes[i]; this->columns[i].len = sizes[i];
} }
int r = 0; int r = 0;
if (this->cpuCount > 8) { if (this->cpuCount > 8) {
this->meterColumns[0].names[0] = xStrdup("LeftCPUs2"); this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->meterColumns[0].modes[0] = BAR_METERMODE; this->columns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs2"); this->columns[1].names[r] = xStrdup("RightCPUs2");
this->meterColumns[1].modes[r++] = BAR_METERMODE; this->columns[1].modes[r++] = BAR_METERMODE;
} else if (this->cpuCount > 4) { } else if (this->cpuCount > 4) {
this->meterColumns[0].names[0] = xStrdup("LeftCPUs"); this->columns[0].names[0] = xStrdup("LeftCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE; this->columns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs"); this->columns[1].names[r] = xStrdup("RightCPUs");
this->meterColumns[1].modes[r++] = BAR_METERMODE; this->columns[1].modes[r++] = BAR_METERMODE;
} else { } else {
this->meterColumns[0].names[0] = xStrdup("AllCPUs"); this->columns[0].names[0] = xStrdup("AllCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE; this->columns[0].modes[0] = BAR_METERMODE;
} }
this->meterColumns[0].names[1] = xStrdup("Memory"); this->columns[0].names[1] = xStrdup("Memory");
this->meterColumns[0].modes[1] = BAR_METERMODE; this->columns[0].modes[1] = BAR_METERMODE;
this->meterColumns[0].names[2] = xStrdup("Swap"); this->columns[0].names[2] = xStrdup("Swap");
this->meterColumns[0].modes[2] = BAR_METERMODE; this->columns[0].modes[2] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Tasks"); this->columns[1].names[r] = xStrdup("Tasks");
this->meterColumns[1].modes[r++] = TEXT_METERMODE; this->columns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("LoadAverage"); this->columns[1].names[r] = xStrdup("LoadAverage");
this->meterColumns[1].modes[r++] = TEXT_METERMODE; this->columns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Uptime"); this->columns[1].names[r] = xStrdup("Uptime");
this->meterColumns[1].modes[r++] = TEXT_METERMODE; this->columns[1].modes[r++] = TEXT_METERMODE;
}
static const char* toFieldName(int i) {
return Process_fields[i].name;
}
static int toFieldIndex(const char* str) {
if (isdigit(str[0])) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(str) + 1;
if (id < Platform_numberOfFields && toFieldName(id)) {
return id;
}
} else {
for (int p = 1; p < LAST_PROCESSFIELD; p++) {
const char* pName = toFieldName(p);
if (pName && strcmp(pName, str) == 0) {
return p;
}
}
}
return -1;
} }
static void readFields(ProcessField* fields, int* flags, const char* line) { static void readFields(ProcessField* fields, int* flags, const char* line) {
@ -245,10 +158,11 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
int i, j; int i, j;
*flags = 0; *flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) { for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
int idx = toFieldIndex(ids[i]); // This "+1" is for compatibility with the older enum format.
if (idx != -1) { int id = atoi(ids[i]) + 1;
fields[j] = idx; if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
*flags |= Process_fields[idx].flags; fields[j] = id;
*flags |= Process_fields[id].flags;
j++; j++;
} }
} }
@ -256,29 +170,6 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
String_freeArray(ids); String_freeArray(ids);
} }
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line) {
ScreenSettings* ss = xCalloc(sizeof(ScreenSettings), 1);
ss->name = xStrdup(name);
ss->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
ss->flags = 0;
ss->direction = 1;
ss->treeView = 0;
readFields(ss->fields, &(ss->flags), line);
this->screens[this->nScreens] = ss;
this->nScreens++;
this->screens = xRealloc(this->screens, sizeof(ScreenSettings*) * (this->nScreens + 1));
this->screens[this->nScreens] = NULL;
return ss;
}
static void Settings_defaultScreens(Settings* this) {
for (unsigned int i = 0; i < Platform_numberOfDefaultScreens; i++) {
ScreenDefaults* defaults = &Platform_defaultScreens[i];
Settings_newScreen(this, defaults->name, defaults->columns);
this->screens[0]->sortKey = toFieldIndex(defaults->sortKey);
}
}
static bool Settings_read(Settings* this, const char* fileName) { static bool Settings_read(Settings* this, const char* fileName) {
FILE* fd; FILE* fd;
@ -290,9 +181,6 @@ static bool Settings_read(Settings* this, const char* fileName) {
bool didReadMeters = false; bool didReadMeters = false;
bool didReadFields = false; bool didReadFields = false;
ProcessField* legacyFields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
int legacyFlags;
bool legacyFieldsRead = false;
for (;;) { for (;;) {
char* line = String_readLine(fd); char* line = String_readLine(fd);
if (!line) { if (!line) {
@ -306,8 +194,15 @@ static bool Settings_read(Settings* this, const char* fileName) {
continue; continue;
} }
if (String_eq(option[0], "fields")) { if (String_eq(option[0], "fields")) {
readFields(legacyFields, &legacyFlags, option[1]); readFields(this->fields, &(this->flags), option[1]);
legacyFieldsRead = true; didReadFields = true;
} else if (String_eq(option[0], "sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) {
this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
} else if (String_eq(option[0], "hide_threads")) { } else if (String_eq(option[0], "hide_threads")) {
this->hideThreads = atoi(option[1]); this->hideThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) { } else if (String_eq(option[0], "hide_kernel_threads")) {
@ -335,6 +230,10 @@ static bool Settings_read(Settings* this, const char* fileName) {
this->detailedCPUTime = atoi(option[1]); this->detailedCPUTime = atoi(option[1]);
} else if (String_eq(option[0], "cpu_count_from_zero")) { } else if (String_eq(option[0], "cpu_count_from_zero")) {
this->countCPUsFromZero = atoi(option[1]); this->countCPUsFromZero = atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_usage")) {
this->showCPUUsage = atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_frequency")) {
this->showCPUFrequency = atoi(option[1]);
} else if (String_eq(option[0], "update_process_names")) { } else if (String_eq(option[0], "update_process_names")) {
this->updateProcessNames = atoi(option[1]); this->updateProcessNames = atoi(option[1]);
} else if (String_eq(option[0], "account_guest_in_cpu_meter")) { } else if (String_eq(option[0], "account_guest_in_cpu_meter")) {
@ -344,6 +243,8 @@ static bool Settings_read(Settings* this, const char* fileName) {
} else if (String_eq(option[0], "color_scheme")) { } else if (String_eq(option[0], "color_scheme")) {
this->colorScheme = atoi(option[1]); this->colorScheme = atoi(option[1]);
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0; if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0;
} else if (String_eq(option[0], "enable_mouse")) {
this->enableMouse = atoi(option[1]);
} else if (String_eq(option[0], "left_meters")) { } else if (String_eq(option[0], "left_meters")) {
Settings_readMeters(this, option[1], 0); Settings_readMeters(this, option[1], 0);
didReadMeters = true; didReadMeters = true;
@ -356,60 +257,46 @@ static bool Settings_read(Settings* this, const char* fileName) {
} else if (String_eq(option[0], "right_meter_modes")) { } else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1); Settings_readMeterModes(this, option[1], 1);
didReadMeters = true; didReadMeters = true;
} else if (strncmp(option[0], "screen:", 7) == 0) { } else if (String_eq(option[0], "vim_mode")) {
Settings_newScreen(this, option[0] + 7, option[1]); this->vimMode = atoi(option[1]);
} else if (String_eq(option[0], ".tree_view")) { #ifdef HAVE_LIBHWLOC
if (this->nScreens > 0) { } else if (String_eq(option[0], "topology_affinity")) {
this->screens[this->nScreens - 1]->treeView = atoi(option[1]); this->topologyAffinity = !!atoi(option[1]);
} #endif
} else if (String_eq(option[0], ".sort_direction")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->direction = atoi(option[1]);
}
} else if (String_eq(option[0], ".sort_key")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->sortKey = toFieldIndex(option[1]);
}
} }
String_freeArray(option); String_freeArray(option);
} }
fclose(fd); fclose(fd);
if (this->nScreens == 0) {
Settings_defaultScreens(this);
if (legacyFieldsRead) {
free(this->screens[0]->fields);
this->screens[0]->fields = legacyFields;
this->screens[0]->flags = legacyFlags;
}
}
if (!didReadMeters) { if (!didReadMeters) {
Settings_defaultMeters(this); Settings_defaultMeters(this);
} }
return didReadFields; return didReadFields;
} }
static void writeFields(FILE* fd, ProcessField* fields, bool byName) { static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
fprintf(fd, "%s=", name);
const char* sep = ""; const char* sep = "";
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
if (byName) { // This "-1" is for compatibility with the older enum format.
fprintf(fd, "%s%s", sep, toFieldName(fields[i])); fprintf(fd, "%s%d", sep, (int) fields[i]-1);
} else {
// This " - 1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i] - 1);
}
sep = " "; sep = " ";
} }
fprintf(fd, "\n"); fprintf(fd, "\n");
} }
static void writeMeters(Settings* this, FILE* fd, int side) { static void writeMeters(Settings* this, FILE* fd, int column) {
writeList(fd, this->meterColumns[side].names, this->meterColumns[side].len); const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%s", sep, this->columns[column].names[i]);
sep = " ";
}
fprintf(fd, "\n");
} }
static void writeMeterModes(Settings* this, FILE* fd, int side) { static void writeMeterModes(Settings* this, FILE* fd, int column) {
const char* sep = ""; const char* sep = "";
for (int i = 0; i < this->meterColumns[side].len; i++) { for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%d", sep, this->meterColumns[side].modes[i]); fprintf(fd, "%s%d", sep, this->columns[column].modes[i]);
sep = " "; sep = " ";
} }
fprintf(fd, "\n"); fprintf(fd, "\n");
@ -427,7 +314,10 @@ bool Settings_write(Settings* this) {
} }
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n"); fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n"); fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
fprintf(fd, "fields="); writeFields(fd, this->screens[0]->fields, false); writeFields(fd, this->fields, "fields");
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads); fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads); fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads); fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
@ -437,34 +327,25 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName); fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes); fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads); fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero); fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero);
fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage);
fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency);
fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames); fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames);
fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter); fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter);
fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme); fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
fprintf(fd, "enable_mouse=%d\n", (int) this->enableMouse);
fprintf(fd, "delay=%d\n", (int) this->delay); fprintf(fd, "delay=%d\n", (int) this->delay);
fprintf(fd, "left_meters="); writeMeters(this, fd, 0); fprintf(fd, "left_meters="); writeMeters(this, fd, 0);
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0); fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1); fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1); fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
fprintf(fd, "vim_mode=%d\n", (int) this->vimMode);
// Legacy compatibility with older versions of htop #ifdef HAVE_LIBHWLOC
fprintf(fd, "tree_view=%d\n", (int) this->screens[0]->treeView); fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity);
// This "-1" is for compatibility with the older enum format. #endif
fprintf(fd, "sort_key=%d\n", (int) this->screens[0]->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->screens[0]->direction);
if (this->screens && this->screens[0]) {
for (unsigned int i = 0; i < this->nScreens; i++) {
ScreenSettings* ss = this->screens[i];
fprintf(fd, "screen:%s=", ss->name);
writeFields(fd, ss->fields, true);
fprintf(fd, ".tree_view=%d\n", (int) ss->treeView);
fprintf(fd, ".sort_key=%s\n", toFieldName(ss->sortKey));
fprintf(fd, ".sort_direction=%d\n", (int) ss->direction);
}
}
fclose(fd); fclose(fd);
return true; return true;
} }
@ -473,22 +354,37 @@ Settings* Settings_new(int cpuCount) {
Settings* this = xCalloc(1, sizeof(Settings)); Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->direction = 1;
this->hideThreads = false; this->hideThreads = false;
this->shadowOtherUsers = false; this->shadowOtherUsers = false;
this->showThreadNames = false; this->showThreadNames = false;
this->hideKernelThreads = false; this->hideKernelThreads = false;
this->hideUserlandThreads = false; this->hideUserlandThreads = false;
this->treeView = false;
this->highlightBaseName = false; this->highlightBaseName = false;
this->highlightMegabytes = false; this->highlightMegabytes = false;
this->detailedCPUTime = false; this->detailedCPUTime = false;
this->countCPUsFromZero = false; this->countCPUsFromZero = false;
this->showCPUUsage = true;
this->showCPUFrequency = false;
this->updateProcessNames = false; this->updateProcessNames = false;
this->cpuCount = cpuCount; this->cpuCount = cpuCount;
this->showProgramPath = true; this->showProgramPath = true;
this->highlightThreads = true; this->highlightThreads = true;
#ifdef HAVE_LIBHWLOC
this->topologyAffinity = false;
#endif
this->screens = xCalloc(sizeof(ScreenSettings*), 1); this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
this->nScreens = 0; // TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
this->flags = 0;
ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
}
char* legacyDotfile = NULL; char* legacyDotfile = NULL;
char* rcfile = getenv("HTOPRC"); char* rcfile = getenv("HTOPRC");
@ -525,6 +421,7 @@ Settings* Settings_new(int cpuCount) {
CRT_restorePrivileges(); CRT_restorePrivileges();
} }
this->colorScheme = 0; this->colorScheme = 0;
this->enableMouse = true;
this->changed = false; this->changed = false;
this->delay = DEFAULT_DELAY; this->delay = DEFAULT_DELAY;
bool ok = false; bool ok = false;
@ -549,20 +446,15 @@ Settings* Settings_new(int cpuCount) {
} }
if (!ok) { if (!ok) {
Settings_defaultMeters(this); Settings_defaultMeters(this);
Settings_defaultScreens(this);
this->hideKernelThreads = true; this->hideKernelThreads = true;
this->highlightMegabytes = true; this->highlightMegabytes = true;
this->highlightThreads = true; this->highlightThreads = true;
this->headerMargin = true; this->headerMargin = true;
} }
this->ssIndex = 0;
this->ss = this->screens[this->ssIndex];
return this; return this;
} }
void ScreenSettings_invertSortOrder(ScreenSettings* this) { void Settings_invertSortOrder(Settings* this) {
if (this->direction == 1) if (this->direction == 1)
this->direction = -1; this->direction = -1;
else else

View File

@ -14,44 +14,31 @@ in the source distribution for its full text.
#include "Process.h" #include "Process.h"
#include <stdbool.h> #include <stdbool.h>
typedef struct {
const char* name;
const char* columns;
const char* sortKey;
} ScreenDefaults;
typedef struct { typedef struct {
int len; int len;
char** names; char** names;
int* modes; int* modes;
} MeterColumnSettings; } MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* filename;
MeterColumnSettings meterColumns[2]; MeterColumnSettings columns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme; int colorScheme;
int delay; int delay;
int cpuCount; int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero; bool countCPUsFromZero;
bool detailedCPUTime; bool detailedCPUTime;
bool showCPUUsage;
bool showCPUFrequency;
bool treeView;
bool showProgramPath; bool showProgramPath;
bool hideThreads; bool hideThreads;
bool shadowOtherUsers; bool shadowOtherUsers;
@ -64,6 +51,11 @@ typedef struct Settings_ {
bool updateProcessNames; bool updateProcessNames;
bool accountGuestInCPUMeter; bool accountGuestInCPUMeter;
bool headerMargin; bool headerMargin;
bool enableMouse;
bool vimMode;
#ifdef HAVE_LIBHWLOC
bool topologyAffinity;
#endif
bool changed; bool changed;
} Settings; } Settings;
@ -73,18 +65,12 @@ typedef struct Settings_ {
#endif #endif
/* extern void Settings_delete(Settings* this);
*/ extern bool Settings_write(Settings* this);
void Settings_delete(Settings* this); extern Settings* Settings_new(int cpuCount);
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line); extern void Settings_invertSortOrder(Settings* this);
bool Settings_write(Settings* this);
Settings* Settings_new(int cpuCount);
void ScreenSettings_invertSortOrder(ScreenSettings* this);
#endif #endif

View File

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

View File

@ -27,11 +27,12 @@ in the source distribution for its full text.
*/ */
char* String_cat(const char* s1, const char* s2) { char* String_cat(const char* s1, const char* s2) {
size_t l1 = strlen(s1); int l1 = strlen(s1);
size_t l2 = strlen(s2); int l2 = strlen(s2);
char* out = xMalloc(l1 + l2 + 1); char* out = xMalloc(l1 + l2 + 1);
strncpy(out, s1, l1); memcpy(out, s1, l1);
strncpy(out + l1, s2, l2 + 1); memcpy(out+l1, s2, l2+1);
out[l1 + l2] = '\0';
return out; return out;
} }
@ -39,7 +40,7 @@ char* String_trim(const char* in) {
while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') { while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') {
in++; in++;
} }
size_t len = strlen(in); int len = strlen(in);
while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) { while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) {
len--; len--;
} }
@ -80,10 +81,7 @@ char** String_split(const char* s, char sep, int* n) {
s += size + 1; s += size + 1;
} }
if (s[0] != '\0') { if (s[0] != '\0') {
size_t size = strlen(s); out[ctr] = xStrdup(s);
char* token = xMalloc(size + 1);
strncpy(token, s, size + 1);
out[ctr] = token;
ctr++; ctr++;
} }
out = xRealloc(out, sizeof(char*) * (ctr + 1)); out = xRealloc(out, sizeof(char*) * (ctr + 1));

View File

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

View File

@ -152,4 +152,3 @@ STrace screen:
LSOF screen: LSOF screen:
TODO TODO

View File

@ -15,7 +15,7 @@ in the source distribution for its full text.
}*/ }*/
int TasksMeter_attributes[] = { int TasksMeter_attributes[] = {
CPU_KERNEL, PROCESS_THREAD, PROCESS, TASKS_RUNNING CPU_SYSTEM, PROCESS_THREAD, PROCESS, TASKS_RUNNING
}; };
static void TasksMeter_updateValues(Meter* this, char* buffer, int len) { static void TasksMeter_updateValues(Meter* this, char* buffer, int len) {

View File

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

View File

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

View File

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

View File

@ -335,3 +335,15 @@ inline int Vector_indexOf(Vector* this, void* search_, Object_Compare compare) {
} }
return -1; return -1;
} }
void Vector_splice(Vector* this, Vector* from) {
assert(Vector_isConsistent(this));
assert(Vector_isConsistent(from));
assert(!(this->owner && from->owner));
int olditmes = this->items;
this->items += from->items;
Vector_checkArraySize(this);
for (int j = 0; j < from->items; j++)
this->array[olditmes + j] = from->array[j];
}

View File

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

View File

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

View File

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

View File

@ -2,10 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65) AC_PREREQ(2.65)
AC_INIT([htop],[3.0.0beta4],[hisham@gobolinux.org]) AC_INIT([htop],[3.0.0],[htop@groups.io])
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}"
year=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u "+%Y")
AC_CONFIG_SRCDIR([htop.c]) AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_AUX_DIR([.]) AC_CONFIG_AUX_DIR([.])
@ -69,7 +66,7 @@ dnl glibc 2.25 deprecates 'major' and 'minor' in <sys/types.h> and requires to
dnl include <sys/sysmacros.h>. However the logic in AC_HEADER_MAJOR has not yet dnl include <sys/sysmacros.h>. However the logic in AC_HEADER_MAJOR has not yet
dnl been updated in Autoconf 2.69, so use a workaround: dnl been updated in Autoconf 2.69, so use a workaround:
m4_version_prereq([2.70], [], m4_version_prereq([2.70], [],
[if test "x$ac_cv_header_sys_mkdev_h" = xno; then [if test "x$ac_cv_header_sys_mkdev_h" != xyes; then
AC_CHECK_HEADER(sys/sysmacros.h, [AC_DEFINE(MAJOR_IN_SYSMACROS, 1, AC_CHECK_HEADER(sys/sysmacros.h, [AC_DEFINE(MAJOR_IN_SYSMACROS, 1,
[Define to 1 if `major', `minor', and `makedev' are declared in <sys/sysmacros.h>.])]) [Define to 1 if `major', `minor', and `makedev' are declared in <sys/sysmacros.h>.])])
fi]) fi])
@ -281,63 +278,29 @@ then
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.]) AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
fi fi
AC_ARG_ENABLE(perfcounters, [AS_HELP_STRING([--enable-perfcounters], [enable hardware performance counters])],, enable_perfcounters="yes") AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no")
if test "x$enable_perfcounters" = "xyes" -a "$my_htop_platform" = "linux"
then
AC_DEFINE(HAVE_PERFCOUNTERS, 1, [Define if hardware performance counter support should be enabled.])
AC_CHECK_HEADERS([linux/perf_counter.h], [have_perf_counter=yes],
[have_perf_counter=no])
AC_CHECK_HEADERS([linux/perf_event.h], [have_perf_event=yes],
[have_perf_event=no])
if test "x${have_perf_counter}" = xno -a "x${have_perf_event}" = xno; then
os=`uname -s -r`
AC_MSG_FAILURE([
------------------------------------------------------------
Could not locate linux/perf_count.h or linux/perf_event.h.
Are performance counters supported on this machine?
Linux 2.6.31+ is required.
uname reports: ${os}
------------------------------------------------------------])
fi
# Check for hardware architecture
no_target=yes
AC_MSG_CHECKING([hardware])
hw=`uname -m`
case $hw in
x86_64 | i386 | i686 ) :
AC_MSG_RESULT([x86])
AC_DEFINE([TARGET_X86], [1], [Define to 1 if the target is x86.])
no_target=no
;;
unknown ) :
AC_MSG_RESULT([unknown])
AC_MSG_WARN([Could not detect architecture])
;;
* ) :
AC_MSG_RESULT([$hw])
;;
esac
if test x$no_target = xyes; then
AC_DEFINE([NOTARGET], [1], [Define to 1 when no specific target is supported.])
fi
fi
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable linux delay accounting])],, enable_delayacct="no")
if test "x$enable_delayacct" = xyes if test "x$enable_delayacct" = xyes
then then
PKG_PROG_PKG_CONFIG() m4_ifdef([PKG_PROG_PKG_CONFIG], [
PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"]) PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"]) PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"])
CFLAGS="$CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS" PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"])
LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS" CFLAGS="$CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS"
AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.]) LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS"
AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.])
], [
pkg_m4_absent=1
m4_warning([configure is generated without pkg.m4. 'make dist' target will be disabled.])
AC_MSG_ERROR([htop on Linux requires pkg-config for checking delayacct requirements. Please install pkg-config and run ./autogen.sh to rebuild the configure script.])
])
fi fi
AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], [Treat warnings as errors (default: warnings are not errors)])], [enable_werror="$enableval"], [enable_werror=no])
AS_IF([test "x$enable_werror" = "xyes"], [AM_CFLAGS="$AM_CFLAGS -Werror"])
AC_SUBST([AM_CFLAGS])
AC_CHECK_PROGS(PYTHON, [python python3 python2])
AC_SUBST(PYTHON)
# Bail out on errors. # Bail out on errors.
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
@ -348,7 +311,7 @@ if test ! -z "$missing_headers"; then
AC_MSG_ERROR([missing headers: $missing_headers]) AC_MSG_ERROR([missing headers: $missing_headers])
fi fi
AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-$year Hisham Muhammad", [Copyright message.]) AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2018 Hisham Muhammad", [Copyright message.])
# We're done, let's go! # We're done, let's go!
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
@ -361,6 +324,7 @@ AM_CONDITIONAL([HTOP_SOLARIS], [test "$my_htop_platform" = solaris])
AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported]) AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported])
AC_SUBST(my_htop_platform) AC_SUBST(my_htop_platform)
AC_CONFIG_FILES([Makefile htop.1]) AC_CONFIG_FILES([Makefile htop.1])
AC_CONFIG_FILES([scripts/MakeHeader.py], [chmod +x scripts/MakeHeader.py])
AC_OUTPUT AC_OUTPUT
if test "$my_htop_platform" = "unsupported" if test "$my_htop_platform" = "unsupported"

View File

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

View File

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

View File

@ -9,6 +9,8 @@ in the source distribution for its full text.
#include "DarwinProcess.h" #include "DarwinProcess.h"
#include "DarwinProcessList.h" #include "DarwinProcessList.h"
#include "CRT.h" #include "CRT.h"
#include "zfs/ZfsArcStats.h"
#include "zfs/openzfs_sysctl.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -21,8 +23,40 @@ in the source distribution for its full text.
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <stdbool.h> #include <stdbool.h>
struct kern {
short int version[3];
};
void GetKernelVersion(struct kern *k) {
static short int version_[3] = {0};
if (!version_[0]) {
// just in case it fails someday
version_[0] = version_[1] = version_[2] = -1;
char str[256] = {0};
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
if (ret == 0) sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
}
memcpy(k->version, version_, sizeof(version_));
}
/* compare the given os version with the one installed returns:
0 if equals the installed version
positive value if less than the installed version
negative value if more than the installed version
*/
int CompareKernelVersion(short int major, short int minor, short int component) {
struct kern k;
GetKernelVersion(&k);
if ( k.version[0] != major) return k.version[0] - major;
if ( k.version[1] != minor) return k.version[1] - minor;
if ( k.version[2] != component) return k.version[2] - component;
return 0;
}
/*{ /*{
#include "ProcessList.h" #include "ProcessList.h"
#include "zfs/ZfsArcStats.h"
#include <mach/mach_host.h> #include <mach/mach_host.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
@ -36,58 +70,27 @@ typedef struct DarwinProcessList_ {
uint64_t kernel_threads; uint64_t kernel_threads;
uint64_t user_threads; uint64_t user_threads;
uint64_t global_diff; uint64_t global_diff;
ZfsArcStats zfs;
} DarwinProcessList; } DarwinProcessList;
}*/ }*/
typedef struct kern {
short int version[3];
} kern_;
static void getKernelVersion(struct kern *k) {
static short int version_[3] = {0};
if (!version_[0]) {
// just in case it fails someday
version_[0] = version_[1] = version_[2] = -1;
char str[256] = {0};
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
if (ret == 0) sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
}
memcpy(k->version, version_, sizeof(version_));
}
static int compareKernelVersion(short int major, short int minor, short int component) {
/*
compare the given os version with the one installed returns:
0 if equals the installed version
positive value if less than the installed version
negative value if more than the installed version
*/
struct kern k;
getKernelVersion(&k);
if ( k.version[0] != major) return k.version[0] - major;
if ( k.version[1] != minor) return k.version[1] - minor;
if ( k.version[2] != component) return k.version[2] - component;
return 0;
}
void ProcessList_getHostInfo(host_basic_info_data_t *p) { void ProcessList_getHostInfo(host_basic_info_data_t *p) {
mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT; mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT;
if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) { if(0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
CRT_fatalError("Unable to retrieve host info\n"); CRT_fatalError("Unable to retrieve host info\n");
} }
} }
void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p) { void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p) {
if (NULL != p && NULL != *p) { if(NULL != p && NULL != *p) {
if (0 != munmap(*p, vm_page_size)) { if(0 != munmap(*p, vm_page_size)) {
CRT_fatalError("Unable to free old CPU load information\n"); CRT_fatalError("Unable to free old CPU load information\n");
} }
*p = NULL;
} }
*p = NULL;
} }
unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) { unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
@ -95,7 +98,7 @@ unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
unsigned cpu_count; unsigned cpu_count;
// TODO Improving the accuracy of the load counts woule help a lot. // TODO Improving the accuracy of the load counts woule help a lot.
if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) { if(0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) {
CRT_fatalError("Unable to retrieve CPU info\n"); CRT_fatalError("Unable to retrieve CPU info\n");
} }
@ -103,10 +106,10 @@ unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
} }
void ProcessList_getVMStats(vm_statistics_t p) { void ProcessList_getVMStats(vm_statistics_t p) {
mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT; mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0)
CRT_fatalError("Unable to retrieve VM statistics\n"); CRT_fatalError("Unable to retrieve VM statistics\n");
} }
struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) { struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) {
@ -133,8 +136,8 @@ struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) {
return processes; return processes;
} }
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
size_t len;
DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList));
ProcessList_init(&this->super, Class(Process), usersTable, pidWhiteList, userId); ProcessList_init(&this->super, Class(Process), usersTable, pidWhiteList, userId);
@ -147,6 +150,10 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
/* Initialize the VM statistics */ /* Initialize the VM statistics */
ProcessList_getVMStats(&this->vm_stats); ProcessList_getVMStats(&this->vm_stats);
/* Initialize the ZFS kstats, if zfs.kext loaded */
openzfs_sysctl_init(&this->zfs);
openzfs_sysctl_updateArcStats(&this->zfs);
this->super.kernelThreads = 0; this->super.kernelThreads = 0;
this->super.userlandThreads = 0; this->super.userlandThreads = 0;
this->super.totalTasks = 0; this->super.totalTasks = 0;
@ -161,65 +168,66 @@ void ProcessList_delete(ProcessList* this) {
} }
void ProcessList_goThroughEntries(ProcessList* super) { void ProcessList_goThroughEntries(ProcessList* super) {
DarwinProcessList *dpl = (DarwinProcessList *)super; DarwinProcessList *dpl = (DarwinProcessList *)super;
bool preExisting = true; bool preExisting = true;
struct kinfo_proc *ps; struct kinfo_proc *ps;
size_t count; size_t count;
DarwinProcess *proc; DarwinProcess *proc;
struct timeval tv; struct timeval tv;
gettimeofday(&tv, NULL); /* Start processing time */ gettimeofday(&tv, NULL); /* Start processing time */
/* Update the global data (CPU times and VM stats) */ /* Update the global data (CPU times and VM stats) */
ProcessList_freeCPULoadInfo(&dpl->prev_load); ProcessList_freeCPULoadInfo(&dpl->prev_load);
dpl->prev_load = dpl->curr_load; dpl->prev_load = dpl->curr_load;
ProcessList_allocateCPULoadInfo(&dpl->curr_load); ProcessList_allocateCPULoadInfo(&dpl->curr_load);
ProcessList_getVMStats(&dpl->vm_stats); ProcessList_getVMStats(&dpl->vm_stats);
openzfs_sysctl_updateArcStats(&dpl->zfs);
/* Get the time difference */ /* Get the time difference */
dpl->global_diff = 0; dpl->global_diff = 0;
for(int i = 0; i < dpl->super.cpuCount; ++i) { for(int i = 0; i < dpl->super.cpuCount; ++i) {
for(size_t j = 0; j < CPU_STATE_MAX; ++j) { for(size_t j = 0; j < CPU_STATE_MAX; ++j) {
dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j]; dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j];
} }
} }
/* Clear the thread counts */ /* Clear the thread counts */
super->kernelThreads = 0; super->kernelThreads = 0;
super->userlandThreads = 0; super->userlandThreads = 0;
super->totalTasks = 0; super->totalTasks = 0;
super->runningTasks = 0; super->runningTasks = 0;
/* We use kinfo_procs for initial data since : /* We use kinfo_procs for initial data since :
* *
* 1) They always succeed. * 1) They always succeed.
* 2) The contain the basic information. * 2) The contain the basic information.
* *
* We attempt to fill-in additional information with libproc. * We attempt to fill-in additional information with libproc.
*/ */
ps = ProcessList_getKInfoProcs(&count); ps = ProcessList_getKInfoProcs(&count);
for(size_t i = 0; i < count; ++i) { for(size_t i = 0; i < count; ++i) {
proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new); proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new);
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting); DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl); DarwinProcess_setFromLibprocPidinfo(proc, dpl);
// Disabled for High Sierra due to bug in macOS High Sierra // Disabled for High Sierra due to bug in macOS High Sierra
bool isScanThreadSupported = ! ( compareKernelVersion(17, 0, 0) >= 0 && compareKernelVersion(17, 5, 0) < 0); bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0);
if (isScanThreadSupported){ if (isScanThreadSupported){
DarwinProcess_scanThreads(proc); DarwinProcess_scanThreads(proc);
} }
super->totalTasks += 1; super->totalTasks += 1;
if (!preExisting) { if(!preExisting) {
proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid); proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid);
ProcessList_add(super, &proc->super); ProcessList_add(super, &proc->super);
} }
} }
free(ps); free(ps);
} }

View File

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

View File

@ -15,6 +15,8 @@ in the source distribution for its full text.
#include "ClockMeter.h" #include "ClockMeter.h"
#include "HostnameMeter.h" #include "HostnameMeter.h"
#include "UptimeMeter.h" #include "UptimeMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#include "DarwinProcessList.h" #include "DarwinProcessList.h"
#include <stdlib.h> #include <stdlib.h>
@ -25,26 +27,13 @@ in the source distribution for its full text.
#include "CPUMeter.h" #include "CPUMeter.h"
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include "DarwinProcess.h" #include "DarwinProcess.h"
typedef enum DarwinProcessFields {
LAST_PROCESSFIELD = 100,
} DarwinProcessField;
}*/ }*/
#ifndef CLAMP #ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
ScreenDefaults Platform_defaultScreens[] = { ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 }, { .name = " 0 Cancel", .number = 0 },
@ -103,14 +92,14 @@ ProcessFieldData Process_fields[] = {
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, }, [TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, [100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
}; };
MeterClass* Platform_meterTypes[] = { MeterClass* Platform_meterTypes[] = {
@ -130,6 +119,8 @@ MeterClass* Platform_meterTypes[] = {
&RightCPUsMeter_class, &RightCPUsMeter_class,
&LeftCPUs2Meter_class, &LeftCPUs2Meter_class,
&RightCPUs2Meter_class, &RightCPUs2Meter_class,
&ZfsArcMeter_class,
&ZfsCompressedArcMeter_class,
&BlankMeter_class, &BlankMeter_class,
NULL NULL
}; };
@ -230,6 +221,8 @@ double Platform_setCPUValues(Meter* mtr, int cpu) {
/* Convert to percent and return */ /* Convert to percent and return */
total = mtr->values[CPU_METER_NICE] + mtr->values[CPU_METER_NORMAL] + mtr->values[CPU_METER_KERNEL]; total = mtr->values[CPU_METER_NICE] + mtr->values[CPU_METER_NORMAL] + mtr->values[CPU_METER_KERNEL];
mtr->values[CPU_METER_FREQUENCY] = -1;
return CLAMP(total, 0.0, 100.0); return CLAMP(total, 0.0, 100.0);
} }
@ -254,6 +247,18 @@ void Platform_setSwapValues(Meter* mtr) {
mtr->values[0] = swapused.xsu_used / 1024; mtr->values[0] = swapused.xsu_used / 1024;
} }
void Platform_setZfsArcValues(Meter* this) {
DarwinProcessList* dpl = (DarwinProcessList*) this->pl;
ZfsArcMeter_readStats(this, &(dpl->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
DarwinProcessList* dpl = (DarwinProcessList*) this->pl;
ZfsCompressedArcMeter_readStats(this, &(dpl->zfs));
}
char* Platform_getProcessEnv(pid_t pid) { char* Platform_getProcessEnv(pid_t pid) {
char* env = NULL; char* env = NULL;

View File

@ -16,18 +16,11 @@ in the source distribution for its full text.
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include "DarwinProcess.h" #include "DarwinProcess.h"
typedef enum DarwinProcessFields {
LAST_PROCESSFIELD = 100,
} DarwinProcessField;
#ifndef CLAMP #ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
extern ScreenDefaults Platform_defaultScreens[]; extern ProcessField Platform_defaultFields[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern const SignalItem Platform_signals[]; extern const SignalItem Platform_signals[];
@ -55,6 +48,10 @@ void Platform_setMemoryValues(Meter* mtr);
void Platform_setSwapValues(Meter* mtr); void Platform_setSwapValues(Meter* mtr);
void Platform_setZfsArcValues(Meter* this);
void Platform_setZfsCompressedArcValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid); char* Platform_getProcessEnv(pid_t pid);
#endif #endif

View File

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

View File

@ -74,7 +74,7 @@ ProcessFieldData Process_fields[] = {
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
@ -121,7 +121,7 @@ void DragonFlyBSDProcess_writeField(Process* this, RichString* str, ProcessField
case PID: xSnprintf(buffer, n, Process_pidFormat, (fp->kernel ? -1 : this->pid)); break; case PID: xSnprintf(buffer, n, Process_pidFormat, (fp->kernel ? -1 : this->pid)); break;
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break; case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
case JAIL:{ case JAIL:{
xSnprintf(buffer, n, "%-11s ", fp->jname); break; xSnprintf(buffer, n, "%-11s ", fp->jname);
if (buffer[11] != '\0') { if (buffer[11] != '\0') {
buffer[11] = ' '; buffer[11] = ' ';
buffer[12] = '\0'; buffer[12] = '\0';

View File

@ -40,15 +40,7 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
ScreenDefaults Platform_defaultScreens[] = { ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
int Platform_numberOfFields = LAST_PROCESSFIELD; int Platform_numberOfFields = LAST_PROCESSFIELD;
@ -187,6 +179,9 @@ double Platform_setCPUValues(Meter* this, int cpu) {
percent = CLAMP(percent, 0.0, 100.0); percent = CLAMP(percent, 0.0, 100.0);
if (isnan(percent)) percent = 0.0; if (isnan(percent)) percent = 0.0;
v[CPU_METER_FREQUENCY] = -1;
return percent; return percent;
} }

View File

@ -21,9 +21,7 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
extern ScreenDefaults Platform_defaultScreens[]; extern ProcessField Platform_defaultFields[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern int Platform_numberOfFields; extern int Platform_numberOfFields;

View File

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

View File

@ -73,7 +73,7 @@ ProcessFieldData Process_fields[] = {
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
@ -119,7 +119,7 @@ void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField fiel
// add FreeBSD-specific fields here // add FreeBSD-specific fields here
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break; case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
case JAIL:{ case JAIL:{
xSnprintf(buffer, n, "%-11s ", fp->jname); break; xSnprintf(buffer, n, "%-11s ", fp->jname);
if (buffer[11] != '\0') { if (buffer[11] != '\0') {
buffer[11] = ' '; buffer[11] = ' ';
buffer[12] = '\0'; buffer[12] = '\0';

View File

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

View File

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

View File

@ -15,6 +15,8 @@ in the source distribution for its full text.
#include "UptimeMeter.h" #include "UptimeMeter.h"
#include "ClockMeter.h" #include "ClockMeter.h"
#include "HostnameMeter.h" #include "HostnameMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#include "FreeBSDProcess.h" #include "FreeBSDProcess.h"
#include "FreeBSDProcessList.h" #include "FreeBSDProcessList.h"
@ -39,16 +41,6 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD; int Platform_numberOfFields = LAST_PROCESSFIELD;
@ -114,6 +106,8 @@ MeterClass* Platform_meterTypes[] = {
&LeftCPUs2Meter_class, &LeftCPUs2Meter_class,
&RightCPUs2Meter_class, &RightCPUs2Meter_class,
&BlankMeter_class, &BlankMeter_class,
&ZfsArcMeter_class,
&ZfsCompressedArcMeter_class,
NULL NULL
}; };
@ -188,6 +182,9 @@ double Platform_setCPUValues(Meter* this, int cpu) {
percent = CLAMP(percent, 0.0, 100.0); percent = CLAMP(percent, 0.0, 100.0);
if (isnan(percent)) percent = 0.0; if (isnan(percent)) percent = 0.0;
v[CPU_METER_FREQUENCY] = -1;
return percent; return percent;
} }
@ -207,6 +204,18 @@ void Platform_setSwapValues(Meter* this) {
this->values[0] = pl->usedSwap; this->values[0] = pl->usedSwap;
} }
void Platform_setZfsArcValues(Meter* this) {
FreeBSDProcessList* fpl = (FreeBSDProcessList*) this->pl;
ZfsArcMeter_readStats(this, &(fpl->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
FreeBSDProcessList* fpl = (FreeBSDProcessList*) this->pl;
ZfsCompressedArcMeter_readStats(this, &(fpl->zfs));
}
void Platform_setTasksValues(Meter* this) { void Platform_setTasksValues(Meter* this) {
// TODO // TODO
} }

View File

@ -20,10 +20,6 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern ProcessField Platform_defaultFields[]; extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields; extern int Platform_numberOfFields;
@ -48,6 +44,10 @@ void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this); void Platform_setSwapValues(Meter* this);
void Platform_setZfsArcValues(Meter* this);
void Platform_setZfsCompressedArcValues(Meter* this);
void Platform_setTasksValues(Meter* this); void Platform_setTasksValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid); char* Platform_getProcessEnv(pid_t pid);

101
htop.1.in
View File

@ -1,17 +1,22 @@
.TH "HTOP" "1" "2015" "@PACKAGE_STRING@" "Utils" .TH "HTOP" "1" "2020" "@PACKAGE_STRING@" "User Commands"
.SH "NAME" .SH "NAME"
htop \- interactive process viewer htop \- interactive process viewer
.SH "SYNOPSIS" .SH "SYNOPSIS"
.LP .LP
.B htop [\fI\-dChusv\fR] .B htop
.RB [ \-dChpustv ]
.SH "DESCRIPTION" .SH "DESCRIPTION"
.LP .LP
Htop is a free (GPL) ncurses-based process viewer for Linux. .B htop
is a cross-platform ncurses-based process viewer.
.LP .LP
It is similar to top, but allows you to scroll vertically and horizontally, It is similar to
so you can see all the processes running on the system, along with their full .BR top ,
command lines, as well as viewing them as a process tree, selecting multiple but allows you to scroll vertically and horizontally, and interact using
processes and acting on them all at once. a pointing device (mouse).
You can observe all processes running on the system, along with their
command line arguments, as well as view them in a tree format, select
multiple processes and acting on them all at once.
.LP .LP
Tasks related to processes (killing, renicing) can be done without Tasks related to processes (killing, renicing) can be done without
entering their PIDs. entering their PIDs.
@ -22,10 +27,14 @@ Mandatory arguments to long options are mandatory for short options too.
.LP .LP
.TP .TP
\fB\-d \-\-delay=DELAY\fR \fB\-d \-\-delay=DELAY\fR
Delay between updates, in tenths of seconds Delay between updates, in tenths of seconds. If the delay value is
less than 1 it is increased to 1, i.e. 1/10 second. If the delay value
is greater than 100, it is decreased to 100, i.e. 10 seconds.
.TP .TP
\fB\-C \-\-no-color \-\-no-colour\fR \fB\-C \-\-no-color \-\-no-colour\fR
Start htop in monochrome mode Start
.B htop
in monochrome mode
.TP .TP
\fB\-h \-\-help \fB\-h \-\-help
Display a help message and exit Display a help message and exit
@ -41,11 +50,13 @@ Show only the processes of a given user
.TP .TP
\fB\-v \-\-version \fB\-v \-\-version
Output version information and exit Output version information and exit
.PP .TP
.br \fB\-t \-\-tree
Show processes in tree view
.SH "INTERACTIVE COMMANDS" .SH "INTERACTIVE COMMANDS"
.LP .LP
The following commands are supported while in htop: The following commands are supported while in
.BR htop :
.LP .LP
.TP 5 .TP 5
.B Up, Alt-k .B Up, Alt-k
@ -82,8 +93,13 @@ Tag or untag a process. Commands that can operate on multiple processes,
like "kill", will then apply over the list of tagged processes, instead like "kill", will then apply over the list of tagged processes, instead
of the currently highlighted one. of the currently highlighted one.
.TP .TP
.B c
Tag the current process and its children. Commands that can operate on multiple
processes, like "kill", will then apply over the list of tagged processes,
instead of the currently highlighted one.
.TP
.B U .B U
Untag all processes (remove all tags added with the Space key). Untag all processes (remove all tags added with the Space or c keys).
.TP .TP
.B s .B s
Trace process system calls: if strace(1) is installed, pressing this key Trace process system calls: if strace(1) is installed, pressing this key
@ -186,14 +202,17 @@ Refresh: redraw screen and recalculate values.
.B Numbers .B Numbers
PID search: type in process ID and the selection highlight will be moved to it. PID search: type in process ID and the selection highlight will be moved to it.
.PD .PD
.SH "COLUMNS" .SH "COLUMNS"
.LP .LP
The following columns can display data about each process. A value of '\-' in The following columns can display data about each process. A value of '\-' in
all the rows indicates that a column is unsupported on your system, or all the rows indicates that a column is unsupported on your system, or
currently unimplemented in htop. The names below are the ones used in the currently unimplemented in
.BR htop .
The names below are the ones used in the
"Available Columns" section of the setup screen. If a different name is "Available Columns" section of the setup screen. If a different name is
shown in htop's main screen, it is shown below in parenthesis. shown in
.BR htop 's
main screen, it is shown below in parenthesis.
.LP .LP
.TP 5 .TP 5
.B Command .B Command
@ -381,36 +400,44 @@ The percentage of time spent swapping in pages. Requires CAP_NET_ADMIN.
.TP .TP
.B All other flags .B All other flags
Currently unsupported (always displays '-'). Currently unsupported (always displays '-').
.SH "CONFIG FILE" .SH "CONFIG FILE"
.LP .LP
By default htop reads its configuration from the XDG-compliant path By default
~/.config/htop/htoprc -- the configuration file is overwritten by htop's .B htop
in-program Setup configuration, so it should not be hand-edited. If no reads its configuration from the XDG-compliant path
user configuration exists htop tries to read the system-wide configuration .IR ~/.config/htop/htoprc .
from @sysconfdir@/htoprc and as a last resort, falls back to its The configuration file is overwritten by
hard coded defaults. .BR htop 's
in-program Setup configuration, so it should not be hand-edited.
If no user configuration exists
.B htop
tries to read the system-wide configuration from
.I @sysconfdir@/htoprc
and as a last resort, falls back to its hard coded defaults.
.LP .LP
You may override the location of the configuration file using the $HTOPRC You may override the location of the configuration file using the $HTOPRC
environment variable (so you can have multiple configurations for different environment variable (so you can have multiple configurations for different
machines that share the same home directory, for example). machines that share the same home directory, for example).
.SH "MEMORY SIZES" .SH "MEMORY SIZES"
.LP .LP
Memory sizes in htop are displayed as they are in tools from the GNU Coreutils Memory sizes in
(when ran with the --human-readable option). This means that sizes are printed .B htop
in powers of 1024. (e.g., 1023M = 1072693248 Bytes) are displayed in a human-readable form.
Sizes are printed in powers of 1024. (e.g., 1023M = 1072693248 Bytes)
.LP .LP
The decision to use this convention was made in order to conserve screen space The decision to use this convention was made in order to conserve screen
and make memory size representations consistent throughout htop. space and make memory size representations consistent throughout
.BR htop .
.SH "SEE ALSO" .SH "SEE ALSO"
proc(5), top(1), free(1), ps(1), uptime(1), limits.conf(5) .BR proc (5),
.BR top (1),
.BR free (1),
.BR ps (1),
.BR uptime (1)
and
.BR limits.conf (5).
.SH "AUTHORS" .SH "AUTHORS"
.LP .LP
htop is developed by Hisham Muhammad <hisham@gobolinux.org>. .B htop
.LP was originally developed by Hisham Muhammad.
This man page was written by Bartosz Fenski <fenio@o2.pl> for the Debian Nowadays it is maintained by the community at <htop@groups.io>.
GNU/Linux distribution (but it may be used by others). It was updated by Hisham
Muhammad, and later by Vincent Launchbury, who wrote the 'Columns' section.

39
htop.c
View File

@ -39,11 +39,12 @@ static void printHelpFlag() {
fputs("htop " VERSION " - " COPYRIGHT "\n" fputs("htop " VERSION " - " COPYRIGHT "\n"
"Released under the GNU GPL.\n\n" "Released under the GNU GPL.\n\n"
"-C --no-color Use a monochrome color scheme\n" "-C --no-color Use a monochrome color scheme\n"
"-m --no-mouse Disable the mouse\n"
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n" "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
"-h --help Print this help screen\n" "-h --help Print this help screen\n"
"-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n" "-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n"
"-t --tree Show the tree view by default\n" "-t --tree Show the tree view by default\n"
"-u --user=USERNAME Show only processes of a given user\n" "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
"-p --pid=PID,[,PID,PID...] Show only the given PIDs\n" "-p --pid=PID,[,PID,PID...] Show only the given PIDs\n"
"-v --version Print version info\n" "-v --version Print version info\n"
"\n" "\n"
@ -62,6 +63,7 @@ typedef struct CommandLineSettings_ {
int sortKey; int sortKey;
int delay; int delay;
bool useColors; bool useColors;
bool enableMouse;
bool treeView; bool treeView;
} CommandLineSettings; } CommandLineSettings;
@ -73,6 +75,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.sortKey = 0, .sortKey = 0,
.delay = -1, .delay = -1,
.useColors = true, .useColors = true,
.enableMouse = true,
.treeView = false, .treeView = false,
}; };
@ -82,18 +85,18 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
{"version", no_argument, 0, 'v'}, {"version", no_argument, 0, 'v'},
{"delay", required_argument, 0, 'd'}, {"delay", required_argument, 0, 'd'},
{"sort-key", required_argument, 0, 's'}, {"sort-key", required_argument, 0, 's'},
{"user", required_argument, 0, 'u'}, {"user", optional_argument, 0, 'u'},
{"no-color", no_argument, 0, 'C'}, {"no-color", no_argument, 0, 'C'},
{"no-colour",no_argument, 0, 'C'}, {"no-colour",no_argument, 0, 'C'},
{"no-mouse", no_argument, 0, 'm'},
{"tree", no_argument, 0, 't'}, {"tree", no_argument, 0, 't'},
{"pid", required_argument, 0, 'p'}, {"pid", required_argument, 0, 'p'},
{"io", no_argument, 0, 'i'},
{0,0,0,0} {0,0,0,0}
}; };
int opt, opti=0; int opt, opti=0;
/* Parse arguments */ /* Parse arguments */
while ((opt = getopt_long(argc, argv, "hvCst::d:u:p:i", long_opts, &opti))) { while ((opt = getopt_long(argc, argv, "hvmCs:td:u::p:", long_opts, &opti))) {
if (opt == EOF) break; if (opt == EOF) break;
switch (opt) { switch (opt) {
case 'h': case 'h':
@ -113,6 +116,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
flags.sortKey = ColumnsPanel_fieldNameToIndex(optarg); flags.sortKey = ColumnsPanel_fieldNameToIndex(optarg);
if (flags.sortKey == -1) { if (flags.sortKey == -1) {
fprintf(stderr, "Error: invalid column \"%s\".\n", optarg); fprintf(stderr, "Error: invalid column \"%s\".\n", optarg);
exit(1);
} }
break; break;
case 'd': case 'd':
@ -121,16 +125,31 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
if (flags.delay > 100) flags.delay = 100; if (flags.delay > 100) flags.delay = 100;
} else { } else {
fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg); fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg);
exit(1);
} }
break; break;
case 'u': case 'u':
if (!optarg && optind < argc && argv[optind] != NULL &&
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
optarg = argv[optind++];
}
if (!optarg) {
optarg = getenv("USER");
flags.userId = geteuid();
}
if (!Action_setUserOnly(optarg, &(flags.userId))) { if (!Action_setUserOnly(optarg, &(flags.userId))) {
fprintf(stderr, "Error: invalid user \"%s\".\n", optarg); fprintf(stderr, "Error: invalid user \"%s\".\n", optarg);
exit(1);
} }
break; break;
case 'C': case 'C':
flags.useColors = false; flags.useColors = false;
break; break;
case 'm':
flags.enableMouse = false;
break;
case 't': case 't':
flags.treeView = true; flags.treeView = true;
break; break;
@ -204,20 +223,22 @@ int main(int argc, char** argv) {
settings->delay = flags.delay; settings->delay = flags.delay;
if (!flags.useColors) if (!flags.useColors)
settings->colorScheme = COLORSCHEME_MONOCHROME; settings->colorScheme = COLORSCHEME_MONOCHROME;
if (!flags.enableMouse)
settings->enableMouse = false;
if (flags.treeView) if (flags.treeView)
settings->screens[0]->treeView = true; settings->treeView = true;
CRT_init(settings->delay, settings->colorScheme); CRT_init(settings->delay, settings->colorScheme);
MainPanel* panel = MainPanel_new(); MainPanel* panel = MainPanel_new();
ProcessList_setPanel(pl, (Panel*) panel); ProcessList_setPanel(pl, (Panel*) panel);
MainPanel_updateTreeFunctions(panel, settings->screens[0]->treeView); MainPanel_updateTreeFunctions(panel, settings->treeView);
if (flags.sortKey > 0) { if (flags.sortKey > 0) {
settings->screens[0]->sortKey = flags.sortKey; settings->sortKey = flags.sortKey;
settings->screens[0]->treeView = false; settings->treeView = false;
settings->screens[0]->direction = 1; settings->direction = 1;
} }
ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel)); ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel));

View File

@ -1,41 +1,10 @@
[Desktop Entry] [Desktop Entry]
Type=Application
Version=1.0 Version=1.0
Name=Htop Name=Htop
Type=Application
Comment=Show System Processes
Comment[ca]=Mostra els processos del sistema
Comment[de]=Systemprozesse anzeigen
Comment[en_GB]=Show System Processes
Comment[es]=Mostrar procesos del sistema
Comment[fi]=Katsele järjestelmän prosesseja
Comment[fr]=Affiche les processus système
Comment[gl]=Mostrar os procesos do sistema.
Comment[it]=Mostra processi di sistema
Comment[ko]=시스템 프로세스 보기
Comment[nb]=Vis systemprosesser
Comment[nl]=Systeemprocessen tonen
Comment[nn]=Vis systemprosessar
Comment[pl]=Pokaż procesy systemowe
Comment[pt]=Mostrar os Processos do Sistema
Comment[pt_BR]=Mostra os processos do sistema
Comment[ru]=Просмотр списка процессов в системе
Comment[sk]=Zobraziť systémové procesy
Comment[sl]=Prikaz sistemskih opravil
Comment[sr]=Приказ системских процеса
Comment[sr@ijekavian]=Приказ системских процеса
Comment[sr@ijekavianlatin]=Prikaz sistemskih procesa
Comment[sr@latin]=Prikaz sistemskih procesa
Comment[sv]=Visa systemprocesser
Comment[tr]=Sistem Süreçlerini Göster
Comment[uk]=Перегляд системних процесів
Comment[zh_CN]=显示系统进程
Comment[zh_TW]=顯示系統行程
Terminal=true
Exec=htop
Icon=htop
Categories=ConsoleOnly;System;
GenericName=Process Viewer GenericName=Process Viewer
GenericName[ca]=Visor de processos GenericName[ca]=Visualitzador de processos
GenericName[da]=Procesfremviser
GenericName[de]=Prozessanzeige GenericName[de]=Prozessanzeige
GenericName[en_GB]=Process Viewer GenericName[en_GB]=Process Viewer
GenericName[es]=Visor de procesos GenericName[es]=Visor de procesos
@ -53,13 +22,46 @@ GenericName[pt_BR]=Visualizador de processos
GenericName[ru]=Монитор процессов GenericName[ru]=Монитор процессов
GenericName[sk]=Prehliadač procesov GenericName[sk]=Prehliadač procesov
GenericName[sl]=Pregledovalnik opravil GenericName[sl]=Pregledovalnik opravil
GenericName[sr]=Приказивач процеса
GenericName[sr@ijekavian]=Приказивач процеса GenericName[sr@ijekavian]=Приказивач процеса
GenericName[sr@ijekavianlatin]=Prikazivač procesa GenericName[sr@ijekavianlatin]=Prikazivač procesa
GenericName[sr@latin]=Prikazivač procesa GenericName[sr@latin]=Prikazivač procesa
GenericName[sr]=Приказивач процеса
GenericName[sv]=Processvisning GenericName[sv]=Processvisning
GenericName[tr]=Süreç Görüntüleyici GenericName[tr]=Süreç Görüntüleyici
GenericName[uk]=Перегляд процесів GenericName[uk]=Перегляд процесів
GenericName[zh_CN]=进程查看器 GenericName[zh_CN]=进程查看器
GenericName[zh_TW]=行程檢視器 GenericName[zh_TW]=行程檢視器
Comment=Show System Processes
Comment[ca]=Visualitzeu els processos del sistema
Comment[da]=Vis systemprocesser
Comment[de]=Systemprozesse anzeigen
Comment[en_GB]=Show System Processes
Comment[es]=Mostrar procesos del sistema
Comment[fi]=Katsele järjestelmän prosesseja
Comment[fr]=Affiche les processus système
Comment[gl]=Mostrar os procesos do sistema.
Comment[it]=Mostra processi di sistema
Comment[ko]=시스템 프로세스 보기
Comment[nb]=Vis systemprosesser
Comment[nl]=Systeemprocessen tonen
Comment[nn]=Vis systemprosessar
Comment[pl]=Pokaż procesy systemowe
Comment[pt]=Mostrar os Processos do Sistema
Comment[pt_BR]=Mostra os processos do sistema
Comment[ru]=Просмотр списка процессов в системе
Comment[sk]=Zobraziť systémové procesy
Comment[sl]=Prikaz sistemskih opravil
Comment[sr@ijekavian]=Приказ системских процеса
Comment[sr@ijekavianlatin]=Prikaz sistemskih procesa
Comment[sr@latin]=Prikaz sistemskih procesa
Comment[sr]=Приказ системских процеса
Comment[sv]=Visa systemprocesser
Comment[tr]=Sistem Süreçlerini Göster
Comment[uk]=Перегляд системних процесів
Comment[zh_CN]=显示系统进程
Comment[zh_TW]=顯示系統行程
Icon=htop
Exec=htop
Terminal=true
Categories=System;Monitor;ConsoleOnly;
Keywords=system;process;task Keywords=system;process;task

2
htop.h
View File

@ -14,6 +14,6 @@ in the source distribution for its full text.
// ---------------------------------------- // ----------------------------------------
int main(int argc, char** argv); extern int main(int argc, char** argv);
#endif #endif

View File

@ -64,7 +64,7 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short
} }
char* line = NULL; char* line = NULL;
for (unsigned short int i = 0; i < lineNum; i++) { for (unsigned short int j = 0; j < lineNum; j++) {
free(line); free(line);
line = String_readLine(file); line = String_readLine(file);
if (!line) break; if (!line) break;
@ -193,17 +193,27 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) {
char* entryName = (char *) dirEntry->d_name; char* entryName = (char *) dirEntry->d_name;
const char filePath[50]; const char filePath[50];
if (entryName[0] == 'B' && entryName[1] == 'A' && entryName[2] == 'T') { xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/type", entryName);
int fd1 = open(filePath, O_RDONLY);
if (fd1 == -1)
continue;
char type[8];
ssize_t typelen = xread(fd1, type, 7);
close(fd1);
if (typelen < 1)
continue;
if (type[0] == 'B' && type[1] == 'a' && type[2] == 't') {
xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName); xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName);
int fd = open(filePath, O_RDONLY); int fd2 = open(filePath, O_RDONLY);
if (fd == -1) { if (fd2 == -1) {
closedir(dir); closedir(dir);
return; return;
} }
char buffer[1024]; char buffer[1024];
ssize_t buflen = xread(fd, buffer, 1023); ssize_t buflen = xread(fd2, buffer, 1023);
close(fd); close(fd2);
if (buflen < 1) { if (buflen < 1) {
closedir(dir); closedir(dir);
return; return;
@ -249,18 +259,18 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) {
} }
xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName); xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName);
int fd = open(filePath, O_RDONLY); int fd3 = open(filePath, O_RDONLY);
if (fd == -1) { if (fd3 == -1) {
closedir(dir); closedir(dir);
return; return;
} }
char buffer[2] = ""; char buffer[2] = "";
for(;;) { for(;;) {
ssize_t res = read(fd, buffer, 1); ssize_t res = read(fd3, buffer, 1);
if (res == -1 && errno == EINTR) continue; if (res == -1 && errno == EINTR) continue;
break; break;
} }
close(fd); close(fd3);
if (buffer[0] == '0') { if (buffer[0] == '0') {
*isOnAC = AC_ABSENT; *isOnAC = AC_ABSENT;
} else if (buffer[0] == '1') { } else if (buffer[0] == '1') {

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