894 Commits

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The end result is:

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

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

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

This changes is python2 and python3 compatible

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

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

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

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

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

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

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

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

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

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

Summary of additions:

* Initial setup of Solaris platform directory
* Add Solaris platform into autoconf template
* Uptime and load averages
* Add dependency on libkstat
* Basic process listing
* Zone name display
* CPU detection
* Per-process memory and CPU usage parsed correctly
* Uses sysconf to discover number of CPUs, instead of more complex libkstat code
* Simple memory display working
* Reduce repetitive calls to the PAGE_SIZE macro when reading memory info
* Add Project, Contract, Task, and Pool into process properties
* Use system major()/minor() implementations and remove extraneous definition of mkdev()
* Get the STARTTIME column working properly, using the Linux implementation as a guide
2018-03-02 18:20:46 -03:00
d4ea7cd65c Fix bashisms (#749)
The configure script relied on bash-specific extensions to shell syntax
and behavior, causing build failures on systems with other /bin/sh
implementations. This commit replaces those with equivalent constructs
that should work in all POSIX shells.
2018-02-26 20:07:52 -03:00
9ca1c993ac Add Contributing Guide! 2018-02-26 11:45:53 -03:00
ccd156f8ba Updates to generated header files 2018-02-26 11:44:46 -03:00
858af2505f Protect against overflows in RichString_setAttrn 2018-02-26 11:05:12 -03:00
655c7293d2 Update ChangeLog 2018-02-26 10:54:01 -03:00
bc5d46982f use CFLAGS from ncurses*-config, if present (#745)
Fixes #695.
2018-02-26 10:19:01 -03:00
c01f40eb3e Fix build failure ('major' undefined) in glibc 2.28. (#746)
glibc 2.28 no longer defines 'major' and 'minor' in <sys/types.h> and
requires us to include <sys/sysmacros.h>. (glibc 2.25 starts
deprecating the macros in <sys/types.h>.) Now do include the latter if
found on the system.

At the moment, let's also utilize AC_HEADER_MAJOR in configure script.
However as Autoconf 2.69 has not yet updated the AC_HEADER_MAJOR macro
to reflect the glibc change [1], so add a workaround code.

Fixes #663. Supersedes pull request #729.

Reference:
[1] https://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=commit;h=e17a30e987d7ee695fb4294a82d987ec3dc9b974

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-02-26 10:15:05 -03:00
eed18dd107 Remove unused function from unsupported/ 2018-02-18 21:18:53 -03:00
f914617508 Make settings file finding sequence more straightforward
Avoid unnecessary access() call and make code read more linearly.
2018-02-18 20:42:17 -03:00
03b2581745 Only consider a read successful when the file seems valid
Require at least the `fields` entry to be present,
so we can have a decent guess that it was indeed a settings file.
2018-02-18 20:35:23 -03:00
8c653212c0 Replace size_t with int/void* union
I was occasionally passing negative values to size_t.
Plus, this better reflects the intent of the variant argument.

Reported by Coverity:
https://scan8.coverity.com/reports.htm#v13253/p10402/fileInstanceId=22093891&defectInstanceId=7543346&mergedDefectId=174179&fileStart=251&fileEnd=500
2018-02-18 10:38:49 -03:00
b064d501ae linux/Battery.c: make sure fd is always closed
Detected by Coverity:
https://scan8.coverity.com/reports.htm#v13252/p10402/fileInstanceId=22093957&defectInstanceId=7543348&mergedDefectId=174180
2018-02-18 10:21:22 -03:00
ff78a1bfce Fix out-of-bounds read
Detected by Coverity:
https://scan8.coverity.com/reports.htm#v13252/p10402/fileInstanceId=22093847&defectInstanceId=7543344&mergedDefectId=174181
2018-02-18 10:17:56 -03:00
f4f35da7e0 Fix indentation 2018-02-18 10:17:29 -03:00
76366be3f1 Update ChangeLog 2018-02-17 21:11:54 -02:00
6dda8d2586 linux/LinuxProcessList.c: Fix indentation. 2018-02-17 20:52:46 -02:00
5fca258f33 call clear() function when SIGWINCH is received. (#660) 2018-02-17 16:25:57 -02:00
70ed51a303 linux/LinuxProcessList: fix reading of number of read syscalls of process
The "if" tests if the character at index "5" is 'r', as a first quick
check. However at index "5" will always be a colon ":". This patch fixes
the off-by-one error. htop now shows proper values in the RD_SYSC
column.

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
2018-02-17 16:14:34 -02:00
df9922a67e Fix preservation of LDFLAGS value during configure script
Fixes #738.
2018-02-17 14:50:55 -02:00
fcdff59f4c Update ChangeLog 2018-02-17 14:50:42 -02:00
b544c22c72 Fix issue with small terminals.
Fixes #733.
2018-02-13 06:41:44 -02:00
f37a050d3d Optimize Vector_size on non-debug builds 2018-02-05 11:01:35 +01:00
03f17688ad Handle unexpected values for character passed to isalnum
It seems that certain negative integer values can crash isalnum().
Let's protect against those.

Fixes #711.
2018-02-05 10:59:20 +01:00
a32d7528f6 Check for pkgconfig's presence before using it.
Fixes #710.
2018-02-05 10:22:16 +01:00
ac2dff2881 Fix color behavior on some terminals.
Fixes #635.
2018-02-05 07:20:27 +01:00
c50440f165 Bump version to 2.1.0 2018-02-04 20:13:55 +01:00
b0588d90ff parseBatInfo: check line for NULL before passing it to String_getToken() 2018-02-04 17:04:47 +01:00
b84ebfd4e8 Clarify we are looking for the null termination
Not for a comparison to zero
2018-02-04 17:02:30 +01:00
b86e14d3cf Typo in man page
*hightlight*
2018-02-04 17:01:39 +01:00
b34d76cd41 Fix: infinite loop in tree view on macOS
Fixes #688, the bug regressed on 584a9bc.

On Mac OS X 10.11.6, all processes have their parents since there's a
special process named "kernel_task", whose PID and PPID are 0. As a
result, `this->processes` is never changed causing infinite `while`.
2018-02-04 16:51:06 +01:00
87be623eac Add support for Linux TASK_IDLE
Linux commit 06eb61844d841d0032a9950ce7f8e783ee49c0d0 ("sched/debug:
Add explicit TASK_IDLE printing") exposes kthreads idling using
TASK_IDLE in procfs as "I (idle)".

Until now, when sorting the STATE ("S") column, htop used the raw
value of the state character for comparison, however that led to the
undesirable effect of TASK_IDLE ('I') tasks being sorted above tasks
that were running ('R').

Thus, explicitly recognize the idle process state, and sort it below
others.
2018-02-04 16:44:21 +01:00
b27712181a Darwin: disable thread reading due to bug in macOS High Sierra 2018-02-04 08:59:29 +01:00
235 changed files with 16217 additions and 10080 deletions

View File

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

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

@ -0,0 +1,136 @@
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-minimal-gcc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: sudo apt-get install libncursesw5-dev
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror --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 -k
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors"
build-ubuntu-latest-minimal-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
- 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 -k
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror"
whitespace_check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: check-whitespaces
run: git diff-tree --check $(git hash-object -t tree /dev/null) HEAD

6
.gitignore vendored
View File

@ -17,12 +17,15 @@ htop
*.h.gch
*/.dirstamp
# automake/autoconf related files
.deps/
Makefile
Makefile.in
INSTALL
aclocal.m4
autom4te.cache/
compile
conf*/
config.guess
config.h
config.h.in
@ -39,3 +42,6 @@ ltmain.sh
m4/
missing
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

12
AUTHORS
View File

@ -1 +1,11 @@
Hisham H. Muhammad
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

530
Action.c
View File

@ -1,82 +1,60 @@
/*
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) {
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) {
Panel* panel = st->panel;
Header* header = st->header;
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);
Panel* panelFocus;
int ch;
bool unfollow = false;
int pid = MainPanel_selectedPid((MainPanel*)panel);
if (header->pl->following == -1) {
int pid = followProcess ? MainPanel_selectedPid((MainPanel*)panel) : -1;
if (followProcess && header->pl->following == -1) {
header->pl->following = pid;
unfollow = true;
}
@ -86,54 +64,59 @@ Object* Action_pickFromVector(State* st, Panel* list, int x) {
}
ScreenManager_delete(scr);
Panel_move(panel, 0, y);
Panel_resize(panel, COLS, LINES-y-1);
Panel_resize(panel, COLS, LINES - y - 1);
if (panelFocus == list && ch == 13) {
if (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, 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;
}
@ -150,45 +133,64 @@ 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;
}
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
ScreenSettings* ss = settings->ss;
ss->sortKey = sortKey;
ss->direction = 1;
ss->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
static bool collapseIntoParent(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
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);
if (q->pid == ppid) {
q->showChildren = false;
Panel_setSelected(panel, i);
return true;
}
}
return false;
}
static Htop_Reaction sortBy(State* st) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by");
ScreenSettings* ss = st->settings->ss;
ProcessField* fields = ss->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] == ss->sortKey)
Panel_setSelected(sortPanel, i);
free(name);
}
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15);
if (field) {
reaction |= Action_setSortKey(st->settings, field->key);
}
Object_delete(sortPanel);
return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
Settings_setSortKey(settings, sortKey);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
}
// ----------------------------------------
static Htop_Reaction actionResize(State* st) {
Panel_resize(st->panel, COLS, LINES-(st->panel->y)-1);
return HTOP_REDRAW_BAR;
static Htop_Reaction actionSetSortColumn(State* st) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by");
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] == Settings_getActiveSortKey(st->settings))
Panel_setSelected(sortPanel, i);
free(name);
}
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false);
if (field) {
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 actionSortByPID(State* st) {
return Action_setSortKey(st->settings, PID);
}
static Htop_Reaction actionSortByMemory(State* st) {
@ -210,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;
}
@ -219,10 +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) {
ScreenSettings* ss = st->settings->ss;
ss->treeView = !ss->treeView;
if (ss->treeView) ss->direction = 1;
st->settings->treeView = !st->settings->treeView;
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;
}
@ -235,6 +243,7 @@ static Htop_Reaction actionIncFilter(State* st) {
}
static Htop_Reaction actionIncSearch(State* st) {
IncSet_reset(((MainPanel*)st->panel)->inc, INC_SEARCH);
IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
@ -250,71 +259,75 @@ static Htop_Reaction actionLowerPriority(State* st) {
}
static Htop_Reaction actionInvertSortOrder(State* st) {
ScreenSettings_invertSortOrder(st->settings->ss);
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;
}
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}
static Htop_Reaction actionQuit() {
return HTOP_QUIT;
}
static Htop_Reaction actionNextScreen(State* st) {
Settings* settings = st->settings;
settings->ssIndex++;
if (settings->ssIndex == settings->nScreens) {
settings->ssIndex = 0;
static Htop_Reaction actionCollapseIntoParent(State* st) {
if (!st->settings->treeView) {
return HTOP_OK;
}
settings->ss = settings->screens[settings->ssIndex];
return HTOP_REFRESH;
bool changed = collapseIntoParent(st->panel);
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}
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;
Affinity* affinity = Affinity_get(p, st->pl);
if (!affinity) return HTOP_OK;
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity);
Affinity_delete(affinity);
if (!p)
return HTOP_OK;
void* set = Action_pickFromVector(st, affinityPanel, 15);
Affinity* affinity1 = Affinity_get(p, st->pl);
if (!affinity1)
return HTOP_OK;
int width;
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width);
width += 1; /* we add a gap between the panels */
Affinity_delete(affinity1);
void* set = Action_pickFromVector(st, affinityPanel, width, true);
if (set) {
Affinity* affinity = AffinityPanel_getAffinity(affinityPanel, st->pl);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (size_t) affinity, NULL);
if (!ok) beep();
Affinity_delete(affinity);
Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl);
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();
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15);
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, (size_t) sgn->key, NULL);
MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL);
napms(500);
}
}
@ -323,16 +336,16 @@ 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);
ListItem* allUsers = ListItem_new("All users", -1);
Panel_insert(usersPanel, 0, (Object*) allUsers);
ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20);
ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false);
if (picked) {
if (picked == allUsers) {
st->pl->userId = -1;
st->pl->userId = (uid_t)-1;
} else {
Action_setUserOnly(ListItem_getRef(picked), &(st->pl->userId));
}
@ -343,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);
@ -358,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);
@ -367,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) {
@ -383,76 +411,100 @@ 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;
clear();
attrset(CRT_colors[HELP_BOLD]);
for (int i = 0; i < LINES-1; i++)
for (int i = 0; i < LINES - 1; i++)
mvhline(i, 0, ' ', COLS);
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("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/");
addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
@ -462,13 +514,13 @@ static Htop_Reaction actionHelp(State* st) {
} else {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "virtualiz");
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_GUEST], "virtualiz");
addattrstr(CRT_colors[BAR_SHADOW], " used%");
}
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("/");
@ -476,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();
@ -517,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);
@ -533,57 +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;
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[']'] = 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['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['?'] = actionHelp;
keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
keys['\t'] = actionNextScreen;
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[' '] = actionTag;
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,31 +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);
}
Object* Action_pickFromVector(State* st, Panel* list, int x);
typedef Htop_Reaction (*Htop_Action)(State* st);
// ----------------------------------------
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess);
bool Action_setUserOnly(const char* userName, uid_t* userId);
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
// ----------------------------------------
Htop_Reaction Action_follow(State* st);
void Action_setBindings(Htop_Action* keys);
#endif

View File

@ -1,37 +1,31 @@
/*
htop - Affinity.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include "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));
@ -79,7 +73,8 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
return affinity;
}
bool Affinity_set(Process* proc, Affinity* this) {
bool Affinity_set(Process* proc, Arg arg) {
Affinity* this = arg.v;
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
for (int i = 0; i < this->used; i++) {
hwloc_bitmap_set(cpuset, this->cpus[i]);
@ -89,21 +84,25 @@ bool Affinity_set(Process* proc, Affinity* this) {
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;
}
bool Affinity_set(Process* proc, Affinity* this) {
bool Affinity_set(Process* proc, Arg arg) {
Affinity* this = arg.v;
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
for (int i = 0; i < this->used; i++) {

View File

@ -1,25 +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
Released under the GNU GPL, see the COPYING file
(C) 2020 Red Hat, Inc. All Rights Reserved.
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;
@ -28,25 +32,18 @@ typedef struct Affinity_ {
int* cpus;
} Affinity;
Affinity* Affinity_new(ProcessList* pl);
void Affinity_delete(Affinity* this);
void Affinity_add(Affinity* this, int id);
#ifdef HAVE_LIBHWLOC
#if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)
Affinity* Affinity_get(Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Affinity* this);
bool Affinity_set(Process* proc, Arg arg);
#elif HAVE_LINUX_AFFINITY
Affinity* Affinity_get(Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Affinity* this);
#endif
#endif /* HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY */
#endif

View File

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

View File

@ -1,23 +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.
*/
#include "Panel.h"
#include "Affinity.h"
#include "ProcessList.h"
#include "ListItem.h"
extern PanelClass AffinityPanel_class;
extern const PanelClass AffinityPanel_class;
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity);
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width);
Affinity* AffinityPanel_getAffinity(Panel* this, 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,8 +14,7 @@ typedef struct AvailableColumnsPanel_ {
Panel* columns;
} AvailableColumnsPanel;
extern PanelClass AvailableColumnsPanel_class;
extern const PanelClass AvailableColumnsPanel_class;
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns);

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, i);
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,8 +23,7 @@ typedef struct AvailableMetersPanel_ {
Panel* rightPanel;
} AvailableMetersPanel;
extern PanelClass AvailableMetersPanel_class;
extern const PanelClass AvailableMetersPanel_class;
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl);

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);
}
return;
xSnprintf(buffer, len, "%.1f%%%s", percent, text);
}
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

45
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,45 @@
Contributing Guide
==================
Thank you so much for taking the time to contribute in to htop!
Bug Reports
-----------
Bug reports should be posted in the [Github issue
tracker](https://github.com/htop-dev/htop/issues).
Bug reports are extremely important since it's impossible for us to test
htop in every possible system, distribution and scenario. Your feedback
is what keeps the tool stable and always improving! Thank you!
Pull Requests
-------------
Code contributions are most welcome! Just [fork the
repo](https://github.com/htop-dev/htop) and send a [pull
request](https://github.com/htop-dev/htop/pulls). Help is especially
appreciated for support of platforms other than Linux. If proposing new
features, please be mindful that htop is a system tool that needs to keep a
small footprint and perform well on systems under stress -- so unfortunately
we can't accept every new feature proposed, as we need to keep the tool slim
and maintainable. Great ideas backed by a PR are always carefully considered
for inclusion though! Also, PRs containing bug fixes and portability tweaks
are always included, please send those in!
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

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

View File

@ -1,125 +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_ITEMCOUNT = 8, // number of entries in this enum
} CPUMeterValues;
}*/
int CPUMeter_attributes[] = {
CPU_NICE, CPU_NORMAL, CPU_KERNEL, 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);
xSnprintf(buffer, size, "%5.1f%%", percent);
if (this->pl->settings->showCPUUsage) {
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent);
}
if (this->pl->settings->showCPUFrequency) {
double cpuFrequency = this->values[CPU_METER_FREQUENCY];
if (isnan(cpuFrequency)) {
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A");
} else {
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%4uMHz", (unsigned)cpuFrequency);
}
}
#ifdef HAVE_SENSORS_SENSORS_H
if (this->pl->settings->showCPUTemperature) {
double cpuTemperature = this->values[CPU_METER_TEMPERATURE];
if (isnan(cpuTemperature))
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A");
else if (this->pl->settings->degreeFahrenheit)
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%3d%sF", (int)(cpuTemperature * 9 / 5 + 32), CRT_degreeSign);
else
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%d%sC", (int)cpuTemperature, CRT_degreeSign);
}
#endif
xSnprintf(buffer, size, "%s%s%s%s%s",
cpuUsageBuffer,
(cpuUsageBuffer[0] && (cpuFrequencyBuffer[0] || cpuTemperatureBuffer[0])) ? " " : "",
cpuFrequencyBuffer,
(cpuFrequencyBuffer[0] && cpuTemperatureBuffer[0]) ? " " : "",
cpuTemperatureBuffer);
}
static void CPUMeter_display(Object* cast, RichString* out) {
static void CPUMeter_display(const Object* cast, RichString* out) {
char buffer[50];
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_KERNEL], 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_KERNEL], 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
@ -137,37 +187,34 @@ static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) {
}
}
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;
if (strchr(Meter_name(this), '2'))
this->h = h * ((count+1) / 2);
else
this->h = h * count;
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;
@ -175,32 +222,84 @@ static void AllCPUsMeter_updateMode(Meter* this, int mode) {
for (int i = 0; i < count; i++) {
Meter_setMode(meters[i], mode);
}
if (strchr(Meter_name(this), '2'))
this->h = h * ((count+1) / 2);
else
this->h = h * count;
this->h = h * ((count + ncol - 1) / ncol);
}
static void AllCPUsMeter_done(Meter* this) {
CPUMeterData* data = this->meterData;
Meter** meters = data->meters;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++)
Meter_delete((Object*)meters[i]);
free(data->meters);
free(data);
}
static void SingleColCPUsMeter_init(Meter* this) {
CPUMeterCommonInit(this, 1);
}
static void SingleColCPUsMeter_updateMode(Meter* this, int mode) {
CPUMeterCommonUpdateMode(this, mode, 1);
}
static void DualColCPUsMeter_init(Meter* this) {
CPUMeterCommonInit(this, 2);
}
static void DualColCPUsMeter_updateMode(Meter* this, int mode) {
CPUMeterCommonUpdateMode(this, mode, 2);
}
static void QuadColCPUsMeter_init(Meter* this) {
CPUMeterCommonInit(this, 4);
}
static void QuadColCPUsMeter_updateMode(Meter* this, int mode) {
CPUMeterCommonUpdateMode(this, mode, 4);
}
static void OctoColCPUsMeter_init(Meter* this) {
CPUMeterCommonInit(this, 8);
}
static void OctoColCPUsMeter_updateMode(Meter* this, int mode) {
CPUMeterCommonUpdateMode(this, mode, 8);
}
static void CPUMeterCommonDraw(Meter* this, int x, int y, int w, int ncol) {
CPUMeterData* data = this->meterData;
Meter** meters = data->meters;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
int colwidth = (w - ncol) / ncol + 1;
int diff = (w - (colwidth * ncol));
int nrows = (count + ncol - 1) / ncol;
for (int i = 0; i < count; i++) {
int d = (i / nrows) > diff ? diff : (i / nrows); // dynamic spacer
int xpos = x + ((i / nrows) * colwidth) + d;
int ypos = y + ((i % nrows) * meters[0]->h);
meters[i]->draw(meters[i], xpos, ypos, colwidth);
}
}
static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) {
Meter** meters = (Meter**) this->drawData;
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;
}
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) {
Meter** meters = (Meter**) this->drawData;
CPUMeterData* data = this->meterData;
Meter** meters = data->meters;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++) {
@ -209,7 +308,8 @@ static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) {
}
}
MeterClass CPUMeter_class = {
const MeterClass CPUMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -226,7 +326,7 @@ MeterClass CPUMeter_class = {
.init = CPUMeter_init
};
MeterClass AllCPUsMeter_class = {
const MeterClass AllCPUsMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@ -240,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,
@ -259,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,
@ -278,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,
@ -297,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,
@ -316,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,
@ -335,8 +435,121 @@ 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
};
const MeterClass AllCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "AllCPUs4",
.uiName = "CPUs (1&2&3&4/4)",
.description = "CPUs (1&2&3&4/4): all CPUs in 4 shorter columns",
.caption = "CPU",
.draw = QuadColCPUsMeter_draw,
.init = QuadColCPUsMeter_init,
.updateMode = QuadColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
const MeterClass LeftCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs4",
.uiName = "CPUs (1-4/8)",
.description = "CPUs (1-4/8): first half in 4 shorter columns",
.caption = "CPU",
.draw = QuadColCPUsMeter_draw,
.init = QuadColCPUsMeter_init,
.updateMode = QuadColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
const MeterClass RightCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
.display = CPUMeter_display
},
.defaultMode = CUSTOM_METERMODE,
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "RightCPUs4",
.uiName = "CPUs (5-8/8)",
.description = "CPUs (5-8/8): second half in 4 shorter columns",
.caption = "CPU",
.draw = 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.
*/
@ -20,32 +18,35 @@ typedef enum {
CPU_METER_STEAL = 5,
CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7,
CPU_METER_ITEMCOUNT = 8, // number of entries in this enum
CPU_METER_FREQUENCY = 8,
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 const MeterClass AllCPUs8Meter_class;
extern const MeterClass LeftCPUs8Meter_class;
extern const MeterClass RightCPUs8Meter_class;
#endif

1115
CRT.c

File diff suppressed because it is too large Load Diff

149
CRT.h
View File

@ -1,61 +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 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 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,
@ -63,6 +44,8 @@ typedef enum ColorElements_ {
FUNCTION_BAR,
FUNCTION_KEY,
FAILED_SEARCH,
FAILED_READ,
PAUSED,
PANEL_HEADER_FOCUS,
PANEL_HEADER_UNFOCUS,
PANEL_SELECTION_FOCUS,
@ -71,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,
@ -80,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,
@ -104,90 +98,85 @@ typedef enum ColorElements_ {
CHECK_MARK,
CHECK_TEXT,
CLOCK,
DATE,
DATETIME,
HELP_BOLD,
HOSTNAME,
CPU_NICE,
CPU_NICE_TEXT,
CPU_NORMAL,
CPU_KERNEL,
CPU_SYSTEM,
CPU_IOWAIT,
CPU_IRQ,
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
PANEL_EDIT,
PRESSURE_STALL_TEN,
PRESSURE_STALL_SIXTY,
PRESSURE_STALL_THREEHUNDRED,
ZFS_MFU,
ZFS_MRU,
ZFS_ANON,
ZFS_HEADER,
ZFS_OTHER,
ZFS_COMPRESSED,
ZFS_RATIO,
ZRAM,
LAST_COLORELEMENT
} ColorElements;
void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_fatalError(const char* note) ATTR_NORETURN;
void CRT_handleSIGSEGV(int sgn);
void CRT_handleSIGSEGV(int signal) ATTR_NORETURN;
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
#define KEY_RECLICK KEY_F(22)
#define KEY_ALT(x) (KEY_F(64 - 26) + ((x) - 'A'))
extern const char *CRT_treeStrAscii[TREE_STR_COUNT];
extern const char* CRT_degreeSign;
#ifdef HAVE_LIBNCURSESW
extern const char *CRT_treeStrUtf8[TREE_STR_COUNT];
extern bool CRT_utf8;
#endif
extern const char **CRT_treeStr;
extern const char* const* CRT_treeStr;
extern int CRT_delay;
extern const int* CRT_colors;
int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX;
extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount;
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);
void *backtraceArray[128];
void CRT_restorePrivileges(void);
#if HAVE_SETUID_ENABLED
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
void CRT_dropPrivileges();
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 /* HAVE_SETUID_ENABLED */
#endif
void CRT_init(const int* delay, int colorScheme, bool allowUnicode);
// TODO: pass an instance of Settings instead.
void CRT_done(void);
void CRT_init(int delay, int colorScheme);
int CRT_readKey(void);
void CRT_done();
void CRT_disableDelay(void);
void CRT_fatalError(const char* note);
int CRT_readKey();
void CRT_disableDelay();
void CRT_enableDelay();
void CRT_enableDelay(void);
void CRT_setColors(int colorScheme);

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 "ScreensPanel.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};
@ -64,11 +54,9 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
ScreenManager_add(this->scr, colors, -1);
}
static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) {
Panel* screens = (Panel*) ScreensPanel_new(this->settings);
Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns;
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns);
ScreenManager_add(this->scr, screens, 20);
ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, -1);
}
@ -99,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;
@ -109,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);
@ -120,14 +109,14 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
CategoriesPanel_makeColorsPage(this);
break;
case 3:
CategoriesPanel_makeScreensPage(this);
CategoriesPanel_makeColumnsPage(this);
break;
}
}
return result;
}
PanelClass CategoriesPanel_class = {
const PanelClass CategoriesPanel_class = {
.super = {
.extends = Class(Panel),
.delete = CategoriesPanel_delete
@ -149,6 +138,6 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
Panel_add(super, (Object*) ListItem_new("Meters", 0));
Panel_add(super, (Object*) ListItem_new("Display options", 0));
Panel_add(super, (Object*) ListItem_new("Colors", 0));
Panel_add(super, (Object*) ListItem_new("Screens", 0));
Panel_add(super, (Object*) ListItem_new("Columns", 0));
return this;
}

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,10 +22,9 @@ typedef struct CategoriesPanel_ {
ProcessList* pl;
} CategoriesPanel;
void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
extern PanelClass CategoriesPanel_class;
extern const PanelClass CategoriesPanel_class;
CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl);

284
ChangeLog
View File

@ -1,3 +1,287 @@
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
from Hisham, the creator and original maintainer, a team
of community maintainers have volunteered to take over a
fork at https://htop.dev and https://github.com/htop-dev
to keep the project going.
* Support ZFS ARC statistics
(thanks to Ross Williams)
* Support more than 2 smaller CPU meter columns
(thanks to Christoph Budziszewski)
* Support Linux proportional set size metrics
(thanks to @linvinus, @ntninja and @himikof)
* Support Linux pressure stall information metrics
(thanks to Ran Benita)
* New display option to show CPU frequency in CPU meters
(thanks to Arnav Singh)
* Update Linux sysfs battery discovery for recent kernels
(thanks to @smattie)
* Add hardware topology information in the affinity panel
(thanks to Bert Wesarg)
* Add timestamp reporting to the strace screen
(thanks to Mario Harjac)
* Add simple, optional vim key mapping mode
(thanks to Daniel Flanagan)
* Added an option to disable the mouse
(thanks to MartinJM)
* Add Solaris11 compatibility
(thanks to Jan Senolt)
* Without an argument -u uses $USER value automatically
(thanks to @solanav)
* Support less(1) search navigation shortcuts
(thanks to @syrrim)
* Update the FreeBSD maximum PID to match FreeBSD change
(thanks to @multiplexd)
* Report values larger than 100 terabytes
(thanks to @adrien1018)
* Widen ST_UID (UID) column to allow for UIDs > 9999
(thanks to DLange)
* BUGFIX: fix makefiles for building with clang
(thanks to Jorge Pereira)
* BUGFIX: fix <sys/sysmacros.h> major() usage
(thanks to @wataash and Kang-Che Sung)
* BUGFIX: fix the STARTTIME column on FreeBSD
(thanks to Rob Crowston)
* BUGFIX: truncate overwide jail names on FreeBSD
(thanks to Rob Crowston)
* BUGFIX: fix reported memory values on FreeBSD
(thanks to Tobias Kortkamp)
* BUGFIX: fix reported CPU meter values on OpenBSD
(thanks to @motet-a)
* BUGFIX: correctly identify other types of zombie process
(thanks to @joder)
* BUGFIX: improve follow-process handling in some situations
(thanks to @wangqr)
* BUGFIX: fix custom meters reverting to unexpected setting
(thanks to @wangqr)
* BUGFIX: close pipe after running lsof(1)
(thanks to Jesin)
* BUGFIX: meters honour setting of counting CPUs from 0/1
(thanks to @rnsanchez)
What's new in version 2.2.0
* Solaris/Illumos/OpenIndiana support
(thanks to Guy M. Broome)
* -t/--tree flag for starting in tree-view mode
(thanks to Daniel Flanagan)
* macOS: detects High Sierra version to avoid OS bug
(thanks to Pierre Malhaire)
* OpenBSD: read battery data
(thanks to @nerd972)
* Various automake and build improvements
(thanks to Kang-Che Sung)
* Check for pkg-config when building with --enable-delayacct
(thanks to @florian2833z for the report)
* Avoid some bashisms in configure script
(thanks to Jesin)
* Use CFLAGS from ncurses*-config if present
(thanks to Michael Klein)
* Header generator supports non-UTF-8 environments
(thanks to @volkov-am)
* Linux: changed detection of kernel threads
* Collapse current subtree pressing Backspace
* BUGFIX: fix behavior of SYSCR column
(thanks to Marc Kleine-Budde)
* BUGFIX: obtain exit code of lsof correctly
(thanks to @wangqr)
* BUGFIX: fix crash with particular keycodes
(thanks to Wellington Torrejais da Silva for the report)
* BUGFIX: fix issue with small terminals
(thanks to Daniel Elf for the report)
* BUGFIX: fix terminal color issues
(thanks to Kang-Che Sung for the report)
* BUGFIX: preserve LDFLAGS when building
(thanks to Lance Frederickson for the report)
* BUGFIX: fixed overflow for systems with >= 100 signals
What's new in version 2.1.0
* Linux: Delay accounting metrics
(thanks to André Carvalho)
* DragonFlyBSD support
(thanks to Diederik de Groot)
* Support for real-time signals
(thanks to Kang-Che Sung)
* 'c' key now works with threads as well
* Session column renamed from SESN to SID
(thanks to Kamyar Rasta)
* Improved UI for meter style selection
(thanks to Kang-Che Sung)
* Improved code for constructing process tree
(thanks to wangqr)
* Compile-time option to disable setuid
* Error checking of various standard library operations
* Replacement of sprintf with snprintf
(thanks to Tomasz Kramkowski)
* Linux: performance improvements in battery meter
* Linux: update process TTY device
* Linux: add support for sorting TASK_IDLE
(thanks to Vladimir Panteleev)
* Linux: add upper-bound to running process counter
(thanks to Lucas Correia Villa Real)
* BUGFIX: avoid crash when battery is removed
(thanks to Jan Chren)
* BUGFIX: macOS: fix infinite loop in tree view
(thanks to Wataru Ashihara)
What's new in version 2.0.2

View File

@ -1,80 +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);
RichString_write(out, CRT_colors[CHECK_BOX], "[");
if (CheckItem_get(this))
RichString_append(out, CRT_colors[CHECK_MARK], "x");
else
RichString_append(out, CRT_colors[CHECK_MARK], " ");
RichString_append(out, CRT_colors[CHECK_BOX], "] ");
RichString_append(out, CRT_colors[CHECK_TEXT], this->text);
}
ObjectClass CheckItem_class = {
.display = CheckItem_display,
.delete = CheckItem_delete
};
CheckItem* CheckItem_newByRef(char* text, bool* ref) {
CheckItem* this = AllocThis(CheckItem);
this->text = text;
this->value = false;
this->ref = ref;
return this;
}
CheckItem* CheckItem_newByVal(char* text, bool value) {
CheckItem* this = AllocThis(CheckItem);
this->text = text;
this->value = value;
this->ref = NULL;
return this;
}
void CheckItem_set(CheckItem* this, bool value) {
if (this->ref)
*(this->ref) = value;
else
this->value = value;
}
bool CheckItem_get(CheckItem* this) {
if (this->ref)
return *(this->ref);
else
return this->value;
}

View File

@ -1,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;
CheckItem* CheckItem_newByRef(char* text, bool* ref);
CheckItem* CheckItem_newByVal(char* text, bool value);
void CheckItem_set(CheckItem* this, bool value);
bool CheckItem_get(CheckItem* this);
#endif

View File

@ -1,33 +1,33 @@
/*
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);
struct tm* lt = localtime_r(&t, &result);
this->values[0] = lt->tm_hour * 60 + lt->tm_min;
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,27 +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;
this->settings->changed = true;
CRT_setColors(mark);
clear();
result = HANDLED | REDRAW;
}
if (result == HANDLED) {
this->settings->changed = true;
const Header* header = this->scr->header;
CRT_setColors(mark);
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);
}
return result;
}
PanelClass ColorsPanel_class = {
const PanelClass ColorsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ColorsPanel_delete
@ -104,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,8 +18,7 @@ typedef struct ColorsPanel_ {
ScreenManager* scr;
} ColorsPanel;
extern PanelClass ColorsPanel_class;
extern const PanelClass ColorsPanel_class;
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr);

View File

@ -1,34 +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;
ScreenSettings* ss;
bool* changed;
bool moving;
} ColumnsPanel;
}*/
static const char* const ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
@ -55,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;
@ -104,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;
@ -116,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
@ -124,54 +115,35 @@ PanelClass ColumnsPanel_class = {
.eventHandler = ColumnsPanel_eventHandler
};
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss) {
Panel* super = (Panel*) this;
Panel_prune(super);
ProcessField* fields = ss->fields;
for (; *fields; fields++) {
if (Process_fields[*fields].name) {
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
}
}
this->ss = ss;
}
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed) {
ColumnsPanel* ColumnsPanel_new(Settings* settings) {
ColumnsPanel* this = AllocThis(ColumnsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->ss = ss;
this->changed = changed;
this->settings = settings;
this->moving = false;
Panel_setHeader(super, "Active Columns");
ColumnsPanel_fill(this, ss);
ProcessField* fields = this->settings->fields;
for (; *fields; fields++) {
if (Process_fields[*fields].name) {
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
}
}
return this;
}
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);
*(this->changed) = true;
this->ss->fields = xRealloc(this->ss->fields, sizeof(ProcessField) * (size+1));
this->ss->flags = 0;
this->settings->changed = true;
this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size + 1));
this->settings->flags = 0;
for (int i = 0; i < size; i++) {
int key = ((ListItem*) Panel_get(super, i))->key;
this->ss->fields[i] = key;
this->ss->flags |= key < 1000 ? Process_fields[key].flags : 0;
this->settings->fields[i] = key;
this->settings->flags |= Process_fields[key].flags;
}
this->ss->fields[size] = 0;
this->settings->fields[size] = 0;
}

View File

@ -1,35 +1,28 @@
/* 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"
typedef struct ColumnsPanel_ {
Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving;
} ColumnsPanel;
extern const PanelClass ColumnsPanel_class;
extern PanelClass ColumnsPanel_class;
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss);
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed);
int ColumnsPanel_fieldNameToIndex(const char* name);
ColumnsPanel* ColumnsPanel_new(Settings* settings);
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:
switch (ch) {
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,24 +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("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("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("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,8 +18,7 @@ typedef struct DisplayOptionsPanel_ {
ScreenManager* scr;
} DisplayOptionsPanel;
extern PanelClass DisplayOptionsPanel_class;
extern const PanelClass DisplayOptionsPanel_class;
DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr);

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);
@ -52,7 +38,7 @@ void EnvScreen_scan(InfoScreen* this) {
char* env = Platform_getProcessEnv(this->process->pid);
CRT_restorePrivileges();
if (env) {
for (char *p = env; *p; p = strrchr(p, 0)+1)
for (char* p = env; *p; p = strrchr(p, 0) + 1)
InfoScreen_addLine(this, p);
free(env);
}
@ -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;
EnvScreen* EnvScreen_new(Process* process);
void EnvScreen_delete(Object* this);
void EnvScreen_draw(InfoScreen* this);
void EnvScreen_scan(InfoScreen* 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,36 +88,61 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
}
}
int FunctionBar_draw(const FunctionBar* this, char* buffer) {
return FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
void FunctionBar_draw(const FunctionBar* this) {
FunctionBar_drawExtra(this, NULL, -1, false);
}
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
int cursorX = 0;
void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor) {
attrset(CRT_colors[FUNCTION_BAR]);
mvhline(LINES-1, 0, ' ', COLS);
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]);
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);
cursorX = x + strlen(buffer);
}
mvaddstr(LINES - 1, x, buffer);
x += strlen(buffer);
}
attrset(CRT_colors[RESET_COLOR]);
return cursorX;
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,27 +1,25 @@
/* 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);
@ -30,9 +28,11 @@ void FunctionBar_delete(FunctionBar* this);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
int FunctionBar_draw(const FunctionBar* this, char* buffer);
void FunctionBar_draw(const FunctionBar* this);
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor);
void FunctionBar_append(const char* buffer, int attr);
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);

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;
fprintf(stderr, "Hashtable %p: items=%zu counted=%zu\n",
(const void*)this,
this->items,
items);
}
int Hashtable_count(Hashtable* this) {
int items = 0;
for (int i = 0; i < this->size; i++) {
HashtableItem* bucket = this->buckets[i];
while (bucket) {
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++;
bucket = bucket->next;
}
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];
}
CRT_fatalError("Hashtable: no prime found");
}
Hashtable* Hashtable_new(int size, bool owner) {
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 {
res = this->buckets[index].value;
}
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));
return value;
}
}
}
assert(Hashtable_isConsistent(this));
return NULL;
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;
}
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) {
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));
return NULL;
} else if (bucketPtr->key == key) {
assert(Hashtable_isConsistent(this));
return bucketPtr->value;
} else
bucketPtr = bucketPtr->next;
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,48 +1,41 @@
/* 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
int Hashtable_count(Hashtable* this);
#endif
Hashtable* Hashtable_new(int size, bool owner);
Hashtable* Hashtable_new(size_t size, bool owner);
void Hashtable_delete(Hashtable* this);
void Hashtable_put(Hashtable* this, unsigned int key, void* value);
void Hashtable_clear(Hashtable* this);
void* Hashtable_remove(Hashtable* this, unsigned int key);
void Hashtable_setSize(Hashtable* this, size_t size);
extern void* Hashtable_get(Hashtable* this, unsigned int key);
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);

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));
@ -62,7 +42,7 @@ void Header_delete(Header* this) {
void Header_populateFromSettings(Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
MeterColumnSettings* colSettings = &this->settings->columns[col];
for (int i = 0; i < colSettings->len; i++) {
Header_addMeterByName(this, colSettings->names[i], col);
if (colSettings->modes[i] != 0) {
@ -75,7 +55,7 @@ void Header_populateFromSettings(Header* this) {
void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
MeterColumnSettings* colSettings = &this->settings->columns[col];
String_freeArray(colSettings->names);
free(colSettings->modes);
@ -83,17 +63,17 @@ void Header_writeBackToSettings(const Header* this) {
Vector* vec = this->columns[col];
int len = Vector_size(vec);
colSettings->names = xCalloc(len+1, sizeof(char*));
colSettings->names = xCalloc(len + 1, sizeof(char*));
colSettings->modes = xCalloc(len, sizeof(int));
colSettings->len = len;
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,6 +101,10 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
break;
}
}
if (paren)
*paren = '(';
return mode;
}
@ -128,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);
@ -145,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);
strncpy(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];
@ -171,10 +142,11 @@ 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) {
@ -209,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,37 +1,29 @@
/* 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
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns);
Header* Header_new(ProcessList* pl, Settings* settings, int nrColumns);
void Header_delete(Header* this);
@ -43,12 +35,10 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column);
void Header_setMode(Header* this, int i, MeterModeId mode, int column);
Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column);
Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, int column);
int Header_size(Header* this, int column);
char* Header_readMeterName(Header* this, int i, int column);
MeterModeId Header_readMeterMode(Header* this, int i, int column);
void Header_reinit(Header* this);

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);
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

370
INSTALL
View File

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

181
IncSet.c
View File

@ -1,61 +1,37 @@
/*
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;
Panel* panel;
FunctionBar* defaultBar;
bool filtering;
bool found;
} IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
}*/
static void IncMode_reset(IncMode* mode) {
mode->index = 0;
mode->buffer[0] = 0;
}
static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL};
static const char* const searchKeys[] = {"F3", "Esc", " "};
static int searchEvents[] = {KEY_F(3), 27, ERR};
void IncSet_reset(IncSet* this, IncType type) {
IncMode_reset(&this->modes[type]);
}
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));
@ -65,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));
@ -78,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;
}
@ -103,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++;
}
}
@ -111,72 +91,75 @@ 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);
}
}
}
static bool search(IncSet* this, 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), this->active->buffer)) {
if (selected == line) {
Panel_setSelected(panel, i);
found = true;
break;
}
}
IncSet_drawBar(this, found ? CRT_colors[FUNCTION_BAR] : CRT_colors[FAILED_SEARCH]);
return found;
}
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
panel->currentBar = this->active->bar;
panel->cursorOn = true;
this->panel = panel;
IncSet_drawBar(this, CRT_colors[FUNCTION_BAR]);
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel);
for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
return true;
}
}
return false;
}
static void IncSet_deactivate(IncSet* this, Panel* panel) {
this->active = NULL;
Panel_setDefaultBar(panel);
panel->cursorOn = false;
FunctionBar_draw(this->defaultBar, NULL);
static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
int size = Panel_size(panel);
int here = Panel_getSelectedIndex(panel);
int i = here;
for (;;) {
i += step;
if (i == size) {
i = 0;
}
if (i == -1) {
i = size - 1;
}
if (i == here) {
return false;
}
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
return true;
}
}
}
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;
int here = Panel_getSelectedIndex(panel);
int i = here;
for(;;) {
i++;
if (i == size) i = 0;
if (i == here) break;
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
break;
}
}
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;
@ -191,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;
@ -200,13 +183,16 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
IncMode_reset(mode);
}
} else {
if (ch == 27) {
IncMode_reset(mode);
}
IncSet_deactivate(this, panel);
}
this->active = NULL;
Panel_setDefaultBar(panel);
doSearch = false;
}
if (doSearch) {
this->found = search(this, panel, getPanelValue);
this->found = search(mode, panel, getPanelValue);
}
if (filterChanged && lines) {
updateWeakPanel(this, panel, lines);
@ -216,18 +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_drawBar(IncSet* this, int attr) {
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
panel->currentBar = this->active->bar;
}
void IncSet_drawBar(const IncSet* this) {
if (this->active) {
int cursorX = FunctionBar_drawAttr(this->active->bar, this->active->buffer, attr);
this->panel->cursorY = LINES - 1;
this->panel->cursorX = cursorX;
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,10 +21,8 @@ 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];
char buffer[INCMODE_MAX + 1];
int index;
FunctionBar* bar;
bool isFilter;
@ -33,26 +31,30 @@ typedef struct IncMode_ {
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
Panel* panel;
FunctionBar* defaultBar;
bool filtering;
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);
IncSet* IncSet_new(FunctionBar* bar);
void IncSet_delete(IncSet* this);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
const char* IncSet_getListItemValue(Panel* panel, int i);
void IncSet_drawBar(IncSet* this, int attr);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
void IncSet_drawBar(const IncSet* this);
int IncSet_synthesizeEvent(IncSet* this, int x);

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, 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);
@ -75,47 +40,63 @@ InfoScreen* InfoScreen_done(InfoScreen* this) {
return this;
}
void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...) {
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);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
va_end(ap);
Panel_draw(this->display, true, true, true, false);
IncSet_drawBar(this->inc);
}
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* 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);
int ch = Panel_getCh(panel);
if (this->inc->active) {
(void) move(LINES - 1, CRT_cursorX);
}
set_escdelay(25);
int ch = getch();
if (ch == ERR) {
if (As_InfoScreen(this)->onErr) {
@ -127,13 +108,15 @@ void InfoScreen_run(InfoScreen* this) {
if (ch == KEY_MOUSE) {
MEVENT mevent;
int ok = getmouse(&mevent);
if (ok == OK)
if (ok == OK) {
if (mevent.y >= panel->y && mevent.y < LINES - 1) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV);
ch = 0;
} if (mevent.y == LINES - 1)
} else if (mevent.y == LINES - 1) {
ch = IncSet_synthesizeEvent(this->inc, mevent.x);
}
}
}
if (this->inc->active) {
IncSet_handleKey(this->inc, ch, panel, IncSet_getListItemValue, this->lines);
@ -153,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);
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,33 +26,25 @@ 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, Process* process, FunctionBar* bar, int height, char* panelHeader);
InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader);
InfoScreen* InfoScreen_done(InfoScreen* this);
void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...);
ATTR_FORMAT(printf, 2, 3)
void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...);
void InfoScreen_addLine(InfoScreen* this, const char* line);

View File

@ -1,48 +1,35 @@
/*
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;
}*/
void ListItem_delete(Object* cast) {
static void ListItem_delete(Object* cast) {
ListItem* this = (ListItem*)cast;
free(this->value);
free(this);
}
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,43 +37,34 @@ void ListItem_display(Object* cast, RichString* out) {
} else {
RichString_prune(out);
}
RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value/*buffer*/);
}
ObjectClass ListItem_class = {
.display = ListItem_display,
.delete = ListItem_delete,
.compare = ListItem_compare
};
void ListItem_init(ListItem* this, const char* value, int key) {
this->value = xStrdup(value);
this->key = key;
this->moving = false;
RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value);
}
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
ListItem_init(this, value, key);
this->value = xStrdup(value);
this->key = key;
this->moving = false;
return this;
}
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,22 +18,14 @@ typedef struct ListItem_ {
bool moving;
} ListItem;
void ListItem_delete(Object* cast);
void ListItem_display(Object* cast, RichString* out);
extern ObjectClass ListItem_class;
void ListItem_init(ListItem* this, const char* value, int key);
extern const ObjectClass ListItem_class;
ListItem* ListItem_new(const char* value, int key);
void ListItem_append(ListItem* this, const char* text);
const char* ListItem_getRef(ListItem* this);
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

@ -1,52 +1,36 @@
/*
htop - ColumnsPanel.c
(C) 2004-2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
(C) 2020 Red Hat, Inc. All Rights Reserved.
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*, size_t);
#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) {
Panel* super = (Panel*) this;
pid_t pid = ch-48 + this->pidSearch;
pid_t pid = ch - 48 + this->pidSearch;
for (int i = 0; i < Panel_size(super); i++) {
Process* p = (Process*) Panel_get(super, i);
if (p && p->pid == pid) {
@ -60,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;
@ -67,24 +56,33 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK;
Settings* settings = this->state->settings;
ScreenSettings* ss = settings->ss;
/* 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 == ss->sortKey) {
ScreenSettings_invertSortOrder(ss);
ss->treeView = false;
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;
@ -95,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,14 +109,13 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
}
if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, settings->ss->treeView);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
}
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;
@ -130,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;
}
@ -143,14 +141,7 @@ 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, size_t arg, bool* wasAnyTagged) {
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
Panel* super = (Panel*) this;
bool ok = true;
bool anyTagged = false;
@ -163,19 +154,43 @@ bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, si
}
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,44 +1,48 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_MainPanel
#define HEADER_MainPanel
/*
htop - ColumnsPanel.h
(C) 2004-2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
(C) 2020 Red Hat, Inc. All Rights Reserved.
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;
State* state;
IncSet* inc;
Htop_Action *keys;
Htop_Action* keys;
pid_t pidSearch;
} MainPanel;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, size_t);
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
void MainPanel_pidSearch(MainPanel* this, int ch);
int MainPanel_selectedPid(MainPanel* this);
const char* MainPanel_getValue(MainPanel* this, int i);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, size_t arg, bool* wasAnyTagged);
extern const PanelClass MainPanel_class;
extern PanelClass MainPanel_class;
MainPanel* MainPanel_new();
MainPanel* MainPanel_new(void);
void MainPanel_setState(MainPanel* this, State* state);

View File

@ -1,121 +1,305 @@
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
htop_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
htop_LDFLAGS =
AM_CPPFLAGS = -DNDEBUG
AM_CFLAGS += -pedantic -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
AM_LDFLAGS =
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 ScreensPanel.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 ScreensPanel.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
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/IOPriority.h \
linux/IOPriorityPanel.h \
linux/LibSensors.h \
linux/LinuxProcess.h \
linux/LinuxProcessList.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/ZfsArcStats.h \
zfs/ZfsCompressedArcMeter.h
if HTOP_LINUX
htop_CFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \
linux/PerfCounter.c
AM_LDFLAGS += -rdynamic
myhtopplatsources = \
linux/IOPriorityPanel.c \
linux/LibSensors.c \
linux/LinuxProcess.c \
linux/LinuxProcessList.c \
linux/Platform.c \
linux/PressureStallMeter.c \
linux/SELinuxMeter.c \
linux/SystemdMeter.c \
linux/ZramMeter.c \
zfs/ZfsArcMeter.c \
zfs/ZfsArcStats.c \
zfs/ZfsCompressedArcMeter.c
myhtopplatheaders = linux/Platform.h linux/IOPriorityPanel.h linux/IOPriority.h \
linux/LinuxProcess.h linux/LinuxProcessList.h linux/LinuxCRT.h linux/Battery.h \
linux/PerfCounter.h
myhtopplatheaders = $(linux_platform_headers)
endif
# FreeBSD
# -------
freebsd_platform_headers = \
freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \
freebsd/Platform.h \
freebsd/ProcessField.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
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.h freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h freebsd/FreeBSDCRT.h freebsd/Battery.h
myhtopplatheaders = $(freebsd_platform_headers)
endif
# DragonFlyBSD
# ------------
dragonflybsd_platform_headers = \
dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/Platform.h \
dragonflybsd/ProcessField.h
if HTOP_DRAGONFLYBSD
htop_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.h dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h dragonflybsd/DragonFlyBSDCRT.h dragonflybsd/Battery.h
myhtopplatheaders = $(dragonflybsd_platform_headers)
endif
# OpenBSD
# -------
openbsd_platform_headers = \
openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h \
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.h openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h openbsd/OpenBSDCRT.h openbsd/Battery.h
myhtopplatheaders = $(openbsd_platform_headers)
endif
# Darwin
# ------
darwin_platform_headers = \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
darwin/Platform.h \
darwin/ProcessField.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
if HTOP_DARWIN
htop_LDFLAGS += -framework IOKit -framework CoreFoundation
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.h darwin/DarwinProcess.h \
darwin/DarwinProcessList.h darwin/DarwinCRT.h darwin/Battery.h
myhtopplatheaders = $(darwin_platform_headers)
endif
# Solaris
# -------
solaris_platform_headers = \
solaris/Platform.h \
solaris/ProcessField.h \
solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(solaris_platform_headers)
endif
# Unsupported
# -----------
unsupported_platform_headers = \
unsupported/Platform.h \
unsupported/ProcessField.h \
unsupported/UnsupportedProcess.h \
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.h \
unsupported/UnsupportedProcess.h unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h unsupported/Battery.h
myhtopplatheaders = $(unsupported_platform_headers)
endif
SUFFIXES = .h
# ----
BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) config.h
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
nodist_htop_SOURCES = config.h
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"
symbols:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DNDEBUG"
$(MAKE) all AM_CPPFLAGS="-ggdb -DDEBUG"
coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"
.c.h:
@srcdir@/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 \
echo 'configure is generated without pkg.m4. Please supply pkg.m4 and run ./autogen.sh to rebuild the configure script.'>&2; \
(exit 1); \
else :; \
fi
.PHONY: lcov

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

333
Meter.c
View File

@ -1,158 +1,65 @@
/*
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) {
const char * prefix = "KMGTPEZY";
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(;;) {
for (;;) {
if (value / 1024 < powi)
break;
if (prefix[1] == 0)
if (prefix[1] == '\0')
break;
powi *= 1024;
@ -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,29 +190,49 @@ 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, 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) {
blockSizes[i] = ceil((value/this->total) * w);
blockSizes[i] = ceil((value / this->total) * w);
} else {
blockSizes[i] = 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,32 +322,32 @@ 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];
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;
}
int i = nValues - (w*2) + 2, k = 0;
int i = nValues - (w * 2) + 2, k = 0;
if (i < 0) {
k = -i/2;
k = -i / 2;
i = 0;
}
for (; i < nValues; i+=2, k++) {
for (; i < nValues - 1; i += 2, k++) {
int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix);
int v2 = CLAMP((int) lround(data->values[i+1] * pix), 1, pix);
int v2 = CLAMP((int) lround(data->values[i + 1] * pix), 1, pix);
int colorIdx = GRAPH_1;
for (int line = 0; line < GRAPH_HEIGHT; line++) {
@ -415,7 +355,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
int line2 = CLAMP(v2 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line)), 0, GraphMeterMode_pixPerRow);
attrset(CRT_colors[colorIdx]);
mvaddstr(y+line, x+k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]);
mvaddstr(y + line, x + k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]);
colorIdx = GRAPH_2;
}
}
@ -425,17 +365,17 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
/* ---------- LEDMeterMode ---------- */
static const char* const LEDMeterMode_digitsAscii[] = {
" __ "," "," __ "," __ "," "," __ "," __ "," __ "," __ "," __ ",
"| |"," |"," __|"," __|","|__|","|__ ","|__ "," |","|__|","|__|",
"|__|"," |","|__ "," __|"," |"," __|","|__|"," |","|__|"," __|"
" __ ", " ", " __ ", " __ ", " ", " __ ", " __ ", " __ ", " __ ", " __ ",
"| |", " |", " __|", " __|", "|__|", "|__ ", "|__ ", " |", "|__|", "|__|",
"|__|", " |", "|__ ", " __|", " |", " __|", "|__|", " |", "|__|", " __|"
};
#ifdef HAVE_LIBNCURSESW
static const char* const LEDMeterMode_digitsUtf8[] = {
"┌──┐","","╶──┐","╶──┐","╷ ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐",
"│ │","","┌──┘"," ──┤","└──┤","└──┐","├──┐","","├──┤","└──┤",
"└──┘","","└──╴","╶──┘","","╶──┘","└──┘","","└──┘"," ──┘"
"┌──┐", "", "╶──┐", "╶──┐", "╷ ╷", "┌──╴", "┌──╴", "╶──┐", "┌──┐", "┌──┐",
"│ │", "", "┌──┘", " ──┤", "└──┤", "└──┐", "├──┐", "", "├──┤", "└──┤",
"└──┘", "", "└──╴", "╶──┘", "", "╶──┘", "└──┘", "", "└──┘", " ──┘"
};
#endif
@ -458,24 +398,24 @@ 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);
int yText =
#ifdef HAVE_LIBNCURSESW
CRT_utf8 ? y+1 :
CRT_utf8 ? y + 1 :
#endif
y+2;
y + 2;
attrset(CRT_colors[LED_COLOR]);
mvaddstr(yText, x, this->caption);
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);
LEDMeterMode_drawDigit(xx, y, c - 48);
xx += 4;
} else {
mvaddch(yText, xx, c);
@ -510,7 +450,7 @@ static MeterMode LEDMeterMode = {
.draw = LEDMeterMode_draw,
};
MeterMode* Meter_modes[] = {
const MeterMode* const Meter_modes[] = {
NULL,
&BarMeterMode,
&TextMeterMode,
@ -521,20 +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,

127
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,27 +121,11 @@ 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
extern MeterClass Meter_class;
Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type);
int Meter_humanUnit(char* buffer, unsigned long int value, int size);
int Meter_humanUnit(char* buffer, unsigned long int value, size_t size);
void Meter_delete(Object* cast);
@ -121,31 +135,8 @@ void Meter_setMode(Meter* this, int modeIndex);
ListItem* Meter_toListItem(Meter* this, bool moving);
/* ---------- TextMeterMode ---------- */
extern const MeterMode* const Meter_modes[];
/* ---------- 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 char* const MetersKeys[] = {"Space", "Enter", "", "Del", "F10"};
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,17 +29,11 @@ struct MetersPanel_ {
bool moving;
};
// 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_cleanup(void);
void MetersPanel_setMoving(MetersPanel* this, bool moving);
extern PanelClass MetersPanel_class;
extern const PanelClass MetersPanel_class;
MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr);

1
NEWS
View File

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

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

@ -1,63 +1,33 @@
/*
htop - Object.c
(C) 2004-2012 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
(C) 2020 Red Hat, Inc. All Rights Reserved.
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;
};
}*/
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,53 +1,66 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Object
#define HEADER_Object
/*
htop - Object.h
(C) 2004-2012 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
(C) 2020 Red Hat, Inc. All Rights Reserved.
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 {
int i;
void* v;
} Arg;
extern ObjectClass Object_class;
extern const ObjectClass Object_class;
#ifdef DEBUG
#ifndef NDEBUG
bool Object_isA(Object* o, const ObjectClass* klass);
#endif
bool Object_isA(const Object* o, const ObjectClass* klass);
#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,49 +39,93 @@ 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 command[1025];
xSnprintf(command, 1024, "lsof -P -p %d -F 2> /dev/null", pid);
FILE* fd = popen(command, "r");
OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_FileData* fdata = NULL;
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) {
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 = 127;
pdata->error = 1;
return pdata;
}
for (;;) {
@ -91,8 +133,11 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
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;
@ -101,21 +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);
}
pdata->error = pclose(fd);
fclose(fd);
int wstatus;
if (waitpid(child, &wstatus, 0) == -1) {
pdata->error = 1;
return pdata;
}
if (!WIFEXITED(wstatus)) {
pdata->error = 1;
} 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);
@ -127,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*/;
char* entry = xMalloc(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'] : "");
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, 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);
@ -151,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(Process* process);
OpenFilesScreen* OpenFilesScreen_new(const Process* process);
void OpenFilesScreen_delete(Object* this);
void OpenFilesScreen_draw(InfoScreen* this);
void OpenFilesScreen_scan(InfoScreen* 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

312
Panel.c
View File

@ -1,98 +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;
int cursorX, cursorY;
WINDOW* window;
Vector* items;
int selected;
int oldSelected;
int selectedLen;
void* eventHandlerState;
int scrollV;
short scrollH;
bool needsRedraw;
bool cursorOn;
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)
void Panel_setCursorToSelection(Panel* this) {
this->cursorY = this->y + this->selected - this->scrollV + 1;
this->cursorX = this->x + this->selectedLen - this->scrollH;
}
PanelClass Panel_class = {
const PanelClass Panel_class = {
.super = {
.extends = Class(Object),
.delete = Panel_delete
@ -100,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));
@ -114,24 +44,24 @@ 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;
this->h = h;
this->cursorX = 0;
this->cursorY = 0;
this->eventHandlerState = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE);
this->scrollV = 0;
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) {
@ -142,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;
}
@ -169,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;
@ -217,33 +138,38 @@ 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) {
@ -265,15 +191,24 @@ 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);
}
}
void Panel_draw(Panel* this, bool focus) {
void Panel_splice(Panel* this, Vector* from) {
assert (this != NULL);
assert (from != NULL);
Vector_splice(this->items, from);
this->needsRedraw = true;
}
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar) {
assert (this != NULL);
int size = Vector_size(this->items);
@ -282,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
@ -302,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
@ -315,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++) {
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++;
@ -348,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);
@ -361,90 +305,105 @@ void Panel_draw(Panel* this, bool focus) {
Object_display(newObj, &new);
int newLen = RichString_sizeVal(new);
this->selectedLen = newLen;
mvhline(y+ this->oldSelected - first, x+0, ' ', this->w);
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));
RichString_printoffnVal(old, y + this->oldSelected - first, x,
scrollH, MINIMUM(oldLen - scrollH, this->w));
attrset(selectionColor);
mvhline(y+this->selected - first, x+0, ' ', this->w);
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));
RichString_printoffnVal(new, y + this->selected - first, x,
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,55 +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 < 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;
}
int Panel_getCh(Panel* this) {
if (this->cursorOn) {
move(this->cursorY, this->cursorX);
curs_set(1);
} else {
curs_set(0);
}
set_escdelay(25);
return getch();
}

80
Panel.h
View File

@ -1,53 +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 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;
int cursorX, cursorY;
WINDOW* window;
Vector* items;
int selected;
int oldSelected;
@ -56,42 +65,30 @@ struct Panel_ {
int scrollV;
short scrollH;
bool needsRedraw;
bool cursorOn;
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 Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0)
#define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this);
extern const PanelClass Panel_class;
extern PanelClass Panel_class;
Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar);
Panel* Panel_new(int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar);
void Panel_delete(Object* cast);
void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar);
void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar);
void Panel_done(Panel* this);
void Panel_setSelectionColor(Panel* this, int color);
void Panel_setSelectionColor(Panel* this, ColorElements colorId);
RichString* Panel_getHeader(Panel* this);
extern void Panel_setHeader(Panel* this, const char* header);
void Panel_setHeader(Panel* this, const char* header);
void Panel_move(Panel* this, int x, int y);
@ -121,13 +118,12 @@ int Panel_size(Panel* this);
void Panel_setSelected(Panel* this, int selected);
void Panel_draw(Panel* this, bool focus);
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar);
void Panel_splice(Panel* this, Vector* from);
bool Panel_onKey(Panel* this, int key);
HandlerResult Panel_selectByTyping(Panel* this, int ch);
int Panel_getCh(Panel* this);
#endif

691
Process.c
View File

@ -1,284 +1,155 @@
/*
htop - Process.c
(C) 2004-2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include "Process.h"
#include "Settings.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>
#include <inttypes.h>
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#if defined(MAJOR_IN_MKDEV)
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS)
#include <sys/sysmacros.h>
#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"
static uid_t Process_getuid = (uid_t)-1;
#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;
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;
uint64_t 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_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
}*/
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_DECIMAL_K 1000L
#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K)
#define ONE_DECIMAL_G (ONE_DECIMAL_M * 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, "%4ldT ", 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, "%4ldG ", 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, "%4ldM ", number / ONE_K);
RichString_appendn(str, processMegabytesColor, buffer, len);
return;
} else if (number >= 1000) {
len = snprintf(buffer, 10, "%2ld", 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);
} else if (number > 10000000000) {
xSnprintf(buffer, 13, "%11lld ", number / 1000);
RichString_appendn(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4);
if (number == ULLONG_MAX) {
RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
} else if (number >= 100000LL * ONE_DECIMAL_T) {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G);
RichString_appendnAscii(str, largeNumberColor, buffer, 12);
} else if (number >= 100LL * ONE_DECIMAL_T) {
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, 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);
}
}
@ -289,110 +160,132 @@ 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);
} else if (rate < ONE_K * ONE_K) {
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);
} else if (rate < ONE_K * ONE_K * ONE_K) {
int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_K / ONE_K);
RichString_appendn(str, processMegabytesColor, 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_appendnAscii(str, processMegabytesColor, buffer, len);
} else if (rate < ONE_T) {
int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G);
RichString_appendnAscii(str, largeNumberColor, buffer, len);
} else {
int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_K / ONE_K / ONE_K);
RichString_appendn(str, largeNumberColor, buffer, len);
int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T);
RichString_appendnAscii(str, largeNumberColor, buffer, len);
}
}
void Process_printPercentage(float val, char* buffer, int n, int* attr) {
if (val >= 0) {
if (val < 100) {
xSnprintf(buffer, n, "%4.1f ", val);
} else if (val < 1000) {
xSnprintf(buffer, n, "%3d. ", (unsigned int)val);
} else {
xSnprintf(buffer, n, "%4d ", (unsigned int)val);
}
} else {
*attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A ");
}
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(Process* this, RichString* str, ProcessField field) {
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: Process_printPercentage(this->percent_cpu, buffer, n, &attr); break;
case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, &attr); break;
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 ", cpuPercentage);
}
break;
}
case PERCENT_MEM: {
if (this->percent_mem > 99.9) {
xSnprintf(buffer, n, "100. ");
} else {
xSnprintf(buffer, n, "%4.1f ", this->percent_mem);
}
break;
}
case COMM: {
if (this->settings->highlightThreads && Process_isThread(this)) {
attr = CRT_colors[PROCESS_THREAD];
baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
}
ScreenSettings* ss = this->settings->ss;
if (!ss->treeView || this->indent == 0) {
if (!this->settings->treeView || this->indent == 0) {
Process_writeCommand(this, attr, baseattr, str);
return;
} else {
@ -401,29 +294,39 @@ 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;
if (indent & (1 << i))
written = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
else
written = snprintf(buf, n, " ");
int written, ret;
if (indent & (1 << i)) {
ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
} else {
ret = snprintf(buf, n, " ");
}
if (ret < 0 || (size_t)ret >= n) {
written = n;
} else {
written = ret;
}
buf += written;
n -= written;
}
const char* draw = CRT_treeStr[lastItem ? (ss->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
const char* draw = CRT_treeStr[lastItem ? 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]
@ -432,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 ");
@ -443,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);
@ -457,41 +360,52 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
}
break;
}
case ST_UID: xSnprintf(buffer, n, "%4d ", this->st_uid); break;
case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break;
case TIME: Process_printTime(str, this->time); return;
case 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->ss->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);
}
@ -500,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,
@ -508,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) {
@ -535,85 +469,100 @@ bool Process_setPriority(Process* this, int priority) {
return (err == 0);
}
bool Process_changePriorityBy(Process* this, size_t delta) {
return Process_setPriority(this, this->nice + delta);
bool Process_changePriorityBy(Process* this, Arg delta) {
return Process_setPriority(this, this->nice + delta.i);
}
void Process_sendSignal(Process* this, size_t sgn) {
bool Process_sendSignal(Process* this, Arg sgn) {
CRT_dropPrivileges();
kill(this->pid, (int) sgn);
bool ok = (kill(this->pid, sgn.i) == 0);
CRT_restorePrivileges();
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;
ScreenSettings* ss = settings->ss;
if (ss->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 (ss->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 (p1->state - 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);
}
}

153
Process.h
View File

@ -1,33 +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
Released under the GNU GPL, see the COPYING file
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#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,
@ -43,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,
@ -52,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;
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;
@ -79,6 +76,7 @@ typedef struct Process_ {
bool tag;
bool showChildren;
bool show;
bool wasShown;
unsigned int pgrp;
unsigned int session;
unsigned int tty_nr;
@ -89,7 +87,7 @@ typedef struct Process_ {
float percent_cpu;
float percent_mem;
char* user;
const char* user;
long int priority;
long int nice;
@ -97,105 +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;
uint64_t 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_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#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_))
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 (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 (1ULL * ONE_DECIMAL_G * ONE_DECIMAL_K)
extern char Process_pidFormat[20];
void Process_setupColumnWidths(void);
void Process_setupColumnWidths();
void Process_humanNumber(RichString* str, unsigned long number, bool coloring);
void Process_humanNumber(RichString* str, unsigned long long number, bool coloring);
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
void Process_fillStarttimeBuffer(Process* this);
void Process_printPercentage(float val, char* buffer, int n, int* attr);
void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring);
void Process_writeField(Process* this, RichString* str, ProcessField field);
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width);
void Process_display(Object* cast, RichString* out);
void Process_display(const Object* cast, RichString* out);
void Process_done(Process* this);
extern ProcessClass Process_class;
extern const ProcessClass Process_class;
void Process_init(Process* this, struct Settings_* settings);
void Process_init(Process* this, const struct Settings_* settings);
void Process_toggleTag(Process* this);
bool Process_isNew(const Process* this);
bool Process_isTomb(const Process* this);
bool Process_setPriority(Process* this, int priority);
bool Process_changePriorityBy(Process* this, size_t delta);
bool Process_changePriorityBy(Process* this, Arg delta);
void Process_sendSignal(Process* this, size_t sgn);
bool Process_sendSignal(Process* this, Arg sgn);
long Process_pidCompare(const void* v1, const void* v2);
int Process_pidCompare(const void* v1, const void* v2);
long Process_compare(const void* v1, const void* v2);
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
#endif

View File

@ -1,104 +1,57 @@
/*
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;
int topoErr = hwloc_topology_init(&this->topology);
if (topoErr == 0) {
topoErr = hwloc_topology_load(this->topology);
}
if (topoErr == 0) {
this->topologyOk = true;
if (hwloc_topology_init(&this->topology) == 0) {
this->topologyOk =
#if HWLOC_API_VERSION < 0x00020000
/* try to ignore the top-level machine object type */
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_MACHINE) &&
/* ignore caches, which don't add structure */
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CORE) &&
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CACHE) &&
0 == hwloc_topology_set_flags(this->topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) &&
#else
0 == hwloc_topology_set_all_types_filter(this->topology, HWLOC_TYPE_FILTER_KEEP_STRUCTURE) &&
#endif
0 == hwloc_topology_load(this->topology);
}
#endif
@ -113,32 +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->ss->fields;
const Settings* settings = this->settings;
const ProcessField* fields = settings->fields;
ProcessField key = Settings_getActiveSortKey(settings);
for (int i = 0; fields[i]; i++) {
unsigned int key = fields[i];
const char* field = Process_fields[key].title;
if (!field) field = "- ";
int color = (!this->settings->ss->treeView && this->settings->ss->sortKey == key)
? CRT_colors[PANEL_SELECTION_FOCUS]
: CRT_colors[PANEL_HEADER_FOCUS];
RichString_append(header, color, 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);
@ -151,90 +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->ss->treeView) {
Vector_insertionSort(this->processes);
} else {
// Save settings
int direction = this->settings->ss->direction;
int sortKey = this->settings->ss->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->ss->sortKey = PID;
this->settings->ss->direction = 1;
Vector_quickSort(this->processes);
// Restore settings
this->settings->ss->sortKey = sortKey;
this->settings->ss->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->tgid == process->pid ? process->ppid : process->tgid;
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) {
@ -243,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->ss->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;
}
@ -293,31 +498,27 @@ 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) {
@ -334,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;
}
@ -348,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,14 +71,15 @@ 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);
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);
void ProcessList_done(ProcessList* this);
@ -89,7 +97,7 @@ int ProcessList_size(ProcessList* this);
void ProcessList_sort(ProcessList* this);
ProcessField ProcessList_keyAt(ProcessList* this, int at);
ProcessField ProcessList_keyAt(const ProcessList* this, int at);
void ProcessList_expandTree(ProcessList* this);
@ -97,6 +105,6 @@ void ProcessList_rebuildPanel(ProcessList* this);
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor);
void ProcessList_scan(ProcessList* this);
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate);
#endif

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

91
README
View File

@ -1,60 +1,69 @@
[![Build Status](https://travis-ci.org/hishamhm/htop.svg?branch=master)](https://travis-ci.org/hishamhm/htop)
[![PayPal donate](https://img.shields.io/badge/paypal-donate-green.svg)](http://hisham.hm/htop/index.php?page=donate)
# [![htop](htop.png)](https://htop.dev)
[htop](http://hisham.hm/htop/)
====
[![CI](https://github.com/htop-dev/htop/workflows/CI/badge.svg)](https://github.com/htop-dev/htop/actions)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/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)
by Hisham Muhammad <hisham@gobolinux.org> (2004 - 2016)
![Screenshot of htop](docs/images/screenshot.png?raw=true)
Introduction
------------
## Introduction
This is `htop`, an interactive process viewer.
It requires `ncurses`. It is developed primarily on Linux,
but we also have code for running under FreeBSD and Mac OS X
(help and testing are wanted for these platforms!)
`htop` is a cross-platform interactive process viewer.
This software has evolved considerably over the years,
and is reasonably complete, but there is always room for improvement.
`htop` allows scrolling the list of processes vertically and horizontally to see their full command lines and related information like memory and CPU consumption.
Comparison between `htop` and classic `top`
-------------------------------------------
The information displayed is configurable through a graphical setup and can be sorted and filtered interactively.
* In `htop` you can scroll the list vertically and horizontally
to see all processes and full command lines.
* In `top` you are subject to a delay for each unassigned
key you press (especially annoying when multi-key escape
sequences are triggered by accident).
* `htop` starts faster (`top` seems to collect data for a while
before displaying anything).
* In `htop` you don't need to type the process number to
kill a process, in `top` you do.
* In `htop` you don't need to type the process number or
the priority value to renice a process, in `top` you do.
* In `htop` you can kill multiple processes at once.
* `top` is older, hence, more tested.
Tasks related to processes (e.g. killing and renicing) can be done without entering their PIDs.
Compilation instructions
------------------------
Running `htop` requires `ncurses` libraries (typically named libncursesw*).
This program is distributed as a standard autotools-based package.
See the [INSTALL](/INSTALL) file for detailed instructions.
For more information and details on how to contribute to `htop` visit [htop.dev](https://htop.dev).
When compiling from a [release tarball](https://hisham.hm/htop/releases/), run:
## Build instructions
./configure && make
This program is distributed as a standard GNU autotools-based package.
For compiling sources downloaded from the Git repository, run:
Compiling `htop` requires the header files for `ncurses` (libncursesw*-dev). Install these and other required packages for C development from your package manager.
./autogen.sh && ./configure && make
Then, when compiling from a [release tarball](https://bintray.com/htop/source/htop), run:
By default `make install` will install into `/usr/local`, for changing
the path use `./configure --prefix=/some/path`.
~~~ shell
./configure && make
~~~
See the manual page (`man htop`) or the on-line help ('F1' or 'h'
inside `htop`) for a list of supported key commands.
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:
If not all keys work check your curses configuration.
~~~ shell
./autogen.sh && ./configure && make
~~~
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.
## 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,67 +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;
}*/
#define charBytes(n) (sizeof(CharType) * (n))
@ -85,27 +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) {
wchar_t data[len+1];
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;
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;
}
}
@ -120,21 +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;
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;
}
}
@ -148,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)
@ -157,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,90 +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 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 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];
CharType chstr[RICHSTRING_MAXLEN + 1];
int highlightAttr;
} RichString;
#define charBytes(n) (sizeof(CharType) * (n))
#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)
#ifdef HAVE_LIBNCURSESW
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
void RichString_setAttrn(RichString* this, int attrs, int start, int charcount);
int RichString_findChar(RichString* this, char c, int start);
#else
void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
int RichString_findChar(RichString* this, char c, int start);
#endif
void RichString_prune(RichString* this);
void RichString_setAttr(RichString* this, int attrs);
void RichString_append(RichString* this, int attrs, const char* data);
void RichString_appendChr(RichString* this, char c, int count);
void RichString_appendn(RichString* this, int attrs, const char* data, int len);
int RichString_appendWide(RichString* this, int attrs, const char* data);
void RichString_write(RichString* this, int attrs, const char* data);
int RichString_appendnWide(RichString* this, int attrs, const char* data, int len);
int RichString_writeWide(RichString* this, int attrs, const char* data);
int RichString_appendAscii(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,46 +47,27 @@ inline int ScreenManager_size(ScreenManager* this) {
}
void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
ScreenManager_insert(this, item, size, Vector_size(this->panels));
}
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx) {
if (this->orientation == HORIZONTAL) {
int lastX = 0;
if (idx > 0) {
Panel* last = (Panel*) Vector_get(this->panels, idx - 1);
if (this->panelCount > 0) {
Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
lastX = last->x + last->w + 1;
}
int height = LINES - this->y1 + this->y2;
if (size <= 0) {
size = COLS-this->x1+this->x2-lastX;
}
if (size > 0) {
Panel_resize(item, size, height);
} else {
Panel_resize(item, COLS - this->x1 + this->x2 - lastX, height);
}
Panel_move(item, lastX, this->y1);
if (idx < this->panelCount) {
for (int i = idx + 1; i <= this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x + size, p->y);
}
}
}
// TODO: VERTICAL
Vector_insert(this->panels, idx, item);
Vector_add(this->panels, item);
item->needsRedraw = true;
this->panelCount++;
}
Panel* ScreenManager_remove(ScreenManager* this, int idx) {
assert(this->panelCount > idx);
int w = ((Panel*) Vector_get(this->panels, idx))->w;
Panel* panel = (Panel*) Vector_remove(this->panels, idx);
this->panelCount--;
if (idx < this->panelCount) {
for (int i = idx; i < this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x - w, p->y);
}
}
return panel;
}
@ -120,34 +77,36 @@ 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);
Panel_resize(panel, panel->w, LINES-y1+y2);
Panel_resize(panel, panel->w, LINES - y1 + y2);
Panel_move(panel, lastX, y1);
lastX = panel->x + panel->w + 1;
}
Panel* panel = (Panel*) Vector_get(this->panels, panels-1);
Panel_resize(panel, COLS-x1+x2-lastX, LINES-y1+y2);
Panel* panel = (Panel*) Vector_get(this->panels, panels - 1);
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) {
static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut) {
ProcessList* pl = this->header->pl;
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->ss->treeView) {
ProcessList_scan(pl, this->state->pauseProcessUpdate);
if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) {
ProcessList_sort(pl);
*sortTimeout = 1;
}
@ -160,30 +119,20 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
*rescan = false;
}
static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
int nPanels = this->panelCount;
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 (i < nPanels) {
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(ScreenManager* this, int focus) {
Panel* panel = (Panel*) Vector_get(this->panels, focus);
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(this, focus);
Panel* panelFocus = (Panel*) Vector_get(this->panels, focus);
double oldTime = 0.0;
@ -192,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;
@ -201,15 +151,17 @@ 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;
ch = Panel_getCh(panelFocus);
set_escdelay(25);
ch = getch();
HandlerResult result = IGNORED;
if (ch == KEY_MOUSE) {
if (ch == KEY_MOUSE && this->settings->enableMouse) {
ch = ERR;
MEVENT mevent;
int ok = getmouse(&mevent);
@ -220,15 +172,15 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
} else {
for (int i = 0; i < this->panelCount; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i);
if (mevent.x >= panel->x && mevent.x <= panel->x+panel->w) {
if (mevent.x >= panel->x && mevent.x <= panel->x + panel->w) {
if (mevent.y == panel->y) {
ch = EVENT_HEADER_CLICK(mevent.x - panel->x);
break;
} else if (mevent.y > panel->y && mevent.y <= panel->y+panel->h) {
} else if (mevent.y > panel->y && mevent.y <= panel->y + panel->h) {
ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) {
focus = i;
panelFocus = setCurrentPanel(this, i);
panelFocus = panel;
Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) {
@ -256,8 +208,9 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (closeTimeout == 100) {
break;
}
} else
} else {
closeTimeout = 0;
}
redraw = false;
continue;
}
@ -274,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;
@ -299,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)
}
tryLeft:
if (focus > 0) {
focus--;
panelFocus = setCurrentPanel(this, 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'):
@ -314,30 +277,39 @@ 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)
}
tryRight:
if (focus < this->panelCount - 1) {
focus++;
panelFocus = setCurrentPanel(this, 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:
defaultHandler:
defaultHandler:
sortTimeout = resetSortTimeout;
Panel_onKey(panelFocus, ch);
break;
}
}
if (lastFocus)
if (lastFocus) {
*lastFocus = panelFocus;
if (lastKey)
}
if (lastKey) {
*lastKey = ch;
}
}

View File

@ -1,50 +1,43 @@
/* 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(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner);
ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const State* state, bool owner);
void ScreenManager_delete(ScreenManager* this);
extern int ScreenManager_size(ScreenManager* this);
int ScreenManager_size(ScreenManager* this);
void ScreenManager_add(ScreenManager* this, Panel* item, int size);
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx);
Panel* ScreenManager_remove(ScreenManager* this, int idx);
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);

View File

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

View File

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

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