395 Commits

Author SHA1 Message Date
1f9e2ded9e Update changelog for upcoming 3.0.3 release, annotate rc1 2020-12-04 14:05:27 +11:00
bd6237eb31 Document implicit incremental search 2020-12-03 22:41:31 +01:00
bc91a382f6 Allow to pass '/' for item search 2020-12-03 22:41:31 +01:00
bda07fa42b Handle 'q' as quit if first character 2020-12-03 22:41:31 +01:00
9adb94a379 Some visual code cleanup 2020-12-03 22:41:31 +01:00
5fe2a88c08 Use common handling for scrolling 2020-12-03 22:41:31 +01:00
a7955c4966 Reduce code duplication 2020-12-03 22:41:31 +01:00
c49ca61dd9 Common order for ESC/q/F10 2020-12-03 22:41:31 +01:00
4f08d2d5ad Fix sensors configure argument 2020-12-03 16:42:38 +01:00
64230ee5cd ci: use clang-11 2020-12-03 16:28:14 +01:00
2ec940e0d2 ci: use correct configure flags for sensors 2020-12-03 16:28:14 +01:00
d1db9da936 Linux: avoid float division by 0 after system sleep
linux/LinuxProcessList.c:1403:63: runtime error: division by zero
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior linux/LinuxProcessList.c:1403:63 in
2020-12-03 13:25:17 +01:00
3695cbd5d8 Silence possible NULL dereference
Found by compiling with LTO

  ProcessList.c: In function ‘ProcessList_updateTreeSetLayer’:
  ProcessList.c:195:15: error: potential null pointer dereference [-Werror=null-dereference]
    195 |       if (proc->tree_depth == deep && proc->tree_left > left && proc->tree_right < right) {
        |               ^
  ProcessList.c:195:15: error: potential null pointer dereference [-Werror=null-dereference]
  ProcessList.c:195:15: error: potential null pointer dereference [-Werror=null-dereference]
2020-12-03 12:32:54 +01:00
fe84840314 Add Linux cwd process column 2020-12-03 09:21:28 +01:00
c6b66a75ea Minor code streamlining 2020-12-02 23:50:05 +01:00
709821ff55 Some minor spelling issues 2020-12-02 23:50:05 +01:00
2d874177bc Avoid expensive build of tree when not using it 2020-12-02 23:50:05 +01:00
bd24664fc2 Avoid useless search for pid 0 2020-12-02 23:50:05 +01:00
d0e71cb75f Reorder field initialization to group fields by task 2020-12-02 23:50:05 +01:00
e3b6049043 Code style cleanup and documentation/comments 2020-12-02 23:50:05 +01:00
b4b952d78d Use common values for initial size estimates for Hashtables 2020-12-02 23:50:05 +01:00
4907d90cff Fix issue with inconsistent displayTreeSet 2020-12-02 23:50:05 +01:00
cf306ff86e Implement sorting in tree mode 2020-12-02 23:50:05 +01:00
4f7d48aa24 Set locale only once and do not override it later 2020-12-02 22:08:25 +01:00
b76eaf187a Dynamically load libsensors at runtime 2020-12-02 21:03:24 +01:00
f7a8952933 Add xReadfile wrapper for reading small to medium size files
Inspired by proposed Linux syscall

Avoid file descriptor leaks like 4af8c63f
2020-12-02 20:39:36 +01:00
1d8192c10b PressureStallMeter: improve display strings
- Shorten name for header setup menu
- Improve caption in bar mode
2020-12-02 19:51:43 +01:00
472f0124cd Meter: document MeterClass string fields 2020-12-02 19:51:43 +01:00
c0b50164dd Use String_eq for readability and consistency 2020-12-02 19:14:22 +01:00
7975cd2ca3 Add cast to unsigned char to avoid signed char misuse 2020-12-02 19:14:22 +01:00
9029cc83ad Merge identical conditional branches 2020-12-02 19:14:22 +01:00
43ee295c23 Drop redundant return statements 2020-12-02 19:14:22 +01:00
ec0f5d0ba9 Compare indices not index with pair
Fixes always true condition.

Found by LGTM.com
2020-12-02 17:54:53 +01:00
bbac4c2a62 Use enum element name instead of magic number 2020-12-02 17:52:16 +01:00
65866c69d6 Fix a little typo (spelling) in the styleguide 2020-12-01 14:15:16 +11:00
f59af39684 Merge branch 'styleguide-tweak' of https://github.com/natoscott/htop into natoscott-styleguide-tweak 2020-12-01 14:11:19 +11:00
6ab1e468ef Update docs/styleguide.md
Co-authored-by: BenBE <BenBE@geshi.org>
2020-12-01 14:05:46 +11:00
cd305b4325 Print G in gigabyte color
When printing a size like 27.2G print the G like the 27 in the gigabyte color.
2020-11-29 15:06:08 +01:00
c1563337ae Implement Hashtable_clear to empty an existing Hashtable 2020-11-29 14:54:10 +01:00
9549ca8c88 Linux: fix process parsing for hidden pid directories 2020-11-29 12:46:25 +01:00
b2a8b2426e Tweak style guide wording around single code statements
There was wording about brace-enclosing single code statements
being 'strongly encouraged' - this isn't consistently used and
IMO it introduces unnecessary noise in otherwise neat, concise
code.

I've reworded (dropped) this section and also fixed a handful
of minor typos while reading this doc a little more carefully.
2020-11-28 23:47:13 +01:00
5ee6875f73 Typo 2020-11-28 20:53:49 +01:00
a7cf6c67d6 Typo fix in docs 2020-11-28 20:47:36 +01:00
0380d0bfd5 Include documentation for COMM and EXE 2020-11-28 20:23:33 +01:00
19b5141685 Hide process selection on ESC
Do not highlight the current process line after pressing ESC in the main
screen.
Restore after pressing any key.
2020-11-28 19:49:38 +01:00
ea4f33409a Update even more snprintfs
Use size of actual buffers instead of magic numbers
2020-11-28 19:33:07 +01:00
7899ae2eb1 Replace more snprintfs, reduce buffer sizes to what is printed 2020-11-28 17:57:51 +01:00
0b29e5074c Use 'N/A' instead of 'no perm' for more consistency 2020-11-28 17:43:08 +01:00
6c306315c8 Fix reading of device nodes > 2 chars from memory maps 2020-11-28 17:06:06 +01:00
a41e5c0a80 configure: do not check functions we are using unconditionally 2020-11-28 12:35:34 +01:00
2ff2859c23 Add compat mode for systems without openat(2) 2020-11-28 12:35:34 +01:00
638207a2ff LinuxProcessList: use openat instead of building path strings
openat() is available since Linux 2.6.16
2020-11-28 12:35:34 +01:00
f704baeb82 Drop unused global ProcessList memory fields
The global ProcessList structure contains a couple of unused
fields.  'sharedMem' has never been used by any Meter, since
its not been anything other than zero in Linux /proc/meminfo
for many, many years.  The freeMem field is only used in the
usedMem calculation, so it can reside on the stack like some
other memory variables used within-calculations-only and not
exposed to the user via a Meter.
2020-11-27 07:55:58 +01:00
fee217551c Drop unneeded parameters to the ScreenManager constructor
All calls to ScreenManager_new always pass the same first
five values, the orientation is always HORIZONTAL and the
y1 parameter is always the height of the passed-in header
struct pointer.  I think its safe to assert at this point
that no VERTICAL orientation will arrive (if it does, its
no harm in re-adding this then) - so we can remove unused
conditionals (and TODOs) based on orientation too.
2020-11-26 23:55:53 +01:00
83bf8cfad6 Make casing of N/A consistent (majority was N/A) 2020-11-26 22:58:34 +01:00
2c27f1d9ab Randomly refresh M_LRS calculation, but latest after 2s 2020-11-26 22:58:34 +01:00
08d6e25301 Distinguish display of no permissions for reading M_LRS 2020-11-26 22:58:34 +01:00
31044d1729 Roll our own strtoull implementation specialized to handle the parsing requirements 2020-11-26 22:58:34 +01:00
cceab5f803 Hardcode actual conversions to read the maps file data 2020-11-26 22:58:34 +01:00
721d9112d9 Only calculate M_LRS size every 5 seconds 2020-11-26 22:58:34 +01:00
7f18b352b0 Calculate library size (M_LRS column) from maps file 2020-11-26 22:58:34 +01:00
46a2e8ac63 IOPriorityPanel: drop unnecessary buffer size decrement
xSnprintf guarantees null-termination within the passed size.
2020-11-26 20:42:38 +01:00
15fe8717b1 configure: create typedefs for fixed-sized integers if needed
If not defined in stdint.h or inttypes.h

See https://www.gnu.org/software/autoconf/manual/autoconf-2.62/html_node/Particular-Types.html
2020-11-26 20:28:38 +01:00
748f3eb7d8 Fix crash when getCommandStr not overloaded for a platform process
Closes: #343
2020-11-26 15:21:01 +01:00
d62c2e9cca LinuxProcessList_recurseProcTree: compute time only once and mark parent const 2020-11-25 22:14:35 +01:00
a6a5686388 Track file descriptors in valgrind script 2020-11-25 22:09:39 +01:00
9b31ee5b63 Drop taskstats conditional
taskstats is only checked on runtime if the column RCHAR, WCHAR, SYSCR,
SYSCW, RBYTES, WBYTES, CNCLWB, IO_READ_RATE, IO_WRITE_RATE or IO_RATE is
selected.

taskstats is currently enabled by default.

Drop the taskstats configuration switch, to reduce the maintenance cost.
2020-11-25 20:49:39 +01:00
c88c80e3bd Drop cgroup conditional
cgroup is only checked on runtime if the column CGROUP is selected.

cgroup is currently disabled by default, but most distributions do
enable it.

Drop the cgroup configuration switch, to reduce the maintenance cost.
2020-11-25 20:49:39 +01:00
267014cbfe Add support to change numeric options in settings screen
Like delay or highlightDelaySecs
2020-11-25 20:46:27 +01:00
adf9185209 Fully support non-ascii characters in Meter-Bar
Currently the code does not handle multi-byte characters, so length-
computations take the raw count of C characters and not the to displayed
size into account.

An example is the degree sign for temperatures.

Closes: #329
2020-11-25 20:45:54 +01:00
c038326a70 LinuxProcessList: fix misspelling 2020-11-25 12:49:17 +01:00
601ad61e7d Unify naming of first argument of Platform_getBattery
Use percent throughout
2020-11-25 12:47:07 +01:00
a3221f3677 Improve Fahrenheit temperature configuration text 2020-11-25 12:44:01 +01:00
1d5b0522ac ProcessLocksScreen_draw: use Process_getCommand instead of raw comm 2020-11-25 12:43:30 +01:00
4af8c63f63 Fix file descriptor leak in LinuxProcessList_readCmdlineFile after xread failure
Found by Coverity
2020-11-24 19:54:25 +01:00
21e3063e2e Include comm before cmdline if exe could not be read, but comm mismatches basename from cmdline
Also highlights entries where exe was marked deleted
2020-11-24 19:05:48 +01:00
ec36c5ccf8 Group the "Merge Command" related options visually 2020-11-24 19:05:48 +01:00
46ee28e897 Refactor command string creation
Hopefully this patch makes it a bit more approachable how it's done.
2020-11-24 19:05:48 +01:00
27b36dab1a Make kernel thread display for COMM/EXE columns less visible and more consistent 2020-11-24 19:05:48 +01:00
45cb99d870 Minor indentation fix 2020-11-24 19:05:48 +01:00
f0a232568f Reduce visual noise to when comm and cmdline actually disagree on the program basename 2020-11-24 19:05:48 +01:00
dde2af1fdb Assume full basename matches COMM when matching full COMM buffer 2020-11-24 19:05:48 +01:00
e33d4d9460 Include merge status with column title when enabled 2020-11-24 19:05:48 +01:00
be60419630 Cleanup some documentation 2020-11-24 19:05:48 +01:00
fcda517a67 Add heuristic for space-separated cmdline 2020-11-24 19:05:48 +01:00
98fce1fb43 Compatibility function for faccessat 2020-11-24 19:05:48 +01:00
09fe94da18 Improving Command display/sort 2020-11-24 19:05:48 +01:00
42c842c190 LinuxProcess_adjustTime: simplify by not using double
Does not work with -ffast-math else.
2020-11-24 17:30:21 +01:00
95f553b10c Move treeView setting to make status bar item correct when using --sort-key, patch from @cgzones
Closes #340
2020-11-24 15:53:36 +01:00
952ee9cd77 LinuxProcessList: fix misspelling 2020-11-24 11:46:17 +01:00
72df930241 DarwinProcessList: retry getting list of all processes on ENOMEM
The process count might change between the two sysctl() calls getting
the size and getting the data.

Retry (3 times) in case the data-retrieval sysctl() call fails with ENOMEM.

see http://mirror.informatimago.com/next/developer.apple.com/qa/qa2001/qa1123.html

Related: #118
2020-11-23 17:00:32 +01:00
6c2849ec81 Linux: fix display of new thread for one cycle when hidden 2020-11-23 14:44:31 +01:00
003f2c06a4 Merge branch 'cleanup-init-done' into master 2020-11-23 17:34:44 +11:00
82a69ee87a Consistent ordering of function declarations for FreeBSD 2020-11-23 17:32:57 +11:00
17eeb7573a LinuxProcessList: skip parsing threads if the kind of thread is disabled 2020-11-22 16:49:43 +01:00
be39de14dd Reduce scope of cached values 2020-11-22 14:24:18 +01:00
be568b1153 Object: assert callbacks exists
Improves stacktraces.

Current stacktrace:
  ./htop(backtrace+0x5b)[0x45d98b]
  ./htop(CRT_handleSIGSEGV+0x189)[0x4eb5e9]
  /lib/x86_64-linux-gnu/libpthread.so.0(+0x14140)[0x7fbbfb1ea140]

New:
  ./htop(backtrace+0x5b)[0x45d98b]
  ./htop(CRT_handleSIGSEGV+0x189)[0x4eb7f9]
  /lib/x86_64-linux-gnu/libpthread.so.0(+0x14140)[0x7f62b0a65140]
  /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x141)[0x7f62b089ac41]
  /lib/x86_64-linux-gnu/libc.so.6(abort+0x123)[0x7f62b0884537]
  /lib/x86_64-linux-gnu/libc.so.6(+0x2540f)[0x7f62b088440f]
  /lib/x86_64-linux-gnu/libc.so.6(+0x345c2)[0x7f62b08935c2]
  ./htop(Vector_delete+0x873)[0x54b303]
  ./htop(Panel_done+0x7b)[0x51abbb]
  ./htop[0x4ed8ee]
  ./htop(Vector_delete+0x414)[0x54aea4]
  ./htop(ScreenManager_delete+0x37)[0x536ea7]
  ./htop[0x4d9d1a]
  ./htop[0x4d5516]
  ./htop[0x5078d7]
  ./htop(ScreenManager_run+0x69f)[0x5388bf]
  ./htop(main+0x7c6)[0x4fcf76]
  /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xea)[0x7f62b0885cca]
  ./htop(_start+0x2a)[0x42688a]
2020-11-22 10:10:34 +01:00
03f9a86918 Reduce scope of local variables 2020-11-22 10:08:27 +01:00
ddda739cb2 Simplify code flow by inlining declarations where they are used
Note that xStrdup always returns non-NULL, thus the one error case cannot be reached.
2020-11-22 10:07:01 +01:00
3cb96f1a36 No need to check for change when no action is required 2020-11-22 10:04:54 +01:00
98943d595e Reduce scope of totaltime 2020-11-22 10:04:18 +01:00
51be2d5415 Fix NULL pointer dereference on kstat_lookup failure 2020-11-22 10:03:55 +01:00
d2c64c16e6 Fix build for custom make targets 2020-11-21 22:31:10 +01:00
0f4e3ebc95 Simplify page size related calculations 2020-11-21 19:39:45 +01:00
fa002c0ba9 Rename virtual memory column from M_SIZE to M_VIRT
Closes: #325
2020-11-21 19:39:45 +01:00
3e5cba91ce LinuxProcess: mark LinuxProcess_printDelay static 2020-11-21 19:26:55 +01:00
4fb82e301d fix indent 2020-11-21 19:26:42 +01:00
f752c6e2d1 Remove unnecessary parens 2020-11-21 16:07:19 +01:00
f5c3349bdb IWYU update (FreeBSD) 2020-11-19 23:51:50 +01:00
7cf5277594 IWYU update (Linux) 2020-11-19 23:51:50 +01:00
5d50f43d5f Add whitespace to improve Linux Platform_init readability 2020-11-19 19:00:00 +11:00
c75c5ef9c6 Minor cleanups to platform-specific init and done
Move platform-specific code out of the htop.c main function
and into the platform sub-directories - primarily this is
the Linux procfs path check and sensors setup/teardown; not
needed on any other platforms.  No functional changes here.
2020-11-19 12:32:07 +11:00
329011bb98 Add missing OpenBSD battery function declaration 2020-11-19 12:00:56 +11:00
0a2105eb22 Spelling corrections 2020-11-18 13:59:55 +01:00
f720868998 Align command line argument descriptions in help output
Also drop #link comment
2020-11-18 13:59:36 +01:00
0eb3c7589d Merge individual Battery.[ch] files into Platform.[ch]
Small changes from review - keep headers sorted and keep local
variable declarations at the top of source files.
2020-11-18 10:17:33 +11:00
ea9622b8c9 Merge individual Battery.[ch] files into Platform.[ch]
Consistent with everything else involving platform-specific
calls from core htop code.
2020-11-18 10:17:33 +11:00
e3af8d0d08 DarwinProcessList: mark local functions static and sort includes 2020-11-17 21:50:38 +01:00
ba2d59020d DarwinProcess: mark local function static and sort includes 2020-11-17 21:50:38 +01:00
b3b890f546 Use 0 as no-match value for sortkey
Field numbers start at 1, and using -1 as no-match special value triggers
static analyzers using a potential negative array access.
2020-11-17 13:06:31 +01:00
f38af725c2 Silence theoretical memory leak
In practice systemctl should never return multiple SystemState= lines.
2020-11-17 13:06:31 +01:00
fec9af4e6f Merge branch 'temperature_v2' of cgzones/htop
Closes #111, closes #49
Closes #93 - thank you for leading the way @DX37 (Maxim Kurnosenko)!
2020-11-17 11:05:15 +01:00
a94fd87b05 Avoid calling Object_isA from inside Vector_isConsistent 2020-11-17 08:06:02 +01:00
307c34b028 Hashtable: use dynamic growth and use primes as size
Dynamically increase the hashmap size to not exceed the load factor and
avoid too long chains.

Switch from Separate Chaining to Robin Hood linear probing to improve
cache locality.

Use primes as size to further avoid collisions.

E.g. on a standard kde system the number of entries in the ProcessTable
might be around 650.
2020-11-17 02:01:02 +01:00
7914ec201e Hashtable update
- use consistent type for key by introducing a new typedef
- use unsigned types for sizes
- name parameters in foreach function typedef
2020-11-17 02:01:02 +01:00
15eab2012d Add process column for normalized CPU usage
Shows the process CPU usage divided by the number of CPU cores
2020-11-16 18:14:06 +01:00
a8a723ffe9 Add debug state to the configure report (thanks @benbe for the idea) 2020-11-16 17:01:51 +01:00
1b225cd7a0 Show CPU temperature in CPU meter
Show the CPU temperature in the CPU meter, like CPU frequency, instead
of using an extra Meter.
2020-11-16 16:38:54 +01:00
309f1d7282 hwloc = (portable) HardWare LOCality, not related to lock 2020-11-16 13:29:37 +01:00
8bc083c6c6 Merge branch 'highlight-new-old-processes'
Thanks to @adsr for the great work
Closes #241, closes #74
Massive rebase, so #keepfingerscrossed
2020-11-16 13:19:31 +01:00
19868a3c29 Fix whitespace before comma in the new color definitions 2020-11-16 13:18:29 +01:00
8f2d129dce Apply patch from BenBE as per https://github.com/htop-dev/htop/pull/241#issuecomment-720081138 2020-11-16 12:55:32 +01:00
0951090fa4 Merge branch 'hili-new-old' of adsr/htop into highlight-new-old-processes 2020-11-16 12:55:07 +01:00
0411fdbcef Use spaceship comparison for TTYs 2020-11-15 22:54:14 +01:00
f856fe6463 Early skip non-directories when searching for process information 2020-11-15 22:54:14 +01:00
ad72b747fa Drop hideThreads Setting
It is only used to read process directories on RedHat beginning with a dot.
Unconditionally accept directories with a starting dot.
2020-11-15 22:54:14 +01:00
9f67b95308 Mark local functions static 2020-11-15 18:35:30 +01:00
91317322fe Mark ProcessList_keyAt argument const 2020-11-15 18:35:30 +01:00
42073babb9 Use uid_t type for Process_getuid 2020-11-15 18:35:30 +01:00
397b5c4bd0 Introduce spaceship comparison for Processes
If currently two unsigned values are compared via `a - b`, in the case b
is actually bigger than a, the result will not be an negative number (as
-1 is expected) but a huge positive number as the subtraction is an
unsigned subtraction.

Avoid over-/underflow affected operations; use comparisons.
Modern compilers will generate sane code, like:
    xor     eax, eax
    cmp     rdi, rsi
    seta    al
    sbb     eax, 0
    ret
2020-11-15 18:25:21 +01:00
d785b1bbc3 Fixup of SystemdMeter merge 2020-11-15 17:53:31 +01:00
f2b2735e07 Resolve merge conflicts, merge #229 "Add SystemdMeter" from @cgzones 2020-11-15 14:52:25 +01:00
bb908f3dc4 Resolve merge conflicts, merge #298 "Macro cleanup" from @BenBE 2020-11-15 14:33:09 +01:00
da2dcf9505 Remove duplicate test for NUL
Fixes #308, thanks @qarmin (Rafał Mikrut)
2020-11-15 14:16:23 +01:00
9e976b899b Merge pull request #239 from StoneBam/list-locks 2020-11-14 16:03:17 +01:00
d431786fca Split data array for file lock information into separate fields 2020-11-14 15:51:26 +01:00
18763051a2 Split platform dependent parts for file locks screen 2020-11-14 15:51:26 +01:00
2d6da2e520 Add compat wrapper for readlinkat 2020-11-09 19:17:57 +01:00
84dad4c38e Implement screen for active file locks 2020-11-09 19:17:57 +01:00
e7b95feee4 Remove unnecessary braces 2020-11-02 22:15:01 +01:00
0e922d4085 Integrate NAN check into assignment
The check for NAN is kept to avoid relying on implementation details of the CLAMP macro/function
2020-11-02 22:15:01 +01:00
cb8bb12974 Shorten initializer 2020-11-02 22:15:01 +01:00
1c060a9d6b Avoid RichString_beginAllocated being ammendable 2020-11-02 22:15:01 +01:00
0d64ca9262 Wrap inline structure definitions 2020-11-02 22:15:01 +01:00
45869513bf Embracing branches 2020-11-02 22:15:01 +01:00
61e14d4bb2 Spacing around operators 2020-11-02 22:15:01 +01:00
b23f8235e2 Whitespace and indentation issues 2020-11-02 22:15:01 +01:00
9a16b1079e Make scope of match macro symmetric 2020-11-02 22:15:01 +01:00
493217e814 Fix indentation to 3 spaces 2020-11-02 22:15:01 +01:00
adf797c295 Spacing after keywords (for) 2020-11-02 22:15:01 +01:00
374edb9ed5 Spacing after keywords (if) 2020-11-02 22:14:59 +01:00
0a51eae11f Spacing after keywords (while) 2020-11-02 22:14:26 +01:00
1877325329 Spacing after keywords (#define) 2020-11-02 22:14:26 +01:00
db0a13970e Convert addattrstr to static inline function
NB: The macro could have been a braced while(0) loop, which without optimization produces more code
2020-11-02 22:14:26 +01:00
c790b6ae67 Remove accidental syntax collision 2020-11-02 22:14:26 +01:00
7ab0915a6c Remove unnecessary trailing semicolon on macros 2020-11-02 22:14:26 +01:00
0806a7958b Assert Vector_get returns an object
It is generally assumed Vector_get returns a non-NULL object.
Use a generic assert in Vector_get instead of in callers.
2020-11-02 19:24:28 +01:00
742e610f1d Merge branch 'wrap' of cgzones/htop 2020-11-02 17:08:38 +01:00
0c1908832b Handle data wraparounds in IO Meters
If the current data is smaller than the previous one, either by a retrieve error
or a device removal or a original data wraparound, sanitize the value to zero.

Fixes: #299
2020-11-02 14:46:42 +01:00
a83f515f0f Address items from review 2020-10-31 20:36:53 -04:00
15652e7b81 Enclose macro arguments in parentheses 2020-10-31 19:54:03 +01:00
2a9e8ca074 Add SystemdMeter 2020-10-31 19:51:42 +01:00
ab17ef4dc0 Zram Meter feature 2020-10-31 18:51:53 +01:00
43d274a617 Use integer type for item count instead of char 2020-10-31 18:36:55 +01:00
59ef15b2ad Fix segmentation fault when column name is NULL.
So, some columns (ex: SECATTR) can be sortable now.
2020-10-31 18:34:34 +01:00
6787c43097 Merge branch 'source-format' of BenBE/htop
Closes #158
2020-10-31 17:58:30 +01:00
de884d17bb Documentation on the repository style guide 2020-10-31 11:11:40 +01:00
dde71c6637 Highlight new and old processes (#74) 2020-10-30 21:56:16 -04:00
bbf01054bf Add compat wrapper for fstatat 2020-10-29 22:21:42 +01:00
049046c700 FreeBSD: update Process 2020-10-29 22:21:42 +01:00
97ea45ca9a FreeBSD: update ProcessList 2020-10-29 22:21:42 +01:00
c2fdfd99eb FreeBSD: implement Platform_getDiskIO() 2020-10-29 22:21:42 +01:00
c91061c84b FreeBSD: Platform update 2020-10-29 22:21:42 +01:00
88eec2dc00 FreeBSD: rework tty process column 2020-10-29 22:21:42 +01:00
ddbb0c2c35 Add HTOP_$platform defines to config.h header
Can be used to conditionally compile platform specific code.
2020-10-29 22:17:52 +01:00
03b773b701 Small ListItem update 2020-10-28 20:49:11 +01:00
6375df49c9 Simplify RichString_begin 2020-10-28 19:57:10 +01:00
887dfde308 Implement Process_getParentPid and Process_isChildOf as functions
Make it more readable and fix unenclosed macro arguments
2020-10-28 19:57:10 +01:00
76797f8d92 Implement Process_isUserlandThread as function
Make it more readable and fix unenclosed macro arguments
2020-10-28 19:57:10 +01:00
d33b2be2ca Implement LinuxProcess_effectiveIOPriority as function
Make it more readable and fix unenclosed macro arguments
2020-10-28 19:57:10 +01:00
6b3dbd5c67 Implement IncSet_filter as function
Make it more readable and fix unenclosed macro arguments
2020-10-28 19:57:10 +01:00
7019949574 Implement RichString_setLen as function
Make it more readable and fix unenclosed macro arguments
2020-10-28 19:57:10 +01:00
8c1f5c5a6f Enclose macro arguments in parentheses 2020-10-28 19:57:10 +01:00
61bae4c9d2 Unify function argument names
Name first argument of ProcessList_goThroughEntries consistently super
Name first argument of ProcessList_new consistently userTable
2020-10-28 19:57:10 +01:00
cf1a9ec180 Refactor LinuxProcessList_readSmapsFile to work line-oriented 2020-10-28 19:46:23 +01:00
e89b289494 Drop duplicate assignment 2020-10-27 18:00:43 +01:00
059810ca65 Drop always true condition 2020-10-27 18:00:43 +01:00
ac2b07eddd Avoid some unnecessary casts and mark some not changing variables const 2020-10-27 18:00:43 +01:00
27870bd4de Drop unneeded variablw initialization and reduce scope 2020-10-27 18:00:43 +01:00
1533ea88a6 Drop duplicate and always true condition
This block is only entered if this->owner is true.
2020-10-27 18:00:43 +01:00
c98d4577c9 Refactor code for reading process environment from procfs 2020-10-27 17:54:37 +01:00
a3bb7cbe64 Hold only a const version of Settings in ProcessList 2020-10-26 19:30:38 +01:00
4eb443926f Hold only a const version of Settings in Process 2020-10-26 19:30:38 +01:00
7109172431 Mark process parameter of Process_writeField consistently const 2020-10-26 19:30:38 +01:00
72103e9613 Hold only a const version of the ProcessList in Meters 2020-10-26 19:30:38 +01:00
f757810f48 Improve handling of no data in Disk and Network IO Meters 2020-10-26 19:17:14 +01:00
167adc0a2b Parse POWER_SUPPLY_CAPACITY
If POWER_SUPPLY_{CHARGE,ENERGY}_NOW is missing then try to use
POWER_SUPPLY_CAPACITY to determine current charge level.
2020-10-26 19:03:09 +01:00
94e32cf1e8 Simplify environment-reading code
Suggested PR changes, thanks @cgzones
2020-10-26 19:01:11 +01:00
0ae2bb1f8e Add process environment for FreeBSD 2020-10-26 19:01:11 +01:00
11bf7be9c2 Mark user field of Process const
It's a non-owning pointer to a hashtable entry.
2020-10-22 22:26:22 +02:00
b08b255b41 Drop unused Platform functions Platform_setTasksValues 2020-10-22 22:26:12 +02:00
f8bd5acdc1 Merge branch 'Fix wrong strncmp replacement' of cgzones/htop 2020-10-20 22:41:24 +02:00
e12d48a661 Fix wrong strncmp replacement
Fixes 4c66eb6d4c
2020-10-20 22:30:13 +02:00
7429c22201 Drop unnecessary cast 2020-10-20 22:29:16 +02:00
45a22080c9 Increase print buffer in NetworkIOMeter_display
In case the packet values wrap-around or have other weird values, the
current buffer might be to small
2020-10-20 21:47:26 +02:00
8a08a3209c IWYU update
- Add Settings forward declaration in Process.h
- Add assert.h include in XUitls.c
- Add conditional stdbool.h include in Object.h
- Drop unneeded stddef.h include in Richstring.c
- Drop unneeded unistd.h include in Process.h
- Drop unneeded string.h include in linux/Platform.c
- Use String_eq to avoid string.h include in Action.c
- Improve script to run custom iwyu version
2020-10-20 21:44:25 +02:00
803234a58d update Github CI
- Add a full featured clang job
- Explicit disable options otherwise enabled by default in minimal job
2020-10-20 21:44:06 +02:00
f8208f2173 Drop tabs in source indentions 2020-10-20 21:43:36 +02:00
dea19b644f s390x support for travis 2020-10-20 21:43:10 +02:00
0c5430982e Merge branch 'screenshot' of nzbart/htop 2020-10-20 10:27:34 +02:00
0ea18a6edb Merge branch 'Xalloc_Cleanup' of cgzones/htop 2020-10-20 10:23:47 +02:00
9f1a9ab2c2 Merge branch 'header_pause' of cgzones/htop
Continue to update generic data in paused mode
2020-10-20 10:17:58 +02:00
a0fb6e34f9 Merge branch 'number-cpus-from-zero' of zevweiss/htop
* This changes the default to count CPUs from zero (instead of starting at one)
* Settings logic is inverted, backwards compatibility is preserved
2020-10-20 10:06:15 +02:00
475fd1ec2d Merge branch 'help_lines' of https://github.com/cgzones/htop 2020-10-20 10:01:16 +02:00
2d57d289b1 Merge branch 'cache_pagesize' of cgzones/htop 2020-10-20 09:52:27 +02:00
4c66eb6d4c XUtils string related updates
- allow count out-parameter of String_split() to be NULL
- introduce xStrndup()
- do not allow NULL pointers passed to String_eq()
  it is not used in any code
- implement String_startsWith(), String_contains_i() and String_eq()
  as inline header functions
- adjust several conversion issues
2020-10-19 15:38:45 +02:00
577416d1a9 Assert allocating non-zero size memory
Allocating zero size memory results in implementation-defined behavior:

  man:malloc(3) :
    If size is 0, then malloc() returns either NULL, or a unique pointer
    value that can later be successfully passed to free().
2020-10-19 15:35:43 +02:00
96e2a4259e Continue to update generic data in paused mode
Generic data, as CPU and memory usage, are used by Meters.
In paused mode they would stop receiving updates and especially Graph
Meters would stop showing continuous data.

Improves: #214
Closes: #253
2020-10-19 14:45:39 +02:00
361877454f Cache PAGE_SIZE
man:sysconf(3) states:
    The values obtained from these functions are system configuration constants.
    They do not change during the lifetime of a process.
2020-10-19 14:42:35 +02:00
0db398d4c3 Allow low and high value of CLAMP to be equal
Can for example occur in RichString_setAttrn(), when pausing and
resuming process tracing:

    htop: RichString.c:56: void RichString_setAttrn(RichString *, int, int, int): Assertion `(0) < (this->chlen - 1)' failed.

    ./htop(backtrace+0x5b)[0x45d9eb]
    ./htop(CRT_handleSIGSEGV+0x189)[0x4ebab9]
    /lib/x86_64-linux-gnu/libpthread.so.0(+0x14140)[0x7fd249d35140]
    /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x141)[0x7fd249b6ac41]
    /lib/x86_64-linux-gnu/libc.so.6(abort+0x123)[0x7fd249b54537]
    /lib/x86_64-linux-gnu/libc.so.6(+0x2540f)[0x7fd249b5440f]
    /lib/x86_64-linux-gnu/libc.so.6(+0x345c2)[0x7fd249b635c2]
    ./htop(RichString_setAttrn+0x234)[0x526de4]
    ./htop(RichString_setAttr+0x50)[0x5275c0]
    ./htop(Panel_draw+0x17b6)[0x514c26]
    ./htop(InfoScreen_run+0x305)[0x4fe7a5]
    ./htop[0x4d59d8]
    ./htop[0x5029cf]
    ./htop(ScreenManager_run+0x69f)[0x52a82f]
    ./htop(main+0x704)[0x4f8774]
    /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xea)[0x7fd249b55cca]
    ./htop(_start+0x2a)[0x4268ea]
2020-10-19 14:13:58 +02:00
0f5262917f Make all required includes explicit
Information as seen by IWYU 0.12 + clang 9 on Linux
2020-10-18 20:09:05 +02:00
29346d0561 Provide basic configuration for IWYU 2020-10-18 20:07:12 +02:00
8c93f31809 Merge branch 'misaligned-struct-access' of BenBE/htop
Closes #263
2020-10-18 19:43:59 +02:00
8534dcb87c Merge branch 'strace-leaks' of BenBE/htop
Closes #262
2020-10-18 19:43:24 +02:00
3e5bc034e5 Ensure full initialization of all fields 2020-10-18 17:35:32 +02:00
4dfedd3930 Fix various file descriptor leaks 2020-10-18 17:35:32 +02:00
81543253cf Fix misaligned access inside taskstats structure
Reported by UB sanitizer (alongside several other messages):
linux/LinuxProcessList.c:782:25: runtime error: member access within misaligned address 0x614000000264 for type 'struct taskstats', which requires 8 byte alignment
0x614000000264: note: pointer points here
  64 01 03 00 0a 00 00 00  00 00 00 00 02 00 00 00  00 00 00 00 4b c8 2e 00  00 00 00 00 3e 45 3c fd
              ^

The issue doesn't cause trouble on x86, but any architecture with stricter memory alignment requirements may inadvertedly break.
2020-10-18 17:20:34 +02:00
c138d14897 Release old memory on error
Avoids leaking memory upon realloc failure.
2020-10-17 20:54:14 +02:00
5e4b182616 Combine XAlloc.[ch] into XUtils.[ch] 2020-10-17 20:54:14 +02:00
872e542f4e Rename StringUtils.[ch] to XUtils.[ch] 2020-10-16 20:30:21 +02:00
c6f04a9c5d Move xAsprintf, xSnprintf and xStrdup to StringUtils.h 2020-10-16 20:29:45 +02:00
7cd093ce95 Add NetworkIOMeter 2020-10-16 20:00:14 +02:00
a802961286 Generalize Meter value colors for IO 2020-10-16 20:00:14 +02:00
e9246abff8 Misc Vector updates
- Move swap() macro to source file and implement as function
- Implement Vector_get() and Vector_size() as inline functions
  to make them type safe and avoid lhs usage
- Comment comparison statistics, they are only needed for performance
  testing
2020-10-16 19:47:34 +02:00
a63cfc8b7c Refactor generating starttime string into Process class 2020-10-16 19:23:40 +02:00
783be7711d Do not use extra starttime process field on Linux 2020-10-16 19:23:40 +02:00
d744dac7ee Add SELinuxMeter 2020-10-16 19:20:07 +02:00
af4f58d013 Misc conversion fixes 2020-10-16 19:16:53 +02:00
1d00893110 Automatically detect if backtrace(3) needs -lexecinfo 2020-10-16 10:58:14 +02:00
846fe8a71f Mark Vector parameter const for non-modifying functions 2020-10-15 20:45:39 +02:00
3c08fa3c63 Keep building on errors
Doing so allows for more than one error to be detected in builds
2020-10-15 20:45:39 +02:00
bfa7d1fbe2 Mark search parameter in Vector_indexOf const 2020-10-15 20:45:39 +02:00
2f9381d867 Keep building on errors
Doing so allows for more than one error to be detected in builds
2020-10-15 20:31:56 +02:00
898a690375 Do not hard-code line numbers in help screen building code 2020-10-14 14:34:40 +02:00
1df7fa387a Misc CRT cleanup 2020-10-14 14:28:02 +02:00
59edb2e80c Enclose macro argument in parentheses 2020-10-13 14:56:01 +02:00
7af06659e2 Mark remaining classes const 2020-10-13 14:56:01 +02:00
5cc20e7cb2 Settings: do not save initial cpu count
Not needed and confusing with ProcessList.cpuCount
2020-10-12 13:15:23 +02:00
9f5b50edd7 CPUMeter: avoid crashes and leaks in case the CPU count changes
E.g. if the HT/SMT mode changes

Use separate data for sub-meters
Do not reuse drawData for maintainability
2020-10-12 13:15:23 +02:00
0b9a001498 Meter: use explicit type for drawData 2020-10-12 13:15:23 +02:00
25022c219d Read CPU count every cycle to avoid issues when HT/SMT mode changes 2020-10-12 13:15:23 +02:00
601480003f Centralise fault handling
This should be done as all platforms essentially did the same anyway and there was nothing platform specific.
2020-10-12 13:06:12 +02:00
b47bc667a2 Add key to pause process list updates 2020-10-12 13:04:00 +02:00
fc301b7447 Compress size of default FunctionBar 2020-10-12 13:04:00 +02:00
55eafd3b39 Add conf*/ and callgrind.out.* to list of ignored files 2020-10-11 14:21:59 +02:00
f8b9ced93f OpenFilesScreen update
- Remove local types and function from header file
- Reduce OpenFiles_Data to neccessary size
- Print file access mode (r/w/u)
- Fix memory leak on consecutive items without an intermediate file item:

    ==15257==ERROR: LeakSanitizer: detected memory leaks

    Direct leak of 120 byte(s) in 12 object(s) allocated from:
        #0 0x48c864 in strdup (htop/htop+0x48c864)
        #1 0x542f68 in xStrdup htop/XAlloc.c:71:17
        #2 0x50e225 in OpenFilesScreen_getProcessData htop/OpenFilesScreen.c:112:25
        #3 0x50cd17 in OpenFilesScreen_scan htop/OpenFilesScreen.c:141:35
        #4 0x4fd3eb in InfoScreen_run htop/InfoScreen.c:81:35
        #5 0x4d58bb in actionLsof htop/Action.c:361:4
        #6 0x501766 in MainPanel_eventHandler htop/MainPanel.c:80:19
        #7 0x5289fa in ScreenManager_run htop/ScreenManager.c:227:19
        #8 0x4f748e in main htop/htop.c:300:4
        #9 0x7ff73e0d8cc9 in __libc_start_main csu/../csu/libc-start.c:308:16

    SUMMARY: AddressSanitizer: 120 byte(s) leaked in 12 allocation(s).
2020-10-10 11:26:43 +02:00
79ad39c718 Mark Object pointer to _display function const 2020-10-10 11:25:19 +02:00
e5fdb80c7d Fix Hashtable_put to allow storing the same pointer 2020-10-09 12:23:16 +02:00
f4439b1b60 Makefile sort correction 2020-10-09 10:40:54 +02:00
41eea8a355 Mark process argument of Process_isThread const 2020-10-09 10:18:40 +02:00
7fa0f19ffd Merge branch 'master' of https://github.com/ryenus/htop
Closes  #223
2020-10-09 09:45:41 +02:00
32a2caa692 use 'w' for command wrapping as 'M' is already used
since 'M' is already used for sort-by-memory, as with:

    keys['M'] = actionSortByMemory;

reorder help info about shortcut keys
2020-10-09 09:03:32 +08:00
4a78f4bb92 Some more locations for ARRAYSIZE 2020-10-08 15:37:03 +02:00
2970cae543 Handle parsing envID & VPid from process status file
Fixes #55
Fixes #192
2020-10-07 13:14:39 +02:00
ba282cfe19 Mark Object instances const 2020-10-07 13:01:53 +02:00
08d85e6143 Mark Object classes and Object class fields const 2020-10-07 13:01:53 +02:00
164051354f Replace copy loop by memmove in Vector_insert
This is basically the same change like in Vector_take,
just in the opposite direction.
2020-10-07 12:59:55 +02:00
1704c29b90 Use memmove for Vector_take
Doing a quick check with callgrind this gives
an average reduction from 1804 cycles/call
down to 491 cycles/call on my test system.

The average was taken over about 40k calls.
2020-10-07 12:59:55 +02:00
769df604b2 Set a -dev version to bug reports show a useful version and not the last release 2020-10-07 10:35:06 +02:00
fbf6424e64 Option to set initial filter
Closes #219
2020-10-07 10:34:25 +02:00
954d6c12f5 Simplify statm parsing and document unused fields 2020-10-06 18:59:02 +02:00
3653ee35c5 Drop redundant cast to same type 2020-10-06 11:46:41 +02:00
db159e7580 Enclose CLAMP macro arguments in parentheses 2020-10-06 11:46:41 +02:00
db472075a4 Enable -Wcast-qual compiler warning 2020-10-06 11:20:07 +02:00
ad3acfc847 Handle Panel_getSelected() returning NULL
Found by compiling with LTO:

    ColumnsPanel.c: In function ‘ColumnsPanel_eventHandler’:
    ColumnsPanel.c:46:59: error: potential null pointer dereference [-Werror=null-dereference]
       46 |             ((ListItem*)Panel_getSelected(super))->moving = this->moving;
          |                                                           ^
    AvailableColumnsPanel.c: In function ‘AvailableColumnsPanel_eventHandler’:
    AvailableColumnsPanel.c:31:8: error: potential null pointer dereference [-Werror=null-dereference]
       31 |    int key = ((ListItem*) Panel_getSelected(super))->key;
          |        ^
    AvailableMetersPanel.c: In function ‘AvailableMetersPanel_eventHandler’:
    AvailableMetersPanel.c:40:24: error: potential null pointer dereference [-Werror=null-dereference]
       40 |    int param = selected->key & 0xff;
          |                        ^
    linux/IOPriorityPanel.c: In function ‘IOPriorityPanel_getIOPriority’:
    linux/IOPriorityPanel.c:37:11: error: potential null pointer dereference [-Werror=null-dereference]
       37 |    return (IOPriority) ( ((ListItem*) Panel_getSelected(this))->key );
          |           ^
2020-10-06 11:17:23 +02:00
e9fa290019 Merge branch 'update-license-and-copyright-info' 2020-10-06 10:27:38 +11:00
dc6523bf60 DateMeter followup 2020-10-05 13:59:05 +02:00
d93cac12be Add a date and datetime meter (#159)
Add a date meter and sort header and source files in Makefile

Change the lists of header and source files sorted alphabetical and one
file per line. This way diffs become better readable and merges easier.
2020-10-05 13:52:58 +02:00
ffd90c28ab Mention platform for platform specific configure options 2020-10-05 12:48:23 +02:00
577984d875 Mark argument in Object_isA const 2020-10-05 12:47:56 +02:00
cdd3913647 Merge identical declarations 2020-10-05 12:47:56 +02:00
49bb1b57f8 Assert that low value is lower than the high value in CLAMP 2020-10-05 12:47:56 +02:00
7758774890 Add Copyright statement to --help (needed as it has the license info) 2020-10-05 12:31:24 +02:00
ff455b0004 limit max screen title length to window width
Applies screen title truncating to all InfoScreen classes.
2020-10-05 12:18:05 +02:00
079c2abf8e Update License consistently to GPLv2 as per COPYING file 2020-10-05 10:13:12 +02:00
90d16b6630 Update copyright statement 2020-10-05 09:47:49 +02:00
72613a38f4 Merge branch '0000/int-sizes/00' of https://github.com/mfwitten/htop into mfwitten-0000/int-sizes/00 2020-10-05 16:19:58 +11:00
c953257de6 Merge pull request #205 from cgzones/arraysize
Introduce ARRAYSIZE
2020-10-05 16:10:02 +11:00
576b82f86a Merge branch 'attr-nonnull' of https://github.com/BenBE/htop into BenBE-attr-nonnull 2020-10-05 15:57:52 +11:00
42946ec113 Introduce ARRAYSIZE 2020-10-03 19:05:40 +02:00
b82a13c6ba Add clang analyzer CI job 2020-10-03 19:04:27 +02:00
d69585b82a Resolve DEBUG compilation issues
Use NDEBUG conditional instead of DEBUG.

Do not call static functions in extern inline ones.
    Vector.c:67:11: error: static function 'Vector_isConsistent' is used in an inline function with external linkage [-Werror,-Wstatic-in-inline]
2020-10-03 19:04:27 +02:00
b7f63292e5 Add --enable-debug configure option to enable asserts
asserts are still disabled by default.
2020-10-03 19:04:27 +02:00
e518459981 Add DiskIOMeter for IO read/write usage 2020-10-03 19:01:38 +02:00
6f387008cb Add security attribute process column 2020-10-03 18:51:17 +02:00
4b14ab9789 Adjust colors
- do not reverse CPU steal and guest in monochrome
- black on black in Light Terminal is not visible, use blue on black
- white on blue in Light Terminal is display as blue on black, use
  yellow on black
- re-draw FunctionBar after color change
2020-10-02 14:41:27 +02:00
8efc88593a InfoScreen: update content on resize 2020-10-02 14:40:15 +02:00
3afa5dfbcc minor typo in Vector.c 2020-10-02 14:39:38 +02:00
816734e2d4 Add screen shot of htop to readme
Added a basic screenshot of htop in action to the readme so that
visitors to the page can quickly get a rough idea about what htop does.
2020-10-01 20:21:44 +13:00
2cde4a7f8e Enable NULL pointer checks via compiler if supported 2020-09-29 18:07:17 +02:00
ab3171d21d Process.{h,c}: Use integer types that are more portable
When building on a 32-bit system, the compiler warned that the
following line uses a constant whose value is the overflow result
of a compile-time computation:

  Process.c (line 109):   } else if (number < 10000 * ONE_M) {

Namely, this constant expression:

  10000 * ONE_M

was intended to produce the following value:

  10485760000

However, the result overflowed to produce:

   1895825408

The reason for this overflow is as follows:

  o The macros are expanded:

      10000 * (ONE_K * ONE_K)
      10000 * (1024L * 1024L)

  o The untyped constant expression "10000" is typed:

      10000U * (1024L * 1024L)

  o The parenthesized expression is evaluated:

      10000U * (1048576L)

  o The left operand ("10000U") is converted:

      10000L * (1048576L)

    Unbound by integer sizes, that last multiplication
    would produce the following value:

      10485760000

    However, on a 32-bit machine, where a long is 32 bits
    (really 31 bits when talking about positive numbers),
    the maximum value that can be computed is 2**31-1:

      2147483647

    Consequently, the computation overflows.

  o The compiler produces a long int value that is the
    the result of overflow (10485760000 % 2**31):

      1895825408L

    Actually, I think this overflow is implementation-defined,
    so it's not even a portable description of what happens.

The solution is to use a long long int (or, even better,
an unsigned long long int) type for the constant expression;
the C standard mandates a sufficiently large maximum value
for such types.

Hence, the following change is made to the bad line:

  -   } else if (number < 10000 * ONE_M) {
  +   } else if (number < 10000ULL * ONE_M) {

However, the whole line is now patently silly, because the
variable "number" is typed "unsigned long", and so it will
always be less than the constant expression (the compiler
will warn about this, too).

Hence, "number" must be typed "unsigned long long"; however,
this necessitates changing all of the string formats from
something like "%lu" to something like "%llu".

Et voila! This commit is born.

Then, for the sake of completeness, the declared types of the
constant-expression macros are updated:

  o ONE_K is made unsigned (a "UL" instead of "L")
  o ONE_T is computed by introducing "1ULL *"
  o Similar changes are made for ONE_DECIMAL_{K,T}

Also, a non-portable overflow-conversion to a signed value
has been replaced with a portable comparison:

  -   if ((long long) number == -1LL) {
  +   if (number == ULLONG_MAX) {

It might be worth reviewing the rest of the code for other
cases where overflows are not handled correctly; even at
runtime, it's often necessary to check for overflow unless
such behavior is expected (especially for signed integer
values, for which overflow has implementation-defined
behavior).
2020-09-29 15:47:52 +00:00
e1c96879f4 Sort headers/includes 2020-09-29 17:41:49 +02:00
dac1e05a2c Fix FreeBSD compile issue
This issue was previously hidden as xSnprintf expanded to only one large command that didn't trigger the GCC formatting check.
2020-09-29 17:41:31 +02:00
736c496dbf Cleanse xStrdup mess 2020-09-29 17:41:31 +02:00
8b55113ea8 Reimplement xAsnprintf and xSnprintf as type-safe functions 2020-09-29 17:41:31 +02:00
241e4b3dbf Drop redundant declarations
- `CRT_fatalError()` is declared twice in CRT.h
- `Process_pidFormat`, `Process_writeField()` and `Process_compare` are
  declared twice in Process.h
- `btime` is defined in LinuxProcess.c and also declared in
  LinuxProcess.h, so drop in LinuxProcessList.h
2020-09-29 10:44:42 +02:00
7ae967a04b Drop redundant return statements 2020-09-29 10:44:42 +02:00
6db2d52261 Covert Meter attributes to file-local constant arrays 2020-09-29 10:44:42 +02:00
843949131a Drop redundant casts to the same type 2020-09-29 10:44:42 +02:00
214c742ae1 command screen: fill current line when scanning 2020-09-29 10:11:28 +02:00
9ee72568dc CPUMeter: add octuple-column CPU meters.
This is a straightforward extension of the existing multi-column CPU meter
code, which now allows for up CPU meters to be displayed in up to 16 columns.

This also adds the meter declarations to all the platform-specific code.
2020-09-28 14:35:35 +02:00
491bf98b90 Add missing 4-column CPU meters to non-Linux platforms. 2020-09-28 14:35:35 +02:00
d22f6573f3 CPUMeter: refactor common CPU meter rendering code.
Instead of scanning the meter name to determine the number of columns in a
CPU meter, move the common code behind some wrapper functions, and specify the
number of columns as an explicit parameter when called from the wrappers.

While this does add a bit of code for all the necessary wrapper functions, this
should be less brittle in case of future changes to the CPU meter code.
2020-09-28 14:35:35 +02:00
e75077a9f8 Merge pull request #107 from cgzones/hwlock_linuxaffinity
Make --enable-hwloc and --enable-linux-affinity mutual exclusive
2020-09-28 16:47:47 +10:00
6191336498 Merge pull request #116 from cgzones/valgrind
Add Valgrind script
2020-09-28 16:46:58 +10:00
8c9bd20013 Merge pull request #181 from cgzones/missing_prototypes
Add -Wmissing-prototypes compiler warning
2020-09-28 16:30:57 +10:00
400178a89b Merge branch 'arc-is-not-cache' of https://github.com/multiplexd/htop into multiplexd-arc-is-not-cache 2020-09-28 14:44:12 +10:00
4e282eb845 Add -Wmissing-prototypes compiler warning 2020-09-25 17:20:35 +02:00
dfa40ad0eb Linux: consider the ZFS ARC to be cache.
This commit is based on a patch originally by @edef1c. The ZFS ARC is a cache
(it's in the name), which will be evicted by the kernel if memory pressure so
requires. Hence, the ARC should not be counted towards a system's total used
memory, and should instead be grouped with the other caches in the system.

Signed-off-by: edef <edef@edef.eu>
2020-09-24 23:27:27 +01:00
18b1e9fba9 Do not drop qualifier in cast
ListItem.c:73:33: warning: cast from 'const void *' to 'struct ListItem_ *' drops const qualifier [-Wcast-qual]
   ListItem* obj1 = (ListItem*) cast1;
                                ^
ListItem.c:74:33: warning: cast from 'const void *' to 'struct ListItem_ *' drops const qualifier [-Wcast-qual]
   ListItem* obj2 = (ListItem*) cast2;
                                ^

Process.c:434:28: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
   Process* p1 = (Process*)v1;
                           ^
Process.c:435:28: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
   Process* p2 = (Process*)v2;
                           ^
Process.c:441:36: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
   Settings *settings = ((Process*)v1)->settings;
                                   ^
Process.c:443:22: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
      p1 = (Process*)v1;
                     ^
Process.c:444:22: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
      p2 = (Process*)v2;
                     ^
Process.c:446:22: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
      p2 = (Process*)v1;
                     ^
Process.c:447:22: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
      p1 = (Process*)v2;
                     ^

AffinityPanel.c:37:16: warning: cast from 'const char *' to 'void *' drops const qualifier [-Wcast-qual]
   free((void*)this->text);
               ^
AffinityPanel.c:39:19: warning: cast from 'const char *' to 'void *' drops const qualifier [-Wcast-qual]
      free((void*)this->indent);
                  ^

linux/LinuxProcess.c:294:36: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
   Settings *settings = ((Process*)v1)->settings;
                                   ^
linux/LinuxProcess.c:296:27: warning: cast from 'const void *' to 'struct LinuxProcess_ *' drops const qualifier [-Wcast-qual]
      p1 = (LinuxProcess*)v1;
                          ^
linux/LinuxProcess.c:297:27: warning: cast from 'const void *' to 'struct LinuxProcess_ *' drops const qualifier [-Wcast-qual]
      p2 = (LinuxProcess*)v2;
                          ^
linux/LinuxProcess.c:299:27: warning: cast from 'const void *' to 'struct LinuxProcess_ *' drops const qualifier [-Wcast-qual]
      p2 = (LinuxProcess*)v1;
                          ^
linux/LinuxProcess.c:300:27: warning: cast from 'const void *' to 'struct LinuxProcess_ *' drops const qualifier [-Wcast-qual]
      p1 = (LinuxProcess*)v2;
                          ^

linux/LinuxProcessList.c:62:32: warning: cast from 'const void *' to 'struct TtyDriver_ *' drops const qualifier [-Wcast-qual]
   TtyDriver* a = (TtyDriver*) va;
                               ^
linux/LinuxProcessList.c:63:32: warning: cast from 'const void *' to 'struct TtyDriver_ *' drops const qualifier [-Wcast-qual]
   TtyDriver* b = (TtyDriver*) vb;
                               ^

linux/Battery.c:130:21: warning: cast from 'const char *' to 'char *' drops const qualifier [-Wcast-qual]
      free((char *) isOnline);
                    ^
linux/Battery.c:197:26: warning: cast from 'const char *' to 'char *' drops const qualifier [-Wcast-qual]
      xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/type", entryName);
                         ^
linux/Battery.c:209:29: warning: cast from 'const char *' to 'char *' drops const qualifier [-Wcast-qual]
         xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName);
                            ^
linux/Battery.c:262:29: warning: cast from 'const char *' to 'char *' drops const qualifier [-Wcast-qual]
         xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName);
                            ^
2020-09-24 20:14:17 +02:00
6a03cd237a Avoid warning about unreachable break statement
htop.c:112:13: warning: 'break' will never be executed [-Wunreachable-code-break]
            break;
            ^~~~~
htop.c:109:13: warning: 'break' will never be executed [-Wunreachable-code-break]
            break;
            ^~~~~
2020-09-24 20:14:17 +02:00
cd1ba1422b Avoid bad function cast warning
linux/Platform.c:142:17: warning: cast from function call of type 'double' to non-matching type 'int' [-Wbad-function-cast]
   return (int) floor(uptime);
                ^~~~~~~~~~~~~
2020-09-24 20:14:17 +02:00
4a1f3fca96 Drop unnecessary usage of comma operator 2020-09-24 20:14:17 +02:00
4296e74ada Include prototype in Battery implementation
linux/Battery.c:291:6: warning: no previous prototype for function 'Battery_getData' [-Wmissing-prototypes]
void Battery_getData(double* level, ACPresence* isOnAC) {
     ^
2020-09-24 20:14:17 +02:00
ce0fd5f6d8 Drop unused macros 2020-09-24 20:14:17 +02:00
edf1b10d2c Read CPU frequency from sysfs by default
Use the more portable sysfs node /sys/devices/system/cpu/cpuX/cpufreq/scaling_cur_freq
to get the CPU frequency.
In case of an error fall back to /proc/cpuinfo .

Also use a fixed width of 4 for the frequency to avoid position jumps
in case the frequency moves in the range 900-1100 MHz.
2020-09-24 20:11:28 +02:00
f4e1f4619f Add DeepCode inline suppression
We just want a non-NUll pointer in the matching pid hashtable.
The pointer is not dereferenced anyways.
2020-09-24 20:04:47 +02:00
594409f299 Add DeepCode inline suppression
commsize is bounded by the allocated length passed in by commLen, saved
into commLenIn
2020-09-24 19:56:30 +02:00
005c4d1f23 Make --enable-hwloc and --enable-linux-affinity mutual exclusive
They can not be supported both at the same time.
Fail configure step instead of silently only use hwloc.
2020-09-24 19:43:27 +02:00
f4bb50294a show selected command wrapped in a separate window
For a process with a very long command, especially with many long
command line arguments, inspecting the command and its arguments could
become inconvenient.

Meanwhile htop supports the concept of "screen", or window, which is
extended here to create a dedicated "CommandScreen", making it possible
to display the command of the selected process in a separate window
meanwhile being wrapped into multiple lines.

Another benefit of using a command screen is, the user can navigate
through the wrapped lines of the command and perform actions like
searching and filtering.
2020-09-24 19:22:25 +02:00
5233817122 Avoid unsigned integer overflow
unsigned integer overflows are well-defined, but they might point to a counting issue.
Having the code free of unsigned overflows makes it easier to spot potential bugs.

  Action.c:332:27: runtime error: implicit conversion from type 'int' of value -1 (32-bit, signed) to type 'uid_t' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
  SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior Action.c:332:27 in
2020-09-24 18:06:59 +02:00
7ecea3d485 Use return value of CLAMP function 2020-09-24 18:06:36 +02:00
ba0fca1800 Add -Wfloat-equal to default build flags 2020-09-24 18:06:36 +02:00
321960bd96 Update delay accounting to use NAN on error 2020-09-24 18:06:36 +02:00
3c65d78d77 Update CPU freq display to use NAN on error 2020-09-24 18:06:36 +02:00
ebcf924643 Use threshold for display of guest/steal/irq meters 2020-09-24 18:06:36 +02:00
d0d3deb73c Properly query sysconf settting and use NAN if unavailable
This also fixes an issue with time returned negative if sysconf(_SC_CLK_TCK) returned an error.
2020-09-24 18:06:36 +02:00
29ec115143 Update IO rate display to use NAN on error 2020-09-24 18:06:36 +02:00
47e2cefe02 Update battery API to use NAN on error 2020-09-24 18:06:36 +02:00
f805093589 align cpu id to right 2020-09-24 17:53:57 +02:00
d2b3a7a375 Drop dead process fields
They are nowhere used.
2020-09-24 17:48:38 +02:00
c1b5201444 Consistent wording/formatting of field descriptions 2020-09-24 17:48:17 +02:00
83257744cc Document M_PSS and M_PSSWP in man page 2020-09-24 17:48:17 +02:00
7844e06eb0 Document field M_SWAP in man page 2020-09-24 17:48:17 +02:00
2565dd3c58 Drop dead code after break 2020-09-24 17:47:17 +02:00
6b11738744 Avoid arithmetic on booleans 2020-09-23 19:09:11 +02:00
f9966b5be3 Use checked allocation wrappers 2020-09-23 17:50:21 +02:00
e0e5997c53 Fix minor regression in number highlighting
Fixes #163
2020-09-21 14:10:07 +02:00
eb260af6bf Fix memory leak on cgroup read failure 2020-09-21 13:55:29 +02:00
2c933f210b htop shows no used memory in Solaris zone 2020-09-21 13:54:45 +02:00
543286256e htop crashes on Solaris 11.4 due to missing ZFS ARC kstats 2020-09-21 13:54:45 +02:00
5ea13e7ea9 Add format attribute 2020-09-18 12:28:40 +02:00
efb971f9df Fail travis CI on compiler warnings 2020-09-18 12:28:40 +02:00
475f729a36 Resolve unused variable on FreeBSD 2020-09-18 12:28:40 +02:00
e719a85994 Mark noreturn functions 2020-09-18 12:28:40 +02:00
b85a31415e Avoid checking of undefined macros
These feature macros are either define or not defined at all at the
configure step.
2020-09-18 12:28:40 +02:00
c3952e7c20 Use strict function prototypes
int foo(); declares a function taking any number of arguments.
2020-09-18 12:28:40 +02:00
7107d1db0b Refactor __attribute__ usage
Use internal macros for compatibility with non GNUC compilers.
2020-09-18 12:28:40 +02:00
f4602f7b4e Add some default compiler warnings
Compatible with gcc and clang.
2020-09-18 12:28:40 +02:00
dd6500c7c7 Sort option in help message 2020-09-18 12:27:45 +02:00
f6b0efded5 Convert short version option to capital V
v is generally used for enabling verbose mode
2020-09-18 12:27:45 +02:00
f3b4e248a3 Drop unused variable 2020-09-18 12:22:18 +02:00
1061bd719a Change option '-m' to '-M' for consistency of cli
`-m` was added as short option for `--no-mouse`, this is inconsistence
to the rest of the cli since otherwise the short options to disable a
feature are capital letters. Therefore this commit renames the option to
`-M`.

This commit also documents the option in the man page.
2020-09-18 12:04:21 +02:00
40441dca8e Enhance highlighting of semi-large and large numbers 2020-09-17 22:08:13 +02:00
3142077c76 Add script to run htop under valgrind
Includes suppressions for ncurses leaks.
2020-09-17 22:06:36 +02:00
c7568bc054 Fix memory leak in actionSetAffinity()
Call correct delete method for AffinityPanel
2020-09-17 22:04:11 +02:00
71c068ad28 Free movingBar memory on exit 2020-09-17 22:04:11 +02:00
8a849bc85a Call character checking function with unsigned char
See https://wiki.sei.cmu.edu/confluence/display/c/STR37-C.+Arguments+to+character-handling+functions+must+be+representable+as+an+unsigned+char
2020-09-17 22:03:24 +02:00
1f5bd5c4c8 Avoid modifying optarg
It might be working, but lets rather not modify getopt's global variable
`optarg`.

Also there is no need to call `getenv("USER")`, just use `geteuid()`.
2020-09-17 21:55:26 +02:00
5d4061732f Allow third party sigsegv handler
For example from sanitizers.
2020-09-17 21:54:21 +02:00
00665e2a2b Avoid unsigned integer overflow
unsigned overflow is well defined, but creates noise when using
sanitizers. unsigned overflow can be a symptom of logic issues of
counter, so its reasonable to use.

linux/LinuxProcessList.c:64:50: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'unsigned int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior linux/LinuxProcessList.c:64:50 in
linux/LinuxProcessList.c:64:11: runtime error: implicit conversion from type 'unsigned int' of value 4294967295 (32-bit, unsigned) to type 'int' changed the value to -1 (32-bit, signed)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior linux/LinuxProcessList.c:64:11 in
linux/LinuxProcessList.c:64:78: runtime error: unsigned integer overflow: 4 - 136 cannot be represented in type 'unsigned int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior linux/LinuxProcessList.c:64:78 in
2020-09-17 21:53:31 +02:00
98ee833932 Add Linux process column for context switches
Displays the incremental sum of voluntary_ctxt_switches and nonvoluntary_ctxt_switches.
2020-09-17 21:53:15 +02:00
ffc65b3827 Reorder check to avoid crash on invalid process field setting
If using a setting from a different development version with an
unsupported process field, first dereferencing Process_fields[id] yields
to a crash:

=================================================================
==19530==ERROR: AddressSanitizer: global-buffer-overflow on address 0x000000612800 at pc 0x000000521d1a bp 0x7ffec47a5ff0 sp 0x7ffec47a5fe8
READ of size 8 at 0x000000612800 thread T0
    #0 0x521d19 in readFields .htop/Settings.c:107:40
    #1 0x51d117 in Settings_read .htop/Settings.c:141:10
    #2 0x51c0c4 in Settings_new .htop/Settings.c:382:12
    #3 0x4eafe2 in main .htop/htop.c:220:25
    #4 0x7fa450570cc9 in __libc_start_main csu/../csu/libc-start.c:308:16
    #5 0x427a59 in _start (.htop/htop+0x427a59)

0x000000612800 is located 0 bytes to the right of global variable 'Process_fields' defined in 'linux/LinuxProcess.c:24:18' (0x6118a0) of size 3936
SUMMARY: AddressSanitizer: global-buffer-overflow .htop/Settings.c:107:40 in readFields
Shadow bytes around the buggy address:
  0x0000800ba4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800ba4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800ba4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800ba4e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800ba4f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0000800ba500:[f9]f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x0000800ba510: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x0000800ba520: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x0000800ba530: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x0000800ba540: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x0000800ba550: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==19530==ABORTING
2020-09-17 21:46:22 +02:00
84d39f95c6 autotools: enable warnings and cleanup
- enable warnings in autogen script
- drop unused m4/ directory usage
- drop AC_TYPE_SIGNAL:
  C99 guarantees the signal return type to be void
- drop AC_CHECK_FILE of procdir:
  most of the time compilation is done on a different system than htop is run
  and there is a runtime check in place
- improve linux_affinity corss compile logic:
  use fourth argument instead of pre-test
2020-09-17 21:45:11 +02:00
37921382f4 Use PROCDIR throughout instead of /proc on Linux 2020-09-17 21:44:27 +02:00
1efddaf1e5 Check for fdopen failure in OpenFilesScreen_getProcessData 2020-09-17 21:43:53 +02:00
b096fdbfc0 Avoid potential buffer overflow in LinuxProcessList_readStatFile
Pass size of allocated command buffer and limit write.
2020-09-17 21:43:53 +02:00
95012d6259 Avoid hardcoding of buffer size 2020-09-17 21:43:05 +02:00
443a943798 Properly close pipe handles when work is done 2020-09-17 21:43:05 +02:00
6921000481 Barely ever seen any 1000 digit PIDs … 2020-09-17 21:43:05 +02:00
6646030116 Update creation date to 2004 (thanks rubyFeedback)
Closes #140 (doc update)
2020-09-17 09:47:28 +02:00
3a1c698578 Update README with correct tarball locations, ncurses hints and support / bug reporting pointers.
Closes #63 (Release URL tarballs)
Closes #61, Closes #82 (Support requests on ncurses)
Closes #89 (ncurses docs)
Closes #28 (htop-dev vs. -legacy)
2020-09-16 18:10:29 +02:00
2899ed4cb0 Number CPUs from zero by default.
Numbering from one is idiosyncratic and inconsistent with basically
everything else in the world; it doesn't make much sense as default
behavior.

All naming is updated to reflect that numbering from one is a
non-default, opt-in option.  The old label of the flag saved in htoprc
("cpu_count_from_zero") is still supported for backwards compatibility
with existing configs, however.
2020-09-15 03:25:08 -05:00
a2fef38be7 Merge pull request #130 from gh-fork-dump/no-python
configure.ac: axe python check
2020-09-15 15:51:36 +10:00
a852fae8e0 configure.ac: axe python check
Now that the automated header script is gone, there's no need for python
now.
2020-09-15 15:19:55 +10:00
217 changed files with 11432 additions and 5528 deletions

View File

@ -3,7 +3,7 @@ name: CI
on: [ push, pull_request ] on: [ push, pull_request ]
jobs: jobs:
build-ubuntu-latest: build-ubuntu-latest-minimal-gcc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -12,48 +12,95 @@ jobs:
- name: Bootstrap - name: Bootstrap
run: ./autogen.sh run: ./autogen.sh
- name: Configure - name: Configure
run: ./configure --enable-werror run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --without-sensors
- name: Enable compatibility modes
run: |
sed -i 's/#define HAVE_FSTATAT 1/#undef HAVE_FSTATAT/g' config.h
sed -i 's/#define HAVE_OPENAT 1/#undef HAVE_OPENAT/g' config.h
sed -i 's/#define HAVE_READLINKAT 1/#undef HAVE_READLINKAT/g' config.h
- name: Build - name: Build
run: make run: make -k
- name: Distcheck - name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors"
build-ubuntu-clang-latest: build-ubuntu-latest-minimal-clang:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
CC: clang-10 CC: clang-11
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-10 main' -y sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y
sudo apt-get update -q sudo apt-get update -q
- name: Install Dependencies - name: Install Dependencies
run: sudo apt-get install clang-10 libncursesw5-dev run: sudo apt-get install clang-11 libncursesw5-dev
- name: Bootstrap - name: Bootstrap
run: ./autogen.sh run: ./autogen.sh
- name: Configure - name: Configure
run: ./configure --enable-werror run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --without-sensors
- name: Build - name: Build
run: make run: make -k
- name: Distcheck - name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors"
build-ubuntu-latest-full-featured: build-ubuntu-latest-full-featured-gcc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
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 run: sudo apt-get install libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev
- name: Bootstrap - name: Bootstrap
run: ./autogen.sh run: ./autogen.sh
- name: Configure - name: Configure
run: ./configure --enable-werror --enable-openvz --enable-cgroup --enable-vserver --enable-ancient-vserver --enable-taskstats --enable-unicode --enable-linux-affinity --enable-hwloc --enable-setuid --enable-delayacct run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors
- name: Build - name: Build
run: make run: make -k
- name: Distcheck - name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-cgroup --enable-vserver --enable-ancient-vserver --enable-taskstats --enable-unicode --enable-linux-affinity --enable-hwloc --enable-setuid --enable-delayacct' 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-full-featured-clang:
runs-on: ubuntu-latest
env:
CC: clang-11
steps:
- uses: actions/checkout@v2
- name: install clang repo
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y
sudo apt-get update -q
- name: Install Dependencies
run: sudo apt-get install clang-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors
- 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-setuid --enable-delayacct --with-sensors'
build-ubuntu-latest-clang-analyzer:
runs-on: ubuntu-latest
env:
CC: clang-11
steps:
- uses: actions/checkout@v2
- name: install clang repo
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y
sudo apt-get update -q
- 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
- name: Bootstrap
run: ./autogen.sh
- 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
- name: Build
run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)"
whitespace_check: whitespace_check:
runs-on: ubuntu-latest runs-on: ubuntu-latest

5
.gitignore vendored
View File

@ -17,6 +17,7 @@ htop
*.h.gch *.h.gch
*/.dirstamp */.dirstamp
# automake/autoconf related files
.deps/ .deps/
Makefile Makefile
Makefile.in Makefile.in
@ -24,6 +25,7 @@ INSTALL
aclocal.m4 aclocal.m4
autom4te.cache/ autom4te.cache/
compile compile
conf*/
config.guess config.guess
config.h config.h
config.h.in config.h.in
@ -40,3 +42,6 @@ ltmain.sh
m4/ m4/
missing missing
stamp-h1 stamp-h1
# files related to valgrind/callgrind
callgrind.out.*

View File

@ -9,4 +9,18 @@ os:
- linux - linux
- osx - osx
script: ./autogen.sh && ./configure && make arch:
- amd64
- s390x
before_script:
if [[ ${TRAVIS_CPU_ARCH} == 's390x' ]]; then
sudo apt-get update && sudo apt-get install -y libncursesw5-dev ;
fi
script:
- ./autogen.sh
# clang might warn about C11 generic selections in isnan()
- CFLAGS=-Wno-c11-extensions ./configure --enable-werror
- make -k
- CFLAGS=-Wno-c11-extensions make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror

266
Action.c
View File

@ -1,34 +1,44 @@
/* /*
htop - Action.c htop - Action.c
(C) 2015 Hisham H. Muhammad (C) 2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" #include "config.h" // IWYU pragma: keep
#include "Action.h" #include "Action.h"
#include "Affinity.h"
#include "AffinityPanel.h" #include <pwd.h>
#include <stdbool.h>
#include <stdlib.h>
#include "CategoriesPanel.h" #include "CategoriesPanel.h"
#include "CommandScreen.h"
#include "CRT.h" #include "CRT.h"
#include "EnvScreen.h" #include "EnvScreen.h"
#include "FunctionBar.h"
#include "Hashtable.h"
#include "IncSet.h"
#include "InfoScreen.h"
#include "ListItem.h"
#include "Macros.h"
#include "MainPanel.h" #include "MainPanel.h"
#include "OpenFilesScreen.h" #include "OpenFilesScreen.h"
#include "Process.h" #include "Process.h"
#include "ProcessLocksScreen.h"
#include "ProvideCurses.h"
#include "ScreenManager.h" #include "ScreenManager.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
#include "StringUtils.h"
#include "TraceScreen.h" #include "TraceScreen.h"
#include "Platform.h" #include "Vector.h"
#include "XUtils.h"
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
#include "Affinity.h"
#include "AffinityPanel.h"
#endif
#include <ctype.h>
#include <math.h>
#include <pwd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/param.h>
#include <sys/time.h>
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; Panel* panel = st->panel;
@ -36,7 +46,7 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
Settings* settings = st->settings; Settings* settings = st->settings;
int y = panel->y; int y = panel->y;
ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, false); ScreenManager* scr = ScreenManager_new(header, settings, st, false);
scr->allowFocusChange = false; scr->allowFocusChange = false;
ScreenManager_add(scr, list, x - 1); ScreenManager_add(scr, list, x - 1);
ScreenManager_add(scr, panel, -1); ScreenManager_add(scr, panel, -1);
@ -54,59 +64,59 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
} }
ScreenManager_delete(scr); ScreenManager_delete(scr);
Panel_move(panel, 0, y); Panel_move(panel, 0, y);
Panel_resize(panel, COLS, LINES-y-1); Panel_resize(panel, COLS, LINES - y - 1);
if (panelFocus == list && ch == 13) { if (panelFocus == list && ch == 13) {
if (followProcess) { if (followProcess) {
Process* selected = (Process*)Panel_getSelected(panel); Process* selected = (Process*)Panel_getSelected(panel);
if (selected && selected->pid == pid) if (selected && selected->pid == pid)
return Panel_getSelected(list); return Panel_getSelected(list);
else
beep(); beep();
} else { } else {
return Panel_getSelected(list); return Panel_getSelected(list);
} }
} }
return NULL; return NULL;
} }
// ---------------------------------------- // ----------------------------------------
static void Action_runSetup(Settings* settings, const Header* header, ProcessList* pl) { static void Action_runSetup(State* st) {
ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, true); ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true);
CategoriesPanel* panelCategories = CategoriesPanel_new(scr, settings, (Header*) header, pl); CategoriesPanel* panelCategories = CategoriesPanel_new(scr, st->settings, st->header, st->pl);
ScreenManager_add(scr, (Panel*) panelCategories, 16); ScreenManager_add(scr, (Panel*) panelCategories, 16);
CategoriesPanel_makeMetersPage(panelCategories); CategoriesPanel_makeMetersPage(panelCategories);
Panel* panelFocus; Panel* panelFocus;
int ch; int ch;
ScreenManager_run(scr, &panelFocus, &ch); ScreenManager_run(scr, &panelFocus, &ch);
ScreenManager_delete(scr); ScreenManager_delete(scr);
if (settings->changed) { if (st->settings->changed) {
Header_writeBackToSettings(header); Header_writeBackToSettings(st->header);
} }
} }
static bool changePriority(MainPanel* panel, int delta) { static bool changePriority(MainPanel* panel, int delta) {
bool anyTagged; bool anyTagged;
bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, (Arg){ .i = delta }, &anyTagged); bool ok = MainPanel_foreachProcess(panel, Process_changePriorityBy, (Arg) { .i = delta }, &anyTagged);
if (!ok) if (!ok)
beep(); beep();
return anyTagged; return anyTagged;
} }
static void addUserToVector(int key, void* userCast, void* panelCast) { static void addUserToVector(hkey_t key, void* userCast, void* panelCast) {
char* user = (char*) userCast; const char* user = userCast;
Panel* panel = (Panel*) panelCast; Panel* panel = panelCast;
Panel_add(panel, (Object*) ListItem_new(user, key)); Panel_add(panel, (Object*) ListItem_new(user, key));
} }
bool Action_setUserOnly(const char* userName, uid_t* userId) { bool Action_setUserOnly(const char* userName, uid_t* userId) {
struct passwd* user = getpwnam(userName); const struct passwd* user = getpwnam(userName);
if (user) { if (user) {
*userId = user->pw_uid; *userId = user->pw_uid;
return true; return true;
} }
*userId = -1; *userId = (uid_t)-1;
return false; return false;
} }
@ -123,14 +133,18 @@ static void tagAllChildren(Panel* panel, Process* parent) {
static bool expandCollapse(Panel* panel) { static bool expandCollapse(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel); Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false; if (!p)
return false;
p->showChildren = !p->showChildren; p->showChildren = !p->showChildren;
return true; return true;
} }
static bool collapseIntoParent(Panel* panel) { static bool collapseIntoParent(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel); Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false; if (!p)
return false;
pid_t ppid = Process_getParentPid(p); pid_t ppid = Process_getParentPid(p);
for (int i = 0; i < Panel_size(panel); i++) { for (int i = 0; i < Panel_size(panel); i++) {
Process* q = (Process*) Panel_get(panel, i); Process* q = (Process*) Panel_get(panel, i);
@ -146,7 +160,6 @@ static bool collapseIntoParent(Panel* panel) {
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) { Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey; settings->sortKey = sortKey;
settings->direction = 1; settings->direction = 1;
settings->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
} }
@ -160,6 +173,7 @@ static Htop_Reaction sortBy(State* st) {
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == st->settings->sortKey) if (fields[i] == st->settings->sortKey)
Panel_setSelected(sortPanel, i); Panel_setSelected(sortPanel, i);
free(name); free(name);
} }
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false); ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false);
@ -167,6 +181,10 @@ static Htop_Reaction sortBy(State* st) {
reaction |= Action_setSortKey(st->settings, field->key); reaction |= Action_setSortKey(st->settings, field->key);
} }
Object_delete(sortPanel); Object_delete(sortPanel);
if (st->pauseProcessUpdate)
ProcessList_sort(st->pl);
return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
} }
@ -174,7 +192,7 @@ static Htop_Reaction sortBy(State* st) {
static Htop_Reaction actionResize(State* st) { static Htop_Reaction actionResize(State* st) {
clear(); clear();
Panel_resize(st->panel, COLS, LINES-(st->panel->y)-1); Panel_resize(st->panel, COLS, LINES - (st->panel->y) - 1);
return HTOP_REDRAW_BAR; return HTOP_REDRAW_BAR;
} }
@ -197,7 +215,6 @@ static Htop_Reaction actionToggleKernelThreads(State* st) {
static Htop_Reaction actionToggleUserlandThreads(State* st) { static Htop_Reaction actionToggleUserlandThreads(State* st) {
st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads; st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads;
st->settings->hideThreads = st->settings->hideUserlandThreads;
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS; return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
} }
@ -206,9 +223,17 @@ static Htop_Reaction actionToggleProgramPath(State* st) {
return HTOP_REFRESH | HTOP_SAVE_SETTINGS; return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
} }
static Htop_Reaction actionToggleMergedCommand(State* st) {
st->settings->showMergedCommand = !st->settings->showMergedCommand;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
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->direction = 1; if (st->settings->treeView) {
st->settings->direction = 1;
}
ProcessList_expandTree(st->pl); ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
} }
@ -272,20 +297,25 @@ static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st); return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
} }
static Htop_Reaction actionQuit() { static Htop_Reaction actionQuit(ATTR_UNUSED State* st) {
return HTOP_QUIT; return HTOP_QUIT;
} }
static Htop_Reaction actionSetAffinity(State* st) { static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1) if (st->pl->cpuCount == 1)
return HTOP_OK; return HTOP_OK;
#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
Panel* panel = st->panel; Panel* panel = st->panel;
Process* p = (Process*) Panel_getSelected(panel); Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK; if (!p)
return HTOP_OK;
Affinity* affinity1 = Affinity_get(p, st->pl); Affinity* affinity1 = Affinity_get(p, st->pl);
if (!affinity1) return HTOP_OK; if (!affinity1)
return HTOP_OK;
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 */ width += 1; /* we add a gap between the panels */
@ -294,24 +324,25 @@ static Htop_Reaction actionSetAffinity(State* st) {
void* set = Action_pickFromVector(st, affinityPanel, width, true); 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, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity2 }, NULL); bool ok = MainPanel_foreachProcess((MainPanel*)panel, Affinity_set, (Arg) { .v = affinity2 }, NULL);
if (!ok) beep(); if (!ok)
beep();
Affinity_delete(affinity2); Affinity_delete(affinity2);
} }
Panel_delete((Object*)affinityPanel); Object_delete(affinityPanel);
#endif #endif
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
} }
static Htop_Reaction actionKill(State* st) { static Htop_Reaction actionKill(State* st) {
Panel* signalsPanel = (Panel*) SignalsPanel_new(); Panel* signalsPanel = SignalsPanel_new();
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true); ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true);
if (sgn) { if (sgn) {
if (sgn->key != 0) { if (sgn->key != 0) {
Panel_setHeader(st->panel, "Sending..."); Panel_setHeader(st->panel, "Sending...");
Panel_draw(st->panel, true); Panel_draw(st->panel, true, true);
refresh(); refresh();
MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (Arg){ .i = sgn->key }, NULL); MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL);
napms(500); napms(500);
} }
} }
@ -329,7 +360,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false); ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false);
if (picked) { if (picked) {
if (picked == allUsers) { if (picked == allUsers) {
st->pl->userId = -1; st->pl->userId = (uid_t)-1;
} else { } else {
Action_setUserOnly(ListItem_getRef(picked), &(st->pl->userId)); Action_setUserOnly(ListItem_getRef(picked), &(st->pl->userId));
} }
@ -345,7 +376,7 @@ Htop_Reaction Action_follow(State* st) {
} }
static Htop_Reaction actionSetup(State* st) { static Htop_Reaction actionSetup(State* st) {
Action_runSetup(st->settings, st->header, st->pl); Action_runSetup(st);
// TODO: shouldn't need this, colors should be dynamic // TODO: shouldn't need this, colors should be dynamic
int headerHeight = Header_calculateHeight(st->header); int headerHeight = Header_calculateHeight(st->header);
Panel_move(st->panel, 0, headerHeight); Panel_move(st->panel, 0, headerHeight);
@ -355,7 +386,9 @@ static Htop_Reaction actionSetup(State* st) {
static Htop_Reaction actionLsof(State* st) { static Htop_Reaction actionLsof(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel); Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK; if (!p)
return HTOP_OK;
OpenFilesScreen* ofs = OpenFilesScreen_new(p); OpenFilesScreen* ofs = OpenFilesScreen_new(p);
InfoScreen_run((InfoScreen*)ofs); InfoScreen_run((InfoScreen*)ofs);
OpenFilesScreen_delete((Object*)ofs); OpenFilesScreen_delete((Object*)ofs);
@ -364,9 +397,22 @@ static Htop_Reaction actionLsof(State* st) {
return HTOP_REFRESH | HTOP_REDRAW_BAR; return HTOP_REFRESH | HTOP_REDRAW_BAR;
} }
static Htop_Reaction actionStrace(State* st) { static Htop_Reaction actionShowLocks(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel); Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK; if (!p) return HTOP_OK;
ProcessLocksScreen* pls = ProcessLocksScreen_new(p);
InfoScreen_run((InfoScreen*)pls);
ProcessLocksScreen_delete((Object*)pls);
clear();
CRT_enableDelay();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static Htop_Reaction actionStrace(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p)
return HTOP_OK;
TraceScreen* ts = TraceScreen_new(p); TraceScreen* ts = TraceScreen_new(p);
bool ok = TraceScreen_forkTracer(ts); bool ok = TraceScreen_forkTracer(ts);
if (ok) { if (ok) {
@ -380,24 +426,36 @@ 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(st->panel);
if (!p) return HTOP_OK; if (!p)
return HTOP_OK;
Process_toggleTag(p); Process_toggleTag(p);
Panel_onKey(st->panel, KEY_DOWN); Panel_onKey(st->panel, KEY_DOWN);
return HTOP_OK; return HTOP_OK;
} }
static Htop_Reaction actionRedraw() { static Htop_Reaction actionRedraw(ATTR_UNUSED State* st) {
clear(); clear();
return HTOP_REFRESH | HTOP_REDRAW_BAR; return HTOP_REFRESH | HTOP_REDRAW_BAR;
} }
static const struct { const char* key; const char* info; } helpLeft[] = { static Htop_Reaction actionTogglePauseProcessUpdate(State* st) {
st->pauseProcessUpdate = !st->pauseProcessUpdate;
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static const struct {
const char* key;
const char* info;
} helpLeft[] = {
{ .key = " Arrows: ", .info = "scroll process list" }, { .key = " Arrows: ", .info = "scroll process list" },
{ .key = " Digits: ", .info = "incremental PID search" }, { .key = " Digits: ", .info = "incremental PID search" },
{ .key = " F3 /: ", .info = "incremental name search" }, { .key = " F3 /: ", .info = "incremental name search" },
{ .key = " F4 \\: ",.info = "incremental name filtering" }, { .key = " F4 \\: ",.info = "incremental name filtering" },
{ .key = " F5 t: ", .info = "tree view" }, { .key = " F5 t: ", .info = "tree view" },
{ .key = " p: ", .info = "toggle program path" }, { .key = " p: ", .info = "toggle program path" },
{ .key = " m: ", .info = "toggle merged command" },
{ .key = " Z: ", .info = "pause/resume process updates" },
{ .key = " u: ", .info = "show processes of a single user" }, { .key = " u: ", .info = "show processes of a single user" },
{ .key = " H: ", .info = "hide/show user process threads" }, { .key = " H: ", .info = "hide/show user process threads" },
{ .key = " K: ", .info = "hide/show kernel threads" }, { .key = " K: ", .info = "hide/show kernel threads" },
@ -409,42 +467,54 @@ static const struct { const char* key; const char* info; } helpLeft[] = {
{ .key = NULL, .info = NULL } { .key = NULL, .info = NULL }
}; };
static const struct { const char* key; const char* info; } helpRight[] = { static const struct {
const char* key;
const char* info;
} helpRight[] = {
{ .key = " Space: ", .info = "tag process" }, { .key = " Space: ", .info = "tag process" },
{ .key = " c: ", .info = "tag process and its children" }, { .key = " c: ", .info = "tag process and its children" },
{ .key = " U: ", .info = "untag all processes" }, { .key = " U: ", .info = "untag all processes" },
{ .key = " F9 k: ", .info = "kill process/tagged processes" }, { .key = " F9 k: ", .info = "kill process/tagged processes" },
{ .key = " F7 ]: ", .info = "higher priority (root only)" }, { .key = " F7 ]: ", .info = "higher priority (root only)" },
{ .key = " F8 [: ", .info = "lower priority (+ nice)" }, { .key = " F8 [: ", .info = "lower priority (+ nice)" },
#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY) #if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
{ .key = " a: ", .info = "set CPU affinity" }, { .key = " a: ", .info = "set CPU affinity" },
#endif #endif
{ .key = " e: ", .info = "show process environment" }, { .key = " e: ", .info = "show process environment" },
{ .key = " i: ", .info = "set IO priority" }, { .key = " i: ", .info = "set IO priority" },
{ .key = " l: ", .info = "list open files with lsof" }, { .key = " l: ", .info = "list open files with lsof" },
{ .key = " x: ", .info = "list file locks of process" },
{ .key = " s: ", .info = "trace syscalls with strace" }, { .key = " s: ", .info = "trace syscalls with strace" },
{ .key = " ", .info = "" }, { .key = " w: ", .info = "wrap process command in multiple lines" },
{ .key = " F2 C S: ", .info = "setup" }, { .key = " F2 C S: ", .info = "setup" },
{ .key = " F1 h: ", .info = "show this help screen" }, { .key = " F1 h: ", .info = "show this help screen" },
{ .key = " F10 q: ", .info = "quit" }, { .key = " F10 q: ", .info = "quit" },
{ .key = NULL, .info = NULL } { .key = NULL, .info = NULL }
}; };
static inline void addattrstr( int attr, const char* str) {
attrset(attr);
addstr(str);
}
static Htop_Reaction actionHelp(State* st) { static Htop_Reaction actionHelp(State* st) {
Settings* settings = st->settings; Settings* settings = st->settings;
clear(); clear();
attrset(CRT_colors[HELP_BOLD]); attrset(CRT_colors[HELP_BOLD]);
for (int i = 0; i < LINES-1; i++) for (int i = 0; i < LINES - 1; i++)
mvhline(i, 0, ' ', COLS); mvhline(i, 0, ' ', COLS);
mvaddstr(0, 0, "htop " VERSION " - " COPYRIGHT); int line = 0;
mvaddstr(1, 0, "Released under the GNU GPL. See 'man' page for more info.");
mvaddstr(line++, 0, "htop " VERSION " - " COPYRIGHT);
mvaddstr(line++, 0, "Released under the GNU GPLv2. See 'man' page for more info.");
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(3, 0, "CPU usage bar: "); line++;
#define addattrstr(a,s) attrset(a);addstr(s) mvaddstr(line++, 0, "CPU usage bar: ");
addattrstr(CRT_colors[BAR_BORDER], "["); addattrstr(CRT_colors[BAR_BORDER], "[");
if (settings->detailedCPUTime) { if (settings->detailedCPUTime) {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/"); addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
@ -465,7 +535,7 @@ static Htop_Reaction actionHelp(State* st) {
} }
addattrstr(CRT_colors[BAR_BORDER], "]"); addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(4, 0, "Memory bar: "); mvaddstr(line++, 0, "Memory bar: ");
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("/");
@ -473,29 +543,49 @@ static Htop_Reaction actionHelp(State* st) {
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(5, 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");
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(6,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.");
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
mvaddstr(7, 0, "In monochrome, meters display as different chars, in order: |#*@$%&."); mvaddstr(line, 0, "In monochrome, meters display as different chars, in order: |#*@$%&.");
} }
mvaddstr( 8, 0, " Status: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep"); line++;
for (int i = 0; helpLeft[i].info; i++) { mvaddstr(9+i, 9, helpLeft[i].info); }
for (int i = 0; helpRight[i].info; i++) { mvaddstr(9+i, 49, helpRight[i].info); } mvaddstr(line++, 0, "Process state: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep");
attrset(CRT_colors[HELP_BOLD]);
for (int i = 0; helpLeft[i].key; i++) { mvaddstr(9+i, 0, helpLeft[i].key); } line++;
for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); }
attrset(CRT_colors[PROCESS_THREAD]); int item;
mvaddstr(16, 32, "threads"); for (item = 0; helpLeft[item].key; item++) {
mvaddstr(17, 26, "threads"); attrset(CRT_colors[DEFAULT_COLOR]);
attrset(CRT_colors[DEFAULT_COLOR]); mvaddstr(line + item, 9, helpLeft[item].info);
attrset(CRT_colors[HELP_BOLD]);
mvaddstr(line + item, 0, helpLeft[item].key);
if (String_eq(helpLeft[item].key, " H: ")) {
attrset(CRT_colors[PROCESS_THREAD]);
mvaddstr(line + item, 32, "threads");
} else if (String_eq(helpLeft[item].key, " K: ")) {
attrset(CRT_colors[PROCESS_THREAD]);
mvaddstr(line + item, 26, "threads");
}
}
int leftHelpItems = item;
for (item = 0; helpRight[item].key; item++) {
attrset(CRT_colors[HELP_BOLD]);
mvaddstr(line + item, 40, helpRight[item].key);
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line + item, 49, helpRight[item].info);
}
line += MAXIMUM(leftHelpItems, item);
line++;
attrset(CRT_colors[HELP_BOLD]); attrset(CRT_colors[HELP_BOLD]);
mvaddstr(23,0, "Press any key to return."); mvaddstr(line++, 0, "Press any key to return.");
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
refresh(); refresh();
CRT_readKey(); CRT_readKey();
@ -514,14 +604,18 @@ static Htop_Reaction actionUntagAll(State* st) {
static Htop_Reaction actionTagAllChildren(State* st) { static Htop_Reaction actionTagAllChildren(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel); Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK; if (!p)
return HTOP_OK;
tagAllChildren(st->panel, p); tagAllChildren(st->panel, 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(st->panel);
if (!p) return HTOP_OK; if (!p)
return HTOP_OK;
EnvScreen* es = EnvScreen_new(p); EnvScreen* es = EnvScreen_new(p);
InfoScreen_run((InfoScreen*)es); InfoScreen_run((InfoScreen*)es);
EnvScreen_delete((Object*)es); EnvScreen_delete((Object*)es);
@ -530,6 +624,18 @@ static Htop_Reaction actionShowEnvScreen(State* st) {
return HTOP_REFRESH | HTOP_REDRAW_BAR; return HTOP_REFRESH | HTOP_REDRAW_BAR;
} }
static Htop_Reaction actionShowCommandScreen(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p)
return HTOP_OK;
CommandScreen* cmdScr = CommandScreen_new(p);
InfoScreen_run((InfoScreen*)cmdScr);
CommandScreen_delete((Object*)cmdScr);
clear();
CRT_enableDelay();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
void Action_setBindings(Htop_Action* keys) { void Action_setBindings(Htop_Action* keys) {
keys[KEY_RESIZE] = actionResize; keys[KEY_RESIZE] = actionResize;
@ -539,6 +645,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['H'] = actionToggleUserlandThreads; keys['H'] = actionToggleUserlandThreads;
keys['K'] = actionToggleKernelThreads; keys['K'] = actionToggleKernelThreads;
keys['p'] = actionToggleProgramPath; keys['p'] = actionToggleProgramPath;
keys['m'] = actionToggleMergedCommand;
keys['t'] = actionToggleTreeView; keys['t'] = actionToggleTreeView;
keys[KEY_F(5)] = actionToggleTreeView; keys[KEY_F(5)] = actionToggleTreeView;
keys[KEY_F(4)] = actionIncFilter; keys[KEY_F(4)] = actionIncFilter;
@ -553,7 +660,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['['] = actionLowerPriority; keys['['] = actionLowerPriority;
keys[KEY_F(8)] = actionLowerPriority; keys[KEY_F(8)] = actionLowerPriority;
keys['I'] = actionInvertSortOrder; keys['I'] = actionInvertSortOrder;
keys[KEY_F(6)] = actionExpandCollapseOrSortColumn; keys[KEY_F(6)] = actionSetSortColumn;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn; keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys['<'] = actionSetSortColumn; keys['<'] = actionSetSortColumn;
keys[','] = actionSetSortColumn; keys[','] = actionSetSortColumn;
@ -574,6 +681,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['S'] = actionSetup; keys['S'] = actionSetup;
keys['C'] = actionSetup; keys['C'] = actionSetup;
keys[KEY_F(2)] = actionSetup; keys[KEY_F(2)] = actionSetup;
keys['x'] = actionShowLocks;
keys['l'] = actionLsof; keys['l'] = actionLsof;
keys['s'] = actionStrace; keys['s'] = actionStrace;
keys[' '] = actionTag; keys[' '] = actionTag;
@ -584,4 +692,6 @@ void Action_setBindings(Htop_Action* keys) {
keys['U'] = actionUntagAll; keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren; keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen; keys['e'] = actionShowEnvScreen;
keys['w'] = actionShowCommandScreen;
keys['Z'] = actionTogglePauseProcessUpdate;
} }

View File

@ -3,12 +3,19 @@
/* /*
htop - Action.h htop - Action.h
(C) 2015 Hisham H. Muhammad (C) 2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include <stdbool.h>
#include <sys/types.h>
#include "Header.h" #include "Header.h"
#include "Object.h"
#include "Panel.h" #include "Panel.h"
#include "Process.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "Settings.h" #include "Settings.h"
#include "UsersTable.h" #include "UsersTable.h"
@ -24,16 +31,18 @@ typedef enum {
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
} Htop_Reaction; } Htop_Reaction;
typedef Htop_Reaction (*Htop_Action)();
typedef struct State_ { typedef struct State_ {
Settings* settings; Settings* settings;
UsersTable* ut; UsersTable* ut;
ProcessList* pl; ProcessList* pl;
Panel* panel; Panel* panel;
Header* header; Header* header;
bool pauseProcessUpdate;
bool hideProcessSelection;
} State; } State;
typedef Htop_Reaction (*Htop_Action)(State* st);
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess); Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess);
bool Action_setUserOnly(const char* userName, uid_t* userId); bool Action_setUserOnly(const char* userName, uid_t* userId);

View File

@ -2,22 +2,27 @@
htop - Affinity.c htop - Affinity.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved. (C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include "Affinity.h" #include "Affinity.h"
#include <stdlib.h> #include <stdlib.h>
#include "XUtils.h"
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
#include <hwloc.h> #include <hwloc.h>
#if __linux__ #include <hwloc/bitmap.h>
#ifdef __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD #define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else #else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS #define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif #endif
#elif HAVE_LINUX_AFFINITY #elif defined(HAVE_LINUX_AFFINITY)
#include <sched.h> #include <sched.h>
#endif #endif
@ -60,7 +65,7 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
} else { } else {
unsigned int id; unsigned int id;
hwloc_bitmap_foreach_begin(id, cpuset); hwloc_bitmap_foreach_begin(id, cpuset);
Affinity_add(affinity, id); Affinity_add(affinity, id);
hwloc_bitmap_foreach_end(); hwloc_bitmap_foreach_end();
} }
} }
@ -69,7 +74,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 (int i = 0; i < this->used; i++) {
hwloc_bitmap_set(cpuset, this->cpus[i]); hwloc_bitmap_set(cpuset, this->cpus[i]);
@ -79,22 +84,25 @@ bool Affinity_set(Process* proc, Arg arg) {
return ok; return ok;
} }
#elif HAVE_LINUX_AFFINITY #elif defined(HAVE_LINUX_AFFINITY)
Affinity* Affinity_get(Process* proc, ProcessList* pl) { Affinity* Affinity_get(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) return NULL; if (!ok)
return NULL;
Affinity* affinity = Affinity_new(pl); Affinity* affinity = Affinity_new(pl);
for (int i = 0; i < pl->cpuCount; i++) { for (int i = 0; i < pl->cpuCount; i++) {
if (CPU_ISSET(i, &cpuset)) if (CPU_ISSET(i, &cpuset)) {
Affinity_add(affinity, i); Affinity_add(affinity, i);
}
} }
return affinity; return affinity;
} }
bool Affinity_set(Process* proc, Arg arg) { 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 (int i = 0; i < this->used; i++) {

View File

@ -4,13 +4,27 @@
htop - Affinity.h htop - Affinity.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved. (C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Process.h" #include "config.h" // IWYU pragma: keep
#include "ProcessList.h" #include "ProcessList.h"
#if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)
#include <stdbool.h>
#include "Object.h"
#include "Process.h"
#endif
#if defined(HAVE_LIBHWLOC) && defined(HAVE_LINUX_AFFINITY)
#error hwloc and linux affinity are mutual exclusive.
#endif
typedef struct Affinity_ { typedef struct Affinity_ {
ProcessList* pl; ProcessList* pl;
int size; int size;
@ -30,6 +44,6 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Arg arg); bool Affinity_set(Process* proc, Arg arg);
#endif #endif /* HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY */
#endif #endif

View File

@ -1,29 +1,41 @@
/* /*
htop - AffinityPanel.c htop - AffinityPanel.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "AffinityPanel.h" #include "config.h" // IWYU pragma: keep
#include "CRT.h"
#include "Vector.h" #include "AffinityPanel.h"
#include <assert.h> #include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "CRT.h"
#include "FunctionBar.h"
#include "Object.h"
#include "ProvideCurses.h"
#include "RichString.h"
#include "Settings.h"
#include "Vector.h"
#include "XUtils.h"
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
#include <hwloc.h> #include <hwloc.h>
#include <hwloc/bitmap.h>
#endif #endif
typedef struct MaskItem_ { typedef struct MaskItem_ {
Object super; Object super;
const char* text; char* text;
const char* indent; /* used also as an condition whether this is a tree node */ char* indent; /* used also as an condition whether this is a tree node */
int value; /* tri-state: 0 - off, 1 - some set, 2 - all set */ int value; /* tri-state: 0 - off, 1 - some set, 2 - all set */
int sub_tree; /* tri-state: 0 - no sub-tree, 1 - open sub-tree, 2 - closed sub-tree */ int sub_tree; /* tri-state: 0 - no sub-tree, 1 - open sub-tree, 2 - closed sub-tree */
Vector *children; Vector* children;
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
bool ownCpuset; bool ownCpuset;
hwloc_bitmap_t cpuset; hwloc_bitmap_t cpuset;
@ -34,9 +46,8 @@ typedef struct MaskItem_ {
static void MaskItem_delete(Object* cast) { static void MaskItem_delete(Object* cast) {
MaskItem* this = (MaskItem*) cast; MaskItem* this = (MaskItem*) cast;
free((void*)this->text); free(this->text);
if (this->indent) free(this->indent);
free((void*)this->indent);
Vector_delete(this->children); Vector_delete(this->children);
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
if (this->ownCpuset) if (this->ownCpuset)
@ -45,16 +56,17 @@ static void MaskItem_delete(Object* cast) {
free(this); free(this);
} }
static void MaskItem_display(Object* cast, RichString* out) { static void MaskItem_display(const Object* cast, RichString* out) {
MaskItem* this = (MaskItem*)cast; const MaskItem* this = (const MaskItem*)cast;
assert (this != NULL); assert (this != NULL);
RichString_append(out, CRT_colors[CHECK_BOX], "["); RichString_append(out, CRT_colors[CHECK_BOX], "[");
if (this->value == 2) if (this->value == 2) {
RichString_append(out, CRT_colors[CHECK_MARK], "x"); RichString_append(out, CRT_colors[CHECK_MARK], "x");
else if (this->value == 1) } else if (this->value == 1) {
RichString_append(out, CRT_colors[CHECK_MARK], "o"); RichString_append(out, CRT_colors[CHECK_MARK], "o");
else } else {
RichString_append(out, CRT_colors[CHECK_MARK], " "); RichString_append(out, CRT_colors[CHECK_MARK], " ");
}
RichString_append(out, CRT_colors[CHECK_BOX], "]"); RichString_append(out, CRT_colors[CHECK_BOX], "]");
RichString_append(out, CRT_colors[CHECK_TEXT], " "); RichString_append(out, CRT_colors[CHECK_TEXT], " ");
if (this->indent) { if (this->indent) {
@ -68,7 +80,7 @@ static void MaskItem_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[CHECK_TEXT], this->text); RichString_append(out, CRT_colors[CHECK_TEXT], this->text);
} }
static ObjectClass MaskItem_class = { static const ObjectClass MaskItem_class = {
.display = MaskItem_display, .display = MaskItem_display,
.delete = MaskItem_delete .delete = MaskItem_delete
}; };
@ -100,11 +112,10 @@ static MaskItem* MaskItem_newSingleton(const char* text, int cpu, bool isSet) {
this->ownCpuset = true; this->ownCpuset = true;
this->cpuset = hwloc_bitmap_alloc(); this->cpuset = hwloc_bitmap_alloc();
hwloc_bitmap_set(this->cpuset, cpu); hwloc_bitmap_set(this->cpuset, cpu);
(void)isSet;
#else #else
this->cpu = cpu; this->cpu = cpu;
#endif #endif
this->value = 2 * isSet; this->value = isSet ? 2 : 0;
return this; return this;
} }
@ -113,11 +124,11 @@ typedef struct AffinityPanel_ {
Panel super; Panel super;
ProcessList* pl; ProcessList* pl;
bool topoView; bool topoView;
Vector *cpuids; Vector* cpuids;
unsigned width; unsigned width;
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
MaskItem *topoRoot; MaskItem* topoRoot;
hwloc_const_cpuset_t allCpuset; hwloc_const_cpuset_t allCpuset;
hwloc_bitmap_t workCpuset; hwloc_bitmap_t workCpuset;
#endif #endif
@ -162,17 +173,18 @@ static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) {
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : ""); FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : "");
FunctionBar_draw(super->currentBar, NULL); FunctionBar_draw(super->currentBar);
int oldSelected = Panel_getSelectedIndex(super); int oldSelected = Panel_getSelectedIndex(super);
Panel_prune(super); Panel_prune(super);
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
if (this->topoView) if (this->topoView) {
AffinityPanel_updateTopo(this, this->topoRoot); AffinityPanel_updateTopo(this, this->topoRoot);
else { } else {
for (int i = 0; i < Vector_size(this->cpuids); i++) for (int i = 0; i < Vector_size(this->cpuids); i++) {
AffinityPanel_updateItem(this, (MaskItem*) Vector_get(this->cpuids, i)); AffinityPanel_updateItem(this, (MaskItem*) Vector_get(this->cpuids, i));
}
} }
#else #else
Panel_splice(super, this->cpuids); Panel_splice(super, this->cpuids);
@ -206,7 +218,7 @@ static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) {
selected->value = 2; selected->value = 2;
} }
#else #else
selected->value = 2 * !selected->value; /* toggle between 0 and 2 */ selected->value = selected->value ? 0 : 2; /* toggle between 0 and 2 */
#endif #endif
result = HANDLED; result = HANDLED;
@ -252,7 +264,7 @@ static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) {
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
static MaskItem *AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem *parent) { static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
const char* type_name = hwloc_obj_type_string(obj->type); const char* type_name = hwloc_obj_type_string(obj->type);
const char* index_prefix = "#"; const char* index_prefix = "#";
unsigned depth = obj->depth; unsigned depth = obj->depth;
@ -271,17 +283,20 @@ static MaskItem *AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
for (unsigned i = 1; i < depth; i++) { for (unsigned i = 1; i < depth; i++) {
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1u << i)) ? CRT_treeStr[TREE_STR_VERT] : " "); xSnprintf(&indent_buf[off], left, "%s ", (indent & (1u << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
size_t len = strlen(&indent_buf[off]); size_t len = strlen(&indent_buf[off]);
off += len, left -= len; off += len;
left -= len;
} }
xSnprintf(&indent_buf[off], left, "%s", xSnprintf(&indent_buf[off], left, "%s",
obj->next_sibling ? CRT_treeStr[TREE_STR_RTEE] : CRT_treeStr[TREE_STR_BEND]); obj->next_sibling ? CRT_treeStr[TREE_STR_RTEE] : CRT_treeStr[TREE_STR_BEND]);
size_t len = strlen(&indent_buf[off]); // Uncomment when further appending to indent_buf
off += len, left -= len; //size_t len = strlen(&indent_buf[off]);
//off += len;
//left -= len;
} }
xSnprintf(buf, 64, "%s %s%u", type_name, index_prefix, index); xSnprintf(buf, sizeof(buf), "%s %s%u", type_name, index_prefix, index);
MaskItem *item = MaskItem_newMask(buf, indent_buf, obj->complete_cpuset, false); MaskItem* item = MaskItem_newMask(buf, indent_buf, obj->complete_cpuset, false);
if (parent) if (parent)
Vector_add(parent->children, item); Vector_add(parent->children, item);
@ -291,34 +306,38 @@ static MaskItem *AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
hwloc_bitmap_and(result, obj->complete_cpuset, this->workCpuset); hwloc_bitmap_and(result, obj->complete_cpuset, this->workCpuset);
int weight = hwloc_bitmap_weight(result); int weight = hwloc_bitmap_weight(result);
hwloc_bitmap_free(result); hwloc_bitmap_free(result);
if (weight == 0 || weight == (hwloc_bitmap_weight(this->workCpuset) + hwloc_bitmap_weight(obj->complete_cpuset))) if (weight == 0 || weight == (hwloc_bitmap_weight(this->workCpuset) + hwloc_bitmap_weight(obj->complete_cpuset))) {
item->sub_tree = 2; item->sub_tree = 2;
}
} }
/* "[x] " + "|- " * depth + ("- ")?(if root node) + name */ /* "[x] " + "|- " * depth + ("- ")?(if root node) + name */
unsigned width = 4 + 3 * depth + (2 * !depth) + strlen(buf); unsigned width = 4 + 3 * depth + (2 * !depth) + strlen(buf);
if (width > this->width) if (width > this->width) {
this->width = width; this->width = width;
}
return item; return item;
} }
static MaskItem *AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem *parent) { static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
MaskItem *item = AffinityPanel_addObject(this, obj, indent, parent); MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent);
if (obj->next_sibling) { if (obj->next_sibling) {
indent |= (1u << obj->depth); indent |= (1u << obj->depth);
} else { } else {
indent &= ~(1u << obj->depth); indent &= ~(1u << obj->depth);
} }
for (unsigned i = 0; i < obj->arity; i++)
for (unsigned i = 0; i < obj->arity; i++) {
AffinityPanel_buildTopology(this, obj->children[i], indent, item); AffinityPanel_buildTopology(this, obj->children[i], indent, item);
}
return parent == NULL ? item : NULL; return parent == NULL ? item : NULL;
} }
#endif #endif
PanelClass AffinityPanel_class = { const PanelClass AffinityPanel_class = {
.super = { .super = {
.extends = Class(Panel), .extends = Class(Panel),
.delete = AffinityPanel_delete .delete = AffinityPanel_delete
@ -369,8 +388,9 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
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);
if (cpu_width > this->width) if (cpu_width > this->width) {
this->width = cpu_width; this->width = cpu_width;
}
bool isSet = false; bool isSet = false;
if (curCpu < affinity->used && affinity->cpus[curCpu] == i) { if (curCpu < affinity->used && affinity->cpus[curCpu] == i) {
@ -389,8 +409,9 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(pl->topology), 0, NULL); this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(pl->topology), 0, NULL);
#endif #endif
if (width) if (width) {
*width = this->width; *width = this->width;
}
AffinityPanel_update(this, false); AffinityPanel_update(this, false);
@ -404,13 +425,14 @@ Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* 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, 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 < this->pl->cpuCount; i++) {
MaskItem* item = (MaskItem*)Vector_get(this->cpuids, i); MaskItem* item = (MaskItem*)Vector_get(this->cpuids, i);
if (item->value) if (item->value) {
Affinity_add(affinity, item->cpu); Affinity_add(affinity, item->cpu);
}
} }
#endif #endif

View File

@ -3,7 +3,7 @@
/* /*
htop - AffinityPanel.h htop - AffinityPanel.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -11,7 +11,7 @@ in the source distribution for its full text.
#include "Affinity.h" #include "Affinity.h"
#include "ProcessList.h" #include "ProcessList.h"
extern PanelClass AffinityPanel_class; extern const PanelClass AffinityPanel_class;
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width); Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width);

View File

@ -1,20 +1,24 @@
/* /*
htop - AvailableColumnsPanel.c htop - AvailableColumnsPanel.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "AvailableColumnsPanel.h" #include "AvailableColumnsPanel.h"
#include "Platform.h"
#include "Header.h"
#include "ColumnsPanel.h"
#include <assert.h>
#include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <stdbool.h>
#include <stdlib.h>
#include "ColumnsPanel.h"
#include "FunctionBar.h"
#include "ListItem.h"
#include "Object.h"
#include "Platform.h"
#include "Process.h"
#include "ProvideCurses.h"
#include "XUtils.h"
static const char* const AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL}; static const char* const AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL};
@ -28,7 +32,6 @@ static void AvailableColumnsPanel_delete(Object* object) {
static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) { static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
AvailableColumnsPanel* this = (AvailableColumnsPanel*) super; AvailableColumnsPanel* this = (AvailableColumnsPanel*) super;
int key = ((ListItem*) Panel_getSelected(super))->key;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
switch(ch) { switch(ch) {
@ -36,6 +39,11 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
case KEY_ENTER: case KEY_ENTER:
case KEY_F(5): case KEY_F(5):
{ {
const ListItem* selected = (ListItem*) Panel_getSelected(super);
if (!selected)
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)); Panel_insert(this->columns, at, (Object*) ListItem_new(Process_fields[key].name, key));
Panel_setSelected(this->columns, at+1); Panel_setSelected(this->columns, at+1);
@ -45,7 +53,7 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
} }
default: default:
{ {
if (ch < 255 && isalpha(ch)) if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
result = Panel_selectByTyping(super, ch); result = Panel_selectByTyping(super, ch);
break; break;
} }
@ -53,7 +61,7 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
return result; return result;
} }
PanelClass AvailableColumnsPanel_class = { const PanelClass AvailableColumnsPanel_class = {
.super = { .super = {
.extends = Class(Panel), .extends = Class(Panel),
.delete = AvailableColumnsPanel_delete .delete = AvailableColumnsPanel_delete

View File

@ -3,7 +3,7 @@
/* /*
htop - AvailableColumnsPanel.h htop - AvailableColumnsPanel.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -14,7 +14,7 @@ typedef struct AvailableColumnsPanel_ {
Panel* columns; Panel* columns;
} AvailableColumnsPanel; } AvailableColumnsPanel;
extern PanelClass AvailableColumnsPanel_class; extern const PanelClass AvailableColumnsPanel_class;
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns); AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns);

View File

@ -1,21 +1,27 @@
/* /*
htop - AvailableMetersPanel.c htop - AvailableMetersPanel.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "AvailableMetersPanel.h" #include "AvailableMetersPanel.h"
#include "MetersPanel.h"
#include "CPUMeter.h"
#include "Header.h"
#include "ListItem.h"
#include "Platform.h"
#include <assert.h> #include <assert.h>
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include "CPUMeter.h"
#include "FunctionBar.h"
#include "Header.h"
#include "ListItem.h"
#include "Meter.h"
#include "MetersPanel.h"
#include "Object.h"
#include "Platform.h"
#include "ProvideCurses.h"
#include "XUtils.h"
static void AvailableMetersPanel_delete(Object* object) { static void AvailableMetersPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
@ -24,19 +30,22 @@ static void AvailableMetersPanel_delete(Object* object) {
free(this); free(this);
} }
static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, MeterClass* type, int param, int column) { static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, const MeterClass* type, int param, int column) {
Meter* meter = (Meter*) Header_addMeterByClass(header, type, param, column); Meter* meter = Header_addMeterByClass(header, type, param, column);
Panel_add(panel, (Object*) Meter_toListItem(meter, false)); Panel_add(panel, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(panel, Panel_size(panel) - 1); Panel_setSelected(panel, Panel_size(panel) - 1);
MetersPanel_setMoving((MetersPanel*)panel, true); MetersPanel_setMoving((MetersPanel*)panel, true);
FunctionBar_draw(panel->currentBar, NULL); FunctionBar_draw(panel->currentBar);
} }
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
AvailableMetersPanel* this = (AvailableMetersPanel*) super; AvailableMetersPanel* this = (AvailableMetersPanel*) super;
Header* header = this->header; Header* header = this->header;
ListItem* selected = (ListItem*) Panel_getSelected(super); const ListItem* selected = (ListItem*) Panel_getSelected(super);
if (!selected)
return IGNORED;
int param = selected->key & 0xff; int param = selected->key & 0xff;
int type = selected->key >> 16; int type = selected->key >> 16;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
@ -74,7 +83,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
return result; return result;
} }
PanelClass AvailableMetersPanel_class = { const PanelClass AvailableMetersPanel_class = {
.super = { .super = {
.extends = Class(Panel), .extends = Class(Panel),
.delete = AvailableMetersPanel_delete .delete = AvailableMetersPanel_delete
@ -98,19 +107,19 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
// 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.
for (int i = 1; Platform_meterTypes[i]; i++) { for (int i = 1; Platform_meterTypes[i]; i++) {
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; const char* label = type->description ? type->description : type->uiName;
Panel_add(super, (Object*) ListItem_new(label, i << 16)); Panel_add(super, (Object*) ListItem_new(label, i << 16));
} }
// Handle (&CPUMeter_class) // Handle (&CPUMeter_class)
MeterClass* type = &CPUMeter_class; const MeterClass* type = &CPUMeter_class;
int cpus = pl->cpuCount; int cpus = pl->cpuCount;
if (cpus > 1) { if (cpus > 1) {
Panel_add(super, (Object*) ListItem_new("CPU average", 0)); Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (int i = 1; i <= cpus; i++) { for (int i = 1; i <= cpus; i++) {
char buffer[50]; char buffer[50];
xSnprintf(buffer, 50, "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1)); xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1));
Panel_add(super, (Object*) ListItem_new(buffer, i)); Panel_add(super, (Object*) ListItem_new(buffer, i));
} }
} else { } else {

View File

@ -3,14 +3,15 @@
/* /*
htop - AvailableMetersPanel.h htop - AvailableMetersPanel.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Settings.h" #include "Header.h"
#include "Panel.h" #include "Panel.h"
#include "ScreenManager.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "ScreenManager.h"
#include "Settings.h"
typedef struct AvailableMetersPanel_ { typedef struct AvailableMetersPanel_ {
Panel super; Panel super;
@ -22,7 +23,7 @@ typedef struct AvailableMetersPanel_ {
Panel* rightPanel; Panel* rightPanel;
} AvailableMetersPanel; } AvailableMetersPanel;
extern 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, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl);

View File

@ -1,7 +1,7 @@
/* /*
htop - BatteryMeter.c htop - BatteryMeter.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com). This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
@ -9,29 +9,27 @@ This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include "Battery.h" #include <math.h>
#include "ProcessList.h"
#include "CRT.h" #include "CRT.h"
#include "StringUtils.h" #include "Object.h"
#include "Platform.h" #include "Platform.h"
#include "XUtils.h"
#include <string.h>
#include <stdlib.h>
int BatteryMeter_attributes[] = { static const int BatteryMeter_attributes[] = {
BATTERY BATTERY
}; };
static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) { static void BatteryMeter_updateValues(Meter* this, char* buffer, int len) {
ACPresence isOnAC; ACPresence isOnAC;
double percent; double percent;
Battery_getData(&percent, &isOnAC); Platform_getBattery(&percent, &isOnAC);
if (percent == -1) { if (isnan(percent)) {
this->values[0] = 0; this->values[0] = NAN;
xSnprintf(buffer, len, "n/a"); xSnprintf(buffer, len, "N/A");
return; return;
} }
@ -55,11 +53,9 @@ static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) {
} else { } else {
xSnprintf(buffer, len, unknownText, percent); xSnprintf(buffer, len, unknownText, percent);
} }
return;
} }
MeterClass BatteryMeter_class = { const MeterClass BatteryMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete .delete = Meter_delete

View File

@ -3,7 +3,7 @@
/* /*
htop - BatteryMeter.h htop - BatteryMeter.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com). This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
@ -17,8 +17,6 @@ typedef enum ACPresence_ {
AC_ERROR AC_ERROR
} ACPresence; } ACPresence;
extern int BatteryMeter_attributes[]; extern const MeterClass BatteryMeter_class;
extern MeterClass BatteryMeter_class;
#endif #endif

View File

@ -32,3 +32,14 @@ Feature Requests
Please label Github issues that are feature requests with the [`feature Please label Github issues that are feature requests with the [`feature
request`](https://github.com/htop-dev/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+) request`](https://github.com/htop-dev/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+)
label. label.
Style Guide
-----------
To make working with the code easier a set of guidelines have evolved in
the past that new contributions should try to follow. While they are not set
in stone and always up for changes should the need arise they still provide
a first orientation to go by when contributing to this repository.
The details of the coding style as well as what to take care about with your
contributions can be found in our [style guide](docs/styleguide.md).

View File

@ -1,30 +1,49 @@
/* /*
htop - CPUMeter.c htop - CPUMeter.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include "CPUMeter.h" #include "CPUMeter.h"
#include "CRT.h" #include <math.h>
#include "Settings.h" #include <stdint.h>
#include "Platform.h"
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <math.h>
int CPUMeter_attributes[] = { #include "CRT.h"
CPU_NICE, CPU_NORMAL, CPU_SYSTEM, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, CPU_IOWAIT #include "Object.h"
#include "Platform.h"
#include "ProcessList.h"
#include "RichString.h"
#include "Settings.h"
#include "XUtils.h"
static const int CPUMeter_attributes[] = {
CPU_NICE,
CPU_NORMAL,
CPU_SYSTEM,
CPU_IRQ,
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
CPU_IOWAIT
}; };
typedef struct CPUMeterData_ {
int cpus;
Meter** meters;
} CPUMeterData;
static void CPUMeter_init(Meter* this) { static void CPUMeter_init(Meter* this) {
int cpu = this->param; int cpu = this->param;
if (this->pl->cpuCount > 1) { if (this->pl->cpuCount > 1) {
char caption[10]; char caption[10];
xSnprintf(caption, sizeof(caption), "%-3d", Settings_cpuId(this->pl->settings, cpu - 1)); xSnprintf(caption, sizeof(caption), "%3d", Settings_cpuId(this->pl->settings, cpu - 1));
Meter_setCaption(this, caption); Meter_setCaption(this, caption);
} }
if (this->param == 0) if (this->param == 0)
@ -35,33 +54,54 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) {
int cpu = this->param; int cpu = this->param;
if (cpu > this->pl->cpuCount) { if (cpu > this->pl->cpuCount) {
xSnprintf(buffer, size, "absent"); xSnprintf(buffer, size, "absent");
for (uint8_t i = 0; i < this->curItems; i++)
this->values[i] = 0;
return; return;
} }
memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT); memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT);
char cpuUsageBuffer[8] = { 0 };
char cpuFrequencyBuffer[16] = { 0 };
char cpuTemperatureBuffer[16] = { 0 };
double percent = Platform_setCPUValues(this, cpu); double percent = Platform_setCPUValues(this, cpu);
if (this->pl->settings->showCPUUsage) {
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%5.1f%%", percent);
}
if (this->pl->settings->showCPUFrequency) { if (this->pl->settings->showCPUFrequency) {
double cpuFrequency = this->values[CPU_METER_FREQUENCY]; double cpuFrequency = this->values[CPU_METER_FREQUENCY];
char cpuFrequencyBuffer[16]; if (isnan(cpuFrequency)) {
if (cpuFrequency < 0) {
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A"); xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A");
} else { } else {
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%.0fMHz", cpuFrequency); xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%4uMHz", (unsigned)cpuFrequency);
} }
if (this->pl->settings->showCPUUsage) {
xSnprintf(buffer, size, "%5.1f%% %s", percent, cpuFrequencyBuffer);
} else {
xSnprintf(buffer, size, "%s", cpuFrequencyBuffer);
}
} else if (this->pl->settings->showCPUUsage) {
xSnprintf(buffer, size, "%5.1f%%", percent);
} else if (size > 0) {
buffer[0] = '\0';
} }
#ifdef HAVE_SENSORS_SENSORS_H
if (this->pl->settings->showCPUTemperature) {
double cpuTemperature = this->values[CPU_METER_TEMPERATURE];
if (isnan(cpuTemperature))
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A");
else if (this->pl->settings->degreeFahrenheit)
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%3d%sF", (int)(cpuTemperature * 9 / 5 + 32), CRT_degreeSign);
else
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%d%sC", (int)cpuTemperature, CRT_degreeSign);
}
#endif
xSnprintf(buffer, size, "%s%s%s%s%s",
cpuUsageBuffer,
(cpuUsageBuffer[0] && (cpuFrequencyBuffer[0] || cpuTemperatureBuffer[0])) ? " " : "",
cpuFrequencyBuffer,
(cpuFrequencyBuffer[0] && cpuTemperatureBuffer[0]) ? " " : "",
cpuTemperatureBuffer);
} }
static void CPUMeter_display(Object* cast, RichString* out) { static void CPUMeter_display(const Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
Meter* this = (Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_prune(out); RichString_prune(out);
if (this->param > this->pl->cpuCount) { if (this->param > this->pl->cpuCount) {
RichString_append(out, CRT_colors[METER_TEXT], "absent"); RichString_append(out, CRT_colors[METER_TEXT], "absent");
@ -83,12 +123,12 @@ static void CPUMeter_display(Object* cast, RichString* out) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "si:"); RichString_append(out, CRT_colors[METER_TEXT], "si:");
RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer); RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer);
if (this->values[CPU_METER_STEAL]) { if (!isnan(this->values[CPU_METER_STEAL])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]);
RichString_append(out, CRT_colors[METER_TEXT], "st:"); RichString_append(out, CRT_colors[METER_TEXT], "st:");
RichString_append(out, CRT_colors[CPU_STEAL], buffer); RichString_append(out, CRT_colors[CPU_STEAL], buffer);
} }
if (this->values[CPU_METER_GUEST]) { if (!isnan(this->values[CPU_METER_GUEST])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]);
RichString_append(out, CRT_colors[METER_TEXT], "gu:"); RichString_append(out, CRT_colors[METER_TEXT], "gu:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer); RichString_append(out, CRT_colors[CPU_GUEST], buffer);
@ -103,16 +143,33 @@ static void CPUMeter_display(Object* cast, RichString* out) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "low:"); RichString_append(out, CRT_colors[METER_TEXT], "low:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
if (this->values[CPU_METER_IRQ]) { if (!isnan(this->values[CPU_METER_IRQ])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "vir:"); RichString_append(out, CRT_colors[METER_TEXT], "vir:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer); RichString_append(out, CRT_colors[CPU_GUEST], buffer);
} }
} }
#ifdef HAVE_SENSORS_SENSORS_H
if (this->pl->settings->showCPUTemperature) {
char cpuTemperatureBuffer[10];
double cpuTemperature = this->values[CPU_METER_TEMPERATURE];
if (isnan(cpuTemperature)) {
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A");
} else if (this->pl->settings->degreeFahrenheit) {
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sF", cpuTemperature * 9 / 5 + 32, CRT_degreeSign);
} else {
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign);
}
RichString_append(out, CRT_colors[METER_TEXT], "temp:");
RichString_append(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer);
}
#endif
} }
static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) { static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) {
int cpus = this->pl->cpuCount; CPUMeterData* data = this->meterData;
int cpus = data->cpus;
switch(Meter_name(this)[0]) { switch(Meter_name(this)[0]) {
default: default:
case 'A': // All case 'A': // All
@ -130,44 +187,34 @@ static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) {
} }
} }
static int MapClassnameToColumncount(Meter* this){ static void CPUMeterCommonInit(Meter* this, int ncol) {
if (strchr(Meter_name(this), '4'))
return 4;
else if (strchr(Meter_name(this), '2'))
return 2;
else
return 1;
}
static void AllCPUsMeter_init(Meter* this) {
int cpus = this->pl->cpuCount; int cpus = this->pl->cpuCount;
if (!this->drawData) CPUMeterData* data = this->meterData;
this->drawData = xCalloc(cpus, sizeof(Meter*)); if (!data) {
Meter** meters = (Meter**) this->drawData; data = this->meterData = xMalloc(sizeof(CPUMeterData));
data->cpus = cpus;
data->meters = xCalloc(cpus, sizeof(Meter*));
}
Meter** meters = data->meters;
int start, count; int start, count;
AllCPUsMeter_getRange(this, &start, &count); AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
if (!meters[i]) if (!meters[i])
meters[i] = Meter_new(this->pl, start+i+1, (MeterClass*) Class(CPUMeter)); meters[i] = Meter_new(this->pl, start + i + 1, (const MeterClass*) Class(CPUMeter));
Meter_init(meters[i]); Meter_init(meters[i]);
} }
if (this->mode == 0) if (this->mode == 0)
this->mode = BAR_METERMODE; this->mode = BAR_METERMODE;
int h = Meter_modes[this->mode]->h; int h = Meter_modes[this->mode]->h;
int ncol = MapClassnameToColumncount(this); this->h = h * ((count + ncol - 1) / ncol);
this->h = h * ((count + ncol - 1)/ ncol);
} }
static void AllCPUsMeter_done(Meter* this) { static void CPUMeterCommonUpdateMode(Meter* this, int mode, int ncol) {
Meter** meters = (Meter**) this->drawData; CPUMeterData* data = this->meterData;
int start, count; Meter** meters = data->meters;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++)
Meter_delete((Object*)meters[i]);
}
static void AllCPUsMeter_updateMode(Meter* this, int mode) {
Meter** meters = (Meter**) this->drawData;
this->mode = mode; this->mode = mode;
int h = Meter_modes[mode]->h; int h = Meter_modes[mode]->h;
int start, count; int start, count;
@ -175,30 +222,84 @@ static void AllCPUsMeter_updateMode(Meter* this, int mode) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
Meter_setMode(meters[i], mode); Meter_setMode(meters[i], mode);
} }
int ncol = MapClassnameToColumncount(this); this->h = h * ((count + ncol - 1) / ncol);
this->h = h * ((count + ncol - 1)/ ncol); }
static void AllCPUsMeter_done(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_delete((Object*)meters[i]);
free(data->meters);
free(data);
}
static void SingleColCPUsMeter_init(Meter* this) {
CPUMeterCommonInit(this, 1);
}
static void SingleColCPUsMeter_updateMode(Meter* this, int mode) {
CPUMeterCommonUpdateMode(this, mode, 1);
}
static void DualColCPUsMeter_init(Meter* this) {
CPUMeterCommonInit(this, 2);
}
static void DualColCPUsMeter_updateMode(Meter* this, int mode) {
CPUMeterCommonUpdateMode(this, mode, 2);
}
static void QuadColCPUsMeter_init(Meter* this) {
CPUMeterCommonInit(this, 4);
}
static void QuadColCPUsMeter_updateMode(Meter* this, int mode) {
CPUMeterCommonUpdateMode(this, mode, 4);
}
static void OctoColCPUsMeter_init(Meter* this) {
CPUMeterCommonInit(this, 8);
}
static void OctoColCPUsMeter_updateMode(Meter* this, int mode) {
CPUMeterCommonUpdateMode(this, mode, 8);
}
static void CPUMeterCommonDraw(Meter* this, int x, int y, int w, int ncol) {
CPUMeterData* data = this->meterData;
Meter** meters = data->meters;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
int colwidth = (w - ncol) / ncol + 1;
int diff = (w - (colwidth * ncol));
int nrows = (count + ncol - 1) / ncol;
for (int i = 0; i < count; i++) {
int d = (i / nrows) > diff ? diff : (i / nrows); // dynamic spacer
int xpos = x + ((i / nrows) * colwidth) + d;
int ypos = y + ((i % nrows) * meters[0]->h);
meters[i]->draw(meters[i], xpos, ypos, colwidth);
}
} }
static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) { static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) {
Meter** meters = (Meter**) this->drawData; CPUMeterCommonDraw(this, x, y, w, 2);
int start, count;
int pad = this->pl->settings->headerMargin ? 2 : 0;
AllCPUsMeter_getRange(this, &start, &count);
int height = (count+1)/2;
int startY = y;
for (int i = 0; i < height; i++) {
meters[i]->draw(meters[i], x, y, (w-pad)/2);
y += meters[i]->h;
}
y = startY;
for (int i = height; i < count; i++) {
meters[i]->draw(meters[i], x+(w-1)/2+1+(pad/2), y, (w-pad)/2);
y += meters[i]->h;
}
} }
static void QuadColCPUsMeter_draw(Meter* this, int x, int y, int w) {
CPUMeterCommonDraw(this, x, y, w, 4);
}
static void OctoColCPUsMeter_draw(Meter* this, int x, int y, int w) {
CPUMeterCommonDraw(this, x, y, w, 8);
}
static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) { static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) {
Meter** meters = (Meter**) this->drawData; CPUMeterData* data = this->meterData;
Meter** meters = data->meters;
int start, count; int start, count;
AllCPUsMeter_getRange(this, &start, &count); AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@ -207,23 +308,8 @@ static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) {
} }
} }
static void MultiColCPUsMeter_draw(Meter* this, int x, int y, int w){
Meter** meters = (Meter**) this->drawData;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
int ncol = MapClassnameToColumncount(this);
int colwidth = (w-ncol)/ncol + 1;
int diff = (w - (colwidth * ncol));
int nrows = (count + ncol - 1) / ncol;
for (int i = 0; i < count; i++){
int d = (i/nrows) > diff ? diff : (i / nrows) ; // dynamic spacer
int xpos = x + ((i / nrows) * colwidth) + d;
int ypos = y + ((i % nrows) * meters[0]->h);
meters[i]->draw(meters[i], xpos, ypos, colwidth);
}
}
MeterClass CPUMeter_class = { const MeterClass CPUMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
@ -240,7 +326,7 @@ MeterClass CPUMeter_class = {
.init = CPUMeter_init .init = CPUMeter_init
}; };
MeterClass AllCPUsMeter_class = { const MeterClass AllCPUsMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
@ -254,12 +340,12 @@ MeterClass AllCPUsMeter_class = {
.description = "CPUs (1/1): all CPUs", .description = "CPUs (1/1): all CPUs",
.caption = "CPU", .caption = "CPU",
.draw = SingleColCPUsMeter_draw, .draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = SingleColCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode, .updateMode = SingleColCPUsMeter_updateMode,
.done = AllCPUsMeter_done .done = AllCPUsMeter_done
}; };
MeterClass AllCPUs2Meter_class = { const MeterClass AllCPUs2Meter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
@ -273,12 +359,12 @@ MeterClass AllCPUs2Meter_class = {
.description = "CPUs (1&2/2): all CPUs in 2 shorter columns", .description = "CPUs (1&2/2): all CPUs in 2 shorter columns",
.caption = "CPU", .caption = "CPU",
.draw = DualColCPUsMeter_draw, .draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = DualColCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode, .updateMode = DualColCPUsMeter_updateMode,
.done = AllCPUsMeter_done .done = AllCPUsMeter_done
}; };
MeterClass LeftCPUsMeter_class = { const MeterClass LeftCPUsMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
@ -292,12 +378,12 @@ MeterClass LeftCPUsMeter_class = {
.description = "CPUs (1/2): first half of list", .description = "CPUs (1/2): first half of list",
.caption = "CPU", .caption = "CPU",
.draw = SingleColCPUsMeter_draw, .draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = SingleColCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode, .updateMode = SingleColCPUsMeter_updateMode,
.done = AllCPUsMeter_done .done = AllCPUsMeter_done
}; };
MeterClass RightCPUsMeter_class = { const MeterClass RightCPUsMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
@ -311,12 +397,12 @@ MeterClass RightCPUsMeter_class = {
.description = "CPUs (2/2): second half of list", .description = "CPUs (2/2): second half of list",
.caption = "CPU", .caption = "CPU",
.draw = SingleColCPUsMeter_draw, .draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = SingleColCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode, .updateMode = SingleColCPUsMeter_updateMode,
.done = AllCPUsMeter_done .done = AllCPUsMeter_done
}; };
MeterClass LeftCPUs2Meter_class = { const MeterClass LeftCPUs2Meter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
@ -330,12 +416,12 @@ MeterClass LeftCPUs2Meter_class = {
.description = "CPUs (1&2/4): first half in 2 shorter columns", .description = "CPUs (1&2/4): first half in 2 shorter columns",
.caption = "CPU", .caption = "CPU",
.draw = DualColCPUsMeter_draw, .draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = DualColCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode, .updateMode = DualColCPUsMeter_updateMode,
.done = AllCPUsMeter_done .done = AllCPUsMeter_done
}; };
MeterClass RightCPUs2Meter_class = { const MeterClass RightCPUs2Meter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
@ -349,12 +435,12 @@ MeterClass RightCPUs2Meter_class = {
.description = "CPUs (3&4/4): second half in 2 shorter columns", .description = "CPUs (3&4/4): second half in 2 shorter columns",
.caption = "CPU", .caption = "CPU",
.draw = DualColCPUsMeter_draw, .draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = DualColCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode, .updateMode = DualColCPUsMeter_updateMode,
.done = AllCPUsMeter_done .done = AllCPUsMeter_done
}; };
MeterClass AllCPUs4Meter_class = { const MeterClass AllCPUs4Meter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
@ -367,13 +453,13 @@ MeterClass AllCPUs4Meter_class = {
.uiName = "CPUs (1&2&3&4/4)", .uiName = "CPUs (1&2&3&4/4)",
.description = "CPUs (1&2&3&4/4): all CPUs in 4 shorter columns", .description = "CPUs (1&2&3&4/4): all CPUs in 4 shorter columns",
.caption = "CPU", .caption = "CPU",
.draw = MultiColCPUsMeter_draw, .draw = QuadColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = QuadColCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode, .updateMode = QuadColCPUsMeter_updateMode,
.done = AllCPUsMeter_done .done = AllCPUsMeter_done
}; };
MeterClass LeftCPUs4Meter_class = { const MeterClass LeftCPUs4Meter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
@ -386,13 +472,13 @@ MeterClass LeftCPUs4Meter_class = {
.uiName = "CPUs (1-4/8)", .uiName = "CPUs (1-4/8)",
.description = "CPUs (1-4/8): first half in 4 shorter columns", .description = "CPUs (1-4/8): first half in 4 shorter columns",
.caption = "CPU", .caption = "CPU",
.draw = MultiColCPUsMeter_draw, .draw = QuadColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = QuadColCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode, .updateMode = QuadColCPUsMeter_updateMode,
.done = AllCPUsMeter_done .done = AllCPUsMeter_done
}; };
MeterClass RightCPUs4Meter_class = { const MeterClass RightCPUs4Meter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
@ -405,8 +491,65 @@ MeterClass RightCPUs4Meter_class = {
.uiName = "CPUs (5-8/8)", .uiName = "CPUs (5-8/8)",
.description = "CPUs (5-8/8): second half in 4 shorter columns", .description = "CPUs (5-8/8): second half in 4 shorter columns",
.caption = "CPU", .caption = "CPU",
.draw = MultiColCPUsMeter_draw, .draw = QuadColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = QuadColCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode, .updateMode = QuadColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
const MeterClass AllCPUs8Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "AllCPUs8",
.uiName = "CPUs (1-8/8)",
.description = "CPUs (1-8/8): all CPUs in 8 shorter columns",
.caption = "CPU",
.draw = OctoColCPUsMeter_draw,
.init = OctoColCPUsMeter_init,
.updateMode = OctoColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
const MeterClass LeftCPUs8Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs8",
.uiName = "CPUs (1-8/16)",
.description = "CPUs (1-8/16): first half in 8 shorter columns",
.caption = "CPU",
.draw = OctoColCPUsMeter_draw,
.init = OctoColCPUsMeter_init,
.updateMode = OctoColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
const MeterClass RightCPUs8Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "RightCPUs8",
.uiName = "CPUs (9-16/16)",
.description = "CPUs (9-16/16): second half in 8 shorter columns",
.caption = "CPU",
.draw = OctoColCPUsMeter_draw,
.init = OctoColCPUsMeter_init,
.updateMode = OctoColCPUsMeter_updateMode,
.done = AllCPUsMeter_done .done = AllCPUsMeter_done
}; };

View File

@ -3,7 +3,7 @@
/* /*
htop - CPUMeter.h htop - CPUMeter.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -19,29 +19,34 @@ typedef enum {
CPU_METER_GUEST = 6, CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7, CPU_METER_IOWAIT = 7,
CPU_METER_FREQUENCY = 8, CPU_METER_FREQUENCY = 8,
CPU_METER_ITEMCOUNT = 9, // number of entries in this enum CPU_METER_TEMPERATURE = 9,
CPU_METER_ITEMCOUNT = 10, // number of entries in this enum
} CPUMeterValues; } CPUMeterValues;
extern int CPUMeter_attributes[]; extern const MeterClass CPUMeter_class;
extern MeterClass CPUMeter_class; extern const MeterClass AllCPUsMeter_class;
extern MeterClass AllCPUsMeter_class; extern const MeterClass AllCPUs2Meter_class;
extern MeterClass AllCPUs2Meter_class; extern const MeterClass LeftCPUsMeter_class;
extern MeterClass LeftCPUsMeter_class; extern const MeterClass RightCPUsMeter_class;
extern MeterClass RightCPUsMeter_class; extern const MeterClass LeftCPUs2Meter_class;
extern MeterClass LeftCPUs2Meter_class; extern const MeterClass RightCPUs2Meter_class;
extern MeterClass RightCPUs2Meter_class; extern const MeterClass AllCPUs4Meter_class;
extern MeterClass AllCPUs4Meter_class; extern const MeterClass LeftCPUs4Meter_class;
extern MeterClass LeftCPUs4Meter_class; extern const MeterClass RightCPUs4Meter_class;
extern MeterClass RightCPUs4Meter_class; extern const MeterClass AllCPUs8Meter_class;
extern const MeterClass LeftCPUs8Meter_class;
extern const MeterClass RightCPUs8Meter_class;
#endif #endif

980
CRT.c

File diff suppressed because it is too large Load Diff

93
CRT.h
View File

@ -3,15 +3,17 @@
/* /*
htop - CRT.h htop - CRT.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h"
#include <stdbool.h> #include <stdbool.h>
#define KEY_WHEELUP KEY_F(20) #include "Macros.h"
#define KEY_WHEELDOWN KEY_F(21) #include "ProvideCurses.h"
#define KEY_RECLICK KEY_F(22)
typedef enum TreeStr_ { typedef enum TreeStr_ {
TREE_STR_HORZ, TREE_STR_HORZ,
@ -26,13 +28,13 @@ typedef enum TreeStr_ {
typedef enum ColorSchemes_ { typedef enum ColorSchemes_ {
COLORSCHEME_DEFAULT = 0, COLORSCHEME_DEFAULT = 0,
COLORSCHEME_MONOCHROME = 1, COLORSCHEME_MONOCHROME,
COLORSCHEME_BLACKONWHITE = 2, COLORSCHEME_BLACKONWHITE,
COLORSCHEME_LIGHTTERMINAL = 3, COLORSCHEME_LIGHTTERMINAL,
COLORSCHEME_MIDNIGHT = 4, COLORSCHEME_MIDNIGHT,
COLORSCHEME_BLACKNIGHT = 5, COLORSCHEME_BLACKNIGHT,
COLORSCHEME_BROKENGRAY = 6, COLORSCHEME_BROKENGRAY,
LAST_COLORSCHEME = 7, LAST_COLORSCHEME,
} ColorSchemes; } ColorSchemes;
typedef enum ColorElements_ { typedef enum ColorElements_ {
@ -41,6 +43,8 @@ typedef enum ColorElements_ {
FUNCTION_BAR, FUNCTION_BAR,
FUNCTION_KEY, FUNCTION_KEY,
FAILED_SEARCH, FAILED_SEARCH,
FAILED_READ,
PAUSED,
PANEL_HEADER_FOCUS, PANEL_HEADER_FOCUS,
PANEL_HEADER_UNFOCUS, PANEL_HEADER_UNFOCUS,
PANEL_SELECTION_FOCUS, PANEL_SELECTION_FOCUS,
@ -49,6 +53,11 @@ typedef enum ColorElements_ {
LARGE_NUMBER, LARGE_NUMBER,
METER_TEXT, METER_TEXT,
METER_VALUE, METER_VALUE,
METER_VALUE_ERROR,
METER_VALUE_IOREAD,
METER_VALUE_IOWRITE,
METER_VALUE_NOTICE,
METER_VALUE_OK,
LED_COLOR, LED_COLOR,
UPTIME, UPTIME,
BATTERY, BATTERY,
@ -58,14 +67,19 @@ typedef enum ColorElements_ {
PROCESS_SHADOW, PROCESS_SHADOW,
PROCESS_TAG, PROCESS_TAG,
PROCESS_MEGABYTES, PROCESS_MEGABYTES,
PROCESS_GIGABYTES,
PROCESS_TREE, PROCESS_TREE,
PROCESS_R_STATE, PROCESS_R_STATE,
PROCESS_D_STATE, PROCESS_D_STATE,
PROCESS_BASENAME, PROCESS_BASENAME,
PROCESS_HIGH_PRIORITY, PROCESS_HIGH_PRIORITY,
PROCESS_LOW_PRIORITY, PROCESS_LOW_PRIORITY,
PROCESS_NEW,
PROCESS_TOMB,
PROCESS_THREAD, PROCESS_THREAD,
PROCESS_THREAD_BASENAME, PROCESS_THREAD_BASENAME,
PROCESS_COMM,
PROCESS_THREAD_COMM,
BAR_BORDER, BAR_BORDER,
BAR_SHADOW, BAR_SHADOW,
GRAPH_1, GRAPH_1,
@ -82,6 +96,8 @@ typedef enum ColorElements_ {
CHECK_MARK, CHECK_MARK,
CHECK_TEXT, CHECK_TEXT,
CLOCK, CLOCK,
DATE,
DATETIME,
HELP_BOLD, HELP_BOLD,
HOSTNAME, HOSTNAME,
CPU_NICE, CPU_NICE,
@ -103,31 +119,30 @@ typedef enum ColorElements_ {
ZFS_OTHER, ZFS_OTHER,
ZFS_COMPRESSED, ZFS_COMPRESSED,
ZFS_RATIO, ZFS_RATIO,
ZRAM,
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
void CRT_fatalError(const char* note) __attribute__ ((noreturn)); void CRT_fatalError(const char* note) ATTR_NORETURN;
void CRT_handleSIGSEGV(int sgn); void CRT_handleSIGSEGV(int signal) ATTR_NORETURN;
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A')) #define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
#define KEY_RECLICK KEY_F(22)
#define KEY_ALT(x) (KEY_F(64 - 26) + ((x) - 'A'))
extern const char* CRT_degreeSign;
extern const char *CRT_treeStrAscii[TREE_STR_COUNT];
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
extern const char *CRT_treeStrUtf8[TREE_STR_COUNT];
extern bool CRT_utf8; extern bool CRT_utf8;
#endif #endif
extern const char **CRT_treeStr; extern const char* const* CRT_treeStr;
extern int CRT_delay; extern const int* CRT_colors;
extern int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT]; extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
@ -137,40 +152,36 @@ extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount; extern int CRT_scrollWheelVAmount;
extern char* CRT_termType; extern const char* CRT_termType;
extern int CRT_colorScheme; extern int CRT_colorScheme;
extern void *backtraceArray[128]; extern long CRT_pageSize;
extern long CRT_pageSizeKB;
#if HAVE_SETUID_ENABLED #ifdef HAVE_SETUID_ENABLED
void CRT_dropPrivileges(); void CRT_dropPrivileges(void);
void CRT_restorePrivileges(); void CRT_restorePrivileges(void);
#else #else /* HAVE_SETUID_ENABLED */
/* Turn setuid operations into NOPs */ /* Turn setuid operations into NOPs */
static inline void CRT_dropPrivileges(void) { }
static inline void CRT_restorePrivileges(void) { }
#ifndef CRT_dropPrivileges #endif /* HAVE_SETUID_ENABLED */
#define CRT_dropPrivileges()
#define CRT_restorePrivileges()
#endif
#endif void CRT_init(const int* delay, int colorScheme, bool allowUnicode);
void CRT_init(int delay, int colorScheme, bool allowUnicode); void CRT_done(void);
void CRT_done(); int CRT_readKey(void);
void CRT_fatalError(const char* note); void CRT_disableDelay(void);
int CRT_readKey(); void CRT_enableDelay(void);
void CRT_disableDelay();
void CRT_enableDelay();
void CRT_setColors(int colorScheme); void CRT_setColors(int colorScheme);

View File

@ -1,22 +1,28 @@
/* /*
htop - CategoriesPanel.c htop - CategoriesPanel.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "CategoriesPanel.h" #include "CategoriesPanel.h"
#include "AvailableMetersPanel.h" #include <ctype.h>
#include "MetersPanel.h" #include <stdbool.h>
#include "DisplayOptionsPanel.h"
#include "ColumnsPanel.h"
#include "ColorsPanel.h"
#include "AvailableColumnsPanel.h"
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include "AvailableColumnsPanel.h"
#include "AvailableMetersPanel.h"
#include "ColorsPanel.h"
#include "ColumnsPanel.h"
#include "DisplayOptionsPanel.h"
#include "FunctionBar.h"
#include "ListItem.h"
#include "MetersPanel.h"
#include "Object.h"
#include "ProvideCurses.h"
#include "Vector.h"
static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@ -81,7 +87,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
break; break;
} }
default: default:
if (ch < 255 && isalpha(ch)) if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
result = Panel_selectByTyping(super, ch); result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP) if (result == BREAK_LOOP)
result = IGNORED; result = IGNORED;
@ -91,6 +97,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
int size = ScreenManager_size(this->scr); int size = ScreenManager_size(this->scr);
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) { switch (selected) {
case 0: case 0:
CategoriesPanel_makeMetersPage(this); CategoriesPanel_makeMetersPage(this);
@ -109,7 +116,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
return result; return result;
} }
PanelClass CategoriesPanel_class = { const PanelClass CategoriesPanel_class = {
.super = { .super = {
.extends = Class(Panel), .extends = Class(Panel),
.delete = CategoriesPanel_delete .delete = CategoriesPanel_delete

View File

@ -3,14 +3,15 @@
/* /*
htop - CategoriesPanel.h htop - CategoriesPanel.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Header.h"
#include "Panel.h" #include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "ScreenManager.h"
#include "Settings.h"
typedef struct CategoriesPanel_ { typedef struct CategoriesPanel_ {
Panel super; Panel super;
@ -23,7 +24,7 @@ typedef struct CategoriesPanel_ {
void CategoriesPanel_makeMetersPage(CategoriesPanel* this); void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
extern 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);

View File

@ -1,3 +1,77 @@
What's new in version 3.0.3
* Process sorting in 'tree' mode
(thanks to Maxim Zhiburt)
* Improved command display/sort functionality
(thanks to Narendran Gopalakrishnan)
* Add screen for active file locks
* Calculate library size (M_LRS column) from maps file
(thanks to Fynn Wulf)
* Add a Zram meter
(thanks to Murloc Knight)
* Add Linux cwd process column
* Dynamically load libsensors at runtime
* Improve PressureStall Meter display strings
* Hide process selection on ESC
* Fully support non-ascii characters in Meter-Bar
* Add support to change numeric options in settings screen
* Rename virtual memory column from M_SIZE to M_VIRT
* Add process column for normalized CPU usage
* Show CPU temperature in CPU meter
* Drop hideThreads Setting
* Add a systemd meter
* Add a network IO meter
* Add a SELinux meter
* Compress size of default FunctionBar
* Updates to the OpenFiles screen
* Continue updating header data in paused mode
* BUGFIX: Handle data wraparounds in IO meters
* BUGFIX: Update InfoScreen content on resize
* Add security attribute process column
* Add DiskIOMeter for IO read/write usage
* Read CPU frequency from sysfs by default
* Add Linux process column for context switches
* Several FreeBSD and Mac OS X platform updates
(thanks to Christian Göttsche)
* Add process environment for FreeBSD
(thanks to Ross Williams)
* Parse POWER_SUPPLY_CAPACITY for Linux Battery meter
(thanks to Jan Palus)
* Add octuple-column CPU meters.
* BUGFIX: On Linux consider ZFS ARC to be cache
(thanks to @multi)
* BUGFIX: Limit screen title length to window width
* Show selected command wrapped in a separate window
(thanks to @ryenus)
* Allow to pass '/' for item search
* Document implicit incremental search
* Handle 'q' as quit if first character
* Avoid expensive build of process tree when not using it
* Include documentation for COMM and EXE
* Distinguish display of no permissions for reading M_LRS
* Only calculate M_LRS size every 5 seconds
* Improvements to comm / cmdline display functionality
* Consistent kernel thread display for COMM/EXE columns
* Central fault handling for all platforms
* Handle parsing envID & VPid from process status file
* Use threshold for display of guest/steal/irq meters
* Enhance highlighting of semi-large and large numbers
* Documentation on the repository style guide
(thanks to Benny Baumann)
* Align processor identifier to the right
(thanks to Christian Hesse)
* Document M_PSS, M_PSSWP, M_SWAP in man page
* Add Date and DateTime meters
(thanks to Michael F. Schönitzer)
* BUGFIX: Fix Solaris 11.4 due to missing ZFS ARC kstats
(thanks to @senjan)
* Code hardening, speedups, fd and memory leak fixes
(thanks to Christian Göttsche and Benny Baumann)
* Number CPUs from zero by default
(thanks to Zev Weiss)
* Remove residual python checks during the build process
(thanks to Stephen Gregoratto)
What's new in version 3.0.2 What's new in version 3.0.2
* BUGFIX: Drop 'vim_mode' - several issues, needs rethink * BUGFIX: Drop 'vim_mode' - several issues, needs rethink

View File

@ -1,69 +0,0 @@
/*
htop - CheckItem.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "CheckItem.h"
#include "CRT.h"
#include <assert.h>
#include <stdlib.h>
static void CheckItem_delete(Object* cast) {
CheckItem* this = (CheckItem*)cast;
assert (this != NULL);
free(this->text);
free(this);
}
static void CheckItem_display(Object* cast, RichString* out) {
CheckItem* this = (CheckItem*)cast;
assert (this != NULL);
RichString_write(out, CRT_colors[CHECK_BOX], "[");
if (CheckItem_get(this))
RichString_append(out, CRT_colors[CHECK_MARK], "x");
else
RichString_append(out, CRT_colors[CHECK_MARK], " ");
RichString_append(out, CRT_colors[CHECK_BOX], "] ");
RichString_append(out, CRT_colors[CHECK_TEXT], this->text);
}
ObjectClass CheckItem_class = {
.display = CheckItem_display,
.delete = CheckItem_delete
};
CheckItem* CheckItem_newByRef(char* text, bool* ref) {
CheckItem* this = AllocThis(CheckItem);
this->text = text;
this->value = false;
this->ref = ref;
return this;
}
CheckItem* CheckItem_newByVal(char* text, bool value) {
CheckItem* this = AllocThis(CheckItem);
this->text = text;
this->value = value;
this->ref = NULL;
return this;
}
void CheckItem_set(CheckItem* this, bool value) {
if (this->ref)
*(this->ref) = value;
else
this->value = value;
}
bool CheckItem_get(CheckItem* this) {
if (this->ref)
return *(this->ref);
else
return this->value;
}

View File

@ -1,29 +0,0 @@
#ifndef HEADER_CheckItem
#define HEADER_CheckItem
/*
htop - CheckItem.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Object.h"
typedef struct CheckItem_ {
Object super;
char* text;
bool* ref;
bool value;
} CheckItem;
extern ObjectClass CheckItem_class;
CheckItem* CheckItem_newByRef(char* text, bool* ref);
CheckItem* CheckItem_newByVal(char* text, bool value);
void CheckItem_set(CheckItem* this, bool value);
bool CheckItem_get(CheckItem* this);
#endif

View File

@ -1,30 +1,33 @@
/* /*
htop - ClockMeter.c htop - ClockMeter.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "ClockMeter.h" #include "config.h" // IWYU pragma: keep
#include "CRT.h" #include "ClockMeter.h"
#include <time.h> #include <time.h>
#include "CRT.h"
#include "Object.h"
int ClockMeter_attributes[] = {
static const int ClockMeter_attributes[] = {
CLOCK CLOCK
}; };
static void ClockMeter_updateValues(Meter* this, char* buffer, int size) { static void ClockMeter_updateValues(Meter* this, char* buffer, int size) {
time_t t = time(NULL); time_t t = time(NULL);
struct tm result; struct tm result;
struct tm *lt = localtime_r(&t, &result); struct tm* lt = localtime_r(&t, &result);
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(buffer, size, "%H:%M:%S", lt);
} }
MeterClass ClockMeter_class = { const MeterClass ClockMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete .delete = Meter_delete

View File

@ -3,14 +3,12 @@
/* /*
htop - ClockMeter.h htop - ClockMeter.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.h"
extern int ClockMeter_attributes[]; extern const MeterClass ClockMeter_class;
extern MeterClass ClockMeter_class;
#endif #endif

View File

@ -1,18 +1,25 @@
/* /*
htop - ColorsPanel.c htop - ColorsPanel.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "ColorsPanel.h" #include "ColorsPanel.h"
#include "CRT.h" #include <stdbool.h>
#include "CheckItem.h"
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "CRT.h"
#include "FunctionBar.h"
#include "Header.h"
#include "Object.h"
#include "OptionItem.h"
#include "ProvideCurses.h"
#include "RichString.h"
#include "Vector.h"
#include "XUtils.h"
// TO ADD A NEW SCHEME: // TO ADD A NEW SCHEME:
// * Increment the size of bool check in ColorsPanel.h // * Increment the size of bool check in ColorsPanel.h
@ -57,6 +64,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
for (int i = 0; ColorSchemeNames[i] != NULL; i++) for (int i = 0; ColorSchemeNames[i] != NULL; i++)
CheckItem_set((CheckItem*)Panel_get(super, i), false); CheckItem_set((CheckItem*)Panel_get(super, i), false);
CheckItem_set((CheckItem*)Panel_get(super, mark), true); CheckItem_set((CheckItem*)Panel_get(super, mark), true);
this->settings->colorScheme = mark; this->settings->colorScheme = mark;
result = HANDLED; result = HANDLED;
} }
@ -68,6 +76,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
clear(); clear();
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0); Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
Header_draw(header); Header_draw(header);
FunctionBar_draw(super->currentBar);
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]); RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);
RichString_setAttr(&(menu->header), CRT_colors[PANEL_HEADER_UNFOCUS]); RichString_setAttr(&(menu->header), CRT_colors[PANEL_HEADER_UNFOCUS]);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2); ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
@ -75,7 +84,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
return result; return result;
} }
PanelClass ColorsPanel_class = { const PanelClass ColorsPanel_class = {
.super = { .super = {
.extends = Class(Panel), .extends = Class(Panel),
.delete = ColorsPanel_delete .delete = ColorsPanel_delete
@ -94,7 +103,7 @@ ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) {
Panel_setHeader(super, "Colors"); Panel_setHeader(super, "Colors");
for (int i = 0; ColorSchemeNames[i] != NULL; i++) { for (int i = 0; ColorSchemeNames[i] != NULL; i++) {
Panel_add(super, (Object*) CheckItem_newByVal(xStrdup(ColorSchemeNames[i]), false)); Panel_add(super, (Object*) CheckItem_newByVal(ColorSchemeNames[i], false));
} }
CheckItem_set((CheckItem*)Panel_get(super, settings->colorScheme), true); CheckItem_set((CheckItem*)Panel_get(super, settings->colorScheme), true);
return this; return this;

View File

@ -3,13 +3,13 @@
/* /*
htop - ColorsPanel.h htop - ColorsPanel.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Panel.h" #include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h" #include "ScreenManager.h"
#include "Settings.h"
typedef struct ColorsPanel_ { typedef struct ColorsPanel_ {
Panel super; Panel super;
@ -18,7 +18,7 @@ typedef struct ColorsPanel_ {
ScreenManager* scr; ScreenManager* scr;
} ColorsPanel; } ColorsPanel;
extern PanelClass ColorsPanel_class; extern const PanelClass ColorsPanel_class;
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr); ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr);

View File

@ -1,20 +1,22 @@
/* /*
htop - ColumnsPanel.c htop - ColumnsPanel.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "ColumnsPanel.h" #include "ColumnsPanel.h"
#include "Platform.h"
#include "StringUtils.h"
#include "ListItem.h"
#include "CRT.h"
#include <assert.h>
#include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <stdlib.h>
#include "CRT.h"
#include "FunctionBar.h"
#include "ListItem.h"
#include "Object.h"
#include "Process.h"
#include "ProvideCurses.h"
#include "XUtils.h"
static const char* const ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL}; static const char* const ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
@ -43,7 +45,9 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
if (selected < size - 1) { if (selected < size - 1) {
this->moving = !(this->moving); this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]); Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
((ListItem*)Panel_getSelected(super))->moving = this->moving; ListItem* selectedItem = (ListItem*) Panel_getSelected(super);
if (selectedItem)
selectedItem->moving = this->moving;
result = HANDLED; result = HANDLED;
} }
break; break;
@ -91,7 +95,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
} }
default: default:
{ {
if (ch < 255 && isalpha(ch)) if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
result = Panel_selectByTyping(super, ch); result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP) if (result == BREAK_LOOP)
result = IGNORED; result = IGNORED;
@ -103,7 +107,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
return result; return result;
} }
PanelClass ColumnsPanel_class = { const PanelClass ColumnsPanel_class = {
.super = { .super = {
.extends = Class(Panel), .extends = Class(Panel),
.delete = ColumnsPanel_delete .delete = ColumnsPanel_delete
@ -130,20 +134,11 @@ ColumnsPanel* ColumnsPanel_new(Settings* settings) {
return this; return this;
} }
int ColumnsPanel_fieldNameToIndex(const char* name) {
for (int j = 1; j <= Platform_numberOfFields; j++) {
if (String_eq(name, Process_fields[j].name)) {
return j;
}
}
return -1;
}
void ColumnsPanel_update(Panel* super) { void ColumnsPanel_update(Panel* super) {
ColumnsPanel* this = (ColumnsPanel*) super; ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_size(super); int size = Panel_size(super);
this->settings->changed = true; this->settings->changed = true;
this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size+1)); this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size + 1));
this->settings->flags = 0; this->settings->flags = 0;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
int key = ((ListItem*) Panel_get(super, i))->key; int key = ((ListItem*) Panel_get(super, i))->key;

View File

@ -3,10 +3,12 @@
/* /*
htop - ColumnsPanel.h htop - ColumnsPanel.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include <stdbool.h>
#include "Panel.h" #include "Panel.h"
#include "Settings.h" #include "Settings.h"
@ -17,12 +19,10 @@ typedef struct ColumnsPanel_ {
bool moving; bool moving;
} ColumnsPanel; } ColumnsPanel;
extern PanelClass ColumnsPanel_class; extern const PanelClass ColumnsPanel_class;
ColumnsPanel* ColumnsPanel_new(Settings* settings); ColumnsPanel* ColumnsPanel_new(Settings* settings);
int ColumnsPanel_fieldNameToIndex(const char* name);
void ColumnsPanel_update(Panel* super); void ColumnsPanel_update(Panel* super);
#endif #endif

68
CommandScreen.c Normal file
View File

@ -0,0 +1,68 @@
#include "config.h" // IWYU pragma: keep
#include "CommandScreen.h"
#include <stdlib.h>
#include <string.h>
#include "Macros.h"
#include "Panel.h"
#include "ProvideCurses.h"
#include "XUtils.h"
static void CommandScreen_scan(InfoScreen* this) {
Panel* panel = this->display;
int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
Panel_prune(panel);
const char* p = Process_getCommand(this->process);
char* line = xMalloc(COLS + 1);
int line_offset = 0, last_spc = -1, len;
for (; *p != '\0'; p++, line_offset++) {
line[line_offset] = *p;
if (*p == ' ') {
last_spc = line_offset;
}
if (line_offset == COLS) {
len = (last_spc == -1) ? line_offset : last_spc;
line[len] = '\0';
InfoScreen_addLine(this, line);
line_offset -= len;
last_spc = -1;
memcpy(line, p - line_offset, line_offset + 1);
}
}
if (line_offset > 0) {
line[line_offset] = '\0';
InfoScreen_addLine(this, line);
}
free(line);
Panel_setSelected(panel, idx);
}
static void CommandScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Command of process %d - %s", this->process->pid, Process_getCommand(this->process));
}
const InfoScreenClass CommandScreen_class = {
.super = {
.extends = Class(Object),
.delete = CommandScreen_delete
},
.scan = CommandScreen_scan,
.draw = CommandScreen_draw
};
CommandScreen* CommandScreen_new(Process* process) {
CommandScreen* this = AllocThis(CommandScreen);
return (CommandScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " ");
}
void CommandScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this));
}

19
CommandScreen.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef HEADER_CommandScreen
#define HEADER_CommandScreen
#include "InfoScreen.h"
#include "Object.h"
#include "Process.h"
typedef struct CommandScreen_ {
InfoScreen super;
} CommandScreen;
extern const InfoScreenClass CommandScreen_class;
CommandScreen* CommandScreen_new(Process* process);
void CommandScreen_delete(Object* this);
#endif

119
Compat.c Normal file
View File

@ -0,0 +1,119 @@
/*
htop - Compat.c
(C) 2020 Christian Göttsche
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include "Compat.h"
#include <errno.h>
#include <fcntl.h> // IWYU pragma: keep
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h> // IWYU pragma: keep
#include "XUtils.h" // IWYU pragma: keep
int Compat_faccessat(int dirfd,
const char* pathname,
int mode,
int flags) {
int ret;
#ifdef HAVE_FACCESSAT
// Implementation note: AT_SYMLINK_NOFOLLOW unsupported on FreeBSD, fallback to lstat in that case
errno = 0;
ret = faccessat(dirfd, pathname, mode, flags);
if (!ret || errno != EINVAL)
return ret;
#endif
// Error out on unsupported configurations
if (dirfd != AT_FDCWD || mode != F_OK) {
errno = EINVAL;
return -1;
}
// Fallback to stat(2)/lstat(2) depending on flags
struct stat statinfo;
if(flags) {
ret = lstat(pathname, &statinfo);
} else {
ret = stat(pathname, &statinfo);
}
return ret;
}
int Compat_fstatat(int dirfd,
const char* dirpath,
const char* pathname,
struct stat* statbuf,
int flags) {
#ifdef HAVE_FSTATAT
(void)dirpath;
return fstatat(dirfd, pathname, statbuf, flags);
#else
(void)dirfd;
char path[4096];
xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname);
if (flags & AT_SYMLINK_NOFOLLOW)
return lstat(path, statbuf);
return stat(path, statbuf);
#endif
}
#ifndef HAVE_OPENAT
int Compat_openat(const char* dirpath,
const char* pathname,
int flags) {
char path[4096];
xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname);
return open(path, flags);
}
#endif /* !HAVE_OPENAT */
int Compat_readlinkat(int dirfd,
const char* dirpath,
const char* pathname,
char* buf,
size_t bufsize) {
#ifdef HAVE_READLINKAT
(void)dirpath;
return readlinkat(dirfd, pathname, buf, bufsize);
#else
(void)dirfd;
char path[4096];
xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname);
return readlink(path, buf, bufsize);
#endif
}

59
Compat.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef HEADER_Compat
#define HEADER_Compat
/*
htop - Compat.h
(C) 2020 Christian Göttsche
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include <fcntl.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/stat.h>
int Compat_faccessat(int dirfd,
const char* pathname,
int mode,
int flags);
int Compat_fstatat(int dirfd,
const char* dirpath,
const char* pathname,
struct stat* statbuf,
int flags);
#ifdef HAVE_OPENAT
typedef int openat_arg_t;
static inline void Compat_openatArgClose(openat_arg_t dirfd) {
close(dirfd);
}
static inline int Compat_openat(openat_arg_t dirfd, const char* pathname, int flags) {
return openat(dirfd, pathname, flags);
}
#else /* HAVE_OPENAT */
typedef const char* openat_arg_t;
static inline void Compat_openatArgClose(openat_arg_t dirpath) {
(void)dirpath;
}
int Compat_openat(openat_arg_t dirpath, const char* pathname, int flags);
#endif /* HAVE_OPENAT */
int Compat_readlinkat(int dirfd,
const char* dirpath,
const char* pathname,
char* buf,
size_t bufsize);
#endif /* HEADER_Compat */

50
DateMeter.c Normal file
View File

@ -0,0 +1,50 @@
/*
htop - DateMeter.c
(C) 2004-2020 Hisham H. Muhammad, Michael Schönitzer
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include "DateMeter.h"
#include <time.h>
#include "CRT.h"
#include "Object.h"
static const int DateMeter_attributes[] = {
DATE
};
static void DateMeter_updateValues(Meter* this, char* buffer, int size) {
time_t t = time(NULL);
struct tm result;
struct tm* lt = localtime_r(&t, &result);
this->values[0] = lt->tm_yday;
int year = lt->tm_year + 1900;
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
this->total = 366;
}
else {
this->total = 365;
}
strftime(buffer, size, "%F", lt);
}
const MeterClass DateMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete
},
.updateValues = DateMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 365,
.attributes = DateMeter_attributes,
.name = "Date",
.uiName = "Date",
.caption = "Date: ",
};

14
DateMeter.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef HEADER_DateMeter
#define HEADER_DateMeter
/*
htop - DateMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
extern const MeterClass DateMeter_class;
#endif

50
DateTimeMeter.c Normal file
View File

@ -0,0 +1,50 @@
/*
htop - DateTimeMeter.c
(C) 2004-2020 Hisham H. Muhammad, Michael Schönitzer
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include "DateTimeMeter.h"
#include <time.h>
#include "CRT.h"
#include "Object.h"
static const int DateTimeMeter_attributes[] = {
DATETIME
};
static void DateTimeMeter_updateValues(Meter* this, char* buffer, int size) {
time_t t = time(NULL);
struct tm result;
struct tm* lt = localtime_r(&t, &result);
int year = lt->tm_year + 1900;
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
this->total = 366;
}
else {
this->total = 365;
}
this->values[0] = lt->tm_yday;
strftime(buffer, size, "%F %H:%M:%S", lt);
}
const MeterClass DateTimeMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete
},
.updateValues = DateTimeMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 365,
.attributes = DateTimeMeter_attributes,
.name = "DateTime",
.uiName = "Date and Time",
.caption = "Date & Time: ",
};

14
DateTimeMeter.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef HEADER_DateTimeMeter
#define HEADER_DateTimeMeter
/*
htop - DateTimeMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
extern const MeterClass DateTimeMeter_class;
#endif

124
DiskIOMeter.c Normal file
View File

@ -0,0 +1,124 @@
/*
htop - DiskIOMeter.c
(C) 2020 Christian Göttsche
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "DiskIOMeter.h"
#include <stdbool.h>
#include <stdio.h>
#include <sys/time.h>
#include "CRT.h"
#include "Macros.h"
#include "Object.h"
#include "Platform.h"
#include "RichString.h"
#include "XUtils.h"
static const int DiskIOMeter_attributes[] = {
METER_VALUE_NOTICE,
METER_VALUE_IOREAD,
METER_VALUE_IOWRITE,
};
static bool hasData = false;
static unsigned long int cached_read_diff = 0;
static unsigned long int cached_write_diff = 0;
static double cached_utilisation_diff = 0.0;
static void DiskIOMeter_updateValues(Meter* this, char* buffer, int len) {
static unsigned long long int cached_last_update = 0;
struct timeval tv;
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 */
if (passedTimeInMs > 500) {
static unsigned long int cached_read_total = 0;
static unsigned long int cached_write_total = 0;
static unsigned long int cached_msTimeSpend_total = 0;
cached_last_update = timeInMilliSeconds;
DiskIOData data;
hasData = Platform_getDiskIO(&data);
if (!hasData) {
this->values[0] = 0;
xSnprintf(buffer, len, "no data");
return;
}
if (data.totalBytesRead > cached_read_total) {
cached_read_diff = (data.totalBytesRead - cached_read_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
} else {
cached_read_diff = 0;
}
cached_read_total = data.totalBytesRead;
if (data.totalBytesWritten > cached_write_total) {
cached_write_diff = (data.totalBytesWritten - cached_write_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
} else {
cached_write_diff = 0;
}
cached_write_total = data.totalBytesWritten;
if (data.totalMsTimeSpend > cached_msTimeSpend_total) {
cached_utilisation_diff = 100 * (double)(data.totalMsTimeSpend - cached_msTimeSpend_total) / passedTimeInMs;
} else {
cached_utilisation_diff = 0.0;
}
cached_msTimeSpend_total = data.totalMsTimeSpend;
}
this->values[0] = cached_utilisation_diff;
this->total = MAXIMUM(this->values[0], 100.0); /* fix total after (initial) spike */
char bufferRead[12], bufferWrite[12];
Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead));
Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite));
snprintf(buffer, len, "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff);
}
static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
if (!hasData) {
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data");
return;
}
char buffer[16];
int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
RichString_write(out, CRT_colors[color], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " read: ");
Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " write: ");
Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
}
const MeterClass DiskIOMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = DIskIOMeter_display
},
.updateValues = DiskIOMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = DiskIOMeter_attributes,
.name = "DiskIO",
.uiName = "Disk IO",
.caption = "Disk IO: "
};

20
DiskIOMeter.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef HEADER_DiskIOMeter
#define HEADER_DiskIOMeter
/*
h top - DiskIOMeter*.h
(C) 2020 Christian Göttsche
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
typedef struct DiskIOData_ {
unsigned long int totalBytesRead;
unsigned long int totalBytesWritten;
unsigned long int totalMsTimeSpend;
} DiskIOData;
extern const MeterClass DiskIOMeter_class;
#endif /* HEADER_DiskIOMeter */

View File

@ -1,18 +1,24 @@
/* /*
htop - DisplayOptionsPanel.c htop - DisplayOptionsPanel.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include "DisplayOptionsPanel.h" #include "DisplayOptionsPanel.h"
#include "CheckItem.h" #include <stdbool.h>
#include "CRT.h"
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "CRT.h"
#include "FunctionBar.h"
#include "Header.h"
#include "Object.h"
#include "OptionItem.h"
#include "ProvideCurses.h"
#include "XUtils.h"
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@ -28,31 +34,52 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
DisplayOptionsPanel* this = (DisplayOptionsPanel*) super; DisplayOptionsPanel* this = (DisplayOptionsPanel*) super;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
CheckItem* selected = (CheckItem*) Panel_getSelected(super); OptionItem* selected = (OptionItem*) Panel_getSelected(super);
switch(ch) { switch (ch) {
case 0x0a: case '\n':
case 0x0d: case '\r':
case KEY_ENTER: case KEY_ENTER:
case KEY_MOUSE: case KEY_MOUSE:
case KEY_RECLICK: case KEY_RECLICK:
case ' ': case ' ':
CheckItem_set(selected, ! (CheckItem_get(selected)) ); switch (OptionItem_kind(selected)) {
result = HANDLED; case OPTION_ITEM_CHECK:
CheckItem_toggle((CheckItem*)selected);
result = HANDLED;
break;
case OPTION_ITEM_NUMBER:
NumberItem_toggle((NumberItem*)selected);
result = HANDLED;
break;
}
break;
case '-':
if (OptionItem_kind(selected) == OPTION_ITEM_NUMBER) {
NumberItem_decrease((NumberItem*)selected);
result = HANDLED;
}
break;
case '+':
if (OptionItem_kind(selected) == OPTION_ITEM_NUMBER) {
NumberItem_increase((NumberItem*)selected);
result = HANDLED;
}
break;
} }
if (result == HANDLED) { if (result == HANDLED) {
this->settings->changed = true; this->settings->changed = true;
const Header* header = this->scr->header; Header* header = this->scr->header;
Header_calculateHeight((Header*) header); Header_calculateHeight(header);
Header_reinit((Header*) header); Header_reinit(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, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
} }
return result; return result;
} }
PanelClass DisplayOptionsPanel_class = { const PanelClass DisplayOptionsPanel_class = {
.super = { .super = {
.extends = Class(Panel), .extends = Class(Panel),
.delete = DisplayOptionsPanel_delete .delete = DisplayOptionsPanel_delete
@ -64,31 +91,41 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
DisplayOptionsPanel* this = AllocThis(DisplayOptionsPanel); DisplayOptionsPanel* this = AllocThis(DisplayOptionsPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(DisplayOptionsFunctions, NULL, NULL); FunctionBar* fuBar = FunctionBar_new(DisplayOptionsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar); Panel_init(super, 1, 1, 1, 1, Class(OptionItem), true, fuBar);
this->settings = settings; this->settings = settings;
this->scr = scr; this->scr = scr;
Panel_setHeader(super, "Display options"); Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView))); Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers))); Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Display threads in a different color"), &(settings->highlightThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Display threads in a different color", &(settings->highlightThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show custom thread names"), &(settings->showThreadNames))); Panel_add(super, (Object*) CheckItem_newByRef("Show custom thread names", &(settings->showThreadNames)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show program path"), &(settings->showProgramPath))); Panel_add(super, (Object*) CheckItem_newByRef("Show program path", &(settings->showProgramPath)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight program \"basename\""), &(settings->highlightBaseName))); Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight large numbers in memory counters"), &(settings->highlightMegabytes))); Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Leave a margin around header"), &(settings->headerMargin))); Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)"), &(settings->detailedCPUTime))); Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero))); Panel_add(super, (Object*) CheckItem_newByRef("Highlight large numbers in memory counters", &(settings->highlightMegabytes)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames))); Panel_add(super, (Object*) CheckItem_newByRef("Leave a margin around header", &(settings->headerMargin)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter))); Panel_add(super, (Object*) CheckItem_newByRef("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)", &(settings->detailedCPUTime)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU percentage numerically"), &(settings->showCPUUsage))); Panel_add(super, (Object*) CheckItem_newByRef("Count CPUs from 1 instead of 0", &(settings->countCPUsFromOne)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency))); Panel_add(super, (Object*) CheckItem_newByRef("Update process names on every refresh", &(settings->updateProcessNames)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable the mouse"), &(settings->enableMouse))); 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 frequency", &(settings->showCPUFrequency)));
#ifdef HAVE_SENSORS_SENSORS_H
Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU temperature (requires libsensors)", &(settings->showCPUTemperature)));
Panel_add(super, (Object*) CheckItem_newByRef("- Show temperature in degree Fahrenheit instead of Celsius", &(settings->degreeFahrenheit)));
#endif
Panel_add(super, (Object*) CheckItem_newByRef("Enable the mouse", &(settings->enableMouse)));
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*) NumberItem_newByRef("- Highlight time (in seconds)", &(settings->highlightDelaySecs), 0, 1, 24*60*60));
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show topology when selecting affinity by default"), &(settings->topologyAffinity))); Panel_add(super, (Object*) CheckItem_newByRef("Show topology when selecting affinity by default", &(settings->topologyAffinity)));
#endif #endif
return this; return this;
} }

View File

@ -3,13 +3,13 @@
/* /*
htop - DisplayOptionsPanel.h htop - DisplayOptionsPanel.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Panel.h" #include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h" #include "ScreenManager.h"
#include "Settings.h"
typedef struct DisplayOptionsPanel_ { typedef struct DisplayOptionsPanel_ {
Panel super; Panel super;
@ -18,7 +18,7 @@ typedef struct DisplayOptionsPanel_ {
ScreenManager* scr; ScreenManager* scr;
} DisplayOptionsPanel; } DisplayOptionsPanel;
extern PanelClass DisplayOptionsPanel_class; extern const PanelClass DisplayOptionsPanel_class;
DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr); DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr);

View File

@ -1,18 +1,20 @@
#include "EnvScreen.h" #include "config.h" // IWYU pragma: keep
#include "config.h" #include "EnvScreen.h"
#include "CRT.h"
#include "IncSet.h"
#include "ListItem.h"
#include "Platform.h"
#include "StringUtils.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "CRT.h"
#include "Macros.h"
#include "Panel.h"
#include "Platform.h"
#include "ProvideCurses.h"
#include "Vector.h"
#include "XUtils.h"
InfoScreenClass EnvScreen_class = { const InfoScreenClass EnvScreen_class = {
.super = { .super = {
.extends = Class(Object), .extends = Class(Object),
.delete = EnvScreen_delete .delete = EnvScreen_delete
@ -24,7 +26,7 @@ InfoScreenClass EnvScreen_class = {
EnvScreen* EnvScreen_new(Process* process) { EnvScreen* EnvScreen_new(Process* process) {
EnvScreen* this = xMalloc(sizeof(EnvScreen)); EnvScreen* this = xMalloc(sizeof(EnvScreen));
Object_setClass(this, Class(EnvScreen)); Object_setClass(this, Class(EnvScreen));
return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " "); return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " ");
} }
void EnvScreen_delete(Object* this) { void EnvScreen_delete(Object* this) {
@ -32,7 +34,7 @@ void EnvScreen_delete(Object* this) {
} }
void EnvScreen_draw(InfoScreen* this) { void EnvScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, this->process->comm); InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process));
} }
void EnvScreen_scan(InfoScreen* this) { void EnvScreen_scan(InfoScreen* this) {
@ -45,7 +47,7 @@ void EnvScreen_scan(InfoScreen* this) {
char* env = Platform_getProcessEnv(this->process->pid); char* env = Platform_getProcessEnv(this->process->pid);
CRT_restorePrivileges(); CRT_restorePrivileges();
if (env) { if (env) {
for (char *p = env; *p; p = strrchr(p, 0)+1) for (char* p = env; *p; p = strrchr(p, 0) + 1)
InfoScreen_addLine(this, p); InfoScreen_addLine(this, p);
free(env); free(env);
} }

View File

@ -2,12 +2,14 @@
#define HEADER_EnvScreen #define HEADER_EnvScreen
#include "InfoScreen.h" #include "InfoScreen.h"
#include "Object.h"
#include "Process.h"
typedef struct EnvScreen_ { typedef struct EnvScreen_ {
InfoScreen super; InfoScreen super;
} EnvScreen; } EnvScreen;
extern InfoScreenClass EnvScreen_class; extern const InfoScreenClass EnvScreen_class;
EnvScreen* EnvScreen_new(Process* process); EnvScreen* EnvScreen_new(Process* process);

View File

@ -1,18 +1,21 @@
/* /*
htop - FunctionBar.c htop - FunctionBar.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "FunctionBar.h" #include "config.h" // IWYU pragma: keep
#include "CRT.h"
#include "RichString.h" #include "FunctionBar.h"
#include "XAlloc.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "CRT.h"
#include "Macros.h"
#include "ProvideCurses.h"
#include "XUtils.h"
static const char* const FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL}; static const char* const FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL};
@ -24,6 +27,8 @@ static int FunctionBar_FEvents[] = {KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_
static const char* const FunctionBar_EnterEscKeys[] = {"Enter", "Esc", NULL}; static const char* const FunctionBar_EnterEscKeys[] = {"Enter", "Esc", NULL};
static const int FunctionBar_EnterEscEvents[] = {13, 27}; static const int FunctionBar_EnterEscEvents[] = {13, 27};
static int currentLen = 0;
FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc) { FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc) {
const char* functions[] = {enter, esc, NULL}; const char* functions[] = {enter, esc, NULL};
return FunctionBar_new(functions, FunctionBar_EnterEscKeys, FunctionBar_EnterEscEvents); return FunctionBar_new(functions, FunctionBar_EnterEscKeys, FunctionBar_EnterEscEvents);
@ -40,20 +45,20 @@ FunctionBar* FunctionBar_new(const char* const* functions, const char* const* ke
} }
if (keys && events) { if (keys && events) {
this->staticData = false; this->staticData = false;
this->keys = xCalloc(15, sizeof(char*)); this->keys.keys = xCalloc(15, sizeof(char*));
this->events = xCalloc(15, sizeof(int)); this->events = xCalloc(15, sizeof(int));
int i = 0; int i = 0;
while (i < 15 && functions[i]) { while (i < 15 && functions[i]) {
this->keys[i] = xStrdup(keys[i]); this->keys.keys[i] = xStrdup(keys[i]);
this->events[i] = events[i]; this->events[i] = events[i];
i++; i++;
} }
this->size = i; this->size = i;
} else { } else {
this->staticData = true; this->staticData = true;
this->keys = (char**) FunctionBar_FKeys; this->keys.constKeys = FunctionBar_FKeys;
this->events = FunctionBar_FEvents; this->events = FunctionBar_FEvents;
this->size = 10; this->size = ARRAYSIZE(FunctionBar_FEvents);
} }
return this; return this;
} }
@ -65,9 +70,9 @@ void FunctionBar_delete(FunctionBar* this) {
free(this->functions); free(this->functions);
if (!this->staticData) { if (!this->staticData) {
for (int i = 0; i < this->size; i++) { for (int i = 0; i < this->size; i++) {
free(this->keys[i]); free(this->keys.keys[i]);
} }
free(this->keys); free(this->keys.keys);
free(this->events); free(this->events);
} }
free(this); free(this);
@ -83,37 +88,60 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
} }
} }
void FunctionBar_draw(const FunctionBar* this, char* buffer) { void FunctionBar_draw(const FunctionBar* this) {
FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]); FunctionBar_drawExtra(this, NULL, -1, false);
} }
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) { void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor) {
attrset(CRT_colors[FUNCTION_BAR]); attrset(CRT_colors[FUNCTION_BAR]);
mvhline(LINES-1, 0, ' ', COLS); mvhline(LINES - 1, 0, ' ', COLS);
int x = 0; int x = 0;
for (int i = 0; i < this->size; i++) { for (int i = 0; i < this->size; i++) {
attrset(CRT_colors[FUNCTION_KEY]); attrset(CRT_colors[FUNCTION_KEY]);
mvaddstr(LINES-1, x, this->keys[i]); mvaddstr(LINES - 1, x, this->keys.constKeys[i]);
x += strlen(this->keys[i]); x += strlen(this->keys.constKeys[i]);
attrset(CRT_colors[FUNCTION_BAR]); attrset(CRT_colors[FUNCTION_BAR]);
mvaddstr(LINES-1, x, this->functions[i]); mvaddstr(LINES - 1, x, this->functions[i]);
x += strlen(this->functions[i]); x += strlen(this->functions[i]);
} }
if (buffer) { if (buffer) {
attrset(attr); if (attr == -1) {
mvaddstr(LINES-1, x, buffer); attrset(CRT_colors[FUNCTION_BAR]);
CRT_cursorX = x + strlen(buffer); } else {
attrset(attr);
}
mvaddstr(LINES - 1, x, buffer);
attrset(CRT_colors[RESET_COLOR]);
x += strlen(buffer);
}
if (setCursor) {
CRT_cursorX = x;
curs_set(1); curs_set(1);
} else { } else {
curs_set(0); curs_set(0);
} }
currentLen = x;
}
void FunctionBar_append(const char* buffer, int attr) {
if (attr == -1) {
attrset(CRT_colors[FUNCTION_BAR]);
} else {
attrset(attr);
}
mvaddstr(LINES - 1, currentLen, buffer);
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
currentLen += strlen(buffer);
} }
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) { int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {
int x = 0; int x = 0;
for (int i = 0; i < this->size; i++) { for (int i = 0; i < this->size; i++) {
x += strlen(this->keys[i]); x += strlen(this->keys.constKeys[i]);
x += strlen(this->functions[i]); x += strlen(this->functions[i]);
if (pos < x) { if (pos < x) {
return this->events[i]; return this->events[i];

View File

@ -3,7 +3,7 @@
/* /*
htop - FunctionBar.h htop - FunctionBar.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -12,7 +12,10 @@ in the source distribution for its full text.
typedef struct FunctionBar_ { typedef struct FunctionBar_ {
int size; int size;
char** functions; char** functions;
char** keys; union {
char** keys;
const char* const* constKeys;
} keys;
int* events; int* events;
bool staticData; bool staticData;
} FunctionBar; } FunctionBar;
@ -25,9 +28,11 @@ void FunctionBar_delete(FunctionBar* this);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text); void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
void FunctionBar_draw(const FunctionBar* this, char* buffer); void FunctionBar_draw(const FunctionBar* this);
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr); void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor);
void FunctionBar_append(const char* buffer, int attr);
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos); int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);

View File

@ -1,152 +1,304 @@
/* /*
htop - Hashtable.c htop - Hashtable.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include "Hashtable.h" #include "Hashtable.h"
#include "XAlloc.h"
#include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "Macros.h"
#include "XUtils.h"
#ifdef DEBUG #ifndef NDEBUG
static bool Hashtable_isConsistent(Hashtable* this) { static void Hashtable_dump(const Hashtable* this) {
int items = 0; fprintf(stderr, "Hashtable %p: size=%u items=%u owner=%s\n",
for (int i = 0; i < this->size; i++) { (const void*)this,
HashtableItem* bucket = this->buckets[i]; this->size,
while (bucket) { this->items,
this->owner ? "yes" : "no");
unsigned int items = 0;
for (unsigned int i = 0; i < this->size; i++) {
fprintf(stderr, " item %5u: key = %5u probe = %2u value = %p\n",
i,
this->buckets[i].key,
this->buckets[i].probe,
this->buckets[i].value ? (const void*)this->buckets[i].value : "(nil)");
if (this->buckets[i].value)
items++; items++;
bucket = bucket->next;
}
} }
return items == this->items;
fprintf(stderr, "Hashtable %p: items=%u counted=%u\n",
(const void*)this,
this->items,
items);
} }
int Hashtable_count(Hashtable* this) { static bool Hashtable_isConsistent(const Hashtable* this) {
int items = 0; unsigned int items = 0;
for (int i = 0; i < this->size; i++) { for (unsigned int i = 0; i < this->size; i++) {
HashtableItem* bucket = this->buckets[i]; if (this->buckets[i].value)
while (bucket) { items++;
}
bool res = items == this->items;
if (!res)
Hashtable_dump(this);
return res;
}
unsigned int Hashtable_count(const Hashtable* this) {
unsigned int items = 0;
for (unsigned int i = 0; i < this->size; i++) {
if (this->buckets[i].value)
items++; items++;
bucket = bucket->next;
}
} }
assert(items == this->items); assert(items == this->items);
return items; return items;
} }
#endif #endif /* NDEBUG */
static HashtableItem* HashtableItem_new(unsigned int key, void* value) { /* https://oeis.org/A014234 */
HashtableItem* this; static const uint64_t OEISprimes[] = {
2, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191,
16381, 32749, 65521, 131071, 262139, 524287, 1048573,
2097143, 4194301, 8388593, 16777213, 33554393,
67108859, 134217689, 268435399, 536870909, 1073741789,
2147483647, 4294967291, 8589934583, 17179869143,
34359738337, 68719476731, 137438953447
};
this = xMalloc(sizeof(HashtableItem)); static uint64_t nextPrime(unsigned int n) {
this->key = key; assert(n <= OEISprimes[ARRAYSIZE(OEISprimes) - 1]);
this->value = value;
this->next = NULL; for (unsigned int i = 0; i < ARRAYSIZE(OEISprimes); i++) {
return this; if (n <= OEISprimes[i])
return OEISprimes[i];
}
return OEISprimes[ARRAYSIZE(OEISprimes) - 1];
} }
Hashtable* Hashtable_new(int size, bool owner) { Hashtable* Hashtable_new(unsigned int size, bool owner) {
Hashtable* this; Hashtable* this;
this = xMalloc(sizeof(Hashtable)); this = xMalloc(sizeof(Hashtable));
this->items = 0; this->items = 0;
this->size = size; this->size = size ? nextPrime(size) : 13;
this->buckets = (HashtableItem**) xCalloc(size, sizeof(HashtableItem*)); this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem));
this->owner = owner; this->owner = owner;
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
return this; return this;
} }
void Hashtable_delete(Hashtable* this) { void Hashtable_delete(Hashtable* this) {
assert(Hashtable_isConsistent(this)); Hashtable_clear(this);
for (int i = 0; i < this->size; i++) {
HashtableItem* walk = this->buckets[i];
while (walk != NULL) {
if (this->owner)
free(walk->value);
HashtableItem* savedWalk = walk;
walk = savedWalk->next;
free(savedWalk);
}
}
free(this->buckets); free(this->buckets);
free(this); free(this);
} }
void Hashtable_put(Hashtable* this, unsigned int key, void* value) { void Hashtable_clear(Hashtable* this) {
assert(Hashtable_isConsistent(this));
if (this->owner)
for (unsigned int i = 0; i < this->size; i++)
free(this->buckets[i].value);
memset(this->buckets, 0, this->size * sizeof(HashtableItem));
this->items = 0;
assert(Hashtable_isConsistent(this));
}
static void insert(Hashtable* this, hkey_t key, void* value) {
unsigned int index = key % this->size; unsigned int index = key % this->size;
HashtableItem** bucketPtr = &(this->buckets[index]); unsigned int probe = 0;
while (true) #ifndef NDEBUG
if (*bucketPtr == NULL) { unsigned int origIndex = index;
*bucketPtr = HashtableItem_new(key, value); #endif
for (;;) {
if (!this->buckets[index].value) {
this->items++; this->items++;
break; this->buckets[index].key = key;
} else if ((*bucketPtr)->key == key) { this->buckets[index].probe = probe;
if (this->owner) this->buckets[index].value = value;
free((*bucketPtr)->value); return;
(*bucketPtr)->value = value;
break;
} else
bucketPtr = &((*bucketPtr)->next);
assert(Hashtable_isConsistent(this));
}
void* Hashtable_remove(Hashtable* this, unsigned int key) {
unsigned int index = key % this->size;
assert(Hashtable_isConsistent(this));
HashtableItem** bucket;
for (bucket = &(this->buckets[index]); *bucket; bucket = &((*bucket)->next) ) {
if ((*bucket)->key == key) {
void* value = (*bucket)->value;
HashtableItem* next = (*bucket)->next;
free(*bucket);
(*bucket) = next;
this->items--;
if (this->owner) {
free(value);
assert(Hashtable_isConsistent(this));
return NULL;
} else {
assert(Hashtable_isConsistent(this));
return value;
}
} }
if (this->buckets[index].key == key) {
if (this->owner && this->buckets[index].value != value)
free(this->buckets[index].value);
this->buckets[index].value = value;
return;
}
/* Robin Hood swap */
if (probe > this->buckets[index].probe) {
HashtableItem tmp = this->buckets[index];
this->buckets[index].key = key;
this->buckets[index].probe = probe;
this->buckets[index].value = value;
key = tmp.key;
probe = tmp.probe;
value = tmp.value;
}
index = (index + 1) % this->size;
probe++;
assert(index != origIndex);
} }
assert(Hashtable_isConsistent(this));
return NULL;
} }
inline void* Hashtable_get(Hashtable* this, unsigned int key) { void Hashtable_setSize(Hashtable* this, unsigned int size) {
unsigned int index = key % this->size;
HashtableItem* bucketPtr = this->buckets[index]; assert(Hashtable_isConsistent(this));
while (true) {
if (bucketPtr == NULL) { if (size <= this->items)
assert(Hashtable_isConsistent(this)); return;
return NULL;
} else if (bucketPtr->key == key) { HashtableItem* oldBuckets = this->buckets;
assert(Hashtable_isConsistent(this)); unsigned int oldSize = this->size;
return bucketPtr->value;
} else this->size = nextPrime(size);
bucketPtr = bucketPtr->next; this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem));
this->items = 0;
/* rehash */
for (unsigned int i = 0; i < oldSize; i++) {
if (!oldBuckets[i].value)
continue;
insert(this, oldBuckets[i].key, oldBuckets[i].value);
} }
free(oldBuckets);
assert(Hashtable_isConsistent(this));
}
void Hashtable_put(Hashtable* this, hkey_t key, void* value) {
assert(Hashtable_isConsistent(this));
assert(this->size > 0);
assert(value);
/* grow on load-factor > 0.7 */
if (10 * this->items > 7 * this->size)
Hashtable_setSize(this, 2 * this->size);
insert(this, key, value);
assert(Hashtable_isConsistent(this));
assert(Hashtable_get(this, key) != NULL);
assert(this->size > this->items);
}
void* Hashtable_remove(Hashtable* this, hkey_t key) {
unsigned int index = key % this->size;
unsigned int probe = 0;
#ifndef NDEBUG
unsigned int origIndex = index;
#endif
assert(Hashtable_isConsistent(this));
void* res = NULL;
while (this->buckets[index].value) {
if (this->buckets[index].key == key) {
if (this->owner) {
free(this->buckets[index].value);
} else {
res = this->buckets[index].value;
}
unsigned int next = (index + 1) % this->size;
while (this->buckets[next].value && this->buckets[next].probe > 0) {
this->buckets[index] = this->buckets[next];
this->buckets[index].probe -= 1;
index = next;
next = (index + 1) % this->size;
}
/* set empty after backward shifting */
this->buckets[index].value = NULL;
this->items--;
break;
}
if (this->buckets[index].probe < probe)
break;
index = (index + 1) % this->size;
probe++;
assert(index != origIndex);
}
assert(Hashtable_isConsistent(this));
assert(Hashtable_get(this, key) == NULL);
/* shrink on load-factor < 0.125 */
if (8 * this->items < this->size)
Hashtable_setSize(this, this->size / 2);
return res;
}
void* Hashtable_get(Hashtable* this, hkey_t key) {
unsigned int index = key % this->size;
unsigned int probe = 0;
void* res = NULL;
#ifndef NDEBUG
unsigned int origIndex = index;
#endif
assert(Hashtable_isConsistent(this));
while (this->buckets[index].value) {
if (this->buckets[index].key == key) {
res = this->buckets[index].value;
break;
}
if (this->buckets[index].probe < probe)
break;
index = (index + 1) != this->size ? (index + 1) : 0;
probe++;
assert(index != origIndex);
}
return res;
} }
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) { void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) {
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
for (int i = 0; i < this->size; i++) { for (unsigned int i = 0; i < this->size; i++) {
HashtableItem* walk = this->buckets[i]; HashtableItem* walk = &this->buckets[i];
while (walk != NULL) { if (walk->value)
f(walk->key, walk->value, userData); f(walk->key, walk->value, userData);
walk = walk->next;
}
} }
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
} }

View File

@ -3,44 +3,49 @@
/* /*
htop - Hashtable.h htop - Hashtable.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include <stdbool.h> #include <stdbool.h>
typedef struct Hashtable_ Hashtable;
typedef void(*Hashtable_PairFunction)(int, void*, void*); typedef unsigned int hkey_t;
typedef struct HashtableItem { typedef void(*Hashtable_PairFunction)(hkey_t key, void* value, void* userdata);
unsigned int key;
typedef struct HashtableItem_ {
hkey_t key;
unsigned int probe;
void* value; void* value;
struct HashtableItem* next;
} HashtableItem; } HashtableItem;
struct Hashtable_ { typedef struct Hashtable_ {
int size; unsigned int size;
HashtableItem** buckets; HashtableItem* buckets;
int items; unsigned int items;
bool owner; bool owner;
}; } Hashtable;
#ifdef DEBUG #ifndef NDEBUG
int Hashtable_count(Hashtable* this); unsigned int Hashtable_count(const Hashtable* this);
#endif #endif /* NDEBUG */
Hashtable* Hashtable_new(int size, bool owner); Hashtable* Hashtable_new(unsigned int size, bool owner);
void Hashtable_delete(Hashtable* this); void Hashtable_delete(Hashtable* this);
void Hashtable_put(Hashtable* this, unsigned int key, void* value); void Hashtable_clear(Hashtable* this);
void* Hashtable_remove(Hashtable* this, unsigned int key); void Hashtable_setSize(Hashtable* this, unsigned int size);
void* Hashtable_get(Hashtable* this, unsigned int key); void Hashtable_put(Hashtable* this, hkey_t key, void* value);
void* Hashtable_remove(Hashtable* this, hkey_t key);
void* Hashtable_get(Hashtable* this, hkey_t key);
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData); void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);

View File

@ -1,20 +1,24 @@
/* /*
htop - Header.c htop - Header.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Header.h" #include "Header.h"
#include "CRT.h" #include <stdbool.h>
#include "StringUtils.h" #include <stdio.h>
#include "Platform.h"
#include <assert.h>
#include <time.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "CRT.h"
#include "Macros.h"
#include "Object.h"
#include "Platform.h"
#include "ProvideCurses.h"
#include "XUtils.h"
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) { Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) {
Header* this = xCalloc(1, sizeof(Header)); Header* this = xCalloc(1, sizeof(Header));
@ -59,17 +63,17 @@ void Header_writeBackToSettings(const Header* this) {
Vector* vec = this->columns[col]; Vector* vec = this->columns[col];
int len = Vector_size(vec); int len = Vector_size(vec);
colSettings->names = xCalloc(len+1, sizeof(char*)); colSettings->names = xCalloc(len + 1, sizeof(char*));
colSettings->modes = xCalloc(len, sizeof(int)); colSettings->modes = xCalloc(len, sizeof(int));
colSettings->len = len; colSettings->len = len;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
Meter* meter = (Meter*) Vector_get(vec, i); Meter* meter = (Meter*) Vector_get(vec, i);
char* name = xCalloc(64, sizeof(char)); char* name;
if (meter->param) { if (meter->param) {
xSnprintf(name, 63, "%s(%d)", As_Meter(meter)->name, meter->param); xAsprintf(&name, "%s(%d)", As_Meter(meter)->name, meter->param);
} else { } else {
xSnprintf(name, 63, "%s", As_Meter(meter)->name); xAsprintf(&name, "%s", As_Meter(meter)->name);
} }
colSettings->names[i] = name; colSettings->names[i] = name;
colSettings->modes[i] = meter->mode; colSettings->modes[i] = meter->mode;
@ -84,11 +88,12 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
int param = 0; int param = 0;
if (paren) { if (paren) {
int ok = sscanf(paren, "(%10d)", &param); int ok = sscanf(paren, "(%10d)", &param);
if (!ok) param = 0; if (!ok)
param = 0;
*paren = '\0'; *paren = '\0';
} }
MeterModeId mode = TEXT_METERMODE; MeterModeId mode = TEXT_METERMODE;
for (MeterClass** type = Platform_meterTypes; *type; type++) { for (const MeterClass* const* type = Platform_meterTypes; *type; type++) {
if (String_eq(name, (*type)->name)) { if (String_eq(name, (*type)->name)) {
Meter* meter = Meter_new(this->pl, param, *type); Meter* meter = Meter_new(this->pl, param, *type);
Vector_add(meters, meter); Vector_add(meters, meter);
@ -96,8 +101,10 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
break; break;
} }
} }
if (paren) if (paren)
*paren = '('; *paren = '(';
return mode; return mode;
} }
@ -106,11 +113,12 @@ void Header_setMode(Header* this, int i, MeterModeId mode, int column) {
if (i >= Vector_size(meters)) if (i >= Vector_size(meters))
return; return;
Meter* meter = (Meter*) Vector_get(meters, i); Meter* meter = (Meter*) Vector_get(meters, i);
Meter_setMode(meter, mode); Meter_setMode(meter, mode);
} }
Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column) { 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);
@ -149,8 +157,9 @@ 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++) {
Meter* meter = (Meter*) Vector_get(this->columns[col], i); Meter* meter = (Meter*) Vector_get(this->columns[col], i);
if (Meter_initFn(meter)) if (Meter_initFn(meter)) {
Meter_init(meter); Meter_init(meter);
}
} }
} }
} }

View File

@ -3,18 +3,19 @@
/* /*
htop - Header.h htop - Header.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.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;
struct ProcessList_* pl; ProcessList* pl;
int nrColumns; int nrColumns;
int pad; int pad;
int height; int height;
@ -22,7 +23,7 @@ typedef struct Header_ {
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_)) #define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns); Header* Header_new(ProcessList* pl, Settings* settings, int nrColumns);
void Header_delete(Header* this); void Header_delete(Header* this);
@ -34,7 +35,7 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column);
void Header_setMode(Header* this, int i, MeterModeId mode, int column); void Header_setMode(Header* this, int i, MeterModeId mode, int column);
Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column); Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, int column);
int Header_size(Header* this, int column); int Header_size(Header* this, int column);

View File

@ -1,27 +1,30 @@
/* /*
htop - HostnameMeter.c htop - HostnameMeter.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "HostnameMeter.h" #include "config.h" // IWYU pragma: keep
#include "CRT.h" #include "HostnameMeter.h"
#include <unistd.h> #include <unistd.h>
#include "CRT.h"
#include "Object.h"
int HostnameMeter_attributes[] = {
static const int HostnameMeter_attributes[] = {
HOSTNAME HOSTNAME
}; };
static void HostnameMeter_updateValues(Meter* this, char* buffer, int size) { static void HostnameMeter_updateValues(Meter* this, char* buffer, int size) {
(void) this; (void) this;
gethostname(buffer, size-1); gethostname(buffer, size - 1);
} }
MeterClass HostnameMeter_class = { const MeterClass HostnameMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete .delete = Meter_delete

View File

@ -3,14 +3,12 @@
/* /*
htop - HostnameMeter.h htop - HostnameMeter.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.h"
extern int HostnameMeter_attributes[]; extern const MeterClass HostnameMeter_class;
extern MeterClass HostnameMeter_class;
#endif #endif

View File

@ -1,17 +1,24 @@
/* /*
htop - IncSet.c htop - IncSet.c
(C) 2005-2012 Hisham H. Muhammad (C) 2005-2012 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include "IncSet.h" #include "IncSet.h"
#include "StringUtils.h"
#include "ListItem.h" #include <ctype.h>
#include "CRT.h"
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "CRT.h"
#include "ListItem.h"
#include "Object.h"
#include "ProvideCurses.h"
#include "XUtils.h"
static void IncMode_reset(IncMode* mode) { static void IncMode_reset(IncMode* mode) {
mode->index = 0; mode->index = 0;
@ -72,7 +79,10 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
ListItem* line = (ListItem*)Vector_get(lines, i); ListItem* line = (ListItem*)Vector_get(lines, i);
if (String_contains_i(line->value, incFilter)) { if (String_contains_i(line->value, incFilter)) {
Panel_add(panel, (Object*)line); Panel_add(panel, (Object*)line);
if (selected == (Object*)line) Panel_setSelected(panel, n); if (selected == (Object*)line) {
Panel_setSelected(panel, n);
}
n++; n++;
} }
} }
@ -80,7 +90,9 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
for (int i = 0; i < Vector_size(lines); i++) { for (int i = 0; i < Vector_size(lines); i++) {
Object* line = Vector_get(lines, i); Object* line = Vector_get(lines, i);
Panel_add(panel, line); Panel_add(panel, line);
if (selected == line) Panel_setSelected(panel, i); if (selected == line) {
Panel_setSelected(panel, i);
}
} }
} }
} }
@ -95,10 +107,11 @@ static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelVa
break; break;
} }
} }
if (found)
FunctionBar_draw(mode->bar, mode->buffer); FunctionBar_drawExtra(mode->bar,
else mode->buffer,
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]); found ? -1 : CRT_colors[FAILED_SEARCH],
true);
return found; return found;
} }
@ -106,11 +119,18 @@ static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getP
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;
for(;;) { for (;;) {
i+=step; i += step;
if (i == size) i = 0; if (i == size) {
if (i == -1) i = size - 1; i = 0;
if (i == here) return false; }
if (i == -1) {
i = size - 1;
}
if (i == here) {
return false;
}
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) { if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i); Panel_setSelected(panel, i);
return true; return true;
@ -129,22 +149,27 @@ bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) { bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
if (ch == ERR) if (ch == ERR)
return true; return true;
IncMode* mode = this->active; IncMode* mode = this->active;
int size = Panel_size(panel); int size = Panel_size(panel);
bool filterChanged = false; bool filterChanged = false;
bool doSearch = true; bool doSearch = true;
if (ch == KEY_F(3)) { if (ch == KEY_F(3)) {
if (size == 0) return true; if (size == 0)
return true;
IncMode_find(mode, panel, getPanelValue, 1); IncMode_find(mode, panel, getPanelValue, 1);
doSearch = false; doSearch = false;
} else if (ch < 255 && isprint((char)ch)) { } else if (0 < ch && ch < 255 && isprint((unsigned char)ch)) {
if (mode->index < INCMODE_MAX) { if (mode->index < INCMODE_MAX) {
mode->buffer[mode->index] = ch; mode->buffer[mode->index] = ch;
mode->index++; mode->index++;
mode->buffer[mode->index] = 0; mode->buffer[mode->index] = 0;
if (mode->isFilter) { if (mode->isFilter) {
filterChanged = true; filterChanged = true;
if (mode->index == 1) this->filtering = true; if (mode->index == 1) {
this->filtering = true;
}
} }
} }
} else if ((ch == KEY_BACKSPACE || ch == 127)) { } else if ((ch == KEY_BACKSPACE || ch == 127)) {
@ -162,7 +187,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
doSearch = false; doSearch = false;
} }
} else if (ch == KEY_RESIZE) { } else if (ch == KEY_RESIZE) {
Panel_resize(panel, COLS, LINES-panel->y-1); Panel_resize(panel, COLS, LINES - panel->y - 1);
} else { } else {
if (mode->isFilter) { if (mode->isFilter) {
filterChanged = true; filterChanged = true;
@ -177,7 +202,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} }
this->active = NULL; this->active = NULL;
Panel_setDefaultBar(panel); Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar, NULL); FunctionBar_draw(this->defaultBar);
doSearch = false; doSearch = false;
} }
if (doSearch) { if (doSearch) {
@ -191,22 +216,20 @@ 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); ListItem* l = (ListItem*) Panel_get(panel, i);
if (l) return l ? l->value : "";
return l->value;
return "";
} }
void IncSet_activate(IncSet* this, IncType type, Panel* panel) { void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]); this->active = &(this->modes[type]);
FunctionBar_draw(this->active->bar, this->active->buffer); FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
panel->currentBar = this->active->bar; panel->currentBar = this->active->bar;
} }
void IncSet_drawBar(IncSet* this) { void IncSet_drawBar(const IncSet* this) {
if (this->active) { if (this->active) {
FunctionBar_draw(this->active->bar, this->active->buffer); FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
} else { } else {
FunctionBar_draw(this->defaultBar, NULL); FunctionBar_draw(this->defaultBar);
} }
} }

View File

@ -3,13 +3,16 @@
/* /*
htop - IncSet.h htop - IncSet.h
(C) 2005-2012 Hisham H. Muhammad (C) 2005-2012 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include <stdbool.h>
#include <stddef.h>
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Panel.h" #include "Panel.h"
#include <stdbool.h> #include "Vector.h"
#define INCMODE_MAX 40 #define INCMODE_MAX 40
@ -18,10 +21,8 @@ typedef enum {
INC_FILTER = 1 INC_FILTER = 1
} IncType; } IncType;
#define IncSet_filter(inc_) (inc_->filtering ? inc_->modes[INC_FILTER].buffer : NULL)
typedef struct IncMode_ { typedef struct IncMode_ {
char buffer[INCMODE_MAX+1]; char buffer[INCMODE_MAX + 1];
int index; int index;
FunctionBar* bar; FunctionBar* bar;
bool isFilter; bool isFilter;
@ -35,6 +36,10 @@ typedef struct IncSet_ {
bool found; bool found;
} IncSet; } IncSet;
static inline const char* IncSet_filter(const IncSet* this) {
return this->filtering ? this->modes[INC_FILTER].buffer : NULL;
}
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);
@ -53,7 +58,7 @@ const char* IncSet_getListItemValue(Panel* panel, int i);
void IncSet_activate(IncSet* this, IncType type, Panel* panel); void IncSet_activate(IncSet* this, IncType type, Panel* panel);
void IncSet_drawBar(IncSet* this); void IncSet_drawBar(const IncSet* this);
int IncSet_synthesizeEvent(IncSet* this, int x); int IncSet_synthesizeEvent(IncSet* this, int x);

View File

@ -1,17 +1,18 @@
#include "config.h" // IWYU pragma: keep
#include "InfoScreen.h" #include "InfoScreen.h"
#include "config.h" #include <stdarg.h>
#include "Object.h" #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "CRT.h" #include "CRT.h"
#include "IncSet.h" #include "IncSet.h"
#include "ListItem.h" #include "ListItem.h"
#include "Platform.h" #include "Object.h"
#include "StringUtils.h" #include "ProvideCurses.h"
#include "XUtils.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL}; static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL};
@ -20,7 +21,7 @@ static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27}; static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, const char* panelHeader) { InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader) {
this->process = process; this->process = process;
if (!bar) { if (!bar) {
bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents); bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents);
@ -42,45 +43,56 @@ InfoScreen* InfoScreen_done(InfoScreen* this) {
void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) { void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
char* title = xMalloc(COLS + 1);
int len = vsnprintf(title, COLS + 1, fmt, ap);
if (len > COLS) {
memset(&title[COLS - 3], '.', 3);
}
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
mvhline(0, 0, ' ', COLS); mvhline(0, 0, ' ', COLS);
(void) wmove(stdscr, 0, 0); mvwprintw(stdscr, 0, 0, title);
vw_printw(stdscr, fmt, ap);
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true; this->display->needsRedraw = true;
Panel_draw(this->display, true); Panel_draw(this->display, true, true);
IncSet_drawBar(this->inc); IncSet_drawBar(this->inc);
free(title);
va_end(ap); va_end(ap);
} }
void InfoScreen_addLine(InfoScreen* this, const char* line) { void InfoScreen_addLine(InfoScreen* this, const char* line) {
Vector_add(this->lines, (Object*) ListItem_new(line, 0)); Vector_add(this->lines, (Object*) ListItem_new(line, 0));
const char* incFilter = IncSet_filter(this->inc); const char* incFilter = IncSet_filter(this->inc);
if (!incFilter || String_contains_i(line, incFilter)) if (!incFilter || String_contains_i(line, incFilter)) {
Panel_add(this->display, (Object*)Vector_get(this->lines, Vector_size(this->lines)-1)); Panel_add(this->display, Vector_get(this->lines, Vector_size(this->lines) - 1));
}
} }
void InfoScreen_appendLine(InfoScreen* this, const char* line) { void InfoScreen_appendLine(InfoScreen* this, const char* line) {
ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines)-1); ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines) - 1);
ListItem_append(last, line); ListItem_append(last, line);
const char* incFilter = IncSet_filter(this->inc); const char* incFilter = IncSet_filter(this->inc);
if (incFilter && Panel_get(this->display, Panel_size(this->display)-1) != (Object*)last && String_contains_i(line, incFilter)) if (incFilter && Panel_get(this->display, Panel_size(this->display) - 1) != (Object*)last && String_contains_i(line, incFilter)) {
Panel_add(this->display, (Object*)last); Panel_add(this->display, (Object*)last);
}
} }
void InfoScreen_run(InfoScreen* this) { void InfoScreen_run(InfoScreen* this) {
Panel* panel = this->display; Panel* panel = this->display;
if (As_InfoScreen(this)->scan) InfoScreen_scan(this); if (As_InfoScreen(this)->scan)
InfoScreen_scan(this);
InfoScreen_draw(this); InfoScreen_draw(this);
bool looping = true; bool looping = true;
while (looping) { while (looping) {
Panel_draw(panel, true); Panel_draw(panel, true, true);
if (this->inc->active) { if (this->inc->active) {
(void) move(LINES-1, CRT_cursorX); (void) move(LINES - 1, CRT_cursorX);
} }
set_escdelay(25); set_escdelay(25);
int ch = getch(); int ch = getch();
@ -102,7 +114,7 @@ void InfoScreen_run(InfoScreen* this) {
} else if (mevent.y == LINES - 1) { } else if (mevent.y == LINES - 1) {
ch = IncSet_synthesizeEvent(this->inc, mevent.x); ch = IncSet_synthesizeEvent(this->inc, mevent.x);
} }
} }
} }
if (this->inc->active) { if (this->inc->active) {
@ -123,20 +135,25 @@ void InfoScreen_run(InfoScreen* this) {
break; break;
case KEY_F(5): case KEY_F(5):
clear(); clear();
if (As_InfoScreen(this)->scan) InfoScreen_scan(this); if (As_InfoScreen(this)->scan)
InfoScreen_scan(this);
InfoScreen_draw(this); InfoScreen_draw(this);
break; break;
case '\014': // Ctrl+L case '\014': // Ctrl+L
clear(); clear();
InfoScreen_draw(this); InfoScreen_draw(this);
break; break;
case 'q':
case 27: case 27:
case 'q':
case KEY_F(10): case KEY_F(10):
looping = false; looping = false;
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)
InfoScreen_scan(this);
InfoScreen_draw(this); InfoScreen_draw(this);
break; break;
default: default:

View File

@ -1,12 +1,25 @@
#ifndef HEADER_InfoScreen #ifndef HEADER_InfoScreen
#define HEADER_InfoScreen #define HEADER_InfoScreen
#include "Process.h" #include <stdbool.h>
#include "Panel.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "IncSet.h" #include "IncSet.h"
#include "Macros.h"
#include "Object.h"
#include "Panel.h"
#include "Process.h"
#include "Vector.h"
typedef struct InfoScreen_ InfoScreen;
typedef struct InfoScreen_ {
Object super;
const Process* process;
Panel* display;
FunctionBar* bar;
IncSet* inc;
Vector* lines;
} InfoScreen;
typedef void(*InfoScreen_Scan)(InfoScreen*); typedef void(*InfoScreen_Scan)(InfoScreen*);
typedef void(*InfoScreen_Draw)(InfoScreen*); typedef void(*InfoScreen_Draw)(InfoScreen*);
@ -14,32 +27,24 @@ typedef void(*InfoScreen_OnErr)(InfoScreen*);
typedef bool(*InfoScreen_OnKey)(InfoScreen*, int); typedef bool(*InfoScreen_OnKey)(InfoScreen*, int);
typedef struct InfoScreenClass_ { typedef struct InfoScreenClass_ {
ObjectClass super; const ObjectClass super;
const InfoScreen_Scan scan; const InfoScreen_Scan scan;
const InfoScreen_Draw draw; const InfoScreen_Draw draw;
const InfoScreen_OnErr onErr; const InfoScreen_OnErr onErr;
const InfoScreen_OnKey onKey; const InfoScreen_OnKey onKey;
} InfoScreenClass; } InfoScreenClass;
#define As_InfoScreen(this_) ((InfoScreenClass*)(((InfoScreen*)(this_))->super.klass)) #define As_InfoScreen(this_) ((const InfoScreenClass*)(((InfoScreen*)(this_))->super.klass))
#define InfoScreen_scan(this_) As_InfoScreen(this_)->scan((InfoScreen*)(this_)) #define InfoScreen_scan(this_) As_InfoScreen(this_)->scan((InfoScreen*)(this_))
#define InfoScreen_draw(this_) As_InfoScreen(this_)->draw((InfoScreen*)(this_)) #define InfoScreen_draw(this_) As_InfoScreen(this_)->draw((InfoScreen*)(this_))
#define InfoScreen_onErr(this_) As_InfoScreen(this_)->onErr((InfoScreen*)(this_)) #define InfoScreen_onErr(this_) As_InfoScreen(this_)->onErr((InfoScreen*)(this_))
#define InfoScreen_onKey(this_, ch_) As_InfoScreen(this_)->onKey((InfoScreen*)(this_), ch_) #define InfoScreen_onKey(this_, ch_) As_InfoScreen(this_)->onKey((InfoScreen*)(this_), ch_)
struct InfoScreen_ { InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader);
Object super;
Process* process;
Panel* display;
FunctionBar* bar;
IncSet* inc;
Vector* lines;
};
InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, const char* panelHeader);
InfoScreen* InfoScreen_done(InfoScreen* this); InfoScreen* InfoScreen_done(InfoScreen* this);
ATTR_FORMAT(printf, 2, 3)
void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...); void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...);
void InfoScreen_addLine(InfoScreen* this, const char* line); void InfoScreen_addLine(InfoScreen* this, const char* line);

View File

@ -1,19 +1,21 @@
/* /*
htop - ListItem.c htop - ListItem.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include "ListItem.h" #include "ListItem.h"
#include "CRT.h"
#include "StringUtils.h"
#include "RichString.h"
#include <string.h>
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "CRT.h"
#include "RichString.h"
#include "XUtils.h"
static void ListItem_delete(Object* cast) { static void ListItem_delete(Object* cast) {
@ -22,32 +24,22 @@ static void ListItem_delete(Object* cast) {
free(this); free(this);
} }
static void ListItem_display(Object* cast, RichString* out) { static void ListItem_display(const Object* cast, RichString* out) {
ListItem* const this = (ListItem*)cast; const ListItem* const this = (const ListItem*)cast;
assert (this != NULL); assert (this != NULL);
/*
int len = strlen(this->value)+1;
char buffer[len+1];
xSnprintf(buffer, len, "%s", this->value);
*/
if (this->moving) { if (this->moving) {
RichString_write(out, CRT_colors[DEFAULT_COLOR], RichString_write(out, CRT_colors[DEFAULT_COLOR],
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
CRT_utf8 ? "" : CRT_utf8 ? "" :
#endif #endif
"+ "); "+ ");
} else { } else {
RichString_prune(out); RichString_prune(out);
} }
RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value/*buffer*/); RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value);
} }
ObjectClass ListItem_class = {
.display = ListItem_display,
.delete = ListItem_delete,
.compare = ListItem_compare
};
ListItem* ListItem_new(const char* value, int key) { ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem); ListItem* this = AllocThis(ListItem);
this->value = xStrdup(value); this->value = xStrdup(value);
@ -57,20 +49,22 @@ ListItem* ListItem_new(const char* value, int key) {
} }
void ListItem_append(ListItem* this, const char* text) { void ListItem_append(ListItem* this, const char* text) {
int oldLen = strlen(this->value); size_t oldLen = strlen(this->value);
int textLen = strlen(text); size_t textLen = strlen(text);
int newLen = strlen(this->value) + textLen; size_t newLen = oldLen + textLen;
this->value = xRealloc(this->value, newLen + 1); this->value = xRealloc(this->value, newLen + 1);
memcpy(this->value + oldLen, text, textLen); memcpy(this->value + oldLen, text, textLen);
this->value[newLen] = '\0'; this->value[newLen] = '\0';
} }
const char* ListItem_getRef(ListItem* this) { static long ListItem_compare(const void* cast1, const void* cast2) {
return this->value; const ListItem* obj1 = (const ListItem*) cast1;
} const ListItem* obj2 = (const ListItem*) cast2;
long ListItem_compare(const void* cast1, const void* cast2) {
ListItem* obj1 = (ListItem*) cast1;
ListItem* obj2 = (ListItem*) cast2;
return strcmp(obj1->value, obj2->value); return strcmp(obj1->value, obj2->value);
} }
const ObjectClass ListItem_class = {
.display = ListItem_display,
.delete = ListItem_delete,
.compare = ListItem_compare
};

View File

@ -3,10 +3,12 @@
/* /*
htop - ListItem.h htop - ListItem.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include <stdbool.h>
#include "Object.h" #include "Object.h"
typedef struct ListItem_ { typedef struct ListItem_ {
@ -16,14 +18,14 @@ typedef struct ListItem_ {
bool moving; bool moving;
} ListItem; } ListItem;
extern ObjectClass ListItem_class; extern const ObjectClass ListItem_class;
ListItem* ListItem_new(const char* value, int key); ListItem* ListItem_new(const char* value, int key);
void ListItem_append(ListItem* this, const char* text); void ListItem_append(ListItem* this, const char* text);
const char* ListItem_getRef(ListItem* this); static inline const char* ListItem_getRef(const ListItem* this) {
return this->value;
long ListItem_compare(const void* cast1, const void* cast2); }
#endif #endif

View File

@ -1,29 +1,36 @@
/* /*
htop - LoadAverageMeter.c htop - LoadAverageMeter.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "LoadAverageMeter.h" #include "LoadAverageMeter.h"
#include "CRT.h" #include "CRT.h"
#include "Object.h"
#include "Platform.h" #include "Platform.h"
#include "RichString.h"
#include "XUtils.h"
int LoadAverageMeter_attributes[] = { static const int LoadAverageMeter_attributes[] = {
LOAD_AVERAGE_ONE, LOAD_AVERAGE_FIVE, LOAD_AVERAGE_FIFTEEN LOAD_AVERAGE_ONE,
LOAD_AVERAGE_FIVE,
LOAD_AVERAGE_FIFTEEN
}; };
int LoadMeter_attributes[] = { LOAD }; static const int LoadMeter_attributes[] = {
LOAD
};
static void LoadAverageMeter_updateValues(Meter* this, char* buffer, int size) { static void LoadAverageMeter_updateValues(Meter* this, char* buffer, int size) {
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]); Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]); xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
} }
static void LoadAverageMeter_display(Object* cast, RichString* out) { static void LoadAverageMeter_display(const Object* cast, RichString* out) {
Meter* this = (Meter*)cast; const Meter* this = (const Meter*)cast;
char buffer[20]; char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer); RichString_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
@ -42,14 +49,14 @@ static void LoadMeter_updateValues(Meter* this, char* buffer, int size) {
xSnprintf(buffer, size, "%.2f", this->values[0]); xSnprintf(buffer, size, "%.2f", this->values[0]);
} }
static void LoadMeter_display(Object* cast, RichString* out) { static void LoadMeter_display(const Object* cast, RichString* out) {
Meter* this = (Meter*)cast; const Meter* this = (const Meter*)cast;
char buffer[20]; char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", ((Meter*)this)->values[0]); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD], buffer); RichString_write(out, CRT_colors[LOAD], buffer);
} }
MeterClass LoadAverageMeter_class = { const MeterClass LoadAverageMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,
@ -66,7 +73,7 @@ MeterClass LoadAverageMeter_class = {
.caption = "Load average: " .caption = "Load average: "
}; };
MeterClass LoadMeter_class = { const MeterClass LoadMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,

View File

@ -3,18 +3,14 @@
/* /*
htop - LoadAverageMeter.h htop - LoadAverageMeter.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.h"
extern int LoadAverageMeter_attributes[]; extern const MeterClass LoadAverageMeter_class;
extern int LoadMeter_attributes[]; extern const MeterClass LoadMeter_class;
extern MeterClass LoadAverageMeter_class;
extern MeterClass LoadMeter_class;
#endif #endif

View File

@ -1,16 +1,62 @@
#ifndef HEADER_Macros #ifndef HEADER_Macros
#define HEADER_Macros #define HEADER_Macros
#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) (((x) > (high)) ? (high) : MAXIMUM(x, low)) #define CLAMP(x, low, high) (assert((low) <= (high)), ((x) > (high)) ? (high) : MAXIMUM(x, low))
#endif
#ifndef ARRAYSIZE
#define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif
#ifndef SPACESHIP_NUMBER
#define SPACESHIP_NUMBER(a, b) (((a) > (b)) - ((a) < (b)))
#endif
#ifndef SPACESHIP_NULLSTR
#define SPACESHIP_NULLSTR(a, b) strcmp((a) ? (a) : "", (b) ? (b) : "")
#endif
#ifdef __GNUC__ // defined by GCC and Clang
#define ATTR_FORMAT(type, index, check) __attribute__((format (type, index, check)))
#define ATTR_NONNULL __attribute__((nonnull))
#define ATTR_NORETURN __attribute__((noreturn))
#define ATTR_UNUSED __attribute__((unused))
#else /* __GNUC__ */
#define ATTR_FORMAT(type, index, check)
#define ATTR_NONNULL
#define ATTR_NORETURN
#define ATTR_UNUSED
#endif /* __GNUC__ */
// ignore casts discarding const specifier, e.g.
// const char [] -> char * / void *
// const char *[2]' -> char *const *
#ifdef __clang__
#define IGNORE_WCASTQUAL_BEGIN _Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wcast-qual\"")
#define IGNORE_WCASTQUAL_END _Pragma("clang diagnostic pop")
#elif defined(__GNUC__)
#define IGNORE_WCASTQUAL_BEGIN _Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
#define IGNORE_WCASTQUAL_END _Pragma("GCC diagnostic pop")
#else
#define IGNORE_WCASTQUAL_BEGIN
#define IGNORE_WCASTQUAL_END
#endif #endif
#endif #endif

View File

@ -2,33 +2,35 @@
htop - ColumnsPanel.c htop - ColumnsPanel.c
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved. (C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "MainPanel.h" #include "MainPanel.h"
#include "Process.h"
#include "Platform.h"
#include "CRT.h"
#include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search ", "Filter ", "Tree ", "SortBy ", "Nice - ", "Nice + ", "Kill ", "Quit ", NULL}; #include "CRT.h"
#include "FunctionBar.h"
#include "Platform.h"
#include "Process.h"
#include "ProcessList.h"
#include "ProvideCurses.h"
#include "Settings.h"
#include "XUtils.h"
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) { void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this); FunctionBar* bar = MainPanel_getFunctionBar(this);
if (mode) { FunctionBar_setLabel(bar, KEY_F(5), mode ? "Sorted" : "Tree ");
FunctionBar_setLabel(bar, KEY_F(5), "Sorted");
FunctionBar_setLabel(bar, KEY_F(6), "Collap");
} else {
FunctionBar_setLabel(bar, KEY_F(5), "Tree ");
FunctionBar_setLabel(bar, KEY_F(6), "SortBy");
}
} }
void MainPanel_pidSearch(MainPanel* this, int ch) { 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); Process* p = (Process*) Panel_get(super, i);
if (p && p->pid == pid) { if (p && p->pid == pid) {
@ -49,15 +51,17 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
if (ch != ERR)
this->state->hideProcessSelection = false;
if (EVENT_IS_HEADER_CLICK(ch)) { if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch); int x = EVENT_HEADER_CLICK_GET_X(ch);
ProcessList* pl = this->state->pl; const ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings; Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1; int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx); ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) { if (field == settings->sortKey) {
Settings_invertSortOrder(settings); Settings_invertSortOrder(settings);
settings->treeView = false;
} else { } else {
reaction |= Action_setSortKey(settings, field); reaction |= Action_setSortKey(settings, field);
} }
@ -75,11 +79,12 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} }
result = HANDLED; result = HANDLED;
} else if (ch == 27) { } else if (ch == 27) {
this->state->hideProcessSelection = true;
return HANDLED; return HANDLED;
} else if (ch != ERR && ch > 0 && ch < KEY_MAX && this->keys[ch]) { } else if (ch != ERR && ch > 0 && ch < KEY_MAX && this->keys[ch]) {
reaction |= (this->keys[ch])(this->state); reaction |= (this->keys[ch])(this->state);
result = HANDLED; result = HANDLED;
} else if (isdigit(ch)) { } else if (0 < ch && ch < 255 && isdigit((unsigned char)ch)) {
MainPanel_pidSearch(this, ch); MainPanel_pidSearch(this, ch);
} else { } else {
if (ch != ERR) { if (ch != ERR) {
@ -92,6 +97,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);
IncSet_drawBar(this->inc); IncSet_drawBar(this->inc);
if (this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}
} }
if (reaction & HTOP_UPDATE_PANELHDR) { if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super)); ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
@ -125,9 +133,7 @@ int MainPanel_selectedPid(MainPanel* this) {
const char* MainPanel_getValue(MainPanel* this, int i) { const char* MainPanel_getValue(MainPanel* this, int i) {
Process* p = (Process*) Panel_get((Panel*)this, i); Process* p = (Process*) Panel_get((Panel*)this, i);
if (p) return Process_getCommand(p);
return p->comm;
return "";
} }
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) { bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
@ -143,14 +149,18 @@ bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Ar
} }
if (!anyTagged) { if (!anyTagged) {
Process* p = (Process*) Panel_getSelected(super); Process* p = (Process*) Panel_getSelected(super);
if (p) ok = fn(p, arg) && ok; if (p) {
ok &= fn(p, arg);
}
} }
if (wasAnyTagged) if (wasAnyTagged)
*wasAnyTagged = anyTagged; *wasAnyTagged = anyTagged;
return ok; return ok;
} }
PanelClass MainPanel_class = { const PanelClass MainPanel_class = {
.super = { .super = {
.extends = Class(Panel), .extends = Class(Panel),
.delete = MainPanel_delete .delete = MainPanel_delete

View File

@ -4,19 +4,27 @@
htop - ColumnsPanel.h htop - ColumnsPanel.h
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved. (C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include <stdbool.h>
#include <sys/types.h>
#include "Action.h" #include "Action.h"
#include "IncSet.h" #include "IncSet.h"
#include "Object.h"
#include "Panel.h" #include "Panel.h"
#include "Process.h"
typedef struct MainPanel_ { typedef struct MainPanel_ {
Panel super; Panel super;
State* state; State* state;
IncSet* inc; IncSet* inc;
Htop_Action *keys; Htop_Action* keys;
pid_t pidSearch; pid_t pidSearch;
} MainPanel; } MainPanel;
@ -34,9 +42,9 @@ const char* MainPanel_getValue(MainPanel* this, int i);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged); bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
extern PanelClass MainPanel_class; extern const PanelClass MainPanel_class;
MainPanel* MainPanel_new(); MainPanel* MainPanel_new(void);
void MainPanel_setState(MainPanel* this, State* state); void MainPanel_setState(MainPanel* this, State* state);

View File

@ -1,5 +1,3 @@
ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = subdir-objects AUTOMAKE_OPTIONS = subdir-objects
bin_PROGRAMS = htop bin_PROGRAMS = htop
@ -12,52 +10,150 @@ applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png pixmap_DATA = htop.png
AM_CFLAGS += -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)" AM_CFLAGS += -pedantic -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
AM_LDFLAGS = AM_LDFLAGS =
AM_CPPFLAGS = -DNDEBUG
myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \ myhtopsources = \
ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \ Action.c \
DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \ Affinity.c \
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \ AffinityPanel.c \
BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \ AvailableColumnsPanel.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \ AvailableMetersPanel.c \
TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \ BatteryMeter.c \
HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \ CategoriesPanel.c \
InfoScreen.c XAlloc.c ClockMeter.c \
ColorsPanel.c \
ColumnsPanel.c \
CommandScreen.c \
Compat.c \
CPUMeter.c \
CRT.c \
DateMeter.c \
DateTimeMeter.c \
DiskIOMeter.c \
DisplayOptionsPanel.c \
EnvScreen.c \
FunctionBar.c \
Hashtable.c \
Header.c \
HostnameMeter.c \
htop.c \
IncSet.c \
InfoScreen.c \
ListItem.c \
LoadAverageMeter.c \
MainPanel.c \
MemoryMeter.c \
Meter.c \
MetersPanel.c \
NetworkIOMeter.c \
Object.c \
OpenFilesScreen.c \
OptionItem.c \
Panel.c \
Process.c \
ProcessList.c \
ProcessLocksScreen.c \
RichString.c \
ScreenManager.c \
Settings.c \
SignalsPanel.c \
SwapMeter.c \
TasksMeter.c \
TraceScreen.c \
UptimeMeter.c \
UsersTable.c \
Vector.c \
XUtils.c
myhtopheaders = AvailableColumnsPanel.h AvailableMetersPanel.h \ myhtopheaders = \
CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \ Action.h \
CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \ Affinity.h \
Hashtable.h Header.h ListItem.h LoadAverageMeter.h MemoryMeter.h \ AffinityPanel.h \
BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \ AvailableColumnsPanel.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \ AvailableMetersPanel.h \
TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \ BatteryMeter.h \
AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \ CPUMeter.h \
EnvScreen.h InfoScreen.h XAlloc.h Macros.h CRT.h \
CategoriesPanel.h \
ClockMeter.h \
ColorsPanel.h \
ColumnsPanel.h \
CommandScreen.h \
Compat.h \
DateMeter.h \
DateTimeMeter.h \
DiskIOMeter.h \
DisplayOptionsPanel.h \
EnvScreen.h \
FunctionBar.h \
Hashtable.h \
Header.h \
HostnameMeter.h \
IncSet.h \
InfoScreen.h \
ListItem.h \
LoadAverageMeter.h \
Macros.h \
MainPanel.h \
MemoryMeter.h \
Meter.h \
MetersPanel.h \
NetworkIOMeter.h \
Object.h \
OpenFilesScreen.h \
OptionItem.h \
Panel.h \
Process.h \
ProcessList.h \
ProcessLocksScreen.h \
ProvideCurses.h \
RichString.h \
ScreenManager.h \
Settings.h \
SignalsPanel.h \
SwapMeter.h \
TasksMeter.h \
TraceScreen.h \
UptimeMeter.h \
UsersTable.h \
Vector.h \
XUtils.h
# Linux # Linux
# ----- # -----
linux_platform_headers = \ linux_platform_headers = \
linux/Platform.h \
linux/IOPriorityPanel.h \
linux/IOPriority.h \ linux/IOPriority.h \
linux/IOPriorityPanel.h \
linux/LibSensors.h \
linux/LinuxProcess.h \ linux/LinuxProcess.h \
linux/LinuxProcessList.h \ linux/LinuxProcessList.h \
linux/LinuxCRT.h \ linux/Platform.h \
linux/Battery.h \
linux/PressureStallMeter.h \ linux/PressureStallMeter.h \
linux/SELinuxMeter.h \
linux/SystemdMeter.h \
linux/ZramMeter.h \
linux/ZramStats.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \ zfs/ZfsArcStats.h \
zfs/ZfsArcStats.h zfs/ZfsCompressedArcMeter.h
if HTOP_LINUX if HTOP_LINUX
AM_LDFLAGS += -rdynamic AM_LDFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c \ myhtopplatsources = \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \ linux/IOPriorityPanel.c \
linux/PressureStallMeter.c \ linux/LibSensors.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c linux/LinuxProcess.c \
linux/LinuxProcessList.c \
linux/Platform.c \
linux/PressureStallMeter.c \
linux/SELinuxMeter.c \
linux/SystemdMeter.c \
linux/ZramMeter.c \
zfs/ZfsArcMeter.c \
zfs/ZfsArcStats.c \
zfs/ZfsCompressedArcMeter.c
myhtopplatheaders = $(linux_platform_headers) myhtopplatheaders = $(linux_platform_headers)
endif endif
@ -69,8 +165,6 @@ freebsd_platform_headers = \
freebsd/Platform.h \ freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \ freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \ freebsd/FreeBSDProcess.h \
freebsd/FreeBSDCRT.h \
freebsd/Battery.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \ zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \ zfs/ZfsArcStats.h \
@ -78,7 +172,7 @@ freebsd_platform_headers = \
if HTOP_FREEBSD if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \ myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c \ freebsd/FreeBSDProcess.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(freebsd_platform_headers) myhtopplatheaders = $(freebsd_platform_headers)
@ -90,14 +184,12 @@ endif
dragonflybsd_platform_headers = \ dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \ dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \ dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h \ dragonflybsd/DragonFlyBSDProcess.h
dragonflybsd/DragonFlyBSDCRT.h \
dragonflybsd/Battery.h
if HTOP_DRAGONFLYBSD if HTOP_DRAGONFLYBSD
AM_LDFLAGS += -lkvm -lkinfo -lexecinfo AM_LDFLAGS += -lkvm -lkinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \ myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c dragonflybsd/DragonFlyBSDProcess.c
myhtopplatheaders = $(dragonflybsd_platform_headers) myhtopplatheaders = $(dragonflybsd_platform_headers)
endif endif
@ -108,13 +200,11 @@ endif
openbsd_platform_headers = \ openbsd_platform_headers = \
openbsd/Platform.h \ openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \ openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h \ openbsd/OpenBSDProcess.h
openbsd/OpenBSDCRT.h \
openbsd/Battery.h
if HTOP_OPENBSD if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \ myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c openbsd/OpenBSDProcess.c
myhtopplatheaders = $(openbsd_platform_headers) myhtopplatheaders = $(openbsd_platform_headers)
endif endif
@ -126,8 +216,6 @@ darwin_platform_headers = \
darwin/Platform.h \ darwin/Platform.h \
darwin/DarwinProcess.h \ darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \ darwin/DarwinProcessList.h \
darwin/DarwinCRT.h \
darwin/Battery.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \ zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \ zfs/ZfsArcStats.h \
@ -136,7 +224,7 @@ darwin_platform_headers = \
if HTOP_DARWIN if HTOP_DARWIN
AM_LDFLAGS += -framework IOKit -framework CoreFoundation AM_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \ myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c \ darwin/DarwinProcessList.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(darwin_platform_headers) myhtopplatheaders = $(darwin_platform_headers)
@ -149,8 +237,6 @@ solaris_platform_headers = \
solaris/Platform.h \ solaris/Platform.h \
solaris/SolarisProcess.h \ solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \ solaris/SolarisProcessList.h \
solaris/SolarisCRT.h \
solaris/Battery.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \ zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h zfs/ZfsArcStats.h
@ -158,7 +244,6 @@ solaris_platform_headers = \
if HTOP_SOLARIS if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \ myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \ solaris/SolarisProcess.c solaris/SolarisProcessList.c \
solaris/SolarisCRT.c solaris/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(solaris_platform_headers) myhtopplatheaders = $(solaris_platform_headers)
@ -170,14 +255,11 @@ endif
unsupported_platform_headers = \ unsupported_platform_headers = \
unsupported/Platform.h \ unsupported/Platform.h \
unsupported/UnsupportedProcess.h \ unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h \ unsupported/UnsupportedProcessList.h
unsupported/UnsupportedCRT.h \
unsupported/Battery.h
if HTOP_UNSUPPORTED if HTOP_UNSUPPORTED
myhtopplatsources = unsupported/Platform.c \ myhtopplatsources = unsupported/Platform.c \
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \ unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c
unsupported/UnsupportedCRT.c unsupported/Battery.c
myhtopplatheaders = $(unsupported_platform_headers) myhtopplatheaders = $(unsupported_platform_headers)
endif endif
@ -191,16 +273,16 @@ target:
echo $(htop_SOURCES) echo $(htop_SOURCES)
profile: profile:
$(MAKE) all CFLAGS="-pg" AM_CPPFLAGS="-pg -O2 -DNDEBUG" $(MAKE) all AM_CPPFLAGS="-pg -O2 -DNDEBUG"
debug: debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG" $(MAKE) all AM_CPPFLAGS="-ggdb -DDEBUG"
coverage: coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov" $(MAKE) all AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" AM_LDFLAGS="-lgcov"
cppcheck: cppcheck:
cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS cppcheck -q -v . --enable=all -DHAVE_OPENVZ
dist-hook: $(top_distdir)/configure dist-hook: $(top_distdir)/configure
@if grep 'pkg_m4_absent' '$(top_distdir)/configure'; then \ @if grep 'pkg_m4_absent' '$(top_distdir)/configure'; then \

View File

@ -1,24 +1,22 @@
/* /*
htop - MemoryMeter.c htop - MemoryMeter.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "MemoryMeter.h" #include "MemoryMeter.h"
#include "CRT.h" #include "CRT.h"
#include "Object.h"
#include "Platform.h" #include "Platform.h"
#include "RichString.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/param.h>
#include <assert.h>
int MemoryMeter_attributes[] = { static const int MemoryMeter_attributes[] = {
MEMORY_USED, MEMORY_BUFFERS, MEMORY_CACHE MEMORY_USED,
MEMORY_BUFFERS,
MEMORY_CACHE
}; };
static void MemoryMeter_updateValues(Meter* this, char* buffer, int size) { static void MemoryMeter_updateValues(Meter* this, char* buffer, int size) {
@ -34,9 +32,9 @@ static void MemoryMeter_updateValues(Meter* this, char* buffer, int size) {
} }
} }
static void MemoryMeter_display(Object* cast, RichString* out) { static void MemoryMeter_display(const Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
Meter* this = (Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":"); RichString_write(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, 50); Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_append(out, CRT_colors[METER_VALUE], buffer);
@ -51,7 +49,7 @@ static void MemoryMeter_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[MEMORY_CACHE], buffer); RichString_append(out, CRT_colors[MEMORY_CACHE], buffer);
} }
MeterClass MemoryMeter_class = { const MeterClass MemoryMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,

View File

@ -3,14 +3,12 @@
/* /*
htop - MemoryMeter.h htop - MemoryMeter.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Meter.h" #include "Meter.h"
extern int MemoryMeter_attributes[]; extern const MeterClass MemoryMeter_class;
extern MeterClass MemoryMeter_class;
#endif #endif

162
Meter.c
View File

@ -1,56 +1,59 @@
/* /*
htop - Meter.c htop - Meter.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include "Meter.h" #include "Meter.h"
#include "RichString.h"
#include "Object.h"
#include "CRT.h"
#include "StringUtils.h"
#include "Settings.h"
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h> #include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "CRT.h"
#include "Macros.h"
#include "Object.h"
#include "ProvideCurses.h"
#include "RichString.h"
#include "XUtils.h"
#define GRAPH_DELAY (DEFAULT_DELAY/2)
#define GRAPH_HEIGHT 4 /* Unit: rows (lines) */ #define GRAPH_HEIGHT 4 /* Unit: rows (lines) */
MeterClass Meter_class = { const MeterClass Meter_class = {
.super = { .super = {
.extends = Class(Object) .extends = Class(Object)
} }
}; };
Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type) { Meter* Meter_new(const struct ProcessList_* pl, 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;
this->param = param; this->param = param;
this->pl = pl; this->pl = pl;
type->curItems = type->maxItems; this->curItems = type->maxItems;
this->values = xCalloc(type->maxItems, sizeof(double)); this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL;
this->total = type->total; this->total = type->total;
this->caption = xStrdup(type->caption); this->caption = xStrdup(type->caption);
if (Meter_initFn(this)) if (Meter_initFn(this)) {
Meter_init(this); Meter_init(this);
}
Meter_setMode(this, type->defaultMode); Meter_setMode(this, type->defaultMode);
return this; return this;
} }
int Meter_humanUnit(char* buffer, unsigned long int value, int size) { int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
const char * prefix = "KMGTPEZY"; const char* prefix = "KMGTPEZY";
unsigned long int powi = 1; unsigned long int powi = 1;
unsigned int written, powj = 1, precision = 2; unsigned int written, powj = 1, precision = 2;
for(;;) { for (;;) {
if (value / 1024 < powi) if (value / 1024 < powi)
break; break;
@ -79,6 +82,7 @@ int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
void Meter_delete(Object* cast) { void Meter_delete(Object* cast) {
if (!cast) if (!cast)
return; return;
Meter* this = (Meter*) cast; Meter* this = (Meter*) cast;
if (Meter_doneFn(this)) { if (Meter_doneFn(this)) {
Meter_done(this); Meter_done(this);
@ -94,7 +98,7 @@ void Meter_setCaption(Meter* this, const char* caption) {
this->caption = xStrdup(caption); this->caption = xStrdup(caption);
} }
static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* out) { static inline void Meter_displayBuffer(const Meter* this, const char* buffer, RichString* out) {
if (Object_displayFn(this)) { if (Object_displayFn(this)) {
Object_display(this, out); Object_display(this, out);
} else { } else {
@ -103,21 +107,26 @@ static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* ou
} }
void Meter_setMode(Meter* this, int modeIndex) { void Meter_setMode(Meter* this, int modeIndex) {
if (modeIndex > 0 && modeIndex == this->mode) if (modeIndex > 0 && modeIndex == this->mode) {
return; return;
if (!modeIndex) }
if (!modeIndex) {
modeIndex = 1; modeIndex = 1;
}
assert(modeIndex < LAST_METERMODE); assert(modeIndex < LAST_METERMODE);
if (Meter_defaultMode(this) == CUSTOM_METERMODE) { if (Meter_defaultMode(this) == CUSTOM_METERMODE) {
this->draw = Meter_drawFn(this); this->draw = Meter_drawFn(this);
if (Meter_updateModeFn(this)) if (Meter_updateModeFn(this)) {
Meter_updateMode(this, modeIndex); Meter_updateMode(this, modeIndex);
}
} else { } else {
assert(modeIndex >= 1); assert(modeIndex >= 1);
free(this->drawData); free(this->drawData);
this->drawData = NULL; this->drawData = NULL;
MeterMode* mode = Meter_modes[modeIndex]; const MeterMode* mode = Meter_modes[modeIndex];
this->draw = mode->draw; this->draw = mode->draw;
this->h = mode->h; this->h = mode->h;
} }
@ -125,18 +134,20 @@ void Meter_setMode(Meter* this, int modeIndex) {
} }
ListItem* Meter_toListItem(Meter* this, bool moving) { ListItem* Meter_toListItem(Meter* this, bool moving) {
char mode[21]; char mode[20];
if (this->mode) if (this->mode) {
xSnprintf(mode, 20, " [%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[11]; }
if (this->param > 0) char number[10];
xSnprintf(number, 10, " %d", this->param); if (this->param > 0) {
else xSnprintf(number, sizeof(number), " %d", this->param);
} else {
number[0] = '\0'; number[0] = '\0';
char buffer[51]; }
xSnprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode); char buffer[50];
xSnprintf(buffer, sizeof(buffer), "%s%s%s", Meter_uiName(this), number, mode);
ListItem* li = ListItem_new(buffer, 0); ListItem* li = ListItem_new(buffer, 0);
li->moving = moving; li->moving = moving;
return li; return li;
@ -185,20 +196,26 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
return; return;
} }
char bar[w + 1];
// The text in the bar is right aligned;
// calculate needed padding and generate leading spaces
const int textLen = mbstowcs(NULL, buffer, 0);
const int padding = MAXIMUM(w - textLen, 0);
RichString_begin(bar);
RichString_appendChr(&bar, ' ', padding);
RichString_append(&bar, 0, buffer);
assert(RichString_sizeVal(bar) >= w);
int blockSizes[10]; int blockSizes[10];
xSnprintf(bar, w + 1, "%*.*s", w, w, buffer);
// First draw in the bar[] buffer... // First draw in the bar[] buffer...
int offset = 0; int offset = 0;
int items = Meter_getItems(this); for (uint8_t i = 0; i < this->curItems; i++) {
for (int i = 0; i < items; i++) {
double value = this->values[i]; double value = this->values[i];
value = CLAMP(value, 0.0, this->total); value = CLAMP(value, 0.0, this->total);
if (value > 0) { if (value > 0) {
blockSizes[i] = ceil((value/this->total) * w); blockSizes[i] = ceil((value / this->total) * w);
} else { } else {
blockSizes[i] = 0; blockSizes[i] = 0;
} }
@ -206,11 +223,11 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// (Control against invalid values) // (Control against invalid values)
nextOffset = CLAMP(nextOffset, 0, w); nextOffset = CLAMP(nextOffset, 0, w);
for (int j = offset; j < nextOffset; j++) for (int j = offset; j < nextOffset; j++)
if (bar[j] == ' ') { if (RichString_getCharVal(bar, j) == ' ') {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
bar[j] = BarMeterMode_characters[i]; RichString_setChar(&bar, j, BarMeterMode_characters[i]);
} else { } else {
bar[j] = '|'; RichString_setChar(&bar, j, '|');
} }
} }
offset = nextOffset; offset = nextOffset;
@ -218,17 +235,19 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// ...then print the buffer. // ...then print the buffer.
offset = 0; offset = 0;
for (int i = 0; i < items; i++) { for (uint8_t i = 0; i < this->curItems; i++) {
attrset(CRT_colors[Meter_attributes(this)[i]]); attrset(CRT_colors[Meter_attributes(this)[i]]);
mvaddnstr(y, x + offset, bar + offset, blockSizes[i]); RichString_printoffnVal(bar, y, x + offset, offset, blockSizes[i]);
offset += blockSizes[i]; offset += blockSizes[i];
offset = CLAMP(offset, 0, w); offset = CLAMP(offset, 0, w);
} }
if (offset < w) { if (offset < w) {
attrset(CRT_colors[BAR_SHADOW]); attrset(CRT_colors[BAR_SHADOW]);
mvaddnstr(y, x + offset, bar + offset, w - offset); RichString_printoffnVal(bar, y, x + offset, offset, w - offset);
} }
RichString_end(bar);
move(y, x + w + 1); move(y, x + w + 1);
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
} }
@ -260,8 +279,10 @@ static int GraphMeterMode_pixPerRow;
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
if (!this->drawData) this->drawData = xCalloc(1, sizeof(GraphData)); if (!this->drawData) {
GraphData* data = (GraphData*) this->drawData; this->drawData = xCalloc(1, sizeof(GraphData));
}
GraphData* data = this->drawData;
const int nValues = METER_BUFFER_LEN; const int nValues = METER_BUFFER_LEN;
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
@ -284,32 +305,32 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
struct timeval now; struct timeval now;
gettimeofday(&now, NULL); gettimeofday(&now, NULL);
if (!timercmp(&now, &(data->time), <)) { if (!timercmp(&now, &(data->time), <)) {
struct timeval delay = { .tv_sec = (int)(CRT_delay/10), .tv_usec = (CRT_delay-((int)(CRT_delay/10)*10)) * 100000 }; int globalDelay = this->pl->settings->delay;
struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay - ((globalDelay / 10) * 10)) * 100000 };
timeradd(&now, &delay, &(data->time)); timeradd(&now, &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[nValues]; char buffer[nValues];
Meter_updateValues(this, buffer, nValues - 1); Meter_updateValues(this, buffer, nValues - 1);
double value = 0.0; double value = 0.0;
int items = Meter_getItems(this); for (uint8_t i = 0; i < this->curItems; i++)
for (int i = 0; i < items; i++)
value += this->values[i]; value += this->values[i];
value /= this->total; value /= this->total;
data->values[nValues - 1] = value; data->values[nValues - 1] = value;
} }
int i = nValues - (w*2) + 2, k = 0; int i = nValues - (w * 2) + 2, k = 0;
if (i < 0) { if (i < 0) {
k = -i/2; k = -i / 2;
i = 0; i = 0;
} }
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); int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix);
int v2 = CLAMP((int) lround(data->values[i+1] * pix), 1, pix); int v2 = CLAMP((int) lround(data->values[i + 1] * 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++) {
@ -317,7 +338,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
int line2 = CLAMP(v2 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line)), 0, GraphMeterMode_pixPerRow); int line2 = CLAMP(v2 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line)), 0, GraphMeterMode_pixPerRow);
attrset(CRT_colors[colorIdx]); attrset(CRT_colors[colorIdx]);
mvaddstr(y+line, x+k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]); mvaddstr(y + line, x + k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]);
colorIdx = GRAPH_2; colorIdx = GRAPH_2;
} }
} }
@ -327,17 +348,17 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
/* ---------- LEDMeterMode ---------- */ /* ---------- LEDMeterMode ---------- */
static const char* const LEDMeterMode_digitsAscii[] = { static const char* const LEDMeterMode_digitsAscii[] = {
" __ "," "," __ "," __ "," "," __ "," __ "," __ "," __ "," __ ", " __ ", " ", " __ ", " __ ", " ", " __ ", " __ ", " __ ", " __ ", " __ ",
"| |"," |"," __|"," __|","|__|","|__ ","|__ "," |","|__|","|__|", "| |", " |", " __|", " __|", "|__|", "|__ ", "|__ ", " |", "|__|", "|__|",
"|__|"," |","|__ "," __|"," |"," __|","|__|"," |","|__|"," __|" "|__|", " |", "|__ ", " __|", " |", " __|", "|__|", " |", "|__|", " __|"
}; };
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
static const char* const LEDMeterMode_digitsUtf8[] = { static const char* const LEDMeterMode_digitsUtf8[] = {
"┌──┐","","╶──┐","╶──┐","╷ ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐", "┌──┐", "", "╶──┐", "╶──┐", "╷ ╷", "┌──╴", "┌──╴", "╶──┐", "┌──┐", "┌──┐",
"│ │","","┌──┘"," ──┤","└──┤","└──┐","├──┐","","├──┤","└──┤", "│ │", "", "┌──┘", " ──┤", "└──┤", "└──┐", "├──┐", "", "├──┤", "└──┤",
"└──┘","","└──╴","╶──┘","","╶──┘","└──┘","","└──┘"," ──┘" "└──┘", "", "└──╴", "╶──┘", "", "╶──┘", "└──┘", "", "└──┘", " ──┘"
}; };
#endif #endif
@ -367,9 +388,9 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
int yText = int yText =
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
CRT_utf8 ? y+1 : CRT_utf8 ? y + 1 :
#endif #endif
y+2; y + 2;
attrset(CRT_colors[LED_COLOR]); attrset(CRT_colors[LED_COLOR]);
mvaddstr(yText, x, this->caption); mvaddstr(yText, x, this->caption);
int xx = x + strlen(this->caption); int xx = x + strlen(this->caption);
@ -377,7 +398,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
char c = RichString_getCharVal(out, i); char c = RichString_getCharVal(out, i);
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
LEDMeterMode_drawDigit(xx, y, c-48); LEDMeterMode_drawDigit(xx, y, c - 48);
xx += 4; xx += 4;
} else { } else {
mvaddch(yText, xx, c); mvaddch(yText, xx, c);
@ -412,7 +433,7 @@ static MeterMode LEDMeterMode = {
.draw = LEDMeterMode_draw, .draw = LEDMeterMode_draw,
}; };
MeterMode* Meter_modes[] = { const MeterMode* const Meter_modes[] = {
NULL, NULL,
&BarMeterMode, &BarMeterMode,
&TextMeterMode, &TextMeterMode,
@ -430,16 +451,15 @@ static void BlankMeter_updateValues(Meter* this, char* buffer, int size) {
} }
} }
static void BlankMeter_display(Object* cast, RichString* out) { static void BlankMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
(void) cast;
RichString_prune(out); RichString_prune(out);
} }
int BlankMeter_attributes[] = { static const int BlankMeter_attributes[] = {
DEFAULT_COLOR DEFAULT_COLOR
}; };
MeterClass BlankMeter_class = { const MeterClass BlankMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,

58
Meter.h
View File

@ -3,15 +3,24 @@
/* /*
htop - Meter.h htop - Meter.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "ListItem.h" #include "config.h" // IWYU pragma: keep
#include <stdbool.h>
#include <stdint.h>
#include <sys/time.h> #include <sys/time.h>
#include "ListItem.h"
#include "Object.h"
#include "ProcessList.h"
#define METER_BUFFER_LEN 256 #define METER_BUFFER_LEN 256
struct Meter_;
typedef struct Meter_ Meter; typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*); typedef void(*Meter_Init)(Meter*);
@ -21,7 +30,7 @@ typedef void(*Meter_UpdateValues)(Meter*, char*, int);
typedef void(*Meter_Draw)(Meter*, int, int, int); typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ { typedef struct MeterClass_ {
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;
@ -29,16 +38,15 @@ typedef struct MeterClass_ {
const Meter_UpdateValues updateValues; const Meter_UpdateValues updateValues;
const int defaultMode; const int defaultMode;
const double total; const double total;
const int* attributes; const int* const attributes;
const char* name; const char* const name; /* internal name of the meter, must not contain any space */
const char* uiName; const char* const uiName; /* display name in header setup menu */
const char* caption; const char* const caption; /* prefix in the actual header */
const char* description; const char* const description; /* optional meter description in header setup menu */
const char maxItems; const uint8_t maxItems;
char curItems;
} MeterClass; } MeterClass;
#define As_Meter(this_) ((MeterClass*)((this_)->super.klass)) #define As_Meter(this_) ((const MeterClass*)((this_)->super.klass))
#define Meter_initFn(this_) As_Meter(this_)->init #define Meter_initFn(this_) As_Meter(this_)->init
#define Meter_init(this_) As_Meter(this_)->init((Meter*)(this_)) #define Meter_init(this_) As_Meter(this_)->init((Meter*)(this_))
#define Meter_done(this_) As_Meter(this_)->done((Meter*)(this_)) #define Meter_done(this_) As_Meter(this_)->done((Meter*)(this_))
@ -49,12 +57,15 @@ typedef struct MeterClass_ {
#define Meter_updateValues(this_, buf_, sz_) \ #define Meter_updateValues(this_, buf_, sz_) \
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_) As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_)
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode #define Meter_defaultMode(this_) As_Meter(this_)->defaultMode
#define Meter_getItems(this_) As_Meter(this_)->curItems
#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_)
#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
#define Meter_uiName(this_) As_Meter(this_)->uiName #define Meter_uiName(this_) As_Meter(this_)->uiName
typedef struct GraphData_ {
struct timeval time;
double values[METER_BUFFER_LEN];
} GraphData;
struct Meter_ { struct Meter_ {
Object super; Object super;
Meter_Draw draw; Meter_Draw draw;
@ -62,11 +73,13 @@ struct Meter_ {
char* caption; char* caption;
int mode; int mode;
int param; int param;
void* drawData; GraphData* drawData;
int h; int h;
struct ProcessList_* pl; const ProcessList* pl;
uint8_t curItems;
double* values; double* values;
double total; double total;
void* meterData;
}; };
typedef struct MeterMode_ { typedef struct MeterMode_ {
@ -84,14 +97,9 @@ typedef enum {
LAST_METERMODE LAST_METERMODE
} MeterModeId; } MeterModeId;
typedef struct GraphData_ { extern const MeterClass Meter_class;
struct timeval time;
double values[METER_BUFFER_LEN];
} GraphData;
extern MeterClass Meter_class; Meter* Meter_new(const ProcessList* pl, int param, const MeterClass* type);
Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type);
int Meter_humanUnit(char* buffer, unsigned long int value, int size); int Meter_humanUnit(char* buffer, unsigned long int value, int size);
@ -103,10 +111,8 @@ void Meter_setMode(Meter* this, int modeIndex);
ListItem* Meter_toListItem(Meter* this, bool moving); ListItem* Meter_toListItem(Meter* this, bool moving);
extern MeterMode* Meter_modes[]; extern const MeterMode* const Meter_modes[];
extern int BlankMeter_attributes[]; extern const MeterClass BlankMeter_class;
extern MeterClass BlankMeter_class;
#endif #endif

View File

@ -1,15 +1,21 @@
/* /*
htop - MetersPanel.c htop - MetersPanel.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "MetersPanel.h" #include "MetersPanel.h"
#include <stdlib.h> #include <stdlib.h>
#include <assert.h>
#include "CRT.h" #include "CRT.h"
#include "FunctionBar.h"
#include "Header.h"
#include "ListItem.h"
#include "Meter.h"
#include "Object.h"
#include "ProvideCurses.h"
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI // Note: In code the meters are known to have bar/text/graph "Modes", but in UI
@ -27,6 +33,13 @@ static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-
static int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)}; static int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
static FunctionBar* Meters_movingBar = NULL; static FunctionBar* Meters_movingBar = NULL;
void MetersPanel_cleanup() {
if (Meters_movingBar) {
FunctionBar_delete(Meters_movingBar);
Meters_movingBar = NULL;
}
}
static void MetersPanel_delete(Object* object) { static void MetersPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
MetersPanel* this = (MetersPanel*) object; MetersPanel* this = (MetersPanel*) object;
@ -48,7 +61,7 @@ void MetersPanel_setMoving(MetersPanel* this, bool moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]); Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]);
super->currentBar = Meters_movingBar; super->currentBar = Meters_movingBar;
} }
FunctionBar_draw(this->super.currentBar, NULL); FunctionBar_draw(this->super.currentBar);
} }
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) { static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {
@ -170,7 +183,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
} }
} }
if (result == HANDLED || sideMove) { if (result == HANDLED || sideMove) {
Header* 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); Header_draw(header);
@ -179,7 +192,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
return result; return result;
} }
PanelClass MetersPanel_class = { const PanelClass MetersPanel_class = {
.super = { .super = {
.extends = Class(Panel), .extends = Class(Panel),
.delete = MetersPanel_delete .delete = MetersPanel_delete

View File

@ -3,14 +3,19 @@
/* /*
htop - MetersPanel.h htop - MetersPanel.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Panel.h" #include <stdbool.h>
#include "Settings.h"
#include "ScreenManager.h"
#include "Panel.h"
#include "ScreenManager.h"
#include "Settings.h"
#include "Vector.h"
struct MetersPanel_;
typedef struct MetersPanel_ MetersPanel; typedef struct MetersPanel_ MetersPanel;
struct MetersPanel_ { struct MetersPanel_ {
@ -24,9 +29,11 @@ struct MetersPanel_ {
bool moving; bool moving;
}; };
void MetersPanel_cleanup(void);
void MetersPanel_setMoving(MetersPanel* this, bool moving); void MetersPanel_setMoving(MetersPanel* this, bool moving);
extern PanelClass MetersPanel_class; extern const PanelClass MetersPanel_class;
MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr); MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr);

125
NetworkIOMeter.c Normal file
View File

@ -0,0 +1,125 @@
#include "NetworkIOMeter.h"
#include <stdbool.h>
#include <stddef.h>
#include <sys/time.h>
#include "CRT.h"
#include "Macros.h"
#include "Object.h"
#include "Platform.h"
#include "RichString.h"
#include "XUtils.h"
static const int NetworkIOMeter_attributes[] = {
METER_VALUE_IOREAD,
METER_VALUE_IOWRITE,
};
static bool hasData = false;
static unsigned long int cached_rxb_diff = 0;
static unsigned long int cached_rxp_diff = 0;
static unsigned long int cached_txb_diff = 0;
static unsigned long int cached_txp_diff = 0;
static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, int len) {
static unsigned long long int cached_last_update = 0;
struct timeval tv;
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 */
if (passedTimeInMs > 500) {
static unsigned long int cached_rxb_total = 0;
static unsigned long int cached_rxp_total = 0;
static unsigned long int cached_txb_total = 0;
static unsigned long int cached_txp_total = 0;
cached_last_update = timeInMilliSeconds;
unsigned long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted;
hasData = Platform_getNetworkIO(&bytesReceived, &packetsReceived, &bytesTransmitted, &packetsTransmitted);
if (!hasData) {
xSnprintf(buffer, len, "no data");
return;
}
if (bytesReceived > cached_rxb_total) {
cached_rxb_diff = (bytesReceived - cached_rxb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
cached_rxb_diff = 1000.0 * cached_rxb_diff / passedTimeInMs; /* convert to per second */
} else {
cached_rxb_diff = 0;
}
cached_rxb_total = bytesReceived;
if (packetsReceived > cached_rxp_total) {
cached_rxp_diff = packetsReceived - cached_rxp_total;
} else {
cached_rxp_diff = 0;
}
cached_rxp_total = packetsReceived;
if (bytesTransmitted > cached_txb_total) {
cached_txb_diff = (bytesTransmitted - cached_txb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
cached_txb_diff = 1000.0 * cached_txb_diff / passedTimeInMs; /* convert to per second */
} else {
cached_txb_diff = 0;
}
cached_txb_total = bytesTransmitted;
if (packetsTransmitted > cached_txp_total) {
cached_txp_diff = packetsTransmitted - cached_txp_total;
} else {
cached_txp_diff = 0;
}
cached_txp_total = packetsTransmitted;
}
char bufferBytesReceived[12], bufferBytesTransmitted[12];
Meter_humanUnit(bufferBytesReceived, cached_rxb_diff, sizeof(bufferBytesReceived));
Meter_humanUnit(bufferBytesTransmitted, cached_txb_diff, sizeof(bufferBytesTransmitted));
xSnprintf(buffer, len, "rx:%siB/s tx:%siB/s", bufferBytesReceived, bufferBytesTransmitted);
}
static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
if (!hasData) {
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data");
return;
}
char buffer[64];
RichString_write(out, CRT_colors[METER_TEXT], "rx: ");
Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
RichString_append(out, CRT_colors[METER_TEXT], " tx: ");
Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff);
RichString_append(out, CRT_colors[METER_TEXT], buffer);
}
const MeterClass NetworkIOMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = NetworkIOMeter_display
},
.updateValues = NetworkIOMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 0,
.total = 100.0,
.attributes = NetworkIOMeter_attributes,
.name = "NetworkIO",
.uiName = "Network IO",
.caption = "Network: "
};

8
NetworkIOMeter.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef HEADER_NetworkIOMeter
#define HEADER_NetworkIOMeter
#include "Meter.h"
extern const MeterClass NetworkIOMeter_class;
#endif /* HEADER_NetworkIOMeter */

View File

@ -2,28 +2,32 @@
htop - Object.c htop - Object.c
(C) 2004-2012 Hisham H. Muhammad (C) 2004-2012 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved. (C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Object.h" #include "Object.h"
ObjectClass Object_class = { #include <stddef.h>
const ObjectClass Object_class = {
.extends = NULL .extends = NULL
}; };
#ifdef DEBUG #ifndef NDEBUG
bool Object_isA(Object* o, const ObjectClass* klass) { bool Object_isA(const Object* o, const ObjectClass* klass) {
if (!o) if (!o)
return false; return false;
const ObjectClass* type = o->klass;
while (type) { for (const ObjectClass* type = o->klass; type; type = type->extends) {
if (type == klass) if (type == klass) {
return true; return true;
type = type->extends; }
} }
return false; return false;
} }
#endif #endif /* NDEBUG */

View File

@ -4,41 +4,50 @@
htop - Object.h htop - Object.h
(C) 2004-2012 Hisham H. Muhammad (C) 2004-2012 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved. (C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "RichString.h" #include "config.h" // IWYU pragma: keep
#include "XAlloc.h"
#include "Macros.h"
#include <assert.h>
#include "RichString.h"
#include "XUtils.h" // IWYU pragma: keep
#ifndef NDEBUG
#include <stdbool.h>
#endif
struct Object_;
typedef struct Object_ Object; typedef struct Object_ Object;
typedef void(*Object_Display)(Object*, RichString*); typedef void(*Object_Display)(const Object*, RichString*);
typedef long(*Object_Compare)(const void*, const void*); typedef long(*Object_Compare)(const void*, const void*);
typedef void(*Object_Delete)(Object*); typedef void(*Object_Delete)(Object*);
#define Object_getClass(obj_) ((Object*)(obj_))->klass #define Object_getClass(obj_) ((const Object*)(obj_))->klass
#define Object_setClass(obj_, class_) Object_getClass(obj_) = (ObjectClass*) class_ #define Object_setClass(obj_, class_) (((Object*)(obj_))->klass = (const ObjectClass*) (class_))
#define Object_delete(obj_) Object_getClass(obj_)->delete((Object*)(obj_)) #define Object_delete(obj_) (assert(Object_getClass(obj_)->delete), Object_getClass(obj_)->delete((Object*)(obj_)))
#define Object_displayFn(obj_) Object_getClass(obj_)->display #define Object_displayFn(obj_) Object_getClass(obj_)->display
#define Object_display(obj_, str_) Object_getClass(obj_)->display((Object*)(obj_), str_) #define Object_display(obj_, str_) (assert(Object_getClass(obj_)->display), Object_getClass(obj_)->display((const Object*)(obj_), str_))
#define Object_compare(obj_, other_) Object_getClass(obj_)->compare((const void*)(obj_), other_) #define Object_compare(obj_, other_) (assert(Object_getClass(obj_)->compare), Object_getClass(obj_)->compare((const void*)(obj_), other_))
#define Class(class_) ((ObjectClass*)(&(class_ ## _class))) #define Class(class_) ((const ObjectClass*)(&(class_ ## _class)))
#define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_)); #define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_))
typedef struct ObjectClass_ { typedef struct ObjectClass_ {
const void* extends; const void* const extends;
const Object_Display display; const Object_Display display;
const Object_Delete delete; const Object_Delete delete;
const Object_Compare compare; const Object_Compare compare;
} ObjectClass; } ObjectClass;
struct Object_ { struct Object_ {
ObjectClass* klass; const ObjectClass* klass;
}; };
typedef union { typedef union {
@ -46,12 +55,12 @@ typedef union {
void* v; void* v;
} Arg; } Arg;
extern ObjectClass Object_class; extern const ObjectClass Object_class;
#ifdef DEBUG #ifndef NDEBUG
bool Object_isA(Object* o, const ObjectClass* klass); bool Object_isA(const Object* o, const ObjectClass* klass);
#endif #endif /* NDEBUG */
#endif #endif

View File

@ -1,93 +1,143 @@
/* /*
htop - OpenFilesScreen.c htop - OpenFilesScreen.c
(C) 2005-2006 Hisham H. Muhammad (C) 2005-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#include "OpenFilesScreen.h" #include "OpenFilesScreen.h"
#include "CRT.h"
#include "ProcessList.h"
#include "IncSet.h"
#include "StringUtils.h"
#include "FunctionBar.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include "Macros.h"
#include "Panel.h"
#include "ProvideCurses.h"
#include "Vector.h"
#include "XUtils.h"
InfoScreenClass OpenFilesScreen_class = {
.super = {
.extends = Class(Object),
.delete = OpenFilesScreen_delete
},
.scan = OpenFilesScreen_scan,
.draw = OpenFilesScreen_draw
};
OpenFilesScreen* OpenFilesScreen_new(Process* process) { typedef struct OpenFiles_Data_ {
char* data[7];
} OpenFiles_Data;
typedef struct OpenFiles_ProcessData_ {
OpenFiles_Data data;
int error;
struct OpenFiles_FileData_* files;
} OpenFiles_ProcessData;
typedef struct OpenFiles_FileData_ {
OpenFiles_Data data;
struct OpenFiles_FileData_* next;
} OpenFiles_FileData;
static size_t getIndexForType(char type) {
switch (type) {
case 'f':
return 0;
case 'a':
return 1;
case 'D':
return 2;
case 'i':
return 3;
case 'n':
return 4;
case 's':
return 5;
case 't':
return 6;
}
/* should never reach here */
abort();
}
static const char* getDataForType(const OpenFiles_Data* data, char type) {
size_t index = getIndexForType(type);
return data->data[index] ? data->data[index] : "";
}
OpenFilesScreen* OpenFilesScreen_new(const Process* process) {
OpenFilesScreen* this = xMalloc(sizeof(OpenFilesScreen)); OpenFilesScreen* this = xMalloc(sizeof(OpenFilesScreen));
Object_setClass(this, Class(OpenFilesScreen)); Object_setClass(this, Class(OpenFilesScreen));
if (Process_isThread(process)) if (Process_isThread(process)) {
this->pid = process->tgid; this->pid = process->tgid;
else } else {
this->pid = process->pid; this->pid = process->pid;
return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " FD TYPE DEVICE SIZE NODE NAME"); }
return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " FD TYPE MODE DEVICE SIZE NODE NAME");
} }
void OpenFilesScreen_delete(Object* this) { void OpenFilesScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this)); free(InfoScreen_done((InfoScreen*)this));
} }
void OpenFilesScreen_draw(InfoScreen* this) { static void OpenFilesScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Snapshot of files open in process %d - %s", ((OpenFilesScreen*)this)->pid, this->process->comm); InfoScreen_drawTitled(this, "Snapshot of files open in process %d - %s", ((OpenFilesScreen*)this)->pid, Process_getCommand(this->process));
} }
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
char buffer[1025];
xSnprintf(buffer, 1024, "%d", pid);
OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData)); OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_FileData* fdata = NULL;
OpenFiles_Data* item = &(pdata->data); int fdpair[2] = {0, 0};
int fdpair[2];
if (pipe(fdpair) == -1) { if (pipe(fdpair) == -1) {
pdata->error = 1; pdata->error = 1;
return pdata; return pdata;
} }
pid_t child = fork(); pid_t child = fork();
if (child == -1) { if (child == -1) {
close(fdpair[1]);
close(fdpair[0]);
pdata->error = 1; pdata->error = 1;
return pdata; return pdata;
} }
if (child == 0) { if (child == 0) {
close(fdpair[0]); close(fdpair[0]);
dup2(fdpair[1], STDOUT_FILENO); dup2(fdpair[1], STDOUT_FILENO);
close(fdpair[1]); close(fdpair[1]);
int fdnull = open("/dev/null", O_WRONLY); int fdnull = open("/dev/null", O_WRONLY);
if (fdnull < 0) if (fdnull < 0) {
exit(1); exit(1);
}
dup2(fdnull, STDERR_FILENO); dup2(fdnull, STDERR_FILENO);
close(fdnull); close(fdnull);
char buffer[32] = {0};
xSnprintf(buffer, sizeof(buffer), "%d", pid);
execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL); execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
exit(127); exit(127);
} }
close(fdpair[1]); close(fdpair[1]);
OpenFiles_Data* item = &(pdata->data);
OpenFiles_FileData* fdata = NULL;
FILE* fd = fdopen(fdpair[0], "r"); FILE* fd = fdopen(fdpair[0], "r");
if (!fd) {
pdata->error = 1;
return pdata;
}
for (;;) { for (;;) {
char* line = String_readLine(fd); char* line = String_readLine(fd);
if (!line) { if (!line) {
break; break;
} }
unsigned char cmd = line[0]; unsigned char cmd = line[0];
if (cmd == 'f') { switch (cmd) {
case 'f': /* file descriptor */
{
OpenFiles_FileData* nextFile = xCalloc(1, sizeof(OpenFiles_FileData)); OpenFiles_FileData* nextFile = xCalloc(1, sizeof(OpenFiles_FileData));
if (fdata == NULL) { if (fdata == NULL) {
pdata->files = nextFile; pdata->files = nextFile;
@ -96,30 +146,60 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
} }
fdata = nextFile; fdata = nextFile;
item = &(fdata->data); item = &(fdata->data);
} /* FALLTHRU */
case 'a': /* file access mode */
case 'D': /* file's major/minor device number */
case 'i': /* file's inode number */
case 'n': /* file name, comment, Internet address */
case 's': /* file's size */
case 't': /* file's type */
{
size_t index = getIndexForType(cmd);
free(item->data[index]);
item->data[index] = xStrdup(line + 1);
break;
}
case 'c': /* process command name */
case 'd': /* file's device character code */
case 'g': /* process group ID */
case 'G': /* file flags */
case 'k': /* link count */
case 'l': /* file's lock status */
case 'L': /* process login name */
case 'o': /* file's offset */
case 'p': /* process ID */
case 'P': /* protocol name */
case 'R': /* parent process ID */
case 'T': /* TCP/TPI information, identified by prefixes */
case 'u': /* process user ID */
/* ignore */
break;
} }
item->data[cmd] = xStrdup(line + 1);
free(line); free(line);
} }
fclose(fd); fclose(fd);
int wstatus; int wstatus;
if (waitpid(child, &wstatus, 0) == -1) { if (waitpid(child, &wstatus, 0) == -1) {
pdata->error = 1; pdata->error = 1;
return pdata; return pdata;
} }
if (!WIFEXITED(wstatus))
if (!WIFEXITED(wstatus)) {
pdata->error = 1; pdata->error = 1;
else } else {
pdata->error = WEXITSTATUS(wstatus); pdata->error = WEXITSTATUS(wstatus);
}
return pdata; return pdata;
} }
static inline void OpenFiles_Data_clear(OpenFiles_Data* data) { static void OpenFiles_Data_clear(OpenFiles_Data* data) {
for (int i = 0; i < 255; i++) for (size_t i = 0; i < ARRAYSIZE(data->data); i++)
if (data->data[i]) free(data->data[i]);
free(data->data[i]);
} }
void OpenFilesScreen_scan(InfoScreen* this) { static void OpenFilesScreen_scan(InfoScreen* this) {
Panel* panel = this->display; Panel* panel = this->display;
int idx = Panel_getSelectedIndex(panel); int idx = Panel_getSelectedIndex(panel);
Panel_prune(panel); Panel_prune(panel);
@ -131,19 +211,20 @@ void OpenFilesScreen_scan(InfoScreen* this) {
} else { } else {
OpenFiles_FileData* fdata = pdata->files; OpenFiles_FileData* fdata = pdata->files;
while (fdata) { while (fdata) {
char** data = fdata->data.data; OpenFiles_Data* data = &fdata->data;
int lenN = data['n'] ? strlen(data['n']) : 0; size_t lenN = strlen(getDataForType(data, 'n'));
int sizeEntry = 5 + 7 + 10 + 10 + 10 + lenN + 5 /*spaces*/ + 1 /*null*/; size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + lenN + 7 /*spaces*/ + 1 /*null*/;
char entry[sizeEntry]; char entry[sizeEntry];
xSnprintf(entry, sizeEntry, "%5.5s %7.7s %10.10s %10.10s %10.10s %s", xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %s",
data['f'] ? data['f'] : "", getDataForType(data, 'f'),
data['t'] ? data['t'] : "", getDataForType(data, 't'),
data['D'] ? data['D'] : "", getDataForType(data, 'a'),
data['s'] ? data['s'] : "", getDataForType(data, 'D'),
data['i'] ? data['i'] : "", getDataForType(data, 's'),
data['n'] ? data['n'] : ""); getDataForType(data, 'i'),
getDataForType(data, 'n'));
InfoScreen_addLine(this, entry); InfoScreen_addLine(this, entry);
OpenFiles_Data_clear(&fdata->data); OpenFiles_Data_clear(data);
OpenFiles_FileData* old = fdata; OpenFiles_FileData* old = fdata;
fdata = fdata->next; fdata = fdata->next;
free(old); free(old);
@ -155,3 +236,12 @@ void OpenFilesScreen_scan(InfoScreen* this) {
Vector_insertionSort(panel->items); Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx); Panel_setSelected(panel, idx);
} }
const InfoScreenClass OpenFilesScreen_class = {
.super = {
.extends = Class(Object),
.delete = OpenFilesScreen_delete
},
.scan = OpenFilesScreen_scan,
.draw = OpenFilesScreen_draw
};

View File

@ -3,40 +3,25 @@
/* /*
htop - OpenFilesScreen.h htop - OpenFilesScreen.h
(C) 2005-2006 Hisham H. Muhammad (C) 2005-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include <sys/types.h>
#include "InfoScreen.h" #include "InfoScreen.h"
#include "Object.h"
typedef struct OpenFiles_Data_ { #include "Process.h"
char* data[256];
} OpenFiles_Data;
typedef struct OpenFiles_ProcessData_ {
OpenFiles_Data data;
int error;
struct OpenFiles_FileData_* files;
} OpenFiles_ProcessData;
typedef struct OpenFiles_FileData_ {
OpenFiles_Data data;
struct OpenFiles_FileData_* next;
} OpenFiles_FileData;
typedef struct OpenFilesScreen_ { typedef struct OpenFilesScreen_ {
InfoScreen super; InfoScreen super;
pid_t pid; pid_t pid;
} OpenFilesScreen; } OpenFilesScreen;
extern InfoScreenClass OpenFilesScreen_class; extern const InfoScreenClass OpenFilesScreen_class;
OpenFilesScreen* OpenFilesScreen_new(Process* process); OpenFilesScreen* OpenFilesScreen_new(const Process* process);
void OpenFilesScreen_delete(Object* this); void OpenFilesScreen_delete(Object* this);
void OpenFilesScreen_draw(InfoScreen* this);
void OpenFilesScreen_scan(InfoScreen* this);
#endif #endif

189
OptionItem.c Normal file
View File

@ -0,0 +1,189 @@
/*
htop - OptionItem.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "OptionItem.h"
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include "CRT.h"
#include "RichString.h"
static void OptionItem_delete(Object* cast) {
OptionItem* this = (OptionItem*)cast;
assert (this != NULL);
free(this->text);
free(this);
}
static void CheckItem_display(const Object* cast, RichString* out) {
const CheckItem* this = (const CheckItem*)cast;
assert (this != NULL);
RichString_write(out, CRT_colors[CHECK_BOX], "[");
if (CheckItem_get(this)) {
RichString_append(out, CRT_colors[CHECK_MARK], "x");
} else {
RichString_append(out, CRT_colors[CHECK_MARK], " ");
}
RichString_append(out, CRT_colors[CHECK_BOX], "] ");
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text);
}
static void NumberItem_display(const Object* cast, RichString* out) {
const NumberItem* this = (const NumberItem*)cast;
assert (this != NULL);
char buffer[12];
RichString_write(out, CRT_colors[CHECK_BOX], "[");
int written;
if (this->scale < 0) {
written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this));
} else if (this->scale > 0) {
written = xSnprintf(buffer, sizeof(buffer), "%d", (int) (pow(10, this->scale) * NumberItem_get(this)));
} else {
written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this));
}
RichString_append(out, CRT_colors[CHECK_MARK], buffer);
RichString_append(out, CRT_colors[CHECK_BOX], "]");
for (int i = written; i < 5; i++) {
RichString_append(out, CRT_colors[CHECK_BOX], " ");
}
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text);
}
const OptionItemClass OptionItem_class = {
.super = {
.extends = Class(Object),
.delete = OptionItem_delete
}
};
const OptionItemClass CheckItem_class = {
.super = {
.extends = Class(OptionItem),
.delete = OptionItem_delete,
.display = CheckItem_display
},
.kind = OPTION_ITEM_CHECK
};
const OptionItemClass NumberItem_class = {
.super = {
.extends = Class(OptionItem),
.delete = OptionItem_delete,
.display = NumberItem_display
},
.kind = OPTION_ITEM_NUMBER
};
CheckItem* CheckItem_newByRef(const char* text, bool* ref) {
CheckItem* this = AllocThis(CheckItem);
this->super.text = xStrdup(text);
this->value = false;
this->ref = ref;
return this;
}
CheckItem* CheckItem_newByVal(const char* text, bool value) {
CheckItem* this = AllocThis(CheckItem);
this->super.text = xStrdup(text);
this->value = value;
this->ref = NULL;
return this;
}
bool CheckItem_get(const CheckItem* this) {
if (this->ref) {
return *(this->ref);
} else {
return this->value;
}
}
void CheckItem_set(CheckItem* this, bool value) {
if (this->ref) {
*(this->ref) = value;
} else {
this->value = value;
}
}
void CheckItem_toggle(CheckItem* this) {
if (this->ref) {
*(this->ref) = !*(this->ref);
} else {
this->value = !this->value;
}
}
NumberItem* NumberItem_newByRef(const char* text, int* ref, int scale, int min, int max) {
assert(min <= max);
NumberItem* this = AllocThis(NumberItem);
this->super.text = xStrdup(text);
this->value = 0;
this->ref = ref;
this->scale = scale;
this->min = min;
this->max = max;
return this;
}
NumberItem* NumberItem_newByVal(const char* text, int value, int scale, int min, int max) {
assert(min <= max);
NumberItem* this = AllocThis(NumberItem);
this->super.text = xStrdup(text);
this->value = CLAMP(value, min, max);
this->ref = NULL;
this->scale = scale;
this->min = min;
this->max = max;
return this;
}
int NumberItem_get(const NumberItem* this) {
if (this->ref) {
return *(this->ref);
} else {
return this->value;
}
}
void NumberItem_decrease(NumberItem* this) {
if (this->ref) {
*(this->ref) = CLAMP(*(this->ref) - 1, this->min, this->max);
} else {
this->value = CLAMP(this->value - 1, this->min, this->max);
}
}
void NumberItem_increase(NumberItem* this) {
if (this->ref) {
*(this->ref) = CLAMP(*(this->ref) + 1, this->min, this->max);
} else {
this->value = CLAMP(this->value + 1, this->min, this->max);
}
}
void NumberItem_toggle(NumberItem* this) {
if (this->ref) {
if (*(this->ref) >= this->max)
*(this->ref) = this->min;
else
*(this->ref) += 1;
} else {
if (this->value >= this->max)
this->value = this->min;
else
this->value += 1;
}
}

70
OptionItem.h Normal file
View File

@ -0,0 +1,70 @@
#ifndef HEADER_OptionItem
#define HEADER_OptionItem
/*
htop - OptionItem.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include <stdbool.h>
#include "Object.h"
enum OptionItemType {
OPTION_ITEM_CHECK,
OPTION_ITEM_NUMBER,
};
typedef struct OptionItemClass_ {
const ObjectClass super;
enum OptionItemType kind;
} OptionItemClass;
#define As_OptionItem(this_) ((const OptionItemClass*)((this_)->super.klass))
#define OptionItem_kind(this_) As_OptionItem(this_)->kind
typedef struct OptionItem_ {
Object super;
char* text;
} OptionItem;
typedef struct CheckItem_ {
OptionItem super;
bool* ref;
bool value;
} CheckItem;
typedef struct NumberItem_ {
OptionItem super;
char* text;
int* ref;
int value;
int scale;
int min;
int max;
} NumberItem;
extern const OptionItemClass OptionItem_class;
extern const OptionItemClass CheckItem_class;
extern const OptionItemClass NumberItem_class;
CheckItem* CheckItem_newByRef(const char* text, bool* ref);
CheckItem* CheckItem_newByVal(const char* text, bool value);
bool CheckItem_get(const CheckItem* this);
void CheckItem_set(CheckItem* this, bool value);
void CheckItem_toggle(CheckItem* this);
NumberItem* NumberItem_newByRef(const char* text, int* ref, int scale, int min, int max);
NumberItem* NumberItem_newByVal(const char* text, int value, int scale, int min, int max);
int NumberItem_get(const NumberItem* this);
void NumberItem_decrease(NumberItem* this);
void NumberItem_increase(NumberItem* this);
void NumberItem_toggle(NumberItem* this);
#endif

144
Panel.c
View File

@ -1,25 +1,28 @@
/* /*
htop - Panel.c htop - Panel.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Panel.h" #include "Panel.h"
#include "CRT.h" #include <assert.h>
#include "RichString.h" #include <ctype.h>
#include "ListItem.h"
#include "StringUtils.h"
#include <math.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <strings.h>
PanelClass Panel_class = { #include "CRT.h"
#include "ListItem.h"
#include "Macros.h"
#include "ProvideCurses.h"
#include "RichString.h"
#include "XUtils.h"
const PanelClass Panel_class = {
.super = { .super = {
.extends = Class(Object), .extends = Class(Object),
.delete = Panel_delete .delete = Panel_delete
@ -27,7 +30,7 @@ PanelClass Panel_class = {
.eventHandler = Panel_selectByTyping, .eventHandler = Panel_selectByTyping,
}; };
Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar) { Panel* Panel_new(int x, int y, int w, int h, bool owner, const ObjectClass* type, FunctionBar* fuBar) {
Panel* this; Panel* this;
this = xMalloc(sizeof(Panel)); this = xMalloc(sizeof(Panel));
Object_setClass(this, Class(Panel)); Object_setClass(this, Class(Panel));
@ -41,7 +44,7 @@ void Panel_delete(Object* cast) {
free(this); free(this);
} }
void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar) { void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar) {
this->x = x; this->x = x;
this->y = y; this->y = y;
this->w = w; this->w = w;
@ -94,8 +97,10 @@ void Panel_move(Panel* this, int x, int y) {
void Panel_resize(Panel* this, int w, int h) { void Panel_resize(Panel* this, int w, int h) {
assert (this != NULL); assert (this != NULL);
if (RichString_sizeVal(this->header) > 0) if (RichString_sizeVal(this->header) > 0) {
h--; h--;
}
this->w = w; this->w = w;
this->h = h; this->h = h;
this->needsRedraw = true; this->needsRedraw = true;
@ -142,33 +147,38 @@ Object* Panel_remove(Panel* this, int i) {
this->needsRedraw = true; this->needsRedraw = true;
Object* removed = Vector_remove(this->items, i); Object* removed = Vector_remove(this->items, i);
if (this->selected > 0 && this->selected >= Vector_size(this->items)) if (this->selected > 0 && this->selected >= Vector_size(this->items)) {
this->selected--; this->selected--;
}
return removed; return removed;
} }
Object* Panel_getSelected(Panel* this) { Object* Panel_getSelected(Panel* this) {
assert (this != NULL); assert (this != NULL);
if (Vector_size(this->items) > 0) if (Vector_size(this->items) > 0) {
return Vector_get(this->items, this->selected); return Vector_get(this->items, this->selected);
else } else {
return NULL; return NULL;
}
} }
void Panel_moveSelectedUp(Panel* this) { void Panel_moveSelectedUp(Panel* this) {
assert (this != NULL); assert (this != NULL);
Vector_moveUp(this->items, this->selected); Vector_moveUp(this->items, this->selected);
if (this->selected > 0) if (this->selected > 0) {
this->selected--; this->selected--;
}
} }
void Panel_moveSelectedDown(Panel* this) { void Panel_moveSelectedDown(Panel* this) {
assert (this != NULL); assert (this != NULL);
Vector_moveDown(this->items, this->selected); Vector_moveDown(this->items, this->selected);
if (this->selected + 1 < Vector_size(this->items)) if (this->selected + 1 < Vector_size(this->items)) {
this->selected++; this->selected++;
}
} }
int Panel_getSelectedIndex(Panel* this) { int Panel_getSelectedIndex(Panel* this) {
@ -190,15 +200,16 @@ void Panel_setSelected(Panel* this, int selected) {
if (selected >= size) { if (selected >= size) {
selected = size - 1; selected = size - 1;
} }
if (selected < 0) if (selected < 0) {
selected = 0; selected = 0;
}
this->selected = selected; this->selected = selected;
if (Panel_eventHandlerFn(this)) { if (Panel_eventHandlerFn(this)) {
Panel_eventHandler(this, EVENT_SET_SELECTED); Panel_eventHandler(this, EVENT_SET_SELECTED);
} }
} }
void Panel_splice(Panel *this, Vector* from) { void Panel_splice(Panel* this, Vector* from) {
assert (this != NULL); assert (this != NULL);
assert (from != NULL); assert (from != NULL);
@ -206,7 +217,7 @@ void Panel_splice(Panel *this, Vector* from) {
this->needsRedraw = true; this->needsRedraw = true;
} }
void Panel_draw(Panel* this, bool focus) { void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
assert (this != NULL); assert (this != NULL);
int size = Vector_size(this->items); int size = Vector_size(this->items);
@ -251,28 +262,29 @@ void Panel_draw(Panel* this, bool focus) {
int upTo = MINIMUM(first + h, size); int upTo = MINIMUM(first + h, size);
int selectionColor = focus int selectionColor = focus
? this->selectionColor ? this->selectionColor
: CRT_colors[PANEL_SELECTION_UNFOCUS]; : CRT_colors[PANEL_SELECTION_UNFOCUS];
if (this->needsRedraw) { if (this->needsRedraw) {
int line = 0; int line = 0;
for(int i = first; line < h && i < upTo; i++) { for (int i = first; line < h && i < upTo; i++) {
Object* itemObj = Vector_get(this->items, i); Object* itemObj = Vector_get(this->items, i);
assert(itemObj); if(!itemObj) continue;
RichString_begin(item); RichString_begin(item);
Object_display(itemObj, &item); Object_display(itemObj, &item);
int itemLen = RichString_sizeVal(item); int itemLen = RichString_sizeVal(item);
int amt = MINIMUM(itemLen - scrollH, this->w); int amt = MINIMUM(itemLen - scrollH, this->w);
bool selected = (i == this->selected); if (highlightSelected && i == this->selected) {
if (selected) { item.highlightAttr = selectionColor;
attrset(selectionColor); }
RichString_setAttr(&item, selectionColor); if (item.highlightAttr) {
attrset(item.highlightAttr);
RichString_setAttr(&item, item.highlightAttr);
this->selectedLen = itemLen; this->selectedLen = itemLen;
} }
mvhline(y + line, x, ' ', this->w); mvhline(y + line, x, ' ', this->w);
if (amt > 0) if (amt > 0)
RichString_printoffnVal(item, y + line, x, scrollH, amt); RichString_printoffnVal(item, y + line, x, scrollH, amt);
if (selected) if (item.highlightAttr)
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
RichString_end(item); RichString_end(item);
line++; line++;
@ -285,7 +297,6 @@ void Panel_draw(Panel* this, bool focus) {
} else { } else {
Object* oldObj = Vector_get(this->items, this->oldSelected); Object* oldObj = Vector_get(this->items, this->oldSelected);
assert(oldObj);
RichString_begin(old); RichString_begin(old);
Object_display(oldObj, &old); Object_display(oldObj, &old);
int oldLen = RichString_sizeVal(old); int oldLen = RichString_sizeVal(old);
@ -294,15 +305,15 @@ void Panel_draw(Panel* this, bool focus) {
Object_display(newObj, &new); Object_display(newObj, &new);
int newLen = RichString_sizeVal(new); int newLen = RichString_sizeVal(new);
this->selectedLen = newLen; this->selectedLen = newLen;
mvhline(y+ this->oldSelected - first, x+0, ' ', this->w); mvhline(y + this->oldSelected - first, x + 0, ' ', this->w);
if (scrollH < oldLen) if (scrollH < oldLen)
RichString_printoffnVal(old, y+this->oldSelected - first, x, RichString_printoffnVal(old, y + this->oldSelected - first, x,
scrollH, MINIMUM(oldLen - scrollH, this->w)); scrollH, MINIMUM(oldLen - scrollH, this->w));
attrset(selectionColor); attrset(selectionColor);
mvhline(y+this->selected - first, x+0, ' ', this->w); mvhline(y + this->selected - first, x + 0, ' ', this->w);
RichString_setAttr(&new, selectionColor); RichString_setAttr(&new, selectionColor);
if (scrollH < newLen) if (scrollH < newLen)
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_end(new);
@ -316,25 +327,27 @@ bool Panel_onKey(Panel* this, int key) {
assert (this != NULL); assert (this != NULL);
int size = Vector_size(this->items); int size = Vector_size(this->items);
#define CLAMP_INDEX(var, delta, min, max) \
CLAMP((var) + (delta), (min), MAXIMUM(0, (max)))
switch (key) { switch (key) {
case KEY_DOWN: case KEY_DOWN:
case KEY_CTRL('N'): case KEY_CTRL('N'):
this->selected++;
break;
case KEY_UP:
case KEY_CTRL('P'):
this->selected--;
break;
#ifdef KEY_C_DOWN #ifdef KEY_C_DOWN
case KEY_C_DOWN: case KEY_C_DOWN:
#endif
this->selected++; this->selected++;
break; break;
#endif
case KEY_UP:
case KEY_CTRL('P'):
#ifdef KEY_C_UP #ifdef KEY_C_UP
case KEY_C_UP: case KEY_C_UP:
#endif
this->selected--; this->selected--;
break; break;
#endif
case KEY_LEFT: case KEY_LEFT:
case KEY_CTRL('B'): case KEY_CTRL('B'):
if (this->scrollH > 0) { if (this->scrollH > 0) {
@ -342,43 +355,45 @@ bool Panel_onKey(Panel* this, int key) {
this->needsRedraw = true; this->needsRedraw = true;
} }
break; break;
case KEY_RIGHT: case KEY_RIGHT:
case KEY_CTRL('F'): case KEY_CTRL('F'):
this->scrollH += CRT_scrollHAmount; this->scrollH += CRT_scrollHAmount;
this->needsRedraw = true; this->needsRedraw = true;
break; break;
case KEY_PPAGE: case KEY_PPAGE:
this->selected -= (this->h - 1); this->selected -= (this->h - 1);
this->scrollV = MAXIMUM(0, this->scrollV - this->h + 1); this->scrollV = CLAMP_INDEX(this->scrollV, -(this->h - 1), 0, size - this->h);
this->needsRedraw = true; this->needsRedraw = true;
break; break;
case KEY_NPAGE: case KEY_NPAGE:
this->selected += (this->h - 1); this->selected += (this->h - 1);
this->scrollV = MAXIMUM(0, MINIMUM(Vector_size(this->items) - this->h, this->scrollV = CLAMP_INDEX(this->scrollV, +(this->h - 1), 0, size - this->h);
this->scrollV + this->h - 1));
this->needsRedraw = true; this->needsRedraw = true;
break; break;
case KEY_WHEELUP: case KEY_WHEELUP:
this->selected -= CRT_scrollWheelVAmount; this->selected -= CRT_scrollWheelVAmount;
this->scrollV -= CRT_scrollWheelVAmount; this->scrollV = CLAMP_INDEX(this->scrollV, -CRT_scrollWheelVAmount, 0, size - this->h);
this->needsRedraw = true; this->needsRedraw = true;
break; break;
case KEY_WHEELDOWN: case KEY_WHEELDOWN:
{
this->selected += CRT_scrollWheelVAmount; this->selected += CRT_scrollWheelVAmount;
this->scrollV += CRT_scrollWheelVAmount; this->scrollV = CLAMP_INDEX(this->scrollV, +CRT_scrollWheelVAmount, 0, size - this->h);
if (this->scrollV > Vector_size(this->items) - this->h) {
this->scrollV = Vector_size(this->items) - this->h;
}
this->needsRedraw = true; this->needsRedraw = true;
break; break;
}
case KEY_HOME: case KEY_HOME:
this->selected = 0; this->selected = 0;
break; break;
case KEY_END: case KEY_END:
this->selected = size - 1; this->selected = size - 1;
break; break;
case KEY_CTRL('A'): case KEY_CTRL('A'):
case '^': case '^':
this->scrollH = 0; this->scrollH = 0;
@ -393,6 +408,8 @@ bool Panel_onKey(Panel* this, int key) {
return false; return false;
} }
#undef CLAMP_INDEX
// ensure selection within bounds // ensure selection within bounds
if (this->selected < 0 || size == 0) { if (this->selected < 0 || size == 0) {
this->selected = 0; this->selected = 0;
@ -401,43 +418,60 @@ bool Panel_onKey(Panel* this, int key) {
this->selected = size - 1; this->selected = size - 1;
this->needsRedraw = true; this->needsRedraw = true;
} }
return true; return true;
} }
HandlerResult Panel_selectByTyping(Panel* this, int ch) { HandlerResult Panel_selectByTyping(Panel* this, int ch) {
int size = Panel_size(this); int size = Panel_size(this);
if (!this->eventHandlerState) if (!this->eventHandlerState)
this->eventHandlerState = xCalloc(100, sizeof(char)); this->eventHandlerState = xCalloc(100, sizeof(char));
char* buffer = this->eventHandlerState; char* buffer = this->eventHandlerState;
if (ch > 0 && ch < 255 && isalnum(ch)) { if (0 < ch && ch < 255 && isgraph((unsigned char)ch)) {
int len = strlen(buffer); int len = strlen(buffer);
if (!len) {
if ('/' == ch) {
ch = '\001';
} else if ('q' == ch) {
return BREAK_LOOP;
}
} else if (1 == len && '\001' == buffer[0]) {
len--;
}
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++) {
len = strlen(buffer); len = strlen(buffer);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
char* cur = ((ListItem*) Panel_get(this, i))->value; const char* cur = ((ListItem*) Panel_get(this, i))->value;
while (*cur == ' ') cur++; while (*cur == ' ') cur++;
if (strncasecmp(cur, buffer, len) == 0) { if (strncasecmp(cur, buffer, len) == 0) {
Panel_setSelected(this, i); Panel_setSelected(this, i);
return HANDLED; return HANDLED;
} }
} }
// if current word did not match, // if current word did not match,
// retry considering the character the start of a new word. // retry considering the character the start of a new word.
buffer[0] = ch; buffer[0] = ch;
buffer[1] = '\0'; buffer[1] = '\0';
} }
return HANDLED; return HANDLED;
} else if (ch != ERR) { } else if (ch != ERR) {
buffer[0] = '\0'; buffer[0] = '\0';
} }
if (ch == 13) { if (ch == 13) {
return BREAK_LOOP; return BREAK_LOOP;
} }
return IGNORED; return IGNORED;
} }

36
Panel.h
View File

@ -3,14 +3,19 @@
/* /*
htop - Panel.h htop - Panel.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Object.h" #include <stdbool.h>
#include "Vector.h"
#include "FunctionBar.h"
#include "FunctionBar.h"
#include "Object.h"
#include "RichString.h"
#include "Vector.h"
struct Panel_;
typedef struct Panel_ Panel; typedef struct Panel_ Panel;
typedef enum HandlerResult_ { typedef enum HandlerResult_ {
@ -22,11 +27,11 @@ typedef enum HandlerResult_ {
SYNTH_KEY = 0x20, SYNTH_KEY = 0x20,
} HandlerResult; } HandlerResult;
#define EVENT_SET_SELECTED -1 #define EVENT_SET_SELECTED (-1)
#define EVENT_HEADER_CLICK(x_) (-10000 + x_) #define EVENT_HEADER_CLICK(x_) (-10000 + (x_))
#define EVENT_IS_HEADER_CLICK(ev_) (ev_ >= -10000 && ev_ <= -9000) #define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) (ev_ + 10000) #define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000)
typedef HandlerResult(*Panel_EventHandler)(Panel*, int); typedef HandlerResult(*Panel_EventHandler)(Panel*, int);
@ -35,14 +40,13 @@ typedef struct PanelClass_ {
const Panel_EventHandler eventHandler; const Panel_EventHandler eventHandler;
} PanelClass; } PanelClass;
#define As_Panel(this_) ((PanelClass*)((this_)->super.klass)) #define As_Panel(this_) ((const PanelClass*)((this_)->super.klass))
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler #define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
#define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_) #define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_)
struct Panel_ { struct Panel_ {
Object super; Object super;
int x, y, w, h; int x, y, w, h;
WINDOW* window;
Vector* items; Vector* items;
int selected; int selected;
int oldSelected; int oldSelected;
@ -57,17 +61,17 @@ struct Panel_ {
int selectionColor; int selectionColor;
}; };
#define Panel_setDefaultBar(this_) do{ (this_)->currentBar = (this_)->defaultBar; }while(0) #define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0)
#define KEY_CTRL(l) ((l)-'A'+1) #define KEY_CTRL(l) ((l)-'A'+1)
extern PanelClass Panel_class; extern const PanelClass Panel_class;
Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar); Panel* Panel_new(int x, int y, int w, int h, bool owner, const ObjectClass* type, FunctionBar* fuBar);
void Panel_delete(Object* cast); void Panel_delete(Object* cast);
void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar); void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar);
void Panel_done(Panel* this); void Panel_done(Panel* this);
@ -105,9 +109,9 @@ int Panel_size(Panel* this);
void Panel_setSelected(Panel* this, int selected); void Panel_setSelected(Panel* this, int selected);
void Panel_draw(Panel* this, bool focus); void Panel_draw(Panel* this, bool focus, bool highlightSelected);
void Panel_splice(Panel *this, Vector* from); void Panel_splice(Panel* this, Vector* from);
bool Panel_onKey(Panel* this, int key); bool Panel_onKey(Panel* this, int key);

345
Process.c
View File

@ -2,41 +2,41 @@
htop - Process.c htop - Process.c
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved. (C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Process.h" #include "config.h" // IWYU pragma: keep
#include "Settings.h"
#include "config.h" #include "Process.h"
#include <assert.h>
#include <limits.h>
#include <math.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/resource.h>
#include "CRT.h" #include "CRT.h"
#include "StringUtils.h" #include "Macros.h"
#include "RichString.h"
#include "Platform.h" #include "Platform.h"
#include "ProcessList.h"
#include "RichString.h"
#include "Settings.h"
#include "XUtils.h"
#include <stdio.h> #if defined(MAJOR_IN_MKDEV)
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <stdbool.h>
#include <pwd.h>
#include <time.h>
#include <assert.h>
#include <math.h>
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h> #include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS) #elif defined(MAJOR_IN_SYSMACROS)
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
#endif #endif
static int Process_getuid = -1;
static uid_t Process_getuid = (uid_t)-1;
char Process_pidFormat[20] = "%7d "; char Process_pidFormat[20] = "%7d ";
@ -44,7 +44,9 @@ static char Process_titleBuffer[20][20];
void Process_setupColumnWidths() { void Process_setupColumnWidths() {
int maxPid = Platform_getMaxPid(); int maxPid = Platform_getMaxPid();
if (maxPid == -1) return; if (maxPid == -1)
return;
int digits = ceil(log10(maxPid)); int digits = ceil(log10(maxPid));
assert(digits < 20); assert(digits < 20);
for (int i = 0; Process_pidColumns[i].label; i++) { for (int i = 0; Process_pidColumns[i].label; i++) {
@ -55,84 +57,104 @@ void Process_setupColumnWidths() {
xSnprintf(Process_pidFormat, sizeof(Process_pidFormat), "%%%dd ", digits); xSnprintf(Process_pidFormat, sizeof(Process_pidFormat), "%%%dd ", digits);
} }
void Process_humanNumber(RichString* str, unsigned long number, bool coloring) { void Process_humanNumber(RichString* str, unsigned long long number, bool coloring) {
char buffer[11]; char buffer[10];
int len; int len;
int largeNumberColor = CRT_colors[LARGE_NUMBER]; int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
int processGigabytesColor = CRT_colors[PROCESS_GIGABYTES];
int processColor = CRT_colors[PROCESS]; int processColor = CRT_colors[PROCESS];
if (!coloring) { if (!coloring) {
largeNumberColor = CRT_colors[PROCESS]; largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS]; processMegabytesColor = CRT_colors[PROCESS];
processGigabytesColor = CRT_colors[PROCESS];
} }
if(number >= (10 * ONE_DECIMAL_M)) { if (number < 1000) {
#ifdef __LP64__ //Plain number, no markings
if(number >= (100 * ONE_DECIMAL_G)) { len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number);
len = snprintf(buffer, 10, "%4luT ", number / ONE_G); RichString_appendn(str, processColor, buffer, len);
RichString_appendn(str, largeNumberColor, buffer, len); } else if (number < 100000) {
return; //2 digit MB, 3 digit KB
} else if (number >= (1000 * ONE_DECIMAL_M)) { len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/1000);
len = snprintf(buffer, 10, "%4.1lfT ", (double)number / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len);
return;
}
#endif
if(number >= (100 * ONE_DECIMAL_M)) {
len = snprintf(buffer, 10, "%4luG ", number / ONE_M);
RichString_appendn(str, largeNumberColor, buffer, len);
return;
}
len = snprintf(buffer, 10, "%4.1lfG ", (double)number / ONE_M);
RichString_appendn(str, largeNumberColor, buffer, len);
return;
} else if (number >= 100000) {
len = snprintf(buffer, 10, "%4luM ", number / ONE_K);
RichString_appendn(str, processMegabytesColor, buffer, len);
return;
} else if (number >= 1000) {
len = snprintf(buffer, 10, "%2lu", number/1000);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendn(str, processMegabytesColor, buffer, len);
number %= 1000; number %= 1000;
len = snprintf(buffer, 10, "%03lu ", number); len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number);
RichString_appendn(str, processColor, buffer, len); RichString_appendn(str, processColor, buffer, len);
return; } else if (number < 1000 * ONE_K) {
//3 digit MB
number /= ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number);
RichString_appendn(str, processMegabytesColor, buffer, len);
} else if (number < 10000 * ONE_K) {
//1 digit GB, 3 digit MB
number /= ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
RichString_appendn(str, processGigabytesColor, buffer, len);
number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number);
RichString_appendn(str, processMegabytesColor, buffer, len);
} else if (number < 100000 * ONE_K) {
//2 digit GB, 1 digit MB
number /= 100 * ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/10);
RichString_appendn(str, processGigabytesColor, buffer, len);
number %= 10;
len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number);
RichString_appendn(str, processMegabytesColor, buffer, len);
RichString_append(str, processGigabytesColor, "G ");
} else if (number < 1000 * ONE_M) {
//3 digit GB
number /= ONE_M;
len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number);
RichString_appendn(str, processGigabytesColor, buffer, len);
} else if (number < 10000ULL * ONE_M) {
//1 digit TB, 3 digit GB
number /= ONE_M;
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
RichString_appendn(str, largeNumberColor, buffer, len);
number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number);
RichString_appendn(str, processGigabytesColor, buffer, len);
} else {
//2 digit TB and above
len = xSnprintf(buffer, sizeof(buffer), "%4.1lfT ", (double)number/ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len);
} }
len = snprintf(buffer, 10, "%5lu ", number);
RichString_appendn(str, processColor, buffer, len);
} }
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring) { void Process_colorNumber(RichString* str, unsigned long long number, bool coloring) {
char buffer[14]; char buffer[13];
int largeNumberColor = CRT_colors[LARGE_NUMBER]; int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
int processColor = CRT_colors[PROCESS]; int processColor = CRT_colors[PROCESS];
int processShadowColor = CRT_colors[PROCESS_SHADOW]; int processShadowColor = CRT_colors[PROCESS_SHADOW];
if (!coloring) { if (!coloring) {
largeNumberColor = CRT_colors[PROCESS]; largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS]; processMegabytesColor = CRT_colors[PROCESS];
processShadowColor = CRT_colors[PROCESS]; processShadowColor = CRT_colors[PROCESS];
} }
if ((long long) number == -1LL) { if (number == ULLONG_MAX) {
int len = snprintf(buffer, 13, " no perm "); int len = xSnprintf(buffer, sizeof(buffer), " N/A ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len); RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (number >= 100000LL * ONE_DECIMAL_T) { } else if (number >= 100000LL * ONE_DECIMAL_T) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_G); xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G);
RichString_appendn(str, largeNumberColor, buffer, 12); RichString_appendn(str, largeNumberColor, buffer, 12);
} else if (number >= 100LL * ONE_DECIMAL_T) { } else if (number >= 100LL * ONE_DECIMAL_T) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_M); xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M);
RichString_appendn(str, largeNumberColor, buffer, 8); RichString_appendn(str, largeNumberColor, buffer, 8);
RichString_appendn(str, processMegabytesColor, buffer+8, 4); RichString_appendn(str, processMegabytesColor, buffer+8, 4);
} else if (number >= 10LL * ONE_DECIMAL_G) { } else if (number >= 10LL * ONE_DECIMAL_G) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_K); xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K);
RichString_appendn(str, largeNumberColor, buffer, 5); RichString_appendn(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3); RichString_appendn(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4); RichString_appendn(str, processColor, buffer+8, 4);
} else { } else {
xSnprintf(buffer, 13, "%11llu ", number); xSnprintf(buffer, sizeof(buffer), "%11llu ", number);
RichString_appendn(str, largeNumberColor, buffer, 2); RichString_appendn(str, largeNumberColor, buffer, 2);
RichString_appendn(str, processMegabytesColor, buffer+2, 3); RichString_appendn(str, processMegabytesColor, buffer+2, 3);
RichString_appendn(str, processColor, buffer+5, 3); RichString_appendn(str, processColor, buffer+5, 3);
@ -147,25 +169,31 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths) {
int minutes = (totalSeconds / 60) % 60; int minutes = (totalSeconds / 60) % 60;
int seconds = totalSeconds % 60; int seconds = totalSeconds % 60;
int hundredths = totalHundredths - (totalSeconds * 100); int hundredths = totalHundredths - (totalSeconds * 100);
char buffer[11]; char buffer[10];
if (hours >= 100) { if (hours >= 100) {
xSnprintf(buffer, 10, "%7lluh ", hours); xSnprintf(buffer, sizeof(buffer), "%7lluh ", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer); RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
} else { } else {
if (hours) { if (hours) {
xSnprintf(buffer, 10, "%2lluh", hours); xSnprintf(buffer, sizeof(buffer), "%2lluh", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer); RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
xSnprintf(buffer, 10, "%02d:%02d ", minutes, seconds); xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds);
} else { } else {
xSnprintf(buffer, 10, "%2d:%02d.%02d ", minutes, seconds, hundredths); xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths);
} }
RichString_append(str, CRT_colors[DEFAULT_COLOR], buffer); RichString_append(str, CRT_colors[DEFAULT_COLOR], buffer);
} }
} }
static inline void Process_writeCommand(Process* this, int attr, int baseattr, RichString* str) { void Process_fillStarttimeBuffer(Process* this) {
struct tm date;
(void) localtime_r(&this->starttime_ctime, &date);
strftime(this->starttime_show, sizeof(this->starttime_show) - 1, (this->starttime_ctime > (time(NULL) - 86400)) ? "%R " : "%b%d ", &date);
}
static inline void Process_writeCommand(const Process* this, int attr, int baseattr, RichString* str) {
int start = RichString_size(str), finish = 0; int start = RichString_size(str), finish = 0;
char* comm = this->comm; const char* comm = this->comm;
if (this->settings->highlightBaseName || !this->settings->showProgramPath) { if (this->settings->highlightBaseName || !this->settings->showProgramPath) {
int i, basename = 0; int i, basename = 0;
@ -178,10 +206,11 @@ static inline void Process_writeCommand(Process* this, int attr, int baseattr, R
} }
} }
if (!finish) { if (!finish) {
if (this->settings->showProgramPath) if (this->settings->showProgramPath) {
start += basename; start += basename;
else } else {
comm += basename; comm += basename;
}
finish = this->basenameOffset - basename; finish = this->basenameOffset - basename;
} }
finish += start - 1; finish += start - 1;
@ -189,20 +218,23 @@ static inline void Process_writeCommand(Process* this, int attr, int baseattr, R
RichString_append(str, attr, comm); RichString_append(str, attr, comm);
if (this->settings->highlightBaseName) if (this->settings->highlightBaseName) {
RichString_setAttrn(str, baseattr, start, finish); RichString_setAttrn(str, baseattr, start, finish);
}
} }
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring) { void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring) {
int largeNumberColor = CRT_colors[LARGE_NUMBER]; int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
int processColor = CRT_colors[PROCESS]; int processColor = CRT_colors[PROCESS];
if (!coloring) { if (!coloring) {
largeNumberColor = CRT_colors[PROCESS]; largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS]; processMegabytesColor = CRT_colors[PROCESS];
} }
if (rate == -1) {
int len = snprintf(buffer, n, " no perm "); if (isnan(rate)) {
int len = xSnprintf(buffer, n, " N/A ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len); RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (rate < ONE_K) { } else if (rate < ONE_K) {
int len = snprintf(buffer, n, "%7.2f B/s ", rate); int len = snprintf(buffer, n, "%7.2f B/s ", rate);
@ -222,7 +254,7 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
} }
} }
void Process_writeField(Process* this, RichString* str, ProcessField field) { void Process_writeField(const Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
int baseattr = CRT_colors[PROCESS_BASENAME]; int baseattr = CRT_colors[PROCESS_BASENAME];
@ -230,13 +262,18 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
bool coloring = this->settings->highlightMegabytes; bool coloring = this->settings->highlightMegabytes;
switch (field) { switch (field) {
case PERCENT_CPU: { case PERCENT_CPU:
if (this->percent_cpu > 999.9) { case PERCENT_NORM_CPU: {
xSnprintf(buffer, n, "%4u ", (unsigned int)this->percent_cpu); float cpuPercentage = this->percent_cpu;
} else if (this->percent_cpu > 99.9) { if (field == PERCENT_NORM_CPU) {
xSnprintf(buffer, n, "%3u. ", (unsigned int)this->percent_cpu); cpuPercentage /= this->processList->cpuCount;
}
if (cpuPercentage > 999.9) {
xSnprintf(buffer, n, "%4u ", (unsigned int)cpuPercentage);
} else if (cpuPercentage > 99.9) {
xSnprintf(buffer, n, "%3u. ", (unsigned int)cpuPercentage);
} else { } else {
xSnprintf(buffer, n, "%4.1f ", this->percent_cpu); xSnprintf(buffer, n, "%4.1f ", cpuPercentage);
} }
break; break;
} }
@ -262,15 +299,19 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
bool lastItem = (this->indent < 0); bool lastItem = (this->indent < 0);
int indent = (this->indent < 0 ? -this->indent : this->indent); int indent = (this->indent < 0 ? -this->indent : this->indent);
for (int i = 0; i < 32; i++) for (int i = 0; i < 32; i++) {
if (indent & (1U << i)) if (indent & (1U << i)) {
maxIndent = i+1; maxIndent = i+1;
for (int i = 0; i < maxIndent - 1; i++) { }
}
for (int i = 0; i < maxIndent - 1; i++) {
int written, ret; int written, ret;
if (indent & (1 << i)) if (indent & (1 << i)) {
ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]); ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
else } else {
ret = snprintf(buf, n, " "); ret = snprintf(buf, n, " ");
}
if (ret < 0 || ret >= n) { if (ret < 0 || ret >= n) {
written = n; written = n;
} else { } else {
@ -288,8 +329,8 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
} }
case MAJFLT: Process_colorNumber(str, this->majflt, coloring); return; case MAJFLT: Process_colorNumber(str, this->majflt, coloring); return;
case MINFLT: Process_colorNumber(str, this->minflt, coloring); return; case MINFLT: Process_colorNumber(str, this->minflt, coloring); return;
case M_RESIDENT: Process_humanNumber(str, this->m_resident * PAGE_SIZE_KB, coloring); return; case M_RESIDENT: Process_humanNumber(str, this->m_resident * CRT_pageSizeKB, coloring); return;
case M_SIZE: Process_humanNumber(str, this->m_size * PAGE_SIZE_KB, coloring); return; case M_VIRT: Process_humanNumber(str, this->m_virt * CRT_pageSizeKB, coloring); return;
case NICE: { case NICE: {
xSnprintf(buffer, n, "%3ld ", this->nice); xSnprintf(buffer, n, "%3ld ", this->nice);
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY] attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
@ -329,7 +370,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break; case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break;
case TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break; case TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break;
case USER: { case USER: {
if (Process_getuid != (int) this->st_uid) if (Process_getuid != this->st_uid)
attr = CRT_colors[PROCESS_SHADOW]; attr = CRT_colors[PROCESS_SHADOW];
if (this->user) { if (this->user) {
xSnprintf(buffer, n, "%-9s ", this->user); xSnprintf(buffer, n, "%-9s ", this->user);
@ -348,16 +389,29 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
RichString_append(str, attr, buffer); RichString_append(str, attr, buffer);
} }
void Process_display(Object* cast, RichString* out) { void Process_display(const Object* cast, RichString* out) {
Process* this = (Process*) cast; const Process* this = (const Process*) cast;
ProcessField* fields = this->settings->fields; const ProcessField* fields = this->settings->fields;
RichString_prune(out); RichString_prune(out);
for (int i = 0; fields[i]; i++) for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]); As_Process(this)->writeField(this, out, fields[i]);
if (this->settings->shadowOtherUsers && (int)this->st_uid != Process_getuid)
if (this->settings->shadowOtherUsers && this->st_uid != Process_getuid) {
RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]); RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]);
if (this->tag == true) }
if (this->tag == true) {
RichString_setAttr(out, CRT_colors[PROCESS_TAG]); RichString_setAttr(out, CRT_colors[PROCESS_TAG]);
}
if (this->settings->highlightChanges) {
if (Process_isTomb(this)) {
out->highlightAttr = CRT_colors[PROCESS_TOMB];
} else if (Process_isNew(this)) {
out->highlightAttr = CRT_colors[PROCESS_NEW];
}
}
assert(out->chlen > 0); assert(out->chlen > 0);
} }
@ -366,7 +420,11 @@ void Process_done(Process* this) {
free(this->comm); free(this->comm);
} }
ProcessClass Process_class = { static const char* Process_getCommandStr(const Process* p) {
return p->comm ? p->comm : "";
}
const ProcessClass Process_class = {
.super = { .super = {
.extends = Class(Object), .extends = Class(Object),
.display = Process_display, .display = Process_display,
@ -374,22 +432,38 @@ ProcessClass Process_class = {
.compare = Process_compare .compare = Process_compare
}, },
.writeField = Process_writeField, .writeField = Process_writeField,
.getCommandStr = Process_getCommandStr,
}; };
void Process_init(Process* this, struct Settings_* settings) { void Process_init(Process* this, const struct Settings_* settings) {
this->settings = settings; this->settings = settings;
this->tag = false; this->tag = false;
this->showChildren = true; this->showChildren = true;
this->show = true; this->show = true;
this->updated = false; this->updated = false;
this->basenameOffset = -1; this->basenameOffset = -1;
if (Process_getuid == -1) Process_getuid = getuid();
if (Process_getuid == (uid_t)-1) {
Process_getuid = getuid();
}
} }
void Process_toggleTag(Process* this) { void Process_toggleTag(Process* this) {
this->tag = this->tag == true ? false : true; this->tag = this->tag == true ? false : true;
} }
bool Process_isNew(const Process* this) {
assert(this->processList);
if (this->processList->scanTs >= this->seenTs) {
return this->processList->scanTs - this->seenTs <= this->processList->settings->highlightDelaySecs;
}
return false;
}
bool Process_isTomb(const Process* this) {
return this->tombTs > 0;
}
bool Process_setPriority(Process* this, int priority) { bool Process_setPriority(Process* this, int priority) {
CRT_dropPrivileges(); CRT_dropPrivileges();
int old_prio = getpriority(PRIO_PROCESS, this->pid); int old_prio = getpriority(PRIO_PROCESS, this->pid);
@ -413,73 +487,74 @@ bool Process_sendSignal(Process* this, Arg sgn) {
} }
long Process_pidCompare(const void* v1, const void* v2) { long Process_pidCompare(const void* v1, const void* v2) {
Process* p1 = (Process*)v1; const Process* p1 = (const Process*)v1;
Process* p2 = (Process*)v2; const Process* p2 = (const Process*)v2;
return (p1->pid - p2->pid); return (p1->pid - p2->pid);
} }
long Process_compare(const void* v1, const void* v2) { long Process_compare(const void* v1, const void* v2) {
Process *p1, *p2; const Process *p1, *p2;
Settings *settings = ((Process*)v1)->settings; const Settings *settings = ((const Process*)v1)->settings;
int r;
if (settings->direction == 1) { if (settings->direction == 1) {
p1 = (Process*)v1; p1 = (const Process*)v1;
p2 = (Process*)v2; p2 = (const Process*)v2;
} else { } else {
p2 = (Process*)v1; p2 = (const Process*)v1;
p1 = (Process*)v2; p1 = (const Process*)v2;
} }
switch (settings->sortKey) { switch (settings->sortKey) {
case PERCENT_CPU: case PERCENT_CPU:
return (p2->percent_cpu > p1->percent_cpu ? 1 : -1); case PERCENT_NORM_CPU:
return SPACESHIP_NUMBER(p2->percent_cpu, p1->percent_cpu);
case PERCENT_MEM: case PERCENT_MEM:
return (p2->m_resident - p1->m_resident); return SPACESHIP_NUMBER(p2->m_resident, p1->m_resident);
case COMM: case COMM:
return strcmp(p1->comm, p2->comm); return SPACESHIP_NULLSTR(Process_getCommand(p1), Process_getCommand(p2));
case MAJFLT: case MAJFLT:
return (p2->majflt - p1->majflt); return SPACESHIP_NUMBER(p2->majflt, p1->majflt);
case MINFLT: case MINFLT:
return (p2->minflt - p1->minflt); return SPACESHIP_NUMBER(p2->minflt, p1->minflt);
case M_RESIDENT: case M_RESIDENT:
return (p2->m_resident - p1->m_resident); return SPACESHIP_NUMBER(p2->m_resident, p1->m_resident);
case M_SIZE: case M_VIRT:
return (p2->m_size - p1->m_size); return SPACESHIP_NUMBER(p2->m_virt, p1->m_virt);
case NICE: case NICE:
return (p1->nice - p2->nice); return SPACESHIP_NUMBER(p1->nice, p2->nice);
case NLWP: case NLWP:
return (p1->nlwp - p2->nlwp); return SPACESHIP_NUMBER(p1->nlwp, p2->nlwp);
case PGRP: case PGRP:
return (p1->pgrp - p2->pgrp); return SPACESHIP_NUMBER(p1->pgrp, p2->pgrp);
case PID: case PID:
return (p1->pid - p2->pid); return SPACESHIP_NUMBER(p1->pid, p2->pid);
case PPID: case PPID:
return (p1->ppid - p2->ppid); return SPACESHIP_NUMBER(p1->ppid, p2->ppid);
case PRIORITY: case PRIORITY:
return (p1->priority - p2->priority); return SPACESHIP_NUMBER(p1->priority, p2->priority);
case PROCESSOR: case PROCESSOR:
return (p1->processor - p2->processor); return SPACESHIP_NUMBER(p1->processor, p2->processor);
case SESSION: case SESSION:
return (p1->session - p2->session); return SPACESHIP_NUMBER(p1->session, p2->session);
case STARTTIME: { case STARTTIME:
if (p1->starttime_ctime == p2->starttime_ctime) r = SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
return (p1->pid - p2->pid); return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid);
else
return (p1->starttime_ctime - p2->starttime_ctime);
}
case STATE: case STATE:
return (Process_sortState(p1->state) - Process_sortState(p2->state)); return SPACESHIP_NUMBER(Process_sortState(p1->state), Process_sortState(p2->state));
case ST_UID: case ST_UID:
return (p1->st_uid - p2->st_uid); return SPACESHIP_NUMBER(p1->st_uid, p2->st_uid);
case TIME: case TIME:
return ((p2->time) - (p1->time)); return SPACESHIP_NUMBER(p2->time, p1->time);
case TGID: case TGID:
return (p1->tgid - p2->tgid); return SPACESHIP_NUMBER(p1->tgid, p2->tgid);
case TPGID: case TPGID:
return (p1->tpgid - p2->tpgid); return SPACESHIP_NUMBER(p1->tpgid, p2->tpgid);
case TTY_NR: case TTY_NR:
return (p1->tty_nr - p2->tty_nr); return SPACESHIP_NUMBER(p1->tty_nr, p2->tty_nr);
case USER: case USER:
return strcmp(p1->user ? p1->user : "", p2->user ? p2->user : ""); return SPACESHIP_NULLSTR(p1->user, p2->user);
default: default:
return (p1->pid - p2->pid); return SPACESHIP_NUMBER(p1->pid, p2->pid);
} }
} }

116
Process.h
View File

@ -4,27 +4,25 @@
htop - Process.h htop - Process.h
(C) 2004-2015 Hisham H. Muhammad (C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved. (C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include "Object.h"
#include "RichString.h"
#ifdef __ANDROID__ #ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get #define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set #define SYS_ioprio_set __NR_ioprio_set
#endif #endif
// On Linux, this works only with glibc 2.1+. On earlier versions
// the behavior is similar to have a hardcoded page size.
#ifndef PAGE_SIZE
#define PAGE_SIZE ( sysconf(_SC_PAGESIZE) )
#endif
#define PAGE_SIZE_KB ( PAGE_SIZE / ONE_K )
#include "Object.h"
#include <sys/types.h>
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x0001
#define DEFAULT_HIGHLIGHT_SECS 5
typedef enum ProcessFields { typedef enum ProcessFields {
NULL_PROCESSFIELD = 0, NULL_PROCESSFIELD = 0,
@ -42,7 +40,7 @@ typedef enum ProcessFields {
NICE = 19, NICE = 19,
STARTTIME = 21, STARTTIME = 21,
PROCESSOR = 38, PROCESSOR = 38,
M_SIZE = 39, M_VIRT = 39,
M_RESIDENT = 40, M_RESIDENT = 40,
ST_UID = 46, ST_UID = 46,
PERCENT_CPU = 47, PERCENT_CPU = 47,
@ -51,6 +49,7 @@ typedef enum ProcessFields {
TIME = 50, TIME = 50,
NLWP = 51, NLWP = 51,
TGID = 52, TGID = 52,
PERCENT_NORM_CPU = 53,
} ProcessField; } ProcessField;
typedef struct ProcessPidColumn_ { typedef struct ProcessPidColumn_ {
@ -58,16 +57,19 @@ typedef struct ProcessPidColumn_ {
const char* label; const char* label;
} ProcessPidColumn; } ProcessPidColumn;
struct Settings_;
typedef struct Process_ { typedef struct Process_ {
Object super; Object super;
struct Settings_* settings; const struct ProcessList_* processList;
const struct Settings_* settings;
unsigned long long int time; unsigned long long int time;
pid_t pid; pid_t pid;
pid_t ppid; pid_t ppid;
pid_t tgid; pid_t tgid;
char* comm; char* comm; /* use Process_getCommand() for Command actually displayed */
int commLen; int commLen;
int indent; int indent;
@ -78,6 +80,7 @@ typedef struct Process_ {
bool tag; bool tag;
bool showChildren; bool showChildren;
bool show; bool show;
bool wasShown;
unsigned int pgrp; unsigned int pgrp;
unsigned int session; unsigned int session;
unsigned int tty_nr; unsigned int tty_nr;
@ -88,7 +91,7 @@ typedef struct Process_ {
float percent_cpu; float percent_cpu;
float percent_mem; float percent_mem;
char* user; const char* user;
long int priority; long int priority;
long int nice; long int nice;
@ -96,101 +99,100 @@ typedef struct Process_ {
char starttime_show[8]; char starttime_show[8];
time_t starttime_ctime; time_t starttime_ctime;
long m_size; long m_virt;
long m_resident; long m_resident;
int exit_signal; int exit_signal;
time_t seenTs;
time_t tombTs;
unsigned long int minflt; unsigned long int minflt;
unsigned long int majflt; unsigned long int majflt;
#ifdef DEBUG
long int itrealvalue;
unsigned long int vsize;
long int rss;
unsigned long int rlim;
unsigned long int startcode;
unsigned long int endcode;
unsigned long int startstack;
unsigned long int kstkesp;
unsigned long int kstkeip;
unsigned long int signal;
unsigned long int blocked;
unsigned long int sigignore;
unsigned long int sigcatch;
unsigned long int wchan;
unsigned long int nswap;
unsigned long int cnswap;
#endif
unsigned int tree_left;
unsigned int tree_right;
unsigned int tree_depth;
unsigned int tree_index;
} Process; } Process;
typedef struct ProcessFieldData_ { typedef struct ProcessFieldData_ {
const char* name; const char* name;
const char* title; const char* title;
const char* description; const char* description;
int flags; uint32_t flags;
} ProcessFieldData; } ProcessFieldData;
// Implemented in platform-specific code: // Implemented in platform-specific code:
void Process_writeField(Process* this, RichString* str, ProcessField field); void Process_writeField(const Process* this, RichString* str, ProcessField field);
long Process_compare(const void* v1, const void* v2); long Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast); void Process_delete(Object* cast);
bool Process_isThread(Process* this); bool Process_isThread(const Process* this);
extern ProcessFieldData Process_fields[]; extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[]; extern ProcessPidColumn Process_pidColumns[];
extern char Process_pidFormat[20]; extern char Process_pidFormat[20];
typedef Process*(*Process_New)(struct Settings_*); typedef Process*(*Process_New)(const struct Settings_*);
typedef void (*Process_WriteField)(Process*, RichString*, ProcessField); typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
typedef const char* (*Process_GetCommandStr)(const Process*);
typedef struct ProcessClass_ { typedef struct ProcessClass_ {
const ObjectClass super; const ObjectClass super;
const Process_WriteField writeField; const Process_WriteField writeField;
const Process_GetCommandStr getCommandStr;
} ProcessClass; } ProcessClass;
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass)) #define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid) #define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_)) static inline pid_t Process_getParentPid(const Process* this) {
return this->tgid == this->pid ? this->ppid : this->tgid;
}
static inline bool Process_isChildOf(const Process* this, pid_t pid) {
return pid == Process_getParentPid(this);
}
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state)) #define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
#define ONE_K 1024L #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 ((long long)ONE_G * ONE_K) #define ONE_T (1ULL * ONE_G * ONE_K)
#define ONE_DECIMAL_K 1000L #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 ((long long)ONE_DECIMAL_G * ONE_DECIMAL_K) #define ONE_DECIMAL_T (1ULL * ONE_DECIMAL_G * ONE_DECIMAL_K)
extern char Process_pidFormat[20]; void Process_setupColumnWidths(void);
void Process_setupColumnWidths(); void Process_humanNumber(RichString* str, unsigned long long number, bool coloring);
void Process_humanNumber(RichString* str, unsigned long number, bool coloring);
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring); void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
void Process_printTime(RichString* str, unsigned long long totalHundredths); void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_fillStarttimeBuffer(Process* this);
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring); void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
void Process_writeField(Process* this, RichString* str, ProcessField field); void Process_display(const Object* cast, RichString* out);
void Process_display(Object* cast, RichString* out);
void Process_done(Process* this); void Process_done(Process* this);
extern ProcessClass Process_class; extern const ProcessClass Process_class;
void Process_init(Process* this, struct Settings_* settings); void Process_init(Process* this, const struct Settings_* settings);
void Process_toggleTag(Process* this); void Process_toggleTag(Process* this);
bool Process_isNew(const Process* this);
bool Process_isTomb(const Process* this);
bool Process_setPriority(Process* this, int priority); bool Process_setPriority(Process* this, int priority);
bool Process_changePriorityBy(Process* this, Arg delta); bool Process_changePriorityBy(Process* this, Arg delta);
@ -199,6 +201,4 @@ bool Process_sendSignal(Process* this, Arg sgn);
long Process_pidCompare(const void* v1, const void* v2); long Process_pidCompare(const void* v1, const void* v2);
long Process_compare(const void* v1, const void* v2);
#endif #endif

View File

@ -1,33 +1,41 @@
/* /*
htop - ProcessList.c htop - ProcessList.c
(C) 2004,2005 Hisham H. Muhammad (C) 2004,2005 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "ProcessList.h" #include "ProcessList.h"
#include "Platform.h"
#include "CRT.h"
#include "StringUtils.h"
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include "CRT.h"
#include "Hashtable.h"
#include "Vector.h"
#include "XUtils.h"
ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
this->processes = Vector_new(klass, true, DEFAULT_SIZE); this->processes = Vector_new(klass, true, DEFAULT_SIZE);
this->processTable = Hashtable_new(140, false); this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); // tree-view auxiliary buffer
this->processTable = Hashtable_new(200, false);
this->displayTreeSet = Hashtable_new(200, false);
this->draftingTreeSet = Hashtable_new(200, false);
this->usersTable = usersTable; this->usersTable = usersTable;
this->pidMatchList = pidMatchList; this->pidMatchList = pidMatchList;
this->userId = userId;
// tree-view auxiliary buffer this->userId = userId;
this->processes2 = Vector_new(klass, true, DEFAULT_SIZE);
// set later by platform-specific code // set later by platform-specific code
this->cpuCount = 0; this->cpuCount = 0;
this->scanTs = 0;
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
this->topologyOk = false; this->topologyOk = false;
if (hwloc_topology_init(&this->topology) == 0) { if (hwloc_topology_init(&this->topology) == 0) {
@ -57,9 +65,13 @@ void ProcessList_done(ProcessList* this) {
hwloc_topology_destroy(this->topology); hwloc_topology_destroy(this->topology);
} }
#endif #endif
Hashtable_delete(this->draftingTreeSet);
Hashtable_delete(this->displayTreeSet);
Hashtable_delete(this->processTable); Hashtable_delete(this->processTable);
Vector_delete(this->processes);
Vector_delete(this->processes2); Vector_delete(this->processes2);
Vector_delete(this->processes);
} }
void ProcessList_setPanel(ProcessList* this, Panel* panel) { void ProcessList_setPanel(ProcessList* this, Panel* panel) {
@ -68,20 +80,31 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
void ProcessList_printHeader(ProcessList* this, RichString* header) { void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header); RichString_prune(header);
ProcessField* fields = this->settings->fields;
const ProcessField* fields = this->settings->fields;
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title; const char* field = Process_fields[fields[i]].title;
if (!field) field = "- "; if (!field) {
if (!this->settings->treeView && this->settings->sortKey == fields[i]) field = "- ";
RichString_append(header, CRT_colors[PANEL_SELECTION_FOCUS], field); }
else
RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field); int color = (this->settings->sortKey == fields[i]) ?
CRT_colors[PANEL_SELECTION_FOCUS] : CRT_colors[PANEL_HEADER_FOCUS];
RichString_append(header, color, field);
if (COMM == fields[i] && this->settings->showMergedCommand) {
RichString_append(header, color, "(merged)");
}
} }
} }
void ProcessList_add(ProcessList* this, Process* p) { void ProcessList_add(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1); assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
assert(Hashtable_get(this->processTable, p->pid) == NULL); assert(Hashtable_get(this->processTable, p->pid) == NULL);
p->processList = this;
// highlighting processes found in first scan by first scan marked "far in the past"
p->seenTs = this->scanTs;
Vector_add(this->processes, p); Vector_add(this->processes, p);
Hashtable_put(this->processTable, p->pid, p); Hashtable_put(this->processTable, p->pid, p);
@ -94,132 +117,339 @@ void ProcessList_add(ProcessList* this, Process* p) {
void ProcessList_remove(ProcessList* this, Process* p) { void ProcessList_remove(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1); assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
assert(Hashtable_get(this->processTable, p->pid) != NULL); assert(Hashtable_get(this->processTable, p->pid) != NULL);
Process* pp = Hashtable_remove(this->processTable, p->pid); Process* pp = Hashtable_remove(this->processTable, p->pid);
assert(pp == p); (void)pp; assert(pp == p); (void)pp;
unsigned int pid = p->pid; unsigned int pid = p->pid;
int idx = Vector_indexOf(this->processes, p, Process_pidCompare); int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
assert(idx != -1); assert(idx != -1);
if (idx >= 0) Vector_remove(this->processes, idx);
if (idx >= 0) {
Vector_remove(this->processes, idx);
}
assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid; assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
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) { Process* ProcessList_get(ProcessList* this, int idx) {
return (Process*) (Vector_get(this->processes, idx)); return (Process*)Vector_get(this->processes, idx);
} }
int ProcessList_size(ProcessList* this) { int ProcessList_size(ProcessList* this) {
return (Vector_size(this->processes)); return Vector_size(this->processes);
} }
static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show) { // ProcessList_updateTreeSetLayer sorts this->displayTreeSet,
// relying only on itself.
//
// Algorithm
//
// The algorithm is based on `depth-first search`,
// even though `breadth-first search` approach may be more efficient on first glance,
// after comparision it may be not, as it's not safe to go deeper without first updating the tree structure.
// If it would be safe that approach would likely bring an advantage in performance.
//
// Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
// First it sorts a list. Then it runs the function recursively for each element of the sorted list.
// After that it updates the settings of processes.
//
// It relies on `leftBound` and `rightBound` as an optimization to cut the list size at the time it builds a 'layer'.
//
// It uses a temporary Hashtable `draftingTreeSet` because it's not safe to traverse a tree
// and at the same time make changes in it.
//
static void ProcessList_updateTreeSetLayer(ProcessList* this, unsigned int leftBound, unsigned int rightBound, unsigned int deep, unsigned int left, unsigned int right, unsigned int* index, unsigned int* treeIndex, int indent) {
// It's guaranteed that layer_size is enough space
// but most likely it needs less. Specifically on first iteration.
int layerSize = (right - left) / 2;
// check if we reach `children` of `leaves`
if (layerSize == 0)
return;
Vector* layer = Vector_new(this->processes->type, false, layerSize);
// Find all processes on the same layer (process with the same `deep` value
// and included in a range from `leftBound` to `rightBound`).
//
// This loop also keeps track of left_bound and right_bound of these processes
// in order not to lose this information once the list is sorted.
//
// The variables left_bound and right_bound are different from what the values lhs and rhs represent.
// While left_bound and right_bound define a range of processes to look at, the values given by lhs and rhs are indices into an array
//
// In the below example note how filtering a range of indices i is different from filtering for processes in the bounds left_bound < x < right_bound …
//
// The nested tree set is sorted by left value, which is guaranteed upon entry/exit of this function.
//
// i | l | r
// 1 | 1 | 9
// 2 | 2 | 8
// 3 | 4 | 5
// 4 | 6 | 7
for (unsigned int i = leftBound; i < rightBound; i++) {
Process* proc = (Process*)Hashtable_get(this->displayTreeSet, i);
assert(proc);
if (proc && proc->tree_depth == deep && proc->tree_left > left && proc->tree_right < right) {
if (Vector_size(layer) > 0) {
Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
// Make a 'right_bound' of previous_process in a layer the current process's index.
//
// Use 'tree_depth' as a temporal variable.
// It's safe to do as later 'tree_depth' will be renovated.
previous_process->tree_depth = proc->tree_index;
}
Vector_add(layer, proc);
}
}
// The loop above changes just up to process-1.
// So the last process of the layer isn't updated by the above code.
//
// Thus, if present, set the `rightBound` to the last process on the layer
if (Vector_size(layer) > 0) {
Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
previous_process->tree_depth = rightBound;
}
Vector_quickSort(layer);
int size = Vector_size(layer);
for (int i = 0; i < size; i++) {
Process* proc = (Process*)Vector_get(layer, i);
unsigned int idx = (*index)++;
int newLeft = (*treeIndex)++;
int level = deep == 0 ? 0 : (int)deep - 1;
int currentIndent = indent == -1 ? 0 : indent | (1 << level);
int nextIndent = indent == -1 ? 0 : ((i < size - 1) ? currentIndent : indent);
unsigned int newLeftBound = proc->tree_index;
unsigned int newRightBound = proc->tree_depth;
ProcessList_updateTreeSetLayer(this, newLeftBound, newRightBound, deep + 1, proc->tree_left, proc->tree_right, index, treeIndex, nextIndent);
int newRight = (*treeIndex)++;
proc->tree_left = newLeft;
proc->tree_right = newRight;
proc->tree_index = idx;
proc->tree_depth = deep;
if (indent == -1) {
proc->indent = 0;
} else if (i == size - 1) {
proc->indent = -currentIndent;
} else {
proc->indent = currentIndent;
}
Hashtable_put(this->draftingTreeSet, proc->tree_index, proc);
// It's not strictly necessary to do this, but doing so anyways
// allows for checking the correctness of the inner workings.
Hashtable_remove(this->displayTreeSet, newLeftBound);
}
Vector_delete(layer);
}
static void ProcessList_updateTreeSet(ProcessList* this) {
unsigned int index = 0;
unsigned int tree_index = 1;
const int vsize = Vector_size(this->processes);
assert(Hashtable_count(this->draftingTreeSet) == 0);
assert((int)Hashtable_count(this->displayTreeSet) == vsize);
ProcessList_updateTreeSetLayer(this, 0, vsize, 0, 0, vsize * 2 + 1, &index, &tree_index, -1);
Hashtable* tmp = this->draftingTreeSet;
this->draftingTreeSet = this->displayTreeSet;
this->displayTreeSet = tmp;
assert(Hashtable_count(this->draftingTreeSet) == 0);
assert((int)Hashtable_count(this->displayTreeSet) == vsize);
}
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show, int* node_counter, int* node_index) {
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--) {
Process* process = (Process*) (Vector_get(this->processes, i)); Process* process = (Process*)Vector_get(this->processes, i);
if (process->show && Process_isChildOf(process, pid)) { if (process->show && Process_isChildOf(process, pid)) {
process = (Process*) (Vector_take(this->processes, i)); process = (Process*)Vector_take(this->processes, i);
Vector_add(children, process); Vector_add(children, process);
} }
} }
int size = Vector_size(children); int size = Vector_size(children);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
Process* process = (Process*) (Vector_get(children, i)); int index = (*node_index)++;
if (!show) Process* process = (Process*)Vector_get(children, i);
int lft = (*node_counter)++;
if (!show) {
process->show = false; process->show = false;
int s = this->processes2->items; }
if (direction == 1)
int s = Vector_size(this->processes2);
if (direction == 1) {
Vector_add(this->processes2, process); Vector_add(this->processes2, process);
else } else {
Vector_insert(this->processes2, 0, process); Vector_insert(this->processes2, 0, process);
assert(this->processes2->items == s+1); (void)s; }
assert(Vector_size(this->processes2) == s + 1); (void)s;
int nextIndent = indent | (1 << level); int nextIndent = indent | (1 << level);
ProcessList_buildTree(this, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false); ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false, node_counter, node_index);
if (i == size - 1) if (i == size - 1) {
process->indent = -nextIndent; process->indent = -nextIndent;
else } else {
process->indent = nextIndent; process->indent = nextIndent;
}
int rht = (*node_counter)++;
process->tree_left = lft;
process->tree_right = rht;
process->tree_depth = level + 1;
process->tree_index = index;
Hashtable_put(this->displayTreeSet, index, process);
} }
Vector_delete(children); Vector_delete(children);
} }
void ProcessList_sort(ProcessList* this) { static long ProcessList_treeProcessCompare(const void* v1, const void* v2) {
if (!this->settings->treeView) { const Process *p1 = (const Process*)v1;
Vector_insertionSort(this->processes); const Process *p2 = (const Process*)v2;
} else {
// Save settings return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
int direction = this->settings->direction; }
int sortKey = this->settings->sortKey;
// Sort by PID static long ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
this->settings->sortKey = PID; const Process *p1 = (const Process*)v1;
this->settings->direction = 1; const Process *p2 = (const Process*)v2;
Vector_quickSort(this->processes);
// Restore settings return SPACESHIP_NUMBER(p1->pid, p2->pid);
this->settings->sortKey = sortKey; }
this->settings->direction = direction;
int vsize = Vector_size(this->processes); // Builds a sorted tree from scratch, without relying on previously gathered information
// Find all processes whose parent is not visible static void ProcessList_buildTree(ProcessList* this) {
int size; int node_counter = 1;
while ((size = Vector_size(this->processes))) { int node_index = 0;
int i; int direction = this->settings->direction;
for (i = 0; i < size; i++) {
Process* process = (Process*)(Vector_get(this->processes, i)); // Sort by PID
// Immediately consume not shown processes Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
if (!process->show) { int vsize = Vector_size(this->processes);
process = (Process*)(Vector_take(this->processes, i));
process->indent = 0; // Find all processes whose parent is not visible
Vector_add(this->processes2, process); int size;
ProcessList_buildTree(this, process->pid, 0, 0, direction, false); while ((size = Vector_size(this->processes))) {
break; int i;
} for (i = 0; i < size; i++) {
pid_t ppid = Process_getParentPid(process); Process* process = (Process*)Vector_get(this->processes, i);
// Bisect the process vector to find parent
int l = 0, r = size; // Immediately consume processes hidden from view
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0) if (!process->show) {
// on Mac OS X 10.11.6) cancel bisecting and regard this process as process = (Process*)Vector_take(this->processes, i);
// root. process->indent = 0;
if (process->pid == ppid) process->tree_depth = 0;
r = 0; process->tree_left = node_counter++;
while (l < r) { process->tree_index = node_index++;
int c = (l + r) / 2; Vector_add(this->processes2, process);
pid_t pid = ((Process*)(Vector_get(this->processes, c)))->pid; ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, false, &node_counter, &node_index);
if (ppid == pid) { process->tree_right = node_counter++;
break; Hashtable_put(this->displayTreeSet, process->tree_index, process);
} else if (ppid < pid) { break;
r = c; }
} else {
l = c + 1; pid_t ppid = Process_getParentPid(process);
}
} // Bisect the process vector to find parent
// If parent not found, then construct the tree with this root int l = 0;
if (l >= r) { int r = size;
process = (Process*)(Vector_take(this->processes, i));
process->indent = 0; // If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
Vector_add(this->processes2, process); // on Mac OS X 10.11.6) cancel bisecting and regard this process as
ProcessList_buildTree(this, process->pid, 0, 0, direction, process->showChildren); // root.
if (process->pid == ppid)
r = 0;
// On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2)
// use a ppid of 0. As that PID can't exist, we can skip searching for it.
if (!ppid)
r = 0;
while (l < r) {
int c = (l + r) / 2;
pid_t pid = ((Process*)Vector_get(this->processes, c))->pid;
if (ppid == pid) {
break; break;
} else if (ppid < pid) {
r = c;
} else {
l = c + 1;
} }
} }
// There should be no loop in the process tree
assert(i < size); // If parent not found, then construct the tree with this node as root
if (l >= r) {
process = (Process*)Vector_take(this->processes, i);
process->indent = 0;
process->tree_depth = 0;
process->tree_left = node_counter++;
process->tree_index = node_index++;
Vector_add(this->processes2, process);
Hashtable_put(this->displayTreeSet, process->tree_index, process);
ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, process->showChildren, &node_counter, &node_index);
process->tree_right = node_counter++;
break;
}
} }
assert(Vector_size(this->processes2) == vsize); (void)vsize;
assert(Vector_size(this->processes) == 0); // There should be no loop in the process tree
// Swap listings around assert(i < size);
Vector* t = this->processes; }
this->processes = this->processes2;
this->processes2 = t; // Swap listings around
Vector* t = this->processes;
this->processes = this->processes2;
this->processes2 = t;
// Check consistency of the built structures
assert(Vector_size(this->processes) == vsize); (void)vsize;
assert(Vector_size(this->processes2) == 0);
}
void ProcessList_sort(ProcessList* this) {
if (this->settings->treeView) {
ProcessList_updateTreeSet(this);
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompare);
} else {
Vector_insertionSort(this->processes);
} }
} }
ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
ProcessField ProcessList_keyAt(ProcessList* this, int at) {
int x = 0; int x = 0;
ProcessField* fields = this->settings->fields; const ProcessField* fields = this->settings->fields;
ProcessField field; ProcessField field;
for (int i = 0; (field = fields[i]); i++) { for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title; const char* title = Process_fields[field].title;
if (!title) title = "- "; if (!title) {
title = "- ";
}
int len = strlen(title); int len = strlen(title);
if (at >= x && at <= x + len) { if (at >= x && at <= x + len) {
return field; return field;
@ -253,7 +483,7 @@ void ProcessList_rebuildPanel(ProcessList* this) {
if ( (!p->show) if ( (!p->show)
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId)) || (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|| (incFilter && !(String_contains_i(p->comm, incFilter))) || (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) ) || (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
hidden = true; hidden = true;
@ -282,12 +512,20 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting,
return proc; return proc;
} }
void ProcessList_scan(ProcessList* this) { void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
struct timespec now;
// in pause mode only gather global data for meters (CPU/memory/...)
if (pauseProcessUpdate) {
ProcessList_goThroughEntries(this, true);
return;
}
// mark all process as "dirty" // mark all process as "dirty"
for (int i = 0; i < Vector_size(this->processes); i++) { for (int i = 0; i < Vector_size(this->processes); i++) {
Process* p = (Process*) Vector_get(this->processes, i); Process* p = (Process*) Vector_get(this->processes, i);
p->updated = false; p->updated = false;
p->wasShown = p->show;
p->show = true; p->show = true;
} }
@ -296,13 +534,46 @@ void ProcessList_scan(ProcessList* this) {
this->kernelThreads = 0; this->kernelThreads = 0;
this->runningTasks = 0; this->runningTasks = 0;
ProcessList_goThroughEntries(this);
// set scanTs
static bool firstScanDone = false;
if (!firstScanDone) {
this->scanTs = 0;
firstScanDone = true;
} else if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
this->scanTs = now.tv_sec;
}
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->updated == false) if (p->tombTs > 0) {
ProcessList_remove(this, p); // remove tombed process
else if (this->scanTs >= p->tombTs) {
ProcessList_remove(this, p);
}
} else if (p->updated == false) {
// process no longer exists
if (this->settings->highlightChanges && p->wasShown) {
// mark tombed
p->tombTs = this->scanTs + this->settings->highlightDelaySecs;
} else {
// immediately remove
ProcessList_remove(this, p);
}
} else {
p->updated = false; p->updated = false;
}
}
if (this->settings->treeView) {
// Clear out the hashtable to avoid any left-over processes from previous build
//
// The sorting algorithm relies on the fact that
// len(this->displayTreeSet) == len(this->processes)
Hashtable_clear(this->displayTreeSet);
ProcessList_buildTree(this);
} }
} }

View File

@ -3,21 +3,29 @@
/* /*
htop - ProcessList.h htop - ProcessList.h
(C) 2004,2005 Hisham H. Muhammad (C) 2004,2005 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Vector.h" #include "config.h" // IWYU pragma: keep
#include <stdbool.h>
#include <sys/types.h>
#include "Hashtable.h" #include "Hashtable.h"
#include "UsersTable.h" #include "Object.h"
#include "Panel.h" #include "Panel.h"
#include "Process.h" #include "Process.h"
#include "RichString.h"
#include "Settings.h" #include "Settings.h"
#include "UsersTable.h"
#include "Vector.h"
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
#include <hwloc.h> #include <hwloc.h>
#endif #endif
#ifndef MAX_NAME #ifndef MAX_NAME
#define MAX_NAME 128 #define MAX_NAME 128
#endif #endif
@ -27,13 +35,16 @@ in the source distribution for its full text.
#endif #endif
typedef struct ProcessList_ { typedef struct ProcessList_ {
Settings* settings; const Settings* settings;
Vector* processes; Vector* processes;
Vector* processes2; Vector* processes2;
Hashtable* processTable; Hashtable* processTable;
UsersTable* usersTable; UsersTable* usersTable;
Hashtable* displayTreeSet;
Hashtable* draftingTreeSet;
Panel* panel; Panel* panel;
int following; int following;
uid_t userId; uid_t userId;
@ -52,8 +63,6 @@ typedef struct ProcessList_ {
unsigned long long int totalMem; unsigned long long int totalMem;
unsigned long long int usedMem; unsigned long long int usedMem;
unsigned long long int freeMem;
unsigned long long int sharedMem;
unsigned long long int buffersMem; unsigned long long int buffersMem;
unsigned long long int cachedMem; unsigned long long int cachedMem;
unsigned long long int totalSwap; unsigned long long int totalSwap;
@ -62,14 +71,15 @@ typedef struct ProcessList_ {
int cpuCount; int cpuCount;
time_t scanTs;
} ProcessList; } ProcessList;
ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidMatchList, uid_t userId); ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
void ProcessList_delete(ProcessList* pl); void ProcessList_delete(ProcessList* pl);
void ProcessList_goThroughEntries(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
void ProcessList_done(ProcessList* this); void ProcessList_done(ProcessList* this);
@ -87,7 +97,7 @@ int ProcessList_size(ProcessList* this);
void ProcessList_sort(ProcessList* this); void ProcessList_sort(ProcessList* this);
ProcessField ProcessList_keyAt(ProcessList* this, int at); ProcessField ProcessList_keyAt(const ProcessList* this, int at);
void ProcessList_expandTree(ProcessList* this); void ProcessList_expandTree(ProcessList* this);
@ -95,6 +105,6 @@ 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); void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate);
#endif #endif

105
ProcessLocksScreen.c Normal file
View File

@ -0,0 +1,105 @@
/*
htop - ProcessLocksScreen.c
(C) 2020 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 "ProcessLocksScreen.h"
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
#include "Panel.h"
#include "Platform.h"
#include "ProvideCurses.h"
#include "Vector.h"
#include "XUtils.h"
ProcessLocksScreen* ProcessLocksScreen_new(const Process* process) {
ProcessLocksScreen* this = xMalloc(sizeof(ProcessLocksScreen));
Object_setClass(this, Class(ProcessLocksScreen));
if (Process_isThread(process))
this->pid = process->tgid;
else
this->pid = process->pid;
return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " ID TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME");
}
void ProcessLocksScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this));
}
static void ProcessLocksScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Snapshot of file locks of process %d - %s", ((ProcessLocksScreen*)this)->pid, Process_getCommand(this->process));
}
static inline void FileLocks_Data_clear(FileLocks_Data* data) {
free(data->locktype);
free(data->exclusive);
free(data->readwrite);
free(data->filename);
}
static void ProcessLocksScreen_scan(InfoScreen* this) {
Panel* panel = this->display;
int idx = Panel_getSelectedIndex(panel);
Panel_prune(panel);
FileLocks_ProcessData* pdata = Platform_getProcessLocks(((ProcessLocksScreen*)this)->pid);
if (!pdata) {
InfoScreen_addLine(this, "This feature is not supported on your platform.");
} else if (pdata->error) {
InfoScreen_addLine(this, "Could not determine file locks.");
} else {
FileLocks_LockData* ldata = pdata->locks;
if (!ldata) {
InfoScreen_addLine(this, "No locks have been found for the selected process.");
}
while (ldata) {
FileLocks_Data* data = &ldata->data;
char entry[512];
if (ULLONG_MAX == data->end) {
xSnprintf(entry, sizeof(entry), "%10d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20s %s",
data->id,
data->locktype, data->exclusive, data->readwrite,
data->dev[0], data->dev[1], data->inode,
data->start, "<END OF FILE>",
data->filename ? data->filename : "<N/A>"
);
} else {
xSnprintf(entry, sizeof(entry), "%10d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20"PRIu64" %s",
data->id,
data->locktype, data->exclusive, data->readwrite,
data->dev[0], data->dev[1], data->inode,
data->start, data->end,
data->filename ? data->filename : "<N/A>"
);
}
InfoScreen_addLine(this, entry);
FileLocks_Data_clear(&ldata->data);
FileLocks_LockData* old = ldata;
ldata = ldata->next;
free(old);
}
}
free(pdata);
Vector_insertionSort(this->lines);
Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx);
}
const InfoScreenClass ProcessLocksScreen_class = {
.super = {
.extends = Class(Object),
.delete = ProcessLocksScreen_delete
},
.scan = ProcessLocksScreen_scan,
.draw = ProcessLocksScreen_draw
};

52
ProcessLocksScreen.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef HEADER_ProcessLocksScreen
#define HEADER_ProcessLocksScreen
/*
htop - ProcessLocksScreen.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#include "InfoScreen.h"
#include "Object.h"
#include "Process.h"
typedef struct ProcessLocksScreen_ {
InfoScreen super;
pid_t pid;
} ProcessLocksScreen;
typedef struct FileLocks_Data_ {
char* locktype;
char* exclusive;
char* readwrite;
char* filename;
int id;
unsigned int dev[2];
uint64_t inode;
uint64_t start;
uint64_t end;
} FileLocks_Data;
typedef struct FileLocks_LockData_ {
FileLocks_Data data;
struct FileLocks_LockData_* next;
} FileLocks_LockData;
typedef struct FileLocks_ProcessData_ {
bool error;
struct FileLocks_LockData_* locks;
} FileLocks_ProcessData;
extern const InfoScreenClass ProcessLocksScreen_class;
ProcessLocksScreen* ProcessLocksScreen_new(const Process* process);
void ProcessLocksScreen_delete(Object* this);
#endif

34
ProvideCurses.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef HEADER_ProvideCurses
#define HEADER_ProvideCurses
/*
htop - RichString.h
(C) 2004,2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h"
// IWYU pragma: begin_exports
#ifdef HAVE_NCURSESW_CURSES_H
#include <ncursesw/curses.h>
#elif defined(HAVE_NCURSES_NCURSES_H)
#include <ncurses/ncurses.h>
#elif defined(HAVE_NCURSES_CURSES_H)
#include <ncurses/curses.h>
#elif defined(HAVE_NCURSES_H)
#include <ncurses.h>
#elif defined(HAVE_CURSES_H)
#include <curses.h>
#endif
#ifdef HAVE_LIBNCURSESW
#include <wchar.h>
#include <wctype.h>
#endif
// IWYU pragma: end_exports
#endif // HEADER_ProvideCurses

50
README
View File

@ -7,37 +7,63 @@
[![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) [![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)
## Introduction ## Introduction
`htop` is a cross-platform interactive process viewer. `htop` is a cross-platform interactive process viewer.
It requires `ncurses`.
For more information and details on how to contribute to `htop` `htop` allows scrolling the list of processes vertically and horizontally to see their full command lines and related information like memory and CPU consumption.
visit [htop.dev](https://htop.dev).
The information displayed is configurable through a graphical setup and can be sorted and filtered interactively.
Tasks related to processes (e.g. killing and renicing) can be done without entering their PIDs.
Running `htop` requires `ncurses` libraries (typically named libncursesw*).
For more information and details on how to contribute to `htop` visit [htop.dev](https://htop.dev).
## Build instructions ## Build instructions
This program is distributed as a standard autotools-based package. This program is distributed as a standard GNU autotools-based package.
For detailed instructions see the `INSTALL` file, which
is created after `./autogen.sh` is run.
When compiling from a [release tarball](https://github.com/htop-dev/htop/releases/), run: Compiling `htop` requires the header files for `ncurses` (libncursesw*-dev). Install these and other required packages for C development from your package manager.
Then, when compiling from a [release tarball](https://bintray.com/htop/source/htop), run:
~~~ shell ~~~ shell
./configure && make ./configure && make
~~~ ~~~
For compiling sources downloaded from the Git repository, run: Alternatively, for compiling sources downloaded from the Git repository (`git clone` or downloads from [Github releases](https://github.com/htop-dev/htop/releases/)),
install the header files for `ncurses` (libncursesw*-dev) and other required development packages from your distribution's package manager. Then run:
~~~ shell ~~~ shell
./autogen.sh && ./configure && make ./autogen.sh && ./configure && make
~~~ ~~~
By default `make install` will install into `/usr/local`, for changing By default `make install` will install into `/usr/local`, for changing the path use `./configure --prefix=/some/path`.
the path use `./configure --prefix=/some/path`.
See the manual page (`man htop`) or the on-line help ('F1' or 'h' See the manual page (`man htop`) or the on-line help ('F1' or 'h' inside `htop`) for a list of supported key commands.
inside `htop`) for a list of supported key commands.
## Support
If you have trouble running `htop` please consult your Operating System / Linux distribution documentation for getting support and filing bugs.
## Bugs, development feedback
We have a [development mailing list](https://htop.dev/mailinglist.html). Feel free to subscribe for release announcements or asking questions on the development of htop.
You can also join our IRC channel #htop on freenode and talk to the developers there.
If you have found an issue with the source of htop, please check whether this has already been reported in our [Github issue tracker](https://github.com/htop-dev/htop/issues).
If not, please file a new issue describing the problem you have found, the location in the source code you are referring to and a possible fix.
## History
`htop` was invented, developed and maintained by Hisham Muhammad from 2004 to 2019. His [legacy repository](https://github.com/hishamhm/htop/) has been archived to preserve the history.
In 2020 a [team](https://github.com/orgs/htop-dev/people) took over the development amicably and continues to maintain `htop` collaboratively.
## License ## License

View File

@ -1,17 +1,19 @@
/* /*
htop - RichString.c htop - RichString.c
(C) 2004,2011 Hisham H. Muhammad (C) 2004,2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "RichString.h" #include "RichString.h"
#include "XAlloc.h"
#include "Macros.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "Macros.h"
#include "XUtils.h"
#define charBytes(n) (sizeof(CharType) * (n)) #define charBytes(n) (sizeof(CharType) * (n))
static void RichString_extendLen(RichString* this, int len) { static void RichString_extendLen(RichString* this, int len) {
@ -34,15 +36,23 @@ static void RichString_extendLen(RichString* this, int len) {
this->chlen = len; this->chlen = len;
} }
#define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0) static void RichString_setLen(RichString* this, int len) {
if (len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) {
RichString_setChar(this, len, 0);
this->chlen = len;
} else {
RichString_extendLen(this, len);
}
}
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) { static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) {
wchar_t data[len+1]; wchar_t data[len + 1];
len = mbstowcs(data, data_c, len); len = mbstowcs(data, data_c, len);
if (len < 0) if (len < 0)
return; return;
int newLen = from + len; int newLen = from + len;
RichString_setLen(this, newLen); RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++) { for (int i = from, j = 0; i < newLen; i++, j++) {
@ -70,13 +80,14 @@ int RichString_findChar(RichString* this, char c, int start) {
return -1; return -1;
} }
#else #else /* HAVE_LIBNCURSESW */
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) { static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) {
int newLen = from + len; int newLen = from + len;
RichString_setLen(this, newLen); RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++) for (int i = from, j = 0; i < newLen; i++, j++) {
this->chptr[i] = (data_c[j] >= 32 ? data_c[j] : '?') | attrs; this->chptr[i] = (data_c[j] >= 32 ? data_c[j] : '?') | attrs;
}
this->chptr[newLen] = 0; this->chptr[newLen] = 0;
} }
@ -99,7 +110,7 @@ int RichString_findChar(RichString* this, char c, int start) {
return -1; return -1;
} }
#endif #endif /* HAVE_LIBNCURSESW */
void RichString_prune(RichString* this) { void RichString_prune(RichString* this) {
if (this->chlen > RICHSTRING_MAXLEN) if (this->chlen > RICHSTRING_MAXLEN)
@ -108,6 +119,15 @@ void RichString_prune(RichString* this) {
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);
}
}
void RichString_setAttr(RichString* this, int attrs) { void RichString_setAttr(RichString* this, int attrs) {
RichString_setAttrn(this, attrs, 0, this->chlen - 1); RichString_setAttrn(this, attrs, 0, this->chlen - 1);
} }

View File

@ -3,49 +3,33 @@
/* /*
htop - RichString.h htop - RichString.h
(C) 2004,2011 Hisham H. Muhammad (C) 2004,2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" #include "config.h"
#include <ctype.h>
#include <assert.h> #include "ProvideCurses.h"
#ifdef HAVE_NCURSESW_CURSES_H
#include <ncursesw/curses.h>
#elif HAVE_NCURSES_NCURSES_H
#include <ncurses/ncurses.h>
#elif HAVE_NCURSES_CURSES_H
#include <ncurses/curses.h>
#elif HAVE_NCURSES_H
#include <ncurses.h>
#elif HAVE_CURSES_H
#include <curses.h>
#endif
#ifdef HAVE_LIBNCURSESW
#include <wctype.h>
#endif
#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); memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr; #define RichString_begin(this) RichString (this); RichString_beginAllocated(this)
#define RichString_beginAllocated(this) memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr; #define RichString_beginAllocated(this) do { memset(&(this), 0, sizeof(RichString)); (this).chptr = (this).chstr; } while(0)
#define RichString_end(this) RichString_prune(&(this)); #define RichString_end(this) RichString_prune(&(this))
#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] & 255)
#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])
#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
@ -54,27 +38,20 @@ in the source distribution for its full text.
typedef struct RichString_ { typedef struct RichString_ {
int chlen; int chlen;
CharType* chptr; CharType* chptr;
CharType chstr[RICHSTRING_MAXLEN+1]; CharType chstr[RICHSTRING_MAXLEN + 1];
int highlightAttr;
} RichString; } RichString;
#ifdef HAVE_LIBNCURSESW
void RichString_setAttrn(RichString* this, int attrs, int start, int finish); void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
int RichString_findChar(RichString* this, char c, int start); int RichString_findChar(RichString* this, char c, int start);
#else
void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
int RichString_findChar(RichString* this, char c, int start);
#endif
void RichString_prune(RichString* this); 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_append(RichString* this, int attrs, const char* data); void RichString_append(RichString* this, int attrs, const char* data);
void RichString_appendn(RichString* this, int attrs, const char* data, int len); void RichString_appendn(RichString* this, int attrs, const char* data, int len);

View File

@ -1,34 +1,37 @@
/* /*
htop - ScreenManager.c htop - ScreenManager.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "ScreenManager.h" #include "ScreenManager.h"
#include "ProcessList.h"
#include "Object.h"
#include "CRT.h"
#include <assert.h> #include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
#include <sys/time.h>
#include "CRT.h"
#include "FunctionBar.h"
#include "Object.h"
#include "ProcessList.h"
#include "ProvideCurses.h"
#include "XUtils.h"
ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner) { ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const State* state, bool owner) {
ScreenManager* this; ScreenManager* this;
this = xMalloc(sizeof(ScreenManager)); this = xMalloc(sizeof(ScreenManager));
this->x1 = x1; this->x1 = 0;
this->y1 = y1; this->y1 = header->height;
this->x2 = x2; this->x2 = 0;
this->y2 = y2; this->y2 = -1;
this->orientation = orientation;
this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE); this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE);
this->panelCount = 0; this->panelCount = 0;
this->header = header; this->header = header;
this->settings = settings; this->settings = settings;
this->state = state;
this->owner = owner; this->owner = owner;
this->allowFocusChange = true; this->allowFocusChange = true;
return this; return this;
@ -44,21 +47,18 @@ inline int ScreenManager_size(ScreenManager* this) {
} }
void ScreenManager_add(ScreenManager* this, Panel* item, int size) { void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
if (this->orientation == HORIZONTAL) { int lastX = 0;
int lastX = 0; if (this->panelCount > 0) {
if (this->panelCount > 0) { Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1); lastX = last->x + last->w + 1;
lastX = last->x + last->w + 1;
}
int height = LINES - this->y1 + this->y2;
if (size > 0) {
Panel_resize(item, size, height);
} else {
Panel_resize(item, COLS-this->x1+this->x2-lastX, height);
}
Panel_move(item, lastX, this->y1);
} }
// TODO: VERTICAL int height = LINES - this->y1 + this->y2;
if (size > 0) {
Panel_resize(item, size, height);
} else {
Panel_resize(item, COLS - this->x1 + this->x2 - lastX, height);
}
Panel_move(item, lastX, this->y1);
Vector_add(this->panels, item); Vector_add(this->panels, item);
item->needsRedraw = true; item->needsRedraw = true;
this->panelCount++; this->panelCount++;
@ -77,33 +77,35 @@ void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) {
this->x2 = x2; this->x2 = x2;
this->y2 = y2; this->y2 = y2;
int panels = this->panelCount; int panels = this->panelCount;
if (this->orientation == HORIZONTAL) { 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+y2);
Panel_move(panel, lastX, y1);
lastX = panel->x + panel->w + 1;
}
Panel* panel = (Panel*) Vector_get(this->panels, panels-1);
Panel_resize(panel, COLS-x1+x2-lastX, LINES-y1+y2);
Panel_move(panel, lastX, y1); Panel_move(panel, lastX, y1);
lastX = panel->x + panel->w + 1;
} }
// TODO: VERTICAL Panel* panel = (Panel*) Vector_get(this->panels, panels - 1);
Panel_resize(panel, COLS - x1 + x2 - lastX, LINES - y1 + y2);
Panel_move(panel, lastX, y1);
} }
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; struct timeval tv;
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.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 = *rescan || *timedOut; *rescan |= *timedOut;
if (newTime < *oldTime) *rescan = true; // clock was adjusted?
if (newTime < *oldTime) {
*rescan = true; // clock was adjusted?
}
if (*rescan) { if (*rescan) {
*oldTime = newTime; *oldTime = newTime;
ProcessList_scan(pl); ProcessList_scan(pl, this->state->pauseProcessUpdate);
if (*sortTimeout == 0 || this->settings->treeView) { if (*sortTimeout == 0 || this->settings->treeView) {
ProcessList_sort(pl); ProcessList_sort(pl);
*sortTimeout = 1; *sortTimeout = 1;
@ -121,15 +123,17 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
const int nPanels = this->panelCount; const int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) { for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i); Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_draw(panel, i == focus); Panel_draw(panel, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection));
if (this->orientation == HORIZONTAL) { mvvline(panel->y, panel->x + panel->w, ' ', panel->h + 1);
mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
}
} }
} }
static Panel* setCurrentPanel(Panel* panel) { static Panel* setCurrentPanel(const ScreenManager* this, Panel* panel) {
FunctionBar_draw(panel->currentBar, NULL); FunctionBar_draw(panel->currentBar);
if (panel == this->state->panel && this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}
return panel; return panel;
} }
@ -137,7 +141,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false; bool quit = false;
int focus = 0; int focus = 0;
Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus)); Panel* panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
double oldTime = 0.0; double oldTime = 0.0;
@ -175,15 +179,15 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
} else { } else {
for (int i = 0; i < this->panelCount; i++) { for (int i = 0; i < this->panelCount; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i); Panel* panel = (Panel*) Vector_get(this->panels, i);
if (mevent.x >= panel->x && mevent.x <= panel->x+panel->w) { if (mevent.x >= panel->x && mevent.x <= panel->x + panel->w) {
if (mevent.y == panel->y) { if (mevent.y == panel->y) {
ch = EVENT_HEADER_CLICK(mevent.x - panel->x); ch = EVENT_HEADER_CLICK(mevent.x - panel->x);
break; break;
} else if (mevent.y > panel->y && mevent.y <= panel->y+panel->h) { } else if (mevent.y > panel->y && mevent.y <= panel->y + panel->h) {
ch = KEY_MOUSE; ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) { if (panel == panelFocus || this->allowFocusChange) {
focus = i; focus = i;
panelFocus = setCurrentPanel(panel); panelFocus = setCurrentPanel(this, panel);
Object* oldSelection = Panel_getSelected(panel); Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1); Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) { if (Panel_getSelected(panel) == oldSelection) {
@ -211,8 +215,9 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (closeTimeout == 100) { if (closeTimeout == 100) {
break; break;
} }
} else } else {
closeTimeout = 0; closeTimeout = 0;
}
redraw = false; redraw = false;
continue; continue;
} }
@ -254,14 +259,21 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (this->panelCount < 2) { if (this->panelCount < 2) {
goto defaultHandler; goto defaultHandler;
} }
if (!this->allowFocusChange)
if (!this->allowFocusChange) {
break; break;
tryLeft: }
if (focus > 0)
tryLeft:
if (focus > 0) {
focus--; focus--;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus)); }
if (Panel_size(panelFocus) == 0 && focus > 0)
panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
if (Panel_size(panelFocus) == 0 && focus > 0) {
goto tryLeft; goto tryLeft;
}
break; break;
case KEY_RIGHT: case KEY_RIGHT:
case KEY_CTRL('F'): case KEY_CTRL('F'):
@ -269,30 +281,39 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (this->panelCount < 2) { if (this->panelCount < 2) {
goto defaultHandler; goto defaultHandler;
} }
if (!this->allowFocusChange) if (!this->allowFocusChange) {
break; break;
tryRight: }
if (focus < this->panelCount - 1)
tryRight:
if (focus < this->panelCount - 1) {
focus++; focus++;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus)); }
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1)
panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) {
goto tryRight; goto tryRight;
}
break; break;
case KEY_F(10):
case 'q':
case 27: case 27:
case 'q':
case KEY_F(10):
quit = true; quit = true;
continue; continue;
default: default:
defaultHandler: defaultHandler:
sortTimeout = resetSortTimeout; sortTimeout = resetSortTimeout;
Panel_onKey(panelFocus, ch); Panel_onKey(panelFocus, ch);
break; break;
} }
} }
if (lastFocus) if (lastFocus) {
*lastFocus = panelFocus; *lastFocus = panelFocus;
if (lastKey) }
if (lastKey) {
*lastKey = ch; *lastKey = ch;
}
} }

View File

@ -3,35 +3,34 @@
/* /*
htop - ScreenManager.h htop - ScreenManager.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Vector.h" #include <stdbool.h>
#include "Header.h"
#include "Settings.h" #include "Action.h"
#include "Panel.h" #include "Header.h"
#include "Panel.h"
#include "Settings.h"
#include "Vector.h"
typedef enum Orientation_ {
VERTICAL,
HORIZONTAL
} Orientation;
typedef struct ScreenManager_ { typedef struct ScreenManager_ {
int x1; int x1;
int y1; int y1;
int x2; int x2;
int y2; int y2;
Orientation orientation;
Vector* panels; Vector* panels;
int panelCount; int panelCount;
const Header* header; Header* header;
const Settings* settings; const Settings* settings;
const State* state;
bool owner; bool owner;
bool allowFocusChange; bool allowFocusChange;
} ScreenManager; } ScreenManager;
ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner); ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const State* state, bool owner);
void ScreenManager_delete(ScreenManager* this); void ScreenManager_delete(ScreenManager* this);

View File

@ -1,26 +1,28 @@
/* /*
htop - Settings.c htop - Settings.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Settings.h" #include "Settings.h"
#include "Platform.h"
#include "StringUtils.h" #include <stdio.h>
#include "Vector.h"
#include "CRT.h"
#include <sys/stat.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h>
#include "CRT.h"
#include "Macros.h"
#include "Meter.h"
#include "Platform.h"
#include "XUtils.h"
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 < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) { for (unsigned int i = 0; i < ARRAYSIZE(this->columns); i++) {
String_freeArray(this->columns[i].names); String_freeArray(this->columns[i].names);
free(this->columns[i].modes); free(this->columns[i].modes);
} }
@ -29,16 +31,14 @@ void Settings_delete(Settings* this) {
static void Settings_readMeters(Settings* this, char* line, int column) { static void Settings_readMeters(Settings* this, char* line, int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
int nIds; char** ids = String_split(trim, ' ', NULL);
char** ids = String_split(trim, ' ', &nIds);
free(trim); free(trim);
this->columns[column].names = ids; this->columns[column].names = ids;
} }
static void Settings_readMeterModes(Settings* this, char* line, int column) { static void Settings_readMeterModes(Settings* this, char* line, int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
int nIds; char** ids = String_split(trim, ' ', NULL);
char** ids = String_split(trim, ' ', &nIds);
free(trim); free(trim);
int len = 0; int len = 0;
for (int i = 0; ids[i]; i++) { for (int i = 0; ids[i]; i++) {
@ -53,9 +53,9 @@ static void Settings_readMeterModes(Settings* this, char* line, int column) {
this->columns[column].modes = modes; this->columns[column].modes = modes;
} }
static void Settings_defaultMeters(Settings* this) { static void Settings_defaultMeters(Settings* this, int initialCpuCount) {
int sizes[] = { 3, 3 }; int sizes[] = { 3, 3 };
if (this->cpuCount > 4) { if (initialCpuCount > 4) {
sizes[1]++; sizes[1]++;
} }
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
@ -64,12 +64,12 @@ static void Settings_defaultMeters(Settings* this) {
this->columns[i].len = sizes[i]; this->columns[i].len = sizes[i];
} }
int r = 0; int r = 0;
if (this->cpuCount > 8) { if (initialCpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2"); this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE; this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2"); this->columns[1].names[r] = xStrdup("RightCPUs2");
this->columns[1].modes[r++] = BAR_METERMODE; this->columns[1].modes[r++] = BAR_METERMODE;
} else if (this->cpuCount > 4) { } else if (initialCpuCount > 4) {
this->columns[0].names[0] = xStrdup("LeftCPUs"); this->columns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE; this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs"); this->columns[1].names[r] = xStrdup("RightCPUs");
@ -90,17 +90,16 @@ static void Settings_defaultMeters(Settings* this) {
this->columns[1].modes[r++] = TEXT_METERMODE; this->columns[1].modes[r++] = TEXT_METERMODE;
} }
static void readFields(ProcessField* fields, int* flags, const char* line) { static void readFields(ProcessField* fields, uint32_t* flags, const char* line) {
char* trim = String_trim(line); char* trim = String_trim(line);
int nIds; char** ids = String_split(trim, ' ', NULL);
char** ids = String_split(trim, ' ', &nIds);
free(trim); free(trim);
int i, j; int i, j;
*flags = 0; *flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) { for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format. // This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1; int id = atoi(ids[i]) + 1;
if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) { if (id > 0 && id < Platform_numberOfFields && Process_fields[id].name) {
fields[j] = id; fields[j] = id;
*flags |= Process_fields[id].flags; *flags |= Process_fields[id].flags;
j++; j++;
@ -110,13 +109,14 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
String_freeArray(ids); String_freeArray(ids);
} }
static bool Settings_read(Settings* this, const char* fileName) { static bool Settings_read(Settings* this, const char* fileName, int initialCpuCount) {
FILE* fd; FILE* fd;
CRT_dropPrivileges(); CRT_dropPrivileges();
fd = fopen(fileName, "r"); fd = fopen(fileName, "r");
CRT_restorePrivileges(); CRT_restorePrivileges();
if (!fd) if (!fd)
return false; return false;
bool didReadMeters = false; bool didReadMeters = false;
bool didReadFields = false; bool didReadFields = false;
for (;;) { for (;;) {
@ -124,7 +124,7 @@ static bool Settings_read(Settings* this, const char* fileName) {
if (!line) { if (!line) {
break; break;
} }
int nOptions; size_t nOptions;
char** option = String_split(line, '=', &nOptions); char** option = String_split(line, '=', &nOptions);
free (line); free (line);
if (nOptions < 2) { if (nOptions < 2) {
@ -141,8 +141,6 @@ static bool Settings_read(Settings* this, const char* fileName) {
this->direction = atoi(option[1]); this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) { } else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]); this->treeView = atoi(option[1]);
} else if (String_eq(option[0], "hide_threads")) {
this->hideThreads = 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")) {
@ -159,6 +157,16 @@ static bool Settings_read(Settings* this, const char* fileName) {
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")) {
this->highlightThreads = atoi(option[1]); this->highlightThreads = atoi(option[1]);
} else if (String_eq(option[0], "highlight_changes")) {
this->highlightChanges = atoi(option[1]);
} else if (String_eq(option[0], "highlight_changes_delay_secs")) {
this->highlightDelaySecs = CLAMP(atoi(option[1]), 1, 24*60*60);
} else if (String_eq(option[0], "find_comm_in_cmdline")) {
this->findCommInCmdline = atoi(option[1]);
} else if (String_eq(option[0], "strip_exe_from_cmdline")) {
this->stripExeFromCmdline = atoi(option[1]);
} else if (String_eq(option[0], "show_merged_command")) {
this->showMergedCommand = atoi(option[1]);
} else if (String_eq(option[0], "header_margin")) { } else if (String_eq(option[0], "header_margin")) {
this->headerMargin = atoi(option[1]); this->headerMargin = atoi(option[1]);
} else if (String_eq(option[0], "expand_system_time")) { } else if (String_eq(option[0], "expand_system_time")) {
@ -166,22 +174,33 @@ static bool Settings_read(Settings* this, const char* fileName) {
this->detailedCPUTime = atoi(option[1]); this->detailedCPUTime = atoi(option[1]);
} else if (String_eq(option[0], "detailed_cpu_time")) { } else if (String_eq(option[0], "detailed_cpu_time")) {
this->detailedCPUTime = atoi(option[1]); this->detailedCPUTime = atoi(option[1]);
} else if (String_eq(option[0], "cpu_count_from_one")) {
this->countCPUsFromOne = atoi(option[1]);
} else if (String_eq(option[0], "cpu_count_from_zero")) { } else if (String_eq(option[0], "cpu_count_from_zero")) {
this->countCPUsFromZero = atoi(option[1]); // old (inverted) naming also supported for backwards compatibility
this->countCPUsFromOne = !atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_usage")) { } else if (String_eq(option[0], "show_cpu_usage")) {
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
} else if (String_eq(option[0], "show_cpu_temperature")) {
this->showCPUTemperature = atoi(option[1]);
} else if (String_eq(option[0], "degree_fahrenheit")) {
this->degreeFahrenheit = atoi(option[1]);
#endif
} else if (String_eq(option[0], "update_process_names")) { } else if (String_eq(option[0], "update_process_names")) {
this->updateProcessNames = atoi(option[1]); this->updateProcessNames = atoi(option[1]);
} else if (String_eq(option[0], "account_guest_in_cpu_meter")) { } else if (String_eq(option[0], "account_guest_in_cpu_meter")) {
this->accountGuestInCPUMeter = atoi(option[1]); this->accountGuestInCPUMeter = atoi(option[1]);
} else if (String_eq(option[0], "delay")) { } else if (String_eq(option[0], "delay")) {
this->delay = atoi(option[1]); this->delay = CLAMP(atoi(option[1]), 1, 255);
} else if (String_eq(option[0], "color_scheme")) { } else if (String_eq(option[0], "color_scheme")) {
this->colorScheme = atoi(option[1]); this->colorScheme = atoi(option[1]);
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0; if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) {
} else if (String_eq(option[0], "enable_mouse")) { this->colorScheme = 0;
}
} else if (String_eq(option[0], "enable_mouse")) {
this->enableMouse = atoi(option[1]); this->enableMouse = atoi(option[1]);
} else if (String_eq(option[0], "left_meters")) { } else if (String_eq(option[0], "left_meters")) {
Settings_readMeters(this, option[1], 0); Settings_readMeters(this, option[1], 0);
@ -204,7 +223,7 @@ static bool Settings_read(Settings* this, const char* fileName) {
} }
fclose(fd); fclose(fd);
if (!didReadMeters) { if (!didReadMeters) {
Settings_defaultMeters(this); Settings_defaultMeters(this, initialCpuCount);
} }
return didReadFields; return didReadFields;
} }
@ -214,7 +233,7 @@ static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
const char* sep = ""; const char* sep = "";
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
// This "-1" is for compatibility with the older enum format. // This "-1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i]-1); fprintf(fd, "%s%d", sep, (int) fields[i] - 1);
sep = " "; sep = " ";
} }
fprintf(fd, "\n"); fprintf(fd, "\n");
@ -252,9 +271,8 @@ bool Settings_write(Settings* this) {
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n"); fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
writeFields(fd, this->fields, "fields"); writeFields(fd, this->fields, "fields");
// This "-1" is for compatibility with the older enum format. // This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey-1); fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction); fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads); fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads); fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers); fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers);
@ -263,12 +281,21 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName); fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes); fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads); fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
fprintf(fd, "highlight_changes=%d\n", (int) this->highlightChanges);
fprintf(fd, "highlight_changes_delay_secs=%d\n", (int) this->highlightDelaySecs);
fprintf(fd, "find_comm_in_cmdline=%d\n", (int) this->findCommInCmdline);
fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline);
fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand);
fprintf(fd, "tree_view=%d\n", (int) this->treeView); fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero); fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne);
fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage); fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage);
fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency); fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency);
#ifdef HAVE_SENSORS_SENSORS_H
fprintf(fd, "show_cpu_temperature=%d\n", (int) this->showCPUTemperature);
fprintf(fd, "degree_fahrenheit=%d\n", (int) this->degreeFahrenheit);
#endif
fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames); fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames);
fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter); fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter);
fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme); fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
@ -285,12 +312,11 @@ bool Settings_write(Settings* this) {
return true; return true;
} }
Settings* Settings_new(int cpuCount) { Settings* Settings_new(int initialCpuCount) {
Settings* this = xCalloc(1, sizeof(Settings)); Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU; this->sortKey = PERCENT_CPU;
this->direction = 1; this->direction = 1;
this->hideThreads = false;
this->shadowOtherUsers = false; this->shadowOtherUsers = false;
this->showThreadNames = false; this->showThreadNames = false;
this->hideKernelThreads = false; this->hideKernelThreads = false;
@ -299,17 +325,25 @@ Settings* Settings_new(int cpuCount) {
this->highlightBaseName = false; this->highlightBaseName = false;
this->highlightMegabytes = false; this->highlightMegabytes = false;
this->detailedCPUTime = false; this->detailedCPUTime = false;
this->countCPUsFromZero = false; this->countCPUsFromOne = false;
this->showCPUUsage = true; this->showCPUUsage = true;
this->showCPUFrequency = false; this->showCPUFrequency = false;
#ifdef HAVE_SENSORS_SENSORS_H
this->showCPUTemperature = false;
this->degreeFahrenheit = false;
#endif
this->updateProcessNames = false; this->updateProcessNames = false;
this->cpuCount = cpuCount;
this->showProgramPath = true; this->showProgramPath = true;
this->highlightThreads = true; this->highlightThreads = true;
this->highlightChanges = false;
this->highlightDelaySecs = DEFAULT_HIGHLIGHT_SECS;
this->findCommInCmdline = true;
this->stripExeFromCmdline = true;
this->showMergedCommand = false;
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
this->topologyAffinity = false; this->topologyAffinity = false;
#endif #endif
this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField)); this->fields = xCalloc(Platform_numberOfFields + 1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector, // TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects). // (and ProcessFields into proper objects).
this->flags = 0; this->flags = 0;
@ -325,7 +359,9 @@ Settings* Settings_new(int cpuCount) {
this->filename = xStrdup(rcfile); this->filename = xStrdup(rcfile);
} else { } else {
const char* home = getenv("HOME"); const char* home = getenv("HOME");
if (!home) home = ""; if (!home)
home = "";
const char* xdgConfigHome = getenv("XDG_CONFIG_HOME"); const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
char* configDir = NULL; char* configDir = NULL;
char* htopDir = NULL; char* htopDir = NULL;
@ -358,37 +394,42 @@ Settings* Settings_new(int cpuCount) {
this->delay = DEFAULT_DELAY; this->delay = DEFAULT_DELAY;
bool ok = false; bool ok = false;
if (legacyDotfile) { if (legacyDotfile) {
ok = Settings_read(this, legacyDotfile); 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)) {
unlink(legacyDotfile); unlink(legacyDotfile);
}
} }
free(legacyDotfile); free(legacyDotfile);
} }
if (!ok) { if (!ok) {
ok = Settings_read(this, this->filename); ok = Settings_read(this, this->filename, initialCpuCount);
} }
if (!ok) { if (!ok) {
this->changed = true; this->changed = true;
// TODO: how to get SYSCONFDIR correctly through Autoconf? // TODO: how to get SYSCONFDIR correctly through Autoconf?
char* systemSettings = String_cat(SYSCONFDIR, "/htoprc"); char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
ok = Settings_read(this, systemSettings); ok = Settings_read(this, systemSettings, initialCpuCount);
free(systemSettings); free(systemSettings);
} }
if (!ok) { if (!ok) {
Settings_defaultMeters(this); Settings_defaultMeters(this, initialCpuCount);
this->hideKernelThreads = true; this->hideKernelThreads = true;
this->highlightMegabytes = true; this->highlightMegabytes = true;
this->highlightThreads = true; this->highlightThreads = true;
this->findCommInCmdline = true;
this->stripExeFromCmdline = true;
this->showMergedCommand = false;
this->headerMargin = true; this->headerMargin = true;
} }
return this; return this;
} }
void Settings_invertSortOrder(Settings* this) { void Settings_invertSortOrder(Settings* this) {
if (this->direction == 1) if (this->direction == 1) {
this->direction = -1; this->direction = -1;
else } else {
this->direction = 1; this->direction = 1;
}
} }

View File

@ -3,14 +3,18 @@
/* /*
htop - Settings.h htop - Settings.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#define DEFAULT_DELAY 15 #include "config.h" // IWYU pragma: keep
#include <stdbool.h>
#include "Process.h" #include "Process.h"
#include <stdbool.h>
#define DEFAULT_DELAY 15
typedef struct { typedef struct {
int len; int len;
@ -23,21 +27,23 @@ typedef struct Settings_ {
MeterColumnSettings columns[2]; MeterColumnSettings columns[2];
ProcessField* fields; ProcessField* fields;
int flags; uint32_t flags;
int colorScheme; int colorScheme;
int delay; int delay;
int cpuCount;
int direction; int direction;
ProcessField sortKey; ProcessField sortKey;
bool countCPUsFromZero; bool countCPUsFromOne;
bool detailedCPUTime; bool detailedCPUTime;
bool showCPUUsage; bool showCPUUsage;
bool showCPUFrequency; bool showCPUFrequency;
#ifdef HAVE_SENSORS_SENSORS_H
bool showCPUTemperature;
bool degreeFahrenheit;
#endif
bool treeView; bool treeView;
bool showProgramPath; bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers; bool shadowOtherUsers;
bool showThreadNames; bool showThreadNames;
bool hideKernelThreads; bool hideKernelThreads;
@ -45,6 +51,11 @@ typedef struct Settings_ {
bool highlightBaseName; bool highlightBaseName;
bool highlightMegabytes; bool highlightMegabytes;
bool highlightThreads; bool highlightThreads;
bool highlightChanges;
int highlightDelaySecs;
bool findCommInCmdline;
bool stripExeFromCmdline;
bool showMergedCommand;
bool updateProcessNames; bool updateProcessNames;
bool accountGuestInCPUMeter; bool accountGuestInCPUMeter;
bool headerMargin; bool headerMargin;
@ -56,13 +67,13 @@ typedef struct Settings_ {
bool changed; bool changed;
} Settings; } Settings;
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromZero ? (cpu) : (cpu)+1) #define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))
void Settings_delete(Settings* this); void Settings_delete(Settings* this);
bool Settings_write(Settings* this); bool Settings_write(Settings* this);
Settings* Settings_new(int cpuCount); Settings* Settings_new(int initialCpuCount);
void Settings_invertSortOrder(Settings* this); void Settings_invertSortOrder(Settings* this);

View File

@ -1,22 +1,21 @@
/* /*
htop - SignalsPanel.c htop - SignalsPanel.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Panel.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
#include "Platform.h"
#include "ListItem.h"
#include "RichString.h"
#include <stdlib.h>
#include <assert.h>
#include <signal.h> #include <signal.h>
#include <stdbool.h>
#include <ctype.h> #include "FunctionBar.h"
#include "ListItem.h"
#include "Object.h"
#include "Panel.h"
#include "Platform.h"
#include "XUtils.h"
Panel* SignalsPanel_new() { Panel* SignalsPanel_new() {
@ -36,7 +35,7 @@ Panel* SignalsPanel_new() {
static char buf[16]; static char buf[16];
for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) { for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) {
int n = sig - SIGRTMIN; int n = sig - SIGRTMIN;
xSnprintf(buf, 16, "%2d SIGRTMIN%-+3d", sig, n); xSnprintf(buf, sizeof(buf), "%2d SIGRTMIN%-+3d", sig, n);
if (n == 0) { if (n == 0) {
buf[11] = '\0'; buf[11] = '\0';
} }

View File

@ -3,15 +3,17 @@
/* /*
htop - SignalsPanel.h htop - SignalsPanel.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Panel.h"
typedef struct SignalItem_ { typedef struct SignalItem_ {
const char* name; const char* name;
int number; int number;
} SignalItem; } SignalItem;
Panel* SignalsPanel_new(); Panel* SignalsPanel_new(void);
#endif #endif

View File

@ -1,142 +0,0 @@
/*
htop - StringUtils.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "StringUtils.h"
#include "XAlloc.h"
#include "config.h"
#include <string.h>
#include <strings.h>
#include <stdlib.h>
char* String_cat(const char* s1, const char* s2) {
int l1 = strlen(s1);
int l2 = strlen(s2);
char* out = xMalloc(l1 + l2 + 1);
memcpy(out, s1, l1);
memcpy(out+l1, s2, l2+1);
out[l1 + l2] = '\0';
return out;
}
char* String_trim(const char* in) {
while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') {
in++;
}
int len = strlen(in);
while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) {
len--;
}
char* out = xMalloc(len+1);
strncpy(out, in, len);
out[len] = '\0';
return out;
}
inline int String_eq(const char* s1, const char* s2) {
if (s1 == NULL || s2 == NULL) {
if (s1 == NULL && s2 == NULL)
return 1;
else
return 0;
}
return (strcmp(s1, s2) == 0);
}
char** String_split(const char* s, char sep, int* n) {
*n = 0;
const int rate = 10;
char** out = xCalloc(rate, sizeof(char*));
int ctr = 0;
int blocks = rate;
char* where;
while ((where = strchr(s, sep)) != NULL) {
int size = where - s;
char* token = xMalloc(size + 1);
strncpy(token, s, size);
token[size] = '\0';
out[ctr] = token;
ctr++;
if (ctr == blocks) {
blocks += rate;
out = (char**) xRealloc(out, sizeof(char*) * blocks);
}
s += size + 1;
}
if (s[0] != '\0') {
out[ctr] = xStrdup(s);
ctr++;
}
out = xRealloc(out, sizeof(char*) * (ctr + 1));
out[ctr] = NULL;
*n = ctr;
return out;
}
void String_freeArray(char** s) {
if (!s) {
return;
}
for (int i = 0; s[i] != NULL; i++) {
free(s[i]);
}
free(s);
}
char* String_getToken(const char* line, const unsigned short int numMatch) {
const unsigned short int len = strlen(line);
char inWord = 0;
unsigned short int count = 0;
char match[50];
unsigned short int foundCount = 0;
for (unsigned short int i = 0; i < len; i++) {
char lastState = inWord;
inWord = line[i] == ' ' ? 0:1;
if (lastState == 0 && inWord == 1)
count++;
if(inWord == 1){
if (count == numMatch && line[i] != ' ' && line[i] != '\0' && line[i] != '\n' && line[i] != (char)EOF) {
match[foundCount] = line[i];
foundCount++;
}
}
}
match[foundCount] = '\0';
return((char*)xStrdup(match));
}
char* String_readLine(FILE* fd) {
const int step = 1024;
int bufSize = step;
char* buffer = xMalloc(step + 1);
char* at = buffer;
for (;;) {
char* ok = fgets(at, step + 1, fd);
if (!ok) {
free(buffer);
return NULL;
}
char* newLine = strrchr(at, '\n');
if (newLine) {
*newLine = '\0';
return buffer;
} else {
if (feof(fd)) {
return buffer;
}
}
bufSize += step;
buffer = xRealloc(buffer, bufSize + 1);
at = buffer + bufSize - step;
}
}

View File

@ -1,34 +0,0 @@
#ifndef HEADER_StringUtils
#define HEADER_StringUtils
/*
htop - StringUtils.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include <stdio.h>
#define String_startsWith(s, match) (strncmp((s),(match),strlen(match)) == 0)
#define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL)
/*
* String_startsWith gives better performance if strlen(match) can be computed
* at compile time (e.g. when they are immutable string literals). :)
*/
char* String_cat(const char* s1, const char* s2);
char* String_trim(const char* in);
int String_eq(const char* s1, const char* s2);
char** String_split(const char* s, char sep, int* n);
void String_freeArray(char** s);
char* String_getToken(const char* line, const unsigned short int numMatch);
char* String_readLine(FILE* fd);
#endif

View File

@ -1,23 +1,19 @@
/* /*
htop - SwapMeter.c htop - SwapMeter.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "SwapMeter.h" #include "SwapMeter.h"
#include "CRT.h" #include "CRT.h"
#include "Object.h"
#include "Platform.h" #include "Platform.h"
#include "RichString.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/param.h>
#include <assert.h>
int SwapMeter_attributes[] = { static const int SwapMeter_attributes[] = {
SWAP SWAP
}; };
@ -34,9 +30,9 @@ static void SwapMeter_updateValues(Meter* this, char* buffer, int size) {
} }
} }
static void SwapMeter_display(Object* cast, RichString* out) { static void SwapMeter_display(const Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
Meter* this = (Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":"); RichString_write(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, 50); Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_append(out, CRT_colors[METER_VALUE], buffer);
@ -45,7 +41,7 @@ static void SwapMeter_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_append(out, CRT_colors[METER_VALUE], buffer);
} }
MeterClass SwapMeter_class = { const MeterClass SwapMeter_class = {
.super = { .super = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete, .delete = Meter_delete,

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