640 Commits
3.0.0 ... 3.0.5

Author SHA1 Message Date
ce6d60e7de Release 3.0.5 2021-01-11 18:18:21 +01:00
5d92a9f20d Merge branch 'fix-palette2' of deviant/htop 2021-01-11 17:45:59 +01:00
b3500ac3b7 Clarify that only the main screen function bar is optionally hidden 2021-01-11 13:50:34 +01:00
2ba8a81d47 Fix clearing the last line in setup on function bar change (thanks cgzones) 2021-01-11 13:47:33 +01:00
V
f2f1c99ad9 Fix white text in the Light Terminal colour scheme 2021-01-11 12:54:19 +01:00
1ffe5d79bd Make Infoscreens the correct height 2021-01-11 12:53:07 +01:00
8502f4e64f Merge branch 'wide_proc_comm' of cgzones/htop 2021-01-11 12:43:39 +01:00
a5db139a0a Linux: use correct column alignment for wide fields
This affects:
- PROC_COMM, PROC_EXE and CWD on Linux
- JAIL on FreeBSD and DragonFlyBSD
- ZONE on Solaris
2021-01-11 12:02:25 +01:00
8a67d7f086 Merge branch 'RichString_attrn' of cgzones/htop 2021-01-11 09:36:42 +01:00
0b89c66f58 Merge branch 'following_exit' of cgzones/htop 2021-01-11 09:36:25 +01:00
3bb731c645 RichString_setAttrn: refactor to take a length instead of a stop index
Fixes: #459
2021-01-10 16:51:25 +01:00
fbaa0cd146 Exit follow mode cleanly after followed process dies 2021-01-10 16:43:24 +01:00
a076488809 Solaris: make Process callbacks static
Fixes prototype of SolarisProcess_compareByKey since 90ea3ac3
2021-01-09 20:17:31 +01:00
6301d5c1da Convert unnecessary static variables
They are not used in any other function and are not used maybe
uninitialized.
2021-01-09 14:31:07 +01:00
4979245aa5 Update help and man page for improved -t / -s options 2021-01-08 21:34:30 +01:00
0155980fd6 Free memory on multiple filter command line arguments 2021-01-08 21:07:05 +01:00
2af90b711f Merge branch 'err_h' of cgzones/htop 2021-01-08 17:39:04 +01:00
d55f394541 Merge branch 'light_color' of cgzones/htop 2021-01-08 17:35:26 +01:00
c7d93a8f30 Merge branch 'ncurses_format' of cgzones/htop 2021-01-08 17:31:06 +01:00
2d2a2df6f2 Refactor crash handler message to avoid embedded directive
CRT.c:821:2: error: embedding a directive within macro arguments has undefined behavior [-Werror,-Wembedded-directive]
#ifdef HAVE_EXECINFO_H
 ^
CRT.c:823:2: error: embedding a directive within macro arguments has undefined behavior [-Werror,-Wembedded-directive]
#endif
 ^
CRT.c:858:2: error: embedding a directive within macro arguments has undefined behavior [-Werror,-Wembedded-directive]
#ifdef HTOP_DARWIN
 ^
CRT.c:862:2: error: embedding a directive within macro arguments has undefined behavior [-Werror,-Wembedded-directive]
#endif
 ^

CRT.c:864:2: error: embedding a directive within macro arguments has undefined behavior [-Werror,-Wembedded-directive]
#ifdef HTOP_DARWIN
 ^
CRT.c:868:2: error: embedding a directive within macro arguments has undefined behavior [-Werror,-Wembedded-directive]
#endif
 ^
2021-01-08 16:53:16 +01:00
de645ea16c ci: enable format attributes in ncurses headers
Avoid format string issues like bfcb8ca0 by helping compilers spot such
bogus usages.

Also use LTO and O3 in the full-featured gcc job, which might trigger
additional warnings on advanced inlining, like
3695cbd5d8 and
ad3acfc847
2021-01-08 14:05:56 +01:00
6ae56f2578 Revert color change on LightTerminal
Partially revert 4b14ab9789

ColorPair(Black,Black) is not actually black on black, but due to
adjustments in CRT_setColors() black on default-background-color.

Thanks to V for reporting.
2021-01-08 11:25:25 +01:00
V
bfcb8ca019 InfoScreen: fix uncontrolled format string
mvwprintw takes a format string as its fourth argument, and title is
user-controlled. This results in e.g. crashing when trying to trace a
process with a format specifier in its command line.
2021-01-08 11:06:38 +01:00
d800d7a3ce Drop usage of formatted error messages from <err.h>
They do not clean up the ncurses environment, leaving the terminal in a
broken state.

Also drop bare usage of exit(3).
2021-01-07 16:10:05 +01:00
27db9297b7 Show arrow indicating order of sorted process column 2021-01-07 14:46:44 +01:00
330d4fe22f Unify prototype of Vector_get
Vector_get() currently takes a `const Vector*` in debug mode and a
`Vector*` else.
2021-01-06 17:14:06 +01:00
8e10cde800 Hashtable: fail hard on too big size request 2021-01-06 16:59:28 +01:00
ca2c01bd16 Hashtable: widen size from int to size_t 2021-01-06 16:59:28 +01:00
7043a93eba Hashtable: hide implementation of Hashtable and HashtableItem 2021-01-06 16:59:28 +01:00
8fe04b7494 Hashtable: use more distinct typename for key type 2021-01-06 16:59:28 +01:00
43d5c61884 LibSensors: add support for Ryzen CPUs 2021-01-06 16:54:59 +01:00
e103ec0317 Declare for loop variables inside the loop 2021-01-06 16:43:18 +01:00
7ff654f2df Drop useless double parenthesis 2021-01-06 16:42:54 +01:00
7386c6fed0 Avoid function cast by refactoring callback prototype 2021-01-06 16:42:45 +01:00
ce9e7fd14f Panel_new: reorder arguments
Reorder owner and type so they match the order of Panel_init
2021-01-04 23:12:43 +01:00
badeaf9e82 IncSet: do not resize on our own and do not search on resize
The supervising ScreenManager will resize all Panels.
Also do not start the search on resize.
2021-01-04 23:12:43 +01:00
a3cced9fb6 Add option to hide the Function Bar
Support three settings:
  - Always show Function Bar
  - Always hide the Function Bar, except in Infoscreens (Env/Locks...)
    and when editing the search and filter mode
  - Hide the Function Bar on ESC until the next user input

Closes: #439
2021-01-04 23:12:43 +01:00
24c5ca9ddf Panel: rework hight logic
The hight of a Panel dpends on whether the Panel has a header or not.
Also the header migth not be set on Panel creation, like in the
MainPanel. This currently causes the cursor to get hidden behind the
FunctionBar on down-scrolling.
2021-01-04 23:12:43 +01:00
eb6f8d569d Action: drop resize callback
The supervising ScreenManager will resize all Panels
2021-01-04 23:12:43 +01:00
8c8149d146 XUtils: check for multiplication overflow in allocation size 2021-01-02 22:35:13 +01:00
a150a81669 Fix CPU percentage on M1 silicon Macs 2021-01-02 22:33:20 +01:00
90ea3ac3c9 Object: return int on comparison
Comparisons do, due to the new introduced shaceship-comparisons,
only return -1, 0, 1 or the result of strcmp().
2021-01-02 00:00:17 +01:00
293c16e22d Only initialize and gather delay accounting data if a related column is enabled
Avoid creating and communicating over a netlink socket by default, which
triggers cap_net_admin checks as root.
2021-01-01 21:34:22 +01:00
f6aa5d29bb Action: remove trivial wrapper function 2021-01-01 21:31:30 +01:00
2c06566405 LoadMeter: dynamically adjust color and total of bar
Change the color and total based on the actual 1min load value:

    < 1         : green and total of 1.0
    < cpu-count : yellow and total of cpu-count
    else        : red and total of 2*cpu-count

Closes: #32
2020-12-26 13:32:29 +01:00
d609c04fe4 CRT: add METER_VALUE_ERROR and adjust some METER_VALUE_WARN colors 2020-12-26 13:32:29 +01:00
ca9d7cd708 Also find libsensors.so.4 for Fedora and friends 2020-12-25 13:05:37 +01:00
debeac49cd Merge branch 'cpufreq' of hadfl/htop for Solaris / OmniOS support 2020-12-25 12:24:03 +01:00
a0b899f29d Note Shift-F3 use in man page 2020-12-25 11:53:02 +01:00
8b83a9f055 Enable going back to previous search matches (Shift-F3) 2020-12-25 11:53:02 +01:00
495f2292dc add support to display CPU frequencies on Solarish platforms 2020-12-25 09:26:50 +00:00
1cc3f8074f Merge branch 'user_wide' of cgzones/htop 2020-12-23 22:55:35 +01:00
aa08279964 Linux: accept clock CPU frequency
processor   : 0
cpu         : POWER8 (architected), altivec supported
clock       : 4024.000000MHz
revision    : 2.0 (pvr 004d 0200)

Closes: #424
2020-12-23 19:58:10 +01:00
5359eae28b Process: use correct column aligning on wide username
Closes: #421
2020-12-23 19:56:51 +01:00
f1463fdd64 Added keybind 'N' for sorting by PID 2020-12-23 18:30:26 +01:00
3edb6e1ea3 Position help labels one step to the right 2020-12-23 18:24:22 +01:00
71ddc6a6a1 Merge branch 'remove-n-keybind' of jakem72360/htop 2020-12-23 18:22:17 +01:00
b9336af76f fix argument type following prototype change in "Invert Process_compare resolution so that superclass matches run first" 2020-12-23 17:35:23 +01:00
f46ddd3230 Remove 'n' and 'N' search inc/dec keybinds 2020-12-24 03:24:15 +11:00
94d7f0b585 RichString: return number of written characters on write/append functions 2020-12-23 12:47:53 +01:00
86d2931255 Restore highlighted header of current sorted process column 2020-12-23 12:47:04 +01:00
0672be7db1 Update version number for git repo builds 2020-12-22 17:41:14 +11:00
0b989ee38c Bump version number for 3.0.4 release 2020-12-22 17:39:42 +11:00
3fb0024fd3 Merge branch 'misc' of https://github.com/cgzones/htop into cgzones-misc 2020-12-22 17:30:29 +11:00
dfb9b82607 Resolve clang-analyzer signed/unsigned comparison CI failure 2020-12-22 16:58:17 +11:00
fc7aead36b Merge branch 'harden_makecommandstr' of https://github.com/BenBE/htop into BenBE-harden_makecommandstr 2020-12-22 16:55:11 +11:00
737cd6167a Merge branch 'resize_bar' of https://github.com/cgzones/htop into cgzones-resize_bar 2020-12-22 15:25:08 +11:00
6502b02666 DiD: Ensure string offsets are inside string boundaries 2020-12-21 22:35:38 +01:00
cdfd407e2e Panel_init: initialize selectedLen member 2020-12-21 22:34:50 +01:00
64c05a1ed5 EnvScreen: mark local class functions static 2020-12-21 22:34:50 +01:00
a7612b0b7d TraceScreen: mark local class functions static 2020-12-21 22:34:50 +01:00
3ec8f67ab2 InfoScreen: drop unused member 2020-12-21 22:34:50 +01:00
10c6810bff Avoid NULL dereference on zombie processes
Fixes #361
2020-12-21 22:29:18 +01:00
068561351f Document dynamic bindings and assumed external configuration 2020-12-21 22:15:28 +01:00
9b8b380c32 De-lazy @cgzones :) 2020-12-21 20:40:00 +01:00
a09ad6b8b4 Action: sort key binding assignments
Avoid accidental duplicate usage.
2020-12-21 15:53:42 +01:00
9a86577cf2 DragonFlyBSD update
- move some functions to file scope
- drop unused global variable
2020-12-20 18:32:04 +01:00
8db8b9edac DragonFlyBSD update
- drop unused kinfo includes and link argument
- detect kvm library necessity at configure step
- fix variable typo
2020-12-20 18:22:41 +01:00
4a73e80338 Make remaining number literals use uppercase 2020-12-20 17:17:51 +01:00
5fa1c7040d Minor typo and comment clarification 2020-12-20 17:15:51 +01:00
3f9c63d5c0 MetersPanel: drop color interruption in FunctionBar 2020-12-20 17:02:20 +01:00
358d20687f Use variable-length-array instead of small dynamic allocation 2020-12-20 17:01:50 +01:00
e3862aa67e Rework drawing of FunctionBar
Draw the FunctionBar within Panel_draw instead of manually throughout
the code.
Add an optional PanelClass function drawFunctionBar, to allow specific
panels to override the default FunctionBar_draw call.
Rework the code on color change, to really change all colors (selection
markers and panel headers).

Closes: #402
2020-12-20 17:01:50 +01:00
7e7a53c415 Mark event arrays const 2020-12-20 16:58:37 +01:00
6b100b0cf4 Use upper case numeric literals
See https://rules.sonarsource.com/c/RSPEC-818
2020-12-20 16:58:17 +01:00
6e46fd6f1f BarMeter: rework text padding
In case the text is too long for the bar, try to fit by truncating at a
space character.

E.g.
    [|24.1% 2000Mhz 40°C]
    [24.1% 2000Mhz 40°C]
    [||||24.1% 2000Mhz]
    [|||24.1% 2000Mhz]
    [||24.1% 2000Mhz]
    [|24.1% 2000Mhz]
    [24.1% 2000Mhz]
    [||||   24.1%]
    [||||  24.1%]
    [|||| 24.1%]
    [||||24.1%]
    [|||24.1%]
    [||24.1%]
    [|24.1%]
    [24.1%]
    [24.1]
    [24.]
    [24]
    [2]
2020-12-20 16:55:17 +01:00
22da57d621 CPUMeter: drop minimum width of CPU usage in bar mode
The usage percentage is the first text, no need to set a minimum width.
The BarMeter does already add padding.
2020-12-20 16:55:17 +01:00
c5e31ba4aa Meter: fix artifacts with very tiny width
- The Bar Meter might override the right border
- The TextMeter might wrap-around into the next line
2020-12-20 16:55:17 +01:00
f878f302ca Remove duplicate newline in CRT_fatalError calls 2020-12-19 21:30:39 +01:00
67ccd6b909 Unhardcode tick-to-ms conversion
Division by 100000.0 worked because `sysconf(_SC_CLK_TCK)` happened to be 100.

By unhardcoding:

1) It becomes more clear what this 100000.0 figure comes from.
2) It protects against bugs in the case `sysconf(_SC_CLK_TCK)` ever changes.
2020-12-19 21:30:39 +01:00
f614b8a19f Mark Platform_defaultFields const 2020-12-19 21:13:32 +01:00
c150e4bde9 Enable -Wformat=2
Now that the global format variable Process_pidFormat is gone, enable
the compiler warning -Wformat=2.
2020-12-19 21:13:32 +01:00
9f68c8d341 Merge Process_pidColumns into Process_fields and rework auto-fit for PID-like columns 2020-12-19 21:13:32 +01:00
89473cc9ae Rework enum ProcessField
Use only one enum instead of a global and a platform specific one.
Drop Platform_numberOfFields global variable.
Set known size of Process_fields array
2020-12-19 21:13:32 +01:00
d872e36308 LinuxProcess: drop dead Process columns 2020-12-19 21:13:32 +01:00
77db240b48 Split boilerplate and platform-independent field comparison
This acheives two things:
- Allows for simple tie-breaking if values compare equal (needed to make sorting the tree-view stable)
- Allows for platform-dependent overriding of the sort-order for specific fields

Also fixes a small oversight on DragonFlyBSD when default-sorting.
2020-12-19 16:02:34 +01:00
2327260ee8 Separate tree and list sort orders
Implements the suggestion from https://github.com/htop-dev/htop/issues/399#issuecomment-747861013

Thanks to the refactors from 0bd5c8fb5da and 6393baa74e5, this was really easy
and clean to do.

It maintains the "Tree view always by PID" option in the Settings, which
results in some specific behaviors such as "clicking on the column header to
exit tree view" and "picking a new sort order to exit tree view", for the sake
of the muscle memory of long time htop users. :)
2020-12-19 16:02:34 +01:00
e8c6994f40 Add "Tree view is always sorted by PID" option to mimic htop 2 behavior 2020-12-19 16:02:34 +01:00
3d1703f16f Invert Process_compare resolution so that superclass matches run first
* This removes duplicated code that adjusts the sort direction from every
  OS-specific folder.
* Most fields in a regular htop screen are OS-independent, so trying
  Process_compare first and only falling back to the OS-specific
  compareByKey function if it's an OS-specific field makes sense.
* This will allow us to override the sortKey in a global way without having
  to edit each OS-specific file.
2020-12-19 16:02:34 +01:00
52fa4e7ee4 Fix typo 2020-12-18 23:35:28 +01:00
27b8d81ed2 ProcessList: save scan time in millisecond
The delay is saved in deciseconds, use a bigger resolution to avoid
timing irregularities.
2020-12-18 22:43:21 +01:00
26993d2d2b Support clock_gettime() on OSX El Capitan and earlier 2020-12-18 22:43:21 +01:00
0401df8cbd Update key mapping documentation for sorting 2020-12-18 07:37:23 +01:00
0cb257586a Move macro definitions close to usage 2020-12-16 19:13:56 +01:00
1193c6e349 Use common naming for bare enum types 2020-12-16 19:13:56 +01:00
edd6130be7 MainPanel: use actual KEY_RESIZE instead of KEY_SHUFFLE
KEY_RESIZE (0632) is equal to KEY_SHUFFLE (0x19a)
2020-12-16 19:12:50 +01:00
107e3c8aa5 MainPanel: do not reset hideProcessSelection on KEY_SHUFFLE
KEY_SHUFFLE might get send from time to time, e.g. in a tmux session.
2020-12-15 14:23:09 +01:00
4eeeb63647 LibSensors: fix unversioned libsensors library name 2020-12-15 13:54:32 +01:00
eb36385a6b LibSensors: restore temperature for Raspberry Pi
sensors output:
  cpu_thermal-virtual-0
  Adapter: Virtual device
  temp1:        +58.0 C  (crit = +90.0 C)
2020-12-15 13:46:46 +01:00
79970f05f3 Meter: restore non-wide-character build
Use mbstowcs() only with wide ncurses support.

Closes: #401
2020-12-15 12:05:39 +01:00
61b8e31b41 Misc CRT cleanup 2020-12-14 21:11:20 +01:00
c9583c692d Handle absence of package CPU temperature
Resolves: #389
2020-12-14 21:07:07 +01:00
4507911cc3 Merge pull request #398 from natoscott/harden-linux-btime-init
Harden the extraction of boot time for the Linux platform
2020-12-14 17:53:24 +11:00
b7836515e8 Harden the extraction of boot time for the Linux platform
There is a possible path - albeit theoretical really - through
the btime initialization code in Linux ProcessList_new(), when
String_startsWith() is always false, which can result in btime
not being initialized.

This commit refactors the code to remove that possibility.
2020-12-14 12:16:32 +11:00
a3db2da4a7 Cleanup initialization of jiffies on the Linux platform
Small cleanups - add error handling, remove a local static
variable and refactor LinuxProcess_adjustTime (also rename
it, as its in LinuxProcessList.c not LinuxProcess.c) - and
while there, move the related 'btime' global variable into
LinuxProcessList.c so it can be made static.

Resolves https://github.com/htop-dev/htop/issues/384
2020-12-14 11:56:13 +11:00
cf982f2928 Merge pull request #395 from natoscott/man-page-linting
Remove superflous breaks around man page section heads
2020-12-14 11:48:01 +11:00
8d69a9a53e Simplify initialization of the Linux haveSmapsRollup variable 2020-12-14 01:46:29 +01:00
366b78edd9 Remove superflous breaks around man page section heads
There is no need to start a paragraph explicitly after
a section header (SH) in troff - some man linters will
complain about this as well.
2020-12-14 11:03:46 +11:00
f8a610e6e1 Merge branch 'fix-dlopen-libsensors-debian' of fasterit/htop 2020-12-13 20:09:06 +01:00
4b1a4a4ebd Merge branch 'fix_mach_timebase' of benbe/htop 2020-12-13 20:02:38 +01:00
3655b6ca0b Add column in darwin to indicate whether the the process is running under translation 2020-12-13 17:58:16 +01:00
1506283aff Move Process_fields from darwin/Platform to darwin/DarwinProcess 2020-12-13 17:58:16 +01:00
4b877eb16a Move Process_fields from unsupported/Platform to unsupported/UnsupportedProcess 2020-12-13 17:58:16 +01:00
f32f0188cd Correct timebase for non-x86 CPUs on Darwin
Fixes: #368
2020-12-13 11:47:34 +01:00
e65cdf947c Sort include in Darwin platform headers 2020-12-13 11:47:34 +01:00
ab60f59ed8 Check if clock_gettime needs linking of librt 2020-12-13 00:55:50 +01:00
8149823d56 Define O_PATH if not already defined 2020-12-13 00:55:50 +01:00
12421f460a Fix dlopen issue for libsensors5 in Debian Buster, Bullseye
libsensors.so is provided only by the -dev package, so search for
libsensors.so.5 (installed from the libsensors5 package) explicitly

see: dpkg-query -S libsensors.so
2020-12-12 20:08:17 +01:00
880eecabf5 Indentation and line continuation fixes in configure.ac 2020-12-12 19:49:52 +01:00
738d31b903 Add sys/dirent.h to iwyu/htop.imp 2020-12-11 20:57:19 +01:00
28bc087d8a Drop redundant sys/dirent.h include
sys/dirent.h is included by dirent.h in FreeBSD, and does not exist in Debian GNU/kFreeBSD
2020-12-11 20:57:19 +01:00
2700d99069 Merge pull request #379 from natoscott/streamline-pagesize-variables
Cull the definitions of pageSize and pageSizeKB from CRT.c
2020-12-11 11:06:40 +11:00
75e9f9a8d9 Cull the definitions of pageSize and pageSizeKB from CRT.c
By storing the per-process m_resident and m_virt values in the form
htop wants to display them in (KB, not pages), we no longer need to
have definitions of pageSize and pageSizeKB in the common CRT code.

These variables were never really CRT (i.e. display) related in the
first place.  It turns out the darwin platform code doesn't need to
use these at all (the process values are extracted from the kernel
in bytes not pages) and the other platforms can each use their own
local pagesize variables, in more appropriate locations.

Some platforms were actually already doing this, so this change is
removing duplication of logic and variables there.
2020-12-10 11:57:48 +11:00
db5687a355 Sort in paused mode after inverting sort order 2020-12-09 13:43:07 +01:00
7b739b6292 Fix pause mode ("Z") in tree view 2020-12-09 13:28:15 +01:00
ded9c5d363 PSI Meter: use constant width and only print ten-duration as bar 2020-12-08 23:09:35 +01:00
2d231d77ca Process: simplify 2020-12-08 22:37:15 +01:00
f6613db5cd Additional code simplification
Additional correction for #375
2020-12-08 21:24:19 +01:00
4c44a70f96 Fix broken tree display on inverted sort order
Fixes #375
2020-12-08 21:12:54 +01:00
157086e750 Split RichString_(append|appendn|write) into wide and ascii
RichString_writeFrom takes a top spot during performance analysis due to the
calls to mbstowcs() and iswprint().

Most of the time we know in advance that we are only going to print regular
ASCII characters.
2020-12-08 20:58:40 +01:00
5506925b34 Use sizeof buffer instead of magic number 2020-12-08 16:36:00 +01:00
c6d9fa279b travis CI: drop macOS and Linux builds
They are covered by GitHub CI
Also testing on s390x does not serve much
2020-12-08 16:07:45 +01:00
dcf7ad386c GitHub CI: add macOS build 2020-12-08 16:07:45 +01:00
30bf212185 Merge branch 'gentoo' of cgzones/htop 2020-12-07 16:29:52 +01:00
05969998c1 SELinuxMeter: silence comparison warning on 32-bit
linux/SELinuxMeter.c: In function ‘hasSELinuxMount’:
linux/SELinuxMeter.c:38:21: warning: comparison of integer expressions of different signedness: ‘__fsword_t’ {aka ‘int’} and ‘unsigned int’ [-Wsign-compare]
   38 |    if (sfbuf.f_type != SELINUX_MAGIC) {
      |                     ^~

Origin: 7df27b78e9/libselinux/src/init.c (L40)
2020-12-07 16:05:12 +01:00
ead978bce6 configure: check for additional linker flags for keypad(3)
Gentoo requires an explicit addition of -ltinfo

Resolves: https://bugs.gentoo.org/show_bug.cgi?id=690840
2020-12-07 15:33:16 +01:00
4f88d38256 Correct the version of htop development repo 2020-12-07 19:57:44 +11:00
f03f48a0fb Change version string to note development repo build 2020-12-07 12:16:06 +11:00
ad8aa2ce77 Bump version number for 3.0.3 release 2020-12-07 11:49:14 +11:00
b92cfa7d7a Merge branch 'conversion' of https://github.com/cgzones/htop into cgzones-conversion 2020-12-07 11:41:22 +11:00
57d9ecc551 OpenBSD update
- compilation failures like `return &this->this;` -> `return &this->super;`
- iwyu update
- misc cleanup
2020-12-06 16:20:55 +01:00
ad764ff972 Introduce METER_BUFFER_CHECK and METER_BUFFER_APPEND_CHR to cleanup writing to bar buffers
Closes: #294
2020-12-06 16:03:44 +01:00
77ec86aff4 Use size_t as type for buffer length in Process 2020-12-06 16:03:44 +01:00
e1ce141bc3 Use size_t as len type for Meter_UpdateValues
Most of the time the parameter is passed to snprintf type functions
2020-12-06 16:03:44 +01:00
d9224c66a4 Use size_t as len type for xSnprintf
Like the C snprintf function
2020-12-06 16:03:44 +01:00
3d15ba5197 Remove unused function Header_readMeterName 2020-12-06 16:03:25 +01:00
7ba25aa3c4 IWYU update 2020-12-06 15:32:16 +01:00
22f8f8000c Initialize buffer for retrieved path
This avoids a warning on GCC 11.

Fixes #369
2020-12-06 11:51:03 +01:00
4c4ba9d949 DragonFlyBSDProcessList: fix missing type 2020-12-06 00:43:41 +01:00
8d1595a20e FreeBSD: fix crash on empty environment
e.g. on kernel threads
2020-12-05 20:34:23 +01:00
876194492f LinuxProcessList: add underscore suffix for raw struct name
Fit the general coding style
2020-12-05 20:25:54 +01:00
5f528b7455 Meter: fix bar coloring without wide ncurses support
attrset() seems to not work with mvaddchnstr()
2020-12-05 20:01:10 +01:00
641fd2c4ad RichString: avoid signed integer misuse 2020-12-05 20:01:10 +01:00
f913680020 Hide degree character without wide ncurses support 2020-12-05 20:01:10 +01:00
f0a9dfc37e Resolve conversion from int to char 2020-12-05 19:58:32 +01:00
1e9b184367 Resolve conversion from int to unsigned and back 2020-12-05 19:58:32 +01:00
ba1549f99b Resolve conversion from int to short 2020-12-05 19:58:32 +01:00
f61e74a4af Resolve conversion from ssize_t to int for readlink return value 2020-12-05 19:58:32 +01:00
8029e9af04 Update htop logo, provide .svg file as well 2020-12-05 13:46:34 +01:00
ef0fc7129e Update AUTHORS file with htop-dev team 2020-12-05 11:07:32 +01:00
bc16fa037f Convert personal copyright authorship to team 2020-12-04 13:55:55 +01:00
cc7f16bb8f Some minor additions to the changelog 2020-12-04 07:51:33 +01:00
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
da62b44b16 Bump version, changelog for minor htop-3.0.2 release 2020-09-15 09:43:36 +10:00
c9ecd0fa74 Revert the vim_mode setting for now, needs a rethink
There have been too many bugs reported in vim_mode, and
the proposed fixes are increasingly fragile - hence we
have decided to back it out for now.  For reference:

   https://github.com/htop-dev/htop/issues/69
   https://github.com/htop-dev/htop/pull/37
   https://github.com/htop-dev/htop/pull/106

The whitespace changes also arrived in commit 12805f61d
not sure what that was about, but backed out as well.
2020-09-15 09:33:58 +10:00
f6662f97fd Merge pull request #120 from cgzones/null
Drop always true condition
2020-09-15 09:21:46 +10:00
ccf0960d5c Merge pull request #123 from hugomg/header-duplicates
Clean up some code duplication in the header files
2020-09-14 17:46:18 +10:00
313d7c980c Merge pull request #127 from gh-fork-dump/openbsd-fix
fix building on openbsd due to remaining WhiteList
2020-09-14 17:42:23 +10:00
fd4ada416d fix building on openbsd due to remaining WhiteList 2020-09-14 13:18:40 +10:00
b6828d7b86 Remove some unused #includes
As suggested by cppclean.
2020-09-12 23:32:31 -03:00
a2ef400e43 Merge identical ifdefs in Affinity.h 2020-09-12 19:21:27 -03:00
5ad3c11eaa Alignment tweak 2020-09-12 19:21:27 -03:00
9207401f97 Clean up some code duplication in the header files
PR htop-dev/htop#70 got rid of the infrastructure for generating header
files, but it left behind some code duplication.

Some of cases are things that belong in the header file and don't need
to be repeated in the C file. Other cases are things that belong in the
C file and don't need to be in the header file.

In this commit I tried to fix all of these that I could find. When given
a choice I preferred keeping things out of the header file, unless they
were being used by someone else.
2020-09-12 19:20:44 -03:00
d5eb72e64d Drop always true condition
`env` is allocated by checked allocation functions and can not be NULL.

This checks confuses clang analyzer and causes a null-dereference
warning on `env[size-1]`.
2020-09-12 18:14:39 +02:00
cd55cfd6d2 Merge branch 'BenBE-avoid-shadowing' 2020-09-09 19:41:16 +10:00
35c3a95ff9 Merge branch 'avoid-shadowing' of https://github.com/BenBE/htop into BenBE-avoid-shadowing 2020-09-09 19:40:50 +10:00
06ba81beec Merge branch 'rgouicem-master' 2020-09-09 19:38:59 +10:00
4d6e4ef53c Merge branch 'master' of https://github.com/rgouicem/htop into rgouicem-master 2020-09-09 19:38:53 +10:00
4597332959 Switch variable/field naming from WhiteList to MatchList 2020-09-09 19:38:15 +10:00
c5808c56db Consolidate repeated macro definitions into one header
The MIN, MAX, CLAMP, MINIMUM, and MAXIMUM macros appear
throughout the codebase with many re-definitions.  Make
a single copy of each in a common header file, and use
the BSD variants of MINIMUM/MAXIMUM due to conflicts in
the system <sys/param.h> headers.
2020-09-09 16:56:04 +10:00
8ec5d4a3a0 Further, minor cleanups to headers post-MakeHeaders
Remove leftover empty ifdef/endif pairs, whitespace.
The generated htop.h file was also unused - removed.
2020-09-08 17:33:50 +10:00
eede79b29a Merge branch 'noheadergen' of https://github.com/zevweiss/htop into zevweiss-noheadergen 2020-09-08 16:45:11 +10:00
13b1e96b12 Avoid shadowing of variables 2020-09-07 17:36:01 +02:00
85ff6960ed Merge branch 't6-patch-freebsd-ci' 2020-09-07 10:25:50 +10:00
a1f2532630 Merge branch 'patch-freebsd-ci' of https://github.com/t6/htop-1 into t6-patch-freebsd-ci 2020-09-07 10:25:44 +10:00
7805575114 fix for double symbol link error on solaris 2020-09-05 15:34:27 +10:00
f884beda97 htop should report the nice level properly 2020-09-05 15:29:15 +10:00
0750ff7e76 Fix regression in -u optional-argument handling
Resolves https://github.com/htop-dev/htop/issues/91
2020-09-04 09:50:18 +10:00
7758ffcdea Remove duplicate jail_errmsg declaration.
Fixes: 11ecc65ebb
2020-09-03 12:00:21 -05:00
7b7822b896 Remove superfluous 'extern's from function declarations.
Applied via:

  $ find * -name '*.h' -exec sed -i -r 's/^extern (.+\()/\1/;' {} +

Suggested-by: Bert Wesarg <bert.wesarg@googlemail.com>
2020-09-03 11:59:26 -05:00
a1a027b9bd Axe automated header generation.
Reasoning:
 - implementation was unsound -- broke down when I added a fairly
   basic macro definition expanding to a struct initializer in a *.c
   file.

 - made it way too easy (e.g. via otherwise totally innocuous git
   commands) to end up with timestamps such that it always ran
   MakeHeader.py but never used its output, leading to overbuild noise
   when running what should be a null 'make'.

 - but mostly: it's just an awkward way of dealing with C code.
2020-09-03 11:58:58 -05:00
35d7e42b88 Add FreeBSD to Travis
Signed-off-by: Tobias Kortkamp <t@tobik.me>
2020-09-03 09:00:17 +02:00
7734dfe55d Merge pull request #86 from t6/patch-freebsd-fno-common
Unbreak with -fno-common on FreeBSD
2020-09-03 16:47:30 +10:00
11ecc65ebb Unbreak with -fno-common on FreeBSD
GCC10 and Clang11 now default to -fno-common.

ld: error: duplicate symbol: jail_errmsg
>>> defined at Platform.c
>>>            freebsd/Platform.o:(jail_errmsg)
>>> defined at FreeBSDProcessList.c
>>>            freebsd/FreeBSDProcessList.o:(.bss+0x90)

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

Improves: d9a5dd4b91
2020-08-31 11:55:53 +02:00
47a7d0bd74 Merge branch 'configure' of https://github.com/cgzones/htop into cgzones-configure 2020-08-31 17:13:37 +10:00
b321177b08 Merge branch 'master' of github.com:htop-dev/htop 2020-08-31 16:57:46 +10:00
800d8c735d Merge branch 'cov_fixes' of https://github.com/cgzones/htop into cgzones-cov_fixes 2020-08-31 16:56:32 +10:00
f14173038e Merge branch 'oom' of https://github.com/cgzones/htop
Closes: #18, #22
2020-08-31 08:32:39 +02:00
fdf8a28e60 Merge branch 'Ckath-vim_mode_setting' 2020-08-31 16:14:48 +10:00
244630f67f Merge branch 'vim_mode_setting' of https://github.com/Ckath/htop into Ckath-vim_mode_setting 2020-08-31 16:14:40 +10:00
0a835e13bf Simplify the --version output, old dates are confusing people
Drop the copyright notice from the version output as a number
of people seem to be confused by what this means, and we can
do without all the (well intentioned) bug reports.
2020-08-31 16:14:23 +10:00
4bd0859b80 Add a badge/link to the released source tarballs 2020-08-31 16:12:44 +10:00
338bd829b0 add toggle for vim mode in options 2020-08-29 15:15:52 +02:00
5c99c6e942 Check btime sscanf parse from /proc/stat
Found by Coverity
2020-08-28 16:46:50 +02:00
a850d81bf5 Avoid use of uninitialized variables
Found by Coverity
2020-08-28 16:46:50 +02:00
05a5fdc47f Ignore sscanf return value of /proc/stat
Found by Coverity
2020-08-28 16:46:50 +02:00
af84d3dfa9 Fail on out-of-range CPU number
Found by Coverity
2020-08-28 16:46:50 +02:00
df41979afc Ignore wmove return value
Found by Coverity
2020-08-28 16:46:50 +02:00
d9a5dd4b91 Improve OOM output
* Fix sort by adding cast
* Shrink column size to 4
* Drop unnecessary maximum field width specifier in sscanf
2020-08-28 14:24:59 +02:00
a48ce9d103 Really tell gcc to ignore return value of fscanf 2020-08-28 13:10:41 +02:00
3f5784a3f0 Convert hwlock CI run to a full featured one 2020-08-28 13:10:41 +02:00
3b084db1c4 Print configured state 2020-08-28 13:10:41 +02:00
979d004214 Improve indent 2020-08-28 13:10:16 +02:00
5bee902665 Drop configure option --enable-proc
Move to HTOP_LINUX, as --enable-proc implies my_htop_platform=linux, and
the Linux features do not work without a proc fs.
2020-08-28 13:10:16 +02:00
3ef5df25bc always display frequency in MHz 2020-08-28 12:15:32 +02:00
2d14269bcd Merge pull request #14 from zdykstra/master
Normalize ZFS ARC caption
2020-08-28 16:58:06 +10:00
b992d52bcf Increae the size of sysfs power supply path buffers
Resolves https://github.com/htop-dev/htop/issues/15
2020-08-28 16:57:21 +10:00
f97fbd668a Normalize ZFS ARC caption
Other captions take the form of LABEL:<space>. This moves the
uncompressed ZFS ARC caption into the same style.
2020-08-28 00:02:35 -05:00
b5e6952cc6 Update link to Coverity project, still pending. 2020-08-27 10:42:40 +10:00
230 changed files with 13422 additions and 9640 deletions

View File

@ -2,8 +2,13 @@ name: CI
on: [ push, pull_request ]
env:
# Enable format attributes in ncurses headers
# Enable fortified memory/string handling
CPPFLAGS: -DGCC_PRINTF -DGCC_SCANF -D_FORTIFY_SOURCE=2
jobs:
build-ubuntu-latest:
build-ubuntu-latest-minimal-gcc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@ -12,48 +17,116 @@ jobs:
- name: Bootstrap
run: ./autogen.sh
- 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
run: make
run: make -k
- 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
env:
CC: clang-10
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-10 main' -y
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-10 libncursesw5-dev
run: sudo apt-get install clang-11 libncursesw5-dev
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --without-sensors
- name: Build
run: make -k
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors"
build-ubuntu-latest-full-featured-gcc:
runs-on: ubuntu-latest
# Enable LTO, might trigger additional warnings on advanced inlining
env:
CFLAGS: -O3 -g -flto
LDFLAGS: -O3 -g -flto
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: sudo apt-get install 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-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)"
build-macos-latest-clang:
runs-on: macOS-latest
env:
CC: clang
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: brew install automake
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror
- name: Build
run: make
run: make -k
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
build-ubuntu-latest-hwloc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: sudo apt-get install libncursesw5-dev libhwloc-dev
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror --enable-hwloc
- name: Build
run: make
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-hwloc'
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror"
whitespace_check:
runs-on: ubuntu-latest

6
.gitignore vendored
View File

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

View File

@ -5,7 +5,11 @@ compiler:
- gcc
os:
- linux
- osx
- freebsd
script: ./autogen.sh && ./configure && make
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

10
AUTHORS
View File

@ -1 +1,11 @@
Originally authored by:
Hisham H. Muhammad
Currently maintained by the htop dev team:
Benny Baumann
Christian Göttsche
Daniel Lange
Nathan Scott
For the full list of contributors see:
git log --format="%aN" | sort -u

418
Action.c
View File

@ -1,66 +1,44 @@
/*
htop - Action.c
(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.
*/
#include "config.h"
#include "config.h" // IWYU pragma: keep
#include "Action.h"
#include "Affinity.h"
#include "AffinityPanel.h"
#include <pwd.h>
#include <stdbool.h>
#include <stdlib.h>
#include "CategoriesPanel.h"
#include "CommandScreen.h"
#include "CRT.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 "OpenFilesScreen.h"
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "ProvideCurses.h"
#include "ScreenManager.h"
#include "SignalsPanel.h"
#include "StringUtils.h"
#include "TraceScreen.h"
#include "Platform.h"
#include "Vector.h"
#include "XUtils.h"
#include <ctype.h>
#include <math.h>
#include <pwd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/param.h>
#include <sys/time.h>
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
#include "Affinity.h"
#include "AffinityPanel.h"
#endif
/*{
#include "IncSet.h"
#include "Settings.h"
#include "Header.h"
#include "UsersTable.h"
#include "ProcessList.h"
#include "Panel.h"
typedef enum {
HTOP_OK = 0x00,
HTOP_REFRESH = 0x01,
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH
HTOP_SAVE_SETTINGS = 0x04,
HTOP_KEEP_FOLLOWING = 0x08,
HTOP_QUIT = 0x10,
HTOP_REDRAW_BAR = 0x20,
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
} Htop_Reaction;
typedef Htop_Reaction (*Htop_Action)();
typedef struct State_ {
Settings* settings;
UsersTable* ut;
ProcessList* pl;
Panel* panel;
Header* header;
} State;
}*/
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) {
Panel* panel = st->panel;
@ -68,7 +46,7 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
Settings* settings = st->settings;
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;
ScreenManager_add(scr, list, x - 1);
ScreenManager_add(scr, panel, -1);
@ -92,53 +70,53 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
Process* selected = (Process*)Panel_getSelected(panel);
if (selected && selected->pid == pid)
return Panel_getSelected(list);
else
beep();
} else {
return Panel_getSelected(list);
}
}
return NULL;
}
// ----------------------------------------
static void Action_runSetup(Settings* settings, const Header* header, ProcessList* pl) {
ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, true);
CategoriesPanel* panelCategories = CategoriesPanel_new(scr, settings, (Header*) header, pl);
static void Action_runSetup(State* st) {
ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true);
CategoriesPanel* panelCategories = CategoriesPanel_new(scr, st->settings, st->header, st->pl);
ScreenManager_add(scr, (Panel*) panelCategories, 16);
CategoriesPanel_makeMetersPage(panelCategories);
Panel* panelFocus;
int ch;
ScreenManager_run(scr, &panelFocus, &ch);
ScreenManager_delete(scr);
if (settings->changed) {
Header_writeBackToSettings(header);
if (st->settings->changed) {
Header_writeBackToSettings(st->header);
}
}
static bool changePriority(MainPanel* panel, int delta) {
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)
beep();
return anyTagged;
}
static void addUserToVector(int key, void* userCast, void* panelCast) {
char* user = (char*) userCast;
Panel* panel = (Panel*) panelCast;
static void addUserToVector(ht_key_t key, void* userCast, void* panelCast) {
const char* user = userCast;
Panel* panel = panelCast;
Panel_add(panel, (Object*) ListItem_new(user, key));
}
bool Action_setUserOnly(const char* userName, uid_t* userId) {
struct passwd* user = getpwnam(userName);
const struct passwd* user = getpwnam(userName);
if (user) {
*userId = user->pw_uid;
return true;
}
*userId = -1;
*userId = (uid_t)-1;
return false;
}
@ -155,14 +133,18 @@ static void tagAllChildren(Panel* panel, Process* parent) {
static bool expandCollapse(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false;
if (!p)
return false;
p->showChildren = !p->showChildren;
return true;
}
static bool collapseIntoParent(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false;
if (!p)
return false;
pid_t ppid = Process_getParentPid(p);
for (int i = 0; i < Panel_size(panel); i++) {
Process* q = (Process*) Panel_get(panel, i);
@ -176,22 +158,23 @@ static bool collapseIntoParent(Panel* panel) {
}
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey;
settings->direction = 1;
settings->treeView = false;
Settings_setSortKey(settings, sortKey);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction sortBy(State* st) {
// ----------------------------------------
static Htop_Reaction actionSetSortColumn(State* st) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by");
ProcessField* fields = st->settings->fields;
for (int i = 0; fields[i]; i++) {
char* name = String_trim(Process_fields[fields[i]].name);
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == st->settings->sortKey)
if (fields[i] == Settings_getActiveSortKey(st->settings))
Panel_setSelected(sortPanel, i);
free(name);
}
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false);
@ -199,15 +182,15 @@ static Htop_Reaction sortBy(State* st) {
reaction |= Action_setSortKey(st->settings, field->key);
}
Object_delete(sortPanel);
if (st->pauseProcessUpdate)
ProcessList_sort(st->pl);
return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
// ----------------------------------------
static Htop_Reaction actionResize(State* st) {
clear();
Panel_resize(st->panel, COLS, LINES-(st->panel->y)-1);
return HTOP_REDRAW_BAR;
static Htop_Reaction actionSortByPID(State* st) {
return Action_setSortKey(st->settings, PID);
}
static Htop_Reaction actionSortByMemory(State* st) {
@ -229,7 +212,6 @@ static Htop_Reaction actionToggleKernelThreads(State* st) {
static Htop_Reaction actionToggleUserlandThreads(State* st) {
st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads;
st->settings->hideThreads = st->settings->hideUserlandThreads;
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
}
@ -238,9 +220,17 @@ static Htop_Reaction actionToggleProgramPath(State* st) {
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) {
st->settings->treeView = !st->settings->treeView;
if (st->settings->treeView) st->settings->direction = 1;
if (st->settings->treeView) {
st->settings->treeDirection = 1;
}
ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
@ -258,16 +248,6 @@ static Htop_Reaction actionIncSearch(State* st) {
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionIncNext(State* st) {
IncSet_next(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionIncPrev(State* st) {
IncSet_prev(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionHigherPriority(State* st) {
bool changed = changePriority((MainPanel*)st->panel, -1);
return changed ? HTOP_REFRESH : HTOP_OK;
@ -280,13 +260,11 @@ static Htop_Reaction actionLowerPriority(State* st) {
static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings);
if (st->pauseProcessUpdate)
ProcessList_sort(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionSetSortColumn(State* st) {
return sortBy(st);
}
static Htop_Reaction actionExpandOrCollapse(State* st) {
bool changed = expandCollapse(st->panel);
return changed ? HTOP_RECALCULATE : HTOP_OK;
@ -304,20 +282,25 @@ static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}
static Htop_Reaction actionQuit() {
static Htop_Reaction actionQuit(ATTR_UNUSED State* st) {
return HTOP_QUIT;
}
static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1)
return HTOP_OK;
#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
Panel* panel = st->panel;
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK;
if (!p)
return HTOP_OK;
Affinity* affinity1 = Affinity_get(p, st->pl);
if (!affinity1) return HTOP_OK;
if (!affinity1)
return HTOP_OK;
int width;
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width);
width += 1; /* we add a gap between the panels */
@ -326,24 +309,25 @@ static Htop_Reaction actionSetAffinity(State* st) {
void* set = Action_pickFromVector(st, affinityPanel, width, true);
if (set) {
Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity2 }, NULL);
if (!ok) beep();
bool ok = MainPanel_foreachProcess((MainPanel*)panel, Affinity_set, (Arg) { .v = affinity2 }, NULL);
if (!ok)
beep();
Affinity_delete(affinity2);
}
Panel_delete((Object*)affinityPanel);
Object_delete(affinityPanel);
#endif
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionKill(State* st) {
Panel* signalsPanel = (Panel*) SignalsPanel_new();
Panel* signalsPanel = SignalsPanel_new();
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true);
if (sgn) {
if (sgn->key != 0) {
Panel_setHeader(st->panel, "Sending...");
Panel_draw(st->panel, true);
Panel_draw(st->panel, false, true, true, State_hideFunctionBar(st));
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);
}
}
@ -352,7 +336,7 @@ static Htop_Reaction actionKill(State* st) {
}
static Htop_Reaction actionFilterByUser(State* st) {
Panel* usersPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Show ", "Cancel "));
Panel* usersPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Show ", "Cancel "));
Panel_setHeader(usersPanel, "Show processes of:");
UsersTable_foreach(st->ut, addUserToVector, usersPanel);
Vector_insertionSort(usersPanel->items);
@ -361,7 +345,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false);
if (picked) {
if (picked == allUsers) {
st->pl->userId = -1;
st->pl->userId = (uid_t)-1;
} else {
Action_setUserOnly(ListItem_getRef(picked), &(st->pl->userId));
}
@ -372,12 +356,12 @@ static Htop_Reaction actionFilterByUser(State* st) {
Htop_Reaction Action_follow(State* st) {
st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel);
Panel_setSelectionColor(st->panel, CRT_colors[PANEL_SELECTION_FOLLOW]);
Panel_setSelectionColor(st->panel, PANEL_SELECTION_FOLLOW);
return HTOP_KEEP_FOLLOWING;
}
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
int headerHeight = Header_calculateHeight(st->header);
Panel_move(st->panel, 0, headerHeight);
@ -387,7 +371,9 @@ static Htop_Reaction actionSetup(State* st) {
static Htop_Reaction actionLsof(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
if (!p)
return HTOP_OK;
OpenFilesScreen* ofs = OpenFilesScreen_new(p);
InfoScreen_run((InfoScreen*)ofs);
OpenFilesScreen_delete((Object*)ofs);
@ -396,9 +382,22 @@ static Htop_Reaction actionLsof(State* st) {
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);
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);
bool ok = TraceScreen_forkTracer(ts);
if (ok) {
@ -412,56 +411,77 @@ static Htop_Reaction actionStrace(State* st) {
static Htop_Reaction actionTag(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
if (!p)
return HTOP_OK;
Process_toggleTag(p);
Panel_onKey(st->panel, KEY_DOWN);
return HTOP_OK;
}
static Htop_Reaction actionRedraw() {
static Htop_Reaction actionRedraw(ATTR_UNUSED State* st) {
clear();
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 = " Digits: ", .info = "incremental PID search" },
{ .key = " F3 /: ", .info = "incremental name search" },
{ .key = " F4 \\: ",.info = "incremental name filtering" },
{ .key = " F5 t: ", .info = "tree view" },
{ .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 = " H: ", .info = "hide/show user process threads" },
{ .key = " K: ", .info = "hide/show kernel threads" },
{ .key = " F: ", .info = "cursor follows process" },
{ .key = " F6 + -: ", .info = "expand/collapse tree" },
{ .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" },
{ .key = " + -: ", .info = "expand/collapse tree" },
{ .key = "N P M T: ", .info = "sort by PID, CPU%, MEM% or TIME" },
{ .key = " I: ", .info = "invert sort order" },
{ .key = " F6 > .: ", .info = "select sort column" },
{ .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 = " c: ", .info = "tag process and its children" },
{ .key = " U: ", .info = "untag all processes" },
{ .key = " F9 k: ", .info = "kill process/tagged processes" },
{ .key = " F7 ]: ", .info = "higher priority (root only)" },
{ .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" },
#endif
{ .key = " e: ", .info = "show process environment" },
{ .key = " i: ", .info = "set IO priority" },
{ .key = " l: ", .info = "list open files with lsof" },
{ .key = " x: ", .info = "list file locks of process" },
{ .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 = " F1 h: ", .info = "show this help screen" },
{ .key = " F10 q: ", .info = "quit" },
{ .key = NULL, .info = NULL }
};
static inline void addattrstr( int attr, const char* str) {
attrset(attr);
addstr(str);
}
static Htop_Reaction actionHelp(State* st) {
Settings* settings = st->settings;
@ -471,12 +491,15 @@ static Htop_Reaction actionHelp(State* st) {
for (int i = 0; i < LINES - 1; i++)
mvhline(i, 0, ' ', COLS);
mvaddstr(0, 0, "htop " VERSION " - " COPYRIGHT);
mvaddstr(1, 0, "Released under the GNU GPL. See 'man' page for more info.");
int line = 0;
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]);
mvaddstr(3, 0, "CPU usage bar: ");
#define addattrstr(a,s) attrset(a);addstr(s)
line++;
mvaddstr(line++, 0, "CPU usage bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
if (settings->detailedCPUTime) {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
@ -497,7 +520,7 @@ static Htop_Reaction actionHelp(State* st) {
}
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(4, 0, "Memory bar: ");
mvaddstr(line++, 0, "Memory bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/");
@ -505,29 +528,49 @@ static Htop_Reaction actionHelp(State* st) {
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(5, 0, "Swap bar: ");
mvaddstr(line++, 0, "Swap bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[SWAP], "used");
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addattrstr(CRT_colors[BAR_BORDER], "]");
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) {
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");
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); }
attrset(CRT_colors[HELP_BOLD]);
for (int i = 0; helpLeft[i].key; i++) { mvaddstr(9+i, 0, helpLeft[i].key); }
for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); }
attrset(CRT_colors[PROCESS_THREAD]);
mvaddstr(16, 32, "threads");
mvaddstr(17, 26, "threads");
line++;
mvaddstr(line++, 0, "Process state: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep");
line++;
int item;
for (item = 0; helpLeft[item].key; item++) {
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line + item, 10, helpLeft[item].info);
attrset(CRT_colors[HELP_BOLD]);
mvaddstr(line + item, 1, helpLeft[item].key);
if (String_eq(helpLeft[item].key, " H: ")) {
attrset(CRT_colors[PROCESS_THREAD]);
mvaddstr(line + item, 33, "threads");
} else if (String_eq(helpLeft[item].key, " K: ")) {
attrset(CRT_colors[PROCESS_THREAD]);
mvaddstr(line + item, 27, "threads");
}
}
int leftHelpItems = item;
for (item = 0; helpRight[item].key; item++) {
attrset(CRT_colors[HELP_BOLD]);
mvaddstr(line + item, 41, helpRight[item].key);
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line + item, 50, helpRight[item].info);
}
line += MAXIMUM(leftHelpItems, item);
line++;
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]);
refresh();
CRT_readKey();
@ -546,14 +589,18 @@ static Htop_Reaction actionUntagAll(State* st) {
static Htop_Reaction actionTagAllChildren(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
if (!p)
return HTOP_OK;
tagAllChildren(st->panel, p);
return HTOP_OK;
}
static Htop_Reaction actionShowEnvScreen(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
if (!p)
return HTOP_OK;
EnvScreen* es = EnvScreen_new(p);
InfoScreen_run((InfoScreen*)es);
EnvScreen_delete((Object*)es);
@ -562,58 +609,71 @@ static Htop_Reaction actionShowEnvScreen(State* st) {
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) {
keys[KEY_RESIZE] = actionResize;
keys['M'] = actionSortByMemory;
keys['T'] = actionSortByTime;
keys['P'] = actionSortByCPU;
keys['H'] = actionToggleUserlandThreads;
keys['K'] = actionToggleKernelThreads;
keys['p'] = actionToggleProgramPath;
keys['t'] = actionToggleTreeView;
keys[KEY_F(5)] = actionToggleTreeView;
keys[KEY_F(4)] = actionIncFilter;
keys['\\'] = actionIncFilter;
keys[KEY_F(3)] = actionIncSearch;
keys['/'] = actionIncSearch;
keys['n'] = actionIncNext;
keys['N'] = actionIncPrev;
keys[']'] = actionHigherPriority;
keys[KEY_F(7)] = actionHigherPriority;
keys['['] = actionLowerPriority;
keys[KEY_F(8)] = actionLowerPriority;
keys['I'] = actionInvertSortOrder;
keys[KEY_F(6)] = actionExpandCollapseOrSortColumn;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys['<'] = actionSetSortColumn;
keys[','] = actionSetSortColumn;
keys['>'] = actionSetSortColumn;
keys['.'] = actionSetSortColumn;
keys[KEY_F(10)] = actionQuit;
keys['q'] = actionQuit;
keys['a'] = actionSetAffinity;
keys[KEY_F(9)] = actionKill;
keys['k'] = actionKill;
keys[KEY_RECLICK] = actionExpandOrCollapse;
keys['+'] = actionExpandOrCollapse;
keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse;
keys['\177'] = actionCollapseIntoParent;
keys['u'] = actionFilterByUser;
keys['F'] = Action_follow;
keys['S'] = actionSetup;
keys['C'] = actionSetup;
keys[KEY_F(2)] = actionSetup;
keys['l'] = actionLsof;
keys['s'] = actionStrace;
keys[' '] = actionTag;
keys['\014'] = actionRedraw; // Ctrl+L
keys[KEY_F(1)] = actionHelp;
keys['h'] = actionHelp;
keys['+'] = actionExpandOrCollapse;
keys[','] = actionSetSortColumn;
keys['-'] = actionExpandOrCollapse;
keys['.'] = actionSetSortColumn;
keys['/'] = actionIncSearch;
keys['<'] = actionSetSortColumn;
keys['='] = actionExpandOrCollapse;
keys['>'] = actionSetSortColumn;
keys['?'] = actionHelp;
keys['C'] = actionSetup;
keys['F'] = Action_follow;
keys['H'] = actionToggleUserlandThreads;
keys['I'] = actionInvertSortOrder;
keys['K'] = actionToggleKernelThreads;
keys['M'] = actionSortByMemory;
keys['N'] = actionSortByPID;
keys['P'] = actionSortByCPU;
keys['S'] = actionSetup;
keys['T'] = actionSortByTime;
keys['U'] = actionUntagAll;
keys['Z'] = actionTogglePauseProcessUpdate;
keys['['] = actionLowerPriority;
keys['\014'] = actionRedraw; // Ctrl+L
keys['\177'] = actionCollapseIntoParent;
keys['\\'] = actionIncFilter;
keys[']'] = actionHigherPriority;
keys['a'] = actionSetAffinity;
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
keys['h'] = actionHelp;
keys['k'] = actionKill;
keys['l'] = actionLsof;
keys['m'] = actionToggleMergedCommand;
keys['p'] = actionToggleProgramPath;
keys['q'] = actionQuit;
keys['s'] = actionStrace;
keys['t'] = actionToggleTreeView;
keys['u'] = actionFilterByUser;
keys['w'] = actionShowCommandScreen;
keys['x'] = actionShowLocks;
keys[KEY_F(1)] = actionHelp;
keys[KEY_F(2)] = actionSetup;
keys[KEY_F(3)] = actionIncSearch;
keys[KEY_F(4)] = actionIncFilter;
keys[KEY_F(5)] = actionToggleTreeView;
keys[KEY_F(6)] = actionSetSortColumn;
keys[KEY_F(7)] = actionHigherPriority;
keys[KEY_F(8)] = actionLowerPriority;
keys[KEY_F(9)] = actionKill;
keys[KEY_F(10)] = actionQuit;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys[KEY_RECLICK] = actionExpandOrCollapse;
}

View File

@ -1,21 +1,24 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Action
#define HEADER_Action
/*
htop - Action.h
(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.
*/
#include "config.h" // IWYU pragma: keep
#include <stdbool.h>
#include <sys/types.h>
#include "IncSet.h"
#include "Settings.h"
#include "Header.h"
#include "UsersTable.h"
#include "ProcessList.h"
#include "Object.h"
#include "Panel.h"
#include "Process.h"
#include "ProcessList.h"
#include "Settings.h"
#include "UsersTable.h"
typedef enum {
HTOP_OK = 0x00,
@ -28,30 +31,30 @@ typedef enum {
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
} Htop_Reaction;
typedef Htop_Reaction (*Htop_Action)();
typedef struct State_ {
Settings* settings;
UsersTable* ut;
ProcessList* pl;
Panel* panel;
Header* header;
bool pauseProcessUpdate;
bool hideProcessSelection;
} State;
static inline bool State_hideFunctionBar(const State* st) {
return st->settings->hideFunctionBar == 2 || (st->settings->hideFunctionBar == 1 && st->hideProcessSelection);
}
extern Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess);
typedef Htop_Reaction (*Htop_Action)(State* st);
// ----------------------------------------
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess);
extern bool Action_setUserOnly(const char* userName, uid_t* userId);
bool Action_setUserOnly(const char* userName, uid_t* userId);
extern Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
// ----------------------------------------
Htop_Reaction Action_follow(State* st);
extern Htop_Reaction Action_follow(State* st);
extern void Action_setBindings(Htop_Action* keys);
void Action_setBindings(Htop_Action* keys);
#endif

View File

@ -2,37 +2,30 @@
htop - Affinity.c
(C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include "Affinity.h"
#include <stdlib.h>
#include "XUtils.h"
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
#if __linux__
#include <hwloc/bitmap.h>
#ifdef __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif
#elif HAVE_LINUX_AFFINITY
#elif defined(HAVE_LINUX_AFFINITY)
#include <sched.h>
#endif
/*{
#include "Process.h"
#include "ProcessList.h"
typedef struct Affinity_ {
ProcessList* pl;
int size;
int used;
int* cpus;
} Affinity;
}*/
Affinity* Affinity_new(ProcessList* pl) {
Affinity* this = xCalloc(1, sizeof(Affinity));
@ -91,17 +84,20 @@ bool Affinity_set(Process* proc, Arg arg) {
return ok;
}
#elif HAVE_LINUX_AFFINITY
#elif defined(HAVE_LINUX_AFFINITY)
Affinity* Affinity_get(Process* proc, ProcessList* pl) {
cpu_set_t cpuset;
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);
for (int i = 0; i < pl->cpuCount; i++) {
if (CPU_ISSET(i, &cpuset))
if (CPU_ISSET(i, &cpuset)) {
Affinity_add(affinity, i);
}
}
return affinity;
}

View File

@ -1,26 +1,29 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Affinity
#define HEADER_Affinity
/*
htop - Affinity.h
(C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#ifdef HAVE_LIBHWLOC
#if __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif
#elif HAVE_LINUX_AFFINITY
#include "config.h" // IWYU pragma: keep
#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
#include "Process.h"
#include "ProcessList.h"
typedef struct Affinity_ {
ProcessList* pl;
@ -29,25 +32,18 @@ typedef struct Affinity_ {
int* cpus;
} Affinity;
Affinity* Affinity_new(ProcessList* pl);
extern Affinity* Affinity_new(ProcessList* pl);
void Affinity_delete(Affinity* this);
extern void Affinity_delete(Affinity* this);
void Affinity_add(Affinity* this, int id);
extern void Affinity_add(Affinity* this, int id);
#if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)
#ifdef HAVE_LIBHWLOC
Affinity* Affinity_get(Process* proc, ProcessList* pl);
extern Affinity* Affinity_get(Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Arg arg);
extern bool Affinity_set(Process* proc, Arg arg);
#elif HAVE_LINUX_AFFINITY
extern Affinity* Affinity_get(Process* proc, ProcessList* pl);
extern bool Affinity_set(Process* proc, Arg arg);
#endif
#endif /* HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY */
#endif

View File

@ -1,33 +1,38 @@
/*
htop - AffinityPanel.c
(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.
*/
#include "AffinityPanel.h"
#include "CRT.h"
#include "config.h" // IWYU pragma: keep
#include "Vector.h"
#include "AffinityPanel.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.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
#include <hwloc.h>
#include <hwloc/bitmap.h>
#endif
/*{
#include "Panel.h"
#include "Affinity.h"
#include "ProcessList.h"
}*/
typedef struct MaskItem_ {
Object super;
const char* text;
const char* indent;
char* text;
char* indent; /* used also as an condition whether this is a tree node */
int value; /* tri-state: 0 - off, 1 - some set, 2 - all set */
int sub_tree; /* tri-state: 0 - no sub-tree, 1 - open sub-tree, 2 - closed sub-tree */
Vector* children;
@ -41,9 +46,8 @@ typedef struct MaskItem_ {
static void MaskItem_delete(Object* cast) {
MaskItem* this = (MaskItem*) cast;
free((void*)this->text);
if (this->indent)
free((void*)this->indent);
free(this->text);
free(this->indent);
Vector_delete(this->children);
#ifdef HAVE_LIBHWLOC
if (this->ownCpuset)
@ -52,29 +56,31 @@ static void MaskItem_delete(Object* cast) {
free(this);
}
static void MaskItem_display(Object* cast, RichString* out) {
MaskItem* this = (MaskItem*)cast;
static void MaskItem_display(const Object* cast, RichString* out) {
const MaskItem* this = (const MaskItem*)cast;
assert (this != NULL);
if (this->value == 2)
RichString_append(out, CRT_colors[CHECK_MARK], CRT_checkStr[CHECK_STR_FULL]);
else if (this->value == 1)
RichString_append(out, CRT_colors[CHECK_MARK], CRT_checkStr[CHECK_STR_PARTIAL]);
else
RichString_append(out, CRT_colors[CHECK_MARK], CRT_checkStr[CHECK_STR_NONE]);
RichString_append(out, CRT_colors[CHECK_TEXT], " ");
if (this->indent)
RichString_append(out, CRT_colors[PROCESS_TREE], this->indent);
if (this->sub_tree) {
RichString_append(out, CRT_colors[PROCESS_TREE],
this->sub_tree == 1
? CRT_collapStr[COLLAP_STR_OPEN]
: CRT_collapStr[COLLAP_STR_CLOSED]);
RichString_append(out, CRT_colors[CHECK_TEXT], " ");
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "[");
if (this->value == 2) {
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
} else if (this->value == 1) {
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "o");
} else {
RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
}
RichString_append(out, CRT_colors[CHECK_TEXT], this->text);
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
if (this->indent) {
RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->indent);
RichString_appendWide(out, CRT_colors[PROCESS_TREE],
this->sub_tree == 2
? CRT_treeStr[TREE_STR_OPEN]
: CRT_treeStr[TREE_STR_SHUT]);
RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
}
RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->text);
}
static ObjectClass MaskItem_class = {
static const ObjectClass MaskItem_class = {
.display = MaskItem_display,
.delete = MaskItem_delete
};
@ -84,7 +90,7 @@ static ObjectClass MaskItem_class = {
static MaskItem* MaskItem_newMask(const char* text, const char* indent, hwloc_bitmap_t cpuset, bool owner) {
MaskItem* this = AllocThis(MaskItem);
this->text = xStrdup(text);
this->indent = xStrdup(indent);
this->indent = xStrdup(indent); /* nonnull for tree node */
this->value = 0;
this->ownCpuset = owner;
this->cpuset = cpuset;
@ -98,7 +104,7 @@ static MaskItem* MaskItem_newMask(const char* text, const char* indent, hwloc_bi
static MaskItem* MaskItem_newSingleton(const char* text, int cpu, bool isSet) {
MaskItem* this = AllocThis(MaskItem);
this->text = xStrdup(text);
this->indent = NULL;
this->indent = NULL; /* not a tree node */
this->sub_tree = 0;
this->children = Vector_new(Class(MaskItem), true, DEFAULT_SIZE);
@ -106,11 +112,10 @@ static MaskItem* MaskItem_newSingleton(const char* text, int cpu, bool isSet) {
this->ownCpuset = true;
this->cpuset = hwloc_bitmap_alloc();
hwloc_bitmap_set(this->cpuset, cpu);
(void)isSet;
#else
this->cpu = cpu;
#endif
this->value = 2 * isSet;
this->value = isSet ? 2 : 0;
return this;
}
@ -168,18 +173,18 @@ static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) {
Panel* super = (Panel*) this;
FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : "");
FunctionBar_draw(super->currentBar, NULL);
int oldSelected = Panel_getSelectedIndex(super);
Panel_prune(super);
#ifdef HAVE_LIBHWLOC
if (this->topoView)
if (this->topoView) {
AffinityPanel_updateTopo(this, this->topoRoot);
else {
for (int i = 0; i < Vector_size(this->cpuids); i++)
} else {
for (int i = 0; i < Vector_size(this->cpuids); i++) {
AffinityPanel_updateItem(this, (MaskItem*) Vector_get(this->cpuids, i));
}
}
#else
Panel_splice(super, this->cpuids);
#endif
@ -212,7 +217,7 @@ static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) {
selected->value = 2;
}
#else
selected->value = 2 * !selected->value; /* toggle between 0 and 2 */
selected->value = selected->value ? 0 : 2; /* toggle between 0 and 2 */
#endif
result = HANDLED;
@ -275,18 +280,20 @@ static MaskItem *AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
indent_buf[0] = '\0';
if (depth > 0) {
for (unsigned i = 1; i < depth; i++) {
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1u << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1U << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
size_t len = strlen(&indent_buf[off]);
off += len, left -= len;
off += len;
left -= len;
}
xSnprintf(&indent_buf[off], left, "%s%s ",
obj->next_sibling ? CRT_treeStr[TREE_STR_RTEE] : CRT_treeStr[TREE_STR_BEND],
CRT_treeStr[TREE_STR_HORZ]);
size_t len = strlen(&indent_buf[off]);
off += len, left -= len;
xSnprintf(&indent_buf[off], left, "%s",
obj->next_sibling ? CRT_treeStr[TREE_STR_RTEE] : CRT_treeStr[TREE_STR_BEND]);
// Uncomment when further appending to indent_buf
//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);
if (parent)
@ -298,14 +305,16 @@ static MaskItem *AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
hwloc_bitmap_and(result, obj->complete_cpuset, this->workCpuset);
int weight = hwloc_bitmap_weight(result);
hwloc_bitmap_free(result);
if (weight == 0 || weight == (hwloc_bitmap_weight(this->workCpuset) + hwloc_bitmap_weight(obj->complete_cpuset)))
if (weight == 0 || weight == (hwloc_bitmap_weight(this->workCpuset) + hwloc_bitmap_weight(obj->complete_cpuset))) {
item->sub_tree = 2;
}
}
/* "[x] " + "|- " * depth + ("[+] ")? + name */
unsigned width = (CRT_utf8 ? 2 : 4) + 3 * depth + (item->sub_tree ? (CRT_utf8 ? 2 : 4) : 0) + strlen(buf);
if (width > this->width)
/* "[x] " + "|- " * depth + ("- ")?(if root node) + name */
unsigned width = 4 + 3 * depth + (2 * !depth) + strlen(buf);
if (width > this->width) {
this->width = width;
}
return item;
}
@ -313,19 +322,21 @@ static MaskItem *AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent);
if (obj->next_sibling) {
indent |= (1u << obj->depth);
indent |= (1U << obj->depth);
} 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);
}
return parent == NULL ? item : NULL;
}
#endif
PanelClass AffinityPanel_class = {
const PanelClass AffinityPanel_class = {
.super = {
.extends = Class(Panel),
.delete = AffinityPanel_delete
@ -375,9 +386,10 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
for (int i = 0; i < pl->cpuCount; i++) {
char number[16];
xSnprintf(number, 9, "CPU %d", Settings_cpuId(pl->settings, i));
unsigned width = 4 + strlen(number);
if (width > this->width)
this->width = width;
unsigned cpu_width = 4 + strlen(number);
if (cpu_width > this->width) {
this->width = cpu_width;
}
bool isSet = false;
if (curCpu < affinity->used && affinity->cpus[curCpu] == i) {
@ -396,8 +408,9 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(pl->topology), 0, NULL);
#endif
if (width)
if (width) {
*width = this->width;
}
AffinityPanel_update(this, false);
@ -416,9 +429,10 @@ Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) {
#else
for (int i = 0; i < this->pl->cpuCount; i++) {
MaskItem* item = (MaskItem*)Vector_get(this->cpuids, i);
if (item->value)
if (item->value) {
Affinity_add(affinity, item->cpu);
}
}
#endif
return affinity;

View File

@ -1,40 +1,20 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_AffinityPanel
#define HEADER_AffinityPanel
/*
htop - AffinityPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#ifdef HAVE_LIBHWLOC
#endif
#include "Panel.h"
#include "Affinity.h"
#include "ProcessList.h"
extern const PanelClass AffinityPanel_class;
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width);
#ifdef HAVE_LIBHWLOC
#endif
#ifdef HAVE_LIBHWLOC
#endif
#ifdef HAVE_LIBHWLOC
#endif
extern PanelClass AffinityPanel_class;
extern Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width);
extern Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl);
Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl);
#endif

View File

@ -1,30 +1,25 @@
/*
htop - AvailableColumnsPanel.c
(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.
*/
#include "AvailableColumnsPanel.h"
#include "Platform.h"
#include "Header.h"
#include "ColumnsPanel.h"
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
/*{
#include "Panel.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"
typedef struct AvailableColumnsPanel_ {
Panel super;
Panel* columns;
} AvailableColumnsPanel;
}*/
static const char* const AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL};
@ -37,7 +32,6 @@ static void AvailableColumnsPanel_delete(Object* object) {
static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
AvailableColumnsPanel* this = (AvailableColumnsPanel*) super;
int key = ((ListItem*) Panel_getSelected(super))->key;
HandlerResult result = IGNORED;
switch(ch) {
@ -45,6 +39,11 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
case KEY_ENTER:
case KEY_F(5):
{
const ListItem* selected = (ListItem*) Panel_getSelected(super);
if (!selected)
break;
int key = selected->key;
int at = Panel_getSelectedIndex(this->columns);
Panel_insert(this->columns, at, (Object*) ListItem_new(Process_fields[key].name, key));
Panel_setSelected(this->columns, at+1);
@ -54,7 +53,7 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
}
default:
{
if (ch < 255 && isalpha(ch))
if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
result = Panel_selectByTyping(super, ch);
break;
}
@ -62,7 +61,7 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
return result;
}
PanelClass AvailableColumnsPanel_class = {
const PanelClass AvailableColumnsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = AvailableColumnsPanel_delete
@ -78,7 +77,7 @@ AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
Panel_setHeader(super, "Available Columns");
for (int i = 1; i < Platform_numberOfFields; i++) {
for (int i = 1; i < LAST_PROCESSFIELD; i++) {
if (i != COMM && Process_fields[i].description) {
char description[256];
xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);

View File

@ -1,11 +1,9 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_AvailableColumnsPanel
#define HEADER_AvailableColumnsPanel
/*
htop - AvailableColumnsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@ -16,9 +14,8 @@ typedef struct AvailableColumnsPanel_ {
Panel* columns;
} AvailableColumnsPanel;
extern const PanelClass AvailableColumnsPanel_class;
extern PanelClass AvailableColumnsPanel_class;
extern AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns);
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns);
#endif

View File

@ -1,38 +1,27 @@
/*
htop - AvailableMetersPanel.c
(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.
*/
#include "AvailableMetersPanel.h"
#include "MetersPanel.h"
#include "CPUMeter.h"
#include "Header.h"
#include "ListItem.h"
#include "Platform.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
/*{
#include "Settings.h"
#include "Panel.h"
#include "ScreenManager.h"
#include "ProcessList.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"
typedef struct AvailableMetersPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
Header* header;
Panel* leftPanel;
Panel* rightPanel;
} AvailableMetersPanel;
}*/
static void AvailableMetersPanel_delete(Object* object) {
Panel* super = (Panel*) object;
@ -41,19 +30,21 @@ static void AvailableMetersPanel_delete(Object* object) {
free(this);
}
static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, MeterClass* type, int param, int column) {
Meter* meter = (Meter*) Header_addMeterByClass(header, type, param, column);
static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, const MeterClass* type, int param, int column) {
Meter* meter = Header_addMeterByClass(header, type, param, column);
Panel_add(panel, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(panel, Panel_size(panel) - 1);
MetersPanel_setMoving((MetersPanel*)panel, true);
FunctionBar_draw(panel->currentBar, NULL);
}
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
AvailableMetersPanel* this = (AvailableMetersPanel*) super;
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 type = selected->key >> 16;
HandlerResult result = IGNORED;
@ -91,7 +82,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
return result;
}
PanelClass AvailableMetersPanel_class = {
const PanelClass AvailableMetersPanel_class = {
.super = {
.extends = Class(Panel),
.delete = AvailableMetersPanel_delete
@ -115,19 +106,19 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
// Platform_meterTypes[0] should be always (&CPUMeter_class), which we will
// handle separately in the code below.
for (int i = 1; Platform_meterTypes[i]; i++) {
MeterClass* type = Platform_meterTypes[i];
const MeterClass* type = Platform_meterTypes[i];
assert(type != &CPUMeter_class);
const char* label = type->description ? type->description : type->uiName;
Panel_add(super, (Object*) ListItem_new(label, i << 16));
}
// Handle (&CPUMeter_class)
MeterClass* type = &CPUMeter_class;
const MeterClass* type = &CPUMeter_class;
int cpus = pl->cpuCount;
if (cpus > 1) {
Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (int i = 1; i <= cpus; i++) {
char buffer[50];
xSnprintf(buffer, 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));
}
} else {

View File

@ -1,18 +1,17 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_AvailableMetersPanel
#define HEADER_AvailableMetersPanel
/*
htop - AvailableMetersPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Settings.h"
#include "Header.h"
#include "Panel.h"
#include "ScreenManager.h"
#include "ProcessList.h"
#include "ScreenManager.h"
#include "Settings.h"
typedef struct AvailableMetersPanel_ {
Panel super;
@ -24,9 +23,8 @@ typedef struct AvailableMetersPanel_ {
Panel* rightPanel;
} AvailableMetersPanel;
extern const PanelClass AvailableMetersPanel_class;
extern PanelClass AvailableMetersPanel_class;
extern 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);
#endif

View File

@ -1,7 +1,7 @@
/*
htop - BatteryMeter.c
(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.
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
@ -9,66 +9,50 @@ This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
#include "BatteryMeter.h"
#include "Battery.h"
#include "ProcessList.h"
#include <math.h>
#include "CRT.h"
#include "StringUtils.h"
#include "Object.h"
#include "Platform.h"
#include "XUtils.h"
#include <string.h>
#include <stdlib.h>
/*{
#include "Meter.h"
typedef enum ACPresence_ {
AC_ABSENT,
AC_PRESENT,
AC_ERROR
} ACPresence;
}*/
int BatteryMeter_attributes[] = {
static const int BatteryMeter_attributes[] = {
BATTERY
};
static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) {
static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) {
ACPresence isOnAC;
double percent;
Battery_getData(&percent, &isOnAC);
Platform_getBattery(&percent, &isOnAC);
if (percent == -1) {
this->values[0] = 0;
xSnprintf(buffer, len, "n/a");
if (isnan(percent)) {
this->values[0] = NAN;
xSnprintf(buffer, len, "N/A");
return;
}
this->values[0] = percent;
const char *onAcText, *onBatteryText, *unknownText;
unknownText = "%.1f%%";
if (this->mode == TEXT_METERMODE) {
onAcText = "%.1f%% (Running on A/C)";
onBatteryText = "%.1f%% (Running on battery)";
} else {
onAcText = "%.1f%%(A/C)";
onBatteryText = "%.1f%%(bat)";
const char* text;
switch (isOnAC) {
case AC_PRESENT:
text = this->mode == TEXT_METERMODE ? " (Running on A/C)" : "(A/C)";
break;
case AC_ABSENT:
text = this->mode == TEXT_METERMODE ? " (Running on battery)" : "(bat)";
break;
case AC_ERROR:
default:
text = "";
break;
}
if (isOnAC == AC_PRESENT) {
xSnprintf(buffer, len, onAcText, percent);
} else if (isOnAC == AC_ABSENT) {
xSnprintf(buffer, len, onBatteryText, percent);
} else {
xSnprintf(buffer, len, unknownText, percent);
xSnprintf(buffer, len, "%.1f%%%s", percent, text);
}
return;
}
MeterClass BatteryMeter_class = {
const MeterClass BatteryMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete

View File

@ -1,11 +1,9 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_BatteryMeter
#define HEADER_BatteryMeter
/*
htop - BatteryMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
@ -19,8 +17,6 @@ typedef enum ACPresence_ {
AC_ERROR
} ACPresence;
extern int BatteryMeter_attributes[];
extern MeterClass BatteryMeter_class;
extern const MeterClass BatteryMeter_class;
#endif

View File

@ -32,3 +32,14 @@ Feature Requests
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+)
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,149 +1,175 @@
/*
htop - CPUMeter.c
(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.
*/
#include "config.h" // IWYU pragma: keep
#include "CPUMeter.h"
#include "CRT.h"
#include "Settings.h"
#include "Platform.h"
#include <assert.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/*{
#include "Meter.h"
#include "CRT.h"
#include "Object.h"
#include "Platform.h"
#include "ProcessList.h"
#include "RichString.h"
#include "Settings.h"
#include "XUtils.h"
typedef enum {
CPU_METER_NICE = 0,
CPU_METER_NORMAL = 1,
CPU_METER_KERNEL = 2,
CPU_METER_IRQ = 3,
CPU_METER_SOFTIRQ = 4,
CPU_METER_STEAL = 5,
CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7,
CPU_METER_FREQUENCY = 8,
CPU_METER_ITEMCOUNT = 9, // number of entries in this enum
} CPUMeterValues;
}*/
int CPUMeter_attributes[] = {
CPU_NICE, CPU_NORMAL, CPU_SYSTEM, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, CPU_IOWAIT
static const int CPUMeter_attributes[] = {
CPU_NICE,
CPU_NORMAL,
CPU_SYSTEM,
CPU_IRQ,
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
CPU_IOWAIT
};
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
typedef struct CPUMeterData_ {
int cpus;
Meter** meters;
} CPUMeterData;
static void CPUMeter_init(Meter* this) {
int cpu = this->param;
if (this->pl->cpuCount > 1) {
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);
}
if (this->param == 0)
Meter_setCaption(this, "Avg");
}
static void CPUMeter_updateValues(Meter* this, char* buffer, int size) {
static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
int cpu = this->param;
if (cpu > this->pl->cpuCount) {
xSnprintf(buffer, size, "absent");
for (uint8_t i = 0; i < this->curItems; i++)
this->values[i] = 0;
return;
}
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);
if (this->pl->settings->showCPUUsage) {
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent);
}
if (this->pl->settings->showCPUFrequency) {
/* Initial frequency is in MHz. Emit it as GHz if it's larger than 1000MHz */
double cpuFrequency = this->values[CPU_METER_FREQUENCY];
char unit = 'M';
char cpuFrequencyBuffer[16];
if (cpuFrequency < 0) {
if (isnan(cpuFrequency)) {
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A");
} else {
if (cpuFrequency > 1000) {
cpuFrequency /= 1000;
unit = 'G';
}
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%.3f%cHz", cpuFrequency, unit);
}
if (this->pl->settings->showCPUUsage) {
xSnprintf(buffer, size, "%5.1f%% %s", percent, cpuFrequencyBuffer);
} else {
xSnprintf(buffer, size, "%s", cpuFrequencyBuffer);
}
} else if (this->pl->settings->showCPUUsage) {
xSnprintf(buffer, size, "%5.1f%%", percent);
} else if (size > 0) {
buffer[0] = '\0';
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%4uMHz", (unsigned)cpuFrequency);
}
}
static void CPUMeter_display(Object* cast, RichString* out) {
#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(const Object* cast, RichString* out) {
char buffer[50];
Meter* this = (Meter*)cast;
const Meter* this = (const Meter*)cast;
RichString_prune(out);
if (this->param > this->pl->cpuCount) {
RichString_append(out, CRT_colors[METER_TEXT], "absent");
RichString_appendAscii(out, CRT_colors[METER_TEXT], "absent");
return;
}
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]);
RichString_append(out, CRT_colors[METER_TEXT], ":");
RichString_append(out, CRT_colors[CPU_NORMAL], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], ":");
RichString_appendAscii(out, CRT_colors[CPU_NORMAL], buffer);
if (this->pl->settings->detailedCPUTime) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sy:");
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sy:");
RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "ni:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "ni:");
RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "hi:");
RichString_append(out, CRT_colors[CPU_IRQ], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "hi:");
RichString_appendAscii(out, CRT_colors[CPU_IRQ], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "si:");
RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer);
if (this->values[CPU_METER_STEAL]) {
RichString_appendAscii(out, CRT_colors[METER_TEXT], "si:");
RichString_appendAscii(out, CRT_colors[CPU_SOFTIRQ], buffer);
if (!isnan(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[CPU_STEAL], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "st:");
RichString_appendAscii(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]);
RichString_append(out, CRT_colors[METER_TEXT], "gu:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "gu:");
RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer);
}
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
RichString_append(out, CRT_colors[METER_TEXT], "wa:");
RichString_append(out, CRT_colors[CPU_IOWAIT], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "wa:");
RichString_appendAscii(out, CRT_colors[CPU_IOWAIT], buffer);
} else {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sys:");
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sys:");
RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "low:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
if (this->values[CPU_METER_IRQ]) {
RichString_appendAscii(out, CRT_colors[METER_TEXT], "low:");
RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
if (!isnan(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[CPU_GUEST], buffer);
}
RichString_appendAscii(out, CRT_colors[METER_TEXT], "vir:");
RichString_appendAscii(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_appendAscii(out, CRT_colors[METER_TEXT], "temp:");
RichString_appendWide(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer);
}
#endif
}
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]) {
default:
case 'A': // All
@ -161,44 +187,34 @@ static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) {
}
}
static int MapClassnameToColumncount(Meter* this){
if (strchr(Meter_name(this), '4'))
return 4;
else if (strchr(Meter_name(this), '2'))
return 2;
else
return 1;
}
static void AllCPUsMeter_init(Meter* this) {
static void CPUMeterCommonInit(Meter* this, int ncol) {
int cpus = this->pl->cpuCount;
if (!this->drawData)
this->drawData = xCalloc(cpus, sizeof(Meter*));
Meter** meters = (Meter**) this->drawData;
CPUMeterData* data = this->meterData;
if (!data) {
data = this->meterData = xMalloc(sizeof(CPUMeterData));
data->cpus = cpus;
data->meters = xCalloc(cpus, sizeof(Meter*));
}
Meter** meters = data->meters;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; 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]);
}
if (this->mode == 0)
this->mode = BAR_METERMODE;
int h = Meter_modes[this->mode]->h;
int ncol = MapClassnameToColumncount(this);
this->h = h * ((count + ncol - 1) / ncol);
}
static void AllCPUsMeter_done(Meter* this) {
Meter** meters = (Meter**) this->drawData;
int start, count;
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;
static void CPUMeterCommonUpdateMode(Meter* this, int mode, int ncol) {
CPUMeterData* data = this->meterData;
Meter** meters = data->meters;
this->mode = mode;
int h = Meter_modes[mode]->h;
int start, count;
@ -206,43 +222,57 @@ static void AllCPUsMeter_updateMode(Meter* this, int mode) {
for (int i = 0; i < count; i++) {
Meter_setMode(meters[i], mode);
}
int ncol = MapClassnameToColumncount(this);
this->h = h * ((count + ncol - 1) / ncol);
}
static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) {
Meter** meters = (Meter**) this->drawData;
static void AllCPUsMeter_done(Meter* this) {
CPUMeterData* data = this->meterData;
Meter** meters = data->meters;
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;
}
for (int i = 0; i < count; i++)
Meter_delete((Object*)meters[i]);
free(data->meters);
free(data);
}
static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) {
Meter** meters = (Meter**) this->drawData;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++) {
meters[i]->draw(meters[i], x, y, w);
y += meters[i]->h;
}
static void SingleColCPUsMeter_init(Meter* this) {
CPUMeterCommonInit(this, 1);
}
static void MultiColCPUsMeter_draw(Meter* this, int x, int y, int w){
Meter** meters = (Meter**) this->drawData;
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 ncol = MapClassnameToColumncount(this);
int colwidth = (w - ncol) / ncol + 1;
int diff = (w - (colwidth * ncol));
int nrows = (count + ncol - 1) / ncol;
@ -254,7 +284,32 @@ static void MultiColCPUsMeter_draw(Meter* this, int x, int y, int w){
}
}
MeterClass CPUMeter_class = {
static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) {
CPUMeterCommonDraw(this, x, y, w, 2);
}
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) {
CPUMeterData* data = this->meterData;
Meter** meters = data->meters;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++) {
meters[i]->draw(meters[i], x, y, w);
y += meters[i]->h;
}
}
const MeterClass CPUMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -271,7 +326,7 @@ MeterClass CPUMeter_class = {
.init = CPUMeter_init
};
MeterClass AllCPUsMeter_class = {
const MeterClass AllCPUsMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -285,12 +340,12 @@ MeterClass AllCPUsMeter_class = {
.description = "CPUs (1/1): all CPUs",
.caption = "CPU",
.draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.init = SingleColCPUsMeter_init,
.updateMode = SingleColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass AllCPUs2Meter_class = {
const MeterClass AllCPUs2Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -304,12 +359,12 @@ MeterClass AllCPUs2Meter_class = {
.description = "CPUs (1&2/2): all CPUs in 2 shorter columns",
.caption = "CPU",
.draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.init = DualColCPUsMeter_init,
.updateMode = DualColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass LeftCPUsMeter_class = {
const MeterClass LeftCPUsMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -323,12 +378,12 @@ MeterClass LeftCPUsMeter_class = {
.description = "CPUs (1/2): first half of list",
.caption = "CPU",
.draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.init = SingleColCPUsMeter_init,
.updateMode = SingleColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass RightCPUsMeter_class = {
const MeterClass RightCPUsMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -342,12 +397,12 @@ MeterClass RightCPUsMeter_class = {
.description = "CPUs (2/2): second half of list",
.caption = "CPU",
.draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.init = SingleColCPUsMeter_init,
.updateMode = SingleColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass LeftCPUs2Meter_class = {
const MeterClass LeftCPUs2Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -361,12 +416,12 @@ MeterClass LeftCPUs2Meter_class = {
.description = "CPUs (1&2/4): first half in 2 shorter columns",
.caption = "CPU",
.draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.init = DualColCPUsMeter_init,
.updateMode = DualColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass RightCPUs2Meter_class = {
const MeterClass RightCPUs2Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -380,12 +435,12 @@ MeterClass RightCPUs2Meter_class = {
.description = "CPUs (3&4/4): second half in 2 shorter columns",
.caption = "CPU",
.draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.init = DualColCPUsMeter_init,
.updateMode = DualColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass AllCPUs4Meter_class = {
const MeterClass AllCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -398,13 +453,13 @@ MeterClass AllCPUs4Meter_class = {
.uiName = "CPUs (1&2&3&4/4)",
.description = "CPUs (1&2&3&4/4): all CPUs in 4 shorter columns",
.caption = "CPU",
.draw = MultiColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.draw = QuadColCPUsMeter_draw,
.init = QuadColCPUsMeter_init,
.updateMode = QuadColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass LeftCPUs4Meter_class = {
const MeterClass LeftCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -417,13 +472,13 @@ MeterClass LeftCPUs4Meter_class = {
.uiName = "CPUs (1-4/8)",
.description = "CPUs (1-4/8): first half in 4 shorter columns",
.caption = "CPU",
.draw = MultiColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.draw = QuadColCPUsMeter_draw,
.init = QuadColCPUsMeter_init,
.updateMode = QuadColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
MeterClass RightCPUs4Meter_class = {
const MeterClass RightCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -436,8 +491,65 @@ MeterClass RightCPUs4Meter_class = {
.uiName = "CPUs (5-8/8)",
.description = "CPUs (5-8/8): second half in 4 shorter columns",
.caption = "CPU",
.draw = MultiColCPUsMeter_draw,
.init = AllCPUsMeter_init,
.updateMode = AllCPUsMeter_updateMode,
.draw = QuadColCPUsMeter_draw,
.init = QuadColCPUsMeter_init,
.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
};

View File

@ -1,11 +1,9 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_CPUMeter
#define HEADER_CPUMeter
/*
htop - CPUMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@ -21,37 +19,34 @@ typedef enum {
CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7,
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;
extern const MeterClass CPUMeter_class;
extern int CPUMeter_attributes[];
extern const MeterClass AllCPUsMeter_class;
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
extern const MeterClass AllCPUs2Meter_class;
extern MeterClass CPUMeter_class;
extern const MeterClass LeftCPUsMeter_class;
extern MeterClass AllCPUsMeter_class;
extern const MeterClass RightCPUsMeter_class;
extern MeterClass AllCPUs2Meter_class;
extern const MeterClass LeftCPUs2Meter_class;
extern MeterClass LeftCPUsMeter_class;
extern const MeterClass RightCPUs2Meter_class;
extern MeterClass RightCPUsMeter_class;
extern const MeterClass AllCPUs4Meter_class;
extern MeterClass LeftCPUs2Meter_class;
extern const MeterClass LeftCPUs4Meter_class;
extern MeterClass RightCPUs2Meter_class;
extern const MeterClass RightCPUs4Meter_class;
extern MeterClass AllCPUs4Meter_class;
extern const MeterClass AllCPUs8Meter_class;
extern MeterClass LeftCPUs4Meter_class;
extern const MeterClass LeftCPUs8Meter_class;
extern MeterClass RightCPUs4Meter_class;
extern const MeterClass RightCPUs8Meter_class;
#endif

568
CRT.c
View File

@ -1,29 +1,31 @@
/*
htop - CRT.c
(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.
*/
#include "config.h"
#include "config.h" // IWYU pragma: keep
#include "CRT.h"
#include "StringUtils.h"
#include "RichString.h"
#include <stdio.h>
#include <errno.h>
#include <langinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <langinfo.h>
#if HAVE_SETUID_ENABLED
#include <unistd.h>
#include <sys/types.h>
#include "ProvideCurses.h"
#include "XUtils.h"
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
#define ColorIndex(i,j) ((7-i)*8+j)
#define ColorIndex(i,j) ((7-(i))*8+(j))
#define ColorPair(i,j) COLOR_PAIR(ColorIndex(i,j))
@ -39,189 +41,63 @@ in the source distribution for its full text.
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
#define KEY_RECLICK KEY_F(22)
#define ColorPairWhiteDefault ColorPair(Red, Red)
#define ColorIndexWhiteDefault ColorIndex(Red, Red)
//#link curses
/*{
#include <stdbool.h>
typedef enum TreeStr_ {
TREE_STR_HORZ,
TREE_STR_VERT,
TREE_STR_RTEE,
TREE_STR_BEND,
TREE_STR_TEND,
TREE_STR_OPEN,
TREE_STR_SHUT,
TREE_STR_COUNT
} TreeStr;
typedef enum CheckStr_ {
CHECK_STR_NONE,
CHECK_STR_PARTIAL,
CHECK_STR_FULL,
CHECK_STR_COUNT
} CheckStr;
typedef enum CollapStr_ {
COLLAP_STR_OPEN,
COLLAP_STR_CLOSED,
COLLAP_STR_COUNT
} CollapStr;
typedef enum ColorSchemes_ {
COLORSCHEME_DEFAULT = 0,
COLORSCHEME_MONOCHROME = 1,
COLORSCHEME_BLACKONWHITE = 2,
COLORSCHEME_LIGHTTERMINAL = 3,
COLORSCHEME_MIDNIGHT = 4,
COLORSCHEME_BLACKNIGHT = 5,
COLORSCHEME_BROKENGRAY = 6,
LAST_COLORSCHEME = 7,
} ColorSchemes;
typedef enum ColorElements_ {
RESET_COLOR,
DEFAULT_COLOR,
FUNCTION_BAR,
FUNCTION_KEY,
FAILED_SEARCH,
PANEL_HEADER_FOCUS,
PANEL_HEADER_UNFOCUS,
PANEL_SELECTION_FOCUS,
PANEL_SELECTION_FOLLOW,
PANEL_SELECTION_UNFOCUS,
LARGE_NUMBER,
METER_TEXT,
METER_VALUE,
LED_COLOR,
UPTIME,
BATTERY,
TASKS_RUNNING,
SWAP,
PROCESS,
PROCESS_SHADOW,
PROCESS_TAG,
PROCESS_MEGABYTES,
PROCESS_TREE,
PROCESS_R_STATE,
PROCESS_D_STATE,
PROCESS_BASENAME,
PROCESS_HIGH_PRIORITY,
PROCESS_LOW_PRIORITY,
PROCESS_THREAD,
PROCESS_THREAD_BASENAME,
BAR_BORDER,
BAR_SHADOW,
GRAPH_1,
GRAPH_2,
MEMORY_USED,
MEMORY_BUFFERS,
MEMORY_BUFFERS_TEXT,
MEMORY_CACHE,
LOAD,
LOAD_AVERAGE_FIFTEEN,
LOAD_AVERAGE_FIVE,
LOAD_AVERAGE_ONE,
CHECK_MARK,
CHECK_TEXT,
CLOCK,
HELP_BOLD,
HOSTNAME,
CPU_NICE,
CPU_NICE_TEXT,
CPU_NORMAL,
CPU_SYSTEM,
CPU_IOWAIT,
CPU_IRQ,
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
PRESSURE_STALL_TEN,
PRESSURE_STALL_SIXTY,
PRESSURE_STALL_THREEHUNDRED,
ZFS_MFU,
ZFS_MRU,
ZFS_ANON,
ZFS_HEADER,
ZFS_OTHER,
ZFS_COMPRESSED,
ZFS_RATIO,
LAST_COLORELEMENT
} ColorElements;
extern void CRT_fatalError(const char* note) __attribute__ ((noreturn));
extern void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
}*/
const char *CRT_treeStrAscii[TREE_STR_COUNT] = {
"-", // TREE_STR_HORZ
"|", // TREE_STR_VERT
"`", // TREE_STR_RTEE
"`", // TREE_STR_BEND
",", // TREE_STR_TEND
"+", // TREE_STR_OPEN
"-", // TREE_STR_SHUT
};
const char *CRT_checkStrAscii[CHECK_STR_COUNT] = {
"[ ]", // CHECK_STR_NONE
"[o]", // CHECK_STR_PARTIAL
"[x]", // CHECK_STR_FULL
};
const char *CRT_collapStrAscii[COLLAP_STR_COUNT] = {
"[-]", // COLLAP_STR_OPEN
"[+]", // COLLAP_STR_CLOSED
static const char* const CRT_treeStrAscii[LAST_TREE_STR] = {
[TREE_STR_VERT] = "|",
[TREE_STR_RTEE] = "`",
[TREE_STR_BEND] = "`",
[TREE_STR_TEND] = ",",
[TREE_STR_OPEN] = "+",
[TREE_STR_SHUT] = "-",
[TREE_STR_ASC] = "+",
[TREE_STR_DESC] = "-",
};
#ifdef HAVE_LIBNCURSESW
const char *CRT_treeStrUtf8[TREE_STR_COUNT] = {
"\xe2\x94\x80", // TREE_STR_HORZ ─
"\xe2\x94\x82", // TREE_STR_VERT │
"\xe2\x94\x9c", // TREE_STR_RTEE ├
"\xe2\x94\x94", // TREE_STR_BEND └
"\xe2\x94\x8c", // TREE_STR_TEND ┌
"+", // TREE_STR_OPEN +
"\xe2\x94\x80", // TREE_STR_SHUT ─
static const char* const CRT_treeStrUtf8[LAST_TREE_STR] = {
[TREE_STR_VERT] = "\xe2\x94\x82", //
[TREE_STR_RTEE] = "\xe2\x94\x9c", //
[TREE_STR_BEND] = "\xe2\x94\x94", //
[TREE_STR_TEND] = "\xe2\x94\x8c", //
[TREE_STR_OPEN] = "+", // +, TODO use 🮯 'BOX DRAWINGS LIGHT HORIZONTAL
// WITH VERTICAL STROKE' (U+1FBAF, "\xf0\x9f\xae\xaf") when
// Unicode 13 is common
[TREE_STR_SHUT] = "\xe2\x94\x80", // ─
[TREE_STR_ASC] = "\xe2\x96\xb3", // △
[TREE_STR_DESC] = "\xe2\x96\xbd", // ▽
};
const char *CRT_checkStrUtf8[CHECK_STR_COUNT] = {
"\xe2\x98\x90", // CHECK_STR_NONE ☐
"\xe2\x98\x92", // CHECK_STR_PARTIAL ☒
"\xe2\x98\x91", // CHECK_STR_FULL ☑
};
const char *CRT_collapStrUtf8[COLLAP_STR_COUNT] = {
"\xe2\x8a\x9f", // COLLAP_STR_OPEN ⊟
"\xe2\x8a\x9e", // COLLAP_STR_CLOSED ⊞
};
#endif
bool CRT_utf8 = false;
const char **CRT_treeStr = CRT_treeStrAscii;
#endif
const char **CRT_checkStr = CRT_checkStrAscii;
const char* const* CRT_treeStr = CRT_treeStrAscii;
const char **CRT_collapStr = CRT_collapStrAscii;
static const int* CRT_delay;
static bool CRT_hasColors;
const char* CRT_degreeSign;
int CRT_delay = 0;
static const char* initDegreeSign(void) {
#ifdef HAVE_LIBNCURSESW
if (CRT_utf8)
return "\xc2\xb0";
int* CRT_colors;
static char buffer[4];
// this might fail if the current locale does not support wide characters
int r = snprintf(buffer, sizeof(buffer), "%lc", 176);
if (r > 0)
return buffer;
#endif
int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
return "";
}
const int* CRT_colors;
static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[COLORSCHEME_DEFAULT] = {
[RESET_COLOR] = ColorPair(White, Black),
[DEFAULT_COLOR] = ColorPair(White, Black),
@ -233,25 +109,38 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
[PANEL_SELECTION_UNFOCUS] = ColorPair(Black, White),
[FAILED_SEARCH] = ColorPair(Red, Cyan),
[FAILED_READ] = A_BOLD | ColorPair(Red, Black),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = A_BOLD | ColorPair(Cyan, Black),
[BATTERY] = A_BOLD | ColorPair(Cyan, Black),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
[METER_TEXT] = ColorPair(Cyan, Black),
[METER_VALUE] = A_BOLD | ColorPair(Cyan, Black),
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
[METER_VALUE_IOREAD] = ColorPair(Green, Black),
[METER_VALUE_IOWRITE] = ColorPair(Blue, Black),
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black),
[METER_VALUE_OK] = ColorPair(Green, Black),
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
[LED_COLOR] = ColorPair(Green, Black),
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black),
[PROCESS] = A_NORMAL,
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Black),
[PROCESS_MEGABYTES] = ColorPair(Cyan, Black),
[PROCESS_GIGABYTES] = ColorPair(Green, Black),
[PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Black),
[PROCESS_TREE] = ColorPair(Cyan, Black),
[PROCESS_R_STATE] = ColorPair(Green, Black),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
[PROCESS_NEW] = ColorPair(Black, Green),
[PROCESS_TOMB] = ColorPair(Black, Red),
[PROCESS_THREAD] = ColorPair(Green, Black),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Black),
[PROCESS_COMM] = ColorPair(Magenta, Black),
[PROCESS_THREAD_COMM] = ColorPair(Blue, Black),
[BAR_BORDER] = A_BOLD,
[BAR_SHADOW] = A_BOLD | ColorPairGrayBlack,
[SWAP] = ColorPair(Red, Black),
@ -267,7 +156,10 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = A_BOLD,
[HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black),
[CLOCK] = A_BOLD,
[CHECK_MARK] = A_BOLD | ColorPair(Cyan,Black),
[DATE] = A_BOLD,
[DATETIME] = A_BOLD,
[CHECK_BOX] = ColorPair(Cyan, Black),
[CHECK_MARK] = A_BOLD,
[CHECK_TEXT] = A_NORMAL,
[HOSTNAME] = A_BOLD,
[CPU_NICE] = ColorPair(Blue, Black),
@ -289,6 +181,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_OTHER] = ColorPair(Magenta, Black),
[ZFS_COMPRESSED] = ColorPair(Blue, Black),
[ZFS_RATIO] = ColorPair(Magenta, Black),
[ZRAM] = ColorPair(Yellow, Black),
},
[COLORSCHEME_MONOCHROME] = {
[RESET_COLOR] = A_NORMAL,
@ -301,25 +194,38 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PANEL_SELECTION_FOLLOW] = A_REVERSE,
[PANEL_SELECTION_UNFOCUS] = A_BOLD,
[FAILED_SEARCH] = A_REVERSE | A_BOLD,
[FAILED_READ] = A_BOLD,
[PAUSED] = A_BOLD | A_REVERSE,
[UPTIME] = A_BOLD,
[BATTERY] = A_BOLD,
[LARGE_NUMBER] = A_BOLD,
[METER_TEXT] = A_NORMAL,
[METER_VALUE] = A_BOLD,
[METER_VALUE_ERROR] = A_BOLD,
[METER_VALUE_IOREAD] = A_NORMAL,
[METER_VALUE_IOWRITE] = A_NORMAL,
[METER_VALUE_NOTICE] = A_BOLD,
[METER_VALUE_OK] = A_NORMAL,
[METER_VALUE_WARN] = A_BOLD,
[LED_COLOR] = A_NORMAL,
[TASKS_RUNNING] = A_BOLD,
[PROCESS] = A_NORMAL,
[PROCESS_SHADOW] = A_DIM,
[PROCESS_TAG] = A_BOLD,
[PROCESS_MEGABYTES] = A_BOLD,
[PROCESS_GIGABYTES] = A_BOLD,
[PROCESS_BASENAME] = A_BOLD,
[PROCESS_TREE] = A_BOLD,
[PROCESS_R_STATE] = A_BOLD,
[PROCESS_D_STATE] = A_BOLD,
[PROCESS_HIGH_PRIORITY] = A_BOLD,
[PROCESS_LOW_PRIORITY] = A_DIM,
[PROCESS_NEW] = A_BOLD,
[PROCESS_TOMB] = A_DIM,
[PROCESS_THREAD] = A_BOLD,
[PROCESS_THREAD_BASENAME] = A_REVERSE,
[PROCESS_COMM] = A_BOLD,
[PROCESS_THREAD_COMM] = A_REVERSE,
[BAR_BORDER] = A_BOLD,
[BAR_SHADOW] = A_DIM,
[SWAP] = A_BOLD,
@ -335,7 +241,10 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = A_BOLD,
[HELP_BOLD] = A_BOLD,
[CLOCK] = A_BOLD,
[CHECK_MARK] = A_BOLD,
[DATE] = A_BOLD,
[DATETIME] = A_BOLD,
[CHECK_BOX] = A_BOLD,
[CHECK_MARK] = A_NORMAL,
[CHECK_TEXT] = A_NORMAL,
[HOSTNAME] = A_BOLD,
[CPU_NICE] = A_NORMAL,
@ -345,8 +254,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_IOWAIT] = A_NORMAL,
[CPU_IRQ] = A_BOLD,
[CPU_SOFTIRQ] = A_BOLD,
[CPU_STEAL] = A_REVERSE,
[CPU_GUEST] = A_REVERSE,
[CPU_STEAL] = A_DIM,
[CPU_GUEST] = A_DIM,
[PRESSURE_STALL_THREEHUNDRED] = A_DIM,
[PRESSURE_STALL_SIXTY] = A_NORMAL,
[PRESSURE_STALL_TEN] = A_BOLD,
@ -357,6 +266,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_OTHER] = A_DIM,
[ZFS_COMPRESSED] = A_BOLD,
[ZFS_RATIO] = A_BOLD,
[ZRAM] = A_NORMAL,
},
[COLORSCHEME_BLACKONWHITE] = {
[RESET_COLOR] = ColorPair(Black, White),
@ -369,25 +279,38 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
[PANEL_SELECTION_UNFOCUS] = ColorPair(Blue, White),
[FAILED_SEARCH] = ColorPair(Red, Cyan),
[FAILED_READ] = ColorPair(Red, White),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = ColorPair(Yellow, White),
[BATTERY] = ColorPair(Yellow, White),
[LARGE_NUMBER] = ColorPair(Red, White),
[METER_TEXT] = ColorPair(Blue, White),
[METER_VALUE] = ColorPair(Black, White),
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, White),
[METER_VALUE_IOREAD] = ColorPair(Green, White),
[METER_VALUE_IOWRITE] = ColorPair(Yellow, White),
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, White),
[METER_VALUE_OK] = ColorPair(Green, White),
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, White),
[LED_COLOR] = ColorPair(Green, White),
[TASKS_RUNNING] = ColorPair(Green, White),
[PROCESS] = ColorPair(Black, White),
[PROCESS_SHADOW] = A_BOLD | ColorPair(Black, White),
[PROCESS_TAG] = ColorPair(White, Blue),
[PROCESS_MEGABYTES] = ColorPair(Blue, White),
[PROCESS_GIGABYTES] = ColorPair(Green, White),
[PROCESS_BASENAME] = ColorPair(Blue, White),
[PROCESS_TREE] = ColorPair(Green, White),
[PROCESS_R_STATE] = ColorPair(Green, White),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, White),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, White),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, White),
[PROCESS_NEW] = ColorPair(White, Green),
[PROCESS_TOMB] = ColorPair(White, Red),
[PROCESS_THREAD] = ColorPair(Blue, White),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, White),
[PROCESS_COMM] = ColorPair(Magenta, White),
[PROCESS_THREAD_COMM] = ColorPair(Green, White),
[BAR_BORDER] = ColorPair(Blue, White),
[BAR_SHADOW] = ColorPair(Black, White),
[SWAP] = ColorPair(Red, White),
@ -403,6 +326,9 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = ColorPair(Black, White),
[HELP_BOLD] = ColorPair(Blue, White),
[CLOCK] = ColorPair(Black, White),
[DATE] = ColorPair(Black, White),
[DATETIME] = ColorPair(Black, White),
[CHECK_BOX] = ColorPair(Blue, White),
[CHECK_MARK] = ColorPair(Black, White),
[CHECK_TEXT] = ColorPair(Black, White),
[HOSTNAME] = ColorPair(Black, White),
@ -425,6 +351,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_OTHER] = ColorPair(Magenta, White),
[ZFS_COMPRESSED] = ColorPair(Cyan, White),
[ZFS_RATIO] = ColorPair(Magenta, White),
[ZRAM] = ColorPair(Yellow, White)
},
[COLORSCHEME_LIGHTTERMINAL] = {
[RESET_COLOR] = ColorPair(Black, Black),
@ -437,25 +364,38 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
[PANEL_SELECTION_UNFOCUS] = ColorPair(Blue, Black),
[FAILED_SEARCH] = ColorPair(Red, Cyan),
[FAILED_READ] = ColorPair(Red, Black),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = ColorPair(Yellow, Black),
[BATTERY] = ColorPair(Yellow, Black),
[LARGE_NUMBER] = ColorPair(Red, Black),
[METER_TEXT] = ColorPair(Blue, Black),
[METER_VALUE] = ColorPair(Black, Black),
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
[METER_VALUE_IOREAD] = ColorPair(Green, Black),
[METER_VALUE_IOWRITE] = ColorPair(Yellow, Black),
[METER_VALUE_NOTICE] = A_BOLD | ColorPairWhiteDefault,
[METER_VALUE_OK] = ColorPair(Green, Black),
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
[LED_COLOR] = ColorPair(Green, Black),
[TASKS_RUNNING] = ColorPair(Green, Black),
[PROCESS] = ColorPair(Black, Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = ColorPair(White, Blue),
[PROCESS_MEGABYTES] = ColorPair(Blue, Black),
[PROCESS_GIGABYTES] = ColorPair(Green, Black),
[PROCESS_BASENAME] = ColorPair(Green, Black),
[PROCESS_TREE] = ColorPair(Blue, Black),
[PROCESS_R_STATE] = ColorPair(Green, Black),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
[PROCESS_NEW] = ColorPair(Black, Green),
[PROCESS_TOMB] = ColorPair(Black, Red),
[PROCESS_THREAD] = ColorPair(Blue, Black),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black),
[PROCESS_COMM] = ColorPair(Magenta, Black),
[PROCESS_THREAD_COMM] = ColorPair(Yellow, Black),
[BAR_BORDER] = ColorPair(Blue, Black),
[BAR_SHADOW] = ColorPairGrayBlack,
[SWAP] = ColorPair(Red, Black),
@ -468,12 +408,15 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, Black),
[LOAD_AVERAGE_FIVE] = ColorPair(Black, Black),
[LOAD_AVERAGE_ONE] = ColorPair(Black, Black),
[LOAD] = ColorPair(White,Black),
[LOAD] = ColorPairWhiteDefault,
[HELP_BOLD] = ColorPair(Blue, Black),
[CLOCK] = ColorPair(White,Black),
[CLOCK] = ColorPairWhiteDefault,
[DATE] = ColorPairWhiteDefault,
[DATETIME] = ColorPairWhiteDefault,
[CHECK_BOX] = ColorPair(Blue, Black),
[CHECK_MARK] = ColorPair(Black, Black),
[CHECK_TEXT] = ColorPair(Black, Black),
[HOSTNAME] = ColorPair(White,Black),
[HOSTNAME] = ColorPairWhiteDefault,
[CPU_NICE] = ColorPair(Cyan, Black),
[CPU_NICE_TEXT] = ColorPair(Cyan, Black),
[CPU_NORMAL] = ColorPair(Green, Black),
@ -493,6 +436,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_OTHER] = A_BOLD | ColorPair(Magenta, Black),
[ZFS_COMPRESSED] = ColorPair(Cyan, Black),
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black),
[ZRAM] = ColorPair(Yellow, Black),
},
[COLORSCHEME_MIDNIGHT] = {
[RESET_COLOR] = ColorPair(White, Blue),
@ -505,25 +449,38 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
[PANEL_SELECTION_UNFOCUS] = A_BOLD | ColorPair(Yellow, Blue),
[FAILED_SEARCH] = ColorPair(Red, Cyan),
[FAILED_READ] = A_BOLD | ColorPair(Red, Blue),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = A_BOLD | ColorPair(Yellow, Blue),
[BATTERY] = A_BOLD | ColorPair(Yellow, Blue),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Blue),
[METER_TEXT] = ColorPair(Cyan, Blue),
[METER_VALUE] = A_BOLD | ColorPair(Cyan, Blue),
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Blue),
[METER_VALUE_IOREAD] = ColorPair(Green, Blue),
[METER_VALUE_IOWRITE] = ColorPair(Black, Blue),
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Blue),
[METER_VALUE_OK] = ColorPair(Green, Blue),
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
[LED_COLOR] = ColorPair(Green, Blue),
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Blue),
[PROCESS] = ColorPair(White, Blue),
[PROCESS_SHADOW] = A_BOLD | ColorPair(Black, Blue),
[PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Blue),
[PROCESS_MEGABYTES] = ColorPair(Cyan, Blue),
[PROCESS_GIGABYTES] = ColorPair(Green, Blue),
[PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Blue),
[PROCESS_TREE] = ColorPair(Cyan, Blue),
[PROCESS_R_STATE] = ColorPair(Green, Blue),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Blue),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Blue),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Blue),
[PROCESS_NEW] = ColorPair(Blue, Green),
[PROCESS_TOMB] = ColorPair(Blue, Red),
[PROCESS_THREAD] = ColorPair(Green, Blue),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Blue),
[PROCESS_COMM] = ColorPair(Magenta, Blue),
[PROCESS_THREAD_COMM] = ColorPair(Black, Blue),
[BAR_BORDER] = A_BOLD | ColorPair(Yellow, Blue),
[BAR_SHADOW] = ColorPair(Cyan, Blue),
[SWAP] = ColorPair(Red, Blue),
@ -539,14 +496,17 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = A_BOLD | ColorPair(White, Blue),
[HELP_BOLD] = A_BOLD | ColorPair(Cyan, Blue),
[CLOCK] = ColorPair(White, Blue),
[CHECK_MARK] = A_BOLD | ColorPair(Cyan,Blue),
[DATE] = ColorPair(White, Blue),
[DATETIME] = ColorPair(White, Blue),
[CHECK_BOX] = ColorPair(Cyan, Blue),
[CHECK_MARK] = A_BOLD | ColorPair(White, Blue),
[CHECK_TEXT] = A_NORMAL | ColorPair(White, Blue),
[HOSTNAME] = ColorPair(White, Blue),
[CPU_NICE] = A_BOLD | ColorPair(Cyan, Blue),
[CPU_NICE_TEXT] = A_BOLD | ColorPair(Cyan, Blue),
[CPU_NORMAL] = A_BOLD | ColorPair(Green, Blue),
[CPU_SYSTEM] = A_BOLD | ColorPair(Red, Blue),
[CPU_IOWAIT] = A_BOLD | ColorPair(Blue,Blue),
[CPU_IOWAIT] = A_BOLD | ColorPair(Black, Blue),
[CPU_IRQ] = A_BOLD | ColorPair(Black, Blue),
[CPU_SOFTIRQ] = ColorPair(Black, Blue),
[CPU_STEAL] = ColorPair(White, Blue),
@ -561,6 +521,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_OTHER] = A_BOLD | ColorPair(Magenta, Blue),
[ZFS_COMPRESSED] = A_BOLD | ColorPair(White, Blue),
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Blue),
[ZRAM] = A_BOLD | ColorPair(Yellow, Blue),
},
[COLORSCHEME_BLACKNIGHT] = {
[RESET_COLOR] = ColorPair(Cyan, Black),
@ -572,26 +533,39 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan),
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
[PANEL_SELECTION_UNFOCUS] = ColorPair(Black, White),
[FAILED_SEARCH] = ColorPair(Red,Cyan),
[FAILED_SEARCH] = ColorPair(Red, Green),
[FAILED_READ] = A_BOLD | ColorPair(Red, Black),
[PAUSED] = A_BOLD | ColorPair(Yellow, Green),
[UPTIME] = ColorPair(Green, Black),
[BATTERY] = ColorPair(Green, Black),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
[METER_TEXT] = ColorPair(Cyan, Black),
[METER_VALUE] = ColorPair(Green, Black),
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
[METER_VALUE_IOREAD] = ColorPair(Green, Black),
[METER_VALUE_IOWRITE] = ColorPair(Blue, Black),
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black),
[METER_VALUE_OK] = ColorPair(Green, Black),
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
[LED_COLOR] = ColorPair(Green, Black),
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black),
[PROCESS] = ColorPair(Cyan, Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Black),
[PROCESS_MEGABYTES] = A_BOLD | ColorPair(Green, Black),
[PROCESS_GIGABYTES] = A_BOLD | ColorPair(Yellow, Black),
[PROCESS_BASENAME] = A_BOLD | ColorPair(Green, Black),
[PROCESS_TREE] = ColorPair(Cyan, Black),
[PROCESS_THREAD] = ColorPair(Green, Black),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black),
[PROCESS_COMM] = ColorPair(Magenta, Black),
[PROCESS_THREAD_COMM] = ColorPair(Yellow, Black),
[PROCESS_R_STATE] = ColorPair(Green, Black),
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
[PROCESS_NEW] = ColorPair(Black, Green),
[PROCESS_TOMB] = ColorPair(Black, Red),
[BAR_BORDER] = A_BOLD | ColorPair(Green, Black),
[BAR_SHADOW] = ColorPair(Cyan, Black),
[SWAP] = ColorPair(Red, Black),
@ -607,6 +581,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = A_BOLD,
[HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black),
[CLOCK] = ColorPair(Green, Black),
[CHECK_BOX] = ColorPair(Green, Black),
[CHECK_MARK] = A_BOLD | ColorPair(Green, Black),
[CHECK_TEXT] = ColorPair(Cyan, Black),
[HOSTNAME] = ColorPair(Green, Black),
@ -629,6 +604,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_OTHER] = ColorPair(Magenta, Black),
[ZFS_COMPRESSED] = ColorPair(Blue, Black),
[ZFS_RATIO] = ColorPair(Magenta, Black),
[ZRAM] = ColorPair(Yellow, Black),
},
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
};
@ -639,71 +615,54 @@ int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10;
char* CRT_termType;
// TODO move color scheme to Settings, perhaps?
int CRT_colorScheme = 0;
void *backtraceArray[128];
ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT;
ATTR_NORETURN
static void CRT_handleSIGTERM(int sgn) {
(void) sgn;
CRT_done();
exit(0);
}
#if HAVE_SETUID_ENABLED
#ifdef HAVE_SETUID_ENABLED
static int CRT_euid = -1;
static int CRT_egid = -1;
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
void CRT_dropPrivileges() {
CRT_egid = getegid();
CRT_euid = geteuid();
if (setegid(getgid()) == -1) {
DIE("Fatal error: failed dropping group privileges.\n");
CRT_fatalError("Fatal error: failed dropping group privileges");
}
if (seteuid(getuid()) == -1) {
DIE("Fatal error: failed dropping user privileges.\n");
CRT_fatalError("Fatal error: failed dropping user privileges");
}
}
void CRT_restorePrivileges() {
if (CRT_egid == -1 || CRT_euid == -1) {
DIE("Fatal error: internal inconsistency.\n");
CRT_fatalError("Fatal error: internal inconsistency");
}
if (setegid(CRT_egid) == -1) {
DIE("Fatal error: failed restoring group privileges.\n");
CRT_fatalError("Fatal error: failed restoring group privileges");
}
if (seteuid(CRT_euid) == -1) {
DIE("Fatal error: failed restoring user privileges.\n");
CRT_fatalError("Fatal error: failed restoring user privileges");
}
}
#else
#endif /* HAVE_SETUID_ENABLED */
/* Turn setuid operations into NOPs */
#ifndef CRT_dropPrivileges
#define CRT_dropPrivileges()
#define CRT_restorePrivileges()
#endif
#endif
static struct sigaction old_sig_handler[32];
// TODO: pass an instance of Settings instead.
void CRT_init(int delay, int colorScheme) {
void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
initscr();
noecho();
CRT_delay = delay;
if (CRT_delay == 0) {
CRT_delay = 1;
}
CRT_colors = CRT_colorSchemes[colorScheme];
CRT_colorScheme = colorScheme;
@ -712,24 +671,25 @@ void CRT_init(int delay, int colorScheme) {
CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White, Black) : color;
}
halfdelay(CRT_delay);
halfdelay(*CRT_delay);
nonl();
intrflush(stdscr, false);
keypad(stdscr, true);
mouseinterval(0);
curs_set(0);
if (has_colors()) {
start_color();
CRT_hasColors = true;
} else {
CRT_hasColors = false;
}
CRT_termType = getenv("TERM");
if (String_eq(CRT_termType, "linux"))
const char* termType = getenv("TERM");
if (termType && String_eq(termType, "linux")) {
CRT_scrollHAmount = 20;
else
} else {
CRT_scrollHAmount = 5;
if (String_startsWith(CRT_termType, "xterm") || String_eq(CRT_termType, "vt220")) {
}
if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) {
define_key("\033[H", KEY_HOME);
define_key("\033[F", KEY_END);
define_key("\033[7~", KEY_HOME);
@ -742,6 +702,7 @@ void CRT_init(int delay, int colorScheme) {
define_key("\033[12~", KEY_F(2));
define_key("\033[13~", KEY_F(3));
define_key("\033[14~", KEY_F(4));
define_key("\033[14;2~", KEY_F(15));
define_key("\033[17;2~", KEY_F(18));
char sequence[3] = "\033a";
for (char c = 'a'; c <= 'z'; c++) {
@ -749,34 +710,50 @@ void CRT_init(int delay, int colorScheme) {
define_key(sequence, KEY_ALT('A' + (c - 'a')));
}
}
#ifndef DEBUG
signal(11, CRT_handleSIGSEGV);
#endif
struct sigaction act;
sigemptyset (&act.sa_mask);
act.sa_flags = (int)SA_RESETHAND | SA_NODEFER;
act.sa_handler = CRT_handleSIGSEGV;
sigaction (SIGSEGV, &act, &old_sig_handler[SIGSEGV]);
sigaction (SIGFPE, &act, &old_sig_handler[SIGFPE]);
sigaction (SIGILL, &act, &old_sig_handler[SIGILL]);
sigaction (SIGBUS, &act, &old_sig_handler[SIGBUS]);
sigaction (SIGPIPE, &act, &old_sig_handler[SIGPIPE]);
sigaction (SIGSYS, &act, &old_sig_handler[SIGSYS]);
sigaction (SIGABRT, &act, &old_sig_handler[SIGABRT]);
signal(SIGTERM, CRT_handleSIGTERM);
signal(SIGQUIT, CRT_handleSIGTERM);
use_default_colors();
if (!has_colors())
CRT_colorScheme = 1;
CRT_colorScheme = COLORSCHEME_MONOCHROME;
CRT_setColors(CRT_colorScheme);
/* initialize locale */
setlocale(LC_CTYPE, "");
#ifdef HAVE_LIBNCURSESW
if(strcmp(nl_langinfo(CODESET), "UTF-8") == 0) {
if (allowUnicode && String_eq(nl_langinfo(CODESET), "UTF-8")) {
CRT_utf8 = true;
CRT_treeStr = CRT_treeStrUtf8;
CRT_checkStr = CRT_checkStrUtf8;
CRT_collapStr = CRT_collapStrUtf8;
} else {
CRT_utf8 = false;
}
#else
(void) allowUnicode;
#endif
CRT_treeStr =
#ifdef HAVE_LIBNCURSESW
CRT_utf8 ? CRT_treeStrUtf8 :
#endif
CRT_treeStrAscii;
#if NCURSES_MOUSE_VERSION > 1
mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
#else
mousemask(BUTTON1_RELEASED, NULL);
#endif
CRT_degreeSign = initDegreeSign();
}
void CRT_done() {
@ -785,7 +762,7 @@ void CRT_done() {
}
void CRT_fatalError(const char* note) {
char* sysMsg = strerror(errno);
const char* sysMsg = strerror(errno);
CRT_done();
fprintf(stderr, "%s: %s\n", note, sysMsg);
exit(2);
@ -796,7 +773,7 @@ int CRT_readKey() {
cbreak();
nodelay(stdscr, FALSE);
int ret = getch();
halfdelay(CRT_delay);
halfdelay(*CRT_delay);
return ret;
}
@ -807,16 +784,16 @@ void CRT_disableDelay() {
}
void CRT_enableDelay() {
halfdelay(CRT_delay);
halfdelay(*CRT_delay);
}
void CRT_setColors(int colorScheme) {
CRT_colorScheme = colorScheme;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (ColorIndex(i,j) != ColorPairGrayBlack) {
int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
for (short int i = 0; i < 8; i++) {
for (short int j = 0; j < 8; j++) {
if (ColorIndex(i, j) != ColorIndexGrayBlack && ColorIndex(i, j) != ColorIndexWhiteDefault) {
short int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? (j == 0 ? -1 : j)
: j;
init_pair(ColorIndex(i, j), i, bg);
@ -824,11 +801,108 @@ void CRT_setColors(int colorScheme) {
}
}
int grayBlackFg = COLORS > 8 ? 8 : 0;
int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? -1
: 0;
short int grayBlackFg = COLORS > 8 ? 8 : 0;
short int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT) ? -1 : 0;
init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg);
init_pair(ColorIndexWhiteDefault, White, -1);
CRT_colors = CRT_colorSchemes[colorScheme];
}
void CRT_handleSIGSEGV(int signal) {
CRT_done();
fprintf(stderr, "\n\n"
"FATAL PROGRAM ERROR DETECTED\n"
"============================\n"
"Please check at https://htop.dev/issues whether this issue has already been reported.\n"
"If no similar issue has been reported before, please create a new issue with the following information:\n"
"\n"
"- Your htop version (htop --version)\n"
"- Your OS and kernel version (uname -a)\n"
"- Your distribution and release (lsb_release -a)\n"
"- Likely steps to reproduce (How did it happened?)\n"
);
#ifdef HAVE_EXECINFO_H
fprintf(stderr, "- Backtrace of the issue (see below)\n");
#endif
fprintf(stderr,
"\n"
);
const char* signal_str = strsignal(signal);
if (!signal_str) {
signal_str = "unknown reason";
}
fprintf(stderr,
"Error information:\n"
"------------------\n"
"A signal %d (%s) was received.\n"
"\n",
signal, signal_str
);
#ifdef HAVE_EXECINFO_H
fprintf(stderr,
"Backtrace information:\n"
"----------------------\n"
"The following function calls were active when the issue was detected:\n"
"---\n"
);
void *backtraceArray[256];
size_t size = backtrace(backtraceArray, ARRAYSIZE(backtraceArray));
backtrace_symbols_fd(backtraceArray, size, 2);
fprintf(stderr,
"---\n"
"\n"
"To make the above information more practical to work with,\n"
"you should provide a disassembly of your binary.\n"
"This can usually be done by running the following command:\n"
"\n"
);
#ifdef HTOP_DARWIN
fprintf(stderr, " otool -tvV `which htop` > ~/htop.otool\n");
#else
fprintf(stderr, " objdump -d -S -w `which htop` > ~/htop.objdump\n");
#endif
fprintf(stderr,
"\n"
"Please include the generated file in your report.\n"
"\n"
);
#endif
fprintf(stderr,
"Running this program with debug symbols or inside a debugger may provide further insights.\n"
"\n"
"Thank you for helping to improve htop!\n"
"\n"
"htop " VERSION " aborting.\n"
"\n"
);
/* Call old sigsegv handler; may be default exit or third party one (e.g. ASAN) */
if (sigaction (signal, &old_sig_handler[signal], NULL) < 0) {
/* This avoids an infinite loop in case the handler could not be reset. */
fprintf(stderr,
"!!! Chained handler could not be restored. Forcing exit.\n"
);
_exit(1);
}
/* Trigger the previous signal handler. */
raise(signal);
// Always terminate, even if installed handler returns
fprintf(stderr,
"!!! Chained handler did not exit. Forcing exit.\n"
);
_exit(1);
}

179
CRT.h
View File

@ -1,75 +1,42 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_CRT
#define HEADER_CRT
/*
htop - CRT.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#if HAVE_SETUID_ENABLED
#endif
#define ColorIndex(i,j) ((7-i)*8+j)
#define ColorPair(i,j) COLOR_PAIR(ColorIndex(i,j))
#define Black COLOR_BLACK
#define Red COLOR_RED
#define Green COLOR_GREEN
#define Yellow COLOR_YELLOW
#define Blue COLOR_BLUE
#define Magenta COLOR_MAGENTA
#define Cyan COLOR_CYAN
#define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
#define KEY_RECLICK KEY_F(22)
//#link curses
#include "config.h"
#include <stdbool.h>
#include "Macros.h"
#include "ProvideCurses.h"
typedef enum TreeStr_ {
TREE_STR_HORZ,
TREE_STR_VERT,
TREE_STR_RTEE,
TREE_STR_BEND,
TREE_STR_TEND,
TREE_STR_OPEN,
TREE_STR_SHUT,
TREE_STR_COUNT
TREE_STR_ASC,
TREE_STR_DESC,
LAST_TREE_STR
} TreeStr;
typedef enum CheckStr_ {
CHECK_STR_NONE,
CHECK_STR_PARTIAL,
CHECK_STR_FULL,
CHECK_STR_COUNT
} CheckStr;
typedef enum CollapStr_ {
COLLAP_STR_OPEN,
COLLAP_STR_CLOSED,
COLLAP_STR_COUNT
} CollapStr;
typedef enum ColorSchemes_ {
COLORSCHEME_DEFAULT = 0,
COLORSCHEME_MONOCHROME = 1,
COLORSCHEME_BLACKONWHITE = 2,
COLORSCHEME_LIGHTTERMINAL = 3,
COLORSCHEME_MIDNIGHT = 4,
COLORSCHEME_BLACKNIGHT = 5,
COLORSCHEME_BROKENGRAY = 6,
LAST_COLORSCHEME = 7,
} ColorSchemes;
typedef enum ColorScheme_ {
COLORSCHEME_DEFAULT,
COLORSCHEME_MONOCHROME,
COLORSCHEME_BLACKONWHITE,
COLORSCHEME_LIGHTTERMINAL,
COLORSCHEME_MIDNIGHT,
COLORSCHEME_BLACKNIGHT,
COLORSCHEME_BROKENGRAY,
LAST_COLORSCHEME
} ColorScheme;
typedef enum ColorElements_ {
RESET_COLOR,
@ -77,6 +44,8 @@ typedef enum ColorElements_ {
FUNCTION_BAR,
FUNCTION_KEY,
FAILED_SEARCH,
FAILED_READ,
PAUSED,
PANEL_HEADER_FOCUS,
PANEL_HEADER_UNFOCUS,
PANEL_SELECTION_FOCUS,
@ -85,6 +54,12 @@ typedef enum ColorElements_ {
LARGE_NUMBER,
METER_TEXT,
METER_VALUE,
METER_VALUE_ERROR,
METER_VALUE_IOREAD,
METER_VALUE_IOWRITE,
METER_VALUE_NOTICE,
METER_VALUE_OK,
METER_VALUE_WARN,
LED_COLOR,
UPTIME,
BATTERY,
@ -94,14 +69,19 @@ typedef enum ColorElements_ {
PROCESS_SHADOW,
PROCESS_TAG,
PROCESS_MEGABYTES,
PROCESS_GIGABYTES,
PROCESS_TREE,
PROCESS_R_STATE,
PROCESS_D_STATE,
PROCESS_BASENAME,
PROCESS_HIGH_PRIORITY,
PROCESS_LOW_PRIORITY,
PROCESS_NEW,
PROCESS_TOMB,
PROCESS_THREAD,
PROCESS_THREAD_BASENAME,
PROCESS_COMM,
PROCESS_THREAD_COMM,
BAR_BORDER,
BAR_SHADOW,
GRAPH_1,
@ -114,9 +94,12 @@ typedef enum ColorElements_ {
LOAD_AVERAGE_FIFTEEN,
LOAD_AVERAGE_FIVE,
LOAD_AVERAGE_ONE,
CHECK_BOX,
CHECK_MARK,
CHECK_TEXT,
CLOCK,
DATE,
DATETIME,
HELP_BOLD,
HOSTNAME,
CPU_NICE,
@ -138,45 +121,30 @@ typedef enum ColorElements_ {
ZFS_OTHER,
ZFS_COMPRESSED,
ZFS_RATIO,
ZRAM,
LAST_COLORELEMENT
} ColorElements;
extern void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_fatalError(const char* note) ATTR_NORETURN;
extern 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_treeStrAscii[TREE_STR_COUNT];
extern const char *CRT_checkStrAscii[CHECK_STR_COUNT];
extern const char *CRT_collapStrAscii[COLLAP_STR_COUNT];
extern const char* CRT_degreeSign;
#ifdef HAVE_LIBNCURSESW
extern const char *CRT_treeStrUtf8[TREE_STR_COUNT];
extern const char *CRT_checkStrUtf8[CHECK_STR_COUNT];
extern const char *CRT_collapStrUtf8[COLLAP_STR_COUNT];
extern bool CRT_utf8;
#endif
extern bool CRT_utf8;
extern const char* const* CRT_treeStr;
extern const char **CRT_treeStr;
extern const char **CRT_checkStr;
extern const char **CRT_collapStr;
extern int CRT_delay;
extern int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern const int* CRT_colors;
extern int CRT_cursorX;
@ -184,47 +152,32 @@ extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount;
extern char* CRT_termType;
extern ColorScheme CRT_colorScheme;
// TODO move color scheme to Settings, perhaps?
#ifdef HAVE_SETUID_ENABLED
extern int CRT_colorScheme;
void CRT_dropPrivileges(void);
extern void *backtraceArray[128];
void CRT_restorePrivileges(void);
#if HAVE_SETUID_ENABLED
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
extern void CRT_dropPrivileges();
extern void CRT_restorePrivileges();
#else
#else /* HAVE_SETUID_ENABLED */
/* Turn setuid operations into NOPs */
static inline void CRT_dropPrivileges(void) { }
static inline void CRT_restorePrivileges(void) { }
#ifndef CRT_dropPrivileges
#define CRT_dropPrivileges()
#define CRT_restorePrivileges()
#endif
#endif
// TODO: pass an instance of Settings instead.
extern void CRT_init(int delay, int colorScheme);
extern void CRT_done();
extern void CRT_fatalError(const char* note);
extern int CRT_readKey();
extern void CRT_disableDelay();
extern void CRT_enableDelay();
extern void CRT_setColors(int colorScheme);
#endif /* HAVE_SETUID_ENABLED */
void CRT_init(const int* delay, int colorScheme, bool allowUnicode);
void CRT_done(void);
int CRT_readKey(void);
void CRT_disableDelay(void);
void CRT_enableDelay(void);
void CRT_setColors(int colorScheme);
#endif

View File

@ -1,38 +1,28 @@
/*
htop - CategoriesPanel.c
(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.
*/
#include "CategoriesPanel.h"
#include "AvailableMetersPanel.h"
#include "MetersPanel.h"
#include "DisplayOptionsPanel.h"
#include "ColumnsPanel.h"
#include "ColorsPanel.h"
#include "AvailableColumnsPanel.h"
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
/*{
#include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
#include "ProcessList.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"
typedef struct CategoriesPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
Header* header;
ProcessList* pl;
} CategoriesPanel;
}*/
static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@ -97,7 +87,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
break;
}
default:
if (ch < 255 && isalpha(ch))
if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP)
result = IGNORED;
@ -107,6 +97,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
int size = ScreenManager_size(this->scr);
for (int i = 1; i < size; i++)
ScreenManager_remove(this->scr, 1);
switch (selected) {
case 0:
CategoriesPanel_makeMetersPage(this);
@ -125,7 +116,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
return result;
}
PanelClass CategoriesPanel_class = {
const PanelClass CategoriesPanel_class = {
.super = {
.extends = Class(Panel),
.delete = CategoriesPanel_delete

View File

@ -1,18 +1,17 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_CategoriesPanel
#define HEADER_CategoriesPanel
/*
htop - CategoriesPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Header.h"
#include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
#include "ProcessList.h"
#include "ScreenManager.h"
#include "Settings.h"
typedef struct CategoriesPanel_ {
Panel super;
@ -23,11 +22,10 @@ typedef struct CategoriesPanel_ {
ProcessList* pl;
} CategoriesPanel;
void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
extern void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
extern const PanelClass CategoriesPanel_class;
extern PanelClass CategoriesPanel_class;
extern CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl);
CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl);
#endif

158
ChangeLog
View File

@ -1,3 +1,160 @@
What's new in version 3.0.5
* BUGFIX / SECURITY: InfoScreen: fix uncontrolled format string
* BUGFIX: Improve white text in the Light Terminal colour scheme
(both of the above thanks to V)
* Enable the function bar on the main screen to be hidden (see Setup -> Display options)
* BUGFIX: Reduce layout issues esp. around printing wide characters (not complete yet)
* BUGFIX: Make the follow function exit cleanly after followed process died
* Solaris: make Process callbacks static
* Update help and man page for improved -t / -s options
* Drop usage of formatted error messages from <err.h>
* Show arrow indicating order of sorted process column
* Lots of plumbing around the internal Hashtable, hardening and code cleanups
* LibSensors: add support for Ryzen CPUs (vor 5 Tagen)
(thanks to Matej Dian)
* BUGFIX: Fix CPU percentage on M1 silicon Macs
(thanks to Luke Groeninger)
* LoadMeter: dynamically adjust color and total of bar
* Find libsensors.so.4 for Fedora and friends
* Add support to display CPU frequencies on Solarish platforms
(thanks to Dominik Hassler)
* Enable going back to previous search matches (Shift-F3)
* Added keybind 'N' for sorting by PID (drops 'n'/'N' as not used before much)
(thanks to Jake Mannens)
What's new in version 3.0.4
* Separate tree and list sort orders
* Invert Process_compare so that superclass matches run first
(thanks to Hisham Muhammad)
* Unhardcode Mac OS tick-to-milliseconds conversion
(thanks to Alexander Momchilov)
* Check if clock_gettime needs linking of librt
* Define O_PATH if not already defined
(thanks to Chris Burr)
* Add column on Mac for processes running under translation
(thanks to Dániel Bakai)
* Configure check for additional linker flags for keypad(3)
* PSI Meter: constant width and only print ten-duration as bar
* Sort in paused mode after inverting sort order
* Handle absence of package CPU temperature
* Meter: restore non-wide-character build
* LibSensors: restore temperature for Raspberry Pi
* MainPanel: do not reset hideProcessSelection on KEY_SHUFFLE
* BarMeter: rework text padding
* Panel: rework drawing of FunctionBar
* Meter: fix artifacts with very tiny width
* DragonFlyBSD updates
* BUGFIX: Fix dlopen issue for libsensors5 for some platforms
* BUGFIX: Fix broken tree display on inverted sort order
* BUGFIX: Fix pause mode ("Z") in tree view
* BUGFIX: Correct timebase for non-x86 CPUs on Darwin
* BUGFIX: Avoid NULL dereference on zombie processes
* Document dynamic bindings and assumed external configuration
* Update key mapping documentation for sorting
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
(thanks to Fynn J. Wulf)
* Calculate library size (M_LRS column) from maps file
(thanks to Fynn J. 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 2 seconds
* Improvements to comm / cmdline display functionality
* Merged view for COMM, EXE and cmdline
(thanks to Narendran Gopalakrishnan and Benny Baumann)
* 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
* BUGFIX: Drop 'vim_mode' - several issues, needs rethink
* BUGFIX: fix regression in -u optional-argument handling
* Build system rework to remove python, header generation
(thanks to Zev Weiss and Hugo Musso Gualandi)
* BUGFIX: report nice level correctly on Solaris
(thanks to Dominik Hassler)
* CI, code quality improvements
(thanks to Tobias Kortkamp, Christian Hesse, Benny Baumann)
What's new in version 3.0.1
* Coverity fixes, CI improvements, documentation updates
* BUGFIX: Fix early exit with longer sysfs battery paths
* BUGFIX: Improve OOM output, fix sorting
(thanks to Christian Göttsche)
* Rework check buttons and tree open/closed
(thanks to Bert Wesarg)
* Add -U/--no-unicode option to disable unicode
(thanks to Christian Hesse)
* Improvements to the affinity panel
(thanks to Bert Wesarg)
What's new in version 3.0.0
* New maintainers - after a prolonged period of inactivity
@ -5,7 +162,6 @@ What's new in version 3.0.0
of community maintainers have volunteered to take over a
fork at https://htop.dev and https://github.com/htop-dev
to keep the project going.
* Support ZFS ARC statistics
(thanks to Ross Williams)
* Support more than 2 smaller CPU meter columns

View File

@ -1,79 +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>
/*{
#include "Object.h"
typedef struct CheckItem_ {
Object super;
char* text;
bool* ref;
bool value;
} CheckItem;
}*/
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);
if (CheckItem_get(this))
RichString_append(out, CRT_colors[CHECK_MARK], CRT_checkStr[CHECK_STR_FULL]);
else
RichString_append(out, CRT_colors[CHECK_MARK], CRT_checkStr[CHECK_STR_NONE]);
RichString_append(out, CRT_colors[CHECK_TEXT], " ");
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,32 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#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;
extern CheckItem* CheckItem_newByRef(char* text, bool* ref);
extern CheckItem* CheckItem_newByVal(char* text, bool value);
extern void CheckItem_set(CheckItem* this, bool value);
extern bool CheckItem_get(CheckItem* this);
#endif

View File

@ -1,25 +1,25 @@
/*
htop - ClockMeter.c
(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.
*/
#include "ClockMeter.h"
#include "config.h" // IWYU pragma: keep
#include "CRT.h"
#include "ClockMeter.h"
#include <time.h>
/*{
#include "Meter.h"
}*/
#include "CRT.h"
#include "Object.h"
int ClockMeter_attributes[] = {
static const int ClockMeter_attributes[] = {
CLOCK
};
static void ClockMeter_updateValues(Meter* this, char* buffer, int size) {
static void ClockMeter_updateValues(Meter* this, char* buffer, size_t size) {
time_t t = time(NULL);
struct tm result;
struct tm* lt = localtime_r(&t, &result);
@ -27,7 +27,7 @@ static void ClockMeter_updateValues(Meter* this, char* buffer, int size) {
strftime(buffer, size, "%H:%M:%S", lt);
}
MeterClass ClockMeter_class = {
const MeterClass ClockMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete

View File

@ -1,18 +1,14 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_ClockMeter
#define HEADER_ClockMeter
/*
htop - ClockMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
extern int ClockMeter_attributes[];
extern MeterClass ClockMeter_class;
extern const MeterClass ClockMeter_class;
#endif

View File

@ -1,18 +1,24 @@
/*
htop - ColorsPanel.c
(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.
*/
#include "ColorsPanel.h"
#include "CRT.h"
#include "CheckItem.h"
#include <assert.h>
#include <stdbool.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"
// TO ADD A NEW SCHEME:
// * Increment the size of bool check in ColorsPanel.h
@ -20,19 +26,6 @@ in the source distribution for its full text.
// * Add a define in CRT.h that matches the order of the array
// * Add the colors in CRT_setColors
/*{
#include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
typedef struct ColorsPanel_ {
Panel super;
Settings* settings;
ScreenManager* scr;
} ColorsPanel;
}*/
static const char* const ColorsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@ -67,28 +60,25 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
case KEY_MOUSE:
case KEY_RECLICK:
case ' ':
assert(mark >= 0);
assert(mark < LAST_COLORSCHEME);
for (int i = 0; ColorSchemeNames[i] != NULL; i++)
CheckItem_set((CheckItem*)Panel_get(super, i), false);
CheckItem_set((CheckItem*)Panel_get(super, mark), true);
this->settings->colorScheme = mark;
result = HANDLED;
}
if (result == HANDLED) {
this->settings->colorScheme = mark;
this->settings->changed = true;
const Header* header = this->scr->header;
CRT_setColors(mark);
clear();
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
Header_draw(header);
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);
RichString_setAttr(&(menu->header), CRT_colors[PANEL_HEADER_UNFOCUS]);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
result = HANDLED | REDRAW;
}
return result;
}
PanelClass ColorsPanel_class = {
const PanelClass ColorsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ColorsPanel_delete
@ -105,9 +95,11 @@ ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) {
this->settings = settings;
this->scr = scr;
assert(ARRAYSIZE(ColorSchemeNames) == LAST_COLORSCHEME + 1);
Panel_setHeader(super, "Colors");
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);
return this;

View File

@ -1,23 +1,15 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_ColorsPanel
#define HEADER_ColorsPanel
/*
htop - ColorsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
// TO ADD A NEW SCHEME:
// * Increment the size of bool check in ColorsPanel.h
// * Add the entry in the ColorSchemeNames array below in the file
// * Add a define in CRT.h that matches the order of the array
// * Add the colors in CRT_setColors
#include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
#include "Settings.h"
typedef struct ColorsPanel_ {
Panel super;
@ -26,9 +18,8 @@ typedef struct ColorsPanel_ {
ScreenManager* scr;
} ColorsPanel;
extern const PanelClass ColorsPanel_class;
extern PanelClass ColorsPanel_class;
extern ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr);
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr);
#endif

View File

@ -1,33 +1,23 @@
/*
htop - ColumnsPanel.c
(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.
*/
#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 <stdlib.h>
/*{
#include "Panel.h"
#include "Settings.h"
#include "CRT.h"
#include "FunctionBar.h"
#include "ListItem.h"
#include "Object.h"
#include "Process.h"
#include "ProvideCurses.h"
#include "XUtils.h"
typedef struct ColumnsPanel_ {
Panel super;
Settings* settings;
bool moving;
} ColumnsPanel;
}*/
static const char* const ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
@ -54,8 +44,10 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
{
if (selected < size - 1) {
this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
((ListItem*)Panel_getSelected(super))->moving = this->moving;
Panel_setSelectionColor(super, this->moving ? PANEL_SELECTION_FOLLOW : PANEL_SELECTION_FOCUS);
ListItem* selectedItem = (ListItem*) Panel_getSelected(super);
if (selectedItem)
selectedItem->moving = this->moving;
result = HANDLED;
}
break;
@ -103,7 +95,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
}
default:
{
if (ch < 255 && isalpha(ch))
if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP)
result = IGNORED;
@ -115,7 +107,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
return result;
}
PanelClass ColumnsPanel_class = {
const PanelClass ColumnsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ColumnsPanel_delete
@ -142,15 +134,6 @@ ColumnsPanel* ColumnsPanel_new(Settings* settings) {
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) {
ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_size(super);

View File

@ -1,14 +1,14 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_ColumnsPanel
#define HEADER_ColumnsPanel
/*
htop - ColumnsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include <stdbool.h>
#include "Panel.h"
#include "Settings.h"
@ -19,13 +19,10 @@ typedef struct ColumnsPanel_ {
bool moving;
} ColumnsPanel;
extern const PanelClass ColumnsPanel_class;
extern PanelClass ColumnsPanel_class;
ColumnsPanel* ColumnsPanel_new(Settings* settings);
extern ColumnsPanel* ColumnsPanel_new(Settings* settings);
extern int ColumnsPanel_fieldNameToIndex(const char* name);
extern void ColumnsPanel_update(Panel* super);
void ColumnsPanel_update(Panel* super);
#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[COLS + 1];
int line_offset = 0, last_spc = -1, len;
for (; *p != '\0'; p++, line_offset++) {
assert(line_offset >= 0 && (size_t)line_offset < sizeof(line));
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);
}
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 - 2, " ");
}
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

153
Compat.c Normal file
View File

@ -0,0 +1,153 @@
/*
htop - Compat.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 "Compat.h"
#include <errno.h>
#include <fcntl.h> // IWYU pragma: keep
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h> // IWYU pragma: keep
#include "XUtils.h" // IWYU pragma: keep
#ifdef HAVE_HOST_GET_CLOCK_SERVICE
#include <mach/clock.h>
#include <mach/mach.h>
#endif
int Compat_faccessat(int dirfd,
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 */
ssize_t 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
}
int Compat_clock_monotonic_gettime(struct timespec *tp) {
#if defined(HAVE_CLOCK_GETTIME)
return clock_gettime(CLOCK_MONOTONIC, tp);
#elif defined(HAVE_HOST_GET_CLOCK_SERVICE)
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
tp->tv_sec = mts.tv_sec;
tp->tv_nsec = mts.tv_nsec;
return 0;
#else
#error No Compat_clock_monotonic_gettime() implementation!
#endif
}

61
Compat.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef HEADER_Compat
#define HEADER_Compat
/*
htop - Compat.h
(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 <fcntl.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/stat.h> // IWYU pragma: keep
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 */
ssize_t Compat_readlinkat(int dirfd,
const char* dirpath,
const char* pathname,
char* buf,
size_t bufsize);
int Compat_clock_monotonic_gettime(struct timespec *tp);
#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, size_t 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, size_t 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 htop dev team
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, size_t 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_writeAscii(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_writeAscii(out, CRT_colors[color], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: ");
Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: ");
Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer));
RichString_appendAscii(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
/*
htop - DiskIOMeter.h
(C) 2020 htop dev team
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,32 +1,24 @@
/*
htop - DisplayOptionsPanel.c
(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.
*/
#include "config.h" // IWYU pragma: keep
#include "DisplayOptionsPanel.h"
#include "CheckItem.h"
#include "CRT.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
/*{
#include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
#include "CRT.h"
#include "FunctionBar.h"
#include "Header.h"
#include "Object.h"
#include "OptionItem.h"
#include "ProvideCurses.h"
typedef struct DisplayOptionsPanel_ {
Panel super;
Settings* settings;
ScreenManager* scr;
} DisplayOptionsPanel;
}*/
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@ -41,31 +33,52 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
DisplayOptionsPanel* this = (DisplayOptionsPanel*) super;
HandlerResult result = IGNORED;
CheckItem* selected = (CheckItem*) Panel_getSelected(super);
OptionItem* selected = (OptionItem*) Panel_getSelected(super);
switch (ch) {
case 0x0a:
case 0x0d:
case '\n':
case '\r':
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
case ' ':
CheckItem_set(selected, ! (CheckItem_get(selected)) );
switch (OptionItem_kind(selected)) {
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) {
this->settings->changed = true;
const Header* header = this->scr->header;
Header_calculateHeight((Header*) header);
Header_reinit((Header*) header);
Header* header = this->scr->header;
Header_calculateHeight(header);
Header_reinit(header);
Header_draw(header);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
}
return result;
}
PanelClass DisplayOptionsPanel_class = {
const PanelClass DisplayOptionsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = DisplayOptionsPanel_delete
@ -77,31 +90,43 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
DisplayOptionsPanel* this = AllocThis(DisplayOptionsPanel);
Panel* super = (Panel*) this;
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->scr = scr;
Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("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(xStrdup("Show program path"), &(settings->showProgramPath)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("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(xStrdup("Leave a margin around header"), &(settings->headerMargin)));
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(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU percentage numerically"), &(settings->showCPUUsage)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable the mouse"), &(settings->enableMouse)));
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID)));
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));
Panel_add(super, (Object*) CheckItem_newByRef("Display threads in a different color", &(settings->highlightThreads)));
Panel_add(super, (Object*) CheckItem_newByRef("Show custom thread names", &(settings->showThreadNames)));
Panel_add(super, (Object*) CheckItem_newByRef("Show program path", &(settings->showProgramPath)));
Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName)));
Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand)));
Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline)));
Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline)));
Panel_add(super, (Object*) CheckItem_newByRef("Highlight large numbers in memory counters", &(settings->highlightMegabytes)));
Panel_add(super, (Object*) CheckItem_newByRef("Leave a margin around header", &(settings->headerMargin)));
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("Count CPUs from 1 instead of 0", &(settings->countCPUsFromOne)));
Panel_add(super, (Object*) CheckItem_newByRef("Update process names on every refresh", &(settings->updateProcessNames)));
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));
Panel_add(super, (Object*) NumberItem_newByRef("Hide main function bar (0 - off, 1 - on ESC until next input, 2 - permanently)", &(settings->hideFunctionBar), 0, 0, 2));
#ifdef HAVE_LIBHWLOC
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
return this;
}

View File

@ -1,17 +1,15 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_DisplayOptionsPanel
#define HEADER_DisplayOptionsPanel
/*
htop - DisplayOptionsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
#include "Settings.h"
typedef struct DisplayOptionsPanel_ {
Panel super;
@ -20,9 +18,8 @@ typedef struct DisplayOptionsPanel_ {
ScreenManager* scr;
} DisplayOptionsPanel;
extern const PanelClass DisplayOptionsPanel_class;
extern PanelClass DisplayOptionsPanel_class;
extern DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr);
DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr);
#endif

View File

@ -1,50 +1,36 @@
#include "EnvScreen.h"
#include "config.h" // IWYU pragma: keep
#include "config.h"
#include "CRT.h"
#include "IncSet.h"
#include "ListItem.h"
#include "Platform.h"
#include "StringUtils.h"
#include "EnvScreen.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/*{
#include "InfoScreen.h"
#include "CRT.h"
#include "Macros.h"
#include "Panel.h"
#include "Platform.h"
#include "ProvideCurses.h"
#include "Vector.h"
#include "XUtils.h"
typedef struct EnvScreen_ {
InfoScreen super;
} EnvScreen;
}*/
InfoScreenClass EnvScreen_class = {
.super = {
.extends = Class(Object),
.delete = EnvScreen_delete
},
.scan = EnvScreen_scan,
.draw = EnvScreen_draw
};
EnvScreen* EnvScreen_new(Process* process) {
EnvScreen* this = xMalloc(sizeof(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 - 2, " ");
}
void EnvScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this));
}
void EnvScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, this->process->comm);
static void EnvScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process));
}
void EnvScreen_scan(InfoScreen* this) {
static void EnvScreen_scan(InfoScreen* this) {
Panel* panel = this->display;
int idx = MAX(Panel_getSelectedIndex(panel), 0);
int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
Panel_prune(panel);
@ -64,3 +50,12 @@ void EnvScreen_scan(InfoScreen* this) {
Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx);
}
const InfoScreenClass EnvScreen_class = {
.super = {
.extends = Class(Object),
.delete = EnvScreen_delete
},
.scan = EnvScreen_scan,
.draw = EnvScreen_draw
};

View File

@ -1,22 +1,18 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_EnvScreen
#define HEADER_EnvScreen
#include "InfoScreen.h"
#include "Object.h"
#include "Process.h"
typedef struct EnvScreen_ {
InfoScreen super;
} EnvScreen;
extern InfoScreenClass EnvScreen_class;
extern const InfoScreenClass EnvScreen_class;
extern EnvScreen* EnvScreen_new(Process* process);
EnvScreen* EnvScreen_new(Process* process);
extern void EnvScreen_delete(Object* this);
extern void EnvScreen_draw(InfoScreen* this);
extern void EnvScreen_scan(InfoScreen* this);
void EnvScreen_delete(Object* this);
#endif

View File

@ -1,32 +1,22 @@
/*
htop - FunctionBar.c
(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.
*/
#include "config.h" // IWYU pragma: keep
#include "FunctionBar.h"
#include "CRT.h"
#include "RichString.h"
#include "XAlloc.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
/*{
#include "CRT.h"
#include "Macros.h"
#include "ProvideCurses.h"
#include "XUtils.h"
#include <stdbool.h>
typedef struct FunctionBar_ {
int size;
char** functions;
char** keys;
int* events;
bool staticData;
} FunctionBar;
}*/
static const char* const FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL};
@ -37,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 int FunctionBar_EnterEscEvents[] = {13, 27};
static int currentLen = 0;
FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc) {
const char* functions[] = {enter, esc, NULL};
return FunctionBar_new(functions, FunctionBar_EnterEscKeys, FunctionBar_EnterEscEvents);
@ -53,20 +45,20 @@ FunctionBar* FunctionBar_new(const char* const* functions, const char* const* ke
}
if (keys && events) {
this->staticData = false;
this->keys = xCalloc(15, sizeof(char*));
this->keys.keys = xCalloc(15, sizeof(char*));
this->events = xCalloc(15, sizeof(int));
int i = 0;
while (i < 15 && functions[i]) {
this->keys[i] = xStrdup(keys[i]);
this->keys.keys[i] = xStrdup(keys[i]);
this->events[i] = events[i];
i++;
}
this->size = i;
} else {
this->staticData = true;
this->keys = (char**) FunctionBar_FKeys;
this->keys.constKeys = FunctionBar_FKeys;
this->events = FunctionBar_FEvents;
this->size = 10;
this->size = ARRAYSIZE(FunctionBar_FEvents);
}
return this;
}
@ -78,9 +70,9 @@ void FunctionBar_delete(FunctionBar* this) {
free(this->functions);
if (!this->staticData) {
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);
@ -96,37 +88,61 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
}
}
void FunctionBar_draw(const FunctionBar* this, char* buffer) {
FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
void FunctionBar_draw(const FunctionBar* this) {
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]);
mvhline(LINES - 1, 0, ' ', COLS);
int x = 0;
for (int i = 0; i < this->size; i++) {
attrset(CRT_colors[FUNCTION_KEY]);
mvaddstr(LINES-1, x, this->keys[i]);
x += strlen(this->keys[i]);
mvaddstr(LINES - 1, x, this->keys.constKeys[i]);
x += strlen(this->keys.constKeys[i]);
attrset(CRT_colors[FUNCTION_BAR]);
mvaddstr(LINES - 1, x, this->functions[i]);
x += strlen(this->functions[i]);
}
if (buffer) {
if (attr == -1) {
attrset(CRT_colors[FUNCTION_BAR]);
} else {
attrset(attr);
}
mvaddstr(LINES - 1, x, buffer);
CRT_cursorX = x + strlen(buffer);
x += strlen(buffer);
}
attrset(CRT_colors[RESET_COLOR]);
if (setCursor) {
CRT_cursorX = x;
curs_set(1);
} else {
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 + 1, buffer);
attrset(CRT_colors[RESET_COLOR]);
currentLen += strlen(buffer) + 1;
}
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {
int x = 0;
for (int i = 0; i < this->size; i++) {
x += strlen(this->keys[i]);
x += strlen(this->keys.constKeys[i]);
x += strlen(this->functions[i]);
if (pos < x) {
return this->events[i];

View File

@ -1,39 +1,39 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_FunctionBar
#define HEADER_FunctionBar
/*
htop - FunctionBar.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include <stdbool.h>
typedef struct FunctionBar_ {
int size;
char** functions;
union {
char** keys;
const char* const* constKeys;
} keys;
int* events;
bool staticData;
} FunctionBar;
FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc);
FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events);
extern FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc);
void FunctionBar_delete(FunctionBar* this);
extern FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
extern void FunctionBar_delete(FunctionBar* this);
void FunctionBar_draw(const FunctionBar* this);
extern void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor);
extern void FunctionBar_draw(const FunctionBar* this, char* buffer);
void FunctionBar_append(const char* buffer, int attr);
extern void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
extern int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);
#endif

View File

@ -1,172 +1,323 @@
/*
htop - Hashtable.c
(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.
*/
#include "config.h" // IWYU pragma: keep
#include "Hashtable.h"
#include "XAlloc.h"
#include <stdlib.h>
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*{
#include <stdbool.h>
#include "CRT.h"
#include "Macros.h"
#include "XUtils.h"
typedef struct Hashtable_ Hashtable;
typedef void(*Hashtable_PairFunction)(int, void*, void*);
typedef struct HashtableItem {
unsigned int key;
typedef struct HashtableItem_ {
ht_key_t key;
size_t probe;
void* value;
struct HashtableItem* next;
} HashtableItem;
struct Hashtable_ {
int size;
HashtableItem** buckets;
int items;
size_t size;
HashtableItem* buckets;
size_t items;
bool owner;
};
}*/
#ifdef DEBUG
static bool Hashtable_isConsistent(Hashtable* this) {
int items = 0;
for (int i = 0; i < this->size; i++) {
HashtableItem* bucket = this->buckets[i];
while (bucket) {
#ifndef NDEBUG
static void Hashtable_dump(const Hashtable* this) {
fprintf(stderr, "Hashtable %p: size=%zu items=%zu owner=%s\n",
(const void*)this,
this->size,
this->items,
this->owner ? "yes" : "no");
size_t items = 0;
for (size_t i = 0; i < this->size; i++) {
fprintf(stderr, " item %5zu: key = %5u probe = %2zu 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++;
bucket = bucket->next;
}
}
return items == this->items;
}
int Hashtable_count(Hashtable* this) {
int items = 0;
for (int i = 0; i < this->size; i++) {
HashtableItem* bucket = this->buckets[i];
while (bucket) {
items++;
bucket = bucket->next;
fprintf(stderr, "Hashtable %p: items=%zu counted=%zu\n",
(const void*)this,
this->items,
items);
}
static bool Hashtable_isConsistent(const Hashtable* this) {
size_t items = 0;
for (size_t i = 0; i < this->size; i++) {
if (this->buckets[i].value)
items++;
}
bool res = items == this->items;
if (!res)
Hashtable_dump(this);
return res;
}
size_t Hashtable_count(const Hashtable* this) {
size_t items = 0;
for (size_t i = 0; i < this->size; i++) {
if (this->buckets[i].value)
items++;
}
assert(items == this->items);
return items;
}
#endif
#endif /* NDEBUG */
static HashtableItem* HashtableItem_new(unsigned int key, void* value) {
HashtableItem* this;
/* https://oeis.org/A014234 */
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));
this->key = key;
this->value = value;
this->next = NULL;
return this;
static uint64_t nextPrime(size_t n) {
/* on 32-bit make sure we do not return primes not fitting in size_t */
for (size_t i = 0; i < ARRAYSIZE(OEISprimes) && OEISprimes[i] < SIZE_MAX; i++) {
if (n <= OEISprimes[i])
return OEISprimes[i];
}
Hashtable* Hashtable_new(int size, bool owner) {
CRT_fatalError("Hashtable: no prime found");
}
Hashtable* Hashtable_new(size_t size, bool owner) {
Hashtable* this;
this = xMalloc(sizeof(Hashtable));
this->items = 0;
this->size = size;
this->buckets = (HashtableItem**) xCalloc(size, sizeof(HashtableItem*));
this->size = size ? nextPrime(size) : 13;
this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem));
this->owner = owner;
assert(Hashtable_isConsistent(this));
return this;
}
void Hashtable_delete(Hashtable* this) {
assert(Hashtable_isConsistent(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);
}
}
Hashtable_clear(this);
free(this->buckets);
free(this);
}
void Hashtable_put(Hashtable* this, unsigned int key, void* value) {
unsigned int index = key % this->size;
HashtableItem** bucketPtr = &(this->buckets[index]);
while (true)
if (*bucketPtr == NULL) {
*bucketPtr = HashtableItem_new(key, value);
this->items++;
break;
} else if ((*bucketPtr)->key == key) {
void Hashtable_clear(Hashtable* this) {
assert(Hashtable_isConsistent(this));
if (this->owner)
free((*bucketPtr)->value);
(*bucketPtr)->value = value;
break;
} else
bucketPtr = &((*bucketPtr)->next);
for (size_t 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));
}
void* Hashtable_remove(Hashtable* this, unsigned int key) {
unsigned int index = key % this->size;
static void insert(Hashtable* this, ht_key_t key, void* value) {
size_t index = key % this->size;
size_t probe = 0;
#ifndef NDEBUG
size_t origIndex = index;
#endif
for (;;) {
if (!this->buckets[index].value) {
this->items++;
this->buckets[index].key = key;
this->buckets[index].probe = probe;
this->buckets[index].value = value;
return;
}
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);
}
}
void Hashtable_setSize(Hashtable* this, size_t 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 (size <= this->items)
return;
HashtableItem* oldBuckets = this->buckets;
size_t oldSize = this->size;
this->size = nextPrime(size);
this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem));
this->items = 0;
/* rehash */
for (size_t 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, ht_key_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) {
if (SIZE_MAX / 2 < this->size)
CRT_fatalError("Hashtable: size overflow");
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, ht_key_t key) {
size_t index = key % this->size;
size_t probe = 0;
#ifndef NDEBUG
size_t 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(value);
assert(Hashtable_isConsistent(this));
return NULL;
free(this->buckets[index].value);
} else {
assert(Hashtable_isConsistent(this));
return value;
}
}
}
assert(Hashtable_isConsistent(this));
return NULL;
res = this->buckets[index].value;
}
inline void* Hashtable_get(Hashtable* this, unsigned int key) {
unsigned int index = key % this->size;
HashtableItem* bucketPtr = this->buckets[index];
while (true) {
if (bucketPtr == NULL) {
assert(Hashtable_isConsistent(this));
return NULL;
} else if (bucketPtr->key == key) {
assert(Hashtable_isConsistent(this));
return bucketPtr->value;
} else
bucketPtr = bucketPtr->next;
size_t 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, ht_key_t key) {
size_t index = key % this->size;
size_t probe = 0;
void* res = NULL;
#ifndef NDEBUG
size_t 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) {
assert(Hashtable_isConsistent(this));
for (int i = 0; i < this->size; i++) {
HashtableItem* walk = this->buckets[i];
while (walk != NULL) {
for (size_t i = 0; i < this->size; i++) {
HashtableItem* walk = &this->buckets[i];
if (walk->value)
f(walk->key, walk->value, userData);
walk = walk->next;
}
}
assert(Hashtable_isConsistent(this));
}

View File

@ -1,49 +1,42 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Hashtable
#define HEADER_Hashtable
/*
htop - Hashtable.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include <stdbool.h>
#include <stddef.h>
typedef unsigned int ht_key_t;
typedef void(*Hashtable_PairFunction)(ht_key_t key, void* value, void* userdata);
typedef struct Hashtable_ Hashtable;
typedef void(*Hashtable_PairFunction)(int, void*, void*);
#ifndef NDEBUG
typedef struct HashtableItem {
unsigned int key;
void* value;
struct HashtableItem* next;
} HashtableItem;
size_t Hashtable_count(const Hashtable* this);
struct Hashtable_ {
int size;
HashtableItem** buckets;
int items;
bool owner;
};
#endif /* NDEBUG */
#ifdef DEBUG
Hashtable* Hashtable_new(size_t size, bool owner);
extern int Hashtable_count(Hashtable* this);
#endif
extern Hashtable* Hashtable_new(int size, bool owner);
extern void Hashtable_delete(Hashtable* this);
extern void Hashtable_put(Hashtable* this, unsigned int key, void* value);
extern void* Hashtable_remove(Hashtable* this, unsigned int key);
extern void* Hashtable_get(Hashtable* this, unsigned int key);
extern void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);
void Hashtable_delete(Hashtable* this);
void Hashtable_clear(Hashtable* this);
void Hashtable_setSize(Hashtable* this, size_t size);
void Hashtable_put(Hashtable* this, ht_key_t key, void* value);
void* Hashtable_remove(Hashtable* this, ht_key_t key);
void* Hashtable_get(Hashtable* this, ht_key_t key);
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);
#endif

View File

@ -1,44 +1,24 @@
/*
htop - Header.c
(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.
*/
#include "Header.h"
#include "CRT.h"
#include "StringUtils.h"
#include "Platform.h"
#include <assert.h>
#include <time.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*{
#include "Meter.h"
#include "Settings.h"
#include "Vector.h"
#include "CRT.h"
#include "Macros.h"
#include "Object.h"
#include "Platform.h"
#include "ProvideCurses.h"
#include "XUtils.h"
typedef struct Header_ {
Vector** columns;
Settings* settings;
struct ProcessList_* pl;
int nrColumns;
int pad;
int height;
} Header;
}*/
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef Header_forEachColumn
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
#endif
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) {
Header* this = xCalloc(1, sizeof(Header));
@ -89,11 +69,11 @@ void Header_writeBackToSettings(const Header* this) {
for (int i = 0; i < len; i++) {
Meter* meter = (Meter*) Vector_get(vec, i);
char* name = xCalloc(64, sizeof(char));
char* name;
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 {
xSnprintf(name, 63, "%s", As_Meter(meter)->name);
xAsprintf(&name, "%s", As_Meter(meter)->name);
}
colSettings->names[i] = name;
colSettings->modes[i] = meter->mode;
@ -108,11 +88,12 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
int param = 0;
if (paren) {
int ok = sscanf(paren, "(%10d)", &param);
if (!ok) param = 0;
if (!ok)
param = 0;
*paren = '\0';
}
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)) {
Meter* meter = Meter_new(this->pl, param, *type);
Vector_add(meters, meter);
@ -120,8 +101,10 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
break;
}
}
if (paren)
*paren = '(';
return mode;
}
@ -130,11 +113,12 @@ void Header_setMode(Header* this, int i, MeterModeId mode, int column) {
if (i >= Vector_size(meters))
return;
Meter* meter = (Meter*) Vector_get(meters, i);
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];
Meter* meter = Meter_new(this->pl, param, type);
@ -147,21 +131,6 @@ int Header_size(Header* this, int column) {
return Vector_size(meters);
}
char* Header_readMeterName(Header* this, int i, int column) {
Vector* meters = this->columns[column];
Meter* meter = (Meter*) Vector_get(meters, i);
int nameLen = strlen(Meter_name(meter));
int len = nameLen + 100;
char* name = xMalloc(len);
memcpy(name, Meter_name(meter), nameLen);
name[nameLen] = '\0';
if (meter->param)
xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
return name;
}
MeterModeId Header_readMeterMode(Header* this, int i, int column) {
Vector* meters = this->columns[column];
@ -173,11 +142,12 @@ void Header_reinit(Header* this) {
Header_forEachColumn(this, col) {
for (int i = 0; i < Vector_size(this->columns[col]); i++) {
Meter* meter = (Meter*) Vector_get(this->columns[col], i);
if (Meter_initFn(meter))
if (Meter_initFn(meter)) {
Meter_init(meter);
}
}
}
}
void Header_draw(const Header* this) {
int height = this->height;
@ -211,7 +181,7 @@ int Header_calculateHeight(Header* this) {
Meter* meter = (Meter*) Vector_get(meters, i);
height += meter->h;
}
maxHeight = MAX(maxHeight, height);
maxHeight = MAXIMUM(maxHeight, height);
}
this->height = maxHeight;
this->pad = pad;

View File

@ -1,60 +1,50 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Header
#define HEADER_Header
/*
htop - Header.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
#include "ProcessList.h"
#include "Settings.h"
#include "Vector.h"
typedef struct Header_ {
Vector** columns;
Settings* settings;
struct ProcessList_* pl;
ProcessList* pl;
int nrColumns;
int pad;
int height;
} Header;
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef Header_forEachColumn
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
#endif
extern Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns);
Header* Header_new(ProcessList* pl, Settings* settings, int nrColumns);
extern void Header_delete(Header* this);
void Header_delete(Header* this);
extern void Header_populateFromSettings(Header* this);
void Header_populateFromSettings(Header* this);
extern void Header_writeBackToSettings(const Header* this);
void Header_writeBackToSettings(const Header* this);
extern MeterModeId Header_addMeterByName(Header* this, char* name, int column);
MeterModeId Header_addMeterByName(Header* this, char* name, int column);
extern void Header_setMode(Header* this, int i, MeterModeId mode, int column);
void Header_setMode(Header* this, int i, MeterModeId mode, int column);
extern Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column);
Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, int column);
extern int Header_size(Header* this, int column);
int Header_size(Header* this, int column);
extern char* Header_readMeterName(Header* this, int i, int column);
MeterModeId Header_readMeterMode(Header* this, int i, int column);
extern MeterModeId Header_readMeterMode(Header* this, int i, int column);
void Header_reinit(Header* this);
extern void Header_reinit(Header* this);
void Header_draw(const Header* this);
extern void Header_draw(const Header* this);
extern int Header_calculateHeight(Header* this);
int Header_calculateHeight(Header* this);
#endif

View File

@ -1,30 +1,30 @@
/*
htop - HostnameMeter.c
(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.
*/
#include "HostnameMeter.h"
#include "config.h" // IWYU pragma: keep
#include "CRT.h"
#include "HostnameMeter.h"
#include <unistd.h>
/*{
#include "Meter.h"
}*/
#include "CRT.h"
#include "Object.h"
int HostnameMeter_attributes[] = {
static const int HostnameMeter_attributes[] = {
HOSTNAME
};
static void HostnameMeter_updateValues(Meter* this, char* buffer, int size) {
static void HostnameMeter_updateValues(Meter* this, char* buffer, size_t size) {
(void) this;
gethostname(buffer, size - 1);
}
MeterClass HostnameMeter_class = {
const MeterClass HostnameMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete

View File

@ -1,18 +1,14 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_HostnameMeter
#define HEADER_HostnameMeter
/*
htop - HostnameMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
extern int HostnameMeter_attributes[];
extern MeterClass HostnameMeter_class;
extern const MeterClass HostnameMeter_class;
#endif

132
IncSet.c
View File

@ -1,51 +1,24 @@
/*
htop - IncSet.c
(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.
*/
#include "config.h" // IWYU pragma: keep
#include "IncSet.h"
#include "StringUtils.h"
#include "Panel.h"
#include "ListItem.h"
#include "CRT.h"
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
/*{
#include "CRT.h"
#include "ListItem.h"
#include "Object.h"
#include "ProvideCurses.h"
#include "XUtils.h"
#include "FunctionBar.h"
#include "Panel.h"
#include <stdbool.h>
#define INCMODE_MAX 40
typedef enum {
INC_SEARCH = 0,
INC_FILTER = 1
} IncType;
#define IncSet_filter(inc_) (inc_->filtering ? inc_->modes[INC_FILTER].buffer : NULL)
typedef struct IncMode_ {
char buffer[INCMODE_MAX+1];
int index;
FunctionBar* bar;
bool isFilter;
} IncMode;
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
FunctionBar* defaultBar;
bool filtering;
bool found;
} IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
}*/
static void IncMode_reset(IncMode* mode) {
mode->index = 0;
@ -56,9 +29,9 @@ void IncSet_reset(IncSet* this, IncType type) {
IncMode_reset(&this->modes[type]);
}
static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL};
static const char* const searchKeys[] = {"F3", "Esc", " "};
static int searchEvents[] = {KEY_F(3), 27, ERR};
static const char* const searchFunctions[] = {"Next ", "Prev ", "Cancel ", " Search: ", NULL};
static const char* const searchKeys[] = {"F3", "S-F3", "Esc", " "};
static const int searchEvents[] = {KEY_F(3), KEY_F(15), 27, ERR};
static inline void IncMode_initSearch(IncMode* search) {
memset(search, 0, sizeof(IncMode));
@ -68,7 +41,7 @@ static inline void IncMode_initSearch(IncMode* search) {
static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
static const char* const filterKeys[] = {"Enter", "Esc", " "};
static int filterEvents[] = {13, 27, ERR};
static const int filterEvents[] = {13, 27, ERR};
static inline void IncMode_initFilter(IncMode* filter) {
memset(filter, 0, sizeof(IncMode));
@ -81,12 +54,13 @@ static inline void IncMode_done(IncMode* mode) {
}
IncSet* IncSet_new(FunctionBar* bar) {
IncSet* this = xCalloc(1, sizeof(IncSet));
IncSet* this = xMalloc(sizeof(IncSet));
IncMode_initSearch(&(this->modes[INC_SEARCH]));
IncMode_initFilter(&(this->modes[INC_FILTER]));
this->active = NULL;
this->filtering = false;
this->defaultBar = bar;
this->filtering = false;
this->found = false;
return this;
}
@ -106,7 +80,10 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
ListItem* line = (ListItem*)Vector_get(lines, i);
if (String_contains_i(line->value, incFilter)) {
Panel_add(panel, (Object*)line);
if (selected == (Object*)line) Panel_setSelected(panel, n);
if (selected == (Object*)line) {
Panel_setSelected(panel, n);
}
n++;
}
}
@ -114,26 +91,23 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
for (int i = 0; i < Vector_size(lines); i++) {
Object* line = Vector_get(lines, i);
Panel_add(panel, line);
if (selected == line) Panel_setSelected(panel, i);
if (selected == line) {
Panel_setSelected(panel, i);
}
}
}
}
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel);
bool found = false;
for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
found = true;
break;
return true;
}
}
if (found)
FunctionBar_draw(mode->bar, mode->buffer);
else
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
return found;
return false;
}
static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
@ -142,9 +116,16 @@ static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getP
int i = here;
for (;;) {
i += step;
if (i == size) i = 0;
if (i == -1) i = size - 1;
if (i == here) return false;
if (i == size) {
i = 0;
}
if (i == -1) {
i = size - 1;
}
if (i == here) {
return false;
}
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
return true;
@ -152,36 +133,33 @@ static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getP
}
}
bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
return IncMode_find(&this->modes[type], panel, getPanelValue, 1);
}
bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
return IncMode_find(&this->modes[type], panel, getPanelValue, -1);
}
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
if (ch == ERR)
return true;
IncMode* mode = this->active;
int size = Panel_size(panel);
bool filterChanged = false;
bool doSearch = true;
if (ch == KEY_F(3)) {
if (size == 0) return true;
IncMode_find(mode, panel, getPanelValue, 1);
if (ch == KEY_F(3) || ch == KEY_F(15)) {
if (size == 0)
return true;
IncMode_find(mode, panel, getPanelValue, ch == KEY_F(3) ? 1 : -1);
doSearch = false;
} else if (ch < 255 && isprint((char)ch)) {
} else if (0 < ch && ch < 255 && isprint((unsigned char)ch)) {
if (mode->index < INCMODE_MAX) {
mode->buffer[mode->index] = ch;
mode->index++;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
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) {
if (mode->index > 0) {
mode->index--;
mode->buffer[mode->index] = 0;
@ -196,7 +174,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
doSearch = false;
}
} else if (ch == KEY_RESIZE) {
Panel_resize(panel, COLS, LINES-panel->y-1);
doSearch = (mode->index > 0);
} else {
if (mode->isFilter) {
filterChanged = true;
@ -211,7 +189,6 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
}
this->active = NULL;
Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar, NULL);
doSearch = false;
}
if (doSearch) {
@ -225,22 +202,19 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
const char* IncSet_getListItemValue(Panel* panel, int i) {
ListItem* l = (ListItem*) Panel_get(panel, i);
if (l)
return l->value;
return "";
return l ? l->value : "";
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
FunctionBar_draw(this->active->bar, this->active->buffer);
panel->currentBar = this->active->bar;
}
void IncSet_drawBar(IncSet* this) {
void IncSet_drawBar(const IncSet* this) {
if (this->active) {
FunctionBar_draw(this->active->bar, this->active->buffer);
FunctionBar_drawExtra(this->active->bar, this->active->buffer, (this->active->isFilter || this->found) ? -1 : CRT_colors[FAILED_SEARCH], true);
} else {
FunctionBar_draw(this->defaultBar, NULL);
FunctionBar_draw(this->defaultBar);
}
}

View File

@ -1,18 +1,18 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_IncSet
#define HEADER_IncSet
/*
htop - IncSet.h
(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.
*/
#include <stdbool.h>
#include <stddef.h>
#include "FunctionBar.h"
#include "Panel.h"
#include <stdbool.h>
#include "Vector.h"
#define INCMODE_MAX 40
@ -21,8 +21,6 @@ typedef enum {
INC_FILTER = 1
} IncType;
#define IncSet_filter(inc_) (inc_->filtering ? inc_->modes[INC_FILTER].buffer : NULL)
typedef struct IncMode_ {
char buffer[INCMODE_MAX + 1];
int index;
@ -38,27 +36,26 @@ typedef struct IncSet_ {
bool found;
} 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);
void IncSet_reset(IncSet* this, IncType type);
extern void IncSet_reset(IncSet* this, IncType type);
IncSet* IncSet_new(FunctionBar* bar);
extern IncSet* IncSet_new(FunctionBar* bar);
void IncSet_delete(IncSet* this);
extern void IncSet_delete(IncSet* this);
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
extern bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
const char* IncSet_getListItemValue(Panel* panel, int i);
extern bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
extern bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
void IncSet_drawBar(const IncSet* this);
extern const char* IncSet_getListItemValue(Panel* panel, int i);
extern void IncSet_activate(IncSet* this, IncType type, Panel* panel);
extern void IncSet_drawBar(IncSet* this);
extern int IncSet_synthesizeEvent(IncSet* this, int x);
int IncSet_synthesizeEvent(IncSet* this, int x);
#endif

View File

@ -1,67 +1,32 @@
#include "config.h" // IWYU pragma: keep
#include "InfoScreen.h"
#include "config.h"
#include "Object.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "CRT.h"
#include "IncSet.h"
#include "ListItem.h"
#include "Platform.h"
#include "StringUtils.h"
#include "Object.h"
#include "ProvideCurses.h"
#include "XUtils.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
/*{
#include "Process.h"
#include "Panel.h"
#include "FunctionBar.h"
#include "IncSet.h"
typedef struct InfoScreen_ InfoScreen;
typedef void(*InfoScreen_Scan)(InfoScreen*);
typedef void(*InfoScreen_Draw)(InfoScreen*);
typedef void(*InfoScreen_OnErr)(InfoScreen*);
typedef bool(*InfoScreen_OnKey)(InfoScreen*, int);
typedef struct InfoScreenClass_ {
ObjectClass super;
const InfoScreen_Scan scan;
const InfoScreen_Draw draw;
const InfoScreen_OnErr onErr;
const InfoScreen_OnKey onKey;
} InfoScreenClass;
#define As_InfoScreen(this_) ((InfoScreenClass*)(((InfoScreen*)(this_))->super.klass))
#define InfoScreen_scan(this_) As_InfoScreen(this_)->scan((InfoScreen*)(this_))
#define InfoScreen_draw(this_) As_InfoScreen(this_)->draw((InfoScreen*)(this_))
#define InfoScreen_onErr(this_) As_InfoScreen(this_)->onErr((InfoScreen*)(this_))
#define InfoScreen_onKey(this_, ch_) As_InfoScreen(this_)->onKey((InfoScreen*)(this_), ch_)
struct InfoScreen_ {
Object super;
Process* process;
Panel* display;
FunctionBar* bar;
IncSet* inc;
Vector* lines;
};
}*/
static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL};
static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
static const 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;
if (!bar) {
bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents);
}
this->display = Panel_new(0, 1, COLS, height, false, Class(ListItem), bar);
this->display = Panel_new(0, 1, COLS, height, Class(ListItem), false, bar);
this->inc = IncSet_new(bar);
this->lines = Vector_new(this->display->items->type, true, DEFAULT_SIZE);
Panel_setHeader(this->display, panelHeader);
@ -78,42 +43,54 @@ InfoScreen* InfoScreen_done(InfoScreen* this) {
void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
char title[COLS + 1];
int len = vsnprintf(title, sizeof(title), fmt, ap);
va_end(ap);
if (len > COLS) {
memset(&title[COLS - 3], '.', 3);
}
attrset(CRT_colors[METER_TEXT]);
mvhline(0, 0, ' ', COLS);
wmove(stdscr, 0, 0);
vw_printw(stdscr, fmt, ap);
mvaddstr(0, 0, title);
attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true;
Panel_draw(this->display, true);
Panel_draw(this->display, true, true, true, false);
IncSet_drawBar(this->inc);
va_end(ap);
}
void InfoScreen_addLine(InfoScreen* this, const char* line) {
Vector_add(this->lines, (Object*) ListItem_new(line, 0));
const char* incFilter = IncSet_filter(this->inc);
if (!incFilter || String_contains_i(line, incFilter))
Panel_add(this->display, (Object*)Vector_get(this->lines, Vector_size(this->lines)-1));
if (!incFilter || String_contains_i(line, incFilter)) {
Panel_add(this->display, Vector_get(this->lines, Vector_size(this->lines) - 1));
}
}
void InfoScreen_appendLine(InfoScreen* this, const char* line) {
ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines) - 1);
ListItem_append(last, line);
const char* incFilter = IncSet_filter(this->inc);
if (incFilter && Panel_get(this->display, Panel_size(this->display)-1) != (Object*)last && String_contains_i(line, incFilter))
if (incFilter && Panel_get(this->display, Panel_size(this->display) - 1) != (Object*)last && String_contains_i(line, incFilter)) {
Panel_add(this->display, (Object*)last);
}
}
void InfoScreen_run(InfoScreen* this) {
Panel* panel = this->display;
if (As_InfoScreen(this)->scan) InfoScreen_scan(this);
if (As_InfoScreen(this)->scan)
InfoScreen_scan(this);
InfoScreen_draw(this);
bool looping = true;
while (looping) {
Panel_draw(panel, true);
Panel_draw(panel, false, true, true, false);
IncSet_drawBar(this->inc);
if (this->inc->active) {
(void) move(LINES - 1, CRT_cursorX);
@ -159,20 +136,25 @@ void InfoScreen_run(InfoScreen* this) {
break;
case KEY_F(5):
clear();
if (As_InfoScreen(this)->scan) InfoScreen_scan(this);
if (As_InfoScreen(this)->scan)
InfoScreen_scan(this);
InfoScreen_draw(this);
break;
case '\014': // Ctrl+L
clear();
InfoScreen_draw(this);
break;
case 'q':
case 27:
case 'q':
case KEY_F(10):
looping = false;
break;
case KEY_RESIZE:
Panel_resize(panel, COLS, LINES - 2);
if (As_InfoScreen(this)->scan)
InfoScreen_scan(this);
InfoScreen_draw(this);
break;
default:

View File

@ -1,14 +1,24 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_InfoScreen
#define HEADER_InfoScreen
#include "Process.h"
#include "Panel.h"
#include <stdbool.h>
#include "FunctionBar.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;
IncSet* inc;
Vector* lines;
} InfoScreen;
typedef void(*InfoScreen_Scan)(InfoScreen*);
typedef void(*InfoScreen_Draw)(InfoScreen*);
@ -16,38 +26,30 @@ typedef void(*InfoScreen_OnErr)(InfoScreen*);
typedef bool(*InfoScreen_OnKey)(InfoScreen*, int);
typedef struct InfoScreenClass_ {
ObjectClass super;
const ObjectClass super;
const InfoScreen_Scan scan;
const InfoScreen_Draw draw;
const InfoScreen_OnErr onErr;
const InfoScreen_OnKey onKey;
} 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_draw(this_) As_InfoScreen(this_)->draw((InfoScreen*)(this_))
#define InfoScreen_onErr(this_) As_InfoScreen(this_)->onErr((InfoScreen*)(this_))
#define InfoScreen_onKey(this_, ch_) As_InfoScreen(this_)->onKey((InfoScreen*)(this_), ch_)
struct InfoScreen_ {
Object super;
Process* process;
Panel* display;
FunctionBar* bar;
IncSet* inc;
Vector* lines;
};
InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader);
extern InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, const char* panelHeader);
InfoScreen* InfoScreen_done(InfoScreen* this);
extern InfoScreen* InfoScreen_done(InfoScreen* this);
ATTR_FORMAT(printf, 2, 3)
void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...);
extern void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...);
void InfoScreen_addLine(InfoScreen* this, const char* line);
extern void InfoScreen_addLine(InfoScreen* this, const char* line);
void InfoScreen_appendLine(InfoScreen* this, const char* line);
extern void InfoScreen_appendLine(InfoScreen* this, const char* line);
extern void InfoScreen_run(InfoScreen* this);
void InfoScreen_run(InfoScreen* this);
#endif

View File

@ -1,31 +1,22 @@
/*
htop - ListItem.c
(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.
*/
#include "config.h" // IWYU pragma: keep
#include "ListItem.h"
#include "CRT.h"
#include "StringUtils.h"
#include "RichString.h"
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
/*{
#include "Object.h"
#include "CRT.h"
#include "RichString.h"
#include "XUtils.h"
typedef struct ListItem_ {
Object super;
char* value;
int key;
bool moving;
} ListItem;
}*/
static void ListItem_delete(Object* cast) {
ListItem* this = (ListItem*)cast;
@ -33,16 +24,12 @@ static void ListItem_delete(Object* cast) {
free(this);
}
static void ListItem_display(Object* cast, RichString* out) {
ListItem* const this = (ListItem*)cast;
static void ListItem_display(const Object* cast, RichString* out) {
const ListItem* const this = (const ListItem*)cast;
assert (this != NULL);
/*
int len = strlen(this->value)+1;
char buffer[len+1];
xSnprintf(buffer, len, "%s", this->value);
*/
if (this->moving) {
RichString_write(out, CRT_colors[DEFAULT_COLOR],
RichString_writeWide(out, CRT_colors[DEFAULT_COLOR],
#ifdef HAVE_LIBNCURSESW
CRT_utf8 ? "" :
#endif
@ -50,15 +37,9 @@ static void ListItem_display(Object* cast, RichString* out) {
} else {
RichString_prune(out);
}
RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value/*buffer*/);
RichString_appendWide(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* this = AllocThis(ListItem);
this->value = xStrdup(value);
@ -68,20 +49,22 @@ ListItem* ListItem_new(const char* value, int key) {
}
void ListItem_append(ListItem* this, const char* text) {
int oldLen = strlen(this->value);
int textLen = strlen(text);
int newLen = strlen(this->value) + textLen;
size_t oldLen = strlen(this->value);
size_t textLen = strlen(text);
size_t newLen = oldLen + textLen;
this->value = xRealloc(this->value, newLen + 1);
memcpy(this->value + oldLen, text, textLen);
this->value[newLen] = '\0';
}
const char* ListItem_getRef(ListItem* this) {
return this->value;
}
long ListItem_compare(const void* cast1, const void* cast2) {
ListItem* obj1 = (ListItem*) cast1;
ListItem* obj2 = (ListItem*) cast2;
static int ListItem_compare(const void* cast1, const void* cast2) {
const ListItem* obj1 = (const ListItem*) cast1;
const ListItem* obj2 = (const ListItem*) cast2;
return strcmp(obj1->value, obj2->value);
}
const ObjectClass ListItem_class = {
.display = ListItem_display,
.delete = ListItem_delete,
.compare = ListItem_compare
};

View File

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

View File

@ -1,58 +1,100 @@
/*
htop - LoadAverageMeter.c
(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.
*/
#include "LoadAverageMeter.h"
#include "CRT.h"
#include "Object.h"
#include "Platform.h"
#include "RichString.h"
#include "XUtils.h"
/*{
#include "Meter.h"
}*/
int LoadAverageMeter_attributes[] = {
LOAD_AVERAGE_ONE, LOAD_AVERAGE_FIVE, LOAD_AVERAGE_FIFTEEN
static const int LoadAverageMeter_attributes[] = {
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 const int OK_attributes[] = {
METER_VALUE_OK
};
static const int Medium_attributes[] = {
METER_VALUE_WARN
};
static const int High_attributes[] = {
METER_VALUE_ERROR
};
static void LoadAverageMeter_updateValues(Meter* this, char* buffer, size_t size) {
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
// only show bar for 1min value
this->curItems = 1;
// change bar color and total based on value
if (this->values[0] < 1.0) {
this->curAttributes = OK_attributes;
this->total = 1.0;
} else if (this->values[0] < this->pl->cpuCount) {
this->curAttributes = Medium_attributes;
this->total = this->pl->cpuCount;
} else {
this->curAttributes = High_attributes;
this->total = 2 * this->pl->cpuCount;
}
xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
}
static void LoadAverageMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
static void LoadAverageMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast;
char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
RichString_writeAscii(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
}
static void LoadMeter_updateValues(Meter* this, char* buffer, int size) {
static void LoadMeter_updateValues(Meter* this, char* buffer, size_t size) {
double five, fifteen;
Platform_getLoadAverage(&this->values[0], &five, &fifteen);
if (this->values[0] > this->total) {
this->total = this->values[0];
// change bar color and total based on value
if (this->values[0] < 1.0) {
this->curAttributes = OK_attributes;
this->total = 1.0;
} else if (this->values[0] < this->pl->cpuCount) {
this->curAttributes = Medium_attributes;
this->total = this->pl->cpuCount;
} else {
this->curAttributes = High_attributes;
this->total = 2 * this->pl->cpuCount;
}
xSnprintf(buffer, size, "%.2f", this->values[0]);
}
static void LoadMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
static void LoadMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast;
char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", ((Meter*)this)->values[0]);
RichString_write(out, CRT_colors[LOAD], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_writeAscii(out, CRT_colors[LOAD], buffer);
}
MeterClass LoadAverageMeter_class = {
const MeterClass LoadAverageMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -69,7 +111,7 @@ MeterClass LoadAverageMeter_class = {
.caption = "Load average: "
};
MeterClass LoadMeter_class = {
const MeterClass LoadMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,

View File

@ -1,22 +1,16 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_LoadAverageMeter
#define HEADER_LoadAverageMeter
/*
htop - LoadAverageMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
extern int LoadAverageMeter_attributes[];
extern const MeterClass LoadAverageMeter_class;
extern int LoadMeter_attributes[];
extern MeterClass LoadAverageMeter_class;
extern MeterClass LoadMeter_class;
extern const MeterClass LoadMeter_class;
#endif

62
Macros.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef HEADER_Macros
#define HEADER_Macros
#include <assert.h> // IWYU pragma: keep
#ifndef MINIMUM
#define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAXIMUM
#define MAXIMUM(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef CLAMP
#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

View File

@ -2,47 +2,30 @@
htop - ColumnsPanel.c
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "MainPanel.h"
#include "Process.h"
#include "Platform.h"
#include "CRT.h"
#include <ctype.h>
#include <stdlib.h>
/*{
#include "Panel.h"
#include "Action.h"
#include "CRT.h"
#include "FunctionBar.h"
#include "Platform.h"
#include "Process.h"
#include "ProcessList.h"
#include "ProvideCurses.h"
#include "Settings.h"
#include "XUtils.h"
typedef struct MainPanel_ {
Panel super;
State* state;
IncSet* inc;
Htop_Action *keys;
pid_t pidSearch;
} MainPanel;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
}*/
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this);
if (mode) {
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");
}
FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree ");
}
void MainPanel_pidSearch(MainPanel* this, int ch) {
@ -61,6 +44,11 @@ void MainPanel_pidSearch(MainPanel* this, int ch) {
}
}
static const char* MainPanel_getValue(Panel* this, int i) {
const Process* p = (const Process*) Panel_get(this, i);
return Process_getCommand(p);
}
static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
MainPanel* this = (MainPanel*) super;
@ -68,22 +56,33 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK;
/* Let supervising ScreenManager handle resize */
if (ch == KEY_RESIZE)
return IGNORED;
/* reset on every normal key */
if (ch != ERR)
this->state->hideProcessSelection = false;
if (EVENT_IS_HEADER_CLICK(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;
int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) {
Settings_invertSortOrder(settings);
if (settings->treeView && settings->treeViewAlwaysByPID) {
settings->treeView = false;
settings->direction = 1;
reaction |= Action_setSortKey(settings, field);
} else if (field == Settings_getActiveSortKey(settings)) {
Settings_invertSortOrder(settings);
} else {
reaction |= Action_setSortKey(settings, field);
}
reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS;
result = HANDLED;
} else if (ch != ERR && this->inc->active) {
bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL);
bool filterChanged = IncSet_handleKey(this->inc, ch, super, MainPanel_getValue, NULL);
if (filterChanged) {
this->state->pl->incFilter = IncSet_filter(this->inc);
reaction = HTOP_REFRESH | HTOP_REDRAW_BAR;
@ -94,11 +93,12 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
}
result = HANDLED;
} else if (ch == 27) {
this->state->hideProcessSelection = true;
return HANDLED;
} else if (ch != ERR && ch > 0 && ch < KEY_MAX && this->keys[ch]) {
reaction |= (this->keys[ch])(this->state);
result = HANDLED;
} else if (isdigit(ch)) {
} else if (0 < ch && ch < 255 && isdigit((unsigned char)ch)) {
MainPanel_pidSearch(this, ch);
} else {
if (ch != ERR) {
@ -110,13 +110,12 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc);
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
result |= REDRAW;
}
if (reaction & HTOP_REFRESH) {
result |= REDRAW;
result |= REFRESH;
}
if (reaction & HTOP_RECALCULATE) {
result |= RESCAN;
@ -129,7 +128,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
}
if (!(reaction & HTOP_KEEP_FOLLOWING)) {
this->state->pl->following = -1;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
}
return result;
}
@ -142,13 +141,6 @@ int MainPanel_selectedPid(MainPanel* this) {
return -1;
}
const char* MainPanel_getValue(MainPanel* this, int i) {
Process* p = (Process*) Panel_get((Panel*)this, i);
if (p)
return p->comm;
return "";
}
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
Panel* super = (Panel*) this;
bool ok = true;
@ -162,19 +154,43 @@ bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Ar
}
if (!anyTagged) {
Process* p = (Process*) Panel_getSelected(super);
if (p) ok = fn(p, arg) && ok;
if (p) {
ok &= fn(p, arg);
}
}
if (wasAnyTagged)
*wasAnyTagged = anyTagged;
return ok;
}
PanelClass MainPanel_class = {
static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) {
MainPanel* this = (MainPanel*) super;
// Do not hide active search and filter bar.
if (hideFunctionBar && !this->inc->active)
return;
IncSet_drawBar(this->inc);
if (this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}
}
static void MainPanel_printHeader(Panel* super) {
MainPanel* this = (MainPanel*) super;
ProcessList_printHeader(this->state->pl, &super->header);
}
const PanelClass MainPanel_class = {
.super = {
.extends = Class(Panel),
.delete = MainPanel_delete
},
.eventHandler = MainPanel_eventHandler
.eventHandler = MainPanel_eventHandler,
.drawFunctionBar = MainPanel_drawFunctionBar,
.printHeader = MainPanel_printHeader
};
MainPanel* MainPanel_new() {

View File

@ -1,18 +1,24 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_MainPanel
#define HEADER_MainPanel
/*
htop - ColumnsPanel.h
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "config.h" // IWYU pragma: keep
#include <stdbool.h>
#include <sys/types.h>
#include "Action.h"
#include "Settings.h"
#include "IncSet.h"
#include "Object.h"
#include "Panel.h"
#include "Process.h"
typedef struct MainPanel_ {
Panel super;
@ -26,23 +32,20 @@ typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
extern void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
void MainPanel_pidSearch(MainPanel* this, int ch);
extern void MainPanel_pidSearch(MainPanel* this, int ch);
int MainPanel_selectedPid(MainPanel* this);
extern int MainPanel_selectedPid(MainPanel* this);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
extern const char* MainPanel_getValue(MainPanel* this, int i);
extern const PanelClass MainPanel_class;
extern bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
MainPanel* MainPanel_new(void);
extern PanelClass MainPanel_class;
void MainPanel_setState(MainPanel* this, State* state);
extern MainPanel* MainPanel_new();
extern void MainPanel_setState(MainPanel* this, State* state);
extern void MainPanel_delete(Object* object);
void MainPanel_delete(Object* object);
#endif

View File

@ -1,67 +1,162 @@
ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = subdir-objects
bin_PROGRAMS = htop
dist_man_MANS = htop.1
EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png scripts/MakeHeader.py \
EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png htop.svg \
install-sh autogen.sh missing
applicationsdir = $(datadir)/applications
applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png
appicondir = $(datadir)/icons/hicolor/scalable/apps
appicon_DATA = htop.svg
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_CPPFLAGS = -DNDEBUG
myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \
ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \
DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \
BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \
TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \
HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \
InfoScreen.c XAlloc.c
myhtopsources = \
Action.c \
Affinity.c \
AffinityPanel.c \
AvailableColumnsPanel.c \
AvailableMetersPanel.c \
BatteryMeter.c \
CategoriesPanel.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 \
CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \
CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \
Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \
BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \
TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \
AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \
EnvScreen.h InfoScreen.h XAlloc.h
all_platform_headers =
myhtopheaders = \
Action.h \
Affinity.h \
AffinityPanel.h \
AvailableColumnsPanel.h \
AvailableMetersPanel.h \
BatteryMeter.h \
CPUMeter.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_platform_headers = \
linux/Platform.h \
linux/IOPriorityPanel.h \
linux/IOPriority.h \
linux/IOPriorityPanel.h \
linux/LibSensors.h \
linux/LinuxProcess.h \
linux/LinuxProcessList.h \
linux/LinuxCRT.h \
linux/Battery.h \
linux/Platform.h \
linux/PressureStallMeter.h \
linux/ProcessField.h \
linux/SELinuxMeter.h \
linux/SystemdMeter.h \
linux/ZramMeter.h \
linux/ZramStats.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
all_platform_headers += $(linux_platform_headers)
zfs/ZfsArcStats.h \
zfs/ZfsCompressedArcMeter.h
if HTOP_LINUX
AM_LDFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \
myhtopplatsources = \
linux/IOPriorityPanel.c \
linux/LibSensors.c \
linux/LinuxProcess.c \
linux/LinuxProcessList.c \
linux/Platform.c \
linux/PressureStallMeter.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
linux/SELinuxMeter.c \
linux/SystemdMeter.c \
linux/ZramMeter.c \
zfs/ZfsArcMeter.c \
zfs/ZfsArcStats.c \
zfs/ZfsCompressedArcMeter.c
myhtopplatheaders = $(linux_platform_headers)
endif
@ -70,21 +165,18 @@ endif
# -------
freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \
freebsd/FreeBSDCRT.h \
freebsd/Battery.h \
freebsd/Platform.h \
freebsd/ProcessField.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
all_platform_headers += $(freebsd_platform_headers)
if HTOP_FREEBSD
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
myhtopplatheaders = $(freebsd_platform_headers)
@ -94,18 +186,16 @@ endif
# ------------
dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/DragonFlyBSDCRT.h \
dragonflybsd/Battery.h
all_platform_headers += $(dragonflybsd_platform_headers)
dragonflybsd/Platform.h \
dragonflybsd/ProcessField.h
if HTOP_DRAGONFLYBSD
AM_LDFLAGS += -lkvm -lkinfo -lexecinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c
myhtopplatsources = \
dragonflybsd/Platform.c \
dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c
myhtopplatheaders = $(dragonflybsd_platform_headers)
endif
@ -114,17 +204,14 @@ endif
# -------
openbsd_platform_headers = \
openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h \
openbsd/OpenBSDCRT.h \
openbsd/Battery.h
all_platform_headers += $(openbsd_platform_headers)
openbsd/Platform.h \
openbsd/ProcessField.h
if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c
openbsd/OpenBSDProcess.c
myhtopplatheaders = $(openbsd_platform_headers)
endif
@ -133,22 +220,19 @@ endif
# ------
darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
darwin/DarwinCRT.h \
darwin/Battery.h \
darwin/Platform.h \
darwin/ProcessField.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
all_platform_headers += $(darwin_platform_headers)
if HTOP_DARWIN
AM_LDFLAGS += -framework IOKit -framework CoreFoundation
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
myhtopplatheaders = $(darwin_platform_headers)
@ -159,20 +243,16 @@ endif
solaris_platform_headers = \
solaris/Platform.h \
solaris/ProcessField.h \
solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \
solaris/SolarisCRT.h \
solaris/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
all_platform_headers += $(solaris_platform_headers)
if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \
solaris/SolarisCRT.c solaris/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(solaris_platform_headers)
@ -183,53 +263,36 @@ endif
unsupported_platform_headers = \
unsupported/Platform.h \
unsupported/ProcessField.h \
unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h \
unsupported/Battery.h
all_platform_headers += $(unsupported_platform_headers)
unsupported/UnsupportedProcessList.h
if HTOP_UNSUPPORTED
myhtopplatsources = unsupported/Platform.c \
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \
unsupported/UnsupportedCRT.c unsupported/Battery.c
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c
myhtopplatheaders = $(unsupported_platform_headers)
endif
# ----
SUFFIXES = .h
BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
nodist_htop_SOURCES = config.h
.PHONY: htop-headers clean-htop-headers
htop-headers: $(myhtopheaders) $(all_platform_headers)
clean-htop-headers:
-rm -f $(myhtopheaders) $(all_platform_headers)
target:
echo $(htop_SOURCES)
profile:
$(MAKE) all CFLAGS="-pg" AM_CPPFLAGS="-pg -O2 -DNDEBUG"
$(MAKE) all AM_CPPFLAGS="-pg -O2 -DNDEBUG"
debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG"
$(MAKE) all AM_CPPFLAGS="-ggdb -DDEBUG"
coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"
.c.h:
./scripts/MakeHeader.py $<
$(MAKE) all AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" AM_LDFLAGS="-lgcov"
cppcheck:
cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS
cppcheck -q -v . --enable=all -DHAVE_OPENVZ
dist-hook: $(top_distdir)/configure
@if grep 'pkg_m4_absent' '$(top_distdir)/configure'; then \

View File

@ -1,60 +1,54 @@
/*
htop - MemoryMeter.c
(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.
*/
#include "MemoryMeter.h"
#include "CRT.h"
#include "Object.h"
#include "Platform.h"
#include "RichString.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/param.h>
#include <assert.h>
/*{
#include "Meter.h"
}*/
int MemoryMeter_attributes[] = {
MEMORY_USED, MEMORY_BUFFERS, MEMORY_CACHE
static const int MemoryMeter_attributes[] = {
MEMORY_USED,
MEMORY_BUFFERS,
MEMORY_CACHE
};
static void MemoryMeter_updateValues(Meter* this, char* buffer, int size) {
static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) {
int written;
Platform_setMemoryValues(this);
written = Meter_humanUnit(buffer, this->values[0], size);
buffer += written;
if ((size -= written) > 0) {
*buffer++ = '/';
size--;
METER_BUFFER_CHECK(buffer, size, written);
METER_BUFFER_APPEND_CHR(buffer, size, '/');
Meter_humanUnit(buffer, this->total, size);
}
}
static void MemoryMeter_display(Object* cast, RichString* out) {
static void MemoryMeter_display(const Object* cast, RichString* out) {
char buffer[50];
Meter* this = (Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], 50);
RichString_append(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[MEMORY_USED], buffer);
Meter_humanUnit(buffer, this->values[1], 50);
RichString_append(out, CRT_colors[METER_TEXT], " buffers:");
RichString_append(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
Meter_humanUnit(buffer, this->values[2], 50);
RichString_append(out, CRT_colors[METER_TEXT], " cache:");
RichString_append(out, CRT_colors[MEMORY_CACHE], buffer);
const Meter* this = (const Meter*)cast;
RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer);
Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:");
RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
Meter_humanUnit(buffer, this->values[2], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:");
RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer);
}
MeterClass MemoryMeter_class = {
const MeterClass MemoryMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,

View File

@ -1,18 +1,14 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_MemoryMeter
#define HEADER_MemoryMeter
/*
htop - MemoryMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
extern int MemoryMeter_attributes[];
extern MeterClass MemoryMeter_class;
extern const MeterClass MemoryMeter_class;
#endif

292
Meter.c
View File

@ -1,152 +1,59 @@
/*
htop - Meter.c
(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.
*/
#include "config.h" // IWYU pragma: keep
#include "Meter.h"
#include "RichString.h"
#include "Object.h"
#include "CRT.h"
#include "StringUtils.h"
#include "ListItem.h"
#include "Settings.h"
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <sys/time.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define METER_BUFFER_LEN 256
#include "CRT.h"
#include "Macros.h"
#include "Object.h"
#include "ProvideCurses.h"
#include "RichString.h"
#include "Settings.h"
#include "XUtils.h"
#define GRAPH_DELAY (DEFAULT_DELAY/2)
#define GRAPH_HEIGHT 4 /* Unit: rows (lines) */
/*{
#include "ListItem.h"
#include <sys/time.h>
typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int);
typedef void(*Meter_UpdateValues)(Meter*, char*, int);
typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ {
ObjectClass super;
const Meter_Init init;
const Meter_Done done;
const Meter_UpdateMode updateMode;
const Meter_Draw draw;
const Meter_UpdateValues updateValues;
const int defaultMode;
const double total;
const int* attributes;
const char* name;
const char* uiName;
const char* caption;
const char* description;
const char maxItems;
char curItems;
} MeterClass;
#define As_Meter(this_) ((MeterClass*)((this_)->super.klass))
#define Meter_initFn(this_) As_Meter(this_)->init
#define Meter_init(this_) As_Meter(this_)->init((Meter*)(this_))
#define Meter_done(this_) As_Meter(this_)->done((Meter*)(this_))
#define Meter_updateModeFn(this_) As_Meter(this_)->updateMode
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_) As_Meter(this_)->draw
#define Meter_doneFn(this_) As_Meter(this_)->done
#define Meter_updateValues(this_, buf_, sz_) \
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_)
#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_name(this_) As_Meter(this_)->name
#define Meter_uiName(this_) As_Meter(this_)->uiName
struct Meter_ {
Object super;
Meter_Draw draw;
char* caption;
int mode;
int param;
void* drawData;
int h;
struct ProcessList_* pl;
double* values;
double total;
};
typedef struct MeterMode_ {
Meter_Draw draw;
const char* uiName;
int h;
} MeterMode;
typedef enum {
CUSTOM_METERMODE = 0,
BAR_METERMODE,
TEXT_METERMODE,
GRAPH_METERMODE,
LED_METERMODE,
LAST_METERMODE
} MeterModeId;
typedef struct GraphData_ {
struct timeval time;
double values[METER_BUFFER_LEN];
} GraphData;
}*/
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
MeterClass Meter_class = {
const MeterClass Meter_class = {
.super = {
.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));
Object_setClass(this, type);
this->h = 1;
this->param = param;
this->pl = pl;
type->curItems = type->maxItems;
this->values = xCalloc(type->maxItems, sizeof(double));
this->curItems = type->maxItems;
this->curAttributes = NULL;
this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL;
this->total = type->total;
this->caption = xStrdup(type->caption);
if (Meter_initFn(this))
if (Meter_initFn(this)) {
Meter_init(this);
}
Meter_setMode(this, type->defaultMode);
return this;
}
int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
int Meter_humanUnit(char* buffer, unsigned long int value, size_t size) {
const char* prefix = "KMGTPEZY";
unsigned long int powi = 1;
unsigned int written, powj = 1, precision = 2;
unsigned int powj = 1, precision = 2;
for (;;) {
if (value / 1024 < powi)
@ -168,15 +75,13 @@ int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
break;
}
written = snprintf(buffer, size, "%.*f%c",
precision, (double) value / powi, *prefix);
return written;
return snprintf(buffer, size, "%.*f%c", precision, (double) value / powi, *prefix);
}
void Meter_delete(Object* cast) {
if (!cast)
return;
Meter* this = (Meter*) cast;
if (Meter_doneFn(this)) {
Meter_done(this);
@ -192,30 +97,35 @@ void Meter_setCaption(Meter* this, const char* 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)) {
Object_display(this, out);
} else {
RichString_write(out, CRT_colors[Meter_attributes(this)[0]], buffer);
RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], buffer);
}
}
void Meter_setMode(Meter* this, int modeIndex) {
if (modeIndex > 0 && modeIndex == this->mode)
if (modeIndex > 0 && modeIndex == this->mode) {
return;
if (!modeIndex)
}
if (!modeIndex) {
modeIndex = 1;
}
assert(modeIndex < LAST_METERMODE);
if (Meter_defaultMode(this) == CUSTOM_METERMODE) {
this->draw = Meter_drawFn(this);
if (Meter_updateModeFn(this))
if (Meter_updateModeFn(this)) {
Meter_updateMode(this, modeIndex);
}
} else {
assert(modeIndex >= 1);
free(this->drawData);
this->drawData = NULL;
MeterMode* mode = Meter_modes[modeIndex];
const MeterMode* mode = Meter_modes[modeIndex];
this->draw = mode->draw;
this->h = mode->h;
}
@ -223,18 +133,20 @@ void Meter_setMode(Meter* this, int modeIndex) {
}
ListItem* Meter_toListItem(Meter* this, bool moving) {
char mode[21];
if (this->mode)
xSnprintf(mode, 20, " [%s]", Meter_modes[this->mode]->uiName);
else
char mode[20];
if (this->mode) {
xSnprintf(mode, sizeof(mode), " [%s]", Meter_modes[this->mode]->uiName);
} else {
mode[0] = '\0';
char number[11];
if (this->param > 0)
xSnprintf(number, 10, " %d", this->param);
else
}
char number[10];
if (this->param > 0) {
xSnprintf(number, sizeof(number), " %d", this->param);
} else {
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);
li->moving = moving;
return li;
@ -244,17 +156,21 @@ ListItem* Meter_toListItem(Meter* this, bool moving) {
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
(void) w;
Meter_updateValues(this, buffer, sizeof(buffer));
attrset(CRT_colors[METER_TEXT]);
mvaddstr(y, x, this->caption);
mvaddnstr(y, x, this->caption, w - 1);
attrset(CRT_colors[RESET_COLOR]);
int captionLen = strlen(this->caption);
x += captionLen;
attrset(CRT_colors[RESET_COLOR]);
w -= captionLen;
if (w <= 0)
return;
RichString_begin(out);
Meter_displayBuffer(this, buffer, &out);
RichString_printVal(out, y, x);
RichString_printoffnVal(out, y, x, 0, w - 1);
RichString_end(out);
}
@ -264,7 +180,7 @@ static const char BarMeterMode_characters[] = "|#*@$%&.";
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
Meter_updateValues(this, buffer, sizeof(buffer));
w -= 2;
attrset(CRT_colors[METER_TEXT]);
@ -274,25 +190,45 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
w -= captionLen;
attrset(CRT_colors[BAR_BORDER]);
mvaddch(y, x, '[');
mvaddch(y, x + w, ']');
mvaddch(y, x + MAXIMUM(w, 0), ']');
attrset(CRT_colors[RESET_COLOR]);
w--;
x++;
if (w < 1) {
attrset(CRT_colors[RESET_COLOR]);
if (w < 1)
return;
// The text in the bar is right aligned;
// Pad with maximal spaces and then calculate needed starting position offset
RichString_begin(bar);
RichString_appendChr(&bar, ' ', w);
RichString_appendWide(&bar, 0, buffer);
int startPos = RichString_sizeVal(bar) - w;
if (startPos > w) {
// Text is too large for bar
// Truncate meter text at a space character
for (int pos = 2 * w; pos > w; pos--) {
if (RichString_getCharVal(bar, pos) == ' ') {
while (pos > w && RichString_getCharVal(bar, pos - 1) == ' ')
pos--;
startPos = pos - w;
break;
}
char bar[w + 1];
}
// If still to large, print the start not the end
startPos = MINIMUM(startPos, w);
}
assert(startPos >= 0);
assert(startPos <= w);
assert(startPos + w <= RichString_sizeVal(bar));
int blockSizes[10];
xSnprintf(bar, w + 1, "%*.*s", w, w, buffer);
// First draw in the bar[] buffer...
int offset = 0;
int items = Meter_getItems(this);
for (int i = 0; i < items; i++) {
for (uint8_t i = 0; i < this->curItems; i++) {
double value = this->values[i];
value = CLAMP(value, 0.0, this->total);
if (value > 0) {
@ -304,11 +240,11 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// (Control against invalid values)
nextOffset = CLAMP(nextOffset, 0, w);
for (int j = offset; j < nextOffset; j++)
if (bar[j] == ' ') {
if (RichString_getCharVal(bar, startPos + j) == ' ') {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
bar[j] = BarMeterMode_characters[i];
RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]);
} else {
bar[j] = '|';
RichString_setChar(&bar, startPos + j, '|');
}
}
offset = nextOffset;
@ -316,17 +252,20 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// ...then print the buffer.
offset = 0;
for (int i = 0; i < items; i++) {
attrset(CRT_colors[Meter_attributes(this)[i]]);
mvaddnstr(y, x + offset, bar + offset, blockSizes[i]);
for (uint8_t i = 0; i < this->curItems; i++) {
int attr = this->curAttributes ? this->curAttributes[i] : Meter_attributes(this)[i];
RichString_setAttrn(&bar, CRT_colors[attr], startPos + offset, blockSizes[i]);
RichString_printoffnVal(bar, y, x + offset, startPos + offset, MINIMUM(blockSizes[i], w - offset));
offset += blockSizes[i];
offset = CLAMP(offset, 0, w);
}
if (offset < w) {
attrset(CRT_colors[BAR_SHADOW]);
mvaddnstr(y, x + offset, bar + offset, w - offset);
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, w - offset);
RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
}
RichString_end(bar);
move(y, x + w + 1);
attrset(CRT_colors[RESET_COLOR]);
}
@ -353,15 +292,16 @@ static const char* const GraphMeterMode_dotsAscii[] = {
/*20*/":", /*21*/":", /*22*/":"
};
static const char* const* GraphMeterMode_dots;
static int GraphMeterMode_pixPerRow;
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
if (!this->drawData) this->drawData = xCalloc(1, sizeof(GraphData));
GraphData* data = (GraphData*) this->drawData;
if (!this->drawData) {
this->drawData = xCalloc(1, sizeof(GraphData));
}
GraphData* data = this->drawData;
const int nValues = METER_BUFFER_LEN;
const char* const* GraphMeterMode_dots;
int GraphMeterMode_pixPerRow;
#ifdef HAVE_LIBNCURSESW
if (CRT_utf8) {
GraphMeterMode_dots = GraphMeterMode_dotsUtf8;
@ -382,18 +322,18 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
struct timeval now;
gettimeofday(&now, NULL);
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));
for (int i = 0; i < nValues - 1; i++)
data->values[i] = data->values[i + 1];
char buffer[nValues];
Meter_updateValues(this, buffer, nValues - 1);
char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, sizeof(buffer));
double value = 0.0;
int items = Meter_getItems(this);
for (int i = 0; i < items; i++)
for (uint8_t i = 0; i < this->curItems; i++)
value += this->values[i];
value /= this->total;
data->values[nValues - 1] = value;
@ -458,7 +398,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
LEDMeterMode_digits = LEDMeterMode_digitsAscii;
char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
Meter_updateValues(this, buffer, sizeof(buffer));
RichString_begin(out);
Meter_displayBuffer(this, buffer, &out);
@ -473,7 +413,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
int xx = x + strlen(this->caption);
int len = RichString_sizeVal(out);
for (int i = 0; i < len; i++) {
char c = RichString_getCharVal(out, i);
int c = RichString_getCharVal(out, i);
if (c >= '0' && c <= '9') {
LEDMeterMode_drawDigit(xx, y, c - 48);
xx += 4;
@ -510,7 +450,7 @@ static MeterMode LEDMeterMode = {
.draw = LEDMeterMode_draw,
};
MeterMode* Meter_modes[] = {
const MeterMode* const Meter_modes[] = {
NULL,
&BarMeterMode,
&TextMeterMode,
@ -521,23 +461,21 @@ MeterMode* Meter_modes[] = {
/* Blank meter */
static void BlankMeter_updateValues(Meter* this, char* buffer, int size) {
(void) this; (void) buffer; (void) size;
static void BlankMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t size) {
if (size > 0) {
*buffer = 0;
}
}
static void BlankMeter_display(Object* cast, RichString* out) {
(void) cast;
static void BlankMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
RichString_prune(out);
}
int BlankMeter_attributes[] = {
static const int BlankMeter_attributes[] = {
DEFAULT_COLOR
};
MeterClass BlankMeter_class = {
const MeterClass BlankMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,

135
Meter.h
View File

@ -1,34 +1,59 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Meter
#define HEADER_Meter
/*
htop - Meter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define METER_BUFFER_LEN 256
#define GRAPH_DELAY (DEFAULT_DELAY/2)
#define GRAPH_HEIGHT 4 /* Unit: rows (lines) */
#include "ListItem.h"
#include "config.h" // IWYU pragma: keep
#include <stdbool.h>
#include <stdint.h>
#include <sys/time.h>
#include "ListItem.h"
#include "Object.h"
#include "ProcessList.h"
#define METER_BUFFER_LEN 256
#define METER_BUFFER_CHECK(buffer, size, written) \
do { \
if ((written) < 0 || (size_t)(written) >= (size)) { \
return; \
} \
(buffer) += (written); \
(size) -= (size_t)(written); \
} while (0)
#define METER_BUFFER_APPEND_CHR(buffer, size, c) \
do { \
if ((size) < 2) { \
return; \
} \
*(buffer)++ = c; \
*(buffer) = '\0'; \
(size)--; \
if ((size) == 0) { \
return; \
} \
} while (0)
struct Meter_;
typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int);
typedef void(*Meter_UpdateValues)(Meter*, char*, int);
typedef void(*Meter_UpdateValues)(Meter*, char*, size_t);
typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ {
ObjectClass super;
const ObjectClass super;
const Meter_Init init;
const Meter_Done done;
const Meter_UpdateMode updateMode;
@ -36,16 +61,15 @@ typedef struct MeterClass_ {
const Meter_UpdateValues updateValues;
const int defaultMode;
const double total;
const int* attributes;
const char* name;
const char* uiName;
const char* caption;
const char* description;
const char maxItems;
char curItems;
const int* const attributes;
const char* const name; /* internal name of the meter, must not contain any space */
const char* const uiName; /* display name in header setup menu */
const char* const caption; /* prefix in the actual header */
const char* const description; /* optional meter description in header setup menu */
const uint8_t maxItems;
} 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_init(this_) As_Meter(this_)->init((Meter*)(this_))
#define Meter_done(this_) As_Meter(this_)->done((Meter*)(this_))
@ -56,12 +80,15 @@ typedef struct MeterClass_ {
#define Meter_updateValues(this_, buf_, sz_) \
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_)
#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_name(this_) As_Meter(this_)->name
#define Meter_uiName(this_) As_Meter(this_)->uiName
typedef struct GraphData_ {
struct timeval time;
double values[METER_BUFFER_LEN];
} GraphData;
struct Meter_ {
Object super;
Meter_Draw draw;
@ -69,11 +96,14 @@ struct Meter_ {
char* caption;
int mode;
int param;
void* drawData;
GraphData* drawData;
int h;
struct ProcessList_* pl;
const ProcessList* pl;
uint8_t curItems;
const int* curAttributes;
double* values;
double total;
void* meterData;
};
typedef struct MeterMode_ {
@ -91,61 +121,22 @@ typedef enum {
LAST_METERMODE
} MeterModeId;
typedef struct GraphData_ {
struct timeval time;
double values[METER_BUFFER_LEN];
} GraphData;
extern const MeterClass Meter_class;
Meter* Meter_new(const ProcessList* pl, int param, const MeterClass* type);
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
int Meter_humanUnit(char* buffer, unsigned long int value, size_t size);
extern MeterClass Meter_class;
void Meter_delete(Object* cast);
extern Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type);
void Meter_setCaption(Meter* this, const char* caption);
extern int Meter_humanUnit(char* buffer, unsigned long int value, int size);
void Meter_setMode(Meter* this, int modeIndex);
extern void Meter_delete(Object* cast);
ListItem* Meter_toListItem(Meter* this, bool moving);
extern void Meter_setCaption(Meter* this, const char* caption);
extern const MeterMode* const Meter_modes[];
extern void Meter_setMode(Meter* this, int modeIndex);
extern ListItem* Meter_toListItem(Meter* this, bool moving);
/* ---------- TextMeterMode ---------- */
/* ---------- BarMeterMode ---------- */
/* ---------- GraphMeterMode ---------- */
#ifdef HAVE_LIBNCURSESW
#define PIXPERROW_UTF8 4
#endif
#define PIXPERROW_ASCII 2
/* ---------- LEDMeterMode ---------- */
#ifdef HAVE_LIBNCURSESW
#endif
extern MeterMode* Meter_modes[];
/* Blank meter */
extern int BlankMeter_attributes[];
extern MeterClass BlankMeter_class;
extern const MeterClass BlankMeter_class;
#endif

View File

@ -1,41 +1,28 @@
/*
htop - MetersPanel.c
(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.
*/
#include "MetersPanel.h"
#include <stdlib.h>
#include <assert.h>
#include "CRT.h"
#include "FunctionBar.h"
#include "Header.h"
#include "ListItem.h"
#include "Meter.h"
#include "Object.h"
#include "ProvideCurses.h"
/*{
#include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
typedef struct MetersPanel_ MetersPanel;
struct MetersPanel_ {
Panel super;
Settings* settings;
Vector* meters;
ScreenManager* scr;
MetersPanel* leftNeighbor;
MetersPanel* rightNeighbor;
bool moving;
};
}*/
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI
// we call them "Styles".
static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL};
static const char* const MetersKeys[] = {"Space", "Enter", "", "Del", "F10"};
static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
static const int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning.
@ -43,9 +30,16 @@ static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
// considered "Ambiguous characters".
static const char* const MetersMovingFunctions[] = {"Style ", "Lock ", "Up ", "Down ", "Left ", "Right ", " ", "Delete", "Done ", NULL};
static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-", "->", " ", "Del", "F10"};
static int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
static const int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
static FunctionBar* Meters_movingBar = NULL;
void MetersPanel_cleanup() {
if (Meters_movingBar) {
FunctionBar_delete(Meters_movingBar);
Meters_movingBar = NULL;
}
}
static void MetersPanel_delete(Object* object) {
Panel* super = (Panel*) object;
MetersPanel* this = (MetersPanel*) object;
@ -61,13 +55,12 @@ void MetersPanel_setMoving(MetersPanel* this, bool moving) {
selected->moving = moving;
}
if (!moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
Panel_setDefaultBar(super);
} else {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]);
Panel_setSelectionColor(super, PANEL_SELECTION_FOLLOW);
super->currentBar = Meters_movingBar;
}
FunctionBar_draw(this->super.currentBar, NULL);
}
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {
@ -189,7 +182,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
}
}
if (result == HANDLED || sideMove) {
Header* header = (Header*) this->scr->header;
Header* header = this->scr->header;
this->settings->changed = true;
Header_calculateHeight(header);
Header_draw(header);
@ -198,7 +191,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
return result;
}
PanelClass MetersPanel_class = {
const PanelClass MetersPanel_class = {
.super = {
.extends = Class(Panel),
.delete = MetersPanel_delete

View File

@ -1,18 +1,21 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_MetersPanel
#define HEADER_MetersPanel
/*
htop - MetersPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
#include <stdbool.h>
#include "Panel.h"
#include "ScreenManager.h"
#include "Settings.h"
#include "Vector.h"
struct MetersPanel_;
typedef struct MetersPanel_ MetersPanel;
struct MetersPanel_ {
@ -26,18 +29,12 @@ struct MetersPanel_ {
bool moving;
};
void MetersPanel_cleanup(void);
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI
// we call them "Styles".
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning.
// In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are
// considered "Ambiguous characters".
void MetersPanel_setMoving(MetersPanel* this, bool moving);
extern void MetersPanel_setMoving(MetersPanel* this, bool moving);
extern const PanelClass MetersPanel_class;
extern PanelClass MetersPanel_class;
extern MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr);
MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr);
#endif

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, size_t 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_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
return;
}
char buffer[64];
RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: ");
Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
RichString_appendAscii(out, CRT_colors[METER_TEXT], " tx: ");
Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff);
RichString_appendAscii(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,68 +2,32 @@
htop - Object.c
(C) 2004-2012 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Object.h"
/*{
#include "RichString.h"
#include "XAlloc.h"
#include <stddef.h>
typedef struct Object_ Object;
typedef void(*Object_Display)(Object*, RichString*);
typedef long(*Object_Compare)(const void*, const void*);
typedef void(*Object_Delete)(Object*);
#define Object_getClass(obj_) ((Object*)(obj_))->klass
#define Object_setClass(obj_, class_) Object_getClass(obj_) = (ObjectClass*) class_
#define Object_delete(obj_) Object_getClass(obj_)->delete((Object*)(obj_))
#define Object_displayFn(obj_) Object_getClass(obj_)->display
#define Object_display(obj_, str_) Object_getClass(obj_)->display((Object*)(obj_), str_)
#define Object_compare(obj_, other_) Object_getClass(obj_)->compare((const void*)(obj_), other_)
#define Class(class_) ((ObjectClass*)(&(class_ ## _class)))
#define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_));
typedef struct ObjectClass_ {
const void* extends;
const Object_Display display;
const Object_Delete delete;
const Object_Compare compare;
} ObjectClass;
struct Object_ {
ObjectClass* klass;
};
typedef union {
int i;
void* v;
} Arg;
}*/
ObjectClass Object_class = {
const ObjectClass Object_class = {
.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)
return false;
const ObjectClass* type = o->klass;
while (type) {
if (type == klass)
for (const ObjectClass* type = o->klass; type; type = type->extends) {
if (type == klass) {
return true;
type = type->extends;
}
}
return false;
}
#endif
#endif /* NDEBUG */

View File

@ -1,45 +1,53 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Object
#define HEADER_Object
/*
htop - Object.h
(C) 2004-2012 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "RichString.h"
#include "XAlloc.h"
#include "config.h" // IWYU pragma: keep
#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 void(*Object_Display)(Object*, RichString*);
typedef long(*Object_Compare)(const void*, const void*);
typedef void(*Object_Display)(const Object*, RichString*);
typedef int(*Object_Compare)(const void*, const void*);
typedef void(*Object_Delete)(Object*);
#define Object_getClass(obj_) ((Object*)(obj_))->klass
#define Object_setClass(obj_, class_) Object_getClass(obj_) = (ObjectClass*) class_
#define Object_getClass(obj_) ((const Object*)(obj_))->klass
#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_display(obj_, str_) Object_getClass(obj_)->display((Object*)(obj_), str_)
#define Object_compare(obj_, other_) Object_getClass(obj_)->compare((const void*)(obj_), other_)
#define Object_display(obj_, str_) (assert(Object_getClass(obj_)->display), Object_getClass(obj_)->display((const Object*)(obj_), str_))
#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_ {
const void* extends;
const void* const extends;
const Object_Display display;
const Object_Delete delete;
const Object_Compare compare;
} ObjectClass;
struct Object_ {
ObjectClass* klass;
const ObjectClass* klass;
};
typedef union {
@ -47,13 +55,12 @@ typedef union {
void* v;
} Arg;
extern const ObjectClass Object_class;
extern ObjectClass Object_class;
#ifndef NDEBUG
#ifdef DEBUG
bool Object_isA(const Object* o, const ObjectClass* klass);
extern bool Object_isA(Object* o, const ObjectClass* klass);
#endif
#endif /* NDEBUG */
#endif

View File

@ -1,33 +1,31 @@
/*
htop - OpenFilesScreen.c
(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.
*/
#include "config.h" // IWYU pragma: keep
#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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
/*{
#include "InfoScreen.h"
#include "Macros.h"
#include "Panel.h"
#include "ProvideCurses.h"
#include "Vector.h"
#include "XUtils.h"
typedef struct OpenFiles_Data_ {
char* data[256];
char* data[7];
} OpenFiles_Data;
typedef struct OpenFiles_ProcessData_ {
@ -41,77 +39,105 @@ typedef struct OpenFiles_FileData_ {
struct OpenFiles_FileData_* next;
} OpenFiles_FileData;
typedef struct OpenFilesScreen_ {
InfoScreen super;
pid_t pid;
} OpenFilesScreen;
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();
}
InfoScreenClass OpenFilesScreen_class = {
.super = {
.extends = Class(Object),
.delete = OpenFilesScreen_delete
},
.scan = OpenFilesScreen_scan,
.draw = OpenFilesScreen_draw
};
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(Process* process) {
OpenFilesScreen* OpenFilesScreen_new(const Process* process) {
OpenFilesScreen* this = xMalloc(sizeof(OpenFilesScreen));
Object_setClass(this, Class(OpenFilesScreen));
if (Process_isThread(process))
if (Process_isThread(process)) {
this->pid = process->tgid;
else
} else {
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 - 2, " FD TYPE MODE DEVICE SIZE NODE NAME");
}
void OpenFilesScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this));
}
void OpenFilesScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Snapshot of files open in process %d - %s", ((OpenFilesScreen*)this)->pid, this->process->comm);
static void OpenFilesScreen_draw(InfoScreen* this) {
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) {
char buffer[1025];
xSnprintf(buffer, 1024, "%d", pid);
OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_FileData* fdata = NULL;
OpenFiles_Data* item = &(pdata->data);
int fdpair[2];
int fdpair[2] = {0, 0};
if (pipe(fdpair) == -1) {
pdata->error = 1;
return pdata;
}
pid_t child = fork();
if (child == -1) {
close(fdpair[1]);
close(fdpair[0]);
pdata->error = 1;
return pdata;
}
if (child == 0) {
close(fdpair[0]);
dup2(fdpair[1], STDOUT_FILENO);
close(fdpair[1]);
int fdnull = open("/dev/null", O_WRONLY);
if (fdnull < 0)
if (fdnull < 0) {
exit(1);
}
dup2(fdnull, STDERR_FILENO);
close(fdnull);
char buffer[32] = {0};
xSnprintf(buffer, sizeof(buffer), "%d", pid);
execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
exit(127);
}
close(fdpair[1]);
OpenFiles_Data* item = &(pdata->data);
OpenFiles_FileData* fdata = NULL;
FILE* fd = fdopen(fdpair[0], "r");
if (!fd) {
pdata->error = 1;
return pdata;
}
for (;;) {
char* line = String_readLine(fd);
if (!line) {
break;
}
unsigned char cmd = line[0];
if (cmd == 'f') {
switch (cmd) {
case 'f': /* file descriptor */
{
OpenFiles_FileData* nextFile = xCalloc(1, sizeof(OpenFiles_FileData));
if (fdata == NULL) {
pdata->files = nextFile;
@ -120,30 +146,60 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
}
fdata = nextFile;
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);
}
fclose(fd);
int wstatus;
if (waitpid(child, &wstatus, 0) == -1) {
pdata->error = 1;
return pdata;
}
if (!WIFEXITED(wstatus))
if (!WIFEXITED(wstatus)) {
pdata->error = 1;
else
} else {
pdata->error = WEXITSTATUS(wstatus);
}
return pdata;
}
static inline void OpenFiles_Data_clear(OpenFiles_Data* data) {
for (int i = 0; i < 255; i++)
if (data->data[i])
static void OpenFiles_Data_clear(OpenFiles_Data* data) {
for (size_t i = 0; i < ARRAYSIZE(data->data); i++)
free(data->data[i]);
}
void OpenFilesScreen_scan(InfoScreen* this) {
static void OpenFilesScreen_scan(InfoScreen* this) {
Panel* panel = this->display;
int idx = Panel_getSelectedIndex(panel);
Panel_prune(panel);
@ -155,19 +211,20 @@ void OpenFilesScreen_scan(InfoScreen* this) {
} else {
OpenFiles_FileData* fdata = pdata->files;
while (fdata) {
char** data = fdata->data.data;
int lenN = data['n'] ? strlen(data['n']) : 0;
int sizeEntry = 5 + 7 + 10 + 10 + 10 + lenN + 5 /*spaces*/ + 1 /*null*/;
OpenFiles_Data* data = &fdata->data;
size_t lenN = strlen(getDataForType(data, 'n'));
size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + lenN + 7 /*spaces*/ + 1 /*null*/;
char entry[sizeEntry];
xSnprintf(entry, sizeEntry, "%5.5s %7.7s %10.10s %10.10s %10.10s %s",
data['f'] ? data['f'] : "",
data['t'] ? data['t'] : "",
data['D'] ? data['D'] : "",
data['s'] ? data['s'] : "",
data['i'] ? data['i'] : "",
data['n'] ? data['n'] : "");
xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %s",
getDataForType(data, 'f'),
getDataForType(data, 't'),
getDataForType(data, 'a'),
getDataForType(data, 'D'),
getDataForType(data, 's'),
getDataForType(data, 'i'),
getDataForType(data, 'n'));
InfoScreen_addLine(this, entry);
OpenFiles_Data_clear(&fdata->data);
OpenFiles_Data_clear(data);
OpenFiles_FileData* old = fdata;
fdata = fdata->next;
free(old);
@ -179,3 +236,12 @@ void OpenFilesScreen_scan(InfoScreen* this) {
Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx);
}
const InfoScreenClass OpenFilesScreen_class = {
.super = {
.extends = Class(Object),
.delete = OpenFilesScreen_delete
},
.scan = OpenFilesScreen_scan,
.draw = OpenFilesScreen_draw
};

View File

@ -1,45 +1,27 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_OpenFilesScreen
#define HEADER_OpenFilesScreen
/*
htop - OpenFilesScreen.h
(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.
*/
#include <sys/types.h>
#include "InfoScreen.h"
typedef struct OpenFiles_Data_ {
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;
#include "Object.h"
#include "Process.h"
typedef struct OpenFilesScreen_ {
InfoScreen super;
pid_t pid;
} OpenFilesScreen;
extern const InfoScreenClass OpenFilesScreen_class;
extern InfoScreenClass OpenFilesScreen_class;
OpenFilesScreen* OpenFilesScreen_new(const Process* process);
extern OpenFilesScreen* OpenFilesScreen_new(Process* process);
extern void OpenFilesScreen_delete(Object* this);
extern void OpenFilesScreen_draw(InfoScreen* this);
extern void OpenFilesScreen_scan(InfoScreen* this);
void OpenFilesScreen_delete(Object* this);
#endif

191
OptionItem.c Normal file
View File

@ -0,0 +1,191 @@
/*
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 "Macros.h"
#include "RichString.h"
#include "XUtils.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_writeAscii(out, CRT_colors[CHECK_BOX], "[");
if (CheckItem_get(this)) {
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
} else {
RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
}
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "] ");
RichString_appendWide(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_writeAscii(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_appendAscii(out, CRT_colors[CHECK_MARK], buffer);
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
for (int i = written; i < 5; i++) {
RichString_appendAscii(out, CRT_colors[CHECK_BOX], " ");
}
RichString_appendWide(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

272
Panel.c
View File

@ -1,91 +1,28 @@
/*
htop - Panel.c
(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.
*/
#include "Panel.h"
#include "CRT.h"
#include "RichString.h"
#include "ListItem.h"
#include "StringUtils.h"
#include <math.h>
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <strings.h>
//#link curses
#include "CRT.h"
#include "ListItem.h"
#include "Macros.h"
#include "ProvideCurses.h"
#include "RichString.h"
#include "XUtils.h"
/*{
#include "Object.h"
#include "Vector.h"
#include "FunctionBar.h"
typedef struct Panel_ Panel;
typedef enum HandlerResult_ {
HANDLED = 0x01,
IGNORED = 0x02,
BREAK_LOOP = 0x04,
REDRAW = 0x08,
RESCAN = 0x10,
SYNTH_KEY = 0x20,
} HandlerResult;
#define EVENT_SET_SELECTED -1
#define EVENT_HEADER_CLICK(x_) (-10000 + x_)
#define EVENT_IS_HEADER_CLICK(ev_) (ev_ >= -10000 && ev_ <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) (ev_ + 10000)
typedef HandlerResult(*Panel_EventHandler)(Panel*, int);
typedef struct PanelClass_ {
const ObjectClass super;
const Panel_EventHandler eventHandler;
} PanelClass;
#define As_Panel(this_) ((PanelClass*)((this_)->super.klass))
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
#define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_)
struct Panel_ {
Object super;
int x, y, w, h;
WINDOW* window;
Vector* items;
int selected;
int oldSelected;
int selectedLen;
void* eventHandlerState;
int scrollV;
short scrollH;
bool needsRedraw;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header;
int selectionColor;
};
#define Panel_setDefaultBar(this_) do{ (this_)->currentBar = (this_)->defaultBar; }while(0)
}*/
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#define KEY_CTRL(l) ((l)-'A'+1)
PanelClass Panel_class = {
const PanelClass Panel_class = {
.super = {
.extends = Class(Object),
.delete = Panel_delete
@ -93,7 +30,7 @@ PanelClass Panel_class = {
.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, const ObjectClass* type, bool owner, FunctionBar* fuBar) {
Panel* this;
this = xMalloc(sizeof(Panel));
Object_setClass(this, Class(Panel));
@ -107,7 +44,7 @@ void Panel_delete(Object* cast) {
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->y = y;
this->w = w;
@ -118,11 +55,13 @@ void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool
this->scrollH = 0;
this->selected = 0;
this->oldSelected = 0;
this->selectedLen = 0;
this->needsRedraw = true;
this->wasFocus = false;
RichString_beginAllocated(this->header);
this->defaultBar = fuBar;
this->currentBar = fuBar;
this->selectionColor = CRT_colors[PANEL_SELECTION_FOCUS];
this->selectionColorId = PANEL_SELECTION_FOCUS;
}
void Panel_done(Panel* this) {
@ -133,19 +72,12 @@ void Panel_done(Panel* this) {
RichString_end(this->header);
}
void Panel_setSelectionColor(Panel* this, int color) {
this->selectionColor = color;
}
RichString* Panel_getHeader(Panel* this) {
assert (this != NULL);
this->needsRedraw = true;
return &(this->header);
void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
this->selectionColorId = colorId;
}
inline void Panel_setHeader(Panel* this, const char* header) {
RichString_write(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
RichString_writeWide(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
this->needsRedraw = true;
}
@ -160,8 +92,6 @@ void Panel_move(Panel* this, int x, int y) {
void Panel_resize(Panel* this, int w, int h) {
assert (this != NULL);
if (RichString_sizeVal(this->header) > 0)
h--;
this->w = w;
this->h = h;
this->needsRedraw = true;
@ -208,34 +138,39 @@ Object* Panel_remove(Panel* this, int i) {
this->needsRedraw = true;
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--;
}
return removed;
}
Object* Panel_getSelected(Panel* this) {
assert (this != NULL);
if (Vector_size(this->items) > 0)
if (Vector_size(this->items) > 0) {
return Vector_get(this->items, this->selected);
else
} else {
return NULL;
}
}
void Panel_moveSelectedUp(Panel* this) {
assert (this != NULL);
Vector_moveUp(this->items, this->selected);
if (this->selected > 0)
if (this->selected > 0) {
this->selected--;
}
}
void Panel_moveSelectedDown(Panel* this) {
assert (this != NULL);
Vector_moveDown(this->items, this->selected);
if (this->selected + 1 < Vector_size(this->items))
if (this->selected + 1 < Vector_size(this->items)) {
this->selected++;
}
}
int Panel_getSelectedIndex(Panel* this) {
assert (this != NULL);
@ -256,8 +191,9 @@ void Panel_setSelected(Panel* this, int selected) {
if (selected >= size) {
selected = size - 1;
}
if (selected < 0)
if (selected < 0) {
selected = 0;
}
this->selected = selected;
if (Panel_eventHandlerFn(this)) {
Panel_eventHandler(this, EVENT_SET_SELECTED);
@ -272,7 +208,7 @@ void Panel_splice(Panel *this, Vector* from) {
this->needsRedraw = true;
}
void Panel_draw(Panel* this, bool focus) {
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar) {
assert (this != NULL);
int size = Vector_size(this->items);
@ -281,19 +217,29 @@ void Panel_draw(Panel* this, bool focus) {
int x = this->x;
int h = this->h;
int headerLen = RichString_sizeVal(this->header);
if (headerLen > 0) {
int attr = focus
if (hideFunctionBar)
h++;
const int header_attr = focus
? CRT_colors[PANEL_HEADER_FOCUS]
: CRT_colors[PANEL_HEADER_UNFOCUS];
attrset(attr);
if (force_redraw) {
if (Panel_printHeaderFn(this))
Panel_printHeader(this);
else
RichString_setAttr(&this->header, header_attr);
}
int headerLen = RichString_sizeVal(this->header);
if (headerLen > 0) {
attrset(header_attr);
mvhline(y, x, ' ', this->w);
if (scrollH < headerLen) {
RichString_printoffnVal(this->header, y, x, scrollH,
MIN(headerLen - scrollH, this->w));
MINIMUM(headerLen - scrollH, this->w));
}
attrset(CRT_colors[RESET_COLOR]);
y++;
h--;
}
// ensure scroll area is on screen
@ -301,7 +247,7 @@ void Panel_draw(Panel* this, bool focus) {
this->scrollV = 0;
this->needsRedraw = true;
} else if (this->scrollV >= size) {
this->scrollV = MAX(size - 1, 0);
this->scrollV = MAXIMUM(size - 1, 0);
this->needsRedraw = true;
}
// ensure selection is on screen
@ -314,31 +260,32 @@ void Panel_draw(Panel* this, bool focus) {
}
int first = this->scrollV;
int upTo = MIN(first + h, size);
int upTo = MINIMUM(first + h, size);
int selectionColor = focus
? this->selectionColor
? CRT_colors[this->selectionColorId]
: CRT_colors[PANEL_SELECTION_UNFOCUS];
if (this->needsRedraw) {
if (this->needsRedraw || force_redraw) {
int line = 0;
for (int i = first; line < h && i < upTo; i++) {
Object* itemObj = Vector_get(this->items, i);
assert(itemObj); if(!itemObj) continue;
RichString_begin(item);
Object_display(itemObj, &item);
int itemLen = RichString_sizeVal(item);
int amt = MIN(itemLen - scrollH, this->w);
bool selected = (i == this->selected);
if (selected) {
attrset(selectionColor);
RichString_setAttr(&item, selectionColor);
int amt = MINIMUM(itemLen - scrollH, this->w);
if (highlightSelected && i == this->selected) {
item.highlightAttr = selectionColor;
}
if (item.highlightAttr) {
attrset(item.highlightAttr);
RichString_setAttr(&item, item.highlightAttr);
this->selectedLen = itemLen;
}
mvhline(y + line, x, ' ', this->w);
if (amt > 0)
RichString_printoffnVal(item, y + line, x, scrollH, amt);
if (selected)
if (item.highlightAttr)
attrset(CRT_colors[RESET_COLOR]);
RichString_end(item);
line++;
@ -347,11 +294,9 @@ void Panel_draw(Panel* this, bool focus) {
mvhline(y + line, x, ' ', this->w);
line++;
}
this->needsRedraw = false;
} else {
Object* oldObj = Vector_get(this->items, this->oldSelected);
assert(oldObj);
RichString_begin(old);
Object_display(oldObj, &old);
int oldLen = RichString_sizeVal(old);
@ -363,88 +308,102 @@ void Panel_draw(Panel* this, bool focus) {
mvhline(y + this->oldSelected - first, x + 0, ' ', this->w);
if (scrollH < oldLen)
RichString_printoffnVal(old, y + this->oldSelected - first, x,
scrollH, MIN(oldLen - scrollH, this->w));
scrollH, MINIMUM(oldLen - scrollH, this->w));
attrset(selectionColor);
mvhline(y + this->selected - first, x + 0, ' ', this->w);
RichString_setAttr(&new, selectionColor);
if (scrollH < newLen)
RichString_printoffnVal(new, y + this->selected - first, x,
scrollH, MIN(newLen - scrollH, this->w));
scrollH, MINIMUM(newLen - scrollH, this->w));
attrset(CRT_colors[RESET_COLOR]);
RichString_end(new);
RichString_end(old);
}
if (focus && (this->needsRedraw || force_redraw || !this->wasFocus)) {
if (Panel_drawFunctionBarFn(this))
Panel_drawFunctionBar(this, hideFunctionBar);
else if (!hideFunctionBar)
FunctionBar_draw(this->currentBar);
}
this->oldSelected = this->selected;
this->wasFocus = focus;
this->needsRedraw = false;
move(0, 0);
}
static int Panel_headerHeight(const Panel* this) {
return RichString_sizeVal(this->header) > 0 ? 1 : 0;
}
bool Panel_onKey(Panel* this, int key) {
assert (this != NULL);
int size = Vector_size(this->items);
const int size = Vector_size(this->items);
#define PANEL_SCROLL(amount) \
do { \
this->selected += (amount); \
this->scrollV = CLAMP(this->scrollV + (amount), 0, MAXIMUM(0, (size - this->h - Panel_headerHeight(this)))); \
this->needsRedraw = true; \
} while (0)
switch (key) {
case KEY_DOWN:
case KEY_CTRL('N'):
this->selected++;
break;
case KEY_UP:
case KEY_CTRL('P'):
this->selected--;
break;
#ifdef KEY_C_DOWN
case KEY_C_DOWN:
#endif
this->selected++;
break;
#endif
case KEY_UP:
case KEY_CTRL('P'):
#ifdef KEY_C_UP
case KEY_C_UP:
#endif
this->selected--;
break;
#endif
case KEY_LEFT:
case KEY_CTRL('B'):
if (this->scrollH > 0) {
this->scrollH -= MAX(CRT_scrollHAmount, 0);
this->scrollH -= MAXIMUM(CRT_scrollHAmount, 0);
this->needsRedraw = true;
}
break;
case KEY_RIGHT:
case KEY_CTRL('F'):
this->scrollH += CRT_scrollHAmount;
this->needsRedraw = true;
break;
case KEY_PPAGE:
this->selected -= (this->h - 1);
this->scrollV = MAX(0, this->scrollV - this->h + 1);
this->needsRedraw = true;
PANEL_SCROLL(-(this->h - Panel_headerHeight(this)));
break;
case KEY_NPAGE:
this->selected += (this->h - 1);
this->scrollV = MAX(0, MIN(Vector_size(this->items) - this->h,
this->scrollV + this->h - 1));
this->needsRedraw = true;
PANEL_SCROLL(+(this->h - Panel_headerHeight(this)));
break;
case KEY_WHEELUP:
this->selected -= CRT_scrollWheelVAmount;
this->scrollV -= CRT_scrollWheelVAmount;
this->needsRedraw = true;
PANEL_SCROLL(-CRT_scrollWheelVAmount);
break;
case KEY_WHEELDOWN:
{
this->selected += CRT_scrollWheelVAmount;
this->scrollV += CRT_scrollWheelVAmount;
if (this->scrollV > Vector_size(this->items) - this->h) {
this->scrollV = Vector_size(this->items) - this->h;
}
this->needsRedraw = true;
PANEL_SCROLL(+CRT_scrollWheelVAmount);
break;
}
case KEY_HOME:
this->selected = 0;
break;
case KEY_END:
this->selected = size - 1;
break;
case KEY_CTRL('A'):
case '^':
this->scrollH = 0;
@ -452,13 +411,15 @@ bool Panel_onKey(Panel* this, int key) {
break;
case KEY_CTRL('E'):
case '$':
this->scrollH = MAX(this->selectedLen - this->w, 0);
this->scrollH = MAXIMUM(this->selectedLen - this->w, 0);
this->needsRedraw = true;
break;
default:
return false;
}
#undef PANEL_SCROLL
// ensure selection within bounds
if (this->selected < 0 || size == 0) {
this->selected = 0;
@ -467,43 +428,60 @@ bool Panel_onKey(Panel* this, int key) {
this->selected = size - 1;
this->needsRedraw = true;
}
return true;
}
HandlerResult Panel_selectByTyping(Panel* this, int ch) {
int size = Panel_size(this);
if (!this->eventHandlerState)
this->eventHandlerState = xCalloc(100, sizeof(char));
char* buffer = this->eventHandlerState;
if (ch > 0 && ch < 255 && isalnum(ch)) {
if (0 < ch && ch < 255 && isgraph((unsigned char)ch)) {
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) {
buffer[len] = ch;
buffer[len+1] = '\0';
}
for (int try = 0; try < 2; try++) {
len = strlen(buffer);
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++;
if (strncasecmp(cur, buffer, len) == 0) {
Panel_setSelected(this, i);
return HANDLED;
}
}
// if current word did not match,
// retry considering the character the start of a new word.
buffer[0] = ch;
buffer[1] = '\0';
}
return HANDLED;
} else if (ch != ERR) {
buffer[0] = '\0';
}
if (ch == 13) {
return BREAK_LOOP;
}
return IGNORED;
}

105
Panel.h
View File

@ -1,52 +1,62 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Panel
#define HEADER_Panel
/*
htop - Panel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
//#link curses
#include <stdbool.h>
#include "Object.h"
#include "Vector.h"
#include "CRT.h"
#include "FunctionBar.h"
#include "Object.h"
#include "RichString.h"
#include "Vector.h"
struct Panel_;
typedef struct Panel_ Panel;
typedef enum HandlerResult_ {
HANDLED = 0x01,
IGNORED = 0x02,
BREAK_LOOP = 0x04,
REDRAW = 0x08,
RESCAN = 0x10,
SYNTH_KEY = 0x20,
REFRESH = 0x08,
REDRAW = 0x10,
RESCAN = 0x20,
SYNTH_KEY = 0x40,
} HandlerResult;
#define EVENT_SET_SELECTED -1
#define EVENT_SET_SELECTED (-1)
#define EVENT_HEADER_CLICK(x_) (-10000 + x_)
#define EVENT_IS_HEADER_CLICK(ev_) (ev_ >= -10000 && ev_ <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) (ev_ + 10000)
#define EVENT_HEADER_CLICK(x_) (-10000 + (x_))
#define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000)
typedef HandlerResult (*Panel_EventHandler)(Panel*, int);
typedef void (*Panel_DrawFunctionBar)(Panel*, bool);
typedef void (*Panel_PrintHeader)(Panel*);
typedef struct PanelClass_ {
const ObjectClass super;
const Panel_EventHandler eventHandler;
const Panel_DrawFunctionBar drawFunctionBar;
const Panel_PrintHeader printHeader;
} 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_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_)
#define Panel_eventHandler(this_, ev_) (assert(As_Panel(this_)->eventHandler), As_Panel(this_)->eventHandler((Panel*)(this_), ev_))
#define Panel_drawFunctionBarFn(this_) As_Panel(this_)->drawFunctionBar
#define Panel_drawFunctionBar(this_, hideFB_) (assert(As_Panel(this_)->drawFunctionBar), As_Panel(this_)->drawFunctionBar((Panel*)(this_), hideFB_))
#define Panel_printHeaderFn(this_) As_Panel(this_)->printHeader
#define Panel_printHeader(this_) (assert(As_Panel(this_)->printHeader), As_Panel(this_)->printHeader((Panel*)(this_)))
struct Panel_ {
Object super;
int x, y, w, h;
WINDOW* window;
Vector* items;
int selected;
int oldSelected;
@ -55,74 +65,65 @@ struct Panel_ {
int scrollV;
short scrollH;
bool needsRedraw;
bool wasFocus;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header;
int selectionColor;
ColorElements selectionColorId;
};
#define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0)
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#define KEY_CTRL(l) ((l)-'A'+1)
extern PanelClass Panel_class;
extern const PanelClass Panel_class;
extern 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, const ObjectClass* type, bool owner, FunctionBar* fuBar);
extern void Panel_delete(Object* cast);
void Panel_delete(Object* cast);
extern 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);
extern void Panel_done(Panel* this);
void Panel_done(Panel* this);
extern void Panel_setSelectionColor(Panel* this, int color);
void Panel_setSelectionColor(Panel* this, ColorElements colorId);
extern RichString* Panel_getHeader(Panel* this);
void Panel_setHeader(Panel* this, const char* header);
extern void Panel_setHeader(Panel* this, const char* header);
void Panel_move(Panel* this, int x, int y);
extern void Panel_move(Panel* this, int x, int y);
void Panel_resize(Panel* this, int w, int h);
extern void Panel_resize(Panel* this, int w, int h);
void Panel_prune(Panel* this);
extern void Panel_prune(Panel* this);
void Panel_add(Panel* this, Object* o);
extern void Panel_add(Panel* this, Object* o);
void Panel_insert(Panel* this, int i, Object* o);
extern void Panel_insert(Panel* this, int i, Object* o);
void Panel_set(Panel* this, int i, Object* o);
extern void Panel_set(Panel* this, int i, Object* o);
Object* Panel_get(Panel* this, int i);
extern Object* Panel_get(Panel* this, int i);
Object* Panel_remove(Panel* this, int i);
extern Object* Panel_remove(Panel* this, int i);
Object* Panel_getSelected(Panel* this);
extern Object* Panel_getSelected(Panel* this);
void Panel_moveSelectedUp(Panel* this);
extern void Panel_moveSelectedUp(Panel* this);
void Panel_moveSelectedDown(Panel* this);
extern void Panel_moveSelectedDown(Panel* this);
int Panel_getSelectedIndex(Panel* this);
extern int Panel_getSelectedIndex(Panel* this);
int Panel_size(Panel* this);
extern int Panel_size(Panel* this);
void Panel_setSelected(Panel* this, int selected);
extern void Panel_setSelected(Panel* this, int selected);
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar);
extern void Panel_draw(Panel* this, bool focus);
void Panel_splice(Panel* this, Vector* from);
extern void Panel_splice(Panel *this, Vector* from);
bool Panel_onKey(Panel* this, int key);
extern bool Panel_onKey(Panel* this, int key);
extern HandlerResult Panel_selectByTyping(Panel* this, int ch);
HandlerResult Panel_selectByTyping(Panel* this, int ch);
#endif

645
Process.c
View File

@ -2,303 +2,154 @@
htop - Process.c
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Process.h"
#include "Settings.h"
#include "config.h" // IWYU pragma: keep
#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 "StringUtils.h"
#include "RichString.h"
#include "Macros.h"
#include "Platform.h"
#include "ProcessList.h"
#include "RichString.h"
#include "Settings.h"
#include "XUtils.h"
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.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
#if defined(MAJOR_IN_MKDEV)
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS)
#include <sys/sysmacros.h>
#endif
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#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 )
static uid_t Process_getuid = (uid_t)-1;
/*{
#include "Object.h"
#include <sys/types.h>
#define PROCESS_FLAG_IO 0x0001
typedef enum ProcessFields {
NULL_PROCESSFIELD = 0,
PID = 1,
COMM = 2,
STATE = 3,
PPID = 4,
PGRP = 5,
SESSION = 6,
TTY_NR = 7,
TPGID = 8,
MINFLT = 10,
MAJFLT = 12,
PRIORITY = 18,
NICE = 19,
STARTTIME = 21,
PROCESSOR = 38,
M_SIZE = 39,
M_RESIDENT = 40,
ST_UID = 46,
PERCENT_CPU = 47,
PERCENT_MEM = 48,
USER = 49,
TIME = 50,
NLWP = 51,
TGID = 52,
} ProcessField;
typedef struct ProcessPidColumn_ {
int id;
const char* label;
} ProcessPidColumn;
typedef struct Process_ {
Object super;
struct Settings_* settings;
unsigned long long int time;
pid_t pid;
pid_t ppid;
pid_t tgid;
char* comm;
int commLen;
int indent;
int basenameOffset;
bool updated;
char state;
bool tag;
bool showChildren;
bool show;
unsigned int pgrp;
unsigned int session;
unsigned int tty_nr;
int tpgid;
uid_t st_uid;
unsigned long int flags;
int processor;
float percent_cpu;
float percent_mem;
char* user;
long int priority;
long int nice;
long int nlwp;
char starttime_show[8];
time_t starttime_ctime;
long m_size;
long m_resident;
int exit_signal;
unsigned long int minflt;
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
} Process;
typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
} ProcessFieldData;
// Implemented in platform-specific code:
void Process_writeField(Process* this, RichString* str, ProcessField field);
long Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast);
bool Process_isThread(Process* this);
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern char Process_pidFormat[20];
typedef Process*(*Process_New)(struct Settings_*);
typedef void (*Process_WriteField)(Process*, RichString*, ProcessField);
typedef struct ProcessClass_ {
const ObjectClass super;
const Process_WriteField writeField;
} ProcessClass;
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
}*/
static int Process_getuid = -1;
#define ONE_K 1024L
#define ONE_M (ONE_K * ONE_K)
#define ONE_G (ONE_M * ONE_K)
#define ONE_T ((long long)ONE_G * ONE_K)
#define ONE_DECIMAL_K 1000L
#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K)
#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K)
#define ONE_DECIMAL_T ((long long)ONE_DECIMAL_G * ONE_DECIMAL_K)
char Process_pidFormat[20] = "%7d ";
static char Process_titleBuffer[20][20];
int Process_pidDigits = 7;
void Process_setupColumnWidths() {
int maxPid = Platform_getMaxPid();
if (maxPid == -1) return;
int digits = ceil(log10(maxPid));
assert(digits < 20);
for (int i = 0; Process_pidColumns[i].label; i++) {
assert(i < 20);
xSnprintf(Process_titleBuffer[i], 20, "%*s ", digits, Process_pidColumns[i].label);
Process_fields[Process_pidColumns[i].id].title = Process_titleBuffer[i];
}
xSnprintf(Process_pidFormat, sizeof(Process_pidFormat), "%%%dd ", digits);
if (maxPid == -1)
return;
Process_pidDigits = ceil(log10(maxPid));
assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS);
}
void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
char buffer[11];
void Process_humanNumber(RichString* str, unsigned long long number, bool coloring) {
char buffer[10];
int len;
int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
int processGigabytesColor = CRT_colors[PROCESS_GIGABYTES];
int processColor = CRT_colors[PROCESS];
if (!coloring) {
largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS];
processGigabytesColor = CRT_colors[PROCESS];
}
if(number >= (10 * ONE_DECIMAL_M)) {
#ifdef __LP64__
if(number >= (100 * ONE_DECIMAL_G)) {
len = snprintf(buffer, 10, "%4luT ", number / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len);
return;
} else if (number >= (1000 * ONE_DECIMAL_M)) {
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);
if (number < 1000) {
//Plain number, no markings
len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number);
RichString_appendnAscii(str, processColor, buffer, len);
} else if (number < 100000) {
//2 digit MB, 3 digit KB
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/1000);
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
number %= 1000;
len = snprintf(buffer, 10, "%03lu ", number);
RichString_appendn(str, processColor, buffer, len);
return;
len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number);
RichString_appendnAscii(str, processColor, buffer, len);
} else if (number < 1000 * ONE_K) {
//3 digit MB
number /= ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number);
RichString_appendnAscii(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_appendnAscii(str, processGigabytesColor, buffer, len);
number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number);
RichString_appendnAscii(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_appendnAscii(str, processGigabytesColor, buffer, len);
number %= 10;
len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number);
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
RichString_appendAscii(str, processGigabytesColor, "G ");
} else if (number < 1000 * ONE_M) {
//3 digit GB
number /= ONE_M;
len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number);
RichString_appendnAscii(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_appendnAscii(str, largeNumberColor, buffer, len);
number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number);
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
} else {
//2 digit TB and above
len = xSnprintf(buffer, sizeof(buffer), "%4.1lfT ", (double)number/ONE_G);
RichString_appendnAscii(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) {
char buffer[14];
char buffer[13];
int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
int processColor = CRT_colors[PROCESS];
int processShadowColor = CRT_colors[PROCESS_SHADOW];
if (!coloring) {
largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS];
processShadowColor = CRT_colors[PROCESS];
}
if ((long long) number == -1LL) {
int len = snprintf(buffer, 13, " no perm ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
if (number == ULLONG_MAX) {
RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
} else if (number >= 100000LL * ONE_DECIMAL_T) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_G);
RichString_appendn(str, largeNumberColor, buffer, 12);
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G);
RichString_appendnAscii(str, largeNumberColor, buffer, 12);
} else if (number >= 100LL * ONE_DECIMAL_T) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_M);
RichString_appendn(str, largeNumberColor, buffer, 8);
RichString_appendn(str, processMegabytesColor, buffer+8, 4);
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M);
RichString_appendnAscii(str, largeNumberColor, buffer, 8);
RichString_appendnAscii(str, processMegabytesColor, buffer+8, 4);
} else if (number >= 10LL * ONE_DECIMAL_G) {
xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_K);
RichString_appendn(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4);
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K);
RichString_appendnAscii(str, largeNumberColor, buffer, 5);
RichString_appendnAscii(str, processMegabytesColor, buffer+5, 3);
RichString_appendnAscii(str, processColor, buffer+8, 4);
} else {
xSnprintf(buffer, 13, "%11llu ", number);
RichString_appendn(str, largeNumberColor, buffer, 2);
RichString_appendn(str, processMegabytesColor, buffer+2, 3);
RichString_appendn(str, processColor, buffer+5, 3);
RichString_appendn(str, processShadowColor, buffer+8, 4);
xSnprintf(buffer, sizeof(buffer), "%11llu ", number);
RichString_appendnAscii(str, largeNumberColor, buffer, 2);
RichString_appendnAscii(str, processMegabytesColor, buffer+2, 3);
RichString_appendnAscii(str, processColor, buffer+5, 3);
RichString_appendnAscii(str, processShadowColor, buffer+8, 4);
}
}
@ -309,96 +160,115 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths) {
int minutes = (totalSeconds / 60) % 60;
int seconds = totalSeconds % 60;
int hundredths = totalHundredths - (totalSeconds * 100);
char buffer[11];
char buffer[10];
if (hours >= 100) {
xSnprintf(buffer, 10, "%7lluh ", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
xSnprintf(buffer, sizeof(buffer), "%7lluh ", hours);
RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer);
} else {
if (hours) {
xSnprintf(buffer, 10, "%2lluh", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
xSnprintf(buffer, 10, "%02d:%02d ", minutes, seconds);
xSnprintf(buffer, sizeof(buffer), "%2lluh", hours);
RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer);
xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds);
} 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_appendAscii(str, CRT_colors[DEFAULT_COLOR], buffer);
}
}
static inline void Process_writeCommand(Process* this, int attr, int baseattr, RichString* str) {
int start = RichString_size(str), finish = 0;
char* comm = this->comm;
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);
int len = 0;
const char* comm = this->comm;
if (this->settings->highlightBaseName || !this->settings->showProgramPath) {
int i, basename = 0;
for (i = 0; i < this->basenameOffset; i++) {
int basename = 0;
for (int i = 0; i < this->basenameOffset; i++) {
if (comm[i] == '/') {
basename = i + 1;
} else if (comm[i] == ':') {
finish = i + 1;
len = i + 1;
break;
}
}
if (!finish) {
if (this->settings->showProgramPath)
if (len == 0) {
if (this->settings->showProgramPath) {
start += basename;
else
} else {
comm += basename;
finish = this->basenameOffset - basename;
}
finish += start - 1;
len = this->basenameOffset - basename;
}
}
RichString_append(str, attr, comm);
RichString_appendWide(str, attr, comm);
if (this->settings->highlightBaseName)
RichString_setAttrn(str, baseattr, start, finish);
if (this->settings->highlightBaseName) {
RichString_setAttrn(str, baseattr, start, len);
}
}
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring) {
void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring) {
int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
int processColor = CRT_colors[PROCESS];
if (!coloring) {
largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS];
}
if (rate == -1) {
int len = snprintf(buffer, n, " no perm ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
if (isnan(rate)) {
RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
} else if (rate < ONE_K) {
int len = snprintf(buffer, n, "%7.2f B/s ", rate);
RichString_appendn(str, processColor, buffer, len);
RichString_appendnAscii(str, processColor, buffer, len);
} else if (rate < ONE_M) {
int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K);
RichString_appendn(str, processColor, buffer, len);
RichString_appendnAscii(str, processColor, buffer, len);
} else if (rate < ONE_G) {
int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M);
RichString_appendn(str, processMegabytesColor, buffer, len);
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
} else if (rate < ONE_T) {
int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len);
RichString_appendnAscii(str, largeNumberColor, buffer, len);
} else {
int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T);
RichString_appendn(str, largeNumberColor, buffer, len);
RichString_appendnAscii(str, largeNumberColor, buffer, len);
}
}
void Process_writeField(Process* this, RichString* str, ProcessField field) {
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width) {
int c = RichString_appendnWide(str, attr, content, MINIMUM(width, strlen(content)));
RichString_appendChr(str, ' ', width + 1 - c);
}
void Process_writeField(const Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int baseattr = CRT_colors[PROCESS_BASENAME];
int n = sizeof(buffer) - 1;
size_t n = sizeof(buffer) - 1;
bool coloring = this->settings->highlightMegabytes;
switch (field) {
case PERCENT_CPU: {
if (this->percent_cpu > 999.9) {
xSnprintf(buffer, n, "%4u ", (unsigned int)this->percent_cpu);
} else if (this->percent_cpu > 99.9) {
xSnprintf(buffer, n, "%3u. ", (unsigned int)this->percent_cpu);
case PERCENT_CPU:
case PERCENT_NORM_CPU: {
float cpuPercentage = this->percent_cpu;
if (field == PERCENT_NORM_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 {
xSnprintf(buffer, n, "%4.1f ", this->percent_cpu);
xSnprintf(buffer, n, "%4.1f ", cpuPercentage);
}
break;
}
@ -424,16 +294,20 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
bool lastItem = (this->indent < 0);
int indent = (this->indent < 0 ? -this->indent : this->indent);
for (int i = 0; i < 32; i++)
if (indent & (1U << i))
for (int i = 0; i < 32; i++) {
if (indent & (1U << i)) {
maxIndent = i+1;
}
}
for (int i = 0; i < maxIndent - 1; i++) {
int written, ret;
if (indent & (1 << i))
if (indent & (1 << i)) {
ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
else
} else {
ret = snprintf(buf, n, " ");
if (ret < 0 || ret >= n) {
}
if (ret < 0 || (size_t)ret >= n) {
written = n;
} else {
written = ret;
@ -441,17 +315,18 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
buf += written;
n -= written;
}
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
Process_writeCommand(this, attr, baseattr, str);
return;
}
}
case MAJFLT: Process_colorNumber(str, this->majflt, 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_SIZE: Process_humanNumber(str, this->m_size * PAGE_SIZE_KB, coloring); return;
case M_RESIDENT: Process_humanNumber(str, this->m_resident, coloring); return;
case M_VIRT: Process_humanNumber(str, this->m_virt, coloring); return;
case NICE: {
xSnprintf(buffer, n, "%3ld ", this->nice);
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
@ -460,9 +335,9 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
break;
}
case NLWP: xSnprintf(buffer, n, "%4ld ", this->nlwp); break;
case PGRP: xSnprintf(buffer, n, Process_pidFormat, this->pgrp); break;
case PID: xSnprintf(buffer, n, Process_pidFormat, this->pid); break;
case PPID: xSnprintf(buffer, n, Process_pidFormat, this->ppid); break;
case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break;
case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break;
case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break;
case PRIORITY: {
if(this->priority <= -100)
xSnprintf(buffer, n, " RT ");
@ -471,7 +346,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
break;
}
case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break;
case SESSION: xSnprintf(buffer, n, Process_pidFormat, this->session); break;
case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break;
case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
case STATE: {
xSnprintf(buffer, n, "%c ", this->state);
@ -487,39 +362,50 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
}
case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break;
case TIME: Process_printTime(str, this->time); return;
case TGID: xSnprintf(buffer, n, Process_pidFormat, this->tgid); break;
case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break;
case TGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tgid); break;
case TPGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tpgid); break;
case TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break;
case USER: {
if (Process_getuid != (int) this->st_uid)
if (Process_getuid != this->st_uid)
attr = CRT_colors[PROCESS_SHADOW];
if (this->user) {
xSnprintf(buffer, n, "%-9s ", this->user);
} else {
Process_printLeftAlignedField(str, attr, this->user, 9);
return;
}
xSnprintf(buffer, n, "%-9d ", this->st_uid);
}
if (buffer[9] != '\0') {
buffer[9] = ' ';
buffer[10] = '\0';
}
break;
}
default:
xSnprintf(buffer, n, "- ");
}
RichString_append(str, attr, buffer);
RichString_appendWide(str, attr, buffer);
}
void Process_display(Object* cast, RichString* out) {
Process* this = (Process*) cast;
ProcessField* fields = this->settings->fields;
void Process_display(const Object* cast, RichString* out) {
const Process* this = (const Process*) cast;
const ProcessField* fields = this->settings->fields;
RichString_prune(out);
for (int i = 0; fields[i]; 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]);
if (this->tag == true)
}
if (this->tag == true) {
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);
}
@ -528,7 +414,11 @@ void Process_done(Process* this) {
free(this->comm);
}
ProcessClass Process_class = {
static const char* Process_getCommandStr(const Process* p) {
return p->comm ? p->comm : "";
}
const ProcessClass Process_class = {
.super = {
.extends = Class(Object),
.display = Process_display,
@ -536,20 +426,36 @@ ProcessClass Process_class = {
.compare = Process_compare
},
.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->tag = false;
this->showChildren = true;
this->show = true;
this->updated = false;
this->basenameOffset = -1;
if (Process_getuid == -1) Process_getuid = getuid();
if (Process_getuid == (uid_t)-1) {
Process_getuid = getuid();
}
}
void Process_toggleTag(Process* this) {
this->tag = this->tag == true ? false : true;
this->tag = !this->tag;
}
bool Process_isNew(const Process* this) {
assert(this->processList);
if (this->processList->scanTs >= this->seenTs) {
return this->processList->scanTs - this->seenTs <= 1000 * this->processList->settings->highlightDelaySecs;
}
return false;
}
bool Process_isTomb(const Process* this) {
return this->tombTs > 0;
}
bool Process_setPriority(Process* this, int priority) {
@ -574,74 +480,89 @@ bool Process_sendSignal(Process* this, Arg sgn) {
return ok;
}
long Process_pidCompare(const void* v1, const void* v2) {
Process* p1 = (Process*)v1;
Process* p2 = (Process*)v2;
return (p1->pid - p2->pid);
int Process_pidCompare(const void* v1, const void* v2) {
const Process* p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2;
return SPACESHIP_NUMBER(p1->pid, p2->pid);
}
long Process_compare(const void* v1, const void* v2) {
Process *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
p1 = (Process*)v1;
p2 = (Process*)v2;
int Process_compare(const void* v1, const void* v2) {
const Process *p1, *p2;
const Settings *settings = ((const Process*)v1)->settings;
if (Settings_getActiveDirection(settings) == 1) {
p1 = (const Process*)v1;
p2 = (const Process*)v2;
} else {
p2 = (Process*)v1;
p1 = (Process*)v2;
p2 = (const Process*)v1;
p1 = (const Process*)v2;
}
switch (settings->sortKey) {
ProcessField key = Settings_getActiveSortKey(settings);
int result = Process_compareByKey(p1, p2, key);
// Implement tie-breaker (needed to make tree mode more stable)
if (!result)
result = SPACESHIP_NUMBER(p1->pid, p2->pid);
return result;
}
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) {
int r;
switch (key) {
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:
return (p2->m_resident - p1->m_resident);
return SPACESHIP_NUMBER(p2->m_resident, p1->m_resident);
case COMM:
return strcmp(p1->comm, p2->comm);
return SPACESHIP_NULLSTR(Process_getCommand(p1), Process_getCommand(p2));
case MAJFLT:
return (p2->majflt - p1->majflt);
return SPACESHIP_NUMBER(p2->majflt, p1->majflt);
case MINFLT:
return (p2->minflt - p1->minflt);
return SPACESHIP_NUMBER(p2->minflt, p1->minflt);
case M_RESIDENT:
return (p2->m_resident - p1->m_resident);
case M_SIZE:
return (p2->m_size - p1->m_size);
return SPACESHIP_NUMBER(p2->m_resident, p1->m_resident);
case M_VIRT:
return SPACESHIP_NUMBER(p2->m_virt, p1->m_virt);
case NICE:
return (p1->nice - p2->nice);
return SPACESHIP_NUMBER(p1->nice, p2->nice);
case NLWP:
return (p1->nlwp - p2->nlwp);
return SPACESHIP_NUMBER(p1->nlwp, p2->nlwp);
case PGRP:
return (p1->pgrp - p2->pgrp);
return SPACESHIP_NUMBER(p1->pgrp, p2->pgrp);
case PID:
return (p1->pid - p2->pid);
return SPACESHIP_NUMBER(p1->pid, p2->pid);
case PPID:
return (p1->ppid - p2->ppid);
return SPACESHIP_NUMBER(p1->ppid, p2->ppid);
case PRIORITY:
return (p1->priority - p2->priority);
return SPACESHIP_NUMBER(p1->priority, p2->priority);
case PROCESSOR:
return (p1->processor - p2->processor);
return SPACESHIP_NUMBER(p1->processor, p2->processor);
case SESSION:
return (p1->session - p2->session);
case STARTTIME: {
if (p1->starttime_ctime == p2->starttime_ctime)
return (p1->pid - p2->pid);
else
return (p1->starttime_ctime - p2->starttime_ctime);
}
return SPACESHIP_NUMBER(p1->session, p2->session);
case STARTTIME:
r = SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid);
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:
return (p1->st_uid - p2->st_uid);
return SPACESHIP_NUMBER(p1->st_uid, p2->st_uid);
case TIME:
return ((p2->time) - (p1->time));
return SPACESHIP_NUMBER(p2->time, p1->time);
case TGID:
return (p1->tgid - p2->tgid);
return SPACESHIP_NUMBER(p1->tgid, p2->tgid);
case TPGID:
return (p1->tpgid - p2->tpgid);
return SPACESHIP_NUMBER(p1->tpgid, p2->tpgid);
case TTY_NR:
return (p1->tty_nr - p2->tty_nr);
return SPACESHIP_NUMBER(p1->tty_nr, p2->tty_nr);
case USER:
return strcmp(p1->user ? p1->user : "", p2->user ? p2->user : "");
return SPACESHIP_NULLSTR(p1->user, p2->user);
default:
return (p1->pid - p2->pid);
return SPACESHIP_NUMBER(p1->pid, p2->pid);
}
}

164
Process.h
View File

@ -1,38 +1,26 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Process
#define HEADER_Process
/*
htop - Process.h
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#ifdef MAJOR_IN_MKDEV
#elif defined(MAJOR_IN_SYSMACROS)
#endif
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#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 <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#define PROCESS_FLAG_IO 0x0001
#include "Object.h"
#include "ProcessField.h"
#include "RichString.h"
typedef enum ProcessFields {
#define PROCESS_FLAG_IO 0x0001
#define DEFAULT_HIGHLIGHT_SECS 5
typedef enum ProcessField_ {
NULL_PROCESSFIELD = 0,
PID = 1,
COMM = 2,
@ -48,7 +36,7 @@ typedef enum ProcessFields {
NICE = 19,
STARTTIME = 21,
PROCESSOR = 38,
M_SIZE = 39,
M_VIRT = 39,
M_RESIDENT = 40,
ST_UID = 46,
PERCENT_CPU = 47,
@ -57,23 +45,27 @@ typedef enum ProcessFields {
TIME = 50,
NLWP = 51,
TGID = 52,
PERCENT_NORM_CPU = 53,
/* Platform specific fields, defined in ${platform}/ProcessField.h */
PLATFORM_PROCESS_FIELDS
LAST_PROCESSFIELD
} ProcessField;
typedef struct ProcessPidColumn_ {
int id;
const char* label;
} ProcessPidColumn;
struct Settings_;
typedef struct Process_ {
Object super;
struct Settings_* settings;
const struct ProcessList_* processList;
const struct Settings_* settings;
unsigned long long int time;
pid_t pid;
pid_t ppid;
pid_t tgid;
char* comm;
char* comm; /* use Process_getCommand() for Command actually displayed */
int commLen;
int indent;
@ -84,6 +76,7 @@ typedef struct Process_ {
bool tag;
bool showChildren;
bool show;
bool wasShown;
unsigned int pgrp;
unsigned int session;
unsigned int tty_nr;
@ -94,7 +87,7 @@ typedef struct Process_ {
float percent_cpu;
float percent_mem;
char* user;
const char* user;
long int priority;
long int nice;
@ -102,109 +95,114 @@ typedef struct Process_ {
char starttime_show[8];
time_t starttime_ctime;
long m_size;
long m_virt;
long m_resident;
int exit_signal;
time_t seenTs;
time_t tombTs;
unsigned long int minflt;
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;
typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
uint32_t flags;
bool pidColumn;
} ProcessFieldData;
// Implemented in platform-specific code:
void Process_writeField(Process* this, RichString* str, ProcessField field);
long Process_compare(const void* v1, const void* v2);
void Process_writeField(const Process* this, RichString* str, ProcessField field);
int Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast);
bool Process_isThread(Process* this);
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern char Process_pidFormat[20];
bool Process_isThread(const Process* this);
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
#define PROCESS_MAX_PID_DIGITS 19
extern int Process_pidDigits;
typedef Process*(*Process_New)(struct Settings_*);
typedef void (*Process_WriteField)(Process*, RichString*, ProcessField);
typedef Process*(*Process_New)(const struct Settings_*);
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
typedef const char* (*Process_GetCommandStr)(const Process*);
typedef struct ProcessClass_ {
const ObjectClass super;
const Process_WriteField writeField;
const Process_CompareByKey compareByKey;
const Process_GetCommandStr getCommandStr;
} ProcessClass;
#define As_Process(this_) ((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_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_))
#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 ONE_K 1024L
#define ONE_K 1024UL
#define ONE_M (ONE_K * 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_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);
extern void Process_setupColumnWidths();
void Process_humanNumber(RichString* str, unsigned long long number, bool coloring);
extern void Process_humanNumber(RichString* str, unsigned long number, bool coloring);
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
extern void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
void Process_printTime(RichString* str, unsigned long long totalHundredths);
extern void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_fillStarttimeBuffer(Process* this);
extern void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring);
extern void Process_writeField(Process* this, RichString* str, ProcessField field);
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width);
extern void Process_display(Object* cast, RichString* out);
void Process_display(const Object* cast, RichString* out);
extern void Process_done(Process* this);
void Process_done(Process* this);
extern ProcessClass Process_class;
extern const ProcessClass Process_class;
extern void Process_init(Process* this, struct Settings_* settings);
void Process_init(Process* this, const struct Settings_* settings);
extern void Process_toggleTag(Process* this);
void Process_toggleTag(Process* this);
extern bool Process_setPriority(Process* this, int priority);
bool Process_isNew(const Process* this);
extern bool Process_changePriorityBy(Process* this, Arg delta);
bool Process_isTomb(const Process* this);
extern bool Process_sendSignal(Process* this, Arg sgn);
bool Process_setPriority(Process* this, int priority);
extern long Process_pidCompare(const void* v1, const void* v2);
bool Process_changePriorityBy(Process* this, Arg delta);
extern long Process_compare(const void* v1, const void* v2);
bool Process_sendSignal(Process* this, Arg sgn);
int Process_pidCompare(const void* v1, const void* v2);
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
#endif

View File

@ -1,96 +1,42 @@
/*
htop - ProcessList.c
(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.
*/
#include "ProcessList.h"
#include "Platform.h"
#include "CRT.h"
#include "StringUtils.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
/*{
#include "Vector.h"
#include "Compat.h"
#include "CRT.h"
#include "Hashtable.h"
#include "UsersTable.h"
#include "Panel.h"
#include "Process.h"
#include "Settings.h"
#include "Macros.h"
#include "Vector.h"
#include "XUtils.h"
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
#endif
#ifndef MAX_NAME
#define MAX_NAME 128
#endif
#ifndef MAX_READ
#define MAX_READ 2048
#endif
typedef struct ProcessList_ {
Settings* settings;
Vector* processes;
Vector* processes2;
Hashtable* processTable;
UsersTable* usersTable;
Panel* panel;
int following;
uid_t userId;
const char* incFilter;
Hashtable* pidWhiteList;
#ifdef HAVE_LIBHWLOC
hwloc_topology_t topology;
bool topologyOk;
#endif
int totalTasks;
int runningTasks;
int userlandThreads;
int kernelThreads;
unsigned long long int totalMem;
unsigned long long int usedMem;
unsigned long long int freeMem;
unsigned long long int sharedMem;
unsigned long long int buffersMem;
unsigned long long int cachedMem;
unsigned long long int totalSwap;
unsigned long long int usedSwap;
unsigned long long int freeSwap;
int cpuCount;
} ProcessList;
ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
void ProcessList_goThroughEntries(ProcessList* pl);
}*/
ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidWhiteList, 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->processTable = Hashtable_new(140, false);
this->usersTable = usersTable;
this->pidWhiteList = pidWhiteList;
this->userId = userId;
this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); // tree-view auxiliary buffer
// tree-view auxiliary buffer
this->processes2 = Vector_new(klass, true, DEFAULT_SIZE);
this->processTable = Hashtable_new(200, false);
this->displayTreeSet = Hashtable_new(200, false);
this->draftingTreeSet = Hashtable_new(200, false);
this->usersTable = usersTable;
this->pidMatchList = pidMatchList;
this->userId = userId;
// set later by platform-specific code
this->cpuCount = 0;
this->scanTs = 0;
#ifdef HAVE_LIBHWLOC
this->topologyOk = false;
if (hwloc_topology_init(&this->topology) == 0) {
@ -120,31 +66,72 @@ void ProcessList_done(ProcessList* this) {
hwloc_topology_destroy(this->topology);
}
#endif
Hashtable_delete(this->draftingTreeSet);
Hashtable_delete(this->displayTreeSet);
Hashtable_delete(this->processTable);
Vector_delete(this->processes);
Vector_delete(this->processes2);
Vector_delete(this->processes);
}
void ProcessList_setPanel(ProcessList* this, Panel* panel) {
this->panel = panel;
}
static const char* alignedProcessFieldTitle(ProcessField field) {
const char* title = Process_fields[field].title;
if (!title)
return "- ";
if (!Process_fields[field].pidColumn)
return title;
static char titleBuffer[PROCESS_MAX_PID_DIGITS + /* space */ 1 + /* null-terminator */ + 1];
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
return titleBuffer;
}
void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header);
ProcessField* fields = this->settings->fields;
const Settings* settings = this->settings;
const ProcessField* fields = settings->fields;
ProcessField key = Settings_getActiveSortKey(settings);
for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title;
if (!field) field = "- ";
if (!this->settings->treeView && this->settings->sortKey == fields[i])
RichString_append(header, CRT_colors[PANEL_SELECTION_FOCUS], field);
else
RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
int color;
if (settings->treeView && settings->treeViewAlwaysByPID) {
color = CRT_colors[PANEL_HEADER_FOCUS];
} else if (key == fields[i]) {
color = CRT_colors[PANEL_SELECTION_FOCUS];
} else {
color = CRT_colors[PANEL_HEADER_FOCUS];
}
RichString_appendWide(header, color, alignedProcessFieldTitle(fields[i]));
if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') {
header->chlen--; // rewind to override space
RichString_appendnWide(header,
CRT_colors[PANEL_SELECTION_FOCUS],
CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_DESC : TREE_STR_ASC],
1);
}
if (COMM == fields[i] && settings->showMergedCommand) {
RichString_appendAscii(header, color, "(merged)");
}
}
}
void ProcessList_add(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
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);
Hashtable_put(this->processTable, p->pid, p);
@ -157,95 +144,287 @@ void ProcessList_add(ProcessList* this, Process* p) {
void ProcessList_remove(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
assert(Hashtable_get(this->processTable, p->pid) != NULL);
Process* pp = Hashtable_remove(this->processTable, p->pid);
assert(pp == p); (void)pp;
unsigned int pid = p->pid;
pid_t pid = p->pid;
int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
assert(idx != -1);
if (idx >= 0) Vector_remove(this->processes, idx);
assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
if (idx >= 0) {
Vector_remove(this->processes, idx);
}
if (this->following != -1 && this->following == pid) {
this->following = -1;
Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
}
assert(Hashtable_get(this->processTable, pid) == NULL);
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
}
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) {
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 comparison it may be not, as it's not safe to go deeper without first updating the tree structure.
// If it would be safe that approach would likely bring an advantage in performance.
//
// Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
// First it sorts a list. Then it runs the function recursively for each element of the sorted list.
// After that it updates the settings of processes.
//
// It relies on `leftBound` and `rightBound` as an optimization to cut the list size at the time it builds a 'layer'.
//
// It uses a temporary Hashtable `draftingTreeSet` because it's not safe to traverse a tree
// and at the same time make changes in it.
//
static void ProcessList_updateTreeSetLayer(ProcessList* this, unsigned int leftBound, unsigned int rightBound, unsigned int deep, unsigned int left, unsigned int right, unsigned int* index, unsigned int* treeIndex, int indent) {
// It's guaranteed that layer_size is enough space
// but most likely it needs less. Specifically on first iteration.
int layerSize = (right - left) / 2;
// check if we reach `children` of `leaves`
if (layerSize == 0)
return;
Vector* layer = Vector_new(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);
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)) {
process = (Process*) (Vector_take(this->processes, i));
process = (Process*)Vector_take(this->processes, i);
Vector_add(children, process);
}
}
int size = Vector_size(children);
for (int i = 0; i < size; i++) {
Process* process = (Process*) (Vector_get(children, i));
if (!show)
int index = (*node_index)++;
Process* process = (Process*)Vector_get(children, i);
int lft = (*node_counter)++;
if (!show) {
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);
else
} else {
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);
ProcessList_buildTree(this, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false);
if (i == size - 1)
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) {
process->indent = -nextIndent;
else
} else {
process->indent = nextIndent;
}
int rht = (*node_counter)++;
process->tree_left = lft;
process->tree_right = rht;
process->tree_depth = level + 1;
process->tree_index = index;
Hashtable_put(this->displayTreeSet, index, process);
}
Vector_delete(children);
}
void ProcessList_sort(ProcessList* this) {
if (!this->settings->treeView) {
Vector_insertionSort(this->processes);
} else {
// Save settings
int direction = this->settings->direction;
int sortKey = this->settings->sortKey;
static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
const Process *p1 = (const Process*)v1;
const Process *p2 = (const Process*)v2;
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
}
static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
const Process *p1 = (const Process*)v1;
const Process *p2 = (const Process*)v2;
return SPACESHIP_NUMBER(p1->pid, p2->pid);
}
// Builds a sorted tree from scratch, without relying on previously gathered information
static void ProcessList_buildTree(ProcessList* this) {
int node_counter = 1;
int node_index = 0;
int direction = Settings_getActiveDirection(this->settings);
// Sort by PID
this->settings->sortKey = PID;
this->settings->direction = 1;
Vector_quickSort(this->processes);
// Restore settings
this->settings->sortKey = sortKey;
this->settings->direction = direction;
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
int vsize = Vector_size(this->processes);
// Find all processes whose parent is not visible
int size;
while ((size = Vector_size(this->processes))) {
int i;
for (i = 0; i < size; i++) {
Process* process = (Process*)(Vector_get(this->processes, i));
// Immediately consume not shown processes
Process* process = (Process*)Vector_get(this->processes, i);
// Immediately consume processes hidden from view
if (!process->show) {
process = (Process*)(Vector_take(this->processes, i));
process = (Process*)Vector_take(this->processes, i);
process->indent = 0;
process->tree_depth = 0;
process->tree_left = node_counter++;
process->tree_index = node_index++;
Vector_add(this->processes2, process);
ProcessList_buildTree(this, process->pid, 0, 0, direction, false);
ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, false, &node_counter, &node_index);
process->tree_right = node_counter++;
Hashtable_put(this->displayTreeSet, process->tree_index, process);
break;
}
pid_t ppid = Process_getParentPid(process);
// Bisect the process vector to find parent
int l = 0, r = size;
int l = 0;
int r = size;
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
// on Mac OS X 10.11.6) cancel bisecting and regard this process as
// root.
if (process->pid == ppid)
r = 0;
// On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2)
// use a ppid of 0. As that PID can't exist, we can skip searching for it.
if (!ppid)
r = 0;
while (l < r) {
int c = (l + r) / 2;
pid_t pid = ((Process*)(Vector_get(this->processes, c)))->pid;
pid_t pid = ((Process*)Vector_get(this->processes, c))->pid;
if (ppid == pid) {
break;
} else if (ppid < pid) {
@ -254,36 +433,51 @@ void ProcessList_sort(ProcessList* this) {
l = c + 1;
}
}
// If parent not found, then construct the tree with this root
// If parent not found, then construct the tree with this node as root
if (l >= r) {
process = (Process*)(Vector_take(this->processes, i));
process = (Process*)Vector_take(this->processes, i);
process->indent = 0;
process->tree_depth = 0;
process->tree_left = node_counter++;
process->tree_index = node_index++;
Vector_add(this->processes2, process);
ProcessList_buildTree(this, process->pid, 0, 0, direction, process->showChildren);
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;
}
}
// There should be no loop in the process tree
assert(i < size);
}
assert(Vector_size(this->processes2) == vsize); (void)vsize;
assert(Vector_size(this->processes) == 0);
// 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(ProcessList* this, int at) {
ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
int x = 0;
ProcessField* fields = this->settings->fields;
const ProcessField* fields = this->settings->fields;
ProcessField field;
for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title;
if (!title) title = "- ";
int len = strlen(title);
int len = strlen(alignedProcessFieldTitle(field));
if (at >= x && at <= x + len) {
return field;
}
@ -304,32 +498,28 @@ void ProcessList_rebuildPanel(ProcessList* this) {
const char* incFilter = this->incFilter;
int currPos = Panel_getSelectedIndex(this->panel);
pid_t currPid = this->following != -1 ? this->following : 0;
int currScrollV = this->panel->scrollV;
Panel_prune(this->panel);
int size = ProcessList_size(this);
int idx = 0;
for (int i = 0; i < size; i++) {
bool hidden = false;
Process* p = ProcessList_get(this, i);
if ( (!p->show)
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|| (incFilter && !(String_contains_i(p->comm, incFilter)))
|| (this->pidWhiteList && !Hashtable_get(this->pidWhiteList, p->tgid)) )
hidden = true;
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
continue;
if (!hidden) {
Panel_set(this->panel, idx, (Object*)p);
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == currPid)) {
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == this->following)) {
Panel_setSelected(this->panel, idx);
this->panel->scrollV = currScrollV;
}
idx++;
}
}
}
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) {
Process* proc = (Process*) Hashtable_get(this->processTable, pid);
@ -345,12 +535,20 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting,
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"
for (int i = 0; i < Vector_size(this->processes); i++) {
Process* p = (Process*) Vector_get(this->processes, i);
p->updated = false;
p->wasShown = p->show;
p->show = true;
}
@ -359,13 +557,48 @@ void ProcessList_scan(ProcessList* this) {
this->kernelThreads = 0;
this->runningTasks = 0;
ProcessList_goThroughEntries(this);
// set scanTs
static bool firstScanDone = false;
if (!firstScanDone) {
this->scanTs = 0;
firstScanDone = true;
} else if (Compat_clock_monotonic_gettime(&now) == 0) {
// save time in millisecond, so with a delay in deciseconds
// there are no irregularities
this->scanTs = 1000 * now.tv_sec + now.tv_nsec / 1000000;
}
ProcessList_goThroughEntries(this, false);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* p = (Process*) Vector_get(this->processes, i);
if (p->updated == false)
if (p->tombTs > 0) {
// remove tombed process
if (this->scanTs >= p->tombTs) {
ProcessList_remove(this, p);
else
}
} else if (p->updated == false) {
// process no longer exists
if (this->settings->highlightChanges && p->wasShown) {
// mark tombed
p->tombTs = this->scanTs + 1000 * this->settings->highlightDelaySecs;
} else {
// immediately remove
ProcessList_remove(this, p);
}
} else {
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

@ -1,25 +1,31 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_ProcessList
#define HEADER_ProcessList
/*
htop - ProcessList.h
(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.
*/
#include "Vector.h"
#include "config.h" // IWYU pragma: keep
#include <stdbool.h>
#include <sys/types.h>
#include "Hashtable.h"
#include "UsersTable.h"
#include "Object.h"
#include "Panel.h"
#include "Process.h"
#include "RichString.h"
#include "Settings.h"
#include "UsersTable.h"
#include "Vector.h"
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
#endif
#ifndef MAX_NAME
#define MAX_NAME 128
#endif
@ -29,18 +35,21 @@ in the source distribution for its full text.
#endif
typedef struct ProcessList_ {
Settings* settings;
const Settings* settings;
Vector* processes;
Vector* processes2;
Hashtable* processTable;
UsersTable* usersTable;
Hashtable* displayTreeSet;
Hashtable* draftingTreeSet;
Panel* panel;
int following;
uid_t userId;
const char* incFilter;
Hashtable* pidWhiteList;
Hashtable* pidMatchList;
#ifdef HAVE_LIBHWLOC
hwloc_topology_t topology;
@ -54,8 +63,6 @@ typedef struct ProcessList_ {
unsigned long long int totalMem;
unsigned long long int usedMem;
unsigned long long int freeMem;
unsigned long long int sharedMem;
unsigned long long int buffersMem;
unsigned long long int cachedMem;
unsigned long long int totalSwap;
@ -64,39 +71,40 @@ typedef struct ProcessList_ {
int cpuCount;
time_t scanTs;
} ProcessList;
ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList, uid_t userId);
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
void ProcessList_goThroughEntries(ProcessList* pl);
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
extern ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
extern void ProcessList_done(ProcessList* this);
void ProcessList_done(ProcessList* this);
extern void ProcessList_setPanel(ProcessList* this, Panel* panel);
void ProcessList_setPanel(ProcessList* this, Panel* panel);
extern void ProcessList_printHeader(ProcessList* this, RichString* header);
void ProcessList_printHeader(ProcessList* this, RichString* header);
extern void ProcessList_add(ProcessList* this, Process* p);
void ProcessList_add(ProcessList* this, Process* p);
extern void ProcessList_remove(ProcessList* this, Process* p);
void ProcessList_remove(ProcessList* this, Process* p);
extern Process* ProcessList_get(ProcessList* this, int idx);
Process* ProcessList_get(ProcessList* this, int idx);
extern int ProcessList_size(ProcessList* this);
int ProcessList_size(ProcessList* this);
extern void ProcessList_sort(ProcessList* this);
void ProcessList_sort(ProcessList* this);
extern ProcessField ProcessList_keyAt(ProcessList* this, int at);
ProcessField ProcessList_keyAt(const ProcessList* this, int at);
extern void ProcessList_expandTree(ProcessList* this);
void ProcessList_expandTree(ProcessList* this);
extern void ProcessList_rebuildPanel(ProcessList* this);
void ProcessList_rebuildPanel(ProcessList* this);
extern 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);
extern void ProcessList_scan(ProcessList* this);
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate);
#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 - 2, " 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

65
README
View File

@ -1,40 +1,69 @@
![htop](https://htop.dev)
# [![htop](htop.png)](https://htop.dev)
[![CI](https://github.com/htop-dev/htop/workflows/CI/badge.svg)](https://github.com/htop-dev/htop/actions)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/21617/badge.svg)](https://scan.coverity.com/projects/21617)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/21665/badge.svg)](https://scan.coverity.com/projects/21665)
[![Mailing List](https://img.shields.io/badge/Mailing%20List-htop-blue.svg)](https://groups.io/g/htop)
[![IRC #htop](https://img.shields.io/badge/IRC-htop-blue.svg)](https://webchat.freenode.net/#htop)
[![Github Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest)
[![Download](https://api.bintray.com/packages/htop/source/htop/images/download.svg)](https://bintray.com/htop/source/htop/_latestVersion)
Introduction
------------
![Screenshot of htop](docs/images/screenshot.png?raw=true)
## Introduction
`htop` is a cross-platform interactive process viewer.
It requires `ncurses`.
For more information and details on how to contribute to `htop`
visit [htop.dev](https://htop.dev).
`htop` allows scrolling the list of processes vertically and horizontally to see their full command lines and related information like memory and CPU consumption.
Build instructions
------------------
The information displayed is configurable through a graphical setup and can be sorted and filtered interactively.
This program is distributed as a standard autotools-based package.
For detailed instructions see the [INSTALL](/INSTALL) file, which
is created after `./autogen.sh` is run.
Tasks related to processes (e.g. killing and renicing) can be done without entering their PIDs.
When compiling from a [release tarball](https://github.com/htop-dev/htop/releases/), run:
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
This program is distributed as a standard GNU autotools-based package.
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
./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
./autogen.sh && ./configure && make
~~~
By default `make install` will install into `/usr/local`, for changing
the path use `./configure --prefix=/some/path`.
By default `make install` will install into `/usr/local`, for changing the path use `./configure --prefix=/some/path`.
See the manual page (`man htop`) or the on-line help ('F1' or 'h'
inside `htop`) for a list of supported key commands.
See the manual page (`man htop`) or the on-line help ('F1' or 'h' inside `htop`) for a list of supported key commands.
## Support
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

View File

@ -1,71 +1,19 @@
/*
htop - RichString.c
(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.
*/
#include "RichString.h"
#include "XAlloc.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#define RICHSTRING_MAXLEN 350
#include "Macros.h"
#include "XUtils.h"
/*{
#include "config.h"
#include <ctype.h>
#include <assert.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_sizeVal(this) ((this).chlen)
#define RichString_begin(this) RichString (this); memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
#define RichString_beginAllocated(this) memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
#define RichString_end(this) RichString_prune(&(this));
#ifdef HAVE_LIBNCURSESW
#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_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 CharType cchar_t
#else
#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_getCharVal(this, i) ((this).chptr[i])
#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)] = ch; } while(0)
#define CharType chtype
#endif
typedef struct RichString_ {
int chlen;
CharType* chptr;
CharType chstr[RICHSTRING_MAXLEN+1];
} RichString;
}*/
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
#define charBytes(n) (sizeof(CharType) * (n))
@ -89,28 +37,46 @@ static void RichString_extendLen(RichString* this, int 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
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) {
static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
wchar_t data[len + 1];
len = mbstowcs(data, data_c, len);
if (len < 0)
return;
if (len <= 0)
return 0;
int newLen = from + len;
RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++) {
this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : '?') } };
}
return wcswidth(data, len);
}
inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
cchar_t* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
for (int i = start; i <= finish; i++) {
ch->attr = attrs;
ch++;
static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data, int from, int len) {
int newLen = from + len;
RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++) {
this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint(data[j]) ? data[j] : '?') } };
}
return len;
}
inline void RichString_setAttrn(RichString* this, int attrs, int start, int charcount) {
int end = CLAMP(start + charcount, 0, this->chlen);
for (int i = start; i < end; i++) {
this->chptr[i].attr = attrs;
}
}
@ -125,22 +91,27 @@ int RichString_findChar(RichString* this, char c, int start) {
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 int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
int newLen = from + len;
RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++)
this->chptr[i] = (data_c[j] >= 32 ? data_c[j] : '?') | attrs;
for (int i = from, j = 0; i < newLen; i++, j++) {
this->chptr[i] = (((unsigned char)data_c[j]) >= 32 ? ((unsigned char)data_c[j]) : '?') | attrs;
}
this->chptr[newLen] = 0;
return len;
}
void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
chtype* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
for (int i = start; i <= finish; i++) {
*ch = (*ch & 0xff) | attrs;
ch++;
static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data_c, int from, int len) {
return RichString_writeFromWide(this, attrs, data_c, from, len);
}
void RichString_setAttrn(RichString* this, int attrs, int start, int charcount) {
int end = CLAMP(start + charcount, 0, this->chlen);
for (int i = start; i < end; i++) {
this->chptr[i] = (this->chptr[i] & 0xff) | attrs;
}
}
@ -154,7 +125,7 @@ int RichString_findChar(RichString* this, char c, int start) {
return -1;
}
#endif
#endif /* HAVE_LIBNCURSESW */
void RichString_prune(RichString* this) {
if (this->chlen > RICHSTRING_MAXLEN)
@ -163,18 +134,39 @@ void RichString_prune(RichString* this) {
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) {
RichString_setAttrn(this, attrs, 0, this->chlen - 1);
RichString_setAttrn(this, attrs, 0, this->chlen);
}
void RichString_append(RichString* this, int attrs, const char* data) {
RichString_writeFrom(this, attrs, data, this->chlen, strlen(data));
int RichString_appendWide(RichString* this, int attrs, const char* data) {
return RichString_writeFromWide(this, attrs, data, this->chlen, strlen(data));
}
void RichString_appendn(RichString* this, int attrs, const char* data, int len) {
RichString_writeFrom(this, attrs, data, this->chlen, len);
int RichString_appendnWide(RichString* this, int attrs, const char* data, int len) {
return RichString_writeFromWide(this, attrs, data, this->chlen, len);
}
void RichString_write(RichString* this, int attrs, const char* data) {
RichString_writeFrom(this, attrs, data, 0, strlen(data));
int RichString_writeWide(RichString* this, int attrs, const char* data) {
return RichString_writeFromWide(this, attrs, data, 0, strlen(data));
}
int RichString_appendAscii(RichString* this, int attrs, const char* data) {
return RichString_writeFromAscii(this, attrs, data, this->chlen, strlen(data));
}
int RichString_appendnAscii(RichString* this, int attrs, const char* data, int len) {
return RichString_writeFromAscii(this, attrs, data, this->chlen, len);
}
int RichString_writeAscii(RichString* this, int attrs, const char* data) {
return RichString_writeFromAscii(this, attrs, data, 0, strlen(data));
}

View File

@ -1,94 +1,67 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_RichString
#define HEADER_RichString
/*
htop - RichString.h
(C) 2004,2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define RICHSTRING_MAXLEN 350
#include "config.h"
#include <ctype.h>
#include <assert.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
#include "ProvideCurses.h"
#ifdef HAVE_LIBNCURSESW
#include <wctype.h>
#endif
#define RichString_size(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_beginAllocated(this) memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
#define RichString_end(this) RichString_prune(&(this));
#define RichString_begin(this) RichString (this); RichString_beginAllocated(this)
#define RichString_beginAllocated(this) do { memset(&(this), 0, sizeof(RichString)); (this).chptr = (this).chstr; } while(0)
#define RichString_end(this) RichString_prune(&(this))
#ifdef HAVE_LIBNCURSESW
#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_setChar(this, at, ch) do { (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while (0)
#define CharType cchar_t
#else
#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_setChar(this, at, ch) do { (this)->chptr[(at)] = ch; } while (0)
#define CharType chtype
#endif
#define RICHSTRING_MAXLEN 350
typedef struct RichString_ {
int chlen;
CharType* chptr;
CharType chstr[RICHSTRING_MAXLEN + 1];
int highlightAttr;
} RichString;
void RichString_setAttrn(RichString* this, int attrs, int start, int charcount);
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
int RichString_findChar(RichString* this, char c, int start);
#define charBytes(n) (sizeof(CharType) * (n))
void RichString_prune(RichString* this);
#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)
void RichString_setAttr(RichString* this, int attrs);
#ifdef HAVE_LIBNCURSESW
void RichString_appendChr(RichString* this, char c, int count);
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
int RichString_appendWide(RichString* this, int attrs, const char* data);
extern int RichString_findChar(RichString* this, char c, int start);
int RichString_appendnWide(RichString* this, int attrs, const char* data, int len);
#else
int RichString_writeWide(RichString* this, int attrs, const char* data);
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
int RichString_appendAscii(RichString* this, int attrs, const char* data);
extern int RichString_findChar(RichString* this, char c, int start);
#endif
extern void RichString_prune(RichString* this);
extern void RichString_setAttr(RichString* this, int attrs);
extern void RichString_append(RichString* this, int attrs, const char* data);
extern void RichString_appendn(RichString* this, int attrs, const char* data, int len);
extern void RichString_write(RichString* this, int attrs, const char* data);
int RichString_appendnAscii(RichString* this, int attrs, const char* data, int len);
int RichString_writeAscii(RichString* this, int attrs, const char* data);
#endif

View File

@ -1,61 +1,37 @@
/*
htop - ScreenManager.c
(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.
*/
#include "ScreenManager.h"
#include "ProcessList.h"
#include "Object.h"
#include "CRT.h"
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/time.h>
/*{
#include "CRT.h"
#include "FunctionBar.h"
#include "Vector.h"
#include "Header.h"
#include "Settings.h"
#include "Panel.h"
#include "Object.h"
#include "ProcessList.h"
#include "ProvideCurses.h"
#include "XUtils.h"
typedef enum Orientation_ {
VERTICAL,
HORIZONTAL
} Orientation;
typedef struct ScreenManager_ {
int x1;
int y1;
int x2;
int y2;
Orientation orientation;
Vector* panels;
int panelCount;
const Header* header;
const Settings* settings;
bool owner;
bool allowFocusChange;
} 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) {
ScreenManager* this;
this = xMalloc(sizeof(ScreenManager));
this->x1 = x1;
this->y1 = y1;
this->x2 = x2;
this->y2 = y2;
this->orientation = orientation;
this->x1 = 0;
this->y1 = header->height;
this->x2 = 0;
this->y2 = -1;
this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE);
this->panelCount = 0;
this->header = header;
this->settings = settings;
this->state = state;
this->owner = owner;
this->allowFocusChange = true;
return this;
@ -71,7 +47,6 @@ inline int ScreenManager_size(ScreenManager* this) {
}
void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
if (this->orientation == HORIZONTAL) {
int lastX = 0;
if (this->panelCount > 0) {
Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
@ -84,8 +59,6 @@ void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
Panel_resize(item, COLS - this->x1 + this->x2 - lastX, height);
}
Panel_move(item, lastX, this->y1);
}
// TODO: VERTICAL
Vector_add(this->panels, item);
item->needsRedraw = true;
this->panelCount++;
@ -104,7 +77,6 @@ void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) {
this->x2 = x2;
this->y2 = y2;
int panels = this->panelCount;
if (this->orientation == HORIZONTAL) {
int lastX = 0;
for (int i = 0; i < panels - 1; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i);
@ -116,8 +88,6 @@ void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) {
Panel_resize(panel, COLS - x1 + x2 - lastX, LINES - y1 + y2);
Panel_move(panel, lastX, y1);
}
// TODO: VERTICAL
}
static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut) {
ProcessList* pl = this->header->pl;
@ -125,13 +95,18 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
struct timeval tv;
gettimeofday(&tv, NULL);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
*timedOut = (newTime - *oldTime > this->settings->delay);
*rescan = *rescan || *timedOut;
if (newTime < *oldTime) *rescan = true; // clock was adjusted?
*rescan |= *timedOut;
if (newTime < *oldTime) {
*rescan = true; // clock was adjusted?
}
if (*rescan) {
*oldTime = newTime;
ProcessList_scan(pl);
if (*sortTimeout == 0 || this->settings->treeView) {
ProcessList_scan(pl, this->state->pauseProcessUpdate);
if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) {
ProcessList_sort(pl);
*sortTimeout = 1;
}
@ -144,27 +119,20 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
*rescan = false;
}
static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_redraw) {
const int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_draw(panel, i == focus);
if (this->orientation == HORIZONTAL) {
mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
Panel_draw(panel, force_redraw, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection), State_hideFunctionBar(this->state));
mvvline(panel->y, panel->x + panel->w, ' ', panel->h + (State_hideFunctionBar(this->state) ? 1 : 0));
}
}
}
static Panel* setCurrentPanel(Panel* panel) {
FunctionBar_draw(panel->currentBar, NULL);
return panel;
}
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false;
int focus = 0;
Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
Panel* panelFocus = (Panel*) Vector_get(this->panels, focus);
double oldTime = 0.0;
@ -173,6 +141,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool timedOut = true;
bool redraw = true;
bool force_redraw = true;
bool rescan = false;
int sortTimeout = 0;
int resetSortTimeout = 5;
@ -182,30 +151,15 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut);
}
if (redraw) {
ScreenManager_drawPanels(this, focus);
if (redraw || force_redraw) {
ScreenManager_drawPanels(this, focus, force_redraw);
force_redraw = false;
}
int prevCh = ch;
set_escdelay(25);
ch = getch();
if (this->settings->vimMode) {
switch (ch) {
case 'h': ch = KEY_LEFT; break;
case 'j': ch = KEY_DOWN; break;
case 'k': ch = KEY_UP; break;
case 'l': ch = KEY_RIGHT; break;
case KEY_LEFT: ch = 'h'; break;
case KEY_DOWN: ch = 'j'; break;
case KEY_UP: ch = 'k'; break;
case KEY_RIGHT: ch = 'l'; break;
case 'K': ch = 'k'; break;
case 'J': ch = 'K'; break;
case 'L': ch = 'l'; break;
}
}
HandlerResult result = IGNORED;
if (ch == KEY_MOUSE && this->settings->enableMouse) {
ch = ERR;
@ -226,7 +180,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) {
focus = i;
panelFocus = setCurrentPanel(panel);
panelFocus = panel;
Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) {
@ -254,8 +208,9 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (closeTimeout == 100) {
break;
}
} else
} else {
closeTimeout = 0;
}
redraw = false;
continue;
}
@ -272,9 +227,12 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (result & SYNTH_KEY) {
ch = result >> 16;
}
if (result & REDRAW) {
if (result & REFRESH) {
sortTimeout = 0;
}
if (result & REDRAW) {
force_redraw = true;
}
if (result & RESCAN) {
rescan = true;
sortTimeout = 0;
@ -297,14 +255,21 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange)
if (!this->allowFocusChange) {
break;
}
tryLeft:
if (focus > 0)
if (focus > 0) {
focus--;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
if (Panel_size(panelFocus) == 0 && focus > 0)
}
panelFocus = (Panel*) Vector_get(this->panels, focus);
if (Panel_size(panelFocus) == 0 && focus > 0) {
goto tryLeft;
}
break;
case KEY_RIGHT:
case KEY_CTRL('F'):
@ -312,18 +277,24 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange)
if (!this->allowFocusChange) {
break;
}
tryRight:
if (focus < this->panelCount - 1)
if (focus < this->panelCount - 1) {
focus++;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1)
}
panelFocus = (Panel*) Vector_get(this->panels, focus);
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) {
goto tryRight;
}
break;
case KEY_F(10):
case 'q':
case 27:
case 'q':
case KEY_F(10):
quit = true;
continue;
default:
@ -334,8 +305,11 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
}
}
if (lastFocus)
if (lastFocus) {
*lastFocus = panelFocus;
if (lastKey)
}
if (lastKey) {
*lastKey = ch;
}
}

View File

@ -1,52 +1,47 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_ScreenManager
#define HEADER_ScreenManager
/*
htop - ScreenManager.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "FunctionBar.h"
#include "Vector.h"
#include "Header.h"
#include "Settings.h"
#include "Panel.h"
#include <stdbool.h>
#include "Action.h"
#include "Header.h"
#include "Panel.h"
#include "Settings.h"
#include "Vector.h"
typedef enum Orientation_ {
VERTICAL,
HORIZONTAL
} Orientation;
typedef struct ScreenManager_ {
int x1;
int y1;
int x2;
int y2;
Orientation orientation;
Vector* panels;
int panelCount;
const Header* header;
Header* header;
const Settings* settings;
const State* state;
bool owner;
bool allowFocusChange;
} ScreenManager;
ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const State* state, bool owner);
extern ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner);
void ScreenManager_delete(ScreenManager* this);
extern void ScreenManager_delete(ScreenManager* this);
int ScreenManager_size(ScreenManager* this);
extern int ScreenManager_size(ScreenManager* this);
void ScreenManager_add(ScreenManager* this, Panel* item, int size);
extern void ScreenManager_add(ScreenManager* this, Panel* item, int size);
Panel* ScreenManager_remove(ScreenManager* this, int idx);
extern Panel* ScreenManager_remove(ScreenManager* this, int idx);
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);
extern void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);
extern void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey);
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey);
#endif

View File

@ -1,84 +1,28 @@
/*
htop - Settings.c
(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.
*/
#include "Settings.h"
#include "Platform.h"
#include "StringUtils.h"
#include "Vector.h"
#include "CRT.h"
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#define DEFAULT_DELAY 15
#include "CRT.h"
#include "Macros.h"
#include "Meter.h"
#include "Platform.h"
#include "XUtils.h"
/*{
#include "Process.h"
#include <stdbool.h>
typedef struct {
int len;
char** names;
int* modes;
} MeterColumnSettings;
typedef struct Settings_ {
char* filename;
MeterColumnSettings columns[2];
ProcessField* fields;
int flags;
int colorScheme;
int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool showCPUUsage;
bool showCPUFrequency;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
bool showThreadNames;
bool hideKernelThreads;
bool hideUserlandThreads;
bool highlightBaseName;
bool highlightMegabytes;
bool highlightThreads;
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
bool enableMouse;
bool vimMode;
#ifdef HAVE_LIBHWLOC
bool topologyAffinity;
#endif
bool changed;
} Settings;
#ifndef Settings_cpuId
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromZero ? (cpu) : (cpu)+1)
#endif
}*/
void Settings_delete(Settings* this) {
free(this->filename);
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);
free(this->columns[i].modes);
}
@ -87,16 +31,14 @@ void Settings_delete(Settings* this) {
static void Settings_readMeters(Settings* this, char* line, int column) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
char** ids = String_split(trim, ' ', NULL);
free(trim);
this->columns[column].names = ids;
}
static void Settings_readMeterModes(Settings* this, char* line, int column) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
char** ids = String_split(trim, ' ', NULL);
free(trim);
int len = 0;
for (int i = 0; ids[i]; i++) {
@ -111,9 +53,9 @@ static void Settings_readMeterModes(Settings* this, char* line, int column) {
this->columns[column].modes = modes;
}
static void Settings_defaultMeters(Settings* this) {
static void Settings_defaultMeters(Settings* this, int initialCpuCount) {
int sizes[] = { 3, 3 };
if (this->cpuCount > 4) {
if (initialCpuCount > 4) {
sizes[1]++;
}
for (int i = 0; i < 2; i++) {
@ -121,14 +63,13 @@ static void Settings_defaultMeters(Settings* this) {
this->columns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i];
}
int r = 0;
if (this->cpuCount > 8) {
if (initialCpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2");
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].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs");
@ -141,7 +82,6 @@ static void Settings_defaultMeters(Settings* this) {
this->columns[0].modes[1] = BAR_METERMODE;
this->columns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("Tasks");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("LoadAverage");
@ -150,17 +90,16 @@ static void Settings_defaultMeters(Settings* this) {
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);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
char** ids = String_split(trim, ' ', NULL);
free(trim);
int i, j;
*flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
if (id > 0 && id < LAST_PROCESSFIELD && Process_fields[id].name) {
fields[j] = id;
*flags |= Process_fields[id].flags;
j++;
@ -170,9 +109,8 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
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;
CRT_dropPrivileges();
fd = fopen(fileName, "r");
CRT_restorePrivileges();
@ -186,7 +124,7 @@ static bool Settings_read(Settings* this, const char* fileName) {
if (!line) {
break;
}
int nOptions;
size_t nOptions;
char** option = String_split(line, '=', &nOptions);
free (line);
if (nOptions < 2) {
@ -199,12 +137,17 @@ static bool Settings_read(Settings* this, const char* fileName) {
} else if (String_eq(option[0], "sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "tree_sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->treeSortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) {
this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_sort_direction")) {
this->treeDirection = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
} else if (String_eq(option[0], "hide_threads")) {
this->hideThreads = atoi(option[1]);
} else if (String_eq(option[0], "tree_view_always_by_pid")) {
this->treeViewAlwaysByPID = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) {
this->hideKernelThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_userland_threads")) {
@ -221,6 +164,16 @@ static bool Settings_read(Settings* this, const char* fileName) {
this->highlightMegabytes = atoi(option[1]);
} else if (String_eq(option[0], "highlight_threads")) {
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")) {
this->headerMargin = atoi(option[1]);
} else if (String_eq(option[0], "expand_system_time")) {
@ -228,21 +181,32 @@ static bool Settings_read(Settings* this, const char* fileName) {
this->detailedCPUTime = atoi(option[1]);
} else if (String_eq(option[0], "detailed_cpu_time")) {
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")) {
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")) {
this->showCPUUsage = atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_frequency")) {
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")) {
this->updateProcessNames = atoi(option[1]);
} else if (String_eq(option[0], "account_guest_in_cpu_meter")) {
this->accountGuestInCPUMeter = atoi(option[1]);
} 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")) {
this->colorScheme = atoi(option[1]);
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0;
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) {
this->colorScheme = 0;
}
} else if (String_eq(option[0], "enable_mouse")) {
this->enableMouse = atoi(option[1]);
} else if (String_eq(option[0], "left_meters")) {
@ -257,8 +221,8 @@ static bool Settings_read(Settings* this, const char* fileName) {
} else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1);
didReadMeters = true;
} else if (String_eq(option[0], "vim_mode")) {
this->vimMode = atoi(option[1]);
} else if (String_eq(option[0], "hide_function_bar")) {
this->hideFunctionBar = atoi(option[1]);
#ifdef HAVE_LIBHWLOC
} else if (String_eq(option[0], "topology_affinity")) {
this->topologyAffinity = !!atoi(option[1]);
@ -268,7 +232,7 @@ static bool Settings_read(Settings* this, const char* fileName) {
}
fclose(fd);
if (!didReadMeters) {
Settings_defaultMeters(this);
Settings_defaultMeters(this, initialCpuCount);
}
return didReadFields;
}
@ -318,7 +282,8 @@ bool Settings_write(Settings* this) {
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "tree_sort_key=%d\n", (int) this->treeSortKey - 1);
fprintf(fd, "tree_sort_direction=%d\n", (int) this->treeDirection);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers);
@ -327,12 +292,22 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
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_always_by_pid=%d\n", (int) this->treeViewAlwaysByPID);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
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_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, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter);
fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
@ -342,7 +317,7 @@ bool Settings_write(Settings* this) {
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
fprintf(fd, "vim_mode=%d\n", (int) this->vimMode);
fprintf(fd, "hide_function_bar=%d\n", (int) this->hideFunctionBar);
#ifdef HAVE_LIBHWLOC
fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity);
#endif
@ -350,13 +325,13 @@ bool Settings_write(Settings* this) {
return true;
}
Settings* Settings_new(int cpuCount) {
Settings* Settings_new(int initialCpuCount) {
Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->treeSortKey = PID;
this->direction = 1;
this->hideThreads = false;
this->treeDirection = 1;
this->shadowOtherUsers = false;
this->showThreadNames = false;
this->hideKernelThreads = false;
@ -365,22 +340,30 @@ Settings* Settings_new(int cpuCount) {
this->highlightBaseName = false;
this->highlightMegabytes = false;
this->detailedCPUTime = false;
this->countCPUsFromZero = false;
this->countCPUsFromOne = false;
this->showCPUUsage = true;
this->showCPUFrequency = false;
#ifdef HAVE_SENSORS_SENSORS_H
this->showCPUTemperature = false;
this->degreeFahrenheit = false;
#endif
this->updateProcessNames = false;
this->cpuCount = cpuCount;
this->showProgramPath = true;
this->highlightThreads = true;
this->highlightChanges = false;
this->highlightDelaySecs = DEFAULT_HIGHLIGHT_SECS;
this->findCommInCmdline = true;
this->stripExeFromCmdline = true;
this->showMergedCommand = false;
this->hideFunctionBar = 0;
#ifdef HAVE_LIBHWLOC
this->topologyAffinity = false;
#endif
this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
this->fields = xCalloc(LAST_PROCESSFIELD + 1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
this->flags = 0;
ProcessField* defaults = Platform_defaultFields;
const ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
@ -392,7 +375,9 @@ Settings* Settings_new(int cpuCount) {
this->filename = xStrdup(rcfile);
} else {
const char* home = getenv("HOME");
if (!home) home = "";
if (!home)
home = "";
const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
char* configDir = NULL;
char* htopDir = NULL;
@ -406,7 +391,6 @@ Settings* Settings_new(int cpuCount) {
htopDir = String_cat(home, "/.config/htop");
}
legacyDotfile = String_cat(home, "/.htoprc");
CRT_dropPrivileges();
(void) mkdir(configDir, 0700);
(void) mkdir(htopDir, 0700);
@ -426,37 +410,50 @@ Settings* Settings_new(int cpuCount) {
this->delay = DEFAULT_DELAY;
bool ok = false;
if (legacyDotfile) {
ok = Settings_read(this, legacyDotfile);
ok = Settings_read(this, legacyDotfile, initialCpuCount);
if (ok) {
// Transition to new location and delete old configuration file
if (Settings_write(this))
if (Settings_write(this)) {
unlink(legacyDotfile);
}
}
free(legacyDotfile);
}
if (!ok) {
ok = Settings_read(this, this->filename);
ok = Settings_read(this, this->filename, initialCpuCount);
}
if (!ok) {
this->changed = true;
// TODO: how to get SYSCONFDIR correctly through Autoconf?
char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
ok = Settings_read(this, systemSettings);
ok = Settings_read(this, systemSettings, initialCpuCount);
free(systemSettings);
}
if (!ok) {
Settings_defaultMeters(this);
Settings_defaultMeters(this, initialCpuCount);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
this->findCommInCmdline = true;
this->stripExeFromCmdline = true;
this->showMergedCommand = false;
this->headerMargin = true;
}
return this;
}
void Settings_invertSortOrder(Settings* this) {
if (this->direction == 1)
this->direction = -1;
else
this->direction = 1;
int* attr = (this->treeView) ? &(this->treeDirection) : &(this->direction);
*attr = (*attr == 1) ? -1 : 1;
}
void Settings_setSortKey(Settings* this, ProcessField sortKey) {
if (this->treeViewAlwaysByPID || !this->treeView) {
this->sortKey = sortKey;
this->direction = 1;
this->treeView = false;
} else {
this->treeSortKey = sortKey;
this->treeDirection = 1;
}
}

View File

@ -1,18 +1,21 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Settings
#define HEADER_Settings
/*
htop - Settings.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define DEFAULT_DELAY 15
#include "config.h" // IWYU pragma: keep
#include <stdbool.h>
#include <stdint.h>
#include "Process.h"
#include <stdbool.h>
#define DEFAULT_DELAY 15
typedef struct {
int len;
@ -22,25 +25,29 @@ typedef struct {
typedef struct Settings_ {
char* filename;
MeterColumnSettings columns[2];
ProcessField* fields;
int flags;
uint32_t flags;
int colorScheme;
int delay;
int cpuCount;
int direction;
int treeDirection;
ProcessField sortKey;
ProcessField treeSortKey;
bool countCPUsFromZero;
bool countCPUsFromOne;
bool detailedCPUTime;
bool showCPUUsage;
bool showCPUFrequency;
#ifdef HAVE_SENSORS_SENSORS_H
bool showCPUTemperature;
bool degreeFahrenheit;
#endif
bool treeView;
bool treeViewAlwaysByPID;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
bool showThreadNames;
bool hideKernelThreads;
@ -48,11 +55,16 @@ typedef struct Settings_ {
bool highlightBaseName;
bool highlightMegabytes;
bool highlightThreads;
bool highlightChanges;
int highlightDelaySecs;
bool findCommInCmdline;
bool stripExeFromCmdline;
bool showMergedCommand;
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
bool enableMouse;
bool vimMode;
int hideFunctionBar; // 0 - off, 1 - on ESC until next input, 2 - permanently
#ifdef HAVE_LIBHWLOC
bool topologyAffinity;
#endif
@ -60,17 +72,26 @@ typedef struct Settings_ {
bool changed;
} Settings;
#ifndef Settings_cpuId
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromZero ? (cpu) : (cpu)+1)
#endif
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))
static inline ProcessField Settings_getActiveSortKey(const Settings* this) {
return (this->treeView)
? (this->treeViewAlwaysByPID ? PID : this->treeSortKey)
: this->sortKey;
}
extern void Settings_delete(Settings* this);
static inline int Settings_getActiveDirection(const Settings* this) {
return this->treeView ? this->treeDirection : this->direction;
}
extern bool Settings_write(Settings* this);
void Settings_delete(Settings* this);
extern Settings* Settings_new(int cpuCount);
bool Settings_write(Settings* this);
extern void Settings_invertSortOrder(Settings* this);
Settings* Settings_new(int initialCpuCount);
void Settings_invertSortOrder(Settings* this);
void Settings_setSortKey(Settings* this, ProcessField sortKey);
#endif

View File

@ -1,34 +1,25 @@
/*
htop - SignalsPanel.c
(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.
*/
#include "Panel.h"
#include "SignalsPanel.h"
#include "Platform.h"
#include "ListItem.h"
#include "RichString.h"
#include <stdlib.h>
#include <assert.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"
/*{
typedef struct SignalItem_ {
const char* name;
int number;
} SignalItem;
}*/
Panel* SignalsPanel_new() {
Panel* this = Panel_new(1, 1, 1, 1, true, Class(ListItem), FunctionBar_newEnterEsc("Send ", "Cancel "));
Panel* this = Panel_new(1, 1, 1, 1, Class(ListItem), true, FunctionBar_newEnterEsc("Send ", "Cancel "));
const int defaultSignal = SIGTERM;
int defaultPosition = 15;
unsigned int i;
@ -44,7 +35,7 @@ Panel* SignalsPanel_new() {
static char buf[16];
for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) {
int n = sig - SIGRTMIN;
xSnprintf(buf, 16, "%2d SIGRTMIN%-+3d", sig, n);
xSnprintf(buf, sizeof(buf), "%2d SIGRTMIN%-+3d", sig, n);
if (n == 0) {
buf[11] = '\0';
}

View File

@ -1,21 +1,19 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_SignalsPanel
#define HEADER_SignalsPanel
/*
htop - SignalsPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
typedef struct SignalItem_ {
const char* name;
int number;
} SignalItem;
extern Panel* SignalsPanel_new();
Panel* SignalsPanel_new(void);
#endif

View File

@ -1,154 +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>
/*{
#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) {
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,36 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#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). :)
*/
extern char* String_cat(const char* s1, const char* s2);
extern char* String_trim(const char* in);
extern int String_eq(const char* s1, const char* s2);
extern char** String_split(const char* s, char sep, int* n);
extern void String_freeArray(char** s);
extern char* String_getToken(const char* line, const unsigned short int numMatch);
extern char* String_readLine(FILE* fd);
#endif

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