539 Commits

Author SHA1 Message Date
a516e0852e Update configure to reflect rc2 in the version 2021-09-05 19:07:29 +02:00
556d7c03e8 Add a Process_printPercentage helper routine
Replace several open-coded variants of percentage formatting.
This function has been ported from Hishams old 'next' branch.
2021-09-05 18:47:07 +02:00
0925c54caa Drop redundant semicolons 2021-09-04 14:17:37 +02:00
d0f5b61aa5 hwloc: use int in hwloc_bitmap_foreach_begin loop
Affinity.c:67:10: runtime error: implicit conversion from type 'int' of value -1 (32-bit, signed) to type 'unsigned int' changed the value to 4294967295 (32-bit, unsigned)
2021-09-04 09:48:53 +02:00
284f8c5e0b configure: output vserver as implied if ancient-vserver is enabled
The build time configuration ancient-vserver implies the configuration
vserver; say so in the configure status report if only ancient-vserver
has been specified.

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

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

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

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

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

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

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

Run and check `make install` and `make installcheck`.
2021-09-02 08:03:21 +02:00
b81bb9038c Fix resource leaks dealing with unrecognised config file version
Plug leaks of an open file descriptor and dynamically allocated
'option' when we bail out early reading unknown config version.
2021-08-31 08:08:01 +02:00
1f2f567ca1 Fix meterPanels size calculation for dynamic array allocation 2021-08-31 08:08:01 +02:00
393330239e Small editorial fixes to ChangeLog 2021-08-27 09:31:06 +02:00
d2c34259b4 Remove trailing whitespace in changelog for CI checks 2021-08-27 12:19:50 +10:00
858ad8029d Update configure to reflect rc1 in the version 2021-08-27 12:14:23 +10:00
6876a4b136 Update ChangeLog 2021-08-27 12:11:22 +10:00
da7a369fa8 Merge branch 'shorten_crash_report' of cgzones/htop, rebased by BenBE 2021-08-26 19:31:50 +02:00
4ed3ab5c2c Shorten crash output to fit on screen 2021-08-26 14:09:39 +02:00
088dc5b9a7 Remove license excemption for PLPA and update GPL-2 license text
The Portable Linux Processor Affinity (PLPA) project has been depreciated in
favour of the Portable Hardware Locality (hwloc) project. So the license
exception present in previous versions of htop is obsolete and thus removed.

The text of COPYING has been updated to the latest upstream license text
of GPL-2 from the Free Software Foundation, Inc. (FSF).
There are only editorial changes like line wrapping, removing page breaks,
updating the "19yy" to "<year>" and changing the FSF address.
2021-08-26 11:37:36 +02:00
16faf82739 Clarify naming of Platform_nanosecondsPerSchedulerTick 2021-08-25 20:45:00 +02:00
df17374a92 Merge branch 'refactor-Darwin-platform-unit-conversion-helpers' of amomchilov/htop 2021-08-25 19:15:17 +02:00
59d0c5b26a Refactor Darwin platform unit conversion helpers 2021-08-25 11:55:05 -04:00
fa48c484cc Merge branch 'fix-macOS-time-calculations' of amomchilov/htop 2021-08-25 17:07:06 +02:00
a5e2eff5e9 configure: resolve autotools 2.70 deprecation warnings
configure.ac:72: warning: The macro `AC_PROG_CC_C99' is obsolete.
    configure.ac:72: You should run autoupdate.
    ./lib/autoconf/c.m4:1659: AC_PROG_CC_C99 is expanded from...
    configure.ac:72: the top level
    configure.ac:134: warning: The macro `AC_HEADER_STDC' is obsolete.
    configure.ac:134: You should run autoupdate.
    ./lib/autoconf/headers.m4:704: AC_HEADER_STDC is expanded from...
    configure.ac:134: the top level
2021-08-25 13:16:40 +02:00
2bf626c4e4 IWYU update 2021-08-25 09:54:30 +02:00
fecf093367 IWYU: add two header rules 2021-08-25 09:54:30 +02:00
c243db0b2c XUtils: move implementation of String_contains_i out of header file
The function strcasestr(3) is only available if _GNU_SOURCE is defined.
If any file includes <string.h> before declaring _GNU_SOURCE, e.g by
including "config.h", compilation fails with the following error:

    In file included from ColumnsPanel.c:8:
    In file included from ./ColumnsPanel.h:12:
    In file included from ./Panel.h:13:
    In file included from ./CRT.h:16:
    In file included from ./Settings.h:17:
    In file included from ./Process.h:15:
    In file included from ./Object.h:17:
    ./XUtils.h:42:11: error: implicit declaration of function 'strcasestr' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
       return strcasestr(s1, s2) != NULL;
              ^
    ./XUtils.h:42:11: note: did you mean 'strcasecmp'?
    /usr/include/strings.h:116:12: note: 'strcasecmp' declared here
    extern int strcasecmp (const char *__s1, const char *__s2)
               ^

Move the implementation to avoid unnecessary includes.
Since LTO is quite common and stable performance should not be impacted
if used.
2021-08-25 09:54:30 +02:00
a18018ed48 Merge branch 'hlayout_id' of cgzones/htop 2021-08-24 20:43:18 +02:00
db076b9c8e HeaderLayout: save name in configuration
Use a name in the user configuration file instead of the compile
time enum value, so that future reorderings or insertions do not change
the user selected layout.
2021-08-24 20:27:59 +02:00
0679e9f45e Unsupported: update platform 2021-08-24 20:23:03 +02:00
7a4d6fa409 Style touch-ups 2021-08-23 10:37:49 -04:00
5b4d63d1be Fix macOS CPU time calculations 2021-08-23 10:37:49 -04:00
ec2307688e Merge branch 'header_fmt' of cgzones/htop 2021-08-23 14:56:05 +02:00
6d10736a64 Merge branch 'config_versions' of fasterit/htop 2021-08-23 14:53:24 +02:00
711a7aacb0 Tiny cleanup from review comments 2021-08-23 14:50:46 +02:00
a912512ac9 Simplify delay.tv_usec calculation from BenBE
Closes #761
2021-08-23 10:42:08 +02:00
35d94a5ae5 Apply approved warning message suggested by nathans 2021-08-23 08:58:14 +02:00
ccb756d3c7 Widen integer type before multiplication
Meter.c:320:71: warning: performing an implicit widening conversion to type '__suseconds_t' (aka 'long') of a multiplication performed in type 'int' [bugprone-implicit-    widening-of-multiplication-result]
          struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay - ((globalDelay / 10) * 10)) * 100000 };
                                                                          ^
2021-08-22 17:53:21 +02:00
5dec9475bb Use break inside loop with false condition
Found by clang-tidy.

    home/christian/Coding/workspaces/htop/Process.c:505:13: warning: 'continue' in loop with false condition is equivalent to 'break' [bugprone-terminating-continue]
               WRITE_HIGHLIGHT(0, strlen(procComm), commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
               ^
    /home/christian/Coding/workspaces/htop/Process.c:461:13: note: expanded from macro 'WRITE_HIGHLIGHT'
                continue;                                                                         \
                ^
2021-08-22 17:53:21 +02:00
c3746dc901 Align parameters names between function declaration and definition
Found by clang-tidy.
2021-08-22 17:53:21 +02:00
6e6334e603 Simplify adding pages in one place 2021-08-22 16:15:59 +02:00
9060a4179d Add option to change Header layout 2021-08-22 16:15:59 +02:00
7269faf651 Only request selection index in ColorsPanel when needed 2021-08-22 15:22:27 +02:00
7146059645 Removed unused field in ColorsPanel 2021-08-22 15:21:52 +02:00
cf45a5d02b MemorySwapMeter: use full width on odd total width 2021-08-22 14:51:13 +02:00
a905c45195 Meter: update documentation to match Doxygen style 2021-08-22 14:50:52 +02:00
9df0f62859 Linux: do not scan frequency for inactive CPUs 2021-08-22 14:50:38 +02:00
68f2bfea61 Abstract resize handling by adding a new Htop reaction 2021-08-22 10:40:59 +02:00
b9e69223d0 ScreenManager: reduce ScreenManager_resize
The main change is the header hight being not included in y1.
This is important if a sub-manager gets resized, e.g. a resize while
editing the Settings or in a pickFromVector selection, and afterwards,
then the sub-manager is closed, the super-ScreenManager gets resized, it
uses the correct header hight.
The header hight might have been changed since the last resize of the
super-manager in the Settings by adding/removing some meters.

This fixes new meters being hidden after added at runtime after a resize
in the main window.
2021-08-22 10:40:59 +02:00
edc3de7cb5 Action: specify implication in code not in comments 2021-08-22 10:40:59 +02:00
a9ddaccc63 Merge branch 'read-settings-defaults' of bjpbakker/htop 2021-08-18 14:47:16 +02:00
a0c244a163 Spell out virtualized 2021-08-18 14:01:25 +02:00
f886759022 Meter: limit LED mode by width
Stop displaying LED-mode if maximum width is reached.
2021-08-17 10:36:10 +02:00
b965417bf7 Add combined memory and swap meter
Closes: #699
2021-08-17 10:36:10 +02:00
3f727d4720 Merge pull request #747 from natoscott/coverity
Coverity scan updates (minor)
2021-08-17 15:42:33 +10:00
d5ff5c48a8 Apply suggestions from code review
Co-authored-by: BenBE <BenBE@geshi.org>
2021-08-17 15:42:10 +10:00
c7f634ec21 PCP: ensure unsigned types used throughout CPU count detection
This cannot be negative in these code locations, but for the
purposes of static checking like Coverity scan make it clear
and used the same unsigned type as ProcessList.h for the CPU
count variable (matching PL activeCPUs and existingCPUs).
2021-08-17 14:41:55 +10:00
c401ac3a98 Ensure DynamicColumn hash lookups never see NULL pointers
This cannot happen in these code locations, but for the purposes
of static checkers like Coverity scan (and for future proofing),
add two more guards on NULL hash table entry pointers.
2021-08-17 14:41:40 +10:00
fefff80631 PCP: PCPMetric.[ch] Mdoule
Split the PCP Metric API (functions `Metric_*`) into their own module.
as @BenBE suggested.
2021-08-16 17:23:07 +02:00
edafa26f9e Simplify Action_pickFromVector() width parameter usage
Pass one less instead of subtracting one inside the function.
2021-08-16 08:05:46 +02:00
68460b25e3 Reset the signal handlers at program exit
The signal handler will access the Settings struct, which gets freed at
normal program finalization.

When using leak sanitizers with ASAN_OPTIONS=abort_on_error=1, which
runs after program termination, any leak causes SIGABRT to be raised,
calling the crash handler, which will derefernce the freed Settings.

    ==44741==ERROR: AddressSanitizer: heap-use-after-free on address 0x60d000000080 at pc 0x0000005680df bp 0x7fffe335e960 sp 0x7fffe335e958
    READ of size 8 at 0x60d000000080 thread T0
        #0 0x5680de in Settings_write /home/christian/Coding/workspaces/htop/Settings.c:329:26
        #1 0x4f77b7 in CRT_handleSIGSEGV /home/christian/Coding/workspaces/htop/CRT.c:1020:4
        #2 0x7f8a1120c13f  (/lib/x86_64-linux-gnu/libpthread.so.0+0x1413f)
        #3 0x7f8a11042ce0 in __libc_signal_restore_set signal/../sysdeps/unix/sysv/linux/internal-signals.h:86:3
        #4 0x7f8a11042ce0 in raise signal/../sysdeps/unix/sysv/linux/raise.c:48:3
        #5 0x7f8a1102c536 in abort stdlib/abort.c:79:7
        #6 0x4c3db6 in __sanitizer::Abort() (/home/christian/Coding/workspaces/htop/htop+0x4c3db6)
        #7 0x4c2090 in __sanitizer::Die() (/home/christian/Coding/workspaces/htop/htop+0x4c2090)
        #8 0x4d0a17 in __lsan::HandleLeaks() (/home/christian/Coding/workspaces/htop/htop+0x4d0a17)
        #9 0x4cd950 in __lsan::DoLeakCheck() (/home/christian/Coding/workspaces/htop/htop+0x4cd950)
        #10 0x7f8a110454d6 in __run_exit_handlers stdlib/exit.c:108:8
        #11 0x7f8a11045679 in exit stdlib/exit.c:139:3
        #12 0x7f8a1102dd10 in __libc_start_main csu/../csu/libc-start.c:342:3
        #13 0x428a19 in _start (/home/christian/Coding/workspaces/htop/htop+0x428a19)

    0x60d000000080 is located 64 bytes inside of 144-byte region [0x60d000000040,0x60d0000000d0)
    freed by thread T0 here:
        #0 0x4a4f72 in free (/home/christian/Coding/workspaces/htop/htop+0x4a4f72)
        #1 0x566693 in Settings_delete /home/christian/Coding/workspaces/htop/Settings.c:32:4
        #2 0x4ede10 in CommandLine_run /home/christian/Coding/workspaces/htop/CommandLine.c:393:4
        #3 0x4d6f32 in main /home/christian/Coding/workspaces/htop/htop.c:15:11
        #4 0x7f8a1102dd09 in __libc_start_main csu/../csu/libc-start.c:308:16

    previously allocated by thread T0 here:
        #0 0x4a5372 in __interceptor_calloc (/home/christian/Coding/workspaces/htop/htop+0x4a5372)
        #1 0x57f61a in xCalloc /home/christian/Coding/workspaces/htop/XUtils.c:55:17
        #2 0x5688a6 in Settings_new /home/christian/Coding/workspaces/htop/Settings.c:392:21
        #3 0x4ecb57 in CommandLine_run /home/christian/Coding/workspaces/htop/CommandLine.c:303:25
        #4 0x4d6f32 in main /home/christian/Coding/workspaces/htop/htop.c:15:11
        #5 0x7f8a1102dd09 in __libc_start_main csu/../csu/libc-start.c:308:16

SUMMARY: AddressSanitizer: heap-use-after-free /home/christian/Coding/workspaces/htop/Settings.c:329:26 in Settings_write
2021-08-16 08:05:07 +02:00
b42c441ee0 Use proper metric to detect kernel threads
Querying kernel threads with `ps -o pid,lid,flags,state,lname -sp 0`
gives that kernel threads have state `K` and flags have mask `0x20000` set.
This corresponds to `LW_SYSTEM` in kernel which is mapped as `L_SYSTEM`/`P_SYSTEM` for userspace.
2021-08-16 08:03:57 +02:00
68123adb6f Build fix for NetBSD 2021-08-16 07:45:04 +02:00
ce27f8379d Respect "Show custom thread names" setting update
Update merged command-line when started with "Show custom thread names"
disabled and enabling at runtime.

Also only consider showThreadNames when working on userland threads.
2021-08-14 17:05:00 +02:00
2d1b6f4783 TasksMeter: save some float casts
Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2021-08-14 11:16:03 +02:00
fc2377f052 Build pcp-htop.5 only when --enable-pcp 2021-08-14 10:44:31 +02:00
4b59a2e6b7 Introduce versioned config files and config_reader_min_version 2021-08-13 23:21:54 +02:00
nia
dd91e9a9da netbsd: Add NetworkIOMeter support 2021-08-13 22:27:14 +02:00
9a07ba2700 Merge pull request #705 from natoscott/pcp-htop-manual
docs: updates and new manual page for pcp-htop
2021-08-13 16:05:38 +10:00
5b5836a2b1 Apply suggestions from code review
Co-authored-by: BenBE <BenBE@geshi.org>
2021-08-13 16:04:25 +10:00
f839095e3b Merge branch 'dynamic-columns' of https://github.com/smalinux/htop into smalinux-dynamic-columns 2021-08-13 15:56:01 +10:00
6f2021f3d9 PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns.  The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.

We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.

Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location.  Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop.  This
final location becomes the preferred place for our own
shipped meter and column files.

The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start.  Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-08-13 07:32:57 +02:00
6974ce8e79 Linux: do not include zram devices in DiskIO
The meter is intended to show *real* IO, which is significantly slower
than zram.
2021-08-10 22:01:42 +02:00
796bc36fe0 Add assert to improve backtraces on NULL function pointer 2021-08-10 21:26:50 +02:00
ba5ef1ac8b CPUMeter: show if a CPU is offline in text mode
Related to #729 as the text mode displays all zero values for offline
CPUs.
2021-08-10 17:09:04 +02:00
671282d309 Correct grammatical error in crash information
Spotted by @natoscott
2021-08-10 17:09:04 +02:00
a8b8f5f836 ScreenManager: drop unused member 2021-08-10 17:09:04 +02:00
51669ecba8 Solaris: the average CPU utilization value must never be marked 'offline'
Similar to #729 only for Solaris
2021-08-10 17:09:04 +02:00
02cfd38671 The average CPU utilization value must never be marked 'offline'
Fixes https://github.com/htop-dev/htop/issues/729
2021-08-10 08:00:35 +02:00
nia
6d3b4a0f2e netbsd: handle repeated ENOMEM from HW_IOSTATS safely 2021-08-09 14:17:06 +02:00
c31fd3c691 Merge branch 'pcp-dynamic-cpu' 2021-08-09 19:24:29 +10:00
nia
324f9d048d netbsd: add more robust error handling for sysctl HW_IOSTATS 2021-08-09 09:08:37 +02:00
nia
5b8654d341 netbsd: Add support for DiskIOMeter 2021-08-09 09:08:37 +02:00
eb4ff3c69c Add Shift-F7, Shift-F8 keybindings for autogroups 2021-08-09 16:23:09 +10:00
1bd95983b2 Add columns for process autogroup identifier and nice value
Adds AGRP (autogroup) and ANI (autogroup nice) columns that
report the information from /proc/PID/autogroup, as well as
handlers for '{' and '}' to change the autogroup nice value.

This is guarded by /proc/sys/kernel/sched_autogroup_enabled
such that sampling and/or changing values wont be attempted
unless the kernel feature is enabled.

Fixes: #720
2021-08-09 07:56:13 +02:00
ee831263c3 PCP: implement a missing piece for offline and hotplug CPUs
Related: #656
2021-08-09 12:42:45 +10:00
aa0424ade8 configure: check for NaN compiler support
Compilers might due to optimizations, like -ffast-math (included in
-Ofast) expect floating point variables to be never NaN and replace each
call to isnan() with false.  Htop uses the value NaN for signaling no
data available for various information.

Warn at configure time if the compiler will ignore NaN values.

Note: this can not be implemented as a compile time static assert, as
some compilers handle compile NaNs differently than runtime NaNs.
2021-08-08 17:02:12 +02:00
10e9ffd8e5 Fix misc typos
[ci skip]
2021-08-08 15:10:58 +02:00
nia
97a859c5bd netbsd: Use newer proplib API. Create aliases so it works on 9.x.
This way we avoid deprecation warnings on the development branch
of NetBSD while keeping the code functioning on the stable branch.
2021-08-05 10:47:14 +02:00
nia
c85aafa608 netbsd: If at least one AC adapter is connected, keep its state. 2021-08-05 10:47:14 +02:00
nia
93ca5af953 netbsd: style: declare variables on first use rather than C89-style 2021-08-05 10:47:14 +02:00
nia
fdcdc54ec4 netbsd: Add battery support
This uses proplib and sysmon_envsys to determine the total charge
percentage of any number of connected batteries as well as the
AC adapter state. Should work with ACPI and non-ACPI systems.
2021-08-05 10:47:14 +02:00
2e3f34f5c1 NetBSD: Rework CPU counting. 2021-08-05 10:25:59 +02:00
04da92dfd1 docs: updates and new manual page for pcp-htop
Add some words about pcp-htop to the main man page, and add a
new man page describing the pcp-htop configuration files that
allow new meters and columns to be defined at runtime.
2021-08-03 14:11:21 +10:00
c1c4b5a1ab Read settings after applying defaults
Default settings are used as a base and only settings specified in `htoprc` are
applied on top of it. This patch removes the special case for applying some
defaults  when the config does not contain a `meters` key. All defauls are set
before any attempt to read settings, so only keys actually present in the config
file are overridden.
2021-08-02 17:33:34 +02:00
ed82ce6456 Merge branch 'cpu_count' of cgzones/htop 2021-08-02 15:21:07 +02:00
e341217fea Properly handle multiple batteries on darwin
This makes the behaviour consistent with other platforms where AC is
marked as present if at least one power source is marked as AC_PRESENT.

Fixes: #711
2021-08-02 14:37:44 +02:00
44e01dd32b Makefile.am fix that actually does a proper substitution 2021-08-02 00:43:10 +02:00
03705a20aa Fix portability issue in Makefile.am
Fixes #662
2021-08-02 00:00:46 +02:00
19ad28a560 PCP: fix per-process user and system time conversions 2021-07-29 10:15:43 +02:00
97d9b320ad PCP: use the correct metric for shared memory calculations 2021-07-29 10:14:51 +02:00
nia
4f3ba680fb Fix indentation style 2021-07-21 21:06:58 +02:00
nia
3fced48eea netbsd: convert snprintf use to xSnprintf 2021-07-21 21:06:58 +02:00
nia
a4b650fdec netbsd: re-initialize freqSize before sysctlbyname() 2021-07-21 21:06:58 +02:00
nia
72cea2881c netbsd: Remove conditional compilation of CPU frequency variables 2021-07-21 21:06:58 +02:00
nia
b4884373e5 netbsd: Support display of CPU frequency 2021-07-21 21:06:58 +02:00
370f89c086 Merge branch 'fix-dragon' of smalinux/htop 2021-07-18 13:03:13 +02:00
32faba0b6d DragonFlyBSD: fixup: ProcessList_new declaration & definition mismatch 2021-07-18 12:41:11 +02:00
82aa956940 PCP: fixup: missing setter for isUserlandThread
fixes the color of PROC_COMM for PCP...
2021-07-18 10:28:52 +02:00
2fe4a6351e Merge branch 'show-thread-names-fix' of BenBE/htop 2021-07-18 10:21:06 +02:00
90b209ee37 PCP: fixup: Missing headers for DynamicMeter 2021-07-18 09:36:59 +02:00
edf236f9fc OpenBSD: support offline CPUs and hot-swapping 2021-07-18 07:58:50 +02:00
f608fc5c8a OpenBSD: fix compile errors
openbsd/OpenBSDProcessList.c:176:56: error: no member named 'ki_pid' in 'struct kinfo_proc'; did you mean 'p_pid'?
   const int mib[] = { CTL_KERN, KERN_PROC_CWD, kproc->ki_pid };
                                                       ^~~~~~
                                                       p_pid
/usr/include/sys/sysctl.h:375:10: note: 'p_pid' declared here
        int32_t p_pid;                  /* PID_T: Process identifier. */
                ^
openbsd/OpenBSDProcessList.c:458:33: error: comparison of integers of different signs: 'int' and 'unsigned int' [-Werror,-Wsign-compare]
      if (opl->cpus[i].cpuIndex == id)
          ~~~~~~~~~~~~~~~~~~~~~ ^  ~~
2021-07-18 07:53:03 +02:00
90cc16efc0 Solaris: support offline CPUs and hot-swapping
Example hot-swapping:
    psradm -F -f 2
2021-07-18 07:53:01 +02:00
f47e88f5e8 DragonFlyBSD: calculate whether to show entry last
Wait until it has been decided what kind of task the entry actually is.
2021-07-18 07:50:50 +02:00
b148a4bed2 DragonFlyBSD: drop void TODO 2021-07-18 07:50:50 +02:00
1fb0c720fe Enable affinity support for non-Linux
sched_getaffinity() and sched_setaffinity() are also available on BSDs.
Remove the Linux restraint.
2021-07-18 07:50:48 +02:00
adcedf87f5 FreeBSD: mark the original kernel thread with pid 0 as such 2021-07-18 07:47:16 +02:00
3451b6c6b8 FreeBSD: calculate whether to show entry last
Wait until it has been decided what kind of task the entry actually is.
2021-07-18 07:47:16 +02:00
af0b67ccd2 FreeBSD: skip exe check for kernel thread
Kernel threads do not have an executable and the check can result in
garbage values as unprivileged user.
2021-07-18 07:47:16 +02:00
fbe3a2155f UptimeMeter: treat all non-positive values as error
Bogus uptime measurements can result in wrap-arounds, leading to
negative garbage values printed.
2021-07-18 07:47:16 +02:00
11d2206f40 Add ProcessList_isCPUonline 2021-07-18 07:47:09 +02:00
41af31be7f Rework CPU counting
Currently htop does not support offline CPUs and hot-swapping, e.g. via
    echo 0 > /sys/devices/system/cpu/cpu2/online

Split the current single cpuCount variable into activeCPUs and
existingCPUs.

Supersedes: #650
Related: #580
2021-07-18 07:44:02 +02:00
7bfd62b8e4 Respect "Show custom thread names" setting 2021-07-17 20:59:50 +02:00
c9abd788b1 Minor README wordsmithing on review of recent deps/build changes 2021-07-16 12:48:07 +10:00
0b787ca0b8 Merge branch 'README-update' of https://github.com/Eideen/htop into Eideen-README-update 2021-07-16 10:36:46 +10:00
nia
e8f27ebc26 Disable mouse option when support is unavailable 2021-07-15 19:09:08 +02:00
nia
2ab8fb83ba netbsd: Support curses libraries without ncurses mouse support
This adds a configure check for the ncurses getmouse() function
and disables mouse-related code paths when mouse support is
not present in the curses library.

This is necessary for stable versions of NetBSD's libcurses, the
development version has stub mouse functions for compatibility
with ncurses.

Signed-off-by: Nia Alarie <nia@NetBSD.org>
2021-07-15 19:09:08 +02:00
d45b4f4a43 Use PATH lookup for systemctl in systemd meter
Before this change, the systemd meter was broken on distros like NixOS,
which have systemctl in PATH, but not at /bin/systemctl. After the
change, it works on all my NixOS machines.
2021-07-15 10:21:12 +02:00
df435931b6 Proper label indentation 2021-07-15 06:57:24 +02:00
279140db21 Align descriptive comments 2021-07-15 06:57:24 +02:00
976c6123f4 Pointer indication aligned to typename 2021-07-15 06:57:24 +02:00
68edf92434 Indentation of string arguments 2021-07-15 06:57:24 +02:00
0d85af2872 Whitespace around operators 2021-07-15 06:57:24 +02:00
458749df45 Code indentation 2021-07-15 06:57:24 +02:00
e7f8d7bcc9 Split statements that should go onto multiple lines 2021-07-15 06:57:24 +02:00
nia
3bc73aa088 netbsd: update README.md (#694)
netbsd: update README.md
2021-07-15 06:41:42 +02:00
f21f81b2de Merge pull request #695 from smalinux/zfs-orgniz
PCP: cleanup: put ZFS in its rightful place
2021-07-15 12:11:09 +10:00
3853978538 PCP: cleanup: put ZFS in its rightful place
`PCPProcessList_updateHeader` for all meters...
2021-07-15 03:47:54 +02:00
2b69f44a9d Fix whitespace oddity in previous DynamicMeter updates
Thanks @BenBE for noticing.
2021-07-14 17:08:36 +10:00
0daefbe4b4 Improve the DynamicMeter_search API to make 'key' optional
Thanks to @BenBE for the suggestion.
2021-07-14 11:58:46 +10:00
9cbee01877 Additional pointer checks in dynamic meter code for Coverity 2021-07-14 11:58:46 +10:00
bf853addc3 Ensure PCP dynamic metric configuration definition uniqueness
It can happen that pcp-htop is presented multiple definitions
of the same dynamic meter, e.g. if /etc/pcp/htop/meters has a
definition matching one in ~/.config/htop/meters - instead of
exiting with a duplicate metric error provide more meaningful
diagnostics (on close) and also just skip over such entries.
System files override home directories which overrides those
found below the current working directory.

Also fix the derived metric error diagnostic; because this is
using CRT_fatalError, which is like perror(3), we must give a
meaningful prefix (like program name) at the string end.
2021-07-14 11:58:46 +10:00
a476490282 Ensure we do not attempt to add a DynamicMeter via the
htoprc that we didn't find during start up.  This just
leaves blank sections of the display as @smalinux found.

Related to https://github.com/htop-dev/htop/pull/682
2021-07-14 11:58:31 +10:00
bf22a8fb13 Add SIGINT handler
This SIGINT handler is installed on top of an optional
handler that some curses/ncurses implementations provide.

This ensures the curser is properly reset when hitting Ctrl+C.
2021-07-13 20:42:30 +02:00
nia
09c7e3e136 netbsd: Support building with libcurses
Right now Unicode support must be disabled, because htop peeks
into the ncurses cchar_t struct with Unicode enabled. NetBSD's cchar_t
has different contents.

Partially fixes #660

Signed-off-by: Nia Alarie <nia@NetBSD.org>
2021-07-13 20:42:30 +02:00
nia
4865e643ad netbsd: Fix display of in-use and cached memory 2021-07-13 19:06:30 +02:00
nia
67ca214cbe netbsd: Add dyanmicMeters stubs to fix the build
Signed-off-by: Nia Alarie <nia@NetBSD.org>
2021-07-13 19:05:24 +02:00
9bba1c6cf7 README updates/formating
Formating add section "prerequisite"
To make it easy to build
Merged  Compiling from source and Github tarball ref: #639
removed tarball downloads #680
2021-07-11 14:10:47 +02:00
e7aaf79166 Remove unnecessary include files from PCPDynamicMeter.c
Also resolve a few unintended style guide transgressions
in the PCP platform code.
2021-07-09 12:42:36 +10:00
9f667f2c74 Remove references to bintray in the README, no longer exists 2021-07-08 16:34:27 +10:00
01f5b89278 Pretty-print values in the PCP DynamicMeter code
Several improvements to the way values are displayed in the
PCP platform DynamicMeter implementation:
- handle the initial 'caption' setting as with regular meters,
  this required a new meter callback because we no longer have
  just a single meter caption for the DynamicMeter case
- if no label is provided for a metric in a configuration file
  use the short form metric name as a fallback
- honour the suffix setting in the configuration file
- convert metric values to the canonical units for htop (kbyte
  and seconds), and use Meter_humanUnit when it makes sense to
  do so.

Also improves the handling of fatal string error messages in a
couple of places, thanks to BenBE for the review feedback.
2021-07-08 10:56:05 +10:00
149774209b Remove Linux-specific cpp conditional in SwapMeter.c
Instead use the common NAN pattern to use of the swap
cached value on platforms that do not support it.
2021-07-07 14:24:32 +10:00
15a71f32fe Add more defensive checks to PCP paths if sampling fails 2021-07-07 14:00:36 +10:00
93be3211ae PCP: use palette meter text colour for dynamic meter labels 2021-07-07 13:59:33 +10:00
f0ed0fdafb Add a new DynamicMeter class for runtime Meter extension
This commit is based on exploratory work by Sohaib Mohamed.
The end goal is two-fold - to support addition of Meters we
build via configuration files for both the PCP platform and
for scripts ( https://github.com/htop-dev/htop/issues/526 )

Here, we focus on generic code and the PCP support.  A new
class DynamicMeter is introduced - it uses the special case
'param' field handling that previously was used only by the
CPUMeter, such that every runtime-configured Meter is given
a unique identifier.  Unlike with the CPUMeter this is used
internally only.  When reading/writing to htoprc instead of
CPU(N) - where N is an integer param (CPU number) - we use
the string name for each meter.  For example, if we have a
configuration for a DynamicMeter for some Redis metrics, we
might read and write "Dynamic(redis)".  This identifier is
subsequently matched (back) up to the configuration file so
we're able to re-create arbitrary user configurations.

The PCP platform configuration file format is fairly simple.
We expand configs from several directories, including the
users homedir alongside htoprc (below htop/meters/) and also
/etc/pcp/htop/meters.  The format will be described via a
new pcp-htop(5) man page, but its basically ini-style and
each Meter has one or more metric expressions associated, as
well as specifications for labels, color and so on via a dot
separated notation for individual metrics within the Meter.

A few initial sample configuration files are provided below
./pcp/meters that give the general idea.  The PCP "derived"
metric specification - see pmRegisterDerived(3) - is used
as the syntax for specifying metrics in PCP DynamicMeters.
2021-07-07 10:59:36 +10:00
865b85eb2d Fix an assert on kernel process name length on the PCP platform 2021-07-07 10:59:36 +10:00
a0f758009b Fix bitmask used to extract CPU identifier for CPUMeter
When manipulating CPUMeters in the AvailableMeterPanel we
use the bottom 16 bits to hold the CPU number.  However,
the bitmask used to extract the CPU number only masks the
lower 8 bits (0xff).
2021-07-07 10:59:36 +10:00
44d1200ca4 Check for alloc_size attribute in configure.ac 2021-07-06 17:20:32 +02:00
3da142b4b6 Merge branch 'solaris-define-fixes' of BenBE/htop
Tested on OmniOS v11 r151036
2021-07-05 07:51:39 +02:00
a60ff33e52 Merge branch 'check-set-escdelay' of BenBE/htop 2021-07-05 07:51:12 +02:00
364e4e692f Reduce visibility of GZONE and UZONE
Fixes #624
2021-07-04 19:34:03 +02:00
32414dace7 Remove unused define on Solaris 2021-07-04 19:32:47 +02:00
18e3fd5ce7 Check for set_escdelay in ncurses 2021-07-04 16:50:41 +02:00
874fb773a7 Fix typo in Generic_gettime_realtime on old Mac
Fixes #673
2021-07-01 23:27:54 +02:00
ecb6a8da78 PCP: Fix minor cut+paste typo 2021-06-30 07:45:47 +02:00
3bed682b1e Always update proc->st_uid + proc->user
Avoids issue #661.
2021-06-26 12:18:37 +02:00
686309e34c Redo the memory values based on the other BSD implementations. 2021-06-26 12:18:37 +02:00
5fe9bcb21c Adds support for counting user and kernel threads. 2021-06-26 12:18:37 +02:00
336acb0309 Adds support for PROC_EXE and CWD. 2021-06-26 12:18:37 +02:00
612462e33d Adds the ELAPSED column for NetBSD.
Additional details regarding ELAPSED column can be found in #627.
2021-06-26 12:18:37 +02:00
58a895e54c Fixes minor whitespace issues and re-arrange headers to conform to style guide. 2021-06-26 12:18:37 +02:00
9de463e756 Implements the NetBSD specific changes for makeCommandStr refactor.
Refer to #388 PR for more details.
2021-06-26 12:18:37 +02:00
fa65c30976 Sets a non-NULL process state in case of kvm_getlwps(3) returns NULL. Thanks @cgzone. 2021-06-26 12:18:37 +02:00
3770769ed1 Replaces WRAP_SUBTRACT with saturatingSub inline function to reduce code duplication. 2021-06-26 12:18:37 +02:00
2f5b3ef733 Refactor saturatingSub() to be part of Macros.h 2021-06-26 12:18:37 +02:00
e42ae55d69 Renames variable from opl to npl for consistency. 2021-06-26 12:18:37 +02:00
497f468ed0 Fix include paths and minor whitespace issues 2021-06-26 12:18:37 +02:00
9b6cecfede Replace strlcpy() by safer String_safeStrncpy() 2021-06-26 12:18:37 +02:00
3414d3b2d4 Replace maximum_PID value with INT32_MAX
Thank you @niacat.
2021-06-26 12:18:37 +02:00
9e3b7c439c Update README for NetBSD 2021-06-26 12:18:37 +02:00
ddcfb179b4 Remove unwanted code, updates the comments 2021-06-26 12:18:37 +02:00
b900e70e80 Update copyright notices 2021-06-26 12:18:37 +02:00
440bb87058 Minor code clean up and corrections. 2021-06-26 12:18:37 +02:00
db98623684 Sync changes from master and fix minor warnings 2021-06-26 12:18:37 +02:00
4b49de44a8 Add NetBSD platform support without procfs dependency
- TODO, clean up the code base and update comments in code.
2021-06-26 12:18:37 +02:00
30dc4a2812 Add a section on PRs for the styleguide 2021-06-26 11:11:13 +02:00
07170aee4c Unsupported: Remark on expectation of username field update 2021-06-22 09:32:11 +02:00
4dce2db832 Solaris: Always update username 2021-06-22 09:32:11 +02:00
1c0bd5155f PCP: Always update username 2021-06-22 09:32:11 +02:00
d2a476cddb OpenBSD: Always update username 2021-06-22 09:32:11 +02:00
6a6b09b431 Darwin: Always update username 2021-06-22 09:32:11 +02:00
8aca6fbfbd Change 3.0.6-dev -> 3.1.0-dev 2021-06-18 07:52:44 +02:00
ad1ca7ee57 Update IRC channel (Part 2) 2021-06-16 14:57:49 +02:00
78793c5584 Update IRC channel
The development channel on IRC moved to libera.chat
2021-06-16 14:54:20 +02:00
92324d3461 Minor whitespace issue in configure.ac 2021-06-13 19:51:00 +02:00
e3d0fc1a5a Fix a PCP diagnostics typo, add missing pmFreeResult null check. 2021-06-13 19:51:00 +02:00
6f9b161b24 Use the PACKAGE macro rather than htop directly, for overriding
Allow other projects (PCP) to be able to ship an htop binary
which uses the custom name (pcp-htop) in several diagnostics
so that its clear which (if any!) binary failed.
2021-06-13 19:51:00 +02:00
0bd1025e94 Resolve a couple of recent memory leaks in pcp-htop
Makes the pcp-htop binary valgrind-clean once more.
2021-06-13 19:51:00 +02:00
df752dd189 Do not override Linux process library size
The library size in statm is unused and always 0 since Linux 2.6.

Fixes: 8154125d4b
2021-06-12 20:44:33 +02:00
45ab05c56a Limit deleted library check
Reading and parsing /proc/<pid>/maps is quite expensive.

Do not check for deleted libraries if the main binary has been deleted;
in this case the deleted binary takes precedence.

Do not check in threads.  The check is void for kernel threads and user-
land threads can just inherit the state from the main process structure.
2021-06-12 16:02:23 +02:00
7a8a6dd828 Do not install recommended packages by default for the CI
This ensures, the minimal dependencies we specify are sufficient.
Also this reduces fallout from broken recommendations.
2021-06-12 15:27:52 +02:00
de1d06300d Apply stale lib highlighting for EXE too 2021-06-11 09:04:23 +02:00
9114cf6ea3 Linux: update process uid on change
Always check if the user of a process changed, e.g. by using setuid(2).
2021-06-09 22:52:18 +02:00
faabbaa71e Linux: drop O_PATH usage
O_PATH is available since Linux 2.6.39, but we are using fstat(2) on the
returned file descriptor in LinuxProcessList_statProcessDir(), which
is only supported since Linux 3.6.

Fixes #534
2021-06-09 22:52:18 +02:00
8154125d4b Check processes for using deleted shared libraries
Shared libraries can be replaced by an upgrade, highlight processes
using deleted shared libraries.

Link with highlightDeletedExe setting, enabled by default.

Currently only checked on Linux.
2021-06-09 14:40:04 +02:00
94d37989b4 Use macros to PCPProcessList value extraction, tweak configure.ac
Resolves a couple of remaining review notes from @BenBE.
2021-06-09 17:09:29 +10:00
144fd0a8d7 Update platform-specific header includes to use pcp paths.
Resolves a couple of remaining review notes from @BenBE.
2021-06-09 17:09:29 +10:00
4bcb5d116b Update the PCP platform to use common Process fields and code
Remove code now that we have common platform-independent command
line wrangling (thanks BenBE!).  Add PCP platform support for a
handful of other recently arriving odds and ends - ELAPSED time,
CWD, and so on.
2021-06-09 17:09:29 +10:00
4d7cee56f0 Rework TTY column for the PCP platform 2021-06-09 17:09:29 +10:00
9ce9557e69 Various code tidyups based on review commentary from BenBE 2021-06-09 17:09:29 +10:00
b232119e4b Resolve some Coverity scan misfires in PCP platform code 2021-06-09 17:09:29 +10:00
da454997bf Remove dynamic allocation of PCP metric atomvalues expansion
This is no longer used and confuses Coverity scans, drop it.
2021-06-09 17:09:29 +10:00
5abd7f2198 Drop CI distcheck on pcp build as pcp-htop.c now contains main 2021-06-09 17:09:29 +10:00
d4a2587568 Add time handling interfaces for the pcp platform
Related to https://github.com/htop-dev/htop/pull/574
2021-06-09 17:09:29 +10:00
5dfb524237 Implement command line and environment handling for pcp htop. 2021-06-09 17:09:29 +10:00
b424a5b137 Implement shared memory support on the PCP platform
Uses the mem.util.shared metric (Shmem from meminfo).
2021-06-09 17:09:29 +10:00
d3af4e670d Update PCP platform to use the old hostname API call
Fixes CI builds which are on an old version of PCP.
2021-06-09 17:09:29 +10:00
956b2ae70c Update PCP platform to match latest API changes
Updates for recent NetworkIO Meter changes, adds support
for the SysArch and HostName Meters.  The SysArch change
is based on work originally by Sohaib Mohamed.
2021-06-09 17:09:29 +10:00
c6f20fbcc6 Fixes and cleanups for ZFS Meters and metrics 2021-06-09 17:09:29 +10:00
0e7ae9a592 Ensure PCP platform ZramMeter always uses initialized data 2021-06-09 17:09:29 +10:00
407d32e121 Fix PCP ZramMeter in presense of missing zram metrics 2021-06-09 17:09:29 +10:00
e1d1a5cec6 Add ZFS ARC statistics and meters to the PCP platform 2021-06-09 17:09:29 +10:00
6bb59f8881 Fix cut+paste typo in --enable-pcp error message 2021-06-09 17:09:29 +10:00
5ef8706d72 Add new CI workflow to check pcp-enabled builds 2021-06-09 17:09:29 +10:00
c14a45ba35 Add a platform for Performance Co-Pilot (PCP) metrics
This introduces an initial platform for extracting metrics
using the PCP performance metrics API - PMAPI(3).  It can
be used via the --enable-pcp=yes configure option.

So far I've added support for live localhost metrics only,
and only using pre-defined metrics already found in htop.
If available, all sampling is performed by pmcd(1) - else,
we fallback to htop doing the metric sampling itself (all
below the PMAPI).  When pmcd is used, it may be configured
to run children with elevated privileges, so htop does not
need to be setuid (authentication with pmcd is available).

Additionally, the PMAPI allows us to support archives (for
historical analysis and for automated regression tests in
htop).  We'll need platform-specific command line argument
additions, which isn't yet feasible in htop (not difficult
to add though).

The goal of this first version is minimal impact in terms
of modifying the htop codebase, to introduce key ideas in
PCP (metric namespace, metadata, APIs and so on) and give
us something to discuss, experiment with and build on.
2021-06-09 17:09:29 +10:00
d075d49a0c htop.1.in: Some grammatical errors are fixed. 2021-05-30 11:15:45 +02:00
f171e360e0 htop.1.in: A grammatical error is fixed. 2021-05-30 11:15:45 +02:00
c752c542fe Unsupported: Implement CWD column 2021-05-25 21:55:04 +02:00
8420df62eb Solaris: Implement CWD column 2021-05-25 21:55:04 +02:00
5e92956abc OpenBSD: Implement CWD column 2021-05-25 21:55:04 +02:00
90f42695d2 FreeBSD: Implement CWD column 2021-05-25 21:55:04 +02:00
c2e2556403 DragonFlyBSD: Implement CWD column 2021-05-25 21:55:04 +02:00
06073699ba Darwin: Implement CWD column 2021-05-25 21:55:04 +02:00
b6ff5c8a2e Move CWD field handling to platform-neutral code 2021-05-25 21:55:04 +02:00
c408add108 Linux: add reset to heuristic
On hard to parse command lines tokenStart might be computed to be bigger
than tokenEnd.
Reset both values in such cases.
2021-05-25 18:20:09 +02:00
550a141860 Add ELAPSED process column
Add process columns showing the elapsed time since the process was
started.
Similar to STARTTIME, but shows the time passed since the process start
instead of the fixed start time of the process.

Closes https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=782636
2021-05-23 15:43:23 +02:00
3d5b6d9282 Fix assert failure on short running thread
The following assert failure might happen on short running threads with
an empty comm value in /proc/${pid}/stat:

htop: Process.c:1159: void Process_updateCmdline(Process *, const char *, int, int): Assertion `(cmdline && basenameStart < (int)strlen(cmdline)) || (!cmdline && basenameStart == 0)' failed.

The specific task is:
    comm=''
    exe='(null)'
    cmdline='/usr/bin/ruby /usr/bin/how-can-i-help --apt'

So basenameStart is 0, while strlen(cmdline) is also 0.
2021-05-23 15:25:23 +02:00
771a1be316 Update documentation for basename coloring of cmdline/exe columns 2021-05-23 09:30:36 +02:00
51ecc62d92 Force update when executable was recently deleted/replaced 2021-05-23 09:30:36 +02:00
bf07c713ba Allow for highlighting of deleted executables to be configured
Fixes #383
2021-05-23 09:30:36 +02:00
d9feff150c Solaris: add EXE and COMM columns and use merged command line helpers 2021-05-23 09:22:21 +02:00
72724d42f3 DragonFlyBSD: add EXE and COMM columns and use merged command line helpers 2021-05-23 09:22:21 +02:00
d445676f09 OpenBSD: add COMM column and use merged command line helpers 2021-05-23 09:22:21 +02:00
4da618030c FreeBSD: add EXE and COMM columns and use merged command line helpers 2021-05-23 09:22:21 +02:00
8ff4eb72ac Darwin: use merged command line helpers 2021-05-23 09:22:21 +02:00
7892ac68fb Linux: use merge command line helpers 2021-05-23 09:22:21 +02:00
6ad4f345dc Unsupported: use merge command line helpers 2021-05-23 09:22:21 +02:00
05fb681d5c Process: add convenience helper functions to update merged command line related data 2021-05-23 09:22:21 +02:00
7c654559c9 Linux: drop obsolete code now in Process_writeField 2021-05-23 09:22:21 +02:00
7ef58f2dcf Drop mc->maxLen field 2021-05-23 09:22:21 +02:00
2824e2989a Reduce code duplication for nearly identical code-paths 2021-05-23 09:22:21 +02:00
9a78155e17 Properly brace macro arguments 2021-05-23 09:22:21 +02:00
aa8552ba88 Move PROC_COMM/PROC_EXE column handling to global Process implementation 2021-05-23 09:22:21 +02:00
a61a2e6d47 Call makeCommandStr on all platforms 2021-05-23 09:22:21 +02:00
bcb18ef822 Move Process_makeCommandStr to global Process implementation 2021-05-23 09:22:21 +02:00
c0d0202440 Move LinuxProcess_getCommandStr to Process_getCommandStr 2021-05-23 09:22:21 +02:00
7224d0e083 Move kernel/userland thread handling to platform-independent implementation 2021-05-23 09:22:21 +02:00
1a1fddae85 Pre-calculate isUserlandThread flag 2021-05-23 09:22:21 +02:00
cdb660adab Move mergeCommand to global process struct 2021-05-23 09:22:21 +02:00
94a52cb5c9 Rename cmdlineBasenameOffset to cmdlineBasenameEnd to properly indicate the fields purpose 2021-05-23 09:22:21 +02:00
666f70c58c Move procCmdlineBasenameOffset as cmdlineBasenameStart to global Process structure 2021-05-23 09:22:21 +02:00
6dc485dd20 Remove duplicate field LinuxProcess->procCmdlineBasenameEnd
This field held practically the same value as cmdlineBasenameEnd
2021-05-23 09:22:21 +02:00
a685661866 Move procExeBasenameOffset to main Process structure
This drops procExeLen, as that field is implicit by strlen(Process->procExe)
2021-05-23 09:22:21 +02:00
93a44acf7e Move procExeDeleted flag to main Process structure 2021-05-23 09:22:21 +02:00
b839987df7 Rename basenameOffset to cmdlineBasenameOffset 2021-05-23 09:22:21 +02:00
d74e8b7554 Move procComm and procExe to main Process structure 2021-05-23 09:22:21 +02:00
10790f0a54 Process: Put the time field more to the end 2021-05-23 09:22:21 +02:00
02431c43e1 Rename command line field from comm to cmdline 2021-05-23 09:22:21 +02:00
fbec3e4005 Refactor makeCommandStr 2021-05-23 09:22:21 +02:00
07a4657a47 DragonFlyBSD: Fix included headers 2021-05-22 18:16:11 +02:00
2c8353e7cf DragonFlyBSD: Indentation and formatting fixes 2021-05-21 22:42:00 +02:00
ee9e7edbc1 Solaris: handle ERR macro redefinitions
On OmniOS /usr/include/sys/regset.h redefines ERR to 13 - \r, breaking
the Enter key.
Since ncruses macros use the ERR macro, we can not use another name.

Closes: #634
2021-05-20 19:16:29 +02:00
a62987c787 Solaris: improve process columns
- fill tty name
- fill session id
- show real tgid not adjusted
- drop unimplemented TPGID, MINFLT and MAJFLT
- adjust header width of ZONEID, which get auto-adjusted as a pid-column
2021-05-20 17:54:40 +02:00
013d2efa51 Solaris: correct process env memory handling
Allow strncpy to NUL-terminate the buffer and do not return a non free-
able string literal.
2021-05-20 16:43:40 +02:00
27be880d0f Solaris: reduce function scopes 2021-05-20 16:43:40 +02:00
6b57898034 Solaris: reduce variable scope
Also check for getloadavg(3c) failure
2021-05-20 16:43:40 +02:00
906dcf5cb3 Solaris: silence signed comparison 2021-05-20 16:43:40 +02:00
8f34225a49 Solaris: fix includes 2021-05-20 16:43:40 +02:00
fdda291a0e Solaris: add kstat lookup wrappers
The system interfaces kstat_lookup() and kstat_data_lookup() take a
non-constant string parameter, but passing string literals is valid.

Add wrapper functions to ignore all the const-discard warnings.
2021-05-20 16:43:40 +02:00
4676e35f42 DragonFlyBSD: fixup columns 2021-05-19 17:53:14 +02:00
69cfaf2381 configure: ignore warning about delay accounting on non-Linux platform
If pkg-config is not installed the following message gets printed, even
on non Linux platform:

"Linux delay accounting support can not be enabled, cause pkg-config is
required for checking its availability"
2021-05-16 20:01:25 +02:00
d2ee40597c Use STDERR_FILENO instead of magic number 2021-05-16 19:55:51 +02:00
1f5f40c091 Print current settings on crash 2021-05-16 19:55:31 +02:00
204bc710ba Adjust to current label reality and the fact that Github can't search for multiple labels ORed ... yet.
Has been only five years ... https://github.com/isaacs/github/issues/660
2021-05-14 11:29:29 +02:00
40ecde9d88 Add Github label disclaimer as per vi's comment
cf. 6900e57efd (commitcomment-50786333)
2021-05-14 11:17:35 +02:00
3f86a011e6 platform-dependent files included relative to main source directory 2021-05-10 18:40:53 +02:00
1b74dfe187 cleaned up includes with iwyu 2021-05-10 18:40:53 +02:00
d9c95369bc Enclose macro argument
Also enclosing is unnecessary in declaration as in
    int (VAR);
2021-05-10 17:48:05 +02:00
d918cd9f2a Align parameter name of Generic_gettime_realtime
Align with name in implementation.
2021-05-10 17:48:05 +02:00
54d7c6a080 Sort include headers 2021-05-10 17:48:05 +02:00
90ae730fd4 Ignore IDE configuration files
[skip ci]
2021-05-10 17:04:46 +02:00
323d7e73aa Linux: update IO fields
- fix header width of IO_READ_RATE

- save data in bytes (not kilobytes) to better compute rate

- fix rate data: multiply with 1000 to compensate time difference in
  milliseconds

- rename unit less variable now into realtimeMs

- use Process_printBytes(..., data * pageSize, ...) instead of
  Process_printKBytes(..., data * pageSizeKB, ...) to avoid wrapper
2021-04-26 18:02:58 +02:00
b41e4d9c54 Rework process field print functions
Make functions formatting data for a process field column less error
prone, unify interfaces and improve some internals.

* Process_printBytes
  - rename from Process_humanNumber
  - take number in bytes, not kilobytes
  - handle petabytes
  - increase buffer to avoid crashes when the passed value is
    ~ ULLONG_MAX

* Process_printKBytes
  - add wrapper for Process_printBytes taking kilobytes keeping -1 as
  special value

* Process_printCount
  - rename from Process_colorNumber

* Process_printTime
  - add coloring parameter as other print functions
  - improve coloring and formatting for larger times

* Process_printRate
  - rename from Process_outputRate
  - use local buffer instead of passed one; this function prints to the
    RichString after all
2021-04-26 18:02:58 +02:00
6bbb454881 LinuxProcess: print default buffer in ascii
`RichString_appendWide()` is more expensive than
`RichString_appendAscii()` due to the calls to `mbstowcs(3)` and
`iswprint(3)`.

Use the latter to print the process field buffer by default.

For the following fields this theoretically can corrupt the output:
  - SECATTR
  - CGROUP
  - CTID
2021-04-26 17:51:45 +02:00
a2be57d768 Process: print default buffer in ascii
`RichString_appendWide()` is more expensive than
`RichString_appendAscii()` due to the calls to `mbstowcs(3)` and
`iswprint(3)`.

Use the latter to print the process field buffer by default.

For the following fields this theoretically can corrupt the output:
  - TTY
2021-04-26 17:51:45 +02:00
436808ff99 Use RichString_appendnAscii where possible
`RichString_appendnAscii()` avoids a `strlen(3)` call over
` RichString_appendAscii()`.
Use the former where the length is available from a previous checked
`snprintf(3)` call.

Keep `RichString_appendAscii()` when passing a string literal and
rely on compilers to optimize the `strlen(3)` call away.
2021-04-26 17:51:45 +02:00
099dab88be ZfsCompressedArcMeter: avoid division by 0
On systems not using ZFS `this->values[0]` is zero.
2021-04-22 17:12:02 +02:00
2d7069feb4 Linux: handle Shmem being part of Cached
See https://lore.kernel.org/patchwork/patch/648763/

Do not show twice by subtracting from Cached.

Closes: #591
2021-04-22 10:48:15 +02:00
3db3737d75 Update FUNDING.yaml to use open_collective directly (thanks cgzones) 2021-04-22 14:37:09 +10:00
a75b99a15e Document '?' key to reach the help screen 2021-04-21 21:49:03 +02:00
615fc934ff Add assert for unreachable switch case
Like the default case in Process_writeField()
2021-04-21 20:58:28 +02:00
bd689ab0d3 Avoid implicit pointer to bool conversion in assignment
Improve readability
2021-04-21 20:58:28 +02:00
d58c2f0606 Drop ProcessList_get and ProcessList_size
Only used inside ProcessList.c and only once each.
2021-04-21 20:58:28 +02:00
5dbca0193d Make MainPanel_pidSearch a static function
Not used elsewhere.
2021-04-21 20:58:28 +02:00
a05e78f531 Linux: use more robust pid parsing
Also add comment to condition
2021-04-21 20:58:28 +02:00
ace5730f89 Add github funding link to htop opencollective page 2021-04-21 16:33:07 +10:00
feec16cbb5 don't include offline CPUs in summary for OpenBSD
By default, OpenBSD disables SMT (hyperthreading) cpu pseudo-cores.
This can be changed at runtime by setting the hw.smt sysctl so they
may become active later, therefore they are still present in cpu
stat structures but are marked as offline.

As done with native top(1), this drops them from the cpu summary
graphs.
2021-04-18 16:58:20 +02:00
d63394b5f6 DragonFlyBSD: resolve sign comparison
Compat.c: In function 'Compat_faccessat':
  Compat.c:46:14: error: comparison of integer expressions of different signedness: 'int' and 'unsigned int' [-Werror=sign-compare]
     46 |    if (dirfd != AT_FDCWD || mode != F_OK) {
        |              ^~
2021-04-14 17:29:56 +02:00
99cde7edec FreeBSD: silence unsigned integer underflow
freebsd/FreeBSDProcessList.c:252:47: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'unsigned int'
      #0 0x397c32 in FreeBSDProcessList_scanCPU /root/workspace/htop/htop/freebsd/FreeBSDProcessList.c:252:47
      #1 0x38fe76 in ProcessList_goThroughEntries /root/workspace/htop/htop/freebsd/FreeBSDProcessList.c:438:4
      #2 0x35ef9a in ProcessList_scan /root/workspace/htop/htop/ProcessList.c:618:4
      #3 0x31ee9e in main /root/workspace/htop/htop/htop.c:468:4
      #4 0x26bbcf in _start /usr/src/lib/csu/amd64/crt1.c:76:7
2021-04-14 17:29:56 +02:00
9a8221568a Rework TTY column
* Rename internal identifier from TTY_NR to just TTY
* Unify column header on platforms
* Use devname(3) on BSD derivate to show the actual terminal,
  simplifies current FreeBSD implementation.
* Use 'unsigned long int' as id type, to fit dev_t on Linux.

Only on Solaris the terminal path is not yet resolved.
2021-04-14 17:29:56 +02:00
36880cd61c Add read-only option
Add command line option to disable all system and process changing
features.
2021-04-14 17:21:43 +02:00
812cfcb94d Rename drop-capabilities options none to off
None might be ambiguous whether we are dropping none or whether we keep
none.

Rename to off to make more clear this option does not drop any.
2021-04-14 17:19:26 +02:00
74d061700c LinuxProcessList_recurseProcTree(): drop non necessary parameter 2021-04-14 17:16:02 +02:00
f3d9ecaa62 Convert process time to days if applicable
With big multicore machines, it's easy to accumulate process time.
2021-04-10 14:43:23 +02:00
0006cc51b7 Merge pull request #593 from natoscott/init-pl-time
Update timestamps for the special process scans at startup also
2021-04-09 21:13:33 +10:00
367561175a Update timestamps for the special process scans at startup also
Resolves https://github.com/htop-dev/htop/issues/592
2021-04-09 10:43:33 +10:00
f3a37f9ef3 Merge branch 'smalinux-CtrTime' 2021-04-07 15:19:18 +10:00
356488aa53 Request the realtime and monotonic clock times once per sample
Refactor the sample time code to make one call to gettimeofday
(aka the realtime clock in clock_gettime, when available) and
one to the monotonic clock.  Stores each in more appropriately
named ProcessList fields for ready access when needed.  Every
platform gets the opportunity to provide their own clock code,
and the existing Mac OS X specific code is moved below darwin
instead of in Compat.

A couple of leftover time(2) calls are converted to use these
ProcessList fields as well, instead of yet again sampling the
system clock.

Related to https://github.com/htop-dev/htop/pull/574
2021-04-05 23:41:07 +02:00
421bdeec60 Merging all the points related to calculating time in one place
The end goal is to consolidate all the points in htop that can only work in
live-only mode today, so that will be able to inject PCP archive mode and have
a chance at it working.
The biggest problem we've got at this moment is all the places that are
independently asking the kernel to 'give me the time right now'.
Each of those needs to be audited and ultimately changed to allow platforms to
manage their own idea of time.
So, all the calls to gettimeofday(2) and time(2) are potential problems.
Ultimately I want to get these down to just one or two.

Related to https://github.com/htop-dev/htop/pull/574
2021-04-05 23:40:41 +02:00
f16aa483dd Merge branch 'illumos-compile-fix' of BenBE/htop 2021-04-04 22:12:54 +02:00
6c66f32fa7 Include signal.h, reorder headers 2021-04-03 21:40:27 +02:00
75fd9edf75 Reorder the header update and process scanning
BenBE points out that some header meters use values calculated
during process scanning - make sure we scan processes first in
order that current values are displayed.

Related to https://github.com/htop-dev/htop/pull/574
2021-03-31 07:50:45 +02:00
8163b8164f TaskMeter: always show number of threads
Always show the number of kernel and userland threads, even when they
are disabled to not be shown in the process list.

The data is already available and might improve understanding the system
utilization.

Use a shadow color in case the kind of thread is hidden, else the normal
meter one.
2021-03-31 00:27:14 +02:00
5afb57b49e FreeBSD: improve actual processor calculation logic 2021-03-30 23:38:32 +02:00
73f5ecf528 Linux: handle garbage in /proc/loadavg
When parsing the content of /proc/loadavg via fscanf(3), ensure client
passed parameters are set to sanitized values.

Related to: #581
2021-03-28 19:20:28 +02:00
272e72680b Merge pull request #575 from natoscott/refactor-command-line
Abstract htop main function to allow for a platform binary
2021-03-25 15:57:15 +11:00
36389fb0da Abstract htop main function to allow for a platform binary
One review request relating to the PCP platform is to have
a clearly separate binary from the regular htop so that we
have no confusion as to what is being requested to run, to
aid debugging, and a bunch of other good reasons.

This commit renames htop.c to CommandLine.c and provides a
minimal htop main function for 'native' platforms to use.
The PCP version of this will setup libpcp.so and then call
the same CommandLine_run function as regular htop.

Related to https://github.com/htop-dev/htop/pull/536
2021-03-25 15:56:15 +11:00
5ef3c26168 Drop always true condition
The variable 'dir' is checked in line 645:
    if (!dir)
        return AC_ERROR;
2021-03-24 19:36:34 +01:00
3e8da0fcb6 Add MEMORY_SHARED to help screen 2021-03-24 19:30:28 +01:00
a19b176099 Correct spelling in comment 2021-03-24 19:27:57 +01:00
9c437ceb0c Drop unused attributes of actually used function parameters
These parameters were once unused, but not anymore.
2021-03-24 19:27:03 +01:00
7b293dc3e2 Linux: fix --drop-capabilities
Do not return false (= argument not handled) when actually handled
2021-03-22 11:56:19 +01:00
6fd5b05151 Merge pull request #564 from natoscott/platform-options
Move libcap use to (Linux) platform-specific code
2021-03-22 17:25:01 +11:00
253ff23f9e Use a platform-specific routine for long option usage
Related to https://github.com/htop-dev/htop/pull/564
2021-03-22 17:16:40 +11:00
d56d23d91a Each platform defines its own long opt macro, prefer printf
Follow up on the two items of feedback from cgzones review,
and resolve a build failure picked up by CI on Mac OS X.

Related to https://github.com/htop-dev/htop/pull/564
2021-03-22 17:16:40 +11:00
0ada9f325f Move libcap use to (Linux) platform-specific code
The libcap code is Linux-specific so move it all below
the linux/ platform subdirectory.  As this feature has
custom command-line long options I provide a mechanism
whereby each platform can add custom long options that
augment the main htop options.  We'll make use this of
this with the pcp/ platform in due course to implement
the --host and --archive options there.

Related to https://github.com/htop-dev/htop/pull/536
2021-03-22 17:16:40 +11:00
57e0ce7b4f Use #if defined() syntax when #elif defined() is present
This prefers the `#if defined()` syntax over the `#ifdef` variant
whenever there's also a `#elif defined()` clause, thus making the
multiple branching structure more obvious and the overall use
more consistent.
2021-03-21 21:49:04 +01:00
1cb3aee07a Merge branch 'cputemp' of cgzones/htop 2021-03-21 18:09:14 +01:00
6ea93fc6c0 Merge branch 'openbsd' of cgzones/htop 2021-03-21 17:48:41 +01:00
63019065dc Merge branch 'cpu_proc_stat' of cgzones/htop 2021-03-21 17:48:02 +01:00
e4e3f6c390 OpenBSD: update
* Set process data for:
    - minflt
    - majflt
    - processor
    - nlwp

* Drop unimplemented nlwp column

* Scan userland threads

* Mark a 'Thread is currently on a CPU.' with 'R', and processes
  'Currently runnable' with 'P', do confine with man:ps(1) and Linux.
  See https://man.openbsd.org/ps.1

* Show CPU frequency
2021-03-20 18:30:08 +01:00
58ad020aca ProcessList: fix treeview on OpenBSD when hiding kernel threads
Currently the tree-view is empty on OpenBSD when kernel threads are
hidden, cause the kernel thread 'swapper' has pid 0 and gets treated as
root of the tree and parent of 'init'.

Do not build any tree with a pid 0 root node.
2021-03-20 18:30:08 +01:00
a11d01568c Use unsigned types for CPU counts and associated variables 2021-03-19 23:30:54 +01:00
70fecb4984 Use consistent style for include check 2021-03-19 22:39:06 +01:00
f46fcf094e Linux: Rework libsensors parsing
Do not read driver depended labels, just count the number of
temperatures given:

  on #CPU:
    platform temp = max cpu temp
    CPU temps = first to last
  on #CPU + 1:
    platform temp = first temp
    CPU temps = second to last
  on #CPU / 2:
    platform temp = max cpu temp
    CPU temps = first to last concat first to last
      (with SMT core x + cpu count is the logical core of the physical
      core x)
  on #CPU / 2 + 1:
    platform temp = first temp
    CPU temps = second to last concat second to last
      (with SMT core x + cpu count is the logical core of the physical
      core x)

Closes: #529
Closes: #538
2021-03-19 22:11:22 +01:00
53bcc5cbff ci: drop hwloc from clang-analyzer build
The hwloc header generates lots of warnings:

  In file included from Action.c:10:
  In file included from ./Action.h:15:
  In file included from ./Header.h:10:
  In file included from ./Meter.h:18:
  In file included from ./ProcessList.h:25:
  In file included from /usr/include/hwloc.h:2371:
  /usr/include/hwloc/helper.h:481:5: warning: Value stored to 'state' is never read [deadcode.DeadStores]
      state = 3;
      ^       ~
  1 warning generated.
2021-03-19 22:07:47 +01:00
db042f259b ci: use focal list for llvm mirror
The llvm bionic packages depend on libffi6, which is not available in
focal.
2021-03-19 22:07:47 +01:00
9a893b9a07 Merge branch 'richstring_memset' of cgzones/htop 2021-03-19 09:58:04 +01:00
67b815a817 Merge branch 'shared_before_cached' of cgzones/htop 2021-03-19 09:49:36 +01:00
ee97916fd5 Merge pull request #436 from cgzones/freebsd
FreeBSD: add support for CPU frequency and temperature
Tested on two physical systems running FreeBSD 12.1
2021-03-18 14:03:51 +02:00
1ba3915f73 Merge pull request #565 from cgzones/ci_clang12
ci: use clang 12
2021-03-18 08:20:17 +11:00
16243a4a7e Action: merge conditions 2021-03-17 17:53:23 +01:00
e942736267 LinuxProcessList: drop unnecessary parenthesis 2021-03-17 17:53:00 +01:00
9f41dc3332 MemoryMeter: show shared memory before cached
Shared memory is less free-able than cached memory.

Show it beforehand.
2021-03-17 16:32:16 +01:00
1e806f9899 RichString: do not unnecessarily clean whole buffer
The local stack buffer does not need to be cleaned to zeros when
  - just initialized, cause the length is set to 0 and the first
    character is set to '\0', so all printing functions will safely stop
  - no further used, i.e. the variable goes out of scope
2021-03-17 15:54:17 +01:00
ac27df373a ci: use clang 12
llvm 12 is stable enough to be used in the CI as compiler and static
analyzer.
2021-03-17 15:27:56 +01:00
d9f2eacbc5 Linux: individually show shared memory
Shmem: Total memory used by shared memory (shmem) and tmpfs

Source: https://www.kernel.org/doc/Documentation/filesystems/proc.txt

Closes: #556
2021-03-15 22:34:14 +01:00
a4173f5209 Improve process following
- stay in follow mode on sort inversion (I)
- stay in follow mode after viewing help screen (h)
- select parent process (where available) when having followed a thread
  and hiding these (H)

Closes: #560
2021-03-14 14:47:15 +01:00
1275139795 Settings_write: fix return value on error
Return a negative errno on fprintf() or flcose() failure, not a return
value of ferror() or flcose().
2021-03-13 18:15:20 +01:00
23797e730e CPUMeter_init: compactify branches 2021-03-12 17:43:23 +01:00
0cfc9b0980 LinuxProcessList: refactor /proc/stat parsing
Combine reading CPU count and CPU usage, only open the file once.
Do not separately initialize totalPeriod and totalTime, cause the value
0 is handled in Platform_setCPUValues().

Take the number of currently running process from the entry
procs_running in /proc/stat instead of counting all scanned process
with state 'R', to include hidden tasks, e.g. threads.
2021-03-12 17:31:45 +01:00
521f1343e3 Settings: check if writing configuration file was successful
Writing to the file stream might fail due to a immutable file or a
filesystem error.
Check the error indicator for the stream and for fclose() failures.
2021-03-12 16:56:06 +01:00
350b48e44c Meter: do not access RichString internals
Use a temporary local variable
2021-03-12 16:49:45 +01:00
c38819a675 Settings: mark non-modified pointer parameters const 2021-03-12 16:48:41 +01:00
d37d66bb3a InfoScreen/ProcessList: do not access Vector internals
Use wrapper function to encapsulate the Vector structure
2021-03-12 16:46:55 +01:00
3f99c2de24 Process: do not access RichString internals
Use wrapper macro to encapsulate the RichString structure
2021-03-12 16:46:04 +01:00
bea7f8e7af Process_compare: reorder checks
Check for result being 0 first, before checking if the result might be
negated, so we do not need to negate 0.
2021-03-12 16:44:46 +01:00
9adcd9051a Object: always include stdbool.h
The function Object_isA() returning bool is nowadays unconditional
2021-03-12 16:38:36 +01:00
8ba4ef327e configure: use portable AND
man:test(1)
    NOTE: Binary -a and -o are inherently ambiguous.  Use 'test EXPR1 &&
    test EXPR2' or 'test EXPR1 || test EXPR2' instead.

Also fix indent
2021-03-12 16:37:17 +01:00
31e59cc60d Merge branch 'misc' of https://github.com/cgzones/htop into cgzones-misc 2021-03-05 13:38:19 +11:00
b862e36ee7 Separate data-update and drawing of header 2021-03-04 23:57:45 +01:00
2d1042adb3 Save text buffer in Meter 2021-03-04 23:57:45 +01:00
23c5b9ce3c Ensure buffer for gethostname(2) is properly terminated 2021-03-04 23:42:24 +01:00
c5770c26af Merge branch 'follow' of cgzones/htop
Closes #557
2021-03-04 07:41:14 +01:00
8c421d527b Resolve trailing-whitespace failure in CI (merge issue) 2021-03-04 15:16:32 +11:00
adaf748ab6 Fix include file ordering of generic headers 2021-03-04 13:44:40 +11:00
61ef1134d9 Move generic (shared) code into its own sub-directory
Code that is shared across some (but not all) platforms
is moved into a 'generic' home. Makefile.am cleanups to
match plus some minor alphabetic reordering/formatting.

As discussed in https://github.com/htop-dev/htop/pull/553
2021-03-04 13:40:59 +11:00
5b50ae3aa3 Separate display from sampling in SysArch and Hostname Meters
Several of our newer meters have merged coding concerns in terms
of extracting values and displaying those values.  This commit
rectifies that for the SysArch and Hostname meters, allowing use
of this code with alternative front/back ends.  The SysArch code
is also refined to detect whether the platform has an os-release
file at all and/or the sys/utsname.h header via configure.ac.
2021-03-04 13:40:11 +11:00
59a150e8d7 Follow followed process when switching thread visibilities
Do not stop following a process when switching the visibility of
userland or kernel threads.

Related: #557
2021-03-03 20:06:14 +01:00
2328e52403 Document PERCENT_NORM_CPU and mention Irix / Solaris modes (top lingo) 2021-03-03 10:46:49 +01:00
0bdceb858d Unsupported: add normalized CPU percentage column 2021-03-03 08:44:37 +01:00
4f9cf1490f Darwin: add normalized CPU percentage column
Missed in 15eab2012d
2021-03-03 08:43:45 +01:00
635d4cfe60 Drop newline at end of if branch 2021-03-02 22:37:47 +01:00
ff4ee2eafc LinuxProcess: Drop dead assignment
Modern compilers are very good at finding uninitialized paths, lets rely
on them.
2021-03-02 22:37:47 +01:00
13b28fa9ed Enclose macro argument in parentheses 2021-03-02 22:03:20 +01:00
979aca98cc Use uppercase floating point literal suffix 2021-03-02 22:03:20 +01:00
df818b9904 Use ATTR_UNUSED instead of void casting 2021-03-02 22:03:20 +01:00
a40347e85b SysArchMeter: avoid static variable 2021-03-02 16:37:11 +01:00
dc8124e1a1 Fix compilation of the 'unsupported' platform (Process flags) 2021-03-02 16:01:14 +11:00
29570c0133 Merge pull request #550 from natoscott/diskio-types
Fix integer sizing issues in the DiskIO Meter
2021-03-02 13:34:52 +11:00
3fe297aa97 Merge pull request #549 from natoscott/network-types
Fix integer sizing issues in the NetworkIO Meter
2021-03-02 13:34:36 +11:00
88a11859a0 Switch NetworkIO Meter to using uint32_t and uint64_t
From review via @BenBE, this is now a whole lot cleaner.
2021-03-02 12:14:44 +11:00
b4736228dc Switch DiskIO Meter to using uint32_t and uint64_t
From review via @BenBE, this is now a whole lot cleaner.
2021-03-02 12:09:29 +11:00
8a1112141d Fix a possible truncation of the intermediate strings in the SysArch meter 2021-03-01 09:56:07 +01:00
7b48fec59a Merge pull request #533 from cgzones/os-release
SysArchMeter: read os-release instead of running lsb-release
2021-03-01 18:38:53 +11:00
00339087b0 Fix integer sizing issues in the DiskIO Meter
On Linux kernels the size of the values exported for block
device bytes has used a 64 bit integer for quite some time
(2.6+ IIRC).  Make the procfs value extraction use correct
types and change internal types used to rate convert these
counters (within the DiskIO Meter) 64 bit integers, where
appropriate.
2021-03-01 12:10:18 +11:00
2d1839289e Fix integer sizing issues in the NetworkIO Meter
On Linux kernels the size of the values exported for network
device bytes and packets has used a 64 bit integer for quite
some time (2.6+ IIRC).  Make the procfs value extraction use
correct types and change internal types used to rate convert
these counters (within the NetworkIO Meter) 64 bit integers,
where appropriate.
2021-03-01 11:55:15 +11:00
379421d3b2 Merge branch 'networkiograph' of Nudin/htop 2021-02-28 18:44:39 +01:00
bb9a60ee8a Implement bar and graph mode for NetworkIOMeter (#408) 2021-02-28 18:36:07 +01:00
07a6efcb22 Make the first tree item stable on expand / collapse again
Regression introduced with 06b1674
2021-02-28 18:16:29 +01:00
76350c0350 Rescale graph when value of total is changed 2021-02-28 17:42:10 +01:00
12c2337939 Merge branch 'remove-setuid' of BenBE/htop 2021-02-17 17:56:58 +01:00
067cd6deb8 Include note in changelog regarding removal of the setuid feature 2021-02-17 17:14:06 +01:00
82157f598e Refactor to remove no-op calls
This removes the call-sites of the removed setuid feature
2021-02-17 15:59:50 +01:00
a73064dda9 Remove setuid support
This support was rarely ever used and has been disabled by default for some time.

As far as the developer team is aware there's no distribution that activated this
feature in their packages by default.
2021-02-17 15:59:50 +01:00
b1befa3287 Merge branch 'enable-better-debug-on-enable-debug' of fasterit/htop 2021-02-17 15:19:59 +01:00
e0dec39203 Merge branch 'fix-duplicate-lines' of hiasen/htop 2021-02-17 15:19:28 +01:00
84e5682473 SysArchMeter: read os-release instead of running lsb-release
os-release is available on FreeBSD by default.
Also avoid executing a third-party program.

Examples:
  Linux 5.10.0-3-amd64 [x86_64] @ Debian GNU/Linux bullseye/sid
  FreeBSD 12.2-RELEASE-p3 [amd64]

Closes: #516
2021-02-17 15:05:36 +01:00
f42090fcfd Drop empty file zfs/ZfsArcStats.c 2021-02-17 00:05:16 +01:00
a89521ed7f Drop -DDEBUG (was removed in d69585b82a), add -Og for make debug target 2021-02-16 22:49:31 +01:00
135efd5705 Enable making with -ggdb3 on configure --enable-debug 2021-02-16 11:24:45 +01:00
525d37d6a4 Shorten keyboard help to fit default screen width 2021-02-16 09:12:07 +01:00
d8d83031d9 InfoScreen: Remove old lines before scanning again 2021-02-15 20:44:34 +01:00
1e57cab605 De-typo DiskIOMeter 2021-02-15 17:42:22 +01:00
b0fd44275d Merge branch 'collapse-tree-view' of fasterit/htop 2021-02-15 12:10:55 +01:00
61c943555b Merge branch 'fix-zero-btime' of natoscott/htop 2021-02-15 10:52:04 +01:00
7433bf4b18 Correctly detect failure to initialize boottime
A zero value for btime (boottime) in /proc/stat is a
real situation that happens, so deal with this case.

Resolves https://github.com/htop-dev/htop/issues/527
2021-02-15 19:32:55 +11:00
8cd90f0c4a Fix a couple of small spelling mistakes in comments 2021-02-15 12:54:20 +11:00
2c6222e30a Option and key ("*") to collapse / expand all branches under PID 1
(and PID 2 if kernel threads are shown)

Based on hishamhm/htop#510 by Krishna Chaitanya, B
Closes #68
2021-02-13 16:47:04 +01:00
c44b2ec795 Small update to test plan
from Krishna Chaitanya, B; found in hishamhm/htop#510
2021-02-13 14:04:15 +01:00
1e39c8fa4d Make ZFS Meter "Unavailable" text match others -> FAILED_READ coloring 2021-02-09 20:25:57 +01:00
4cb2b5fc1c Merge branch 'fix_zfs_arc_ratio_color' of overhacked/htop 2021-02-09 20:02:13 +01:00
f73c98abd4 Forgot to correctly color ZFS ARC ratio
`ZFS_RATIO` in `CRT.c` was unused, because I forgot
to colorize the ARC ratio in the Compressed ARC meter.

The intent was to improve readability of the meter by
highlighting the most relevant value, the ratio, in
a brighter color, for most themes. This change effects
that intent.
2021-02-09 17:07:00 +00:00
f273bfd083 Linux: restore memory calculation regarding HugePages
Subtract the total amount of huge page memory from total and used memory.

Restores behavior from #450 (see also #447)

Follow-up of 3d497a37
2021-02-09 16:01:05 +01:00
7ba3396a4c Update ChangeLog and report credits for the MemAvailable issue and initial implementation 2021-02-09 13:24:01 +01:00
69d3b9ccf1 Merge branch 'mem' of cgzones/htop
* Use MemAvailable info from Linux 3.14+ where available
* Thanks to Chris Cheney for reporting and Tomas Wido for an initial implementation

Closes #281
Closes #385
2021-02-09 13:15:52 +01:00
e86acd6893 Remove force sort order to ASC when returning to tree mode
Bug found by BenBE via IRC
2021-02-07 13:46:57 +01:00
3d497a3760 Linux: overhaul memory partition
Use similar calculation than procps.
Show AvailableMemory in text mode.
Use total minus available memory instead of manually computed used-
memory as fraction part in bar mode (if available).
2021-02-07 12:41:52 +01:00
0d67263b36 Merge branch 'stderr_cache' of cgzones/htop 2021-02-06 16:10:22 +01:00
7b1fa1bf49 Cache stderr to be able to print assert messages 2021-02-05 19:21:28 +01:00
fd2a0cf421 FreeBSD: add support for CPU frequency and temperature 2021-02-05 16:32:25 +01:00
64a1ab848f configure: ignore usage of C11 _Generic on FreeBSD
BatteryMeter.c:30:8: error: '_Generic' is a C11 extension [-Werror,-Wc11-extensions]
   if (isnan(percent)) {
       ^
/usr/include/math.h:114:2: note: expanded from macro 'isnan'
        __fp_type_select(x, __inline_isnanf, __inline_isnan, __inline_isnanl)
        ^
/usr/include/math.h:82:39: note: expanded from macro '__fp_type_select'
#define __fp_type_select(x, f, d, ld) _Generic((x),                     \
                                      ^
1 error generated.
2021-02-05 15:20:00 +01:00
cae47bb28d configure: add -lelf for static build on FreeBSD 2021-02-05 15:15:01 +01:00
fd4e6b432b Use MainPanel type in State struct
The State struct holds a pointer to the main process panel.
Use the distinct MainPanel type, to improve maintainability regrading
its usage.
This avoids usages of down-casts from Panel to MainPanel, only up-casts
from MainPanel to Panel are now required.
2021-02-05 14:12:49 +01:00
fd1ad863dc Merge branch 'lsb' of eworm-de/htop (skip "n/a" values for SysArchMeter) 2021-02-04 17:27:16 +01:00
85a855f5b2 SysArchMeter: skip "n/a" values
Unavailable values are returned as "n/a" from lsb_release, skip these.

$ lsb_release -a
LSB Version:    1.4
Distributor ID: Arch
Description:    Arch Linux
Release:        rolling
Codename:       n/a
2021-02-04 13:26:39 +01:00
8fb51627b2 Improve initial setup for systems with many CPUs
Resolves #435
2021-02-02 18:03:17 +01:00
b612256486 Leave less right margin next to long (text) meters 2021-02-02 11:07:29 +01:00
ba630e8ad5 Make descriptions to -shelp available so people find threads
Fixes #511
2021-02-02 10:08:59 +01:00
ef87877826 Fix typo, align with man page, drop dots 2021-02-02 10:08:59 +01:00
f3eab4e796 Explain historic naming of Light-Weight Processes column aka threads 2021-02-02 10:08:59 +01:00
de3e271206 Merge branch 'comm' of cgzones/htop
Already in Debian so making sure upstream has it, too
2021-02-02 09:56:32 +01:00
12208af777 DiD: Avoid negative cmdlineBasenameOffset 2021-02-01 22:09:39 +01:00
12f5f06e88 Check for sortTimeout to not run towards -inf
Seems to happen on Mac OS "Big Sur" (~forced application sleep)
Partial fix for #510
2021-02-01 16:02:31 +01:00
3808b3b553 Remove unused key definitions 2021-02-01 16:01:04 +01:00
06b5828dc4 Fix Shift+Function key on Qt-based terminals
Fixes #508.
2021-02-01 09:02:36 +01:00
8bd543562b Quote SYSCONFDIR definition
As SYSCONFDIR is a compile time string literal, use compile time string
concatenation instead of a runtime one.

Also drop related TODO, cause we indeed using the correct way of getting
$sysconfdir from autoconf
2021-01-31 21:44:34 +01:00
06b1674aa6 Improve handling when selected last process entry
If the last process entry is selected and the process dies, stay at the
end of the list and do not jump to the start.

Also if the last entry is selected keep, after rebuilding the process
list due to a new scan, the last entry selected.
2021-01-31 21:44:00 +01:00
51e79ddc07 [#480] SysArchMeter to view kernel/arch info
At start, SysArchMeter calls the uname function to obtain the kernel
version and architecture. If available, the distro version is obtained
by calling lsb_release. The obtained values are stored in static
variables and used when updating the meter.
2021-01-31 20:08:09 +01:00
7bfa466abe Linux: silence UBSAN implicit conversions
pgrp and session might be -1

linux/LinuxProcessList.c:312:20: runtime error: implicit conversion from type 'unsigned long' of value 18446744073709551615 (64-bit, unsigned) to type 'unsigned int' changed the value to 4294967295 (32-bit, unsigned)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior linux/LinuxProcessList.c:312:20 in
linux/LinuxProcessList.c:314:23: runtime error: implicit conversion from type 'unsigned long' of value 18446744073709551615 (64-bit, unsigned) to type 'unsigned int' changed the value to 4294967295 (32-bit, unsigned)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior linux/LinuxProcessList.c:314:23 in
2021-01-30 14:21:26 +01:00
1014e897a7 Linux: document /proc/PID/stat field parsing 2021-01-30 14:21:26 +01:00
69efa94f9f Use String_eq wrapper instead of raw strcmp 2021-01-30 14:21:26 +01:00
04cf590967 FreeBSD: drop minflt and implement majflt 2021-01-30 14:21:26 +01:00
46370231e3 FreeBSD: drop unused idle thread code 2021-01-30 14:21:26 +01:00
8c43856380 FreeBSD: populate processor field 2021-01-30 14:21:26 +01:00
79620d01c0 FreeBSD: drop unused jail_errmsg variable 2021-01-30 14:21:26 +01:00
399add39ca FreeBSD: simplify kernel thread logic 2021-01-30 14:21:26 +01:00
56c4055fd1 FreeBSD: drop unused ProcessList fields 2021-01-30 14:21:26 +01:00
fa499fc155 FreeBSD: misc 2021-01-30 14:21:26 +01:00
fdaa15bd8d Linux: overhaul io process fields
- avoid UBSAN conversions
- print N/A on no data (i.e. as unprivileged user)
- fix rate calculation to show bytes (instead of a thousandth)
- print bytes as human number (i.e. 8MB) instead of 8388608
- stabilize sorting by adjusting NAN values to very tiny negative number
2021-01-30 14:21:26 +01:00
fee744abd2 Update generic process field display
- sort cases by identifier
- use check snprintf
- color nice value of 0 as gray
- color cpu and memory percentages of 0.0 as gray
- color number of threads of 1 as gray
- color idle and sleeping state as gray
- color tgid matching pid (indicating main thread) as gray
2021-01-30 14:21:26 +01:00
d5de1bc23d Overhaul sorting of state process column
Do not sort by ascii value of the state identifier, sort by relevance
2021-01-30 14:21:26 +01:00
a3c8285237 Refactor to tty_nr process field display
If no terminal name can be found, fall back to generic display method
with major and minor device numbers.

Print special value '(none)' in case both are zero.
2021-01-30 14:21:26 +01:00
03d6345c89 Process: document process fields
Drop unused fields 'flags' and 'exit_signal'
2021-01-30 14:21:26 +01:00
93378b9ee5 fix typo/missing newline for --enable-static
https://i.imgur.com/byraZxG.png
2021-01-30 13:20:09 +01:00
3acf28c259 Unsupported: pass compilation 2021-01-29 14:12:44 +01:00
bd694c0ce6 Do not call exit(3) in signal handler
Call safe _exit(2) instead
2021-01-29 12:38:30 +01:00
fd8c0611af Use different function on different detection method to avoid caching
Using the same function for the same library causes AC_CHECK_LIB to use
cached results.
Since we change the detection method via different or no
ncurses(5|6)-config invocation, avoid such caching by using different
functions.
2021-01-28 23:40:37 +01:00
f27bab470b Drop duplicate option
Option subdir-objects is now define in configure.ac
2021-01-28 23:40:37 +01:00
34da6fdadb Avoid syntax injection inside code block
Co-authored-by: BenBE <BenBE@geshi.org>
2021-01-28 09:21:18 +01:00
e54a790b14 TraceScreen: draw panel header 2021-01-28 09:21:18 +01:00
3c61813ea6 InfoScreen: add mouse wheel scroll 2021-01-28 09:21:18 +01:00
fd45845829 InfoScreen: fix mouse selection 2021-01-28 09:21:18 +01:00
92fb69f5a0 Merge branch 'todo' of cgzones/htop
Make CRT_init get an instance of Settings instead of a separate colorScheme
2021-01-28 07:36:14 +01:00
5644d0194b Merge branch 'default_libcap' (configure overhaul) of cgzones/htop 2021-01-28 07:31:15 +01:00
6dba60f6bd Pass Settings to CRT_init
Resolve todo
2021-01-27 17:14:15 +01:00
211121f060 Drop invalid todo
The surrounding code has nothing to do with colors
2021-01-27 17:06:21 +01:00
d77703b3dc ci: use as-needed linker flag in gcc full-featured build
Test whether there are any linking issues
2021-01-27 15:36:50 +01:00
3035e29e74 Use typedef names instead of raw struct ones 2021-01-26 21:16:23 +01:00
575edffb4b Add configure option to create static htop binary 2021-01-25 18:01:39 +01:00
759a34039c configure: fail immediately on missing requirement 2021-01-25 17:57:21 +01:00
38b6a0148f configure: misc modernizations
- require autoconf version 2.69
  was released in 2012 and one still can configure and build on older
  systems (just not generate the configure script)
- use modern C99 compiler check
- drop obsolete checks: AC_C_CONST, AC_FUNC_CLOSEDIR_VOID, AC_FUNC_STAT
- drop AC_HEADER_STDBOOL in favor of C99 compatibility
2021-01-25 17:57:21 +01:00
f3623b7880 configure: reformat for improved reabability 2021-01-25 17:57:21 +01:00
5e103ff9d1 configure: overhaul option handling
Switch Linux capabilities default from "no" to "check"

Document default settings

Use more readable formatting
2021-01-25 17:54:37 +01:00
0f04714a03 Fix possible division by zero
Do not pass a nmemb of 0 to calloc, cause it's unportable and forbidden
with our wrapper.

Found by Coverity
2021-01-25 17:33:29 +01:00
2ec44098f9 Allow meters in text mode to span empty neighbors to the right
Closes: #484
2021-01-23 15:20:47 +01:00
6f6e0ec571 Update copyright for 2021 2021-01-22 20:06:51 +01:00
d269d7247f Merge branch 'mop-up-sort-mess' of fasterit/htop 2021-01-22 16:26:42 +01:00
041feeca18 Add note to users about sort logic changes to ChangeLog 2021-01-22 16:24:33 +01:00
074703bd5c Implement stable tie-breaker and add more defaultSortDesc fields as per cgzones' suggestions, simplify Process_compare flow from BenBE 2021-01-22 09:57:44 +01:00
4dadbe3b34 configure: add -Winit-self warning 2021-01-21 19:49:07 +01:00
4531b31d92 Sort out the mess around column sorting that had accumulated over time 2021-01-21 14:27:23 +01:00
b20bb543ce Find the correct library for clock_gettime before trying to use it
Otherwise if clock_gettime is librt then this code will incorrectly believe
that the function does not exist at all.
2021-01-20 15:04:03 +01:00
03824da684 Linux: individual huge page values in the huge page meter 2021-01-19 18:06:48 +01:00
4d85848988 Linux: handle hugepages
Subtract hugepages from normal memory.
Add a HugePageMeter.

Closes: #447
2021-01-19 18:06:48 +01:00
71f51a20c1 Define PATH_MAX for GNU/hurd
Otherwise fails with
"> linux/LinuxProcessList.c:889:20: error: ‘PATH_MAX’ undeclared (first use in this function)"
2021-01-16 12:31:44 +01:00
1f20c0fb3d Linux: fall back to cpuinfo on slow scaling_cur_freq read
On some AMD and Intel CPUs read()ing scaling_cur_freq is quite slow
(> 1ms). This delay accumulates for every core.
If the read on CPU 0 takes longer than 500us bail out and fall back to
reading the frequencies from /proc/cpuinfo.
Once the condition has been met, bail out early for the next couple of
scans.

Closes: #471
2021-01-15 20:55:53 +01:00
b5a5e83470 LED Meter: display wide characters and restore non-wide ncurses support
Print wide characters, like degree sign, properly via mvadd_wch().
Ignore attributes when returning value from RichString_getCharVal() in
non-wide ncurses mode to test against raw characters.
2021-01-15 20:41:10 +01:00
b9adc30b86 RichString: implement safe rewind
The current rewind logic causes issues when rewinding over the short
string optimization boundary.
2021-01-15 20:41:10 +01:00
08ac22ddb9 RichString: refactor writing limited amount of columns
Closes: #468
2021-01-15 20:41:10 +01:00
ceee96dcba Do not try to set not owned capabilities
If the process has already less capabilities than we are trying to keep,
do not try to set them.
2021-01-13 22:12:06 +01:00
5fde0e0127 RichString_appendChr: add parameter to set attributes
Allows to set attributes when padding process fields in non-wide ncurses
mode.

Closes: #475
2021-01-13 19:22:33 +01:00
78b993dbb4 kfreeBSD: include config.h for _GNU_SOURCE (part 2)
strcasestr(3) is a GNU extension and when compiling freebsd/Platform.c
on kfreebsd for Debian <string.h> is included before we define
_GNU_SOURCE, so the function is not available.

In file included from ./Object.h:16,
                 from ./ListItem.h:12,
                 from ./Meter.h:16,
                 from ./Header.h:10,
                 from ./Action.h:15,
                 from freebsd/Platform.h:13,
                 from freebsd/Platform.c:8:
./XUtils.h: In function ‘String_contains_i’:
./XUtils.h:43:11: warning: implicit declaration of function ‘strcasestr’; did you mean ‘strcasecmp’? [-Wimplicit-function-declaration]
   43 |    return strcasestr(s1, s2) != NULL;
      |           ^~~~~~~~~~
      |           strcasecmp
./XUtils.h:43:30: warning: comparison between pointer and integer
   43 |    return strcasestr(s1, s2) != NULL;
      |                              ^~
In file included from ./Object.h:16,
                 from ./ProcessList.h:16,
                 from freebsd/FreeBSDProcessList.h:15,
                 from freebsd/FreeBSDProcessList.c:8:
./XUtils.h: In function ‘String_contains_i’:
./XUtils.h:43:11: warning: implicit declaration of function ‘strcasestr’; did you mean ‘strcasecmp’? [-Wimplicit-function-declaration]
   43 |    return strcasestr(s1, s2) != NULL;
      |           ^~~~~~~~~~
      |           strcasecmp
./XUtils.h:43:30: warning: comparison between pointer and integer
   43 |    return strcasestr(s1, s2) != NULL;
      |                              ^~
2021-01-12 19:05:46 +01:00
47cebafd77 kfreeBSD: include config.h for _GNU_SOURCE
strcasestr(3) is a GNU extension and when compiling freebsd/Platform.c
on kfreebsd for Debian <string.h> is included before we define
_GNU_SOURCE, so the function is not available.

In file included from ./Object.h:16,
                 from ./ListItem.h:12,
                 from ./Meter.h:16,
                 from ./Header.h:10,
                 from ./Action.h:15,
                 from freebsd/Platform.h:13,
                 from freebsd/Platform.c:8:
./XUtils.h: In function ‘String_contains_i’:
./XUtils.h:43:11: warning: implicit declaration of function ‘strcasestr’; did you mean ‘strcasecmp’? [-Wimplicit-function-declaration]
   43 |    return strcasestr(s1, s2) != NULL;
      |           ^~~~~~~~~~
      |           strcasecmp
./XUtils.h:43:30: warning: comparison between pointer and integer
   43 |    return strcasestr(s1, s2) != NULL;
      |                              ^~
In file included from ./Object.h:16,
                 from ./ProcessList.h:16,
                 from freebsd/FreeBSDProcessList.h:15,
                 from freebsd/FreeBSDProcessList.c:8:
./XUtils.h: In function ‘String_contains_i’:
./XUtils.h:43:11: warning: implicit declaration of function ‘strcasestr’; did you mean ‘strcasecmp’? [-Wimplicit-function-declaration]
   43 |    return strcasestr(s1, s2) != NULL;
      |           ^~~~~~~~~~
      |           strcasecmp
./XUtils.h:43:30: warning: comparison between pointer and integer
   43 |    return strcasestr(s1, s2) != NULL;
      |                              ^~
2021-01-12 17:02:16 +01:00
c865313e2d Reset cache values when setting comm value
Maybe fixes #361
2021-01-12 16:43:06 +01:00
66dd77aa6b Hashtable: use appropriate return type for nextPrime
The return value is guaranteed to be smaller than SIZE_MAX, so return
size_t (matters on 32 bit architectures).
2021-01-12 16:37:43 +01:00
2b62126aea Mark several non-modified pointer variables const 2021-01-11 23:47:00 +01:00
960f52b783 SELinuxMeter: hardcode SELINUX_MAGIC value
Avoid <linux/magic.h> include, not found by musl-gcc.
The value of SELINUX_MAGIC should really never change.
2021-01-11 23:45:47 +01:00
e6d536dd3f Add compiler hints on memory allocating utility functions 2021-01-11 21:08:58 +01:00
37e186fd66 Linux: Add SwapCached to the swap meter
According to the Linux kernel documentation, "SwapCached" tracks "memory
that once was swapped out, is swapped back in but still also is
in the swapfile (if memory is needed it doesn't need to be swapped out
AGAIN because it is already in the swapfile. This saves I/O)."
2021-01-11 20:27:47 +01:00
f4404effa4 Add option to drop Linux capabilities
Conflicts with setuid support, but that is commonly not enabled.
2021-01-11 20:19:51 +01:00
d72b0a682e Mark several non-modified pointer variables const 2021-01-11 20:12:34 +01:00
1b2d48bc9a Remove dead code 2021-01-11 20:12:34 +01:00
d9240999e9 Process: drop commLen
It is only used on Linux to optimize memory handling in case the command
changes to a smaller-or-equal string.

This "optimization" however causes more code bloat and maintenance cost
on string handling issues than it gains.
2021-01-11 20:12:34 +01:00
70f48f1f44 Add wrapper function for free and strdup
Reduces code in callers and helps avoiding memory leaks.
2021-01-11 20:12:34 +01:00
958112c5a3 Refactor setting filter and use safe strncpy 2021-01-11 20:12:34 +01:00
a118928dee XUtils: add safe strncpy implementation
The standard strncpy fails to null-terminate the destination in case
the source is longer than the passed size.
2021-01-11 20:12:34 +01:00
3715301fe3 Drop always false condition
The previous if conditional branch would have been taken
in case this condition would be true,
2021-01-11 20:12:34 +01:00
d53398fb48 Fix git log remainder in ChangeLog 2021-01-11 19:40:19 +01:00
58ce887d14 Update version number to 3.0.6-dev to identify git repo builds 2021-01-11 18:59:55 +01:00
236 changed files with 14525 additions and 4237 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
open_collective: htop

View File

@ -13,11 +13,11 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install Dependencies - name: Install Dependencies
run: sudo apt-get install libncursesw5-dev run: sudo apt-get install --no-install-recommends libncursesw5-dev
- name: Bootstrap - name: Bootstrap
run: ./autogen.sh run: ./autogen.sh
- name: Configure - name: Configure
run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --without-sensors run: ./configure --enable-werror --enable-affinity --disable-unicode --disable-sensors
- name: Enable compatibility modes - name: Enable compatibility modes
run: | run: |
sed -i 's/#define HAVE_FSTATAT 1/#undef HAVE_FSTATAT/g' config.h sed -i 's/#define HAVE_FSTATAT 1/#undef HAVE_FSTATAT/g' config.h
@ -26,31 +26,72 @@ jobs:
- name: Build - name: Build
run: make -k run: make -k
- name: Distcheck - name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors" run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-affinity --disable-unicode --disable-sensors"
build-ubuntu-latest-minimal-clang: build-ubuntu-latest-minimal-clang:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
CC: clang-11 CC: clang-12
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: install clang repo - name: install clang repo
run: | run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add - 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-11 main' -y sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main' -y
sudo apt-get update -q sudo apt-get update -q
- name: Install Dependencies - name: Install Dependencies
run: sudo apt-get install clang-11 libncursesw5-dev run: sudo apt-get install --no-install-recommends clang-12 libncursesw5-dev
- name: Bootstrap - name: Bootstrap
run: ./autogen.sh run: ./autogen.sh
- name: Configure - name: Configure
run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --without-sensors run: ./configure --enable-werror --enable-affinity --disable-unicode --disable-sensors
- name: Build - name: Build
run: make -k run: make -k
- name: Distcheck - name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors" run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-affinity --disable-unicode --disable-sensors"
build-ubuntu-latest-full-featured-gcc: build-ubuntu-latest-full-featured-gcc:
runs-on: ubuntu-latest
# Enable LTO, might trigger additional warnings on advanced inlining
env:
CFLAGS: -O3 -g -flto
LDFLAGS: -O3 -g -flto -Wl,--as-needed
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: sudo apt-get install --no-install-recommends libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-delayacct --enable-sensors --enable-capabilities
- name: Build
run: make -k
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-delayacct --enable-sensors --enable-capabilities'
build-ubuntu-latest-full-featured-clang:
runs-on: ubuntu-latest
env:
CC: clang-12
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/focal/ llvm-toolchain-focal-12 main' -y
sudo apt-get update -q
- name: Install Dependencies
run: sudo apt-get install --no-install-recommends clang-12 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-delayacct --enable-sensors --enable-capabilities
- name: Build
run: make -k
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-delayacct --enable-sensors --enable-capabilities'
build-ubuntu-latest-gcc-static:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Enable LTO, might trigger additional warnings on advanced inlining # Enable LTO, might trigger additional warnings on advanced inlining
env: env:
@ -59,57 +100,51 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install Dependencies - name: Install Dependencies
run: sudo apt-get install libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev run: sudo apt-get install --no-install-recommends libncursesw5-dev libtinfo-dev libgpm-dev libsensors4-dev libcap-dev
- name: Bootstrap - name: Bootstrap
run: ./autogen.sh run: ./autogen.sh
- name: Configure - name: Configure
run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors run: ./configure --enable-static --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --disable-hwloc --disable-delayacct --enable-sensors --enable-capabilities
- name: Build - name: Build
run: make -k run: make -k
- name: Distcheck - name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors' run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-static --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --disable-hwloc --disable-delayacct --enable-sensors --enable-capabilities'
build-ubuntu-latest-full-featured-clang: build-ubuntu-latest-pcp:
runs-on: ubuntu-latest # Turns out 'ubuntu-latest' can be older than 20.04, we want PCP v5+
env: runs-on: ubuntu-20.04
CC: clang-11
steps: steps:
- uses: actions/checkout@v2 - 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-11 main' -y
sudo apt-get update -q
- name: Install Dependencies - name: Install Dependencies
run: sudo apt-get install clang-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev run: sudo apt-get install --no-install-recommends libpcp3-dev libncursesw5-dev libtinfo-dev libgpm-dev
- name: Bootstrap - name: Bootstrap
run: ./autogen.sh run: ./autogen.sh
- name: Configure - name: Configure
run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors # Until Ubuntu catches up with pcp-5.2.3+, cannot use -werror due to:
# passing argument 2 of pmLookupName from incompatible pointer type
run: ./configure --enable-pcp --enable-unicode
- name: Build - name: Build
run: make -k run: make -k
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors'
build-ubuntu-latest-clang-analyzer: build-ubuntu-latest-clang-analyzer:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
CC: clang-11 CC: clang-12
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: install clang repo - name: install clang repo
run: | run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add - 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-11 main' -y sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main' -y
sudo apt-get update -q sudo apt-get update -q
- name: Install Dependencies - name: Install Dependencies
run: sudo apt-get install clang-11 clang-tools-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev run: sudo apt-get install --no-install-recommends clang-12 clang-tools-12 libncursesw5-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev
- name: Bootstrap - name: Bootstrap
run: ./autogen.sh run: ./autogen.sh
- name: Configure - name: Configure
run: scan-build-11 -analyze-headers --status-bugs ./configure --enable-debug --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors run: scan-build-12 -analyze-headers --status-bugs ./configure --enable-debug --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-delayacct --enable-sensors --enable-capabilities
- name: Build - name: Build
run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)" run: scan-build-12 -analyze-headers --status-bugs make -j"$(nproc)"
build-macos-latest-clang: build-macos-latest-clang:
runs-on: macOS-latest runs-on: macOS-latest

8
.gitignore vendored
View File

@ -1,5 +1,6 @@
# the binary: # the binaries:
htop htop
pcp-htop
# all object files # all object files
*.o *.o
@ -36,6 +37,7 @@ config.sub
configure configure
depcomp depcomp
htop.1 htop.1
pcp-htop.5
install-sh install-sh
libtool libtool
ltmain.sh ltmain.sh
@ -45,3 +47,7 @@ stamp-h1
# files related to valgrind/callgrind # files related to valgrind/callgrind
callgrind.out.* callgrind.out.*
# IDE workspace configurations
/.idea/
/.vscode/

View File

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

275
Action.c
View File

@ -13,9 +13,10 @@ in the source distribution for its full text.
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include "CRT.h"
#include "CategoriesPanel.h" #include "CategoriesPanel.h"
#include "CommandScreen.h" #include "CommandScreen.h"
#include "CRT.h" #include "DynamicColumn.h"
#include "EnvScreen.h" #include "EnvScreen.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Hashtable.h" #include "Hashtable.h"
@ -34,26 +35,25 @@ in the source distribution for its full text.
#include "Vector.h" #include "Vector.h"
#include "XUtils.h" #include "XUtils.h"
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) #if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
#include "Affinity.h" #include "Affinity.h"
#include "AffinityPanel.h" #include "AffinityPanel.h"
#endif #endif
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) { Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) {
Panel* panel = st->panel; MainPanel* mainPanel = st->mainPanel;
Header* header = st->header; Header* header = st->header;
Settings* settings = st->settings;
int y = panel->y; int y = ((Panel*)mainPanel)->y;
ScreenManager* scr = ScreenManager_new(header, settings, st, false); ScreenManager* scr = ScreenManager_new(header, st->settings, st, false);
scr->allowFocusChange = false; scr->allowFocusChange = false;
ScreenManager_add(scr, list, x - 1); ScreenManager_add(scr, list, x);
ScreenManager_add(scr, panel, -1); ScreenManager_add(scr, (Panel*)mainPanel, -1);
Panel* panelFocus; Panel* panelFocus;
int ch; int ch;
bool unfollow = false; bool unfollow = false;
int pid = followProcess ? MainPanel_selectedPid((MainPanel*)panel) : -1; int pid = followProcess ? MainPanel_selectedPid(mainPanel) : -1;
if (followProcess && header->pl->following == -1) { if (followProcess && header->pl->following == -1) {
header->pl->following = pid; header->pl->following = pid;
unfollow = true; unfollow = true;
@ -63,11 +63,11 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
header->pl->following = -1; header->pl->following = -1;
} }
ScreenManager_delete(scr); ScreenManager_delete(scr);
Panel_move(panel, 0, y); Panel_move((Panel*)mainPanel, 0, y);
Panel_resize(panel, COLS, LINES - y - 1); Panel_resize((Panel*)mainPanel, COLS, LINES - y - 1);
if (panelFocus == list && ch == 13) { if (panelFocus == list && ch == 13) {
if (followProcess) { if (followProcess) {
Process* selected = (Process*)Panel_getSelected(panel); const Process* selected = (const Process*)Panel_getSelected((Panel*)mainPanel);
if (selected && selected->pid == pid) if (selected && selected->pid == pid)
return Panel_getSelected(list); return Panel_getSelected(list);
@ -84,12 +84,8 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
static void Action_runSetup(State* st) { static void Action_runSetup(State* st) {
ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true); ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true);
CategoriesPanel* panelCategories = CategoriesPanel_new(scr, st->settings, st->header, st->pl); CategoriesPanel_new(scr, st->settings, st->header, st->pl);
ScreenManager_add(scr, (Panel*) panelCategories, 16); ScreenManager_run(scr, NULL, NULL);
CategoriesPanel_makeMetersPage(panelCategories);
Panel* panelFocus;
int ch;
ScreenManager_run(scr, &panelFocus, &ch);
ScreenManager_delete(scr); ScreenManager_delete(scr);
if (st->settings->changed) { if (st->settings->changed) {
Header_writeBackToSettings(st->header); Header_writeBackToSettings(st->header);
@ -141,7 +137,7 @@ static bool expandCollapse(Panel* panel) {
} }
static bool collapseIntoParent(Panel* panel) { static bool collapseIntoParent(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel); const Process* p = (Process*) Panel_getSelected(panel);
if (!p) if (!p)
return false; return false;
@ -168,16 +164,25 @@ static Htop_Reaction actionSetSortColumn(State* st) {
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel ")); Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by"); Panel_setHeader(sortPanel, "Sort by");
ProcessField* fields = st->settings->fields; const ProcessField* fields = st->settings->fields;
Hashtable* dynamicColumns = st->settings->dynamicColumns;
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 = NULL;
if (fields[i] >= LAST_PROCESSFIELD) {
DynamicColumn* column = Hashtable_get(dynamicColumns, fields[i]);
if (!column)
continue;
name = xStrdup(column->caption ? column->caption : column->name);
} else {
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] == Settings_getActiveSortKey(st->settings)) if (fields[i] == Settings_getActiveSortKey(st->settings))
Panel_setSelected(sortPanel, i); Panel_setSelected(sortPanel, i);
free(name); free(name);
} }
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false); const ListItem* field = (const ListItem*) Action_pickFromVector(st, sortPanel, 14, false);
if (field) { if (field) {
reaction |= Action_setSortKey(st->settings, field->key); reaction |= Action_setSortKey(st->settings, field->key);
} }
@ -207,12 +212,12 @@ static Htop_Reaction actionSortByTime(State* st) {
static Htop_Reaction actionToggleKernelThreads(State* st) { static Htop_Reaction actionToggleKernelThreads(State* st) {
st->settings->hideKernelThreads = !st->settings->hideKernelThreads; st->settings->hideKernelThreads = !st->settings->hideKernelThreads;
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS; return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING;
} }
static Htop_Reaction actionToggleUserlandThreads(State* st) { static Htop_Reaction actionToggleUserlandThreads(State* st) {
st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads; st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads;
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS; return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING;
} }
static Htop_Reaction actionToggleProgramPath(State* st) { static Htop_Reaction actionToggleProgramPath(State* st) {
@ -227,34 +232,47 @@ static Htop_Reaction actionToggleMergedCommand(State* st) {
static Htop_Reaction actionToggleTreeView(State* st) { static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView; st->settings->treeView = !st->settings->treeView;
if (st->settings->treeView) {
st->settings->treeDirection = 1;
}
ProcessList_expandTree(st->pl); if (!st->settings->allBranchesCollapsed)
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;
} }
static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) {
st->settings->allBranchesCollapsed = !st->settings->allBranchesCollapsed;
if (st->settings->allBranchesCollapsed)
ProcessList_collapseAllBranches(st->pl);
else
ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionIncFilter(State* st) { static Htop_Reaction actionIncFilter(State* st) {
IncSet* inc = ((MainPanel*)st->panel)->inc; IncSet* inc = (st->mainPanel)->inc;
IncSet_activate(inc, INC_FILTER, st->panel); IncSet_activate(inc, INC_FILTER, (Panel*)st->mainPanel);
st->pl->incFilter = IncSet_filter(inc); st->pl->incFilter = IncSet_filter(inc);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
} }
static Htop_Reaction actionIncSearch(State* st) { static Htop_Reaction actionIncSearch(State* st) {
IncSet_reset(((MainPanel*)st->panel)->inc, INC_SEARCH); IncSet_reset(st->mainPanel->inc, INC_SEARCH);
IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel); IncSet_activate(st->mainPanel->inc, INC_SEARCH, (Panel*)st->mainPanel);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; 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); if (Settings_isReadonly())
return HTOP_OK;
bool changed = changePriority(st->mainPanel, -1);
return changed ? HTOP_REFRESH : HTOP_OK; return changed ? HTOP_REFRESH : HTOP_OK;
} }
static Htop_Reaction actionLowerPriority(State* st) { static Htop_Reaction actionLowerPriority(State* st) {
bool changed = changePriority((MainPanel*)st->panel, 1); if (Settings_isReadonly())
return HTOP_OK;
bool changed = changePriority(st->mainPanel, 1);
return changed ? HTOP_REFRESH : HTOP_OK; return changed ? HTOP_REFRESH : HTOP_OK;
} }
@ -262,11 +280,11 @@ static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings); Settings_invertSortOrder(st->settings);
if (st->pauseProcessUpdate) if (st->pauseProcessUpdate)
ProcessList_sort(st->pl); ProcessList_sort(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING;
} }
static Htop_Reaction actionExpandOrCollapse(State* st) { static Htop_Reaction actionExpandOrCollapse(State* st) {
bool changed = expandCollapse(st->panel); bool changed = expandCollapse((Panel*)st->mainPanel);
return changed ? HTOP_RECALCULATE : HTOP_OK; return changed ? HTOP_RECALCULATE : HTOP_OK;
} }
@ -274,7 +292,7 @@ static Htop_Reaction actionCollapseIntoParent(State* st) {
if (!st->settings->treeView) { if (!st->settings->treeView) {
return HTOP_OK; return HTOP_OK;
} }
bool changed = collapseIntoParent(st->panel); bool changed = collapseIntoParent((Panel*)st->mainPanel);
return changed ? HTOP_RECALCULATE : HTOP_OK; return changed ? HTOP_RECALCULATE : HTOP_OK;
} }
@ -287,13 +305,14 @@ static Htop_Reaction actionQuit(ATTR_UNUSED State* st) {
} }
static Htop_Reaction actionSetAffinity(State* st) { static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1) if (Settings_isReadonly())
return HTOP_OK; return HTOP_OK;
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) if (st->pl->activeCPUs == 1)
Panel* panel = st->panel; return HTOP_OK;
Process* p = (Process*) Panel_getSelected(panel); #if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
const Process* p = (const Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!p) if (!p)
return HTOP_OK; return HTOP_OK;
@ -303,33 +322,36 @@ static Htop_Reaction actionSetAffinity(State* st) {
int width; int width;
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width); Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width);
width += 1; /* we add a gap between the panels */
Affinity_delete(affinity1); Affinity_delete(affinity1);
void* set = Action_pickFromVector(st, affinityPanel, width, true); const void* set = Action_pickFromVector(st, affinityPanel, width, true);
if (set) { if (set) {
Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl); Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, Affinity_set, (Arg) { .v = affinity2 }, NULL); bool ok = MainPanel_foreachProcess(st->mainPanel, Affinity_set, (Arg) { .v = affinity2 }, NULL);
if (!ok) if (!ok)
beep(); beep();
Affinity_delete(affinity2); Affinity_delete(affinity2);
} }
Object_delete(affinityPanel); Object_delete(affinityPanel);
#endif
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
#else
return HTOP_OK;
#endif
} }
static Htop_Reaction actionKill(State* st) { static Htop_Reaction actionKill(State* st) {
if (Settings_isReadonly())
return HTOP_OK;
Panel* signalsPanel = SignalsPanel_new(); Panel* signalsPanel = SignalsPanel_new();
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true); const ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 14, true);
if (sgn) { if (sgn && sgn->key != 0) {
if (sgn->key != 0) { Panel_setHeader((Panel*)st->mainPanel, "Sending...");
Panel_setHeader(st->panel, "Sending..."); Panel_draw((Panel*)st->mainPanel, false, true, true, State_hideFunctionBar(st));
Panel_draw(st->panel, false, true, true, State_hideFunctionBar(st)); refresh();
refresh(); MainPanel_foreachProcess(st->mainPanel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL);
MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL); napms(500);
napms(500);
}
} }
Panel_delete((Object*)signalsPanel); Panel_delete((Object*)signalsPanel);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
@ -342,7 +364,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, false); const ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 19, false);
if (picked) { if (picked) {
if (picked == allUsers) { if (picked == allUsers) {
st->pl->userId = (uid_t)-1; st->pl->userId = (uid_t)-1;
@ -355,22 +377,21 @@ static Htop_Reaction actionFilterByUser(State* st) {
} }
Htop_Reaction Action_follow(State* st) { Htop_Reaction Action_follow(State* st) {
st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel); st->pl->following = MainPanel_selectedPid(st->mainPanel);
Panel_setSelectionColor(st->panel, PANEL_SELECTION_FOLLOW); Panel_setSelectionColor((Panel*)st->mainPanel, PANEL_SELECTION_FOLLOW);
return HTOP_KEEP_FOLLOWING; return HTOP_KEEP_FOLLOWING;
} }
static Htop_Reaction actionSetup(State* st) { static Htop_Reaction actionSetup(State* st) {
Action_runSetup(st); Action_runSetup(st);
// TODO: shouldn't need this, colors should be dynamic return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR | HTOP_RESIZE;
int headerHeight = Header_calculateHeight(st->header);
Panel_move(st->panel, 0, headerHeight);
Panel_resize(st->panel, COLS, LINES-headerHeight-1);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
} }
static Htop_Reaction actionLsof(State* st) { static Htop_Reaction actionLsof(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel); if (Settings_isReadonly())
return HTOP_OK;
const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!p) if (!p)
return HTOP_OK; return HTOP_OK;
@ -383,8 +404,9 @@ static Htop_Reaction actionLsof(State* st) {
} }
static Htop_Reaction actionShowLocks(State* st) { static Htop_Reaction actionShowLocks(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel); const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!p) return HTOP_OK; if (!p)
return HTOP_OK;
ProcessLocksScreen* pls = ProcessLocksScreen_new(p); ProcessLocksScreen* pls = ProcessLocksScreen_new(p);
InfoScreen_run((InfoScreen*)pls); InfoScreen_run((InfoScreen*)pls);
ProcessLocksScreen_delete((Object*)pls); ProcessLocksScreen_delete((Object*)pls);
@ -394,7 +416,10 @@ static Htop_Reaction actionShowLocks(State* st) {
} }
static Htop_Reaction actionStrace(State* st) { static Htop_Reaction actionStrace(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel); if (Settings_isReadonly())
return HTOP_OK;
const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!p) if (!p)
return HTOP_OK; return HTOP_OK;
@ -410,12 +435,12 @@ static Htop_Reaction actionStrace(State* st) {
} }
static Htop_Reaction actionTag(State* st) { static Htop_Reaction actionTag(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel); Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!p) if (!p)
return HTOP_OK; return HTOP_OK;
Process_toggleTag(p); Process_toggleTag(p);
Panel_onKey(st->panel, KEY_DOWN); Panel_onKey((Panel*)st->mainPanel, KEY_DOWN);
return HTOP_OK; return HTOP_OK;
} }
@ -431,49 +456,51 @@ static Htop_Reaction actionTogglePauseProcessUpdate(State* st) {
static const struct { static const struct {
const char* key; const char* key;
bool roInactive;
const char* info; const char* info;
} helpLeft[] = { } helpLeft[] = {
{ .key = " Arrows: ", .info = "scroll process list" }, { .key = " Arrows: ", .roInactive = false, .info = "scroll process list" },
{ .key = " Digits: ", .info = "incremental PID search" }, { .key = " Digits: ", .roInactive = false, .info = "incremental PID search" },
{ .key = " F3 /: ", .info = "incremental name search" }, { .key = " F3 /: ", .roInactive = false, .info = "incremental name search" },
{ .key = " F4 \\: ",.info = "incremental name filtering" }, { .key = " F4 \\: ", .roInactive = false, .info = "incremental name filtering" },
{ .key = " F5 t: ", .info = "tree view" }, { .key = " F5 t: ", .roInactive = false, .info = "tree view" },
{ .key = " p: ", .info = "toggle program path" }, { .key = " p: ", .roInactive = false, .info = "toggle program path" },
{ .key = " m: ", .info = "toggle merged command" }, { .key = " m: ", .roInactive = false, .info = "toggle merged command" },
{ .key = " Z: ", .info = "pause/resume process updates" }, { .key = " Z: ", .roInactive = false, .info = "pause/resume process updates" },
{ .key = " u: ", .info = "show processes of a single user" }, { .key = " u: ", .roInactive = false, .info = "show processes of a single user" },
{ .key = " H: ", .info = "hide/show user process threads" }, { .key = " H: ", .roInactive = false, .info = "hide/show user process threads" },
{ .key = " K: ", .info = "hide/show kernel threads" }, { .key = " K: ", .roInactive = false, .info = "hide/show kernel threads" },
{ .key = " F: ", .info = "cursor follows process" }, { .key = " F: ", .roInactive = false, .info = "cursor follows process" },
{ .key = " + -: ", .info = "expand/collapse tree" }, { .key = " + - *: ", .roInactive = false, .info = "expand/collapse tree/toggle all" },
{ .key = "N P M T: ", .info = "sort by PID, CPU%, MEM% or TIME" }, { .key = "N P M T: ", .roInactive = false, .info = "sort by PID, CPU%, MEM% or TIME" },
{ .key = " I: ", .info = "invert sort order" }, { .key = " I: ", .roInactive = false, .info = "invert sort order" },
{ .key = " F6 > .: ", .info = "select sort column" }, { .key = " F6 > .: ", .roInactive = false, .info = "select sort column" },
{ .key = NULL, .info = NULL } { .key = NULL, .info = NULL }
}; };
static const struct { static const struct {
const char* key; const char* key;
bool roInactive;
const char* info; const char* info;
} helpRight[] = { } helpRight[] = {
{ .key = " Space: ", .info = "tag process" }, { .key = " Space: ", .roInactive = false, .info = "tag process" },
{ .key = " c: ", .info = "tag process and its children" }, { .key = " c: ", .roInactive = false, .info = "tag process and its children" },
{ .key = " U: ", .info = "untag all processes" }, { .key = " U: ", .roInactive = false, .info = "untag all processes" },
{ .key = " F9 k: ", .info = "kill process/tagged processes" }, { .key = " F9 k: ", .roInactive = true, .info = "kill process/tagged processes" },
{ .key = " F7 ]: ", .info = "higher priority (root only)" }, { .key = " F7 ]: ", .roInactive = true, .info = "higher priority (root only)" },
{ .key = " F8 [: ", .info = "lower priority (+ nice)" }, { .key = " F8 [: ", .roInactive = false, .info = "lower priority (+ nice)" },
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) #if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
{ .key = " a: ", .info = "set CPU affinity" }, { .key = " a: ", .roInactive = true, .info = "set CPU affinity" },
#endif #endif
{ .key = " e: ", .info = "show process environment" }, { .key = " e: ", .roInactive = false, .info = "show process environment" },
{ .key = " i: ", .info = "set IO priority" }, { .key = " i: ", .roInactive = true, .info = "set IO priority" },
{ .key = " l: ", .info = "list open files with lsof" }, { .key = " l: ", .roInactive = true, .info = "list open files with lsof" },
{ .key = " x: ", .info = "list file locks of process" }, { .key = " x: ", .roInactive = false, .info = "list file locks of process" },
{ .key = " s: ", .info = "trace syscalls with strace" }, { .key = " s: ", .roInactive = true, .info = "trace syscalls with strace" },
{ .key = " w: ", .info = "wrap process command in multiple lines" }, { .key = " w: ", .roInactive = false, .info = "wrap process command in multiple lines" },
{ .key = " F2 C S: ", .info = "setup" }, { .key = " F2 C S: ", .roInactive = false, .info = "setup" },
{ .key = " F1 h: ", .info = "show this help screen" }, { .key = " F1 h ?: ", .roInactive = false, .info = "show this help screen" },
{ .key = " F10 q: ", .info = "quit" }, { .key = " F10 q: ", .roInactive = false, .info = "quit" },
{ .key = NULL, .info = NULL } { .key = NULL, .info = NULL }
}; };
@ -483,8 +510,6 @@ static inline void addattrstr( int attr, const char* str) {
} }
static Htop_Reaction actionHelp(State* st) { static Htop_Reaction actionHelp(State* st) {
Settings* settings = st->settings;
clear(); clear();
attrset(CRT_colors[HELP_BOLD]); attrset(CRT_colors[HELP_BOLD]);
@ -501,7 +526,7 @@ static Htop_Reaction actionHelp(State* st) {
mvaddstr(line++, 0, "CPU usage bar: "); mvaddstr(line++, 0, "CPU usage bar: ");
addattrstr(CRT_colors[BAR_BORDER], "["); addattrstr(CRT_colors[BAR_BORDER], "[");
if (settings->detailedCPUTime) { if (st->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_SYSTEM], "kernel"); addstr("/"); addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
@ -515,8 +540,8 @@ static Htop_Reaction actionHelp(State* st) {
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_SYSTEM], "kernel"); addstr("/"); addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_GUEST], "virtualiz"); addattrstr(CRT_colors[CPU_GUEST], "virtualized");
addattrstr(CRT_colors[BAR_SHADOW], " used%"); addattrstr(CRT_colors[BAR_SHADOW], " used%");
} }
addattrstr(CRT_colors[BAR_BORDER], "]"); addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
@ -524,14 +549,21 @@ static Htop_Reaction actionHelp(State* st) {
addattrstr(CRT_colors[BAR_BORDER], "["); addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/"); addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/"); addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/");
addattrstr(CRT_colors[MEMORY_SHARED], "shared"); addstr("/");
addattrstr(CRT_colors[MEMORY_CACHE], "cache"); addattrstr(CRT_colors[MEMORY_CACHE], "cache");
addattrstr(CRT_colors[BAR_SHADOW], " used/total"); addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addattrstr(CRT_colors[BAR_BORDER], "]"); addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line++, 0, "Swap bar: "); mvaddstr(line++, 0, "Swap bar: ");
addattrstr(CRT_colors[BAR_BORDER], "["); addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[SWAP], "used"); addattrstr(CRT_colors[SWAP], "used");
#ifdef HTOP_LINUX
addattrstr(CRT_colors[BAR_SHADOW], "/");
addattrstr(CRT_colors[SWAP_CACHE], "cache");
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
#else
addattrstr(CRT_colors[BAR_SHADOW], " used/total"); addattrstr(CRT_colors[BAR_SHADOW], " used/total");
#endif
addattrstr(CRT_colors[BAR_BORDER], "]"); addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen."); mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen.");
@ -544,26 +576,28 @@ static Htop_Reaction actionHelp(State* st) {
line++; line++;
const bool readonly = Settings_isReadonly();
int item; int item;
for (item = 0; helpLeft[item].key; item++) { for (item = 0; helpLeft[item].key; item++) {
attrset(CRT_colors[DEFAULT_COLOR]); attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[DEFAULT_COLOR]);
mvaddstr(line + item, 10, helpLeft[item].info); mvaddstr(line + item, 10, helpLeft[item].info);
attrset(CRT_colors[HELP_BOLD]); attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[HELP_BOLD]);
mvaddstr(line + item, 1, helpLeft[item].key); mvaddstr(line + item, 1, helpLeft[item].key);
if (String_eq(helpLeft[item].key, " H: ")) { if (String_eq(helpLeft[item].key, " H: ")) {
attrset(CRT_colors[PROCESS_THREAD]); attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[PROCESS_THREAD]);
mvaddstr(line + item, 33, "threads"); mvaddstr(line + item, 33, "threads");
} else if (String_eq(helpLeft[item].key, " K: ")) { } else if (String_eq(helpLeft[item].key, " K: ")) {
attrset(CRT_colors[PROCESS_THREAD]); attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[PROCESS_THREAD]);
mvaddstr(line + item, 27, "threads"); mvaddstr(line + item, 27, "threads");
} }
} }
int leftHelpItems = item; int leftHelpItems = item;
for (item = 0; helpRight[item].key; item++) { for (item = 0; helpRight[item].key; item++) {
attrset(CRT_colors[HELP_BOLD]); attrset((helpRight[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[HELP_BOLD]);
mvaddstr(line + item, 41, helpRight[item].key); mvaddstr(line + item, 41, helpRight[item].key);
attrset(CRT_colors[DEFAULT_COLOR]); attrset((helpRight[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[DEFAULT_COLOR]);
mvaddstr(line + item, 50, helpRight[item].info); mvaddstr(line + item, 50, helpRight[item].info);
} }
line += MAXIMUM(leftHelpItems, item); line += MAXIMUM(leftHelpItems, item);
@ -576,28 +610,28 @@ static Htop_Reaction actionHelp(State* st) {
CRT_readKey(); CRT_readKey();
clear(); clear();
return HTOP_RECALCULATE | HTOP_REDRAW_BAR; return HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_KEEP_FOLLOWING;
} }
static Htop_Reaction actionUntagAll(State* st) { static Htop_Reaction actionUntagAll(State* st) {
for (int i = 0; i < Panel_size(st->panel); i++) { for (int i = 0; i < Panel_size((Panel*)st->mainPanel); i++) {
Process* p = (Process*) Panel_get(st->panel, i); Process* p = (Process*) Panel_get((Panel*)st->mainPanel, i);
p->tag = false; p->tag = false;
} }
return HTOP_REFRESH; return HTOP_REFRESH;
} }
static Htop_Reaction actionTagAllChildren(State* st) { static Htop_Reaction actionTagAllChildren(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel); Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!p) if (!p)
return HTOP_OK; return HTOP_OK;
tagAllChildren(st->panel, p); tagAllChildren((Panel*)st->mainPanel, p);
return HTOP_OK; return HTOP_OK;
} }
static Htop_Reaction actionShowEnvScreen(State* st) { static Htop_Reaction actionShowEnvScreen(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel); Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!p) if (!p)
return HTOP_OK; return HTOP_OK;
@ -610,7 +644,7 @@ static Htop_Reaction actionShowEnvScreen(State* st) {
} }
static Htop_Reaction actionShowCommandScreen(State* st) { static Htop_Reaction actionShowCommandScreen(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel); Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!p) if (!p)
return HTOP_OK; return HTOP_OK;
@ -624,6 +658,7 @@ static Htop_Reaction actionShowCommandScreen(State* st) {
void Action_setBindings(Htop_Action* keys) { void Action_setBindings(Htop_Action* keys) {
keys[' '] = actionTag; keys[' '] = actionTag;
keys['*'] = actionExpandOrCollapseAllBranches;
keys['+'] = actionExpandOrCollapse; keys['+'] = actionExpandOrCollapse;
keys[','] = actionSetSortColumn; keys[','] = actionSetSortColumn;
keys['-'] = actionExpandOrCollapse; keys['-'] = actionExpandOrCollapse;

View File

@ -20,22 +20,26 @@ in the source distribution for its full text.
#include "Settings.h" #include "Settings.h"
#include "UsersTable.h" #include "UsersTable.h"
typedef enum { typedef enum {
HTOP_OK = 0x00, HTOP_OK = 0x00,
HTOP_REFRESH = 0x01, HTOP_REFRESH = 0x01,
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH HTOP_RECALCULATE = 0x02 | HTOP_REFRESH,
HTOP_SAVE_SETTINGS = 0x04, HTOP_SAVE_SETTINGS = 0x04,
HTOP_KEEP_FOLLOWING = 0x08, HTOP_KEEP_FOLLOWING = 0x08,
HTOP_QUIT = 0x10, HTOP_QUIT = 0x10,
HTOP_REDRAW_BAR = 0x20, HTOP_REDRAW_BAR = 0x20,
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH HTOP_UPDATE_PANELHDR = 0x40 | HTOP_REFRESH,
HTOP_RESIZE = 0x80 | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR,
} Htop_Reaction; } Htop_Reaction;
struct MainPanel_; // IWYU pragma: keep
typedef struct State_ { typedef struct State_ {
Settings* settings; Settings* settings;
UsersTable* ut; UsersTable* ut;
ProcessList* pl; ProcessList* pl;
Panel* panel; struct MainPanel_* mainPanel;
Header* header; Header* header;
bool pauseProcessUpdate; bool pauseProcessUpdate;
bool hideProcessSelection; bool hideProcessSelection;

View File

@ -14,7 +14,7 @@ in the source distribution for its full text.
#include "XUtils.h" #include "XUtils.h"
#ifdef HAVE_LIBHWLOC #if defined(HAVE_LIBHWLOC)
#include <hwloc.h> #include <hwloc.h>
#include <hwloc/bitmap.h> #include <hwloc/bitmap.h>
#ifdef __linux__ #ifdef __linux__
@ -22,7 +22,7 @@ in the source distribution for its full text.
#else #else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS #define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif #endif
#elif defined(HAVE_LINUX_AFFINITY) #elif defined(HAVE_AFFINITY)
#include <sched.h> #include <sched.h>
#endif #endif
@ -30,7 +30,7 @@ in the source distribution for its full text.
Affinity* Affinity_new(ProcessList* pl) { Affinity* Affinity_new(ProcessList* pl) {
Affinity* this = xCalloc(1, sizeof(Affinity)); Affinity* this = xCalloc(1, sizeof(Affinity));
this->size = 8; this->size = 8;
this->cpus = xCalloc(this->size, sizeof(int)); this->cpus = xCalloc(this->size, sizeof(unsigned int));
this->pl = pl; this->pl = pl;
return this; return this;
} }
@ -40,32 +40,32 @@ void Affinity_delete(Affinity* this) {
free(this); free(this);
} }
void Affinity_add(Affinity* this, int id) { void Affinity_add(Affinity* this, unsigned int id) {
if (this->used == this->size) { if (this->used == this->size) {
this->size *= 2; this->size *= 2;
this->cpus = xRealloc(this->cpus, sizeof(int) * this->size); this->cpus = xRealloc(this->cpus, sizeof(unsigned int) * this->size);
} }
this->cpus[this->used] = id; this->cpus[this->used] = id;
this->used++; this->used++;
} }
#ifdef HAVE_LIBHWLOC #if defined(HAVE_LIBHWLOC)
Affinity* Affinity_get(Process* proc, ProcessList* pl) { Affinity* Affinity_get(const Process* proc, ProcessList* pl) {
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
bool ok = (hwloc_get_proc_cpubind(pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); bool ok = (hwloc_get_proc_cpubind(pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
Affinity* affinity = NULL; Affinity* affinity = NULL;
if (ok) { if (ok) {
affinity = Affinity_new(pl); affinity = Affinity_new(pl);
if (hwloc_bitmap_last(cpuset) == -1) { if (hwloc_bitmap_last(cpuset) == -1) {
for (int i = 0; i < pl->cpuCount; i++) { for (unsigned int i = 0; i < pl->existingCPUs; i++) {
Affinity_add(affinity, i); Affinity_add(affinity, i);
} }
} else { } else {
unsigned int id; int id;
hwloc_bitmap_foreach_begin(id, cpuset); hwloc_bitmap_foreach_begin(id, cpuset)
Affinity_add(affinity, id); Affinity_add(affinity, (unsigned)id);
hwloc_bitmap_foreach_end(); hwloc_bitmap_foreach_end();
} }
} }
@ -76,7 +76,7 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
bool Affinity_set(Process* proc, Arg arg) { bool Affinity_set(Process* proc, Arg arg) {
Affinity* this = arg.v; 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 (unsigned int i = 0; i < this->used; i++) {
hwloc_bitmap_set(cpuset, this->cpus[i]); hwloc_bitmap_set(cpuset, this->cpus[i]);
} }
bool ok = (hwloc_set_proc_cpubind(this->pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); bool ok = (hwloc_set_proc_cpubind(this->pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
@ -84,16 +84,16 @@ bool Affinity_set(Process* proc, Arg arg) {
return ok; return ok;
} }
#elif defined(HAVE_LINUX_AFFINITY) #elif defined(HAVE_AFFINITY)
Affinity* Affinity_get(Process* proc, ProcessList* pl) { Affinity* Affinity_get(const Process* proc, ProcessList* pl) {
cpu_set_t cpuset; cpu_set_t cpuset;
bool ok = (sched_getaffinity(proc->pid, sizeof(cpu_set_t), &cpuset) == 0); bool ok = (sched_getaffinity(proc->pid, sizeof(cpu_set_t), &cpuset) == 0);
if (!ok) if (!ok)
return NULL; return NULL;
Affinity* affinity = Affinity_new(pl); Affinity* affinity = Affinity_new(pl);
for (int i = 0; i < pl->cpuCount; i++) { for (unsigned int i = 0; i < pl->existingCPUs; i++) {
if (CPU_ISSET(i, &cpuset)) { if (CPU_ISSET(i, &cpuset)) {
Affinity_add(affinity, i); Affinity_add(affinity, i);
} }
@ -105,7 +105,7 @@ bool Affinity_set(Process* proc, Arg arg) {
Affinity* this = arg.v; 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 (unsigned int i = 0; i < this->used; i++) {
CPU_SET(this->cpus[i], &cpuset); CPU_SET(this->cpus[i], &cpuset);
} }
bool ok = (sched_setaffinity(proc->pid, sizeof(unsigned long), &cpuset) == 0); bool ok = (sched_setaffinity(proc->pid, sizeof(unsigned long), &cpuset) == 0);

View File

@ -12,7 +12,7 @@ in the source distribution for its full text.
#include "ProcessList.h" #include "ProcessList.h"
#if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY) #if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)
#include <stdbool.h> #include <stdbool.h>
#include "Object.h" #include "Object.h"
@ -20,30 +20,30 @@ in the source distribution for its full text.
#endif #endif
#if defined(HAVE_LIBHWLOC) && defined(HAVE_LINUX_AFFINITY) #if defined(HAVE_LIBHWLOC) && defined(HAVE_AFFINITY)
#error hwloc and linux affinity are mutual exclusive. #error hwloc and affinity support are mutual exclusive.
#endif #endif
typedef struct Affinity_ { typedef struct Affinity_ {
ProcessList* pl; ProcessList* pl;
int size; unsigned int size;
int used; unsigned int used;
int* cpus; unsigned int* cpus;
} Affinity; } Affinity;
Affinity* Affinity_new(ProcessList* pl); Affinity* Affinity_new(ProcessList* pl);
void Affinity_delete(Affinity* this); void Affinity_delete(Affinity* this);
void Affinity_add(Affinity* this, int id); void Affinity_add(Affinity* this, unsigned int id);
#if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY) #if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)
Affinity* Affinity_get(Process* proc, ProcessList* pl); Affinity* Affinity_get(const Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Arg arg); bool Affinity_set(Process* proc, Arg arg);
#endif /* HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY */ #endif /* HAVE_LIBHWLOC || HAVE_AFFINITY */
#endif #endif

View File

@ -357,7 +357,7 @@ static const char* const AffinityPanelFunctions[] = {
static const char* const AffinityPanelKeys[] = {"Enter", "Esc", "F1", "F2", "F3"}; 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)}; static const int AffinityPanelEvents[] = {13, 27, KEY_F(1), KEY_F(2), KEY_F(3)};
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) { Panel* AffinityPanel_new(ProcessList* pl, const Affinity* affinity, int* width) {
AffinityPanel* this = AllocThis(AffinityPanel); AffinityPanel* this = AllocThis(AffinityPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, Class(MaskItem), false, FunctionBar_new(AffinityPanelFunctions, AffinityPanelKeys, AffinityPanelEvents)); Panel_init(super, 1, 1, 1, 1, Class(MaskItem), false, FunctionBar_new(AffinityPanelFunctions, AffinityPanelKeys, AffinityPanelEvents));
@ -382,8 +382,11 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
Panel_setHeader(super, "Use CPUs:"); Panel_setHeader(super, "Use CPUs:");
int curCpu = 0; unsigned int curCpu = 0;
for (int i = 0; i < pl->cpuCount; i++) { for (unsigned int i = 0; i < pl->existingCPUs; i++) {
if (!ProcessList_isCPUonline(this->pl, i))
continue;
char number[16]; char number[16];
xSnprintf(number, 9, "CPU %d", Settings_cpuId(pl->settings, i)); xSnprintf(number, 9, "CPU %d", Settings_cpuId(pl->settings, i));
unsigned cpu_width = 4 + strlen(number); unsigned cpu_width = 4 + strlen(number);
@ -418,17 +421,17 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
} }
Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) { Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) {
AffinityPanel* this = (AffinityPanel*) super; const AffinityPanel* this = (AffinityPanel*) super;
Affinity* affinity = Affinity_new(pl); Affinity* affinity = Affinity_new(pl);
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
int i; int i;
hwloc_bitmap_foreach_begin(i, this->workCpuset) hwloc_bitmap_foreach_begin(i, this->workCpuset)
Affinity_add(affinity, i); Affinity_add(affinity, (unsigned)i);
hwloc_bitmap_foreach_end(); hwloc_bitmap_foreach_end();
#else #else
for (int i = 0; i < this->pl->cpuCount; i++) { for (int i = 0; i < Vector_size(this->cpuids); i++) {
MaskItem* item = (MaskItem*)Vector_get(this->cpuids, i); const MaskItem* item = (const MaskItem*)Vector_get(this->cpuids, i);
if (item->value) { if (item->value) {
Affinity_add(affinity, item->cpu); Affinity_add(affinity, item->cpu);
} }

View File

@ -7,13 +7,14 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Panel.h"
#include "Affinity.h" #include "Affinity.h"
#include "Panel.h"
#include "ProcessList.h" #include "ProcessList.h"
extern const PanelClass AffinityPanel_class; extern const PanelClass AffinityPanel_class;
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width); Panel* AffinityPanel_new(ProcessList* pl, const Affinity* affinity, int* width);
Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl); Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl);

View File

@ -7,15 +7,17 @@ in the source distribution for its full text.
#include "AvailableColumnsPanel.h" #include "AvailableColumnsPanel.h"
#include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include "ColumnsPanel.h" #include "ColumnsPanel.h"
#include "DynamicColumn.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Hashtable.h"
#include "ListItem.h" #include "ListItem.h"
#include "Object.h" #include "Object.h"
#include "Platform.h"
#include "Process.h" #include "Process.h"
#include "ProvideCurses.h" #include "ProvideCurses.h"
#include "XUtils.h" #include "XUtils.h"
@ -30,6 +32,15 @@ static void AvailableColumnsPanel_delete(Object* object) {
free(this); free(this);
} }
static void AvailableColumnsPanel_insert(AvailableColumnsPanel* this, int at, int key) {
const char* name;
if (key >= LAST_PROCESSFIELD)
name = DynamicColumn_init(key);
else
name = Process_fields[key].name;
Panel_insert(this->columns, at, (Object*) ListItem_new(name, key));
}
static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) { static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
AvailableColumnsPanel* this = (AvailableColumnsPanel*) super; AvailableColumnsPanel* this = (AvailableColumnsPanel*) super;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
@ -43,10 +54,9 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
if (!selected) if (!selected)
break; break;
int key = selected->key;
int at = Panel_getSelectedIndex(this->columns); int at = Panel_getSelectedIndex(this->columns);
Panel_insert(this->columns, at, (Object*) ListItem_new(Process_fields[key].name, key)); AvailableColumnsPanel_insert(this, at, selected->key);
Panel_setSelected(this->columns, at+1); Panel_setSelected(this->columns, at + 1);
ColumnsPanel_update(this->columns); ColumnsPanel_update(this->columns);
result = HANDLED; result = HANDLED;
break; break;
@ -69,14 +79,25 @@ const PanelClass AvailableColumnsPanel_class = {
.eventHandler = AvailableColumnsPanel_eventHandler .eventHandler = AvailableColumnsPanel_eventHandler
}; };
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) { static void AvailableColumnsPanel_addDynamicColumn(ht_key_t key, void* value, void* data) {
AvailableColumnsPanel* this = AllocThis(AvailableColumnsPanel); const DynamicColumn* column = (const DynamicColumn*) value;
Panel* super = (Panel*) this; Panel* super = (Panel*) data;
FunctionBar* fuBar = FunctionBar_new(AvailableColumnsFunctions, NULL, NULL); const char* title = column->caption ? column->caption : column->heading;
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); if (!title)
title = column->name; // fallback to the only mandatory field
char description[256];
xSnprintf(description, sizeof(description), "%s - %s", title, column->description);
Panel_add(super, (Object*) ListItem_new(description, key));
}
Panel_setHeader(super, "Available Columns"); // Handle DynamicColumns entries in the AvailableColumnsPanel
static void AvailableColumnsPanel_addDynamicColumns(Panel* super, Hashtable* dynamicColumns) {
assert(dynamicColumns);
Hashtable_foreach(dynamicColumns, AvailableColumnsPanel_addDynamicColumn, super);
}
// Handle remaining Platform Meter entries in the AvailableColumnsPanel
static void AvailableColumnsPanel_addPlatformColumn(Panel* super) {
for (int i = 1; i < LAST_PROCESSFIELD; i++) { for (int i = 1; i < LAST_PROCESSFIELD; i++) {
if (i != COMM && Process_fields[i].description) { if (i != COMM && Process_fields[i].description) {
char description[256]; char description[256];
@ -84,6 +105,18 @@ AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
Panel_add(super, (Object*) ListItem_new(description, i)); Panel_add(super, (Object*) ListItem_new(description, i));
} }
} }
}
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns, Hashtable* dynamicColumns) {
AvailableColumnsPanel* this = AllocThis(AvailableColumnsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(AvailableColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
Panel_setHeader(super, "Available Columns");
AvailableColumnsPanel_addPlatformColumn(super);
AvailableColumnsPanel_addDynamicColumns(super, dynamicColumns);
this->columns = columns; this->columns = columns;
return this; return this;
} }

View File

@ -7,8 +7,10 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Hashtable.h"
#include "Panel.h" #include "Panel.h"
typedef struct AvailableColumnsPanel_ { typedef struct AvailableColumnsPanel_ {
Panel super; Panel super;
Panel* columns; Panel* columns;
@ -16,6 +18,6 @@ typedef struct AvailableColumnsPanel_ {
extern const PanelClass AvailableColumnsPanel_class; extern const PanelClass AvailableColumnsPanel_class;
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns); AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns, Hashtable* dynamicColumns);
#endif #endif

View File

@ -12,9 +12,12 @@ in the source distribution for its full text.
#include <stdlib.h> #include <stdlib.h>
#include "CPUMeter.h" #include "CPUMeter.h"
#include "DynamicMeter.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Hashtable.h"
#include "Header.h" #include "Header.h"
#include "ListItem.h" #include "ListItem.h"
#include "Macros.h"
#include "Meter.h" #include "Meter.h"
#include "MetersPanel.h" #include "MetersPanel.h"
#include "Object.h" #include "Object.h"
@ -27,14 +30,15 @@ static void AvailableMetersPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
AvailableMetersPanel* this = (AvailableMetersPanel*) object; AvailableMetersPanel* this = (AvailableMetersPanel*) object;
Panel_done(super); Panel_done(super);
free(this->meterPanels);
free(this); free(this);
} }
static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, const MeterClass* type, int param, int column) { static inline void AvailableMetersPanel_addMeter(Header* header, MetersPanel* panel, const MeterClass* type, unsigned int param, size_t column) {
Meter* meter = Header_addMeterByClass(header, type, param, column); const Meter* meter = Header_addMeterByClass(header, type, param, column);
Panel_add(panel, (Object*) Meter_toListItem(meter, false)); Panel_add((Panel*)panel, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(panel, Panel_size(panel) - 1); Panel_setSelected((Panel*)panel, Panel_size((Panel*)panel) - 1);
MetersPanel_setMoving((MetersPanel*)panel, true); MetersPanel_setMoving(panel, true);
} }
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
@ -45,7 +49,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
if (!selected) if (!selected)
return IGNORED; return IGNORED;
int param = selected->key & 0xff; unsigned int param = selected->key & 0xffff;
int type = selected->key >> 16; int type = selected->key >> 16;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
bool update = false; bool update = false;
@ -55,7 +59,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
case 'l': case 'l':
case 'L': case 'L':
{ {
AvailableMetersPanel_addMeter(header, this->leftPanel, Platform_meterTypes[type], param, 0); AvailableMetersPanel_addMeter(header, this->meterPanels[0], Platform_meterTypes[type], param, 0);
result = HANDLED; result = HANDLED;
update = true; update = true;
break; break;
@ -67,7 +71,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
case 'r': case 'r':
case 'R': case 'R':
{ {
AvailableMetersPanel_addMeter(header, this->rightPanel, Platform_meterTypes[type], param, 1); AvailableMetersPanel_addMeter(header, this->meterPanels[this->columns - 1], Platform_meterTypes[type], param, this->columns - 1);
result = (KEY_LEFT << 16) | SYNTH_KEY; result = (KEY_LEFT << 16) | SYNTH_KEY;
update = true; update = true;
break; break;
@ -76,8 +80,9 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
if (update) { if (update) {
this->settings->changed = true; this->settings->changed = true;
Header_calculateHeight(header); Header_calculateHeight(header);
Header_updateData(header);
Header_draw(header); Header_draw(header);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2); ScreenManager_resize(this->scr);
} }
return result; return result;
} }
@ -90,7 +95,51 @@ const PanelClass AvailableMetersPanel_class = {
.eventHandler = AvailableMetersPanel_eventHandler .eventHandler = AvailableMetersPanel_eventHandler
}; };
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl) { // Handle (&CPUMeter_class) entries in the AvailableMetersPanel
static void AvailableMetersPanel_addCPUMeters(Panel* super, const MeterClass* type, const ProcessList* pl) {
if (pl->existingCPUs > 1) {
Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (unsigned int i = 1; i <= pl->existingCPUs; i++) {
char buffer[50];
xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(pl->settings, i - 1));
Panel_add(super, (Object*) ListItem_new(buffer, i));
}
} else {
Panel_add(super, (Object*) ListItem_new(type->uiName, 1));
}
}
typedef struct {
Panel* super;
unsigned int id;
unsigned int offset;
} DynamicIterator;
static void AvailableMetersPanel_addDynamicMeter(ATTR_UNUSED ht_key_t key, void* value, void* data) {
const DynamicMeter* meter = (const DynamicMeter*)value;
DynamicIterator* iter = (DynamicIterator*)data;
unsigned int identifier = (iter->offset << 16) | iter->id;
const char* label = meter->description ? meter->description : meter->caption;
if (!label)
label = meter->name; /* last fallback to name, guaranteed set */
Panel_add(iter->super, (Object*) ListItem_new(label, identifier));
iter->id++;
}
// Handle (&DynamicMeter_class) entries in the AvailableMetersPanel
static void AvailableMetersPanel_addDynamicMeters(Panel* super, const ProcessList* pl, unsigned int offset) {
DynamicIterator iter = { .super = super, .id = 1, .offset = offset };
assert(pl->dynamicMeters != NULL);
Hashtable_foreach(pl->dynamicMeters, AvailableMetersPanel_addDynamicMeter, &iter);
}
// Handle remaining Platform Meter entries in the AvailableMetersPanel
static void AvailableMetersPanel_addPlatformMeter(Panel* super, const MeterClass* type, unsigned int offset) {
const char* label = type->description ? type->description : type->uiName;
Panel_add(super, (Object*) ListItem_new(label, offset << 16));
}
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr, const ProcessList* pl) {
AvailableMetersPanel* this = AllocThis(AvailableMetersPanel); AvailableMetersPanel* this = AllocThis(AvailableMetersPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done "); FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done ");
@ -98,31 +147,24 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
this->settings = settings; this->settings = settings;
this->header = header; this->header = header;
this->leftPanel = leftMeters; this->columns = columns;
this->rightPanel = rightMeters; this->meterPanels = meterPanels;
this->scr = scr; this->scr = scr;
Panel_setHeader(super, "Available meters"); Panel_setHeader(super, "Available meters");
// Platform_meterTypes[0] should be always (&CPUMeter_class), which we will // Platform_meterTypes[0] should be always (&CPUMeter_class) which we will
// handle separately in the code below. // handle separately in the code below. Likewise, identifiers for Dynamic
for (int i = 1; Platform_meterTypes[i]; i++) { // Meters are handled separately - similar to CPUs, this allows generation
// of multiple different Meters (also using 'param' to distinguish them).
for (unsigned int i = 1; Platform_meterTypes[i]; i++) {
const MeterClass* type = Platform_meterTypes[i]; const MeterClass* type = Platform_meterTypes[i];
assert(type != &CPUMeter_class); assert(type != &CPUMeter_class);
const char* label = type->description ? type->description : type->uiName; if (type == &DynamicMeter_class)
Panel_add(super, (Object*) ListItem_new(label, i << 16)); AvailableMetersPanel_addDynamicMeters(super, pl, i);
} else
// Handle (&CPUMeter_class) AvailableMetersPanel_addPlatformMeter(super, type, i);
const MeterClass* type = &CPUMeter_class;
int cpus = pl->cpuCount;
if (cpus > 1) {
Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (int i = 1; i <= cpus; i++) {
char buffer[50];
xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1));
Panel_add(super, (Object*) ListItem_new(buffer, i));
}
} else {
Panel_add(super, (Object*) ListItem_new("CPU", 1));
} }
AvailableMetersPanel_addCPUMeters(super, &CPUMeter_class, pl);
return this; return this;
} }

View File

@ -7,24 +7,28 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include <stddef.h>
#include "Header.h" #include "Header.h"
#include "MetersPanel.h"
#include "Panel.h" #include "Panel.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "ScreenManager.h" #include "ScreenManager.h"
#include "Settings.h" #include "Settings.h"
typedef struct AvailableMetersPanel_ { typedef struct AvailableMetersPanel_ {
Panel super; Panel super;
ScreenManager* scr; ScreenManager* scr;
Settings* settings; Settings* settings;
Header* header; Header* header;
Panel* leftPanel; size_t columns;
Panel* rightPanel; MetersPanel** meterPanels;
} AvailableMetersPanel; } AvailableMetersPanel;
extern const PanelClass AvailableMetersPanel_class; extern const PanelClass AvailableMetersPanel_class;
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl); AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel **meterPanels, ScreenManager* scr, const ProcessList* pl);
#endif #endif

View File

@ -21,7 +21,7 @@ static const int BatteryMeter_attributes[] = {
BATTERY BATTERY
}; };
static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) { static void BatteryMeter_updateValues(Meter* this) {
ACPresence isOnAC; ACPresence isOnAC;
double percent; double percent;
@ -29,7 +29,7 @@ static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) {
if (isnan(percent)) { if (isnan(percent)) {
this->values[0] = NAN; this->values[0] = NAN;
xSnprintf(buffer, len, "N/A"); xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "N/A");
return; return;
} }
@ -49,7 +49,7 @@ static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) {
break; break;
} }
xSnprintf(buffer, len, "%.1f%%%s", percent, text); xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1f%%%s", percent, text);
} }
const MeterClass BatteryMeter_class = { const MeterClass BatteryMeter_class = {

View File

@ -11,6 +11,7 @@ This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
#include "Meter.h" #include "Meter.h"
typedef enum ACPresence_ { typedef enum ACPresence_ {
AC_ABSENT, AC_ABSENT,
AC_PRESENT, AC_PRESENT,

View File

@ -29,9 +29,9 @@ are always included, please send those in!
Feature Requests Feature Requests
---------------- ----------------
Please label Github issues that are feature requests with the [`feature Please label Github issues that are feature requests with one of the `feature request`
request`](https://github.com/htop-dev/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+) labels. If you can't do this yourself, don't worry. The friendly folks from the
label. core team will distribute and fixup Github labels as part of the regular reviews.
Style Guide Style Guide
----------- -----------

58
COPYING
View File

@ -1,12 +1,12 @@
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 2, June 1991 Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
675 Mass Ave, Cambridge, MA 02139, USA 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
Preamble Preamble
The licenses for most software are designed to take away your The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public freedom to share and change it. By contrast, the GNU General Public
@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to the GNU Lesser General Public License instead.) You can apply it to
your programs, too. your programs, too.
When we speak of free software, we are referring to freedom, not When we speak of free software, we are referring to freedom, not
@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and The precise terms and conditions for copying, distribution and
modification follow. modification follow.
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains 0. This License applies to any program or other work which contains
@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on does not normally print such an announcement, your work based on
the Program is not required to print an announcement.) the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program, identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in and can be reasonably considered independent and separate works in
@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not distribution of the source code, even though third parties are not
compelled to copy the source along with the object code. compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program 4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is otherwise to copy, modify, sublicense or distribute the Program is
@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License. be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in 8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License original copyright holder who places the Program under this License
@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally. of promoting the sharing and reuse of software generally.
NO WARRANTY NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES. POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it possible use to the public, the best way to achieve this is to make it
@ -291,7 +291,7 @@ convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found. the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> <one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author> Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -303,16 +303,16 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License along
along with this program; if not, write to the Free Software with this program; if not, write to the Free Software Foundation, Inc.,
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this If the program is interactive, make it output a short notice like this
when it starts in an interactive mode: when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details. under certain conditions; type `show c' for details.
@ -335,21 +335,5 @@ necessary. Here is a sample; alter the names:
This General Public License does not permit incorporating your program into This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. Public License instead of this License.
Appendix 2: Special exception concerning PLPA
In the following exception, "PLPA" means (i) code released by the
Portable Linux Processor Affinity Project, or (ii) derivative works of
such code, in both cases provided that the code is covered entirely by
free software licensing terms.
As a special exception to the GNU GPL, the licensors of htop give you
permission to combine GNU GPL-licensed code in htop (and derivative
works of such code) with PLPA. You may copy and distribute such a
combined work following the terms of the GNU GPL for htop and the
applicable licenses of the version of PLPA used in your combined work,
provided that you include the source code of such version of PLPA when
and as the GNU GPL requires distribution of source code.

View File

@ -10,7 +10,6 @@ in the source distribution for its full text.
#include "CPUMeter.h" #include "CPUMeter.h"
#include <math.h> #include <math.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -35,37 +34,48 @@ static const int CPUMeter_attributes[] = {
}; };
typedef struct CPUMeterData_ { typedef struct CPUMeterData_ {
int cpus; unsigned int cpus;
Meter** meters; Meter** meters;
} CPUMeterData; } CPUMeterData;
static void CPUMeter_init(Meter* this) { static void CPUMeter_init(Meter* this) {
int cpu = this->param; unsigned int cpu = this->param;
if (this->pl->cpuCount > 1) { if (cpu == 0) {
Meter_setCaption(this, "Avg");
} else if (this->pl->activeCPUs > 1) {
char caption[10]; char caption[10];
xSnprintf(caption, sizeof(caption), "%3d", Settings_cpuId(this->pl->settings, cpu - 1)); xSnprintf(caption, sizeof(caption), "%3u", Settings_cpuId(this->pl->settings, cpu - 1));
Meter_setCaption(this, caption); Meter_setCaption(this, caption);
} }
if (this->param == 0)
Meter_setCaption(this, "Avg");
} }
static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) { // Custom uiName runtime logic to include the param (processor)
int cpu = this->param; static void CPUMeter_getUiName(const Meter* this, char* buffer, size_t length) {
if (cpu > this->pl->cpuCount) { if (this->param > 0)
xSnprintf(buffer, size, "absent"); xSnprintf(buffer, length, "%s %u", Meter_uiName(this), this->param);
for (uint8_t i = 0; i < this->curItems; i++) else
this->values[i] = 0; xSnprintf(buffer, length, "%s", Meter_uiName(this));
}
static void CPUMeter_updateValues(Meter* this) {
memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT);
unsigned int cpu = this->param;
if (cpu > this->pl->existingCPUs) {
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "absent");
return;
}
double percent = Platform_setCPUValues(this, cpu);
if (isnan(percent)) {
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "offline");
return; return;
} }
memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT);
char cpuUsageBuffer[8] = { 0 }; char cpuUsageBuffer[8] = { 0 };
char cpuFrequencyBuffer[16] = { 0 }; char cpuFrequencyBuffer[16] = { 0 };
char cpuTemperatureBuffer[16] = { 0 }; char cpuTemperatureBuffer[16] = { 0 };
double percent = Platform_setCPUValues(this, cpu);
if (this->pl->settings->showCPUUsage) { if (this->pl->settings->showCPUUsage) {
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent); xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent);
} }
@ -79,7 +89,7 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
} }
} }
#ifdef HAVE_SENSORS_SENSORS_H #ifdef BUILD_WITH_CPU_TEMP
if (this->pl->settings->showCPUTemperature) { if (this->pl->settings->showCPUTemperature) {
double cpuTemperature = this->values[CPU_METER_TEMPERATURE]; double cpuTemperature = this->values[CPU_METER_TEMPERATURE];
if (isnan(cpuTemperature)) if (isnan(cpuTemperature))
@ -91,7 +101,7 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
} }
#endif #endif
xSnprintf(buffer, size, "%s%s%s%s%s", xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s%s%s%s%s",
cpuUsageBuffer, cpuUsageBuffer,
(cpuUsageBuffer[0] && (cpuFrequencyBuffer[0] || cpuTemperatureBuffer[0])) ? " " : "", (cpuUsageBuffer[0] && (cpuFrequencyBuffer[0] || cpuTemperatureBuffer[0])) ? " " : "",
cpuFrequencyBuffer, cpuFrequencyBuffer,
@ -101,75 +111,82 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
static void CPUMeter_display(const Object* cast, RichString* out) { static void CPUMeter_display(const Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
int len;
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_prune(out);
if (this->param > this->pl->cpuCount) { if (this->param > this->pl->existingCPUs) {
RichString_appendAscii(out, CRT_colors[METER_TEXT], "absent"); RichString_appendAscii(out, CRT_colors[METER_SHADOW], " absent");
return; return;
} }
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]);
if (this->curItems == 0) {
RichString_appendAscii(out, CRT_colors[METER_SHADOW], " offline");
return;
}
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]);
RichString_appendAscii(out, CRT_colors[METER_TEXT], ":"); RichString_appendAscii(out, CRT_colors[METER_TEXT], ":");
RichString_appendAscii(out, CRT_colors[CPU_NORMAL], buffer); RichString_appendnAscii(out, CRT_colors[CPU_NORMAL], buffer, len);
if (this->pl->settings->detailedCPUTime) { if (this->pl->settings->detailedCPUTime) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]); len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sy:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "sy:");
RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer); RichString_appendnAscii(out, CRT_colors[CPU_SYSTEM], buffer, len);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "ni:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "ni:");
RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_appendnAscii(out, CRT_colors[CPU_NICE_TEXT], buffer, len);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]); len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "hi:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "hi:");
RichString_appendAscii(out, CRT_colors[CPU_IRQ], buffer); RichString_appendnAscii(out, CRT_colors[CPU_IRQ], buffer, len);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]); len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "si:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "si:");
RichString_appendAscii(out, CRT_colors[CPU_SOFTIRQ], buffer); RichString_appendnAscii(out, CRT_colors[CPU_SOFTIRQ], buffer, len);
if (!isnan(this->values[CPU_METER_STEAL])) { if (!isnan(this->values[CPU_METER_STEAL])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]); len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "st:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "st:");
RichString_appendAscii(out, CRT_colors[CPU_STEAL], buffer); RichString_appendnAscii(out, CRT_colors[CPU_STEAL], buffer, len);
} }
if (!isnan(this->values[CPU_METER_GUEST])) { if (!isnan(this->values[CPU_METER_GUEST])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]); len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "gu:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "gu:");
RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer); RichString_appendnAscii(out, CRT_colors[CPU_GUEST], buffer, len);
} }
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]); len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "wa:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "wa:");
RichString_appendAscii(out, CRT_colors[CPU_IOWAIT], buffer); RichString_appendnAscii(out, CRT_colors[CPU_IOWAIT], buffer, len);
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]); len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sys:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "sys:");
RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer); RichString_appendnAscii(out, CRT_colors[CPU_SYSTEM], buffer, len);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "low:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "low:");
RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_appendnAscii(out, CRT_colors[CPU_NICE_TEXT], buffer, len);
if (!isnan(this->values[CPU_METER_IRQ])) { if (!isnan(this->values[CPU_METER_IRQ])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]); len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "vir:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "vir:");
RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer); RichString_appendnAscii(out, CRT_colors[CPU_GUEST], buffer, len);
} }
} }
#ifdef HAVE_SENSORS_SENSORS_H #ifdef BUILD_WITH_CPU_TEMP
if (this->pl->settings->showCPUTemperature) { if (this->pl->settings->showCPUTemperature) {
char cpuTemperatureBuffer[10]; char cpuTemperatureBuffer[10];
double cpuTemperature = this->values[CPU_METER_TEMPERATURE]; double cpuTemperature = this->values[CPU_METER_TEMPERATURE];
if (isnan(cpuTemperature)) { if (isnan(cpuTemperature)) {
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A"); len = xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A");
} else if (this->pl->settings->degreeFahrenheit) { } else if (this->pl->settings->degreeFahrenheit) {
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sF", cpuTemperature * 9 / 5 + 32, CRT_degreeSign); len = xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sF", cpuTemperature * 9 / 5 + 32, CRT_degreeSign);
} else { } else {
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign); len = xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign);
} }
RichString_appendAscii(out, CRT_colors[METER_TEXT], "temp:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "temp:");
RichString_appendWide(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer); RichString_appendnWide(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer, len);
} }
#endif #endif
} }
static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) { static void AllCPUsMeter_getRange(const Meter* this, int* start, int* count) {
CPUMeterData* data = this->meterData; const CPUMeterData* data = this->meterData;
int cpus = data->cpus; unsigned int cpus = data->cpus;
switch(Meter_name(this)[0]) { switch(Meter_name(this)[0]) {
default: default:
case 'A': // All case 'A': // All
@ -187,8 +204,17 @@ static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) {
} }
} }
static void AllCPUsMeter_updateValues(Meter* this) {
CPUMeterData* data = this->meterData;
Meter** meters = data->meters;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++)
Meter_updateValues(meters[i]);
}
static void CPUMeterCommonInit(Meter* this, int ncol) { static void CPUMeterCommonInit(Meter* this, int ncol) {
int cpus = this->pl->cpuCount; unsigned int cpus = this->pl->existingCPUs;
CPUMeterData* data = this->meterData; CPUMeterData* data = this->meterData;
if (!data) { if (!data) {
data = this->meterData = xMalloc(sizeof(CPUMeterData)); data = this->meterData = xMalloc(sizeof(CPUMeterData));
@ -316,6 +342,7 @@ const MeterClass CPUMeter_class = {
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = CPUMeter_updateValues, .updateValues = CPUMeter_updateValues,
.getUiName = CPUMeter_getUiName,
.defaultMode = BAR_METERMODE, .defaultMode = BAR_METERMODE,
.maxItems = CPU_METER_ITEMCOUNT, .maxItems = CPU_METER_ITEMCOUNT,
.total = 100.0, .total = 100.0,
@ -332,6 +359,7 @@ const MeterClass AllCPUsMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
@ -351,6 +379,7 @@ const MeterClass AllCPUs2Meter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
@ -370,6 +399,7 @@ const MeterClass LeftCPUsMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
@ -389,6 +419,7 @@ const MeterClass RightCPUsMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
@ -408,6 +439,7 @@ const MeterClass LeftCPUs2Meter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
@ -427,6 +459,7 @@ const MeterClass RightCPUs2Meter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
@ -446,6 +479,7 @@ const MeterClass AllCPUs4Meter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
@ -465,6 +499,7 @@ const MeterClass LeftCPUs4Meter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
@ -484,6 +519,7 @@ const MeterClass RightCPUs4Meter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
@ -503,6 +539,7 @@ const MeterClass AllCPUs8Meter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
@ -522,6 +559,7 @@ const MeterClass LeftCPUs8Meter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
@ -541,6 +579,7 @@ const MeterClass RightCPUs8Meter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,

View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
typedef enum { typedef enum {
CPU_METER_NICE = 0, CPU_METER_NICE = 0,
CPU_METER_NORMAL = 1, CPU_METER_NORMAL = 1,

335
CRT.c
View File

@ -10,6 +10,7 @@ in the source distribution for its full text.
#include "CRT.h" #include "CRT.h"
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <langinfo.h> #include <langinfo.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
@ -24,6 +25,10 @@ in the source distribution for its full text.
#include <execinfo.h> #include <execinfo.h>
#endif #endif
#if !defined(NDEBUG) && defined(HAVE_MEMFD_CREATE)
#include <sys/mman.h>
#endif
#define ColorIndex(i,j) ((7-(i))*8+(j)) #define ColorIndex(i,j) ((7-(i))*8+(j))
@ -76,6 +81,7 @@ bool CRT_utf8 = false;
const char* const* CRT_treeStr = CRT_treeStrAscii; const char* const* CRT_treeStr = CRT_treeStrAscii;
static const Settings* CRT_crashSettings;
static const int* CRT_delay; static const int* CRT_delay;
const char* CRT_degreeSign; const char* CRT_degreeSign;
@ -114,6 +120,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[UPTIME] = A_BOLD | ColorPair(Cyan, Black), [UPTIME] = A_BOLD | ColorPair(Cyan, Black),
[BATTERY] = A_BOLD | ColorPair(Cyan, Black), [BATTERY] = A_BOLD | ColorPair(Cyan, Black),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black), [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
[METER_TEXT] = ColorPair(Cyan, Black), [METER_TEXT] = ColorPair(Cyan, Black),
[METER_VALUE] = A_BOLD | ColorPair(Cyan, Black), [METER_VALUE] = A_BOLD | ColorPair(Cyan, Black),
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
@ -131,7 +138,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_GIGABYTES] = ColorPair(Green, Black), [PROCESS_GIGABYTES] = ColorPair(Green, Black),
[PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Black), [PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Black),
[PROCESS_TREE] = ColorPair(Cyan, Black), [PROCESS_TREE] = ColorPair(Cyan, Black),
[PROCESS_R_STATE] = ColorPair(Green, Black), [PROCESS_RUN_STATE] = ColorPair(Green, Black),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
@ -144,17 +151,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[BAR_BORDER] = A_BOLD, [BAR_BORDER] = A_BOLD,
[BAR_SHADOW] = A_BOLD | ColorPairGrayBlack, [BAR_SHADOW] = A_BOLD | ColorPairGrayBlack,
[SWAP] = ColorPair(Red, Black), [SWAP] = ColorPair(Red, Black),
[SWAP_CACHE] = ColorPair(Yellow, Black),
[GRAPH_1] = A_BOLD | ColorPair(Cyan, Black), [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black),
[GRAPH_2] = ColorPair(Cyan, Black), [GRAPH_2] = ColorPair(Cyan, Black),
[MEMORY_USED] = ColorPair(Green, Black), [MEMORY_USED] = ColorPair(Green, Black),
[MEMORY_BUFFERS] = ColorPair(Blue, Black), [MEMORY_BUFFERS] = ColorPair(Blue, Black),
[MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black), [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black),
[MEMORY_CACHE] = ColorPair(Yellow, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black),
[MEMORY_SHARED] = ColorPair(Magenta, Black),
[HUGEPAGE_1] = ColorPair(Green, Black),
[HUGEPAGE_2] = ColorPair(Yellow, Black),
[HUGEPAGE_3] = ColorPair(Red, Black),
[HUGEPAGE_4] = ColorPair(Blue, Black),
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Cyan, Black), [LOAD_AVERAGE_FIFTEEN] = ColorPair(Cyan, Black),
[LOAD_AVERAGE_FIVE] = A_BOLD | ColorPair(Cyan, Black), [LOAD_AVERAGE_FIVE] = A_BOLD | ColorPair(Cyan, Black),
[LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Black), [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Black),
[LOAD] = A_BOLD, [LOAD] = A_BOLD,
[HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black), [HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black),
[HELP_SHADOW] = A_BOLD | ColorPairGrayBlack,
[CLOCK] = A_BOLD, [CLOCK] = A_BOLD,
[DATE] = A_BOLD, [DATE] = A_BOLD,
[DATETIME] = A_BOLD, [DATETIME] = A_BOLD,
@ -182,6 +196,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_COMPRESSED] = ColorPair(Blue, Black), [ZFS_COMPRESSED] = ColorPair(Blue, Black),
[ZFS_RATIO] = ColorPair(Magenta, Black), [ZFS_RATIO] = ColorPair(Magenta, Black),
[ZRAM] = ColorPair(Yellow, Black), [ZRAM] = ColorPair(Yellow, Black),
[DYNAMIC_GRAY] = ColorPairGrayBlack,
[DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack,
[DYNAMIC_RED] = ColorPair(Red, Black),
[DYNAMIC_GREEN] = ColorPair(Green, Black),
[DYNAMIC_BLUE] = ColorPair(Blue, Black),
[DYNAMIC_CYAN] = ColorPair(Cyan, Black),
[DYNAMIC_MAGENTA] = ColorPair(Magenta, Black),
[DYNAMIC_YELLOW] = ColorPair(Yellow, Black),
[DYNAMIC_WHITE] = ColorPair(White, Black),
}, },
[COLORSCHEME_MONOCHROME] = { [COLORSCHEME_MONOCHROME] = {
[RESET_COLOR] = A_NORMAL, [RESET_COLOR] = A_NORMAL,
@ -199,6 +222,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[UPTIME] = A_BOLD, [UPTIME] = A_BOLD,
[BATTERY] = A_BOLD, [BATTERY] = A_BOLD,
[LARGE_NUMBER] = A_BOLD, [LARGE_NUMBER] = A_BOLD,
[METER_SHADOW] = A_DIM,
[METER_TEXT] = A_NORMAL, [METER_TEXT] = A_NORMAL,
[METER_VALUE] = A_BOLD, [METER_VALUE] = A_BOLD,
[METER_VALUE_ERROR] = A_BOLD, [METER_VALUE_ERROR] = A_BOLD,
@ -216,7 +240,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_GIGABYTES] = A_BOLD, [PROCESS_GIGABYTES] = A_BOLD,
[PROCESS_BASENAME] = A_BOLD, [PROCESS_BASENAME] = A_BOLD,
[PROCESS_TREE] = A_BOLD, [PROCESS_TREE] = A_BOLD,
[PROCESS_R_STATE] = A_BOLD, [PROCESS_RUN_STATE] = A_BOLD,
[PROCESS_D_STATE] = A_BOLD, [PROCESS_D_STATE] = A_BOLD,
[PROCESS_HIGH_PRIORITY] = A_BOLD, [PROCESS_HIGH_PRIORITY] = A_BOLD,
[PROCESS_LOW_PRIORITY] = A_DIM, [PROCESS_LOW_PRIORITY] = A_DIM,
@ -229,17 +253,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[BAR_BORDER] = A_BOLD, [BAR_BORDER] = A_BOLD,
[BAR_SHADOW] = A_DIM, [BAR_SHADOW] = A_DIM,
[SWAP] = A_BOLD, [SWAP] = A_BOLD,
[SWAP_CACHE] = A_NORMAL,
[GRAPH_1] = A_BOLD, [GRAPH_1] = A_BOLD,
[GRAPH_2] = A_NORMAL, [GRAPH_2] = A_NORMAL,
[MEMORY_USED] = A_BOLD, [MEMORY_USED] = A_BOLD,
[MEMORY_BUFFERS] = A_NORMAL, [MEMORY_BUFFERS] = A_NORMAL,
[MEMORY_BUFFERS_TEXT] = A_NORMAL, [MEMORY_BUFFERS_TEXT] = A_NORMAL,
[MEMORY_CACHE] = A_NORMAL, [MEMORY_CACHE] = A_NORMAL,
[MEMORY_SHARED] = A_NORMAL,
[HUGEPAGE_1] = A_BOLD,
[HUGEPAGE_2] = A_NORMAL,
[HUGEPAGE_3] = A_REVERSE | A_BOLD,
[HUGEPAGE_4] = A_REVERSE,
[LOAD_AVERAGE_FIFTEEN] = A_DIM, [LOAD_AVERAGE_FIFTEEN] = A_DIM,
[LOAD_AVERAGE_FIVE] = A_NORMAL, [LOAD_AVERAGE_FIVE] = A_NORMAL,
[LOAD_AVERAGE_ONE] = A_BOLD, [LOAD_AVERAGE_ONE] = A_BOLD,
[LOAD] = A_BOLD, [LOAD] = A_BOLD,
[HELP_BOLD] = A_BOLD, [HELP_BOLD] = A_BOLD,
[HELP_SHADOW] = A_DIM,
[CLOCK] = A_BOLD, [CLOCK] = A_BOLD,
[DATE] = A_BOLD, [DATE] = A_BOLD,
[DATETIME] = A_BOLD, [DATETIME] = A_BOLD,
@ -267,6 +298,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_COMPRESSED] = A_BOLD, [ZFS_COMPRESSED] = A_BOLD,
[ZFS_RATIO] = A_BOLD, [ZFS_RATIO] = A_BOLD,
[ZRAM] = A_NORMAL, [ZRAM] = A_NORMAL,
[DYNAMIC_GRAY] = A_DIM,
[DYNAMIC_DARKGRAY] = A_DIM,
[DYNAMIC_RED] = A_BOLD,
[DYNAMIC_GREEN] = A_NORMAL,
[DYNAMIC_BLUE] = A_NORMAL,
[DYNAMIC_CYAN] = A_BOLD,
[DYNAMIC_MAGENTA] = A_NORMAL,
[DYNAMIC_YELLOW] = A_NORMAL,
[DYNAMIC_WHITE] = A_BOLD,
}, },
[COLORSCHEME_BLACKONWHITE] = { [COLORSCHEME_BLACKONWHITE] = {
[RESET_COLOR] = ColorPair(Black, White), [RESET_COLOR] = ColorPair(Black, White),
@ -284,6 +324,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[UPTIME] = ColorPair(Yellow, White), [UPTIME] = ColorPair(Yellow, White),
[BATTERY] = ColorPair(Yellow, White), [BATTERY] = ColorPair(Yellow, White),
[LARGE_NUMBER] = ColorPair(Red, White), [LARGE_NUMBER] = ColorPair(Red, White),
[METER_SHADOW] = ColorPair(Blue, White),
[METER_TEXT] = ColorPair(Blue, White), [METER_TEXT] = ColorPair(Blue, White),
[METER_VALUE] = ColorPair(Black, White), [METER_VALUE] = ColorPair(Black, White),
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, White), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, White),
@ -301,7 +342,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_GIGABYTES] = ColorPair(Green, White), [PROCESS_GIGABYTES] = ColorPair(Green, White),
[PROCESS_BASENAME] = ColorPair(Blue, White), [PROCESS_BASENAME] = ColorPair(Blue, White),
[PROCESS_TREE] = ColorPair(Green, White), [PROCESS_TREE] = ColorPair(Green, White),
[PROCESS_R_STATE] = ColorPair(Green, White), [PROCESS_RUN_STATE] = ColorPair(Green, White),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, White), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, White),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, White), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, White),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, White), [PROCESS_LOW_PRIORITY] = ColorPair(Green, White),
@ -314,17 +355,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[BAR_BORDER] = ColorPair(Blue, White), [BAR_BORDER] = ColorPair(Blue, White),
[BAR_SHADOW] = ColorPair(Black, White), [BAR_SHADOW] = ColorPair(Black, White),
[SWAP] = ColorPair(Red, White), [SWAP] = ColorPair(Red, White),
[SWAP_CACHE] = ColorPair(Yellow, White),
[GRAPH_1] = A_BOLD | ColorPair(Blue, White), [GRAPH_1] = A_BOLD | ColorPair(Blue, White),
[GRAPH_2] = ColorPair(Blue, White), [GRAPH_2] = ColorPair(Blue, White),
[MEMORY_USED] = ColorPair(Green, White), [MEMORY_USED] = ColorPair(Green, White),
[MEMORY_BUFFERS] = ColorPair(Cyan, White), [MEMORY_BUFFERS] = ColorPair(Cyan, White),
[MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, White), [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, White),
[MEMORY_CACHE] = ColorPair(Yellow, White), [MEMORY_CACHE] = ColorPair(Yellow, White),
[MEMORY_SHARED] = ColorPair(Magenta, White),
[HUGEPAGE_1] = ColorPair(Green, White),
[HUGEPAGE_2] = ColorPair(Yellow, White),
[HUGEPAGE_3] = ColorPair(Red, White),
[HUGEPAGE_4] = ColorPair(Blue, White),
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, White), [LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, White),
[LOAD_AVERAGE_FIVE] = ColorPair(Black, White), [LOAD_AVERAGE_FIVE] = ColorPair(Black, White),
[LOAD_AVERAGE_ONE] = ColorPair(Black, White), [LOAD_AVERAGE_ONE] = ColorPair(Black, White),
[LOAD] = ColorPair(Black, White), [LOAD] = ColorPair(Black, White),
[HELP_BOLD] = ColorPair(Blue, White), [HELP_BOLD] = ColorPair(Blue, White),
[HELP_SHADOW] = A_BOLD | ColorPair(Black, White),
[CLOCK] = ColorPair(Black, White), [CLOCK] = ColorPair(Black, White),
[DATE] = ColorPair(Black, White), [DATE] = ColorPair(Black, White),
[DATETIME] = ColorPair(Black, White), [DATETIME] = ColorPair(Black, White),
@ -351,7 +399,16 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_OTHER] = ColorPair(Magenta, White), [ZFS_OTHER] = ColorPair(Magenta, White),
[ZFS_COMPRESSED] = ColorPair(Cyan, White), [ZFS_COMPRESSED] = ColorPair(Cyan, White),
[ZFS_RATIO] = ColorPair(Magenta, White), [ZFS_RATIO] = ColorPair(Magenta, White),
[ZRAM] = ColorPair(Yellow, White) [ZRAM] = ColorPair(Yellow, White),
[DYNAMIC_GRAY] = ColorPair(Black, White),
[DYNAMIC_DARKGRAY] = A_BOLD | ColorPair(Black, White),
[DYNAMIC_RED] = ColorPair(Red, White),
[DYNAMIC_GREEN] = ColorPair(Green, White),
[DYNAMIC_BLUE] = ColorPair(Blue, White),
[DYNAMIC_CYAN] = ColorPair(Yellow, White),
[DYNAMIC_MAGENTA] = ColorPair(Magenta, White),
[DYNAMIC_YELLOW] = ColorPair(Yellow, White),
[DYNAMIC_WHITE] = A_BOLD | ColorPair(Black, White),
}, },
[COLORSCHEME_LIGHTTERMINAL] = { [COLORSCHEME_LIGHTTERMINAL] = {
[RESET_COLOR] = ColorPair(Black, Black), [RESET_COLOR] = ColorPair(Black, Black),
@ -369,6 +426,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[UPTIME] = ColorPair(Yellow, Black), [UPTIME] = ColorPair(Yellow, Black),
[BATTERY] = ColorPair(Yellow, Black), [BATTERY] = ColorPair(Yellow, Black),
[LARGE_NUMBER] = ColorPair(Red, Black), [LARGE_NUMBER] = ColorPair(Red, Black),
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
[METER_TEXT] = ColorPair(Blue, Black), [METER_TEXT] = ColorPair(Blue, Black),
[METER_VALUE] = ColorPair(Black, Black), [METER_VALUE] = ColorPair(Black, Black),
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
@ -386,7 +444,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_GIGABYTES] = ColorPair(Green, Black), [PROCESS_GIGABYTES] = ColorPair(Green, Black),
[PROCESS_BASENAME] = ColorPair(Green, Black), [PROCESS_BASENAME] = ColorPair(Green, Black),
[PROCESS_TREE] = ColorPair(Blue, Black), [PROCESS_TREE] = ColorPair(Blue, Black),
[PROCESS_R_STATE] = ColorPair(Green, Black), [PROCESS_RUN_STATE] = ColorPair(Green, Black),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
@ -399,17 +457,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[BAR_BORDER] = ColorPair(Blue, Black), [BAR_BORDER] = ColorPair(Blue, Black),
[BAR_SHADOW] = ColorPairGrayBlack, [BAR_SHADOW] = ColorPairGrayBlack,
[SWAP] = ColorPair(Red, Black), [SWAP] = ColorPair(Red, Black),
[SWAP_CACHE] = ColorPair(Yellow, Black),
[GRAPH_1] = A_BOLD | ColorPair(Cyan, Black), [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black),
[GRAPH_2] = ColorPair(Cyan, Black), [GRAPH_2] = ColorPair(Cyan, Black),
[MEMORY_USED] = ColorPair(Green, Black), [MEMORY_USED] = ColorPair(Green, Black),
[MEMORY_BUFFERS] = ColorPair(Cyan, Black), [MEMORY_BUFFERS] = ColorPair(Cyan, Black),
[MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, Black), [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, Black),
[MEMORY_CACHE] = ColorPair(Yellow, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black),
[MEMORY_SHARED] = ColorPair(Magenta, Black),
[HUGEPAGE_1] = ColorPair(Green, Black),
[HUGEPAGE_2] = ColorPair(Yellow, Black),
[HUGEPAGE_3] = ColorPair(Red, Black),
[HUGEPAGE_4] = ColorPair(Blue, Black),
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, Black), [LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, Black),
[LOAD_AVERAGE_FIVE] = ColorPair(Black, Black), [LOAD_AVERAGE_FIVE] = ColorPair(Black, Black),
[LOAD_AVERAGE_ONE] = ColorPair(Black, Black), [LOAD_AVERAGE_ONE] = ColorPair(Black, Black),
[LOAD] = ColorPairWhiteDefault, [LOAD] = ColorPairWhiteDefault,
[HELP_BOLD] = ColorPair(Blue, Black), [HELP_BOLD] = ColorPair(Blue, Black),
[HELP_SHADOW] = A_BOLD | ColorPairGrayBlack,
[CLOCK] = ColorPairWhiteDefault, [CLOCK] = ColorPairWhiteDefault,
[DATE] = ColorPairWhiteDefault, [DATE] = ColorPairWhiteDefault,
[DATETIME] = ColorPairWhiteDefault, [DATETIME] = ColorPairWhiteDefault,
@ -437,6 +502,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_COMPRESSED] = ColorPair(Cyan, Black), [ZFS_COMPRESSED] = ColorPair(Cyan, Black),
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black), [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black),
[ZRAM] = ColorPair(Yellow, Black), [ZRAM] = ColorPair(Yellow, Black),
[DYNAMIC_GRAY] = ColorPairGrayBlack,
[DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack,
[DYNAMIC_RED] = ColorPair(Red, Black),
[DYNAMIC_GREEN] = ColorPair(Green, Black),
[DYNAMIC_BLUE] = ColorPair(Blue, Black),
[DYNAMIC_CYAN] = ColorPair(Cyan, Black),
[DYNAMIC_MAGENTA] = ColorPair(Magenta, Black),
[DYNAMIC_YELLOW] = ColorPair(Yellow, Black),
[DYNAMIC_WHITE] = ColorPairWhiteDefault,
}, },
[COLORSCHEME_MIDNIGHT] = { [COLORSCHEME_MIDNIGHT] = {
[RESET_COLOR] = ColorPair(White, Blue), [RESET_COLOR] = ColorPair(White, Blue),
@ -454,6 +528,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[UPTIME] = A_BOLD | ColorPair(Yellow, Blue), [UPTIME] = A_BOLD | ColorPair(Yellow, Blue),
[BATTERY] = A_BOLD | ColorPair(Yellow, Blue), [BATTERY] = A_BOLD | ColorPair(Yellow, Blue),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Blue), [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Blue),
[METER_SHADOW] = ColorPair(Cyan, Blue),
[METER_TEXT] = ColorPair(Cyan, Blue), [METER_TEXT] = ColorPair(Cyan, Blue),
[METER_VALUE] = A_BOLD | ColorPair(Cyan, Blue), [METER_VALUE] = A_BOLD | ColorPair(Cyan, Blue),
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Blue), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Blue),
@ -471,7 +546,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_GIGABYTES] = ColorPair(Green, Blue), [PROCESS_GIGABYTES] = ColorPair(Green, Blue),
[PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Blue), [PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Blue),
[PROCESS_TREE] = ColorPair(Cyan, Blue), [PROCESS_TREE] = ColorPair(Cyan, Blue),
[PROCESS_R_STATE] = ColorPair(Green, Blue), [PROCESS_RUN_STATE] = ColorPair(Green, Blue),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Blue), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Blue),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Blue), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Blue),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Blue), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Blue),
@ -484,17 +559,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[BAR_BORDER] = A_BOLD | ColorPair(Yellow, Blue), [BAR_BORDER] = A_BOLD | ColorPair(Yellow, Blue),
[BAR_SHADOW] = ColorPair(Cyan, Blue), [BAR_SHADOW] = ColorPair(Cyan, Blue),
[SWAP] = ColorPair(Red, Blue), [SWAP] = ColorPair(Red, Blue),
[SWAP_CACHE] = A_BOLD | ColorPair(Yellow, Blue),
[GRAPH_1] = A_BOLD | ColorPair(Cyan, Blue), [GRAPH_1] = A_BOLD | ColorPair(Cyan, Blue),
[GRAPH_2] = ColorPair(Cyan, Blue), [GRAPH_2] = ColorPair(Cyan, Blue),
[MEMORY_USED] = A_BOLD | ColorPair(Green, Blue), [MEMORY_USED] = A_BOLD | ColorPair(Green, Blue),
[MEMORY_BUFFERS] = A_BOLD | ColorPair(Cyan, Blue), [MEMORY_BUFFERS] = A_BOLD | ColorPair(Cyan, Blue),
[MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Cyan, Blue), [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Cyan, Blue),
[MEMORY_CACHE] = A_BOLD | ColorPair(Yellow, Blue), [MEMORY_CACHE] = A_BOLD | ColorPair(Yellow, Blue),
[MEMORY_SHARED] = A_BOLD | ColorPair(Magenta, Blue),
[HUGEPAGE_1] = A_BOLD | ColorPair(Green, Blue),
[HUGEPAGE_2] = A_BOLD | ColorPair(Yellow, Blue),
[HUGEPAGE_3] = A_BOLD | ColorPair(Red, Blue),
[HUGEPAGE_4] = A_BOLD | ColorPair(White, Blue),
[LOAD_AVERAGE_FIFTEEN] = A_BOLD | ColorPair(Black, Blue), [LOAD_AVERAGE_FIFTEEN] = A_BOLD | ColorPair(Black, Blue),
[LOAD_AVERAGE_FIVE] = A_NORMAL | ColorPair(White, Blue), [LOAD_AVERAGE_FIVE] = A_NORMAL | ColorPair(White, Blue),
[LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Blue), [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Blue),
[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),
[HELP_SHADOW] = A_BOLD | ColorPair(Black, Blue),
[CLOCK] = ColorPair(White, Blue), [CLOCK] = ColorPair(White, Blue),
[DATE] = ColorPair(White, Blue), [DATE] = ColorPair(White, Blue),
[DATETIME] = ColorPair(White, Blue), [DATETIME] = ColorPair(White, Blue),
@ -522,6 +604,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_COMPRESSED] = A_BOLD | ColorPair(White, Blue), [ZFS_COMPRESSED] = A_BOLD | ColorPair(White, Blue),
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Blue), [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Blue),
[ZRAM] = A_BOLD | ColorPair(Yellow, Blue), [ZRAM] = A_BOLD | ColorPair(Yellow, Blue),
[DYNAMIC_GRAY] = ColorPairGrayBlack,
[DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack,
[DYNAMIC_RED] = ColorPair(Red, Blue),
[DYNAMIC_GREEN] = ColorPair(Green, Blue),
[DYNAMIC_BLUE] = ColorPair(Black, Blue),
[DYNAMIC_CYAN] = ColorPair(Cyan, Blue),
[DYNAMIC_MAGENTA] = ColorPair(Magenta, Blue),
[DYNAMIC_YELLOW] = ColorPair(Yellow, Blue),
[DYNAMIC_WHITE] = ColorPair(White, Blue),
}, },
[COLORSCHEME_BLACKNIGHT] = { [COLORSCHEME_BLACKNIGHT] = {
[RESET_COLOR] = ColorPair(Cyan, Black), [RESET_COLOR] = ColorPair(Cyan, Black),
@ -539,6 +630,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[UPTIME] = ColorPair(Green, Black), [UPTIME] = ColorPair(Green, Black),
[BATTERY] = ColorPair(Green, Black), [BATTERY] = ColorPair(Green, Black),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black), [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
[METER_TEXT] = ColorPair(Cyan, Black), [METER_TEXT] = ColorPair(Cyan, Black),
[METER_VALUE] = ColorPair(Green, Black), [METER_VALUE] = ColorPair(Green, Black),
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
@ -560,7 +652,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black),
[PROCESS_COMM] = ColorPair(Magenta, Black), [PROCESS_COMM] = ColorPair(Magenta, Black),
[PROCESS_THREAD_COMM] = ColorPair(Yellow, Black), [PROCESS_THREAD_COMM] = ColorPair(Yellow, Black),
[PROCESS_R_STATE] = ColorPair(Green, Black), [PROCESS_RUN_STATE] = ColorPair(Green, Black),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
@ -569,17 +661,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[BAR_BORDER] = A_BOLD | ColorPair(Green, Black), [BAR_BORDER] = A_BOLD | ColorPair(Green, Black),
[BAR_SHADOW] = ColorPair(Cyan, Black), [BAR_SHADOW] = ColorPair(Cyan, Black),
[SWAP] = ColorPair(Red, Black), [SWAP] = ColorPair(Red, Black),
[SWAP_CACHE] = ColorPair(Yellow, Black),
[GRAPH_1] = A_BOLD | ColorPair(Green, Black), [GRAPH_1] = A_BOLD | ColorPair(Green, Black),
[GRAPH_2] = ColorPair(Green, Black), [GRAPH_2] = ColorPair(Green, Black),
[MEMORY_USED] = ColorPair(Green, Black), [MEMORY_USED] = ColorPair(Green, Black),
[MEMORY_BUFFERS] = ColorPair(Blue, Black), [MEMORY_BUFFERS] = ColorPair(Blue, Black),
[MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black), [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black),
[MEMORY_CACHE] = ColorPair(Yellow, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black),
[MEMORY_SHARED] = ColorPair(Magenta, Black),
[HUGEPAGE_1] = ColorPair(Green, Black),
[HUGEPAGE_2] = ColorPair(Yellow, Black),
[HUGEPAGE_3] = ColorPair(Red, Black),
[HUGEPAGE_4] = ColorPair(Blue, Black),
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Green, Black), [LOAD_AVERAGE_FIFTEEN] = ColorPair(Green, Black),
[LOAD_AVERAGE_FIVE] = ColorPair(Green, Black), [LOAD_AVERAGE_FIVE] = ColorPair(Green, Black),
[LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(Green, Black), [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(Green, Black),
[LOAD] = A_BOLD, [LOAD] = A_BOLD,
[HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black), [HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black),
[HELP_SHADOW] = A_BOLD | ColorPairGrayBlack,
[CLOCK] = ColorPair(Green, Black), [CLOCK] = ColorPair(Green, Black),
[CHECK_BOX] = ColorPair(Green, Black), [CHECK_BOX] = ColorPair(Green, Black),
[CHECK_MARK] = A_BOLD | ColorPair(Green, Black), [CHECK_MARK] = A_BOLD | ColorPair(Green, Black),
@ -605,6 +704,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_COMPRESSED] = ColorPair(Blue, Black), [ZFS_COMPRESSED] = ColorPair(Blue, Black),
[ZFS_RATIO] = ColorPair(Magenta, Black), [ZFS_RATIO] = ColorPair(Magenta, Black),
[ZRAM] = ColorPair(Yellow, Black), [ZRAM] = ColorPair(Yellow, Black),
[DYNAMIC_GRAY] = ColorPairGrayBlack,
[DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack,
[DYNAMIC_RED] = ColorPair(Red, Black),
[DYNAMIC_GREEN] = ColorPair(Green, Black),
[DYNAMIC_BLUE] = ColorPair(Blue, Black),
[DYNAMIC_CYAN] = ColorPair(Cyan, Black),
[DYNAMIC_MAGENTA] = ColorPair(Magenta, Black),
[DYNAMIC_YELLOW] = ColorPair(Yellow, Black),
[DYNAMIC_WHITE] = ColorPair(White, Black),
}, },
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated. [COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
}; };
@ -618,53 +726,139 @@ int CRT_scrollWheelVAmount = 10;
ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT; ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT;
ATTR_NORETURN ATTR_NORETURN
static void CRT_handleSIGTERM(int sgn) { static void CRT_handleSIGTERM(ATTR_UNUSED int sgn) {
(void) sgn;
CRT_done(); CRT_done();
exit(0); _exit(0);
} }
#ifdef HAVE_SETUID_ENABLED #ifndef NDEBUG
static int CRT_euid = -1; static int stderrRedirectNewFd = -1;
static int stderrRedirectBackupFd = -1;
static int CRT_egid = -1; static int createStderrCacheFile(void) {
#if defined(HAVE_MEMFD_CREATE)
return memfd_create("htop.stderr-redirect", 0);
#elif defined(O_TMPFILE)
return open("/tmp", O_TMPFILE | O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
#else
char tmpName[] = "htop.stderr-redirectXXXXXX";
mode_t curUmask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
int r = mkstemp(tmpName);
umask(curUmask);
if (r < 0)
return r;
void CRT_dropPrivileges() { (void) unlink(tmpName);
CRT_egid = getegid();
CRT_euid = geteuid(); return r;
if (setegid(getgid()) == -1) { #endif /* HAVE_MEMFD_CREATE */
CRT_fatalError("Fatal error: failed dropping group privileges");
}
if (seteuid(getuid()) == -1) {
CRT_fatalError("Fatal error: failed dropping user privileges");
}
} }
void CRT_restorePrivileges() { static void redirectStderr(void) {
if (CRT_egid == -1 || CRT_euid == -1) { stderrRedirectNewFd = createStderrCacheFile();
CRT_fatalError("Fatal error: internal inconsistency"); if (stderrRedirectNewFd < 0) {
} /* ignore failure */
if (setegid(CRT_egid) == -1) { return;
CRT_fatalError("Fatal error: failed restoring group privileges");
}
if (seteuid(CRT_euid) == -1) {
CRT_fatalError("Fatal error: failed restoring user privileges");
} }
stderrRedirectBackupFd = dup(STDERR_FILENO);
dup2(stderrRedirectNewFd, STDERR_FILENO);
} }
#endif /* HAVE_SETUID_ENABLED */ static void dumpStderr(void) {
if (stderrRedirectNewFd < 0)
return;
fsync(STDERR_FILENO);
dup2(stderrRedirectBackupFd, STDERR_FILENO);
lseek(stderrRedirectNewFd, 0, SEEK_SET);
bool header = false;
char buffer[8192];
for (;;) {
errno = 0;
ssize_t res = read(stderrRedirectNewFd, buffer, sizeof(buffer));
if (res < 0) {
if (errno == EINTR)
continue;
break;
}
if (res == 0) {
break;
}
if (res > 0) {
if (!header) {
fprintf(stderr, ">>>>>>>>>> stderr output >>>>>>>>>>\n");
header = true;
}
(void)! write(STDERR_FILENO, buffer, res);
}
}
if (header)
fprintf(stderr, "\n<<<<<<<<<< stderr output <<<<<<<<<<\n");
close(stderrRedirectNewFd);
stderrRedirectNewFd = -1;
}
#else /* !NDEBUG */
static void redirectStderr(void) {
}
static void dumpStderr(void) {
}
#endif /* !NDEBUG */
static struct sigaction old_sig_handler[32]; static struct sigaction old_sig_handler[32];
// TODO: pass an instance of Settings instead. static void CRT_installSignalHandlers(void) {
struct sigaction act;
sigemptyset (&act.sa_mask);
act.sa_flags = (int)SA_RESETHAND | SA_NODEFER;
act.sa_handler = CRT_handleSIGSEGV;
sigaction (SIGSEGV, &act, &old_sig_handler[SIGSEGV]);
sigaction (SIGFPE, &act, &old_sig_handler[SIGFPE]);
sigaction (SIGILL, &act, &old_sig_handler[SIGILL]);
sigaction (SIGBUS, &act, &old_sig_handler[SIGBUS]);
sigaction (SIGPIPE, &act, &old_sig_handler[SIGPIPE]);
sigaction (SIGSYS, &act, &old_sig_handler[SIGSYS]);
sigaction (SIGABRT, &act, &old_sig_handler[SIGABRT]);
signal(SIGINT, CRT_handleSIGTERM);
signal(SIGTERM, CRT_handleSIGTERM);
signal(SIGQUIT, CRT_handleSIGTERM);
}
void CRT_resetSignalHandlers(void) {
sigaction (SIGSEGV, &old_sig_handler[SIGSEGV], NULL);
sigaction (SIGFPE, &old_sig_handler[SIGFPE], NULL);
sigaction (SIGILL, &old_sig_handler[SIGILL], NULL);
sigaction (SIGBUS, &old_sig_handler[SIGBUS], NULL);
sigaction (SIGPIPE, &old_sig_handler[SIGPIPE], NULL);
sigaction (SIGSYS, &old_sig_handler[SIGSYS], NULL);
sigaction (SIGABRT, &old_sig_handler[SIGABRT], NULL);
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
}
void CRT_init(const Settings* settings, bool allowUnicode) {
redirectStderr();
void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
initscr(); initscr();
noecho(); noecho();
CRT_delay = delay; CRT_crashSettings = settings;
CRT_colors = CRT_colorSchemes[colorScheme]; CRT_delay = &(settings->delay);
CRT_colorScheme = colorScheme; CRT_colors = CRT_colorSchemes[settings->colorScheme];
CRT_colorScheme = settings->colorScheme;
for (int i = 0; i < LAST_COLORELEMENT; i++) { for (int i = 0; i < LAST_COLORELEMENT; i++) {
unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i]; unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i];
@ -675,7 +869,9 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
nonl(); nonl();
intrflush(stdscr, false); intrflush(stdscr, false);
keypad(stdscr, true); keypad(stdscr, true);
#ifdef HAVE_GETMOUSE
mouseinterval(0); mouseinterval(0);
#endif
curs_set(0); curs_set(0);
if (has_colors()) { if (has_colors()) {
@ -690,6 +886,10 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
} }
if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) { if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) {
#ifdef HTOP_NETBSD
#define define_key(s_, k_) define_key((char*)s_, k_)
IGNORE_WCASTQUAL_BEGIN
#endif
define_key("\033[H", KEY_HOME); define_key("\033[H", KEY_HOME);
define_key("\033[F", KEY_END); define_key("\033[F", KEY_END);
define_key("\033[7~", KEY_HOME); define_key("\033[7~", KEY_HOME);
@ -698,6 +898,7 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
define_key("\033OQ", KEY_F(2)); define_key("\033OQ", KEY_F(2));
define_key("\033OR", KEY_F(3)); define_key("\033OR", KEY_F(3));
define_key("\033OS", KEY_F(4)); define_key("\033OS", KEY_F(4));
define_key("\033O2R", KEY_F(15));
define_key("\033[11~", KEY_F(1)); define_key("\033[11~", KEY_F(1));
define_key("\033[12~", KEY_F(2)); define_key("\033[12~", KEY_F(2));
define_key("\033[13~", KEY_F(3)); define_key("\033[13~", KEY_F(3));
@ -709,22 +910,13 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
sequence[1] = c; sequence[1] = c;
define_key(sequence, KEY_ALT('A' + (c - 'a'))); define_key(sequence, KEY_ALT('A' + (c - 'a')));
} }
#ifdef HTOP_NETBSD
IGNORE_WCASTQUAL_END
#undef define_key
#endif
} }
struct sigaction act; CRT_installSignalHandlers();
sigemptyset (&act.sa_mask);
act.sa_flags = (int)SA_RESETHAND | SA_NODEFER;
act.sa_handler = CRT_handleSIGSEGV;
sigaction (SIGSEGV, &act, &old_sig_handler[SIGSEGV]);
sigaction (SIGFPE, &act, &old_sig_handler[SIGFPE]);
sigaction (SIGILL, &act, &old_sig_handler[SIGILL]);
sigaction (SIGBUS, &act, &old_sig_handler[SIGBUS]);
sigaction (SIGPIPE, &act, &old_sig_handler[SIGPIPE]);
sigaction (SIGSYS, &act, &old_sig_handler[SIGSYS]);
sigaction (SIGABRT, &act, &old_sig_handler[SIGABRT]);
signal(SIGTERM, CRT_handleSIGTERM);
signal(SIGQUIT, CRT_handleSIGTERM);
use_default_colors(); use_default_colors();
if (!has_colors()) if (!has_colors())
@ -747,10 +939,12 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
#endif #endif
CRT_treeStrAscii; CRT_treeStrAscii;
#ifdef HAVE_GETMOUSE
#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
mousemask(BUTTON1_RELEASED, NULL); mousemask(BUTTON1_RELEASED, NULL);
#endif
#endif #endif
CRT_degreeSign = initDegreeSign(); CRT_degreeSign = initDegreeSign();
@ -759,6 +953,8 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
void CRT_done() { void CRT_done() {
curs_set(1); curs_set(1);
endwin(); endwin();
dumpStderr();
} }
void CRT_fatalError(const char* note) { void CRT_fatalError(const char* note) {
@ -818,15 +1014,14 @@ void CRT_handleSIGSEGV(int signal) {
"============================\n" "============================\n"
"Please check at https://htop.dev/issues whether this issue has already been reported.\n" "Please check at https://htop.dev/issues whether this issue has already been reported.\n"
"If no similar issue has been reported before, please create a new issue with the following information:\n" "If no similar issue has been reported before, please create a new issue with the following information:\n"
"\n" " - Your "PACKAGE" version: '"VERSION"'\n"
"- Your htop version (htop --version)\n" " - Your OS and kernel version (uname -a)\n"
"- Your OS and kernel version (uname -a)\n" " - Your distribution and release (lsb_release -a)\n"
"- Your distribution and release (lsb_release -a)\n" " - Likely steps to reproduce (How did it happen?)\n"
"- Likely steps to reproduce (How did it happened?)\n"
); );
#ifdef HAVE_EXECINFO_H #ifdef HAVE_EXECINFO_H
fprintf(stderr, "- Backtrace of the issue (see below)\n"); fprintf(stderr, " - Backtrace of the issue (see below)\n");
#endif #endif
fprintf(stderr, fprintf(stderr,
@ -845,46 +1040,46 @@ void CRT_handleSIGSEGV(int signal) {
signal, signal_str signal, signal_str
); );
fprintf(stderr,
"Setting information:\n"
"--------------------\n");
Settings_write(CRT_crashSettings, true);
fprintf(stderr, "\n\n");
#ifdef HAVE_EXECINFO_H #ifdef HAVE_EXECINFO_H
fprintf(stderr, fprintf(stderr,
"Backtrace information:\n" "Backtrace information:\n"
"----------------------\n" "----------------------\n"
"The following function calls were active when the issue was detected:\n"
"---\n"
); );
void *backtraceArray[256]; void* backtraceArray[256];
size_t size = backtrace(backtraceArray, ARRAYSIZE(backtraceArray)); size_t size = backtrace(backtraceArray, ARRAYSIZE(backtraceArray));
backtrace_symbols_fd(backtraceArray, size, 2); backtrace_symbols_fd(backtraceArray, size, STDERR_FILENO);
fprintf(stderr, fprintf(stderr,
"---\n"
"\n" "\n"
"To make the above information more practical to work with,\n" "To make the above information more practical to work with, "
"you should provide a disassembly of your binary.\n" "please also provide a disassembly of your "PACKAGE" binary. "
"This can usually be done by running the following command:\n" "This can usually be done by running the following command:\n"
"\n" "\n"
); );
#ifdef HTOP_DARWIN #ifdef HTOP_DARWIN
fprintf(stderr, " otool -tvV `which htop` > ~/htop.otool\n"); fprintf(stderr, " otool -tvV `which "PACKAGE"` > ~/htop.otool\n");
#else #else
fprintf(stderr, " objdump -d -S -w `which htop` > ~/htop.objdump\n"); fprintf(stderr, " objdump -d -S -w `which "PACKAGE"` > ~/htop.objdump\n");
#endif #endif
fprintf(stderr, fprintf(stderr,
"\n" "\n"
"Please include the generated file in your report.\n" "Please include the generated file in your report.\n"
"\n"
); );
#endif #endif
fprintf(stderr, fprintf(stderr,
"Running this program with debug symbols or inside a debugger may provide further insights.\n" "Running this program with debug symbols or inside a debugger may provide further insights.\n"
"\n" "\n"
"Thank you for helping to improve htop!\n" "Thank you for helping to improve "PACKAGE"!\n"
"\n"
"htop " VERSION " aborting.\n"
"\n" "\n"
); );

38
CRT.h
View File

@ -13,6 +13,7 @@ in the source distribution for its full text.
#include "Macros.h" #include "Macros.h"
#include "ProvideCurses.h" #include "ProvideCurses.h"
#include "Settings.h"
typedef enum TreeStr_ { typedef enum TreeStr_ {
@ -52,6 +53,7 @@ typedef enum ColorElements_ {
PANEL_SELECTION_FOLLOW, PANEL_SELECTION_FOLLOW,
PANEL_SELECTION_UNFOCUS, PANEL_SELECTION_UNFOCUS,
LARGE_NUMBER, LARGE_NUMBER,
METER_SHADOW,
METER_TEXT, METER_TEXT,
METER_VALUE, METER_VALUE,
METER_VALUE_ERROR, METER_VALUE_ERROR,
@ -65,13 +67,14 @@ typedef enum ColorElements_ {
BATTERY, BATTERY,
TASKS_RUNNING, TASKS_RUNNING,
SWAP, SWAP,
SWAP_CACHE,
PROCESS, PROCESS,
PROCESS_SHADOW, PROCESS_SHADOW,
PROCESS_TAG, PROCESS_TAG,
PROCESS_MEGABYTES, PROCESS_MEGABYTES,
PROCESS_GIGABYTES, PROCESS_GIGABYTES,
PROCESS_TREE, PROCESS_TREE,
PROCESS_R_STATE, PROCESS_RUN_STATE,
PROCESS_D_STATE, PROCESS_D_STATE,
PROCESS_BASENAME, PROCESS_BASENAME,
PROCESS_HIGH_PRIORITY, PROCESS_HIGH_PRIORITY,
@ -90,6 +93,11 @@ typedef enum ColorElements_ {
MEMORY_BUFFERS, MEMORY_BUFFERS,
MEMORY_BUFFERS_TEXT, MEMORY_BUFFERS_TEXT,
MEMORY_CACHE, MEMORY_CACHE,
MEMORY_SHARED,
HUGEPAGE_1,
HUGEPAGE_2,
HUGEPAGE_3,
HUGEPAGE_4,
LOAD, LOAD,
LOAD_AVERAGE_FIFTEEN, LOAD_AVERAGE_FIFTEEN,
LOAD_AVERAGE_FIVE, LOAD_AVERAGE_FIVE,
@ -101,6 +109,7 @@ typedef enum ColorElements_ {
DATE, DATE,
DATETIME, DATETIME,
HELP_BOLD, HELP_BOLD,
HELP_SHADOW,
HOSTNAME, HOSTNAME,
CPU_NICE, CPU_NICE,
CPU_NICE_TEXT, CPU_NICE_TEXT,
@ -122,6 +131,15 @@ typedef enum ColorElements_ {
ZFS_COMPRESSED, ZFS_COMPRESSED,
ZFS_RATIO, ZFS_RATIO,
ZRAM, ZRAM,
DYNAMIC_GRAY,
DYNAMIC_DARKGRAY,
DYNAMIC_RED,
DYNAMIC_GREEN,
DYNAMIC_BLUE,
DYNAMIC_CYAN,
DYNAMIC_MAGENTA,
DYNAMIC_YELLOW,
DYNAMIC_WHITE,
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
@ -154,24 +172,12 @@ extern int CRT_scrollWheelVAmount;
extern ColorScheme CRT_colorScheme; extern ColorScheme CRT_colorScheme;
#ifdef HAVE_SETUID_ENABLED void CRT_init(const Settings* settings, bool allowUnicode);
void CRT_dropPrivileges(void);
void CRT_restorePrivileges(void);
#else /* HAVE_SETUID_ENABLED */
/* Turn setuid operations into NOPs */
static inline void CRT_dropPrivileges(void) { }
static inline void CRT_restorePrivileges(void) { }
#endif /* HAVE_SETUID_ENABLED */
void CRT_init(const int* delay, int colorScheme, bool allowUnicode);
void CRT_done(void); void CRT_done(void);
void CRT_resetSignalHandlers(void);
int CRT_readKey(void); int CRT_readKey(void);
void CRT_disableDelay(void); void CRT_disableDelay(void);

View File

@ -17,11 +17,16 @@ in the source distribution for its full text.
#include "ColumnsPanel.h" #include "ColumnsPanel.h"
#include "DisplayOptionsPanel.h" #include "DisplayOptionsPanel.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Header.h"
#include "HeaderLayout.h"
#include "HeaderOptionsPanel.h"
#include "ListItem.h" #include "ListItem.h"
#include "Macros.h"
#include "MetersPanel.h" #include "MetersPanel.h"
#include "Object.h" #include "Object.h"
#include "ProvideCurses.h" #include "ProvideCurses.h"
#include "Vector.h" #include "Vector.h"
#include "XUtils.h"
static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@ -33,14 +38,24 @@ static void CategoriesPanel_delete(Object* object) {
free(this); free(this);
} }
void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { static void CategoriesPanel_makeMetersPage(CategoriesPanel* this) {
MetersPanel* leftMeters = MetersPanel_new(this->settings, "Left column", this->header->columns[0], this->scr); size_t columns = HeaderLayout_getColumns(this->scr->header->headerLayout);
MetersPanel* rightMeters = MetersPanel_new(this->settings, "Right column", this->header->columns[1], this->scr); MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel*));
leftMeters->rightNeighbor = rightMeters;
rightMeters->leftNeighbor = leftMeters; for (size_t i = 0; i < columns; i++) {
Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, (Panel*) leftMeters, (Panel*) rightMeters, this->scr, this->pl); char titleBuffer[32];
ScreenManager_add(this->scr, (Panel*) leftMeters, 20); xSnprintf(titleBuffer, sizeof(titleBuffer), "Column %zu", i + 1);
ScreenManager_add(this->scr, (Panel*) rightMeters, 20); meterPanels[i] = MetersPanel_new(this->settings, titleBuffer, this->header->columns[i], this->scr);
if (i != 0) {
meterPanels[i]->leftNeighbor = meterPanels[i - 1];
meterPanels[i - 1]->rightNeighbor = meterPanels[i];
}
ScreenManager_add(this->scr, (Panel*) meterPanels[i], 20);
}
Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, columns, meterPanels, this->scr, this->pl);
ScreenManager_add(this->scr, availableMeters, -1); ScreenManager_add(this->scr, availableMeters, -1);
} }
@ -50,17 +65,36 @@ static void CategoriesPanel_makeDisplayOptionsPage(CategoriesPanel* this) {
} }
static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) { static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
Panel* colors = (Panel*) ColorsPanel_new(this->settings, this->scr); Panel* colors = (Panel*) ColorsPanel_new(this->settings);
ScreenManager_add(this->scr, colors, -1); ScreenManager_add(this->scr, colors, -1);
} }
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) { static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
Panel* columns = (Panel*) ColumnsPanel_new(this->settings); Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns); Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns, this->settings->dynamicColumns);
ScreenManager_add(this->scr, columns, 20); ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, -1); ScreenManager_add(this->scr, availableColumns, -1);
} }
static void CategoriesPanel_makeHeaderOptionsPage(CategoriesPanel* this) {
Panel* colors = (Panel*) HeaderOptionsPanel_new(this->settings, this->scr);
ScreenManager_add(this->scr, colors, -1);
}
typedef void (* CategoriesPanel_makePageFunc)(CategoriesPanel* ref);
typedef struct CategoriesPanelPage_ {
const char* name;
CategoriesPanel_makePageFunc ctor;
} CategoriesPanelPage;
static const CategoriesPanelPage categoriesPanelPages[] = {
{ .name = "Display options", .ctor = CategoriesPanel_makeDisplayOptionsPage },
{ .name = "Header layout", .ctor = CategoriesPanel_makeHeaderOptionsPage },
{ .name = "Meters", .ctor = CategoriesPanel_makeMetersPage },
{ .name = "Columns", .ctor = CategoriesPanel_makeColumnsPage },
{ .name = "Colors", .ctor = CategoriesPanel_makeColorsPage },
};
static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) { static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
CategoriesPanel* this = (CategoriesPanel*) super; CategoriesPanel* this = (CategoriesPanel*) super;
@ -98,19 +132,8 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
for (int i = 1; i < size; i++) for (int i = 1; i < size; i++)
ScreenManager_remove(this->scr, 1); ScreenManager_remove(this->scr, 1);
switch (selected) { if (selected >= 0 && (size_t)selected < ARRAYSIZE(categoriesPanelPages)) {
case 0: categoriesPanelPages[selected].ctor(this);
CategoriesPanel_makeMetersPage(this);
break;
case 1:
CategoriesPanel_makeDisplayOptionsPage(this);
break;
case 2:
CategoriesPanel_makeColorsPage(this);
break;
case 3:
CategoriesPanel_makeColumnsPage(this);
break;
} }
} }
return result; return result;
@ -135,9 +158,10 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
this->header = header; this->header = header;
this->pl = pl; this->pl = pl;
Panel_setHeader(super, "Setup"); Panel_setHeader(super, "Setup");
Panel_add(super, (Object*) ListItem_new("Meters", 0)); for (size_t i = 0; i < ARRAYSIZE(categoriesPanelPages); i++)
Panel_add(super, (Object*) ListItem_new("Display options", 0)); Panel_add(super, (Object*) ListItem_new(categoriesPanelPages[i].name, 0));
Panel_add(super, (Object*) ListItem_new("Colors", 0));
Panel_add(super, (Object*) ListItem_new("Columns", 0)); ScreenManager_add(scr, super, 16);
categoriesPanelPages[0].ctor(this);
return this; return this;
} }

View File

@ -13,6 +13,7 @@ in the source distribution for its full text.
#include "ScreenManager.h" #include "ScreenManager.h"
#include "Settings.h" #include "Settings.h"
typedef struct CategoriesPanel_ { typedef struct CategoriesPanel_ {
Panel super; Panel super;
ScreenManager* scr; ScreenManager* scr;
@ -22,8 +23,6 @@ typedef struct CategoriesPanel_ {
ProcessList* pl; ProcessList* pl;
} CategoriesPanel; } CategoriesPanel;
void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
extern const PanelClass CategoriesPanel_class; extern const PanelClass CategoriesPanel_class;
CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl); CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl);

152
ChangeLog
View File

@ -1,3 +1,153 @@
What's new in version 3.1.0
* Updated COPYING file to remove the PLPA exemption (appendix 2)
With this change the license is now GPLv2 without any additional wording.
* Improved default sort ordering
Note for users: This may lead to an inverted sort order on startup of
htop 3.1.0 compared to previous versions.
This is due to what is stored in your htoprc file. Solution: Press I
(to invert sort order).
This changed setting will be saved by htop on exit as long as it can
write to your htoprc file.
* The compile-time option to cater specifically for running htop as
setuid has been removed
* Add read-only option
This allows htop to be run in an non-intrusive fashion where it acts only
as a process viewer disabling all functions to manipulate system state.
Note: This is not a security feature!
* Move the code for handling the command line formatting related tasks
to be shared across all platforms
This means important features like stale binary/library highlighting
can now be available on all supported platforms.
* Make the EXE and COMM columns available on all platforms
All supported platforms have the name of the executable (EXE) and a
self-chosen thread/command name (COMM) available one way or the other.
Moving this column to be handled as a platform-independently available
information simplifies the markup of the command line.
* Introduce configuration file versioning and config_reader_min_version
Starting with this version the configuration file contains a version
identifying the minimum version of the configuration parser needed to
fully understand the configuration file format.
Old configuration file formats are automatically upgraded when
saving the config file (htoprc).
* Make the configuration parser friendlier to users (thanks to Bart Bakker)
With this change only settings that cannot be parsed properly are
reset to their defaults.
* Improve default display for systems with many CPUs
* Add the process ELAPSED time column
* Improve the process STATE column sorting
* Reworked handling resize and redrawing of the UI
* Fixed an issue where the LED meter mode could overflow allotted space
* Allow text mode Meters to span empty neighbors to the right
* Rescale graph meters when value of total changes
(thanks to Michael Schönitzer)
* Update generic process field display
Usually "uninteresting" values in columns like 1 thread, nice value
of 0, CPU and memory of 0%, idle/sleeping state, etc. are shown with
reduced intensity (dark grey)
* Option and key ("*") to collapse / expand all branches under PID 1
(and PID 2 if kernel threads are shown) (thanks to Krishna Chaitanya)
* Keep following a process when inverting the sort order, displaying
the help screen or hiding/unhiding userland threads.
If a thread is currently selected the selection is updated to point
to the thread's parent process. (thanks to Gonzalo, et.al.)
* Reorder process scanning to be performed before updating the display
of the meters in the header
* Always check the user for a process for any changes.
This affects multiple platforms that previously didn't correctly handle
the user field for a process to change at runtime (e.g. due to seteuid
or similar syscalls).
* Disable mouse option when support is unavailable
* Support curses libraries without ncurses mouse support
(thanks to Santhosh Raju)
* Support offline and hot-swapping of CPUs on all platforms
* Fix the CPU Meter for machines with more than 256 CPUs
* Supplemented the "show updated/deleted executables" feature (red basename)
to indicate when linked libraries were updated (yellow basename)
* Apply the stale binary highlighting for the EXE column in addition to
the command line field
* Add new combined Memory and Swap meter
* Implement bar and graph mode for NetworkIO Meter
(thanks to Michael F. Schönitzer)
* Rework TTY column to be more consistent across platforms
* Make the CWD column generally available on all platforms
(thanks to Santhosh Raju et. al.)
* Add Performance Co-Pilot (PCP) platform support
This is added via a separate pcp-htop(1) binary which provides remote host
analysis, new Meters for any PCP metric and new Columns for any PCP process
metric - see the pcp-htop(5) man page for further details.
(thanks to Sohaib Mohamed)
* Add Linux columns and key bindings for process autogroup identifier
and nice value
* Change available and used memory reporting on Linux to be based on
MemAvailable (Kernel 3.14+) (thanks to Chris Cheney and Tomas Wido)
* Add a new SysArchMeter showing kernel and platform information
(thanks to ahgamut)
* Linux memory usage explicitly treats tmpfs memory usage as shared memory
This is to make memory used by tmpfs visible as this cannot be freed
unlike normal filesystem cache data.
* Exclude zram devices when calculating DiskIO on Linux
* Use PATH lookup for systemctl in systemd meter (thanks to Scott Olson)
* Add native platform support for NetBSD
This allows htop to run on NetBSD without the need for active Linux
emulation of the procfs filesystem.
(thanks to Santhosh Raju and Nia Alarie)
* Add NetworkIO, DiskIO, CPU frequency, and battery meter support on NetBSD
(thanks to Nia Alarie)
* Fix NetBSD display of in-use and cached memory (thanks to Nia Alarie)
* Rework NetBSD CPU and memory accounting (thanks to Santhosh Raju)
* Fix NetBSD accounting of user and kernel threads (thanks to Santhosh Raju)
* Initial work to allow building with default libcurses on NetBSD
(thanks to Santhosh Raju)
* FreeBSD updates - implement process majflt and processor column values
* Add FreeBSD support for CPU frequency and temperature
* Fixes and cleanups for ZFS Meters and metrics
* Correctly color the ZFS ARC ratio (thanks to Ross Williams)
* Bugfixes related to CPU time display/calculations for darwin on M1 systems
(thanks to Alexander Momchilov)
* Harmonize the handling of multiple batteries across different platforms.
The system is now considered to run on AC if at least one power supply
marked as AC is found in the system.
Battery capacity is summed up over all batteries found.
This also changes the old behavior that batteries reported by the
system after the first AC adapter where sometimes ignored.
* Correctly handle multiple batteries on Darwin.
Resolves a possible memory leak on systems with multiple batteries.
* Handle Linux Shmem being part of Cached in the MemoryMeter
* Add SwapCached to the Linux swap meter (thanks to David Zarzycki)
* Convert process time to days if applicable (thanks to David Zarzycki)
* Always show the number of threads in the TaskMeter, even when threads
are not shown in the process list
* Fix Linux --drop-capabilities option handling
* Correctly detect failure to initialize Linux boottime
* Overhaul the Linux memory fields to partition them like free(1) now does
* Improve the Linux process I/O column values
* Rework the libsensors parsing on Linux
* Update the MemoryMeter to display shared memory
* Update OpenBSD platform - implement additional columns, scan LWP,
proper markup for STATE, show CPU frequency
* Fix the tree view on OpenBSD when hiding kernel threads
* Remove old InfoScreen lines before re-scanning (thanks to Øystein Hiåsen)
* Document historic naming of Light-Weight Processes column aka threads
* Improve user interaction when the last process entry is selected
* Draw the panel header on the TraceScreen (thanks to Youngjae Lee)
* Add mouse wheel scroll and fix mouse selection on the InfoScreen
(thanks to Youngjae Lee)
* Add a HugepageMeter and subtract hugepages from normal memory
* Display wide characters in LED meters and restore non-wide ncurses support
* Add command line option to drop Linux capabilities
* Support scheduler affinity on platforms beyond Linux
* Report on any failure to write the configuration file
* Cache stderr to be able to print assert messages.
These messages are shown in case htop terminates unexpectedly.
* Print current settings on crash
* Reset signal handlers on program exit
* Add configure script option to create a static htop binary
* Resolved longer-standing compilation issues on Solaris/Illumos
* Check for availability of set_escdelay in configure
(thanks to Stefan Polluks)
* Build system updates for autotools 2.70
What's new in version 3.0.5 What's new in version 3.0.5
* BUGFIX / SECURITY: InfoScreen: fix uncontrolled format string * BUGFIX / SECURITY: InfoScreen: fix uncontrolled format string
@ -11,7 +161,7 @@ What's new in version 3.0.5
* Drop usage of formatted error messages from <err.h> * Drop usage of formatted error messages from <err.h>
* Show arrow indicating order of sorted process column * Show arrow indicating order of sorted process column
* Lots of plumbing around the internal Hashtable, hardening and code cleanups * Lots of plumbing around the internal Hashtable, hardening and code cleanups
* LibSensors: add support for Ryzen CPUs (vor 5 Tagen) * LibSensors: add support for Ryzen CPUs
(thanks to Matej Dian) (thanks to Matej Dian)
* BUGFIX: Fix CPU percentage on M1 silicon Macs * BUGFIX: Fix CPU percentage on M1 silicon Macs
(thanks to Luke Groeninger) (thanks to Luke Groeninger)

View File

@ -10,21 +10,24 @@ in the source distribution for its full text.
#include "ClockMeter.h" #include "ClockMeter.h"
#include <time.h> #include <time.h>
#include <sys/time.h>
#include "CRT.h" #include "CRT.h"
#include "Object.h" #include "Object.h"
#include "ProcessList.h"
static const int ClockMeter_attributes[] = { static const int ClockMeter_attributes[] = {
CLOCK CLOCK
}; };
static void ClockMeter_updateValues(Meter* this, char* buffer, size_t size) { static void ClockMeter_updateValues(Meter* this) {
time_t t = time(NULL); const ProcessList* pl = this->pl;
struct tm result; struct tm result;
struct tm* lt = localtime_r(&t, &result); const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result);
this->values[0] = lt->tm_hour * 60 + lt->tm_min; this->values[0] = lt->tm_hour * 60 + lt->tm_min;
strftime(buffer, size, "%H:%M:%S", lt); strftime(this->txtBuffer, sizeof(this->txtBuffer), "%H:%M:%S", lt);
} }
const MeterClass ClockMeter_class = { const MeterClass ClockMeter_class = {

View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
extern const MeterClass ClockMeter_class; extern const MeterClass ClockMeter_class;
#endif #endif

View File

@ -7,17 +7,16 @@ in the source distribution for its full text.
#include "ColorsPanel.h" #include "ColorsPanel.h"
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include "CRT.h" #include "CRT.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Header.h" #include "Macros.h"
#include "Object.h" #include "Object.h"
#include "OptionItem.h" #include "OptionItem.h"
#include "ProvideCurses.h" #include "ProvideCurses.h"
#include "RichString.h"
#include "Vector.h"
// TO ADD A NEW SCHEME: // TO ADD A NEW SCHEME:
@ -51,7 +50,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
ColorsPanel* this = (ColorsPanel*) super; ColorsPanel* this = (ColorsPanel*) super;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
int mark = Panel_getSelectedIndex(super); int mark;
switch(ch) { switch(ch) {
case 0x0a: case 0x0a:
@ -60,6 +59,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
case KEY_MOUSE: case KEY_MOUSE:
case KEY_RECLICK: case KEY_RECLICK:
case ' ': case ' ':
mark = Panel_getSelectedIndex(super);
assert(mark >= 0); assert(mark >= 0);
assert(mark < LAST_COLORSCHEME); assert(mark < LAST_COLORSCHEME);
for (int i = 0; ColorSchemeNames[i] != NULL; i++) for (int i = 0; ColorSchemeNames[i] != NULL; i++)
@ -86,14 +86,13 @@ const PanelClass ColorsPanel_class = {
.eventHandler = ColorsPanel_eventHandler .eventHandler = ColorsPanel_eventHandler
}; };
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) { ColorsPanel* ColorsPanel_new(Settings* settings) {
ColorsPanel* this = AllocThis(ColorsPanel); ColorsPanel* this = AllocThis(ColorsPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColorsFunctions, NULL, NULL); FunctionBar* fuBar = FunctionBar_new(ColorsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar); Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);
this->settings = settings; this->settings = settings;
this->scr = scr;
assert(ARRAYSIZE(ColorSchemeNames) == LAST_COLORSCHEME + 1); assert(ARRAYSIZE(ColorSchemeNames) == LAST_COLORSCHEME + 1);

View File

@ -8,18 +8,17 @@ in the source distribution for its full text.
*/ */
#include "Panel.h" #include "Panel.h"
#include "ScreenManager.h"
#include "Settings.h" #include "Settings.h"
typedef struct ColorsPanel_ { typedef struct ColorsPanel_ {
Panel super; Panel super;
Settings* settings; Settings* settings;
ScreenManager* scr;
} ColorsPanel; } ColorsPanel;
extern const PanelClass ColorsPanel_class; extern const PanelClass ColorsPanel_class;
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr); ColorsPanel* ColorsPanel_new(Settings* settings);
#endif #endif

View File

@ -7,11 +7,14 @@ in the source distribution for its full text.
#include "ColumnsPanel.h" #include "ColumnsPanel.h"
#include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include "CRT.h" #include "CRT.h"
#include "DynamicColumn.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Hashtable.h"
#include "ListItem.h" #include "ListItem.h"
#include "Object.h" #include "Object.h"
#include "Process.h" #include "Process.h"
@ -115,6 +118,26 @@ const PanelClass ColumnsPanel_class = {
.eventHandler = ColumnsPanel_eventHandler .eventHandler = ColumnsPanel_eventHandler
}; };
static void ColumnsPanel_add(Panel* super, unsigned int key, Hashtable* columns) {
const char* name;
if (key < LAST_PROCESSFIELD) {
name = Process_fields[key].name;
} else {
const DynamicColumn* column = Hashtable_get(columns, key);
assert(column);
if (!column) {
name = NULL;
} else {
name = column->caption ? column->caption : column->heading;
if (!name)
name = column->name; /* name is a mandatory field */
}
}
if (name == NULL)
name = "- ";
Panel_add(super, (Object*) ListItem_new(name, key));
}
ColumnsPanel* ColumnsPanel_new(Settings* settings) { ColumnsPanel* ColumnsPanel_new(Settings* settings) {
ColumnsPanel* this = AllocThis(ColumnsPanel); ColumnsPanel* this = AllocThis(ColumnsPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
@ -125,12 +148,11 @@ ColumnsPanel* ColumnsPanel_new(Settings* settings) {
this->moving = false; this->moving = false;
Panel_setHeader(super, "Active Columns"); Panel_setHeader(super, "Active Columns");
ProcessField* fields = this->settings->fields; Hashtable* dynamicColumns = settings->dynamicColumns;
for (; *fields; fields++) { const ProcessField* fields = settings->fields;
if (Process_fields[*fields].name) { for (; *fields; fields++)
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields)); ColumnsPanel_add(super, *fields, dynamicColumns);
}
}
return this; return this;
} }
@ -143,7 +165,8 @@ void ColumnsPanel_update(Panel* super) {
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->settings->fields[i] = key; this->settings->fields[i] = key;
this->settings->flags |= Process_fields[key].flags; if (key < LAST_PROCESSFIELD)
this->settings->flags |= Process_fields[key].flags;
} }
this->settings->fields[size] = 0; this->settings->fields[size] = 0;
} }

View File

@ -12,6 +12,7 @@ in the source distribution for its full text.
#include "Panel.h" #include "Panel.h"
#include "Settings.h" #include "Settings.h"
typedef struct ColumnsPanel_ { typedef struct ColumnsPanel_ {
Panel super; Panel super;

400
CommandLine.c Normal file
View File

@ -0,0 +1,400 @@
/*
htop - CommandLine.c
(C) 2004-2011 Hisham H. Muhammad
(C) 2020-2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include "CommandLine.h"
#include <assert.h>
#include <getopt.h>
#include <locale.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "Action.h"
#include "CRT.h"
#include "DynamicColumn.h"
#include "DynamicMeter.h"
#include "Hashtable.h"
#include "Header.h"
#include "IncSet.h"
#include "MainPanel.h"
#include "MetersPanel.h"
#include "Panel.h"
#include "Platform.h"
#include "Process.h"
#include "ProcessList.h"
#include "ProvideCurses.h"
#include "ScreenManager.h"
#include "Settings.h"
#include "UsersTable.h"
#include "XUtils.h"
static void printVersionFlag(const char* name) {
printf("%s " VERSION "\n", name);
}
static void printHelpFlag(const char* name) {
printf("%s " VERSION "\n"
COPYRIGHT "\n"
"Released under the GNU GPLv2.\n\n"
"-C --no-color Use a monochrome color scheme\n"
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
"-F --filter=FILTER Show only the commands matching the given filter\n"
"-h --help Print this help screen\n"
"-H --highlight-changes[=DELAY] Highlight new and old processes\n"
"-M --no-mouse Disable the mouse\n"
"-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
" --readonly Disable all system and process changing features\n"
"-s --sort-key=COLUMN Sort by COLUMN in list view (try --sort-key=help for a list)\n"
"-t --tree Show the tree view (can be combined with -s)\n"
"-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
"-U --no-unicode Do not use unicode but plain ASCII\n"
"-V --version Print version info\n", name);
Platform_longOptionsUsage(name);
printf("\n"
"Long options may be passed with a single dash.\n\n"
"Press F1 inside %s for online help.\n"
"See 'man %s' for more information.\n", name, name);
}
// ----------------------------------------
typedef struct CommandLineSettings_ {
Hashtable* pidMatchList;
char* commFilter;
uid_t userId;
int sortKey;
int delay;
bool useColors;
bool enableMouse;
bool treeView;
bool allowUnicode;
bool highlightChanges;
int highlightDelaySecs;
bool readonly;
} CommandLineSettings;
static CommandLineSettings parseArguments(const char* program, int argc, char** argv) {
CommandLineSettings flags = {
.pidMatchList = NULL,
.commFilter = NULL,
.userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2))
.sortKey = 0,
.delay = -1,
.useColors = true,
.enableMouse = true,
.treeView = false,
.allowUnicode = true,
.highlightChanges = false,
.highlightDelaySecs = -1,
.readonly = false,
};
const struct option long_opts[] =
{
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
{"delay", required_argument, 0, 'd'},
{"sort-key", required_argument, 0, 's'},
{"user", optional_argument, 0, 'u'},
{"no-color", no_argument, 0, 'C'},
{"no-colour", no_argument, 0, 'C'},
{"no-mouse", no_argument, 0, 'M'},
{"no-unicode", no_argument, 0, 'U'},
{"tree", no_argument, 0, 't'},
{"pid", required_argument, 0, 'p'},
{"filter", required_argument, 0, 'F'},
{"highlight-changes", optional_argument, 0, 'H'},
{"readonly", no_argument, 0, 128},
PLATFORM_LONG_OPTIONS
{0, 0, 0, 0}
};
int opt, opti = 0;
/* Parse arguments */
while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:H::", long_opts, &opti))) {
if (opt == EOF)
break;
switch (opt) {
case 'h':
printHelpFlag(program);
exit(0);
case 'V':
printVersionFlag(program);
exit(0);
case 's':
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
if (String_eq(optarg, "help")) {
for (int j = 1; j < LAST_PROCESSFIELD; j++) {
const char* name = Process_fields[j].name;
const char* description = Process_fields[j].description;
if (name) printf("%19s %s\n", name, description);
}
exit(0);
}
flags.sortKey = 0;
for (int j = 1; j < LAST_PROCESSFIELD; j++) {
if (Process_fields[j].name == NULL)
continue;
if (String_eq(optarg, Process_fields[j].name)) {
flags.sortKey = j;
break;
}
}
if (flags.sortKey == 0) {
fprintf(stderr, "Error: invalid column \"%s\".\n", optarg);
exit(1);
}
break;
case 'd':
if (sscanf(optarg, "%16d", &(flags.delay)) == 1) {
if (flags.delay < 1) flags.delay = 1;
if (flags.delay > 100) flags.delay = 100;
} else {
fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg);
exit(1);
}
break;
case 'u':
{
const char *username = optarg;
if (!username && optind < argc && argv[optind] != NULL &&
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
username = argv[optind++];
}
if (!username) {
flags.userId = geteuid();
} else if (!Action_setUserOnly(username, &(flags.userId))) {
fprintf(stderr, "Error: invalid user \"%s\".\n", username);
exit(1);
}
break;
}
case 'C':
flags.useColors = false;
break;
case 'M':
#ifdef HAVE_GETMOUSE
flags.enableMouse = false;
#endif
break;
case 'U':
flags.allowUnicode = false;
break;
case 't':
flags.treeView = true;
break;
case 'p': {
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
char* argCopy = xStrdup(optarg);
char* saveptr;
const char* pid = strtok_r(argCopy, ",", &saveptr);
if (!flags.pidMatchList) {
flags.pidMatchList = Hashtable_new(8, false);
}
while(pid) {
unsigned int num_pid = atoi(pid);
// deepcode ignore CastIntegerToAddress: we just want a non-NUll pointer here
Hashtable_put(flags.pidMatchList, num_pid, (void *) 1);
pid = strtok_r(NULL, ",", &saveptr);
}
free(argCopy);
break;
}
case 'F': {
assert(optarg);
free_and_xStrdup(&flags.commFilter, optarg);
break;
}
case 'H': {
const char *delay = optarg;
if (!delay && optind < argc && argv[optind] != NULL &&
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
delay = argv[optind++];
}
if (delay) {
if (sscanf(delay, "%16d", &(flags.highlightDelaySecs)) == 1) {
if (flags.highlightDelaySecs < 1)
flags.highlightDelaySecs = 1;
} else {
fprintf(stderr, "Error: invalid highlight delay value \"%s\".\n", delay);
exit(1);
}
}
flags.highlightChanges = true;
break;
}
case 128:
flags.readonly = true;
break;
default:
if (Platform_getLongOption(opt, argc, argv) == false)
exit(1);
break;
}
}
return flags;
}
static void CommandLine_delay(ProcessList* pl, unsigned long millisec) {
struct timespec req = {
.tv_sec = 0,
.tv_nsec = millisec * 1000000L
};
while (nanosleep(&req, &req) == -1)
continue;
Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs);
}
static void setCommFilter(State* state, char** commFilter) {
ProcessList* pl = state->pl;
IncSet* inc = state->mainPanel->inc;
IncSet_setFilter(inc, *commFilter);
pl->incFilter = IncSet_filter(inc);
free(*commFilter);
*commFilter = NULL;
}
int CommandLine_run(const char* name, int argc, char** argv) {
/* initialize locale */
const char* lc_ctype;
if ((lc_ctype = getenv("LC_CTYPE")) || (lc_ctype = getenv("LC_ALL")))
setlocale(LC_CTYPE, lc_ctype);
else
setlocale(LC_CTYPE, "");
CommandLineSettings flags = parseArguments(name, argc, argv);
if (flags.readonly)
Settings_enableReadonly();
Platform_init();
Process_setupColumnWidths();
UsersTable* ut = UsersTable_new();
Hashtable* dc = DynamicColumns_new();
Hashtable* dm = DynamicMeters_new();
if (!dc)
dc = Hashtable_new(0, true);
ProcessList* pl = ProcessList_new(ut, dm, dc, flags.pidMatchList, flags.userId);
Settings* settings = Settings_new(pl->activeCPUs, dc);
pl->settings = settings;
Header* header = Header_new(pl, settings, 2);
Header_populateFromSettings(header);
if (flags.delay != -1)
settings->delay = flags.delay;
if (!flags.useColors)
settings->colorScheme = COLORSCHEME_MONOCHROME;
#ifdef HAVE_GETMOUSE
if (!flags.enableMouse)
settings->enableMouse = false;
#endif
if (flags.treeView)
settings->treeView = true;
if (flags.highlightChanges)
settings->highlightChanges = true;
if (flags.highlightDelaySecs != -1)
settings->highlightDelaySecs = flags.highlightDelaySecs;
if (flags.sortKey > 0) {
// -t -s <key> means "tree sorted by key"
// -s <key> means "list sorted by key" (previous existing behavior)
if (!flags.treeView) {
settings->treeView = false;
}
Settings_setSortKey(settings, flags.sortKey);
}
CRT_init(settings, flags.allowUnicode);
MainPanel* panel = MainPanel_new();
ProcessList_setPanel(pl, (Panel*) panel);
MainPanel_updateTreeFunctions(panel, settings->treeView);
State state = {
.settings = settings,
.ut = ut,
.pl = pl,
.mainPanel = panel,
.header = header,
.pauseProcessUpdate = false,
.hideProcessSelection = false,
};
MainPanel_setState(panel, &state);
if (flags.commFilter)
setCommFilter(&state, &(flags.commFilter));
ScreenManager* scr = ScreenManager_new(header, settings, &state, true);
ScreenManager_add(scr, (Panel*) panel, -1);
ProcessList_scan(pl, false);
CommandLine_delay(pl, 75);
ProcessList_scan(pl, false);
if (settings->allBranchesCollapsed)
ProcessList_collapseAllBranches(pl);
ScreenManager_run(scr, NULL, NULL);
attron(CRT_colors[RESET_COLOR]);
mvhline(LINES - 1, 0, ' ', COLS);
attroff(CRT_colors[RESET_COLOR]);
refresh();
Platform_done();
CRT_done();
if (settings->changed) {
int r = Settings_write(settings, false);
if (r < 0)
fprintf(stderr, "Can not save configuration to %s: %s\n", settings->filename, strerror(-r));
}
Header_delete(header);
ProcessList_delete(pl);
ScreenManager_delete(scr);
MetersPanel_cleanup();
UsersTable_delete(ut);
if (flags.pidMatchList)
Hashtable_delete(flags.pidMatchList);
CRT_resetSignalHandlers();
/* Delete these last, since they can get accessed in the crash handler */
Settings_delete(settings);
DynamicColumns_delete(dc);
DynamicMeters_delete(dm);
return 0;
}

14
CommandLine.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef HEADER_CommandLine
#define HEADER_CommandLine
/*
htop - CommandLine.h
(C) 2004-2011 Hisham H. Muhammad
(C) 2020-2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
int CommandLine_run(const char* name, int argc, char** argv);
#endif

View File

@ -2,13 +2,13 @@
#include "CommandScreen.h" #include "CommandScreen.h"
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "Macros.h" #include "Macros.h"
#include "Panel.h" #include "Panel.h"
#include "ProvideCurses.h" #include "ProvideCurses.h"
#include "XUtils.h"
static void CommandScreen_scan(InfoScreen* this) { static void CommandScreen_scan(InfoScreen* this) {

View File

@ -11,18 +11,12 @@ in the source distribution for its full text.
#include <errno.h> #include <errno.h>
#include <fcntl.h> // IWYU pragma: keep #include <fcntl.h> // IWYU pragma: keep
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> // IWYU pragma: keep #include <sys/types.h> // IWYU pragma: keep
#include "XUtils.h" // IWYU pragma: keep #include "XUtils.h" // IWYU pragma: keep
#ifdef HAVE_HOST_GET_CLOCK_SERVICE
#include <mach/clock.h>
#include <mach/mach.h>
#endif
int Compat_faccessat(int dirfd, int Compat_faccessat(int dirfd,
const char* pathname, const char* pathname,
@ -43,7 +37,7 @@ int Compat_faccessat(int dirfd,
#endif #endif
// Error out on unsupported configurations // Error out on unsupported configurations
if (dirfd != AT_FDCWD || mode != F_OK) { if (dirfd != (int)AT_FDCWD || mode != F_OK) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
@ -123,31 +117,3 @@ ssize_t Compat_readlinkat(int dirfd,
#endif #endif
} }
int Compat_clock_monotonic_gettime(struct timespec *tp) {
#if defined(HAVE_CLOCK_GETTIME)
return clock_gettime(CLOCK_MONOTONIC, tp);
#elif defined(HAVE_HOST_GET_CLOCK_SERVICE)
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
tp->tv_sec = mts.tv_sec;
tp->tv_nsec = mts.tv_nsec;
return 0;
#else
#error No Compat_clock_monotonic_gettime() implementation!
#endif
}

View File

@ -56,6 +56,4 @@ ssize_t Compat_readlinkat(int dirfd,
char* buf, char* buf,
size_t bufsize); size_t bufsize);
int Compat_clock_monotonic_gettime(struct timespec *tp);
#endif /* HEADER_Compat */ #endif /* HEADER_Compat */

View File

@ -10,28 +10,30 @@ in the source distribution for its full text.
#include "DateMeter.h" #include "DateMeter.h"
#include <time.h> #include <time.h>
#include <sys/time.h>
#include "CRT.h" #include "CRT.h"
#include "Object.h" #include "Object.h"
#include "ProcessList.h"
static const int DateMeter_attributes[] = { static const int DateMeter_attributes[] = {
DATE DATE
}; };
static void DateMeter_updateValues(Meter* this, char* buffer, size_t size) { static void DateMeter_updateValues(Meter* this) {
time_t t = time(NULL); const ProcessList* pl = this->pl;
struct tm result; struct tm result;
struct tm* lt = localtime_r(&t, &result); const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result);
this->values[0] = lt->tm_yday; this->values[0] = lt->tm_yday;
int year = lt->tm_year + 1900; int year = lt->tm_year + 1900;
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
this->total = 366; this->total = 366;
} } else {
else {
this->total = 365; this->total = 365;
} }
strftime(buffer, size, "%F", lt); strftime(this->txtBuffer, sizeof(this->txtBuffer), "%F", lt);
} }
const MeterClass DateMeter_class = { const MeterClass DateMeter_class = {

View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
extern const MeterClass DateMeter_class; extern const MeterClass DateMeter_class;
#endif #endif

View File

@ -10,28 +10,30 @@ in the source distribution for its full text.
#include "DateTimeMeter.h" #include "DateTimeMeter.h"
#include <time.h> #include <time.h>
#include <sys/time.h>
#include "CRT.h" #include "CRT.h"
#include "Object.h" #include "Object.h"
#include "ProcessList.h"
static const int DateTimeMeter_attributes[] = { static const int DateTimeMeter_attributes[] = {
DATETIME DATETIME
}; };
static void DateTimeMeter_updateValues(Meter* this, char* buffer, size_t size) { static void DateTimeMeter_updateValues(Meter* this) {
time_t t = time(NULL); const ProcessList* pl = this->pl;
struct tm result; struct tm result;
struct tm* lt = localtime_r(&t, &result); const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result);
int year = lt->tm_year + 1900; int year = lt->tm_year + 1900;
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
this->total = 366; this->total = 366;
} } else {
else {
this->total = 365; this->total = 365;
} }
this->values[0] = lt->tm_yday; this->values[0] = lt->tm_yday;
strftime(buffer, size, "%F %H:%M:%S", lt); strftime(this->txtBuffer, sizeof(this->txtBuffer), "%F %H:%M:%S", lt);
} }
const MeterClass DateTimeMeter_class = { const MeterClass DateTimeMeter_class = {

View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
extern const MeterClass DateTimeMeter_class; extern const MeterClass DateTimeMeter_class;
#endif #endif

View File

@ -9,12 +9,12 @@ in the source distribution for its full text.
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <sys/time.h>
#include "CRT.h" #include "CRT.h"
#include "Macros.h" #include "Macros.h"
#include "Object.h" #include "Object.h"
#include "Platform.h" #include "Platform.h"
#include "ProcessList.h"
#include "RichString.h" #include "RichString.h"
#include "XUtils.h" #include "XUtils.h"
@ -26,51 +26,55 @@ static const int DiskIOMeter_attributes[] = {
}; };
static bool hasData = false; static bool hasData = false;
static unsigned long int cached_read_diff = 0; static uint32_t cached_read_diff;
static unsigned long int cached_write_diff = 0; static uint32_t cached_write_diff;
static double cached_utilisation_diff = 0.0; static double cached_utilisation_diff;
static void DiskIOMeter_updateValues(Meter* this, char* buffer, size_t len) { static void DiskIOMeter_updateValues(Meter* this) {
static unsigned long long int cached_last_update = 0; const ProcessList* pl = this->pl;
struct timeval tv; static uint64_t cached_last_update;
gettimeofday(&tv, NULL); uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update;
unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000;
unsigned long long int passedTimeInMs = timeInMilliSeconds - cached_last_update;
/* update only every 500ms */ /* update only every 500ms */
if (passedTimeInMs > 500) { if (passedTimeInMs > 500) {
static unsigned long int cached_read_total = 0; static uint64_t cached_read_total;
static unsigned long int cached_write_total = 0; static uint64_t cached_write_total;
static unsigned long int cached_msTimeSpend_total = 0; static uint64_t cached_msTimeSpend_total;
uint64_t diff;
cached_last_update = timeInMilliSeconds; cached_last_update = pl->realtimeMs;
DiskIOData data; DiskIOData data;
hasData = Platform_getDiskIO(&data); hasData = Platform_getDiskIO(&data);
if (!hasData) { if (!hasData) {
this->values[0] = 0; this->values[0] = 0;
xSnprintf(buffer, len, "no data"); xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data");
return; return;
} }
if (data.totalBytesRead > cached_read_total) { if (data.totalBytesRead > cached_read_total) {
cached_read_diff = (data.totalBytesRead - cached_read_total) / 1024; /* Meter_humanUnit() expects unit in kilo */ diff = data.totalBytesRead - cached_read_total;
diff /= 1024; /* Meter_humanUnit() expects unit in kilo */
cached_read_diff = (uint32_t)diff;
} else { } else {
cached_read_diff = 0; cached_read_diff = 0;
} }
cached_read_total = data.totalBytesRead; cached_read_total = data.totalBytesRead;
if (data.totalBytesWritten > cached_write_total) { if (data.totalBytesWritten > cached_write_total) {
cached_write_diff = (data.totalBytesWritten - cached_write_total) / 1024; /* Meter_humanUnit() expects unit in kilo */ diff = data.totalBytesWritten - cached_write_total;
diff /= 1024; /* Meter_humanUnit() expects unit in kilo */
cached_write_diff = (uint32_t)diff;
} else { } else {
cached_write_diff = 0; cached_write_diff = 0;
} }
cached_write_total = data.totalBytesWritten; cached_write_total = data.totalBytesWritten;
if (data.totalMsTimeSpend > cached_msTimeSpend_total) { if (data.totalMsTimeSpend > cached_msTimeSpend_total) {
cached_utilisation_diff = 100 * (double)(data.totalMsTimeSpend - cached_msTimeSpend_total) / passedTimeInMs; diff = data.totalMsTimeSpend - cached_msTimeSpend_total;
cached_utilisation_diff = 100.0 * (double)diff / passedTimeInMs;
} else { } else {
cached_utilisation_diff = 0.0; cached_utilisation_diff = 0.0;
} }
@ -83,20 +87,21 @@ static void DiskIOMeter_updateValues(Meter* this, char* buffer, size_t len) {
char bufferRead[12], bufferWrite[12]; char bufferRead[12], bufferWrite[12];
Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead)); Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead));
Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite)); Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite));
snprintf(buffer, len, "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff); snprintf(this->txtBuffer, sizeof(this->txtBuffer), "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff);
} }
static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
if (!hasData) { if (!hasData) {
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data"); RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
return; return;
} }
char buffer[16]; char buffer[16];
int len;
int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE; int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff); len = xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
RichString_writeAscii(out, CRT_colors[color], buffer); RichString_appendnAscii(out, CRT_colors[color], buffer, len);
RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: "); RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: ");
Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer)); Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer));
@ -111,7 +116,7 @@ const MeterClass DiskIOMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
.display = DIskIOMeter_display .display = DiskIOMeter_display
}, },
.updateValues = DiskIOMeter_updateValues, .updateValues = DiskIOMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,

View File

@ -9,10 +9,11 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
typedef struct DiskIOData_ { typedef struct DiskIOData_ {
unsigned long int totalBytesRead; uint64_t totalBytesRead;
unsigned long int totalBytesWritten; uint64_t totalBytesWritten;
unsigned long int totalMsTimeSpend; uint64_t totalMsTimeSpend;
} DiskIOData; } DiskIOData;
extern const MeterClass DiskIOMeter_class; extern const MeterClass DiskIOMeter_class;

View File

@ -72,8 +72,9 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
Header* header = this->scr->header; Header* header = this->scr->header;
Header_calculateHeight(header); Header_calculateHeight(header);
Header_reinit(header); Header_reinit(header);
Header_updateData(header);
Header_draw(header); Header_draw(header);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2); ScreenManager_resize(this->scr);
} }
return result; return result;
} }
@ -98,6 +99,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_setHeader(super, "Display options"); Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView))); Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID))); Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->allBranchesCollapsed)));
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers))); Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));
@ -105,6 +107,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_add(super, (Object*) CheckItem_newByRef("Show custom thread names", &(settings->showThreadNames))); Panel_add(super, (Object*) CheckItem_newByRef("Show custom thread names", &(settings->showThreadNames)));
Panel_add(super, (Object*) CheckItem_newByRef("Show program path", &(settings->showProgramPath))); Panel_add(super, (Object*) CheckItem_newByRef("Show program path", &(settings->showProgramPath)));
Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName))); Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName)));
Panel_add(super, (Object*) CheckItem_newByRef("Highlight out-dated/removed programs", &(settings->highlightDeletedExe)));
Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand))); Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand)));
Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline))); Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline)));
Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline))); Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline)));
@ -116,14 +119,24 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_add(super, (Object*) CheckItem_newByRef("Add guest time in CPU meter percentage", &(settings->accountGuestInCPUMeter))); Panel_add(super, (Object*) CheckItem_newByRef("Add guest time in CPU meter percentage", &(settings->accountGuestInCPUMeter)));
Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU percentage numerically", &(settings->showCPUUsage))); Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU percentage numerically", &(settings->showCPUUsage)));
Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU frequency", &(settings->showCPUFrequency))); Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU frequency", &(settings->showCPUFrequency)));
#ifdef HAVE_SENSORS_SENSORS_H #ifdef BUILD_WITH_CPU_TEMP
Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU temperature (requires libsensors)", &(settings->showCPUTemperature))); Panel_add(super, (Object*) CheckItem_newByRef(
#if defined(HTOP_LINUX)
"Also show CPU temperature (requires libsensors)",
#elif defined(HTOP_FREEBSD)
"Also show CPU temperature",
#else
#error Unknown temperature implementation!
#endif
&(settings->showCPUTemperature)));
Panel_add(super, (Object*) CheckItem_newByRef("- Show temperature in degree Fahrenheit instead of Celsius", &(settings->degreeFahrenheit))); Panel_add(super, (Object*) CheckItem_newByRef("- Show temperature in degree Fahrenheit instead of Celsius", &(settings->degreeFahrenheit)));
#endif #endif
#ifdef HAVE_GETMOUSE
Panel_add(super, (Object*) CheckItem_newByRef("Enable the mouse", &(settings->enableMouse))); Panel_add(super, (Object*) CheckItem_newByRef("Enable the mouse", &(settings->enableMouse)));
#endif
Panel_add(super, (Object*) NumberItem_newByRef("Update interval (in seconds)", &(settings->delay), -1, 1, 255)); Panel_add(super, (Object*) NumberItem_newByRef("Update interval (in seconds)", &(settings->delay), -1, 1, 255));
Panel_add(super, (Object*) CheckItem_newByRef("Highlight new and old processes", &(settings->highlightChanges))); Panel_add(super, (Object*) CheckItem_newByRef("Highlight new and old processes", &(settings->highlightChanges)));
Panel_add(super, (Object*) NumberItem_newByRef("- Highlight time (in seconds)", &(settings->highlightDelaySecs), 0, 1, 24*60*60)); Panel_add(super, (Object*) NumberItem_newByRef("- Highlight time (in seconds)", &(settings->highlightDelaySecs), 0, 1, 24 * 60 * 60));
Panel_add(super, (Object*) NumberItem_newByRef("Hide main function bar (0 - off, 1 - on ESC until next input, 2 - permanently)", &(settings->hideFunctionBar), 0, 0, 2)); Panel_add(super, (Object*) NumberItem_newByRef("Hide main function bar (0 - off, 1 - on ESC until next input, 2 - permanently)", &(settings->hideFunctionBar), 0, 0, 2));
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
Panel_add(super, (Object*) CheckItem_newByRef("Show topology when selecting affinity by default", &(settings->topologyAffinity))); Panel_add(super, (Object*) CheckItem_newByRef("Show topology when selecting affinity by default", &(settings->topologyAffinity)));

View File

@ -11,6 +11,7 @@ in the source distribution for its full text.
#include "ScreenManager.h" #include "ScreenManager.h"
#include "Settings.h" #include "Settings.h"
typedef struct DisplayOptionsPanel_ { typedef struct DisplayOptionsPanel_ {
Panel super; Panel super;

66
DynamicColumn.c Normal file
View File

@ -0,0 +1,66 @@
/*
htop - DynamicColumn.c
(C) 2021 Sohaib Mohammed
(C) 2021 htop dev team
(C) 2021 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include "DynamicColumn.h"
#include <stddef.h>
#include "Platform.h"
#include "RichString.h"
#include "XUtils.h"
Hashtable* DynamicColumns_new(void) {
return Platform_dynamicColumns();
}
void DynamicColumns_delete(Hashtable* dynamics) {
if (dynamics) {
Platform_dynamicColumnsDone(dynamics);
Hashtable_delete(dynamics);
}
}
const char* DynamicColumn_init(unsigned int key) {
return Platform_dynamicColumnInit(key);
}
typedef struct {
const char* name;
const DynamicColumn* data;
unsigned int key;
} DynamicIterator;
static void DynamicColumn_compare(ht_key_t key, void* value, void* data) {
const DynamicColumn* column = (const DynamicColumn*)value;
DynamicIterator* iter = (DynamicIterator*)data;
if (String_eq(iter->name, column->name)) {
iter->data = column;
iter->key = key;
}
}
const DynamicColumn* DynamicColumn_search(Hashtable* dynamics, const char* name, unsigned int* key) {
DynamicIterator iter = { .key = 0, .data = NULL, .name = name };
if (dynamics)
Hashtable_foreach(dynamics, DynamicColumn_compare, &iter);
if (key)
*key = iter.key;
return iter.data;
}
const DynamicColumn* DynamicColumn_lookup(Hashtable* dynamics, unsigned int key) {
return (const DynamicColumn*) Hashtable_get(dynamics, key);
}
bool DynamicColumn_writeField(const Process* proc, RichString* str, unsigned int key) {
return Platform_dynamicColumnWriteField(proc, str, key);
}

34
DynamicColumn.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef HEADER_DynamicColumn
#define HEADER_DynamicColumn
#include <stdbool.h>
#include "Hashtable.h"
#include "Process.h"
#include "RichString.h"
#define DYNAMIC_MAX_COLUMN_WIDTH 28
#define DYNAMIC_DEFAULT_COLUMN_WIDTH -5
typedef struct DynamicColumn_ {
char name[32]; /* unique, internal-only name */
char* heading; /* displayed in main screen */
char* caption; /* displayed in setup menu (short name) */
char* description; /* displayed in setup menu (detail) */
int width; /* display width +/- for value alignment */
} DynamicColumn;
Hashtable* DynamicColumns_new(void);
void DynamicColumns_delete(Hashtable* dynamics);
const char* DynamicColumn_init(unsigned int key);
const DynamicColumn* DynamicColumn_lookup(Hashtable* dynamics, unsigned int key);
const DynamicColumn* DynamicColumn_search(Hashtable* dynamics, const char* name, unsigned int* key);
bool DynamicColumn_writeField(const Process* proc, RichString* str, unsigned int key);
#endif

131
DynamicMeter.c Normal file
View File

@ -0,0 +1,131 @@
/*
htop - DynamicMeter.c
(C) 2021 htop dev team
(C) 2021 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include "DynamicMeter.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "CRT.h"
#include "Object.h"
#include "Platform.h"
#include "ProcessList.h"
#include "RichString.h"
#include "XUtils.h"
static const int DynamicMeter_attributes[] = {
DYNAMIC_GRAY,
DYNAMIC_DARKGRAY,
DYNAMIC_RED,
DYNAMIC_GREEN,
DYNAMIC_BLUE,
DYNAMIC_CYAN,
DYNAMIC_MAGENTA,
DYNAMIC_YELLOW,
DYNAMIC_WHITE
};
Hashtable* DynamicMeters_new(void) {
return Platform_dynamicMeters();
}
void DynamicMeters_delete(Hashtable* dynamics) {
if (dynamics) {
Platform_dynamicMetersDone(dynamics);
Hashtable_delete(dynamics);
}
}
typedef struct {
unsigned int key;
const char* name;
bool found;
} DynamicIterator;
static void DynamicMeter_compare(ht_key_t key, void* value, void* data) {
const DynamicMeter* meter = (const DynamicMeter*)value;
DynamicIterator* iter = (DynamicIterator*)data;
if (String_eq(iter->name, meter->name)) {
iter->found = true;
iter->key = key;
}
}
bool DynamicMeter_search(Hashtable* dynamics, const char* name, unsigned int* key) {
DynamicIterator iter = { .key = 0, .name = name, .found = false };
if (dynamics)
Hashtable_foreach(dynamics, DynamicMeter_compare, &iter);
if (key)
*key = iter.key;
return iter.found;
}
const char* DynamicMeter_lookup(Hashtable* dynamics, unsigned int key) {
const DynamicMeter* meter = Hashtable_get(dynamics, key);
return meter ? meter->name : NULL;
}
static void DynamicMeter_init(Meter* meter) {
Platform_dynamicMeterInit(meter);
}
static void DynamicMeter_updateValues(Meter* meter) {
Platform_dynamicMeterUpdateValues(meter);
}
static void DynamicMeter_display(const Object* cast, RichString* out) {
const Meter* meter = (const Meter*)cast;
Platform_dynamicMeterDisplay(meter, out);
}
static const char* DynamicMeter_getCaption(const Meter* this) {
const ProcessList* pl = this->pl;
const DynamicMeter* meter = Hashtable_get(pl->dynamicMeters, this->param);
if (meter)
return meter->caption ? meter->caption : meter->name;
return this->caption;
}
static void DynamicMeter_getUiName(const Meter* this, char* name, size_t length) {
const ProcessList* pl = this->pl;
const DynamicMeter* meter = Hashtable_get(pl->dynamicMeters, this->param);
if (meter) {
const char* uiName = meter->caption;
if (uiName) {
int len = strlen(uiName);
if (len > 2 && uiName[len - 2] == ':')
len -= 2;
xSnprintf(name, length, "%.*s", len, uiName);
} else {
xSnprintf(name, length, "%s", meter->name);
}
}
}
const MeterClass DynamicMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = DynamicMeter_display
},
.init = DynamicMeter_init,
.updateValues = DynamicMeter_updateValues,
.getCaption = DynamicMeter_getCaption,
.getUiName = DynamicMeter_getUiName,
.defaultMode = TEXT_METERMODE,
.maxItems = 0,
.total = 100.0,
.attributes = DynamicMeter_attributes,
.name = "Dynamic",
.uiName = "Dynamic",
.caption = "",
};

28
DynamicMeter.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef HEADER_DynamicMeter
#define HEADER_DynamicMeter
#include <stdbool.h>
#include "Hashtable.h"
#include "Meter.h"
typedef struct DynamicMeter_ {
char name[32]; /* unique name, cannot contain spaces */
char* caption;
char* description;
unsigned int type;
double maximum;
} DynamicMeter;
Hashtable* DynamicMeters_new(void);
void DynamicMeters_delete(Hashtable* dynamics);
const char* DynamicMeter_lookup(Hashtable* dynamics, unsigned int key);
bool DynamicMeter_search(Hashtable* dynamics, const char* name, unsigned int* key);
extern const MeterClass DynamicMeter_class;
#endif

View File

@ -5,7 +5,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "CRT.h"
#include "Macros.h" #include "Macros.h"
#include "Panel.h" #include "Panel.h"
#include "Platform.h" #include "Platform.h"
@ -34,11 +33,9 @@ static void EnvScreen_scan(InfoScreen* this) {
Panel_prune(panel); Panel_prune(panel);
CRT_dropPrivileges();
char* env = Platform_getProcessEnv(this->process->pid); char* env = Platform_getProcessEnv(this->process->pid);
CRT_restorePrivileges();
if (env) { if (env) {
for (char* p = env; *p; p = strrchr(p, 0) + 1) for (const char* p = env; *p; p = strrchr(p, 0) + 1)
InfoScreen_addLine(this, p); InfoScreen_addLine(this, p);
free(env); free(env);
} }

View File

@ -5,6 +5,7 @@
#include "Object.h" #include "Object.h"
#include "Process.h" #include "Process.h"
typedef struct EnvScreen_ { typedef struct EnvScreen_ {
InfoScreen super; InfoScreen super;
} EnvScreen; } EnvScreen;

View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include <stdbool.h> #include <stdbool.h>
typedef struct FunctionBar_ { typedef struct FunctionBar_ {
int size; int size;
char** functions; char** functions;

View File

@ -11,7 +11,6 @@ in the source distribution for its full text.
#include <assert.h> #include <assert.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -19,6 +18,10 @@ in the source distribution for its full text.
#include "Macros.h" #include "Macros.h"
#include "XUtils.h" #include "XUtils.h"
#ifndef NDEBUG
#include <stdio.h>
#endif
typedef struct HashtableItem_ { typedef struct HashtableItem_ {
ht_key_t key; ht_key_t key;
@ -95,7 +98,7 @@ static const uint64_t OEISprimes[] = {
34359738337, 68719476731, 137438953447 34359738337, 68719476731, 137438953447
}; };
static uint64_t nextPrime(size_t n) { static size_t nextPrime(size_t n) {
/* on 32-bit make sure we do not return primes not fitting in size_t */ /* on 32-bit make sure we do not return primes not fitting in size_t */
for (size_t i = 0; i < ARRAYSIZE(OEISprimes) && OEISprimes[i] < SIZE_MAX; i++) { for (size_t i = 0; i < ARRAYSIZE(OEISprimes) && OEISprimes[i] < SIZE_MAX; i++) {
if (n <= OEISprimes[i]) if (n <= OEISprimes[i])

247
Header.c
View File

@ -7,12 +7,17 @@ in the source distribution for its full text.
#include "Header.h" #include "Header.h"
#include <assert.h>
#include <math.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "CRT.h" #include "CRT.h"
#include "CPUMeter.h"
#include "DynamicMeter.h"
#include "Macros.h" #include "Macros.h"
#include "Object.h" #include "Object.h"
#include "Platform.h" #include "Platform.h"
@ -20,15 +25,17 @@ in the source distribution for its full text.
#include "XUtils.h" #include "XUtils.h"
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) { Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout) {
Header* this = xCalloc(1, sizeof(Header)); Header* this = xCalloc(1, sizeof(Header));
this->columns = xCalloc(nrColumns, sizeof(Vector*)); this->columns = xMallocArray(HeaderLayout_getColumns(hLayout), sizeof(Vector*));
this->settings = settings; this->settings = settings;
this->pl = pl; this->pl = pl;
this->nrColumns = nrColumns; this->headerLayout = hLayout;
Header_forEachColumn(this, i) { Header_forEachColumn(this, i) {
this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE); this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE);
} }
return this; return this;
} }
@ -36,42 +43,120 @@ void Header_delete(Header* this) {
Header_forEachColumn(this, i) { Header_forEachColumn(this, i) {
Vector_delete(this->columns[i]); Vector_delete(this->columns[i]);
} }
free(this->columns); free(this->columns);
free(this); free(this);
} }
void Header_populateFromSettings(Header* this) { void Header_setLayout(Header* this, HeaderLayout hLayout) {
Header_forEachColumn(this, col) { size_t oldColumns = HeaderLayout_getColumns(this->headerLayout);
MeterColumnSettings* colSettings = &this->settings->columns[col]; size_t newColumns = HeaderLayout_getColumns(hLayout);
for (int i = 0; i < colSettings->len; i++) {
Header_addMeterByName(this, colSettings->names[i], col); this->headerLayout = hLayout;
if (colSettings->modes[i] != 0) {
Header_setMode(this, i, colSettings->modes[i], col); if (newColumns == oldColumns)
return;
if (newColumns > oldColumns) {
this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*));
for (size_t i = oldColumns; i < newColumns; i++)
this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE);
} else {
// move meters from to-be-deleted columns into last one
for (size_t i = newColumns; i < oldColumns; i++) {
for (int j = this->columns[i]->items - 1; j >= 0; j--) {
Vector_add(this->columns[newColumns - 1], Vector_take(this->columns[i], j));
}
Vector_delete(this->columns[i]);
}
this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*));
}
Header_calculateHeight(this);
}
static void Header_addMeterByName(Header* this, const char* name, MeterModeId mode, unsigned int column) {
assert(column < HeaderLayout_getColumns(this->headerLayout));
Vector* meters = this->columns[column];
const char* paren = strchr(name, '(');
unsigned int param = 0;
size_t nameLen;
if (paren) {
int ok = sscanf(paren, "(%10u)", &param); // CPUMeter
if (!ok) {
char dynamic[32] = {0};
if (sscanf(paren, "(%30s)", dynamic)) { // DynamicMeter
char* end;
if ((end = strrchr(dynamic, ')')) == NULL)
return; // htoprc parse failure
*end = '\0';
if (!DynamicMeter_search(this->pl->dynamicMeters, dynamic, &param))
return; // name lookup failure
} else {
param = 0;
} }
} }
nameLen = paren - name;
} else {
nameLen = strlen(name);
} }
for (const MeterClass* const* type = Platform_meterTypes; *type; type++) {
if (0 == strncmp(name, (*type)->name, nameLen) && (*type)->name[nameLen] == '\0') {
Meter* meter = Meter_new(this->pl, param, *type);
if (mode != 0) {
Meter_setMode(meter, mode);
}
Vector_add(meters, meter);
break;
}
}
}
void Header_populateFromSettings(Header* this) {
Header_setLayout(this, this->settings->hLayout);
Header_forEachColumn(this, col) {
const MeterColumnSetting* colSettings = &this->settings->hColumns[col];
Vector_prune(this->columns[col]);
for (size_t i = 0; i < colSettings->len; i++) {
Header_addMeterByName(this, colSettings->names[i], colSettings->modes[i], col);
}
}
Header_calculateHeight(this); Header_calculateHeight(this);
} }
void Header_writeBackToSettings(const Header* this) { void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) { Settings_setHeaderLayout(this->settings, this->headerLayout);
MeterColumnSettings* colSettings = &this->settings->columns[col];
String_freeArray(colSettings->names); Header_forEachColumn(this, col) {
MeterColumnSetting* colSettings = &this->settings->hColumns[col];
if (colSettings->names) {
for (size_t j = 0; j < colSettings->len; j++)
free(colSettings->names[j]);
free(colSettings->names);
}
free(colSettings->modes); free(colSettings->modes);
Vector* vec = this->columns[col]; const Vector* vec = this->columns[col];
int len = Vector_size(vec); int len = Vector_size(vec);
colSettings->names = xCalloc(len + 1, sizeof(char*)); colSettings->names = len ? xCalloc(len, sizeof(char*)) : NULL;
colSettings->modes = xCalloc(len, sizeof(int)); colSettings->modes = len ? xCalloc(len, sizeof(int)) : NULL;
colSettings->len = len; colSettings->len = len;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
Meter* meter = (Meter*) Vector_get(vec, i); const Meter* meter = (Meter*) Vector_get(vec, i);
char* name; char* name;
if (meter->param) { if (meter->param && As_Meter(meter) == &DynamicMeter_class) {
xAsprintf(&name, "%s(%d)", As_Meter(meter)->name, meter->param); const char* dynamic = DynamicMeter_lookup(this->pl->dynamicMeters, meter->param);
xAsprintf(&name, "%s(%s)", As_Meter(meter)->name, dynamic);
} else if (meter->param && As_Meter(meter) == &CPUMeter_class) {
xAsprintf(&name, "%s(%u)", As_Meter(meter)->name, meter->param);
} else { } else {
xAsprintf(&name, "%s", As_Meter(meter)->name); xAsprintf(&name, "%s", As_Meter(meter)->name);
} }
@ -81,44 +166,9 @@ void Header_writeBackToSettings(const Header* this) {
} }
} }
MeterModeId Header_addMeterByName(Header* this, char* name, int column) { Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, unsigned int column) {
Vector* meters = this->columns[column]; assert(column < HeaderLayout_getColumns(this->headerLayout));
char* paren = strchr(name, '(');
int param = 0;
if (paren) {
int ok = sscanf(paren, "(%10d)", &param);
if (!ok)
param = 0;
*paren = '\0';
}
MeterModeId mode = TEXT_METERMODE;
for (const MeterClass* const* type = Platform_meterTypes; *type; type++) {
if (String_eq(name, (*type)->name)) {
Meter* meter = Meter_new(this->pl, param, *type);
Vector_add(meters, meter);
mode = meter->mode;
break;
}
}
if (paren)
*paren = '(';
return mode;
}
void Header_setMode(Header* this, int i, MeterModeId mode, int column) {
Vector* meters = this->columns[column];
if (i >= Vector_size(meters))
return;
Meter* meter = (Meter*) Vector_get(meters, i);
Meter_setMode(meter, mode);
}
Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, int column) {
Vector* meters = this->columns[column]; Vector* meters = this->columns[column];
Meter* meter = Meter_new(this->pl, param, type); Meter* meter = Meter_new(this->pl, param, type);
@ -126,18 +176,6 @@ Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, i
return meter; return meter;
} }
int Header_size(Header* this, int column) {
Vector* meters = this->columns[column];
return Vector_size(meters);
}
MeterModeId Header_readMeterMode(Header* this, int i, int column) {
Vector* meters = this->columns[column];
Meter* meter = (Meter*) Vector_get(meters, i);
return meter->mode;
}
void Header_reinit(Header* this) { void Header_reinit(Header* this) {
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
for (int i = 0; i < Vector_size(this->columns[col]); i++) { for (int i = 0; i < Vector_size(this->columns[col]); i++) {
@ -150,35 +188,94 @@ void Header_reinit(Header* this) {
} }
void Header_draw(const Header* this) { void Header_draw(const Header* this) {
int height = this->height; const int height = this->height;
int pad = this->pad; const int pad = this->pad;
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
mvhline(y, 0, ' ', COLS); mvhline(y, 0, ' ', COLS);
} }
int width = COLS / this->nrColumns - (pad * this->nrColumns - 1) - 1; const int width = COLS - pad;
int x = pad; int x = pad;
float roundingLoss = 0.0F;
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
Vector* meters = this->columns[col]; Vector* meters = this->columns[col];
float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0F;
roundingLoss += colWidth - floorf(colWidth);
if (roundingLoss >= 1.0F) {
colWidth += 1.0F;
roundingLoss -= 1.0F;
}
for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) { for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i); Meter* meter = (Meter*) Vector_get(meters, i);
meter->draw(meter, x, y, width);
float actualWidth = colWidth;
if (meter->mode == TEXT_METERMODE) {
for (int j = 1; j < meter->columnWidthCount; j++) {
actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0F;
}
}
assert(meter->draw);
meter->draw(meter, x, y, floorf(actualWidth));
y += meter->h; y += meter->h;
} }
x += width + pad;
x += floorf(colWidth);
} }
} }
void Header_updateData(Header* this) {
Header_forEachColumn(this, col) {
Vector* meters = this->columns[col];
int items = Vector_size(meters);
for (int i = 0; i < items; i++) {
Meter* meter = (Meter*) Vector_get(meters, i);
Meter_updateValues(meter);
}
}
}
/*
* Calculate how many columns the current meter is allowed to span,
* by counting how many columns to the right are empty or contain a BlankMeter.
* Returns the number of columns to span, i.e. if the direct neighbor is occupied 1.
*/
static int calcColumnWidthCount(const Header* this, const Meter* curMeter, const int pad, const unsigned int curColumn, const int curHeight) {
for (size_t i = curColumn + 1; i < HeaderLayout_getColumns(this->headerLayout); i++) {
const Vector* meters = this->columns[i];
int height = pad;
for (int j = 0; j < Vector_size(meters); j++) {
const Meter* meter = (const Meter*) Vector_get(meters, j);
if (height >= curHeight + curMeter->h)
break;
height += meter->h;
if (height <= curHeight)
continue;
if (!Object_isA((const Object*) meter, (const ObjectClass*) &BlankMeter_class))
return i - curColumn;
}
}
return HeaderLayout_getColumns(this->headerLayout) - curColumn;
}
int Header_calculateHeight(Header* this) { int Header_calculateHeight(Header* this) {
int pad = this->settings->headerMargin ? 2 : 0; const int pad = this->settings->headerMargin ? 2 : 0;
int maxHeight = pad; int maxHeight = pad;
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
Vector* meters = this->columns[col]; const Vector* meters = this->columns[col];
int height = pad; int height = pad;
for (int i = 0; i < Vector_size(meters); i++) { for (int i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i); Meter* meter = (Meter*) Vector_get(meters, i);
meter->columnWidthCount = calcColumnWidthCount(this, meter, pad, col, height);
height += meter->h; height += meter->h;
} }
maxHeight = MAXIMUM(maxHeight, height); maxHeight = MAXIMUM(maxHeight, height);

View File

@ -7,44 +7,42 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "HeaderLayout.h"
#include "Meter.h" #include "Meter.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "Settings.h" #include "Settings.h"
#include "Vector.h" #include "Vector.h"
typedef struct Header_ { typedef struct Header_ {
Vector** columns; Vector** columns;
Settings* settings; Settings* settings;
ProcessList* pl; ProcessList* pl;
int nrColumns; HeaderLayout headerLayout;
int pad; int pad;
int height; int height;
} Header; } Header;
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_)) #define Header_forEachColumn(this_, i_) for (size_t (i_)=0; (i_) < HeaderLayout_getColumns((this_)->headerLayout); ++(i_))
Header* Header_new(ProcessList* pl, Settings* settings, int nrColumns); Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout);
void Header_delete(Header* this); void Header_delete(Header* this);
void Header_setLayout(Header* this, HeaderLayout hLayout);
void Header_populateFromSettings(Header* this); void Header_populateFromSettings(Header* this);
void Header_writeBackToSettings(const Header* this); void Header_writeBackToSettings(const Header* this);
MeterModeId Header_addMeterByName(Header* this, char* name, int column); Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, unsigned int column);
void Header_setMode(Header* this, int i, MeterModeId mode, int column);
Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, int column);
int Header_size(Header* this, int column);
MeterModeId Header_readMeterMode(Header* this, int i, int column);
void Header_reinit(Header* this); void Header_reinit(Header* this);
void Header_draw(const Header* this); void Header_draw(const Header* this);
void Header_updateData(Header* this);
int Header_calculateHeight(Header* this); int Header_calculateHeight(Header* this);
#endif #endif

77
HeaderLayout.h Normal file
View File

@ -0,0 +1,77 @@
#ifndef HEADER_HeaderLayout
#define HEADER_HeaderLayout
/*
htop - HeaderLayout.h
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "Macros.h"
#include "XUtils.h"
typedef enum HeaderLayout_ {
HF_TWO_50_50,
HF_TWO_33_67,
HF_TWO_67_33,
HF_THREE_33_34_33,
HF_THREE_25_25_50,
HF_THREE_25_50_25,
HF_THREE_50_25_25,
HF_THREE_40_20_40,
HF_FOUR_25_25_25_25,
LAST_HEADER_LAYOUT
} HeaderLayout;
static const struct {
uint8_t columns;
const uint8_t widths[4];
const char* name;
const char* description;
} HeaderLayout_layouts[LAST_HEADER_LAYOUT] = {
[HF_TWO_50_50] = { 2, { 50, 50, 0, 0 }, "two_50_50", "2 columns - 50/50 (default)", },
[HF_TWO_33_67] = { 2, { 33, 67, 0, 0 }, "two_33_67", "2 columns - 33/67", },
[HF_TWO_67_33] = { 2, { 67, 33, 0, 0 }, "two_67_33", "2 columns - 67/33", },
[HF_THREE_33_34_33] = { 3, { 33, 34, 33, 0 }, "three_33_34_33", "3 columns - 33/34/33", },
[HF_THREE_25_25_50] = { 3, { 25, 25, 50, 0 }, "three_25_25_50", "3 columns - 25/25/50", },
[HF_THREE_25_50_25] = { 3, { 25, 50, 25, 0 }, "three_25_50_25", "3 columns - 25/50/25", },
[HF_THREE_50_25_25] = { 3, { 50, 25, 25, 0 }, "three_50_25_25", "3 columns - 50/25/25", },
[HF_THREE_40_20_40] = { 3, { 40, 20, 40, 0 }, "three_40_20_40", "3 columns - 40/20/40", },
[HF_FOUR_25_25_25_25] = { 4, { 25, 25, 25, 25 }, "four_25_25_25_25", "4 columns - 25/25/25/25", },
};
static inline size_t HeaderLayout_getColumns(HeaderLayout hLayout) {
/* assert the layout is initialized */
assert(0 <= hLayout);
assert(hLayout < LAST_HEADER_LAYOUT);
assert(HeaderLayout_layouts[hLayout].name[0]);
assert(HeaderLayout_layouts[hLayout].description[0]);
return HeaderLayout_layouts[hLayout].columns;
}
static inline const char* HeaderLayout_getName(HeaderLayout hLayout) {
/* assert the layout is initialized */
assert(0 <= hLayout);
assert(hLayout < LAST_HEADER_LAYOUT);
assert(HeaderLayout_layouts[hLayout].name[0]);
assert(HeaderLayout_layouts[hLayout].description[0]);
return HeaderLayout_layouts[hLayout].name;
}
static inline HeaderLayout HeaderLayout_fromName(const char* name) {
for (size_t i = 0; i < LAST_HEADER_LAYOUT; i++) {
if (String_eq(HeaderLayout_layouts[i].name, name))
return (HeaderLayout) i;
}
return LAST_HEADER_LAYOUT;
}
#endif /* HEADER_HeaderLayout */

87
HeaderOptionsPanel.c Normal file
View File

@ -0,0 +1,87 @@
/*
htop - HeaderOptionsPanel.c
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "HeaderOptionsPanel.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include "CRT.h"
#include "FunctionBar.h"
#include "Header.h"
#include "HeaderLayout.h"
#include "Object.h"
#include "OptionItem.h"
#include "ProvideCurses.h"
static const char* const HeaderOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static void HeaderOptionsPanel_delete(Object* object) {
Panel* super = (Panel*) object;
HeaderOptionsPanel* this = (HeaderOptionsPanel*) object;
Panel_done(super);
free(this);
}
static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) {
HeaderOptionsPanel* this = (HeaderOptionsPanel*) super;
HandlerResult result = IGNORED;
int mark;
switch(ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
case ' ':
mark = Panel_getSelectedIndex(super);
assert(mark >= 0);
assert(mark < LAST_HEADER_LAYOUT);
for (int i = 0; i < LAST_HEADER_LAYOUT; i++)
CheckItem_set((CheckItem*)Panel_get(super, i), false);
CheckItem_set((CheckItem*)Panel_get(super, mark), true);
Header_setLayout(this->scr->header, mark);
this->settings->changed = true;
ScreenManager_resize(this->scr);
result = HANDLED;
}
return result;
}
const PanelClass HeaderOptionsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = HeaderOptionsPanel_delete
},
.eventHandler = HeaderOptionsPanel_eventHandler
};
HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* scr) {
HeaderOptionsPanel* this = AllocThis(HeaderOptionsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(HeaderOptionsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);
this->scr = scr;
this->settings = settings;
Panel_setHeader(super, "Header Layout");
for (int i = 0; i < LAST_HEADER_LAYOUT; i++) {
Panel_add(super, (Object*) CheckItem_newByVal(HeaderLayout_layouts[i].description, false));
}
CheckItem_set((CheckItem*)Panel_get(super, settings->hLayout), true);
return this;
}

26
HeaderOptionsPanel.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef HEADER_HeaderOptionsPanel
#define HEADER_HeaderOptionsPanel
/*
htop - ColorsPanel.h
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "ScreenManager.h"
#include "Settings.h"
typedef struct HeaderOptionsPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
} HeaderOptionsPanel;
extern const PanelClass HeaderOptionsPanel_class;
HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* scr);
#endif /* HEADER_HeaderOptionsPanel */

View File

@ -9,19 +9,17 @@ in the source distribution for its full text.
#include "HostnameMeter.h" #include "HostnameMeter.h"
#include <unistd.h>
#include "CRT.h" #include "CRT.h"
#include "Object.h" #include "Object.h"
#include "Platform.h"
static const int HostnameMeter_attributes[] = { static const int HostnameMeter_attributes[] = {
HOSTNAME HOSTNAME
}; };
static void HostnameMeter_updateValues(Meter* this, char* buffer, size_t size) { static void HostnameMeter_updateValues(Meter* this) {
(void) this; Platform_getHostname(this->txtBuffer, sizeof(this->txtBuffer));
gethostname(buffer, size - 1);
} }
const MeterClass HostnameMeter_class = { const MeterClass HostnameMeter_class = {

View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
extern const MeterClass HostnameMeter_class; extern const MeterClass HostnameMeter_class;
#endif #endif

View File

@ -29,6 +29,13 @@ void IncSet_reset(IncSet* this, IncType type) {
IncMode_reset(&this->modes[type]); IncMode_reset(&this->modes[type]);
} }
void IncSet_setFilter(IncSet* this, const char* filter) {
IncMode* mode = &this->modes[INC_FILTER];
size_t len = String_safeStrncpy(mode->buffer, filter, sizeof(mode->buffer));
mode->index = len;
this->filtering = true;
}
static const char* const searchFunctions[] = {"Next ", "Prev ", "Cancel ", " Search: ", NULL}; static const char* const searchFunctions[] = {"Next ", "Prev ", "Cancel ", " Search: ", NULL};
static const char* const searchKeys[] = {"F3", "S-F3", "Esc", " "}; static const char* const searchKeys[] = {"F3", "S-F3", "Esc", " "};
static const int searchEvents[] = {KEY_F(3), KEY_F(15), 27, ERR}; static const int searchEvents[] = {KEY_F(3), KEY_F(15), 27, ERR};
@ -70,8 +77,8 @@ void IncSet_delete(IncSet* this) {
free(this); free(this);
} }
static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) { static void updateWeakPanel(const IncSet* this, Panel* panel, Vector* lines) {
Object* selected = Panel_getSelected(panel); const Object* selected = Panel_getSelected(panel);
Panel_prune(panel); Panel_prune(panel);
if (this->filtering) { if (this->filtering) {
int n = 0; int n = 0;
@ -98,7 +105,7 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
} }
} }
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) { static bool search(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel); int size = Panel_size(panel);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) { if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
@ -110,7 +117,7 @@ static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelVa
return false; return false;
} }
static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) { static bool IncMode_find(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
int size = Panel_size(panel); int size = Panel_size(panel);
int here = Panel_getSelectedIndex(panel); int here = Panel_getSelectedIndex(panel);
int i = here; int i = here;
@ -201,7 +208,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} }
const char* IncSet_getListItemValue(Panel* panel, int i) { const char* IncSet_getListItemValue(Panel* panel, int i) {
ListItem* l = (ListItem*) Panel_get(panel, i); const ListItem* l = (const ListItem*) Panel_get(panel, i);
return l ? l->value : ""; return l ? l->value : "";
} }

View File

@ -14,6 +14,7 @@ in the source distribution for its full text.
#include "Panel.h" #include "Panel.h"
#include "Vector.h" #include "Vector.h"
#define INCMODE_MAX 40 #define INCMODE_MAX 40
typedef enum { typedef enum {
@ -40,6 +41,8 @@ static inline const char* IncSet_filter(const IncSet* this) {
return this->filtering ? this->modes[INC_FILTER].buffer : NULL; return this->filtering ? this->modes[INC_FILTER].buffer : NULL;
} }
void IncSet_setFilter(IncSet* this, const char* filter);
typedef const char* (*IncMode_GetPanelValue)(Panel*, int); typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
void IncSet_reset(IncSet* this, IncType type); void IncSet_reset(IncSet* this, IncType type);

View File

@ -4,7 +4,6 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "CRT.h" #include "CRT.h"
@ -28,7 +27,7 @@ InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBa
} }
this->display = Panel_new(0, 1, COLS, height, Class(ListItem), false, bar); this->display = Panel_new(0, 1, COLS, height, Class(ListItem), false, bar);
this->inc = IncSet_new(bar); this->inc = IncSet_new(bar);
this->lines = Vector_new(this->display->items->type, true, DEFAULT_SIZE); this->lines = Vector_new(Vector_type(this->display->items), true, DEFAULT_SIZE);
Panel_setHeader(this->display, panelHeader); Panel_setHeader(this->display, panelHeader);
return this; return this;
} }
@ -95,7 +94,9 @@ void InfoScreen_run(InfoScreen* this) {
if (this->inc->active) { if (this->inc->active) {
(void) move(LINES - 1, CRT_cursorX); (void) move(LINES - 1, CRT_cursorX);
} }
#ifdef HAVE_SET_ESCDELAY
set_escdelay(25); set_escdelay(25);
#endif
int ch = getch(); int ch = getch();
if (ch == ERR) { if (ch == ERR) {
@ -105,18 +106,29 @@ void InfoScreen_run(InfoScreen* this) {
} }
} }
#ifdef HAVE_GETMOUSE
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.bstate & BUTTON1_RELEASED) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV); if (mevent.y >= panel->y && mevent.y < LINES - 1) {
ch = 0; Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
} else if (mevent.y == LINES - 1) { ch = 0;
ch = IncSet_synthesizeEvent(this->inc, mevent.x); } else if (mevent.y == LINES - 1) {
ch = IncSet_synthesizeEvent(this->inc, mevent.x);
}
} }
#if NCURSES_MOUSE_VERSION > 1
else if (mevent.bstate & BUTTON4_PRESSED) {
ch = KEY_WHEELUP;
} else if (mevent.bstate & BUTTON5_PRESSED) {
ch = KEY_WHEELDOWN;
}
#endif
} }
} }
#endif
if (this->inc->active) { if (this->inc->active) {
IncSet_handleKey(this->inc, ch, panel, IncSet_getListItemValue, this->lines); IncSet_handleKey(this->inc, ch, panel, IncSet_getListItemValue, this->lines);
@ -136,8 +148,10 @@ void InfoScreen_run(InfoScreen* this) {
break; break;
case KEY_F(5): case KEY_F(5):
clear(); clear();
if (As_InfoScreen(this)->scan) if (As_InfoScreen(this)->scan) {
Vector_prune(this->lines);
InfoScreen_scan(this); InfoScreen_scan(this);
}
InfoScreen_draw(this); InfoScreen_draw(this);
break; break;
@ -152,8 +166,10 @@ void InfoScreen_run(InfoScreen* this) {
break; break;
case KEY_RESIZE: case KEY_RESIZE:
Panel_resize(panel, COLS, LINES - 2); Panel_resize(panel, COLS, LINES - 2);
if (As_InfoScreen(this)->scan) if (As_InfoScreen(this)->scan) {
Vector_prune(this->lines);
InfoScreen_scan(this); InfoScreen_scan(this);
}
InfoScreen_draw(this); InfoScreen_draw(this);
break; break;

View File

@ -31,11 +31,9 @@ static void ListItem_display(const Object* cast, RichString* out) {
if (this->moving) { if (this->moving) {
RichString_writeWide(out, CRT_colors[DEFAULT_COLOR], RichString_writeWide(out, CRT_colors[DEFAULT_COLOR],
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
CRT_utf8 ? "" : CRT_utf8 ? "" :
#endif #endif
"+ "); "+ ");
} else {
RichString_prune(out);
} }
RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value); RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value);
} }

View File

@ -11,6 +11,7 @@ in the source distribution for its full text.
#include "Object.h" #include "Object.h"
typedef struct ListItem_ { typedef struct ListItem_ {
Object super; Object super;
char* value; char* value;

View File

@ -10,6 +10,7 @@ in the source distribution for its full text.
#include "CRT.h" #include "CRT.h"
#include "Object.h" #include "Object.h"
#include "Platform.h" #include "Platform.h"
#include "ProcessList.h"
#include "RichString.h" #include "RichString.h"
#include "XUtils.h" #include "XUtils.h"
@ -36,7 +37,7 @@ static const int High_attributes[] = {
METER_VALUE_ERROR METER_VALUE_ERROR
}; };
static void LoadAverageMeter_updateValues(Meter* this, char* buffer, size_t size) { static void LoadAverageMeter_updateValues(Meter* this) {
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]); Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
// only show bar for 1min value // only show bar for 1min value
@ -46,29 +47,31 @@ static void LoadAverageMeter_updateValues(Meter* this, char* buffer, size_t size
if (this->values[0] < 1.0) { if (this->values[0] < 1.0) {
this->curAttributes = OK_attributes; this->curAttributes = OK_attributes;
this->total = 1.0; this->total = 1.0;
} else if (this->values[0] < this->pl->cpuCount) { } else if (this->values[0] < this->pl->activeCPUs) {
this->curAttributes = Medium_attributes; this->curAttributes = Medium_attributes;
this->total = this->pl->cpuCount; this->total = this->pl->activeCPUs;
} else { } else {
this->curAttributes = High_attributes; this->curAttributes = High_attributes;
this->total = 2 * this->pl->cpuCount; this->total = 2 * this->pl->activeCPUs;
} }
xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]); xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
} }
static void LoadAverageMeter_display(const Object* cast, RichString* out) { static void LoadAverageMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
char buffer[20]; char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]); int len;
RichString_writeAscii(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]); len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer); RichString_appendnAscii(out, CRT_colors[LOAD_AVERAGE_ONE], buffer, len);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]); len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer); RichString_appendnAscii(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer, len);
len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
RichString_appendnAscii(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer, len);
} }
static void LoadMeter_updateValues(Meter* this, char* buffer, size_t size) { static void LoadMeter_updateValues(Meter* this) {
double five, fifteen; double five, fifteen;
Platform_getLoadAverage(&this->values[0], &five, &fifteen); Platform_getLoadAverage(&this->values[0], &five, &fifteen);
@ -76,22 +79,24 @@ static void LoadMeter_updateValues(Meter* this, char* buffer, size_t size) {
if (this->values[0] < 1.0) { if (this->values[0] < 1.0) {
this->curAttributes = OK_attributes; this->curAttributes = OK_attributes;
this->total = 1.0; this->total = 1.0;
} else if (this->values[0] < this->pl->cpuCount) { } else if (this->values[0] < this->pl->activeCPUs) {
this->curAttributes = Medium_attributes; this->curAttributes = Medium_attributes;
this->total = this->pl->cpuCount; this->total = this->pl->activeCPUs;
} else { } else {
this->curAttributes = High_attributes; this->curAttributes = High_attributes;
this->total = 2 * this->pl->cpuCount; this->total = 2 * this->pl->activeCPUs;
} }
xSnprintf(buffer, size, "%.2f", this->values[0]); xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f", this->values[0]);
} }
static void LoadMeter_display(const Object* cast, RichString* out) { static void LoadMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
char buffer[20]; char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]); int len;
RichString_writeAscii(out, CRT_colors[LOAD], buffer);
len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_appendnAscii(out, CRT_colors[LOAD], buffer, len);
} }
const MeterClass LoadAverageMeter_class = { const MeterClass LoadAverageMeter_class = {

View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
extern const MeterClass LoadAverageMeter_class; extern const MeterClass LoadAverageMeter_class;
extern const MeterClass LoadMeter_class; extern const MeterClass LoadMeter_class;

View File

@ -4,27 +4,31 @@
#include <assert.h> // IWYU pragma: keep #include <assert.h> // IWYU pragma: keep
#ifndef MINIMUM #ifndef MINIMUM
#define MINIMUM(a, b) ((a) < (b) ? (a) : (b)) #define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
#endif #endif
#ifndef MAXIMUM #ifndef MAXIMUM
#define MAXIMUM(a, b) ((a) > (b) ? (a) : (b)) #define MAXIMUM(a, b) ((a) > (b) ? (a) : (b))
#endif #endif
#ifndef CLAMP #ifndef CLAMP
#define CLAMP(x, low, high) (assert((low) <= (high)), ((x) > (high)) ? (high) : MAXIMUM(x, low)) #define CLAMP(x, low, high) (assert((low) <= (high)), ((x) > (high)) ? (high) : MAXIMUM(x, low))
#endif #endif
#ifndef ARRAYSIZE #ifndef ARRAYSIZE
#define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0])) #define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif #endif
#ifndef SPACESHIP_NUMBER #ifndef SPACESHIP_NUMBER
#define SPACESHIP_NUMBER(a, b) (((a) > (b)) - ((a) < (b))) #define SPACESHIP_NUMBER(a, b) (((a) > (b)) - ((a) < (b)))
#endif #endif
#ifndef SPACESHIP_NULLSTR #ifndef SPACESHIP_NULLSTR
#define SPACESHIP_NULLSTR(a, b) strcmp((a) ? (a) : "", (b) ? (b) : "") #define SPACESHIP_NULLSTR(a, b) strcmp((a) ? (a) : "", (b) ? (b) : "")
#endif
#ifndef SPACESHIP_DEFAULTSTR
#define SPACESHIP_DEFAULTSTR(a, b, s) strcmp((a) ? (a) : (s), (b) ? (b) : (s))
#endif #endif
#ifdef __GNUC__ // defined by GCC and Clang #ifdef __GNUC__ // defined by GCC and Clang
@ -33,6 +37,7 @@
#define ATTR_NONNULL __attribute__((nonnull)) #define ATTR_NONNULL __attribute__((nonnull))
#define ATTR_NORETURN __attribute__((noreturn)) #define ATTR_NORETURN __attribute__((noreturn))
#define ATTR_UNUSED __attribute__((unused)) #define ATTR_UNUSED __attribute__((unused))
#define ATTR_MALLOC __attribute__((malloc))
#else /* __GNUC__ */ #else /* __GNUC__ */
@ -40,13 +45,26 @@
#define ATTR_NONNULL #define ATTR_NONNULL
#define ATTR_NORETURN #define ATTR_NORETURN
#define ATTR_UNUSED #define ATTR_UNUSED
#define ATTR_MALLOC
#endif /* __GNUC__ */ #endif /* __GNUC__ */
#ifdef HAVE_ATTR_ALLOC_SIZE
#define ATTR_ALLOC_SIZE1(a) __attribute__((alloc_size (a)))
#define ATTR_ALLOC_SIZE2(a, b) __attribute__((alloc_size (a, b)))
#else
#define ATTR_ALLOC_SIZE1(a)
#define ATTR_ALLOC_SIZE2(a, b)
#endif /* HAVE_ATTR_ALLOC_SIZE */
// ignore casts discarding const specifier, e.g. // ignore casts discarding const specifier, e.g.
// const char [] -> char * / void * // const char [] -> char * / void *
// const char *[2]' -> char *const * // const char *[2]' -> char *const *
#ifdef __clang__ #if defined(__clang__)
#define IGNORE_WCASTQUAL_BEGIN _Pragma("clang diagnostic push") \ #define IGNORE_WCASTQUAL_BEGIN _Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wcast-qual\"") _Pragma("clang diagnostic ignored \"-Wcast-qual\"")
#define IGNORE_WCASTQUAL_END _Pragma("clang diagnostic pop") #define IGNORE_WCASTQUAL_END _Pragma("clang diagnostic pop")
@ -59,4 +77,9 @@
#define IGNORE_WCASTQUAL_END #define IGNORE_WCASTQUAL_END
#endif #endif
/* This subtraction is used by Linux / NetBSD / OpenBSD for calculation of CPU usage items. */
static inline unsigned long long saturatingSub(unsigned long long a, unsigned long long b) {
return a > b ? a - b : 0;
}
#endif #endif

View File

@ -21,18 +21,19 @@ in the source distribution for its full text.
#include "XUtils.h" #include "XUtils.h"
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};
static const char* const MainFunctions_ro[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", " ", " ", " ", "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);
FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree "); FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree ");
} }
void MainPanel_pidSearch(MainPanel* this, int ch) { static void MainPanel_pidSearch(MainPanel* this, int ch) {
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
pid_t pid = ch - 48 + this->pidSearch; pid_t pid = ch - 48 + this->pidSearch;
for (int i = 0; i < Panel_size(super); i++) { for (int i = 0; i < Panel_size(super); i++) {
Process* p = (Process*) Panel_get(super, i); const Process* p = (const Process*) Panel_get(super, i);
if (p && p->pid == pid) { if (p && p->pid == pid) {
Panel_setSelected(super, i); Panel_setSelected(super, i);
break; break;
@ -111,6 +112,9 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
if (reaction & HTOP_REDRAW_BAR) { if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView); MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
} }
if (reaction & HTOP_RESIZE) {
result |= RESIZE;
}
if (reaction & HTOP_UPDATE_PANELHDR) { if (reaction & HTOP_UPDATE_PANELHDR) {
result |= REDRAW; result |= REDRAW;
} }
@ -134,7 +138,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} }
int MainPanel_selectedPid(MainPanel* this) { int MainPanel_selectedPid(MainPanel* this) {
Process* p = (Process*) Panel_getSelected((Panel*)this); const Process* p = (const Process*) Panel_getSelected((Panel*)this);
if (p) { if (p) {
return p->pid; return p->pid;
} }
@ -195,7 +199,7 @@ const PanelClass MainPanel_class = {
MainPanel* MainPanel_new() { MainPanel* MainPanel_new() {
MainPanel* this = AllocThis(MainPanel); MainPanel* this = AllocThis(MainPanel);
Panel_init((Panel*) this, 1, 1, 1, 1, Class(Process), false, FunctionBar_new(MainFunctions, NULL, NULL)); Panel_init((Panel*) this, 1, 1, 1, 1, Class(Process), false, FunctionBar_new(Settings_isReadonly() ? MainFunctions_ro : MainFunctions, NULL, NULL));
this->keys = xCalloc(KEY_MAX, sizeof(Htop_Action)); this->keys = xCalloc(KEY_MAX, sizeof(Htop_Action));
this->inc = IncSet_new(MainPanel_getFunctionBar(this)); this->inc = IncSet_new(MainPanel_getFunctionBar(this));

View File

@ -34,8 +34,6 @@ typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode); void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
void MainPanel_pidSearch(MainPanel* this, int ch);
int MainPanel_selectedPid(MainPanel* this); int MainPanel_selectedPid(MainPanel* this);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged); bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);

View File

@ -1,10 +1,14 @@
AUTOMAKE_OPTIONS = subdir-objects
bin_PROGRAMS = htop
dist_man_MANS = htop.1 dist_man_MANS = htop.1
EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png htop.svg \ EXTRA_DIST = \
install-sh autogen.sh missing $(dist_man_MANS) \
autogen.sh \
htop.desktop \
htop.png \
htop.svg \
build-aux/compile \
build-aux/depcomp \
build-aux/install-sh \
build-aux/missing
applicationsdir = $(datadir)/applications applicationsdir = $(datadir)/applications
applications_DATA = htop.desktop applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps pixmapdir = $(datadir)/pixmaps
@ -12,7 +16,7 @@ pixmap_DATA = htop.png
appicondir = $(datadir)/icons/hicolor/scalable/apps appicondir = $(datadir)/icons/hicolor/scalable/apps
appicon_DATA = htop.svg appicon_DATA = htop.svg
AM_CFLAGS += -pedantic -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)" AM_CFLAGS += -pedantic -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR="\"$(sysconfdir)\"" -I"$(top_srcdir)/$(my_htop_platform)"
AM_LDFLAGS = AM_LDFLAGS =
myhtopsources = \ myhtopsources = \
@ -26,6 +30,7 @@ myhtopsources = \
ClockMeter.c \ ClockMeter.c \
ColorsPanel.c \ ColorsPanel.c \
ColumnsPanel.c \ ColumnsPanel.c \
CommandLine.c \
CommandScreen.c \ CommandScreen.c \
Compat.c \ Compat.c \
CPUMeter.c \ CPUMeter.c \
@ -34,18 +39,21 @@ myhtopsources = \
DateTimeMeter.c \ DateTimeMeter.c \
DiskIOMeter.c \ DiskIOMeter.c \
DisplayOptionsPanel.c \ DisplayOptionsPanel.c \
DynamicColumn.c \
DynamicMeter.c \
EnvScreen.c \ EnvScreen.c \
FunctionBar.c \ FunctionBar.c \
Hashtable.c \ Hashtable.c \
Header.c \ Header.c \
HeaderOptionsPanel.c \
HostnameMeter.c \ HostnameMeter.c \
htop.c \
IncSet.c \ IncSet.c \
InfoScreen.c \ InfoScreen.c \
ListItem.c \ ListItem.c \
LoadAverageMeter.c \ LoadAverageMeter.c \
MainPanel.c \ MainPanel.c \
MemoryMeter.c \ MemoryMeter.c \
MemorySwapMeter.c \
Meter.c \ Meter.c \
MetersPanel.c \ MetersPanel.c \
NetworkIOMeter.c \ NetworkIOMeter.c \
@ -61,6 +69,7 @@ myhtopsources = \
Settings.c \ Settings.c \
SignalsPanel.c \ SignalsPanel.c \
SwapMeter.c \ SwapMeter.c \
SysArchMeter.c \
TasksMeter.c \ TasksMeter.c \
TraceScreen.c \ TraceScreen.c \
UptimeMeter.c \ UptimeMeter.c \
@ -81,16 +90,21 @@ myhtopheaders = \
ClockMeter.h \ ClockMeter.h \
ColorsPanel.h \ ColorsPanel.h \
ColumnsPanel.h \ ColumnsPanel.h \
CommandLine.h \
CommandScreen.h \ CommandScreen.h \
Compat.h \ Compat.h \
DateMeter.h \ DateMeter.h \
DateTimeMeter.h \ DateTimeMeter.h \
DiskIOMeter.h \ DiskIOMeter.h \
DisplayOptionsPanel.h \ DisplayOptionsPanel.h \
DynamicColumn.h \
DynamicMeter.h \
EnvScreen.h \ EnvScreen.h \
FunctionBar.h \ FunctionBar.h \
Hashtable.h \ Hashtable.h \
Header.h \ Header.h \
HeaderLayout.h \
HeaderOptionsPanel.h \
HostnameMeter.h \ HostnameMeter.h \
IncSet.h \ IncSet.h \
InfoScreen.h \ InfoScreen.h \
@ -99,6 +113,7 @@ myhtopheaders = \
Macros.h \ Macros.h \
MainPanel.h \ MainPanel.h \
MemoryMeter.h \ MemoryMeter.h \
MemorySwapMeter.h \
Meter.h \ Meter.h \
MetersPanel.h \ MetersPanel.h \
NetworkIOMeter.h \ NetworkIOMeter.h \
@ -115,6 +130,7 @@ myhtopheaders = \
Settings.h \ Settings.h \
SignalsPanel.h \ SignalsPanel.h \
SwapMeter.h \ SwapMeter.h \
SysArchMeter.h \
TasksMeter.h \ TasksMeter.h \
TraceScreen.h \ TraceScreen.h \
UptimeMeter.h \ UptimeMeter.h \
@ -126,6 +142,10 @@ myhtopheaders = \
# ----- # -----
linux_platform_headers = \ linux_platform_headers = \
generic/gettime.h \
generic/hostname.h \
generic/uname.h \
linux/HugePageMeter.h \
linux/IOPriority.h \ linux/IOPriority.h \
linux/IOPriorityPanel.h \ linux/IOPriorityPanel.h \
linux/LibSensors.h \ linux/LibSensors.h \
@ -142,9 +162,11 @@ linux_platform_headers = \
zfs/ZfsArcStats.h \ zfs/ZfsArcStats.h \
zfs/ZfsCompressedArcMeter.h zfs/ZfsCompressedArcMeter.h
if HTOP_LINUX linux_platform_sources = \
AM_LDFLAGS += -rdynamic generic/gettime.c \
myhtopplatsources = \ generic/hostname.c \
generic/uname.c \
linux/HugePageMeter.c \
linux/IOPriorityPanel.c \ linux/IOPriorityPanel.c \
linux/LibSensors.c \ linux/LibSensors.c \
linux/LinuxProcess.c \ linux/LinuxProcess.c \
@ -155,10 +177,13 @@ myhtopplatsources = \
linux/SystemdMeter.c \ linux/SystemdMeter.c \
linux/ZramMeter.c \ linux/ZramMeter.c \
zfs/ZfsArcMeter.c \ zfs/ZfsArcMeter.c \
zfs/ZfsArcStats.c \
zfs/ZfsCompressedArcMeter.c zfs/ZfsCompressedArcMeter.c
if HTOP_LINUX
AM_LDFLAGS += -rdynamic
myhtopplatprogram = htop
myhtopplatheaders = $(linux_platform_headers) myhtopplatheaders = $(linux_platform_headers)
myhtopplatsources = $(linux_platform_sources)
endif endif
# FreeBSD # FreeBSD
@ -169,17 +194,29 @@ freebsd_platform_headers = \
freebsd/FreeBSDProcess.h \ freebsd/FreeBSDProcess.h \
freebsd/Platform.h \ freebsd/Platform.h \
freebsd/ProcessField.h \ freebsd/ProcessField.h \
generic/gettime.h \
generic/hostname.h \
generic/openzfs_sysctl.h \
generic/uname.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \ zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h zfs/ZfsCompressedArcMeter.h
freebsd_platform_sources = \
freebsd/Platform.c \
freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c \
generic/gettime.c \
generic/hostname.c \
generic/openzfs_sysctl.c \
generic/uname.c \
zfs/ZfsArcMeter.c \
zfs/ZfsCompressedArcMeter.c
if HTOP_FREEBSD if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \ myhtopplatprogram = htop
freebsd/FreeBSDProcess.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(freebsd_platform_headers) myhtopplatheaders = $(freebsd_platform_headers)
myhtopplatsources = $(freebsd_platform_sources)
endif endif
# DragonFlyBSD # DragonFlyBSD
@ -189,31 +226,75 @@ dragonflybsd_platform_headers = \
dragonflybsd/DragonFlyBSDProcessList.h \ dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h \ dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/Platform.h \ dragonflybsd/Platform.h \
dragonflybsd/ProcessField.h dragonflybsd/ProcessField.h \
generic/gettime.h \
generic/hostname.h \
generic/uname.h
dragonflybsd_platform_sources = \
dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c \
dragonflybsd/Platform.c \
generic/gettime.c \
generic/hostname.c \
generic/uname.c
if HTOP_DRAGONFLYBSD if HTOP_DRAGONFLYBSD
myhtopplatsources = \ myhtopplatprogram = htop
dragonflybsd/Platform.c \
dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c
myhtopplatheaders = $(dragonflybsd_platform_headers) myhtopplatheaders = $(dragonflybsd_platform_headers)
myhtopplatsources = $(dragonflybsd_platform_sources)
endif
# NetBSD
# -------
netbsd_platform_headers = \
generic/gettime.h \
generic/hostname.h \
generic/uname.h \
netbsd/Platform.h \
netbsd/ProcessField.h \
netbsd/NetBSDProcess.h \
netbsd/NetBSDProcessList.h
netbsd_platform_sources = \
generic/gettime.c \
generic/hostname.c \
generic/uname.c \
netbsd/Platform.c \
netbsd/NetBSDProcess.c \
netbsd/NetBSDProcessList.c
if HTOP_NETBSD
myhtopplatprogram = htop
myhtopplatheaders = $(netbsd_platform_headers)
myhtopplatsources = $(netbsd_platform_sources)
endif endif
# OpenBSD # OpenBSD
# ------- # -------
openbsd_platform_headers = \ openbsd_platform_headers = \
generic/gettime.h \
generic/hostname.h \
generic/uname.h \
openbsd/OpenBSDProcessList.h \ openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h \ openbsd/OpenBSDProcess.h \
openbsd/Platform.h \ openbsd/Platform.h \
openbsd/ProcessField.h openbsd/ProcessField.h
if HTOP_OPENBSD openbsd_platform_sources = \
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \ generic/gettime.c \
openbsd/OpenBSDProcess.c generic/hostname.c \
generic/uname.c \
openbsd/OpenBSDProcessList.c \
openbsd/OpenBSDProcess.c \
openbsd/Platform.c
if HTOP_OPENBSD
myhtopplatprogram = htop
myhtopplatheaders = $(openbsd_platform_headers) myhtopplatheaders = $(openbsd_platform_headers)
myhtopplatsources = $(openbsd_platform_sources)
endif endif
# Darwin # Darwin
@ -224,59 +305,126 @@ darwin_platform_headers = \
darwin/DarwinProcessList.h \ darwin/DarwinProcessList.h \
darwin/Platform.h \ darwin/Platform.h \
darwin/ProcessField.h \ darwin/ProcessField.h \
generic/gettime.h \
generic/hostname.h \
generic/openzfs_sysctl.h \
generic/uname.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \ zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h zfs/ZfsCompressedArcMeter.h
darwin_platform_sources = \
darwin/Platform.c \
darwin/DarwinProcess.c \
darwin/DarwinProcessList.c \
generic/gettime.c \
generic/hostname.c \
generic/openzfs_sysctl.c \
generic/uname.c \
zfs/ZfsArcMeter.c \
zfs/ZfsCompressedArcMeter.c
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 \ myhtopplatprogram = htop
darwin/DarwinProcessList.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(darwin_platform_headers) myhtopplatheaders = $(darwin_platform_headers)
myhtopplatsources = $(darwin_platform_sources)
endif endif
# Solaris # Solaris
# ------- # -------
solaris_platform_headers = \ solaris_platform_headers = \
solaris/Platform.h \ generic/gettime.h \
generic/hostname.h \
generic/uname.h \
solaris/ProcessField.h \ solaris/ProcessField.h \
solaris/Platform.h \
solaris/SolarisProcess.h \ solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \ solaris/SolarisProcessList.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \ zfs/ZfsArcStats.h \
zfs/ZfsArcStats.h zfs/ZfsCompressedArcMeter.h
solaris_platform_sources = \
generic/gettime.c \
generic/hostname.c \
generic/uname.c \
solaris/Platform.c \
solaris/SolarisProcess.c \
solaris/SolarisProcessList.c \
zfs/ZfsArcMeter.c \
zfs/ZfsCompressedArcMeter.c
if HTOP_SOLARIS if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \ myhtopplatprogram = htop
solaris/SolarisProcess.c solaris/SolarisProcessList.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(solaris_platform_headers) myhtopplatheaders = $(solaris_platform_headers)
myhtopplatsources = $(solaris_platform_sources)
endif
# Performance Co-Pilot (PCP)
# --------------------------
pcp_platform_headers = \
linux/PressureStallMeter.h \
linux/ZramMeter.h \
linux/ZramStats.h \
pcp/PCPDynamicColumn.h \
pcp/PCPDynamicMeter.h \
pcp/PCPMetric.h \
pcp/PCPProcess.h \
pcp/PCPProcessList.h \
pcp/Platform.h \
pcp/ProcessField.h \
zfs/ZfsArcMeter.h \
zfs/ZfsArcStats.h \
zfs/ZfsCompressedArcMeter.h
pcp_platform_sources = \
linux/PressureStallMeter.c \
linux/ZramMeter.c \
pcp/PCPDynamicColumn.c \
pcp/PCPDynamicMeter.c \
pcp/PCPMetric.c \
pcp/PCPProcess.c \
pcp/PCPProcessList.c \
pcp/Platform.c \
zfs/ZfsArcMeter.c \
zfs/ZfsCompressedArcMeter.c
if HTOP_PCP
myhtopplatprogram = pcp-htop
myhtopplatheaders = $(pcp_platform_headers)
myhtopplatsources = $(pcp_platform_sources)
pcp_htop_SOURCES = $(myhtopplatprogram).c $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
endif endif
# Unsupported # Unsupported
# ----------- # -----------
unsupported_platform_headers = \ unsupported_platform_headers = \
generic/gettime.h \
unsupported/Platform.h \ unsupported/Platform.h \
unsupported/ProcessField.h \ unsupported/ProcessField.h \
unsupported/UnsupportedProcess.h \ unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h unsupported/UnsupportedProcessList.h
if HTOP_UNSUPPORTED unsupported_platform_sources = \
myhtopplatsources = unsupported/Platform.c \ generic/gettime.c \
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c unsupported/Platform.c \
unsupported/UnsupportedProcess.c \
unsupported/UnsupportedProcessList.c
if HTOP_UNSUPPORTED
myhtopplatprogram = htop
myhtopplatsources = $(unsupported_platform_sources)
myhtopplatheaders = $(unsupported_platform_headers) myhtopplatheaders = $(unsupported_platform_headers)
endif endif
# ---- # ----
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) bin_PROGRAMS = $(myhtopplatprogram)
htop_SOURCES = $(myhtopplatprogram).c $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
nodist_htop_SOURCES = config.h nodist_htop_SOURCES = config.h
target: target:
@ -286,10 +434,10 @@ profile:
$(MAKE) all AM_CPPFLAGS="-pg -O2 -DNDEBUG" $(MAKE) all AM_CPPFLAGS="-pg -O2 -DNDEBUG"
debug: debug:
$(MAKE) all AM_CPPFLAGS="-ggdb -DDEBUG" $(MAKE) all AM_CPPFLAGS="-ggdb3 -Og" CFLAGS="`printf ' %s ' "$(CFLAGS)"|sed -E 's#[[:space:]]-O[^[:space:]]+[[:space:]]# #g'` -ggdb3 -Og"
coverage: coverage:
$(MAKE) all AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" AM_LDFLAGS="-lgcov" $(MAKE) all AM_CPPFLAGS="-fprofile-arcs -ftest-coverage" AM_LDFLAGS="-lgcov"
cppcheck: cppcheck:
cppcheck -q -v . --enable=all -DHAVE_OPENVZ cppcheck -q -v . --enable=all -DHAVE_OPENVZ

View File

@ -7,6 +7,9 @@ in the source distribution for its full text.
#include "MemoryMeter.h" #include "MemoryMeter.h"
#include <math.h>
#include <stddef.h>
#include "CRT.h" #include "CRT.h"
#include "Object.h" #include "Object.h"
#include "Platform.h" #include "Platform.h"
@ -16,14 +19,24 @@ in the source distribution for its full text.
static const int MemoryMeter_attributes[] = { static const int MemoryMeter_attributes[] = {
MEMORY_USED, MEMORY_USED,
MEMORY_BUFFERS, MEMORY_BUFFERS,
MEMORY_SHARED,
MEMORY_CACHE MEMORY_CACHE
}; };
static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) { static void MemoryMeter_updateValues(Meter* this) {
char* buffer = this->txtBuffer;
size_t size = sizeof(this->txtBuffer);
int written; int written;
/* shared and available memory are not supported on all platforms */
this->values[2] = NAN;
this->values[4] = NAN;
Platform_setMemoryValues(this); Platform_setMemoryValues(this);
written = Meter_humanUnit(buffer, this->values[0], size); /* Do not print available memory in bar mode */
this->curItems = 4;
written = Meter_humanUnit(buffer, isnan(this->values[4]) ? this->values[0] : this->total - this->values[4], size);
METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_CHECK(buffer, size, written);
METER_BUFFER_APPEND_CHR(buffer, size, '/'); METER_BUFFER_APPEND_CHR(buffer, size, '/');
@ -34,18 +47,36 @@ static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) {
static void MemoryMeter_display(const Object* cast, RichString* out) { static void MemoryMeter_display(const Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_writeAscii(out, CRT_colors[METER_TEXT], ":"); RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, sizeof(buffer)); Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer); RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer);
Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:");
RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer); RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
Meter_humanUnit(buffer, this->values[2], sizeof(buffer));
/* shared memory is not supported on all platforms */
if (!isnan(this->values[2])) {
Meter_humanUnit(buffer, this->values[2], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " shared:");
RichString_appendAscii(out, CRT_colors[MEMORY_SHARED], buffer);
}
Meter_humanUnit(buffer, this->values[3], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:");
RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer); RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer);
/* available memory is not supported on all platforms */
if (!isnan(this->values[4])) {
Meter_humanUnit(buffer, this->values[4], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " available:");
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
}
} }
const MeterClass MemoryMeter_class = { const MeterClass MemoryMeter_class = {
@ -56,7 +87,7 @@ const MeterClass MemoryMeter_class = {
}, },
.updateValues = MemoryMeter_updateValues, .updateValues = MemoryMeter_updateValues,
.defaultMode = BAR_METERMODE, .defaultMode = BAR_METERMODE,
.maxItems = 3, .maxItems = 5,
.total = 100.0, .total = 100.0,
.attributes = MemoryMeter_attributes, .attributes = MemoryMeter_attributes,
.name = "Memory", .name = "Memory",

View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
extern const MeterClass MemoryMeter_class; extern const MeterClass MemoryMeter_class;
#endif #endif

102
MemorySwapMeter.c Normal file
View File

@ -0,0 +1,102 @@
/*
htop - MemorySwapMeter.c
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "MemorySwapMeter.h"
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include "Macros.h"
#include "MemoryMeter.h"
#include "Object.h"
#include "SwapMeter.h"
#include "XUtils.h"
typedef struct MemorySwapMeterData_ {
Meter* memoryMeter;
Meter* swapMeter;
} MemorySwapMeterData;
static void MemorySwapMeter_updateValues(Meter* this) {
MemorySwapMeterData* data = this->meterData;
Meter_updateValues(data->memoryMeter);
Meter_updateValues(data->swapMeter);
}
static void MemorySwapMeter_draw(Meter* this, int x, int y, int w) {
MemorySwapMeterData* data = this->meterData;
assert(data->memoryMeter->draw);
data->memoryMeter->draw(data->memoryMeter, x, y, w / 2);
assert(data->swapMeter->draw);
data->swapMeter->draw(data->swapMeter, x + w / 2, y, w - w / 2);
}
static void MemorySwapMeter_init(Meter* this) {
MemorySwapMeterData* data = this->meterData;
if (!data) {
data = this->meterData = xMalloc(sizeof(MemorySwapMeterData));
data->memoryMeter = NULL;
data->swapMeter = NULL;
}
if (!data->memoryMeter)
data->memoryMeter = Meter_new(this->pl, 0, (const MeterClass*) Class(MemoryMeter));
if (!data->swapMeter)
data->swapMeter = Meter_new(this->pl, 0, (const MeterClass*) Class(SwapMeter));
if (Meter_initFn(data->memoryMeter))
Meter_init(data->memoryMeter);
if (Meter_initFn(data->swapMeter))
Meter_init(data->swapMeter);
if (this->mode == 0)
this->mode = BAR_METERMODE;
this->h = MAXIMUM(Meter_modes[data->memoryMeter->mode]->h, Meter_modes[data->swapMeter->mode]->h);
}
static void MemorySwapMeter_updateMode(Meter* this, int mode) {
MemorySwapMeterData* data = this->meterData;
this->mode = mode;
Meter_setMode(data->memoryMeter, mode);
Meter_setMode(data->swapMeter, mode);
this->h = MAXIMUM(Meter_modes[data->memoryMeter->mode]->h, Meter_modes[data->swapMeter->mode]->h);
}
static void MemorySwapMeter_done(Meter* this) {
MemorySwapMeterData* data = this->meterData;
Meter_delete((Object*)data->swapMeter);
Meter_delete((Object*)data->memoryMeter);
free(data);
}
const MeterClass MemorySwapMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
},
.updateValues = MemorySwapMeter_updateValues,
.defaultMode = CUSTOM_METERMODE,
.name = "MemorySwap",
.uiName = "Memory & Swap",
.description = "Combined memory and swap usage",
.caption = "M&S",
.draw = MemorySwapMeter_draw,
.init = MemorySwapMeter_init,
.updateMode = MemorySwapMeter_updateMode,
.done = MemorySwapMeter_done
};

15
MemorySwapMeter.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef HEADER_MemorySwapMeter
#define HEADER_MemorySwapMeter
/*
htop - MemorySwapMeter.h
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
extern const MeterClass MemorySwapMeter_class;
#endif

111
Meter.c
View File

@ -32,7 +32,7 @@ const MeterClass Meter_class = {
} }
}; };
Meter* Meter_new(const struct ProcessList_* pl, int param, const MeterClass* type) { Meter* Meter_new(const struct ProcessList_* pl, unsigned int param, const MeterClass* type) {
Meter* this = xCalloc(1, sizeof(Meter)); Meter* this = xCalloc(1, sizeof(Meter));
Object_setClass(this, type); Object_setClass(this, type);
this->h = 1; this->h = 1;
@ -93,15 +93,14 @@ void Meter_delete(Object* cast) {
} }
void Meter_setCaption(Meter* this, const char* caption) { void Meter_setCaption(Meter* this, const char* caption) {
free(this->caption); free_and_xStrdup(&this->caption, caption);
this->caption = xStrdup(caption);
} }
static inline void Meter_displayBuffer(const Meter* this, const char* buffer, RichString* out) { static inline void Meter_displayBuffer(const Meter* this, RichString* out) {
if (Object_displayFn(this)) { if (Object_displayFn(this)) {
Object_display(this, out); Object_display(this, out);
} else { } else {
RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], buffer); RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], this->txtBuffer);
} }
} }
@ -132,21 +131,20 @@ void Meter_setMode(Meter* this, int modeIndex) {
this->mode = modeIndex; this->mode = modeIndex;
} }
ListItem* Meter_toListItem(Meter* this, bool moving) { ListItem* Meter_toListItem(const Meter* this, bool moving) {
char mode[20]; char mode[20];
if (this->mode) { if (this->mode) {
xSnprintf(mode, sizeof(mode), " [%s]", Meter_modes[this->mode]->uiName); xSnprintf(mode, sizeof(mode), " [%s]", Meter_modes[this->mode]->uiName);
} else { } else {
mode[0] = '\0'; mode[0] = '\0';
} }
char number[10]; char name[32];
if (this->param > 0) { if (Meter_getUiNameFn(this))
xSnprintf(number, sizeof(number), " %d", this->param); Meter_getUiName(this, name, sizeof(name));
} else { else
number[0] = '\0'; xSnprintf(name, sizeof(name), "%s", Meter_uiName(this));
}
char buffer[50]; char buffer[50];
xSnprintf(buffer, sizeof(buffer), "%s%s%s", Meter_uiName(this), number, mode); xSnprintf(buffer, sizeof(buffer), "%s%s", name, mode);
ListItem* li = ListItem_new(buffer, 0); ListItem* li = ListItem_new(buffer, 0);
li->moving = moving; li->moving = moving;
return li; return li;
@ -155,23 +153,21 @@ ListItem* Meter_toListItem(Meter* this, bool moving) {
/* ---------- TextMeterMode ---------- */ /* ---------- TextMeterMode ---------- */
static void TextMeterMode_draw(Meter* this, int x, int y, int w) { static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN]; const char* caption = Meter_getCaption(this);
Meter_updateValues(this, buffer, sizeof(buffer));
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
mvaddnstr(y, x, this->caption, w - 1); mvaddnstr(y, x, caption, w - 1);
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
int captionLen = strlen(this->caption); int captionLen = strlen(caption);
x += captionLen; x += captionLen;
w -= captionLen; w -= captionLen;
if (w <= 0) if (w <= 0)
return; return;
RichString_begin(out); RichString_begin(out);
Meter_displayBuffer(this, buffer, &out); Meter_displayBuffer(this, &out);
RichString_printoffnVal(out, y, x, 0, w - 1); RichString_printoffnVal(out, y, x, 0, w - 1);
RichString_end(out); RichString_delete(&out);
} }
/* ---------- BarMeterMode ---------- */ /* ---------- BarMeterMode ---------- */
@ -179,13 +175,11 @@ static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
static const char BarMeterMode_characters[] = "|#*@$%&."; static const char BarMeterMode_characters[] = "|#*@$%&.";
static void BarMeterMode_draw(Meter* this, int x, int y, int w) { static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN]; const char* caption = Meter_getCaption(this);
Meter_updateValues(this, buffer, sizeof(buffer));
w -= 2; w -= 2;
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
int captionLen = 3; int captionLen = 3;
mvaddnstr(y, x, this->caption, captionLen); mvaddnstr(y, x, caption, captionLen);
x += captionLen; x += captionLen;
w -= captionLen; w -= captionLen;
attrset(CRT_colors[BAR_BORDER]); attrset(CRT_colors[BAR_BORDER]);
@ -202,8 +196,8 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// The text in the bar is right aligned; // The text in the bar is right aligned;
// Pad with maximal spaces and then calculate needed starting position offset // Pad with maximal spaces and then calculate needed starting position offset
RichString_begin(bar); RichString_begin(bar);
RichString_appendChr(&bar, ' ', w); RichString_appendChr(&bar, 0, ' ', w);
RichString_appendWide(&bar, 0, buffer); RichString_appendWide(&bar, 0, this->txtBuffer);
int startPos = RichString_sizeVal(bar) - w; int startPos = RichString_sizeVal(bar) - w;
if (startPos > w) { if (startPos > w) {
// Text is too large for bar // Text is too large for bar
@ -217,7 +211,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
} }
} }
// If still to large, print the start not the end // If still too large, print the start not the end
startPos = MINIMUM(startPos, w); startPos = MINIMUM(startPos, w);
} }
assert(startPos >= 0); assert(startPos >= 0);
@ -264,7 +258,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset); RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
} }
RichString_end(bar); RichString_delete(&bar);
move(y, x + w + 1); move(y, x + w + 1);
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
@ -293,12 +287,13 @@ static const char* const GraphMeterMode_dotsAscii[] = {
}; };
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
const ProcessList* pl = this->pl;
if (!this->drawData) { if (!this->drawData) {
this->drawData = xCalloc(1, sizeof(GraphData)); this->drawData = xCalloc(1, sizeof(GraphData));
} }
GraphData* data = this->drawData; GraphData* data = this->drawData;
const int nValues = METER_BUFFER_LEN; const int nValues = METER_GRAPHDATA_SIZE;
const char* const* GraphMeterMode_dots; const char* const* GraphMeterMode_dots;
int GraphMeterMode_pixPerRow; int GraphMeterMode_pixPerRow;
@ -313,29 +308,24 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
GraphMeterMode_pixPerRow = PIXPERROW_ASCII; GraphMeterMode_pixPerRow = PIXPERROW_ASCII;
} }
const char* caption = Meter_getCaption(this);
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
int captionLen = 3; int captionLen = 3;
mvaddnstr(y, x, this->caption, captionLen); mvaddnstr(y, x, caption, captionLen);
x += captionLen; x += captionLen;
w -= captionLen; w -= captionLen;
struct timeval now; if (!timercmp(&pl->realtime, &(data->time), <)) {
gettimeofday(&now, NULL);
if (!timercmp(&now, &(data->time), <)) {
int globalDelay = this->pl->settings->delay; int globalDelay = this->pl->settings->delay;
struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay - ((globalDelay / 10) * 10)) * 100000 }; struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay % 10) * 100000L };
timeradd(&now, &delay, &(data->time)); timeradd(&pl->realtime, &delay, &(data->time));
for (int i = 0; i < nValues - 1; i++) for (int i = 0; i < nValues - 1; i++)
data->values[i] = data->values[i + 1]; data->values[i] = data->values[i + 1];
char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, sizeof(buffer));
double value = 0.0; double value = 0.0;
for (uint8_t i = 0; i < this->curItems; i++) for (uint8_t i = 0; i < this->curItems; i++)
value += this->values[i]; value += this->values[i];
value /= this->total;
data->values[nValues - 1] = value; data->values[nValues - 1] = value;
} }
@ -346,8 +336,10 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
} }
for (; i < nValues - 1; i += 2, k++) { for (; i < nValues - 1; i += 2, k++) {
int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT; int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix); if (this->total < 1)
int v2 = CLAMP((int) lround(data->values[i + 1] * pix), 1, pix); this->total = 1;
int v1 = CLAMP((int) lround(data->values[i] / this->total * pix), 1, pix);
int v2 = CLAMP((int) lround(data->values[i + 1] / this->total * pix), 1, pix);
int colorIdx = GRAPH_1; int colorIdx = GRAPH_1;
for (int line = 0; line < GRAPH_HEIGHT; line++) { for (int line = 0; line < GRAPH_HEIGHT; line++) {
@ -384,12 +376,10 @@ static const char* const* LEDMeterMode_digits;
static void LEDMeterMode_drawDigit(int x, int y, int n) { static void LEDMeterMode_drawDigit(int x, int y, int n) {
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
mvaddstr(y+i, x, LEDMeterMode_digits[i * 10 + n]); mvaddstr(y + i, x, LEDMeterMode_digits[i * 10 + n]);
} }
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) { static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
(void) w;
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
if (CRT_utf8) if (CRT_utf8)
LEDMeterMode_digits = LEDMeterMode_digitsUtf8; LEDMeterMode_digits = LEDMeterMode_digitsUtf8;
@ -397,11 +387,8 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
#endif #endif
LEDMeterMode_digits = LEDMeterMode_digitsAscii; LEDMeterMode_digits = LEDMeterMode_digitsAscii;
char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, sizeof(buffer));
RichString_begin(out); RichString_begin(out);
Meter_displayBuffer(this, buffer, &out); Meter_displayBuffer(this, &out);
int yText = int yText =
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
@ -409,21 +396,32 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
#endif #endif
y + 2; y + 2;
attrset(CRT_colors[LED_COLOR]); attrset(CRT_colors[LED_COLOR]);
mvaddstr(yText, x, this->caption); const char* caption = Meter_getCaption(this);
int xx = x + strlen(this->caption); mvaddstr(yText, x, caption);
int xx = x + strlen(caption);
int len = RichString_sizeVal(out); int len = RichString_sizeVal(out);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
int c = RichString_getCharVal(out, i); int c = RichString_getCharVal(out, i);
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
LEDMeterMode_drawDigit(xx, y, c - 48); if (xx - x + 4 > w)
break;
LEDMeterMode_drawDigit(xx, y, c - '0');
xx += 4; xx += 4;
} else { } else {
if (xx - x + 1 > w)
break;
#ifdef HAVE_LIBNCURSESW
const cchar_t wc = { .chars = { c, '\0' }, .attr = 0 }; /* use LED_COLOR from attrset() */
mvadd_wch(yText, xx, &wc);
#else
mvaddch(yText, xx, c); mvaddch(yText, xx, c);
#endif
xx += 1; xx += 1;
} }
} }
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
RichString_end(out); RichString_delete(&out);
} }
static MeterMode BarMeterMode = { static MeterMode BarMeterMode = {
@ -461,14 +459,11 @@ const MeterMode* const Meter_modes[] = {
/* Blank meter */ /* Blank meter */
static void BlankMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t size) { static void BlankMeter_updateValues(Meter* this) {
if (size > 0) { this->txtBuffer[0] = '\0';
*buffer = 0;
}
} }
static void BlankMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { static void BlankMeter_display(ATTR_UNUSED const Object* cast, ATTR_UNUSED RichString* out) {
RichString_prune(out);
} }
static const int BlankMeter_attributes[] = { static const int BlankMeter_attributes[] = {

29
Meter.h
View File

@ -10,6 +10,7 @@ in the source distribution for its full text.
#include "config.h" // IWYU pragma: keep #include "config.h" // IWYU pragma: keep
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <sys/time.h> #include <sys/time.h>
@ -18,7 +19,8 @@ in the source distribution for its full text.
#include "ProcessList.h" #include "ProcessList.h"
#define METER_BUFFER_LEN 256 #define METER_TXTBUFFER_LEN 256
#define METER_GRAPHDATA_SIZE 256
#define METER_BUFFER_CHECK(buffer, size, written) \ #define METER_BUFFER_CHECK(buffer, size, written) \
do { \ do { \
@ -49,16 +51,20 @@ typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*); typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*); typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int); typedef void(*Meter_UpdateMode)(Meter*, int);
typedef void(*Meter_UpdateValues)(Meter*, char*, size_t); typedef void(*Meter_UpdateValues)(Meter*);
typedef void(*Meter_Draw)(Meter*, int, int, int); typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef const char* (*Meter_GetCaption)(const Meter*);
typedef void(*Meter_GetUiName)(const Meter*, char*, size_t);
typedef struct MeterClass_ { typedef struct MeterClass_ {
const ObjectClass super; const ObjectClass super;
const Meter_Init init; const Meter_Init init;
const Meter_Done done; const Meter_Done done;
const Meter_UpdateMode updateMode; const Meter_UpdateMode updateMode;
const Meter_Draw draw;
const Meter_UpdateValues updateValues; const Meter_UpdateValues updateValues;
const Meter_Draw draw;
const Meter_GetCaption getCaption;
const Meter_GetUiName getUiName;
const int defaultMode; const int defaultMode;
const double total; const double total;
const int* const attributes; const int* const attributes;
@ -77,8 +83,11 @@ typedef struct MeterClass_ {
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_) #define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_) As_Meter(this_)->draw #define Meter_drawFn(this_) As_Meter(this_)->draw
#define Meter_doneFn(this_) As_Meter(this_)->done #define Meter_doneFn(this_) As_Meter(this_)->done
#define Meter_updateValues(this_, buf_, sz_) \ #define Meter_updateValues(this_) As_Meter(this_)->updateValues((Meter*)(this_))
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_) #define Meter_getUiNameFn(this_) As_Meter(this_)->getUiName
#define Meter_getUiName(this_,n_,l_) As_Meter(this_)->getUiName((const Meter*)(this_),n_,l_)
#define Meter_getCaptionFn(this_) As_Meter(this_)->getCaption
#define Meter_getCaption(this_) (Meter_getCaptionFn(this_) ? As_Meter(this_)->getCaption((const Meter*)(this_)) : (this_)->caption)
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode #define Meter_defaultMode(this_) As_Meter(this_)->defaultMode
#define Meter_attributes(this_) As_Meter(this_)->attributes #define Meter_attributes(this_) As_Meter(this_)->attributes
#define Meter_name(this_) As_Meter(this_)->name #define Meter_name(this_) As_Meter(this_)->name
@ -86,7 +95,7 @@ typedef struct MeterClass_ {
typedef struct GraphData_ { typedef struct GraphData_ {
struct timeval time; struct timeval time;
double values[METER_BUFFER_LEN]; double values[METER_GRAPHDATA_SIZE];
} GraphData; } GraphData;
struct Meter_ { struct Meter_ {
@ -95,12 +104,14 @@ struct Meter_ {
char* caption; char* caption;
int mode; int mode;
int param; unsigned int param;
GraphData* drawData; GraphData* drawData;
int h; int h;
int columnWidthCount; /**< only used internally by the Header */
const ProcessList* pl; const ProcessList* pl;
uint8_t curItems; uint8_t curItems;
const int* curAttributes; const int* curAttributes;
char txtBuffer[METER_TXTBUFFER_LEN];
double* values; double* values;
double total; double total;
void* meterData; void* meterData;
@ -123,7 +134,7 @@ typedef enum {
extern const MeterClass Meter_class; extern const MeterClass Meter_class;
Meter* Meter_new(const ProcessList* pl, int param, const MeterClass* type); Meter* Meter_new(const ProcessList* pl, unsigned int param, const MeterClass* type);
int Meter_humanUnit(char* buffer, unsigned long int value, size_t size); int Meter_humanUnit(char* buffer, unsigned long int value, size_t size);
@ -133,7 +144,7 @@ void Meter_setCaption(Meter* this, const char* caption);
void Meter_setMode(Meter* this, int modeIndex); void Meter_setMode(Meter* this, int modeIndex);
ListItem* Meter_toListItem(Meter* this, bool moving); ListItem* Meter_toListItem(const Meter* this, bool moving);
extern const MeterMode* const Meter_modes[]; extern const MeterMode* const Meter_modes[];

View File

@ -185,8 +185,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
Header* header = this->scr->header; Header* header = this->scr->header;
this->settings->changed = true; this->settings->changed = true;
Header_calculateHeight(header); Header_calculateHeight(header);
Header_draw(header); ScreenManager_resize(this->scr);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
} }
return result; return result;
} }
@ -216,7 +215,7 @@ MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* met
this->leftNeighbor = NULL; this->leftNeighbor = NULL;
Panel_setHeader(super, header); Panel_setHeader(super, header);
for (int i = 0; i < Vector_size(meters); i++) { for (int i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i); const Meter* meter = (const Meter*) Vector_get(meters, i);
Panel_add(super, (Object*) Meter_toListItem(meter, false)); Panel_add(super, (Object*) Meter_toListItem(meter, false));
} }
return this; return this;

View File

@ -1,13 +1,14 @@
#include "NetworkIOMeter.h" #include "NetworkIOMeter.h"
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stdint.h>
#include <sys/time.h>
#include "CRT.h" #include "CRT.h"
#include "Macros.h" #include "Macros.h"
#include "Object.h" #include "Object.h"
#include "Platform.h" #include "Platform.h"
#include "Process.h"
#include "ProcessList.h"
#include "RichString.h" #include "RichString.h"
#include "XUtils.h" #include "XUtils.h"
@ -19,71 +20,81 @@ static const int NetworkIOMeter_attributes[] = {
static bool hasData = false; static bool hasData = false;
static unsigned long int cached_rxb_diff = 0; static uint32_t cached_rxb_diff;
static unsigned long int cached_rxp_diff = 0; static uint32_t cached_rxp_diff;
static unsigned long int cached_txb_diff = 0; static uint32_t cached_txb_diff;
static unsigned long int cached_txp_diff = 0; static uint32_t cached_txp_diff;
static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t len) { static void NetworkIOMeter_updateValues(Meter* this) {
static unsigned long long int cached_last_update = 0; const ProcessList* pl = this->pl;
static uint64_t cached_last_update = 0;
struct timeval tv; uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update;
gettimeofday(&tv, NULL);
unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000;
unsigned long long int passedTimeInMs = timeInMilliSeconds - cached_last_update;
/* update only every 500ms */ /* update only every 500ms */
if (passedTimeInMs > 500) { if (passedTimeInMs > 500) {
static unsigned long int cached_rxb_total = 0; static uint64_t cached_rxb_total;
static unsigned long int cached_rxp_total = 0; static uint64_t cached_rxp_total;
static unsigned long int cached_txb_total = 0; static uint64_t cached_txb_total;
static unsigned long int cached_txp_total = 0; static uint64_t cached_txp_total;
uint64_t diff;
cached_last_update = timeInMilliSeconds; cached_last_update = pl->realtimeMs;
unsigned long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted; NetworkIOData data;
hasData = Platform_getNetworkIO(&data);
hasData = Platform_getNetworkIO(&bytesReceived, &packetsReceived, &bytesTransmitted, &packetsTransmitted);
if (!hasData) { if (!hasData) {
xSnprintf(buffer, len, "no data"); xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data");
return; return;
} }
if (bytesReceived > cached_rxb_total) { if (data.bytesReceived > cached_rxb_total) {
cached_rxb_diff = (bytesReceived - cached_rxb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */ diff = data.bytesReceived - cached_rxb_total;
cached_rxb_diff = 1000.0 * cached_rxb_diff / passedTimeInMs; /* convert to per second */ diff /= ONE_K; /* Meter_humanUnit() expects unit in kilo */
diff = (1000 * diff) / passedTimeInMs; /* convert to per second */
cached_rxb_diff = (uint32_t)diff;
} else { } else {
cached_rxb_diff = 0; cached_rxb_diff = 0;
} }
cached_rxb_total = bytesReceived; cached_rxb_total = data.bytesReceived;
if (packetsReceived > cached_rxp_total) { if (data.packetsReceived > cached_rxp_total) {
cached_rxp_diff = packetsReceived - cached_rxp_total; diff = data.packetsReceived - cached_rxp_total;
cached_rxp_diff = (uint32_t)diff;
} else { } else {
cached_rxp_diff = 0; cached_rxp_diff = 0;
} }
cached_rxp_total = packetsReceived; cached_rxp_total = data.packetsReceived;
if (bytesTransmitted > cached_txb_total) { if (data.bytesTransmitted > cached_txb_total) {
cached_txb_diff = (bytesTransmitted - cached_txb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */ diff = data.bytesTransmitted - cached_txb_total;
cached_txb_diff = 1000.0 * cached_txb_diff / passedTimeInMs; /* convert to per second */ diff /= ONE_K; /* Meter_humanUnit() expects unit in kilo */
diff = (1000 * diff) / passedTimeInMs; /* convert to per second */
cached_txb_diff = (uint32_t)diff;
} else { } else {
cached_txb_diff = 0; cached_txb_diff = 0;
} }
cached_txb_total = bytesTransmitted; cached_txb_total = data.bytesTransmitted;
if (packetsTransmitted > cached_txp_total) { if (data.packetsTransmitted > cached_txp_total) {
cached_txp_diff = packetsTransmitted - cached_txp_total; diff = data.packetsTransmitted - cached_txp_total;
cached_txp_diff = (uint32_t)diff;
} else { } else {
cached_txp_diff = 0; cached_txp_diff = 0;
} }
cached_txp_total = packetsTransmitted; cached_txp_total = data.packetsTransmitted;
}
this->values[0] = cached_rxb_diff;
this->values[1] = cached_txb_diff;
if (cached_rxb_diff + cached_txb_diff > this->total) {
this->total = cached_rxb_diff + cached_txb_diff;
} }
char bufferBytesReceived[12], bufferBytesTransmitted[12]; char bufferBytesReceived[12], bufferBytesTransmitted[12];
Meter_humanUnit(bufferBytesReceived, cached_rxb_diff, sizeof(bufferBytesReceived)); Meter_humanUnit(bufferBytesReceived, cached_rxb_diff, sizeof(bufferBytesReceived));
Meter_humanUnit(bufferBytesTransmitted, cached_txb_diff, sizeof(bufferBytesTransmitted)); Meter_humanUnit(bufferBytesTransmitted, cached_txb_diff, sizeof(bufferBytesTransmitted));
xSnprintf(buffer, len, "rx:%siB/s tx:%siB/s", bufferBytesReceived, bufferBytesTransmitted); xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "rx:%siB/s tx:%siB/s", bufferBytesReceived, bufferBytesTransmitted);
} }
static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
@ -93,6 +104,7 @@ static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* o
} }
char buffer[64]; char buffer[64];
int len;
RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: "); RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: ");
Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer)); Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer));
@ -104,8 +116,8 @@ static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* o
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff); len = xSnprintf(buffer, sizeof(buffer), " (%u/%u packets) ", cached_rxp_diff, cached_txp_diff);
RichString_appendAscii(out, CRT_colors[METER_TEXT], buffer); RichString_appendnAscii(out, CRT_colors[METER_TEXT], buffer, len);
} }
const MeterClass NetworkIOMeter_class = { const MeterClass NetworkIOMeter_class = {
@ -116,7 +128,7 @@ const MeterClass NetworkIOMeter_class = {
}, },
.updateValues = NetworkIOMeter_updateValues, .updateValues = NetworkIOMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 0, .maxItems = 2,
.total = 100.0, .total = 100.0,
.attributes = NetworkIOMeter_attributes, .attributes = NetworkIOMeter_attributes,
.name = "NetworkIO", .name = "NetworkIO",

View File

@ -3,6 +3,14 @@
#include "Meter.h" #include "Meter.h"
typedef struct NetworkIOData_ {
uint64_t bytesReceived;
uint64_t packetsReceived;
uint64_t bytesTransmitted;
uint64_t packetsTransmitted;
} NetworkIOData;
extern const MeterClass NetworkIOMeter_class; extern const MeterClass NetworkIOMeter_class;
#endif /* HEADER_NetworkIOMeter */ #endif /* HEADER_NetworkIOMeter */

View File

@ -15,8 +15,6 @@ const ObjectClass Object_class = {
.extends = NULL .extends = NULL
}; };
#ifndef NDEBUG
bool Object_isA(const Object* o, const ObjectClass* klass) { bool Object_isA(const Object* o, const ObjectClass* klass) {
if (!o) if (!o)
return false; return false;
@ -29,5 +27,3 @@ bool Object_isA(const Object* o, const ObjectClass* klass) {
return false; return false;
} }
#endif /* NDEBUG */

View File

@ -11,14 +11,11 @@ in the source distribution for its full text.
#include "config.h" // IWYU pragma: keep #include "config.h" // IWYU pragma: keep
#include <assert.h> #include <assert.h>
#include <stdbool.h>
#include "RichString.h" #include "RichString.h"
#include "XUtils.h" // IWYU pragma: keep #include "XUtils.h" // IWYU pragma: keep
#ifndef NDEBUG
#include <stdbool.h>
#endif
struct Object_; struct Object_;
typedef struct Object_ Object; typedef struct Object_ Object;
@ -57,10 +54,6 @@ typedef union {
extern const ObjectClass Object_class; extern const ObjectClass Object_class;
#ifndef NDEBUG
bool Object_isA(const Object* o, const ObjectClass* klass); bool Object_isA(const Object* o, const ObjectClass* klass);
#endif /* NDEBUG */
#endif #endif

View File

@ -13,6 +13,7 @@ in the source distribution for its full text.
#include "Object.h" #include "Object.h"
#include "Process.h" #include "Process.h"
typedef struct OpenFilesScreen_ { typedef struct OpenFilesScreen_ {
InfoScreen super; InfoScreen super;
pid_t pid; pid_t pid;

View File

@ -53,7 +53,7 @@ static void NumberItem_display(const Object* cast, RichString* out) {
} else { } else {
written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this)); written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this));
} }
RichString_appendAscii(out, CRT_colors[CHECK_MARK], buffer); RichString_appendnAscii(out, CRT_colors[CHECK_MARK], buffer, written);
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]"); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
for (int i = written; i < 5; i++) { for (int i = written; i < 5; i++) {
RichString_appendAscii(out, CRT_colors[CHECK_BOX], " "); RichString_appendAscii(out, CRT_colors[CHECK_BOX], " ");

24
Panel.c
View File

@ -69,7 +69,7 @@ void Panel_done(Panel* this) {
free(this->eventHandlerState); free(this->eventHandlerState);
Vector_delete(this->items); Vector_delete(this->items);
FunctionBar_delete(this->defaultBar); FunctionBar_delete(this->defaultBar);
RichString_end(this->header); RichString_delete(&this->header);
} }
void Panel_setSelectionColor(Panel* this, ColorElements colorId) { void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
@ -172,13 +172,13 @@ void Panel_moveSelectedDown(Panel* this) {
} }
} }
int Panel_getSelectedIndex(Panel* this) { int Panel_getSelectedIndex(const Panel* this) {
assert (this != NULL); assert (this != NULL);
return this->selected; return this->selected;
} }
int Panel_size(Panel* this) { int Panel_size(const Panel* this) {
assert (this != NULL); assert (this != NULL);
return Vector_size(this->items); return Vector_size(this->items);
@ -246,8 +246,8 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
if (this->scrollV < 0) { if (this->scrollV < 0) {
this->scrollV = 0; this->scrollV = 0;
this->needsRedraw = true; this->needsRedraw = true;
} else if (this->scrollV >= size) { } else if (this->scrollV > size - h) {
this->scrollV = MAXIMUM(size - 1, 0); this->scrollV = MAXIMUM(size - h, 0);
this->needsRedraw = true; this->needsRedraw = true;
} }
// ensure selection is on screen // ensure selection is on screen
@ -269,7 +269,7 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
if (this->needsRedraw || force_redraw) { if (this->needsRedraw || force_redraw) {
int line = 0; int line = 0;
for (int i = first; line < h && i < upTo; i++) { for (int i = first; line < h && i < upTo; i++) {
Object* itemObj = Vector_get(this->items, i); const Object* itemObj = Vector_get(this->items, i);
RichString_begin(item); RichString_begin(item);
Object_display(itemObj, &item); Object_display(itemObj, &item);
int itemLen = RichString_sizeVal(item); int itemLen = RichString_sizeVal(item);
@ -287,7 +287,7 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
RichString_printoffnVal(item, y + line, x, scrollH, amt); RichString_printoffnVal(item, y + line, x, scrollH, amt);
if (item.highlightAttr) if (item.highlightAttr)
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
RichString_end(item); RichString_delete(&item);
line++; line++;
} }
while (line < h) { while (line < h) {
@ -296,11 +296,11 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
} }
} else { } else {
Object* oldObj = Vector_get(this->items, this->oldSelected); const Object* oldObj = Vector_get(this->items, this->oldSelected);
RichString_begin(old); RichString_begin(old);
Object_display(oldObj, &old); Object_display(oldObj, &old);
int oldLen = RichString_sizeVal(old); int oldLen = RichString_sizeVal(old);
Object* newObj = Vector_get(this->items, this->selected); const Object* newObj = Vector_get(this->items, this->selected);
RichString_begin(new); RichString_begin(new);
Object_display(newObj, &new); Object_display(newObj, &new);
int newLen = RichString_sizeVal(new); int newLen = RichString_sizeVal(new);
@ -316,8 +316,8 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
RichString_printoffnVal(new, y + this->selected - first, x, RichString_printoffnVal(new, y + this->selected - first, x,
scrollH, MINIMUM(newLen - scrollH, this->w)); scrollH, MINIMUM(newLen - scrollH, this->w));
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
RichString_end(new); RichString_delete(&new);
RichString_end(old); RichString_delete(&old);
} }
if (focus && (this->needsRedraw || force_redraw || !this->wasFocus)) { if (focus && (this->needsRedraw || force_redraw || !this->wasFocus)) {
@ -454,7 +454,7 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
if (len < 99) { if (len < 99) {
buffer[len] = ch; buffer[len] = ch;
buffer[len+1] = '\0'; buffer[len + 1] = '\0';
} }
for (int try = 0; try < 2; try++) { for (int try = 0; try < 2; try++) {

10
Panel.h
View File

@ -7,6 +7,9 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include "CRT.h" #include "CRT.h"
@ -26,7 +29,8 @@ typedef enum HandlerResult_ {
REFRESH = 0x08, REFRESH = 0x08,
REDRAW = 0x10, REDRAW = 0x10,
RESCAN = 0x20, RESCAN = 0x20,
SYNTH_KEY = 0x40, RESIZE = 0x40,
SYNTH_KEY = 0x80,
} HandlerResult; } HandlerResult;
#define EVENT_SET_SELECTED (-1) #define EVENT_SET_SELECTED (-1)
@ -112,9 +116,9 @@ void Panel_moveSelectedUp(Panel* this);
void Panel_moveSelectedDown(Panel* this); void Panel_moveSelectedDown(Panel* this);
int Panel_getSelectedIndex(Panel* this); int Panel_getSelectedIndex(const Panel* this);
int Panel_size(Panel* this); int Panel_size(const Panel* this);
void Panel_setSelected(Panel* this, int selected); void Panel_setSelected(Panel* this, int selected);

1060
Process.c

File diff suppressed because it is too large Load Diff

262
Process.h
View File

@ -17,7 +17,9 @@ in the source distribution for its full text.
#include "RichString.h" #include "RichString.h"
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x00000001
#define PROCESS_FLAG_CWD 0x00000002
#define DEFAULT_HIGHLIGHT_SECS 5 #define DEFAULT_HIGHLIGHT_SECS 5
typedef enum ProcessField_ { typedef enum ProcessField_ {
@ -28,7 +30,7 @@ typedef enum ProcessField_ {
PPID = 4, PPID = 4,
PGRP = 5, PGRP = 5,
SESSION = 6, SESSION = 6,
TTY_NR = 7, TTY = 7,
TPGID = 8, TPGID = 8,
MINFLT = 10, MINFLT = 10,
MAJFLT = 12, MAJFLT = 12,
@ -46,90 +48,240 @@ typedef enum ProcessField_ {
NLWP = 51, NLWP = 51,
TGID = 52, TGID = 52,
PERCENT_NORM_CPU = 53, PERCENT_NORM_CPU = 53,
ELAPSED = 54,
PROC_COMM = 124,
PROC_EXE = 125,
CWD = 126,
/* Platform specific fields, defined in ${platform}/ProcessField.h */ /* Platform specific fields, defined in ${platform}/ProcessField.h */
PLATFORM_PROCESS_FIELDS PLATFORM_PROCESS_FIELDS
/* Do not add new fields after this entry (dynamic entries follow) */
LAST_PROCESSFIELD LAST_PROCESSFIELD
} ProcessField; } ProcessField;
struct Settings_; struct Settings_;
/* Holds information about regions of the cmdline that should be
* highlighted (e.g. program basename, delimiter, comm). */
typedef struct ProcessCmdlineHighlight_ {
size_t offset; /* first character to highlight */
size_t length; /* How many characters to highlight, zero if unused */
int attr; /* The attributes used to highlight */
int flags; /* Special flags used for selective highlighting, zero for always */
} ProcessCmdlineHighlight;
/* ProcessMergedCommand is populated by Process_makeCommandStr: It
* contains the merged Command string, and the information needed by
* Process_writeCommand to color the string. str will be NULL for kernel
* threads and zombies */
typedef struct ProcessMergedCommand_ {
char* str; /* merged Command string */
size_t highlightCount; /* how many portions of cmdline to highlight */
ProcessCmdlineHighlight highlights[8]; /* which portions of cmdline to highlight */
bool cmdlineChanged : 1; /* whether cmdline changed */
bool exeChanged : 1; /* whether exe changed */
bool commChanged : 1; /* whether comm changed */
bool prevMergeSet : 1; /* whether showMergedCommand was set */
bool prevPathSet : 1; /* whether showProgramPath was set */
bool prevCommSet : 1; /* whether findCommInCmdline was set */
bool prevCmdlineSet : 1; /* whether stripExeFromCmdline was set */
bool prevShowThreadNames : 1; /* whether showThreadNames was set */
} ProcessMergedCommand;
typedef struct Process_ { typedef struct Process_ {
/* Super object for emulated OOP */
Object super; Object super;
/* Pointer to quasi-global data structures */
const struct ProcessList_* processList; const struct ProcessList_* processList;
const struct Settings_* settings; const struct Settings_* settings;
unsigned long long int time; /* Process identifier */
pid_t pid; pid_t pid;
/* Parent process identifier */
pid_t ppid; pid_t ppid;
/* Thread group identifier */
pid_t tgid; pid_t tgid;
char* comm; /* use Process_getCommand() for Command actually displayed */
int commLen;
int indent;
int basenameOffset; /* Process group identifier */
bool updated; int pgrp;
char state; /* Session identifier */
bool tag; int session;
bool showChildren;
bool show; /* Foreground group identifier of the controlling terminal */
bool wasShown;
unsigned int pgrp;
unsigned int session;
unsigned int tty_nr;
int tpgid; int tpgid;
uid_t st_uid;
unsigned long int flags;
int processor;
float percent_cpu; /* This is a kernel (helper) task */
float percent_mem; bool isKernelThread;
/* This is a userland thread / LWP */
bool isUserlandThread;
/* Controlling terminal identifier of the process */
unsigned long int tty_nr;
/* Controlling terminal name of the process */
char* tty_name;
/* User identifier */
uid_t st_uid;
/* User name */
const char* user; const char* user;
/* Process runtime (in hundredth of a second) */
unsigned long long int time;
/*
* Process name including arguments.
* Use Process_getCommand() for Command actually displayed.
*/
char* cmdline;
/* End Offset in cmdline of the process basename */
int cmdlineBasenameEnd;
/* Start Offset in cmdline of the process basename */
int cmdlineBasenameStart;
/* The process' "command" name */
char* procComm;
/* The main process executable */
char* procExe;
/* The process/thread working directory */
char* procCwd;
/* Offset in procExe of the process basename */
int procExeBasenameOffset;
/* Tells if the executable has been replaced in the filesystem since start */
bool procExeDeleted;
/* Tells if the process uses replaced shared libraries since start */
bool usesDeletedLib;
/* CPU number last executed on */
int processor;
/* CPU usage during last cycle (in percent) */
float percent_cpu;
/* Memory usage during last cycle (in percent) */
float percent_mem;
/* Scheduling priority */
long int priority; long int priority;
/* Nice value */
long int nice; long int nice;
/* Number of threads in this process */
long int nlwp; long int nlwp;
char starttime_show[8];
/* Process start time (in seconds elapsed since the Epoch) */
time_t starttime_ctime; time_t starttime_ctime;
/* Process start time (cached formatted string) */
char starttime_show[8];
/* Total program size (in kilobytes) */
long m_virt; long m_virt;
/* Resident set size (in kilobytes) */
long m_resident; long m_resident;
int exit_signal; /* Number of minor faults the process has made which have not required loading a memory page from disk */
time_t seenTs;
time_t tombTs;
unsigned long int minflt; unsigned long int minflt;
/* Number of major faults the process has made which have required loading a memory page from disk */
unsigned long int majflt; unsigned long int majflt;
/*
* Process state (platform dependent):
* D - Waiting
* I - Idle
* L - Acquiring lock
* R - Running
* S - Sleeping
* T - Stopped (on a signal)
* X - Dead
* Z - Zombie
* t - Tracing stop
* ? - Unknown
*/
char state;
/* Whether the process was updated during the current scan */
bool updated;
/* Whether the process was tagged by the user */
bool tag;
/* Whether to display this process */
bool show;
/* Whether this process was shown last cycle */
bool wasShown;
/* Whether to show children of this process in tree-mode */
bool showChildren;
/*
* Internal time counts for showing new and exited processes.
*/
uint64_t seenStampMs;
uint64_t tombStampMs;
/*
* Internal state for tree-mode.
*/
int indent;
unsigned int tree_left; unsigned int tree_left;
unsigned int tree_right; unsigned int tree_right;
unsigned int tree_depth; unsigned int tree_depth;
unsigned int tree_index; unsigned int tree_index;
/*
* Internal state for merged Command display
*/
ProcessMergedCommand mergedCommand;
} Process; } Process;
typedef struct ProcessFieldData_ { typedef struct ProcessFieldData_ {
/* Name (displayed in setup menu) */
const char* name; const char* name;
/* Title (display in main screen); must have same width as the printed values */
const char* title; const char* title;
/* Description (displayed in setup menu) */
const char* description; const char* description;
/* Scan flag to enable scan-method otherwise not run */
uint32_t flags; uint32_t flags;
/* Whether the values are process identifiers; adjusts the width of title and values if true */
bool pidColumn; bool pidColumn;
/* Whether the column should be sorted in descending order by default */
bool defaultSortDesc;
} ProcessFieldData; } ProcessFieldData;
// Implemented in platform-specific code: // Implemented in platform-specific code:
void Process_writeField(const Process* this, RichString* str, ProcessField field); void Process_writeField(const Process* this, RichString* str, ProcessField field);
int Process_compare(const void* v1, const void* v2); int Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast); void Process_delete(Object* cast);
bool Process_isThread(const Process* this);
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
#define PROCESS_MAX_PID_DIGITS 19 #define PROCESS_MAX_PID_DIGITS 19
extern int Process_pidDigits; extern int Process_pidDigits;
typedef Process*(*Process_New)(const struct Settings_*); typedef Process* (*Process_New)(const struct Settings_*);
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField); typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField); typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
typedef const char* (*Process_GetCommandStr)(const Process*); typedef const char* (*Process_GetCommandStr)(const Process*);
@ -143,7 +295,7 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass)) #define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm) #define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : Process_getCommandStr((const Process*)(this_)))
#define Process_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_)) #define Process_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_))
static inline pid_t Process_getParentPid(const Process* this) { static inline pid_t Process_getParentPid(const Process* this) {
@ -154,33 +306,58 @@ static inline bool Process_isChildOf(const Process* this, pid_t pid) {
return pid == Process_getParentPid(this); return pid == Process_getParentPid(this);
} }
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state)) static inline bool Process_isKernelThread(const Process* this) {
return this->isKernelThread;
}
static inline bool Process_isUserlandThread(const Process* this) {
return this->isUserlandThread;
}
static inline bool Process_isThread(const Process* this) {
return Process_isUserlandThread(this) || Process_isKernelThread(this);
}
#define CMDLINE_HIGHLIGHT_FLAG_SEPARATOR 0x00000001
#define CMDLINE_HIGHLIGHT_FLAG_BASENAME 0x00000002
#define CMDLINE_HIGHLIGHT_FLAG_COMM 0x00000004
#define CMDLINE_HIGHLIGHT_FLAG_DELETED 0x00000008
#define ONE_K 1024UL #define ONE_K 1024UL
#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 (1ULL * ONE_G * ONE_K) #define ONE_T (1ULL * ONE_G * ONE_K)
#define ONE_P (1ULL * ONE_T * ONE_K)
#define ONE_DECIMAL_K 1000UL #define ONE_DECIMAL_K 1000UL
#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 (1ULL * ONE_DECIMAL_G * ONE_DECIMAL_K) #define ONE_DECIMAL_T (1ULL * ONE_DECIMAL_G * ONE_DECIMAL_K)
#define ONE_DECIMAL_P (1ULL * ONE_DECIMAL_T * ONE_DECIMAL_K)
void Process_setupColumnWidths(void); void Process_setupColumnWidths(void);
void Process_humanNumber(RichString* str, unsigned long long number, bool coloring); /* Takes number in bytes (base 1024). Prints 6 columns. */
void Process_printBytes(RichString* str, unsigned long long number, bool coloring);
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring); /* Takes number in kilo bytes (base 1024). Prints 6 columns. */
void Process_printKBytes(RichString* str, unsigned long long number, bool coloring);
void Process_printTime(RichString* str, unsigned long long totalHundredths); /* Takes number as count (base 1000). Prints 12 columns. */
void Process_printCount(RichString* str, unsigned long long number, bool coloring);
/* Takes time in hundredths of a seconds. Prints 9 columns. */
void Process_printTime(RichString* str, unsigned long long totalHundredths, bool coloring);
/* Takes rate in bare unit (base 1024) per second. Prints 12 columns. */
void Process_printRate(RichString* str, double rate, bool coloring);
void Process_fillStarttimeBuffer(Process* this); void Process_fillStarttimeBuffer(Process* this);
void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring);
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width); void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width);
void Process_printPercentage(float val, char* buffer, int n, int* attr);
void Process_display(const Object* cast, RichString* out); void Process_display(const Object* cast, RichString* out);
void Process_done(Process* this); void Process_done(Process* this);
@ -205,4 +382,17 @@ int Process_pidCompare(const void* v1, const void* v2);
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key); int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
// Avoid direct calls, use Process_getCommand instead
const char* Process_getCommandStr(const Process* this);
void Process_updateComm(Process* this, const char* comm);
void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart, int basenameEnd);
void Process_updateExe(Process* this, const char* exe);
/* This function constructs the string that is displayed by
* Process_writeCommand and also returned by Process_getCommandStr */
void Process_makeCommandStr(Process* this);
void Process_writeCommand(const Process* this, int attr, int baseAttr, RichString* str);
#endif #endif

View File

@ -11,15 +11,16 @@ in the source distribution for its full text.
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "Compat.h"
#include "CRT.h" #include "CRT.h"
#include "DynamicColumn.h"
#include "Hashtable.h" #include "Hashtable.h"
#include "Macros.h" #include "Macros.h"
#include "Platform.h"
#include "Vector.h" #include "Vector.h"
#include "XUtils.h" #include "XUtils.h"
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) {
this->processes = Vector_new(klass, true, DEFAULT_SIZE); this->processes = Vector_new(klass, true, DEFAULT_SIZE);
this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); // tree-view auxiliary buffer this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); // tree-view auxiliary buffer
@ -29,13 +30,18 @@ ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, Users
this->usersTable = usersTable; this->usersTable = usersTable;
this->pidMatchList = pidMatchList; this->pidMatchList = pidMatchList;
this->dynamicMeters = dynamicMeters;
this->dynamicColumns = dynamicColumns;
this->userId = userId; this->userId = userId;
// set later by platform-specific code // set later by platform-specific code
this->cpuCount = 0; this->activeCPUs = 0;
this->existingCPUs = 0;
this->monotonicMs = 0;
this->scanTs = 0; // always maintain valid realtime timestamps
Platform_gettime_realtime(&this->realtime, &this->realtimeMs);
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
this->topologyOk = false; this->topologyOk = false;
@ -79,7 +85,22 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
this->panel = panel; this->panel = panel;
} }
static const char* alignedProcessFieldTitle(ProcessField field) { static const char* alignedDynamicColumnTitle(const ProcessList* this, int key) {
const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key);
if (column == NULL)
return "- ";
static char titleBuffer[DYNAMIC_MAX_COLUMN_WIDTH + /* space */ 1 + /* null terminator */ + 1];
int width = column->width;
if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s", width, column->heading);
return titleBuffer;
}
static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessField field) {
if (field >= LAST_PROCESSFIELD)
return alignedDynamicColumnTitle(this, field);
const char* title = Process_fields[field].title; const char* title = Process_fields[field].title;
if (!title) if (!title)
return "- "; return "- ";
@ -93,8 +114,8 @@ static const char* alignedProcessFieldTitle(ProcessField field) {
return titleBuffer; return titleBuffer;
} }
void ProcessList_printHeader(ProcessList* this, RichString* header) { void ProcessList_printHeader(const ProcessList* this, RichString* header) {
RichString_prune(header); RichString_rewind(header, RichString_size(header));
const Settings* settings = this->settings; const Settings* settings = this->settings;
const ProcessField* fields = settings->fields; const ProcessField* fields = settings->fields;
@ -111,12 +132,12 @@ void ProcessList_printHeader(ProcessList* this, RichString* header) {
color = CRT_colors[PANEL_HEADER_FOCUS]; color = CRT_colors[PANEL_HEADER_FOCUS];
} }
RichString_appendWide(header, color, alignedProcessFieldTitle(fields[i])); RichString_appendWide(header, color, alignedProcessFieldTitle(this, fields[i]));
if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') { if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') {
header->chlen--; // rewind to override space RichString_rewind(header, 1); // rewind to override space
RichString_appendnWide(header, RichString_appendnWide(header,
CRT_colors[PANEL_SELECTION_FOCUS], CRT_colors[PANEL_SELECTION_FOCUS],
CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_DESC : TREE_STR_ASC], CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_ASC : TREE_STR_DESC],
1); 1);
} }
if (COMM == fields[i] && settings->showMergedCommand) { if (COMM == fields[i] && settings->showMergedCommand) {
@ -131,7 +152,7 @@ void ProcessList_add(ProcessList* this, Process* p) {
p->processList = this; p->processList = this;
// highlighting processes found in first scan by first scan marked "far in the past" // highlighting processes found in first scan by first scan marked "far in the past"
p->seenTs = this->scanTs; p->seenStampMs = this->monotonicMs;
Vector_add(this->processes, p); Vector_add(this->processes, p);
Hashtable_put(this->processTable, p->pid, p); Hashtable_put(this->processTable, p->pid, p);
@ -141,11 +162,11 @@ void ProcessList_add(ProcessList* this, Process* p) {
assert(Hashtable_count(this->processTable) == Vector_count(this->processes)); assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
} }
void ProcessList_remove(ProcessList* this, Process* p) { void ProcessList_remove(ProcessList* this, const Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1); assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
assert(Hashtable_get(this->processTable, p->pid) != NULL); assert(Hashtable_get(this->processTable, p->pid) != NULL);
Process* pp = Hashtable_remove(this->processTable, p->pid); const Process* pp = Hashtable_remove(this->processTable, p->pid);
assert(pp == p); (void)pp; assert(pp == p); (void)pp;
pid_t pid = p->pid; pid_t pid = p->pid;
@ -165,14 +186,6 @@ void ProcessList_remove(ProcessList* this, Process* p) {
assert(Hashtable_count(this->processTable) == Vector_count(this->processes)); assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
} }
Process* ProcessList_get(ProcessList* this, int idx) {
return (Process*)Vector_get(this->processes, idx);
}
int ProcessList_size(ProcessList* this) {
return Vector_size(this->processes);
}
// ProcessList_updateTreeSetLayer sorts this->displayTreeSet, // ProcessList_updateTreeSetLayer sorts this->displayTreeSet,
// relying only on itself. // relying only on itself.
// //
@ -202,7 +215,7 @@ static void ProcessList_updateTreeSetLayer(ProcessList* this, unsigned int leftB
if (layerSize == 0) if (layerSize == 0)
return; return;
Vector* layer = Vector_new(this->processes->type, false, layerSize); Vector* layer = Vector_new(Vector_type(this->processes), false, layerSize);
// Find all processes on the same layer (process with the same `deep` value // Find all processes on the same layer (process with the same `deep` value
// and included in a range from `leftBound` to `rightBound`). // and included in a range from `leftBound` to `rightBound`).
@ -311,6 +324,11 @@ static void ProcessList_updateTreeSet(ProcessList* this) {
} }
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show, int* node_counter, int* node_index) { static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show, int* node_counter, int* node_index) {
// On OpenBSD the kernel thread 'swapper' has pid 0.
// Do not treat it as root of any tree.
if (pid == 0)
return;
Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE); Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
@ -361,15 +379,15 @@ static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level,
} }
static int ProcessList_treeProcessCompare(const void* v1, const void* v2) { static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
const Process *p1 = (const Process*)v1; const Process* p1 = (const Process*)v1;
const Process *p2 = (const Process*)v2; const Process* p2 = (const Process*)v2;
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left); return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
} }
static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) { static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
const Process *p1 = (const Process*)v1; const Process* p1 = (const Process*)v1;
const Process *p2 = (const Process*)v2; const Process* p2 = (const Process*)v2;
return SPACESHIP_NUMBER(p1->pid, p2->pid); return SPACESHIP_NUMBER(p1->pid, p2->pid);
} }
@ -477,7 +495,7 @@ ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
const ProcessField* fields = this->settings->fields; const ProcessField* fields = this->settings->fields;
ProcessField field; ProcessField field;
for (int i = 0; (field = fields[i]); i++) { for (int i = 0; (field = fields[i]); i++) {
int len = strlen(alignedProcessFieldTitle(field)); int len = strlen(alignedProcessFieldTitle(this, field));
if (at >= x && at <= x + len) { if (at >= x && at <= x + len) {
return field; return field;
} }
@ -494,17 +512,40 @@ void ProcessList_expandTree(ProcessList* this) {
} }
} }
void ProcessList_collapseAllBranches(ProcessList* this) {
int size = Vector_size(this->processes);
for (int i = 0; i < size; i++) {
Process* process = (Process*) Vector_get(this->processes, i);
// FreeBSD has pid 0 = kernel and pid 1 = init, so init has tree_depth = 1
if (process->tree_depth > 0 && process->pid > 1)
process->showChildren = false;
}
}
void ProcessList_rebuildPanel(ProcessList* this) { void ProcessList_rebuildPanel(ProcessList* this) {
const char* incFilter = this->incFilter; const char* incFilter = this->incFilter;
int currPos = Panel_getSelectedIndex(this->panel); const int currPos = Panel_getSelectedIndex(this->panel);
int currScrollV = this->panel->scrollV; const int currScrollV = this->panel->scrollV;
const int currSize = Panel_size(this->panel);
Panel_prune(this->panel); Panel_prune(this->panel);
int size = ProcessList_size(this);
/* Follow main process if followed a userland thread and threads are now hidden */
const Settings* settings = this->settings;
if (this->following != -1 && settings->hideUserlandThreads) {
const Process* followedProcess = (const Process*) Hashtable_get(this->processTable, this->following);
if (followedProcess && Process_isThread(followedProcess) && Hashtable_get(this->processTable, followedProcess->tgid) != NULL) {
this->following = followedProcess->tgid;
}
}
const int processCount = Vector_size(this->processes);
int idx = 0; int idx = 0;
for (int i = 0; i < size; i++) { bool foundFollowed = false;
Process* p = ProcessList_get(this, i);
for (int i = 0; i < processCount; i++) {
Process* p = (Process*) Vector_get(this->processes, i);
if ( (!p->show) if ( (!p->show)
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId)) || (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
@ -513,31 +554,47 @@ void ProcessList_rebuildPanel(ProcessList* this) {
continue; continue;
Panel_set(this->panel, idx, (Object*)p); Panel_set(this->panel, idx, (Object*)p);
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == this->following)) {
if (this->following != -1 && p->pid == this->following) {
foundFollowed = true;
Panel_setSelected(this->panel, idx); Panel_setSelected(this->panel, idx);
this->panel->scrollV = currScrollV; this->panel->scrollV = currScrollV;
} }
idx++; idx++;
} }
if (this->following != -1 && !foundFollowed) {
/* Reset if current followed pid not found */
this->following = -1;
Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
}
if (this->following == -1) {
/* If the last item was selected, keep the new last item selected */
if (currPos > 0 && currPos == currSize - 1)
Panel_setSelected(this->panel, Panel_size(this->panel) - 1);
else
Panel_setSelected(this->panel, currPos);
this->panel->scrollV = currScrollV;
}
} }
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) { Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) {
Process* proc = (Process*) Hashtable_get(this->processTable, pid); Process* proc = (Process*) Hashtable_get(this->processTable, pid);
*preExisting = proc; *preExisting = proc != NULL;
if (proc) { if (proc) {
assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1); assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1);
assert(proc->pid == pid); assert(proc->pid == pid);
} else { } else {
proc = constructor(this->settings); proc = constructor(this->settings);
assert(proc->comm == NULL); assert(proc->cmdline == NULL);
proc->pid = pid; proc->pid = pid;
} }
return proc; return proc;
} }
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
struct timespec now;
// in pause mode only gather global data for meters (CPU/memory/...) // in pause mode only gather global data for meters (CPU/memory/...)
if (pauseProcessUpdate) { if (pauseProcessUpdate) {
ProcessList_goThroughEntries(this, true); ProcessList_goThroughEntries(this, true);
@ -558,37 +615,35 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
this->runningTasks = 0; this->runningTasks = 0;
// set scanTs // set scan timestamp
static bool firstScanDone = false; static bool firstScanDone = false;
if (!firstScanDone) { if (firstScanDone) {
this->scanTs = 0; Platform_gettime_monotonic(&this->monotonicMs);
} else {
this->monotonicMs = 0;
firstScanDone = true; firstScanDone = true;
} else if (Compat_clock_monotonic_gettime(&now) == 0) {
// save time in millisecond, so with a delay in deciseconds
// there are no irregularities
this->scanTs = 1000 * now.tv_sec + now.tv_nsec / 1000000;
} }
ProcessList_goThroughEntries(this, false); ProcessList_goThroughEntries(this, false);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* p = (Process*) Vector_get(this->processes, i); Process* p = (Process*) Vector_get(this->processes, i);
if (p->tombTs > 0) { Process_makeCommandStr(p);
if (p->tombStampMs > 0) {
// remove tombed process // remove tombed process
if (this->scanTs >= p->tombTs) { if (this->monotonicMs >= p->tombStampMs) {
ProcessList_remove(this, p); ProcessList_remove(this, p);
} }
} else if (p->updated == false) { } else if (p->updated == false) {
// process no longer exists // process no longer exists
if (this->settings->highlightChanges && p->wasShown) { if (this->settings->highlightChanges && p->wasShown) {
// mark tombed // mark tombed
p->tombTs = this->scanTs + 1000 * this->settings->highlightDelaySecs; p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs;
} else { } else {
// immediately remove // immediately remove
ProcessList_remove(this, p); ProcessList_remove(this, p);
} }
} else {
p->updated = false;
} }
} }

View File

@ -9,7 +9,10 @@ in the source distribution for its full text.
#include "config.h" // IWYU pragma: keep #include "config.h" // IWYU pragma: keep
#include <limits.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include "Hashtable.h" #include "Hashtable.h"
@ -34,6 +37,9 @@ in the source distribution for its full text.
#define MAX_READ 2048 #define MAX_READ 2048
#endif #endif
typedef unsigned long long int memory_t;
#define MEMORY_MAX ULLONG_MAX
typedef struct ProcessList_ { typedef struct ProcessList_ {
const Settings* settings; const Settings* settings;
@ -45,6 +51,13 @@ typedef struct ProcessList_ {
Hashtable* displayTreeSet; Hashtable* displayTreeSet;
Hashtable* draftingTreeSet; Hashtable* draftingTreeSet;
Hashtable* dynamicMeters; /* runtime-discovered meters */
Hashtable* dynamicColumns; /* runtime-discovered Columns */
struct timeval realtime; /* time of the current sample */
uint64_t realtimeMs; /* current time in milliseconds */
uint64_t monotonicMs; /* same, but from monotonic clock */
Panel* panel; Panel* panel;
int following; int following;
uid_t userId; uid_t userId;
@ -56,44 +69,44 @@ typedef struct ProcessList_ {
bool topologyOk; bool topologyOk;
#endif #endif
int totalTasks; unsigned int totalTasks;
int runningTasks; unsigned int runningTasks;
int userlandThreads; unsigned int userlandThreads;
int kernelThreads; unsigned int kernelThreads;
unsigned long long int totalMem; memory_t totalMem;
unsigned long long int usedMem; memory_t usedMem;
unsigned long long int buffersMem; memory_t buffersMem;
unsigned long long int cachedMem; memory_t cachedMem;
unsigned long long int totalSwap; memory_t sharedMem;
unsigned long long int usedSwap; memory_t availableMem;
unsigned long long int freeSwap;
int cpuCount; memory_t totalSwap;
memory_t usedSwap;
memory_t cachedSwap;
time_t scanTs; unsigned int activeCPUs;
unsigned int existingCPUs;
} ProcessList; } ProcessList;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); /* Implemented by platforms */
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId);
void ProcessList_delete(ProcessList* pl); void ProcessList_delete(ProcessList* pl);
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id);
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId);
void ProcessList_done(ProcessList* this); void ProcessList_done(ProcessList* this);
void ProcessList_setPanel(ProcessList* this, Panel* panel); void ProcessList_setPanel(ProcessList* this, Panel* panel);
void ProcessList_printHeader(ProcessList* this, RichString* header); void ProcessList_printHeader(const ProcessList* this, RichString* header);
void ProcessList_add(ProcessList* this, Process* p); void ProcessList_add(ProcessList* this, Process* p);
void ProcessList_remove(ProcessList* this, Process* p); void ProcessList_remove(ProcessList* this, const Process* p);
Process* ProcessList_get(ProcessList* this, int idx);
int ProcessList_size(ProcessList* this);
void ProcessList_sort(ProcessList* this); void ProcessList_sort(ProcessList* this);
@ -101,10 +114,16 @@ ProcessField ProcessList_keyAt(const ProcessList* this, int at);
void ProcessList_expandTree(ProcessList* this); void ProcessList_expandTree(ProcessList* this);
void ProcessList_collapseAllBranches(ProcessList* this);
void ProcessList_rebuildPanel(ProcessList* this); void ProcessList_rebuildPanel(ProcessList* this);
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor); Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor);
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate); void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate);
static inline Process* ProcessList_findProcess(ProcessList* this, pid_t pid) {
return (Process*) Hashtable_get(this->processTable, pid);
}
#endif #endif

View File

@ -12,7 +12,7 @@ in the source distribution for its full text.
// IWYU pragma: begin_exports // IWYU pragma: begin_exports
#ifdef HAVE_NCURSESW_CURSES_H #if defined(HAVE_NCURSESW_CURSES_H)
#include <ncursesw/curses.h> #include <ncursesw/curses.h>
#elif defined(HAVE_NCURSES_NCURSES_H) #elif defined(HAVE_NCURSES_NCURSES_H)
#include <ncurses/ncurses.h> #include <ncurses/ncurses.h>

115
README
View File

@ -3,9 +3,8 @@
[![CI](https://github.com/htop-dev/htop/workflows/CI/badge.svg)](https://github.com/htop-dev/htop/actions) [![CI](https://github.com/htop-dev/htop/workflows/CI/badge.svg)](https://github.com/htop-dev/htop/actions)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/21665/badge.svg)](https://scan.coverity.com/projects/21665) [![Coverity Scan Build Status](https://scan.coverity.com/projects/21665/badge.svg)](https://scan.coverity.com/projects/21665)
[![Mailing List](https://img.shields.io/badge/Mailing%20List-htop-blue.svg)](https://groups.io/g/htop) [![Mailing List](https://img.shields.io/badge/Mailing%20List-htop-blue.svg)](https://groups.io/g/htop)
[![IRC #htop](https://img.shields.io/badge/IRC-htop-blue.svg)](https://webchat.freenode.net/#htop) [![IRC #htop](https://img.shields.io/badge/IRC-htop-blue.svg)](https://web.libera.chat/#htop)
[![Github Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest) [![Github Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest)
[![Download](https://api.bintray.com/packages/htop/source/htop/images/download.svg)](https://bintray.com/htop/source/htop/_latestVersion)
![Screenshot of htop](docs/images/screenshot.png?raw=true) ![Screenshot of htop](docs/images/screenshot.png?raw=true)
@ -25,25 +24,123 @@ For more information and details on how to contribute to `htop` visit [htop.dev]
## Build instructions ## Build instructions
This program is distributed as a standard GNU autotools-based package. ### Prerequisite
List of build-time dependencies:
* `build-essential` standard GNU autotools-based
* `autoconf`
* `autotools`
* `ncurses`
Compiling `htop` requires the header files for `ncurses` (libncursesw*-dev). Install these and other required packages for C development from your package manager. **Note about `ncurses`:**
> htop requires ncurses 6.0. Be aware the appropriate package is sometimes still called libncurses5 (on Debian/Ubuntu). Also ncurses usually comes in two flavours:
>* With Unicode support.
>* Without Unicode support.
>
> This is also something that is reflected in the package name on Debian/Ubuntu (via the additional 'w' - 'w'ide character support).
Then, when compiling from a [release tarball](https://bintray.com/htop/source/htop), run: List of additional build-time dependencies (based on feature flags):
* `sensors`
* `hwloc`
* `libcap`
Compiling `htop` requires the header files for `ncurses` . Install these and other required packages for C development from your package manager.
**Debian/Ubuntu**
~~~ shell ~~~ shell
./configure && make sudo apt install libncursesw5-dev autotools-dev autoconf
~~~ ~~~
Alternatively, for compiling sources downloaded from the Git repository (`git clone` or downloads from [Github releases](https://github.com/htop-dev/htop/releases/)), **Fedora/RHEL**
install the header files for `ncurses` (libncursesw*-dev) and other required development packages from your distribution's package manager. Then run: ~~~ shell
sudo dnf install ncurses-devel automake autoconf
~~~
### Compiling from source:
To compile from sources downloaded from the Git repository (`git clone` or downloads from [Github releases](https://github.com/htop-dev/htop/releases/)), then run:
~~~ shell ~~~ shell
./autogen.sh && ./configure && make ./autogen.sh && ./configure && make
~~~ ~~~
By default `make install` will install into `/usr/local`, for changing the path use `./configure --prefix=/some/path`. By default `make install` will install into `/usr/local`, for changing the path use `./configure --prefix=/some/path`.
### Install
To install on the local system run `make install`. By default `make install` installs into `/usr/local`. To change this path use `./configure --prefix=/some/path`.
### Build Options
`htop` has several build-time options to enable/disable additional features.
#### Generic
* `--enable-unicode`:
enable Unicode support
dependency: *libncursesw*
default: *yes*
* `--enable-pcp`:
enable Performance Co-Pilot support via a new pcp-htop utility
dependency: *libpcp*
default: *no*
* `--enable-affinity`:
enable `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc
default: *check*
* `--enable-hwloc`:
enable hwloc support for CPU affinity; disables affinity support
dependency: *libhwloc*
default: *no*
* `--enable-static`:
build a static htop binary; hwloc and delay accounting are not supported
default: *no*
* `--enable-debug`:
Enable asserts and internal sanity checks; implies a performance penalty
default: *no*
#### Linux
* `--enable-sensors`:
enable libsensors(3) support for reading temperature data
dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available
default: *check*
* `--enable-capabilities`:
enable Linux capabilities support
dependency: *libcap*
default: *check*
* `--with-proc`:
location of a Linux-compatible proc filesystem
default: */proc*
* `--enable-openvz`:
enable OpenVZ support
default: *no*
* `--enable-vserver`:
enable VServer support
default: *no*
* `--enable-ancient-vserver`:
enable ancient VServer support (implies `--enable-vserver`)
default: *no*
* `--enable-delayacct`:
enable Linux delay accounting support
dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
default: *check*
## Runtime dependencies:
`htop` has a set of fixed minimum runtime dependencies, which is kept as minimal as possible:
* `ncurses` libraries for terminal handling (wide character support).
### Runtime optional dependencies:
`htop` has a set of fixed optional dependencies, depending on build/configure option used:
* `libdl`, if not building static and support for some of the optional libraries is enabled, is always required when support for to optionally load dependencies (i.e. `libsensors`, `systemd`) is present.
* `libcap`, user-space interfaces to the POSIX 1003.1e, is always required when `--enable-capabilities` was used to configure `htop`.
* `libsensors`, readout of temperatures and CPU speeds, is optional even when `--enable-sensors` was used to configure `htop`.
* `systemd` is optional when `--enable-static` was not used to configure `htop` (Linux only). If building statically and `libsystemd` is not found by `configure` support for the SystemD meter is disabled entirely.
`htop` checks for the availability of the actual runtime lib as `htop` runs.
**BSD**
On most *BSD systems you also have `kvm` as a static requirement to read all the kernel information.
More information on required and optional dependencies can be found in [configure.ac](configure.ac).
## Usage
See the manual page (`man htop`) or the on-line help ('F1' or 'h' inside `htop`) for a list of supported key commands. See the manual page (`man htop`) or the on-line help ('F1' or 'h' inside `htop`) for a list of supported key commands.
## Support ## Support
@ -54,7 +151,7 @@ If you have trouble running `htop` please consult your Operating System / Linux
We have a [development mailing list](https://htop.dev/mailinglist.html). Feel free to subscribe for release announcements or asking questions on the development of htop. We have a [development mailing list](https://htop.dev/mailinglist.html). Feel free to subscribe for release announcements or asking questions on the development of htop.
You can also join our IRC channel #htop on freenode and talk to the developers there. You can also join our IRC channel #htop on Libera.Chat and talk to the developers there.
If you have found an issue with the source of htop, please check whether this has already been reported in our [Github issue tracker](https://github.com/htop-dev/htop/issues). If you have found an issue with the source of htop, please check whether this has already been reported in our [Github issue tracker](https://github.com/htop-dev/htop/issues).
If not, please file a new issue describing the problem you have found, the location in the source code you are referring to and a possible fix. If not, please file a new issue describing the problem you have found, the location in the source code you are referring to and a possible fix.

View File

@ -46,6 +46,10 @@ static void RichString_setLen(RichString* this, int len) {
} }
} }
void RichString_rewind(RichString* this, int count) {
RichString_setLen(this, this->chlen - count);
}
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) { static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
@ -60,7 +64,37 @@ static inline int RichString_writeFromWide(RichString* this, int attrs, const ch
this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : '?') } }; this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : '?') } };
} }
return wcswidth(data, len); return len;
}
int RichString_appendnWideColumns(RichString* this, int attrs, const char* data_c, int len, int* columns) {
wchar_t data[len + 1];
len = mbstowcs(data, data_c, len);
if (len <= 0)
return 0;
int from = this->chlen;
int newLen = from + len;
RichString_setLen(this, newLen);
int columnsWritten = 0;
int pos = from;
for (int j = 0; j < len; j++) {
wchar_t c = iswprint(data[j]) ? data[j] : '?';
int cwidth = wcwidth(c);
if (cwidth > *columns)
break;
*columns -= cwidth;
columnsWritten += cwidth;
this->chptr[pos] = (CharType) { .attr = attrs & 0xffffff, .chars = { c, '\0' } };
pos++;
}
RichString_setLen(this, pos);
*columns = columnsWritten;
return pos - from;
} }
static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data, int from, int len) { static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data, int from, int len) {
@ -80,9 +114,18 @@ inline void RichString_setAttrn(RichString* this, int attrs, int start, int char
} }
} }
int RichString_findChar(RichString* this, char c, int start) { void RichString_appendChr(RichString* this, int attrs, char c, int count) {
wchar_t wc = btowc(c); int from = this->chlen;
cchar_t* ch = this->chptr + start; int newLen = from + count;
RichString_setLen(this, newLen);
for (int i = from; i < newLen; i++) {
this->chptr[i] = (CharType) { .attr = attrs, .chars = { c, 0 } };
}
}
int RichString_findChar(const RichString* this, char c, int start) {
const wchar_t wc = btowc(c);
const cchar_t* ch = this->chptr + start;
for (int i = start; i < this->chlen; i++) { for (int i = start; i < this->chlen; i++) {
if (ch->chars[0] == wc) if (ch->chars[0] == wc)
return i; return i;
@ -104,6 +147,12 @@ static inline int RichString_writeFromWide(RichString* this, int attrs, const ch
return len; return len;
} }
int RichString_appendnWideColumns(RichString* this, int attrs, const char* data_c, int len, int* columns) {
int written = RichString_writeFromWide(this, attrs, data_c, this->chlen, MINIMUM(len, *columns));
*columns = written;
return written;
}
static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data_c, int from, int len) { static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data_c, int from, int len) {
return RichString_writeFromWide(this, attrs, data_c, from, len); return RichString_writeFromWide(this, attrs, data_c, from, len);
} }
@ -115,8 +164,17 @@ void RichString_setAttrn(RichString* this, int attrs, int start, int charcount)
} }
} }
int RichString_findChar(RichString* this, char c, int start) { void RichString_appendChr(RichString* this, int attrs, char c, int count) {
chtype* ch = this->chptr + start; int from = this->chlen;
int newLen = from + count;
RichString_setLen(this, newLen);
for (int i = from; i < newLen; i++) {
this->chptr[i] = c | attrs;
}
}
int RichString_findChar(const RichString* this, char c, int start) {
const chtype* ch = this->chptr + start;
for (int i = start; i < this->chlen; i++) { for (int i = start; i < this->chlen; i++) {
if ((*ch & 0xff) == (chtype) c) if ((*ch & 0xff) == (chtype) c)
return i; return i;
@ -127,19 +185,10 @@ int RichString_findChar(RichString* this, char c, int start) {
#endif /* HAVE_LIBNCURSESW */ #endif /* HAVE_LIBNCURSESW */
void RichString_prune(RichString* this) { void RichString_delete(RichString* this) {
if (this->chlen > RICHSTRING_MAXLEN) if (this->chlen > RICHSTRING_MAXLEN) {
free(this->chptr); free(this->chptr);
memset(this, 0, sizeof(RichString)); this->chptr = this->chstr;
this->chptr = this->chstr;
}
void RichString_appendChr(RichString* this, char c, int count) {
int from = this->chlen;
int newLen = from + count;
RichString_setLen(this, newLen);
for (int i = from; i < newLen; i++) {
RichString_setChar(this, i, c);
} }
} }

View File

@ -15,20 +15,25 @@ in the source distribution for its full text.
#define RichString_size(this) ((this)->chlen) #define RichString_size(this) ((this)->chlen)
#define RichString_sizeVal(this) ((this).chlen) #define RichString_sizeVal(this) ((this).chlen)
#define RichString_begin(this) RichString (this); RichString_beginAllocated(this) #define RichString_begin(this) RichString this; RichString_beginAllocated(this)
#define RichString_beginAllocated(this) do { memset(&(this), 0, sizeof(RichString)); (this).chptr = (this).chstr; } while(0) #define RichString_beginAllocated(this) \
#define RichString_end(this) RichString_prune(&(this)) do { \
(this).chlen = 0, \
(this).chptr = (this).chstr; \
RichString_setChar(&(this), 0, 0); \
(this).highlightAttr = 0; \
} while(0)
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr) #define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr)
#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + (off), n) #define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + (off), n)
#define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255) #define RichString_getCharVal(this, i) ((this).chptr[i].chars[0])
#define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while (0) #define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while (0)
#define CharType cchar_t #define CharType cchar_t
#else #else
#define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr) #define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr)
#define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, (this).chptr + (off), n) #define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, (this).chptr + (off), n)
#define RichString_getCharVal(this, i) ((this).chptr[i]) #define RichString_getCharVal(this, i) ((this).chptr[i] & 0xff)
#define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = ch; } while (0) #define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = ch; } while (0)
#define CharType chtype #define CharType chtype
#endif #endif
@ -42,20 +47,27 @@ typedef struct RichString_ {
int highlightAttr; int highlightAttr;
} RichString; } RichString;
void RichString_delete(RichString* this);
void RichString_rewind(RichString* this, int count);
void RichString_setAttrn(RichString* this, int attrs, int start, int charcount); void RichString_setAttrn(RichString* this, int attrs, int start, int charcount);
int RichString_findChar(RichString* this, char c, int start); int RichString_findChar(const RichString* this, char c, int start);
void RichString_prune(RichString* this);
void RichString_setAttr(RichString* this, int attrs); void RichString_setAttr(RichString* this, int attrs);
void RichString_appendChr(RichString* this, char c, int count); void RichString_appendChr(RichString* this, int attrs, char c, int count);
/* All appending and writing functions return the number of written characters (not columns). */
int RichString_appendWide(RichString* this, int attrs, const char* data); int RichString_appendWide(RichString* this, int attrs, const char* data);
int RichString_appendnWide(RichString* this, int attrs, const char* data, int len); int RichString_appendnWide(RichString* this, int attrs, const char* data, int len);
/* columns takes the maximum number of columns to write and contains on return the number of columns written. */
int RichString_appendnWideColumns(RichString* this, int attrs, const char* data, int len, int* columns);
int RichString_writeWide(RichString* this, int attrs, const char* data); int RichString_writeWide(RichString* this, int attrs, const char* data);
int RichString_appendAscii(RichString* this, int attrs, const char* data); int RichString_appendAscii(RichString* this, int attrs, const char* data);

View File

@ -5,6 +5,8 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include "ScreenManager.h" #include "ScreenManager.h"
#include <assert.h> #include <assert.h>
@ -15,6 +17,7 @@ in the source distribution for its full text.
#include "CRT.h" #include "CRT.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Object.h" #include "Object.h"
#include "Platform.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "ProvideCurses.h" #include "ProvideCurses.h"
#include "XUtils.h" #include "XUtils.h"
@ -24,7 +27,7 @@ ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const
ScreenManager* this; ScreenManager* this;
this = xMalloc(sizeof(ScreenManager)); this = xMalloc(sizeof(ScreenManager));
this->x1 = 0; this->x1 = 0;
this->y1 = header->height; this->y1 = 0;
this->x2 = 0; this->x2 = 0;
this->y2 = -1; this->y2 = -1;
this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE); this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE);
@ -32,7 +35,6 @@ ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const
this->header = header; this->header = header;
this->settings = settings; this->settings = settings;
this->state = state; this->state = state;
this->owner = owner;
this->allowFocusChange = true; this->allowFocusChange = true;
return this; return this;
} }
@ -42,23 +44,23 @@ void ScreenManager_delete(ScreenManager* this) {
free(this); free(this);
} }
inline int ScreenManager_size(ScreenManager* this) { inline int ScreenManager_size(const ScreenManager* this) {
return this->panelCount; return this->panelCount;
} }
void ScreenManager_add(ScreenManager* this, Panel* item, int size) { void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
int lastX = 0; int lastX = 0;
if (this->panelCount > 0) { if (this->panelCount > 0) {
Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1); const Panel* last = (const 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->header ? this->header->height : 0) + this->y2;
if (size > 0) { if (size > 0) {
Panel_resize(item, size, height); Panel_resize(item, size, height);
} else { } else {
Panel_resize(item, COLS - this->x1 + this->x2 - lastX, height); Panel_resize(item, COLS - this->x1 + this->x2 - lastX, height);
} }
Panel_move(item, lastX, this->y1); Panel_move(item, lastX, this->y1 + (this->header ? this->header->height : 0));
Vector_add(this->panels, item); Vector_add(this->panels, item);
item->needsRedraw = true; item->needsRedraw = true;
this->panelCount++; this->panelCount++;
@ -71,30 +73,26 @@ Panel* ScreenManager_remove(ScreenManager* this, int idx) {
return panel; return panel;
} }
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) { void ScreenManager_resize(ScreenManager* this) {
this->x1 = x1; int y1_header = this->y1 + (this->header ? this->header->height : 0);
this->y1 = y1;
this->x2 = x2;
this->y2 = y2;
int panels = this->panelCount; int panels = this->panelCount;
int lastX = 0; int lastX = 0;
for (int i = 0; i < panels - 1; i++) { for (int i = 0; i < panels - 1; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i); Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_resize(panel, panel->w, LINES - y1 + y2); Panel_resize(panel, panel->w, LINES - y1_header + this->y2);
Panel_move(panel, lastX, y1); Panel_move(panel, lastX, y1_header);
lastX = panel->x + panel->w + 1; lastX = panel->x + panel->w + 1;
} }
Panel* panel = (Panel*) Vector_get(this->panels, panels - 1); Panel* panel = (Panel*) Vector_get(this->panels, panels - 1);
Panel_resize(panel, COLS - x1 + x2 - lastX, LINES - y1 + y2); Panel_resize(panel, COLS - this->x1 + this->x2 - lastX, LINES - y1_header + this->y2);
Panel_move(panel, lastX, y1); Panel_move(panel, lastX, y1_header);
} }
static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut) { static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut) {
ProcessList* pl = this->header->pl; ProcessList* pl = this->header->pl;
struct timeval tv; Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs);
gettimeofday(&tv, NULL); double newTime = ((double)pl->realtime.tv_sec * 10) + ((double)pl->realtime.tv_usec / 100000);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
*timedOut = (newTime - *oldTime > this->settings->delay); *timedOut = (newTime - *oldTime > this->settings->delay);
*rescan |= *timedOut; *rescan |= *timedOut;
@ -105,7 +103,10 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) { if (*rescan) {
*oldTime = newTime; *oldTime = newTime;
// scan processes first - some header values are calculated there
ProcessList_scan(pl, this->state->pauseProcessUpdate); ProcessList_scan(pl, this->state->pauseProcessUpdate);
// always update header, especially to avoid gaps in graph meters
Header_updateData(this->header);
if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) { if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) {
ProcessList_sort(pl); ProcessList_sort(pl);
*sortTimeout = 1; *sortTimeout = 1;
@ -123,7 +124,11 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_
const int nPanels = this->panelCount; const int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) { for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i); Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_draw(panel, force_redraw, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection), State_hideFunctionBar(this->state)); Panel_draw(panel,
force_redraw,
i == focus,
panel != (Panel*)this->state->mainPanel || !this->state->hideProcessSelection,
State_hideFunctionBar(this->state));
mvvline(panel->y, panel->x + panel->w, ' ', panel->h + (State_hideFunctionBar(this->state) ? 1 : 0)); mvvline(panel->y, panel->x + panel->w, ' ', panel->h + (State_hideFunctionBar(this->state) ? 1 : 0));
} }
} }
@ -157,10 +162,13 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
} }
int prevCh = ch; int prevCh = ch;
#ifdef HAVE_SET_ESCDELAY
set_escdelay(25); set_escdelay(25);
#endif
ch = getch(); ch = getch();
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
#ifdef HAVE_GETMOUSE
if (ch == KEY_MOUSE && this->settings->enableMouse) { if (ch == KEY_MOUSE && this->settings->enableMouse) {
ch = ERR; ch = ERR;
MEVENT mevent; MEVENT mevent;
@ -181,7 +189,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (panel == panelFocus || this->allowFocusChange) { if (panel == panelFocus || this->allowFocusChange) {
focus = i; focus = i;
panelFocus = panel; panelFocus = panel;
Object* oldSelection = Panel_getSelected(panel); const 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) {
ch = KEY_RECLICK; ch = KEY_RECLICK;
@ -201,8 +209,10 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
} }
} }
} }
#endif
if (ch == ERR) { if (ch == ERR) {
sortTimeout--; if (sortTimeout > 0)
sortTimeout--;
if (prevCh == ch && !timedOut) { if (prevCh == ch && !timedOut) {
closeTimeout++; closeTimeout++;
if (closeTimeout == 100) { if (closeTimeout == 100) {
@ -233,6 +243,10 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (result & REDRAW) { if (result & REDRAW) {
force_redraw = true; force_redraw = true;
} }
if (result & RESIZE) {
ScreenManager_resize(this);
force_redraw = true;
}
if (result & RESCAN) { if (result & RESCAN) {
rescan = true; rescan = true;
sortTimeout = 0; sortTimeout = 0;
@ -247,7 +261,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
switch (ch) { switch (ch) {
case KEY_RESIZE: case KEY_RESIZE:
{ {
ScreenManager_resize(this, this->x1, this->y1, this->x2, this->y2); ScreenManager_resize(this);
continue; continue;
} }
case KEY_LEFT: case KEY_LEFT:

View File

@ -26,7 +26,6 @@ typedef struct ScreenManager_ {
Header* header; Header* header;
const Settings* settings; const Settings* settings;
const State* state; const State* state;
bool owner;
bool allowFocusChange; bool allowFocusChange;
} ScreenManager; } ScreenManager;
@ -34,13 +33,13 @@ ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const
void ScreenManager_delete(ScreenManager* this); void ScreenManager_delete(ScreenManager* this);
int ScreenManager_size(ScreenManager* this); int ScreenManager_size(const ScreenManager* this);
void ScreenManager_add(ScreenManager* this, Panel* item, int size); void ScreenManager_add(ScreenManager* this, Panel* item, int size);
Panel* ScreenManager_remove(ScreenManager* this, int idx); Panel* ScreenManager_remove(ScreenManager* this, int idx);
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2); void ScreenManager_resize(ScreenManager* this);
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey); void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey);

View File

@ -7,12 +7,17 @@ in the source distribution for its full text.
#include "Settings.h" #include "Settings.h"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "CRT.h" #include "CRT.h"
#include "DynamicColumn.h"
#include "Macros.h" #include "Macros.h"
#include "Meter.h" #include "Meter.h"
#include "Platform.h" #include "Platform.h"
@ -22,21 +27,27 @@ in the source distribution for its full text.
void Settings_delete(Settings* this) { void Settings_delete(Settings* this) {
free(this->filename); free(this->filename);
free(this->fields); free(this->fields);
for (unsigned int i = 0; i < ARRAYSIZE(this->columns); i++) { for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
String_freeArray(this->columns[i].names); if (this->hColumns[i].names) {
free(this->columns[i].modes); for (uint8_t j = 0; j < this->hColumns[i].len; j++)
free(this->hColumns[i].names[j]);
free(this->hColumns[i].names);
}
free(this->hColumns[i].modes);
} }
free(this->hColumns);
free(this); free(this);
} }
static void Settings_readMeters(Settings* this, char* line, int column) { static void Settings_readMeters(Settings* this, const char* line, unsigned int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
char** ids = String_split(trim, ' ', NULL); char** ids = String_split(trim, ' ', NULL);
free(trim); free(trim);
this->columns[column].names = ids; column = MINIMUM(column, HeaderLayout_getColumns(this->hLayout) - 1);
this->hColumns[column].names = ids;
} }
static void Settings_readMeterModes(Settings* this, char* line, int column) { static void Settings_readMeterModes(Settings* this, const char* line, unsigned int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
char** ids = String_split(trim, ' ', NULL); char** ids = String_split(trim, ' ', NULL);
free(trim); free(trim);
@ -44,86 +55,121 @@ static void Settings_readMeterModes(Settings* this, char* line, int column) {
for (int i = 0; ids[i]; i++) { for (int i = 0; ids[i]; i++) {
len++; len++;
} }
this->columns[column].len = len; column = MINIMUM(column, HeaderLayout_getColumns(this->hLayout) - 1);
int* modes = xCalloc(len, sizeof(int)); this->hColumns[column].len = len;
int* modes = len ? xCalloc(len, sizeof(int)) : NULL;
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->columns[column].modes = modes; this->hColumns[column].modes = modes;
} }
static void Settings_defaultMeters(Settings* this, int initialCpuCount) { static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount) {
int sizes[] = { 3, 3 }; int sizes[] = { 3, 3 };
if (initialCpuCount > 4) { if (initialCpuCount > 4 && initialCpuCount <= 128) {
sizes[1]++; sizes[1]++;
} }
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*)); this->hColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->columns[i].modes = xCalloc(sizes[i], sizeof(int)); this->hColumns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i]; this->hColumns[i].len = sizes[i];
} }
int r = 0; int r = 0;
if (initialCpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2"); if (initialCpuCount > 128) {
this->columns[0].modes[0] = BAR_METERMODE; // Just show the average, ricers need to config for impressive screenshots
this->columns[1].names[r] = xStrdup("RightCPUs2"); this->hColumns[0].names[0] = xStrdup("CPU");
this->columns[1].modes[r++] = BAR_METERMODE; this->hColumns[0].modes[0] = BAR_METERMODE;
} else if (initialCpuCount > 32) {
this->hColumns[0].names[0] = xStrdup("LeftCPUs8");
this->hColumns[0].modes[0] = BAR_METERMODE;
this->hColumns[1].names[r] = xStrdup("RightCPUs8");
this->hColumns[1].modes[r++] = BAR_METERMODE;
} else if (initialCpuCount > 16) {
this->hColumns[0].names[0] = xStrdup("LeftCPUs4");
this->hColumns[0].modes[0] = BAR_METERMODE;
this->hColumns[1].names[r] = xStrdup("RightCPUs4");
this->hColumns[1].modes[r++] = BAR_METERMODE;
} else if (initialCpuCount > 8) {
this->hColumns[0].names[0] = xStrdup("LeftCPUs2");
this->hColumns[0].modes[0] = BAR_METERMODE;
this->hColumns[1].names[r] = xStrdup("RightCPUs2");
this->hColumns[1].modes[r++] = BAR_METERMODE;
} else if (initialCpuCount > 4) { } else if (initialCpuCount > 4) {
this->columns[0].names[0] = xStrdup("LeftCPUs"); this->hColumns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE; this->hColumns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs"); this->hColumns[1].names[r] = xStrdup("RightCPUs");
this->columns[1].modes[r++] = BAR_METERMODE; this->hColumns[1].modes[r++] = BAR_METERMODE;
} else { } else {
this->columns[0].names[0] = xStrdup("AllCPUs"); this->hColumns[0].names[0] = xStrdup("AllCPUs");
this->columns[0].modes[0] = BAR_METERMODE; this->hColumns[0].modes[0] = BAR_METERMODE;
} }
this->columns[0].names[1] = xStrdup("Memory"); this->hColumns[0].names[1] = xStrdup("Memory");
this->columns[0].modes[1] = BAR_METERMODE; this->hColumns[0].modes[1] = BAR_METERMODE;
this->columns[0].names[2] = xStrdup("Swap"); this->hColumns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE; this->hColumns[0].modes[2] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("Tasks"); this->hColumns[1].names[r] = xStrdup("Tasks");
this->columns[1].modes[r++] = TEXT_METERMODE; this->hColumns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("LoadAverage"); this->hColumns[1].names[r] = xStrdup("LoadAverage");
this->columns[1].modes[r++] = TEXT_METERMODE; this->hColumns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("Uptime"); this->hColumns[1].names[r] = xStrdup("Uptime");
this->columns[1].modes[r++] = TEXT_METERMODE; this->hColumns[1].modes[r++] = TEXT_METERMODE;
} }
static void readFields(ProcessField* fields, uint32_t* flags, const char* line) { static void Settings_readFields(Settings* settings, const char* line) {
char* trim = String_trim(line); char* trim = String_trim(line);
char** ids = String_split(trim, ' ', NULL); char** ids = String_split(trim, ' ', NULL);
free(trim); free(trim);
int i, j;
*flags = 0; settings->flags = 0;
for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i]; i++) {
unsigned int i, j;
for (j = 0, i = 0; ids[i]; i++) {
if (j >= UINT_MAX / sizeof(ProcessField))
continue;
if (j >= LAST_PROCESSFIELD) {
settings->fields = xRealloc(settings->fields, j * sizeof(ProcessField));
memset(&settings->fields[j], 0, sizeof(ProcessField));
}
// Dynamically-defined columns are always stored by-name.
char dynamic[32] = {0};
if (sscanf(ids[i], "Dynamic(%30s)", dynamic)) {
char* end;
if ((end = strrchr(dynamic, ')')) == NULL)
continue;
*end = '\0';
unsigned int key;
if (!DynamicColumn_search(settings->dynamicColumns, dynamic, &key))
continue;
settings->fields[j++] = key;
continue;
}
// This "+1" is for compatibility with the older enum format. // This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1; int id = atoi(ids[i]) + 1;
if (id > 0 && id < LAST_PROCESSFIELD && Process_fields[id].name) { if (id > 0 && id < LAST_PROCESSFIELD && Process_fields[id].name) {
fields[j] = id; settings->flags |= Process_fields[id].flags;
*flags |= Process_fields[id].flags; settings->fields[j++] = id;
j++;
} }
} }
fields[j] = NULL_PROCESSFIELD; settings->fields[j] = NULL_PROCESSFIELD;
String_freeArray(ids); String_freeArray(ids);
} }
static bool Settings_read(Settings* this, const char* fileName, int initialCpuCount) { static bool Settings_read(Settings* this, const char* fileName, unsigned int initialCpuCount) {
FILE* fd; FILE* fd = fopen(fileName, "r");
CRT_dropPrivileges();
fd = fopen(fileName, "r");
CRT_restorePrivileges();
if (!fd) if (!fd)
return false; return false;
bool didReadMeters = false; bool didReadMeters = false;
bool didReadFields = false; bool didReadAny = false;
for (;;) { for (;;) {
char* line = String_readLine(fd); char* line = String_readLine(fd);
if (!line) { if (!line) {
break; break;
} }
didReadAny = true;
size_t nOptions; size_t nOptions;
char** option = String_split(line, '=', &nOptions); char** option = String_split(line, '=', &nOptions);
free (line); free (line);
@ -131,9 +177,18 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
String_freeArray(option); String_freeArray(option);
continue; continue;
} }
if (String_eq(option[0], "fields")) { if (String_eq(option[0], "config_reader_min_version")) {
readFields(this->fields, &(this->flags), option[1]); this->config_version = atoi(option[1]);
didReadFields = true; if (this->config_version > CONFIG_READER_MIN_VERSION) {
// the version of the config file on disk is newer than what we can read
fprintf(stderr, "WARNING: %s specifies configuration format version v%d, but this %s binary supports up to v%d.", fileName, this->config_version, PACKAGE, CONFIG_READER_MIN_VERSION);
fprintf(stderr, " The configuration version will be downgraded to v%d when %s exits.\n", CONFIG_READER_MIN_VERSION, PACKAGE);
String_freeArray(option);
fclose(fd);
return false;
}
} else if (String_eq(option[0], "fields")) {
Settings_readFields(this, option[1]);
} else if (String_eq(option[0], "sort_key")) { } else if (String_eq(option[0], "sort_key")) {
// This "+1" is for compatibility with the older enum format. // This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1; this->sortKey = atoi(option[1]) + 1;
@ -148,6 +203,8 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
this->treeView = atoi(option[1]); this->treeView = atoi(option[1]);
} else if (String_eq(option[0], "tree_view_always_by_pid")) { } else if (String_eq(option[0], "tree_view_always_by_pid")) {
this->treeViewAlwaysByPID = atoi(option[1]); this->treeViewAlwaysByPID = atoi(option[1]);
} else if (String_eq(option[0], "all_branches_collapsed")) {
this->allBranchesCollapsed = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) { } else if (String_eq(option[0], "hide_kernel_threads")) {
this->hideKernelThreads = atoi(option[1]); this->hideKernelThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_userland_threads")) { } else if (String_eq(option[0], "hide_userland_threads")) {
@ -160,6 +217,8 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
this->showProgramPath = atoi(option[1]); this->showProgramPath = atoi(option[1]);
} else if (String_eq(option[0], "highlight_base_name")) { } else if (String_eq(option[0], "highlight_base_name")) {
this->highlightBaseName = atoi(option[1]); this->highlightBaseName = atoi(option[1]);
} else if (String_eq(option[0], "highlight_deleted_exe")) {
this->highlightDeletedExe = atoi(option[1]);
} else if (String_eq(option[0], "highlight_megabytes")) { } else if (String_eq(option[0], "highlight_megabytes")) {
this->highlightMegabytes = atoi(option[1]); this->highlightMegabytes = atoi(option[1]);
} else if (String_eq(option[0], "highlight_threads")) { } else if (String_eq(option[0], "highlight_threads")) {
@ -167,7 +226,7 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
} else if (String_eq(option[0], "highlight_changes")) { } else if (String_eq(option[0], "highlight_changes")) {
this->highlightChanges = atoi(option[1]); this->highlightChanges = atoi(option[1]);
} else if (String_eq(option[0], "highlight_changes_delay_secs")) { } else if (String_eq(option[0], "highlight_changes_delay_secs")) {
this->highlightDelaySecs = CLAMP(atoi(option[1]), 1, 24*60*60); this->highlightDelaySecs = CLAMP(atoi(option[1]), 1, 24 * 60 * 60);
} else if (String_eq(option[0], "find_comm_in_cmdline")) { } else if (String_eq(option[0], "find_comm_in_cmdline")) {
this->findCommInCmdline = atoi(option[1]); this->findCommInCmdline = atoi(option[1]);
} else if (String_eq(option[0], "strip_exe_from_cmdline")) { } else if (String_eq(option[0], "strip_exe_from_cmdline")) {
@ -190,7 +249,7 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
this->showCPUUsage = atoi(option[1]); this->showCPUUsage = atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_frequency")) { } else if (String_eq(option[0], "show_cpu_frequency")) {
this->showCPUFrequency = atoi(option[1]); this->showCPUFrequency = atoi(option[1]);
#ifdef HAVE_SENSORS_SENSORS_H #ifdef BUILD_WITH_CPU_TEMP
} else if (String_eq(option[0], "show_cpu_temperature")) { } else if (String_eq(option[0], "show_cpu_temperature")) {
this->showCPUTemperature = atoi(option[1]); this->showCPUTemperature = atoi(option[1]);
} else if (String_eq(option[0], "degree_fahrenheit")) { } else if (String_eq(option[0], "degree_fahrenheit")) {
@ -207,8 +266,16 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) { if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) {
this->colorScheme = 0; this->colorScheme = 0;
} }
#ifdef HAVE_GETMOUSE
} else if (String_eq(option[0], "enable_mouse")) { } else if (String_eq(option[0], "enable_mouse")) {
this->enableMouse = atoi(option[1]); this->enableMouse = atoi(option[1]);
#endif
} else if (String_eq(option[0], "header_layout")) {
this->hLayout = isdigit((unsigned char)option[1][0]) ? ((HeaderLayout) atoi(option[1])) : HeaderLayout_fromName(option[1]);
if (this->hLayout < 0 || this->hLayout >= LAST_HEADER_LAYOUT)
this->hLayout = HF_TWO_50_50;
free(this->hColumns);
this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting));
} 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;
@ -221,6 +288,12 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
} else if (String_eq(option[0], "right_meter_modes")) { } else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1); Settings_readMeterModes(this, option[1], 1);
didReadMeters = true; didReadMeters = true;
} else if (String_startsWith(option[0], "column_meters_")) {
Settings_readMeters(this, option[1], atoi(option[0] + strlen("column_meters_")));
didReadMeters = true;
} else if (String_startsWith(option[0], "column_meter_modes_")) {
Settings_readMeterModes(this, option[1], atoi(option[0] + strlen("column_meter_modes_")));
didReadMeters = true;
} else if (String_eq(option[0], "hide_function_bar")) { } else if (String_eq(option[0], "hide_function_bar")) {
this->hideFunctionBar = atoi(option[1]); this->hideFunctionBar = atoi(option[1]);
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
@ -234,116 +307,160 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
if (!didReadMeters) { if (!didReadMeters) {
Settings_defaultMeters(this, initialCpuCount); Settings_defaultMeters(this, initialCpuCount);
} }
return didReadFields; return didReadAny;
} }
static void writeFields(FILE* fd, ProcessField* fields, const char* name) { static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns, const char* name, char separator) {
fprintf(fd, "%s=", name); fprintf(fd, "%s=", name);
const char* sep = ""; const char* sep = "";
for (int i = 0; fields[i]; i++) { for (unsigned int i = 0; fields[i]; i++) {
// This "-1" is for compatibility with the older enum format. if (fields[i] >= LAST_PROCESSFIELD) {
fprintf(fd, "%s%d", sep, (int) fields[i] - 1); const DynamicColumn* column = DynamicColumn_lookup(columns, fields[i]);
fprintf(fd, "%sDynamic(%s)", sep, column->name);
} 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"); fputc(separator, fd);
} }
static void writeMeters(Settings* this, FILE* fd, int column) { static void writeMeters(const Settings* this, FILE* fd, char separator, unsigned int column) {
const char* sep = ""; const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) { for (uint8_t i = 0; i < this->hColumns[column].len; i++) {
fprintf(fd, "%s%s", sep, this->columns[column].names[i]); fprintf(fd, "%s%s", sep, this->hColumns[column].names[i]);
sep = " "; sep = " ";
} }
fprintf(fd, "\n"); fputc(separator, fd);
} }
static void writeMeterModes(Settings* this, FILE* fd, int column) { static void writeMeterModes(const Settings* this, FILE* fd, char separator, unsigned int column) {
const char* sep = ""; const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) { for (uint8_t i = 0; i < this->hColumns[column].len; i++) {
fprintf(fd, "%s%d", sep, this->columns[column].modes[i]); fprintf(fd, "%s%d", sep, this->hColumns[column].modes[i]);
sep = " "; sep = " ";
} }
fprintf(fd, "\n"); fputc(separator, fd);
} }
bool Settings_write(Settings* this) { int Settings_write(const Settings* this, bool onCrash) {
FILE* fd; FILE* fd;
char separator;
CRT_dropPrivileges(); if (onCrash) {
fd = fopen(this->filename, "w"); fd = stderr;
CRT_restorePrivileges(); separator = ';';
} else {
if (fd == NULL) { fd = fopen(this->filename, "w");
return false; if (fd == NULL)
return -errno;
separator = '\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"); #define printSettingInteger(setting_, value_) \
writeFields(fd, this->fields, "fields"); fprintf(fd, setting_ "=%d%c", (int) (value_), separator)
#define printSettingString(setting_, value_) \
fprintf(fd, setting_ "=%s%c", value_, separator)
if (!onCrash) {
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
}
printSettingString("htop_version", VERSION);
printSettingInteger("config_reader_min_version", CONFIG_READER_MIN_VERSION);
writeFields(fd, this->fields, this->dynamicColumns, "fields", separator);
// This "-1" is for compatibility with the older enum format. // This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1); printSettingInteger("sort_key", this->sortKey - 1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction); printSettingInteger("sort_direction", this->direction);
fprintf(fd, "tree_sort_key=%d\n", (int) this->treeSortKey - 1); printSettingInteger("tree_sort_key", this->treeSortKey - 1);
fprintf(fd, "tree_sort_direction=%d\n", (int) this->treeDirection); printSettingInteger("tree_sort_direction", this->treeDirection);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads); printSettingInteger("hide_kernel_threads", this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads); printSettingInteger("hide_userland_threads", this->hideUserlandThreads);
fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers); printSettingInteger("shadow_other_users", this->shadowOtherUsers);
fprintf(fd, "show_thread_names=%d\n", (int) this->showThreadNames); printSettingInteger("show_thread_names", this->showThreadNames);
fprintf(fd, "show_program_path=%d\n", (int) this->showProgramPath); printSettingInteger("show_program_path", this->showProgramPath);
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName); printSettingInteger("highlight_base_name", this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes); printSettingInteger("highlight_deleted_exe", this->highlightDeletedExe);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads); printSettingInteger("highlight_megabytes", this->highlightMegabytes);
fprintf(fd, "highlight_changes=%d\n", (int) this->highlightChanges); printSettingInteger("highlight_threads", this->highlightThreads);
fprintf(fd, "highlight_changes_delay_secs=%d\n", (int) this->highlightDelaySecs); printSettingInteger("highlight_changes", this->highlightChanges);
fprintf(fd, "find_comm_in_cmdline=%d\n", (int) this->findCommInCmdline); printSettingInteger("highlight_changes_delay_secs", this->highlightDelaySecs);
fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline); printSettingInteger("find_comm_in_cmdline", this->findCommInCmdline);
fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand); printSettingInteger("strip_exe_from_cmdline", this->stripExeFromCmdline);
fprintf(fd, "tree_view=%d\n", (int) this->treeView); printSettingInteger("show_merged_command", this->showMergedCommand);
fprintf(fd, "tree_view_always_by_pid=%d\n", (int) this->treeViewAlwaysByPID); printSettingInteger("tree_view", this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); printSettingInteger("tree_view_always_by_pid", this->treeViewAlwaysByPID);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); printSettingInteger("all_branches_collapsed", this->allBranchesCollapsed);
fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne); printSettingInteger("header_margin", this->headerMargin);
fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage); printSettingInteger("detailed_cpu_time", this->detailedCPUTime);
fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency); printSettingInteger("cpu_count_from_one", this->countCPUsFromOne);
#ifdef HAVE_SENSORS_SENSORS_H printSettingInteger("show_cpu_usage", this->showCPUUsage);
fprintf(fd, "show_cpu_temperature=%d\n", (int) this->showCPUTemperature); printSettingInteger("show_cpu_frequency", this->showCPUFrequency);
fprintf(fd, "degree_fahrenheit=%d\n", (int) this->degreeFahrenheit); #ifdef BUILD_WITH_CPU_TEMP
printSettingInteger("show_cpu_temperature", this->showCPUTemperature);
printSettingInteger("degree_fahrenheit", this->degreeFahrenheit);
#endif #endif
fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames); printSettingInteger("update_process_names", this->updateProcessNames);
fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter); printSettingInteger("account_guest_in_cpu_meter", this->accountGuestInCPUMeter);
fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme); printSettingInteger("color_scheme", this->colorScheme);
fprintf(fd, "enable_mouse=%d\n", (int) this->enableMouse); #ifdef HAVE_GETMOUSE
fprintf(fd, "delay=%d\n", (int) this->delay); printSettingInteger("enable_mouse", this->enableMouse);
fprintf(fd, "left_meters="); writeMeters(this, fd, 0); #endif
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0); printSettingInteger("delay", (int) this->delay);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1); printSettingInteger("hide_function_bar", (int) this->hideFunctionBar);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
fprintf(fd, "hide_function_bar=%d\n", (int) this->hideFunctionBar);
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity); printSettingInteger("topology_affinity", this->topologyAffinity);
#endif #endif
fclose(fd);
return true; printSettingString("header_layout", HeaderLayout_getName(this->hLayout));
for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
fprintf(fd, "column_meters_%u=", i);
writeMeters(this, fd, separator, i);
fprintf(fd, "column_meter_modes_%u=", i);
writeMeterModes(this, fd, separator, i);
}
#undef printSettingString
#undef printSettingInteger
if (onCrash)
return 0;
int r = 0;
if (ferror(fd) != 0)
r = (errno != 0) ? -errno : -EBADF;
if (fclose(fd) != 0)
r = r ? r : -errno;
return r;
} }
Settings* Settings_new(int initialCpuCount) { Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns) {
Settings* this = xCalloc(1, sizeof(Settings)); Settings* this = xCalloc(1, sizeof(Settings));
this->dynamicColumns = dynamicColumns;
this->hLayout = HF_TWO_50_50;
this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting));
this->sortKey = PERCENT_CPU; this->sortKey = PERCENT_CPU;
this->treeSortKey = PID; this->treeSortKey = PID;
this->direction = 1; this->direction = -1;
this->treeDirection = 1; this->treeDirection = 1;
this->shadowOtherUsers = false; this->shadowOtherUsers = false;
this->showThreadNames = false; this->showThreadNames = false;
this->hideKernelThreads = false; this->hideKernelThreads = true;
this->hideUserlandThreads = false; this->hideUserlandThreads = false;
this->treeView = false; this->treeView = false;
this->allBranchesCollapsed = false;
this->highlightBaseName = false; this->highlightBaseName = false;
this->highlightMegabytes = false; this->highlightDeletedExe = true;
this->highlightMegabytes = true;
this->detailedCPUTime = false; this->detailedCPUTime = false;
this->countCPUsFromOne = false; this->countCPUsFromOne = false;
this->showCPUUsage = true; this->showCPUUsage = true;
this->showCPUFrequency = false; this->showCPUFrequency = false;
#ifdef HAVE_SENSORS_SENSORS_H #ifdef BUILD_WITH_CPU_TEMP
this->showCPUTemperature = false; this->showCPUTemperature = false;
this->degreeFahrenheit = false; this->degreeFahrenheit = false;
#endif #endif
@ -356,6 +473,7 @@ Settings* Settings_new(int initialCpuCount) {
this->stripExeFromCmdline = true; this->stripExeFromCmdline = true;
this->showMergedCommand = false; this->showMergedCommand = false;
this->hideFunctionBar = 0; this->hideFunctionBar = 0;
this->headerMargin = true;
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
this->topologyAffinity = false; this->topologyAffinity = false;
#endif #endif
@ -370,7 +488,7 @@ Settings* Settings_new(int initialCpuCount) {
} }
char* legacyDotfile = NULL; char* legacyDotfile = NULL;
char* rcfile = getenv("HTOPRC"); const char* rcfile = getenv("HTOPRC");
if (rcfile) { if (rcfile) {
this->filename = xStrdup(rcfile); this->filename = xStrdup(rcfile);
} else { } else {
@ -391,7 +509,6 @@ Settings* Settings_new(int initialCpuCount) {
htopDir = String_cat(home, "/.config/htop"); htopDir = String_cat(home, "/.config/htop");
} }
legacyDotfile = String_cat(home, "/.htoprc"); legacyDotfile = String_cat(home, "/.htoprc");
CRT_dropPrivileges();
(void) mkdir(configDir, 0700); (void) mkdir(configDir, 0700);
(void) mkdir(htopDir, 0700); (void) mkdir(htopDir, 0700);
free(htopDir); free(htopDir);
@ -402,10 +519,11 @@ Settings* Settings_new(int initialCpuCount) {
free(legacyDotfile); free(legacyDotfile);
legacyDotfile = NULL; legacyDotfile = NULL;
} }
CRT_restorePrivileges();
} }
this->colorScheme = 0; this->colorScheme = 0;
#ifdef HAVE_GETMOUSE
this->enableMouse = true; this->enableMouse = true;
#endif
this->changed = false; this->changed = false;
this->delay = DEFAULT_DELAY; this->delay = DEFAULT_DELAY;
bool ok = false; bool ok = false;
@ -413,7 +531,7 @@ Settings* Settings_new(int initialCpuCount) {
ok = Settings_read(this, legacyDotfile, initialCpuCount); ok = Settings_read(this, legacyDotfile, initialCpuCount);
if (ok) { if (ok) {
// Transition to new location and delete old configuration file // Transition to new location and delete old configuration file
if (Settings_write(this)) { if (Settings_write(this, false) == 0) {
unlink(legacyDotfile); unlink(legacyDotfile);
} }
} }
@ -424,20 +542,10 @@ Settings* Settings_new(int initialCpuCount) {
} }
if (!ok) { if (!ok) {
this->changed = true; this->changed = true;
// TODO: how to get SYSCONFDIR correctly through Autoconf? Settings_read(this, SYSCONFDIR "/htoprc", initialCpuCount);
char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
ok = Settings_read(this, systemSettings, initialCpuCount);
free(systemSettings);
} }
if (!ok) { if (!ok) {
Settings_defaultMeters(this, initialCpuCount); Settings_defaultMeters(this, initialCpuCount);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
this->findCommInCmdline = true;
this->stripExeFromCmdline = true;
this->showMergedCommand = false;
this->headerMargin = true;
} }
return this; return this;
} }
@ -450,10 +558,43 @@ void Settings_invertSortOrder(Settings* this) {
void Settings_setSortKey(Settings* this, ProcessField sortKey) { void Settings_setSortKey(Settings* this, ProcessField sortKey) {
if (this->treeViewAlwaysByPID || !this->treeView) { if (this->treeViewAlwaysByPID || !this->treeView) {
this->sortKey = sortKey; this->sortKey = sortKey;
this->direction = 1; this->direction = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1;
this->treeView = false; this->treeView = false;
} else { } else {
this->treeSortKey = sortKey; this->treeSortKey = sortKey;
this->treeDirection = 1; this->treeDirection = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1;
} }
} }
static bool readonly = false;
void Settings_enableReadonly(void) {
readonly = true;
}
bool Settings_isReadonly(void) {
return readonly;
}
void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout) {
unsigned int oldColumns = HeaderLayout_getColumns(this->hLayout);
unsigned int newColumns = HeaderLayout_getColumns(hLayout);
if (newColumns > oldColumns) {
this->hColumns = xReallocArray(this->hColumns, newColumns, sizeof(MeterColumnSetting));
memset(this->hColumns + oldColumns, 0, (newColumns - oldColumns) * sizeof(MeterColumnSetting));
} else if (newColumns < oldColumns) {
for (unsigned int i = newColumns; i < oldColumns; i++) {
if (this->hColumns[i].names) {
for (uint8_t j = 0; j < this->hColumns[i].len; j++)
free(this->hColumns[i].names[j]);
free(this->hColumns[i].names);
}
free(this->hColumns[i].modes);
}
this->hColumns = xReallocArray(this->hColumns, newColumns, sizeof(MeterColumnSetting));
}
this->hLayout = hLayout;
this->changed = true;
}

View File

@ -12,20 +12,27 @@ in the source distribution for its full text.
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "Hashtable.h"
#include "HeaderLayout.h"
#include "Process.h" #include "Process.h"
#define DEFAULT_DELAY 15 #define DEFAULT_DELAY 15
#define CONFIG_READER_MIN_VERSION 2
typedef struct { typedef struct {
int len; uint8_t len;
char** names; char** names;
int* modes; int* modes;
} MeterColumnSettings; } MeterColumnSetting;
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* filename;
MeterColumnSettings columns[2]; int config_version;
HeaderLayout hLayout;
MeterColumnSetting* hColumns;
Hashtable* dynamicColumns;
ProcessField* fields; ProcessField* fields;
uint32_t flags; uint32_t flags;
@ -41,18 +48,20 @@ typedef struct Settings_ {
bool detailedCPUTime; bool detailedCPUTime;
bool showCPUUsage; bool showCPUUsage;
bool showCPUFrequency; bool showCPUFrequency;
#ifdef HAVE_SENSORS_SENSORS_H #ifdef BUILD_WITH_CPU_TEMP
bool showCPUTemperature; bool showCPUTemperature;
bool degreeFahrenheit; bool degreeFahrenheit;
#endif #endif
bool treeView; bool treeView;
bool treeViewAlwaysByPID; bool treeViewAlwaysByPID;
bool allBranchesCollapsed;
bool showProgramPath; bool showProgramPath;
bool shadowOtherUsers; bool shadowOtherUsers;
bool showThreadNames; bool showThreadNames;
bool hideKernelThreads; bool hideKernelThreads;
bool hideUserlandThreads; bool hideUserlandThreads;
bool highlightBaseName; bool highlightBaseName;
bool highlightDeletedExe;
bool highlightMegabytes; bool highlightMegabytes;
bool highlightThreads; bool highlightThreads;
bool highlightChanges; bool highlightChanges;
@ -63,7 +72,9 @@ typedef struct Settings_ {
bool updateProcessNames; bool updateProcessNames;
bool accountGuestInCPUMeter; bool accountGuestInCPUMeter;
bool headerMargin; bool headerMargin;
#ifdef HAVE_GETMOUSE
bool enableMouse; bool enableMouse;
#endif
int hideFunctionBar; // 0 - off, 1 - on ESC until next input, 2 - permanently int hideFunctionBar; // 0 - off, 1 - on ESC until next input, 2 - permanently
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
bool topologyAffinity; bool topologyAffinity;
@ -86,12 +97,18 @@ static inline int Settings_getActiveDirection(const Settings* this) {
void Settings_delete(Settings* this); void Settings_delete(Settings* this);
bool Settings_write(Settings* this); int Settings_write(const Settings* this, bool onCrash);
Settings* Settings_new(int initialCpuCount); Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns);
void Settings_invertSortOrder(Settings* this); void Settings_invertSortOrder(Settings* this);
void Settings_setSortKey(Settings* this, ProcessField sortKey); void Settings_setSortKey(Settings* this, ProcessField sortKey);
void Settings_enableReadonly(void);
bool Settings_isReadonly(void);
void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout);
#endif #endif

View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include "Panel.h" #include "Panel.h"
typedef struct SignalItem_ { typedef struct SignalItem_ {
const char* name; const char* name;
int number; int number;

View File

@ -5,8 +5,13 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include "SwapMeter.h" #include "SwapMeter.h"
#include <math.h>
#include <stddef.h>
#include "CRT.h" #include "CRT.h"
#include "Object.h" #include "Object.h"
#include "Platform.h" #include "Platform.h"
@ -14,11 +19,16 @@ in the source distribution for its full text.
static const int SwapMeter_attributes[] = { static const int SwapMeter_attributes[] = {
SWAP SWAP,
SWAP_CACHE
}; };
static void SwapMeter_updateValues(Meter* this, char* buffer, size_t size) { static void SwapMeter_updateValues(Meter* this) {
char* buffer = this->txtBuffer;
size_t size = sizeof(this->txtBuffer);
int written; int written;
this->values[1] = NAN; /* 'cached' not present on all platforms */
Platform_setSwapValues(this); Platform_setSwapValues(this);
written = Meter_humanUnit(buffer, this->values[0], size); written = Meter_humanUnit(buffer, this->values[0], size);
@ -38,6 +48,12 @@ static void SwapMeter_display(const Object* cast, RichString* out) {
Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
if (!isnan(this->values[1])) {
Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:");
RichString_appendAscii(out, CRT_colors[SWAP_CACHE], buffer);
}
} }
const MeterClass SwapMeter_class = { const MeterClass SwapMeter_class = {
@ -48,7 +64,7 @@ const MeterClass SwapMeter_class = {
}, },
.updateValues = SwapMeter_updateValues, .updateValues = SwapMeter_updateValues,
.defaultMode = BAR_METERMODE, .defaultMode = BAR_METERMODE,
.maxItems = 1, .maxItems = 2,
.total = 100.0, .total = 100.0,
.attributes = SwapMeter_attributes, .attributes = SwapMeter_attributes,
.name = "Swap", .name = "Swap",

View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
extern const MeterClass SwapMeter_class; extern const MeterClass SwapMeter_class;
#endif #endif

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